/* =========================================================================
   HippaStrippa — Application styles
   Token-driven (see tokens.css). Organised in layers:
     1. Base / reset            5. Forms & fields
     2. Accessibility           6. Buttons & chips
     3. App shell (topbar)      7. Workspace components
     4. Layout (grids, panels)  8. Login · QA · Modal · Responsive
   Class names are kept stable because the page scripts reference them.
   ========================================================================= */

/* ---- 1. Base ------------------------------------------------------------- */
*,
*::before,
*::after {
  box-sizing: border-box;
}

/* The `hidden` attribute must always win over component `display` rules
   (flex/grid/inline-flex would otherwise silently override it). */
[hidden] {
  display: none !important;
}

html {
  font-size: 16px;
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  min-height: 100vh;
  font-family: var(--font-sans);
  line-height: 1.65;
  color: var(--text);
  background:
    radial-gradient(circle at top left, var(--halo-1), transparent 30%),
    radial-gradient(circle at top right, var(--halo-2), transparent 26%),
    var(--bg);
  -webkit-font-smoothing: antialiased;
}

/* Page ground is a clean flat sailcloth cream (dark: deep harbor). The old
   watercolour wash was removed — a precise instrument, not a painted texture.
   An imperceptible grain adds tactile reality without reading decorative. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  opacity: 0.5;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.035'/%3E%3C/svg%3E");
}

h1, h2, h3 {
  color: var(--ink);
  line-height: 1.2;
  letter-spacing: -0.01em;
}

h1 { font-size: clamp(1.6rem, 1.2rem + 1.4vw, 2.1rem); margin: 0 0 0.4rem; }
h2 { font-size: 1.2rem; margin: 0 0 1rem; }
h3 { font-size: 1rem; font-weight: 600; margin: 0; }

p { margin: 0 0 0.6rem; }

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

code, pre, .mono { font-family: var(--font-mono); }

/* ---- 2. Accessibility ---------------------------------------------------- */
:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

.skip-link {
  position: absolute;
  left: 0.75rem;
  top: -3rem;
  z-index: 50;
  background: var(--accent);
  color: var(--accent-ink);
  padding: 0.6rem 1rem;
  border-radius: var(--r-sm);
  font-weight: 600;
  transition: top 120ms ease;
}
.skip-link:focus {
  top: 0.75rem;
  text-decoration: none;
}

/* (Reduced-motion is handled by one authoritative block near the end of this
   file — see "Reduced motion — honour the user's preference everywhere".) */

/* ---- 3. App shell -------------------------------------------------------- */
.topbar {
  position: relative;
  z-index: 10;
  /* Floating nav — contents sit directly on the page background, no bar. */
  background: transparent;
  border: none;
}

.topbar-inner,
.page-shell {
  max-width: 1440px;
  margin: 0 auto;
  padding: 0.85rem 1.5rem;
}

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

.brand {
  display: inline-flex;
  align-items: baseline;   /* boat's bottom sits on the wordmark baseline */
  gap: 0.6rem;
  /* Wordmark in Spectral (our display serif), not the UI sans — distinctive,
     editorial, and its lighter weight balances the fine-lined sailboat mark. */
  font-family: var(--font-display);
  font-size: 1.55rem;
  font-weight: 600;
  letter-spacing: -0.005em;
  color: var(--ink);
}
.brand:hover { text-decoration: none; color: var(--ink); }

/* Free-standing sailboat mark — the plain boat PNG used as a mask, painted with
   --ink so it's Harbor Navy in light and Sailcloth Cream in dark (matches the
   wordmark). No disc, no ring. */
.brand-mark {
  flex-shrink: 0;
  display: block;
  width: 27px;
  height: 31px;
  background-color: var(--ink);
  -webkit-mask: url('/img/ch_plain_logo.png') no-repeat center / contain;
          mask: url('/img/ch_plain_logo.png') no-repeat center / contain;
}

.brand-suffix {
  font-weight: 400;
  color: var(--muted);
}

.topbar-actions {
  display: flex;
  align-items: center;
  gap: 0.85rem;
}

.nav-links {
  display: flex;
  gap: 1.1rem;
  align-items: center;
}

.nav-links a,
.link-button {
  font-weight: 600;
  color: var(--text);
  text-decoration: none;
}
.nav-links a:hover { color: var(--accent); text-decoration: none; }

.link-button {
  border: none;
  background: transparent;
  cursor: pointer;
  padding: 0;
  font: inherit;
}
.link-button:hover { color: var(--accent); }

.tenant-label {
  padding: 0.32rem 0.7rem;
  border-radius: var(--r-pill);
  background: var(--accent-soft);
  color: var(--accent-soft-ink);
  font-size: 0.82rem;
  font-weight: 700;
  font-family: var(--font-mono);
}

/* Theme toggle */
.theme-toggle {
  display: inline-grid;
  place-items: center;
  width: 2.4rem;
  height: 2.4rem;
  border-radius: var(--r-pill);
  border: 1px solid var(--line-strong);
  background: var(--panel-2);
  color: var(--text);
  cursor: pointer;
  transition: border-color 120ms ease, color 120ms ease, background 120ms ease;
}
.theme-toggle:hover {
  color: var(--accent);
  border-color: var(--accent);
}
.theme-toggle .icon-sun { display: none; }
.theme-toggle .icon-moon { display: block; }
:root[data-theme="dark"] .theme-toggle .icon-sun { display: block; }
:root[data-theme="dark"] .theme-toggle .icon-moon { display: none; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .theme-toggle .icon-sun { display: block; }
  :root:not([data-theme="light"]) .theme-toggle .icon-moon { display: none; }
}

/* ---- 4. Layout ----------------------------------------------------------- */
.page-shell { padding-top: 1.75rem; padding-bottom: 3rem; }

.workspace-header {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  align-items: flex-start;
  margin-bottom: 1.5rem;
}
.workspace-header p { color: var(--muted); margin: 0; }

.workspace-grid {
  display: grid;
  gap: 1.1rem;
  /* 3-col Index layout removed — now uses .step-flow; .qa-grid overrides for QA page */
}
.qa-grid { grid-template-columns: 1fr 1fr; }

.panel {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: 2rem 2.25rem;
  box-shadow: var(--shadow-1);
}

/* ---- 5. Forms & fields --------------------------------------------------- */
.field {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  margin-bottom: 1.1rem;
}
.field label,
.panel > p { color: var(--muted); }
.field > label { font-size: 0.9rem; font-weight: 600; color: var(--text); }

/* Borderless fields — the fill (--panel-2) on the card (--panel) does the
   separation, not a stroke. A transparent 1.5px border reserves space so the
   focus ring can appear without shifting layout. */
.field input,
.field textarea,
.field select,
.entity-style {
  border: 1.5px solid transparent;
  border-radius: var(--r);
  padding: 0.85rem 1rem;
  font: inherit;
  color: var(--ink);
  background: var(--panel-2);
  box-shadow: none;
  transition: border-color var(--dur-1) var(--ease), box-shadow var(--dur-1) var(--ease);
}
.field input::placeholder,
.field textarea::placeholder { color: var(--muted); opacity: 0.85; }

.field input:focus,
.field select:focus,
.entity-style:focus {
  outline: none;
  border-color: var(--accent);   /* border only — no ring (it clipped at the card edge) */
}

/* ---- Select control: a real design-system component, not the OS default ---- */
/* Dropdowns sit on the near-white --panel (not the warm --panel-2 of text inputs
   and not the surrounding disclosure fill) so a *chooser* reads a step brighter
   than a *type-in* field — it stands out by elevation, not a hue or a stroke. The
   selected value never wraps: nowrap + ellipsis keeps every option on one line. */
.field select,
.entity-style {
  appearance: none;
  -webkit-appearance: none;
  cursor: pointer;
  padding-right: 2.6rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  color: var(--ink);
  background-color: var(--panel);
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%235c676d' stroke-width='2.25' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>");
  background-repeat: no-repeat;
  background-position: right 1rem center;
  background-size: 1.05rem;
}
.field select:hover,
.entity-style:hover {
  background-color: color-mix(in srgb, var(--panel) 90%, var(--accent));
}
/* Dark: off-white has no equivalent, so the "brighter than the field" cue becomes
   "raised surface" — the lifted --panel-3 reads a step above the dark disclosure. */
:root[data-theme="dark"] .field select,
:root[data-theme="dark"] .entity-style {
  background-color: var(--panel-3);
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%238a9599' stroke-width='2.25' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>");
}
:root[data-theme="dark"] .field select:hover,
:root[data-theme="dark"] .entity-style:hover {
  background-color: color-mix(in srgb, var(--panel-3) 88%, var(--accent));
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .field select,
  :root:not([data-theme="light"]) .entity-style {
    background-color: var(--panel-3);
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%238a9599' stroke-width='2.25' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>");
  }
  :root:not([data-theme="light"]) .field select:hover,
  :root:not([data-theme="light"]) .entity-style:hover {
    background-color: color-mix(in srgb, var(--panel-3) 88%, var(--accent));
  }
}

/* Open dropdown popup — styled to the design system via the customizable-select
   API. Progressive enhancement: browsers without `base-select` (Firefox/Safari,
   older Chromium) keep the styled closed select above + the native popup. The
   double-arrow the old note warned about is avoided by dropping our background
   SVG chevron here and using the built-in ::picker-icon instead. */
