/* CiteProof design system.
 *
 * Hand-authored, and that is a product decision rather than a constraint we
 * happened to accept: there is no build step in deployment and the CSP allows
 * only 'self'. A CDN-hosted stylesheet or a webfont would be a supply-chain
 * dependency on a product whose entire pitch is trustworthiness, and a
 * stylesheet that decides what a legal document looks like is exactly the kind
 * of thing nobody should be fetching from someone else's CDN.
 *
 * The visual direction is "archival legal document meets forensic instrument".
 * It is derived from what the product actually is -- a citation is text inside
 * brackets, and checking it means resolving that text against a public record --
 * rather than applied from a current style trend. Concretely that means:
 *
 *   * Warm archival paper rather than cold white. A law report is printed on
 *     cream stock, and pure #ffffff reads as software.
 *   * A display serif for authority, a sans for interface, a true monospace for
 *     citation strings and record IDs. A reporter citation must look like a
 *     citation; body text is the wrong register for it.
 *   * Hairline rules and tick marks borrowed from the edge of a bound volume,
 *     used sparingly as instrument detail.
 *   * Warm-tinted shadows, because a shadow on cream paper that is neutral grey
 *     reads as dirt.
 *
 * FOUR INVARIANTS hold across every rule below. They are load-bearing and each
 * one has bitten us before:
 *
 *  1. Colour is never the only signal. Every status carries a text badge, so the
 *     page is legible to a colour-blind reader and survives monochrome print.
 *  2. "Not checked" never looks like "fine". The muted tone is reserved for
 *     results we could not verify, because reading it as a clearance is the one
 *     failure this product must never produce.
 *  3. The brand accent is blue, never red. Red means "this citation is wrong"
 *     on this page; a red logo would make that signal ambiguous.
 *  4. A rule that styles a template class must exist. tests/test_frontend.py
 *     fails the build if a template emits a class with no rule here, which is
 *     the only thing standing between a refactor and a silently broken page.
 *
 * DARK MODE is designed, not token-swapped. Shadows cannot darken an already
 * dark surface, so the light theme's shadow model is inverted: elevation is
 * carried by an inset top highlight plus a low-alpha light ring, which is how
 * physical layering actually reads in the dark.
 *
 * PRINT is a first-class target. Lawyers print reports and staple them to
 * filings, so there is a real stylesheet for it at the foot of this file.
 */

/* ==========================================================================
   1. Tokens
   ========================================================================== */

:root {
  /* Native chrome follows the scheme: without this, scrollbars, the file
   * picker and autofill render light inside dark mode. */
  color-scheme: light dark;
  /* -- ink and paper ------------------------------------------------------
   * The paper is warm (hue ~40) and the ink is cool (hue ~215). That split is
   * what makes the page read as printed stock rather than a screen: warm
   * substrate, cool marks on it. Pure #000 and #fff are avoided deliberately
   * -- both are available in this design but neither is correct here. */
  --paper:      #fbfaf7;   /* warm archival white, the page field          */
  --paper-raised: #fffefb; /* cards and panels sitting on the field        */
  --paper-sunk: #f4f2ec;   /* wells, table stripes, inset regions          */

  --ink:        #151a21;   /* body text, near-black with a blue cast       */
  --ink-strong: #0b0e13;   /* hover of primary controls                    */
  --ink-soft:   #454e5a;   /* secondary prose                              */
  --ink-faint:  #5c6572;   /* metadata, hints, eyebrow labels              */
  /* There is deliberately no quieter ink token below this one. On this warm
   * paper --ink-faint sits near 5:1 even on the tinted surfaces (sunk wells,
   * washes, tone rails), which is the floor for small text, so any
   * additional "decorative" grey lands under AA. A token that cannot be
   * read is not a design accent, it is a defect waiting to be used: quiet is
   * carried by size and position here, not by fading the text out. */

  --line:        #e0dcd2;  /* the default hairline, warm grey              */
  --line-strong: #c3bdae;  /* emphasised hairline, input borders           */
  --line-faint:  #ebe8e0;  /* internal dividers inside a panel             */

  /* -- brand ---------------------------------------------------------------
   * Signature is a deep archival blue: court seals, navy robes, the blue tick
   * of an attested document. Brass is the instrument accent, used only for fine
   * detail (ticks, the progress sweep, the mark's tick) where it should read as
   * a machined part rather than a brand colour. */
  --brand:        #1c3f6e;
  --brand-deep:   #142f54;
  --brand-bright: #2b5c9c;
  --brand-wash:   #eef2f8;
  --brand-line:   #c3d2e6;

  --brass:       #9a7b3f;
  --brass-bright:#c09a4f;
  /* Brass as a border or a wash needs no contrast; brass as text does. The
   * decorative brass is 3.94:1, so text that must be read gets its own
   * darkened token rather than borrowing the decorative one. */
  --brass-ink:    #7a5f2c;
  /* Text on the brand fill. White in the light scheme; near-black in the dark
   * scheme, where the brand itself is a light blue that white fails against. */
  --on-brand:    #ffffff;
  /* Mesh hero washes (landing atmosphere). Light values are soft daylight
   * tints of the brand hue on paper; the dark twins live in both dark-theme
   * blocks below and must match each other exactly. */
  --mesh-a: oklch(85% 0.06 258);
  --mesh-b: oklch(91% 0.035 250);
  --mesh-c: oklch(79% 0.085 265);
  --mesh-grid: rgba(28, 63, 110, 0.08);
  --mesh-card: rgba(255, 254, 251, 0.78);
  --mesh-card-line: rgba(28, 63, 110, 0.16);

  /* -- status tones --------------------------------------------------------
   * Each tone is a background / ink / border triple, where the ink token is the
   * same value as the border token. One token per tone means a badge and the
   * panel behind it cannot drift apart in colour. */
  --ok-bg:   #e8f1ea;  --ok-ink:   #14562f;  --ok-line:   #86b899;
  --warn-bg: #fbf0da;  --warn-ink: #6b4708;  --warn-line: #d8b164;
  --bad-bg:  #fbe9e5;  --bad-ink:  #8a1f14;  --bad-line:  #dd9086;
  --mute-bg: #eeece5;  --mute-ink: #4d5460;  --mute-line: #c7c2b6;

  --focus: #1c5fd0;

  /* -- measure and scale --------------------------------------------------- */
  --shell: 1120px;        /* chrome, marketing, and the report instrument  */
  --measure: 66ch;        /* long-form legal prose                         */
  --measure-wide: 78ch;   /* prose that carries tables or definition lists */

  --radius-sm: 3px;
  --radius:    6px;
  --radius-lg: 10px;
  --radius-xl: 16px;

  --s1:  4px;
  --s2:  8px;
  --s3:  12px;
  --s4:  16px;
  --s5:  24px;
  --s6:  32px;
  --s7:  48px;
  --s8:  72px;

  /* -- type ----------------------------------------------------------------
   * Three explicit families, because the product has three registers:
   * authority (serif, for propositions), interface (sans, for everything the
   * user operates), and reference (mono, for anything that is a citation or an
   * identifier and must be character-exact).
   *
   * No webfonts, so these are ordered stacks that degrade through serifs that
   * actually exist. The serif list leads with the highest-quality ones on each
   * platform and ends at Georgia, which is on every desktop and reads
   * acceptably small rather than breaking the page. */
  --font-display: "Iowan Old Style", "Palatino Linotype", Palatino,
    "Book Antiqua", "Hoefler Text", Georgia, "Times New Roman", serif;
  --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
    "Helvetica Neue", Helvetica, Arial, sans-serif;
  --font-mono: ui-monospace, "Cascadia Mono", "SF Mono", "Segoe UI Mono",
    Menlo, Consolas, "Liberation Mono", monospace;

  /* Modular scale, ~1.26 ratio, floored to whole pixels so it never lands on a
   * fractional size that a rasteriser rounds unpredictably. The old ladder ran
   * to 40px; the hero needs to go further than that to read as a display line. */
  --step--2: 11.5px;
  --step--1: 13px;
  --step-0:  15.5px;
  --step-1:  18px;
  --step-2:  21.5px;
  --step-3:  27px;
  --step-4:  34px;
  --step-5:  43px;
  --step-6:  54px;

  --leading-tight: 1.08;
  --leading-snug:  1.25;
  --leading-body:  1.62;

  --tracking-tight: -0.021em;
  --tracking-snug:  -0.012em;
  --tracking-wide:   0.14em;  /* small-caps eyebrow labels */

  --weight-normal:   400;
  --weight-medium:   500;
  --weight-semibold: 600;
  --weight-bold:     700;

  /* -- elevation -----------------------------------------------------------
   * Shadows are warm (hue ~35) to match the paper. A neutral-grey shadow on a
   * cream field reads as a smudge rather than as depth, which is the single
   * cheapest way to make a light UI look cheap.
   *
   * Every level pairs a hairline with the shadow. The hairline carries the
   * edge in monochrome print and in forced-colors mode, where the shadow
   * disappears entirely. */
  --elev-0: none;
  --elev-1: 0 1px 2px rgba(58, 42, 20, 0.07), 0 1px 1px rgba(58, 42, 20, 0.04);
  --elev-2: 0 1px 2px rgba(58, 42, 20, 0.06), 0 10px 24px -10px rgba(58, 42, 20, 0.18);
  --elev-3: 0 2px 4px rgba(58, 42, 20, 0.07), 0 24px 48px -20px rgba(58, 42, 20, 0.26);

  /* -- motion --------------------------------------------------------------
   * One duration for feedback and one for state change. Short, because a
   * verification tool that makes you wait to acknowledge a click feels broken.
   * Both are zeroed wholesale under prefers-reduced-motion at the foot of this
   * file. */
  --dur-fast: 90ms;
  --dur:      160ms;
  --dur-slow: 320ms;
  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);

  /* -- ornament ------------------------------------------------------------
   * The tick rule is the edge of a bound reporter volume: fine vertical marks
   * on a hairline baseline. Used twice on the whole site (site header and the
   * report masthead) so it reads as a signature rather than as a texture. */
  --tick: repeating-linear-gradient(90deg,
    var(--line-strong) 0 1px, transparent 1px 7px);
}

