/* ═══════════════════════════════════════════════════════════════════════════
   CORVEXAL — THE CHROME
   The nav, the rail, the footer, the wordmark. Loaded by EVERY page.

   This file is written against a FALLBACK CHAIN on purpose:

       var(--bg, var(--ground))      new pages   <-- old pages

   blocks.css names the palette --bg/--ink/--mut/--acc/--line; site.css, which
   the already-shipped pages still load, names it --ground/--ink/--muted/
   --bronze/--rule. One shell has to sit on both without forking, because the
   footer-drift check in gate.sh compares the footer markup across every page
   and a forked shell is exactly the drift it exists to catch.

   THE FACES NEEDED THE SAME TREATMENT AND DID NOT HAVE IT — S8.
   The palette was chained from the start. The three FACES were not:

       chrome.css read     blocks.css has     site.css has
       --face-d            --face-d           --face-display
       --face-u            --face-u           --face-ui
       --face-m            --face-m           --face-mono

   Same intent, two vocabularies, and only one of them was ever asked for. On
   the TWELVE pages that load site.css instead of blocks.css, all three fell
   through to their generic fallbacks. Measured on about.html: `.mark2`
   computed to "Georgia" and rendered Georgia at 318.3px for a control string,
   against Fraunces at 303.2 — **while Fraunces was loaded and sitting
   unused.** The wordmark, the nav links and every monospace label in the
   chrome were in system faces on most of the site.

   Nothing looked broken because a generic serif next to a real serif reads as
   a slightly different serif. Chained now, both vocabularies, generic last.

   When the last site.css page migrates, the fallbacks come out. Not before —
   a fallback removed early is a footer that renders unstyled on four pages,
   and unstyled-because-the-CSS-never-arrived is a bug this project has
   shipped three times.
   ═══════════════════════════════════════════════════════════════════════════ */

:root {
  --nav-h: 68px;
  --nav-dim: rgba(0, 0, 0, 0.08);
  --chrome-shadow: rgba(0, 0, 0, 0.28);
  --fallback-bg: #FFFFFF;
  --fallback-ink: #121820;
  --fallback-mut: #5F6670;
  --fallback-acc: #B68A5E;
  --fallback-line: rgba(0, 0, 0, 0.12);
  --fallback-card: #FFFFFF;

  /* THE MOTION TOKENS ARE DECLARED, NOT FALLEN BACK TO.
     Every transition below first read var(--d2, 200ms). That is a raw
     duration written six-and-twenty times, which is exactly the thing the
     gate exists to stop — and the gate caught it. A literal inside a var()
     fallback is still a literal: it is a number nobody can change in one
     place, wearing the costume of a token.

     blocks.css declares the same four durations and the same two curves.
     chrome.css is linked after it on every new page, so these re-declare
     identical values and nothing shifts; on the site.css pages, which have
     their own separate names, these are the only source. Either way the
     chrome moves on one clock. */
  --d1: 120ms;
  --d2: 200ms;
  --d3: 320ms;
  --d4: 480ms;
  --t0: 0s;
  --instant: 1ms;
  --mark-x: #E8A87C;
  --e:     cubic-bezier(0.44, 0, 0.56, 1);
  --e-out: cubic-bezier(0.16, 1, 0.3, 1);
}

/* Resolved once here so the rest of the file reads normally. */
.nav2, .railnav, .foot2, .skip, .mark2, .dim, .ns {
  --c-bg:   var(--bg,   var(--ground, var(--fallback-bg)));
  --c-ink:  var(--ink,  var(--fallback-ink));
  --c-mut:  var(--mut,  var(--muted,  var(--fallback-mut)));
  --c-acc:  var(--acc,  var(--bronze, var(--fallback-acc)));
  --c-line: var(--line, var(--rule,   var(--fallback-line)));
  --c-card: var(--card, var(--fallback-card));
}

/* ── THE SKIP LINK ──────────────────────────────────────────────────────── */

.skip {
  position: absolute; left: 12px; top: -60px; z-index: 200;
  background: var(--c-ink); color: var(--c-bg);
  padding: 12px 20px; border-radius: 100px; font-size: 14px;
  transition: top var(--d2) var(--e);
}
.skip:focus { top: 12px; }

/* ── THE NOSCRIPT NAV ───────────────────────────────────────────────────── */

