/*
 * DemoGarden design system.
 *
 * Hand-written, dependency-free CSS implementing the design tokens (palette,
 * type scale, cards, badges, stepper, buttons) so local development needs no
 * Node/Tailwind toolchain. Templates use ONLY these plain semantic classes.
 * The Dockerfile MAY later swap in a Tailwind build stage; if it does, keep
 * these class names as the stable contract between templates and styles.
 *
 * Visual language adapted from inkcompass (mozaiqlabs) — engraved-paper
 * calm, serif display type, generous cards — restyled to DemoGarden's
 * garden/greenhouse identity. No external fonts, no CDN references: the
 * demo must survive conference Wi-Fi.
 */

/* ------------------------------------------------------------------ */
/* Tokens                                                             */
/* ------------------------------------------------------------------ */

:root {
  color-scheme: light;

  /* Palette */
  --ink:        #14231b;   /* deep evergreen — primary text, dark surfaces */
  --ink-700:    #1e3328;
  --ink-600:    #2a4536;
  --ink-500:    #43604f;   /* muted text */
  --ink-faint:  rgba(20, 35, 27, .06);

  --paper:      #f4f2e8;   /* page background */
  --paper-card: #fffef9;   /* card surface */
  --paper-line: #e3dfcd;   /* hairlines and borders */

  --leaf:       #3e7d51;   /* brand accent — actions, focus */
  --leaf-600:   #326743;
  --leaf-soft:  #e4efe6;

  --pollen:     #b98a3d;   /* secondary accent — highlights, in-progress */
  --pollen-600: #a1762f;
  --pollen-soft:#f5ecd9;

  --sage:       #2f7a5b;   /* success */
  --sage-soft:  #e4f0e8;
  --amber:      #b06f15;   /* attention */
  --amber-soft: #f6ebd5;
  --rose:       #a3324a;   /* failure */
  --rose-soft:  #f6e3e6;
  --slate:      #3b5a72;   /* informational */
  --slate-soft: #e3ecf2;

  /* Type */
  --font-display: 'Iowan Old Style', 'Palatino Linotype', Palatino, Georgia, serif;
  --font-sans: system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
  --font-mono: ui-monospace, 'SF Mono', SFMono-Regular, Menlo, Consolas, 'Liberation Mono', monospace;

  --text-xs:   .6875rem;   /* labels, badges */
  --text-sm:   .8125rem;   /* secondary copy */
  --text-base: .9375rem;   /* body */
  --text-lg:   1.125rem;   /* card headings */
  --text-xl:   1.375rem;   /* section headings */
  --text-2xl:  1.75rem;    /* page subtitles */
  --text-3xl:  2.375rem;   /* page titles (display) */

  /* Shape + elevation */
  --radius:    .75rem;
  --radius-lg: 1rem;
  --shadow-card: 0 1px 2px rgba(20, 35, 27, .04), 0 8px 24px -12px rgba(20, 35, 27, .18);
  --shadow-lift: 0 12px 40px -16px rgba(20, 35, 27, .35);
}

/* ------------------------------------------------------------------ */
/* Base                                                               */
/* ------------------------------------------------------------------ */

* { box-sizing: border-box; }

html, body { height: 100%; margin: 0; }

body {
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: var(--text-base);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}

::selection { background: var(--pollen-soft); color: var(--ink); }

a { color: var(--leaf-600); text-decoration: none; }
a:hover { text-decoration: underline; }

h1, h2, h3 { font-family: var(--font-display); font-weight: 500; margin: 0; letter-spacing: -.01em; }
h1 { font-size: var(--text-3xl); }
h2 { font-size: var(--text-xl); }
h3 { font-size: var(--text-lg); }

.muted { color: var(--ink-500); }
.mono  { font-family: var(--font-mono); font-size: var(--text-xs); }

.eyebrow {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: .08em;
  color: var(--ink-500);
}

/* ------------------------------------------------------------------ */
/* Layout                                                             */
/* ------------------------------------------------------------------ */

.topbar {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: .875rem 2rem;
  background: var(--paper-card);
  border-bottom: 1px solid var(--paper-line);
}