/* -- dark mode ------------------------------------------------------------
 * Not an inversion. The paper becomes a cool near-black ink-blue and the text
 * a warm off-white, which preserves the warm/cool relationship of the light
 * theme instead of flattening it to grey-on-grey.
 *
 * Shadows are replaced outright: an inset top highlight stands in for the
 * top-light that a real raised surface catches, and a low-alpha light ring
 * supplies the edge. Both survive on a dark field where a drop shadow would be
 * invisible.
 *
 * Three states, one source of variables below (keep the two blocks
 * identical -- the second exists because CSS cannot share declarations
 * between a selector and a media query):
 *   no data-theme  -> follows the OS via the media query;
 *   data-theme="dark"  -> dark regardless of OS;
 *   data-theme="light" -> light regardless of OS (the media query excludes it).
 */
:root[data-theme="dark"] {
    --paper:       #13171e;
    --paper-raised:#1a1f28;
    --paper-sunk:  #0f1319;

    --ink:        #e7eaf0;
    --ink-strong: #ffffff;
    --ink-soft:   #b3bcc9;
    --ink-faint:  #8d97a5;

    --line:        #2c333e;
    --line-strong: #414b58;
    --line-faint:  #232a34;

    --brand:        #7ba7de;
    --brand-deep:   #a8c6ea;
    --brand-bright: #9dbce8;
    --brand-wash:   #16202e;
    --brand-line:   #2e4360;

    --brass:        #c0a163;
    --brass-bright: #d4b878;
    --brass-ink:    #c0a163;
    --on-brand:     #0b1118;

    /* Each background is desaturated toward the dark surface and each ink is
     * lifted until it clears 4.5:1 against its own background. These are not
     * the light values with a filter applied. */
    --ok-bg:   #16301f;  --ok-ink:   #93d9b1;  --ok-line:   #2f6b46;
    --warn-bg: #33260e;  --warn-ink: #e7c377;  --warn-line: #6f5620;
    --bad-bg:  #35191a;  --bad-ink:  #f0a79c;  --bad-line: #7a3a34;
    --mute-bg: #22272f;  --mute-ink: #b3bcc9;  --mute-line: #454e5a;

    --focus: #7aa7ff;

    --elev-1: inset 0 1px 0 rgba(255, 255, 255, 0.045);
    --elev-2: inset 0 1px 0 rgba(255, 255, 255, 0.06), 0 0 0 1px rgba(255, 255, 255, 0.05);
    --elev-3: inset 0 1px 0 rgba(255, 255, 255, 0.08),
              0 0 0 1px rgba(255, 255, 255, 0.07),
              0 16px 36px -16px rgba(0, 0, 0, 0.7);

    --tick: repeating-linear-gradient(90deg,
      var(--line-strong) 0 1px, transparent 1px 7px);

    /* Mesh twins: keep identical with the :not([data-theme="light"]) block. */
    --mesh-a: oklch(48.8% 0.243 264.4);
    --mesh-b: oklch(26% 0.09 258);
    --mesh-c: oklch(35% 0.15 260 / 0.4);
    --mesh-grid: rgba(255, 255, 255, 0.035);
    --mesh-card: rgba(7, 17, 31, 0.65);
    --mesh-card-line: rgba(255, 255, 255, 0.08);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --paper:       #13171e;
    --paper-raised:#1a1f28;
    --paper-sunk:  #0f1319;

    --ink:        #e7eaf0;
    --ink-strong: #ffffff;
    --ink-soft:   #b3bcc9;
    --ink-faint:  #8d97a5;

    --line:        #2c333e;
    --line-strong: #414b58;
    --line-faint:  #232a34;

    --brand:        #7ba7de;
    --brand-deep:   #a8c6ea;
    --brand-bright: #9dbce8;
    --brand-wash:   #16202e;
    --brand-line:   #2e4360;

    --brass:        #c0a163;
    --brass-bright: #d4b878;
    --brass-ink:    #c0a163;
    --on-brand:     #0b1118;

    /* Each background is desaturated toward the dark surface and each ink is
     * lifted until it clears 4.5:1 against its own background. These are not
     * the light values with a filter applied. */
    --ok-bg:   #16301f;  --ok-ink:   #93d9b1;  --ok-line:   #2f6b46;
    --warn-bg: #33260e;  --warn-ink: #e7c377;  --warn-line: #6f5620;
    --bad-bg:  #35191a;  --bad-ink:  #f0a79c;  --bad-line:  #7a3a34;
    --mute-bg: #22272f;  --mute-ink: #b3bcc9;  --mute-line: #454e5a;

    --focus: #7aa7ff;

    --elev-1: inset 0 1px 0 rgba(255, 255, 255, 0.045);
    --elev-2: inset 0 1px 0 rgba(255, 255, 255, 0.06), 0 0 0 1px rgba(255, 255, 255, 0.05);
    --elev-3: inset 0 1px 0 rgba(255, 255, 255, 0.08),
              0 0 0 1px rgba(255, 255, 255, 0.07),
              0 16px 36px -16px rgba(0, 0, 0, 0.7);

    --tick: repeating-linear-gradient(90deg,
      var(--line-strong) 0 1px, transparent 1px 7px);

    /* Mesh twins: keep identical with the :root[data-theme="dark"] block. */
    --mesh-a: oklch(48.8% 0.243 264.4);
    --mesh-b: oklch(26% 0.09 258);
    --mesh-c: oklch(35% 0.15 260 / 0.4);
    --mesh-grid: rgba(255, 255, 255, 0.035);
    --mesh-card: rgba(7, 17, 31, 0.65);
    --mesh-card-line: rgba(255, 255, 255, 0.08);
  }
}

/* ==========================================================================
   2. Base
   ========================================================================== */

* { box-sizing: border-box; }

html {
  scroll-behavior: smooth;
  /* Anchor targets are cleared by scroll-margin on the sections themselves; this
   * covers anything that is not one of them. */
  scroll-padding-top: 96px;
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  font: var(--step-0)/var(--leading-body) var(--font-sans);
  color: var(--ink);
  background: var(--paper);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  /* The paper is warm, so a purely flat field looks under-lit. A very soft
   * off-centre wash gives the top of the page a light source without becoming
   * a visible gradient. */
  background-image: radial-gradient(
    120% 60% at 50% -10%,
    color-mix(in srgb, var(--paper-raised) 70%, transparent) 0%,
    transparent 70%
  );
  background-attachment: fixed;
}

/* ==========================================================================
    3. Focus, motion, forced colors
   ========================================================================== */

:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}
/* Programmatic focus targets are focused by the browser (the skip link, the
 * in-page bucket anchors), which does not always match :focus-visible. They
 * must still show a ring, or focus appears to be lost. */
main:focus-visible, .bucket-detail:focus-visible { outline: 2px solid var(--focus); }
main:focus, .bucket-detail:focus { outline: none; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  /* Entrance animations are opacity-only and idempotent, so honouring
   * reduced-motion by snapping them to their end state is enough. */
  .reveal { opacity: 1 !important; transform: none !important; }
}

@media (forced-colors: active) {
  /* Elevation and tone washes are all discarded in forced-colors mode, so the
   * hairline has to come back or every panel boundary vanishes. (The valid
   * spelling is forced-colors; forced-colours is ignored by browsers.) */
  .button, .result, .bucket, .bucket-summary, .filter-bar, .panel {
    border: 1px solid CanvasText !important;
  }
  .badge { border: 1px solid CanvasText !important; }
}

/* ==========================================================================
   4. Layout primitives
   ==========================================================================
   * The site has three page archetypes and they must not share a layout:
   *
   *   marketing  -- hero-led, full shell, generous vertical rhythm
   *   prose      -- long-form legal copy, tight measure, centred, calm
   *   instrument -- the report, full shell, dense, data-first
   *
   * `main` holds the widest measure and each archetype constrains its own
   * top-level child. Doing it this way rather than switching a class on <body>
   * means a page cannot be left in the wrong archetype by forgetting to set
   * it: the fallback is the wide shell, which is the safe default.
 */

main {
  max-width: var(--shell);
  margin: 0 auto;
  padding: var(--s6) var(--s5) var(--s8);
}

/* Prose archetype. .doc is emitted by every long-form legal page. */
.doc {
  max-width: var(--measure);
  margin: 0 auto;
  padding-top: var(--s5);
}

/* Instrument archetype. .verify is the working page: a form that submits, a
 * progress region, then the report. The section stays shell-wide because the
 * report needs the width; the input half gets a console masthead instead, a
 * ruled title that separates "operate" from "read" before the report's own
 * tick rule arrives further down. */
.verify { padding-top: var(--s2); }
.verify > h1 {
  font-size: var(--step-4);
  letter-spacing: var(--tracking-tight);
  padding-bottom: var(--s3);
  border-bottom: 1px solid var(--line);
  margin-bottom: var(--s5);
}

/* ==========================================================================
   5. Chrome
   ========================================================================== */

.skip-link {
  position: absolute;
  left: var(--s3);
  top: var(--s3);
  z-index: 100;
  padding: var(--s2) var(--s4);
  background: var(--brand);
  color: var(--on-brand);
  border: 1px solid var(--brand-deep);
  border-radius: var(--radius);
  text-decoration: none;
  font-weight: var(--weight-semibold);
  font-size: var(--step--1);
  box-shadow: var(--elev-2);
  transform: translateY(-200%);
  transition: transform var(--dur) var(--ease-out);
}
.skip-link:focus { transform: translateY(0); }