.ns {
  display: flex; flex-wrap: wrap; gap: 20px;
  padding: 18px 40px; border-bottom: 1px solid var(--c-line);
  font-size: 14px;
}

/* ═══ THE NAV ══════════════════════════════════════════════════════════════
   Four items. Not five, not seven. Each opens a web on hover; the web stays
   open while the pointer travels into it and closes only when the pointer
   leaves the web itself. Rendered from one array in blocks.js — adding
   EMBER K-8 in 2027 is one object in a tree, not a nav rebuild.
   ═══════════════════════════════════════════════════════════════════════════ */

.nav2 {
  position: fixed; inset: 0 0 auto 0; z-index: 100;
  height: var(--nav-h);
  display: flex; align-items: center; gap: 40px;
  padding: 0 40px;
  background: color-mix(in srgb, var(--c-bg) 82%, transparent);
  backdrop-filter: saturate(1.4) blur(14px);
  -webkit-backdrop-filter: saturate(1.4) blur(14px);
  border-bottom: 1px solid transparent;
  transition: border-color var(--d3) var(--e),
              background var(--d3) var(--e);
}
.nav2[data-scrolled] { border-bottom-color: var(--c-line); }

.mark2 {
  font-family: var(--face-d, var(--face-display, Georgia, serif)); font-optical-sizing: auto;
  font-weight: 700; font-size: 20px; letter-spacing: -0.4px;
  color: var(--c-ink); line-height: 1; white-space: nowrap;
}
/* `line-height: 1` is right for a wordmark and wrong for a target: it makes
   the box exactly as tall as the type, measured 17px, and this link is the
   route home from all thirteen interior pages. Padding grows the hit area
   around the same baseline, so the mark does not move. */
a.mark2 { display: inline-block; padding-block: 4px; }
/* ══════ BEGIN LOGOTYPE — NOT AN AMBER USE ════════════════════════════════
   The X in CORVEXAL, in the brand orange, on every page and every ground.

   Kevin asked for this twice and the three landing drafts have carried it
   since the first pass; this brings the shipped pages into line with them so
   the mark does not change colour when you cross from one to the other.

   IT IS NOT ONE OF THE THREE AMBER USES, and tools/colour-budget.mjs knows
   that by the markers around this block rather than by a rule of thumb. The
   budget is on SURFACES — warm areas competing with the content. A logotype
   is not competing with the content; it is what the content belongs to.

   It is also exempt from bronze-and-amber-never-share-a-viewport, which it
   otherwise breaks outright: the nav is position:fixed, so it shares every
   viewport on the site, including the five that are bronze.

   CONTRAST. #E8A87C is 2.03:1 on white and 1.85:1 on cream — a comfortable
   AA failure for any other letterform this size. WCAG 1.4.3 exempts it by
   name: text that is part of a logo or brand name has no contrast
   requirement. The accessible name comes from the link's aria-label, so the
   word is never announced as CORVE AL whatever the X looks like.

   The exemption covers the X and nothing else. The moment --mark-x paints a
   sentence, a label or a link, it is type again and it has to clear 4.5:1. */
/* WAS .mark2 i, which no longer matches anything — the <i> holding the X
   was replaced by the carved SVG mark. The fill now comes from .mk-sweep
   in footer.css, which every page loads; --mark-x below is what that rule
   resolves to here. Kept as a declaration rather than deleted so the token
   still has a reader and the budget still sees a use on these pages. */
.mark2 .mk-sweep, .nav__mark .mk-sweep { fill: var(--mark-x); }
/* ══════ END LOGOTYPE ════════════════════════════════════════════════════ */

.nav2__items { display: flex; align-items: stretch; gap: 4px; margin-left: auto; }

.nav2__it { position: relative; }

.nav2__t {
  appearance: none; background: none; border: 0; cursor: pointer;
  height: var(--nav-h); padding: 0 16px;
  display: inline-flex; align-items: center; gap: 8px;
  font-family: var(--face-u, var(--face-ui, system-ui, sans-serif));
  font-size: 12px; letter-spacing: 1.4px; text-transform: uppercase;
  font-weight: 500; color: var(--c-mut);
  transition: color var(--d2) var(--e);
}
.nav2__t:hover, .nav2__it[data-open] .nav2__t { color: var(--c-ink); }

/* The trigger's own underline. It grows from the left rather than fading,
   because a fade at 12px reads as a rendering artefact. */