.topbar .brand {
  display: flex;
  align-items: baseline;
  gap: .5rem;
  font-family: var(--font-display);
  font-size: var(--text-lg);
  color: var(--ink);
  text-decoration: none;
}

.topbar .brand .brand-mark { color: var(--leaf); }
.topbar .brand .brand-env  { font-family: var(--font-mono); font-size: var(--text-xs); color: var(--ink-500); }
.topbar nav { margin-left: auto; display: flex; align-items: center; gap: 1.25rem; font-size: var(--text-sm); }

.page { max-width: 72rem; margin: 0 auto; padding: 2.5rem 2rem 4rem; }

.page-head { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 1rem; }
.page-head p, .page-head .page-sub { margin: .375rem 0 0; color: var(--ink-500); }

/* A quiet, link-shaped action: inline forms whose button must read as text, not chrome. */
.inline-action { display: inline; }
.link-quiet {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  font-size: var(--text-sm);
  color: var(--ink-500);
  text-decoration: underline dotted;
  text-underline-offset: 3px;
  cursor: pointer;
}
.link-quiet:hover { color: var(--ink); }
.link-quiet:focus-visible { outline: 2px solid var(--leaf); outline-offset: 2px; }

.footer {
  padding: 1.5rem 2rem;
  border-top: 1px solid var(--paper-line);
  color: var(--ink-500);
  font-size: var(--text-xs);
  font-family: var(--font-mono);
}

/* ------------------------------------------------------------------ */
/* Cards                                                              */
/* ------------------------------------------------------------------ */

.card {
  background: var(--paper-card);
  border: 1px solid var(--paper-line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  padding: 1.5rem;
}

.card-title { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }

.card-grid { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr)); margin-top: 1.75rem; }

/* KPI stat cards (landing strip) */

.kpi { padding: 1.25rem; }
.kpi .kpi-label { font-family: var(--font-mono); font-size: var(--text-xs); text-transform: uppercase; letter-spacing: .08em; color: var(--ink-500); }
.kpi .kpi-value { font-family: var(--font-display); font-size: var(--text-3xl); line-height: 1.1; margin-top: .5rem; }
.kpi .kpi-value.ok      { color: var(--sage); }
.kpi .kpi-value.accent  { color: var(--pollen-600); }
.kpi .kpi-value.danger  { color: var(--rose); }

/* Empty state */

.empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .875rem;
  padding: 4rem 1.5rem;
  text-align: center;
}
.empty .empty-icon {
  display: grid;
  place-items: center;
  height: 3.5rem;
  width: 3.5rem;
  border-radius: 50%;
  background: var(--ink-faint);
  color: var(--leaf);
  font-size: 1.5rem;
}

/* ------------------------------------------------------------------ */
/* Badges                                                             */
/* ------------------------------------------------------------------ */

.badge {
  display: inline-flex;
  align-items: center;
  gap: .375rem;
  padding: .1875rem .625rem;
  border-radius: 999px;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: .02em;
  background: var(--ink-faint);
  color: var(--ink-500);
  white-space: nowrap;
}

.badge-ok      { background: var(--sage-soft);   color: var(--sage); }
.badge-busy    { background: var(--pollen-soft); color: var(--pollen-600); }
.badge-warn    { background: var(--amber-soft);  color: var(--amber); }
.badge-err     { background: var(--rose-soft);   color: var(--rose); }
.badge-info    { background: var(--slate-soft);  color: var(--slate); }
.badge-replay  { background: var(--slate-soft);  color: var(--slate); border: 1px dashed var(--slate); }

.badge .dot { height: .5rem; width: .5rem; border-radius: 50%; background: currentColor; }
.badge .dot.pulse { animation: pulse 1.4s ease-in-out infinite; }

@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: .25; } }

/* ------------------------------------------------------------------ */
/* Buttons                                                            */
/* ------------------------------------------------------------------ */

.btn {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  padding: .625rem 1.25rem;
  border: 1px solid transparent;
  border-radius: var(--radius);
  font: inherit;
  font-weight: 500;
  cursor: pointer;
  text-decoration: none;
  transition: background .15s ease, box-shadow .15s ease;
}
.btn:hover { text-decoration: none; }
.btn:focus-visible { outline: 2px solid var(--leaf); outline-offset: 2px; }
.btn[disabled], .btn.disabled { opacity: .45; cursor: not-allowed; pointer-events: none; }

