/* ============================================================
   Waystone landing page styles.
   Extracted VERBATIM from the inline <style> in index.html on the Beta 2
   rebuild. Lifted out first, unchanged, so that the move and the later
   deletions stay separately reversible.
   _shared.css holds the tokens, chrome and the console primitives.
   ============================================================ */
    /* ============================================================
       Landing-specific styles only. Tokens come from _shared.css.
       ============================================================ */

    /* ──────────────── §1 Hero (V1 canvas constellation) ──────────────── */
    .hero {
      position: relative;
      min-height: 100vh;
      min-height: 100svh;
      display: flex; flex-direction: column;
      justify-content: center;
      padding-top: 120px;
      padding-bottom: 80px;
      overflow: hidden;
      isolation: isolate;
    }
    .hero > .wrap { position: relative; z-index: 3; }
    .hero::before {
      content: ''; position: absolute; inset: 0; z-index: 0;
      background:
        radial-gradient(ellipse 55% 65% at 60% 40%, rgba(201,168,76,0.10) 0%, transparent 60%),
        radial-gradient(ellipse 30% 50% at 15% 70%, rgba(201,168,76,0.055) 0%, transparent 55%);
      pointer-events: none;
    }
    .hero::after {
      content: ''; position: absolute; inset: auto 0 0 0; height: 240px;
      background: linear-gradient(to bottom, transparent 0%, var(--bg) 100%);
      z-index: 2; pointer-events: none;
    }
    .hero-canvas {
      position: absolute; inset: 0;
      width: 100%; height: 100%;
      z-index: 1; display: block; pointer-events: none;
      opacity: 0;
      transition: opacity 1200ms ease;
    }
    .hero-canvas.is-on { opacity: 1; }

    .hero-inner {
      position: relative;
      display: flex; flex-direction: column; align-items: flex-start;
      gap: 28px;
    }
    .hero-eyebrow { display: inline-flex; align-items: center; gap: 12px; }
    .hero-eyebrow .mono { color: var(--t3); }
    .hero-eyebrow .gold-rule { width: 28px; margin: 0; }

    .hero-wordmark {
      font-family: var(--font-display);
      font-weight: 300;
      font-size: clamp(72px, 12.5vw, 168px);
      letter-spacing: 0.005em;
      line-height: 0.95;
      color: var(--t1);
      margin: 6px 0 0;
      display: block;
    }
    .hero-wordmark .stone {
      color: var(--gold);
      background: linear-gradient(
        100deg,
        var(--gold) 0%, var(--gold) 35%,
        var(--gold-bright) 47%,
        #FFF0CC 50%,
        var(--gold-bright) 53%,
        var(--gold) 65%, var(--gold) 100%
      );
      background-size: 260% 100%;
      background-position: 130% 0;
      -webkit-background-clip: text;
              background-clip: text;
      -webkit-text-fill-color: transparent;
      animation: stoneSweep 7.5s cubic-bezier(0.65, 0, 0.35, 1) infinite;
    }
    @keyframes stoneSweep {
      0%   { background-position: 130% 0; }
      55%  { background-position: -30% 0; }
      100% { background-position: -130% 0; }
    }

    .hero-tagline { max-width: 22ch; margin-top: 12px; }
    .hero-sub {
      font: 400 16.5px/1.65 var(--font-body);
      color: var(--t2);
      max-width: 50ch;
      margin-top: 18px;
    }

    @media (prefers-reduced-motion: reduce) {
      .hero-wordmark .stone { animation: none; background: none; -webkit-text-fill-color: var(--gold); color: var(--gold); }
      .hero-canvas { display: none; }
    }

    .hero-cue {
      position: absolute; left: 50%; bottom: 36px;
      transform: translateX(-50%);
      display: flex; flex-direction: column; align-items: center;
      gap: 12px;
      font: 500 10px/1 var(--font-body);
      letter-spacing: var(--tr-label); text-transform: uppercase;
      color: var(--t2);
      opacity: 0;
      animation: cue-in 1.4s var(--ease-out) 1.2s forwards,
                 cue-pulse 3.4s var(--ease-in-out) 2.6s infinite;
    }
    .hero-cue .line {
      width: 1px; height: 36px;
      background: linear-gradient(180deg, transparent, var(--gold-dim) 30%, var(--gold) 100%);
    }
    @keyframes cue-in { to { opacity: 0.85; } }
    @keyframes cue-pulse {
      0%, 100% { transform: translate(-50%, 0); opacity: 0.85; }
      50%      { transform: translate(-50%, 6px); opacity: 0.5; }
    }

    /* ──────────────── §2 Premise ──────────────── */
    .premise { padding-block: var(--sp-xxl); }
    .premise-grid {
      display: block;
    }
    .premise-col-text { min-width: 0; max-width: 760px; }
    .premise-col-devices { min-width: 0; position: relative; }

    /* Device composition — iMac (main) + iPhone overlapping lower-right */
    .device-combo {
      position: relative;
      width: 100%;
      max-width: 1180px;
      margin: clamp(48px, 6vw, 96px) auto 0;
      /* leave room for the phone to drop below the monitor's bottom edge */
      padding-bottom: clamp(48px, 7%, 96px);
    }

    /* iMac display: screenshot behind, real monitor PNG on top */
    .device-imac {
      position: relative;
      width: 100%;
      aspect-ratio: 1189 / 883;
      filter:
        drop-shadow(0 60px 70px rgba(0,0,0,0.55))
        drop-shadow(0 22px 36px rgba(0,0,0,0.28));
    }
    /* The frame art's aperture is 1.93:1, wider than a screen. A 16:9 capture
       was being cover-cropped 8% — the app's header and search bar lost off the
       top and bottom. The image now sits 16:9 in the aperture's height, centred,
       on black; the sliver either side reads as bezel. */
    .device-imac-screen {
      position: absolute;
      left: 6.85%;
      top: 4.304%;
      width: 86.37%;
      height: 65.459%;
      object-fit: cover;
      object-position: center center;
      display: block;
      background: #000;
    }
    .device-imac::before {
      content: ""; position: absolute;
      left: 3.028%; top: 4.304%; width: 94.029%; height: 65.459%;
      background: #000;
    }
    .device-imac-frame {
      position: absolute;
      inset: 0;
      width: 100%; height: 100%;
      display: block;
      pointer-events: none;
    }

    /* iPhone overlapping the lower-right, sitting in front */
    .device-iphone {
      position: absolute;
      right: -1%;
      bottom: 0;
      width: 27%;
      aspect-ratio: 396 / 808;
      z-index: 2;
      filter:
        drop-shadow(0 44px 56px rgba(0,0,0,0.6))
        drop-shadow(0 16px 28px rgba(0,0,0,0.35));
    }
    .device-iphone-screen {
      position: absolute;
      left: 5.051%;
      top: 2.104%;
      width: 90.152%;
      height: 95.668%;
      object-fit: cover;
      object-position: top center;
      display: block;
      border-radius: clamp(12px, 7%, 28px);
      background: #050608;
    }
    .device-iphone-frame {
      position: absolute;
      inset: 0;
      width: 100%; height: 100%;
      display: block;
      pointer-events: none;
    }
    @media (max-width: 560px) {
      .device-iphone { width: 34%; right: 0; }
    }

    /* Availability + CTAs, sitting directly beneath the device composition */
    .premise-cta {
      margin: clamp(30px, 4vw, 52px) auto 0;
      max-width: 1180px;
      display: flex; flex-direction: column; align-items: center; gap: 20px;
      text-align: center;
    }
    .premise-cta-label {
      font-family: var(--font-display); font-weight: 500;
      font-size: clamp(23px, 2.4vw, 31px); line-height: 1.1; letter-spacing: 0.2px; color: var(--t1);
    }
    .premise-cta-row { display: flex; flex-wrap: wrap; justify-content: center; gap: 14px; }
    .premise-cta-meta {
      font: 500 10.5px/1.5 var(--font-mono); letter-spacing: 0.08em; text-transform: uppercase; color: var(--t3);
    }

    /* §2 stage — premise body lines + pin field behind */
    .premise-stage {
      position: relative;
      isolation: isolate;
    }
    .premise-field {
      position: absolute; inset: -20px -8% -20px -8%;
      z-index: -1; pointer-events: none;
    }
    /* The field bleeds 8% past its container on both sides, which puts it wider
       than the viewport once the grid is one column — a real horizontal scroll
       between 641px and 940px. Pre-existing; the reset just stopped too low.
       It is decorative (z-index -1, pointer-events none), so clipping costs
       nothing. */
    @media (max-width: 940px) { .premise-field { inset: -20px 0; } }
    .premise-pin {
      position: absolute;
      width: 8px; height: 8px; border-radius: 50%;
      background: var(--gold);
      box-shadow:
        0 0 0 1px rgba(201,168,76,0.10),
        0 0 24px rgba(240,208,128,0.55),
        0 0 6px rgba(240,208,128,0.75);
      opacity: 0;
      transform: scale(0.4);
      transition: opacity 1100ms var(--ease-out), transform 1100ms var(--ease-out);
    }
    .premise-pin.is-on { opacity: 0.92; transform: scale(1); }
    .premise-pin::after {
      content: ""; position: absolute; inset: -3px;
      border-radius: 50%;
      border: 1px solid rgba(201,168,76,0.35);
      animation: ws-pulse 2.6s var(--ease-in-out) infinite;
    }
    /* Three pin positions — scattered across the field, behind the prose */
    .premise-pin[data-pin-i="1"] { right: 4%; top: 8%; }
    .premise-pin[data-pin-i="2"] { left: 2%;  top: 48%; }
    .premise-pin[data-pin-i="3"] { right: 18%; bottom: 12%; }

    .premise-body { position: relative; z-index: 1; }
    .premise-body .lede .line {
      display: block;
      opacity: 0;
      transform: translateY(8px);
      transition: opacity 720ms var(--ease-out), transform 720ms var(--ease-out);
    }
    .premise-body .lede .line.is-on { opacity: 1; transform: none; }
    .premise-body .lede .line.line--lead { color: var(--t1); }
    .premise-body .lede .line + .line { margin-top: 0.25em; }
    .premise-body .lede:first-child {
      font-size: clamp(19px, 1.55vw, 23px);
      line-height: 1.45;
      color: var(--t1);
    }
    .premise-body .lede:first-child .line--lead { font-weight: 500; }
    .premise-body .lede + .lede { margin-top: 1.1em; color: var(--t1-soft); }
    @media (prefers-reduced-motion: reduce) {
      .premise-pin { opacity: 0.92; transform: scale(1); }
      .premise-body .lede .line { opacity: 1; transform: none; }
    }
    .premise .display { max-width: 16ch; }
    .premise-body { margin-top: var(--sp-lg); max-width: 56ch; }

    /* ──────────── §3 How it works — guided walkthrough (faithful app UI) ──────────── */
    .walk { padding-block: var(--sp-xxl); }
    .walk-head { max-width: 760px; }
    .walk-head .lede { margin-top: var(--sp-md); }
    .walk-stage {
      margin-top: var(--sp-xl);
      display: grid; grid-template-columns: 0.92fr 1fr; gap: var(--sp-xl);
      align-items: stretch;
    }
    @media (max-width: 940px) { .walk-stage { grid-template-columns: 1fr; gap: var(--sp-lg); } }

    /* left — the map with the tapped place, + caption (stretches to the panel height) */
    .walk-aside { display: flex; flex-direction: column; gap: 16px; min-width: 0; }
    .walk-map {
      position: relative; width: 100%; flex: 1 1 auto; min-height: 420px;
      border: 1px solid var(--rule); border-radius: var(--r-lg);
      overflow: hidden; background: #0A0C12;
      box-shadow: 0 30px 70px rgba(0,0,0,0.5), inset 0 1px 0 rgba(255,255,255,0.04);
    }
    @media (max-width: 940px) { .walk-map { flex: none; aspect-ratio: 16 / 11; min-height: 0; } }
    .walk-map-img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: center; display: block; }
    .walk-map::after {
      content: ""; position: absolute; inset: 0; z-index: 7; pointer-events: none;
      background-image:
        linear-gradient(to right, var(--gold) 0 16px, transparent 16px),
        linear-gradient(to bottom, var(--gold) 0 16px, transparent 16px),
        linear-gradient(to left, var(--gold) 0 16px, transparent 16px),
        linear-gradient(to top, var(--gold) 0 16px, transparent 16px);
      background-size: 1px 16px, 16px 1px, 1px 16px, 16px 1px;
      background-position: top left, top left, top right, bottom left;
      background-repeat: no-repeat; opacity: 0.35;
    }
    .walk-cap { max-width: 52ch; }

    /* right — the narration panel, a faithful recreation of the in-app reader */
    .np {
      --np-text: #EDE9E2; --np-text-2: #9A9080; --np-text-3: #6A6459;
      --np-gold: #C9A84C; --np-gold-muted: rgba(201,168,76,0.32);
      position: relative; display: flex; flex-direction: column; min-width: 0;
      height: clamp(560px, 64vh, 648px);
      background: linear-gradient(180deg, rgba(20,25,37,0.97), rgba(11,15,24,0.98));
      border: 1px solid rgba(244,213,141,0.10); border-radius: var(--r-lg);
      box-shadow: 0 34px 90px rgba(0,0,0,0.55), inset 0 1px 0 rgba(255,255,255,0.05);
      overflow: hidden;
    }
    @media (max-width: 940px) { .np { height: clamp(520px, 78vh, 600px); } }
    .np-head { flex: 0 0 auto; display: flex; align-items: center; justify-content: space-between; padding: 15px 18px 0; }
    /* the narration body scrolls inside the drawer; header + composer stay pinned */
    .np-scroll { flex: 1 1 auto; overflow-y: auto; overscroll-behavior: contain; scrollbar-width: thin; scrollbar-color: rgba(201,168,76,0.30) transparent; }
    .np-scroll::-webkit-scrollbar { width: 10px; }
    .np-scroll::-webkit-scrollbar-track { background: transparent; }
    .np-scroll::-webkit-scrollbar-thumb { background: rgba(201,168,76,0.22); border-radius: 8px; border: 3px solid transparent; background-clip: padding-box; }
    .np-scroll:hover::-webkit-scrollbar-thumb { background: rgba(201,168,76,0.42); background-clip: padding-box; }
    .np-eyebrow { font: 600 10px/1 var(--font-mono); letter-spacing: 0.22em; text-transform: uppercase; color: var(--np-gold); opacity: 0.85; }
    .np-close { width: 24px; height: 24px; border: 0; background: transparent; color: var(--np-gold); opacity: 0.65; font-size: 13px; line-height: 1; cursor: default; }
    .np-title { font-family: var(--font-display); font-weight: 500; font-size: clamp(25px, 2.5vw, 33px); line-height: 1.08; letter-spacing: 0.4px; color: var(--np-gold); text-align: center; padding: 6px 26px 0; }
    .np-divider { display: flex; align-items: center; gap: 12px; padding: 12px 26px 0; }
    .np-divider span { flex: 1; height: 1px; background: var(--np-gold-muted); opacity: 0.4; }
    .np-divider b { font-weight: 400; font-size: 12px; color: var(--np-gold); opacity: 0.55; }
    .np-body { padding: 16px 26px 2px; }
    .np-para { margin: 0 0 14px; font-family: var(--font-display); font-weight: 300; font-size: 16.5px; line-height: 1.62; letter-spacing: 0.2px; color: var(--np-text); }
    .np-para--first { font-weight: 400; }
    .np-dropcap { float: left; font-family: var(--font-display); font-weight: 600; font-size: 50px; line-height: 0.8; color: var(--np-gold); padding: 4px 11px 0 0; }
    .np-para--q { font-style: italic; color: var(--np-text-2); margin-bottom: 0; }
    .np-stat { padding: 14px 26px 0; font: 600 9px/1.4 var(--font-mono); letter-spacing: 0.16em; text-transform: uppercase; color: var(--np-text-3); }
    .np-helpful { padding: 12px 26px 0; }
    .np-helpful-label { font: 600 9px/1 var(--font-mono); letter-spacing: 0.18em; text-transform: uppercase; color: var(--np-text-3); }
    .np-helpful-btns { display: flex; gap: 10px; margin-top: 9px; }
    .np-pill {
      display: inline-flex; align-items: center; gap: 7px; padding: 7px 14px; border-radius: var(--r-pill);
      border: 1px solid rgba(201,168,76,0.28); background: rgba(201,168,76,0.04);
      color: var(--np-gold); font: 500 12px/1 var(--font-body); cursor: default;
    }
    .np-pill svg { width: 13px; height: 13px; fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
    .np-sources { margin: 16px 26px 0; border-top: 1px solid rgba(244,213,141,0.09); }
    .np-sources summary { display: flex; align-items: center; gap: 9px; list-style: none; cursor: default; padding: 13px 0; font: 600 11px/1 var(--font-mono); letter-spacing: 0.16em; text-transform: uppercase; color: var(--np-gold); }
    .np-sources summary::-webkit-details-marker { display: none; }
    .np-sources summary b { font-weight: 400; opacity: 0.7; }
    .np-explore { padding: 4px 26px 0; }
    .np-explore-label { display: block; text-align: center; font: 600 9px/1 var(--font-mono); letter-spacing: 0.2em; text-transform: uppercase; color: var(--np-text-3); margin-bottom: 12px; }
    .np-q {
      display: flex; align-items: center; gap: 11px; width: 100%; text-align: left;
      padding: 11px 15px; margin-bottom: 9px; border-radius: var(--r-md);
      border: 1px solid rgba(244,213,141,0.12); background: rgba(255,255,255,0.02);
      color: var(--np-text); font: 400 13px/1.4 var(--font-body); cursor: default;
    }
    .np-q svg { flex: 0 0 auto; width: 14px; height: 14px; color: var(--np-gold); fill: none; stroke: currentColor; stroke-width: 1.6; }
    .np-composer { flex: 0 0 auto; position: relative; display: flex; align-items: center; gap: 11px; padding: 16px 18px 18px; border-top: 1px solid rgba(244,213,141,0.09); }
    .np-composer::before { content: ""; position: absolute; left: 1px; right: 1px; top: -34px; height: 34px; background: linear-gradient(to top, rgba(11,15,24,0.97), transparent); pointer-events: none; }
    .np-input { flex: 1; height: 40px; border-radius: var(--r-pill); border: 1px solid rgba(244,213,141,0.14); background: rgba(255,255,255,0.03); display: flex; align-items: center; padding: 0 16px; font: 400 13px/1 var(--font-body); color: var(--np-text-3); }
    .np-send { flex: 0 0 38px; width: 38px; height: 38px; border-radius: 50%; border: 1px solid rgba(244,213,141,0.22); background: rgba(201,168,76,0.08); color: var(--np-gold); display: flex; align-items: center; justify-content: center; }
    .np-send svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }

    /* armed → revealed staging (JS-gated; no-JS / reduced-motion shows it all) */
    .walk-stage.is-armed .np           { opacity: 0; transform: translateY(22px); }
    .walk-stage.is-revealed .np           { opacity: 1; transform: none; transition: opacity .6s var(--ease-out) .95s, transform .7s var(--ease-out) .95s; }

    /* The plate is now one real screenshot, so the phone no longer needs its own
       substitute image: the hand-rebuilt pin and callout that .walk-shot existed
       to replace are gone. The crop tightens on narrow screens so the card stays
       readable rather than shrinking with the frame. */
    @media (max-width: 940px) {
      .walk-map { aspect-ratio: 16 / 11; }
    }
    @media (max-width: 640px) {
      .walk-map { aspect-ratio: 4 / 3; }
      .walk-map-img { object-position: 22% center; }
    }

    /* ─── The map panel ─── */
    .demo-map {
      position: relative;
      aspect-ratio: 5 / 7;
      max-height: 720px;
      background: #0A0C12;
      border: 1px solid var(--rule);
      border-radius: var(--r-lg);
      overflow: hidden;
      box-shadow: 0 30px 90px rgba(0,0,0,0.5), inset 0 1px 0 rgba(255,255,255,0.04);
      /* Lens-driven tint variables */
      --map-tint:   transparent;
      --map-land:   #11141C;
      --map-stroke: #2A3040;
      --pin-color:  var(--gold);
      --pin-glow:   rgba(240,208,128,0.7);
      transition: --map-tint 600ms, --map-land 600ms, --map-stroke 600ms;
    }
    .demo-map[data-lens="folklore"] {
      --map-tint:   rgba(120,90,180,0.07);
      --map-land:   #14111C;
      --map-stroke: #3A2F4A;
      --pin-color:  #E0C8FF;
      --pin-glow:   rgba(224,200,255,0.55);
    }
    .demo-map[data-lens="dark"] {
      --map-tint:   rgba(160,40,40,0.05);
      --map-land:   #1A1014;
      --map-stroke: #4A2A2E;
      --pin-color:  #FF9B7A;
      --pin-glow:   rgba(255,155,122,0.55);
    }
    .demo-map[data-lens="ancient"] {
      --map-tint:   rgba(74,158,204,0.06);
      --map-land:   #0E1620;
      --map-stroke: #2A4A60;
      --pin-color:  var(--ancient-blue);
      --pin-glow:   rgba(74,158,204,0.65);
    }

    /* Pins */
    .demo-pin {
      position: absolute;
      width: 24px; height: 24px;
      transform: translate(-50%, -50%);
      border-radius: 50%;
      background: transparent;
      border: 0;
      cursor: pointer;
      padding: 0;
      z-index: 3;
      opacity: 1;
      transition: opacity 360ms var(--ease-out), transform 280ms var(--ease-out);
    }
    .demo-pin[hidden] { display: none; }
    .demo-pin:hover::before { opacity: 1; transform: scale(1.3); }
    .demo-pin:hover::after  { transform: translate(-50%, -50%) scale(1.2); }
    .demo-pin.is-active::before { opacity: 1; transform: scale(1.6); }
    .demo-pin.is-active::after  { transform: translate(-50%, -50%) scale(1.4); }
    .demo-pin.is-active {
      animation: demo-pin-pulse 2.4s var(--ease-in-out) infinite;
    }
    @keyframes demo-pin-pulse {
      0%, 100% { opacity: 0.8; transform: scale(1); }
      50%      { opacity: 1; transform: scale(1.12); }
    }   /* opacity and transform compose on the GPU; the brightness filter it used repainted the pin every frame */

    /* "Tap a pin" prompt — only visible until first interaction */
    .demo-prompt {
      position: absolute;
      left: 50%; bottom: 24px;
      transform: translateX(-50%);
      padding: 10px 18px;
      background: rgba(11,13,18,0.85);
      backdrop-filter: blur(8px);
      border: 1px solid var(--rule-strong);
      border-radius: var(--r-pill);
      font: 500 10.5px/1 var(--font-mono); letter-spacing: 0.08em; text-transform: uppercase;
      color: var(--gold);
      z-index: 4;
      pointer-events: none;
      animation: demo-prompt-pulse 2.6s var(--ease-in-out) infinite;
    }
    @keyframes demo-prompt-pulse {
      0%, 100% { opacity: 0.85; }
      50%      { opacity: 0.4; }
    }
    .demo-map.is-touched .demo-prompt { display: none; }

    /* ─── The narration card ─── */
    .demo-card {
      position: relative;
      background: var(--surface);
      border: 1px solid var(--rule);
      border-radius: var(--r-lg);
      padding: 32px 32px 36px;
      display: flex; flex-direction: column;
      min-height: 480px;
      box-shadow: var(--shadow-card);
    }
    /* Cross-fade between site/lens combinations */
    .demo-card[data-fading] .demo-card-eyebrow,
    .demo-card[data-fading] .demo-card-title,
    .demo-card[data-fading] .demo-card-meta,
    .demo-card[data-fading] .demo-card-body { opacity: 0; transition: opacity 220ms; }

    /* Lens-explain block beneath the stage */
    .demo-lens-explain {
      max-width: 720px;
      margin: var(--sp-xl) auto 0;
      display: grid; gap: 22px;
      grid-template-columns: 1fr 1.6fr;
      align-items: start;
    }
    @media (max-width: 760px) { .demo-lens-explain { grid-template-columns: 1fr; } }

    /* ──────────────── §4 Ancient Ground (LiDAR viewer) ────────────────
       The viewer chrome is a 1:1 copy of the web app's AncientGroundModal
       (waystone-web/src/components/ancientground/AncientGroundModal.css): the
       glass frame + masked gold rim, header, light-direction dial, LiDAR↔Photo
       slider, loading radar, and under-viewer tips. App design tokens the
       landing sheet doesn't carry are bridged on .ag-stack (scoped so the app's
       --gold-soft etc. don't leak elsewhere). The one change is environmental:
       this is an inline section, not a full-screen modal, so .ag-stack /
       .ag-frame are sized in flow and the header's ✕ close is dropped. */
    .ag { padding-block: var(--sp-xxl); }
    .ag-head { max-width: 720px; margin-bottom: var(--sp-lg); }

    .ag-stack {
      /* token bridge — exact values from waystone-web/src/styles/tokens.css */
      --gold-soft: #F4D58D;
      --gold-soft-dark: #D4A03C;
      --moonlight: var(--t1);
      --mist: var(--t2);
      --shadow-fg: var(--t3);
      --fs-h3: 28px;
      --fs-lg: 17px;
      --fs-body: 15px;
      --fs-sm: 13px;
      --fs-xs: 11px;
      --sp-sm: 8px;
      --sp-lg: 24px;
      display: flex;
      flex-direction: column;
      gap: 10px;
    }

    /* Frame — liquid-glass viewport with a masked gold rim + lens highlight.
       Verbatim from .ag-frame, minus the modal entrance scale-in; sized as an
       aspect box in flow rather than as a centred modal child. */
    .ag-frame {
      position: relative;
      width: 100%;
      aspect-ratio: 16 / 10;
      max-height: 80vh;
      border-radius: var(--r-xl);
      overflow: hidden;
      background: rgba(10, 15, 26, 0.3);
      backdrop-filter: blur(8px) saturate(1.05);
      -webkit-backdrop-filter: blur(8px) saturate(1.05);
      box-shadow:
        0 24px 70px rgba(0, 0, 0, 0.62),
        0 0 30px rgba(244, 213, 141, 0.13),
        inset 0 1px 0 rgba(255, 255, 255, 0.06);
    }
    .ag-frame::before {
      content: ''; position: absolute; inset: 0; z-index: 3;
      border-radius: inherit; padding: 1.5px;
      background: linear-gradient(180deg, var(--gold-soft), var(--gold-soft-dark));
      -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
      -webkit-mask-composite: xor;
              mask-composite: exclude;
      pointer-events: none;
    }
    .ag-frame::after {
      content: ''; position: absolute; inset: 0; z-index: 3;
      border-radius: inherit;
      background: linear-gradient(150deg, rgba(255, 255, 255, 0.08), transparent 42%);
      pointer-events: none;
    }

    /* Canvas + interaction layers (mirror .ag-canvas / .ag-overlay). */
    .ag-viewport {
      position: absolute; inset: 0; z-index: 0;
      background: transparent;
      opacity: 0;
      transition: opacity 600ms var(--ease-out);
    }
    .ag-viewport.is-loaded { opacity: 1; }
    .ag-frame canvas { display: block; width: 100% !important; height: 100% !important; }
    .ag-overlay {
      position: absolute; inset: 0; z-index: 1;
      cursor: grab; touch-action: none;
    }
    .ag-overlay:active { cursor: grabbing; }

    /* Header — centred title with flanking rules (the modal's ✕ is dropped:
       an inline section has nothing to close). */
    .ag-header {
      position: absolute; z-index: 4; top: 0; left: 0; right: 0;
      display: flex; align-items: center; justify-content: center;
      padding: 10px 14px;
      pointer-events: none;
    }
    .ag-titlewrap {
      display: flex; align-items: center; gap: 14px;
      padding: 5px 18px; border-radius: var(--r-pill);
      background: rgba(10, 15, 26, 0.45);
      backdrop-filter: blur(8px);
      -webkit-backdrop-filter: blur(8px);
    }
    .ag-title-rule {
      width: 26px; height: 1px;
      background: linear-gradient(90deg, transparent, var(--gold-soft));
    }
    .ag-titlewrap .ag-title-rule:last-child {
      background: linear-gradient(90deg, var(--gold-soft), transparent);
    }
    .ag-title {
      font-family: var(--font-display); font-weight: 600;
      font-size: var(--fs-lg); letter-spacing: 2px; text-transform: uppercase;
      color: var(--gold-soft);
      text-shadow: 0 0 14px rgba(244, 213, 141, 0.3);
    }

    /* Loading — aerial-survey radar sweep + cycling message. */
    .ag-loading {
      position: absolute; z-index: 4; inset: 0;
      display: flex; align-items: center; justify-content: center;
      pointer-events: none; padding: 0 24px;
      transition: opacity 500ms var(--ease-out);
    }
    .ag-loadcard { display: flex; flex-direction: column; align-items: center; gap: var(--sp-lg); }
    .ag-radar {
      position: relative; width: 76px; height: 76px;
      border-radius: 50%; border: 1px solid rgba(244, 213, 141, 0.22);
    }
    .ag-radar::after {
      content: ''; position: absolute; left: 50%; top: 50%;
      width: 5px; height: 5px; border-radius: 50%;
      background: var(--gold-soft); box-shadow: 0 0 8px var(--gold-soft);
      transform: translate(-50%, -50%);
    }
    .ag-radar-sweep {
      position: absolute; inset: -1px; border-radius: 50%;
      background: conic-gradient(from 0deg, transparent 0deg, rgba(244, 213, 141, 0.32) 50deg, transparent 90deg);
      animation: ag-sweep 1.8s linear infinite;
    }
    .ag-radar-ring {
      position: absolute; inset: 0; border-radius: 50%;
      border: 1px solid rgba(244, 213, 141, 0.4);
      animation: ag-ping 2.6s ease-out infinite;
    }
    .ag-radar-ring:nth-child(3) { animation-delay: 1.3s; }
    @keyframes ag-sweep { to { transform: rotate(360deg); } }
    @keyframes ag-ping {
      0% { transform: scale(0.35); opacity: 0.9; }
      100% { transform: scale(1); opacity: 0; }
    }
    .ag-loading-text {
      font-family: var(--font-display); font-style: italic;
      font-size: var(--fs-lg); color: var(--gold-soft); letter-spacing: 0.3px;
    }

    /* LiDAR ↔ Photo blend slider — native range, gold rail + thumb. */
    .ag-slider {
      position: absolute; z-index: 4; bottom: 16px; left: 50%;
      transform: translateX(-50%);
      display: flex; align-items: center; gap: 14px;
      padding: 10px 20px; border-radius: var(--r-pill);
      border: 1px solid rgba(244, 213, 141, 0.22);
      background: rgba(10, 15, 26, 0.62);
      box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4), inset 0 1px 0 rgba(255, 255, 255, 0.08);
      backdrop-filter: blur(14px) saturate(130%);
      -webkit-backdrop-filter: blur(14px) saturate(130%);
      opacity: 0; pointer-events: none;
      transition: opacity 600ms var(--ease-out);
    }
    .ag-slider.is-ready { opacity: 1; pointer-events: auto; }
    .ag-slider-label {
      font-family: var(--font-display); font-weight: 600;
      font-size: var(--fs-sm); color: rgba(244, 213, 141, 0.7); letter-spacing: 0.4px;
    }
    .ag-slider input[type="range"] {
      -webkit-appearance: none; appearance: none;
      width: 200px; height: 4px; border-radius: 2px;
      background: linear-gradient(90deg, rgba(244, 213, 141, 0.55), rgba(244, 213, 141, 0.14));
      outline: none; cursor: pointer;
    }
    .ag-slider input[type="range"]::-webkit-slider-thumb {
      -webkit-appearance: none; appearance: none;
      width: 16px; height: 16px; border-radius: 50%;
      background: var(--gold-soft); border: 1.5px solid rgba(255, 255, 255, 0.45);
      box-shadow: 0 1px 5px rgba(0, 0, 0, 0.5); cursor: pointer;
    }
    .ag-slider input[type="range"]::-moz-range-thumb {
      width: 16px; height: 16px; border: 1.5px solid rgba(255, 255, 255, 0.45);
      border-radius: 50%; background: var(--gold-soft);
      box-shadow: 0 1px 5px rgba(0, 0, 0, 0.5); cursor: pointer;
    }
    .ag-slider input[type="range"]::-moz-range-track {
      height: 4px; border-radius: 2px; background: rgba(244, 213, 141, 0.2);
    }

    /* Light-direction dial — drag the glowing sun around a compass disc. */
    .ag-lightdial {
      position: absolute; z-index: 4; left: 18px; bottom: 18px;
      width: 60px; height: 60px; border-radius: 50%;
      border: 1px solid rgba(244, 213, 141, 0.28);
      background: radial-gradient(circle at 50% 38%, rgba(40, 46, 60, 0.5), rgba(10, 15, 26, 0.72));
      box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.08), 0 6px 18px rgba(0, 0, 0, 0.4);
      backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
      cursor: pointer;
      opacity: 0; pointer-events: none;
      transition: opacity 600ms var(--ease-out);
    }
    .ag-lightdial.is-ready { opacity: 1; pointer-events: auto; }
    .ag-lightdial-cross {
      position: absolute; left: 50%; top: 50%;
      width: 3px; height: 3px; border-radius: 50%;
      background: rgba(244, 213, 141, 0.5); transform: translate(-50%, -50%);
    }
    .ag-lightdial-dot {
      position: absolute; width: 12px; height: 12px; border-radius: 50%;
      background: radial-gradient(circle at 40% 35%, #fff, var(--gold-soft) 70%);
      box-shadow: 0 0 9px 2px rgba(244, 213, 141, 0.6);
      transform: translate(-50%, -50%); pointer-events: none;
    }

    /* No-coverage / error states (kept for parity; not reached on the landing). */
    .ag-empty { max-width: 420px; text-align: center; pointer-events: none; }
    .ag-empty-title {
      font-family: var(--font-display); font-weight: 700;
      font-size: var(--fs-h3); color: var(--gold); margin-bottom: var(--sp-sm);
    }
    .ag-empty-body {
      font-family: var(--font-body); font-size: var(--fs-sm); line-height: 1.5;
      color: var(--mist); margin: 0 0 var(--sp-sm);
    }
    .ag-error { font-family: var(--font-body); font-size: var(--fs-body); color: var(--mist); }

    /* Under-viewer bar — control tips on a frosted pill. */
    .ag-underbar {
      display: flex; align-items: center; justify-content: center;
      flex-wrap: wrap; gap: 8px 16px;
      padding: 8px 16px; border-radius: var(--r-lg);
      background: rgba(10, 15, 26, 0.55);
      border: 1px solid rgba(244, 213, 141, 0.12);
      backdrop-filter: blur(12px) saturate(120%);
      -webkit-backdrop-filter: blur(12px) saturate(120%);
    }
    .ag-tips {
      display: flex; flex-wrap: wrap; align-items: center; gap: 4px 14px;
      font-family: var(--font-body); font-size: var(--fs-xs);
      color: rgba(237, 233, 226, 0.42);
    }
    .ag-tips span { white-space: nowrap; }
    .ag-tips b { color: rgba(244, 213, 141, 0.8); font-weight: 600; }

    /* AAME layer chips — per-overlay toggles in the under-bar (verbatim from
       AncientGroundModal.css). Built dynamically from the viewer's overlay groups. */
    .ag-overlay-chips { display: flex; flex-wrap: wrap; gap: 6px; }
    .ag-overlay-chip {
      display: inline-flex; align-items: center; gap: 7px;
      padding: 5px 12px; border-radius: var(--r-pill);
      border: 1px solid rgba(255, 255, 255, 0.08);
      background: rgba(255, 255, 255, 0.03);
      color: var(--mist);
      font-family: var(--font-body); font-weight: 500; font-size: var(--fs-sm);
      cursor: pointer; opacity: 0.5;
      transition: opacity 0.2s var(--ease-out), border-color 0.2s var(--ease-out), color 0.2s var(--ease-out);
    }
    .ag-overlay-chip.is-on { opacity: 1; color: var(--moonlight); border-color: rgba(244, 213, 141, 0.3); }
    .ag-overlay-dot { width: 9px; height: 9px; border-radius: 50%; opacity: 0.55; transition: opacity 0.2s var(--ease-out); }
    .ag-overlay-chip.is-on .ag-overlay-dot { opacity: 1; }
    /* intro: each chip pulses a gold glow once the survey has faded in, to show it's toggleable */
    .ag-overlay-chip.is-introglow { animation: ag-chip-glow 1.1s var(--ease-out); }
    @keyframes ag-chip-glow {
      0%   { box-shadow: 0 0 0 0 rgba(244, 213, 141, 0); }
      38%  { box-shadow: 0 0 18px 2px rgba(244, 213, 141, 0.6); border-color: rgba(244, 213, 141, 0.85); color: var(--moonlight); }
      100% { box-shadow: 0 0 0 0 rgba(244, 213, 141, 0); }
    }

    /* Context-pin label — frosted card anchored above the pin. */
    .ag-pin-label {
      position: absolute; z-index: 4;
      transform: translate(-50%, -100%);
      max-width: 220px; padding: 9px 14px;
      border-radius: var(--r-md);
      background: rgba(10, 15, 26, 0.82);
      border: 1px solid rgba(244, 213, 141, 0.28);
      box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45), inset 0 1px 0 rgba(255, 255, 255, 0.06);
      backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
      text-align: center; pointer-events: none;
      animation: ag-in 0.18s var(--ease-out);
    }
    .ag-pin-label-name { font-family: var(--font-display); font-weight: 600; font-size: var(--fs-lg); line-height: 1.2; color: var(--gold-soft); }
    .ag-pin-label-cat { margin-top: 2px; font-family: var(--font-body); font-size: var(--fs-xs); letter-spacing: 0.12em; text-transform: uppercase; color: var(--mist); }

    /* AAME feature info card — shown when an overlay polygon is clicked. */
    .ag-overlay-card {
      position: absolute; z-index: 5;
      transform: translate(-50%, -100%);
      width: 210px; max-width: 70%;
      padding: 10px 12px 11px;
      border-radius: var(--r-md);
      background: rgba(10, 15, 26, 0.9);
      border: 1px solid rgba(244, 213, 141, 0.22);
      box-shadow: 0 10px 26px rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(255, 255, 255, 0.06);
      backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px);
      pointer-events: auto;
      animation: ag-in 0.18s var(--ease-out);
    }
    .ag-overlay-card-close {
      position: absolute; top: 5px; right: 6px;
      width: 20px; height: 20px; border: none; border-radius: 50%;
      background: transparent; color: var(--mist); font-size: 11px; cursor: pointer;
      transition: background 0.2s var(--ease-out), color 0.2s var(--ease-out);
    }
    .ag-overlay-card-close:hover { background: rgba(255, 255, 255, 0.08); color: var(--moonlight); }
    .ag-overlay-card-eyebrow { font-family: var(--font-body); font-weight: 600; font-size: 9px; letter-spacing: 0.14em; text-transform: uppercase; }
    .ag-overlay-card-type { margin-top: 3px; padding-right: 14px; font-family: var(--font-display); font-weight: 600; font-size: 16px; line-height: 1.2; color: var(--moonlight); }
    .ag-overlay-card-period { margin-top: 1px; font-family: var(--font-display); font-style: italic; font-size: var(--fs-xs); color: var(--mist); }
    .ag-overlay-card-row { margin-top: 6px; font-family: var(--font-body); font-size: 10px; line-height: 1.4; color: rgba(237, 233, 226, 0.7); }
    .ag-overlay-card-row span { display: block; font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase; color: var(--shadow-fg); margin-bottom: 1px; }

    @keyframes ag-in {
      from { opacity: 0; transform: scale(0.96); }
      to { opacity: 1; transform: scale(1); }
    }

    .ag-caption {
      margin-top: 18px;
      display: flex; flex-wrap: wrap; gap: 18px;
      justify-content: space-between;
      font: 500 11px/1.4 var(--font-mono);
      letter-spacing: 0.06em; text-transform: uppercase;
      color: var(--t2);   /* a credit line is read: --t3 sits at 1.7:1 on this ground */
    }
    .ag-caption .right { color: var(--gold); }

    /* ── Phone: the viewer overlays were laid out for a wide desktop frame and
       collide on a narrow one. Give the viewer a taller (near-square) frame, move
       the light dial to the top-left so it clears the bottom blend slider, shrink
       the slider so its pill fits, and drop the mouse-only control tips (they make
       no sense on touch) — leaving just the layer chips under the viewer. ── */
    @media (max-width: 640px) {
      .ag-frame { aspect-ratio: 4 / 3; max-height: none; }
      .ag-header { padding: 7px 10px; }
      .ag-titlewrap { gap: 9px; padding: 4px 13px; max-width: calc(100% - 24px); flex-wrap: wrap; justify-content: center; }
      .ag-title-rule { width: 18px; }
      .ag-title { font-size: 12.5px; letter-spacing: 1.2px; }

      .ag-lightdial { left: 11px; top: 11px; bottom: auto; width: 42px; height: 42px; }

      .ag-slider { bottom: 11px; gap: 9px; padding: 7px 13px; }
      .ag-slider input[type="range"] { width: 108px; }
      .ag-slider-label { font-size: 10.5px; }

      .ag-tips { display: none; }
      .ag-underbar { padding: 7px 11px; gap: 7px; }
      .ag-overlay-chips { justify-content: center; gap: 6px; }
      .ag-overlay-chip { font-size: 12px; padding: 4px 11px; }

      .ag-caption { gap: 5px 14px; font-size: 10px; justify-content: center; text-align: center; }
    }

    /* ──────────────── §6 Where the Words Come From ──────────────── */
    .corpus { padding-block: var(--sp-xxl); position: relative; }
    .corpus-head { max-width: 820px; }
    .corpus-head .lede { margin-top: var(--sp-md); }

    /* ════ §5 — One tap, two pages (app POI callout → two reading surfaces) ════ */
    .tp { margin: var(--sp-lg) 0 var(--sp-md); position: relative; }

    .tp-stage-note {
      max-width: 54ch; margin: 0 auto var(--sp-lg);
      text-align: center;
      font: 400 15px/1.6 var(--font-body);
      color: var(--t2);
    }
    .tp-stage-note b { color: var(--t1); font-weight: 600; }

    .tp-stage { display: flex; flex-direction: column; align-items: center; width: 100%; max-width: 880px; margin: 0 auto; }

    /* ── The app's POI callout, as it ships today (274px; POICallout.css 2026-08-10) ──
       Ported values, with three deliberate edits for this page:
         1. the app's --gold-soft ink is --gold-ink here (the site's --gold-soft is a wash);
         2. --tr-label is 0.22em here and 0.15em in the app, so the meta line takes a literal;
         3. the card's glass is laid over an opaque base — there is no live map behind it on
            this page, so backdrop-filter has nothing to blur and is left off.
       The two inert doors are <span>s, so no hover/active states are ported for them. */
    .tp-callout { position: relative; width: 274px; max-width: 100%; z-index: 3; }
    .tp-co-card {
      position: relative;
      border: 1px solid rgba(255,255,255,0.09);
      border-top-color: rgba(255,255,255,0.16);
      border-radius: var(--r-card);
      background:
        linear-gradient(176deg, rgba(58,64,82,0.16) 0%, rgba(8,11,20,0) 38%),
        linear-gradient(180deg, rgba(10,13,23,0.80) 0%, rgba(5,8,16,0.90) 100%),
        #0B0D12;
      box-shadow:
        0 16px 40px rgba(0,0,0,0.55),
        0 2px 8px rgba(0,0,0,0.35),
        inset 0 1px 0 rgba(255,255,255,0.14),
        inset 0 -1px 0 rgba(0,0,0,0.45);
      overflow: hidden;
    }
    /* The lamp at the head of the card. */
    .tp-co-card::after {
      content: ''; position: absolute; inset: 0; pointer-events: none; border-radius: inherit;
      background: radial-gradient(120% 80% at 26% -14%, rgba(244,213,141,0.13), transparent 62%);
    }
    .tp-co-card > * { position: relative; z-index: 1; }

    /* Close over bookmark, one column at the top right. */
    .tp-co-tools { position: absolute; top: 8px; right: 8px; z-index: 3; display: flex; flex-direction: column; gap: 6px; }
    .tp-co-close {
      width: 28px; height: 28px;
      display: flex; align-items: center; justify-content: center;
      padding: 0; border: 0; border-radius: 50%;
      font-size: 11px; line-height: 1; cursor: default;
      color: var(--gold-dim);
      background: linear-gradient(145deg, rgba(58,65,84,0.72), rgba(15,20,32,0.86));
      box-shadow:
        -2px -2px 5px rgba(255,255,255,0.045),
         3px  3px 8px rgba(0,0,0,0.55),
        inset 0 1px 0 rgba(255,255,255,0.07);
    }
    .tp-co-bookmark {
      width: 28px; height: 28px;
      display: flex; align-items: center; justify-content: center;
      border: 0; border-radius: 10px;
      color: var(--gold-dim);
      background: linear-gradient(145deg, rgba(41,47,63,0.6), rgba(12,17,28,0.76));
      box-shadow:
        -3px -3px 7px rgba(255,255,255,0.03),
         4px  4px 10px rgba(0,0,0,0.5),
        inset 0 1px 0 rgba(255,255,255,0.05);
    }
    .tp-co-bookmark svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }

    /* 46px of right padding clears the tool column. */
    .tp-co-entry { display: flex; align-items: center; gap: 10px; padding: 11px 46px 10px 12px; }
    .tp-co-vignette {
      width: 40px; height: 40px; flex: 0 0 40px;
      object-fit: contain; opacity: 0.8;
      filter:
        brightness(0) saturate(100%) invert(72%) sepia(38%) saturate(518%) hue-rotate(3deg) brightness(93%) contrast(88%)
        drop-shadow(0 0 8px rgba(244,213,141,0.3));
    }
    .tp-co-body { flex: 1; min-width: 0; }
    .tp-co-title { font-family: var(--font-display); font-weight: 500; font-size: 19px; line-height: 1.18; letter-spacing: 0.01em; color: var(--gold-ink); }
    .tp-co-meta { font-family: var(--font-body); font-weight: 600; font-size: 8.5px; letter-spacing: 0.15em; text-transform: uppercase; color: rgba(237,233,226,0.5); margin-top: 4px; }
    .tp-co-dist { font-family: var(--font-body); font-size: 10.5px; font-variant-numeric: tabular-nums; color: rgba(237,233,226,0.58); margin-top: 3px; }
    /* Provenance — which register, what licence, and a link to the official record. */
    .tp-co-origin {
      display: block; margin-top: 3px;
      font-family: var(--font-body); font-size: 10px; letter-spacing: 0.02em;
      color: var(--t2); text-decoration: none;
      transition: color 0.2s var(--ease-out);
    }
    a.tp-co-origin:hover { color: var(--gold-ink); }
    .tp-co-origin-arr { opacity: 0.75; }

    .tp-co-footer { display: flex; flex-direction: column; gap: 6px; padding: 2px 9px 10px; border: 0; }
    .tp-co-action {
      position: relative;
      display: flex; align-items: center; justify-content: center; gap: 8px;
      padding: 9px 12px; border: 0; border-radius: 11px;
      font-family: var(--font-display); font-weight: 500; font-size: 15px; letter-spacing: 0.03em;
      color: rgba(237,233,226,0.8);
      background: linear-gradient(145deg, rgba(41,47,63,0.6), rgba(12,17,28,0.76));
      box-shadow:
        -4px -4px 9px rgba(255,255,255,0.03),
         5px  5px 13px rgba(0,0,0,0.5),
        inset 0 1px 0 rgba(255,255,255,0.05);
    }
    .tp-co-action-arr { font-size: 16px; line-height: 1; }
    /* The doors: coloured glass lit from inside, one rule driven by --tint-*. */
    .tp-co-action--door {
      color: var(--tint-lit);
      background:
        linear-gradient(160deg, rgba(255,255,255,0.09), rgba(255,255,255,0) 42%),
        linear-gradient(180deg, var(--tint-a), var(--tint-b));
      box-shadow:
        inset 0 1px 0 rgba(255,255,255,0.16),
        inset 0 0 22px var(--tint-inner),
        0 4px 14px rgba(0,0,0,0.4);
    }
    .tp-co-action--explore {
      --tint-a: rgba(201,168,76,0.26); --tint-b: rgba(140,112,40,0.20);
      --tint-inner: rgba(244,213,141,0.14); --tint-lit: #f7e6bd;
      cursor: pointer;
    }
    .tp-co-action--library {
      --tint-a: rgba(148,108,74,0.26); --tint-b: rgba(96,68,46,0.22);
      --tint-inner: rgba(214,178,140,0.14); --tint-lit: #ecd9c3;
      cursor: default;
    }
    .tp-co-action--ag {
      --tint-a: rgba(74,138,186,0.26); --tint-b: rgba(40,82,122,0.22);
      --tint-inner: rgba(150,205,240,0.14); --tint-lit: #cbe7f8;
      cursor: default;
    }
    /* Tail — a shard of the same glass, pointing at the pin. */
    .tp-co-tail {
      width: 14px; height: 14px; margin: -8px auto 0;
      transform: rotate(45deg);
      border-radius: 0 0 3px 0;
      background: rgba(6,9,17,0.9);
      border-right: 1px solid rgba(255,255,255,0.09);
      border-bottom: 1px solid rgba(255,255,255,0.09);
    }

    /* ── The demo pointer that taps Explore (sibling of the card, glides in from off-card) ── */
    .tp-cursor, .tp-tap-ring { position: absolute; z-index: 6; pointer-events: none; }
    .tp-cursor {
      /* The footer is now three stacked doors, so the tap point is 134px above the
         foot of the callout (the centre of "AI Research"), not in a single-row bar.
         The pointer's tip sits ~2px below its own top edge, hence 134 − 31 + 2. */
      left: 25%; bottom: 105px;
      width: 25px; height: 31px; opacity: 0;
      transform-origin: top left;
      filter: drop-shadow(0 3px 4px rgba(0,0,0,0.6));
    }
    .tp-cursor svg { display: block; width: 100%; height: 100%; }
    .tp-tap-ring {
      left: 25%; bottom: 134px;   /* centre of the "AI Research" door */
      width: 16px; height: 16px; border-radius: 50%;
      border: 1.5px solid var(--gold-bright);
      transform: translate(-50%, -50%) scale(0.3); opacity: 0;
    }
    /* The whole §5 reveal is driven by a single scroll-scrubbed GSAP timeline in
       landing.js — progress is dictated by scroll amount, not time. GSAP owns the
       callout / cursor / ring / fork / page states (gsap.set + scrubbed .to()s),
       so there are no CSS keyframes here. At rest (no JS) everything sits visible;
       only the demo cursor + tap-ring stay hidden until the timeline reveals them. */

    /* ── The fork — gilt rules splitting toward both pages ── */
    .tp-fork { position: relative; width: 100%; max-width: 880px; height: 52px; }
    .tp-fork i {
      position: absolute; display: block; background: var(--rule-strong);
    }
    /* Centred via margins (not translate) so GSAP owns transform for the draw-on:
       stem scaleY 0->1, bar scaleX 0->1 (splits from the centre), drops scaleY 0->1. */
    .tp-fork .tp-stem  { left: 50%; top: 0;    width: 1px; height: 24px; margin-left: -0.5px; transform-origin: 50% 0; }
    .tp-fork .tp-bar   { left: 25%; top: 24px; width: 50%; height: 1px; transform-origin: 50% 50%; }
    .tp-fork .tp-dropL { left: 25%; top: 24px; width: 1px; height: 28px; margin-left: -0.5px; transform-origin: 50% 0; }
    .tp-fork .tp-dropR { left: 75%; top: 24px; width: 1px; height: 28px; margin-left: -0.5px; transform-origin: 50% 0; }
    .tp-fork .tp-node {
      width: 6px; height: 6px; border-radius: 50%;
      background: var(--gold); margin: -3px 0 0 -3px; transform-origin: 50% 50%;
    }
    .tp-fork .tp-node--top { left: 50%; top: 24px; }
    .tp-fork .tp-node--l   { left: 25%; top: 52px; }
    .tp-fork .tp-node--r   { left: 75%; top: 52px; }
    @media (max-width: 880px) {
      .tp-fork { height: 30px; }
      .tp-fork .tp-bar, .tp-fork .tp-dropL, .tp-fork .tp-dropR,
      .tp-fork .tp-node--l, .tp-fork .tp-node--r { display: none; }
      .tp-fork .tp-stem { height: 28px; }
      .tp-fork .tp-node--top { top: 28px; }
    }

    /* ── The two reading surfaces (verbatim NarrationPanel — dark glass, warm-grey prose) ── */
    .tp-pages {
      display: grid; grid-template-columns: 1fr 1fr; gap: 22px;
      width: 100%; align-items: stretch;
    }
    @media (max-width: 880px) { .tp-pages { grid-template-columns: 1fr; align-items: start; } }

    /* Equal-height columns: each surface fills its column so the two match with no
       wasted gutter. The chat's transcript absorbs the slack and drops its composer
       to the foot — exactly how a real chat client leaves room above the input. */
    .tp-col { display: flex; flex-direction: column; }
    .tp-col > .tp-reader, .tp-col > .tp-chat { flex: 1 1 auto; }
    @media (prefers-reduced-motion: reduce) {
      .tp-cursor, .tp-tap-ring { display: none; }
    }

    .tp-reader {
      --np-text: #EDE9E2; --np-text-2: #9A9080; --np-text-3: #6A6459;
      --np-gold: #C9A84C; --np-gold-muted: rgba(201,168,76,0.35); --np-flag: #E0795A;
      position: relative; display: flex; flex-direction: column;
      background: linear-gradient(180deg, rgba(20,25,37,0.96), rgba(12,16,26,0.96));
      border: 1px solid rgba(244,213,141,0.08);
      border-radius: var(--r-lg);
      box-shadow: 0 22px 60px rgba(0,0,0,0.5);
      overflow: hidden;
    }

    .tp-rd-header {
      display: flex; align-items: center; justify-content: space-between; gap: 10px;
      padding: 12px 18px; border-bottom: 1px solid rgba(201,168,76,0.08);
    }
    .tp-rd-eyebrow { font-family: var(--font-body); font-size: 10px; letter-spacing: var(--tr-label); text-transform: uppercase; color: var(--np-gold); }
    .tp-rd-chip {
      display: inline-flex; align-items: center; gap: 5px;
      font-family: var(--font-body); font-weight: 600; font-size: 9px;
      letter-spacing: 0.1em; text-transform: uppercase;
      padding: 4px 9px; border-radius: 20px; white-space: nowrap;
    }
    .tp-rd-chip i { width: 5px; height: 5px; border-radius: 50%; background: currentColor; }
    .tp-rd-chip--warn { color: #E0795A; background: rgba(224,121,90,0.12); border: 1px solid rgba(224,121,90,0.3); }
    .tp-rd-chip--ok   { color: var(--np-gold); background: rgba(201,168,76,0.1); border: 1px solid rgba(201,168,76,0.32); }

    .tp-rd-body { padding: 18px 22px 20px; }

    .tp-rd-chapter { display: flex; flex-direction: column; align-items: center; text-align: center; }
    .tp-rd-illus {
      width: 74%; height: 84px; margin-bottom: 12px;
      background-color: var(--np-gold); opacity: 0.9;
      -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
      -webkit-mask-position: center; mask-position: center;
      -webkit-mask-size: contain; mask-size: contain;
    }
    .tp-rd-name { font-family: var(--font-display); font-weight: 400; font-size: 25px; line-height: 1.14; letter-spacing: 0.5px; color: var(--np-gold); }
    .tp-rd-rule { width: 40px; height: 1px; background: var(--np-gold-muted); margin: 11px 0; }
    .tp-rd-cat { font-family: var(--font-body); font-weight: 700; font-size: 10px; letter-spacing: var(--tr-label); text-transform: uppercase; color: var(--np-text-3); }

    .tp-rd-divider { display: flex; align-items: center; align-self: stretch; gap: 12px; margin: 18px 0 16px; }
    .tp-rd-divider span { flex: 1; height: 1px; background: var(--np-gold-muted); opacity: 0.25; }
    .tp-rd-divider b { font-weight: 400; font-size: 12px; color: var(--np-gold); opacity: 0.4; }

    .tp-rd-para { margin: 0 0 15px; font-family: var(--font-display); font-weight: 300; font-size: 16.5px; line-height: 1.62; letter-spacing: 0.2px; color: var(--np-text); }
    .tp-rd-para:last-child { margin-bottom: 0; }
    .tp-rd-para--first { font-weight: 400; }
    .tp-rd-dropcap { float: left; font-family: var(--font-display); font-weight: 600; font-size: 50px; line-height: 0.82; color: var(--np-gold); padding: 4px 10px 0 0; }

    .tp-flag { color: var(--np-flag); text-decoration: underline; text-decoration-style: dotted; text-decoration-color: rgba(224,121,90,0.55); text-underline-offset: 3px; }
    .tp-cite { font-family: var(--font-body); font-weight: 600; font-size: 9px; color: var(--np-gold); vertical-align: super; margin-left: 1px; }

    .tp-rd-pull { display: flex; flex-direction: column; align-items: center; padding: 0 8px; margin: 2px 0; }
    .tp-rd-pull span { width: 60%; height: 1px; background: var(--np-gold-muted); margin: 15px 0; }
    .tp-rd-pull p { margin: 0; font-family: var(--font-display); font-style: italic; font-weight: 300; font-size: 18px; line-height: 1.55; text-align: center; color: var(--np-text); }

    .tp-sources { margin-top: 18px; border-top: 1px solid rgba(244,213,141,0.08); }
    .tp-sources-summary {
      display: flex; align-items: center; gap: 6px; list-style: none; cursor: pointer;
      padding: 13px 0 9px;
      font-family: var(--font-body); font-weight: 500; font-size: 13px; letter-spacing: 0.04em; text-transform: uppercase; color: var(--np-gold);
    }
    .tp-sources-summary::-webkit-details-marker { display: none; }
    .tp-sources-chev { font-size: 10px; transition: transform 0.2s var(--ease-out); }
    .tp-sources[open] .tp-sources-chev { transform: rotate(90deg); }
    .tp-sources-list { display: flex; flex-direction: column; gap: 12px; padding-bottom: 2px; }
    .tp-source-row { background: #11141C; border: 1px solid rgba(244,213,141,0.08); border-radius: var(--r-md); padding: 13px 14px; }
    .tp-source-title { font-family: var(--font-display); font-weight: 600; font-size: 15.5px; line-height: 1.2; color: var(--np-gold); }
    .tp-source-sub { font-family: var(--font-body); font-size: 11px; line-height: 1.45; color: var(--np-text-2); margin-top: 3px; }
    .tp-source-passage { font-family: var(--font-body); font-size: 12.5px; line-height: 1.5; color: var(--np-text-2); margin-top: 8px; }
    /* A row with no transcribed passage: set apart from the verbatim quotations
       above it so the slot is never read as a quotation. */
    .tp-source-passage--none { font-style: italic; color: var(--np-text-3); }
    .tp-source-link { display: block; margin-top: 8px; font-family: var(--font-body); font-size: 11px; color: var(--np-gold); word-break: break-all; }

    .tp-caption { margin-top: 14px; text-align: center; font-family: var(--font-body); font-size: 12.5px; line-height: 1.5; }
    .tp-caption--ai { color: #9A9080; }
    .tp-caption--ws { color: var(--gold); }

    /* ── The generic AI chatbox (left) — neutral, un-branded, cool grey, sans-serif.
       Deliberately NOT Waystone: no gold, no Cormorant, no editorial chrome. ── */
    .tp-chat {
      --np-flag: #E0795A;             /* so the shared .tp-flag annotation resolves here too */
      position: relative; display: flex; flex-direction: column;
      background: #16171B;
      border: 1px solid rgba(255,255,255,0.08);
      border-radius: var(--r-lg);
      box-shadow: 0 22px 60px rgba(0,0,0,0.5);
      overflow: hidden;
    }
    .tp-chat-head {
      display: flex; align-items: center; gap: 9px;
      padding: 12px 16px; border-bottom: 1px solid rgba(255,255,255,0.07);
    }
    .tp-chat-ava {
      flex: 0 0 26px; width: 26px; height: 26px; border-radius: 7px;
      display: flex; align-items: center; justify-content: center;
      background: rgba(125,135,165,0.16); color: #9aa6c4;
    }
    .tp-chat-ava svg { width: 15px; height: 15px; display: block; }
    .tp-chat-name { font-family: var(--font-body); font-weight: 600; font-size: 13px; color: #C9CBD2; letter-spacing: 0.01em; }
    .tp-chat-tag {
      margin-left: auto;
      display: inline-flex; align-items: center; gap: 5px;
      font-family: var(--font-body); font-weight: 600; font-size: 9px;
      letter-spacing: 0.1em; text-transform: uppercase;
      padding: 4px 9px; border-radius: 20px; white-space: nowrap;
      color: #E0795A; background: rgba(224,121,90,0.12); border: 1px solid rgba(224,121,90,0.3);
    }
    .tp-chat-tag i { width: 5px; height: 5px; border-radius: 50%; background: currentColor; }

    .tp-chat-body { flex: 1 1 auto; display: flex; flex-direction: column; gap: 14px; padding: 16px; }
    .tp-chat-row { display: flex; gap: 9px; }
    .tp-chat-row--user { justify-content: flex-end; }
    .tp-chat-bubble {
      max-width: 84%; margin: 0; padding: 9px 13px;
      background: #2A2C33; border-radius: 14px 14px 4px 14px;
      font-family: var(--font-body); font-size: 13px; line-height: 1.5; color: #E6E7EB;
    }
    .tp-chat-row--ai { align-items: flex-start; }
    .tp-chat-ava--sm { flex: 0 0 24px; width: 24px; height: 24px; border-radius: 6px; margin-top: 1px; }
    .tp-chat-ava--sm svg { width: 14px; height: 14px; }
    .tp-chat-msg {
      margin: 0; padding-top: 1px;
      font-family: var(--font-body); font-weight: 400; font-size: 13.5px; line-height: 1.6; color: #D6D7DC;
    }
    .tp-chat-foot {
      display: flex; align-items: center; gap: 9px;
      padding: 11px 16px 0; border-top: 1px solid rgba(255,255,255,0.07);
    }
    .tp-chat-input {
      flex: 1; height: 34px; border-radius: 9px;
      background: rgba(255,255,255,0.04); border: 1px solid rgba(255,255,255,0.08);
      display: flex; align-items: center; padding: 0 12px;
      font-family: var(--font-body); font-size: 12px; color: #6E7079;
    }
    .tp-chat-send {
      flex: 0 0 30px; width: 30px; height: 30px; border-radius: 8px;
      background: rgba(125,135,165,0.18); color: #9aa6c4;
      display: flex; align-items: center; justify-content: center;
    }
    .tp-chat-send svg { width: 14px; height: 14px; display: block; }
    .tp-chat-disc {
      margin: 0; padding: 9px 16px 13px;
      text-align: center; font-family: var(--font-body); font-size: 10.5px; color: #6E7079;
    }

    /* The library panel */
    .library {
      position: relative;
      margin: var(--sp-xxl) 0 var(--sp-xl);
    }
    /* Collage: the book leads large on the left; the text floats over its inner
       edge as a frosted card — like a note laid on an open book. */
    .library-figure { display: flex; flex-direction: column; gap: 14px; width: 73%; max-width: 1000px; }
    /* A real digitised county-history scan, mounted like an archival print:
       dark mat (padding) + gold hairline, the page lifted on a soft shadow, and
       a warm vignette so the bright scan seats into the dark page. */
    .library-image {
      position: relative;
      aspect-ratio: 1345 / 999;          /* the two-page spread */
      background:
        linear-gradient(135deg, rgba(255,255,255,0.03), transparent 45%),
        var(--surface);
      border: 1px solid var(--rule-strong);
      border-radius: var(--r-lg);
      box-shadow: var(--shadow-card), inset 0 1px 0 rgba(255,255,255,0.05);
      overflow: hidden;
      padding: 14px;
    }
    .library-scan {
      display: block; width: 100%; height: 100%;
      object-fit: cover; border-radius: 4px;
      box-shadow: 0 10px 28px rgba(0,0,0,0.5);
      filter: saturate(0.9) contrast(1.03) brightness(0.97);
    }
    .library-image::after {
      content: ""; position: absolute; inset: 14px; border-radius: 4px; pointer-events: none;
      background: radial-gradient(125% 115% at 50% 42%, transparent 52%, rgba(12,9,4,0.30));
      box-shadow: inset 0 0 50px rgba(18,12,2,0.4);
    }
    .library-tag {
      position: absolute; left: 22px; bottom: 22px; z-index: 2;
      display: inline-flex; align-items: center; gap: 6px;
      padding: 5px 11px; border-radius: var(--r-pill);
      background: rgba(10,12,18,0.62); border: 1px solid rgba(201,168,76,0.3);
      backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
      font: 600 9px/1 var(--font-mono); letter-spacing: 0.16em; text-transform: uppercase; color: var(--gold);
    }
    .library-tag i { width: 5px; height: 5px; border-radius: 50%; background: var(--gold); box-shadow: 0 0 6px var(--gold); }
    .library-caption {
      font: 500 10.5px/1.4 var(--font-mono);
      letter-spacing: 0.06em; text-transform: uppercase;
      color: var(--gold-dim);
    }
    .library-text {
      position: absolute; z-index: 2; top: 50%; right: 0; transform: translateY(-50%);
      width: 42%; max-width: 468px;
      display: flex; flex-direction: column; gap: 15px;
      padding: 34px 36px;
      background: rgba(11,13,18,0.85);
      border: 1px solid var(--rule-strong);
      border-radius: var(--r-lg);
      box-shadow: 0 30px 80px rgba(0,0,0,0.55), inset 0 1px 0 rgba(255,255,255,0.05);
      backdrop-filter: blur(18px) saturate(120%); -webkit-backdrop-filter: blur(18px) saturate(120%);
    }
    .library-text h3 {
      font-family: var(--font-display); font-weight: 400;
      font-size: clamp(24px, 2.5vw, 31px); line-height: 1.1; color: var(--t1);
    }
    .library-text h3::before {
      content: ""; display: block; width: 40px; height: 2px; margin-bottom: 16px;
      background: linear-gradient(90deg, var(--gold), transparent);
    }
    .library-prose p { font: 400 14.5px/1.62 var(--font-body); color: var(--t1-soft); }
    .library-prose p + p { margin-top: 12px; }
    /* below ~1080px the overlap can't breathe → fall back to a clean stack */
    @media (max-width: 1080px) {
      .library { display: flex; flex-direction: column; gap: var(--sp-lg); }
      .library-figure { width: 100%; max-width: 1040px; margin-inline: auto; }
      .library-text {
        position: static; transform: none; width: 100%; max-width: 1040px; margin-inline: auto;
        background: transparent; border: 0; box-shadow: none; padding: 0;
        backdrop-filter: none; -webkit-backdrop-filter: none;
        display: grid; grid-template-columns: 1fr 1.5fr; gap: 16px var(--sp-xl); align-items: start;
      }
      .library-text h3::before { display: none; }
    }
    @media (max-width: 760px) { .library-text { grid-template-columns: 1fr; gap: 12px; } }

    /* counters — unchanged but reformatted for top border above */
    .counters {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
      gap: 0;
      margin: var(--sp-xl) 0 var(--sp-lg);
      border-top: 1px solid var(--border);
      border-left: 1px solid var(--border);
    }
    .counter {
      padding: 28px 24px;
      border-right: 1px solid var(--border);
      border-bottom: 1px solid var(--border);
      display: flex; flex-direction: column; gap: 8px;
    }
    .counter-num {
      font-family: var(--font-display);
      font-weight: 400;
      font-size: clamp(34px, 4vw, 52px);
      line-height: 1;
      color: var(--t1);
      letter-spacing: -0.01em;
    }
    .counter-label {
      font: 500 10.5px/1.4 var(--font-body);
      letter-spacing: var(--tr-label); text-transform: uppercase;
      color: var(--t2);
    }

    .marquee {
      margin-top: var(--sp-xl);
      padding: 20px 0;
      border-top: 1px solid var(--border);
      border-bottom: 1px solid var(--border);
      overflow: hidden;
      mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
    }
    .marquee-track {
      display: flex; gap: 56px;
      animation: marquee-drift 60s linear infinite;
      width: max-content;
    }
    .marquee:hover .marquee-track { animation-play-state: paused; }
    .marquee span {
      font: 500 11px/1 var(--font-mono);
      letter-spacing: 0.12em; text-transform: uppercase;
      color: var(--t2);
      display: inline-flex; align-items: center; gap: 56px;
      white-space: nowrap;
    }
    .marquee span::after { content: "·"; color: var(--gold-dim); }
    @keyframes marquee-drift { to { transform: translateX(-50%); } }
    /* off screen, the endless animations stop (a class the page's script sets) */
    .hero.is-off *, .marquee.is-off .marquee-track { animation-play-state: paused !important; }


    /* ──────────────── Professional tools (§7) ──────────────── */
    .protools { padding-block: var(--sp-xxl); }
    .protools-head { max-width: 780px; }

    /* Two exhibits, side by side, each a real screenshot with its caption under
       it. Not the three icon cards this section used to be: those described the
       viewer and the lens wipe, which now own whole sections above, and a card
       with a roman numeral and an icon is decoration standing in for evidence. */
    .exhibits {
      margin-top: var(--sp-lg);
      display: grid;
      grid-template-columns: repeat(2, minmax(0, 1fr));
      gap: 30px;
    }
    .exhibit { margin: 0; }
    .exhibit img {
      display: block; width: 100%;
      /* Both exhibits forced to one ratio so their captions start on the same
         line. 8:5 is the cross-section chart's native ratio, so that one is not
         cropped at all; the probe loses a little top and bottom, which its card
         sits clear of. */
      aspect-ratio: 8 / 5; height: auto; object-fit: cover;
      border-radius: var(--r-lg);
      border: 1px solid var(--rule);
      background: #0a0c12;
      box-shadow: 0 22px 60px rgba(0,0,0,0.42);
    }
    .exhibit figcaption { margin-top: 17px; }
    .exhibit-title {
      margin: 0 0 7px;
      font-family: var(--font-display);
      font-size: 23px; line-height: 1.2; font-weight: 400;
      color: var(--t1);
    }
    .exhibit figcaption p {
      margin: 0;
      font: 400 14.5px/1.65 var(--font-body);
      color: var(--t1-soft);
    }

    /* the three that need a sentence, not a screenshot */
    .alsos {
      margin-top: var(--sp-lg);
      padding-top: var(--sp-md);
      border-top: 1px solid var(--rule);
      display: grid;
      grid-template-columns: repeat(3, minmax(0, 1fr));
      gap: 28px;
    }
    .also h3, .also h4 {
      margin: 0 0 6px;
      font: 500 13px/1.3 var(--font-mono);
      letter-spacing: 0.08em; text-transform: uppercase;
      color: var(--gold-dim);
    }
    .also p {
      margin: 0;
      font: 400 13.5px/1.65 var(--font-body);
      color: var(--t2);   /* body copy and the OpenStreetMap credit: 4.9:1, not 1.7:1 */
    }

    @media (max-width: 900px) {
      .exhibits { grid-template-columns: 1fr; gap: 26px; }
      .alsos { grid-template-columns: 1fr; gap: 20px; }
    }

    /* ──────────────── Person — maker's note (no portrait) ──────────────── */
    .person-note { max-width: 680px; margin-top: var(--sp-lg); }
    .person-note .lede + .lede { margin-top: 1em; }
    .person-sign {
      font-family: var(--font-display); font-style: italic; font-weight: 400;
      font-size: 21px; line-height: 1.3; color: var(--gold);
      margin-top: 30px;
    }
    .person-note .linkish { margin-top: 18px; }

    /* ──────────────── §9 Invitation ──────────────── */
    .invite {
      padding-top: var(--sp-xxl);
      padding-bottom: calc(var(--sp-xxl) + 40px);
      text-align: center;
      position: relative;
      overflow: hidden;
    }
    .invite::before {
      content: ""; position: absolute; inset: 0;
      background:
        radial-gradient(60% 50% at 50% 30%, rgba(201,168,76,0.05), transparent 70%);
      pointer-events: none;
      z-index: -1;
    }
    .invite .display { margin: 0 auto; max-width: 18ch; }
    .invite-sub {
      font-family: var(--font-display);
      font-style: italic; font-weight: 300;
      font-size: clamp(18px, 2vw, 24px);
      color: var(--t2);
      margin: 22px auto 38px;
      max-width: 42ch;
    }

    /* ════════════════════════════════════════════════════════════════════════
       THE TAKE — the sections rebuilt on the take-fable branch. Each is a
       different shape on purpose: a pinned stage, a plate beside a console, a
       drawn graphic, two footprints and a road. Two adjacent sections built to
       the same template read as one long template.
       ════════════════════════════════════════════════════════════════════ */

    /* ──────────────── §3 The field — pinned, accumulating ──────────────── */
    .field { padding-block: var(--sp-xxl) 0; }
    .field-head { max-width: 820px; margin-bottom: var(--sp-lg); }
    .field .display { max-width: 14ch; }

    .field-stage {
      position: relative;
      height: 100vh; height: 100svh;
      overflow: hidden;
      background: #050608;
      isolation: isolate;
    }
    .field-frame { position: absolute; inset: 0; z-index: 0; }
    .field-img {
      position: absolute; inset: 0;
      width: 100%; height: 100%; object-fit: cover; object-position: 46% 52%;
      /* the frames are shot at 1700x1000 with the town centred; on a 16:10 stage
         that leaves it small. Scale up about the town rather than re-shooting. */
      transform: scale(1.16); transform-origin: 46% 58%;
      opacity: 0;
      transition: opacity 0.9s var(--ease-out);
    }
    .field-img.is-on { opacity: 1; }
    /* THE CALLOUTS: the app's pin-anchored card at label scale, to the design
       system's rules — glass over the map (rgba(10,15,26,.82) + 1px gold),
       radius 10 for callouts, the card shadow, a 1px top highlight, the
       category icon as a gold vignette, a display title, metadata in the
       console's capitals joined by middle dots. Never text straight on the map. */
    /* the cards' layer over the map: each anchor is moved to its ground's screen position by the page's script
       (translate), and its card hangs above that point */
    .field-callouts { position: absolute; inset: 0; overflow: hidden; pointer-events: none; z-index: 3; }
    .field-callout-anchor { position: absolute; left: 0; top: 0; width: 0; height: 0; pointer-events: none; will-change: transform; }
    .field-callout {
      position: absolute; left: 0; bottom: 10px; width: max-content; transform-origin: 50% 100%;   /* the anchor has no width: the card sizes to its text, up to its max */
      pointer-events: none; opacity: 0; transform: translate(-50%, 6px) scale(0.97);
      transition: opacity 260ms cubic-bezier(0.16, 1, 0.3, 1), transform 260ms cubic-bezier(0.16, 1, 0.3, 1);
      display: flex; align-items: center; gap: 9px;   /* no `position` here: the rule above already sets absolute, and a second declaration would beat it and drop the card below its ground */
      padding: 8px 12px 8px 9px; max-width: 232px;
      background: rgba(10, 15, 26, 0.82);
      -webkit-backdrop-filter: blur(14px) saturate(140%); backdrop-filter: blur(14px) saturate(140%);
      border: 1px solid rgba(201, 168, 76, 0.28);
      border-radius: 10px;
      box-shadow: 0 2px 8px rgba(0, 0, 0, 0.30), inset 0 1px 0 rgba(255, 255, 255, 0.05);
      color: #ede9e2; text-align: left;
    }
    /* the tail, to the ground the card names */
    .field-callout::after {
      content: ''; position: absolute; left: 50%; bottom: -6px; width: 10px; height: 10px; margin-left: -5px;
      transform: rotate(45deg); border-radius: 0 0 2px 0;
      background: rgba(10, 15, 26, 0.9);
      border-right: 1px solid rgba(201, 168, 76, 0.28); border-bottom: 1px solid rgba(201, 168, 76, 0.28);
    }
    /* no room above the ground point (near the top of the frame, or under the
       sticky nav): the card hangs below the point instead, and the tail turns
       over to keep pointing at the ground it names */
    .field-callout.is-below { top: 10px; bottom: auto; transform-origin: 50% 0; }
    .field-callout.is-below::after {
      top: -6px; bottom: auto; border-radius: 2px 0 0 0;
      border-right: 0; border-bottom: 0;
      border-left: 1px solid rgba(201, 168, 76, 0.28); border-top: 1px solid rgba(201, 168, 76, 0.28);
    }
    .field-callout-icon {
      flex: 0 0 26px; width: 26px; height: 26px; object-fit: contain; opacity: 0.85;
      filter: brightness(0) saturate(100%) invert(72%) sepia(38%) saturate(518%) hue-rotate(3deg) brightness(93%) contrast(88%) drop-shadow(0 0 6px rgba(244, 213, 141, 0.3));
    }
    .field-callout-body { min-width: 0; }
    .field-callout-title { display: block; font: 500 15.5px/1.15 var(--font-display); letter-spacing: 0.01em; color: #f4d58d; }   /* the app's callout gold; the page's --gold-soft is a translucent fill */
    .field-callout-meta { display: block; margin-top: 3px; font: 600 8.5px/1.35 var(--font-body); letter-spacing: 0.15em; text-transform: uppercase; color: rgba(237, 233, 226, 0.55); }
    .field-callout.is-on { opacity: 1; transform: translate(-50%, 0) scale(1); }
    .field-callout.is-clipped { opacity: 0 !important; }   /* its ground is at the edge of the frame */
    .field-map { position: absolute; inset: 0; background: #0b0d12; }   /* the map draws as its tiles arrive; no still stands in for it */
    .field-map .maplibregl-canvas { outline: none; }
    .field-stage.is-live .field-video, .field-video[hidden] { display: none; }
    .field-video {
      position: absolute; inset: 0;
      width: 100%; height: 100%; object-fit: cover; object-position: 50% 52%;
      display: block; background: #050608;
    }
    /* the copy needs a floor to sit on: a right-hand shade, and a foot */
    .field-vignette {
      position: absolute; inset: 0; pointer-events: none;
      /* only as much shade as the copy needs to stand on: the photograph must
         stay luminous, or the reveal of the wall out of the grass has no grass */
      background:
        linear-gradient(90deg, transparent 54%, rgba(5,6,8,0.40) 76%, rgba(5,6,8,0.78) 100%),
        linear-gradient(180deg, rgba(5,6,8,0.20) 0, transparent 12%, transparent 82%, rgba(5,6,8,0.70) 100%);
    }

    .field-beats {
      position: absolute; z-index: 2;
      right: clamp(20px, 5vw, 80px); top: 50%; transform: translateY(-50%);
      width: min(400px, 40vw);
      display: grid;
    }
    .field-beat {
      grid-area: 1 / 1;           /* all beats stacked; one shows */
      opacity: 0;
      transform: translateY(14px);
      transition: opacity 0.55s var(--ease-out), transform 0.55s var(--ease-out);
      pointer-events: none;
          padding: 22px 26px 24px; border-radius: 6px;
      background: rgba(8, 10, 14, 0.42);
      -webkit-backdrop-filter: blur(16px) saturate(0.9); backdrop-filter: blur(16px) saturate(0.9);
      box-shadow: 0 30px 80px rgba(0,0,0,0.35);
    }
    .field-beat.is-on { opacity: 1; transform: none; }
    .field-beat-tag {
      display: block; margin-bottom: 12px;
      font: 500 10.5px/1 var(--font-mono); letter-spacing: 0.16em; text-transform: uppercase;
      color: var(--gold);
    }
    .field-beat p {
      margin: 0;
      font-family: var(--font-display); font-weight: 400;
      font-size: clamp(22px, 2.1vw, 30px); line-height: 1.32;
      color: var(--t1);
      text-wrap: pretty;
    }
    .field-beat p b { font-weight: 500; color: var(--gold-ink); }
    .field-beat--last p { font-size: clamp(24px, 2.3vw, 33px); }

    .field-count {
      position: absolute; z-index: 2; left: clamp(20px, 4vw, 56px); top: 26px;
      display: flex; align-items: center; gap: 6px;
    }
    .field-count i {
      width: 12px; height: 12px; border-radius: 2px;
      background: rgba(255,255,255,0.08);
      box-shadow: inset 0 0 0 1px rgba(244,213,141,0.22);
      transition: background 0.4s var(--ease-out), box-shadow 0.4s var(--ease-out);
    }
    .field-count i.is-on {
      background: var(--gold-ink);
      box-shadow: 0 0 8px rgba(244,213,141,0.55), inset 0 0 0 1px rgba(244,213,141,0.9);
    }
    .field-count-label {
      margin-left: 8px;
      font: 500 10px/1 var(--font-mono); letter-spacing: 0.14em; text-transform: uppercase;
      color: rgba(237,233,226,0.55);
    }

    .field-keys {
      position: absolute; z-index: 3;
      left: clamp(20px, 4vw, 56px); bottom: 54px;
      display: flex; gap: 7px; flex-wrap: wrap;
    }
    /* below ~1600px the credit line wraps to two lines and .field-caption grows
       to about 50px, so a fixed 54px put the chips on top of the stage note */
    @media (max-width: 1600px) { .field-keys { bottom: 92px; } }
    .field-key {
      position: relative;
      padding: 11px 18px 15px;
      font: 500 12.5px/1 var(--font-body);
      cursor: pointer;
    }

    .field-caption {
      position: absolute; z-index: 2; left: clamp(20px, 4vw, 56px); right: clamp(20px, 4vw, 56px); bottom: 16px;
      display: flex; flex-wrap: wrap; gap: 8px 18px; justify-content: space-between;
      font: 500 10px/1.4 var(--font-mono); letter-spacing: 0.06em; text-transform: uppercase;
      color: rgba(237,233,226,0.82);
      text-shadow: 0 1px 2px rgba(0,0,0,0.95), 0 0 12px rgba(0,0,0,0.85);   /* the line sits over the satellite frame */
    }
    .field-caption .right { color: var(--gold); }

    @media (max-width: 900px) {
      /* THE STAGE IS ONE SCREEN, AND IT NOW FITS ONE.
         MEASURED at 390x844 before this: .field-frame 422 (50svh) + .field-beats
         373 + .field-keys 97 + .field-caption 78 = 970 px inside an 844 px
         viewport, at every scroll offset through the pin — so the six layer keys
         and the whole data credit sat below the fold for the entire hold, and on
         a phone the keys are the section's only instrument. The budget the old
         comment wrote (150 px of beat) was never met either: the tallest beat
         measured 359 px. So the stage is capped at the viewport, the frame drops
         to 42svh, the beat becomes the flexible row and is clipped rather than
         pushing the keys off, and the credit is moved out of the stage
         altogether by landing.js (see .field-caption--flow below). Budget now:
         354 frame + flexible beat + 44 keys, inside 844. */
      .field-stage { height: 100svh; min-height: 0; overflow: hidden; display: flex; flex-direction: column; }
      .field-frame { position: relative; height: 42svh; flex: none; }
      .field-vignette { background: linear-gradient(180deg, transparent 60%, #050608 100%); }
      .field-beats { position: relative; right: auto; top: auto; transform: none; width: auto; padding: 14px clamp(20px, 4vw, 56px) 0; min-height: 0; flex: 1 1 auto; overflow: hidden; }
      /* Cormorant at 20/1.32 in a 298 px column ran to eleven lines and is the
         least legible body setting on the site — smaller in apparent size than
         the 16 px DM Sans used for every other paragraph. DM Sans at 17/1.55
         reads larger and takes about a fifth fewer lines, which is also part of
         what makes the stage fit. Desktop keeps the serif. */
      .field-beat p { font: 400 17px/1.55 var(--font-body); }
      /* the counter cleared the fixed nav: at top 14 it was drawn at y 14..26,
         entirely underneath a 390x77 fixed bar, for the whole 7.6-screen pin */
      .field-count { top: 88px; }
      .field-keys { position: relative; left: auto; bottom: auto; padding: 14px clamp(20px, 4vw, 56px) 16px; gap: 8px; }
      .field-key { padding: 13px 14px 15px; font-size: 13px; min-height: 44px; }
      .field-caption { position: relative; left: auto; right: auto; bottom: auto; padding: 12px clamp(20px, 4vw, 56px) 14px; font-size: 12px; gap: 4px 12px; }
    }
    @media (prefers-reduced-motion: reduce) {
      .field-img, .field-beat { transition: none; }
    }

    /* ──────────────── §4 The instrument — plate beside a console ──────────── */
    .inst { padding-block: var(--sp-xxl); }
    /* The head carries `wrap wrap--stage`, like §7's, because the showcase below it sits on the
       stage width. Its own narrower max-width would win at equal specificity and `.wrap`'s auto
       margins would then centre it: measured, that put the headline's left edge 760 px inboard
       of the frame's at 2560 and again at 3440. Cap the type, not the wrap. */
    .inst-head { max-width: 2320px; }
    .inst-head > * { max-width: 800px; }
    .inst-deck {
      margin-top: var(--sp-lg);
      display: grid;
      grid-template-columns: minmax(0, 1.55fr) minmax(300px, 0.75fr);
      gap: 26px;
      align-items: stretch;
    }
    .inst-plate {
      position: relative;
      aspect-ratio: 17 / 10;
      overflow: hidden;
      border: 1px solid var(--rule);
      border-radius: var(--r-lg);
      background: #0a0c12;
      box-shadow: 0 30px 90px rgba(0,0,0,0.5), inset 0 1px 0 rgba(255,255,255,0.04);
    }
    /* The incoming frame fades in ON TOP; the outgoing one holds for the length
       of that fade before it goes, so a quick run of swaps — the demo tween, or
       a dragged dial — never leaves the plate dark between frames. */
    .inst-frame {
      position: absolute; inset: 0; z-index: 1;
      width: 100%; height: 100%; object-fit: cover; display: block;
      opacity: 0;
      transition: opacity 0.42s var(--ease-out) 0.42s;
    }
    .inst-frame.is-on { opacity: 1; z-index: 2; transition-delay: 0s; }
    .inst-tag {
      position: absolute; top: 12px; left: 14px; z-index: 3;
      font: 500 10px/1 var(--font-mono); letter-spacing: 0.14em; text-transform: uppercase;
      color: #e0c891;
      padding: 6px 10px; border-radius: var(--r-sm);
      background: rgba(10,12,18,0.5); border: 1px solid rgba(255,255,255,0.09);
      backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
      pointer-events: none;
    }
    /* The console: a well the dial sits in. Console material wants a mid-tone
       surface under it, so the well carries its own. */
    .inst-console {
      display: flex; flex-direction: column; gap: 14px;
      padding: 22px 22px 20px;
      background: linear-gradient(180deg, rgba(17,20,28,0.9), rgba(8,11,18,0.9));
    }
    .inst-row { display: flex; align-items: baseline; justify-content: space-between; }
    .inst-label { font: 500 10.5px/1 var(--font-mono); letter-spacing: 0.14em; text-transform: uppercase; color: var(--t2); }
    .inst-val { font-family: var(--font-display); font-weight: 500; font-size: 30px; line-height: 1; color: var(--gold-ink); font-variant-numeric: tabular-nums; }
    .inst-scale { display: flex; justify-content: space-between; margin-top: -6px; font: 500 9.5px/1 var(--font-mono); letter-spacing: 0.1em; color: var(--t3); }
    .inst-readout { margin: 6px 0 0; font: 400 14px/1.6 var(--font-body); color: var(--t1-soft); min-height: 3.2em; }
    /* "Looking for" — the app names its presets for the quarry, not the kernel */
    .inst-presets { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 7px; margin-top: 2px; }
    .inst-preset { position: relative; padding: 10px 11px 16px; text-align: left; cursor: pointer; font: 500 12.5px/1.2 var(--font-body); }
    .inst-preset small { display: block; margin-top: 4px; font: 400 10.5px/1.3 var(--font-body); color: var(--t3); }
    .inst-preset.is-on small { color: var(--gold-dim); }
    .inst-presets-label { margin: 8px 0 0; font: 500 10px/1 var(--font-mono); letter-spacing: 0.14em; text-transform: uppercase; color: var(--t3); }
    .inst-note { margin: auto 0 0; padding-top: 14px; border-top: 1px solid rgba(255,255,255,0.06); font: 400 12.5px/1.6 var(--font-body); color: var(--t2); }

    .readings {
      margin-top: var(--sp-lg);
      display: grid;
      grid-template-columns: repeat(4, minmax(0, 1fr));
      gap: 18px;
    }
    .reading { margin: 0; }
    .reading img {
      /* 16:9 from a 17:10 source, biased low: at pitch 48 the far edge of the
         frame runs past the loaded bronze tiles, and the fort is the subject. */
      display: block; width: 100%; height: auto; aspect-ratio: 16 / 9; object-fit: cover; object-position: center 88%;
      border-radius: var(--r-md);
      border: 1px solid var(--rule);
      background: #0a0c12;
    }
    .reading figcaption { margin-top: 11px; font: 400 12.5px/1.55 var(--font-body); color: var(--t3); }
    .reading figcaption b { display: block; margin-bottom: 3px; font-weight: 500; font-size: 13.5px; color: var(--t1); }
    .inst-baked { margin-top: var(--sp-md); max-width: 78ch; font: 400 14px/1.65 var(--font-body); color: var(--t2); }
    .inst-stand { margin: var(--sp-xl) 0 var(--sp-md); max-width: 62ch; }
    .inst-stand { font-family: var(--font-body); font-size: clamp(16px, 1.3vw, 19px); line-height: 1.7; color: var(--t1-soft); }

    @media (max-width: 1000px) {
      .inst-deck { grid-template-columns: 1fr; }
      .readings { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; }
    }
    @media (prefers-reduced-motion: reduce) { .inst-frame { transition: none; } }

    /* ──────────────── §7 The walk — a drawn graphic ──────────────── */
    /* This section is wider than the rest of the page: the map is the evidence
       and it is read at size. 2320 caps it so a 3440 screen and the 2560 GPU
       capture both land on the same 2208px content width (both clamp the
       padding to 56), i.e. the screenshot IS the layout Matt sees. */
    .wrap--stage { max-width: 2320px; }
    .walkg { padding-block: var(--sp-xxl); }
    /* The head carries `wrap wrap--stage`, so it must keep the stage's own box —
       its own narrower max-width would win at equal specificity and `.wrap`'s
       auto margins would then centre it, throwing the headline 760px right of
       the stage's left edge at 2560. Cap the type, not the wrap. */
    .walkg-head { max-width: 2320px; }
    .walkg-head > * { max-width: 800px; }
    /* Three blocks in one row: key, map, list. align-items:start means the row
       is as tall as the tallest of them, so the list can never need an inner
       scrollbar — a tenth row makes the section taller instead. Do not re-add
       overflow to .walkg-list. Nothing is absolutely positioned against the
       stage any more; only the four route statistics sit over the map. */
    .walkg-stage {
      position: static;
      margin-top: var(--sp-lg);
      display: grid;
      grid-template-columns: 280px minmax(0, 1fr) 400px;
      gap: 28px;
      align-items: start;
    }
    .walkg-key  { grid-column: 1; align-self: start; }
    .walkg-map  { grid-column: 2; }
    .walkg-list { grid-column: 3; align-self: start; }
    .walkg-map {
      position: relative; margin: 0; width: 100%;
      aspect-ratio: 16 / 9;
      overflow: hidden;
      border-radius: var(--r-lg);
      border: 1px solid var(--rule);
      background: #0a0c12;
      box-shadow: 0 30px 90px rgba(0,0,0,0.5);
    }
    .walkg-base { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; }
    .walkg-svg { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
    /* The frame is 2560 units wide and is displayed 1472px wide, so one unit is
       0.575 CSS px: every stroke below is set for that scale. Four stacked paths
       share one `d` and one dash offset, so the glow grows with the line — a wide
       soft stroke, not a blur filter: the filter re-rasterised the whole drawing
       every scroll frame. */
    .walkg-paths { position: absolute; inset: 0; width: 100%; height: 100%; display: block; object-fit: cover; }
    .walkg-route-halo { fill: none; stroke: rgba(244,213,141,0.07); stroke-width: 44; stroke-linecap: round; stroke-linejoin: round; }
    .walkg-route-glow { fill: none; stroke: rgba(244,213,141,0.18); stroke-width: 28; stroke-linecap: round; stroke-linejoin: round; }
    .walkg-route-case { fill: none; stroke: rgba(5,8,14,0.45); stroke-width: 16; stroke-linecap: round; stroke-linejoin: round; }
    .walkg-route { fill: none; stroke: var(--gold-ink); stroke-width: 10; stroke-linecap: round; stroke-linejoin: round; }
    /* The sites are drawn with the app's own pin sprites (96×96 at pixelRatio 3,
       centre-anchored as layers.json anchors them), at 64 units — 36.8 CSS px,
       the size the app draws them at 1:1. */
    .walkg-pin { opacity: 0; transform-box: fill-box; transform-origin: 50% 50%; transform: scale(0.62); transition: opacity 0.28s var(--ease-out), transform 0.34s cubic-bezier(0.2,0.9,0.3,1); }
    .walkg-pin.is-on { opacity: 1; transform: none; }
    .walkg-badge { fill: #0b0d12; stroke: var(--gold-ink); stroke-width: 2.5; opacity: 0; transition: opacity 0.3s var(--ease-out); }
    .walkg-badge-n { fill: var(--gold-ink); font: 600 18px var(--font-body); text-anchor: middle; opacity: 0; transition: opacity 0.3s var(--ease-out); }
    .walkg-site-label { fill: #ede9e2; font: 500 28px var(--font-display); paint-order: stroke; stroke: rgba(4,7,12,0.92); stroke-width: 7px; stroke-linejoin: round; opacity: 0; transition: opacity 0.3s var(--ease-out); }
    .walkg-site-label.is-left { text-anchor: end; }
    .walkg-badge.is-on, .walkg-badge-n.is-on, .walkg-site-label.is-on { opacity: 1; }
    .walkg-site-ring { fill: none; stroke: rgba(244,213,141,0.55); stroke-width: 4; opacity: 0; }
    .walkg-site-ring.is-on { animation: walkg-ping 1.4s var(--ease-out) 1 forwards; }
    @keyframes walkg-ping { 0% { opacity: 0.9; r: 34; } 100% { opacity: 0; r: 90; } }
    /* The key — the app's own key (HikingPanel's .hk-key), drawn at 1.88× so it
       reads at page scale. Sentence-case body type, as the app's is; only the
       panel heading is a label, matching .walkg-list-head so the two side
       panels read as a pair. */
    .walkg-key { padding: 16px 16px 14px; display: flex; flex-direction: column; gap: 11px; border: 1px solid rgba(201,168,76,0.22); border-radius: 10px; }
    .walkg-key-head { margin: 0; padding-bottom: 10px; border-bottom: 1px solid rgba(255,255,255,0.06); font: 500 10.5px/1 var(--font-mono); letter-spacing: 0.14em; text-transform: uppercase; color: var(--t2); }
    .walkg-key-row { display: flex; align-items: center; gap: 10px; }
    .walkg-key-row span { font: 400 14px/1.4 var(--font-body); color: var(--t1-soft); }
    .walkg-sw { flex: none; width: 64px; height: 14px; }
    .walkg-sw--route { filter: drop-shadow(0 0 4px rgba(244,213,141,0.55)); }
    .walkg-li-n { display: inline-grid; place-items: center; width: 22px; height: 22px; border-radius: 50%; border: 1.5px solid var(--gold-ink); font: 600 11px/1 var(--font-body); color: var(--gold-ink); }
    /* The one thing still over the map, as the app keeps its stats over its own.
       Bottom RIGHT, not left: site 4 (Boscreege Farm) sits at the bottom left of
       this frame and the panel covered its pin and name outright. Nothing of the
       route or the nine sites reaches the bottom right corner. */
    .walkg-stats {
      position: absolute; right: 14px; bottom: 14px; z-index: 2;
      display: flex; gap: 0;
      border-radius: var(--r-well);
      background: rgba(6,8,14,0.72);
      box-shadow: inset 2px 2px 6px rgba(0,0,0,0.6), inset -1px -1px 3px rgba(255,255,255,0.03);
      backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
      overflow: hidden;
    }
    .walkg-stats span { display: flex; flex-direction: column; gap: 5px; padding: 10px 16px 11px; border-right: 1px solid rgba(255,255,255,0.06); }
    .walkg-stats span:last-child { border-right: 0; }
    .walkg-stats i { font: 500 9px/1 var(--font-mono); letter-spacing: 0.16em; text-transform: uppercase; color: var(--t2); font-style: normal; }
    .walkg-stats b { font-family: var(--font-display); font-weight: 500; font-size: 20px; line-height: 1; color: var(--gold-ink); font-variant-numeric: tabular-nums; }
    /* In normal flow beside the map, not over it: nine rows plus the head come
       to ~700px against an 828px map, so it fits with room. No max-height and
       no overflow — a longer list must lengthen the section, never scroll. */
    .walkg-list { position: static; padding: 16px 18px 14px; overflow: visible; max-height: none; border: 1px solid rgba(201,168,76,0.22); border-radius: 10px; background: rgba(10,15,26,0.82); }
    .walkg-list-head { display: flex; justify-content: space-between; padding-bottom: 10px; border-bottom: 1px solid rgba(255,255,255,0.06); font: 500 10.5px/1 var(--font-mono); letter-spacing: 0.14em; text-transform: uppercase; color: var(--t2); }
    .walkg-list ol { list-style: none; margin: 0; padding: 4px 0 0; position: static; overflow: visible; }
    /* number, name, distance along — the distance off the path runs under the
       name, indented past the number */
    .walkg-list li {
      display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: 2px 12px; align-items: baseline;
      padding: 9px 0; border-bottom: 1px solid rgba(255,255,255,0.05);
      opacity: 0; transform: translateX(8px);
      transition: opacity 0.35s var(--ease-out), transform 0.35s var(--ease-out);
    }
    .walkg-list li.is-on { opacity: 1; transform: none; }
    .walkg-list li b { font-family: var(--font-display); font-weight: 500; font-size: 17px; line-height: 1.25; color: var(--t1); }
    .walkg-list li i { font: 500 10px/1.4 var(--font-mono); letter-spacing: 0.06em; color: var(--gold-dim); font-style: normal; white-space: nowrap; }
    /* --t3 is a rule/disabled ink (1.75:1 on this ground); this line carries a
       figure the lede tells the reader to read, so it takes --t2 (5.1:1). */
    .walkg-list li small { grid-column: 2 / -1; font: 400 11px/1.4 var(--font-body); color: var(--t2); }
    .walkg-facts {
      margin-top: var(--sp-lg); padding-top: var(--sp-md); border-top: 1px solid var(--rule);
      display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 40px;
    }
    .walkg-facts .also { max-width: 62ch; }
    /* Three columns need 280 + 400 + 56 of gaps + 112 of page padding = 848px
       before the map starts, so the three-up form only pays above 2056px of
       viewport. Below that: map and list, key as a strip under the map. */
    @media (max-width: 2055px) {
      .walkg-stage { grid-template-columns: minmax(0, 1fr) 360px; }
      .walkg-map { grid-column: 1; grid-row: 1; }
      .walkg-key { grid-column: 1; grid-row: 2; flex-direction: row; flex-wrap: wrap; gap: 10px 26px; align-items: center; }
      .walkg-key-head { border-bottom: 0; padding: 0 4px 0 0; }
      .walkg-list { grid-column: 2; grid-row: 1 / -1; }
    }
    @media (max-width: 1200px) {
      .walkg-stage { grid-template-columns: 1fr; gap: 20px; }
      .walkg-map, .walkg-key, .walkg-list { grid-column: 1; grid-row: auto; }
      .walkg-key { flex-direction: row; flex-wrap: wrap; }
      .walkg-list { align-self: stretch; }
      .walkg-facts { grid-template-columns: 1fr; gap: 20px; }
    }
    @media (max-width: 640px) { .walkg-stats b { font-size: 16px; } .walkg-stats span { padding: 8px 11px 9px; } .walkg-list li b { font-size: 15px; } }

    /* ──────────────── §9 Where it stops — two footprints, then the road ────── */
    .edges { padding-block: var(--sp-xxl); }
    .edges-head { max-width: 800px; }
    .edges-pair {
      margin-top: var(--sp-lg);
      display: grid; grid-template-columns: 1fr 1fr; gap: 26px;
      max-width: 1000px;
    }
    .edge { margin: 0; }
    .edge img {
      display: block; width: 100%; height: auto; aspect-ratio: 11 / 10; object-fit: cover;
      border-radius: var(--r-lg); border: 1px solid var(--rule); background: #0a0c12;
      filter: saturate(0.9);
    }
    .edge figcaption { margin-top: 12px; font: 400 13px/1.55 var(--font-body); color: var(--t3); }
    .edge figcaption b { display: block; margin-bottom: 3px; font-weight: 500; font-size: 14px; color: var(--t1); }

    /* The road, in the shape Matt drew on the About page: a line with stops,
       the live stop lit. Gold means live and nothing else, so the shipped stop
       is quiet, the current one is lit, the built-and-dark one is half. */
    .road {
      position: relative;
      list-style: none; margin: var(--sp-xl) 0 0; padding: 0;
      display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 28px;
    }
    .road::before {
      content: ""; position: absolute; left: 0; right: 0; top: 5px; height: 1px;
      background: linear-gradient(90deg, rgba(201,168,76,0.4), rgba(201,168,76,0.14) 55%, rgba(201,168,76,0.06));
    }
    .road-stop { position: relative; padding-top: 24px; }
    .road-dot {
      position: absolute; left: 0; top: 0;
      width: 11px; height: 11px; border-radius: 50%;
      background: rgba(255,255,255,0.08);
      box-shadow: inset 0 0 0 1px rgba(255,255,255,0.14);
    }
    .road-stop.is-done .road-dot { background: rgba(201,168,76,0.35); box-shadow: inset 0 0 0 1px rgba(201,168,76,0.5); }
    .road-stop.is-now  .road-dot { background: var(--gold-ink); box-shadow: 0 0 10px rgba(244,213,141,0.6), inset 0 0 0 1px rgba(244,213,141,0.9); }
    .road-stop.is-now  .road-dot::after {
      content: ""; position: absolute; inset: -4px; border-radius: 50%;
      border: 1px solid rgba(244,213,141,0.45);
      animation: ws-pulse 2.6s var(--ease-in-out) infinite;
    }
    .road-stop.is-next .road-dot { background: rgba(244,213,141,0.22); box-shadow: inset 0 0 0 1px rgba(244,213,141,0.45); }
    .road-when { display: block; font: 500 10.5px/1 var(--font-mono); letter-spacing: 0.14em; text-transform: uppercase; color: var(--t3); margin-bottom: 10px; }
    .road-stop.is-now .road-when { color: var(--gold); }
    .road-title { margin: 0 0 8px; font-family: var(--font-display); font-weight: 400; font-size: 22px; line-height: 1.2; color: var(--t1); }
    .road-desc { margin: 0; font: 400 13.5px/1.6 var(--font-body); color: var(--t1-soft); }
    .road-stop:not(.is-now) .road-desc { color: var(--t2); }
    @media (max-width: 900px) {
      .edges-pair { grid-template-columns: 1fr 1fr; gap: 14px; }
      .road { grid-template-columns: 1fr; gap: 22px; }
      .road::before { display: none; }
      .road-stop { padding-top: 0; padding-left: 24px; }
      .road-dot { top: 4px; }
    }

    /* ──────────────── §11 Invitation — the last line ──────────────── */
    .invite-last {
      margin: 34px auto 0; max-width: 40ch;
      font-family: var(--font-display); font-style: italic; font-weight: 300;
      font-size: clamp(17px, 1.7vw, 22px); color: var(--t2);
    }

    /* ──────────────── The four maps, credited (under §3) ──────────────── */
    .fourmaps {
      margin-top: var(--sp-lg);
      display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 22px;
    }
    .fourmap { position: relative; padding-top: 16px; border-top: 1px solid var(--rule); }
    .fourmap-n { display: block; margin-bottom: 10px; font: 500 10.5px/1 var(--font-mono); letter-spacing: 0.16em; color: var(--gold-dim); }
    .fourmap h3 { margin: 0 0 4px; font-family: var(--font-display); font-weight: 400; font-size: 22px; line-height: 1.2; color: var(--t1); }
    .fourmap-from { margin: 0 0 10px; font: 500 10.5px/1.4 var(--font-mono); letter-spacing: 0.06em; text-transform: uppercase; color: var(--gold); }
    .fourmap p:last-child { margin: 0; font: 400 13.5px/1.6 var(--font-body); color: var(--t1-soft); }
    .fourmap em { font-style: normal; color: var(--t1); }
    @media (max-width: 1000px) { .fourmaps { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 22px; } }
    @media (max-width: 560px) { .fourmaps { grid-template-columns: 1fr; } }

    /* ──────────────── The Research Library, as its own exhibit ──────────────── */
    .libsearch {
      margin: var(--sp-xl) 0 0;
      display: grid; grid-template-columns: minmax(0, 1.5fr) minmax(280px, 0.8fr); gap: 32px; align-items: center;
    }
    .libsearch-shot { margin: 0; }
    .libsearch-shot img {
      display: block; width: 100%; height: auto;
      border-radius: var(--r-lg); border: 1px solid var(--rule); background: #0a0c12;
      box-shadow: 0 26px 70px rgba(0,0,0,0.5);
    }
    .libsearch-text h3 {
      margin: 0 0 12px;
      font-family: var(--font-display); font-weight: 400;
      font-size: clamp(24px, 2.4vw, 30px); line-height: 1.12; color: var(--t1);
    }
    .libsearch-text p { margin: 0; font: 400 14.5px/1.65 var(--font-body); color: var(--t1-soft); }
    @media (max-width: 900px) { .libsearch { grid-template-columns: 1fr; gap: 20px; } }

    /* ──────────────── The road, as its own section ──────────────── */
    .roadsec { padding-block: var(--sp-xxl); }
    .road-head { max-width: 760px; }

    /* ──────────────── Research — reading the maps as data ──────────────── */
    .research { padding-block: var(--sp-xxl); }
    .research-head { max-width: 860px; }
    .research-note { padding-top: 14px; border-top: 1px solid var(--rule); }
    .research-note h3 { margin: 0 0 7px; font-family: var(--font-display); font-weight: 400; font-size: 22px; line-height: 1.2; color: var(--t1); }
    .research-note p { margin: 0; font: 400 14px/1.62 var(--font-body); color: var(--t1-soft); }
    /* the notes about the Library's work: marked in gold */
    .research-note--partner { border-top-color: rgba(201,168,76,0.35); }
    .research-note--partner h3 { color: #f4d58d; }
    /* the invitation to other heritage bodies: a card with the one contact */
    /* the card runs the full column, square with the three notes above it; the
       reading measure is capped on the text inside instead */
    .research-invite {
      margin-top: var(--sp-lg); padding: 26px 28px 28px;
      border: 1px solid rgba(201,168,76,0.22); border-radius: var(--r-card);
      background: linear-gradient(180deg, rgba(17,20,28,0.9), rgba(8,11,18,0.9));
      box-shadow: inset 0 1px 0 rgba(255,255,255,0.04), 0 18px 50px rgba(0,0,0,0.4);
    }
    .research-invite h3 { margin: 0 0 8px; max-width: 760px; font-family: var(--font-display); font-weight: 400; font-size: 24px; line-height: 1.2; color: var(--t1); }
    .research-invite p { margin: 0 0 16px; max-width: 760px; font: 400 14.5px/1.62 var(--font-body); color: var(--t1-soft); }
    .research-invite .btn { margin-top: 2px; }

    /* ──────────────── Partners — the National Library of Scotland ──────────────── */
    .partners { padding-block: var(--sp-xxl); }
    /* three notes in a row under the full-width figure: the same card as the
       research notes, laid across instead of down */
    .partner-notes {
      margin-top: var(--sp-lg);
      display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 30px;
    }
    @media (max-width: 900px) { .partner-notes { grid-template-columns: 1fr; gap: 22px; } }

    /* ──────────────── Roman Britain — the Roman Roads Research Association ────
       #roman is built out of the partners section's own furniture: .section-head
       with .eyebrow / .gold-rule / .display--sm / .lede, one .research-fig--wide
       figure, and two .research-note--partner cards in .partner-notes--two.
       What is new here is only the section's padding, the head measure, and the
       phone swap: under 700 px the figure's <picture> serves the card cut from
       the same frame at native pixels (412 x 330), so it is held at its own
       width rather than stretched across a tablet; between 700 and 1599 px it
       serves a 1440 x 900 window cut from the same frame, so the card is 312
       CSS px there instead of 175. */
    .roman { padding-block: var(--sp-xxl); }
    .roman-head { max-width: 860px; }
    @media (max-width: 699px) {
      .roman-fig img { max-width: 412px; margin-inline: auto; }
      /* under 700 px the picture is the card alone, so the clause about the
         road is not describing anything the reader can see */
      .roman-cap-wide { display: none; }
    }
    /* the four-source rights credit is not the same job as the sentence saying
       what you are looking at, so it takes its own line one step down, at the
       size .research-plates-credit already uses for a multi-source credit */
    .roman-fig .roman-credit {
      display: block; margin-top: 7px;
      font-size: 10.5px; line-height: 1.5;
    }
    /* MEASURED at 3440x1440: at the 1320 px wrap the 2560 px frame draws the
       fort's card 175 px wide, and its date rows and its Record key are then
       5-6 px of type — the one thing the figure is here to show, unreadable.
       On screens wide enough for it the section takes the page's own stage
       measure (.wrap--stage, 2320 px), as §7 and §4 do: the picture is then
       2208 px wide and the card 321 px, against the 372 px it measures in the
       captured frame, and every line of it reads. The head stays at 860, so the
       lede keeps its reading measure; the two notes let go of their 1000 px cap
       and run the width of the picture, as §7's fact row does at this width,
       because at 2320 px a 1000 px block leaves the right half of the section
       empty. Each note's paragraph keeps a 720 px measure inside its column. */
    @media (min-width: 1600px) {
      #roman.wrap--wide { max-width: 2320px; }
      #roman .partner-notes--two { max-width: none; column-gap: 96px; }
      #roman .research-note--partner p { max-width: 720px; }
    }

    /* ──────────────── Research — the three parts ──────────────── */
    .rpart { margin-top: var(--sp-xl); padding-top: var(--sp-lg); border-top: 1px solid var(--rule); }
    .rpart:first-of-type { margin-top: var(--sp-lg); }
    .rpart-head { display: flex; flex-direction: column; gap: 16px; margin-bottom: var(--sp-md); max-width: 860px; }
    .rpart-title {
      margin: 0; font-family: var(--font-display); font-weight: 400;
      font-size: clamp(24px, 2.6vw, 34px); line-height: 1.14; color: var(--t1);
    }
    /* MEASURED 2026-09-14 at 1440x900: at 860 px this 14.5 px body ran 105-130
       characters a line across fourteen paragraphs — half again the longest
       measure on the page (#protools 86, #walk 87). 640 px brings the worst
       case to 94 and the rest to 53-90. */
    .rpart-text { max-width: 640px; }
    .rpart-text p { margin: 0; font: 400 14.5px/1.7 var(--font-body); color: var(--t1-soft); }

    /* figures in the research parts */
    .research-fig { margin: 0; }
    .research-fig picture { display: block; }
    .research-fig img {
      display: block; width: 100%; height: auto;
      border-radius: var(--r-md); border: 1px solid var(--rule-strong);
      background: #0a0c12; box-shadow: 0 26px 70px rgba(0,0,0,0.5);
    }
    .research-fig--wide { margin-top: var(--sp-lg); }
    /* long captions read as sentences, not as a slab of capitals */
    .research-fig figcaption {
      margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--rule);
      font: 400 11.5px/1.62 var(--font-body); letter-spacing: 0; text-transform: none;
      color: var(--t2); max-width: 780px;
    }

    /* The two-chain diagram's rules lived here. The diagram itself was removed
       from §11 on 2026-09-14 and replaced by the eight illustrated provenance
       steps (.rsteps / .rstep, at the end of this file), so .rdiagram-scroll,
       .rdiagram-lines and the whole .rd-* family had nothing left to style.
       Checked against index.html before deleting: zero uses of any of them
       (.tp-rd-* in §7 is a different family and is untouched). */

    /* ──────────────── §4 The 3D showcase — one viewer, the layers as keys ─────── */
    .showcase { margin-top: var(--sp-lg); }
    .showcase-stack { gap: 12px; }
    .showcase-frame { aspect-ratio: 16 / 9; max-height: 82vh; }
    .mc-still { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: 1; }
    .mc-still[hidden] { display: none; }
    .showcase-tag {
      position: absolute; z-index: 4; left: 14px; top: 12px;
      font: 500 10px/1 var(--font-mono); letter-spacing: 0.14em; text-transform: uppercase;
      color: #e0c891;
      padding: 6px 10px; border-radius: var(--r-sm);
      background: rgba(10,12,18,0.5); border: 1px solid rgba(255,255,255,0.09);
      backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
      pointer-events: none;
    }
    /* The tag sets words in caps. One reading now carries a unit in it — "Live SLRM · 10 m" —
       and the caps turned the metre symbol into an upper-case M, which is the prefix mega. The
       unit is marked in the tag's text and keeps its own case. */
    .showcase-tag .u { text-transform: none; }
    /* Nine keys in a row of a plate; two rows on a laptop, three on a phone. The
       key face names the layer, the small line says what it is for. */
    .showcase-keys {
      display: grid; grid-template-columns: repeat(9, minmax(0, 1fr)); gap: 7px;
      padding: 12px;
      border-radius: var(--r-card);
      background: linear-gradient(180deg, rgba(17,20,28,0.9), rgba(8,11,18,0.9));
      box-shadow: inset 0 1px 0 rgba(255,255,255,0.04), 0 18px 50px rgba(0,0,0,0.45);
    }
    /* THE PANEL beside the viewer: one reading at a time. Four keys in a column,
       each naming a reading of this ground and saying in one line what it shows,
       then one switch for whether the photograph sits under it. Everything here
       is the page's own console material: a key is pressed out of the plate and
       sinks when it is live, and the filament — the one gesture for live — is
       stood on its end so the chosen reading carries a gold rule down its edge. */
    /* the frame and the readout under it in the first column; the panel spans both rows in the second, never
       scrolling inside itself (an inner scroll took the wheel from the page and stalled the scroll) */
    /* THE VIEWER RENDERS AT ITS FRAME'S SIZE. On a DPR-1 screen the canvas is exactly the
       frame's CSS size, and with the panel beside it in a 1320 px wrap the frame was 814 px
       wide on a 3440 px screen (Matt, 2026-09-14: "the rendering resolution has been degraded").
       The showcase now sits on the walk's stage width (.wrap--stage, 2320 px), so the frame is
       1926 x 1083 at 3440 and 2560 and 1046 x 588 at 1440; the panel keeps its 380 px column. */
    .showcase-grid { display: grid; grid-template-columns: minmax(0, 1fr) 380px; grid-template-rows: auto 1fr; gap: 14px; align-items: start; }
    .showcase-grid .showcase-frame { max-height: none; grid-column: 1; grid-row: 1; }   /* 16:9 from its width */
    .showcase-grid .showcase-readout { grid-column: 1; grid-row: 2; align-self: start; margin: 0; }
    /* `start`, not `stretch`. On the stage the frame is 1083 px tall and the panel's five
       controls come to about 515 px, so stretching it left 511 px of empty plate — measured at
       2560 and 3440, 46.6% of the panel's own height — with `.lr-foot`'s `margin: auto 0 0`
       holding the footnote at the bottom of the gap. The panel is now the size of what is in it. */
    .lr-panel { grid-column: 2; grid-row: 1 / 3; align-self: start; }
    .lr-panel {
      display: flex; flex-direction: column; gap: 12px; min-height: 0; overflow: visible;
      padding: 16px 16px 18px; border-radius: var(--r-card);
      background: linear-gradient(180deg, rgba(17,20,28,0.92), rgba(8,11,18,0.92));
      box-shadow: inset 0 1px 0 rgba(255,255,255,0.04), 0 18px 50px rgba(0,0,0,0.45);
      border: 1px solid rgba(201,168,76,0.12);
    }
    .lr-panel-head { margin: 2px 0 0; }
    .lr-keys { display: flex; flex-direction: column; gap: 8px; }
    .lr-key {
      position: relative; display: block; width: 100%; margin: 0;
      padding: 13px 15px 15px 21px;   /* the extra on the left is the rule's lane */
      text-align: left; cursor: pointer;
    }
    .lr-key-name { display: block; font: 500 19px/1.15 var(--font-display); letter-spacing: 0.01em; color: var(--t1); transition: color 0.22s var(--ease-out); }
    .lr-key.is-on .lr-key-name { color: var(--gold-ink); }
    .lr-key-line { display: block; margin-top: 5px; font: 400 12.5px/1.5 var(--font-body); color: rgba(237,233,226,0.62); text-wrap: pretty; transition: color 0.22s var(--ease-out); }
    .lr-key.is-on .lr-key-line { color: rgba(237,233,226,0.8); }

    /* THE LIVE SLRM KEY. One reading carries a setting of its own — the search
       radius — and that setting belongs to the reading, not to the panel: it is
       inside the key, not on a surface of its own. A button cannot contain an
       input, so this one key is a div wearing the same `.key .lr-key` face as the
       other four, holding the button that selects the reading and, under it, the
       radius row. The card is what lights up; the button inside is only the part
       you press to choose. */
    .lr-key--slrm { cursor: default; }
    .lr-key-main {
      display: block; width: 100%; margin: 0; padding: 0; border: 0;
      background: none; color: inherit; font: inherit; text-align: left; cursor: pointer;
    }
    /* The button fills the card's padding box, so the whole face of this key is pressable the way
       the whole face of the other four is. MEASURED before this: the card lifted 1.5px and took the
       gold bloom on hover anywhere in its 21px left lane, its 15px right and its 13/15 top and
       bottom — 27.3% of the card at 1440x900 — while `elementFromPoint` there returned the DIV and
       a real click left the reading on Bare earth. Negative margins reach out to the card's edges
       and equal padding puts the text back where it was, so nothing moves and the card's own height
       is unchanged; `width: auto` because the page is border-box and a 100% width plus that padding
       would have run the button off the right edge. The bottom is only claimed while the row is
       shut — while it is open the row is what lives down there. */
    .lr-key--slrm .lr-key-main { width: auto; margin: -13px -15px 0 -21px; padding: 13px 15px 0 21px; }
    .lr-key--slrm:not(.is-on) .lr-key-main { margin-bottom: -15px; padding-bottom: 15px; }
    /* the filament is a child of the card, not of the button, and being positioned it paints over
       it: without this it kept a 2.5px inert strip down the lane it sits in */
    .lr-key--slrm .filament { pointer-events: none; }
    /* now that the button is the card's own box, its ring takes the card's radius and traces the
       card edge at the same 3px offset `.key:focus-visible` gives the other four */
    .lr-key-main:focus-visible { outline: 1px solid var(--gold-ink); outline-offset: 3px; border-radius: var(--r-key); }
    /* the card's own press must come from the button, not from the range: without this the
       whole card sank by 1.5px every time the radius was dragged (`.key:active` in _shared.css
       fires on the div while any descendant is being pressed) */
    .lr-key--slrm:active { transform: none; }
    /* and while it is the live reading it must not rise out of its own sink either: MEASURED,
       holding the range moved the card from translateY(1px) to none, a 1px hop under the thumb */
    .lr-key--slrm.is-on:active { transform: translateY(1px); }
    .lr-key--slrm:has(.lr-key-main:active) { transform: translateY(1.5px); }
    .lr-key--slrm:has(.lr-key-main:disabled) { cursor: default; opacity: 0.45; }

    /* The row opens only under its own reading. The grid row runs 0fr → 1fr, so the open
       height is the row's own at whatever width and type size it is given, never a guessed
       max-height; `visibility` is what takes the range out of the tab order while it is shut,
       and it is held to the end of the close so the row does not vanish before it has folded. */
    .lr-slrm-ctl {
      display: grid; grid-template-rows: 0fr; margin-top: 0; opacity: 0; visibility: hidden;
      transition: grid-template-rows 0.34s var(--ease-out), margin-top 0.34s var(--ease-out),
                  opacity 0.18s var(--ease-out), visibility 0s linear 0.34s;
    }
    .lr-slrm-ctl.is-open {
      grid-template-rows: 1fr; margin-top: 13px; opacity: 1; visibility: visible;
      transition: grid-template-rows 0.34s var(--ease-out), margin-top 0.34s var(--ease-out),
                  opacity 0.26s var(--ease-out) 0.08s, visibility 0s;
    }
    .lr-slrm-inner { overflow: hidden; display: grid; gap: 7px; }
    /* `position: relative` so the label and the value keep their own pixels: the range's hit
       padding reaches up past the 7px gap, and an in-flow input painted over the head row. MEASURED
       at 390x844 before this, `elementFromPoint` at the centre of the "10 m" output returned the
       INPUT and a real click there took the radius from 10 m to 40 m. */
    .lr-slrm-head { position: relative; display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
    /* the panel's own two label idioms, unchanged: the note's caps mono for the name of the
       setting, the switch state's caps mono in gold for the number it is reading */
    .lr-slrm-label { font: 500 9px/1.3 var(--font-mono); letter-spacing: 0.14em; text-transform: uppercase; color: var(--t2); cursor: pointer; }
    /* no text-transform here, unlike the switch's On/Off it sits beside: the switch's is a word
       and the panel sets words in caps, this is a unit symbol and an upper-case M is mega */
    .lr-slrm-out   { font: 500 10px/1 var(--font-mono); letter-spacing: 0.12em; color: var(--gold-ink); }

    /* The range: a thin gold groove cut into the card with the same lit dot the light dial
       uses for the sun. `--p` is written by the module from the same number the output shows,
       so the filled part of the track and the words always say the same thing. */
    /* The track is 3px and the dot 12px, which would leave a 16px-tall thing to hit with a
       thumb. The padding grows the hit area and the equal negative margin takes the growth
       back out of the layout, so the row is laid out at the height it draws at and pressed at
       36px (44px under 700px, the panel's own touch floor). content-box is what keeps the
       padding out of the drawn height. */
    .lr-slrm-range {
      -webkit-appearance: none; appearance: none; box-sizing: content-box;
      display: block; width: 100%; height: 16px; padding: 10px 0; margin: -10px 0;
      background: transparent; cursor: pointer;
    }
    .lr-slrm-range::-webkit-slider-runnable-track {
      height: 3px; border-radius: 2px;
      background: linear-gradient(90deg, var(--gold-ink) 0 var(--p, 25%), rgba(244,213,141,0.16) var(--p, 25%) 100%);
      box-shadow: inset 0 1px 2px rgba(0,0,0,0.6);
    }
    .lr-slrm-range::-webkit-slider-thumb {
      -webkit-appearance: none; appearance: none; border: 0;
      width: 12px; height: 12px; margin-top: -4.5px; border-radius: 50%;
      /* --gold-ink, not --gold-soft: outside .ag-stack the app's token bridge is not in scope
         and --gold-soft is the sheet's own rgba(201,168,76,0.18), which would put a nearly
         transparent dot on the track */
      background: radial-gradient(circle at 40% 35%, #fff, var(--gold-ink) 72%);
      box-shadow: 0 0 7px rgba(244,213,141,0.55), 0 1px 3px rgba(0,0,0,0.55);
    }
    .lr-slrm-range::-moz-range-track    { height: 3px; border-radius: 2px; background: rgba(244,213,141,0.16); box-shadow: inset 0 1px 2px rgba(0,0,0,0.6); }
    .lr-slrm-range::-moz-range-progress { height: 3px; border-radius: 2px; background: var(--gold-ink); }
    .lr-slrm-range::-moz-range-thumb {
      width: 12px; height: 12px; border: 0; border-radius: 50%;
      background: radial-gradient(circle at 40% 35%, #fff, var(--gold-ink) 72%);
      box-shadow: 0 0 7px rgba(244,213,141,0.55), 0 1px 3px rgba(0,0,0,0.55);
    }
    /* The keyboard ring goes round the control group, not round the input's own box. That box is
       the hit area (36px, 44px under 700px), not the 16px band the control draws in, so a ring on
       the input at 4px offset ran a gold line straight through the SEARCH RADIUS / 10 m line above
       it and through the 5 10 15 25 40 line below it — MEASURED at 1440x900: ring top 427.9 against
       a label box of 423.2–434.9, ring bottom 471.9 against a stops band of 464.9–475.9.
       `.lr-slrm-inner` is the label, the track and the stops as one object, which is what the focus
       is actually on. An element's own outline is not clipped by its own `overflow: hidden`, so the
       inner still draws it; at 4px it clears the key's line above by 9px (the row's 13px margin). */
    .lr-slrm-range:focus-visible { outline: none; }
    .lr-slrm-ctl:has(.lr-slrm-range:focus-visible) .lr-slrm-inner {
      outline: 1px solid var(--gold-ink); outline-offset: 4px; border-radius: 4px;
    }

    /* The five stops are the tick marks and the scale at once: the radius in metres under the
       position it sits at. Absolutely placed at 0/25/50/75/100% of the thumb's own travel — the
       track is inset by half a thumb at each end, which is what the 6px margin measures — so a
       number stands under the dot and not beside it. The live one takes the gold; the output
       above says the same number in words, so the state never rests on colour alone. */
    /* `pointer-events: none`: the row is aria-hidden and does nothing on click, and being
       positioned it painted over the bottom of the range's hit padding. MEASURED at 390x844,
       `elementFromPoint` down the range's own box returned the INPUT for only 35px of its 44. */
    .lr-slrm-stops { position: relative; pointer-events: none; height: 11px; margin: 0 6px; }
    .lr-slrm-stops span {
      position: absolute; top: 0; transform: translateX(-50%);
      font: 500 9.5px/1 var(--font-mono); letter-spacing: 0.06em; color: var(--t2);
      transition: color 0.22s var(--ease-out);
    }
    .lr-slrm-stops span:nth-child(1) { left: 0; }
    .lr-slrm-stops span:nth-child(2) { left: 25%; }
    .lr-slrm-stops span:nth-child(3) { left: 50%; }
    .lr-slrm-stops span:nth-child(4) { left: 75%; }
    .lr-slrm-stops span:nth-child(5) { left: 100%; }
    .lr-slrm-stops[data-i="0"] span:nth-child(1),
    .lr-slrm-stops[data-i="1"] span:nth-child(2),
    .lr-slrm-stops[data-i="2"] span:nth-child(3),
    .lr-slrm-stops[data-i="3"] span:nth-child(4),
    .lr-slrm-stops[data-i="4"] span:nth-child(5) { color: var(--gold-ink); }

    /* THE SWITCH under the keys: one binary, named, with its state in words as
       well as in light, so the state reads in greyscale and at a glance. */
    .lr-switch {
      position: relative; display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 12px;
      width: 100%; margin: 5px 0 0; padding: 13px 15px 15px 21px;   /* the extra margin above sets it apart from the five readings: it is a setting of the panel's, not another reading. The padding measures the row at 45px tall — at 12/14 it came out at 43, under the 44px touch minimum. */
      border-radius: var(--r-key-sm); text-align: left; cursor: pointer;
    }
    .lr-switch-name { font: 500 14px/1.2 var(--font-body); color: var(--t1-soft); transition: color 0.22s var(--ease-out); }
    .lr-switch.is-on .lr-switch-name { color: var(--gold-ink); }
    .lr-switch-state { flex: none; font: 500 10px/1 var(--font-mono); letter-spacing: 0.16em; text-transform: uppercase; color: rgba(237,233,226,0.55); transition: color 0.22s var(--ease-out); }
    .lr-switch.is-on .lr-switch-state { color: var(--gold-ink); }
    /* The locked note: why this reading settles the photograph itself. It takes the whole
       second line of the switch — pinned into the right-hand column beside the state word it
       broke into three ragged lines at 380 px (measured at 3440) — so the switch reads the way
       a key does: the name, then one quiet line under it. Caps mono at the panel's faintest
       ink, so it never competes with the name or with the state word. */
    .lr-switch-note { flex: 0 0 100%; order: 3; min-height: 12px; margin-top: -3px; font: 500 9px/1.3 var(--font-mono); letter-spacing: 0.14em; text-transform: uppercase; color: var(--t2); }

    /* The filament, stood on its end: the groove and its ember run down the left
       edge, so the live key is marked by a rule and not by a border or a tick.
       The groove is shallower than the horizontal one in _shared.css on purpose —
       four unlit grooves stacked in a column read as four ruled left borders at
       that depth, which is the same failure the shared rule's own note warns of. */
    .lr-key .filament, .lr-switch .filament {
      left: 9px; right: auto; top: 13px; bottom: 13px; width: 2.5px; height: auto;
      background: rgba(3,5,11,0.3);
      box-shadow: inset 1px 0 2px rgba(0,0,0,0.55), 1px 0 0 rgba(255,255,255,0.03);
    }
    .lr-key .ember, .lr-switch .ember {
      background: linear-gradient(180deg, rgba(201,168,76,0.5), var(--gold-ink) 55%, var(--gold-ink));
      transform: scaleY(0.05); transform-origin: center top;
    }
    .lr-key:not(.is-on):hover .ember, .lr-switch:not(.is-on):hover .ember { opacity: 0.26; transform: scaleY(0.18); }
    .lr-key.is-on .ember, .lr-switch.is-on .ember { opacity: 1; transform: scaleY(1); }
    .lr-key:disabled, .lr-switch:disabled { cursor: default; opacity: 0.45; }
    /* Locked: the two readings that settle the photograph themselves — the LiDAR/satellite
       blend carries it inside its texture, the multidirectional hillshade uses none. Dimmed
       and un-pressable, but still focusable and still announced; the note inside the button
       says why, so the dim never reads as a fault. `pointer-events: none` is what makes it
       inert to the hand — it takes the hover lift, the press sink and the ember's catch with
       it in one declaration, rather than fighting `.key:hover` and `.key:active` in _shared.css
       selector by selector. The button stays in the tab order and stays announced; the module
       returns early on the click a keyboard press still fires.
       The dimming is per part, not a blanket opacity on the button. MEASURED off the GPU capture,
       a 0.55 opacity over the whole switch multiplied the note's own alpha and put the one line
       that says WHY the switch is inert at 1.90:1 on the panel ground, at 9px — under the name it
       sits beneath, which reads 4.62:1. The locked switch then reads as broken rather than as
       locked. Each part is dimmed on its own instead, and the note keeps the page's credit-line
       ink (--t2, the value the panel's own foot rule uses): measured 4.75:1 after. */
    .lr-switch[aria-disabled="true"] { cursor: default; pointer-events: none; }
    .lr-switch[aria-disabled="true"] .lr-switch-name  { color: rgba(244,213,141,0.62); }
    .lr-switch[aria-disabled="true"] .lr-switch-state { color: rgba(237,233,226,0.42); }
    .lr-switch[aria-disabled="true"] .ember           { opacity: 0.45; }

    .lr-foot { margin: auto 0 0; padding-top: 13px; border-top: 1px solid rgba(255,255,255,0.06); font: 400 12px/1.6 var(--font-body); color: var(--t2); }

    @media (max-width: 1100px) {
      .showcase-grid { grid-template-columns: 1fr; grid-template-rows: none; }
      .lr-panel { grid-column: 1; grid-row: auto; }   /* below the viewer and its readout */
      /* The panel is the stage's full width here, and the radius control stretched with it:
         MEASURED, the track ran 637px at 768 and 872px at 1024 against the 310px it is drawn at in
         the 380px column, which put 218px of thumb travel between two of five fixed stops and left
         the setting's name about 870px from the value it names. Capped at the width it was designed
         at, so the label, the track and the stops stay one object; the key's own text still runs
         the panel's full width, as the other four keys' text does. */
      .lr-slrm-inner { max-width: 340px; }
    }
    /* At 640px and below the light dial moves to the top-left of the frame (the
       §3 phone block), where the reading tag already sits: measured at 390x844,
       the tag covered the dial almost completely. Below that breakpoint and only
       there, the tag goes to the foot of the frame, which is empty. The number
       must track the dial's own breakpoint: at 768 the dial is still bottom-left,
       and a tag moved down at 900 landed on it (measured). */
    @media (max-width: 640px) {
      .showcase-frame .showcase-tag { top: auto; bottom: 12px; }
    }
    @media (max-width: 700px) {
      .lr-key { padding: 12px 14px 14px 19px; }
      .lr-key-name { font-size: 18px; }
      .lr-key-line { font-size: 12px; }
      .lr-switch { padding: 13px 14px 15px 19px; }   /* never below the 44px touch minimum */
      .lr-slrm-range { padding: 14px 0; margin: -14px 0; }   /* 44px to the thumb, and the row still laid out at 16 */
      /* and the 44 has to be its own: at the 7px the row is drawn with, 14px of hit padding reached
         7px up into the head row and 9px down under the stops. 15px of gap is the 14 plus a pixel
         of clearance at each end, so the whole 44 belongs to the range and nothing else. The row
         grows 53 -> 69px and the card 214 -> 230; that is what an honest 44px control costs. */
      .lr-slrm-inner { gap: 15px; }
      /* the card's padding is 12/14/14/19 here, so the button's reach is too */
      .lr-key--slrm .lr-key-main { margin: -12px -14px 0 -19px; padding: 12px 14px 0 19px; }
      .lr-key--slrm:not(.is-on) .lr-key-main { margin-bottom: -14px; padding-bottom: 14px; }
    }
    @media (prefers-reduced-motion: reduce) {
      .lr-key .ember, .lr-switch .ember { transition: none; }
      .lr-key.is-on .ember, .lr-switch.is-on .ember { opacity: 1; transform: scaleY(1); }
      /* the radius row still opens and shuts; it just does it at once */
      .lr-slrm-ctl, .lr-slrm-ctl.is-open { transition: none; }
      .lr-slrm-stops span { transition: none; }
    }
    .mc-radius { display: grid; grid-template-columns: 1fr; gap: 6px; padding: 10px 14px; border: 1px solid var(--rule); border-radius: 6px; }
    .mc-radius-label { font: 500 11px/1.4 var(--font-mono, "DM Mono", monospace); letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-dim, #9aa0ac); }
    .mc-radius-label b { color: var(--gold, #c9a84c); font-weight: 500; margin-left: 6px; }
    .mc-radius input[type="range"] { width: 100%; accent-color: var(--gold, #c9a84c); }
    .mc-radius-stops { display: flex; justify-content: space-between; font: 500 10px/1 var(--font-mono, "DM Mono", monospace); color: var(--ink-dim, #9aa0ac); letter-spacing: 0.04em; }
    @media (max-width: 760px) { .showcase-tools { grid-template-columns: 1fr; } }
    .mc-key {
      position: relative; padding: 11px 10px 16px;
      text-align: left; cursor: pointer;
      font: 500 12.5px/1.2 var(--font-body);
      min-width: 0;
    }
    .mc-key small { display: block; margin-top: 4px; font: 400 10px/1.3 var(--font-body); color: var(--t3); white-space: normal; }
    .mc-key.is-on small { color: var(--gold-dim); }
    .showcase-readout { padding: 14px 18px; }
    .showcase-readout p { margin: 0; font: 400 14px/1.6 var(--font-body); color: var(--t1-soft); min-height: 2.4em; }
    @media (max-width: 1100px) { .showcase-keys { grid-template-columns: repeat(5, minmax(0, 1fr)); } }
    @media (max-width: 700px)  { .showcase-keys { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 6px; padding: 8px; } .mc-key { padding: 9px 8px 14px; font-size: 11.5px; } .showcase-frame { aspect-ratio: 4 / 3; max-height: none; } }

/* PARTNERS, two cards: what is built and what is being built. The status word
   sits above the title in the eyebrow style so the two read as a pair. */
.partner-notes--two { grid-template-columns: repeat(2, minmax(0, 1fr)); max-width: 1000px; }
.research-note p.partner-status { margin: 0 0 8px; font: 500 11px/1 var(--font-mono, "DM Mono", monospace); letter-spacing: .16em; text-transform: uppercase; color: var(--gold, #C9A84C); }
.research-note p.partner-status + h3 { margin-top: 0; }
@media (max-width: 900px) { .partner-notes--two { grid-template-columns: 1fr; } }

/* ════════════════════════════════════════════════════════════════════════
   §11 RESEARCH — the rebuilt section.
   The page's own parts do the work: .sec / .wrap--wide / .section-head /
   .eyebrow .num / .gold-rule / .display--sm / .lede / .rpart / .research-fig /
   .research-note / .well / .key + .filament. What is new here is only what
   the section actually needed: a paper mount for the facsimile plates, the
   decoder window and its card, the six face cards, and the provenance steps.
   Nothing in here reveals on scroll except the text, which carries
   [data-reveal] in the markup; figures and the map frame never do.
   ════════════════════════════════════════════════════════════════════════ */

/* Paragraph runs inside a research part ---------------------------------- */
.rpart-text p + p { margin-top: 1.05em; }
.rpart-text--after { margin-top: var(--sp-md); }

/* A named block inside a part (the two interactive pieces, and the strip) - */
.rblock { margin-top: var(--sp-xl); }
.rblock-title {
  margin: 0 0 12px;
  font-family: var(--font-display); font-weight: 400;
  /* MEASURED: at 30 px a block title sat 4 px under the 34 px part title, so
     "Read a sheet." read as a peer of "A semantic gazetteer…" rather than a
     block inside it. 24 px is the page's own sub-head size (#partners). */
  font-size: clamp(20px, 1.7vw, 24px); line-height: 1.16; color: var(--t1);
}
.rblock-lede { margin: 0 0 22px; max-width: 640px; font: 400 14.5px/1.7 var(--font-body); color: var(--t1-soft); }
.research .research-foot, .research .rpg-caption { max-width: 640px; }
.research > .section-head .lede { max-width: 720px; }

/* Figure variants. MEASURED: every plate here is a photograph of a page at its
   own native pixel size, so no figure is ever given more width than its file
   has — an upscaled facsimile softens the engraved type. The shortfall against
   the 1208 px track is taken as a symmetrical inset instead of a ragged right
   edge. Widths are native + 2 px border (+ 28 px paper padding on a plate). */
.research-fig--mid { max-width: 1102px; margin-inline: auto; }
.research-fig--ant { max-width: 1030px; margin-inline: auto; }
/* a facsimile is a photograph of a page: it is mounted on paper, not floated
   on the dark ground, or its own white edge reads as a blown-out image */
.research-fig--plate img {
  padding: 12px 14px;
  background: #EFE7D3;
  border-color: rgba(201, 168, 76, 0.30);
  box-shadow: 0 16px 40px rgba(0, 0, 0, 0.45);
}

/* From the work — three plates, one under the other. MEASURED: laid two
   across, the Old English strip (1133 px of engraved names) came down to a
   590 px column and Crawford's page of 1921 text to 5 px type. Each plate is
   given its own native width instead, so every word on it can be read. */
.rwork { display: grid; grid-template-columns: minmax(0, 1fr); gap: 26px; max-width: 1163px; margin-inline: auto; }
/* The two label strips are ONE plate of two rows: laid out as two separate
   panels they came out at different heights with a band of empty cream beside
   the shorter one. 1163 = the 1133 px strip + 28 px paper + 2 px border, so
   the strip prints 1:1. */
.rwork-plate {
  display: grid; gap: 16px; justify-items: center;
  padding: 12px 14px;
  background: #EFE7D3;
  border: 1px solid rgba(201, 168, 76, 0.30);
  border-radius: var(--r-md);
  box-shadow: 0 16px 40px rgba(0, 0, 0, 0.45);
}
.rwork-plate img { display: block; width: auto; max-width: 100%; height: auto; border: 0; border-radius: 0; background: none; box-shadow: none; }
.rwork-crawford { max-width: 1130px; margin-inline: auto; }
.rwork-row {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  gap: 14px 18px;
}
.rwork-row img { width: auto; max-width: 100%; height: auto; border: 0; border-radius: 0; background: none; box-shadow: none; }
.rwork-note { margin-top: var(--sp-md); max-width: 860px; }

/* ──────────────── Read a sheet — the decoder window ──────────────── */
.rdec-keys { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; }
.rdec-key {
  position: relative;
  padding: 11px 18px 15px;
  font: 500 12.5px/1 var(--font-body);
  cursor: pointer;
}
.rdec-hint { margin: 0 0 16px; max-width: 640px; font: 400 12.5px/1.6 var(--font-body); color: var(--t2); }

.rdec-figure { margin-top: 0; }
/* MEASURED TRAP, three times over. .research-fig img sets display:block,
   .rdec-row display:flex and .rdec-keys display:flex, and an author display
   rule beats the user agent's [hidden] { display:none } — so the offline still
   painted over the live map, the empty Period row showed on every label that
   has no period, and the two keys stayed up in the no-WebGL state. One rule at
   (0,2,0) puts the attribute back in charge across the whole section. */
.research [hidden] { display: none; }
.rdec-stage { position: relative; }
.rdec-frame {
  position: relative; overflow: hidden;
  aspect-ratio: 16 / 9; min-height: 420px;
  border-radius: var(--r-md); border: 1px solid var(--rule-strong);
  background: #0B0D12;
  box-shadow: 0 26px 70px rgba(0, 0, 0, 0.5);
}
.rdec-map { position: absolute; inset: 0; }
.rdec-still { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; border: 0; border-radius: 0; box-shadow: none; }

/* MapLibre's own chrome, brought onto the dark ground */
.rdec-frame .maplibregl-ctrl-group { background: rgba(17, 20, 28, 0.9); border: 1px solid var(--rule-strong); box-shadow: 0 4px 14px rgba(0, 0, 0, 0.5); }
.rdec-frame .maplibregl-ctrl-group button + button { border-top: 1px solid var(--rule); }
.rdec-frame .maplibregl-ctrl button .maplibregl-ctrl-icon { filter: invert(0.82) sepia(0.28); }
.rdec-frame .maplibregl-ctrl-attrib { background: rgba(11, 13, 18, 0.78); border-radius: 6px 0 0 0; }
.rdec-frame .maplibregl-ctrl-attrib, .rdec-frame .maplibregl-ctrl-attrib a { color: var(--t2); font: 400 10px/1.5 var(--font-body); }
.rdec-frame .maplibregl-ctrl-attrib a { text-decoration: none; }

/* the markers. MapLibre rewrites the root element's transform on every frame,
   so nothing that moves may live on .rdec-mk itself — only on the inner span. */
.rdec-mk { width: 22px; height: 22px; cursor: pointer; color: var(--gold); }
.rdec-mk:focus-visible { outline: 1px solid var(--gold-ink); outline-offset: 4px; border-radius: 50%; }
.rdec-mk-in {
  position: relative; display: block; width: 100%; height: 100%;
  transform-origin: center; transition: transform 0.16s var(--ease-out);
  filter: drop-shadow(0 1px 1.5px rgba(11, 13, 18, 0.9)) drop-shadow(0 0 1px rgba(11, 13, 18, 0.9));
}
.rdec-mk-in svg { display: block; position: relative; z-index: 2; }
.rdec-mk:hover .rdec-mk-in, .rdec-mk.is-active .rdec-mk-in { transform: scale(1.3); }
.rdec-mk-pulse {
  position: absolute; left: 1px; top: 1px; right: 1px; bottom: 1px;
  border-radius: 50%; border: 1.5px solid currentColor;
  opacity: 0; pointer-events: none; z-index: 1;
}
.rdec-mk.is-cue .rdec-mk-pulse { animation: rdecPulse 1.4s var(--ease-out) 1; }
@keyframes rdecPulse { from { opacity: 0.75; transform: scale(0.5); } to { opacity: 0; transform: scale(2.8); } }

/* the reading card — inside the frame's right on a wide screen */
.rdec-card {
  position: absolute; z-index: 4; top: 18px; right: 18px;
  width: 336px; max-height: calc(100% - 36px); overflow-y: auto;
  border-radius: var(--r-card); overflow-x: hidden;
  border: 1px solid var(--rule-strong);
  background: linear-gradient(180deg, rgba(18, 21, 30, 0.975), rgba(10, 13, 20, 0.975));
  backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05), 0 24px 64px rgba(0, 0, 0, 0.62);
  opacity: 0; visibility: hidden; transform: translateY(-6px);
  transition: opacity 0.28s var(--ease-out), transform 0.28s var(--ease-out), visibility 0s linear 0.28s;
}
.rdec-card.is-open { opacity: 1; visibility: visible; transform: none; transition-delay: 0s; }
.rdec-card-x {
  position: absolute; top: 9px; right: 9px; z-index: 3;
  width: 26px; height: 26px; border-radius: 50%; border: 1px solid var(--rule-strong);
  background: rgba(11, 13, 18, 0.6); color: var(--t2);
  font: 400 15px/1 var(--font-body); cursor: pointer;
}
.rdec-card-x:hover { color: var(--t1); border-color: var(--gold-dim); }
.rdec-card-x:focus-visible { outline: 1px solid var(--gold-ink); outline-offset: 2px; }
.rdec-crop { display: flex; align-items: center; justify-content: center; min-height: 96px; padding: 10px 12px; background: #EFE7D3; }
.rdec-crop img { max-width: 100%; width: auto; height: auto; }
.rdec-cbody { padding: 15px 17px 17px; }
.rdec-tag {
  display: inline-flex; align-items: center; gap: 7px;
  font: 500 10px/1 var(--font-mono); letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--gold); border: 1px solid var(--rule-strong); border-radius: 20px; padding: 4px 10px;
}
.rdec-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--gold); }
.rdec-read { margin: 12px 0 3px; font-size: 40px; line-height: 1.06; color: #F2EFE6; word-break: break-word; }
.rdec-ocr { margin: 0 0 10px; font: 400 11.5px/1.5 var(--font-mono); color: var(--t2); }
.rdec-ocr s { color: #C08A55; text-decoration-thickness: 1px; }
.rdec-row { display: flex; gap: 10px; margin: 7px 0; font: 400 13px/1.5 var(--font-body); color: var(--t1-soft); }
.rdec-row b { flex: 0 0 64px; padding-top: 2px; font: 400 9.5px/1.5 var(--font-mono); letter-spacing: 0.08em; text-transform: uppercase; color: var(--t2); }
.rdec-fac:not(:empty) { margin-top: 12px; padding: 7px 9px 5px; background: #EFE7D3; border-radius: var(--r-sm); }
.rdec-fac img { display: block; width: 100%; height: auto; }
.rdec-fac span { display: block; margin-top: 5px; font: 400 8.5px/1.4 var(--font-mono); letter-spacing: 0.03em; text-transform: uppercase; color: #8A7C55; }
.rdec-note { margin: 12px 0 0; padding-top: 10px; border-top: 1px solid var(--rule); font: 400 12.5px/1.6 var(--font-body); font-style: italic; color: var(--t2); }
.rdec-note:empty { display: none; }

/* the same thirty-nine readings as text, when there is no map to put them on */
.rdec-list { margin-top: 16px; }
.rdec-list-head { margin: 0 0 12px; font: 400 13px/1.6 var(--font-body); color: var(--t1-soft); }
.rdec-list ul { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 12px 24px; }
.rdec-list li { padding-top: 9px; border-top: 1px solid var(--rule); }
.rdec-list li b { display: block; font: 400 19px/1.2 var(--font-display); color: var(--t1); }
.rdec-list li span { display: block; margin-top: 3px; font: 400 11px/1.5 var(--font-body); color: var(--t2); }

@media (max-width: 900px) {
  /* MEASURED: without a definite width, aspect-ratio 4/3 + min-height 300 gives
     the box a transferred minimum WIDTH of 400 px. In a 350 px column on a
     390 px phone that widened the layout viewport to 421 and made the whole
     page scroll sideways. width:100% makes the ratio derive the height. */
  .rdec-frame { width: 100%; aspect-ratio: 4 / 3; min-height: 300px; }
  .rdec-card {
    position: static; width: auto; max-height: none; margin-top: 14px;
    display: none; opacity: 1; visibility: visible; transform: none; transition: none;
    backdrop-filter: none; -webkit-backdrop-filter: none;
  }
  .rdec-card.is-open { display: block; }
  .rdec-read { font-size: 34px; }
}

/* ──────────────── Set a name as the survey would ──────────────── */
.rpg-input-row { display: flex; flex-direction: column; gap: 9px; max-width: 420px; margin-bottom: 26px; }
.rpg-label { font: 600 10.5px/1 var(--font-body); letter-spacing: var(--tr-label); text-transform: uppercase; color: var(--gold-dim); }
.rpg-input {
  width: 100%; padding: 14px 16px; border: 0;
  font: 400 17px/1.3 var(--font-body); color: var(--t1);
  caret-color: var(--gold-ink);
}
.rpg-input:focus { outline: 1px solid var(--gold-ink); outline-offset: 2px; }

.rpg-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 18px; }
.rpg-face {
  padding: 18px 18px 20px;
  border: 1px solid var(--rule); border-radius: var(--r-card);
  background: linear-gradient(180deg, rgba(17, 20, 28, 0.9), rgba(8, 11, 18, 0.9));
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.035), 0 14px 38px rgba(0, 0, 0, 0.34);
}
.rpg-face--lead { border-color: rgba(201, 168, 76, 0.28); }
.rpg-well { display: flex; align-items: center; min-height: 104px; padding: 14px 16px; overflow: hidden; }
.rpg-sample {
  display: block; width: 100%;
  font-family: var(--font-display); font-weight: 400;
  font-size: clamp(26px, 2.6vw, 38px); line-height: 1.14;
  color: #EDE9DE; word-break: break-word;
}
/* the six faces are only switched on when the section comes near; until then
   the sample is the same word in the page's own display face, and the browser
   has no reason to fetch a woff2 six screens ahead of the reader */
.rpg.is-lettered .rpg-sample { font-family: var(--rpg-face), 'Cormorant Garamond', serif; }
.rpg-face--caps .rpg-sample { text-transform: uppercase; }
.rpg-name { margin: 14px 0 0; font: 500 10.5px/1 var(--font-mono); letter-spacing: 0.09em; text-transform: uppercase; color: var(--gold); }
.rpg-face--lead .rpg-name { color: var(--gold-ink); }
.rpg-means { margin: 7px 0 0; font: 400 12.5px/1.55 var(--font-body); color: var(--t2); }
.rpg-face--lead .rpg-means { color: var(--t1-soft); }
.rpg-caption {
  margin: 16px 0 0; padding-top: 10px; border-top: 1px solid var(--rule);
  max-width: 860px; font: 400 11.5px/1.62 var(--font-body); color: var(--t2);
}
@media (max-width: 1040px) { .rpg-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 620px)  { .rpg-grid { grid-template-columns: 1fr; } .rpg-well { min-height: 88px; } }
/* MEASURED at 390 px: two columns put each sample in a 113 px well, and at
   22 px the widest of the six (MERRIVALE in Egyptian Capitals) sets 109 px on
   one line — so the grid comes down from 1,202 px to 630 px with nothing
   wrapping. At 375 and 360 the well is 106 and 98 px and that sample wraps, so
   those widths keep one column at the full 26 px. */
@media (min-width: 390px) and (max-width: 620px) {
  .rpg-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
  .rpg-face { padding: 14px 14px 16px; }
  .rpg-well { min-height: 74px; padding: 12px 13px; }
  .rpg-sample { font-size: 22px; }
  .rpg-means { font-size: 11.5px; }
}

/* ──────────────── How the archaeology got onto the map ──────────────── */
.rera { display: flex; flex-direction: column; gap: 13px; margin-top: var(--sp-lg); }
/* The era's eyebrow is a date, so it is set as data, in the mono face and not
   uppercased. MEASURED at 3440x1440: uppercased, "1840s to 1890s" prints
   "1840S TO 1890S", and at the eyebrow's 0.22em tracking the "0S" pair reads
   as "OS" — which in a section about the Ordnance Survey is a real misreading. */
.rera .eyebrow { font-family: var(--font-mono); font-weight: 500; font-size: 11px; letter-spacing: 0.12em; text-transform: none; }
.rera-title { margin: 0; font-family: var(--font-display); font-weight: 400; font-size: clamp(20px, 1.7vw, 24px); line-height: 1.16; color: var(--t1); }

.rsteps { list-style: none; margin: var(--sp-md) 0 0; padding: 0; }
/* One grid for the whole sequence: numeral, title and text in a left column
   capped to the body measure, one fixed 420 px figure column on the right, and
   every image in that column at the same width. MEASURED: with a 340 px column
   reserved on all eight steps, five of them stood empty, and at 768 the prose
   was squeezed to 323 px beside 340 px of nothing. A step with no figure drops
   the second track; its text column is capped to the same 640 px, so the left
   edge and the measure stay aligned down the sequence. */
.rstep {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 420px);
  gap: 22px 44px; align-items: start;
  margin-top: 26px; padding-top: 26px; border-top: 1px solid var(--rule);
}
.rstep--solo { grid-template-columns: minmax(0, 1fr); }
.rstep-n { margin-bottom: 11px; }
.rstep-n .num { margin-right: 0; font-size: 12px; letter-spacing: 0.1em; }
.rstep-title { margin: 0 0 9px; font-family: var(--font-display); font-weight: 400; font-size: clamp(20px, 1.8vw, 25px); line-height: 1.16; color: var(--t1); }
.rstep-text p { margin: 0; max-width: 640px; font: 400 14.5px/1.7 var(--font-body); color: var(--t1-soft); }
.rstep-fig { margin: 0; }
.rstep-fig figcaption { margin-top: 10px; padding-top: 9px; font-size: 11px; }
.rstep-fig--crop img, .rstep-fig--pin img { width: auto; max-width: 100%; margin: 0 auto; }
.rstep-fig--pin img { width: 88px; height: 88px; border: 0; background: none; box-shadow: none; }
/* MEASURED: the two facsimile plates in this sequence are 1000x115 and
   888x185 — pages of printed and handwritten matter. Put in the 340 px
   figure column they came out at about 5 px of type, which cannot be read.
   Those two steps drop their plate below the text at its own native width;
   the small crop and the pin stay in the column, where they are already at
   full size. */
.rstep--stack { grid-template-columns: minmax(0, 1fr); }
.rstep--stack .rstep-fig { max-width: 1000px; margin-inline: auto; }
.rstep--stack .rstep-fig figcaption { font-size: 11.5px; }
/* native width + 28 px paper padding + 2 px border, centred in the track */
.rstep--stack .rstep-fig--rows { max-width: 1030px; margin-inline: auto; }
.rstep--stack .rstep-fig--cover { max-width: 918px; margin-inline: auto; }
/* MEASURED: at 768 a step with a figure ran 323 px of prose beside a 340 px
   column while the stacked steps beside it ran 674. Stacking from 860 down
   keeps one measure for every step on a tablet. */
@media (max-width: 860px) {
  .rstep { grid-template-columns: 1fr; gap: 18px; }
  .rstep-fig { order: -1; }
}

/* MEASURED: a facsimile is a page of printed matter, and the widest of them is
   1,100 px of 1921 text. On a 390 px phone the column is 350 px, which put that
   page's type at about 4 px — a smudge, not a document. Under 760 px each plate
   keeps a legible width and its figure scrolls sideways; the caption is pinned
   to the left so it stays a readable column while the plate moves under it.
   The two small pictures in the provenance steps are already at native size. */
@media (max-width: 700px) {
  .research-fig--plate:not(.rstep-fig--crop):not(.rstep-fig--pin) { overflow-x: auto; overscroll-behavior-x: contain; }
  .research-fig--plate:not(.rstep-fig--crop):not(.rstep-fig--pin) > img { min-width: 660px; }
  .research-fig--plate > figcaption { position: sticky; left: 0; width: calc(100vw - 2 * clamp(20px, 4vw, 56px)); }
  /* the two-row label plate is one page of engraved names: it scrolls sideways
     at a legible width, like the other plates, with its caption pinned left */
  /* MEASURED: a grid item that carries a max-width stops stretching to its
     track and is sized fit-content instead, so the desktop plate caps (1030 /
     918 / 1130 px) made these figures take their 660 px min-content and the
     layout viewport widened to 680 on a 390 px phone. Cap them to the track. */
  .rwork-crawford, .rstep--stack .rstep-fig, .rstep-fig--rows, .rstep-fig--cover { max-width: 100%; }
  .rwork-fig { overflow-x: auto; overscroll-behavior-x: contain; }
  .rwork-plate { min-width: 690px; justify-items: start; }
  .rwork-fig > figcaption { position: sticky; left: 0; width: calc(100vw - 2 * clamp(20px, 4vw, 56px)); }
  .rwork-row { justify-content: flex-start; }
  .rwork-row img { flex: 0 0 auto; max-width: none; }
  /* The three positives are three pictures in one file. MEASURED at 390x844: in the 350 px
     column they came out 350x118, i.e. 116 px a monument, too small to read an earthwork or
     the 2 px boundary line. They take the plates' own treatment — a legible width that scrolls
     sideways, caption pinned left — at 690 px, which is 230 px a monument. */
  .research-fig--positives { overflow-x: auto; overscroll-behavior-x: contain; }
  .research-fig--positives > img { width: 690px; min-width: 690px; max-width: none; }
  .research-fig--positives > figcaption { position: sticky; left: 0; width: calc(100vw - 2 * clamp(20px, 4vw, 56px)); }
}

/* the closing line of the section */
.research-foot {
  margin-top: var(--sp-lg); padding-top: 18px; border-top: 1px solid var(--rule);
  max-width: 860px; font: 400 13.5px/1.7 var(--font-body); color: var(--t2);
}

/* ── §11 condensed, for the landing page only ─────────────────────────────
   Everything above this comment is the section itself, which now lives on
   research.html; both pages link this file, so none of it is removed. What
   the landing page carries instead is the head, three one-line items and the
   link. The items take the section's own numbering (the 01/02/03 the three
   parts carry on research.html) and the rule the closing line uses. */
/* MEASURED at 1440x900: with .sec's own 120 px top and bottom, a head, three
   lines and a link stood 792 px tall — most of it padding around 550 px of
   copy. At 72 px it is 696 px. research.html's section carries .rpage and so
   is excluded: there the full section keeps the 120 px it has always had. */
#research:not(.rpage) { padding-block: var(--sp-xl); }
.research-brief {
  list-style: none; margin: 0; padding: 0;
  max-width: 860px; display: grid; gap: 12px;
}
.research-brief li {
  display: grid; grid-template-columns: 34px minmax(0, 1fr); align-items: baseline;
  padding-top: 12px; border-top: 1px solid var(--rule);
  font: 400 15px/1.6 var(--font-body); color: var(--t1-soft);
}
.research-brief .num {
  font: 500 11px/1 var(--font-mono); letter-spacing: 0.1em; color: var(--gold);
}
.research-brief-cta { margin-top: var(--sp-lg); }
@media (max-width: 560px) {
  .research-brief li { grid-template-columns: 28px minmax(0, 1fr); font-size: 14.5px; }
}

/* ── §11 teaser plates, for the landing page only ─────────────────────────
   Three pictures cut out of assets/research/ and set beside the copy, so the
   block has something to look at: two of the three greyscale LiDAR frames
   that lead research.html's detector section, the 1881 OS 404 antiquities
   rows, and three engraved names off the six-inch sheets. All three are
   paper-and-grey with the page's own gold in them, and all three are on the
   page they link to. Each plate is a link to research.html. The card is the page's own
   card — the .rpg-face recipe: 1 px --rule border, --r-card corner, the same
   gradient fill, inset top highlight and drop shadow — so these read as the
   cards the rest of the page uses.

   The section becomes a grid at every width. Below 1000 px it is one column
   and `order` puts the plates between the lede and the numbered list; the
   three plates then sit in a row that scrolls sideways, because the 1881
   table stops being readable under about 270 px wide (MEASURED: the crop was
   rendered at 306, 270 and 240 px and looked at; 240 fails — the re-cut crop
   was looked at again at 310 and 270 and holds). At 1000 px and
   up the copy takes a column capped at 640 px on the left and the plates a
   344 px column on the right.

   MEASURED heights of #research, the section's own box, against the brief's
   caps of 900 px at 1440x900 and 760 px at 3440x1440: 696 and 698 before the
   plates went in, 736 and 739 with them — and still 736 and 739 after the
   first plate was re-cut, because the plate column is the shorter of the two
   (577 px against the copy's) and the section's height is set by the copy,
   not by the pictures. The pictures render 310 px wide — 310x155, 310x107 and
   310x62 — against 650 and 660 px files, so 2.1x. */
#research:not(.rpage) {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  align-items: start;
}
#research:not(.rpage) > .research-head      { order: 1; }
#research:not(.rpage) > .research-plates    { order: 2; }
#research:not(.rpage) > .research-brief     { order: 3; }
#research:not(.rpage) > .research-brief-cta { order: 4; justify-self: start; }

.research-plate-row { display: grid; gap: 12px; }
/* a column, caption under picture. The caption's margin-top:auto is a belt on a
   card that is ever taller than its content; since the sideways row was set to
   align-items:start no card is, so it never fires in either layout. */
.research-plate {
  display: flex; flex-direction: column;
  padding: 12px 12px 11px;
  border: 1px solid var(--rule);
  border-radius: var(--r-card);
  background: linear-gradient(180deg, rgba(17, 20, 28, 0.9), rgba(8, 11, 18, 0.9));
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.035), 0 14px 38px rgba(0, 0, 0, 0.34);
  text-decoration: none; color: inherit;
  transition:
    border-color var(--dur-fast) var(--ease-out),
    box-shadow   var(--dur-fast) var(--ease-out),
    transform    var(--dur-fast) var(--ease-out);
}
/* the picture is the card's whole width; --raised under it so a slow image
   leaves a card-shaped hole rather than a gap in the stack */
.research-plate img {
  display: block; width: 100%; height: auto;
  border-radius: 4px;
  background: var(--raised);
}
.research-plate-cap {
  display: block; margin-top: auto; padding-top: 8px;
  font: 500 10px/1.3 var(--font-mono);
  letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--gold);
}
.research-plate:hover {
  border-color: rgba(201, 168, 76, 0.32);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05), 0 18px 44px rgba(0, 0, 0, 0.42);
  transform: translateY(-2px);
}
.research-plate:focus-visible {
  outline: 2px solid var(--gold-ink); outline-offset: 3px;
  border-color: rgba(201, 168, 76, 0.45);
}
.research-plates-credit {
  margin: 12px 0 0;
  font: 400 10.5px/1.5 var(--font-body); color: var(--t2);
}
@media (prefers-reduced-motion: reduce) {
  .research-plate { transition: none; }
  .research-plate:hover { transform: none; }
}

/* one column: the plates ride in a row that scrolls sideways, sized so the
   next one peeks and the row reads as scrollable without a control */
@media (max-width: 999px) {
  #research:not(.rpage) > .research-plates { margin-bottom: var(--sp-lg); }
  .research-plate-row {
    grid-auto-flow: column;
    /* each card is its own content's height. MEASURED at 390, the default `stretch` made all
       three cards 230 px tall against images of 184 / 88 / 55 px, so the two flatter plates
       showed 105 and 135 px of bare dark card under the picture with the caption alone at the
       foot — a card whose image had failed to load, not a plate. With `start` they measure
       230 / 134 / 101, and #research does not change height. */
    align-items: start;
    /* 86% of the row leaves the next card showing by about 50 px at 390, so
       the row reads as scrollable; the 324 px ceiling keeps the picture at
       298 px on a tablet, above the 270 px at which the 1881 table stops
       being readable. */
    grid-auto-columns: min(324px, 86%);
    overflow-x: auto; overscroll-behavior-x: contain;
    scroll-snap-type: x proximity;
    padding-bottom: 8px;
  }
  .research-plate { scroll-snap-align: start; }
}

/* two columns */
@media (min-width: 1000px) {
  #research:not(.rpage) {
    grid-template-columns: minmax(0, 1fr) 336px;
    column-gap: clamp(36px, 4vw, 72px);
  }
  #research:not(.rpage) > .research-head,
  #research:not(.rpage) > .research-brief,
  #research:not(.rpage) > .research-brief-cta { grid-column: 1; max-width: 640px; }
  #research:not(.rpage) > .research-head .lede { max-width: 640px; }
  #research:not(.rpage) > .research-plates { grid-column: 2; grid-row: 1 / span 3; }
  /* MEASURED: the copy set to 640 px runs two lines longer than it did at 860,
     and at the page's 40 px rhythm the section stood 758 px at 3440x1440 —
     2 px under the cap, which is not headroom. 32 px between head, list and
     link brings it to 739 and keeps the two columns ending level. */
  #research:not(.rpage) > .research-head      { margin-bottom: 32px; }
  #research:not(.rpage) > .research-brief-cta { margin-top: 32px; }
}

@media (prefers-reduced-motion: reduce) {
  .rdec-mk-in { transition: none; }
  .rdec-mk.is-cue .rdec-mk-pulse { animation: none; }
  .rdec-card { transition: none; }
}

/* the light dial takes the finger, not the page's scroll, while it is being dragged */
.ag-lightdial { touch-action: none; }


/* ══════════════════════════════════════════════════════════════════════════════
   §P — THE PHONE PASS                                        15 September 2026

   Everything below this line carries a width ceiling. The breakpoints used are
   `max-width: 1023px` (phone and tablet; the live field stage and the 3D viewer
   both switch at 1024 px, so this is the same line the page already draws),
   `max-width: 640px` (phone) and, for the coarse-pointer clauses,
   `max-width: 1366px` — an iPad Pro in landscape is exactly 1366, and no
   display 1440 px or wider is reached whatever it is pointed with. The short-
   viewport block is `(max-height: 560px) and (max-width: 1023px)`.
   The ceilings were put on after the fact and for cause: measured with a touch
   context at 3440x1440, the bare `(hover: none) and (pointer: coarse)` clauses
   fired on a desktop-sized display, and a bare `(max-height: 560px)` fired on
   any 1600x520 or 3440x520 browser window.
   Two clauses keep no ceiling, both flagged where they stand: `.ag-overlay`'s
   `touch-action: pan-y` (paints nothing; undoes a scroll trap for any finger)
   and the decoder's hover-versus-tap instruction (a device that reports
   `hover: none` cannot hover at any width).

   The measurements quoted are from 390x844 (iPhone 13, dpr 2) on the review
   server unless another size is named. Before this pass the page stood at
   29,948 px — 35.5 screens — with 11.3 MB at load, 21 tap targets under 40 px
   and 63 text runs over 20 characters under 13 px.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── P1 · RHYTHM ───────────────────────────────────────────────────────────
   Every section carried desktop's 120 px top and bottom padding unchanged, so
   a quarter of a screen of black separated every block: 2,944 px of pure
   padding, three and a half screens of the page. --sp-xxl is used for exactly
   that and for the footer's top margin and nothing else (16 call sites, all
   section padding), so the token is what moves, not sixteen rules.
   MEASURED at 390: 252 css px of unbroken black between "OPEN BETA · MOBILE
   APP COMING" and the next eyebrow. */
@media (max-width: 640px) {
  :root { --sp-xxl: 64px; }
  .invite { padding-bottom: 88px; }
  .research { padding-block: 48px; }
}

/* ── P2 · THE METADATA LAYER, MADE READABLE ────────────────────────────────
   123 text runs rendered below 12 px and 47 of them at or below 10.5 px, and
   three separate label styles failed AA on the page's own ground. Measured
   contrasts on #0B0D12: --t3 #363B48 = 1.74:1, --gold-dim #7A6228 = 3.34:1.
   The tokens are load-bearing on desktop, so they are not touched: these are
   phone-only overrides of the classes that use them.
   #A98A3C measures 5.1:1 and still reads as the dim gold rather than the
   bright --gold #C9A84C, which the page reserves for live state.
   #7A8090 (--t2) measures 4.92:1 and is already the site's own secondary. */
/* …or any touch device up to tablet size: a phone held sideways is 844x390 and
   a tablet in portrait is 768x1024, both of which sail past a 760 px gate while
   still being a thumb on glass. A mouse reports hover:hover and pointer:fine,
   so no mouse-driven desktop can match the second clause — but a touch-primary
   display can, at any size, and MEASURED at 3440x1440 with a touch context the
   clause fired and took four of the five reading keys' prose, the eyebrows, the
   buttons and the whole nav bar with it. 1366 px is the ceiling: it holds every
   case named above, an iPad Pro in landscape included, and leaves every display
   1440 px and wider on the desktop layout whatever it is pointed with. */
@media (max-width: 760px), (max-width: 1366px) and (hover: none) and (pointer: coarse) {
  /* 3.34:1 → 5.1:1 */
  .eyebrow,
  .also h3,
  .library-caption,
  .walkg-list li i,
  .rpart-head .eyebrow,
  .rera .eyebrow,
  .rpg-label { color: #A98A3C; }

  /* 1.74:1 → 4.92:1. The roadmap's dimming of a stage it has passed is carried
     by the dot and by the gold-versus-grey title, not by a label nobody can
     read. */
  .road-stop:not(.is-now) .road-when,
  .hero-eyebrow .mono,
  .premise-cta-meta { color: var(--t2); }

  /* the 12 px floor: short labels and captions, each on its own line, so the
     extra leading costs a few hundred px across a 29,948 px page */
  .eyebrow,
  .road-when,
  .showcase-tag,
  .ag-caption span,
  .field-count-label,
  .research-plate-cap,
  .hero-cue span,
  .premise-cta-meta,
  .counter-label,
  .library-tag,
  .np-eyebrow,
  .tp-rd-eyebrow,
  .tp-rd-cat,
  .rpg-label,
  .rpg-name,
  .rdec-row b,
  #rdec-cls,
  .lr-slrm-label,
  .lr-slrm-stops span,
  .walkg-list li i,
  .walkg-list-head span,
  #walkg-count,
  .partner-status,
  .hero-eyebrow .mono { font-size: 12px; }

  /* THREE RUNS THE LIST ABOVE DID NOT REACH, measured identically at 390x844,
     375x812 and 430x932.
     · `.walkg-stats i` (9 px) — DISTANCE / TIME / GRADE / CLIMB, the smallest
       CSS type left on the page. At 12 px the stock 0.16em tracking pushes the
       four cells past 350 px, so the tracking comes back to 0.12em.
     · `.np-sources summary` (11 px) — a disclosure the reader is meant to open.
     · `.partner-status` is in the list above but never won: `.research-note
       p.partner-status` (0-2-1) outranks a bare class, so it is restated here
       at matching specificity. */
  .walkg-stats i { font-size: 12px; letter-spacing: 0.12em; }
  .np-sources summary { font-size: 12px; }
  .research-note p.partner-status { font-size: 12px; }

  .tp-co-meta,
  .tp-co-dist,
  .tp-chat-tag,
  .tp-rd-chip,
  .np-stat,
  .np-helpful-label,
  .np-explore-label,
  .field-caption span,
  .research-plates-credit,
  .roman-credit,
  .library-caption,
  .tp-chat-disc,
  .tp-source-sub,
  /* the reader mock's own citation link. It reads 11 px wherever the reader
     column is on screen — MEASURED at 768x1024 and 844x390, three runs, and on
     a phone too the moment the WAYSTONE key is pressed. It is the line that
     carries the source the whole section is arguing about. */
  .tp-source-link,
  .rdec-hint,
  .exhibit figcaption p,
  .lr-key-line,
  .fourmap-from,
  .foot-meta span,
  .lr-foot { font-size: 13px; }

  /* research.html's own captions and card lines. .rstep--stack .rstep-fig
     figcaption is three classes deep and wins over a bare .research-fig
     figcaption, so it is named here too. */
  .research-fig figcaption,
  .rstep-fig figcaption,
  .rstep--stack .rstep-fig figcaption,
  .rpg-means,
  .rpg-caption,
  .research-foot { font-size: 12.5px; }

  /* the callout mock's source-and-licence line, the one line in it that is a
     real link; its sibling rows are already at 13 */
  .tp-co-origin { font-size: 12px; }
  /* THIS ONE SITS ON PAPER, NOT ON THE DARK GROUND. `.rdec-fac` is filled
     #EFE7D3 (landing.css §R), so the dark-ground gold #A98A3C computed for
     #0B0D12 measured 2.67:1 on it — worse than the 3.33:1 the base #8A7C55
     already had. #63562C on #EFE7D3 is 5.6:1. Every other colour lifted in this
     block sits on the page's own dark ground. */
  #rdec-fac span, .rdec-fac span { font-size: 12px; color: #63562C; }   /* 8.5 px at 3.33:1 on cream → 12 px at 5.6:1 */

  /* the beat's own tag, which names the layer on screen */
  .field-beat-tag { font-size: 12px; }

  /* the eyebrow's 22% tracking at 12 px pushes a three-word label past the
     column; 14% holds the same voice and fits */
  .eyebrow { letter-spacing: 0.14em; }

  /* the sub-AA runs, all of them inside the two app mocks:
     #8C867A on #121419 = 5.0:1, #8E9099 on #16171B = 5.6:1.
     `.tp-chat-input` is the AI mock's "Ask a follow-up…" line: a drawn input,
     not a live placeholder, so it is text a reader will try to read. It
     measured rgb(110,112,121) on rgb(22,23,27) = 3.63:1 and was the last
     failure left on the page. */
  .np-input, .np-stat, .np-helpful-label, .np-explore-label { color: #8C867A; }
  .tp-chat-disc, .tp-chat-input { color: #8E9099; }

  /* display heads run to two and three lines here and 1.12 leading crowds
     them; 1.18 costs 10 px across a three-line head at 28 px */
  .display { line-height: 1.18; }
}

/* ── P3 · TAP TARGETS ──────────────────────────────────────────────────────
   30 interactive elements measured under 44 px, including every primary call
   to action (all of them 43 px tall — one short), the menu button, the six
   layer keys, both mock ✕ buttons and all nine footer links. The two ✕ glyphs
   grow outward with a negative margin so the mock's own layout does not move. */
/* …or any touch device up to tablet size: a phone held sideways is 844x390 and
   a tablet in portrait is 768x1024, both of which sail past a 760 px gate while
   still being a thumb on glass. A mouse reports hover:hover and pointer:fine,
   so no mouse-driven desktop can match the second clause — but a touch-primary
   display can, at any size, and MEASURED at 3440x1440 with a touch context the
   clause fired and took four of the five reading keys' prose, the eyebrows, the
   buttons and the whole nav bar with it. 1366 px is the ceiling: it holds every
   case named above, an iPad Pro in landscape included, and leaves every display
   1440 px and wider on the desktop layout whatever it is pointed with. */
@media (max-width: 760px), (max-width: 1366px) and (hover: none) and (pointer: coarse) {
  .btn { min-height: 48px; justify-content: center; }
  .btn > span:first-child { font-size: 14px; }
  .linkish { min-height: 44px; font-size: 14px; }
  .hero-cue { min-height: 48px; }

  .np-close, .tp-co-close {
    width: 44px; height: 44px; font-size: 17px; cursor: pointer;
    display: flex; align-items: center; justify-content: center;
    margin: -10px -10px 0 0;
  }
  .np-sources summary, .tp-sources-summary { min-height: 44px; padding-block: 15px; cursor: pointer; }
  .np-pill { min-height: 44px; padding-inline: 16px; }
  #tp-explore-btn, .tp-co-action { min-height: 44px; }
  .np-input { min-height: 44px; height: auto; font-size: 14px; }
  .tp-co-origin { display: inline-flex; align-items: center; min-height: 44px; }
  .np-q { min-height: 44px; }
}

/* ── P4 · HERO ─────────────────────────────────────────────────────────────
   The first screen carried no call to action at all: the first .btn on the
   page stood at y=2,109, two and a half screens down, past the whole premise
   section. The key below is the premise's own, repeated, and it exists only
   under 640 px. */
.hero-cta { display: none; }
@media (max-width: 640px) {
  .hero-cta { display: inline-flex; margin-top: 30px; width: 100%; }
  .hero-cue { margin-top: 0; }
}

/* ── P5 · PREMISE ──────────────────────────────────────────────────────────
   Two devices, neither legible: the iMac's screenshot drew 302 px wide from a
   2560 px source and the phone's 107 px wide from 1206. The section's claim is
   "Free, in any browser" — one legible device carries it. And the three
   decorative gold motes sat on top of the body copy, because at 390 the text
   column is 90% of the viewport: the pin core was measured over the "p" of
   "prehistoric cemeteries". They ride in the gutter now. */
@media (max-width: 640px) {

  /* the field reaches into the page gutter but never past it: at -18px with the
     pins another 2px outside that, the document's scrollWidth measured 393
     against a 390 px viewport */
  .premise-field { inset: -20px -14px; }
  .premise-pin[data-pin-i="1"] { right: 0; }
  .premise-pin[data-pin-i="2"] { left: 0; }
  .premise-pin[data-pin-i="3"] { right: 0; }

  /* the two keys sit in a 350 px column and were wasting 88-120 px of it */
  .premise-cta-row { flex-direction: column; align-items: stretch; }
  .premise-cta-row .btn { width: 100%; }
}

/* ── P6 · THE FIELD ────────────────────────────────────────────────────────
   The stage itself is fixed in the max-width: 900px block above. What is left
   here is the counter, the credit that landing.js moves out of the stage, and
   a way past a pin that still runs about four screens. */
@media (max-width: 900px) {
  .field-stage { position: relative; }

  /* THE COUNTER. Five 12 px chips over a satellite frame are not a counter:
     measured at y=2864, five empty squares and the words "OF FIVE LAYERS" in
     10 px at 55% alpha over green field, absent altogether from two of the six
     beats. The numeral is the same state the chips carried, written by
     landing.js to data-n, and it sits on the app's own map-label protection
     plate — the house rule is that text is never placed directly on the map.
     Pixel-sampled from the pinned stage, the old label peaked at 6.63:1 on a
     dark frame and had nothing at all behind it on the satellite and OS beats. */

  /* THE CREDIT, OUT OF THE STAGE. landing.js moves the node to just after the
     stage on narrow screens and marks it. At 8.5 px clamped to two lines it
     ended in an ellipsis: Historic England, the Roman Roads Research
     Association and the record holders could not be read at all, and a licence
     credit has to be readable. */
  .field-caption--flow {
    display: block; position: static;
    padding: 16px clamp(20px, 4vw, 56px) 0;
    font: 500 12px/1.55 var(--font-mono);
    text-shadow: none;
  }
  .field-caption--flow span { display: block; }
  .field-caption--flow .right { margin-top: 6px; -webkit-line-clamp: none; overflow: visible; }

}

/* ── P7 · THE INSTRUMENT ───────────────────────────────────────────────────
   The scroll trap first: .ag-overlay is `touch-action: none` across a
   350x262 box spanning 90% of the viewport, 9,378 px down a 29,948 px page,
   so a thumb landing anywhere in it orbits the model instead of scrolling. By
   spec that disables browser panning on the element entirely. pan-y hands the
   vertical back and keeps the horizontal drag for the model.
   THIS ONE KEEPS NO WIDTH CEILING, unlike every other coarse-pointer clause in
   §P. It sets no size, no colour and no layout — it paints nothing, so no
   pixel comparison at any width can see it — and the trap it undoes is a trap
   for any finger, including a finger on a 1920 px touchscreen, which a 1366 px
   ceiling would leave stuck. */
@media (pointer: coarse) {
  .ag-overlay { touch-action: pan-y; }
}
/* …or any touch device up to tablet size: a phone held sideways is 844x390 and
   a tablet in portrait is 768x1024, both of which sail past a 760 px gate while
   still being a thumb on glass. A mouse reports hover:hover and pointer:fine,
   so no mouse-driven desktop can match the second clause — but a touch-primary
   display can, at any size, and MEASURED at 3440x1440 with a touch context the
   clause fired and took four of the five reading keys' prose, the eyebrows, the
   buttons and the whole nav bar with it. 1366 px is the ceiling: it holds every
   case named above, an iPad Pro in landscape included, and leaves every display
   1440 px and wider on the desktop layout whatever it is pointed with. */
@media (max-width: 760px), (max-width: 1366px) and (hover: none) and (pointer: coarse) {
  /* THE PANEL: ONE READING'S PROSE AT A TIME. All five keys were shown fully
     expanded — 632 px of key list, one and a half screens — and the readout
     above repeated the live key's sentence word for word. Every word stays in
     the DOM; only the chosen one is shown. Measured saving: about 280 px. */
  .lr-key, .lr-key-main { min-height: 44px; }

  /* the credits: centred wrapped all-caps mono is the hardest small setting
     there is, and .roman-credit on this same page is already left-aligned */
  .ag-caption { justify-content: flex-start; text-align: left; font-size: 12px; letter-spacing: 0.04em; }
  .ag-caption span { font-size: 12px; }
  /* .roman-fig .roman-credit is two classes deep and wins over the floor block
     above it; it is a licence and a permission, and 10.5 px is not readable */
  .roman-fig .roman-credit { font-size: 12.5px; }

  /* THE RADIUS. The one control the copy asks the reader to move had a
     12x12 px grab handle, a 9 px label, 9.5 px stops and a 10 px readout. */
  .lr-slrm-range::-webkit-slider-thumb { width: 26px; height: 26px; margin-top: -11.5px; }
  .lr-slrm-range::-moz-range-thumb { width: 26px; height: 26px; }
  /* the track itself, not only its handle: 340x36 is a 36 px target */
  .lr-slrm-range { touch-action: pan-y; min-height: 44px; }
  .lr-slrm-out { font-size: 13px; }
  .lr-slrm-stops { height: 15px; }

  /* the dial: 42x42 with a 12 px dot, and a title attribute for an instruction
     on a device that never shows one */
  .ag-lightdial { width: 52px; height: 52px; }
  .ag-lightdial-dot { width: 16px; height: 16px; }
}
/* THE PHONE'S IDLE STATE. index.html's module shows the still and this key
   instead of building the viewer: measured, the viewer pulls 6,854 KB and
   takes the heap from 8 MB to 58 MB to fill a 350x263 frame. */
.mc-load { display: none; }
@media (max-width: 1023px) {
  .mc-load[hidden] { display: none; }
  .mc-load {
    display: inline-flex; align-items: center; justify-content: center;
    position: absolute; z-index: 5; left: 50%; top: 50%; transform: translate(-50%, -50%);
    min-height: 48px; padding: 14px 20px 16px;
    font: 500 13px/1 var(--font-body); letter-spacing: 0.08em; text-transform: uppercase;
    white-space: nowrap;
  }
  .mc-still { display: block; width: 100%; height: 100%; object-fit: cover; }
}

/* ── P8 · THE READER MOCK ──────────────────────────────────────────────────
   484 px of a 927 px narration was shown and 443 px of it was hidden inside a
   nested scroller with `overscroll-behavior: contain`, no visible scrollbar on
   touch and no arrow — so a flick that started on the text scrolled the panel,
   and at its end the same gesture would not hand the page back. The panel
   grows to its content instead: the trap is gone, the narration no longer
   fades out mid-sentence, and the cost is about 443 px in a 1,586 px section.*/
@media (max-width: 640px) {
  .np { height: auto; }
  .np-scroll { overflow: visible; overscroll-behavior: auto; }
  .np-composer::before { display: none; }
  .walk-stage { gap: 26px; }
}

/* ── P9 · THE CORPUS ───────────────────────────────────────────────────────
   The switch. Stacked, "Fluent and specific — but not one claim is sourced."
   sat at y=14,063 and "Every line drawn from a real record…" at y=15,157:
   1,094 px apart, so the comparison the section exists to make could never be
   seen. Above 640 px this control does not exist. */
@media (max-width: 640px) {

  /* (the archive list is laid out in its own ≤1023 block below, because
     _shared.js stops duplicating the track at that width and a clipped,
     un-animated ticker would hide most of the names on a tablet) */

  /* the digitised page's caption was crossed by the plate's own bottom rule */
  .library-caption { margin-top: 14px; }

}

/* THE ARCHIVE LIST STANDS STILL, on every touch-sized screen.
   A 6,735 px ticker of archive names at 11 px, on a device where nothing can be
   hovered to pause it, cannot be read — you wait for a name to drift past.
   _shared.js stops duplicating the track below 1024 px (duplicating it here
   would print every name twice), so the breakpoint has to match: at 641-1023 px
   an un-duplicated, un-animated track inside `overflow: hidden` would simply
   hide the last eight names off the right edge. Same twelve names, same order,
   standing still. */
@media (max-width: 1023px) {
  .marquee { overflow: visible; -webkit-mask-image: none; mask-image: none; }
  /* minmax(0, 1fr), not 1fr: a grid track's automatic minimum is min-content,
     and "National Monuments Service of Ireland" pushed the column — and the
     document's scrollWidth — past the viewport */
  .marquee-track { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 10px 20px; width: auto; animation: none; height: auto; align-items: start; }
  /* the ticker's span is `white-space: nowrap` with a 56 px gap before a middle
     dot — in a 167 px grid column that printed every name over its neighbour */
  .marquee span { display: block; white-space: normal; gap: 0; font-size: 12.5px; letter-spacing: 0.06em; line-height: 1.35; }
  .marquee span::after { content: none; }
}
@media (min-width: 641px) and (max-width: 1023px) {
  .marquee-track { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* ── P10 · THE WALK ────────────────────────────────────────────────────────
   The key wrapped into a ragged mess: the "Key" head and the first swatch
   shared a row, and the remaining three labels started at a different indent
   from the first. A two-column grid puts every label on its own swatch. */
@media (max-width: 640px) {
  .walkg-key { display: grid; grid-template-columns: 64px 1fr; gap: 12px 12px; align-items: center; }
  .walkg-key-head { grid-column: 1 / -1; font-size: 12px; }
  .walkg-key-row { display: contents; }
  .walkg-key-row span { font-size: 13px; }

  /* the stats strip is moved out of the map by landing.js; here it stops being
     a plate over the picture and becomes a full-width row under it */
  .walkg-stats--flow { position: static; width: 100%; margin-top: 10px; border-radius: var(--r-md); }

  /* THE SVG ANNOTATION LAYER COMES OFF THE PICTURE. The graphic is drawn in a
     2560x1440 viewBox and on a phone the figure is 348 css px wide — a scale of
     0.136 — so its live SVG text painted at, MEASURED, 5 px for the nine site
     names (font-size 28, bounding height 5) and 3 px for the numbered badges
     (font-size 18, r 15 → a 4 px disc). That is not small type, it is a smudge
     printed over the evidence, and two of the nine badges are already
     overlapping at that size, so enlarging them is not open either: the pair at
     Chysauster measure 5.9 css px between centres and a legible badge is 18.
     Nothing is lost by taking them off. The KEY beside the map names four
     things — public footpath, public bridleway, path or track, the planned walk
     — and all four are drawn lines, all four still read. Every one of the nine
     names, its number and its two distances is printed in full in ALONG THE WAY
     immediately below, in the order they are walked.
     The app's own pin sprites stay, and grow: 64 units is 8.7 css px, which is
     a speck. Scaled about their own centre (transform-box: fill-box is already
     set, so the anchor does not move) they land at 14.8 px — the size the app
     itself draws a pin at. */
  .walkg-site-label, .walkg-badge, .walkg-badge-n, .walkg-site-ring { display: none; }
  .walkg-pin { transform: scale(1.05); }
  .walkg-pin.is-on { transform: scale(1.7); }

  .walkg-list li small { font-size: 12px; }

}

/* ── P11 · THE LETTERED SUB-BLOCKS ─────────────────────────────────────────
   In both Walk and Pro tools the supporting detail was set smaller than the
   lede above it: 13 px gold headings at 3.34:1 over 13.5 px grey paragraphs,
   under a 16 px lede at 16.06:1. The hierarchy was inverted. */
@media (max-width: 640px) {
  .also h3 { font-size: 14px; }
  .also p { font-size: 15px; color: rgba(237,233,226,0.80); }
}
/* the two exhibits are forced to one 8:5 ratio so their captions start on the
   same line — which is a two-column argument. Stacked on a phone, with each
   picture now a crop of its own shape, that ratio cover-crops the sides off the
   cross-section (the "Vertical exaggeration ×3." line among them). */
@media (max-width: 1023px) {
  .exhibit img { aspect-ratio: auto; object-fit: contain; }
}

/* ── P12 · PARTNERS ────────────────────────────────────────────────────────
   The scanned-versus-ink plate is two halves of one file and the caption names
   them as left and right, so they are not stacked: the copy does not change.
   What the phone gets is the plate at the full screen width instead of inside
   the 20 px gutter — 390 px rather than 350 — and a source cut for it. */
@media (max-width: 640px) {
  .partners-fig { margin-inline: calc(clamp(20px, 4vw, 56px) * -1); border-radius: 0; }
  .partners-fig figcaption { padding-inline: clamp(20px, 4vw, 56px); font-size: 12px; }
  .exhibit figcaption { font-size: 12px; }
}

/* ── P13 · THE RESEARCH TEASER ─────────────────────────────────────────────
   Three plates in a 927 px horizontal scroller inside a 350 px window, with no
   scrollbar, no dots, no arrow and no peeking edge: the first plate ended at
   x=321 of a 390 px screen, so nothing of the second was visible, and the
   three were 230, 141 and 101 px tall so the row read as broken. */
@media (max-width: 999px) {
  .research-plate-row { grid-auto-columns: 78%; scroll-padding-inline: 20px; scroll-snap-type: x mandatory; }
  .research-plate { scroll-snap-align: start; }
  .research-plate img { width: 100%; height: auto; }
  .research-plate-cap { font-size: 12px; }
}

/* ── P14 · THE ROADMAP ─────────────────────────────────────────────────────
   No rail was drawn on a phone, so four stages read as loose bullets: dots at
   x≈41 with their text indented to x≈75 and nothing between them. */
@media (max-width: 640px) {
  /* the wide rail is `display: none` at this breakpoint and nothing replaced it.
     The dot is 11 px at left 0 of a stop that is padded 24 px, so the rail runs
     down the dots' own centre line at x = 5. */
  .roadsec .road { position: relative; }
  .roadsec .road::before {
    content: ""; display: block; position: absolute;
    left: 5px; right: auto; top: 10px; bottom: 12px; width: 1px; height: auto;
    background: linear-gradient(180deg, rgba(201,168,76,0.30), rgba(201,168,76,0.10));
  }
  .road-stop { position: relative; }
}

/* ── P15 · THE INVITATION ──────────────────────────────────────────────────
   Seven lines of centred italic Cormorant at 18 px in mist grey is the hardest
   configuration on the page, and it sits at the conversion point, above a
   248x43 key in a 350 px column. */
@media (max-width: 640px) {
  .invite { text-align: left; }
  .invite .display { margin: 0; max-width: none; }
  /* the rule carries an inline `margin: 0 auto` from the markup, which centres
     it; with the section left-aligned it has to start where the text does */
  .invite .gold-rule { margin-inline: 0 auto !important; }
  .invite-sub { font: 400 17px/1.6 var(--font-body); color: rgba(237,233,226,0.86); margin-inline: 0; max-width: none; }
  .invite-last { text-align: left; margin-inline: 0; font-size: 15px; }
  .invite .btn { width: 100%; min-height: 52px; }
}

/* ── P16 · FIRST LAYOUT ────────────────────────────────────────────────────
   NOT DONE, AND MEASURED NOT TO BE WORTH IT ANY MORE.
   The audit found a single 968 ms task laying out the whole 30,000 px document
   before any of this page's own JavaScript ran, holding first contentful paint
   at 1,308 ms, and showed `content-visibility: auto` with
   `contain-intrinsic-size: auto 1600px` on the eleven sections below the field
   cutting FCP to 624 ms. That measurement was taken against the page BEFORE
   this pass. Re-measured here at 390x844 and 4x CPU throttle, three runs each,
   with the /diag loop patched out and everything else in this pass applied:

     with content-visibility  FCP 764 / 520 / 1140 ms   load 1517 / 1440 / 2055
     without                  FCP 768 / 512 /  520 ms   load 1818 / 1712 / 1720

   FCP is a wash — the layout cost the rule was buying back has already gone
   with the 75 ScrollTriggers (_shared.js), the 1,157 KB module graph and the
   6.7 MB video. What the rule still costs is real: every one of the eleven
   sections renders blank to an element screenshot, because an off-screen
   render is exactly what content-visibility skips, so the project's own
   capture tool (_bake/_gpu, scroll-shot.cjs) cannot photograph two thirds of
   the phone page — and document height read 27,207 px with the rule against
   26,650 without, the 557 px being contain-intrinsic-size placeholders on a
   page whose field stage is scroll-pinned. A page you cannot photograph and
   whose scroll geometry is provisional is not worth a wash. */

/* ── P17 · research.html ───────────────────────────────────────────────────
   The decoder's whole interaction is tapping a label marker, and the markers
   were 22x22 — half the minimum, with 29 of the 39 on screen at once and three
   pairs closer together than a fingertip. The hit box grows to 44 without the
   gold dot growing with it; the map keeps cooperativeGestures, so a reader can
   pinch to separate a crowded pair (raising the map's own initial zoom instead
   would have pushed markers off a frame that takes two fingers to pan). */
.rpart-index { display: none; }   /* phone only; above 760 px it is not rendered at all */
/* The decoder's instruction, hover-first or tap-first by what the device has.
   No width ceiling here either, and deliberately: `hover: none` means the
   device cannot hover at all, so a 1920 px touch-only display told to hover a
   label is told to do something it cannot do. This swaps one sentence for the
   sentence beside it in the same markup; it moves no layout. */
.rdec-hint-coarse { display: none; }
@media (hover: none) and (pointer: coarse) {
  .rdec-hint-fine { display: none; }
  .rdec-hint-coarse { display: inline; }
}
/* …or any touch device up to tablet size: a phone held sideways is 844x390 and
   a tablet in portrait is 768x1024, both of which sail past a 760 px gate while
   still being a thumb on glass. A mouse reports hover:hover and pointer:fine,
   so no mouse-driven desktop can match the second clause — but a touch-primary
   display can, at any size, and MEASURED at 3440x1440 with a touch context the
   clause fired and took four of the five reading keys' prose, the eyebrows, the
   buttons and the whole nav bar with it. 1366 px is the ceiling: it holds every
   case named above, an iPad Pro in landscape included, and leaves every display
   1440 px and wider on the desktop layout whatever it is pointed with. */
@media (max-width: 760px), (max-width: 1366px) and (hover: none) and (pointer: coarse) {
  /* 46, not 44: measured, three of the thirty-nine reported 43.99 at exactly
     44 px because the marker's own transform lands them on a half pixel */
  .rdec-mk { width: 46px; height: 46px; display: grid; place-items: center; }
  .rdec-mk-in { width: 22px; height: 22px; }
  .rdec-card-x { width: 44px; height: 44px; font-size: 19px; }
  .rdec-key { min-height: 46px; flex: 1 1 0; justify-content: center; text-align: center; }
  .maplibregl-ctrl-group button,
  .maplibregl-ctrl button.maplibregl-ctrl-zoom-in,
  .maplibregl-ctrl button.maplibregl-ctrl-zoom-out { width: 44px; height: 44px; }

  /* the part index, under the nav: one section 9,990 px tall in an 11,020 px
     document, thirteen screens with three headings as its only landmarks */
  .rpart-index {
    position: sticky; top: 77px; z-index: 40;
    display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
    margin: 0 calc(clamp(20px, 4vw, 56px) * -1) var(--sp-md);
    padding: 0 clamp(20px, 4vw, 56px);
    background: color-mix(in srgb, var(--bg) 92%, transparent);
    backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
    border-bottom: 1px solid var(--border);
  }
  .rpart-index a {
    display: flex; align-items: center; gap: 7px; min-height: 46px;
    font: 500 12px/1.2 var(--font-body); letter-spacing: 0.06em;
    color: var(--t1-soft);
  }
  .rpart-index a i { font-family: var(--font-mono); font-style: normal; color: #A98A3C; }
}
/* THE WIDE PLATES SAY THEY CONTINUE.
   Eight figures are horizontal scrollers 660-690 px wide inside a 350 px
   column with no visible affordance, and on first sight each shows about half
   of itself — often the half the caption is not about (Figsbury Ring entirely
   off-screen; the OS 404 typeface columns off-screen). The caption is already
   pinned to the left of the scroller, so the hint and the edge fade ride on it
   and stay put while the plate moves underneath. */
@media (max-width: 700px) {
  .research-fig--plate:not(.rstep-fig--crop):not(.rstep-fig--pin) > figcaption,
  .rwork-fig > figcaption,
  .research-fig--positives > figcaption,
  .rdec-figure > figcaption { position: sticky; }
  .research-fig--plate:not(.rstep-fig--crop):not(.rstep-fig--pin) > figcaption::before,
  .rwork-fig > figcaption::before,
  .research-fig--positives > figcaption::before {
    content: "Scroll \2192";
    display: block; margin-bottom: 7px;
    font: 500 12px/1 var(--font-mono); letter-spacing: 0.1em; text-transform: uppercase;
    color: #A98A3C;
  }
  /* the fade sits on the caption, which is pinned at the viewport's left edge
     and is the full column wide, so its right edge IS the scroller's right edge */
  .research-fig--plate:not(.rstep-fig--crop):not(.rstep-fig--pin) > figcaption::after,
  .rwork-fig > figcaption::after,
  .research-fig--positives > figcaption::after {
    content: ""; position: absolute; right: 0; bottom: 100%; width: 46px; height: 60vh;
    pointer-events: none;
    background: linear-gradient(90deg, rgba(11,13,18,0), rgba(11,13,18,0.92));
  }
  .research-fig--plate > figcaption, .rwork-fig > figcaption, .research-fig--positives > figcaption { position: sticky; }
}

/* ── P18 · SHORT VIEWPORTS (a phone held sideways) ─────────────────────────
   At 844x390 the document is 24,734 px — 63.4 screens — and the fixed nav
   takes 18.5% of the viewport height, leaving 318 px between the bar and the
   fold; the field stage overflowed by 139 px.
   THE WIDTH BOUND IS THE POINT. Height alone matched any browser window 560 px
   or shorter, a mouse-driven desktop included: MEASURED at 1600x520 and at
   3440x520 the nav shrank to 50 px, the wordmark to 18 px and the LIVE field
   stage — untouchable on desktop — was cut to 46svh (239 px) with 15 px beat
   copy, while `#field-stage.is-live` was still true. A phone held sideways is
   844x390 and a small phone sideways is 667x375, so both stay inside a 1023 px
   ceiling and every desktop-width window keeps the desktop layout. */
@media (max-height: 560px) and (max-width: 1023px) {
  .field-stage { height: 100svh; overflow: hidden; }
  .field-frame { height: 46svh; }
  .field-beat p { font-size: 15px; }
  .field-skip { top: calc(46svh - 54px); }
}

/* ── P19 · THE SHOWCASE FRAME ON A PHONE, EDGE TO EDGE AND TALL ──────────────
   Matt, 16 September: "the showcase is just a tiny sliver, you can't actually
   see it properly, it needs to be the whole screen." Under 640 px the frame
   leaves the gutter, takes the full viewport width at 4:5, and the still behind
   the load key is rendered whole at that shape (showcase-still-phone.webp), so
   nothing is cropped. The panel below keeps the desktop keys, at full width. */
@media (max-width: 640px) {
  .showcase-frame, .showcase-grid .showcase-frame {
    width: 100vw; margin-left: calc(50% - 50vw); margin-right: 0;
    border-radius: 0; aspect-ratio: 4 / 5; max-height: none;
  }
  .showcase-frame::before, .showcase-frame::after { border-radius: 0; }
  .mc-still { object-fit: cover; }
}