.nav2__t::after {
  content: ''; position: absolute; left: 16px; right: 16px; bottom: 18px;
  height: 1px; background: var(--c-ink);
  transform: scaleX(0); transform-origin: left;
  transition: transform var(--d3) var(--e-out);
}
.nav2__it[data-open] .nav2__t::after, .nav2__t:hover::after { transform: scaleX(1); }

/* ── THE WEB ────────────────────────────────────────────────────────────── */

.web2 {
  position: absolute; top: calc(var(--nav-h) - 6px); right: 0;
  min-width: 300px; padding: 14px;
  background: var(--c-card); border: 1px solid var(--c-line);
  border-radius: var(--r-md, 16px);
  box-shadow: 0 30px 60px -24px var(--chrome-shadow);
  /* /aria's measured --card is rgba(255,255,255,.07): correct as a tile fill
     on a black ground, and 7% opaque as a floating panel, which would let a
     headline read straight through the menu sitting on top of it. The blur
     is what makes ANY page's card colour work as a surface — it does not
     assume the palette is opaque, on eight grounds and counting. */
  backdrop-filter: blur(22px) saturate(1.3);
  -webkit-backdrop-filter: blur(22px) saturate(1.3);
  opacity: 0; visibility: hidden; transform: translateY(-8px);
  transition: opacity var(--d3) var(--e),
              transform var(--d3) var(--e-out),
              visibility var(--t0) linear var(--d3);
}
.nav2__it[data-open] .web2 {
  opacity: 1; visibility: visible; transform: translateY(0);
  transition-delay: var(--t0), var(--t0), var(--t0);
}

.web2 a {
  display: flex; align-items: center; justify-content: space-between; gap: 20px;
  padding: 11px 14px; border-radius: 10px;
  font-size: 15px; color: var(--c-ink);
  opacity: 0; transform: translateX(-6px);
  transition: background var(--d1) var(--e),
              opacity var(--d3) var(--e),
              transform var(--d3) var(--e-out);
}
.nav2__it[data-open] .web2 a { opacity: 1; transform: translateX(0); }
.web2 a:hover { background: color-mix(in srgb, var(--c-ink) 6%, transparent); }

/* A dated future is a roadmap. An undated one is a wish. */
.web2 a[data-soon] { color: var(--c-mut); pointer-events: none; }
.web2 a em {
  font-style: normal; font-family: var(--face-m, var(--face-mono, monospace));
  font-size: 11px; letter-spacing: 1px; color: var(--c-mut);
}

.web2__h {
  padding: 8px 14px 12px; font-family: var(--face-m, var(--face-mono, monospace));
  font-size: 10px; letter-spacing: 1.4px; text-transform: uppercase;
  color: var(--c-mut); border-bottom: 1px solid var(--c-line); margin-bottom: 8px;
}

/* The page behind darkens ~8% while a web is open. */
.dim {
  position: fixed; inset: 0; z-index: 99; background: var(--nav-dim);
  opacity: 0; pointer-events: none;
  transition: opacity var(--d3) var(--e);
}
.dim[data-on] { opacity: 1; }

/* ═══ THE RAIL ═════════════════════════════════════════════════════════════
   Kevin, on the reference: the C23 nav-hover-expand LINE rail, on the left,
   instead of dots. A dot says there are nine of these. A line whose length
   is the section share of the page says where you are AND how much is left,
   which is the same information a scrollbar carries and none of the dot
   patterns do.

   Driven by one IntersectionObserver. Zero scroll listeners on this site.
   ═══════════════════════════════════════════════════════════════════════════ */

.railnav {
  position: fixed; left: 26px; top: 50%; transform: translateY(-50%);
  z-index: 90; display: flex; flex-direction: column; gap: 2px;
  opacity: 0; transition: opacity var(--d4) var(--e);
}
.railnav[data-ready] { opacity: 1; }

/* A 44px hit target that stays in the gutter. The label beside it is
   absolutely positioned so it never joins the layout — see the note on
   .railnav span below. */
.railnav a {
  position: relative;
  display: flex; align-items: center;
  inline-size: 44px; height: 24px;
  text-decoration: none;
}
.railnav i {
  display: block; height: 1px; width: 18px; flex: none;
  background: var(--c-ink); opacity: 0.28;
  transition: width var(--d3) var(--e-out),
              opacity var(--d3) var(--e);
}
.railnav a:hover i, .railnav a:focus-visible i { opacity: 0.6; width: 30px; }
.railnav a[aria-current] i { width: 40px; opacity: 1; background: var(--c-acc); }