@supports (appearance: base-select) {
  .field select,
  .entity-style {
    appearance: base-select;
    background-image: none;   /* use ::picker-icon, not the SVG chevron (no double arrow) */
    padding-right: 2.2rem;
  }
  .field select::picker-icon,
  .entity-style::picker-icon {
    color: var(--muted);
    transition: transform var(--dur-2) var(--ease);
  }
  .field select:open::picker-icon,
  .entity-style:open::picker-icon { transform: rotate(180deg); }

  .field select::picker(select),
  .entity-style::picker(select) {
    appearance: base-select;
    margin-top: 0.35rem;
    padding: 0.3rem;
    background: var(--panel);
    border: 1px solid var(--line);
    border-radius: var(--r);
    box-shadow: var(--shadow-2);
  }
  .field option,
  .entity-style option {
    padding: 0.5rem 0.7rem;
    border-radius: var(--r-sm);
    color: var(--ink);
    background: transparent;
  }
  .field option:hover,
  .entity-style option:hover,
  .field option:focus,
  .entity-style option:focus {
    background: var(--panel-3);
  }
  .field option:checked,
  .entity-style option:checked {
    background: var(--accent-soft);
    color: var(--accent-soft-ink);
    font-weight: 600;
  }
  .field option::checkmark,
  .entity-style option::checkmark { color: var(--accent); }
}

.field textarea {
  resize: vertical;
  min-height: 9rem;
  padding: 1rem 1.1rem;
  font-family: var(--font-mono);
  font-size: 0.92rem;
  line-height: 1.55;
}

.field textarea:focus {
  outline: none;
  border-color: var(--accent);
}

.inline-field {
  flex-direction: row;
  align-items: center;
  gap: 0.6rem;
}
.inline-field input { accent-color: var(--accent); width: 1.05rem; height: 1.05rem; }

.options-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0.85rem;
}

/* ---- 6. Buttons & chips -------------------------------------------------- */
.primary-button,
.secondary-button {
  border-radius: var(--r);
  border: 1.5px solid transparent;
  font: inherit;
  font-size: var(--fs-sm);
  cursor: pointer;
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  transition: background var(--dur-1) var(--ease), color var(--dur-1) var(--ease),
              border-color var(--dur-1) var(--ease), transform var(--dur-1) var(--ease),
              box-shadow var(--dur-1) var(--ease);
}
.primary-button:hover,
.secondary-button:hover { text-decoration: none; }

/* Primary CTA — flat fill, no soft glow. Press = a quick spring scale-down
   (tactile), not a Material rise. */
.primary-button {
  width: 100%;
  background: var(--cta-bg);
  color: var(--cta-ink);
  font-weight: 600;
  letter-spacing: -0.005em;
  padding: 0.72rem 1.6rem;
}
.primary-button:hover { background: var(--cta-hover); }
/* Press feedback only — no resting transform (keeps the label crisp; the button
   animates only on press and on the copy success-state transform). */
.primary-button:active { background: var(--cta-press); transform: scale(0.975); }

/* Secondary — hairline, fills with a subtle tint on hover (not a border swap). */
/* Filled ghost — a subtle fill, no resting border (borderless system). */
.secondary-button {
  background: var(--panel-2);
  color: var(--text);
  font-weight: 600;
  padding: 0.72rem 1.4rem;
  border-color: transparent;
}
.secondary-button:hover {
  background: var(--panel-3);
  color: var(--ink);
}
.secondary-button:active { transform: scale(0.985); }

button:disabled,
.disabled-link {
  opacity: 0.42;
  pointer-events: none;
  box-shadow: none;
  transform: none;
}

.button-row { display: flex; gap: 0.6rem; flex-wrap: wrap; }

/* ---- 7. Workspace components --------------------------------------------- */
.callout {
  padding: 0.8rem 1rem;
  border-radius: var(--r-sm);
  background: var(--panel-2);
  border: none;
  color: var(--text);
  margin-bottom: 1rem;
  font-size: 0.92rem;
}
/* Failure state — a clear, tinted callout (shown when a job fails). */
.callout.callout-error {
  background: color-mix(in srgb, var(--danger-ink) 12%, var(--panel));
  color: var(--danger-ink);
  font-weight: 500;
}

/* Processing notes — a caution callout (OCR/PDF warnings) shown at the top of the
   Step-3 result. Warm rope/ochre, distinct from success + error. Hidden when there
   are no warnings (the common text-input case). */
.warning-callout {
  display: flex;
  align-items: flex-start;
  gap: 0.55rem;
  padding: 0.7rem 0.95rem;
  border-radius: var(--r);
  background: var(--warn-bg);
  border: 1px solid var(--warn-line);
  color: var(--warn-ink);
  font-size: var(--fs-sm);
  line-height: 1.45;
}
.warning-callout .icon-warn { flex: 0 0 auto; display: inline-flex; margin-top: 0.1rem; }
.warning-callout-body { min-width: 0; }
.warning-callout strong { display: block; font-family: var(--font-sans); font-weight: 700; margin-bottom: 0.15rem; }
.warning-callout ul { margin: 0; padding-left: 1.05rem; }
.warning-callout li { margin: 0.08rem 0; }

/* While a job is in flight the visual progress lives in .proc-panel below; the
   callout is kept only as the aria-live announcement for screen readers, so we
   collapse it visually (and kill the old throbber sweep). */
.callout.is-running {
  position: absolute !important;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}
/* Exposure / risk card */
.exposure-card {
  display: flex;
  flex-direction: column;
  border-radius: var(--r);
  padding: 1.35rem 1.6rem;
  margin-bottom: 1.1rem;
  border: none;               /* result banner: its fill (tinted) does the separation */
  background: var(--panel-2);
  /* smooth colour transition between states */
  transition: background-color var(--dur-2) var(--ease), border-color var(--dur-2) var(--ease);
}
.exposure-mainline {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.75rem;
}
.exposure-mainline { justify-content: flex-start; margin-top: 0.4rem; }
.exposure-score-group { display: inline-flex; align-items: baseline; gap: 0.4rem; }

/* eyebrow — shared utility for all label/eyebrow treatments. Editorial: clean
   sans small-caps, NOT monospace. Mono is reserved for real machine content
   (the output box, [NAME] token chips, pasted document text). */
.mono-eyebrow {
  font-family: var(--font-sans);
  font-size: var(--fs-eyebrow);
  font-weight: 600;
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  color: var(--muted);
}

.exposure-level { font-weight: 700; color: var(--ink); }
.exposure-number {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(2.6rem, 1.8rem + 3vw, 4rem);
  line-height: 1;
  letter-spacing: -0.02em;
  color: var(--ink);
}
.exposure-scale { color: var(--muted); font-family: var(--font-sans); font-weight: 500; font-size: 1rem; letter-spacing: 0.01em; }
.exposure-message { margin: 0.7rem 0 0; color: var(--text); font-weight: 600; }
.exposure-support { margin-top: 0.4rem; color: var(--muted); font-size: 0.9rem; }

/* Step 7 — reassurance line shown after de-identification */
.exposure-aftermath {
  margin-top: 0.5rem;
  font-size: 0.85rem;
  font-weight: 500;
  color: var(--accent-soft-ink);
}

.exposure-idle {
  background: var(--panel-2);
}
/* In idle state the numeral reverts to Inter — Spectral only for a real score */
.exposure-idle .exposure-number {
  font-family: var(--font-sans);
  font-weight: 400;
  font-size: 2rem;
  color: var(--muted);
}
/* Traffic-light result banner (fill only, no stroke). Green = original already
   clean (no action). Ochre caution = identifying data found & removed (review the
   output). Red is reserved for the failure state (shown via the error block).
   Text colours are scoped under step 3 to outrank the neutral defaults there. */
.exposure-clean { background: var(--success-bg); }
.exposure-caution { background: var(--warn-bg); }
.step[data-step="3"] .exposure-clean .exposure-level { color: var(--success); }
.step[data-step="3"] .exposure-caution .exposure-level,
.step[data-step="3"] .exposure-caution .exposure-scale,
.step[data-step="3"] .exposure-caution .exposure-number { color: var(--warn-ink); }

/* Output box — developer-vault code surface */
.output-box {
  flex: 1;
  min-height: 120px;
  max-height: 60vh;
  white-space: pre-wrap;
  word-break: break-word;
  background: var(--panel-2);
  border: 1.5px solid transparent;   /* borderless; fill separates. focus ring below */
  border-radius: var(--r);
  padding: 1.1rem 1.25rem;
  overflow: auto;
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
  line-height: 1.6;
  color: var(--ink);
}
.output-box.has-content { min-height: 160px; }
.small-box { min-height: 200px; }

/* Policy / entity coverage */
.policy-card {
  margin: 1.25rem 0;
  border: none;
  border-radius: var(--r);
  padding: 1.25rem 1.5rem;
  background: var(--panel-2);
}
.policy-card-header {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 1rem;
  margin-bottom: 0.7rem;
}
.policy-card-header h3 {
  margin: 0 0 var(--space-1);
  font-family: var(--font-sans);
  font-size: var(--fs-eyebrow);
  font-weight: 600;
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  color: var(--muted);
}
.policy-card-header p,
.policy-card-note { margin: 0; color: var(--muted); font-size: 0.9rem; }
.policy-card-note { margin-bottom: 0.9rem; }
.policy-card-disabled { opacity: 0.7; }

.master-toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  padding: 0.6rem 0.85rem;
  border-radius: var(--r-sm);
  background: var(--accent-soft);
  border: none;
  color: var(--accent-soft-ink);
  font-weight: 700;
  font-size: 0.9rem;
  white-space: nowrap;
}
.master-toggle input { accent-color: var(--accent); }

.entity-matrix { display: grid; gap: 0.7rem; }
.entity-disclosure,
.upload-preview-card {
  margin-top: 0.85rem;
  border: none;
  border-radius: var(--r);
  background: var(--panel-2);   /* fill distinguishes it from the card, no stroke */
  overflow: hidden;
}
.upload-preview-card { margin: 0 0 1.1rem; }