.site-header {
  position: sticky;
  top: 0;
  z-index: 40;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s5);
  max-width: var(--shell);
  margin: 0 auto;
  padding: var(--s3) var(--s5);
  /* Translucent + blur so it floats over content without tearing the layout,
   * with a solid fallback behind it for engines without color-mix. */
  background: var(--paper);
  background: color-mix(in srgb, var(--paper) 86%, transparent);
  backdrop-filter: saturate(1.6) blur(12px);
  -webkit-backdrop-filter: saturate(1.6) blur(12px);
}
/* The tick rule. A 5px band of fine marks directly under the header, reading as
 * the top edge of a bound volume. This is the ornament's first of two uses. */
.site-header::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: -1px;
  height: 5px;
  padding-left: var(--s5);
  background-image: var(--tick);
  opacity: 0.55;
  pointer-events: none;
}

.brand {
  display: flex;
  align-items: center;
  gap: var(--s3);
  font-family: var(--font-display);
  font-size: 20px;
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-snug);
  color: var(--ink);
  text-decoration: none;
  flex: 0 0 auto;
}
/* The mark. Two hairline rules are the brackets a citation lives inside; the
 * heavy tick between them is the verification. It is the product in 24 pixels,
 * it is drawn in currentColor so it inherits ink, focus and hover, and it needs
 * no asset request. */
.brand-mark {
  flex: 0 0 auto;
  width: 26px;
  height: 26px;
  color: var(--brand);
}
.brand-mark .brand-mark-tick {
  stroke: var(--brass);
  transition: stroke var(--dur) var(--ease);
}
.brand:hover .brand-mark .brand-mark-tick { stroke: var(--brass-bright); }
/* The rules stroke in currentColor (the brand blue) and only the tick is
 * overridden to brass, so the mark is two-tone without a second element. */
.brand-mark .brand-mark-rule { stroke: currentColor; }

.brand-word { line-height: 1; }

.brand-sub {
  font-family: var(--font-sans);
  font-size: var(--step--2);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--ink-faint);
  padding-left: var(--s3);
  border-left: 1px solid var(--line);
  align-self: center;
}

.site-header nav {
  display: flex;
  align-items: center;
  gap: var(--s5);
  flex-wrap: wrap;
}
.site-header nav a {
  position: relative;
  /* A 44px hit area: nav links otherwise stand ~24px tall, which is a precise
   * tap target for no one. inline-flex centres the text in the taller box, and
   * the underline stays pinned to the box's bottom edge. */
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  color: var(--ink-soft);
  text-decoration: none;
  font-size: var(--step--1);
  font-weight: var(--weight-medium);
  letter-spacing: 0.002em;
  padding: var(--s1) 0;
  transition: color var(--dur-fast) var(--ease);
}
/* The underline grows from the leading edge rather than appearing, which reads
 * as a deliberate indication rather than a decoration. */
.site-header nav a::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 1.5px;
  background: var(--brand);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--dur) var(--ease-out);
}
.site-header nav a:hover { color: var(--ink); }
.site-header nav a:hover::after { transform: scaleX(1); }

/* Theme toggle: a quiet header action like sign-out, not a call. Hidden
 * until JS reveals it -- without JS there is nothing to toggle, and the OS
 * media query already picks the theme. Icons swap on the effective theme:
 * the explicit override and the OS-following state each need their rule. */
.theme-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  padding: 0 var(--s2);
  background: transparent;
  border: 1px solid transparent;
  border-radius: 999px;
  color: var(--ink-soft);
  cursor: pointer;
}
.theme-toggle:hover { color: var(--ink); border-color: var(--line); }
.theme-toggle:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.theme-toggle svg { width: 20px; height: 20px; display: block; }
.theme-toggle .icon-moon { display: none; }
:root[data-theme="dark"] .theme-toggle .icon-moon { display: block; }
:root[data-theme="dark"] .theme-toggle .icon-sun { display: none; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .theme-toggle .icon-moon { display: block; }
  :root:not([data-theme="light"]) .theme-toggle .icon-sun { display: none; }
}
/* Native chrome follows the override too, not just the OS. */
:root[data-theme="light"] { color-scheme: light; }
:root[data-theme="dark"] { color-scheme: dark; }

.nav-identity { font-size: var(--step--1); }
.nav-email {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  background: var(--paper-sunk);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 3px var(--s3);
  color: var(--ink-soft);
}
/* Inline with the identity pill: sign-out is a quiet action, not a call. */
.nav-logout { display: inline; margin-left: var(--s2); }
.nav-logout .button-quiet { font-size: var(--step--1); }

.site-footer {
  max-width: var(--shell);
  margin: 0 auto;
  padding: var(--s6) var(--s5) var(--s7);
  border-top: 1px solid var(--line);
}
.site-footer .limits {
  max-width: var(--measure);
  margin: 0;
  font-size: var(--step--1);
  line-height: 1.55;
  color: var(--ink-faint);
}
.footer-nav {
  display: flex;
  gap: var(--s5);
  flex-wrap: wrap;
  margin-top: var(--s4);
  padding-top: var(--s4);
  border-top: 1px solid var(--line-faint);
}
.footer-nav a {
  font-size: var(--step--1);
  color: var(--ink-soft);
  text-decoration: none;
  transition: color var(--dur-fast) var(--ease);
}
.footer-nav a:hover { color: var(--brand); text-decoration: underline; }

/* The colophon: the mark and wordmark again, small and quiet, closing the
 * document the way a printed volume carries its imprint on the last page. */
.colophon {
  display: flex;
  align-items: center;
  gap: var(--s2);
  margin: var(--s5) 0 0;
  font-family: var(--font-display);
  font-size: var(--step--1);
  letter-spacing: 0.02em;
  color: var(--ink-faint);
}
.colophon-mark { width: 15px; height: 15px; color: var(--ink-faint); }

/* ==========================================================================
   6. Typography
   ==========================================================================
   * Display type is set in the serif and tracked tight. The UA default ladder
   * puts h1 at 2em and h2 at 1.5em, a jump small enough that nothing reads as a
   * section break -- which is what made every page read as a form rather than a
   * document. Each level also carries weight and colour, so the hierarchy
   * survives greyscale, zoom to 200%, and a monochrome print.
 */

h1, h2, h3 {
  font-family: var(--font-display);
  color: var(--ink);
  text-wrap: balance;
}

h1 {
  font-size: var(--step-5);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-tight);
  font-weight: var(--weight-semibold);
  margin: 0 0 var(--s4);
}
h2 {
  font-size: var(--step-3);
  line-height: var(--leading-snug);
  letter-spacing: var(--tracking-snug);
  font-weight: var(--weight-semibold);
  margin: var(--s7) 0 var(--s3);
  padding-bottom: var(--s2);
  border-bottom: 1px solid var(--line);
}
h3 {
  font-size: var(--step-1);
  line-height: var(--leading-snug);
  font-weight: var(--weight-semibold);
  margin: var(--s5) 0 var(--s2);
}
/* h4 is a label, not a heading: small-caps tracked, ink-faint, no size step.
   It sits above panels and quote blocks rather than in the document outline. */
h4 {
  font-family: var(--font-sans);
  font-size: var(--step--2);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
  font-weight: var(--weight-bold);
  color: var(--ink-faint);
  margin: var(--s4) 0 var(--s2);
}

p { margin: 0 0 var(--s4); }

.lede {
  font-family: var(--font-display);
  font-size: var(--step-2);
  line-height: 1.5;
  color: var(--ink-soft);
  max-width: 34ch;
  margin: 0 0 var(--s6);
  /* Optically, a serif at display size wants slightly more leading than the
   * body ladder gives it. */
  letter-spacing: 0;
}
.muted { color: var(--ink-faint); font-size: var(--step--1); }

code {
  font-family: var(--font-mono);
  font-size: 0.88em;
  background: var(--paper-sunk);
  border: 1px solid var(--line-faint);
  padding: 1px 5px;
  border-radius: var(--radius-sm);
  color: var(--ink);
}

strong { font-weight: var(--weight-semibold); }

blockquote {
  font-family: var(--font-display);
  font-size: var(--step-1);
  line-height: 1.5;
}

/* ==========================================================================
   7. Buttons
   ========================================================================== */

.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s2);
  padding: 11px 22px;
  border-radius: var(--radius);
  background: var(--brand);
  color: var(--on-brand);
  text-decoration: none;
  border: 1px solid var(--brand-deep);
  cursor: pointer;
  font-family: var(--font-sans);
  font-size: var(--step-0);
  font-weight: var(--weight-semibold);
  letter-spacing: -0.002em;
  box-shadow: var(--elev-1);
  transition: background var(--dur-fast) var(--ease),
    border-color var(--dur-fast) var(--ease),
    box-shadow var(--dur) var(--ease-out),
    transform var(--dur-fast) var(--ease);
}
.button:hover {
  background: var(--brand-bright);
  border-color: var(--brand);
  box-shadow: var(--elev-2);
}
.button:active { transform: translateY(1px); box-shadow: none; }
.button[disabled] {
  opacity: 0.5;
  cursor: progress;
  transform: none;
  box-shadow: none;
}

/* The secondary action. Quiet enough to sit beside the primary without
 * competing, but still a real control with a visible edge. */
.button-quiet {
  background: transparent;
  color: var(--ink);
  border-color: var(--line-strong);
  box-shadow: none;
}
.button-quiet:hover {
  background: var(--paper-sunk);
  border-color: var(--ink-soft);
  color: var(--ink);
  box-shadow: var(--elev-1);
}