.btn-primary { background: var(--ink); color: var(--paper-card); box-shadow: var(--shadow-card); }
.btn-primary:hover { background: var(--ink-700); }

.btn-accent { background: var(--leaf); color: #fff; box-shadow: var(--shadow-card); }
.btn-accent:hover { background: var(--leaf-600); }

.btn-quiet { background: transparent; color: var(--ink); border-color: var(--paper-line); }
.btn-quiet:hover { background: var(--ink-faint); }

.btn-danger { background: var(--rose); color: #fff; }
.btn-danger:hover { background: color-mix(in srgb, var(--rose) 85%, black); }

.btn-sm { padding: .375rem .875rem; font-size: var(--text-sm); }

/* Two-step inline confirm (delete, live replay): the first click arms, the second submits.
   The armed row ("Delete forever?" + Cancel) is far wider than the quiet trigger it replaces,
   so it is capped at the width of whatever holds it — a landing card's content box — and wraps
   inside that cap. Arming can never push Cancel outside its card, at any viewport width. The
   live-replay card opts out below: it leaves the flow (popover / absolute) and sizes itself. */
.two-step-confirm { position: relative; display: inline-flex; max-width: 100%; }
.two-step-confirm .armed-actions {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .5rem;
  max-width: 100%;
}
.two-step-confirm .btn[hidden],
.two-step-confirm .armed-actions[hidden] { display: none; }
.btn-delete { color: var(--rose); }

/* The replay trigger stays visible and in place while armed (its form carries
   data-keep-trigger), so the card drops beneath a button the presenter can still see instead of
   opening a blank gap in the header. .is-armed reads as pressed while the card is up. */
.confirm-replay .btn[data-arm].is-armed {
  background: var(--ink-700);
  box-shadow: inset 0 1px 3px rgb(0 0 0 / .35);
}

/* The live-replay warning is a sentence, so its armed state drops down as a small card.
   This absolute placement is the no-popover fallback only — see :popover-open below. */
.confirm-replay .armed-actions {
  position: absolute;
  top: calc(100% + .5rem);
  right: 0;
  z-index: 60;
  /* Out of flow, so it is sized by the viewport, never by the trigger it hangs off — the
     inline confirm's containing-box cap above would squeeze this card to the button's width. */
  max-width: none;
  width: min(24rem, calc(100vw - 3rem));
  flex-wrap: wrap;
  justify-content: flex-end;
  padding: .875rem 1rem;
  background: var(--paper-card);
  border: 1px solid var(--amber);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lift);
}

/* Preferred path: base.html's two-step script shows the armed card as a native popover in the
   browser's top layer — immune to ancestor stacking contexts, transforms, and overflow — and
   positions it under the trigger via inline left/top, clamped to the viewport. Neutralize the
   UA popover box (inset: 0 + auto margins) and the absolute fallback above. */
.confirm-replay .armed-actions:popover-open {
  position: fixed;
  inset: auto;
  margin: 0;
  display: inline-flex;
}
.confirm-replay .replay-warning {
  flex-basis: 100%;
  font-size: var(--text-sm);
  color: var(--ink-600);
  text-align: left;
}

/* ------------------------------------------------------------------ */
/* Stepper (per-document pipeline: Classified → Extracting → …)       */
/* ------------------------------------------------------------------ */

.stepper { display: flex; align-items: center; gap: .25rem; list-style: none; margin: 0; padding: 0; }
.stepper li { display: flex; align-items: center; flex: 1; gap: .25rem; }
.stepper li:last-child { flex: none; }

.step { display: flex; flex-direction: column; align-items: center; gap: .375rem; }

.step-dot {
  display: grid;
  place-items: center;
  height: 2rem;
  width: 2rem;
  border-radius: 50%;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  background: var(--ink-faint);
  color: var(--ink-500);
  transition: background .2s ease;
}

.step-label { font-size: var(--text-xs); font-weight: 500; color: var(--ink-500); }

.step.done    .step-dot { background: var(--ink);    color: var(--paper-card); }
.step.done    .step-label { color: var(--ink); }
.step.current .step-dot { background: var(--pollen); color: var(--ink); box-shadow: 0 0 0 4px var(--pollen-soft); }
.step.current .step-label { color: var(--ink); }
.step.failed  .step-dot { background: var(--rose);   color: #fff; }
.step.failed  .step-label { color: var(--rose); }
/* Amendment 4: the attempt failed but an earlier extraction is still in place and in use —
   amber, so the pipeline reads "something went wrong here", never "this document is dead". */
.step.warn    .step-dot { background: var(--amber);  color: #fff; }
.step.warn    .step-label { color: var(--amber); }

.step-line { height: 1px; flex: 1; background: var(--paper-line); margin-bottom: 1.25rem; }
.step-line.done { background: var(--ink); }

/* ------------------------------------------------------------------ */
/* Dropzone (upload)                                                  */
/* ------------------------------------------------------------------ */

.dropzone {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .75rem;
  padding: 3.5rem 1.5rem;
  border: 2px dashed var(--paper-line);
  border-radius: var(--radius-lg);
  background: var(--paper-card);
  color: var(--ink-500);
  text-align: center;
  cursor: pointer;
  transition: border-color .15s ease, background .15s ease;
}
.dropzone:hover, .dropzone.dragover { border-color: var(--leaf); background: var(--leaf-soft); }

.dropzone input[type='file'] { display: none; }

.dropzone .dropzone-icon {
  display: grid;
  place-items: center;
  height: 3.5rem;
  width: 3.5rem;
  border-radius: 50%;
  background: var(--ink);
  color: var(--paper-card);
  font-size: 1.375rem;
}

.dropzone .dropzone-title { font-family: var(--font-display); font-size: var(--text-xl); color: var(--ink); }

.upload-page { max-width: 44rem; margin: 0 auto; }

.upload-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  margin: 1.25rem 0 2rem;
}
.upload-actions .upload-rules { max-width: 26rem; line-height: 1.6; }

.step-card { padding: 1rem 1.25rem; }
.step-card p { margin: .375rem 0 0; font-size: var(--text-sm); }

/* ------------------------------------------------------------------ */
/* File cards (package view) and session cards (landing)              */
/* ------------------------------------------------------------------ */

.file-grid {
  display: grid;
  gap: 1rem;
  grid-template-columns: repeat(auto-fill, minmax(17rem, 1fr));
  margin-top: 1.75rem;
}

.file-card { display: flex; flex-direction: column; gap: .625rem; padding: 1.25rem; }
.file-card .file-name { font-family: var(--font-mono); font-size: var(--text-sm); color: var(--ink); word-break: break-all; }
.file-card .file-meta { display: flex; gap: .875rem; }
.file-card .file-badges { display: flex; flex-wrap: wrap; gap: .375rem; margin-top: auto; }

.section-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  margin-top: 2.25rem;
}

.session-grid { margin-top: 1rem; }

.session-card {
  display: flex;
  flex-direction: column;
  gap: .625rem;
  padding: 1.25rem;
  color: inherit;
  text-decoration: none;
  transition: box-shadow .15s ease, transform .15s ease;
}
.session-card:hover { text-decoration: none; box-shadow: var(--shadow-lift); transform: translateY(-2px); }
.session-card .session-name { overflow-wrap: anywhere; }
.session-card .session-name a { color: inherit; text-decoration: none; }
.session-card .session-name a:hover { text-decoration: underline; }
.session-card .session-meta { display: flex; flex-wrap: wrap; gap: .375rem .875rem; font-size: var(--text-sm); }
/* Wraps, so an armed delete confirm takes a line of its own instead of shouldering the row
   (Open, simulate) out through the card's edge. */
.session-card .session-actions { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; margin-top: auto; }

/* ------------------------------------------------------------------ */
/* Simulation (recorded playback)                                     */
/* ------------------------------------------------------------------ */

.page-actions { display: flex; flex-wrap: wrap; align-items: center; gap: .75rem; }

.simulation-watermark {
  position: fixed;
  right: 1.25rem;
  bottom: 1.25rem;
  z-index: 50;
  padding: .375rem 1rem;
  border: 1px dashed var(--slate);
  border-radius: 999px;
  background: var(--slate-soft);
  color: var(--slate);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: .35em;
  text-transform: uppercase;
  opacity: .8;
  pointer-events: none;
}

/* ------------------------------------------------------------------ */
/* Tables                                                             */
/* ------------------------------------------------------------------ */

.table { width: 100%; border-collapse: collapse; font-size: var(--text-sm); }
.table th {
  text-align: left;
  padding: .75rem 1.5rem;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: .08em;
  color: var(--ink-500);
  border-bottom: 1px solid var(--paper-line);
}
.table td { padding: 1rem 1.5rem; border-bottom: 1px solid var(--paper-line); }
.table tr:last-child td { border-bottom: 0; }
.table tbody tr:hover { background: var(--paper); }

/* ------------------------------------------------------------------ */
/* Forms                                                              */
/* ------------------------------------------------------------------ */

.field { display: flex; flex-direction: column; gap: .375rem; margin-bottom: 1.125rem; }
.field label { font-size: var(--text-sm); font-weight: 500; }
.field input {
  padding: .625rem .875rem;
  border: 1px solid var(--paper-line);
  border-radius: var(--radius);
  background: var(--paper-card);
  font: inherit;
  color: var(--ink);
}
.field input:focus { outline: 2px solid var(--leaf); outline-offset: 1px; border-color: var(--leaf); }

.form-errors {
  margin: 0 0 1.125rem;
  padding: .75rem 1rem;
  border-radius: var(--radius);
  background: var(--rose-soft);
  color: var(--rose);
  font-size: var(--text-sm);
}

/* Flash messages (django.contrib.messages), rendered by base.html. */

.messages { margin-bottom: 1.5rem; display: flex; flex-direction: column; gap: .625rem; }

.alert {
  padding: .75rem 1rem;
  border-radius: var(--radius);
  border: 1px solid transparent;
  font-size: var(--text-sm);
}
.alert-error,
.alert-warning { background: var(--rose-soft);  color: var(--rose);  border-color: color-mix(in srgb, var(--rose) 25%, transparent); }
.alert-success { background: var(--sage-soft);  color: var(--sage);  border-color: color-mix(in srgb, var(--sage) 25%, transparent); }
.alert-info,
.alert-debug   { background: var(--slate-soft); color: var(--slate); border-color: color-mix(in srgb, var(--slate) 25%, transparent); }

/* ------------------------------------------------------------------ */
/* Live pipeline (package view)                                       */
/* ------------------------------------------------------------------ */

.toggle { display: inline-flex; align-items: center; gap: .5rem; cursor: pointer; user-select: none; }
.toggle input { accent-color: var(--leaf); height: 1rem; width: 1rem; margin: 0; }

/* The fragment's live indicator: pulsing dot + "live · updated Ns ago". */
.pipeline-head { display: flex; justify-content: flex-end; margin-bottom: .875rem; }
.pipeline-live {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  padding: .3125rem .875rem;
  border: 1px solid var(--paper-line);
  border-radius: 999px;
  background: var(--paper-card);
  color: var(--sage);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
}
.pipeline-live .dot { height: .5rem; width: .5rem; border-radius: 50%; background: currentColor; }
.pipeline-live .dot.pulse { animation: pulse 1.4s ease-in-out infinite; }
.pipeline-live.degraded { color: var(--amber); }
.pipeline-live.settled { color: var(--ink-500); }

.reconnect {
  display: flex;
  align-items: center;
  gap: .5rem;
  margin-bottom: 1rem;
  padding: .625rem 1rem;
  border-radius: var(--radius);
  background: var(--amber-soft);
  color: var(--amber);
  font-size: var(--text-sm);
}
.reconnect .dot { height: .5rem; width: .5rem; border-radius: 50%; background: currentColor; }

/* Per-document extraction progress (Amendment 2): a slim bar whose width is
   morphed in by idiomorph on each poll — the transition animates the change. */
.doc-progress { display: flex; flex-direction: column; gap: .375rem; }
.progress-track {
  height: .375rem;
  border-radius: 999px;
  background: var(--ink-faint);
  overflow: hidden;
}
.progress-fill {
  height: 100%;
  border-radius: inherit;
  background: var(--pollen);
  transition: width .8s cubic-bezier(.2, .7, .2, 1);
}
/* Message, stall note, elapsed clock — separated by middots the markup never carries, so a
   caption whose parts drop out (no message yet, not stalled) never leaves a dangling one. */
.progress-caption { display: flex; flex-wrap: wrap; align-items: baseline; gap: .125rem .375rem; letter-spacing: .02em; }
.progress-caption > span + span::before { content: '\00b7'; margin-right: .375rem; }

/* Indeterminate activity bar for an extraction whose percentage has stopped moving: a
   frozen fill reads as broken, motion reads as work. Reduced motion is honored below. */
.activity-track {
  position: relative;
  height: .375rem;
  border-radius: 999px;
  background: var(--ink-faint);
  overflow: hidden;
}
.activity-sweep {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 40%;
  border-radius: inherit;
  background: var(--pollen);
  animation: sweep 1.2s ease-in-out infinite;
}

@keyframes sweep { from { left: -40%; } to { left: 100%; } }

.analyzer-grid {
  display: grid;
  gap: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
  margin-top: 1.75rem;
}
.analyzer-card { display: flex; flex-direction: column; gap: .75rem; }
.analyzer-card p { margin: 0; font-size: var(--text-sm); }
.analyzer-verdict { display: flex; align-items: center; gap: .625rem; font-size: var(--text-sm); }
/* The completed analyzer's headline number. These rule engines run in-process over extracted
   JSON, so the honest figure is milliseconds — set at heading scale, not buried in the counts. */
.analyzer-duration { display: flex; align-items: baseline; gap: .375rem; color: var(--ink-500); }
.analyzer-duration strong {
  font-family: var(--font-display);
  font-size: var(--text-xl);
  font-weight: 600;
  color: var(--sage);
}
.analyzer-counts { display: flex; flex-wrap: wrap; gap: .375rem .875rem; }
.analyzer-actions { display: flex; flex-wrap: wrap; gap: .5rem; margin-top: auto; }

.stats-strip {
  display: grid;
  gap: .75rem;
  grid-template-columns: repeat(auto-fit, minmax(7.5rem, 1fr));
  margin-top: 1.75rem;
}
.stat-tile { padding: .875rem 1rem; }
.stat-tile .stat-label {
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: .08em;
  color: var(--ink-500);
}
.stat-tile .stat-value { font-family: var(--font-display); font-size: var(--text-xl); margin-top: .25rem; }

/* Report panels (revealed on settle) and the full report page */

.report-panel { margin-top: 1rem; display: flex; flex-direction: column; gap: .875rem; }
.report-frame {
  width: 100%;
  height: 34rem;
  border: 1px solid var(--paper-line);
  border-radius: var(--radius);
  background: #fff;
}
.report-frame-tall { height: 52rem; }
.findings { display: flex; flex-wrap: wrap; gap: .375rem 1rem; }

/* ------------------------------------------------------------------ */
/* Motion + htmx affordances                                          */
/* ------------------------------------------------------------------ */

@keyframes rise { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
/* fill-mode must be `backwards`, never `both`/`forwards`: filling forwards keeps the transform
   animation applied after it finishes, and a transform-animated element is a permanent stacking
   context (and containing block for fixed descendants) — which trapped overlays such as the
   armed replay-confirm card beneath later `.rise` siblings. `backwards` still holds the hidden
   from-state through the stagger delay, then releases the element to its natural style (opacity
   1, no transform — identical to the to-keyframe, so nothing jumps at animation end). */
.rise { animation: rise .6s cubic-bezier(.2, .7, .2, 1) backwards; }
.rise-1 { animation-delay: .05s; }
.rise-2 { animation-delay: .12s; }
.rise-3 { animation-delay: .19s; }
.rise-4 { animation-delay: .26s; }

.htmx-swapping { opacity: 0; transition: opacity .15s; }

/* The pipeline fragment morphs in place — the generic swap fade would read as a flash. */
#pipeline.htmx-swapping { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .rise, .rise-1, .rise-2, .rise-3, .rise-4 { animation: none; }
  .badge .dot.pulse { animation: none; }
  .pipeline-live .dot.pulse { animation: none; }
  .progress-fill { transition: none; }
  .activity-sweep { animation: none; left: 0; width: 100%; opacity: .5; }
}