.entity-disclosure summary,
.upload-preview-card summary {
  list-style: none;
  cursor: pointer;
  padding: 0.9rem 1rem;
  font-weight: 700;
  color: var(--accent-soft-ink);
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.entity-disclosure summary::-webkit-details-marker,
.upload-preview-card summary::-webkit-details-marker { display: none; }
.entity-disclosure summary::after,
.upload-preview-card summary::after {
  content: "+";
  font-size: 1.2rem;
  line-height: 1;
  color: var(--accent);
}
.entity-disclosure[open] summary,
.upload-preview-card[open] summary {
  border-bottom: 1px solid var(--line);
  background: var(--panel-2);
}
.entity-disclosure[open] summary::after,
.upload-preview-card[open] summary::after { content: "\2212"; }
.entity-disclosure-body { padding: 0.9rem; }

/* Advanced options disclosure (Step 1) */
.advanced-disclosure {
  margin: 0.25rem 0 1.1rem;
  border: none;
  border-radius: var(--r);
  background: var(--panel-2);   /* fill separates from the card, no stroke */
  overflow: hidden;
}

.advanced-disclosure > summary {
  list-style: none;
  cursor: pointer;
  padding: 0.9rem 1rem;
  font-family: var(--font-sans);
  font-size: var(--fs-eyebrow);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.advanced-disclosure > summary::-webkit-details-marker { display: none; }
.advanced-disclosure > summary::after {
  content: "+";
  font-size: 1.2rem;
  line-height: 1;
  color: var(--accent);
}
.advanced-disclosure[open] > summary {
  border-bottom: 1px solid var(--line);
  background: var(--panel-2);
}
.advanced-disclosure[open] > summary::after { content: "\2212"; }

.advanced-disclosure-body {
  padding: 1.25rem 1.5rem;
}
.advanced-disclosure-body > .policy-card { margin: 0.5rem 0 0.25rem; }

.entity-row {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.8rem;
  align-items: start;
  border: none;
  border-radius: var(--r);
  padding: 0.85rem 0.9rem;
  background: var(--panel);
  transition: opacity 120ms ease;
}
/* No hover state — these are configuration rows, not clickable cards. */
.entity-row.is-disabled { opacity: 0.6; background: var(--panel-2); }
.entity-row.is-locked { background: var(--panel-2); }

.entity-row-main {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
}
.entity-choice {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: start;
  gap: 0.75rem;
  cursor: pointer;
  flex: 1;
  min-width: 0;
}
.entity-choice input { margin-top: 0.2rem; accent-color: var(--accent); }
.entity-copy { display: grid; gap: 0.2rem; min-width: 0; }
.entity-copy strong { font-size: 0.96rem; color: var(--ink); }
.entity-copy span { color: var(--muted); font-size: 0.86rem; line-height: 1.35; }

.entity-row-actions { display: grid; gap: 0.35rem; padding-left: 2rem; max-width: 240px; }
.entity-row-actions label {
  font-size: 0.74rem;
  font-weight: 700;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}
.entity-style { width: 100%; min-width: 0; }

.entity-badge {
  white-space: nowrap;
  border-radius: var(--r-pill);
  padding: 0.28rem 0.6rem;
  background: var(--panel-3);
  color: var(--muted);
  font-size: 0.76rem;
  font-weight: 700;
}

/* Upload preview surfaces */
.upload-preview-meta {
  display: flex;
  justify-content: space-between;
  gap: 0.75rem;
  align-items: center;
  padding: 0.85rem 1rem 0;
  color: var(--muted);
  font-size: 0.9rem;
}
.upload-preview-meta-copy { display: grid; gap: 0.15rem; }
.upload-preview-meta strong { color: var(--ink); font-size: 0.94rem; }
.preview-launch-button { flex-shrink: 0; }
.upload-preview-host { padding: 0.85rem 1rem 1rem; }

.upload-preview-frame,
.upload-preview-image,
.upload-preview-text {
  width: 100%;
  border: 1px solid var(--line-strong);
  border-radius: var(--r);
  background: var(--panel);
}
.upload-preview-frame { min-height: 420px; }
.upload-preview-image { display: block; max-height: 520px; object-fit: contain; }
.upload-preview-text {
  min-height: 240px;
  max-height: 420px;
  margin: 0;
  padding: 1rem;
  overflow: auto;
  white-space: pre-wrap;
  font-family: var(--font-mono);
  font-size: 0.88rem;
  color: var(--ink);
}
.upload-preview-empty {
  border: none;
  border-radius: var(--r);
  padding: 1rem;
  color: var(--muted);
  background: var(--panel-2);
}

/* ---- 8a. Preview modal --------------------------------------------------- */
.preview-modal {
  width: min(92vw, 1200px);
  max-width: 1200px;
  border: none;
  padding: 0;
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-pop);
  background: transparent;
}
.preview-modal::backdrop {
  background: rgba(11, 22, 30, 0.58);
  backdrop-filter: blur(4px);
}
.preview-modal-shell {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: 1.15rem;
}
.preview-modal-header {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 1rem;
  margin-bottom: 0.9rem;
}
.preview-modal-header h2 { margin: 0 0 0.25rem; font-size: 1.15rem; }
.preview-modal-header p { margin: 0; color: var(--muted); }
.preview-modal-host .upload-preview-frame { min-height: 78vh; }
.preview-modal-host .upload-preview-image { max-height: 78vh; }
.preview-modal-host .upload-preview-text { min-height: 68vh; max-height: 78vh; }

/* ---- 8b. Login (split panel) -------------------------------------------- */
.login-shell {
  /* fill the space below the topbar and centre the split — the offset covers the
     topbar + this shell's own vertical padding so the page never scrolls. */
  min-height: calc(100dvh - 150px);
  display: grid;
  place-items: center;
  padding: 2rem 1rem;
}
.login-split {
  width: min(1120px, 94vw);
  display: grid;
  grid-template-columns: 1fr 1fr;
  background: var(--panel);
  border-radius: var(--r-lg);
  overflow: hidden;
  box-shadow: var(--shadow-2);
}
@media (min-width: 1650px) { .login-split { width: 1280px; } }
:root[data-theme="dark"] .login-split { border: 1px solid var(--line); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .login-split { border: 1px solid var(--line); }
}

.login-form-col {
  display: flex;
  flex-direction: column;
  padding: 3.5rem 3.25rem;
}
.login-brand {
  display: inline-flex;
  align-items: baseline;   /* boat's bottom sits on the wordmark baseline */
  gap: 0.6rem;
  margin-bottom: 1.75rem;
}
.login-brand .wordmark { font-family: var(--font-display); font-size: 1.7rem; font-weight: 600; color: var(--ink); }
.login-form-col h1 {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 2rem;
  letter-spacing: -0.02em;
  margin: 0 0 0.5rem;
}
.login-tagline { color: var(--muted); margin-bottom: 1.4rem; line-height: 1.5; }
.login-form-col form { margin-top: 0.25rem; }

.trust-row {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  margin: auto 0 0;   /* pin trust row to the bottom of the column */
  padding-top: 1.4rem;
}
.trust-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--text);
}
.trust-chip-icon {
  flex: 0 0 auto;
  width: 1.55rem;
  height: 1.55rem;
  display: inline-grid;
  place-items: center;
  border-radius: 50%;
  background: var(--accent-soft);
  color: var(--accent);
  font-size: 0.9rem;
}

/* Right column — solid Harbor Navy brand panel */
.login-brand-col {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 2rem;
  padding: 3.5rem 3.25rem;
  /* Fixed brand colours (Harbor Navy + Sailcloth Cream), NOT --accent/--bg: this
     panel stays navy in both light and dark themes, like the logo disc. */
  background: #1e3b45;
  color: #f1ebe1;
  overflow: hidden;
}
/* subtle nautical wave pattern */
.login-brand-col::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0.07;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='54' height='18'%3E%3Cpath d='M0 13 q 13.5 -11 27 0 t 27 0' fill='none' stroke='%23ffffff' stroke-width='1.1'/%3E%3C/svg%3E");
  background-size: 54px 18px;
}
.peek-stack {
  position: relative;
  min-height: 290px;
  margin-top: 0.5rem;
  z-index: 1;
}
.peek-card {
  position: absolute;
  width: 80%;
  border-radius: var(--r);
  padding: 1.15rem 1.35rem;
  font-family: var(--font-mono);
  font-size: 0.92rem;
  line-height: 1.95;
}
.peek-line { display: flex; gap: 0.5rem; white-space: nowrap; }
.peek-k {
  display: inline-block;
  min-width: 4.6em;
  color: rgba(241, 235, 225, 0.42);
}
/* Raw note — recedes behind, faint (a document waiting to be cleaned). */
.peek-raw {
  top: 0;
  left: 0;
  transform: rotate(-3deg);
  background: rgba(6, 15, 19, 0.45);
  color: rgba(241, 235, 225, 0.3);
}
/* Cleaned note — crisp and OPAQUE in front, so it cleanly covers the raw card
   (no text bleed-through) — the "resolved into tokens" result. */
.peek-clean {
  top: 52px;
  left: 18%;
  background: #26474f;
  color: #f5f0e8;
  box-shadow: 0 26px 50px rgba(0, 0, 0, 0.4);
  animation: peek-in 620ms var(--ease-out) both;
}
@keyframes peek-in {
  from { opacity: 0; transform: translateY(16px); }
  to   { opacity: 1; transform: translateY(0); }
}
.peek-tok {
  background: rgba(127, 163, 176, 0.32);
  color: #dcecf1;
  border-radius: 4px;
  padding: 0.04em 0.4em;
  font-weight: 600;
}
.brand-panel-foot {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  gap: 1rem;
}
.brand-panel-seal {
  flex: 0 0 auto;
  width: 3.4rem;
  height: 3.4rem;
  display: block;
  color: rgba(241, 235, 225, 0.82);
}
.brand-panel-seal .trust-seal { display: block; width: 100%; height: 100%; }
.brand-panel-tagline {
  margin: 0;
  font-family: var(--font-display);
  font-size: 1.2rem;
  line-height: 1.45;
  color: #f1ebe1;   /* fixed cream on the always-navy panel */
}