.button-row {
  display: flex;
  gap: var(--s3);
  flex-wrap: wrap;
  align-items: center;
}

/* ==========================================================================
   8. Forms
   ========================================================================== */

label {
  display: block;
  font-size: var(--step--1);
  font-weight: var(--weight-semibold);
  letter-spacing: 0.01em;
  color: var(--ink);
  margin: var(--s4) 0 var(--s2);
}

input[type="text"], input[type="email"], input[type="file"], textarea, select {
  width: 100%;
  padding: 10px 12px;
  font-family: var(--font-sans);
  /* 16px floor: anything smaller makes iOS Safari zoom on focus, which
   * reflows the form under the user's fingers. */
  font-size: max(16px, var(--step-0));
  color: var(--ink);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  background: var(--paper-raised);
  box-shadow: inset 0 1px 2px rgba(58, 42, 20, 0.04);
  transition: border-color var(--dur-fast) var(--ease),
    box-shadow var(--dur-fast) var(--ease);
}
/* Honeypot trap: looks like a normal text field to bots, sits off-screen
 * for humans. NOT display:none (most bots skip hidden inputs, and some
 * check computed visibility). tabindex -1 and aria-hidden keep it out of
 * keyboard and screen-reader flows. */
.hp-trap {
  position: absolute;
  left: -9999px;
  top: auto;
  width: 1px;
  height: 1px;
  overflow: hidden;
}
input:focus-visible, textarea:focus-visible {  /* One ring system, not two: the global :focus-visible outline already draws
   * a 2px ring, so inputs tighten its offset against their own border and tint
   * the border to match, instead of stacking a second halo on top of it. */
  outline: 2px solid var(--focus);
  outline-offset: 1px;
  border-color: var(--focus);
  box-shadow: none;
}
input::placeholder, textarea::placeholder { color: var(--ink-faint); }

input[type="file"] {
  padding: var(--s3);
  background: var(--paper-sunk);
  font-size: var(--step--1);
  cursor: pointer;
}
/* The native file button, restyled to match the rest of the controls. */
input[type="file"]::file-selector-button {
  font-family: var(--font-sans);
  font-size: var(--step--1);
  font-weight: var(--weight-semibold);
  color: var(--ink);
  background: var(--paper-raised);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  padding: 5px 12px;
  margin-right: var(--s3);
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease);
}
input[type="file"]::file-selector-button:hover {
  background: var(--paper-sunk);
}

/* A brief is a citation-dense document, so the input that receives it is set
 * in mono at a comfortable measure. It reads as source material, which is what
 * it is. */
textarea {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  line-height: 1.65;
  resize: vertical;
  min-height: 220px;
}

.link-form, .verify-form { max-width: var(--measure); }
.link-form button, .verify-form button { margin-top: var(--s4); }
.field-hint { margin: var(--s2) 0 0; }

/* A fieldset with no border or legend chrome, used purely to give the two
 * alternative inputs a shared programmatic name. */
.input-choice { border: 0; margin: 0; padding: 0; }
.input-choice-legend {
  padding: 0;
  font-family: var(--font-sans);
  font-size: var(--step--2);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
  font-weight: var(--weight-bold);
  color: var(--ink-faint);
  margin-bottom: var(--s1);
}

.notice {
  border: 1px solid var(--ok-line);
  background: var(--ok-bg);
  color: var(--ok-ink);
  border-left: 3px solid var(--ok-ink);
  border-radius: var(--radius);
  padding: var(--s3) var(--s4);
  font-size: var(--step-0);
  margin: 0 0 var(--s5);
}

/* Error variant of .notice: same shape, danger tone. Used for sign-in link
 * failures (expired/used links), where the next step is requesting a fresh
 * link rather than reading a report. */
.notice-error {
  border: 1px solid var(--bad-line);
  background: var(--bad-bg);
  color: var(--bad-ink);
  border-left: 3px solid var(--bad-ink);
  border-radius: var(--radius);
  padding: var(--s3) var(--s4);
  font-size: var(--step-0);
  margin: 0 0 var(--s5);
}

/* The privacy note is the most important sentence on the verify form and it was
 * a small grey line under a label. Framed as a panel so it reads as a
 * commitment rather than fine print. */
.privacy-note {
  margin: var(--s5) 0;
  padding: var(--s4);
  background: var(--paper-sunk);
  border: 1px solid var(--line);
  border-left: 3px solid var(--brand);
  border-radius: var(--radius);
  font-size: var(--step--1);
  line-height: 1.6;
  color: var(--ink-soft);
}
.privacy-note strong { color: var(--ink); }

/* ==========================================================================
   9. Panels and banners
   ========================================================================== */

/* A generic raised surface. Exists so that panels share one elevation recipe
 * rather than each redefining a shadow. */
.panel {
  background: var(--paper-raised);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--elev-1);
}

/* The base rule is the safe default: a banner means something was incomplete,
 * so it renders as "bad" until a specific kind says otherwise. Every BannerKind
 * has a rule below -- tests/test_banner_styles.py enforces that join, because a
 * missing rule degrades silently to this one. */
.banner {
  position: relative;
  border: 1px solid var(--bad-line);
  border-left: 3px solid var(--bad-ink);
  background: var(--bad-bg);
  color: var(--bad-ink);
  padding: var(--s4);
  border-radius: var(--radius);
  margin: 0 0 var(--s5);
  font-size: var(--step-0);
  line-height: 1.55;
  /* A short vertical rule at the leading edge, the convention for a callout in
   * a printed report. */
  box-shadow: var(--elev-1);
}
.banner .muted {
  display: block;
  margin-top: var(--s2);
  color: inherit;
  opacity: 0.86;
  font-size: var(--step--1);
}
/* Extraction problems: the document itself is suspect, so the record covers less
 * than the reader may assume. Bad, not warn. */
.banner-extraction_failed,
.banner-partial_extraction,
.banner-zero_citations {
  border-color: var(--bad-line);
  border-left-color: var(--bad-ink);
  background: var(--bad-bg);
  color: var(--bad-ink);
}
/* A budget or availability problem, not a problem with the brief. Warn. */
.banner-degraded_quota {
  border-color: var(--warn-line);
  border-left-color: var(--warn-ink);
  background: var(--warn-bg);
  color: var(--warn-ink);
}

/* ==========================================================================
   10. Marketing archetype
   ==========================================================================
   * The hero is the only place on the site allowed to be theatrical, and even
   * here the drama is typographic rather than decorative: a display serif at
   * 54px against a wide measure, on a panel that reads as a printed card. There
   * is no illustration and no gradient mesh, because a legal tool that looks
   * like a startup landing page has already lost the argument.
 */

.hero {
  position: relative;
  margin-bottom: var(--s7);
  padding: var(--s7) var(--s6) var(--s6);
  background: var(--paper-raised);
  border: 1px solid var(--line);
  border-radius: var(--radius-xl);
  box-shadow: var(--elev-2);
  overflow: hidden;
}
/* Light from the upper left, as if the card were lit. Subtle enough that the
 * fill is not perceptible as a fill. */
.hero::before {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(90% 70% at 12% 0%,
    color-mix(in srgb, var(--brand-wash) 85%, transparent) 0%,
    transparent 62%);
  pointer-events: none;
}
/* The hero mark, at a size where its construction is legible: two bracket rules
 * and a brass tick. Positioned behind the copy at low contrast so it reads as
 * a watermark on the printed card. */
.hero::after {
  content: "";
  position: absolute;
  top: var(--s6);
  right: var(--s6);
  width: 168px;
  height: 168px;
  opacity: 0.09;
  color: var(--brand);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%231c3f6e' stroke-width='1.1'%3E%3Cpath d='M7 3v18'/%3E%3Cpath d='M17 3v18'/%3E%3Cpath d='M9.5 12.5l2.5 2.5 5-6' stroke-width='1.6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-size: contain;
  pointer-events: none;
}
@media (prefers-color-scheme: dark) {
  /* The watermark is navy ink baked into a data URI. On the dark field it
   * sinks to nothing, so it is removed outright rather than shipped as an
   * invisible asset. Kept beside the component rather than in the token
   * block, where a component rule does not belong. The explicit-override
   * twin below must match it: one shows without the other and the hero
   * carries a ghost watermark in exactly one theme state. */
  :root:not([data-theme="light"]) .hero::after { display: none; }
}
:root[data-theme="dark"] .hero::after { display: none; }
/* Everything in the hero sits above the two washes. */
.hero > * { position: relative; z-index: 1; }

.hero-eyebrow {
  display: flex;
  align-items: center;
  gap: var(--s3);
  font-family: var(--font-sans);
  font-size: var(--step--2);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--brand);
  margin-bottom: var(--s4);
}
/* The leading rule: a short hairline that turns the eyebrow into a label on a
 * document rather than a floating caption. */
.hero-eyebrow::before {
  content: "";
  width: 28px;
  height: 1px;
  background: currentColor;
  flex: 0 0 auto;
}

.hero h1 {
  font-size: var(--step-6);
  line-height: 1.02;
  letter-spacing: -0.028em;
  max-width: 20ch;
  margin: 0 0 var(--s4);
  font-weight: var(--weight-semibold);
}
.hero .lede { max-width: 46ch; margin-bottom: var(--s6); }
.hero .button { box-shadow: var(--elev-2); }

/* Mesh hero (landing atmosphere). Layered washes behind the standard hero
 * copy: precision grid, atmospheric mesh, film grain. All colour comes from
 * --mesh-* tokens so light and dark render from the same markup; content
 * classes below must each keep a rule (the template-coverage test enforces
 * it). Decorative layers are pointer-events:none and aria-hidden in markup.
 * Static gradients only -- no animation, so reduced-motion needs nothing. */