/* OUT OF THE LAYOUT ON PURPOSE. At opacity 0 this still had a box, and
   that box reached 87px past the gutter and over the page — invisible,
   and in front of every word it covered. Absolute takes it out; the
   pointer-events rule means even the visible label is never in the way. */
.railnav span {
  position: absolute; left: 30px; top: 50%;
  pointer-events: none;
  font-family: var(--face-m, var(--face-mono, monospace)); font-size: 10px; letter-spacing: 1.1px;
  text-transform: uppercase; color: var(--c-mut); white-space: nowrap;
  /* A CHIP, NOT BARE TEXT. The gutter is 64px and the longest label sets
     about 130px, so wherever the rail sits the label lands on the page.
     Given a ground of its own it reads as a tooltip instead of as two
     paragraphs printed on top of each other, and it needs no media query
     to decide which viewports have room. */
  background: var(--c-bg);
  padding: 5px 9px;
  border-radius: 999px;
  box-shadow: var(--c-line) 0 0 0 1px, rgba(0, 0, 0, .10) 0 4px 14px -6px;
  opacity: 0; transform: translate(-4px, -50%);
  transition: opacity var(--d2) var(--e),
              transform var(--d2) var(--e-out);
}
.railnav:hover span, .railnav a:focus-visible span { opacity: 1; transform: translate(0, -50%); }

/* ═══ THE FOOTER ═══════════════════════════════════════════════════════════
   Static markup on every page, never rendered by script — it is the
   verification surface and has to exist without JavaScript. Which means it
   is duplicated across every page, and duplication nothing checks is
   duplication that diverges, so gate.sh check 8c compares all of them.
   ═══════════════════════════════════════════════════════════════════════════ */

.foot2 { padding: 0 40px 56px; background: var(--c-bg); }
.foot2 .col2 { max-width: 1280px; margin-inline: auto; }
.foot2 hr { height: 1px; border: 0; background: var(--c-line); margin: 0 0 44px; }
.foot2__g {
  display: grid; grid-template-columns: 2fr 1fr 1fr 1fr; gap: 32px;
}
.foot2__l { display: flex; flex-direction: column; gap: 10px; }
.foot2__l a, .foot2 p, .foot2 address {
  font-size: 14px; line-height: 22px; color: var(--c-mut); font-style: normal;
}
.foot2__l a { transition: color var(--d1) var(--e); }
.foot2__l a:hover { color: var(--c-ink); }
.foot2__b {
  display: flex; flex-wrap: wrap; gap: 8px 28px;
  margin-top: 48px; padding-top: 22px; border-top: 1px solid var(--c-line);
}

/* An unresolved fact, marked so it can be counted. gate.sh keeps the ledger. */

/* ═══ RESPONSIVE ═══════════════════════════════════════════════════════════ */