@media (max-width: 820px) {
  .login-split { grid-template-columns: 1fr; width: min(440px, 94vw); }
  .login-brand-col { display: none; }
  .login-form-col { padding: 2.25rem 1.9rem; }
}

.error-banner {
  background: var(--danger-bg);
  color: var(--danger-ink);
  border: 1px solid var(--danger-line);
  border-radius: var(--r-sm);
  padding: 0.8rem 1rem;
  margin-bottom: 1rem;
  font-weight: 600;
}

/* ---- 8c. Responsive ------------------------------------------------------ */
@media (max-width: 1100px) {
  .workspace-grid,
  .qa-grid { grid-template-columns: 1fr; }

  /* .workspace-grid.has-results order rules removed — Index now uses .step-flow */
}

@media (max-width: 760px) {
  .topbar-inner,
  .page-shell { padding-left: 1rem; padding-right: 1rem; }

  .options-grid,
  .entity-row { grid-template-columns: 1fr; }

  .policy-card-header,
  .entity-row-main,
  .panel-title,
  .workspace-header,
  .upload-preview-meta,
  .preview-modal-header {
    flex-direction: column;
    align-items: stretch;
  }

  .button-row { width: 100%; }
  .button-row > * { flex: 1; }

  .entity-row-actions { padding-left: 0; max-width: none; }
  .preview-modal { width: calc(100vw - 1rem); }
}

/* =========================================================================
   9. Step Flow — guided Add · Clean · Copy interface
   Mobile-first; token-driven; reduced-motion handled globally in section 2.
   ========================================================================= */

/* ---- Flow container ------------------------------------------------------ */
.step-flow {
  margin: 0 auto;
  /* Vertically centre the whole workspace (stepper + card + meta) in the space
     below the topbar, so big screens don't leave a sea of dead space below. */
  min-height: calc(100dvh - 150px);
  display: flex;
  flex-direction: column;
  justify-content: center;
}

/* Quiet session context, relocated below the card (replaces the old top row). */
.flow-meta {
  margin: 1.15rem 0 0;
  text-align: center;
  font-size: var(--fs-xs);
  color: var(--muted);
  letter-spacing: 0.01em;
}

/* ---- Stepper (desktop: horizontal circles + connectors) ------------------ */
.stepper { margin-bottom: 2rem; }

.stepper-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  align-items: flex-start;
}

.stepper-item {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.45rem;
  text-align: center;
  position: relative;
}

/* Connector line drawn after each item except the last */
.stepper-item:not(:last-child)::after {
  content: "";
  position: absolute;
  top: 0.8rem;                     /* vertically centred on the 1.6rem bubble */
  left: calc(50% + 1rem);         /* start after this bubble's right edge */
  width: calc(100% - 2rem);       /* span to next bubble's left edge */
  height: 1.5px;
  background: var(--line-strong);
  transition: background var(--dur-3) var(--ease);
}
.stepper-item.is-complete:not(:last-child)::after { background: var(--accent); }

/* Bubble */
.stepper-bubble {
  position: relative;
  z-index: 1;
  display: inline-grid;
  place-items: center;
  width: 1.6rem;
  height: 1.6rem;
  border-radius: var(--r-pill);
  border: none;
  background: var(--panel-3);   /* inactive node = subtle fill, no stroke */
  color: var(--muted);
  font-size: 0.76rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  transition: background var(--dur-2) var(--ease), border-color var(--dur-2) var(--ease),
              color var(--dur-2) var(--ease), transform var(--dur-2) var(--ease-out);
  flex-shrink: 0;
}
.stepper-item.is-active .stepper-bubble,
.stepper-item.is-complete .stepper-bubble {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
}
.stepper-item.is-active .stepper-bubble { transform: scale(1.12); }

/* Show check icon for completed steps, number for others */
.stepper-num  { display: block; }
.stepper-check { display: none; font-size: 0.9rem; line-height: 1; }
.stepper-item.is-complete .stepper-num   { display: none; }
.stepper-item.is-complete .stepper-check { display: block; }

/* Step label — uppercase eyebrow for a precise, engineered read */
.stepper-label {
  font-size: var(--fs-eyebrow);
  font-weight: 600;
  text-transform: uppercase;
  color: var(--muted);
  letter-spacing: var(--tracking-eyebrow);
  transition: color var(--dur-2) var(--ease);
}
.stepper-item.is-active .stepper-label,
.stepper-item.is-complete .stepper-label { color: var(--accent-soft-ink); }

/* Completed steps are navigable — click a done step to slide back to it. */
.stepper-item.is-complete { cursor: pointer; }
.stepper-item.is-complete:hover .stepper-bubble {
  background: var(--accent-hover);
  border-color: var(--accent-hover);
}
.stepper-item.is-complete:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 4px;
  border-radius: var(--r);
}

/* Mobile compact strip */
.stepper-mobile { display: none; }

/* ---- Step sections ------------------------------------------------------- */
/* The sheet-body slide-over owns the step transition; the step section must NOT
   run its own fade/translate or the two animations fight each other. */
.step[hidden] { display: none; }

.step-heading {
  font-family: var(--font-display);
  font-weight: 400;   /* Spectral 400 reads crisp at display; 300 is too delicate */
  font-size: var(--fs-display);
  line-height: var(--lh-tight);
  letter-spacing: -0.025em;
  color: var(--ink);
  margin: 0 0 var(--space-3);
  flex: 0 0 auto;
  outline: none; /* receives programmatic focus; no visible ring needed here */
}
/* Editorial italic emphasis on one key word (Spectral italic). */
.step-heading em {
  font-style: italic;
  font-weight: inherit;
}

.step-subhead {
  margin: 0 0 var(--space-5);
  color: var(--muted);
  font-size: var(--fs-sm);
  flex: 0 0 auto;
}

.step-trust {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.4rem 0.85rem;
  border-radius: var(--r-pill);
  background: var(--mint-soft);
  color: var(--mint-ink);
  border: none;
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: var(--fs-sm);
  letter-spacing: 0.01em;
}

/* ---- Inline icon sizing helpers ------------------------------------------ */
.icon-trust       { font-size: 1rem;   display: inline-flex; align-items: center; }
.icon-card-label  { font-size: 1.1rem; display: inline-flex; align-items: center; }
.icon-btn         { font-size: 1rem;   display: inline-flex; align-items: center; }
.icon-dropzone    { font-size: 2.25rem; display: block; color: var(--muted); margin-bottom: 0.2rem; }

/* ---- Input cards (Step 1) ------------------------------------------------ */
.input-cards {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1rem;
  margin-bottom: 1.25rem;
}

.input-card {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.input-card-label {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-family: var(--font-sans);
  font-size: var(--fs-eyebrow);
  font-weight: 600;
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  color: var(--muted);
}

/* Textarea inside the paste card — fills the card height */
.paste-input-card textarea {
  flex: 1;
  border: 1.5px solid transparent;
  border-radius: var(--r);
  padding: 1rem 1.1rem;
  font: inherit;
  font-family: var(--font-mono);
  font-size: 0.92rem;
  line-height: 1.55;
  color: var(--ink);
  background: var(--panel-2);
  box-shadow: none;
  resize: vertical;
  min-height: 220px;
  transition: border-color 120ms ease, box-shadow 120ms ease;
}
.paste-input-card textarea::placeholder { color: var(--muted); opacity: 0.85; }
.paste-input-card textarea:focus {
  outline: none;
  border-color: var(--accent);
}
/* Sample-note fill — the note simply fades in smoothly (no scanning line). Only
   the text colour animates (transparent → ink), so the field/border stay steady. */
.paste-input-card textarea.is-filling {
  animation: paste-fade 420ms var(--ease) both;
}
@keyframes paste-fade {
  from { color: transparent; }
  to   { color: var(--ink); }
}

/* ---- Upload dropzone ----------------------------------------------------- */
.dropzone {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-height: 240px;
  /* Borderless at rest — fill + icon/text carry the affordance. A dashed accent
     border appears only on hover / drag-over / has-file. */
  border: 1.5px dashed transparent;
  border-radius: var(--r);
  background: var(--panel-2);
  cursor: pointer;
  padding: 1.25rem;
  text-align: center;
  transition: border-color var(--dur-2) var(--ease), background var(--dur-2) var(--ease), box-shadow var(--dur-2) var(--ease);
}
.dropzone:hover,
.dropzone:focus-within {
  background: var(--mint-soft);
  border-color: var(--mint);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--mint) 18%, transparent);
}
.dropzone:hover .icon-dropzone,
.dropzone:focus-within .icon-dropzone {
  color: var(--mint);
}
.dropzone.is-dragover {
  border-style: solid;
  border-color: var(--mint);
  background: var(--mint-soft);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--mint) 18%, transparent);
}
.dropzone.is-dragover .icon-dropzone {
  color: var(--mint);
}
.dropzone.has-file {
  border-style: solid;
  border-color: var(--accent);
  background: var(--accent-soft);
}

/* Screen-reader-only input. The wrapping <label class="dropzone"> triggers
   the file picker on click/tap, so the input needs no full-size overlay
   (a full-bleed invisible input becomes a phantom hit-target). Drag-and-drop
   is handled by listeners on .dropzone itself. */
.dropzone-input {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

.dropzone-idle {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.55rem;
  pointer-events: none;
}

.dropzone-headline {
  font-weight: 700;
  color: var(--text);
  font-size: 0.95rem;
}

.dropzone-browse {
  color: var(--accent);
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 2px;
}

.dropzone-hint {
  font-size: var(--fs-xs);
  color: var(--muted);
  font-family: var(--font-sans);
  letter-spacing: 0.02em;
}

.dropzone-file-chosen {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-weight: 700;
  color: var(--accent-soft-ink);
  font-size: 0.9rem;
  pointer-events: none;
  word-break: break-all;
}

/* ---- Step footer & navigation buttons ------------------------------------ */
.step-footer {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.65rem;
  flex: 0 0 auto; /* fixed footer row below the scroll region */
  margin-top: 1rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--line);
}