.mhero {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius);
  background-color: var(--paper);
  padding: var(--s7) var(--s5);
  margin-bottom: var(--s7);
}
.mhero-mesh {
  position: absolute;
  inset: -20%;
  background:
    radial-gradient(circle at 50% 25%, var(--mesh-a) 0%, transparent 45%),
    radial-gradient(circle at 20% 45%, var(--mesh-b) 0%, transparent 50%),
    radial-gradient(circle at 80% 50%, var(--mesh-c) 0%, transparent 50%);
  opacity: 0.85;
  filter: blur(75px);
  pointer-events: none;
}
.mhero-grid {
  position: absolute;
  inset: 0;
  background-image:
    linear-gradient(to right, var(--mesh-grid) 1px, transparent 1px),
    linear-gradient(to bottom, var(--mesh-grid) 1px, transparent 1px);
  background-size: 32px 32px;
  mask-image: radial-gradient(ellipse 70% 60% at 50% 30%, black 20%, transparent 100%);
  -webkit-mask-image: radial-gradient(ellipse 70% 60% at 50% 30%, black 20%, transparent 100%);
  pointer-events: none;
}
.mhero-noise {
  position: absolute;
  inset: 0;
  opacity: 0.04;
  filter: url(#mhero-noise-filter);
  pointer-events: none;
}
.mhero-content {
  position: relative;
  max-width: 800px;
  margin: 0 auto;
  text-align: center;
}
.mhero-content h1 {
  font-size: clamp(2.5rem, 6vw, 4.25rem);
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: -0.03em;
  color: var(--ink);
  max-width: 20ch;
  margin: 0 auto var(--s4);
}
.mhero-content .lede { max-width: 46ch; margin: 0 auto var(--s6); }
.mhero-content .button-row { justify-content: center; }
.mhero-pill {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  padding: 6px 14px;
  border-radius: 999px;
  background: var(--mesh-card);
  border: 1px solid var(--mesh-card-line);
  color: var(--ink-soft);
  font-size: var(--step--2);
  font-weight: var(--weight-medium);
  letter-spacing: 0.02em;
  margin-bottom: var(--s5);
}
.mhero-pill-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background-color: var(--brand-bright);
  box-shadow: 0 0 8px var(--brand-bright);
}
.mhero-demobar {
  display: flex;
  align-items: center;
  gap: var(--s2);
  background: var(--mesh-card);
  border: 1px solid var(--mesh-card-line);
  border-radius: 10px;
  padding: 7px 7px 7px 16px;
  max-width: 580px;
  margin: 0 auto var(--s6);
  text-align: left;
}
.mhero-cite {
  flex: 1;
  min-width: 0;
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.mhero-card {
  position: relative;
  width: 100%;
  max-width: 680px;
  margin: var(--s4) auto 0;
  padding: 20px;
  border-radius: 12px;
  background: var(--mesh-card);
  border: 1px solid var(--mesh-card-line);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s4);
  text-align: left;
}
.mhero-card-top { display: flex; align-items: center; gap: 12px; }
.mhero-check {
  width: 32px;
  height: 32px;
  flex-shrink: 0;
  border-radius: 8px;
  background: var(--ok-bg);
  border: 1px solid var(--ok-line);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--ok-ink);
  font-size: 14px;
  font-weight: bold;
}
.mhero-card-title { color: var(--ink); font-weight: var(--weight-semibold); font-size: var(--step--1); margin-bottom: 3px; }
.mhero-card-sub { color: var(--ink-soft); font-size: var(--step--2); }
.mhero-tag {
  font-size: var(--step--2);
  font-family: var(--font-mono);
  color: var(--brand-bright);
  background: var(--brand-wash);
  padding: 4px 10px;
  border-radius: 4px;
  border: 1px solid var(--brand-line);
  white-space: nowrap;
}
@media (max-width: 640px) {
  .mhero { padding: var(--s6) var(--s4); }
  .mhero-demobar { flex-direction: column; align-items: stretch; text-align: center; }
  .mhero-card { flex-direction: column; align-items: flex-start; }
}

/* Entrance. Short, once, and opacity-led so it degrades to nothing under
 * reduced motion rather than leaving content stranded at opacity 0. */
.reveal { animation: reveal var(--dur-slow) var(--ease-out) both; }
.reveal-2 { animation-delay: 60ms; }
.reveal-3 { animation-delay: 120ms; }
@keyframes reveal {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}

.demos { margin-bottom: var(--s7); }

.demo-list {
  list-style: none;
  padding: 0;
  margin: var(--s4) 0 0;
  display: grid;
  gap: var(--s3);
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
}
/* Demo cards. A numbered tab on the leading edge, like the tabbed index of a
 * bound volume -- the third appearance of the volume motif. */
.demo-list li {
  position: relative;
  padding: var(--s5) var(--s4) var(--s4) var(--s5);
  background: var(--paper-raised);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--elev-1);
  transition: border-color var(--dur) var(--ease),
    box-shadow var(--dur) var(--ease-out),
    transform var(--dur) var(--ease-out);
}
.demo-list li::before {
  content: "";
  position: absolute;
  left: 0; top: var(--s4);
  width: 3px; height: 28px;
  border-radius: 0 2px 2px 0;
  background: var(--brand);
  opacity: 0.28;
  transition: opacity var(--dur) var(--ease), height var(--dur) var(--ease-out);
}
.demo-list li:hover {
  border-color: var(--line-strong);
  box-shadow: var(--elev-2);
  transform: translateY(-2px);
}
.demo-list li:hover::before { opacity: 1; height: 40px; }
.demo-list .muted { display: block; margin-top: var(--s2); }
.demo-link {
  display: block;
  font-family: var(--font-display);
  font-weight: var(--weight-semibold);
  font-size: var(--step-1);
  line-height: var(--leading-snug);
  letter-spacing: var(--tracking-snug);
  text-decoration: none;
  color: var(--ink);
}
.demo-link:hover { color: var(--brand); }

/* The data-flow disclosure. A definition list on paper, with the terms set in
 * small-caps so the eye can scan the categories without reading the prose. */
.disclosure {
  padding: var(--s5);
  background: var(--paper-sunk);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
}
.disclosure h2 { margin-top: 0; }
.disclosure dl { margin: 0; }
.disclosure dt {
  font-size: var(--step--2);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
  font-weight: var(--weight-bold);
  /* ink-soft, not ink-faint: the disclosure panel is sunk, and faint on sunk
   * is 4.22:1. The label reading survives on size and tracking alone. */
  color: var(--ink-soft);
  margin-top: var(--s4);
}
.disclosure dt:first-of-type { margin-top: 0; }
.disclosure dd {
  margin: var(--s1) 0 0;
  color: var(--ink-soft);
  max-width: var(--measure-wide);
}

/* ==========================================================================
   11. Prose archetype
   ==========================================================================
   * Long-form legal copy. Tighter measure than the marketing pages, and the
   * heading rules are dropped -- a hairline under every h2 turns a readable
   * contract into a stack of bars.
 */

.doc { max-width: var(--measure); }
.doc h2 { border-bottom: 0; padding-bottom: 0; }
.doc > p { font-family: var(--font-display); font-size: var(--step-1); line-height: 1.7; }
.doc ul, .doc ol { padding-left: var(--s5); }
.doc li { margin-bottom: var(--s2); color: var(--ink-soft); }
.doc li strong { color: var(--ink); }

.steps-prose { list-style: none; padding: 0; }
.steps-prose li {
  font-family: var(--font-display);
  font-size: var(--step-1);
  line-height: 1.6;
}

.qa { margin: 0; }
.qa dt {
  font-weight: var(--weight-semibold);
  color: var(--ink);
  margin-top: var(--s5);
}
.qa dd { margin: var(--s1) 0 0; color: var(--ink-soft); }

/* Share intents: quiet inline links, never buttons shouting for attention.
 * Plain anchors only -- no SDKs, no tracking pixels, same outbound shape
 * as the research links elsewhere on the page. */
.share-row { display: flex; flex-wrap: wrap; gap: var(--s2) var(--s3); align-items: baseline; margin-top: var(--s5); padding-top: var(--s4); border-top: 1px solid var(--line); }
.share-label { font-size: var(--step--2); text-transform: uppercase; letter-spacing: var(--tracking-wide); color: var(--ink-faint); font-weight: var(--weight-semibold); }
.share-link { font-size: var(--step-0); font-weight: var(--weight-semibold); padding: var(--s2) var(--s4); border: 1px solid var(--line-strong); border-radius: 999px; text-decoration: none; }
.share-link:hover { border-color: var(--brand-bright); }

/* Bottom-of-landing share call: a heading plus the same share row, no new
 * chrome. The row's own top border separates it from the FAQ above. */
.share-cta { margin-top: var(--s8); }
.share-cta h2 { margin-bottom: 0; }

/* Landing FAQ section: spacing only -- questions and answers reuse .qa. */
.faq { margin-top: var(--s8); }

/* Honest comparison table on /why-citeproof. Plain table semantics (th
 * scope) so screen readers and no-CSS readers get rows that make sense;
 * zebra striping is decoration only, never the carrier of meaning. */
.compare { width: 100%; border-collapse: collapse; margin: var(--s4) 0; font-size: var(--step--1); }
.compare th, .compare td { text-align: left; padding: var(--s2) var(--s3); border-bottom: 1px solid var(--line); vertical-align: top; }
.compare thead th { font-size: var(--step--2); text-transform: uppercase; letter-spacing: var(--tracking-wide); color: var(--ink-faint); }
.compare tbody tr:nth-child(even) { background: var(--paper-sunk); }

/* Wide tables (the /why-citeproof comparison) scroll horizontally on narrow
 * viewports instead of crushing columns or overflowing the page. tabindex +
 * region role make the scroller keyboard-reachable; the table keeps full
 * th scope semantics inside. */
