/* ============================================================
   site.css — shared chrome for every page: the masthead, the
   case/essay crumb bar, the paper grain, and the reveal/word-rise
   primitives both home.css and case.css build on. Loads after
   tokens.css. Paired with site.js.
   Rebuilt 2026-08-05 for THE RECORD (broadsheet redesign).
   ============================================================ */

/* — Cross-document view transitions (ratified 2026-07-03; kept through
   the redesign). Declared STATICALLY — no gates, no automation detection;
   what ships is what runs. The headless-Chromium freeze is handled where
   it belongs: the test browser launches with the feature disabled
   (playwright.config.js launchOptions), so the suite verifies the
   progressive-enhancement floor. The 2026-07-06 named-group morph (nav/
   rail/case-title snapshotting apart from root) doesn't carry forward —
   it named chrome elements (.globalnav, .rail) this redesign retires;
   every page now gets the same plain root fade+rise instead. —  */
@view-transition { navigation: auto; }
@media (prefers-reduced-motion: reduce) {
  @view-transition { navigation: none; }
}
@media (prefers-reduced-motion: no-preference) {
  ::view-transition-old(root) { animation: vt-out .18s ease both; }
  ::view-transition-new(root) { animation: vt-in .26s cubic-bezier(.16, 1, .3, 1) both; }
}
@keyframes vt-out { to { opacity: 0; } }
@keyframes vt-in { from { opacity: 0; transform: translateY(10px); } }

@layer chrome {
  /* ---- shared page container — one width, every page ---- */
  .wrap { max-width: var(--w-page); margin: 0 auto; padding-inline: var(--edge); }

  /* ---- paper grain — a faint noise texture over the whole page,
     screened in dark mode so it lightens instead of muddying ink ---- */
  body::after {
    content: ""; position: fixed; inset: 0; z-index: 99; pointer-events: none;
    opacity: .5; mix-blend-mode: multiply;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='240' height='240'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='2'/%3E%3CfeColorMatrix values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .05 0'/%3E%3C/filter%3E%3Crect width='240' height='240' filter='url(%23n)'/%3E%3C/svg%3E");
  }
  html.dark body::after { mix-blend-mode: screen; opacity: .22; }

  /* ---- masthead — scrolls with the page; no fixed nav, no --nav-h offset ---- */
  .mast { border-bottom: 2px solid var(--ink); position: relative; }
  .mast::after { content: ""; display: block; height: 1px; background: var(--ink); margin-bottom: 3px; }
  .mast .row { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: baseline; gap: .6rem 1.5rem; padding: 1.1rem 0 .9rem; }
  .mast .name { font-family: var(--fm); font-size: var(--t-label); font-weight: 500; text-transform: uppercase; letter-spacing: .22em; }
  .mast .loc { font-family: var(--fm); font-size: var(--t-label); text-transform: uppercase; letter-spacing: .14em; color: var(--mut); }
  .mast .end { display: flex; flex-wrap: wrap; align-items: baseline; gap: .8rem 1.4rem; }
  .mnav { display: flex; flex-wrap: wrap; gap: .8rem 1.6rem; }
  .mnav a { font-family: var(--fm); font-size: var(--t-label); text-transform: uppercase; letter-spacing: .14em; color: var(--mut); }
  .mnav a:hover { color: var(--verm); }
  @media (max-width: 1100px) { .mast .loc { display: none; } }
  /* the canvas hides .mnav entirely below 860px with no alternative affordance —
     shrink and wrap instead, so every link stays reachable at every width
     (capabilities.spec.js: "nav is usable at every width") */
  @media (max-width: 640px) { .mnav { gap: .5rem .9rem; font-size: var(--t-micro); } }
  /* no-JS floor: the toggle is dead without JS, so it doesn't render as a
     false affordance (same precedent as the old .ctx-picker) */
  .mode { display: none; }
  .js .mode { display: inline-block; font-family: var(--fm); font-size: var(--t-micro); font-weight: 500; text-transform: uppercase; letter-spacing: .16em; color: var(--mut); background: none; border: 1px solid var(--rule); padding: .35em .9em; cursor: pointer; transition: color .25s, border-color .25s; }
  .js .mode:hover { color: var(--verm); border-color: var(--verm); }

  /* ---- crumb — case/essay pages only; harmless when absent elsewhere ---- */
  .crumb { border-bottom: 1px solid var(--rule); }
  .crumb .row { display: flex; flex-wrap: wrap; justify-content: space-between; gap: .5rem 1.5rem; padding: .75rem 0; font-family: var(--fm); font-size: var(--t-label); text-transform: uppercase; letter-spacing: .14em; color: var(--mut); }
  /* position readout ranks by ink+weight, not accent — its two siblings are
     links, and the accent is reserved for what's clickable */
  .crumb .row .mid { color: var(--ink); font-weight: 500; }
  @media (max-width: 720px) { .crumb .row .mid { display: none; } }

  /* ---- folio — the fine-print strip closing every page's footer ---- */
  .folio { border-top: 2px solid var(--ink); margin-top: var(--sp-xl); padding: .9rem 0 2.2rem; display: flex; flex-wrap: wrap; justify-content: space-between; gap: .5rem 2rem; font-family: var(--fm); font-size: var(--t-micro); text-transform: uppercase; letter-spacing: .14em; color: var(--mut); }

  /* ---- reveal (.rv) + word-rise (.wr → .wm/.wi) — shared entrance
     motion for both home.css and case.css. site.js splits .wr targets
     into masked word spans and observes every .rv/.wr/.fig/.pillars/
     .notices/.spine/.t-photo target on the page (each page only has a
     subset; the rest are no-ops). Pre-hidden only when .js landed AND
     motion is allowed, so a no-JS or reduced-motion reader never sees
     hidden content. ---- */
  .js .rv { opacity: 0; transform: translateY(28px); }
  .js .rv.in { opacity: 1; transform: none; transition: opacity .8s ease, transform .85s cubic-bezier(.16,1,.3,1); }
  .wm { display: inline-block; overflow: hidden; vertical-align: bottom; padding-bottom: .18em; margin-bottom: -.18em; }
  .js .wm .wi { display: inline-block; transform: translateY(118%); transition: transform .9s cubic-bezier(.16,1,.3,1); }
  .js .wr.in .wm .wi { transform: none; }

  /* ---- independent failsafe — a tiny inline script in every page's
     <head> adds html.revealed around 3s regardless of whether site.js
     (or home.js/case.js) ever loaded. If the reveal script is blocked
     or fails, this is what keeps content from staying permanently
     hidden — belt-and-suspenders with the observer's own setTimeout
     inside site.js, which only runs if the script loaded at all. ---- */
  .revealed .rv { opacity: 1 !important; transform: none !important; transition: none !important; }
  .revealed .wm .wi { transform: none !important; transition: none !important; }

  @media (prefers-reduced-motion: reduce) {
    .js .rv { opacity: 1 !important; transform: none !important; transition: none !important; }
    .js .wm .wi { transform: none !important; transition: none !important; }
  }
}