.step-footer .primary-button,
.step-footer .secondary-button {
  min-height: 44px; /* WCAG touch target */
}
/* Editorial restraint: the single-step CTA is a compact right-aligned button,
   not a full-bleed bar. (The dual footer already sizes its primary to auto.) */
.step-footer:not(.step-footer-dual) .primary-button { width: auto; }

.step-footer-dual {
  flex-direction: row;
  justify-content: space-between;
  align-items: center;
}
/* Editorial restraint: Back sits left, the primary is a compact right-aligned
   button (not a full-bleed bar) — matches the single-step footer. */
.step-footer-dual .primary-button { width: auto; flex: 0 0 auto; }
.step-footer-dual .secondary-button { flex: 0 0 auto; }

.step-hint {
  font-size: 0.85rem;
  color: var(--muted);
  margin: 0;
  text-align: right;
}

/* ---- Step 3 — processing (maritime sonar) -------------------------------- */
/* A calm, centered radar/sonar emblem — deterministic runs are usually quick, so
   this is an indeterminate on-brand loader (not a progress bar). Fills the card's
   middle and centres on both axes; the real backend stage message sits beneath. */
.proc-panel {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1.4rem;
  min-height: 240px;
  padding: 2rem 0;
  text-align: center;
}
.proc-sonar {
  position: relative;
  width: 92px;
  height: 92px;
  border-radius: 50%;
  border: 1.5px solid color-mix(in srgb, var(--sage) 42%, transparent);
}
/* inner ring */
.proc-sonar::before {
  content: "";
  position: absolute;
  inset: 19px;
  border-radius: 50%;
  border: 1px solid color-mix(in srgb, var(--sage) 32%, transparent);
}
/* centre dot */
.proc-sonar::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  transform: translate(-50%, -50%);
  background: var(--accent);
}
/* the rotating sweep — a fading sea-cliff wedge (radar trail) */
.sonar-sweep {
  position: absolute;
  inset: -1.5px;
  border-radius: 50%;
  overflow: hidden;
  background: conic-gradient(from 0deg,
    color-mix(in srgb, var(--sage) 52%, transparent) 0deg,
    color-mix(in srgb, var(--sage) 10%, transparent) 54deg,
    transparent 76deg,
    transparent 360deg);
  animation: sonar-sweep 2.2s linear infinite;
}
@keyframes sonar-sweep { to { transform: rotate(360deg); } }
.proc-stage {
  margin: 0;
  color: var(--muted);
  font-family: var(--font-sans);
  font-size: var(--fs-sm);
  letter-spacing: 0.01em;
}

/* ---- Step 3 — result layout ---------------------------------------------- */
.step3-result {
  display: flex;
  flex-direction: column;
  gap: 0.7rem;
  margin-top: 0.4rem;
  /* The OUTPUT box scrolls internally and the output-hero shrinks first, so the
     result banner + collapsed "what was removed" stay pinned and always visible.
     The result itself is NOT a scroll region (that would defeat the shrink). */
  flex: 1 1 auto;
  min-height: 0;
}
/* The collapsed "what was removed" row is pinned; when expanded, its body scrolls
   within a cap (see the max-height in the Step-3 priority block). */
.step3-result .step-scroll {
  flex: 0 0 auto;
  min-height: 0;
  overflow-y: auto;
  margin: 0;
  padding: 0;
}

.output-hero {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

/* Output box inherits its base styles from section 7; these extend it. Capped
   so it's a comfortable pane inside the scrollable card, not a giant block. */
.output-hero .output-box { min-height: 110px; max-height: 240px; }
.output-hero .output-box.has-content { min-height: 130px; }

.output-action-row {
  display: flex;
  flex-wrap: wrap;
  gap: 1.1rem;      /* air between the CTA and the ghost action */
  align-items: center;
}
/* Override the global .primary-button { width:100% } — in this row the buttons size
   to their content so Copy + Download read as a clear primary/secondary pair with
   air between them (not a full-width bar that forces Download to wrap under it).
   "Nothing is stored" is pushed to the right by its own auto margin. */
.output-action-row .primary-button,
.output-action-row .secondary-button {
  width: auto;
  flex: 0 0 auto;
  min-height: 44px;
}
/* Copy = the dark CTA (kept), sized with a comfortable presence. */
.output-action-row .primary-button { padding-inline: 1.5rem; }
/* Download = a quiet ghost action: transparent until hover, so it reads as clearly
   secondary to the dark CTA and the row breathes instead of stacking two heavy
   blocks. */
.output-action-row .secondary-button {
  background: transparent;
  color: var(--text);
  padding-inline: 1.1rem;
}
.output-action-row .secondary-button:hover {
  background: var(--panel-2);
  color: var(--ink);
}

.findings-section-label {
  font-family: var(--font-sans);
  font-size: var(--fs-eyebrow);
  font-weight: 600;
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  color: var(--muted);
  margin: 0 0 var(--space-3);
}

/* Result — the "safe to share" banner stays condensed on the card; the "Details"
   button opens the full breakdown in a modal. On a 0-PHI note there's nothing to
   review, so the button is hidden (.no-detail). */
.result-summary { display: block; }
.result-summary-row { display: block; }
/* "Details" — a real button at the right end of the banner that opens the modal. */
.result-toggle {
  order: 3;
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  flex: 0 0 auto;
  border: none;
  background: none;
  cursor: pointer;
  padding: 0.2rem 0.15rem;
  font-family: var(--font-sans);
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--muted);
  transition: color var(--dur-1) var(--ease);
}
.result-toggle:hover { color: var(--ink); }
.result-toggle:hover .result-toggle-chev { transform: translateX(2px); }
.result-toggle-chev {
  display: inline-flex;
  transition: transform var(--dur-2) var(--ease);
}
/* Details is a toggle — flip the chevron to point back at the note while the
   pane is open ("this will collapse it"). The open rule outranks the hover nudge. */
.sheet.findings-open .result-toggle .result-toggle-chev { transform: rotate(180deg); }
.result-summary.no-detail .result-toggle { display: none; }

/* ---- Findings pane — the second column of the SAME step-3 card --------------
   Opening adds .findings-open to the step-3 sheet, which animates --findings-w.
   That one value grows the findings grid column AND shrinks the card's symmetric
   margins by the same amount (see the ≥1360px block near #step3-result), so the
   card widens by exactly --findings-w and stays centered while the note column
   keeps a constant width (no reflow). The pane lives inside .sheet-body, so it
   shares the one rounded surface + shadow — the only seam is an intentional
   hairline. Base (mobile-first) here = stacked below the note (reveal by height);
   the side-by-side grid is opted into at ≥1360px. */
@property --findings-w { syntax: "<length>"; inherits: true; initial-value: 0px; }

.result-note-col {
  display: flex;
  flex-direction: column;
  gap: 0.9rem;      /* vertical breathing room between the note area and the banner */
  min-width: 0;
  min-height: 0;
  flex: 1 1 auto;   /* stacked: owns the flexible height (grid mode ignores this) */
}

.findings-pane {
  overflow: hidden;
  min-width: 0;
  /* Stacked default: reveal by height. The ≥1360px block makes it width-driven. */
  max-height: 0;
  transition: max-height var(--dur-4) var(--ease-out);
}
.findings-pane[hidden] { display: none; }
.sheet.findings-open .findings-pane { max-height: 52vh; }   /* stacked reveal height */
.findings-pane-inner {
  width: auto;                             /* full width when stacked */
  height: 100%;
  display: flex;
  flex-direction: column;
  min-height: 0;
  padding-top: 1rem;
  box-sizing: border-box;
}
/* Header mirrors the note's .output-head — a single eyebrow row with a control on
   the right — so "Findings" lines up with "Cleaned note" / the Highlight toggle. */
.findings-pane-head {
  flex: 0 0 auto;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 0.85rem;
}
.findings-pane-head .findings-section-label { margin: 0; }
.findings-pane-sub { margin: 0.35rem 0 0.9rem; color: var(--muted); font-size: var(--fs-sm); }
/* Small, quiet close — an icon, not a full button. */
.findings-close {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.75rem;
  height: 1.75rem;
  padding: 0;
  border: none;
  background: none;
  border-radius: var(--r-sm);
  color: var(--muted);
  cursor: pointer;
  transition: background var(--dur-1) var(--ease), color var(--dur-1) var(--ease);
}
.findings-close:hover { background: var(--panel-2); color: var(--ink); }
.findings-close svg { width: 1.05rem; height: 1.05rem; }
.findings-host {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding-right: 0.15rem;
}

/* Clean list — one consistent UI font (Inter/--font-sans) throughout; numbers in
   tabular figures for alignment. Light category header + plain value rows, hairline
   between groups. */
.fl-group { padding: 0.55rem 0; border-top: 1px solid var(--line); }
.fl-group:first-child { border-top: none; padding-top: 0; }
.fl-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.75rem;
  margin-bottom: 0.3rem;
}
.fl-cat { font: 600 var(--fs-sm)/1.2 var(--font-sans); color: var(--ink); }
.fl-count { font-family: var(--font-sans); font-variant-numeric: tabular-nums; font-size: var(--fs-xs); font-weight: 600; color: var(--muted); }
.fl-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 0.12rem 0;
}
.fl-val { font-family: var(--font-sans); font-size: var(--fs-sm); color: var(--text); overflow-wrap: anywhere; }
.fl-withheld { font-style: italic; color: var(--muted); font-size: var(--fs-sm); }
/* Failure detail in the pane — danger FILL only (the borderless system: surfaces
   differ by fill, never a stroke). Same geometry as the caution strip, so caution
   and failure read as one component in two clearly different tones. */
.fl-error {
  margin: 0;
  padding: 0.6rem 0.8rem;
  border-radius: var(--r);
  background: var(--danger-bg);
  color: var(--danger-ink);
  font-size: var(--fs-sm);
  line-height: 1.55;
}