.table-scroll { overflow-x: auto; margin: var(--s4) 0; border: 1px solid var(--line); border-radius: var(--radius); }
.table-scroll:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
/* Breakout: long-form pages set .doc to a 66ch prose measure, which a
 * five-column matrix can never fit. The scroller escapes to shell width
 * (centered via the 50%/translate pair, since percentages resolve against
 * the narrow parent) so desktop readers see every column with no scroll;
 * phones still scroll, which is the correct fallback there. The 2x margin
 * inside the min() covers the scrollbar inside 100vw, so the breakout
 * itself never causes page-level sideways scroll. */
.doc .table-scroll {
  width: min(calc(var(--shell) - 2 * var(--s5)), calc(100vw - 2 * var(--s5)));
  margin-left: 50%;
  transform: translateX(-50%);
}
.table-scroll .compare { margin: 0; border: 0; width: 100%; }
.table-scroll .compare th[scope="row"] { min-width: 13ch; }
.table-scroll .compare td { min-width: 15ch; }

.next-steps {
  list-style: none;
  padding: 0;
  margin: var(--s7) 0 0;
  display: grid;
  gap: var(--s4);
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  counter-reset: steps;
}
.next-steps li {
  display: flex;
  gap: var(--s3);
  align-items: flex-start;
  font-size: var(--step--1);
  color: var(--ink-soft);
  line-height: 1.5;
  margin: 0;
}
/* The step numeral. A brass-ringed figure, because this is the one place on
 * the site where a sequence is being explained. */
.step-num {
  flex: 0 0 auto;
  width: 24px; height: 24px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--brass);
  border-radius: 50%;
  font-family: var(--font-mono);
  font-size: var(--step--2);
  font-weight: var(--weight-bold);
  color: var(--brass-ink);
  font-variant-numeric: tabular-nums;
  background: var(--paper-raised);
}

/* ==========================================================================
   12. Tables
   ==========================================================================
   * Long tables scroll rather than clip on a narrow screen. `display: block`
   * with overflow-x is the only way to do that on a <table> without wrapping
   * every cell in a scroll container.
 */

.price-table, .info-table {
  width: 100%;
  border-collapse: collapse;
  margin: var(--s5) 0;
  font-size: var(--step--1);
  display: block;
  overflow-x: auto;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--paper-raised);
}
.price-table th, .price-table td,
.info-table th, .info-table td {
  text-align: left;
  vertical-align: top;
  padding: var(--s3) var(--s4);
  border-bottom: 1px solid var(--line-faint);
}
/* The header row is a label row: small-caps, tracked, and ruled beneath with
 * something firmer than the body hairlines. */
.price-table thead th, .info-table thead th {
  font-size: var(--step--2);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
  font-weight: var(--weight-bold);
  color: var(--ink-faint);
  background: var(--paper-sunk);
  border-bottom: 1px solid var(--line-strong);
  white-space: nowrap;
}
.price-table tbody th, .info-table tbody th {
  font-weight: var(--weight-semibold);
  color: var(--ink);
  white-space: nowrap;
}
.info-table tbody th { min-width: 150px; }
.price-table tbody tr:last-child th,
.price-table tbody tr:last-child td,
.info-table tbody tr:last-child th,
.info-table tbody tr:last-child td { border-bottom: 0; }
/* The price column, set in mono so it cannot be misread as body text and so
 * the digits align down the column. Scoped to the second cell only: the
 * third cell is prose ("What you get") and must stay proportional. */
.price-table tbody td:nth-child(2) { font-family: var(--font-mono); font-size: var(--step--1); }

/* The signup form inside a pricing cell. */
.price-table td form { margin: var(--s3) 0 0; }

.pricing { max-width: var(--shell); }
.pricing > .muted {
  max-width: var(--measure);
  margin-top: var(--s6);
  padding-top: var(--s4);
  border-top: 1px solid var(--line-faint);
}

/* ==========================================================================
   13. Sign-in
   ========================================================================== */

.signin { max-width: var(--measure); }
.signin-panel {
  margin: var(--s5) 0 0;
  padding: var(--s6);
  background: var(--paper-raised);
  border: 1px solid var(--line);
  border-radius: var(--radius-xl);
  box-shadow: var(--elev-2);
}
.signin-panel input[type="email"] { width: 100%; max-width: 420px; }

/* ==========================================================================
   14. The report: counts, coverage, summary
   ========================================================================== */

/* The counts line is the reader's first orientation: did the tool read the
 * whole document? Set as a display figure so it cannot be skimmed past. */
.counts {
  font-family: var(--font-mono);
  font-size: var(--step-2);
  font-weight: var(--weight-bold);
  letter-spacing: -0.01em;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  margin: 0 0 var(--s3);
}

/* How the answer was produced. "42 citations checked" reads like 42 live
 * queries; stating the cache split is the difference between a claim the
 * reader can audit and one they must take on faith. */
.coverage {
  list-style: none;
  padding: var(--s3) 0;
  margin: 0 0 var(--s4);
  display: flex;
  flex-wrap: wrap;
  gap: var(--s5);
  font-size: var(--step--2);
  color: var(--ink-faint);
  border-top: 1px solid var(--line-faint);
  border-bottom: 1px solid var(--line-faint);
}
.coverage li { display: flex; align-items: baseline; gap: var(--s2); }
.coverage-count {
  font-family: var(--font-mono);
  font-size: var(--step-0);
  font-weight: var(--weight-bold);
  color: var(--brand);
  font-variant-numeric: tabular-nums;
}

.snapshot {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  color: var(--ink-faint);
  margin: 0 0 var(--s5);
  letter-spacing: 0.01em;
}

.caveat {
  margin: 0 0 var(--s5);
  padding: var(--s3) var(--s4);
  border-left: 3px solid var(--warn-ink);
  background: var(--warn-bg);
  color: var(--warn-ink);
  border-radius: 0 var(--radius) var(--radius) 0;
  font-size: var(--step--1);
  line-height: 1.55;
}

/* -- bucket summary --------------------------------------------------------
 * Sticky: on a 40-citation report the counts scroll away within a screen, and
 * a reader working through "needs attention" loses track of how much is left.
 * It condenses rather than disappearing -- the counts are the orientation, so
 * they never leave. */

.bucket-summary {
  position: sticky;
  /* 76px clears the sticky header, whose 44px nav targets made it taller than
   * the 64px this used to assume. */
  top: 76px;
  z-index: 30;
  list-style: none;
  padding: var(--s2);
  margin: var(--s5) 0;
  display: grid;
  gap: var(--s2);
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  background: color-mix(in srgb, var(--paper) 92%, transparent);
  backdrop-filter: saturate(1.5) blur(10px);
  -webkit-backdrop-filter: saturate(1.5) blur(10px);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--elev-2);
}
.bucket {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--paper-raised);
  overflow: hidden;
}
.bucket-link {
  display: block;
  padding: var(--s3) var(--s4);
  color: inherit;
  text-decoration: none;
  transition: background var(--dur-fast) var(--ease);
}
.bucket-link:hover { background: color-mix(in srgb, currentColor 7%, transparent); }
.bucket-link-static { cursor: default; }
.bucket-count {
  display: block;
  font-family: var(--font-display);
  font-size: 32px;
  font-weight: var(--weight-bold);
  line-height: 1;
  letter-spacing: var(--tracking-tight);
  font-variant-numeric: tabular-nums;
}
.bucket-label {
  display: block;
  margin-top: var(--s2);
  font-size: var(--step--2);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
  font-weight: var(--weight-bold);
}
.bucket-hint {
  display: block;
  margin-top: var(--s2);
  font-size: var(--step--2);
  color: var(--ink-faint);
}

.bucket-ok   { border-color: var(--ok-line);   background: var(--ok-bg); }
.bucket-warn { border-color: var(--warn-line); background: var(--warn-bg); }
.bucket-mute { border-color: var(--mute-line); background: var(--mute-bg); }
.bucket-ok   .bucket-count, .bucket-ok   .bucket-label { color: var(--ok-ink); }
.bucket-warn .bucket-count, .bucket-warn .bucket-label { color: var(--warn-ink); }
.bucket-mute .bucket-count, .bucket-mute .bucket-label { color: var(--mute-ink); }

/* An empty bucket is dimmed rather than hidden: "0 couldn't check" is
 * information, and an absent tile invites the reader to assume it was not
 * counted. */
.bucket-empty {
  background: var(--paper);
  border-style: dashed;
}
.bucket-empty .bucket-count { color: var(--ink-faint); }

/* -- filter bar ------------------------------------------------------------ */

.filter-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s2);
  margin: var(--s5) 0;
  padding: var(--s3);
  background: var(--paper-sunk);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.filter-label {
  font-size: var(--step--2);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
  font-weight: var(--weight-bold);
  /* Same sunk-panel math as .disclosure dt: faint would be 4.22:1 here. */
  color: var(--ink-soft);
  margin-right: var(--s2);
}
.filter {
  font-family: var(--font-sans);
  font-size: var(--step--1);
  font-weight: var(--weight-medium);
  padding: 5px var(--s3);
  cursor: pointer;
  background: var(--paper-raised);
  color: var(--ink-soft);
  border: 1px solid var(--line);
  border-radius: 999px;
  transition: background var(--dur-fast) var(--ease),
    color var(--dur-fast) var(--ease),
    border-color var(--dur-fast) var(--ease);
}
.filter:hover { border-color: var(--line-strong); color: var(--ink); }
/* Selected state uses weight, fill and border together -- never colour alone. */
.filter.is-active {
  background: var(--ink);
  color: var(--paper);
  border-color: var(--ink);
  font-weight: var(--weight-semibold);
}
.filter-count {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  margin-left: auto;
  /* Sunk panel again: faint is 4.22:1 against it, soft is 7.53:1. */
  color: var(--ink-soft);
}