@media (max-width: 1180px) { .railnav { display: none; } }
@media (max-width: 1080px) {
  .nav2 { padding: 0 22px; gap: 20px; }
  .nav2__items { gap: 0; }
  .nav2__t { padding: 0 10px; font-size: 11px; letter-spacing: 1px; }
  .web2 { right: -10px; min-width: 260px; }
  .foot2 { padding: 0 22px 44px; }
  .foot2__g { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 560px) {
  /* A horizontal strip, scrolled. "WHAT WE BUILD" was wrapping to three
     lines inside a 68px bar and pushing CONTACT off the edge with no way to
     reach it — a nav item that exists and cannot be tapped is worse than one
     that was never there. nowrap plus flex:none makes the row honest about
     being wider than the phone, and it scrolls. */
  .nav2 { overflow-x: auto; overflow-y: hidden; gap: 12px; padding: 0 16px; }
  .nav2__items { gap: 0; flex: none; }
  .nav2__it { flex: none; }
  .nav2__t { white-space: nowrap; padding: 0 9px; font-size: 10.5px; letter-spacing: 0.9px; }
  .mark2 { flex: none; font-size: 17px; }
  /* The web is anchored to a trigger that can now be anywhere along a
     scrolled row, so it becomes a full-width sheet under the bar instead. */
  .web2 { position: fixed; left: 8px; right: 8px; top: var(--nav-h); min-width: 0; }
  .foot2__g { grid-template-columns: 1fr; gap: 26px; }
}
@media (prefers-reduced-motion: reduce) {
  .web2, .web2 a, .railnav i, .railnav span { transition-duration: var(--instant); }
}

/* ══ ARIA'S MENU ON A PHONE ═══════════════════════════════════════════════
   Kevin: "I don't want shit looking half done or cut off."

   Below 560px this bar was a horizontally scrolled strip, and the reasoning
   in the RESPONSIVE block above was sound — an item that exists and cannot be
   tapped is worse than one that was never there. But the result is four
   labels sliced mid-word at the right edge with nothing to say the row
   continues, and it reads as broken whether or not it scrolls.

   So it collapses, exactly like the other twenty-three pages: the strip goes,
   a burger arrives, and the panel lists every destination including the
   leaves. Built by blocks.js from the TREE it already holds.               */

.nav2__burger {
  display: none;
  width: 44px; height: 44px;
  flex: none;
  margin-left: auto;
  align-items: center; justify-content: center;
  flex-direction: column; gap: 5px;
  background: none; border: 0; padding: 0; cursor: pointer;
  border-radius: 50%;
}
.nav2__burger span {
  display: block; width: 18px; height: 1.5px;
  background: var(--c-ink); border-radius: 2px;
  transition: transform var(--d2) var(--e), opacity var(--d2) var(--e);
}
.nav2__burger[aria-expanded="true"] span:first-child { transform: translateY(3.25px) rotate(45deg); }
.nav2__burger[aria-expanded="true"] span:last-child  { transform: translateY(-3.25px) rotate(-45deg); }

.nav2__menu {
  position: fixed;
  left: 8px; right: 8px;
  top: var(--nav-h);
  max-height: calc(100vh - var(--nav-h) - 16px);
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 14px;
  border-radius: 22px;
  /* FULLY OPAQUE, and the translucent version is why. At 96% with an 18px
     backdrop blur the page still read straight through it — the headline
     behind the sheet was legible under the menu items. A desktop dropdown
     can afford to be a pane of glass because it covers a hundred pixels of
     quiet page; a phone sheet covers the whole screen, and every pixel it
     lets through is a pixel competing with a destination.

     var(--c-bg) with no mix: black on this page, cream on any other that
     ever uses this bar, because the token already resolves per page. */
  background: var(--c-bg);
  border: 1px solid var(--c-line);
  box-shadow: rgba(0,0,0,.40) 0 30px 60px -28px;
  z-index: 120;
}
.nav2__menu-g {
  font-family: var(--face-m, var(--face-mono, monospace));
  font-size: 10.5px; letter-spacing: .14em; text-transform: uppercase;
  color: var(--c-mut); margin: 14px 0 4px; padding: 0 10px;
}
.nav2__menu-g:first-child { margin-top: 2px; }
.nav2__menu-a {
  display: flex; align-items: center; gap: 10px;
  min-height: 44px; padding: 8px 10px;
  border-radius: 12px;
  font-size: 15px; color: var(--c-ink); text-decoration: none;
}
.nav2__menu-a em {
  margin-left: auto; font-style: normal;
  font-family: var(--face-m, var(--face-mono, monospace));
  font-size: 10.5px; color: var(--c-mut);
  padding: 2px 7px; border-radius: 100px;
  background: color-mix(in srgb, var(--c-ink) 8%, transparent);
}

@media (max-width: 560px) {
  .nav2__burger { display: flex; }
  /* the strip was the thing that looked cut off */
  .nav2 .nav2__items { display: none; }
  .nav2 { overflow: visible; }
}
@media (min-width: 561px) {
  .nav2__menu { display: none; }
}

@media (max-width: 560px) {
  /* The page does not scroll behind the open sheet. The sheet has its own
     max-height and overscroll-behavior:contain, so it scrolls internally.
     Caught by audit-unstyled.cjs: blocks.js was setting .nav2-open on <html>
     and no rule read it — the JS half of a feature with no CSS half, which
     is now the fifth instance of that shape this week. */
  html.nav2-open { overflow: hidden; }
}

/* The header brand link is the way back to the front door and measured 26px
   tall on /aria. Same minimum as everything else a thumb has to find. */
@media (pointer: coarse) {
  .nav2 .mark2 { min-height: 44px; display: inline-flex; align-items: center; }
}