/* Caution strip — a compact, warn-toned heads-up (fill only, no stroke): identifying
   data was found in the ORIGINAL, review the output. Deliberately small so the
   findings list below keeps the scroll space. The 0–100 risk score of the original
   is a quiet tooltipped badge, not a headline. */
.findings-caution {
  margin-bottom: 0.9rem;
  padding: 0.6rem 0.8rem;
  border-radius: var(--r);
  background: var(--warn-bg);
}
.findings-caution[hidden] { display: none; }
.findings-caution-text {
  margin: 0;
  color: var(--warn-ink);
  font-size: var(--fs-sm);
  font-weight: 500;
  line-height: 1.5;
}

/* Failure state — the note content is replaced by a short error block, and the
   findings pane auto-extends alongside with the full explanation. */
.step3-error-block { display: none; flex-direction: column; gap: 1rem; align-items: flex-start; }
.step3-result.is-error .output-hero,
.step3-result.is-error .result-note-col > .step-scroll { display: none; }
.step3-result.is-error .step3-error-block { display: flex; }
.step3-error-message { margin: 0; color: var(--text); font-size: 0.95rem; line-height: 1.5; }

/* ---- Mobile overrides (≤600px) ------------------------------------------ */
@media (max-width: 600px) {
  .stepper-list  { display: none; }
  .stepper-mobile {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
  }
  .stepper-mobile-text {
    font-size: 0.85rem;
    font-weight: 700;
    color: var(--accent-soft-ink);
  }
  .stepper-mobile-bar {
    display: grid;
    grid-template-columns: 1fr 1fr 1fr;
    gap: 3px;
    height: 4px;
    border-radius: var(--r-pill);
    overflow: hidden;
  }
  .stepper-mobile-seg {
    background: var(--line-strong);
    border-radius: var(--r-pill);
    transition: background 300ms ease;
  }
  .stepper-mobile-seg.is-filled { background: var(--accent); }

  .input-cards { grid-template-columns: 1fr; }

  /* Tighten the dense topbar nav at phone widths. */
  .topbar-inner { padding-left: 0.75rem; padding-right: 0.75rem; }
  .nav-links { gap: 0.65rem; }
  .topbar-actions { gap: 0.5rem; }

  .step-footer {
    align-items: stretch;
  }
  .step-footer .primary-button,
  .step-footer .secondary-button {
    width: 100%;
  }
  .step-footer-dual {
    flex-direction: column-reverse;
    align-items: stretch;
  }
  .step-hint { text-align: center; }

  .output-action-row {
    flex-direction: column;
    align-items: stretch;
  }
  .output-action-row .primary-button,
  .output-action-row .secondary-button {
    max-width: none;
    width: 100%;
  }
}

/* =========================================================================
   10. Sheet Deck — side-by-side sliding panels for Add · Clean · Copy
   Desktop flex layout is the default (no media query).
   Mobile grid override at ≤700 px.
   Tokens used: --accent --accent-soft --accent-soft-ink --panel --panel-2
                --line --shadow-1 --shadow-2 --r-lg --font-mono --font-sans
                --muted
   ========================================================================= */

/* Step-flow holds the ~920 px deck plus breathing room. Everything — context
   line, stepper map, deck — shares one centred column so the whole flow reads
   as a single object, not a lopsided spread. */
.step-flow {
  max-width: min(1120px, 94vw);
}
@media (min-width: 1650px) { .step-flow { max-width: 1240px; } }

/* The stepper "map" shares the SAME column as the visible card — the deck is the
   full step-flow width and the card sits inside 2×64px gutters, so size the stepper
   to (100% − 128px) and it auto-tracks whatever width the deck scales to.
   NOTE: an explicit width (not max-width) is required because .step-flow is a flex
   column — margin:auto on a flex item with auto width disables stretch and collapses
   it to content width (the "ADDCLEANCOPY" bug). */
.step-flow > .stepper {
  width: calc(100% - 128px);
  max-width: none;
  margin-left: auto;
  margin-right: auto;
}

/* The stepper is the MAP: it caps the sheet directly above it. Labels, numbers,
   and state live here (single source of truth); the side peeks carry no text. */
.stepper {
  display: block;
  margin: 0 auto 0.9rem;
}
.stepper-list {
  display: flex;   /* shown at all widths ≥700 px; mobile swaps to the strip */
}

/* ==========================================================================
   Sheet deck — a stacked slide-over. All three steps occupy one grid cell; the
   active step's content sits centred and in flow on a consistent SHEET SURFACE
   (defining the deck height), while the neighbouring steps are parked off-side
   and their thin paper-EDGE peeks poke out from BEHIND the active sheet like a
   stack of pages. Advancing slides the surface (translateX) + cross-fades — a
   real slide-over — and the peeks fade/restack in sync. The top stepper is the
   map; the peeks are the physical stack. No duplicated labels.
   ========================================================================== */
.sheet-deck {
  position: relative;
  width: 100%;
  max-width: 1012px;
  margin: 0 auto;
  min-height: 240px;
}

.sheet-deck > form {
  position: relative;
  display: grid;
  grid-template-areas: "stack";
}

.sheet {
  grid-area: stack;   /* all steps overlap in one full-width cell */
  position: relative;
  min-width: 0;
}

/* ---- Sheet body: the sliding content surface ----------------------------
   A CONSISTENT frame: fixed side gutters (46 px) hold the peeks, and a shared
   min-height keeps every step the same size so the slide never resizes the card
   (the old "card changes length mid-transition" jar). The footer pins to the
   bottom, so the primary button sits in the same place on every step. */
.sheet-body {
  display: flex;
  flex-direction: column;
  margin: 0 64px;   /* gutters hold a visible portion of the neighbouring cards */
  /* One consistent, viewport-fitted workspace: the SAME height on every step,
     derived from the viewport so it fits a 14" laptop and scales up. The middle
     region (.step-scroll) scrolls for long input/output — the card never grows
     past the fold. */
  height: clamp(460px, calc(100vh - 260px), 900px);
  height: clamp(460px, calc(100dvh - 260px), 900px);
  overflow: hidden; /* the inner .step-scroll handles overflow; card never grows */
  padding: 2.5rem 3rem;
  background: var(--panel);
  border: none;                 /* light: shadow separates. dark: hairline restored below */
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-2);
  transition: opacity var(--dur-3) var(--ease), transform var(--dur-4) var(--ease);
  /* No permanent will-change: leaving the card on a composited layer softens the
     rasterised text (notably the mono output). The transition promotes as needed. */
}
/* Dark ground barely shows a shadow — restore a hairline for card separation. */
:root[data-theme="dark"] .sheet-body { border: 1px solid var(--line); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .sheet-body { border: 1px solid var(--line); }
}
/* Active: in flow (defines deck height), painted above the peeks. */
.sheet[data-state="active"] .sheet-body {
  position: relative;
  z-index: 3;
  opacity: 1;
  transform: translateX(0);
}
/* Inactive: out of flow (no height impact), parked off-side + faded. Slides in
   from whichever side it was parked when it becomes active. The 46 px insets
   match the active card's gutters so the slide stays aligned. */
.sheet[data-state="left"] .sheet-body,
.sheet[data-state="right"] .sheet-body {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  z-index: 0;
  opacity: 0;
  pointer-events: none;
}
.sheet[data-state="left"]  .sheet-body { transform: translateX(-56px); }
.sheet[data-state="right"] .sheet-body { transform: translateX(56px); }

/* (Step-1 watercolour corner art removed — the workspace is a clean instrument.) */

/* The step section fills the card so its footer can pin to the bottom. */
.sheet-body > .step { flex: 1 1 auto; display: flex; flex-direction: column; min-height: 0; position: relative; z-index: 1; }

/* Heading (+ subhead) and footer are fixed; only the middle scrolls. This is
   what keeps the card a constant size while long content stays reachable. */
.step-scroll {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  display: flex;
  flex-direction: column;
  margin-right: -0.9rem;   /* pull the scrollbar into the padding gutter */
  padding-right: 0.9rem;
  scrollbar-width: thin;
  scrollbar-color: var(--line-strong) transparent;
}
.step-scroll::-webkit-scrollbar { width: 8px; }
.step-scroll::-webkit-scrollbar-thumb { background: var(--line-strong); border-radius: var(--r-pill); }
.step-scroll::-webkit-scrollbar-track { background: transparent; }
/* Don't let scroll children shrink to fit — they must keep full height so the
   region OVERFLOWS and scrolls (otherwise a flex child with overflow:hidden
   clips its own content, e.g. expanded advanced options). Step-1 fills instead
   and overrides this with a more specific rule. */
.step-scroll > * { flex-shrink: 0; }

/* Step 1: paste is the primary action and FILLS the card; upload is a slim
   secondary strip below it. (Not two equal boxes — they aren't equal peers.) */
.step[data-step="1"] .input-cards {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  flex: 1 1 auto;
  min-height: 0;
  margin-bottom: 0;
}
.step[data-step="1"] .paste-input-card { flex: 1 1 auto; min-height: 0; }
.step[data-step="1"] .paste-input-card textarea {
  flex: 1 1 auto;       /* fills the available card height */
  min-height: 120px;
  max-height: none;
  height: auto;
  resize: none;
  overflow-y: auto;
}
/* Upload = compact horizontal strip: icon · prompt · file types on one line. */
/* Step 2: top-align so expanding the advanced options flows down and scrolls
   cleanly within the card (centring made the expand feel like it was clipping).
   When collapsed, the extra room simply reads as breathing space. */
.step[data-step="2"] .step-scroll { justify-content: flex-start; }