/* ==========================================================================
   15. The report: sections and results
   ==========================================================================
   * The bucket section is deliberately untoned. The summary tiles carry the
   * colour; a full-width tinted band behind a long citation list reads as one
   * giant alert and loses the per-result signal entirely.
 */

.record {
  margin-top: var(--s7);
  position: relative;
  padding-top: var(--s5);
}
/* The second and final use of the tick rule: the report is a document that
 * begins here, and it gets the same edge as the site header. */
.record::before {
  content: "";
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 5px;
  background-image: var(--tick);
  opacity: 0.55;
  pointer-events: none;
}

.bucket-detail {
  margin: var(--s7) 0 0;
  /* Clears the sticky header and the sticky summary, so "jump to section"
   * cannot park a heading underneath the chrome it is meant to orient from.
   * Sized for the 44px-nav header plus the summary tile row. */
  scroll-margin-top: 160px;
}
.demos {
  /* Anchor target of the nav "Examples" link: same clearance treatment. */
  scroll-margin-top: 160px;
}
.bucket-detail h2 { margin: 0 0 var(--s1); }
.bucket-note {
  margin: 0 0 var(--s4);
  font-size: var(--step--1);
  color: var(--ink-soft);
  max-width: var(--measure-wide);
}
.bucket-note.warn { color: var(--warn-ink); }
.bucket-note.ok   { color: var(--ok-ink); }
.bucket-note.mute { color: var(--mute-ink); }

/* Each result is a card with its own tone rail, so a long report reads as a
 * sequence of discrete findings rather than one undifferentiated list. The
 * badge stays the first and loudest element inside it. */

.result {
  position: relative;
  margin-top: var(--s3);
  padding: var(--s4) var(--s5);
  padding-left: calc(var(--s5) + 3px);
  background: var(--paper-raised);
  border: 1px solid var(--line);
  border-left: 3px solid var(--line-strong);
  border-radius: var(--radius-lg);
  box-shadow: var(--elev-1);
  transition: border-color var(--dur) var(--ease),
    box-shadow var(--dur) var(--ease-out);
}
.result:hover {
  border-color: var(--line-strong);
  box-shadow: var(--elev-2);
}
/* Tone rail. The badge already states the status in words, so this is a second
 * cue rather than the only one -- colour stays non-load-bearing. */
.result[data-tone="ok"]   { border-left-color: var(--ok-ink); }
.result[data-tone="warn"] { border-left-color: var(--warn-ink); }
.result[data-tone="mute"] { border-left-color: var(--mute-ink); }

.result-head {
  display: flex;
  gap: var(--s3);
  align-items: baseline;
  flex-wrap: wrap;
}
/* Labels inside a result -- "Brief claims", "Quote as written" -- are headings
 * in the document outline (they were h4 under an h2, which skipped a level, so
 * they are now h3) but they are labels in the visual design. Scoping the
 * small-caps treatment to .result reconciles the two: the outline is valid and
 * the page still reads as annotated findings rather than nested prose. */
.result h3 {
  font-family: var(--font-sans);
  font-size: var(--step--2);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
  font-weight: var(--weight-bold);
  color: var(--ink-faint);
  margin: var(--s4) 0 var(--s2);
}
/* A citation string is a reference, not prose: mono, and set at the size of
 * body text so it is legible rather than decorative. */
.cite {
  font-family: var(--font-mono);
  font-weight: var(--weight-bold);
  font-size: var(--step-1);
  letter-spacing: -0.015em;
  line-height: 1.4;
  color: var(--ink);
}
/* A resolved citation links to its opinion. Same type as .cite, underlined
 * in brand so the link reads as navigation rather than a verdict restyle;
 * colour is never the signal (the badge beside it is). */
a.cite-link { color: var(--brand-bright); text-decoration: underline; text-underline-offset: 2px; }
a.cite-link:hover { color: var(--brand); }
/* Formatting flags: muted assistance under the citation, never competing
 * with the verdict badge. The code pill names the rule class; the sentence
 * says what to check. */
.format-notes { list-style: none; margin: var(--s2) 0 0; padding: 0; }
.format-notes li { font-size: var(--step--1); color: var(--ink-soft); margin: var(--s1) 0; }
.format-code {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  background: var(--paper-sunk);
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: 1px 6px;
  margin-right: var(--s2);
  white-space: nowrap;
}
.detail {
  margin: var(--s3) 0 0;
  color: var(--ink-soft);
  font-size: var(--step-0);
  line-height: 1.6;
}

/* The badge. Inverted against its own tone so it reads as a stamped status
 * rather than a soft chip, which is what a lawyer scanning a report expects. */
.badge {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  font-weight: var(--weight-bold);
  letter-spacing: 0.06em;
  padding: 3px 8px;
  border: 1px solid;
  border-radius: var(--radius-sm);
  white-space: nowrap;
  flex: 0 0 auto;
  align-self: flex-start;
}
.badge-ok   { background: var(--ok-bg);   color: var(--ok-ink);   border-color: var(--ok-line); }
.badge-warn { background: var(--warn-bg); color: var(--warn-ink); border-color: var(--warn-line); }
.badge-bad  { background: var(--bad-bg);  color: var(--bad-ink);  border-color: var(--bad-line); }
.badge-mute { background: var(--mute-bg); color: var(--mute-ink); border-color: var(--mute-line); }

/* Claim metadata: what the engine actually extracted from the sentence, so a
 * reader can tell "no year found" apart from "year found and it was wrong". */
.claim-meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s2);
  font-size: var(--step--2);
  color: var(--ink-faint);
  width: 100%;
  margin-top: var(--s1);
}
.cite-type {
  font-family: var(--font-mono);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-weight: var(--weight-bold);
  color: var(--brand);
}
.hint {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  padding: 1px 6px;
  background: var(--paper-sunk);
  border: 1px solid var(--line-faint);
  border-radius: var(--radius-sm);
  color: var(--ink-soft);
}

/* Which case the citation actually resolved to. This is the answer the reader
 * came for, so it sits directly under the detail line rather than behind a
 * disclosure: a badge alone says "passed" without saying what passed. */
.resolved {
  margin: var(--s3) 0 0;
  padding: var(--s2) var(--s3);
  background: var(--paper-sunk);
  border-radius: var(--radius-sm);
  font-size: var(--step--1);
  line-height: 1.5;
  display: flex;
  flex-wrap: wrap;
  gap: var(--s2);
  align-items: baseline;
}
.resolved-label {
  font-size: var(--step--2);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
  font-weight: var(--weight-bold);
  color: var(--ink-faint);
}
.resolved-name {
  font-family: var(--font-display);
  font-size: var(--step-1);
  font-weight: var(--weight-semibold);
  color: var(--ink);
}
.resolved-court, .resolved-date {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  color: var(--ink-soft);
}
.resolved-date::before {
  content: "\00b7";
  margin-right: var(--s2);
  color: var(--line-strong);
}

/* Subsequent history, honestly framed. The box must read as a next step,
 * never as a verdict: the title states what CiteProof does not decide, and
 * the note points subscribers at the citators that do. Muted tokens (not
 * warn) so an answered question does not borrow the urgency of a flag. */
.goodlaw {
  margin-top: var(--s4);
  padding: var(--s4);
  background: var(--paper-sunk);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  border-left: 4px solid var(--line-strong);
}
.goodlaw-title { margin-top: 0; font-weight: var(--weight-semibold); }
.goodlaw-links { list-style: none; padding: 0; margin: var(--s2) 0; }
.goodlaw-links li { margin: var(--s1) 0; font-size: var(--step--1); }

/* Partner sign-off. The panel counts flags reviewed; each item carries a
 * native checkbox so the workflow survives no-JS (tick and print) while the
 * script adds counting, memory and the copyable log. Warn tokens, because
 * this panel governs the flags, not the furniture. */
.review-panel {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s2) var(--s4);
  align-items: center;
  margin: var(--s3) 0 0;
  padding: var(--s3) var(--s4);
  background: var(--warn-bg);
  border: 1px solid var(--warn-line);
  border-radius: var(--radius);
}
.review-progress { margin: 0; font-weight: var(--weight-semibold); color: var(--warn-ink); }
.review-check {
  display: flex;
  gap: var(--s2);
  align-items: baseline;
  margin-top: var(--s3);
  padding-top: var(--s3);
  border-top: 1px dashed var(--line);
  cursor: pointer;
}
.review-box { width: 1.1em; height: 1.1em; accent-color: var(--warn-ink); flex: none; }
.review-label { font-size: var(--step--1); color: var(--ink-soft); }

/* Ambiguous: more than one case fits. Listing them is the difference between
 * "we don't know" and "it is one of these three, pick". */
.candidates {
  margin-top: var(--s4);
  padding: var(--s4);
  background: var(--warn-bg);
  border: 1px solid var(--warn-line);
  border-radius: var(--radius);
}
.candidates h3 { margin-top: 0; color: var(--warn-ink); }
.candidate-list { list-style: none; padding: 0; margin: 0; }
.candidate-list li {
  padding: var(--s2) 0;
  font-size: var(--step--1);
  display: flex;
  flex-wrap: wrap;
  gap: var(--s3);
  align-items: baseline;
}
.candidate-list li + li { border-top: 1px solid var(--warn-line); }
.candidate-name {
  font-family: var(--font-display);
  font-weight: var(--weight-semibold);
  color: var(--ink);
}

/* Quote comparison. Two columns on a wide shell, because a diff you have to
 * read vertically is not a diff. */
.side-by-side {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--s5);
  margin-top: var(--s4);
  padding: var(--s4);
  background: var(--paper-sunk);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.side-by-side h3 { margin-top: 0; }
