

    /* ============================================================
       CAPABILITY NAV
       One bar, shared across the capability pages. Each entry is a plain
       link to the page it names; the current one is marked at runtime by
       markCurrentCapabilityNavLink().
       ============================================================ */
    .cap-tabs {
      display: flex;
      flex-wrap: wrap;
      align-items: baseline;
      gap: 2px 22px;
      margin: 0 0 26px 0;
      font-family: var(--mono);
      font-size: 12px;
      text-transform: uppercase;
      letter-spacing: 0.1em;
    }
    .cap-tab {
      color: #bcbcb6;
      text-decoration: none;
      cursor: pointer;
      padding-bottom: 7px;
      border-bottom: 2px solid transparent;
      white-space: nowrap;
      transition: color 0.3s, border-color 0.3s;
    }
    .cap-tab:hover { color: var(--graphite); border-bottom-color: #d6d6d0; }
    /* Each long tab carries a full label and a one-word label. Only one is ever
       in the a11y tree, since the other is display:none, so the link's accessible
       name is always what is on screen. The mobile sheet does the swap.
       Classify has no spans: it is already one word. */
    .cap-short { display: none; }

    .cap-tab[aria-current="page"] {
      color: var(--accent);
      border-bottom-color: var(--accent);
      cursor: default;
    }

    /* A page carries one capability's panels, and shows all of them.
       There is deliberately no entrance animation. One used to fade the
       incoming panel up when the tabs swapped them in place; now that each
       capability is its own page it would run on every load, leaving the
       sheet visibly empty for half a second on each navigation. Panels
       behind the annotation layer keep their own timed reveal. */
    .cap-panel { display: block; }

    /* --- Showcase: document splitting ---------------------------
       At rest it reads as an undifferentiated pile of pages. On hover
       the cuts appear and each run names itself. Same idiom as the
       extraction showcase: hover reveals the structure that was
       already there. */
    /* Spacing between runs matches spacing inside them, so at rest the
       pile gives nothing away. The reveal uses outline rather than
       border because outline costs no layout — the strip must not
       reflow mid-transition. */
    /* --- Showcase: document splitting ---------------------------
       The real thing: 104 pages of one e-signed closing package, cut
       into 33 documents. Structure only, never page images or values —
       the source is a live borrower file. The strip is ~3461px, four
       times the width of the sheet, so it runs off the right edge and
       is fed through by the cursor. */
    .strip-viewport {
      overflow: hidden;
      cursor: crosshair;
      /* the packet visibly carries on past the edge of the sheet */
      -webkit-mask-image: linear-gradient(to right, #000 90%, transparent);
      mask-image: linear-gradient(to right, #000 90%, transparent);
    }
    .split-strip {
      display: flex;
      flex-wrap: nowrap;
      width: max-content;
      gap: 0 4px;
      padding: 2px 0;
      transform: translateX(0);
      transition: transform 0.7s ease;
    }
    /* Hovering feeds the packet through; leaving snaps it back. */
    .strip-viewport:hover .split-strip {
      transform: translateX(-2560px);
      transition: transform 46s linear;
    }
    .doc-run {
      display: flex;
      flex: none;
      gap: 3px;
      position: relative;
      padding: 20px 0 6px;
      outline: 1px solid transparent;
      outline-offset: 1px;
      /* A label is nearly always wider than the document it names, so
         it is simply cut to the width of its own pages. A one-page
         document gets four characters. The clip IS the page count. */
      overflow: hidden;
      transition: outline-color 0.5s cubic-bezier(0.16, 1, 0.3, 1),
                  background 0.5s cubic-bezier(0.16, 1, 0.3, 1);
    }
    .doc-run::after {
      content: attr(data-label);
      position: absolute;
      top: 5px;
      left: 0;
      font-family: var(--mono);
      font-size: 9px;
      letter-spacing: 0.07em;
      text-transform: uppercase;
      color: var(--accent);
      white-space: nowrap;
      opacity: 0;
      transform: translateY(4px);
      transition: opacity 0.5s, transform 0.5s;
    }
    .pg {
      --sk: #d9d9d4;
      width: 30px;
      height: 40px;
      flex: none;
      box-sizing: border-box;
      background-color: #eaeae6;
      background-repeat: no-repeat;
      border: 1px solid #e0e0db;
      transition: background-color 0.5s, border-color 0.5s;
    }
    /* Five page skeletons. Which one a page gets comes from its
       document's type, and the last page of anything long enough to be
       signed gets the signature block instead. */
    .sk-prose {
      background-image: repeating-linear-gradient(to bottom, var(--sk) 0 1px, transparent 1px 4px);
      background-size: 20px 30px;
      background-position: 4px 5px;
    }
    .sk-form {
      background-image:
        linear-gradient(var(--sk), var(--sk)),
        repeating-linear-gradient(to bottom, var(--sk) 0 1px, transparent 1px 6px);
      background-size: 11px 2px, 20px 24px;
      background-position: 4px 5px, 4px 12px;
    }
    .sk-ledger {
      background-image: repeating-linear-gradient(to bottom, var(--sk) 0 1px, transparent 1px 7px);
      background-size: 20px 29px;
      background-position: 4px 6px;
    }
    .sk-notice {
      background-image:
        linear-gradient(var(--sk), var(--sk)),
        repeating-linear-gradient(to bottom, var(--sk) 0 1px, transparent 1px 4px);
      background-size: 15px 2px, 20px 11px;
      background-position: 4px 7px, 4px 15px;
    }
    .sk-sig {
      background-image:
        repeating-linear-gradient(to bottom, var(--sk) 0 1px, transparent 1px 4px),
        linear-gradient(var(--sk), var(--sk));
      background-size: 20px 8px, 13px 1px;
      background-position: 4px 5px, 4px 27px;
    }
    .strip-viewport:hover .doc-run {
      outline-color: rgba(217, 37, 37, 0.55);
      background: rgba(217, 37, 37, 0.03);
    }
    .strip-viewport:hover .doc-run::after { opacity: 1; transform: none; }
    .strip-viewport:hover .pg {
      --sk: #c6c6c0;
      background-color: #ffffff;
      border-color: #d8d8d2;
    }

    /* --- Showcase: document matching ----------------------------
       A requirement checklist that resolves under the cursor. Every state
       the matcher can return gets its own mark: approved and recommended
       carry a document, superseded and pending carry the wrong one or none
       yet, and only a true gap fills solid so the misses read first. */
    .match-list { cursor: crosshair; }
    .match-group {
      display: flex;
      align-items: baseline;
      justify-content: space-between;
      gap: 12px;
      margin: 18px 0 2px;
      padding-bottom: 5px;
      border-bottom: 1px solid var(--line);
      font-family: var(--mono);
      font-size: 9px;
      font-weight: 400;
      letter-spacing: 0.14em;
      text-transform: uppercase;
      color: #cdcdc7;
      transition: color 0.45s;
    }
    .match-group:first-child { margin-top: 0; }
    .match-count { font-variant-numeric: tabular-nums; }
    .match-row {
      display: flex;
      align-items: baseline;
      gap: 14px;
      padding: 7px 2px;
      border-bottom: 1px solid var(--line);
      transition: border-color 0.45s cubic-bezier(0.16, 1, 0.3, 1);
    }
    .match-row:last-child { border-bottom: 0; }
    .match-req {
      flex: 1 1 auto;
      font-size: 14px;
      color: var(--graphite);
      transition: color 0.45s;
    }
    .match-doc {
      font-family: var(--mono);
      font-size: 10px;
      letter-spacing: 0.03em;
      color: #c6c6c0;
      text-align: right;
      transition: color 0.45s;
    }
    .match-pp {
      margin-left: 7px;
      color: #d7d7d1;
      transition: color 0.45s;
    }
    .match-state {
      flex: 0 0 auto;
      width: 86px;
      text-align: center;
      font-family: var(--mono);
      font-size: 8px;
      letter-spacing: 0.08em;
      text-transform: uppercase;
      padding: 3px 0;
      border: 1px solid var(--line);
      color: #c6c6c0;
      transition: color 0.45s, border-color 0.45s, background 0.45s;
    }

    .match-list:hover .match-group { color: var(--graphite); }
    .match-list:hover .match-req { color: var(--ink); }
    .match-list:hover .match-doc { color: var(--graphite); }
    .match-list:hover .match-pp { color: #a9a9a3; }
    /* Settled: a document is bound and filed. */
    .match-list:hover .is-approved .match-state {
      border-color: var(--ink);
      color: var(--ink);
    }
    /* The engine's answer, still waiting on an approval. */
    .match-list:hover .is-recommended .match-state {
      border-style: dashed;
      border-color: var(--ink);
      color: var(--ink);
    }
    /* Bound, but the run has not finished reading it. */
    .match-list:hover .is-pending .match-state {
      border-style: dotted;
      border-color: var(--graphite);
      color: var(--graphite);
    }
    /* A real document for the wrong period, so it satisfies nothing. */
    .match-list:hover .is-superseded .match-state {
      border-color: #cfcfc9;
      color: #a9a9a3;
    }
    .match-list:hover .is-superseded .match-doc {
      color: #a9a9a3;
      text-decoration: line-through;
    }
    /* Arrived, but no requirement claims it. */
    .match-list:hover .is-unassigned .match-req {
      color: var(--graphite);
      font-style: italic;
    }
    .match-list:hover .is-unassigned .match-state {
      border-style: dashed;
      border-color: var(--graphite);
      color: var(--graphite);
    }
    /* The gap. The only state that fills. */
    .match-list:hover .is-gap { border-bottom-color: var(--accent); }
    .match-list:hover .is-gap .match-doc { color: var(--accent); }
    .match-list:hover .is-gap .match-state {
      border-color: var(--accent);
      background: var(--accent);
      color: #ffffff;
    }
    .match-list:hover .match-row:nth-of-type(1) * { transition-delay: 0.00s; }
    .match-list:hover .match-row:nth-of-type(2) * { transition-delay: 0.03s; }
    .match-list:hover .match-row:nth-of-type(3) * { transition-delay: 0.06s; }
    .match-list:hover .match-row:nth-of-type(4) * { transition-delay: 0.09s; }
    .match-list:hover .match-row:nth-of-type(5) * { transition-delay: 0.12s; }
    .match-list:hover .match-row:nth-of-type(6) * { transition-delay: 0.15s; }
    .match-list:hover .match-row:nth-of-type(7) * { transition-delay: 0.18s; }
    .match-list:hover .match-row:nth-of-type(8) * { transition-delay: 0.21s; }
    .match-list:hover .match-row:nth-of-type(9) * { transition-delay: 0.24s; }
    .match-list:hover .match-row:nth-of-type(10) * { transition-delay: 0.27s; }
    .match-list:hover .match-row:nth-of-type(11) * { transition-delay: 0.30s; }
    .match-list:hover .match-row:nth-of-type(12) * { transition-delay: 0.33s; }
    .match-list:hover .match-row:nth-of-type(13) * { transition-delay: 0.36s; }
    .match-list:hover .match-row:nth-of-type(14) * { transition-delay: 0.39s; }
    .match-list:hover .match-row:nth-of-type(15) * { transition-delay: 0.42s; }
    .match-list:hover .match-row:nth-of-type(16) * { transition-delay: 0.45s; }
    .match-list:hover .match-row:nth-of-type(17) * { transition-delay: 0.48s; }
    .match-list:hover .match-row:nth-of-type(18) * { transition-delay: 0.51s; }

    /* Capability pipelines that run four steps instead of five. */
    .pipeline--4 { grid-template-columns: repeat(4, 1fr); }

    /* ============================================================
       VERSO — the sheet turns over.
       Nothing below applies at rest. The page carries no transform
       and no perspective until .is-inverted lands, so the
       fixed-position intake overlay keeps behaving normally.
       ============================================================ */

    .trigger {
      color: var(--ink, #000000);
      cursor: crosshair;
      white-space: nowrap;
      border-bottom: 3px dashed rgba(22, 22, 22, 0.4);
      transition: border-bottom-color 0.3s;
    }
    .trigger:hover,
    .trigger:focus-visible { border-bottom-color: var(--ink, #000000); }

    .page {
      transform-origin: center center;
      transition: transform 1.1s cubic-bezier(0.64, 0, 0.05, 1),
                  background-color 0.6s ease 0.45s;
    }

    /* The turn */
    body.is-inverted .page {
      transform-style: preserve-3d;
      transform: perspective(3000px) rotateY(180deg);
      background-color: var(--paper-dark);
      pointer-events: none;
    }
    /* the trigger stays live so mouseleave still fires */
    body.is-inverted .trigger {
      pointer-events: auto;
    }
    /* The printed side, now seen through the paper. Ghosting is done
       with colour, never opacity or filter: the annotation layer lives
       inside main and would inherit either one, which is precisely what
       would render the whole reveal invisible. */
    body.is-inverted .page :where(h1, h2, h3, p, span, div, a, li, b, strong, em, label, pre, code, ol) {
      color: #b7b7b1;
      transition: color 0.4s ease 0.45s;
    }
    body.is-inverted .page img { opacity: 0.1; }
    /* Solid blocks would read as holes punched in the sheet. */
    body.is-inverted .intake-trigger,
    body.is-inverted .output-code {
      background: transparent;
      border-color: rgba(0, 0, 0, 0.08);
    }
    body.is-inverted .document-viewer {
      background: transparent;
      border-color: rgba(0, 0, 0, 0.08);
    }
    body.is-inverted .viewer-header { background: transparent; }
    body.is-inverted .pg {
      --sk: #e4e4df;
      background-color: transparent;
      border-color: rgba(0, 0, 0, 0.06);
    }


    /* ============================================================
       ANNOTATION LAYER - what the turn actually reveals.
       Sheaf reading its own page: claims boxed, confidence scored,
       its own marketing hyperbole flagged. The sheet is mirrored, so
       every annotation carries rotateY(180deg) to cancel that out and
       read the right way round on the back.
       ============================================================ */

    .scan-lamp {
      position: absolute;
      left: 0; right: 0; top: 0;
      height: 2px;
      background: var(--accent);
      box-shadow: 0 0 22px 5px rgba(217, 37, 37, 0.45);
      opacity: 0;
      pointer-events: none;
      z-index: 60;
      transform: rotateY(180deg);
    }
    body.is-inverted .scan-lamp {
      animation: scan-pass 4.4s linear forwards 0.95s;
    }
    /* Travels one viewport height, not 100% - .scan-lamp spans the whole
       sheet, so a 0->100% sweep clears the visible area almost at once. */
    @keyframes scan-pass {
      0%   { top: 0;     opacity: 0; }
      3%   { opacity: 1; }
      97%  { opacity: 1; }
      100% { top: 100vh; opacity: 0; }
    }

    /* A boxed claim. Completely inert until the sheet turns. */
    .extract-box { position: relative; }
    .eb-inline { display: inline-block; }
    .extract-box::before,
    .extract-box::after {
      opacity: 0;
      pointer-events: none;
      transform: rotateY(180deg);
    }
    .extract-box::before {
      content: "";
      position: absolute;
      inset: -6px -10px;
      border: 1px solid var(--accent);
      background: rgba(217, 37, 37, 0.04);
      z-index: 1;
    }
    .extract-box::after {
      content: attr(data-label) " [CF:" attr(data-conf) "]";
      position: absolute;
      top: calc(100% + 3px);
      right: -10px; /* sits on the left once mirrored */
      background: var(--accent);
      color: #ffffff;
      font-family: var(--mono);
      font-size: 10px;
      font-weight: 400;
      letter-spacing: 0.06em;
      padding: 2px 5px;
      white-space: nowrap;
      z-index: 2;
    }
    /* The flag Sheaf puts on its own marketing copy. */
    .eb-flag::before {
      border: 1px dashed #c2870f;
      background: rgba(194, 135, 15, 0.05);
    }
    .eb-flag::after { background: #c2870f; }

    @keyframes anno-in { from { opacity: 0; } to { opacity: 1; } }
    body.is-inverted .extract-box::before,
    body.is-inverted .extract-box::after {
      animation: anno-in 0.28s linear forwards;
    }
    body.is-inverted .eb-1::before, body.is-inverted .eb-1::after { animation-delay: 1.25s; }
    body.is-inverted .eb-2::before, body.is-inverted .eb-2::after { animation-delay: 1.55s; }
    body.is-inverted .eb-3::before, body.is-inverted .eb-3::after { animation-delay: 1.95s; }
    body.is-inverted .eb-4::before, body.is-inverted .eb-4::after { animation-delay: 2.35s; }
    body.is-inverted .eb-5::before, body.is-inverted .eb-5::after { animation-delay: 2.60s; }

    /* Each showcase holds its revealed state while the sheet is turned,
       and its labels counter-rotate the same way the claim boxes do. */
    body.is-inverted .dense-text .filler { color: #d2d2cd; }
    body.is-inverted .dense-text .entity { border-color: rgba(217, 37, 37, 0.45); }
    body.is-inverted .dense-text .entity::before { opacity: 1; }
    body.is-inverted .dense-text .entity::after {
      opacity: 1;
      transform: rotateY(180deg);
    }
    body.is-inverted .dense-text .entity:nth-of-type(1)::after { transition-delay: 2.20s; }
    body.is-inverted .dense-text .entity:nth-of-type(2)::after { transition-delay: 2.28s; }
    body.is-inverted .dense-text .entity:nth-of-type(3)::after { transition-delay: 2.36s; }
    body.is-inverted .dense-text .entity:nth-of-type(4)::after { transition-delay: 2.44s; }
    body.is-inverted .dense-text .entity:nth-of-type(5)::after { transition-delay: 2.52s; }
    body.is-inverted .dense-text .entity:nth-of-type(6)::after { transition-delay: 2.60s; }
    body.is-inverted .dense-text .entity:nth-of-type(7)::after { transition-delay: 2.68s; }
    body.is-inverted .dense-text .entity:nth-of-type(8)::after { transition-delay: 2.76s; }
    body.is-inverted .doc-run {
      outline-color: rgba(217, 37, 37, 0.55);
      background: rgba(217, 37, 37, 0.03);
    }
    body.is-inverted .doc-run::after {
      opacity: 1;
      transform: rotateY(180deg);
    }
    body.is-inverted .doc-run::after { transition-delay: 2.20s; }
    body.is-inverted .is-gap { border-bottom-color: rgba(217, 37, 37, 0.45); }

    /* Mobile & A11y */
    /* ============================================================
       Technical explainer drawer.

       SEO posture, deliberately: every word below is in the served
       HTML, inside real headings, lists and a figure. Nothing is
       fetched, injected or built by script. The drawer is a
       presentation layer over indexable prose, and the prose has an
       address of its own that a link can point at.

       Closed state is visibility, not display:none, and the whole
       thing lays itself flat for print — so the content survives
       reader modes and paper as ordinary document text.
       ============================================================ */
    .spec-drawer {
      position: fixed;
      inset: 0;
      z-index: 200;
      visibility: hidden;
      transition: visibility 0s linear 0.55s;
    }
    .spec-drawer:target {
      visibility: visible;
      transition-delay: 0s;
    }

    .spec-scrim {
      position: absolute;
      inset: 0;
      display: block;
      background: rgba(10, 10, 10, 0.55);
      opacity: 0;
      transition: opacity 0.45s ease;
    }
    .spec-drawer:target .spec-scrim { opacity: 1; }

    .spec-panel {
      position: absolute;
      top: 0;
      right: 0;
      height: 100%;
      width: min(580px, 100%);
      box-sizing: border-box;
      padding: 0 44px 64px;
      background: var(--paper);
      border-left: 1px solid var(--line);
      box-shadow: -30px 0 70px rgba(0, 0, 0, 0.4);
      overflow-y: auto;
      overscroll-behavior: contain;
      transform: translateX(100%);
      transition: transform 0.55s cubic-bezier(0.16, 1, 0.3, 1);
      outline: none;
    }
    .spec-drawer:target .spec-panel { transform: none; }

    .spec-head {
      position: sticky;
      top: 0;
      z-index: 2;
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: 20px;
      padding: 30px 0 16px;
      background: var(--paper);
      border-bottom: 1px solid var(--ink);
    }
    .spec-eyebrow {
      font-family: var(--mono);
      font-size: 10px;
      letter-spacing: 0.14em;
      text-transform: uppercase;
      color: var(--graphite);
    }
    .spec-dismiss {
      font-family: var(--mono);
      font-size: 10px;
      letter-spacing: 0.12em;
      text-transform: uppercase;
      color: var(--ink);
      text-decoration: none;
      border: 1px solid var(--line);
      padding: 6px 12px;
      white-space: nowrap;
      transition: color 0.25s, border-color 0.25s;
    }
    .spec-dismiss:hover {
      color: var(--accent);
      border-color: var(--accent);
    }

    .spec-title {
      font-size: 30px;
      line-height: 1.1;
      letter-spacing: -0.02em;
      margin: 30px 0 14px;
      color: var(--ink);
    }
    .spec-standfirst {
      font-size: 16px;
      line-height: 1.5;
      color: var(--graphite);
      margin: 0 0 30px;
    }
    .spec-body h3 {
      font-size: 17px;
      letter-spacing: -0.01em;
      margin: 38px 0 12px;
      color: var(--ink);
    }
    .spec-body p {
      font-size: 14.5px;
      line-height: 1.62;
      color: #3d3d3a;
      margin: 0 0 14px;
    }
    .spec-body code {
      font-family: var(--mono);
      font-size: 12.5px;
      background: var(--paper-dark);
      padding: 1px 5px;
      white-space: nowrap;
    }

    /* Block payload. .spec-body code is inline and nowrap, so a <pre> has to
       undo both or the JSON renders as one unbroken line. */
    .spec-pre {
      margin: 0 0 14px;
      padding: 14px 16px;
      background: var(--paper-dark);
      border: 1px solid var(--line);
      overflow-x: auto;
    }
    .spec-pre code {
      display: block;
      background: none;
      padding: 0;
      white-space: pre;
      font-size: 12px;
      line-height: 1.7;
      color: var(--ink);
    }
    .spec-rule {
      margin: 32px 0 30px;
      border: 0;
      border-top: 1px solid var(--line);
    }

    .spec-figure {
      margin: 0;
      padding: 22px 18px 18px;
      border: 1px solid var(--line);
      background: var(--paper-dark);
    }
    .spec-diagram {
      display: block;
      width: 100%;
      height: auto;
    }
    .spec-figure figcaption {
      font-family: var(--mono);
      font-size: 10px;
      line-height: 1.6;
      letter-spacing: 0.02em;
      color: var(--graphite);
      margin-top: 16px;
      text-align: center;
    }

    .d-box { fill: none; stroke: var(--ink); stroke-width: 1; }
    .d-box--ghost {
      fill: none;
      stroke: var(--graphite);
      stroke-width: 1;
      stroke-dasharray: 4 3;
    }
    .d-label {
      font-family: var(--mono);
      font-size: 11px;
      letter-spacing: 0.08em;
      fill: var(--ink);
    }
    .d-sub {
      font-family: var(--mono);
      font-size: 9px;
      letter-spacing: 0.03em;
      fill: var(--graphite);
    }
    .d-sub--flag { fill: var(--accent); }
    .d-arrow { stroke: var(--ink); stroke-width: 1; }
    .d-arrow--ghost {
      stroke: var(--graphite);
      stroke-width: 1;
      stroke-dasharray: 4 3;
    }
    .d-head { fill: var(--ink); }
    .d-head--ghost { fill: var(--graphite); }

    .spec-dl { margin: 0 0 6px; }
    .spec-dl dt {
      font-family: var(--mono);
      font-size: 10px;
      letter-spacing: 0.12em;
      text-transform: uppercase;
      color: var(--ink);
      margin-top: 16px;
    }
    .spec-dl dd {
      margin: 5px 0 0;
      padding-left: 12px;
      border-left: 1px solid var(--line);
      font-size: 13.5px;
      line-height: 1.55;
      color: var(--graphite);
    }

    .spec-ol {
      margin: 0 0 14px;
      padding-left: 0;
      list-style: none;
      counter-reset: spec;
    }
    .spec-ol li {
      counter-increment: spec;
      position: relative;
      padding-left: 30px;
      margin-bottom: 11px;
      font-size: 13.5px;
      line-height: 1.55;
      color: #3d3d3a;
    }
    .spec-ol li::before {
      content: counter(spec, decimal-leading-zero);
      position: absolute;
      left: 0;
      top: 2px;
      font-family: var(--mono);
      font-size: 10px;
      color: var(--accent);
    }

    .spec-note {
      margin-top: 34px;
      padding-top: 18px;
      border-top: 1px solid var(--line);
      font-size: 12.5px;
      line-height: 1.6;
      color: var(--graphite);
    }

    /* The way in. One per capability page, in the served markup, so a
       crawler reaches the spec without running any script. */
    .spec-link {
      display: flex;
      align-items: baseline;
      gap: 18px;
      margin-top: 56px;
      padding: 18px 22px;
      border: 1px solid var(--line);
      border-left: 3px solid var(--ink);
      text-decoration: none;
      color: inherit;
      transition: background-color 0.3s, border-left-color 0.3s;
    }
    .spec-link:hover {
      background: var(--paper-dark);
      border-left-color: var(--accent);
    }
    .spec-link-eyebrow {
      font-family: var(--mono);
      font-size: 10px;
      letter-spacing: 0.14em;
      text-transform: uppercase;
      color: var(--accent);
      white-space: nowrap;
    }
    .spec-link-title {
      font-size: 15px;
      line-height: 1.4;
      color: var(--ink);
      flex: 1;
    }
    .spec-link-cue {
      font-family: var(--mono);
      font-size: 11px;
      color: var(--graphite);
      white-space: nowrap;
    }
    .spec-link:hover .spec-link-cue { color: var(--accent); }

    /* --- Showcase: adaptive review ------------------------------
       The decisions a desk has already made, and what became of them. At
       rest it reads as a list of open questions; under the cursor most are
       already policy, and the ones reserved for a person stay reserved. */
    .rev-list { cursor: crosshair; }
    .rev-row {
      display: flex;
      align-items: baseline;
      gap: 14px;
      padding: 7px 2px;
      border-bottom: 1px solid var(--line);
    }
    .rev-row:last-child { border-bottom: 0; }
    .rev-q {
      flex: 1 1 auto;
      font-size: 14px;
      color: var(--graphite);
      transition: color 0.45s;
    }
    .rev-a {
      font-family: var(--mono);
      font-size: 10px;
      letter-spacing: 0.03em;
      color: #c6c6c0;
      text-align: right;
      transition: color 0.45s;
    }
    /* The tally only appears once the list resolves — at rest the point is
       the question, not how many times it has already been answered. */
    .rev-n {
      margin-left: 7px;
      color: #d7d7d1;
      opacity: 0;
      transition: opacity 0.45s, color 0.45s;
    }
    .rev-state {
      flex: 0 0 auto;
      width: 86px;
      text-align: center;
      font-family: var(--mono);
      font-size: 8px;
      letter-spacing: 0.08em;
      text-transform: uppercase;
      padding: 3px 0;
      border: 1px solid var(--line);
      color: #c6c6c0;
      transition: color 0.45s, border-color 0.45s, background 0.45s;
    }
    .rev-list:hover .match-group { color: var(--graphite); }
    .rev-list:hover .rev-q { color: var(--ink); }
    .rev-list:hover .rev-a { color: var(--graphite); }
    .rev-list:hover .rev-n { opacity: 1; color: #a9a9a3; }
    /* Learned: answered the same way twice, so it stopped being a question.
       Solid ink rather than accent — this is the good outcome, and accent is
       spoken for by the misses on the matching panel. */
    .rev-list:hover .is-auto .rev-state {
      border-color: var(--ink);
      background: var(--ink);
      color: #ffffff;
    }
    /* Watched: one more agreeing decision promotes it. */
    .rev-list:hover .is-learning .rev-state {
      border-style: dashed;
      border-color: var(--ink);
      color: var(--ink);
    }
    /* Reserved: policy puts a person on this one however often it recurs. */
    .rev-list:hover .is-always .rev-state {
      border-style: dotted;
      border-color: var(--graphite);
      color: var(--graphite);
    }

    /* --- Showcase: corpus intelligence --------------------------
       One plain question put to every file at once. At rest it reads as
       the answer, because that is what you asked for. Under the cursor
       each row names the two documents it had to read together to find
       it -- same idiom as the other panels: hover reveals the evidence
       that was underneath the whole time. */
    .q-block { cursor: crosshair; }
    .q-ask {
      display: flex;
      align-items: baseline;
      gap: 9px;
      margin: 0 0 12px;
      padding-bottom: 12px;
      border-bottom: 1px solid var(--line);
      font-size: 15px;
      line-height: 1.45;
      color: var(--ink);
    }
    .q-caret {
      flex: 0 0 auto;
      font-family: var(--mono);
      font-size: 13px;
      color: var(--accent);
    }
    .q-row {
      display: flex;
      align-items: baseline;
      gap: 14px;
      padding: 7px 2px;
      border-bottom: 1px solid var(--line);
    }
    .q-row:last-child { border-bottom: 0; }
    .q-subject {
      flex: 1 1 auto;
      font-size: 14px;
      color: var(--graphite);
      transition: color 0.45s;
    }
    .q-delta {
      flex: 0 0 auto;
      font-family: var(--mono);
      font-size: 12px;
      color: var(--graphite);
      transition: color 0.45s;
    }
    /* The pages behind the number. Withheld at rest: the answer is the
       point, and the proof is the thing you reach for second. */
    .q-cite {
      flex: 0 0 auto;
      width: 176px;
      text-align: right;
      white-space: nowrap;
      font-family: var(--mono);
      font-size: 9px;
      letter-spacing: 0.03em;
      color: #d7d7d1;
      opacity: 0;
      transition: opacity 0.45s, color 0.45s;
    }
    .q-block:hover .q-subject { color: var(--ink); }
    .q-block:hover .q-delta   { color: var(--accent); }
    .q-block:hover .q-cite    { opacity: 1; color: #a9a9a3; }
    .q-foot {
      display: flex;
      justify-content: space-between;
      gap: 14px;
      margin-top: 13px;
      padding-top: 12px;
      border-top: 1px solid var(--line);
      font-family: var(--mono);
      font-size: 9px;
      letter-spacing: 0.08em;
      text-transform: uppercase;
      color: #c6c6c0;
    }

    /* The quiet way in, used at the top of the page where the title
       has to keep the weight. The bordered .spec-link does the same
       job further down, where it can afford the room. */
    .spec-inline {
      display: inline-flex;
      align-items: center;
      gap: 8px;
      margin: -26px 0 32px;
      padding-bottom: 4px;
      font-family: var(--mono);
      font-size: 11px;
      letter-spacing: 0.08em;
      text-transform: uppercase;
      color: var(--graphite);
      text-decoration: none;
      border-bottom: 1px dashed var(--line);
      transition: color 0.3s, border-color 0.3s;
    }
    .spec-inline:hover {
      color: var(--accent);
      border-bottom-color: var(--accent);
    }
    .spec-inline-cue {
      display: inline-block;
      transition: transform 0.3s;
    }
    .spec-inline:hover .spec-inline-cue { transform: translateX(3px); }

    /* Fixed, so closing a drawer scrolls the page by exactly nothing —
       the anchor is already in the viewport wherever you happen to be.
       Lives outside .page for the same reason the drawers do. */
    .close-anchor {
      position: fixed;
      top: 0;
      left: 0;
      width: 1px;
      height: 1px;
      overflow: hidden;
      pointer-events: none;
    }

    @media print {
      .spec-drawer { position: static; visibility: visible; }
      .spec-scrim,
      .spec-dismiss,
      .spec-link-cue { display: none; }
      .spec-panel {
        position: static;
        width: auto;
        height: auto;
        overflow: visible;
        transform: none;
        padding: 0;
        border-left: 0;
        box-shadow: none;
      }
      .spec-head { position: static; }
    }

    @media (prefers-reduced-motion: reduce) {
      .spec-panel,
      .spec-scrim { transition: none; }
      html { scroll-behavior: auto; }
      *, *::before, *::after { transition-duration: 0.01ms !important; animation-duration: 0.01ms !important; }

      /* The turn is a physical motion effect — drop it entirely. */
      body.is-inverted .page {
        transform: none !important;
        pointer-events: auto !important;
      }
      /* No self-feeding packet; scroll it by hand instead. */
      .strip-viewport { overflow-x: auto; }
      .split-strip,
      .strip-viewport:hover .split-strip { transform: none; transition: none; }
      .scan-lamp,
      .extract-box::before,
      .extract-box::after { display: none !important; }
    }

