/* Citrail — The Evidence Desk
   One stylesheet. Tokens mirror frontend/app/globals.css so the public site and
   the workspace are the same material in whichever mode the visitor prefers.
   Rules: nothing below 12px, four text tiers, three radii, no decorative glow.
   See DESIGN.md. */

/* ── Tokens ─────────────────────────────────────────────────────────────── */

:root {
  color-scheme: light dark;

  /* ── Three colours ────────────────────────────────────────────────────────
     Black is the ground, ivory is the ink, amber is the only thing that
     affirms. Written in hex because these are brand facts measured against
     WCAG, not points on a ramp. Shared with the application: DESIGN.md §5. */

  /* Surfaces: warm paper, white sheets laid on it. */
  --paper: light-dark(#F7F5F0, #0A0A0A);
  --paper-2: light-dark(#F1EDE5, #131316);
  --sheet: light-dark(#FFFFFF, #16161A);
  --rule: light-dark(#E2DED5, #2A2A2E);
  --rule-soft: light-dark(#EBE7DE, #202024);

  /* Ink: four tiers, all AA on their ground. Never an opacity guess. */
  --ink: light-dark(#17171A, #F3EFE6);
  --ink-2: light-dark(#45433E, #A8A49B);
  --ink-3: light-dark(#67645E, #8A867D);
  --ink-faint: light-dark(#B0ABA1, #56534D);

  /* The action is ink, not a hue. The blue is gone: a filled black button is
     the loudest thing on paper without spending the one colour the product
     reserves for evidence. The token names stay so the 1000 lines below keep
     working, and so the diff shows a repaint rather than a rewrite. */
  --blue: light-dark(#17171A, #F3EFE6);
  --blue-strong: light-dark(#3A3833, #FFFFFF);
  --blue-wash: light-dark(rgba(23, 23, 26, 0.06), rgba(243, 239, 230, 0.08));
  --on-blue: light-dark(#F7F5F0, #0A0A0A);

  /* Proof. --amber paints, --amber-ink is read: the seal itself only reaches
     3.02:1 on paper and cannot carry text. The seal keeps its value in both
     modes — it is the brand. Only the readable amber lifts on the dark ground,
     because ivory and gold sit closer together than ink and gold do. */
  --amber: #B4862F;
  --amber-strong: light-dark(#8A6420, #D9A945);
  --amber-ink: light-dark(#7A5718, #D9A945);
  --on-amber: #17171A;

  /* "Done" is not "proven", so the confirmation is ink and the check does the
     talking. Only evidence gets the seal. */
  --success: light-dark(#17171A, #F3EFE6);
  --success-wash: light-dark(rgba(23, 23, 26, 0.07), rgba(243, 239, 230, 0.08));

  /* The brick: the fourth value, and the only one tolerated outside the three.
     A destructive state cannot be signalled in the ink of ordinary text. */
  --alert: light-dark(#8C3E2F, #CE7A63);
  --on-alert: light-dark(#F7F5F0, #0A0A0A);

  /* The deliberately inverted band (deployment, claim card, final call).
     It has its own tokens because it must stay dark in BOTH modes. Reusing
     --ink as a surface would flip it to white in dark mode. */
  --band: light-dark(#0A0A0A, #000000);
  --on-band: #F3EFE6;
  --on-band-2: #A8A49B;
  --on-band-3: #8A867D;
  --band-rule: light-dark(rgba(243, 239, 230, 0.14), rgba(243, 239, 230, 0.12));
  --band-rule-soft: light-dark(rgba(243, 239, 230, 0.09), rgba(243, 239, 230, 0.08));
  --band-surface: rgba(243, 239, 230, 0.05);

  /* The primary action inverts against the page. A literally white button
     would vanish on the light paper, so light mode takes the ink end and dark
     mode the paper end. Same surface, same contrast, both ways round. */
  --cta: light-dark(#17171A, #F3EFE6);
  --cta-hover: light-dark(#3A3833, #FFFFFF);
  --on-cta: light-dark(#F7F5F0, #0A0A0A);

  /* The wordmark's proof point: the one place amber stands for the brand. */
  --nav-mark: light-dark(#17171A, #F3EFE6);

  --grid: light-dark(rgba(23, 23, 26, 0.05), rgba(243, 239, 230, 0.045));
  --shadow: light-dark(rgba(23, 23, 26, 0.40), rgba(0, 0, 0, 0.62));

  /* A filled button is a raised edge: a 1px inset hairline along the top and a
     short cast underneath. The button is ivory in dark mode, so the sheen has
     to be brighter than its own face to register at all. */
  --btn-sheen: light-dark(rgba(255, 255, 255, 0.17), rgba(255, 255, 255, 0.62));
  --btn-cast: light-dark(rgba(23, 23, 26, 0.16), rgba(0, 0, 0, 0.55));

  /* The scanned-page mock. Mode-invariant on purpose, for the same reason the
     band is: it depicts a photocopy, and a photocopy does not have a theme. */
  --scan-paper: #DCD8CF;
  --scan-ink: #6E6A62;
  /* Assombri de sept points : `.scan-label` est du mono de 12 px, et l'ancien
     #67635B ne donnait que 4,2:1 sur le papier. On reste dans le même gris
     chaud, la barre de titre du fac-similé ne bouge pas à l'œil. */
  --scan-ink-2: #605C55;

  --shadow-deep: light-dark(rgba(23, 23, 26, 0.55), rgba(0, 0, 0, 0.78));

  /* Type scale. --t-meta is the floor; nothing on this page is smaller.

     The display size is set by measurement, not by taste: "Don't trust the
     answer." runs 10.78em in Libre Caslon Text, and the hero copy column is
     535px at a 1360px viewport (561px once the shell caps at 1340px). 48px is
     the largest size that keeps it on one line, which is what the explicit
     <br> in the markup asks for. Instrument Serif was far narrower and could
     afford 70px; keeping that number here produced three lines and an orphan
     reading "answer." */
  --t-display: clamp(2.4rem, 5vw, 4rem);
  --t-h2: clamp(1.75rem, 2.9vw, 2.5rem);
  --t-h3: 1.1875rem;
  --t-lead: 1.125rem;
  --t-body: 1rem;
  --t-ui: 0.875rem;
  --t-meta: 0.75rem;

  /* Two radii, no arbitrary values. Nearly square: the angle belongs to the
     document, not to the consumer app. Matches --radius in the application. */
  --r-control: 4px;
  --r-panel: 4px;

  /* Three families, three disjoint roles. Mixing all three inside one block
     is a defect. Libre Caslon Text carries what affirms, Public Sans the whole
     interface, IBM Plex Mono only what gets verified. */
  --font-display: "Libre Caslon Text", Georgia, "Times New Roman", serif;
  --font-sans: "Public Sans", system-ui, -apple-system, sans-serif;
  --font-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;

  --ease: cubic-bezier(0.2, 0.75, 0.2, 1);
  --shell: min(1340px, calc(100% - 80px));
  --nav-h: 76px;
}

/* ── The switch ───────────────────────────────────────────────────────────
   Every token above is written once, as light-dark(), and resolves against the
   root's own colour-scheme. So the control narrows that single property and the
   whole page repaints — there is no second palette to keep in step, and no way
   for the two modes to drift apart.

   Absent an explicit choice there is no attribute, and `light dark` above leaves
   the page following the operating system exactly as it did before the control
   existed. index.html applies the stored choice before the first paint. */
:root[data-theme="light"] { color-scheme: light; }
:root[data-theme="dark"]  { color-scheme: dark; }

/* ── Fonts ──────────────────────────────────────────────────────────────────
   Self-hosted, latin + latin-ext only: this page is English, and the cyrillic
   and vietnamese subsets would triple the payload for glyphs it never renders.
   The application loads the same three families through next/font.

   Public Sans is a variable font, so one file covers 400 to 600 — Google serves
   the identical bytes for each static weight, which is 90 KB of duplicate. */

@font-face {
  font-family: "Libre Caslon Text";
  src: url("assets/fonts/Caslon-400-ext.woff2") format("woff2");
  font-style: normal; font-weight: 400; font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Libre Caslon Text";
  src: url("assets/fonts/Caslon-400-lat.woff2") format("woff2");
  font-style: normal; font-weight: 400; font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Libre Caslon Text";
  src: url("assets/fonts/Caslon-400i-ext.woff2") format("woff2");
  font-style: italic; font-weight: 400; font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Libre Caslon Text";
  src: url("assets/fonts/Caslon-400i-lat.woff2") format("woff2");
  font-style: italic; font-weight: 400; font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Libre Caslon Text";
  src: url("assets/fonts/Caslon-700-ext.woff2") format("woff2");
  font-style: normal; font-weight: 700; font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Libre Caslon Text";
  src: url("assets/fonts/Caslon-700-lat.woff2") format("woff2");
  font-style: normal; font-weight: 700; font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Public Sans";
  src: url("assets/fonts/PublicSans-var-ext.woff2") format("woff2");
  font-style: normal; font-weight: 100 900; font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Public Sans";
  src: url("assets/fonts/PublicSans-var-lat.woff2") format("woff2");
  font-style: normal; font-weight: 100 900; font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face { font-family: "IBM Plex Mono"; src: url("assets/fonts/PlexMono-400-ext.woff2") format("woff2"); font-style: normal; font-weight: 400; font-display: swap; unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; }
@font-face { font-family: "IBM Plex Mono"; src: url("assets/fonts/PlexMono-400-lat.woff2") format("woff2"); font-style: normal; font-weight: 400; font-display: swap; unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: "IBM Plex Mono"; src: url("assets/fonts/PlexMono-500-ext.woff2") format("woff2"); font-style: normal; font-weight: 500; font-display: swap; unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; }
@font-face { font-family: "IBM Plex Mono"; src: url("assets/fonts/PlexMono-500-lat.woff2") format("woff2"); font-style: normal; font-weight: 500; font-display: swap; unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }

/* ── Variant: the evidence dossier ──────────────────────────────────────────
   Reached with ?variant=dossier. Nothing here touches the default identity.

   The premise: this page is a working file, not a product page that mentions
   files. Three consequences follow, and they are the whole direction.

   1. The colour system means something again. Blue is action and nothing else.
      Amber is proof and nothing else, which is why the navigation mark returns
      to blue here: navigating is a command, not a piece of evidence.
   2. Warm paper, cold ink. The default palette is a cool graphite; a desk is
      not. Every surface carries a trace of yellow, every ink a trace of blue.
   3. Corners are square, because documents are. Rules separate, because that
      is what a dossier does with a page. */

@font-face { font-family: "IBM Plex Serif"; src: url("assets/fonts/PlexSerif-400-ext.woff2") format("woff2"); font-style: normal; font-weight: 400; font-display: swap; unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; }
@font-face { font-family: "IBM Plex Serif"; src: url("assets/fonts/PlexSerif-400-lat.woff2") format("woff2"); font-style: normal; font-weight: 400; font-display: swap; unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: "IBM Plex Serif"; src: url("assets/fonts/PlexSerif-600-ext.woff2") format("woff2"); font-style: normal; font-weight: 600; font-display: swap; unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; }
@font-face { font-family: "IBM Plex Serif"; src: url("assets/fonts/PlexSerif-600-lat.woff2") format("woff2"); font-style: normal; font-weight: 600; font-display: swap; unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: "IBM Plex Sans"; src: url("assets/fonts/PlexSans-400-ext.woff2") format("woff2"); font-style: normal; font-weight: 400; font-display: swap; unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; }
@font-face { font-family: "IBM Plex Sans"; src: url("assets/fonts/PlexSans-400-lat.woff2") format("woff2"); font-style: normal; font-weight: 400; font-display: swap; unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: "IBM Plex Sans"; src: url("assets/fonts/PlexSans-500-ext.woff2") format("woff2"); font-style: normal; font-weight: 500; font-display: swap; unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; }
@font-face { font-family: "IBM Plex Sans"; src: url("assets/fonts/PlexSans-500-lat.woff2") format("woff2"); font-style: normal; font-weight: 500; font-display: swap; unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: "IBM Plex Sans"; src: url("assets/fonts/PlexSans-600-ext.woff2") format("woff2"); font-style: normal; font-weight: 600; font-display: swap; unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; }
@font-face { font-family: "IBM Plex Sans"; src: url("assets/fonts/PlexSans-600-lat.woff2") format("woff2"); font-style: normal; font-weight: 600; font-display: swap; unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }

:root[data-variant="dossier"] {
  /* Warm paper, laid sheets, cold ink — and in dark, the same desk under a
     lamp: still warm, never blue-black. */
  --paper: light-dark(oklch(0.975 0.007 84), oklch(0.158 0.009 78));
  --paper-2: light-dark(oklch(0.953 0.009 84), oklch(0.140 0.009 78));
  --sheet: light-dark(oklch(0.995 0.003 84), oklch(0.198 0.011 78));
  --rule: light-dark(oklch(0.865 0.011 84), oklch(0.99 0.02 84 / 12%));
  --rule-soft: light-dark(oklch(0.915 0.008 84), oklch(0.99 0.02 84 / 8%));

  --ink: light-dark(oklch(0.235 0.016 264), oklch(0.945 0.007 84));
  --ink-2: light-dark(oklch(0.395 0.017 264), oklch(0.775 0.013 84));
  --ink-3: light-dark(oklch(0.535 0.018 264), oklch(0.635 0.015 84));
  --ink-faint: light-dark(oklch(0.79 0.012 264), oklch(0.40 0.013 84));

  /* Blue commands. It is the action colour, the navigation mark and the
     primary button, and it is nothing else. */
  --blue: light-dark(oklch(0.475 0.155 252), oklch(0.695 0.140 250));
  --blue-strong: light-dark(oklch(0.395 0.150 252), oklch(0.775 0.125 250));
  --blue-wash: light-dark(oklch(0.475 0.155 252 / 9%), oklch(0.695 0.140 250 / 13%));
  --on-blue: light-dark(oklch(0.99 0.002 84), oklch(0.155 0.012 264));
  --nav-mark: var(--blue);
  --cta: var(--blue);
  --cta-hover: var(--blue-strong);
  --on-cta: var(--on-blue);

  /* Amber proves. One meaning, no decoration. */
  --amber: light-dark(oklch(0.885 0.145 92), oklch(0.845 0.145 92));
  --amber-strong: light-dark(oklch(0.825 0.160 88), oklch(0.885 0.135 90));
  --amber-ink: light-dark(oklch(0.455 0.105 78), oklch(0.855 0.130 90));

  --band: light-dark(oklch(0.205 0.014 264), oklch(0.118 0.010 78));
  --grid: light-dark(oklch(0.235 0.016 264 / 6%), oklch(0.99 0.02 84 / 5%));

  /* One superfamily built for technical documentation. The three roles were
     drawn to agree with each other, so the page reads as one document. */
  --font-display: "IBM Plex Serif", Georgia, serif;
  --font-sans: "IBM Plex Sans", system-ui, sans-serif;
  --font-mono: "IBM Plex Mono", ui-monospace, monospace;

  /* Measured, not guessed: Plex Serif 600 sets the first line at 10.93em,
     against 7.07em for Instrument Serif. It is the widest of the three, and
     the margin rule takes 31px more, so 46px is what holds two lines in a
     530px column. A dossier title is not a billboard; the weight and the
     structure carry it instead of the size. */
  --t-display: clamp(2.05rem, 3.2vw, 2.9rem);
  --t-h2: clamp(1.9rem, 3vw, 2.7rem);

  /* Documents do not have rounded corners. */
  --r-control: 2px;
  --r-panel: 3px;
}

/* Plex Serif carries a real 600, so the display holds its weight instead of
   relying on size alone. */
:root[data-variant="dossier"] .hero h1 { font-weight: 600; letter-spacing: -0.022em; line-height: 1.04; }
:root[data-variant="dossier"] .section-intro h2, :root[data-variant="dossier"] .verification-head h2,
:root[data-variant="dossier"] .deployment-copy h2, :root[data-variant="dossier"] .pipeline-head h2,
:root[data-variant="dossier"] .pricing-head h2, :root[data-variant="dossier"] .faq-intro h2,
:root[data-variant="dossier"] .final-panel h2 { font-weight: 600; letter-spacing: -0.018em; }

/* The marker stroke is re-derived for Plex Serif: its p descends further and
   its inline box is deeper than Instrument Serif's, so the default offset
   would sit inside the descender. */
:root[data-variant="dossier"] .hero h1 span::after { bottom: 0.005em; height: 0.058em; }

/* A document column, marked as one. The rule is structure, not ornament: it
   says where the text block begins, the way a margin does on a page. */
:root[data-variant="dossier"] .hero-copy { padding-left: 30px; border-left: 1px solid var(--rule); }

/* The proof reference stops being a caption and becomes the file's colophon:
   ruled off, the page number pushed to the opposite edge. */
:root[data-variant="dossier"] .hero-source {
  display: flex; justify-content: space-between; gap: 16px;
  margin-top: 18px; padding-top: 13px;
  border-top: 1px solid var(--rule);
}

@media (max-width: 760px) {
  :root[data-variant="dossier"] .hero-copy { padding-left: 14px; }
  :root[data-variant="dossier"] .hero h1 { font-size: clamp(1.45rem, 7.6vw, 2.05rem); }
  :root[data-variant="dossier"] .hero-source { display: block; }
}

/* ── Base ───────────────────────────────────────────────────────────────── */

*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }

html { scroll-behavior: smooth; scroll-padding-top: calc(var(--nav-h) + 16px); }

/* La chronologie de la couture est hissée ici : le héros au-dessus et la
   section en dessous en dépendent, et un nom de chronologie ne se voit que
   depuis les descendants de qui le déclare. */
main { timeline-scope: --seam; }

body {
  min-width: 320px;
  touch-action: manipulation;
  /* `clip` et non `hidden` : les deux rognent le débordement horizontal, mais
     `hidden` fait du corps un conteneur de défilement, ce qui neutralise tout
     `position: sticky` de la page. La couture ne tenait pas. */
  overflow-x: clip;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-sans);
  font-weight: 500;
  font-size: var(--t-body);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

a { color: inherit; }
button, a { font: inherit; -webkit-tap-highlight-color: transparent; }
button { color: inherit; }
img, video, svg { display: block; max-width: 100%; }
::selection { background: var(--amber); color: var(--on-amber); }
:focus-visible { outline: 2px solid var(--blue); outline-offset: 3px; }

.shell {
  width: var(--shell);
  margin-inline: auto;
  padding-inline: env(safe-area-inset-left) env(safe-area-inset-right);
}

.skip-link {
  position: fixed; z-index: 100; left: 12px; top: -60px;
  padding: 10px 14px; border-radius: var(--r-control);
  background: var(--band); color: var(--on-band);
  font-size: var(--t-ui); text-decoration: none;
}
.skip-link:focus { top: 12px; }

/* Section label: the topic in plain words, with its index in mono.
   One per section, never a decorative small-caps strip. */
.section-label {
  display: flex; align-items: center; gap: 10px;
  color: var(--ink-3); font-size: var(--t-ui); font-weight: 500;
}
.section-label span { color: var(--blue); font-family: var(--font-mono); font-size: var(--t-meta); }
.section-label.inverse { color: var(--ink-3); }
.section-label.inverse span { color: var(--amber-ink); }

.section-intro h2, .verification-head h2, .deployment-copy h2,
.pipeline-head h2, .pricing-head h2, .faq-intro h2 {
  margin-top: 22px;
  font-family: var(--font-display);
  font-size: var(--t-h2);
  font-weight: 400;
  line-height: 1.06;
  letter-spacing: -0.014em;
  text-wrap: balance;
}

/* Section head: the title claims the left column, its qualifier sits in the
   right one and shares the same baseline. Not a floating corner paragraph. */
.section-intro, .verification-head, .pipeline-head {
  display: grid; grid-template-columns: 1.05fr 0.95fr; align-items: end; gap: 60px;
}
.section-intro > p, .verification-head > p, .pipeline-head > p,
.faq-intro > p {
  max-width: 48ch;
  color: var(--ink-2);
  font-size: var(--t-lead);
  line-height: 1.6;
}
.faq-intro > p { margin-top: 20px; }

/* ── Header ─────────────────────────────────────────────────────────────── */

.site-header {
  position: fixed; inset: 0 0 auto; z-index: 60;
  border-bottom: 1px solid transparent;
  transition: background 0.2s, border-color 0.2s;
}
.site-header.scrolled, .site-header.menu-open {
  border-color: var(--rule);
  background: color-mix(in oklab, var(--paper) 92%, transparent);
  -webkit-backdrop-filter: blur(18px);
  backdrop-filter: blur(18px);
}

/* Translucent over the page is right for a scrolled bar and wrong behind an
   open drawer, where the hero ghosts through the wordmark. */
.site-header.menu-open { background: var(--paper); }

.nav-shell { position: relative; height: var(--nav-h); display: flex; align-items: center; }

.brand, .footer-brand { display: inline-flex; align-items: center; gap: 9px; text-decoration: none; }
/* The wordmark is never the element that gives way when the bar runs short. */
.brand { flex: none; }
.brand img, .footer-brand img { width: 26px; height: 26px; }
.brand span, .footer-brand span { font-size: 15px; font-weight: 700; letter-spacing: -0.025em; }
.brand b, .footer-brand b { color: var(--amber-ink); font-weight: 700; }

.desktop-nav {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  display: flex; align-items: center; gap: 32px;
}
/* Padding rather than margin: the pointer and touch target fills the bar
   instead of stopping at the cap height of a 14px word. */
.desktop-nav > a {
  position: relative; padding-block: 12px;
  color: var(--ink-3);
  font-size: var(--t-ui); text-decoration: none;
  transition: color 0.18s;
}
/* Ink, not amber. This underline says "you are here", and being here is not a
   piece of evidence. It used to be amber because the alternative was the blue
   and a blue underline read as a link inside a link; with the blue gone the
   action IS the ink, so the argument disappears with it. The wordmark keeps
   its gold point, which is the one place the seal stands for the brand. */
.desktop-nav > a::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 5px; height: 2px;
  background: var(--nav-mark);
  transform: scaleX(0); transform-origin: left;
  transition: transform 0.25s var(--ease);
}
.desktop-nav > a:hover::after, .desktop-nav > a.active::after { transform: scaleX(1); }
.desktop-nav > a:hover, .desktop-nav > a.active { color: var(--ink); }

/* ── Les deux entrées qui ouvrent ────────────────────────────────────────
   Solutions et Deployment ne sont pas des liens : ce sont des portes. Elles
   portent le même trait que les autres entrées — la barre doit se lire comme
   une seule chose — et le chevron dit que quelque chose s'ouvre. */
.nav-menu { position: relative; display: flex; }
.nav-trigger {
  position: relative; display: inline-flex; align-items: center; gap: 5px;
  padding: 12px 0; border: 0; background: none;
  color: var(--ink-3); font: inherit; font-size: var(--t-ui); cursor: pointer;
  transition: color 0.18s;
}
.nav-trigger::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 5px; height: 2px;
  background: var(--nav-mark);
  transform: scaleX(0); transform-origin: left;
  transition: transform 0.25s var(--ease);
}
.nav-trigger:hover, .nav-trigger[aria-expanded="true"], .nav-trigger.active { color: var(--ink); }
.nav-trigger:hover::after, .nav-trigger[aria-expanded="true"]::after, .nav-trigger.active::after { transform: scaleX(1); }

/* Pendant qu'un panneau est ouvert, la marque « vous êtes ici » s'efface —
   le trait ET l'encre pleine. C'est le panneau ouvert qui porte la marque, et
   lui seul : deux entrées appuyées en même temps pour deux raisons
   différentes se lisent comme une erreur. */
.desktop-nav[data-open="1"] .active::after { transform: scaleX(0); }
.desktop-nav[data-open="1"] .active { color: var(--ink-3); }
.desktop-nav[data-open="1"] .nav-trigger[aria-expanded="true"]::after { transform: scaleX(1); }
.desktop-nav[data-open="1"] .nav-trigger[aria-expanded="true"] { color: var(--ink); }
.nav-trigger svg { width: 12px; height: 12px; fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; transition: transform 0.22s var(--ease); }
.nav-trigger[aria-expanded="true"] svg { transform: rotate(180deg); }

/* Le panneau se détache par un fond plein et un filet, pas par une ombre :
   §3 du brief interdit ombre, dégradé et halo. */
.nav-panel {
  position: absolute; top: calc(100% + 10px); left: 50%; z-index: 5;
  width: 400px; padding: 14px 15px 8px;
  border: 1px solid var(--rule); border-radius: var(--r-panel);
  background: var(--sheet);
  opacity: 0; visibility: hidden; transform: translate(-50%, -4px);
  transition: opacity 0.18s var(--ease), transform 0.18s var(--ease), visibility 0.18s;
}
.nav-panel[data-open="1"] { opacity: 1; visibility: visible; transform: translate(-50%, 0); }
.nav-panel-note { margin: 0 0 10px; padding-bottom: 10px; border-bottom: 1px solid var(--rule-soft); color: var(--ink-3); font-size: var(--t-meta); line-height: 1.5; }
.nav-panel ul { margin: 0; padding: 0; list-style: none; }
.nav-panel li + li { margin-top: 2px; }
/* Le lien couvre toute la rangée : la cible est la rangée, pas le titre. */
.nav-panel a { display: block; padding: 8px 9px; border-radius: var(--r-control); text-decoration: none; transition: background 0.15s; }
.nav-panel a:hover, .nav-panel a:focus-visible { background: var(--paper-2); }
.nav-panel b { display: block; color: var(--ink); font-size: var(--t-ui); font-weight: 500; }
.nav-panel span { display: block; margin-top: 2px; color: var(--ink-3); font-size: var(--t-meta); line-height: 1.5; }

/* « How it works » ne nomme que les quatre étapes : la barre est une porte, et
   ce qu'il y a derrière chaque étape se montre dans sa section, pas ici. Sans
   ligne de description, le panneau se resserre sur les seuls titres. */
.nav-panel[data-plain] { width: 268px; }

.nav-actions { display: flex; align-items: center; gap: 16px; margin-left: auto; }
.nav-login { padding-block: 11px; color: var(--ink-3); font-size: var(--t-ui); text-decoration: none; transition: color 0.18s; }
.nav-login:hover { color: var(--ink); }

/* ── Buttons ────────────────────────────────────────────────────────────── */

.button {
  min-height: 40px;
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  padding: 0 17px;
  border: 1px solid transparent; border-radius: var(--r-control);
  font-size: var(--t-ui); font-weight: 500; letter-spacing: 0.01em;
  text-decoration: none; white-space: nowrap; cursor: pointer;
  transition: transform 0.12s var(--ease), background 0.18s, box-shadow 0.12s, color 0.18s, border-color 0.18s;
}
/* The press goes DOWN, like the application's, and it does not scale: §10 asks
   for 1 to 2 px "sans élasticité", and a scale is elasticity. The button used
   to lift on hover, which made the landing and the app disagree about which
   way a button moves. */
.button:active { transform: translateY(1px); transition-duration: 0.06s; }
.button svg { width: 15px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; transition: transform 0.18s; }
.button:hover svg { transform: translateX(2px); }

.button-small { min-height: 34px; padding-inline: 13px; font-size: var(--t-ui); }
.button-primary { background: var(--cta); color: var(--on-cta);
  box-shadow: inset 0 1px 0 0 var(--btn-sheen), 0 1px 2px -1px var(--btn-cast); }
.button-primary:active { box-shadow: inset 0 1px 0 0 transparent; }
.button-primary:hover { background: var(--cta-hover); }
.button-light { border-color: var(--rule); background: var(--sheet); color: var(--ink); }
.button-light:hover { border-color: var(--blue); color: var(--blue); }
.button-dark { background: var(--band); color: var(--on-band);
  box-shadow: inset 0 1px 0 0 rgba(243, 239, 230, 0.15), 0 1px 2px -1px rgba(0, 0, 0, 0.4); }
.button-dark:active { box-shadow: inset 0 1px 0 0 transparent; }
/* The dark button lives on band logic, so it lightens within the band
   instead of taking --blue, which is near-black on paper. */
.button-dark:hover { background: color-mix(in oklab, var(--on-band) 17%, var(--band)); color: var(--on-band); }

.menu-toggle {
  position: relative; width: 40px; height: 40px; display: none; place-items: center;
  padding: 0; border: 1px solid var(--rule); border-radius: var(--r-control);
  background: var(--sheet); cursor: pointer;
  transition: border-color 0.18s;
}
.menu-toggle:hover { border-color: var(--ink-3); }
.menu-toggle[aria-expanded="true"] { border-color: var(--ink); }
.menu-toggle span { position: absolute; width: 16px; height: 1.5px; background: var(--ink); transition: transform 0.2s; }
.menu-toggle span:first-child { transform: translateY(-3.5px); }
.menu-toggle span:last-child { transform: translateY(3.5px); }
.menu-toggle[aria-expanded="true"] span:first-child { transform: rotate(45deg); }
.menu-toggle[aria-expanded="true"] span:last-child { transform: rotate(-45deg); }

/* The mode switch. Borderless, like the one in the workspace: the header
   already carries a filled button and a rule, and a third box would make the
   bar read as a toolbar. It sits left of "Log in" so the primary action stays
   the last thing on the line. */
.theme-toggle {
  display: grid; place-items: center; width: 34px; height: 34px; padding: 0;
  border: 0; border-radius: var(--r-control);
  background: transparent; color: var(--ink-3); cursor: pointer;
  transition: color 0.18s, background-color 0.18s;
}
.theme-toggle:hover { color: var(--ink); background: var(--blue-wash); }
.theme-toggle svg { width: 17px; height: 17px; display: block; }

/* light-dark() returns a colour and nothing else, so which icon shows is the
   one thing the switch cannot express as a token. Same three-way contract as
   the tokens: an explicit choice wins over the system in both directions. */
.icon-sun { display: none; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .icon-sun { display: inline; }
  :root:not([data-theme="light"]) .icon-moon { display: none; }
}
:root[data-theme="dark"] .icon-sun { display: inline; }
:root[data-theme="dark"] .icon-moon { display: none; }

.mobile-nav { padding: 8px 24px 20px; border-top: 1px solid var(--rule); background: var(--paper); }
.mobile-nav a { display: block; padding: 13px 0; border-bottom: 1px solid var(--rule-soft); font-size: var(--t-body); text-decoration: none; }
.mobile-nav a:last-child { border-bottom: 0; }
/* Le tiroir n'a pas de sous-menus : il déplie tout, et un intertitre en mono
   dit à quoi appartiennent les rangées qui suivent. */
.mobile-nav-h { margin: 18px 0 2px; color: var(--ink-faint); font-family: var(--font-mono); font-size: var(--t-meta); letter-spacing: 0.06em; text-transform: uppercase; }
.mobile-nav a.mobile-sub { padding-left: 12px; color: var(--ink-2); }
/* The drawer is the only route to signup once the header button no longer
   fits, so the primary action stays a button here instead of becoming the
   last indistinguishable row of a list. */
.mobile-nav .nav-cta {
  min-height: 48px; margin-top: 16px; padding: 0 22px;
  display: flex; align-items: center; justify-content: center;
  border-bottom: 0; border-radius: var(--r-control);
  background: var(--cta); color: var(--on-cta); font-weight: 600;
  transition: background 0.18s;
}
.mobile-nav .nav-cta:hover { background: var(--cta-hover); }
/* One primary action on screen at a time: the drawer takes it over while open. */
.site-header.menu-open .nav-actions .button-small { display: none; }
/* Above the burger breakpoint the drawer must not survive a resize: only the
   hidden attribute closes it, and the button that toggles it is gone. */
@media (min-width: 1021px) { .mobile-nav { display: none; } }

/* ── Hero ───────────────────────────────────────────────────────────────── */

/* The capture outweighs the pitch, 56 to 44, which lands it about a third
   larger than the column it used to sit beside. Everything else on this
   screen is tuned down to let that happen. */
/* 70 px sous la barre, 126 px avant le filet suivant — resserré de 22 px des
   deux côtés le 5 septembre 2026. Le haut seul aurait remonté le bloc tout en
   agrandissant le vide sous lui dans le pli : l'impact se serait déplacé au
   lieu de se renforcer. Les deux ensemble raccourcissent la section, donc le
   filet de la section suivante remonte avec elle. */
.hero { position: relative; padding: calc(var(--nav-h) + 70px) 0 120px; background: var(--paper); }
.hero::after { content: ""; position: absolute; inset: var(--nav-h) 0 auto; height: 1px; background: var(--rule-soft); }

/* 45/55 : la fenêtre produit prend la plus grande moitié, et le titre grandit
   quand même. Ce n'est pas un compromis, c'est un changement de découpe.

   Mesuré dans Libre Caslon Text, à la lettre près :

     découpe en 2 lignes   "Don't trust the answer."   10.29 em   ← la contrainte
     découpe en 3 lignes   "Verify the proof."          7.42 em

   La ligne la plus longue raccourcit de 28 %, donc le corps peut grandir
   d'autant. À un shell de 1200 px avec 56 px de gouttière, 45fr donne 515 px :
   515 / 7.42 = 69 px possibles, contre 50 px si le titre restait en deux
   lignes. --t-display s'arrête à 64 px — la valeur mesurée chez Linear,
   Vercel, Raycast et Attio — ce qui laisse 40 px de marge pour qu'aucune ligne
   ne casse à aucun zoom.

   Le titre passe donc à trois lignes dans le markup. "Verify the proof." reste
   entière sur la dernière : c'est la chute, et c'est elle qui porte le trait
   d'ambre sous "proof.". */
.hero-layout {
  position: relative;
  display: grid; grid-template-columns: minmax(0, 45fr) minmax(0, 55fr);
  align-items: center; gap: 56px;
}

/* La barre de jetons a été retirée le 5 septembre 2026. Elle occupait le
   créneau de la pastille d'annonce d'Attio et devait porter les identifiants
   réels du run — mais deux de ses quatre valeurs (l'identifiant et le motif
   d'arrêt) n'existent nulle part dans les données cuites, et elle affichait
   donc des points. `DESIGN.md` §13 interdit le repère qui ne désigne rien :
   une barre de mesure sans mesure est du décor, et sur une page qui vend la
   vérifiabilité c'est le pire endroit possible. À rétablir seulement le jour
   où un run réel fournit les quatre valeurs. */
.hero-copy { position: relative; z-index: 3; }

.hero h1 {
  margin: 0 0 46px;
  font-family: var(--font-display);
  font-size: var(--t-display);
  font-weight: 400;
  line-height: 1.06;
  letter-spacing: -0.018em;
  text-wrap: balance;
}
/* The signature, drawn once: a marker stroke under one word rather than a
   block behind it. An annotation in a margin, not a highlighter dragged
   across a paragraph. The proof mark includes its closing point so the
   sentence resolves as one deliberate accent.

   The offset is derived, not guessed, and it is derived AGAIN whenever the
   display face changes. Read from the font itself (unitsPerEm 1000):

     Libre Caslon Text   p yMin -0.250em   box descent -0.260em
     Instrument Serif    p yMin -0.210em   box descent -0.310em

   Caslon's p almost fills its descent — 0.01em of room where Instrument Serif
   left 0.10em — so the stroke can no longer sit inside the inline box without
   cutting through the descender. It hangs below instead:
     bottom = 0.260 - (0.250 + 0.030 clearance + 0.050 stroke) = -0.070em
   Nothing clips it: no ancestor on the hero sets overflow, and "proof" ends
   the last line. */
.hero h1 span { position: relative; }
.hero h1 span::after {
  content: ""; position: absolute;
  left: -0.04em; right: -0.04em; bottom: -0.07em; height: 0.05em;
  border-radius: 1px;
  background: var(--amber);
  transform: rotate(0.5deg) scaleX(0); transform-origin: left;
  /* Starts once the headline has stopped moving, not during it.
     The headline enters from 0.08s over 0.7s and settles at 0.78s; the stroke
     used to start at 0.55s, so the one gesture that carries meaning was drawn
     under a title that was itself still arriving, and nobody saw it. A beat of
     silence after 0.78s is what turns it from noise into a gesture. */
  animation: mark-in 0.85s 1.05s var(--ease) forwards;
}
@keyframes mark-in { to { transform: rotate(0.5deg) scaleX(1); } }

.hero-lede { max-width: 44ch; color: var(--ink-2); font-size: var(--t-lead); line-height: 1.68; }

.hero-actions { display: flex; align-items: center; flex-wrap: wrap; gap: 18px 26px; margin-top: 44px; }
/* Deliberately smaller than the same button elsewhere on the page: on this
   screen the capture has to win, and one blue fill is the whole budget. */
.hero-actions .button { min-height: 42px; padding-inline: 18px; }
.hero-loop { display: inline-flex; align-items: center; gap: 9px; color: var(--ink-3); font-size: var(--t-ui); font-weight: 600; text-decoration: none; transition: color 0.18s; }
.hero-loop:hover { color: var(--ink); }
.hero-loop svg { width: 8px; fill: var(--blue); transition: transform 0.18s; }
.hero-loop:hover svg { transform: translateX(2px); }

.hero-product { position: relative; min-width: 0; }
.hero-shot { margin: 0; }
/* One frame, not two. The border is the whole treatment: no mat, no lift,
   nothing for the eye to mistake for window chrome. */
.hero-frame {
  overflow: hidden;
  border: 1px solid var(--rule); border-radius: var(--r-panel);
  background: var(--sheet);
}
.hero-frame img { width: 100%; height: auto; }
/* At this scale the filename inside the capture stops being legible, so the
   proof loop is named once underneath it, in the type reserved for what gets
   verified. Real values, read off that capture. */
.hero-source {
  margin-top: 14px;
  color: var(--ink-3);
  font-family: var(--font-mono);
  font-size: var(--t-meta); line-height: 1.5;
  overflow-wrap: anywhere;
}
.hero-source b { color: var(--amber-ink); font-weight: 400; }

.hero-enter { opacity: 0; transform: translateY(12px); animation: hero-in 0.7s var(--delay) var(--ease) forwards; }
@keyframes hero-in { to { opacity: 1; transform: none; } }

/* ── La fenêtre produit ───────────────────────────────────────────────────
   La moitié droite du héros rejoue l'application, en DOM.

   Pourquoi en DOM et pas en image : le visiteur doit pouvoir survoler une
   citation et cliquer dessus. Une capture ne répond pas, et c'est justement la
   promesse qui tomberait. Cursor peut se contenter d'une image parce que son
   contenu est du code, que personne ne vérifie ; le nôtre est une preuve.

   Une seule exception, et elle est de principe : la PAGE du document reste un
   raster. Une page de PDF est une image ; la redessiner en DOM serait la
   capture reconstituée que le brief interdit, et surtout un mensonge sur la
   seule chose qui doive être vraie.

   Pas de feux tricolores ni de barre de titre de bureau : notre produit est une
   application web, et lui dessiner une fenêtre macOS serait un décor. Le cadre
   est un filet d'un pixel, comme partout ailleurs sur cette page. */
.hero-window {
  position: relative;
  display: flex; flex-direction: column;
  overflow: hidden;
  border: 1px solid var(--rule); border-radius: 10px;
  background: var(--sheet);
  /* Le rembobinage : l'image d'arrivée s'efface, la fenêtre se remonte à vide
     et la séquence repart. Un fondu court, pour que ça se lise comme un retour
     en arrière voulu et non comme un défaut d'affichage. */
  transition: opacity 0.28s linear;
  box-shadow: 0 1px 2px var(--btn-cast), 0 18px 40px -28px var(--shadow);
}
.hero-window[data-rewind="1"] { opacity: 0; }

/* La barre de fenêtre. Les trois pastilles sont demandées : elles disent en un
   coup d'œil « ceci est une application », ce qu'aucun filet ne dit aussi vite.
   Elles sont muettes — inertes, sans état de survol — parce qu'elles décrivent
   un cadre, pas des commandes qui marcheraient. */
/* La ligne de crédits, sous la planche — le cadre du brief §3, à la place de
   la barre de titre qui imitait une fenêtre de système. À gauche ce que c'est
   et quand ; à droite où le produit se trouve. Rien d'ornemental : chaque
   moitié est une information qu'un visiteur peut vérifier. */
/* `hw-credits` sans préfixe était déjà pris par la ligne de crédits du volet
   de preuve, plus bas dans ce fichier : la règle la plus tardive gagnait, et
   celle-ci se faisait imposer sa taille et sa couleur. Le nom porte donc la
   planche. */
.hw-plate-credits {
  display: flex; align-items: baseline; justify-content: space-between; gap: 16px;
  margin-top: 11px; padding-top: 9px;
  border-top: 1px solid var(--rule);
  color: var(--ink-3);
  font-family: var(--font-mono); font-size: var(--t-meta); letter-spacing: 0.02em;
}
.hw-plate-credits b { flex: none; color: var(--ink-2); font-weight: 400; }

/* Hauteur fixe : la fenêtre traverse cinq écrans, et si elle se redimensionnait
   à chaque transition toute la page sauterait sous elle. */
.hw-body { position: relative; height: 470px; overflow: hidden; }

/* Un écran occupe tout le corps ; un seul est visible à la fois. */
.hw-screen {
  position: absolute; inset: 0;
  display: flex; flex-direction: column;
  padding: 14px;
  overflow: hidden;
  opacity: 0; visibility: hidden;
  transition: opacity 0.3s var(--ease);
}
.hw-screen[data-on="1"] { opacity: 1; visibility: visible; }

/* ── Le curseur ──────────────────────────────────────────────────────────
   C'est lui qui transforme une suite d'états en démonstration : sans main
   visible, le spectateur voit des choses changer sans comprendre qui agit.
   Il est décoratif au sens strict — aria-hidden, jamais focusable — et il
   disparaît dès que le visiteur prend la main. */
.hw-cursor {
  position: absolute; left: 0; top: 0; z-index: 30;
  width: 18px; height: 18px;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.25s var(--ease);
  will-change: transform;
}
.hw-cursor[data-on="1"] { opacity: 1; }
.hw-cursor svg { display: block; filter: drop-shadow(0 1px 1px rgba(0,0,0,.35)); }
/* L'onde du clic : elle dure moins qu'un battement de paupière, juste assez
   pour qu'on sache qu'un clic a eu lieu et pas seulement un survol. */
.hw-ripple {
  position: absolute; left: 0; top: 0; z-index: 29;
  width: 26px; height: 26px; margin: -13px 0 0 -13px;
  border: 1.5px solid var(--amber); border-radius: 50%;
  opacity: 0; pointer-events: none;
}
.hw-ripple[data-fire="1"] { animation: hw-ripple 0.5s var(--ease); }
@keyframes hw-ripple {
  from { opacity: 0.9; transform: scale(0.35); }
  to   { opacity: 0;   transform: scale(1.4); }
}

/* Le rail latéral de l'application. Étroit, muet, sans état de survol : il dit
   « ceci est un espace de travail » et ne prétend rien de plus. */
.hw-shell { display: flex; height: 100%; }
.hw-rail { flex: none; width: 34px; display: flex; flex-direction: column; align-items: center; gap: 13px; padding: 12px 0; border-right: 1px solid var(--rule); background: var(--paper-2); }
/* Les cinq entrées portent les icônes de la vraie barre latérale. Plus de
   boîte autour : une application n'encadre pas ses icônes de navigation, et
   le carré vide se lisait comme un emplacement à remplir. */
.hw-rail i { display: grid; place-items: center; width: 17px; height: 17px; font-style: normal; }
.hw-rail i svg { width: 15px; height: 15px; fill: none; stroke: var(--ink-3); stroke-width: 1.25; stroke-linecap: round; stroke-linejoin: round; opacity: 0.7; transition: opacity 0.18s, stroke 0.18s; }
.hw-mark img { display: block; width: 17px; height: 17px; border-radius: 50%; }
/* L'entrée qu'on ouvre : elle s'allume quand la conversation est à l'écran. */
.hw-rail i[data-act] svg { opacity: 0.95; }
.hw-rail i[data-on="1"] svg { opacity: 1; stroke: var(--ink); stroke-width: 1.45; }
.hw-main { position: relative; flex: 1; min-width: 0; }

/* ── Écran des collections ──────────────────────────────────────────────── */
.hw-cards { display: grid; grid-template-columns: 1fr 1fr; gap: 9px; margin-top: 12px; }
.hw-card { border: 1px solid var(--rule); border-radius: var(--r-panel); background: var(--sheet); cursor: pointer; transition: border-color 0.18s, background-color 0.18s; }
.hw-card:hover, .hw-card[data-hot="1"] { border-color: var(--ink-3); background: var(--paper-2); }
.hw-card-h { display: flex; align-items: baseline; gap: 6px; padding: 9px 10px 6px; }
.hw-card-h b { color: var(--ink); font-size: var(--t-meta); font-weight: 600; }
.hw-card-h span { margin-left: auto; color: var(--ink-faint); font-size: 10px; }
.hw-card-b { display: flex; align-items: center; gap: 6px; margin: 0 10px; padding: 6px 8px; border-radius: 3px; background: var(--paper-2); color: var(--ink-3); font-size: 10px; }
.hw-card-b em { margin-left: auto; font-style: normal; }
.hw-card-f { padding: 7px 10px 9px; color: var(--ink-3); font-size: 10px; }

/* ── Les quatre compteurs de la collection ─────────────────────────────── */
/* `minmax(0, 1fr)` et non `1fr` : sans cela, une valeur longue et insécable —
   la méthode d'indexation — impose sa largeur à sa colonne et déforme les
   quatre compteurs. */
.hw-stats { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 7px; margin-top: 10px; }
.hw-stat { padding: 8px 9px; border: 1px solid var(--rule); border-radius: var(--r-panel); }
.hw-stat u { display: block; color: var(--ink-faint); font-family: var(--font-mono); font-size: 9px; letter-spacing: 0.06em; text-transform: uppercase; text-decoration: none; }
.hw-stat b { display: block; margin: 2px 0 1px; color: var(--ink); font-family: var(--font-display); font-size: 19px; font-weight: 400; }
.hw-stat span { color: var(--ink-faint); font-size: 9px; }
/* Une valeur qui est une phrase et non un nombre : la méthode d'indexation.
   Elle garde la casse et la taille du texte, et se coupe plutôt que de pousser
   la carte — les quatre compteurs doivent rester alignés. */
.hw-stat b[data-text] { font-family: var(--font-sans); font-size: 11px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* La barre de preuve et les onglets de filtre, tels que l'application les pose. */
.hw-bar-note { display: flex; align-items: center; gap: 10px; margin-top: 9px; padding: 8px 10px; border: 1px solid var(--rule); border-radius: var(--r-panel); }
.hw-bar-note div { min-width: 0; }
.hw-bar-note b { display: block; color: var(--ink); font-size: 10px; font-weight: 600; }
.hw-bar-note span { color: var(--ink-3); font-size: 9px; }
.hw-tabs { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 9px; }
.hw-tab { padding: 2px 7px; border-radius: 999px; color: var(--ink-3); font-size: 10px; }
.hw-tab[data-on="1"] { background: var(--paper-2); color: var(--ink); }
.hw-tab u { color: var(--ink-faint); text-decoration: none; }

/* L'état vide, mot pour mot celui de l'application. */
.hw-empty { flex: 1; display: grid; place-content: center; justify-items: center; gap: 4px; margin-top: 9px; padding: 18px; border: 1px solid var(--rule); border-radius: var(--r-panel); text-align: center; }
.hw-empty b { color: var(--ink); font-size: var(--t-meta); font-weight: 600; }
.hw-empty span { max-width: 34ch; color: var(--ink-3); font-size: 10px; line-height: 1.5; }
.hw-empty[hidden] { display: none; }

/* ── La profondeur : descendre dans un document, et en remonter ──────────
   Deux écrans se trouvent sous la collection — le workflow et l'inspection.
   La barre de retour est ce qui les rend navigables plutôt que sans issue. */
.hw-back { display: flex; align-items: center; gap: 10px; padding-bottom: 9px; border-bottom: 1px solid var(--rule-soft); }
.hw-back span { color: var(--ink-3); font-size: 11px; }
.hw-back b { color: var(--ink); font-size: 12px; font-weight: 600; }
/* La ligne du document, en tête du workflow : son nom à gauche, son état à
   droite. Le même bandeau pendant et après le run, parce que c'est le même
   document — seuls ses trois indicateurs changent. */
.hw-docrow { display: flex; align-items: center; gap: 10px; padding: 9px 0; border-bottom: 1px solid var(--rule-soft); }
.hw-docrow > b { flex: 1; min-width: 0; color: var(--ink); font-size: 12px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hw-docmeta { display: flex; align-items: center; gap: 10px; color: var(--ink-3); font-size: 10px; white-space: nowrap; }
.hw-auto { position: absolute; right: 8px; top: 8px; z-index: 5; padding: 3px 8px; border: 1px solid var(--rule); border-radius: 4px; background: var(--sheet); color: var(--ink-2); font-size: 10px; }
.hw-docname { cursor: pointer; }

/* ── L'inspection du document : ses outils, puis ses passages ───────────── */
.hw-tools { display: flex; align-items: center; gap: 7px; margin-top: 9px; }
.hw-tools .hw-tabs { margin-top: 0; }
.hw-search { flex: 1; min-width: 0; padding: 4px 8px; border: 1px solid var(--rule); border-radius: var(--r-panel); color: var(--ink-faint); font-size: 10px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hw-note-row { display: flex; align-items: baseline; gap: 10px; margin-top: 8px; }
.hw-note-row u { flex: 1; color: var(--ink-faint); font-family: var(--font-mono); font-size: 9px; letter-spacing: 0.06em; text-transform: uppercase; text-decoration: none; }
.hw-note-row span { color: var(--ink-3); font-size: 9px; }
/* La liste déborde par le bas, et c'est juste : le produit en a vingt-cinq et
   la fenêtre en montre deux. Un écran qui prétendrait tout tenir mentirait sur
   la taille du document. */
.hw-plist { flex: 1; min-height: 0; display: flex; flex-direction: column; gap: 7px; margin-top: 7px; overflow: hidden; }
.hw-pass { flex: none; border: 1px solid var(--rule); border-radius: var(--r-panel); }
.hw-pass-h { display: flex; align-items: center; gap: 8px; padding: 6px 9px; border-bottom: 1px solid var(--rule-soft); color: var(--ink-faint); font-family: var(--font-mono); font-size: 9px; }
.hw-pass-h > div { flex: 1; display: flex; align-items: center; gap: 7px; min-width: 0; }
.hw-pass-h b { color: var(--ink-2); font-weight: 400; }
.hw-pass-h em { font-style: normal; }
.hw-tag { padding: 1px 5px; border: 1px solid var(--rule); border-radius: 3px; color: var(--ink-3); }
.hw-pass-b { display: flex; gap: 9px; padding: 8px 9px; }
.hw-pass-b p { margin: 0; color: var(--ink-2); font-size: 10px; line-height: 1.55; }
.hw-pass-t { min-width: 0; flex: 1; }
.hw-pass-t .hw-mini { margin: 4px 0 6px; font-size: 9px; }
.hw-pass-t .hw-mini th, .hw-pass-t .hw-mini td { padding: 2px 5px; }
/* La légende du tableau, telle que l'ingestion l'a appariée à sa région. */
.hw-cap { display: block; color: var(--ink); font-family: var(--font-display); font-size: 11px; font-weight: 400; }
.hw-more { color: var(--ink-3); font-size: 9px; text-decoration: underline; text-underline-offset: 2px; }
/* La vignette : un seul raster, cadré par une fenêtre et décalé — la même
   mécanique que la carte de survol, donc le découpage ne peut pas dériver du
   cadre qu'il est censé montrer. */
.hw-thumb { position: relative; flex: none; overflow: hidden; border: 1px solid var(--rule); border-radius: 3px; background: #fff; }
.hw-thumb img { position: absolute; display: block; max-width: none; }
/* Le cadre de la région : un trait, sans remplissage. La vignette EST déjà la
   région ; la teinter reviendrait à colorer toute l'image. */
.hw-thumb i { position: absolute; border: 1px solid var(--amber-strong); }
.hw-thumb u { position: absolute; right: 2px; bottom: 1px; padding: 0 2px; border-radius: 2px; background: color-mix(in oklch, var(--sheet) 82%, transparent); color: var(--ink-3); font-family: var(--font-mono); font-size: 7px; text-decoration: none; }

/* ── Le mobilier commun aux écrans ───────────────────────────────────────
   Repris de l'application, en plus compact : dans 629 px on garde la
   hiérarchie et les mots, on resserre les espaces. */
.hw-kicker { color: var(--ink-faint); font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.08em; text-transform: uppercase; }
.hw-h { margin: 2px 0 2px; font-family: var(--font-display); font-size: 22px; font-weight: 400; line-height: 1.1; }
.hw-sub { color: var(--ink-3); font-size: var(--t-meta); }
.hw-head { display: flex; align-items: flex-start; gap: 12px; padding-bottom: 12px; border-bottom: 1px solid var(--rule-soft); }
.hw-head > div:first-child { min-width: 0; flex: 1; }
/* L'en-tête serré : un titre long et quatre actions. Le titre cède, les
   actions non — un bouton coupé ne se clique pas. */
.hw-head[data-tight] { padding-bottom: 9px; }
.hw-head[data-tight] .hw-h { font-size: 15px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hw-head[data-tight] .hw-sub { font-size: 10px; line-height: 1.45; }
.hw-head[data-tight] .hw-btn { padding: 5px 8px; font-size: 10px; }
.hw-acts { flex: none; display: flex; gap: 8px; }
.hw-btn {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 11px;
  border: 1px solid var(--rule); border-radius: var(--r-control);
  background: var(--sheet); color: var(--ink-2);
  font-size: var(--t-meta); white-space: nowrap;
}
.hw-btn[data-primary] { border-color: var(--cta); background: var(--cta); color: var(--on-cta); }

/* La table de documents. Les colonnes sont celles de l'application, parce que
   c'est là que se lit l'état d'un document : cherchable ou non, combien de
   passages, par quelle méthode, et l'action qui reste à faire. Les retirer
   aurait fait disparaître l'action `Index`, qui est le geste suivant. */
.hw-table { margin-top: 10px; border: 1px solid var(--rule); border-radius: var(--r-panel); overflow: hidden; }
/* Sept colonnes dans 560 px : les en-têtes se touchaient. Les valeurs sont
   courtes, ce sont les intitulés qui débordaient — d'où la gouttière plus
   large et un corps réduit sur la seule ligne d'en-tête. */
.hw-tr { display: grid; grid-template-columns: minmax(0, 1fr) 58px 46px 24px 42px 38px 74px; align-items: center; gap: 9px; padding: 8px 10px; font-size: 10px; }
.hw-tr + .hw-tr { border-top: 1px solid var(--rule-soft); }
.hw-th { background: var(--paper-2); color: var(--ink-3); font-family: var(--font-mono); font-size: 8px; letter-spacing: 0.02em; text-transform: uppercase; }
.hw-th span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hw-doc { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--ink); font-size: 10px; }
.hw-cell { color: var(--ink-3); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* L'interrupteur « Search » : éteint tant que le document n'est pas indexé. */
.hw-toggle { width: 22px; height: 12px; border-radius: 999px; background: var(--ink-faint); position: relative; opacity: 0.6; transition: background-color 0.25s, opacity 0.25s; }
.hw-toggle::after { content: ""; position: absolute; top: 2px; left: 2px; width: 8px; height: 8px; border-radius: 50%; background: var(--sheet); transition: transform 0.25s var(--ease); }
.hw-toggle[data-on="1"] { background: var(--ink); opacity: 1; }
.hw-toggle[data-on="1"]::after { transform: translateX(10px); }
/* L'action de la ligne : c'est elle qui ouvre les options d'indexation. */
.hw-act { color: var(--ink-2); cursor: pointer; white-space: nowrap; }
.hw-act:hover { color: var(--ink); }
.hw-chip {
  display: inline-block; padding: 1px 7px;
  border: 1px solid var(--rule); border-radius: 999px;
  color: var(--ink-3); font-family: var(--font-mono); font-size: 10px;
}
.hw-chip[data-state="running"] { border-color: var(--amber); color: var(--amber-ink); }
.hw-chip[data-state="done"] { border-color: var(--ink-faint); color: var(--ink-2); }

/* ── Le sélecteur de fichiers ────────────────────────────────────────────
   Il manquait, et son absence faisait apparaître le document par magie.
   Il est volontairement générique : un panneau, des emplacements, une liste.
   Copier une version précise de macOS daterait la page et se verrait faux sur
   une autre machine ; ce qu'on montre, c'est le GESTE d'aller chercher un
   fichier, pas le système de quelqu'un.

   Les trois fichiers listés sont ceux du run réel — le règlement de
   consultation, le CCTP et l'avis de marché — mais nommés par leur NATURE.
   Leurs noms d'origine portaient la référence du marché, son numéro de lot et
   sa date : sur une page publique, ça renseigne sur l'organisation qui les a
   produits. La liste reste vraie, elle cesse d'être identifiante. */
.hw-picker { width: 100%; max-width: 420px; border: 1px solid var(--rule); border-radius: 9px; background: var(--sheet); box-shadow: 0 22px 46px -22px var(--shadow); overflow: hidden; }
.hw-picker-h { display: flex; align-items: center; gap: 7px; padding: 8px 11px; border-bottom: 1px solid var(--rule); background: var(--paper-2); }
/* Les trois pastilles n'existent QUE dans ce dialogue, et c'est le seul endroit
   où elles sont vraies : c'est la boîte de dialogue du système, pas Citrail.
   Elles restent grises — le rouge, l'orange et le vert de macOS seraient les
   seules couleurs de la page hors palette, et ne diraient rien. */
.hw-lights { display: flex; gap: 6px; }
.hw-lights i { width: 10px; height: 10px; border-radius: 50%; background: var(--ink-faint); opacity: 0.55; }
.hw-picker-h b { margin: 0 auto; color: var(--ink-2); font-size: 10px; font-weight: 600; }
.hw-picker-b { display: grid; grid-template-columns: 96px 1fr; min-height: 168px; }
.hw-places { padding: 8px 6px; border-right: 1px solid var(--rule-soft); background: var(--paper-2); }
.hw-places span { display: block; padding: 3px 7px; border-radius: 3px; color: var(--ink-3); font-size: 10px; }
.hw-places span[data-on="1"] { background: var(--sheet); color: var(--ink); }
.hw-files { padding: 6px; overflow: hidden; }
.hw-file-row { display: flex; align-items: center; gap: 7px; padding: 5px 7px; border-radius: 3px; color: var(--ink-2); font-size: 10px; }
.hw-file-row u { width: 9px; height: 11px; border: 1px solid var(--ink-faint); border-radius: 1px; text-decoration: none; flex: none; }
.hw-file-row b { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 400; }
.hw-file-row em { margin-left: auto; font-style: normal; color: var(--ink-faint); }
.hw-file-row[data-picked="1"] { background: var(--cta); color: var(--on-cta); }
.hw-file-row[data-picked="1"] u { border-color: var(--on-cta); }
.hw-file-row[data-picked="1"] em { color: var(--on-cta); opacity: 0.7; }
.hw-picker-f { display: flex; justify-content: flex-end; gap: 8px; padding: 9px 11px; border-top: 1px solid var(--rule-soft); }

/* La modale d'options, telle que l'application la pose. */
.hw-veil { position: absolute; inset: 0; z-index: 20; display: grid; place-items: center; padding: 20px; background: rgba(23,23,26,0.28); opacity: 0; visibility: hidden; transition: opacity 0.25s var(--ease); }
.hw-veil[data-on="1"] { opacity: 1; visibility: visible; }
.hw-modal { width: 100%; max-width: 400px; border: 1px solid var(--rule); border-radius: 8px; background: var(--sheet); box-shadow: 0 20px 44px -24px var(--shadow); }
.hw-modal h3 { margin: 0; padding: 13px 15px; border-bottom: 1px solid var(--rule-soft); font-family: var(--font-sans); font-size: var(--t-ui); font-weight: 600; }
.hw-modal p { margin: 0; padding: 13px 15px; color: var(--ink-2); font-size: var(--t-meta); line-height: 1.6; }
.hw-modal footer { display: flex; justify-content: flex-end; gap: 9px; padding: 12px 15px; border-top: 1px solid var(--rule-soft); }
/* « Advanced options » : replié par défaut, comme dans l'application. Le
   déplier fait apparaître la seule option qui change le découpage, et c'est
   pour ça qu'elle est repliée — elle n'est pas le chemin recommandé. */
.hw-adv { padding: 0 15px 12px; }
.hw-adv-t { display: inline-flex; align-items: center; gap: 5px; color: var(--ink-2); font-size: var(--t-meta); cursor: pointer; }
.hw-adv-t::before { content: "›"; display: inline-block; transition: transform 0.22s var(--ease); }
.hw-adv[data-open="1"] .hw-adv-t::before { transform: rotate(90deg); }
/* Le dépliage se fait sur `grid-template-rows`, de 0fr à 1fr, et non sur une
   `max-height`. Deux raisons : une max-height s'anime en refaisant la mise en
   page à chaque image, et surtout elle demande un plafond en dur — 90px ici —
   qui découperait le texte dès qu'il se répartit autrement : autre police,
   fenêtre plus étroite, traduction plus longue. La grille mesure la hauteur
   réelle du contenu et ne peut donc rien couper.
   L'enfant porte le `overflow: hidden` et la marge, pour qu'aucune propriété
   de boîte ne soit animée en plus. */
.hw-adv-b { display: grid; grid-template-rows: 0fr; opacity: 0; transition: grid-template-rows 0.3s var(--ease), opacity 0.25s var(--ease); }
.hw-adv[data-open="1"] .hw-adv-b { grid-template-rows: 1fr; opacity: 1; }
.hw-adv-row { display: grid; grid-template-columns: 13px 1fr; gap: 7px; overflow: hidden; }
.hw-adv[data-open="1"] .hw-adv-row { padding-top: 9px; }
.hw-check { width: 12px; height: 12px; margin-top: 1px; border: 1px solid var(--input, var(--rule)); border-radius: 2px; position: relative; transition: background-color 0.18s, border-color 0.18s; }
.hw-check[data-on="1"] { background: var(--cta); border-color: var(--cta); }
.hw-check[data-on="1"]::after { content: ""; position: absolute; left: 3px; top: 0.5px; width: 3px; height: 6px; border: solid var(--on-cta); border-width: 0 1.5px 1.5px 0; transform: rotate(42deg); }

/* Le réglage du regroupement, qui n'apparaît qu'une fois la case cochée : il
   n'existe pas tant que l'option est éteinte, donc il ne s'affiche pas grisé.
   Même technique de dépliage que ci-dessus — la grille, pas une hauteur. */
.hw-merge { display: grid; grid-template-rows: 0fr; opacity: 0; transition: grid-template-rows 0.3s var(--ease), opacity 0.25s var(--ease); }
.hw-adv[data-merge="1"] .hw-merge { grid-template-rows: 1fr; opacity: 1; }
.hw-merge-in { overflow: hidden; }
.hw-adv[data-merge="1"] .hw-merge-in { padding-top: 11px; }
.hw-lab { display: block; margin-bottom: 4px; color: var(--ink-faint); font-family: var(--font-mono); font-size: 9px; letter-spacing: 0.06em; text-transform: uppercase; }
/* L'interrupteur à deux positions : la décision de fusion. */
.hw-seg { display: inline-flex; border: 1px solid var(--rule); border-radius: 4px; overflow: hidden; }
.hw-seg span { padding: 4px 10px; color: var(--ink-3); font-size: 10px; cursor: pointer; transition: background-color 0.18s, color 0.18s; }
.hw-seg span[data-on="1"] { background: var(--cta); color: var(--on-cta); }
.hw-hint { margin-top: 5px; color: var(--ink-3); font-size: 9px; }
.hw-fields { display: grid; grid-template-columns: 1fr 1fr; gap: 9px; margin-top: 10px; }
.hw-field input, .hw-field-box { display: block; width: 100%; padding: 5px 8px; border: 1px solid var(--input, var(--rule)); border-radius: 3px; background: var(--sheet); color: var(--ink); font-size: 10px; }
.hw-field span { display: block; margin-top: 4px; color: var(--ink-faint); font-size: 9px; line-height: 1.4; }

/* La barre de progression de la ligne, pendant l'indexation. */
/* `display: flex` l'emportait sur le `display: none` que le navigateur applique
   à [hidden] : la barre restait visible sous le compte final. Un `display` posé
   par une classe bat toujours le style par défaut de l'attribut, et c'est le
   piège classique de `hidden` sur un élément qu'on a mis en flex. */
.hw-prog { display: flex; align-items: center; gap: 5px; }
.hw-prog[hidden] { display: none; }
.hw-cell[hidden] { display: none; }
.hw-prog i { flex: 1; height: 4px; border-radius: 999px; background: var(--rule); overflow: hidden; }
/* Le remplissage s'anime en `scaleX`, pas en `width` : une largeur animée
   refait la mise en page à chaque image, alors qu'une transformation reste
   sur le compositeur. `--p` porte donc une fraction de 0 à 1, pas un
   pourcentage — c'est ce que `scaleX` attend. */
.hw-prog i::after { content: ""; display: block; height: 100%; width: 100%; background: var(--ink); transform: scaleX(var(--p, 0)); transform-origin: left; transition: transform 0.5s linear; }
.hw-prog b { color: var(--ink-3); font-weight: 400; font-size: 9px; }
.hw-adv-b b { display: block; color: var(--ink); font-size: 10px; font-weight: 500; }
.hw-adv-b span { color: var(--ink-3); font-size: 9px; line-height: 1.5; }

/* ── La modale « Processing details » ────────────────────────────────────
   Ouverte depuis la bulle de la ligne pendant que l'indexation tourne. Elle
   porte le journal horodaté — la preuve que le produit rend des comptes sur
   ce qu'il fait, et pas seulement sur ce qu'il a trouvé. */
.hw-facts { display: grid; grid-template-columns: 1fr 1fr; gap: 9px 14px; padding: 12px 15px; }
.hw-fact u { display: block; color: var(--ink-faint); font-family: var(--font-mono); font-size: 9px; letter-spacing: 0.06em; text-transform: uppercase; text-decoration: none; }
.hw-fact b { display: block; margin-top: 2px; color: var(--ink); font-size: 10px; font-weight: 400; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hw-log { margin: 0 15px 14px; padding: 9px 11px; border-radius: 3px; background: var(--paper-2); color: var(--ink-2); font-family: var(--font-mono); font-size: 9px; line-height: 1.8; }
.hw-log span { display: block; }
.hw-log u { color: var(--ink-faint); text-decoration: none; margin-right: 8px; }

/* ── Le canevas du workflow ──────────────────────────────────────────────
   Les huit étages ne sont pas une liste mais un graphe : c'est ainsi que le
   produit les montre, et l'enchaînement se lit dans les flèches. À 629 px on
   n'en voit que deux à la fois — d'où le bouton d'ajustement, qui recule pour
   montrer la chaîne entière. Ce recul EST le geste : on passe du détail d'une
   étape à la forme du pipeline. */
.hw-canvas { position: relative; flex: 1; min-height: 0; display: grid; grid-template-columns: 1fr 128px; gap: 10px; margin-top: 8px; }
.hw-flow { position: relative; overflow: hidden; border: 1px solid var(--rule-soft); border-radius: var(--r-panel); background: var(--paper-2); }
/* Le centrage vertical vient du flex, pas d'un `translateY(-50%)` : ce dernier
   se calcule sur la hauteur NON mise à l'échelle, donc les nœuds tombaient vers
   le bas dès qu'on reculait. La transformation ne porte plus que sur l'axe
   horizontal et l'échelle, autour du milieu gauche — le milieu ne bouge plus. */
/* Aucun écart entre un nœud et son lien : le lien OCCUPE l'espace qui les
   sépare, d'un bord à l'autre. Avec un `gap`, la flèche flottait au milieu sans
   toucher personne — deux blocs voisins mais pas reliés. */
.hw-nodes { position: absolute; inset: 0 auto 0 0; display: flex; align-items: center; gap: 0; padding: 0 14px; transform-origin: 0 50%; transition: transform 0.6s var(--ease); }
.hw-node { position: relative; flex: none; width: 168px; padding: 8px 9px; border: 1px solid var(--rule); border-radius: 5px; background: var(--sheet); transition: border-color 0.25s; }
.hw-node[data-state="running"] { border-color: var(--ink); }
/* Les prises, aux deux bouts de la chaîne d'une étape : elles disent où un lien
   s'attache, et ne s'affichent donc pas là où il n'y en a pas. */
.hw-node::before, .hw-node::after { content: ""; position: absolute; top: 50%; width: 5px; height: 5px; margin-top: -3px; border: 1px solid var(--rule); border-radius: 50%; background: var(--sheet); }
.hw-node::before { left: -3px; }
.hw-node::after { right: -3px; }
.hw-node:first-of-type::before, .hw-node:last-of-type::after { display: none; }
/* Le cadre qui se dessine autour de l'étape en cours.
 *
 * Il se trace au lieu d'apparaître : le trait qui fait le tour du bloc dit
 * « c'est ici que ça se passe, maintenant », là où une bordure allumée d'un coup
 * ne dirait que « ceci est sélectionné ». Les dimensions sont posées en JS au
 * moment où l'étape démarre — un `rect` SVG ne connaît pas les pourcentages de
 * son hôte, et la hauteur d'un nœud dépend de ce qu'il affiche. */
.hw-ring { position: absolute; inset: -4px; overflow: visible; opacity: 0; transition: opacity 0.25s var(--ease); pointer-events: none; }
/* 0,34 s pour faire le tour, quand une étape en dure 0,43 : le carré doit se
   refermer AVANT que l'étape passe en « Success », sans quoi il s'efface en
   cours de route et ne se referme jamais. */
.hw-ring rect { fill: none; stroke: var(--ink); stroke-width: 1.5; transition: stroke-dashoffset 0.34s var(--ease); }
.hw-node[data-state="running"] .hw-ring { opacity: 1; }
.hw-node-h { display: flex; align-items: center; gap: 6px; margin-bottom: 5px; }
.hw-node-h u { flex: 1; color: var(--ink-faint); font-family: var(--font-mono); font-size: 8px; letter-spacing: 0.06em; text-transform: uppercase; text-decoration: none; }
.hw-node b { display: block; margin-bottom: 5px; color: var(--ink); font-size: 11px; font-weight: 600; }
.hw-node dl { display: grid; grid-template-columns: auto 1fr; gap: 1px 8px; margin: 0; font-size: 9px; }
.hw-node dt { color: var(--ink-faint); }
.hw-node dd { margin: 0; color: var(--ink-2); text-align: right; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hw-badge { padding: 1px 6px; border-radius: 999px; border: 1px solid var(--rule); color: var(--ink-3); font-size: 8px; white-space: nowrap; }
.hw-badge[data-state="running"] { background: var(--cta); border-color: var(--cta); color: var(--on-cta); }
.hw-badge[data-state="done"] { border-color: var(--ink-faint); color: var(--ink-2); }
/* La flèche entre deux étages : un tireté et une pointe, en SVG.
 *
 * Une flèche, pas un trait : la pointe dit le sens, le tireté dit qu'il y a un
 * FLUX. Sans pointe, on ne lit qu'une chaîne de tirets qui pourrait couler dans
 * les deux sens. C'est la convention de React Flow, et c'est celle de
 * l'application — la reprendre évite d'inventer un vocabulaire.
 *
 * Le lien fait toute la largeur de l'écart : il part du bord du nœud précédent
 * et sa pointe touche le suivant. C'est ce qui les rend reliés plutôt que
 * simplement voisins.
 *
 * Le défilement porte du sens et ne tourne donc pas en permanence : il ne
 * s'anime que sur le lien qui transmet à cet instant. Un tireté animé partout
 * serait une décoration, et une décoration qui bouge est pire qu'une décoration.
 *
 * `stroke-dashoffset` ne touche ni la mise en page ni la composition — c'est du
 * pur dessin. Et le décalage vaut deux fois la période du motif, sans quoi la
 * boucle sauterait à chaque tour. */
.hw-link {
  flex: none; width: 46px; height: 12px; overflow: visible;
  color: var(--ink-faint); opacity: 0.55;
  transition: color 0.3s var(--ease), opacity 0.3s var(--ease);
}
.hw-link line { stroke: currentColor; stroke-width: 1.5; stroke-dasharray: 4 3; }
.hw-link polygon { fill: currentColor; }
.hw-link[data-on="1"], .hw-link[data-flow="1"] { color: var(--ink); opacity: 1; }
.hw-link[data-flow="1"] line { animation: hw-dash 0.62s linear infinite; }
@keyframes hw-dash { to { stroke-dashoffset: -14; } }

@media (prefers-reduced-motion: reduce) {
  .hw-link[data-flow="1"] line { animation: none; }
  .hw-ring rect { transition: none; }
}

/* Le volet de droite : l'étape en cours, dans le détail. */
.hw-rail-d { overflow: hidden; padding-left: 10px; border-left: 1px solid var(--rule-soft); font-size: 9px; }
.hw-rail-d u { display: block; margin-top: 7px; color: var(--ink-faint); font-family: var(--font-mono); font-size: 8px; letter-spacing: 0.06em; text-transform: uppercase; text-decoration: none; }
.hw-rail-d b { display: block; color: var(--ink); font-weight: 400; overflow-wrap: anywhere; }
.hw-rail-d > b:first-child { margin-bottom: 4px; font-size: 12px; font-weight: 600; }

/* Les commandes de zoom, en bas à gauche du canevas — dont l'ajustement. */
.hw-zoom { position: absolute; left: 8px; bottom: 8px; z-index: 5; display: flex; flex-direction: column; border: 1px solid var(--rule); border-radius: 4px; overflow: hidden; background: var(--sheet); }
.hw-zoom span { display: grid; place-items: center; width: 20px; height: 18px; color: var(--ink-3); font-size: 11px; cursor: pointer; }
.hw-zoom span + span { border-top: 1px solid var(--rule-soft); }
.hw-zoom span:hover { background: var(--paper-2); color: var(--ink); }

/* ── Acte 1 : l'ingestion ─────────────────────────────────────────────────
   Huit étages, deux fournisseurs, relevés sur l'enregistrement. Ils s'allument
   dans l'ordre réel et affichent la sortie réelle de CE document. */
.hw-stage {
  display: grid; grid-template-columns: 16px 1fr auto;
  align-items: baseline; gap: 4px 10px;
  padding: 7px 4px;
  border-bottom: 1px solid var(--rule-soft);
  opacity: 0.42;
  transition: opacity 0.35s var(--ease);
}
.hw-stage:last-child { border-bottom: 0; }
.hw-stage[data-state="running"], .hw-stage[data-state="done"] { opacity: 1; }

/* La pastille d'état : vide en attente, ambre pendant, encre une fois faite.
   L'ambre ne désigne pas ici une preuve mais le travail en cours — c'est le
   seul endroit de la page où elle a ce sens, et il est borné à l'acte 1. */
.hw-dot {
  width: 7px; height: 7px; margin-top: 5px; border-radius: 50%;
  border: 1px solid var(--ink-faint);
}
.hw-stage[data-state="running"] .hw-dot { border-color: var(--amber); background: var(--amber); animation: hw-pulse 1.1s ease-in-out infinite; }
.hw-stage[data-state="done"] .hw-dot { border-color: var(--ink); background: var(--ink); }
@keyframes hw-pulse { 50% { opacity: 0.35; } }

.hw-name { color: var(--ink); font-size: var(--t-ui); }
.hw-by { margin-left: 7px; color: var(--ink-faint); font-size: var(--t-meta); }
.hw-took { color: var(--ink-3); font-family: var(--font-mono); font-size: var(--t-meta); }
.hw-out {
  grid-column: 2 / -1;
  color: var(--ink-3); font-family: var(--font-mono); font-size: var(--t-meta);
  opacity: 0; transform: translateY(-2px);
  transition: opacity 0.3s var(--ease), transform 0.3s var(--ease);
}
.hw-stage[data-state="done"] .hw-out { opacity: 1; transform: none; }

/* ── Actes 2 et 3 : la question, puis la preuve ──────────────────────────
   L'écran se scinde quand le visiteur ouvre une citation : la réponse se
   resserre à gauche, la page arrive à droite. C'est le geste que le produit
   fait vraiment, et la mise en page le refait à l'échelle de la fenêtre. */
.hw-split { display: grid; grid-template-columns: 1fr 0fr; gap: 0; flex: 1; min-height: 0; transition: grid-template-columns 0.42s var(--ease); }
.hw-split[data-open="1"] { grid-template-columns: 1fr 260px; }
/* Le volet de preuve réserve 260 px fixes. Sous ~700 px de large, la fenêtre
   n'a plus de quoi les donner sans écraser le fil de conversation à une
   soixantaine de pixels, où la question se rend un mot par ligne. En dessous,
   le partage ne s'ouvre donc pas : la réponse et ses citations tiennent toute
   la largeur, ce qui reste vrai et lisible. Ouvrir la preuve est une
   démonstration de grand écran. */
@media (max-width: 700px) {
  .hw-split[data-open="1"] { grid-template-columns: 1fr 0fr; }
  .hw-split[data-open="1"] .hw-panel { border-left: 0; }
  .hw-split[data-open="1"] + .hw-composer { left: 50%; width: min(calc(100% - 34px), 620px); }
}

/* Le fil défile. La réponse est plus longue que la fenêtre — c'est le cas dans
   le produit aussi — et il faut pouvoir y redescendre à la main. */
.hw-thread { min-width: 0; min-height: 0; margin-bottom: 88px; overflow-y: auto; overflow-x: hidden; padding-right: 8px; scrollbar-width: thin; scrollbar-color: var(--rule) transparent; }
.hw-thread::-webkit-scrollbar { width: 6px; }
.hw-thread::-webkit-scrollbar-thumb { border-radius: 3px; background: var(--rule); }
.hw-ask { padding: 8px 10px; border: 1px solid var(--rule); border-radius: var(--r-panel); background: var(--paper-2); color: var(--ink); font-size: var(--t-meta); }
.hw-ask[hidden] { display: none; }

/* ── La session vide, puis l'attente ─────────────────────────────────────
   Le produit ouvre une conversation sur sa propre promesse. On la reprend
   telle quelle : c'est la même phrase que porte le titre de la page, et
   l'entendre deux fois — une fois comme promesse, une fois dans le produit —
   n'est pas une répétition mais une vérification. */
.hw-hello { display: grid; place-content: center; justify-items: center; gap: 5px; height: 100%; padding: 0 18px; text-align: center; }
.hw-hello[hidden] { display: none; }
.hw-hello h3 { margin: 0; color: var(--ink); font-family: var(--font-display); font-size: 21px; font-weight: 400; line-height: 1.22; }
.hw-hello h3 em { font-style: normal; background: linear-gradient(transparent 62%, var(--amber) 62%); }
.hw-hello-s { max-width: 40ch; margin: 2px 0 0; color: var(--ink-3); font-size: var(--t-meta); line-height: 1.5; }
.hw-ready { display: flex; align-items: center; gap: 6px; margin: 6px 0 0; color: var(--ink-3); font-size: 10px; }
.hw-ready i { width: 5px; height: 5px; border-radius: 50%; background: var(--ink-3); }
/* Ce que l'agent fait, pendant qu'il le fait. Une étape en cours tourne, une
   étape faite porte sa coche — et ses sujets restent lisibles en dessous : ce
   sont eux qu'on a approuvés, on doit pouvoir vérifier qu'ils n'ont pas changé. */
.hw-steps { margin-top: 10px; }
.hw-steps[hidden] { display: none; }
.hw-step { margin-bottom: 7px; }
.hw-step[hidden] { display: none; }
.hw-step-h { display: flex; align-items: center; gap: 8px; color: var(--ink-3); font-size: var(--t-meta); }
.hw-step-h b { color: var(--ink); font-weight: 600; }
.hw-step-h i { flex: none; width: 11px; height: 11px; border: 1.5px solid var(--rule); border-top-color: var(--ink-3); border-radius: 50%; animation: hw-spin 0.9s linear infinite; }
.hw-step[data-state="done"] .hw-step-h i { border: 0; border-radius: 0; animation: none; color: var(--ink); font-style: normal; font-size: 11px; line-height: 1; }
.hw-step[data-state="done"] .hw-step-h i::before { content: "✓"; }
.hw-step-t { margin: 3px 0 0 19px; color: var(--ink-3); font-size: 10px; }
.hw-step-t span { display: block; }
@keyframes hw-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .hw-step-h i { animation: none; } }

/* ── La demande d'accord ─────────────────────────────────────────────────
   Le produit s'arrête ici et demande. C'est l'écran qui dit le plus sur ce
   qu'il est : rien n'est cherché tant que l'on n'a pas vu ce qui sera cherché,
   et avec quelle méthode. */
.hw-approve { margin-top: 10px; border: 1px solid var(--rule); border-radius: 6px; background: var(--sheet); }
.hw-approve[hidden] { display: none; }
.hw-approve-h { display: flex; gap: 9px; padding: 8px 11px; border-bottom: 1px solid var(--rule-soft); }
.hw-shield { display: grid; place-items: center; flex: none; width: 18px; height: 18px; border: 1px solid var(--rule); border-radius: 4px; background: var(--paper-2); color: var(--ink-2); font-size: 9px; font-style: normal; }
.hw-approve-t { display: flex; align-items: center; gap: 7px; }
.hw-approve-t b { color: var(--ink); font-size: 11px; font-weight: 600; }
/* La mention est une étiquette, pas un titre : sans taille propre elle héritait
   du corps de l'écran et pesait plus lourd que ce qu'elle qualifie. */
.hw-approve-t .hw-tag { font-family: var(--font-mono); font-size: 8px; letter-spacing: 0.06em; text-transform: uppercase; }
.hw-approve-h p { margin: 2px 0 0; color: var(--ink-3); font-size: 9px; }
.hw-approve-b { padding: 8px 11px; }
.hw-approve-b > b { display: block; color: var(--ink); font-size: 10px; font-weight: 600; }
.hw-approve-b > b:not(:first-child) { margin-top: 7px; }
.hw-approve-b > span { display: block; margin-bottom: 4px; color: var(--ink-3); font-size: 9px; }
.hw-topic { display: flex; align-items: center; gap: 8px; margin-bottom: 3px; padding: 3px 8px; border: 1px solid var(--rule); border-radius: var(--r-panel); background: var(--paper-2); }
.hw-topic span { flex: 1; min-width: 0; color: var(--ink); font-size: 10px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hw-topic u { color: var(--ink-faint); font-size: 9px; text-decoration: none; }
.hw-add { display: block; color: var(--ink-3); font-size: 10px; }
.hw-modes { display: grid; grid-template-columns: 1fr 1fr; gap: 7px; }
.hw-mode { padding: 5px 8px; border: 1px solid var(--rule); border-radius: var(--r-panel); }
.hw-mode[data-on="1"] { border-color: var(--ink-3); background: var(--paper-2); }
.hw-mode b { display: block; color: var(--ink); font-size: 10px; font-weight: 600; }
.hw-mode span { display: block; margin-top: 2px; color: var(--ink-3); font-size: 9px; }
.hw-mode-h { display: flex; align-items: baseline; gap: 6px; }
.hw-mode-h u { color: var(--ink-faint); font-family: var(--font-mono); font-size: 8px; letter-spacing: 0.06em; text-transform: uppercase; text-decoration: none; }
.hw-approve-f { display: flex; align-items: center; gap: 10px; padding: 7px 11px; border-top: 1px solid var(--rule-soft); }
.hw-approve-f .hw-btn { padding: 5px 9px; font-size: 10px; }
.hw-approve-f > span:first-child { flex: 1; min-width: 0; color: var(--ink-3); font-size: 9px; }
.hw-cancel { color: var(--ink-2); font-size: 10px; }

/* ── Le composeur ────────────────────────────────────────────────────────
   Au premier regard, le champ habite la conversation : assez central pour
   inviter la question, assez bas pour laisser la promesse respirer. Après
   l'envoi, le même objet descend au bord de la session. Une largeur mesurée
   laisse le panneau de preuve et le fil lisibles autour de lui. */
.hw-composer {
  position: absolute; top: calc(100% - 68px); left: 50%; z-index: 4;
  width: min(calc(100% - 52px), 520px);
  margin: 0; padding: 9px 10px 8px;
  border: 1px solid var(--rule); border-radius: 6px; background: var(--sheet);
  transform: translate(-50%, -50%);
  transition: top 0.52s var(--ease), left 0.42s var(--ease), width 0.42s var(--ease), box-shadow 0.28s var(--ease);
}
.hw-screen[data-chat-state="empty"] .hw-composer {
  top: 66%;
  width: min(calc(100% - 72px), 500px);
  box-shadow: 0 14px 30px -25px var(--shadow);
}
.hw-split[data-open="1"] + .hw-composer {
  left: calc((100% - 260px) / 2);
  width: min(calc(100% - 300px), 500px);
}
.hw-composer-f { min-height: 15px; color: var(--ink-faint); font-size: var(--t-meta); }
.hw-composer-f[data-typed="1"] { color: var(--ink); }
.hw-composer-f[data-typed="1"]::after { content: ""; display: inline-block; width: 1px; height: 11px; margin-left: 1px; background: var(--ink); vertical-align: -1px; animation: hw-caret 1s steps(1) infinite; }
@keyframes hw-caret { 50% { opacity: 0; } }
.hw-composer-r { display: flex; align-items: center; gap: 10px; margin-top: 9px; color: var(--ink-3); font-size: 10px; }
.hw-scope { flex: 1; }
.hw-effort { display: inline-flex; align-items: center; gap: 4px; line-height: 1; white-space: nowrap; }
.hw-effort b { color: var(--ink); font-weight: 500; }
.hw-effort-chevron { width: 5px; height: 5px; margin: -2px 1px 0 1px; border-right: 1px solid currentColor; border-bottom: 1px solid currentColor; transform: rotate(45deg); }
.hw-send { display: grid; place-items: center; width: 20px; height: 20px; border-radius: 4px; background: var(--paper-2); color: var(--ink-2); font-size: 11px; }
.hw-send[data-sent="1"] { background: var(--cta); color: var(--on-cta); }
.hw-note { position: absolute; right: 14px; bottom: 18px; left: 14px; z-index: 3; color: var(--ink-faint); font-size: 9px; text-align: center; }
.hw-split[data-open="1"] + .hw-composer + .hw-note { right: calc(260px + 14px); }
@media (max-width: 700px) { .hw-split[data-open="1"] + .hw-composer + .hw-note { right: 14px; } }
@media (prefers-reduced-motion: reduce) { .hw-composer { transition: none; } }
.hw-facets { display: flex; flex-wrap: wrap; gap: 5px; margin: 9px 0; }
.hw-facet { padding: 1px 7px; border: 1px solid var(--rule); border-radius: 999px; color: var(--ink-3); font-family: var(--font-mono); font-size: 10px; opacity: 0; transform: translateY(3px); transition: opacity 0.25s var(--ease), transform 0.25s var(--ease); }
.hw-facet[data-on="1"] { opacity: 1; transform: none; }

/* La réponse. Les blocs arrivent un par un, comme le streaming du produit.
   Le fil reste volontairement plus calme que la question : la preuve vient
   des citations et du panneau, pas d'un mur d'encre. */
.hw-answer { font-size: var(--t-meta); line-height: 1.62; color: var(--ink-3); }
.hw-answer > * { opacity: 0; transform: translateY(3px); transition: opacity 0.3s var(--ease), transform 0.3s var(--ease); }
.hw-answer > [data-on="1"] { opacity: 1; transform: none; }
.hw-answer h4 { margin: 11px 0 4px; color: var(--ink-2); font-family: var(--font-sans); font-size: var(--t-meta); font-weight: 600; }
.hw-answer p, .hw-answer li { margin: 0 0 6px; }
/* Le gras que le modèle a écrit — « Points de convergence : » — appuie sur
   l'encre, il ne change pas de corps : c'est un accent dans la phrase. */
.hw-answer strong { color: var(--ink-2); font-weight: 600; }
.hw-answer ul { margin: 0 0 6px; padding-left: 15px; }
.hw-mini { width: 100%; border-collapse: collapse; margin: 5px 0 8px; font-size: 10px; }
.hw-mini th, .hw-mini td { padding: 3px 6px; border: 1px solid var(--rule-soft); text-align: left; }
.hw-mini th { background: var(--paper-2); color: var(--ink-3); font-weight: 500; }

/* La puce de citation : `p.12`, exactement comme dans l'application. Elle prend
   le sceau quand elle est active, et jamais autrement. */
/* Pas de marge propre : la puce s'insère au milieu de la phrase, et le texte
   du modèle porte déjà ses espaces. En ajouter une creusait un trou avant
   chaque citation. */
.hw-cite {
  display: inline-block; padding: 0 3px; border-radius: 2px;
  border-bottom: 1px solid var(--rule);
  color: var(--ink-3); font-size: 0.86em; cursor: pointer;
  transition: background-color 0.15s, color 0.15s, border-color 0.15s, box-shadow 0.15s, transform 0.15s;
}
.hw-cite:hover, .hw-cite[data-active="1"] { border-color: var(--amber-strong); background: color-mix(in srgb, var(--amber) 34%, transparent); color: var(--ink); box-shadow: 0 1px 0 var(--amber-strong); transform: translateY(-.5px); }
.hw-cite:focus-visible { outline: 2px solid var(--amber-strong); outline-offset: 3px; }

/* Ce que la réponse n'établit pas : le produit l'écrit lui-même, la fenêtre le
   montre tel quel. Sans cadre, le passage reste dans le fil au lieu de devenir
   une boîte concurrente du verdict de preuve. */
.hw-limits { margin-top: 10px; padding: 7px 0 4px; border: 0; border-radius: 0; }
.hw-limits b { display: block; margin-bottom: 3px; color: var(--ink-2); font-size: 10px; font-weight: 600; }
.hw-limits span { color: var(--ink-3); font-size: 10px; line-height: 1.5; }

/* Le panneau de preuve, et la page qui s'y pose. */
/* Le filet n'apparaît qu'avec le panneau : fermé, sa colonne fait zéro, mais
   sa bordure restait tracée — un trait vertical sans rien derrière. */
.hw-panel { min-width: 0; overflow: hidden; }
.hw-split[data-open="1"] .hw-panel { border-left: 1px solid var(--rule); }
.hw-panel-in { width: 260px; height: 100%; display: flex; flex-direction: column; padding-left: 11px; }
.hw-panel-h { display: flex; align-items: baseline; gap: 6px; padding-bottom: 7px; border-bottom: 1px solid var(--rule-soft); }
.hw-panel-h b { color: var(--ink); font-size: 11px; font-weight: 600; }
.hw-panel-h span { margin-left: auto; color: var(--ink-3); font-family: var(--font-mono); font-size: 10px; }
/* La page n'est PAS montrée en entier. Dans 260 px, un A4 complet fait 368 px
   de haut : le passage y est illisible et le cadre flotte sur une vignette,
   ce qui ne prouve rien. Le produit descend jusqu'au passage ; la fenêtre fait
   pareil, en cadrant la page autour de la région citée. L'image est posée en
   position absolue et décalée — un seul raster, aucun découpage à produire. */
.hw-page { position: relative; flex: 1; min-height: 0; margin-top: 8px; overflow: hidden; border: 1px solid var(--rule); border-radius: 3px; background: #fff; }
.hw-page img { position: absolute; display: block; max-width: none; }
/* Le cadre du passage : le même que pose le panneau de preuve du produit. */
.hw-frame {
  position: absolute; border: 2px solid var(--highlight-strong, var(--amber-strong));
  background: color-mix(in oklch, var(--amber) 12%, transparent);
  border-radius: 3px;
  opacity: 0; transform: scale(0.985);
  transition: opacity 0.3s var(--ease), transform 0.3s var(--ease);
}
.hw-frame[data-on="1"] { opacity: 1; transform: none; }
.hw-credits { margin-top: 7px; color: var(--ink-3); font-family: var(--font-mono); font-size: 10px; overflow-wrap: anywhere; }

/* ── Le sceau et les gestes, au bas de la réponse ────────────────────────
   « Verified » ne décore pas : il dit que les citations de cette réponse ont
   été retrouvées dans les sources. Et « Review evidence » est ce qu'on fait
   quand on ne le croit pas sur parole. Une seule barre garde ces gestes
   attachés à la réponse, juste au-dessus du composeur. */
.hw-verdict { display: flex; align-items: center; gap: 10px; margin-top: 10px; opacity: 0; transition: opacity 0.3s var(--ease); }
.hw-verdict[hidden] { display: none; }
.hw-verdict[data-on="1"] { opacity: 1; }
.hw-verified { display: inline-flex; align-items: center; gap: 5px; padding: 2px 8px; border-radius: 999px; background: color-mix(in oklch, var(--amber) 22%, transparent); color: var(--amber-ink); font-size: 10px; }
.hw-actions { position: relative; z-index: 5; display: flex; align-items: center; flex: 1; min-width: 0; gap: 12px; margin-top: 0; padding-top: 0; border-top: 0; color: var(--ink-3); font-size: 10px; }
.hw-actions span { display: inline-flex; align-items: center; gap: 5px; min-height: 20px; padding: 2px 3px; border: 1px solid transparent; border-radius: 3px; cursor: pointer; transition: color .18s var(--ease), background-color .18s var(--ease), border-color .18s var(--ease); }
.hw-actions span:hover { color: var(--ink); background: var(--paper-2); border-color: var(--rule); }
.hw-actions span:hover .hw-icon { transform: translateY(-.5px); }
.hw-actions .hw-icon { transition: transform .18s var(--ease); }
.hw-review { margin-left: auto; }
.hw-icon { flex: none; width: 12px; height: 12px; fill: none; stroke: currentColor; stroke-width: 1.4; stroke-linecap: round; stroke-linejoin: round; }
.hw-verified .hw-icon { width: 11px; height: 11px; }

/* La preuve se révèle en deux temps : la citation attire brièvement l'œil,
   puis le volet Evidence arrive. La grille garde son propre mouvement ; ces
   deux signaux lui donnent une lecture éditoriale sans animation permanente. */
.hw-split[data-proof="1"] .hw-cite[data-active="1"] { animation: hw-proof-cite .64s .04s var(--ease) both; }
.hw-split[data-proof="1"] .hw-panel { animation: hw-proof-panel .48s .12s var(--ease) both; transform-origin: left center; }
@keyframes hw-proof-cite {
  0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--amber) 0%, transparent); }
  38% { box-shadow: 0 2px 0 var(--amber-strong), 0 0 0 3px color-mix(in srgb, var(--amber) 18%, transparent); }
  100% { box-shadow: 0 1px 0 var(--amber-strong), 0 0 0 0 color-mix(in srgb, var(--amber) 0%, transparent); }
}
@keyframes hw-proof-panel {
  0% { opacity: .72; transform: translateX(6px); }
  100% { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .hw-split[data-proof="1"] .hw-cite[data-active="1"], .hw-split[data-proof="1"] .hw-panel { animation: none; }
}

/* ── L'aperçu d'une citation ─────────────────────────────────────────────
   Il montre LE PASSAGE : son contenu rendu à sa forme, la page d'où il vient,
   la facette à laquelle il répond, et son score. Un survol suffit à voir sur
   quoi la phrase s'appuie — c'est toute la promesse de la page, en un geste. */
.hw-hover {
  position: absolute; z-index: 25; width: 292px;
  border: 1px solid var(--rule); border-radius: 6px;
  background: var(--sheet); box-shadow: 0 12px 28px -16px var(--shadow);
  padding: 8px 9px; pointer-events: none;
  opacity: 0; transform: translateY(3px);
  transition: opacity 0.18s var(--ease), transform 0.18s var(--ease);
}
.hw-hover[data-on="1"] { opacity: 1; transform: none; }
.hw-hover-h { display: flex; align-items: center; gap: 6px; padding-bottom: 6px; color: var(--ink-3); font-size: 10px; }
.hw-hover-h b { flex: 1; min-width: 0; color: var(--ink-2); font-family: var(--font-mono); font-weight: 400; }
.hw-hover-i { flex: none; font-style: normal; color: var(--ink-faint); }
/* Le contenu est borné en hauteur, pas tronqué dans la donnée : un passage de
   quarante lignes se lit dans le panneau, pas dans une bulle de survol. */
.hw-hover-b { max-height: 168px; overflow: hidden; }
.hw-hover-b p { margin: 0; color: var(--ink-2); font-size: 10px; line-height: 1.5; }
.hw-hover-b .hw-mini { margin: 0; font-size: 9px; }
.hw-hover-b .hw-mini th, .hw-hover-b .hw-mini td { padding: 2px 5px; }
.hw-hover-f { margin-top: 7px; color: var(--ink-3); font-size: 9px; }
.hw-hover-f span { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hw-crop { position: relative; overflow: hidden; border: 1px solid var(--rule-soft); border-radius: 3px; background: #fff; }
.hw-crop img { position: absolute; max-width: none; }

/* L'agrandissement d'une région, ouvert en cliquant sa vignette. Il n'ajoute
   aucune information : la même image, au même cadrage, à une taille où elle se
   lit. C'est le geste qui compte — la preuve se vérifie à l'œil, ici, sans
   quitter la liste. */
.hw-region {
  position: absolute; z-index: 30;
  padding: 8px;
  border: 1px solid var(--rule); border-radius: 6px;
  background: var(--sheet);
  opacity: 0; transform: scale(0.98);
  transition: opacity 0.2s var(--ease), transform 0.2s var(--ease);
  pointer-events: none;
}
.hw-region[data-on="1"] { opacity: 1; transform: none; pointer-events: auto; }
.hw-region-h { display: flex; align-items: baseline; gap: 10px; margin-bottom: 6px; color: var(--ink-3); font-size: 10px; }
.hw-region-h b { flex: 1; color: var(--ink); font-weight: 600; }
.hw-region-h span { font-family: var(--font-mono); }

/* La main rendue au visiteur : une ligne, une fois, puis elle s'efface. */
.hw-yours {
  position: absolute; left: 14px; bottom: 12px; z-index: 26;
  padding: 4px 9px;
  border: 1px solid var(--amber); border-radius: 999px;
  background: var(--sheet); color: var(--amber-ink);
  font-family: var(--font-mono); font-size: 10px;
  opacity: 0; transition: opacity 0.4s var(--ease);
}
.hw-yours[data-on="1"] { opacity: 1; }

/* Le compte final, qui n'apparaît qu'une fois les huit étages passés. */
.hw-total {
  display: flex; flex-wrap: wrap; gap: 6px 18px;
  margin-top: 12px; padding-top: 12px;
  border-top: 1px solid var(--rule);
  color: var(--ink-2); font-family: var(--font-mono); font-size: var(--t-meta);
  opacity: 0; transition: opacity 0.45s var(--ease);
}
.hw-total[data-shown="1"] { opacity: 1; }
.hw-total b { color: var(--ink); font-weight: 400; }

@media (prefers-reduced-motion: reduce) {
  .hw-stage, .hw-out, .hw-total { opacity: 1; transform: none; transition: none; }
  .hw-dot { animation: none; }
}

/* ── The gesture ──────────────────────────────────────────────────────────
   The right half of the hero replays the one move that defines the product:
   a claim carries a citation, the citation is clicked, the source page lays
   itself down, and the passage that proves the claim is boxed on it.

   Three rules govern what may appear here.

   The sheet is a SCHEMATIC, not a screenshot. Its lines are rules, not
   lettering, and it never grows window chrome, a title bar or a toolbar. The
   brief allows an authored diagram of a mechanism and forbids a reconstructed
   capture of an interface; the distance between the two is exactly the chrome.

   The amber rectangle is placed from --hl-x/y/w/h, which are the ingestion
   bbox expressed as percentages of the page. It is the only element here that
   claims to be a measurement, and it is the only one that carries one. Corner
   registration marks are deliberately absent until real coordinates exist:
   four marks that designate nothing would be decor, which DESIGN.md forbids.

   One 6s loop drives every part, so the beats cannot drift apart. No JS. */
.hero-gesture { display: flex; flex-direction: column; align-items: center; }

.gesture-answer {
  width: 100%; max-width: 400px;
  padding: 16px 18px;
  border: 1px solid var(--rule); border-radius: var(--r-panel);
  background: var(--sheet);
  opacity: 0; animation: gesture-answer 6s var(--ease) infinite;
}
.gesture-label {
  display: block; margin-bottom: 8px;
  color: var(--ink-faint);
  font-family: var(--font-mono); font-size: var(--t-meta);
  letter-spacing: 0.06em; text-transform: uppercase;
}
.gesture-answer p { margin: 0; color: var(--ink); font-size: var(--t-ui); line-height: 1.6; }

/* The chip is ink until the moment it is used, then it takes the seal. An
   amber element that does not designate evidence is a bug, not a decoration. */
.gesture-cite {
  display: inline-block; min-width: 17px; margin-left: 5px; padding: 0 4px;
  border: 1px solid var(--rule); border-radius: 2px;
  color: var(--ink-3);
  font-family: var(--font-mono); font-size: var(--t-meta); text-align: center;
  animation: gesture-cite 6s var(--ease) infinite;
}

/* The connector draws from the chip down to the page: the click, made visible
   as the line it actually follows. */
.gesture-link {
  width: 1px; height: 34px;
  background: var(--amber);
  transform: scaleY(0); transform-origin: top;
  animation: gesture-link 6s var(--ease) infinite;
}

/* Not a full A4. The product scrolls a page to the passage rather than showing
   the sheet whole, and a true 1/1.414 here made a column half a metre tall
   against a 600px copy column, two thirds of it blank. This is the page at the
   passage: tall enough to read as a page, short enough to sit level with the
   headline beside it. */
.gesture-page { width: 100%; max-width: 400px; margin: 0; }
.gesture-sheet {
  position: relative;
  aspect-ratio: 1 / 1.15;
  padding: 26px 22px;
  border: 1px solid var(--rule); border-radius: var(--r-panel);
  background: var(--sheet);
  opacity: 0; animation: gesture-sheet 6s var(--ease) infinite;
}
.gesture-line { display: block; height: 3px; margin-bottom: 9px; border-radius: 1px; background: var(--rule); }
.gesture-line:nth-child(4n) { width: 62%; margin-bottom: 20px; }
.gesture-line:nth-child(5n) { width: 88%; }
.gesture-line:nth-child(7n) { width: 74%; }

/* The measurement. Percentages of the page box, straight from the bbox the
   ingestion stored. It sweeps left to right — the same stroke the product
   draws on a real source page, and the same one under the word "proof". */
.gesture-hl {
  position: absolute;
  left: var(--hl-x); top: var(--hl-y);
  width: var(--hl-w); height: var(--hl-h);
  border-radius: 2px;
  background: color-mix(in srgb, var(--amber) 45%, transparent);
  transform: scaleX(0); transform-origin: left;
  animation: gesture-hl 6s var(--ease) infinite;
}

.gesture-credits {
  margin-top: 14px;
  color: var(--ink-3);
  font-family: var(--font-mono); font-size: var(--t-meta); line-height: 1.5;
  text-align: center; overflow-wrap: anywhere;
  opacity: 0; animation: gesture-credits 6s var(--ease) infinite;
}
.gesture-credits b { color: var(--amber-ink); font-weight: 400; }

@keyframes gesture-answer  { 0%, 2% { opacity: 0; transform: translateY(8px); } 10%, 92% { opacity: 1; transform: none; } 100% { opacity: 0; } }
@keyframes gesture-cite    { 0%, 14% { border-color: var(--rule); background: transparent; color: var(--ink-3); }
                             18%, 92% { border-color: var(--amber); background: var(--amber); color: var(--on-amber); }
                             100% { border-color: var(--rule); background: transparent; color: var(--ink-3); } }
@keyframes gesture-link    { 0%, 22% { transform: scaleY(0); } 32%, 92% { transform: scaleY(1); } 100% { transform: scaleY(0); } }
@keyframes gesture-sheet   { 0%, 28% { opacity: 0; transform: translateY(10px); } 40%, 92% { opacity: 1; transform: none; } 100% { opacity: 0; } }
@keyframes gesture-hl      { 0%, 44% { transform: scaleX(0); } 56%, 92% { transform: scaleX(1); } 100% { transform: scaleX(0); } }
@keyframes gesture-credits { 0%, 56% { opacity: 0; } 64%, 92% { opacity: 1; } 100% { opacity: 0; } }

/* Reduced motion keeps the whole statement, and only removes the replay:
   the claim, the lit citation, the page and the boxed passage all stand. */
@media (prefers-reduced-motion: reduce) {
  .gesture-answer, .gesture-sheet, .gesture-credits { opacity: 1; transform: none; animation: none; }
  .gesture-link { transform: scaleY(1); animation: none; }
  .gesture-hl { transform: scaleX(1); animation: none; }
  .gesture-cite { border-color: var(--amber); background: var(--amber); color: var(--on-amber); animation: none; }
}

/* ── Product demo ───────────────────────────────────────────────────────── */

.product-demo { padding: 128px 0; border-top: 1px solid var(--rule); background: var(--sheet); }

.demo-stage {
  margin-top: 60px; overflow: hidden;
  border: 1px solid var(--rule); border-radius: var(--r-panel);
  background: var(--paper-2);
}
.demo-toolbar {
  height: 52px; display: grid; grid-template-columns: 1fr auto 1fr; align-items: center;
  padding: 0 18px; border-bottom: 1px solid var(--rule);
  color: var(--ink-3); font-family: var(--font-mono); font-size: var(--t-meta);
}
.demo-toolbar > div { display: flex; align-items: center; gap: 8px; }
.record-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--alert); }
.demo-toggle {
  justify-self: end;
  min-height: 32px; padding: 0 12px;
  border: 1px solid var(--rule); border-radius: var(--r-control);
  background: var(--sheet); color: var(--ink-2);
  font-family: var(--font-mono); font-size: var(--t-meta);
  cursor: pointer; transition: color 0.18s, border-color 0.18s;
}
.demo-toggle:hover { color: var(--ink); border-color: var(--ink-3); }
.video-frame { background: var(--paper-2); }
.video-frame video { width: 100%; height: auto; }

.demo-index { display: grid; grid-template-columns: repeat(4, 1fr); border: 1px solid var(--rule); border-top: 0; }
.demo-index > div { display: grid; min-height: 82px; padding: 18px 20px; border-right: 1px solid var(--rule); transition: background 0.2s; }
.demo-index > div:last-child { border-right: 0; }
.demo-index > div:hover { background: var(--paper-2); }
.demo-index span { color: var(--ink-3); font-family: var(--font-mono); font-size: var(--t-meta); }
.demo-index strong { align-self: end; font-size: var(--t-ui); font-weight: 600; }

/* ── Verification workbench ─────────────────────────────────────────────── */

.verification-section { padding: 128px 0; background: var(--paper); }

.verification-workbench {
  margin-top: 60px; overflow: hidden;
  border: 1px solid var(--rule); border-radius: var(--r-panel);
  background: var(--sheet);
}
.workbench-tabs { display: grid; grid-template-columns: repeat(3, 1fr); border-bottom: 1px solid var(--rule); background: var(--paper-2); }
.feature-tab {
  min-height: 96px;
  display: grid; grid-template-columns: 34px 1fr; grid-template-rows: auto auto;
  align-content: center; gap: 3px 10px;
  padding: 18px 24px;
  border: 0; border-right: 1px solid var(--rule);
  background: transparent; text-align: left; cursor: pointer;
  transition: background 0.18s, box-shadow 0.18s;
}
.feature-tab:last-child { border-right: 0; }
.feature-tab > span { grid-row: 1/3; color: var(--ink-3); font-family: var(--font-mono); font-size: var(--t-meta); }
.feature-tab strong { font-size: var(--t-h3); font-weight: 600; letter-spacing: -0.015em; }
.feature-tab small { color: var(--ink-3); font-size: var(--t-ui); }
.feature-tab:hover { background: var(--sheet); }
.feature-tab.active { background: var(--sheet); box-shadow: inset 0 2px 0 var(--blue); }
.feature-tab.active > span { color: var(--blue); }

.feature-canvas {
  position: relative; min-height: 620px; overflow: hidden;
  background-color: var(--paper-2);
  background-image: linear-gradient(var(--grid) 1px, transparent 1px),
                    linear-gradient(90deg, var(--grid) 1px, transparent 1px);
  background-size: 30px 30px;
}
.feature-canvas::after { content: ""; position: absolute; inset: 20px; border: 1px solid var(--rule-soft); pointer-events: none; }
.canvas-ruler { position: absolute; z-index: 3; }
.ruler-x { left: 40px; right: 40px; top: 11px; height: 6px; border-top: 1px solid var(--rule); background: repeating-linear-gradient(90deg, transparent 0 29px, var(--ink-faint) 29px 30px); }
.ruler-y { top: 40px; bottom: 40px; left: 11px; width: 6px; border-left: 1px solid var(--rule); background: repeating-linear-gradient(0deg, transparent 0 29px, var(--ink-faint) 29px 30px); }

.feature-panel { position: absolute; inset: 0; z-index: 2; animation: panel-in 0.4s var(--ease); }
.feature-panel[hidden] { display: none; }
@keyframes panel-in { from { opacity: 0; transform: translateY(8px); } }

.canvas-caption {
  position: absolute; left: 34px; right: 34px; bottom: 26px;
  display: flex; justify-content: space-between;
  color: var(--ink-3); font-family: var(--font-mono); font-size: var(--t-meta);
}

/* Panel 1: the evidence thread. A passage, a wire, the claim it supports. */
.evidence-card {
  position: absolute; width: min(430px, 68%); left: 8%; top: 10%;
  padding: 24px; border: 1px solid var(--rule); border-radius: var(--r-panel);
  background: var(--sheet);
  box-shadow: 0 24px 50px -34px var(--shadow);
}
.evidence-meta { display: flex; justify-content: space-between; color: var(--ink-3); font-family: var(--font-mono); font-size: var(--t-meta); }
.evidence-card > p { margin: 22px 0; font-family: var(--font-display); font-size: 21px; line-height: 1.5; }
.evidence-card mark { padding: 0 2px; background: var(--amber); color: var(--on-amber); }
.evidence-file { display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: 10px; padding-top: 16px; border-top: 1px solid var(--rule-soft); }
.evidence-file > span { padding: 4px 6px; border-radius: 4px; background: var(--ink); color: var(--paper); font-family: var(--font-mono); font-size: var(--t-meta); }
.evidence-file > div { display: grid; gap: 2px; }
.evidence-file strong { font-size: var(--t-ui); }
.evidence-file small { color: var(--ink-3); font-family: var(--font-mono); font-size: var(--t-meta); }
.evidence-file b { width: 22px; height: 22px; display: grid; place-items: center; border-radius: 50%; background: var(--success-wash); color: var(--success); font-size: var(--t-meta); }

.trace-wire { position: absolute; left: 46%; top: 48%; width: 37%; border-top: 1px dashed var(--ink-faint); transform: rotate(27deg); }
.trace-wire i { position: absolute; top: -3px; left: 0; width: 6px; height: 6px; border-radius: 50%; background: var(--blue); animation: wire 2.4s var(--ease) infinite; }
@keyframes wire { 70%, 100% { left: 100%; } }

.claim-card {
  position: absolute; width: min(300px, 48%); right: 7%; bottom: 13%;
  padding: 20px; border: 1px solid var(--rule); border-radius: var(--r-panel);
  background: var(--band); color: var(--on-band);
  box-shadow: 0 24px 45px -30px var(--shadow-deep);
}
.claim-card > span { color: var(--on-band-3); font-family: var(--font-mono); font-size: var(--t-meta); }
.claim-card p { margin: 14px 0 16px; font-family: var(--font-display); font-size: 17px; line-height: 1.45; }
.claim-card small { display: flex; align-items: center; gap: 7px; color: var(--on-band-3); font-family: var(--font-mono); font-size: var(--t-meta); }
.claim-card small i { width: 6px; height: 6px; border-radius: 50%; background: var(--amber); }

/* Panel 2: layout-aware OCR recovering structure from a scan. */
.scan-document { position: absolute; width: 62%; left: 8%; top: 7%; overflow: hidden; border: 1px solid var(--rule); border-radius: var(--r-panel); background: var(--sheet); box-shadow: 0 24px 50px -34px var(--shadow); }
.scan-top { height: 40px; display: flex; align-items: center; justify-content: space-between; padding: 0 15px; border-bottom: 1px solid var(--rule-soft); color: var(--ink-3); font-family: var(--font-mono); font-size: var(--t-meta); }
.scan-page { position: relative; height: 430px; overflow: hidden; margin: 16px 22px 22px; padding: 32px; background: var(--scan-paper); filter: grayscale(0.4); }
.scan-page::before { content: ""; position: absolute; inset: 0; opacity: 0.18; background-image: radial-gradient(var(--scan-ink) 0.5px, transparent 0.5px); background-size: 4px 4px; }
.scan-label { position: relative; color: var(--scan-ink-2); font-family: var(--font-mono); font-size: var(--t-meta); letter-spacing: 0.12em; }
.scan-heading { position: relative; width: 56%; height: 12px; margin-top: 14px; background: var(--scan-ink-2); }
.scan-columns { position: relative; display: grid; grid-template-columns: 1fr 1fr; gap: 14px; margin-top: 18px; }
.scan-columns div { height: 82px; opacity: 0.4; background: repeating-linear-gradient(to bottom, var(--scan-ink) 0 2px, transparent 2px 8px); }
/* The seal belongs here: this rectangle IS the evidence being found. */
.detected-table { position: relative; height: 150px; margin-top: 26px; border: 2px solid var(--amber); background: color-mix(in srgb, var(--amber) 12%, transparent); }
.detected-table > span { position: absolute; left: -2px; top: -20px; padding: 3px 6px; background: var(--amber); color: var(--on-amber); font-family: var(--font-mono); font-size: var(--t-meta); }
.table-lines { height: 100%; background-image: linear-gradient(color-mix(in srgb, var(--amber) 34%, transparent) 1px, transparent 1px), linear-gradient(90deg, color-mix(in srgb, var(--amber) 34%, transparent) 1px, transparent 1px); background-size: 100% 31px, 25% 100%; }
.scan-beam { position: absolute; left: 0; right: 0; top: 0; height: 1px; background: var(--amber); animation: scan 3.5s ease-in-out infinite; }
@keyframes scan { 50%, 100% { top: 100%; } }

.ocr-status { position: absolute; right: 7%; bottom: 10%; min-width: 290px; display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: 12px; padding: 14px; border: 1px solid var(--rule); border-radius: var(--r-panel); background: var(--sheet); box-shadow: 0 22px 40px -28px var(--shadow); }
.ocr-status > span { width: 32px; height: 32px; display: grid; place-items: center; border-radius: var(--r-control); background: var(--blue-wash); color: var(--blue); }
.ocr-status > div { display: grid; gap: 2px; }
.ocr-status strong { font-size: var(--t-ui); }
.ocr-status small { color: var(--ink-3); font-family: var(--font-mono); font-size: var(--t-meta); }
.ocr-status b { color: var(--blue); font-family: var(--font-mono); font-size: var(--t-meta); font-weight: 500; }

/* Panel 3: the three checks stored with an answer. */
.audit-card { position: absolute; width: min(500px, 78%); left: 50%; top: 48%; padding: 26px; border: 1px solid var(--rule); border-radius: var(--r-panel); background: var(--sheet); box-shadow: 0 28px 55px -36px var(--shadow); transform: translate(-50%, -50%); }
.audit-head { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding-bottom: 18px; border-bottom: 1px solid var(--rule-soft); }
.audit-head > div { display: grid; gap: 5px; }
.audit-head span { color: var(--ink-3); font-family: var(--font-mono); font-size: var(--t-meta); }
.audit-head strong { font-size: var(--t-h3); font-weight: 600; }
/* 500 is the heaviest mono shipped; a <b> here would inherit 700 and be
   synthesised into a smeared fake bold. */
.audit-head b { padding: 5px 9px; border-radius: 999px; background: var(--success-wash); color: var(--success); font-family: var(--font-mono); font-size: var(--t-meta); font-weight: 500; }
.metric { margin-top: 20px; }
.metric > div:first-child { display: flex; justify-content: space-between; font-size: var(--t-ui); }
.metric > div strong { color: var(--blue); font-family: var(--font-mono); font-size: var(--t-ui); font-weight: 500; }
.metric-bar { height: 4px; margin: 8px 0 6px; border-radius: 2px; background: var(--rule); }
.metric-bar i { display: block; width: var(--score); height: 100%; border-radius: 2px; background: var(--blue); animation: score 0.7s var(--ease); }
@keyframes score { from { width: 0; } }
.metric small { color: var(--ink-3); font-size: var(--t-meta); }

/* ── Deployment ─────────────────────────────────────────────────────────── */

.deployment-section { padding: 132px 0; background: var(--band); color: var(--on-band); }
.deployment-section .section-label.inverse { color: var(--on-band-3); }
.deployment-section .section-label.inverse span { color: var(--amber); }
.deployment-grid { display: grid; grid-template-columns: 0.8fr 1.2fr; align-items: center; gap: 84px; }
.deployment-copy h2 { max-width: 16ch; }
.deployment-copy > p { margin-top: 20px; max-width: 46ch; color: var(--on-band-2); font-size: var(--t-lead); line-height: 1.62; }
.deployment-copy dl { display: grid; grid-template-columns: 1fr 1fr; margin-top: 44px; border-top: 1px solid var(--band-rule); border-left: 1px solid var(--band-rule); }
.deployment-copy dl > div { display: grid; gap: 8px; min-height: 88px; padding: 16px; border-right: 1px solid var(--band-rule); border-bottom: 1px solid var(--band-rule); }
.deployment-copy dt { color: var(--on-band-3); font-family: var(--font-mono); font-size: var(--t-meta); }
.deployment-copy dd { align-self: end; font-size: var(--t-ui); }

.boundary-map { overflow: hidden; border: 1px solid var(--band-rule); border-radius: var(--r-panel); background: var(--band-surface); }
.boundary-head, .boundary-foot { display: flex; align-items: center; justify-content: space-between; padding: 0 16px; color: var(--on-band-3); font-family: var(--font-mono); font-size: var(--t-meta); }
.boundary-head { height: 46px; border-bottom: 1px solid var(--band-rule-soft); }
.boundary-foot { height: 50px; border-top: 1px solid var(--band-rule-soft); }
/* On the band, "in place" reads in band ink. --success is #17171A in light
   mode and would disappear here. */
.boundary-head span:last-child { display: flex; align-items: center; gap: 7px; color: var(--on-band); }
.boundary-head i { width: 6px; height: 6px; border-radius: 50%; background: var(--on-band-2); }
.boundary-foot strong { color: var(--amber); font-weight: 400; }

/* Nodes and connectors alternate as real grid tracks, so a line always lands
   in the gap it belongs to instead of being placed by percentage guesswork. */
.boundary-body {
  position: relative;
  display: grid; grid-template-columns: repeat(4, 1fr 26px) 1fr;
  align-items: stretch;
  padding: 40px 28px;
}
.boundary-node {
  position: relative; z-index: 2; min-height: 128px;
  display: grid; align-content: center; gap: 6px; padding: 14px;
  border: 1px solid var(--band-rule);
  border-radius: var(--r-control);
  background: var(--band-surface);
  transition: border-color 0.25s, transform 0.25s var(--ease);
}
.boundary-node:hover { border-color: color-mix(in oklab, var(--on-band) 42%, transparent); transform: translateY(-2px); }
.boundary-node > span { color: var(--amber); font-family: var(--font-mono); font-size: var(--t-meta); }
.boundary-node strong { margin-top: 6px; font-size: var(--t-ui); }
.boundary-node small { color: var(--on-band-3); font-family: var(--font-mono); font-size: var(--t-meta); }
/* The node the whole diagram leads to: lifted with band ink, not a hue. */
.answer-node { border-color: color-mix(in oklab, var(--on-band) 48%, transparent); background: color-mix(in oklab, var(--on-band) 11%, transparent); }
.answer-node > span { color: var(--on-band); }

.flow-line { position: relative; align-self: center; height: 1px; overflow: hidden; background: var(--band-rule); }
.flow-line b { position: absolute; width: 14px; height: 1px; background: var(--on-band); animation: flow 1.8s linear infinite; }
@keyframes flow { from { left: -14px; } to { left: 100%; } }

/* ── Request lifecycle ──────────────────────────────────────────────────── */

.pipeline-section { padding: 132px 0; background: var(--sheet); }
.pipeline-grid { display: grid; grid-template-columns: repeat(4, 1fr); margin-top: 60px; border-block: 1px solid var(--rule); border-left: 1px solid var(--rule); }
.pipeline-grid article { min-height: 360px; display: flex; flex-direction: column; padding: 22px; border-right: 1px solid var(--rule); transition: background 0.25s; }
.pipeline-grid article:hover { background: var(--paper-2); }
.pipeline-grid article > div { display: flex; justify-content: space-between; gap: 10px; color: var(--ink-3); font-family: var(--font-mono); font-size: var(--t-meta); }
.pipeline-grid article > div span { color: var(--blue); }
.pipeline-grid article > div b { font-weight: 400; }
.pipeline-grid h3 { margin-top: 68px; font-size: var(--t-h3); font-weight: 600; letter-spacing: -0.018em; }
.pipeline-grid p { margin-top: 12px; color: var(--ink-2); font-size: var(--t-ui); line-height: 1.62; }
.pipeline-grid small { margin-top: auto; padding-top: 18px; border-top: 1px solid var(--rule-soft); color: var(--ink-3); font-family: var(--font-mono); font-size: var(--t-meta); }

.stack-line { display: grid; grid-template-columns: 1.3fr repeat(5, 1fr); border: 1px solid var(--rule); border-top: 0; background: var(--paper-2); }
.stack-line > * { min-height: 56px; display: grid; place-items: center; border-right: 1px solid var(--rule); color: var(--ink-3); font-family: var(--font-mono); font-size: var(--t-meta); }
.stack-line > *:last-child { border-right: 0; }
.stack-line > span { place-items: center start; padding-left: 18px; }

/* ── Pricing ────────────────────────────────────────────────────────────── */

.pricing-section { padding: 132px 0; background: var(--paper); }
.pricing-head { max-width: 42ch; }
.pricing-grid { display: grid; grid-template-columns: repeat(4, 1fr); margin-top: 60px; border: 1px solid var(--rule); border-radius: var(--r-panel); overflow: hidden; background: var(--sheet); }
.pricing-plan { display: flex; flex-direction: column; padding: 30px 26px; border-right: 1px solid var(--rule); }
.pricing-plan:last-child { border-right: 0; }
/* One plan carries the recommendation. It reads as the current sheet on the
   desk, not as a brighter card. */
.pricing-plan.featured { background: var(--paper-2); box-shadow: inset 0 2px 0 var(--blue); }
.pricing-plan > span { color: var(--ink-3); font-family: var(--font-mono); font-size: var(--t-meta); }
.pricing-plan.featured > span { color: var(--blue); }
.pricing-plan h3 { margin-top: 12px; font-size: 1.375rem; font-weight: 600; letter-spacing: -0.02em; }
.pricing-plan > p { margin-top: 8px; color: var(--ink-3); font-size: var(--t-ui); line-height: 1.5; }
.pricing-price { margin-top: 22px; display: flex; align-items: baseline; gap: 6px; }
.pricing-price strong { font-size: 1.875rem; font-weight: 600; letter-spacing: -0.03em; }
.pricing-price small { color: var(--ink-3); font-size: var(--t-ui); }
.pricing-plan ul { margin: 22px 0 0; padding: 0; list-style: none; display: grid; gap: 11px; }
.pricing-plan li { display: grid; grid-template-columns: auto 1fr; gap: 9px; align-items: start; color: var(--ink-2); font-size: var(--t-ui); line-height: 1.45; }
.pricing-plan li::before { content: "+"; color: var(--success); font-family: var(--font-mono); }
.pricing-plan .button { margin-top: 28px; }
.pricing-note { margin-top: 22px; color: var(--ink-3); font-size: var(--t-ui); }

/* ── FAQ ────────────────────────────────────────────────────────────────── */

.faq-section { padding: 128px 0; border-top: 1px solid var(--rule); background: var(--paper-2); }
.faq-grid { display: grid; grid-template-columns: 0.85fr 1.15fr; gap: 90px; }
.faq-intro h2 { max-width: 14ch; }
.faq-intro a { text-decoration-color: var(--amber-strong); text-decoration-thickness: 2px; text-underline-offset: 4px; }
.faq-list { border-top: 1px solid var(--rule); }
.faq-list details { border-bottom: 1px solid var(--rule); }
.faq-list summary { display: flex; align-items: center; justify-content: space-between; gap: 24px; padding: 24px 2px; list-style: none; font-size: var(--t-h3); font-weight: 600; cursor: pointer; }
.faq-list summary::-webkit-details-marker { display: none; }
.faq-list summary span { position: relative; width: 24px; height: 24px; flex: 0 0 auto; border: 1px solid var(--rule); border-radius: var(--r-control); transition: border-color 0.18s, background 0.18s; }
.faq-list summary:hover span { border-color: var(--amber-strong); background: var(--sheet); }
.faq-list summary span::before, .faq-list summary span::after { content: ""; position: absolute; left: 7px; right: 7px; top: 11px; height: 1.5px; background: var(--ink); transition: transform 0.18s; }
.faq-list summary span::after { transform: rotate(90deg); }
.faq-list details[open] summary span::after { transform: none; }
.faq-list details p { max-width: 62ch; padding: 0 48px 24px 2px; color: var(--ink-2); font-size: var(--t-body); line-height: 1.65; }
.faq-list details[open] p { animation: faq-in 0.35s var(--ease); }
@keyframes faq-in { from { opacity: 0; transform: translateY(-5px); } }

/* ── Final call ─────────────────────────────────────────────────────────── */

.final-section { padding: 96px 0; background: var(--sheet); }
.final-panel { position: relative; overflow: hidden; padding: 32px 40px 64px; border-radius: var(--r-panel); background: var(--band); color: var(--on-band); }
.final-meta { position: relative; z-index: 2; display: flex; justify-content: space-between; padding-bottom: 26px; border-bottom: 1px solid var(--band-rule-soft); color: var(--on-band-3); font-family: var(--font-mono); font-size: var(--t-meta); }
.final-meta span:last-child { display: flex; align-items: center; gap: 7px; color: var(--success); }
.final-meta i { width: 6px; height: 6px; border-radius: 50%; background: var(--success); }
.final-panel h2 { position: relative; z-index: 2; max-width: 20ch; margin-top: 54px; font-family: var(--font-display); font-size: var(--t-h2); font-weight: 400; line-height: 1.06; letter-spacing: -0.014em; }
.final-panel > p { position: relative; z-index: 2; margin-top: 20px; max-width: 46ch; color: var(--on-band-2); font-size: var(--t-lead); }
.final-panel > div:last-child { position: relative; z-index: 2; display: flex; align-items: center; gap: 26px; margin-top: 32px; }
.final-panel .button-light { border-color: transparent; background: var(--on-band); color: var(--band); }
.final-panel .button-light:hover { background: var(--amber); color: var(--on-amber); }
.final-panel > div:last-child > a:last-child { color: var(--on-band-2); font-size: var(--t-ui); text-decoration: none; }
.final-panel > div:last-child > a:last-child:hover { color: var(--paper); }

/* ── Footer ─────────────────────────────────────────────────────────────── */

.site-footer { padding: 68px 0 24px; background: var(--paper-2); color: var(--ink-2); border-top: 1px solid var(--rule); }
.footer-grid { display: grid; grid-template-columns: 1fr auto auto; gap: 90px; padding-bottom: 56px; }
.footer-grid > div:first-child p { margin-top: 12px; max-width: 34ch; color: var(--ink-3); font-size: var(--t-ui); }
.footer-grid > div:not(:first-child) { min-width: 120px; display: grid; align-content: start; gap: 10px; }
.footer-grid > div:not(:first-child) span { margin-bottom: 4px; color: var(--ink-3); font-family: var(--font-mono); font-size: var(--t-meta); }
.footer-grid a { width: max-content; color: var(--ink-2); font-size: var(--t-ui); text-decoration: none; transition: color 0.18s; }
.footer-grid a:hover { color: var(--ink); }
.footer-bottom { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding-top: 20px; border-top: 1px solid var(--rule); color: var(--ink-3); font-family: var(--font-mono); font-size: var(--t-meta); }
.footer-bottom a { text-decoration: none; transition: color 0.18s; }
.footer-bottom a:hover { color: var(--ink); }

/* ── Scroll reveal ──────────────────────────────────────────────────────── */

.js .reveal { opacity: 0; transform: translateY(16px); transition: opacity 0.6s var(--ease), transform 0.6s var(--ease); }
.js .reveal.visible { opacity: 1; transform: none; }
/* Printing never scrolls, so the observer never fires: show everything. */
@media print { .js .reveal { opacity: 1; transform: none; } }

/* ── Responsive ─────────────────────────────────────────────────────────── */

@media (max-width: 1180px) {
  :root { --shell: min(1340px, calc(100% - 48px)); }
  .hero-layout { grid-template-columns: 1fr; gap: 60px; }
  .hero-copy { max-width: 760px; }
  .hero h1 { max-width: 18ch; }
  .deployment-grid { grid-template-columns: 1fr; gap: 56px; }
  .deployment-copy { max-width: 760px; }
  .boundary-body { padding-inline: 22px; }
}

@media (max-width: 1020px) {
  .desktop-nav { display: none; }
  .menu-toggle { display: grid; }
  .hero { padding: calc(var(--nav-h) + 60px) 0 108px; }
  .hero-product { width: min(760px, 100%); margin-inline: auto; }
  .section-intro, .verification-head, .pipeline-head { grid-template-columns: 1fr; gap: 24px; }
  .pipeline-grid { grid-template-columns: repeat(2, 1fr); }
  .pipeline-grid article:nth-child(-n+2) { border-bottom: 1px solid var(--rule); }
  .stack-line { grid-template-columns: repeat(3, 1fr); }
  .stack-line > span { grid-column: 1/4; }
  .pricing-grid { grid-template-columns: repeat(2, 1fr); }
  .pricing-plan:nth-child(2n) { border-right: 0; }
  .pricing-plan:nth-child(-n+2) { border-bottom: 1px solid var(--rule); }
  .faq-grid { grid-template-columns: 1fr; gap: 48px; }
}

@media (max-width: 760px) {
  :root { --shell: min(100% - 32px, 1340px); --nav-h: 68px; }
  /* Drop the secondary link, never the primary action. Log in also lives in
     the drawer; hiding the button left the weaker of the two on screen. */
  .nav-actions .nav-login { display: none; }
  .nav-actions .button-small { padding-inline: 11px; }

  /* The desktop sizes are tuned against a mouse and against the text beside
     them. A finger has its own floor: §11 asks for 44px, and proportion is not
     a reason to go under it. */
  .button, .button-small, .hero-actions .button { min-height: 44px; }
  .theme-toggle { width: 44px; height: 44px; }

  .hero { padding: calc(var(--nav-h) + 44px) 0 88px; }
  .hero h1 { max-width: none; font-size: clamp(2.1rem, 11.5vw, 3rem); }
  .hero-lede { font-size: var(--t-body); }
  /* The primary action keeps its own width here. A button stretched across
     the viewport shouts louder than the capture it is meant to introduce. */
  .hero-actions { gap: 18px 24px; margin-top: 26px; }

  .product-demo, .verification-section, .deployment-section,
  .pipeline-section, .pricing-section, .faq-section { padding-block: 88px; }

  .demo-stage { margin-top: 40px; }
  .demo-toolbar { grid-template-columns: 1fr auto; }
  .demo-toolbar > span:not(.demo-loop-label) { display: none; }
  .demo-index { grid-template-columns: 1fr 1fr; }
  .demo-index > div:nth-child(2n) { border-right: 0; }
  .demo-index > div:nth-child(-n+2) { border-bottom: 1px solid var(--rule); }

  .workbench-tabs { grid-template-columns: 1fr; }
  .feature-tab { min-height: 72px; border-right: 0; border-bottom: 1px solid var(--rule); }
  .feature-canvas { min-height: 540px; }
  .evidence-card { width: 84%; left: 5%; top: 7%; padding: 18px; }
  .evidence-card > p { margin-block: 18px; font-size: 17px; }
  .claim-card { width: 64%; right: 4%; bottom: 12%; padding: 16px; }
  .trace-wire { top: 50%; }
  .scan-document { width: 82%; left: 5%; }
  .scan-page { height: 330px; margin: 12px 15px 16px; padding: 22px; }
  .ocr-status { min-width: 240px; right: 4%; bottom: 9%; }
  .audit-card { width: 88%; padding: 20px; }
  .canvas-caption { left: 26px; right: 26px; }

  .deployment-copy dl { grid-template-columns: 1fr; }
  .boundary-body { grid-template-columns: 1fr; padding: 28px 24px; }
  .boundary-node { min-height: 0; }
  .flow-line { justify-self: center; width: 1px; height: 16px; }
  .flow-line b { display: none; }

  .pipeline-grid { grid-template-columns: 1fr; }
  .pipeline-grid article { min-height: 0; border-bottom: 1px solid var(--rule); }
  .pipeline-grid h3 { margin-top: 40px; }
  .stack-line { grid-template-columns: 1fr 1fr; }
  .stack-line > span { grid-column: 1/3; }

  .pricing-grid { grid-template-columns: 1fr; }
  .pricing-plan { border-right: 0; border-bottom: 1px solid var(--rule); }
  .pricing-plan:last-child { border-bottom: 0; }

  .final-section { padding-block: 68px; }
  .final-panel { padding: 26px 22px 54px; }
  .final-panel > div:last-child { align-items: stretch; flex-direction: column; gap: 18px; }
  .final-panel > div:last-child > a:last-child { text-align: center; }

  .footer-grid { grid-template-columns: 1fr 1fr; gap: 44px; }
  .footer-grid > div:first-child { grid-column: 1/3; }
  .footer-bottom { flex-wrap: wrap; gap: 12px; }
}

@media (max-width: 430px) {
  .feature-canvas { min-height: 500px; }
  .claim-card { width: 72%; }
  .boundary-body { padding-inline: 20px; }
}

/* Below this the wordmark and the button cannot share the bar without the
   wordmark being clipped. The drawer keeps the signup path one tap away. */
@media (max-width: 344px) {
  .nav-actions .button-small { display: none; }
}

/* ── La bande d'index, entre le héros et la descente ────────────────────── */

/* Deux filets la cadrent : celui du haut est le sien, celui du bas appartient
   déjà à la section Ingestion. Une bande entre deux filets, c'est le
   vocabulaire de la page, pas un séparateur ajouté. */
.stages { padding: 40px 0 0; border-top: 1px solid var(--rule); background: var(--paper); }

/* La thèse. Elle se distingue du chapô du héros par la VOIX, pas par le corps :
   `--t-lead` fait 18 px, exactement la taille de ce chapô, et le projet tient
   à ses huit tailles. Le Caslon à 19 px et en encre pleine, contre un chapô en
   sans à 18 px et en encre secondaire, lit comme un aparté d'auteur — ce
   qu'est une ligne de thèse. */
.stages-line {
  max-width: 40ch; margin: 0 0 30px;
  color: var(--ink);
  font-family: var(--font-display); font-size: var(--t-h3);
  font-weight: 400; line-height: 1.44;
}

/* Le trait qui franchit la couture.
   Il descend SOUS LA PLANCHE, pas dans la colonne de texte : l'œil finit sur
   la planche, et c'est là que quelque chose doit continuer. Un premier essai
   le faisait courir à gauche, sur l'axe du titre — rien ne continuait là où
   le regard était, et la bande devenait un grand vide.
   `--seam-x` est posé par demo.js à partir de la position mesurée de la
   fenêtre, comme le connecteur entre l'ingestion et la recherche : c'est le
   même dispositif, à la seule couture qui n'en avait pas. */
/* La bande reprend la découpe du héros : la phrase sous la colonne de texte,
   le gros plan sous la planche. La composition ne change pas de grille en
   passant la couture, elle la continue. */
/* Une seule colonne, et toute la largeur : la composition de la couture est
   un plan unique où la page, ses fragments et le faisceau partagent le même
   repère. En grille à deux colonnes les fragments partaient hors de la leur.
   La chronologie est NOMMÉE et portée ici : avec `view()` chaque élément suit
   sa propre boîte, et un cadre de trente pixels a fini sa course avant d'avoir
   commencé. Un sujet commun, donc, et un ordre qui tient. */
.stages .shell {
  position: relative;
  view-timeline: --seam block;
}
/* La phrase est dans le même repère que le reste, et elle s'efface pendant la
   décomposition : l'entre-deux doit être presque vide, c'est ce qui le rend
   lisible comme un entre-deux. */
.stages-line {
  position: absolute; left: 0; top: 18px; z-index: 2;
  opacity: 1;
}

/* ── La couture : le document se décompose ──────────────────────────────
   Le brief est net — la transition ne doit pas ressembler à une animation
   posée entre deux blocs. Le document du héros perd ses éléments secondaires,
   montre ses cadres et ses coordonnées, se détache en fragments, et ces
   fragments se reconstruisent en faisceau : la forme exacte de l'entrée du
   schéma d'ingestion, juste en dessous.
   Tout est piloté par le défilement sur une chronologie NOMMÉE, portée par la
   bande. Chaque élément y prend sa propre plage et sa propre courbe — c'est ce
   qui sépare une composition d'un décalage régulier. */
.seam { position: sticky; top: 92px; }
/* La hauteur de l'hôte est la course : la composition y tient collée pendant
   qu'elle défile, ce qui donne au brief ses mille pixels de lecture. Le décor
   ne fait que 640 px de haut ; le reste est de la piste. */
.stages-proof { position: relative; }

.seam-doc {
  position: absolute; top: 0;
  border: 1px solid var(--rule); border-radius: var(--r-panel);
  background: #fff;
  overflow: hidden;
}
.seam-doc img { position: absolute; inset: 0; width: 100%; height: 100%; }
/* Les cadres sont posés sur les bbox vraies, en pourcentages de page : les
   mêmes coordonnées que le volet de preuve, aucun ajustement à l'œil. */
.seam-box { position: absolute; border: 1px solid var(--amber); border-radius: 1px; }
.seam-coord {
  position: absolute;
  white-space: nowrap;
  color: var(--amber-strong);
  font-family: var(--font-mono); font-size: 9px; letter-spacing: 0.04em;
}

.seam-frag { position: absolute; margin: 0; }
.seam-frag-win {
  position: relative; overflow: hidden;
  border: 1px solid var(--rule);
  background: #fff;
}
.seam-frag-win img { position: absolute; max-width: none; }
.seam-frag figcaption {
  margin-top: 6px;
  color: var(--ink-3);
  font-family: var(--font-mono); font-size: 9px; letter-spacing: 0.04em;
}

/* Le faisceau, tracé après coup. Mêmes filets que le schéma d'en dessous. */
.seam-trunk { position: absolute; width: 1px; background: var(--rule); transform-origin: 50% 0; }
.seam-tie { position: absolute; height: 1px; background: var(--rule); transform-origin: 0 50%; }

@keyframes seam-box-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes seam-doc-out { from { opacity: 1; } to { opacity: 0; } }
/* Le héros ne disparaît pas : il perd du poids pendant que la preuve devient
   le point focal. C'est la phase d'isolation. */
@keyframes seam-dim { from { opacity: 1; } to { opacity: 0.28; } }
/* Le fragment part de son cadre sur la page et rejoint sa place. La
   transformation de départ le remet exactement là où il était, à la bonne
   échelle : il se détache du document au lieu d'y arriver. */
@keyframes seam-frag-out {
  from { transform: translate(var(--dx), var(--dy)) scale(var(--s)); opacity: 0; }
  14% { opacity: 1; }
  to { transform: none; opacity: 1; }
}
@keyframes seam-trunk-in { from { transform: scaleY(0); } to { transform: scaleY(1); } }
@keyframes seam-tie-in { from { transform: scaleX(0); } to { transform: scaleX(1); } }

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .seam-doc, .seam-box, .seam-coord, .seam-frag, .seam-trunk, .seam-tie,
    .stages-line, .hero-copy, #ingestion .section-intro > div > *,
    #ingestion .section-intro > p {
      animation-timeline: --seam;
      animation-fill-mode: both;
      animation-duration: 1ms; /* remplacé par la plage ; requis pour armer */
    }
    /* La trace laissée du document : elle ne s'efface pas avec lui. */
    .seam-coord[data-keep="1"] { animation-name: seam-box-in; }
  }
}

/* Sous 1020 px la décomposition n'a plus la largeur qu'elle demande : la
   bande redevient une simple charnière, la page reste posée avec son cadre, et
   les fragments ne partent pas. Mieux vaut une transition sobre qu'une
   chorégraphie à l'étroit. */
@media (max-width: 1020px) {
  .stages-line { position: static; }
  .seam { position: static; height: auto !important; }
  .seam-doc { position: relative; left: 0 !important; margin-top: 30px; }
  .seam-frag, .seam-trunk, .seam-tie, .seam-coord { display: none; }
  /* Rien n'attend plus une phase qui n'existe pas à cette largeur : le titre
     de la section reprend son comportement normal, sans quoi il restait
     invisible en attendant une chorégraphie désactivée. */
  .seam-box, .hero-copy, #ingestion .section-intro > div > *,
  #ingestion .section-intro > p { animation: none !important; opacity: 1 !important; }
}

/* ── 01 Ingestion : la chaîne, dessinée ─────────────────────────────────── */

.ingestion-section, .search-section { padding: 104px 0; border-top: 1px solid var(--rule); background: var(--paper); }
/* La descente occupe la fin de la section : elle n'a donc pas besoin du bas
   de page habituel, sa flèche pointe déjà vers le filet suivant. */
.ingestion-section { padding-bottom: 16px; }

/* Le connecteur entre deux sections. Il se cale sous le bloc d'où il part —
   `--bridge-x` est posé par le moteur, à partir de la position mesurée de ce
   bloc — et il se recentre dès que le canevas ne tient plus en largeur. */
.flow-bridge { position: relative; height: 132px; margin-top: 4px; }
.flow-bridge svg { position: absolute; top: 16px; left: var(--bridge-x, 50%); transform: translateX(-50%); }
/* Le libellé pousse vers la GAUCHE : la descente est calée sous le dernier
   bloc, donc près du bord droit, et un texte qui partirait à droite sortirait
   de la colonne dès 1280 px. */
.flow-bridge p {
  position: absolute; top: 54px; right: calc(100% - var(--bridge-x, 50%) + 18px); margin: 0;
  color: var(--ink-3);
  font-family: var(--font-mono); font-size: var(--t-meta); letter-spacing: 0.04em;
  white-space: nowrap; text-align: right;
}
@media (max-width: 1200px) {
  .flow-bridge { --bridge-x: 50% !important; }
}

.flow-head {
  display: flex; align-items: baseline; justify-content: space-between; gap: 20px;
  margin: 44px 0 16px; padding-bottom: 11px;
  border-bottom: 1px solid var(--rule);
}
.flow-legend { margin: 0; color: var(--ink-3); font-family: var(--font-mono); font-size: var(--t-meta); letter-spacing: 0.06em; text-transform: uppercase; }
.flow-toggle {
  flex: none; padding: 5px 13px;
  border: 1px solid var(--rule); border-radius: var(--r-control);
  background: var(--sheet); color: var(--ink-2);
  font-family: var(--font-mono); font-size: var(--t-meta); cursor: pointer;
  transition: color 0.18s, border-color 0.18s;
}
.flow-toggle:hover { color: var(--ink); border-color: var(--ink-faint); }

/* Le dessin a une largeur propre : sur un écran étroit il défile dans sa
   boîte, il ne pousse jamais la page. */
.flow-wrap { overflow-x: auto; overscroll-behavior-x: contain; }
.flow-canvas {
  position: relative;
  border: 1px solid var(--rule); border-radius: var(--r-panel);
  background-color: var(--sheet);
  background-image: radial-gradient(var(--grid) 1px, transparent 1px);
  background-size: 19px 19px;
}

.flow-edges { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
.flow-edge { fill: none; stroke: var(--rule); stroke-width: 1.4; transition: stroke 0.25s var(--ease); }
.flow-edge[data-on="1"] { stroke: var(--ink-faint); }
.flow-arrow { fill: var(--ink-faint); }
/* Le flux est une deuxième ligne posée sur la première : l'arête au repos ne
   bouge pas, et ce qui la parcourt est un segment unique, piloté en JavaScript,
   qui part du bloc précédent et s'arrête contre le suivant. */
.flow-dash {
  fill: none; stroke: var(--amber-ink); stroke-width: 2.1; stroke-linecap: round;
  opacity: 0; transition: opacity 0.18s linear;
}
.flow-dash[data-on="1"] { opacity: 1; }
/* Le nom d'une sortie, posé sur la poignée d'où elle part. */
.flow-elabel { fill: var(--ink-faint); font-family: var(--font-mono); font-size: 9.5px; letter-spacing: 0.04em; text-anchor: middle; }

.flow-nodes { position: relative; margin: 0; padding: 0; list-style: none; }
.flow-node {
  position: absolute; width: 170px; padding: 10px 12px 12px;
  border: 1px solid var(--rule); border-radius: var(--r-panel);
  background: var(--sheet);
  transition: border-color 0.26s cubic-bezier(0.22, 1, 0.36, 1);
}
.flow-node[data-on="run"] { border-color: var(--ink); }
.flow-node[data-on="done"] { border-color: var(--ink-faint); }
.flow-node[data-kind="port"] { background: var(--paper-2); }

/* L'impulsion qui traverse le bloc : une barre fine, rognée à ses bords, qui
   va de gauche à droite. Contenue dans le composant, et sans halo — un
   dégradé lumineux global est exactement ce que §3 du brief refuse. */
.flow-scan { position: absolute; inset: 0; overflow: hidden; border-radius: inherit; pointer-events: none; }
.flow-scan i {
  position: absolute; top: 0; bottom: 0; left: 0; width: 2px;
  background: var(--amber-ink);
  opacity: 0; transition: opacity 0.14s linear;
}
.flow-node[data-on="run"] .flow-scan i { opacity: 0.9; }

/* L'horloge de la séquence : présente pour être animée, jamais vue. */
.flow-clock { position: absolute; width: 0; height: 0; overflow: hidden; }

/* Une entrée n'est pas une étape : pas de carte, juste une icône et son nom,
   et trois d'entre elles convergent sur la première étape. */
.flow-node[data-kind="input"] {
  width: auto; padding: 4px 6px;
  border: 0; background: none;
  text-align: center;
}
.flow-icon { display: block; width: 19px; height: 19px; margin: 0 auto 5px; fill: none; stroke: var(--ink-3); stroke-width: 1.2; stroke-linejoin: round; }
.flow-node[data-kind="input"] b { color: var(--ink-2); font-weight: 400; white-space: nowrap; }
.flow-node[data-on="run"] .flow-icon { stroke: var(--amber-ink); }
.flow-node[data-kind="input"][data-on="run"] b { color: var(--ink); }

/* Le raccord des trois entrées n'est pas un nœud : c'est un point de taille
   nulle, qui sert d'ancrage au tronc et ne dessine rien. Quand les trois
   signaux se rejoignent, une impulsion brève marque l'endroit sur le tracé,
   puis disparaît — pas de pastille permanente au milieu du dessin. */
.flow-node[data-kind="joint"] { width: 0; height: 0; padding: 0; border: 0; background: none; }
.flow-node[data-kind="joint"]::after {
  content: ""; position: absolute; left: -2.5px; top: -2.5px;
  width: 5px; height: 5px; border-radius: 50%;
  background: var(--amber-ink);
  opacity: 0; transition: opacity 0.24s cubic-bezier(0.22, 1, 0.36, 1);
}
.flow-node[data-kind="joint"][data-on="done"]::after { opacity: 1; }

.flow-top { display: flex; align-items: center; justify-content: space-between; gap: 8px; min-height: 13px; margin-bottom: 5px; }
.flow-tag, .flow-state {
  color: var(--ink-faint);
  font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.09em; text-transform: uppercase;
  transition: color 0.25s var(--ease);
}
.flow-node[data-on="run"] .flow-tag { color: var(--amber-ink); }
.flow-state { flex: none; color: var(--ink-3); }
.flow-node[data-on="run"] .flow-state { color: var(--amber-ink); }
.flow-node b { display: block; color: var(--ink); font-size: var(--t-ui); font-weight: 500; }
.flow-note { display: block; margin-top: 4px; color: var(--ink-3); font-size: var(--t-meta); line-height: 1.5; }
.flow-chips { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 9px; }
.flow-chips i {
  padding: 2px 6px;
  border: 1px solid var(--rule); border-radius: 3px;
  color: var(--ink-2);
  font-family: var(--font-mono); font-size: 10px; font-style: normal;
}

.flow-handle {
  position: absolute; width: 7px; height: 7px;
  border: 1px solid var(--ink-faint); border-radius: 50%;
  background: var(--sheet);
}
.flow-handle[data-side="l"] { left: -4px; top: 50%; margin-top: -4px; }
.flow-handle[data-side="r"] { right: -4px; top: 50%; margin-top: -4px; }
.flow-handle[data-side="t"] { top: -4px; left: 50%; margin-left: -4px; }
.flow-handle[data-side="b"] { bottom: -4px; left: 50%; margin-left: -4px; }

/* ── Reduced motion ─────────────────────────────────────────────────────── */

@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;
  }
  .hero-enter, .js .reveal { opacity: 1; transform: none; }
  .hero h1 span::after { transform: rotate(0.5deg) scaleX(1); }
  .metric-bar i { width: var(--score); }
}