.side-by-side p { margin: 0; }
.side-by-side blockquote {
  margin: 0;
  padding-left: var(--s3);
  border-left: 2px solid var(--line-strong);
  font-size: var(--step-0);
  line-height: 1.55;
  color: var(--ink);
}
.pin {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  color: var(--brand);
  font-weight: var(--weight-bold);
  letter-spacing: 0.02em;
}
/* Passage-match confidence: neutral chrome beside the finding, never a
 * second verdict signal. Muted by design; the guardrail list below it
 * carries the reasons. */
.quote-confidence {
  font-size: var(--step--2);
  font-weight: var(--weight-normal);
  color: var(--ink-soft);
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: 1px 6px;
  margin-left: var(--s2);
  white-space: nowrap;
}

/* Guardrails: why a quote did not verify. The engine already decided this --
 * naming the reasons turns a diagnosable failure into something the reader can
 * act on. */
.guardrails {
  list-style: none;
  padding: 0;
  margin: var(--s4) 0 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--s2);
}
.guardrail {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  line-height: 1.4;
  padding: 3px var(--s2);
  border: 1px solid var(--warn-line);
  background: var(--warn-bg);
  color: var(--warn-ink);
  border-radius: var(--radius-sm);
}

/* Semantic change: a word the opinion has and the brief does not, or the other
 * way round. The strongest evidence the tool can produce, because it inverts
 * the meaning of the quote while still scoring ~95% similar. Rendered as a
 * diff because that is what it is, and in the worst tone on the page: this is
 * the one finding a reader must not skim past. */
.semantic-change {
  margin-top: var(--s4);
  padding: var(--s4);
  border: 1px solid var(--bad-line);
  border-left: 3px solid var(--bad-ink);
  background: var(--bad-bg);
  border-radius: var(--radius);
}
.semantic-change h3 { margin-top: 0; color: var(--bad-ink); }
.diff-line {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--step--1);
  display: flex;
  flex-wrap: wrap;
  gap: var(--s2);
}
.diff-quoted {
  background: transparent;
  color: var(--bad-ink);
  border: 1px solid var(--bad-line);
  border-radius: var(--radius-sm);
  padding: 1px var(--s2);
  text-decoration: line-through;
  text-decoration-thickness: 1.5px;
}
.diff-actual {
  background: var(--ok-bg);
  color: var(--ok-ink);
  border: 1px solid var(--ok-line);
  border-radius: var(--radius-sm);
  padding: 1px var(--s2);
  font-weight: var(--weight-bold);
}
.semantic-change .muted {
  display: block;
  margin-top: var(--s3);
  color: inherit;
  opacity: 0.9;
}
.semantic-change code {
  background: transparent;
  border: 0;
  padding: 0 2px;
  color: inherit;
  font-weight: var(--weight-bold);
}

.quote-score {
  margin: var(--s3) 0 0;
  font-family: var(--font-mono);
  font-size: var(--step--2);
  color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
}

.disclaimer {
  margin-top: var(--s7);
  padding: var(--s4) var(--s5);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  font-size: var(--step--1);
  line-height: 1.6;
  color: var(--ink-soft);
  background: var(--paper-sunk);
}

/* ==========================================================================
   16. Progress
   ========================================================================== */

.progress {
  margin: var(--s6) 0;
  padding: var(--s5);
  background: var(--paper-raised);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--elev-2);
}
.progress .status { margin: var(--s2) 0 0; }
.progress .status[data-stage] {
  font-size: var(--step--1);
  color: var(--ink-soft);
}

/* An indeterminate sweep in brass. Determinacy is unavailable -- the server
 * reports stages, not percentages -- so this signals "working" and nothing
 * more, which is the honest thing for it to claim. */
.progress-bar {
  position: relative;
  height: 3px;
  background: var(--paper-sunk);
  border-radius: 999px;
  overflow: hidden;
  margin-top: var(--s4);
}
.progress-bar::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg,
    transparent 0%, var(--brass) 45%, var(--brass-bright) 50%, var(--brass) 55%, transparent 100%);
  animation: sweep 1.6s var(--ease) infinite;
}
.progress[data-done="1"] .progress-bar::after { display: none; }
@keyframes sweep {
  from { transform: translateX(-100%); }
  to   { transform: translateX(100%); }
}

/* Findings so far. A plain list, not a table: they are provisional, and a table
 * would imply a settled result the run has not reached. */
.partial-results {
  list-style: none;
  padding: var(--s4) 0 0;
  margin: var(--s5) 0 0;
  border-top: 1px solid var(--line-faint);
  display: grid;
  gap: var(--s2);
}
.partial-result {
  display: flex;
  gap: var(--s3);
  align-items: baseline;
  flex-wrap: wrap;
  font-size: var(--step--1);
  animation: reveal var(--dur) var(--ease-out) both;
}

/* ==========================================================================
   17. Responsive
   ==========================================================================
   * Breakpoints are set where the layout actually breaks rather than at device
   * names. 640px is where the side-by-side diff stops being readable and the
   * sticky header starts colliding with the nav.
 */

@media (max-width: 900px) {
  .side-by-side { grid-template-columns: 1fr; gap: var(--s4); }
  /* Tracks the taller 44px-nav header, like the desktop values above. */
  .bucket-summary { top: 70px; }
  .bucket-detail { scroll-margin-top: 152px; }
}

@media (max-width: 640px) {
  main { padding: var(--s5) var(--s4) var(--s7); }

  h1 { font-size: var(--step-4); }
  .hero { padding: var(--s5) var(--s4) var(--s5); }
  .hero h1 { font-size: var(--step-4); }
  .hero::after { display: none; }

  .site-header {
    flex-wrap: wrap;
    gap: var(--s2);
    padding: var(--s3) var(--s4);
  }
  .site-header nav { gap: var(--s4); font-size: var(--step--1); }
  .brand-sub { display: none; }
  .site-footer { padding: var(--s5) var(--s4) var(--s6); }

  .bucket-count { font-size: 26px; }
  .result { padding: var(--s4); padding-left: calc(var(--s4) + 3px); }
  .bucket-summary { grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); }
  .coverage { gap: var(--s4); }
  .signin-panel { padding: var(--s5) var(--s4); }
}

/* ==========================================================================
   18. Print
   ==========================================================================
   * Lawyers print reports and staple them to filings, so this is a real target
   * rather than an afterthought. Everything that carries meaning in colour also
   * carries it in a text badge, so dropping the washes costs nothing: the page
   * stays fully readable in monochrome.
 */

@media print {
  /* The page field goes white and the marks go black, because ink on paper is
   * what the reader is about to put on paper. */
  body {
    background: #fff;
    color: #000;
    font-size: 10.5pt;
  }
  .site-header, .site-footer, .skip-link, .filter-bar, .button-row { display: none !important; }
  /* A filtered view must never print as a complete report: the filter bar
   * (which carries the "N of M shown" qualifier) is hidden above, so hidden
   * results are forced back on for print. The printed report is always whole. */
  .result[hidden], .bucket-detail[hidden] { display: block !important; }

  main { max-width: none; padding: 0; }
  .doc { max-width: none; }

  .site-header::after, .record::before { display: none; }

  /* Shadows and washes are dropped; the hairline becomes the boundary. */
  .result, .bucket, .banner, .panel, .disclosure, .side-by-side,
  .candidates, .semantic-change, .privacy-note, .signin-panel,
  .progress, .hero, .demo-list li, .price-table, .info-table {
    box-shadow: none !important;
    background: transparent !important;
  }
  .result, .bucket, .banner, .panel, .side-by-side, .candidates,
  .semantic-change, .disclosure {
    border: 1pt solid #000 !important;
  }
  .badge, .cite, .coverage-count { color: #000 !important; }
  .semantic-change, .banner, .candidates { border-left-width: 3pt !important; }

  /* Avoid a result being split across two sheets. */
  .result, .bucket-detail, .banner { break-inside: avoid; page-break-inside: avoid; }
  .bucket-detail { break-before: auto; }

  /* The browser's own link printing adds "(url)" after every link, which turns
   * a report into noise. The URLs that matter are in the metadata block. */
  a[href^="http"]::after { content: "" !important; }
  a { text-decoration: none; color: #000; }

  h2, h3, h4 { break-after: avoid; page-break-after: avoid; }
}

/* ==========================================================================
   19. Word add-in taskpane
   ==========================================================================
   * Narrow column (Word taskpane ~300px), same tokens as the product: the
   * taskpane links this stylesheet, so no second design system. Compact
   * vertical rhythm; results reuse .badge/.cite/.detail verbatim.
 */
.addin-wrap { max-width: 560px; margin: 0 auto; padding: var(--s4); }
.addin-title { font-size: var(--step-1); margin: 0 0 var(--s2); }
.addin-label { display: block; font-weight: var(--weight-semibold); font-size: var(--step--1); margin: var(--s4) 0 var(--s1); }
.addin-row { display: flex; gap: var(--s2); align-items: center; }
.addin-key { flex: 1; min-width: 0; font-family: var(--font-mono); }
.addin-brief { min-height: 160px; font-size: 16px; }
.addin-status { margin: var(--s4) 0 var(--s2); min-height: 1.5em; }
.addin-results { list-style: none; margin: var(--s3) 0; padding: 0; }
.addin-result { border: 1px solid var(--line); border-radius: var(--radius); padding: var(--s3); margin-bottom: var(--s3); background: var(--paper-raised); }

/* ==========================================================================
   20. Utility
   ==========================================================================
   * Last, so it beats every `display` above it without needing !important on
   * each component. The client filters by setting this attribute on .result and
   * .bucket-detail, several of which are grid or flex containers -- without
   * this, filtered-out results would stay on screen.
 */

[hidden] { display: none !important; }