.step[data-step="1"] .upload-input-card { flex: 0 0 auto; }
.step[data-step="1"] .dropzone {
  min-height: 0;
  height: auto;
  flex-direction: row;
  align-items: center;
  justify-content: flex-start;
  gap: 0.85rem;
  padding: 0.9rem 1.2rem;
  text-align: left;
}
.step[data-step="1"] .dropzone-idle {
  flex-direction: row;
  align-items: center;
  gap: 0.75rem;
  flex-wrap: wrap;
}
.step[data-step="1"] .icon-dropzone { font-size: 1.35rem; margin: 0; }
.step[data-step="1"] .dropzone-headline { font-size: 0.95rem; }
.step[data-step="1"] .dropzone-hint { margin-left: auto; }

/* Step 3 priority: the cleaned OUTPUT is the star (sizes to content, scrolls
   internally when long); Copy/Download are fixed right under it; the "safe to
   share" banner + collapsed "what was removed" are FIXED and ALWAYS visible.
   The output-hero owns the flexible space and SHRINKS first, so the banner and
   detail can never be pushed past the card's fixed height. */
.step[data-step="3"] #step3-result { flex: 1 1 auto; min-height: 0; justify-content: flex-start; }
/* Processing-notes callout (when present) is pinned above the note. */
.step[data-step="3"] .result-note-col > .warning-callout { flex: 0 0 auto; }
.step[data-step="3"] .output-hero {
  flex: 1 1 auto;      /* GROWS to fill the card — the note is the priority */
  min-height: 130px;   /* floor = head + a little box + buttons, so the box never
                          squeezes its own controls (and shrinks when detail opens) */
  display: flex;
  flex-direction: column;
}
.step[data-step="3"] .output-head,
.step[data-step="3"] .output-action-row,
.step[data-step="3"] .output-ephemeral { flex: 0 0 auto; }
.step[data-step="3"] .output-hero .output-box {
  flex: 1 1 auto;      /* the box takes all the freed height; scrolls when longer */
  min-height: 0;
  max-height: none;    /* the card's fixed height bounds it — let the note fill */
  overflow-y: auto;
  scrollbar-gutter: stable;   /* reserve the scrollbar so the reveal can't reflow */
}
.step[data-step="3"] .output-action-row { flex: 0 0 auto; }
/* The result row (the "safe to share" banner) is FIXED and always visible; the
   full breakdown opens in the in-card findings pane rather than expanding inline. */
.step[data-step="3"] .result-note-col > .step-scroll { flex: 0 0 auto; min-height: 0; overflow: visible; }

/* Side-by-side findings pane — only where there's room to widen the card. The
   whole open/close animation is one custom property (--findings-w): it grows the
   findings grid column and, via the symmetric margin calc, widens the card by the
   same amount and re-centers it. Note column = 1fr stays a constant width. */
@media (min-width: 1360px) {
  .step[data-step="3"] #step3-result {
    display: grid;
    grid-template-columns: minmax(0, 1fr) var(--findings-w);
    gap: 0;
    align-items: stretch;
  }
  /* When open, keep the note content off the divider so "Highlight" (and the note)
     have room to breathe. Only when open, so the closed note stays symmetric. */
  .sheet.findings-open .result-note-col { padding-right: 1.6rem; }
  .sheet[data-step-sheet="3"].findings-open { --findings-w: min(440px, 24vw); --findings-w-max: min(440px, 24vw); }
  /* One animated variable does it all. The margin calc is UNCONDITIONAL (not gated
     on .findings-open) so it animates BOTH ways: at --findings-w:0 it equals 64px
     (closed, identical to base), and it interpolates smoothly to/from the open value
     — no snap on close. Transition --findings-w on the CARD only; #step3-result
     inherits the already-animating value (it's a descendant), so its grid column and
     the card margin move in perfect lockstep with no compounding. */
  .sheet[data-step-sheet="3"] .sheet-body {
    margin-inline: calc(64px - var(--findings-w) / 2);
    transition: opacity var(--dur-3) var(--ease), transform var(--dur-4) var(--ease), --findings-w var(--dur-4) var(--ease-out);
  }
  /* Width-driven in this mode (fixed-width inner = wipe reveal, no reflow). The
     divider is unconditional too — at 0 width (closed) the pane is invisible, so the
     hairline can't show; on close it shrinks away with the column instead of blinking
     out. No note-column padding toggle (that reflowed the note) — the gap lives on
     the findings side only. */
  .findings-pane { max-height: none; box-shadow: inset 1px 0 0 var(--line); }
  .findings-pane-inner { width: var(--findings-w-max, 440px); padding-top: 0; padding-left: 1.6rem; }
}

/* The banner: one compact row — seal · "Safe to share" · count · Details toggle. */
.step[data-step="3"] .exposure-card {
  display: flex;
  flex-flow: row nowrap;
  align-items: center;
  justify-content: flex-start;
  column-gap: 0.55rem;
  padding: 0.6rem 1.1rem;
  margin-bottom: 0;
}

/* Success mark — the trust seal (instrument-dial emblem) stamps the result with a
   fades in with the result banner (no separate pop — one motion per element). */
.exposure-seal { display: none; }
.exposure-card.exposure-caution .exposure-seal,
.exposure-card.exposure-clean .exposure-seal {
  display: block;
  order: -1;
  flex: 0 0 auto;
  width: 1.75rem;
  height: 1.75rem;
  color: var(--success);
}
.exposure-card.exposure-caution .exposure-seal { color: var(--warn-ink); }
.exposure-seal .trust-seal { display: block; width: 100%; height: 100%; }
.step[data-step="3"] .exposure-level {
  order: 1;
  font-family: var(--font-sans);
  font-size: 1rem;
  font-weight: 700;
  text-transform: none;
  letter-spacing: 0;
  color: var(--risk-low-ink);
}
.step[data-step="3"] .exposure-mainline { order: 2; margin: 0; }
.step[data-step="3"] .exposure-number {
  font-family: var(--font-sans);
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--text);
}
.step[data-step="3"] .exposure-scale { font-size: 0.95rem; color: var(--muted); }
/* The Copy button sits right above, so the long reassurance line is redundant —
   keep the banner to one compact, confident line. */
.step[data-step="3"] .exposure-message { display: none; }

/* ---- Paper-edge peeks: clean edges of the neighbouring sheets, stacked like
   pages behind the active card. Same paper colour as the card (not coloured
   bars) so they read as depth, not clutter. Anchored to the card edges via
   calc(100% - Npx) so they hold position at any deck width; --peek-i (JS) fans
   multiples. The top stepper is the primary back-nav; these are the physical
   stack + a secondary click target. z-index 1 → behind the card (z 3). */
.sheet-spine {
  position: absolute;
  top: 26px;
  bottom: 26px;
  width: 58px;
  padding: 0;
  border-radius: var(--r-lg);
  border: none;                 /* soft page edges — separated by shadow, not a stroke */
  background: transparent;
  cursor: default;
  z-index: 1;
  /* Base edge position is set once (right/left); only the fan offset + hover
     nudge move, and those ride on transform (GPU) — no per-frame layout. */
  --peek-shift: 0px;
  transition: opacity var(--dur-2) var(--ease),
              transform var(--dur-3) var(--ease),
              background var(--dur-1) var(--ease),
              box-shadow var(--dur-1) var(--ease),
              border-color var(--dur-1) var(--ease);
}
/* The active step has no peek — it IS the card. */
.sheet[data-state="active"] .sheet-spine { opacity: 0; pointer-events: none; }

/* Completed steps: a visible portion of the previous card, tucked 8 px behind
   the LEFT card edge. Fan the stack with a transform, not by re-laying-out. */
.sheet[data-state="left"] .sheet-spine {
  right: calc(100% - 72px);
  transform: translateX(calc(var(--peek-i, 0) * -14px));
  background: var(--panel);
  box-shadow: -12px 0 28px rgba(20, 34, 42, 0.10);
  cursor: pointer;
}
.sheet[data-state="left"] .sheet-spine:hover {
  transform: translateX(calc(var(--peek-i, 0) * -14px - 10px));
  background: var(--accent-soft);
}
/* Upcoming steps: a quieter portion of the next card behind the RIGHT edge. */
.sheet[data-state="right"] .sheet-spine {
  left: calc(100% - 72px);
  transform: translateX(calc(var(--peek-i, 0) * 14px));
  background: var(--panel-2);
  opacity: 0.85;
  box-shadow: 12px 0 28px rgba(20, 34, 42, 0.07);
}

/* The peeks are wordless — labels live in the top stepper map. */
.spine-num,
.spine-label { display: none; }

/* ---- Mobile navigation bar ------------------------------------------------ */
.sheet-mobile-nav { display: none; }

/* ---- Mobile (≤700 px) — full-width sheets slide edge to edge -------------- */
@media (max-width: 700px) {
  #step2-back { display: none; }

  /* Swap the horizontal map for the compact progress strip. */
  .stepper-list { display: none; }
  .stepper-mobile { display: flex; flex-direction: column; gap: 0.5rem; }

  .sheet-deck > form { overflow: hidden; border-radius: var(--r-lg); }

  /* The whole sheet slides (not just the body); only the active one is in flow. */
  .sheet { overflow: hidden; transition: transform 440ms cubic-bezier(.4, 0, .2, 1); }
  .sheet[data-state="active"] { position: relative; transform: translateX(0); }
  .sheet[data-state="left"]   { position: absolute; inset: 0; transform: translateX(-100%); }
  .sheet[data-state="right"]  { position: absolute; inset: 0; transform: translateX(100%); }

  .sheet-spine { display: none; }

  /* Body is a normal card inside the sliding sheet — reset the desktop slide
     and the fixed viewport height (phones size to content + page scrolls). */
  .sheet-body,
  .sheet[data-state="left"] .sheet-body,
  .sheet[data-state="right"] .sheet-body {
    position: static;
    opacity: 1;
    transform: none;
    pointer-events: auto;
    padding: 1.5rem 1.25rem;
    max-width: none;
    margin: 0;
    height: auto;
    min-height: 0;
  }
  /* No internal scroll on phones — let the whole page scroll instead. */
  .step-scroll {
    overflow: visible;
    margin-right: 0;
    padding-right: 0;
  }
  .step[data-step="1"] .input-cards { flex: 0 0 auto; }
  .step[data-step="1"] .paste-input-card textarea { min-height: 190px; }
  .step[data-step="1"] .dropzone { min-height: 200px; }

  .sheet-mobile-nav {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    margin-bottom: 1.25rem;
  }
}

@media (min-width: 900px) {
  .sheet-mobile-nav { display: none; }
}

/* =========================================================================
   11. Motion — smooth state changes (expand/collapse, reveals)
   ========================================================================= */

/* Smooth expand/collapse for every disclosure (advanced options, findings).
   Relies on interpolate-size (tokens.css); degrades to an instant toggle where
   ::details-content / size-keyword animation isn't supported. */
details::details-content {
  block-size: 0;
  overflow: clip;
  opacity: 0;
  transition: block-size var(--dur-4) var(--ease-out),
              opacity var(--dur-4) var(--ease-out),
              content-visibility var(--dur-4) allow-discrete;
}
details[open]::details-content {
  block-size: auto;
  opacity: 1;
}
.advanced-disclosure > summary { transition: color var(--dur-1) var(--ease); }

/* Gentle reveal for the Step-3 processing emblem. */
@keyframes rise-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}
#step3-skeleton:not([hidden]) { animation: rise-in var(--dur-3) var(--ease) both; }

/* Result reveal — one clean opacity fade of the whole result, driven by a class
   toggle + forced reflow in JS (not a keyframe on the [hidden]→shown swap, which
   flashed a frame of final-state content). No transform is left behind, so the
   note text stays crisp. */
#step3-result { transition: opacity var(--dur-2) var(--ease); }
#step3-result.is-revealing { opacity: 0; }

/* ---- Reduced motion — honour the user's preference everywhere ------------- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  .sheet-body { transform: none !important; }
  details::details-content { transition: none; }
}

/* ==========================================================================
   UX enhancements — inputs, flow confirmation, highlighted output, receipt.
   Front-end only; all colours/spacing/motion come from existing tokens.
   ========================================================================== */

/* ---- Step 1: paste-card head + sample-note link -------------------------- */
.input-card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
}
.sample-note-link {
  border: none;
  background: none;
  padding: 0;
  cursor: pointer;
  font-family: var(--font-sans);
  font-size: var(--fs-xs);
  font-weight: 600;
  color: var(--accent);
  letter-spacing: 0.01em;
  text-decoration: underline;
  text-underline-offset: 3px;
  transition: color var(--dur-1) var(--ease);
}
.sample-note-link:hover { color: var(--accent-hover); }

/* ---- Step 1: whole-card drop overlay ------------------------------------- */
.card-drop-overlay {
  position: absolute;
  inset: 0;
  z-index: 3;
  display: grid;
  place-items: center;
  border-radius: var(--r);
  background: color-mix(in srgb, var(--panel) 82%, transparent);
  backdrop-filter: blur(2px);
  border: 2px dashed var(--accent);
  opacity: 0;
  transform: scale(0.985);
  pointer-events: none;             /* never intercepts drops; the sheet listener does */
  transition: opacity var(--dur-2) var(--ease), transform var(--dur-2) var(--ease-out);
}
.step.is-file-dragging .card-drop-overlay {
  opacity: 1;
  transform: none;
}
.card-drop-inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.35rem;
  color: var(--accent);
  text-align: center;
}
.card-drop-inner .icon-dropzone { color: var(--accent); margin: 0; }
.card-drop-headline {
  font-family: var(--font-display);
  font-size: var(--fs-lg);
  color: var(--ink);
}
.card-drop-hint { font-size: var(--fs-xs); color: var(--muted); }

/* ---- Step 2: plain-English confirmation summary -------------------------- */
.clean-summary {
  display: flex;
  align-items: flex-start;
  gap: 0.7rem;
  margin: 0 0 var(--space-5);
  padding: 1rem 1.1rem;
  border-radius: var(--r);
  background: var(--accent-soft);
  color: var(--accent-soft-ink);
}
.clean-summary-icon {
  flex: 0 0 auto;
  font-size: 1.15rem;
  display: inline-flex;
  margin-top: 0.05rem;
  color: var(--accent);
}
.clean-summary-text {
  margin: 0;
  font-size: var(--fs-sm);
  font-weight: 600;
  line-height: 1.5;
  color: var(--accent-soft-ink);
}

/* Inline mono token used in prose (e.g. "a label like [NAME]"). Matches the
   finding-example token chips but tuned for running text. */
.mono-token {
  font-family: var(--font-mono);
  font-size: 0.82em;
  font-weight: 600;
  letter-spacing: 0.03em;
  white-space: nowrap;
  background: var(--mono-chip-bg);
  color: var(--mono-chip-ink);
  border: none;
  border-radius: 5px;
  padding: 0.04em 0.4em;
}

/* ---- Step 3: output head (label + count + highlight toggle) --------------- */
.output-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  flex-wrap: wrap;
}
.output-head-label { margin: 0; }
.output-head-actions {
  display: inline-flex;
  align-items: center;
  gap: 0.85rem;
}
.redaction-count {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  font-weight: 600;
  color: var(--accent-soft-ink);
  font-variant-numeric: tabular-nums;
}
.highlight-toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-family: var(--font-sans);
  font-size: var(--fs-xs);
  font-weight: 600;
  color: var(--muted);
  cursor: pointer;
  user-select: none;
}
.highlight-toggle input { accent-color: var(--accent); cursor: pointer; }

/* ---- Step 3: inline redaction chips inside the output -------------------- */
.redaction-chip {
  font-family: var(--font-mono);
  font-size: 0.86em;
  font-weight: 600;
  letter-spacing: 0.02em;
  background: var(--mono-chip-bg);
  color: var(--mono-chip-ink);
  border: none;
  border-radius: 5px;
  padding: 0.04em 0.34em;
}

/* (The cleaned note fades in with its .output-hero container — see the Step-3
   result reveal sequence above; no separate output animation needed.) */

/* ---- Step 3: Copy button success state ----------------------------------- */
#copy-button .icon-btn-check { display: none; }
#copy-button.is-copied .icon-btn-check { display: inline-flex; }
#copy-button.is-copied .icon-btn-copy { display: none; }
#copy-button.is-copied {
  background: var(--success);
  border-color: var(--success);
  color: var(--success-ink);
  animation: copied-pop 320ms var(--ease-out);
}
#copy-button.is-copied .copy-button-label { color: var(--success-ink); }
#copy-button.is-copied .icon-btn { color: var(--success-ink); }
@keyframes copied-pop {
  0%   { transform: scale(1); }
  40%  { transform: scale(1.05); }
  100% { transform: scale(1); }
}

/* ---- Step 3: ephemerality reassurance ------------------------------------ */
.output-ephemeral {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  margin: 0 0 0 auto;   /* pushes to the right end of the action row */
  font-size: var(--fs-xs);
  color: var(--muted);
}
.icon-ephemeral { font-size: 0.9rem; display: inline-flex; color: var(--mint-ink); }

/* ---- Step 3: 0-PHI clean state ------------------------------------------- */
/* On a clean note the bare "0" reads like a failing grade. Hide it and lead with
   "Already safe", with the scan detail as an indented subtext line beneath —
   overriding the Step-3 row layout (which was built for "Safe to share · N"). */
.exposure-card.exposure-clean .exposure-number,
.exposure-card.exposure-clean .exposure-mainline { display: none; }
.step[data-step="3"] .exposure-card.exposure-clean {
  flex-wrap: wrap;
  row-gap: 0.1rem;
  align-items: center;
}
.step[data-step="3"] .exposure-card.exposure-clean .exposure-level { order: 1; }
.step[data-step="3"] .exposure-card.exposure-clean .exposure-support {
  display: block;
  order: 2;
  flex-basis: 100%;
  margin: 0 0 0 3.2rem;   /* align the subtext under the headline, clearing the motif */
  font-size: 0.9rem;
  font-weight: 500;
  color: var(--muted);
}

/* ---- Step 2: method choice (segmented control, replaces the profile select) */
.method-choice { border: 0; padding: 0; margin: 0 0 1.2rem; min-width: 0; }
.field-eyebrow {
  display: block;
  padding: 0;
  margin-bottom: var(--space-2);
  font-family: var(--font-sans);
  font-size: var(--fs-eyebrow);
  font-weight: 600;
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  color: var(--muted);
}
.method-options {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));  /* stacks when narrow */
  gap: 0.75rem;
}
.method-option { position: relative; display: block; cursor: pointer; }
.method-option > input {
  position: absolute;
  inset: 0;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}
.method-option-inner {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  height: 100%;
  padding: 0.85rem 1rem;
  border: 1.5px solid transparent;   /* borderless at rest; selected/hover ring below */
  border-radius: var(--r);
  background: var(--panel-2);
  transition: border-color var(--dur-1) var(--ease),
              background var(--dur-1) var(--ease),
              box-shadow var(--dur-1) var(--ease);
}
.method-option:hover .method-option-inner { border-color: color-mix(in srgb, var(--accent) 45%, transparent); }
.method-option > input:checked + .method-option-inner {
  border-color: var(--accent);
  background: var(--accent-soft);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 14%, transparent);
}
.method-option > input:focus-visible + .method-option-inner {
  outline: 3px solid var(--focus);
  outline-offset: 2px;
}
.method-option > input:disabled { cursor: not-allowed; }
.method-option > input:disabled + .method-option-inner { opacity: 0.55; cursor: not-allowed; }
.method-name { font-weight: 700; font-size: 1rem; color: var(--ink); }
.method-badge { font-size: var(--fs-xs); font-weight: 600; letter-spacing: 0.01em; color: var(--accent); }
.method-sub { font-size: var(--fs-xs); font-weight: 500; color: var(--muted); }
