/* ==========================================================================
   Base — reset, typography, accessibility, shared components
   ========================================================================== */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  margin: 0;
  background: var(--bg-warm);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  line-height: var(--lh-normal);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
/* The single source of truth for the anchor offset.
   `scroll-padding-top` on the root is deliberately NOT also set: Lenis sums
   the scrollport padding and the target margin, and so do browsers for a
   fragment jump, so declaring both landed every anchor 168px from the top
   before a manual offset on top of it made it 248px. One mechanism, one
   number, and native, Lenis and Playwright all agree.
   It also stops an element scrolled to the top of the viewport from sitting
   underneath the fixed header, where a click on it is silently intercepted. */
[id], summary, .faq-item, .step, .perm, .gain, .source, .type-card {
  scroll-margin-top: calc(var(--header-total) + var(--sp-3));
}

/* No `overflow-x: hidden` here on purpose. It was a crutch added when the
   decorative canvases were full-bleed; every section that hosts one now
   clips itself, and on the root it only ever hid real overflow from the
   layout tests instead of fixing it. */

h1, h2, h3, h4, p, figure, blockquote, ul, ol, dl { margin: 0; }
ul, ol { padding: 0; list-style: none; }

img, svg, canvas, video { display: block; max-width: 100%; }
canvas { width: 100%; height: 100%; }

a { color: inherit; text-decoration: none; }

button, input, select, textarea {
  font: inherit;
  color: inherit;
  margin: 0;
}

button { background: none; border: 0; cursor: pointer; }

code {
  font-family: ui-monospace, 'SF Mono', 'Cascadia Code', Menlo, monospace;
  font-size: 0.9em;
  background: var(--indigo-tint);
  color: var(--indigo-deep);
  padding: 0.15em 0.4em;
  border-radius: 5px;
}

[hidden] { display: none !important; }

::selection { background: var(--indigo); color: #fff; }

:focus-visible {
  outline: 2px solid var(--indigo);
  outline-offset: 3px;
  border-radius: 4px;
}

/* --- Scrollers ---------------------------------------------------------
   A scroller clips an absolutely positioned descendant only if it is that
   descendant's containing block. Without this, anything positioned inside
   the comparison table escapes the scroller and widens the page. */
.table-scroll,
[data-deck],
.type-grid { position: relative; }

/* --- Utilities ---------------------------------------------------------- */

.visually-hidden {
  position: absolute;
  /* Anchored to the left edge of whatever contains it. An absolutely
     positioned element with no offsets stays at the spot it would have had
     in the flow, and in a wide table or a sideways-scrolling row that spot
     can be hundreds of pixels to the right: a 1px box there still counts
     as the page's scrollable width, and a scroller that is not itself the
     positioned container does not clip it. The comparison table has
     several hundred of these, one per cell. */
  left: 0;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: absolute;
  top: 0; left: 50%;
  transform: translate(-50%, -110%);
  z-index: var(--z-skip);
  background: var(--charcoal);
  color: var(--on-dark);
  padding: var(--sp-3) var(--sp-5);
  border-radius: 0 0 var(--r-md) var(--r-md);
  font-weight: 600;
  font-size: var(--fs-small);
  transition: transform var(--dur-base) var(--ease-out);
}
.skip-link:focus { transform: translate(-50%, 0); }

.wrap {
  width: 100%;
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.wrap-narrow { max-width: var(--wrap-narrow); }

/* --- Typographic primitives --------------------------------------------- */

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  font-size: var(--fs-eyebrow);
  font-weight: 700;
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  color: var(--ink-soft);
  margin-bottom: var(--sp-4);
}
/* Without one, the headline moves up to where the label used to sit, so acts
   stay on the same optical baseline. */
.act-head > h2:first-child { margin-top: 0; }

.act-title {
  font-size: var(--fs-h2);
  line-height: var(--lh-snug);
  letter-spacing: var(--tracking-display);
  font-weight: 700;
  text-wrap: balance;
  max-width: 20ch;
}

.act-lede {
  margin-top: var(--sp-5);
  font-size: var(--fs-lede);
  line-height: var(--lh-loose);
  color: var(--ink-soft);
  max-width: 62ch;
  text-wrap: pretty;
}
.act-lede em { font-style: italic; color: var(--ink); }

/* --- Header ------------------------------------------------------------- */

.site-header {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: var(--z-header);
  /* viewport-fit=cover lets content run under a notch, so the header adds
     the inset back itself. Everywhere it is zero, this is just --header-h. */
  padding-top: env(safe-area-inset-top);
  height: var(--header-total);
  display: flex;
  align-items: center;
  transition: background var(--dur-base) var(--ease-out),
              border-color var(--dur-base) var(--ease-out),
              box-shadow var(--dur-base) var(--ease-out);
  border-bottom: 1px solid transparent;
}
.site-header.is-stuck {
  background: color-mix(in srgb, var(--bg-warm) 82%, transparent);
  backdrop-filter: blur(16px) saturate(1.4);
  -webkit-backdrop-filter: blur(16px) saturate(1.4);
  border-bottom-color: var(--line);
}

.nav-row {
  display: flex;
  align-items: center;
  gap: var(--sp-5);
  width: 100%;
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  font-weight: 800;
  letter-spacing: var(--tracking-tight);
  font-size: 1.0625rem;
  flex-shrink: 0;
  /* A full thumb target. It measured 30px tall, which is under both the 44px
     convention and comfortable on a phone. Negative margin keeps the visual
     row height unchanged so the header does not grow. */
  min-height: 44px;
  margin-block: -7px;
  padding-inline: 2px;
  border-radius: var(--r-sm);
}
/* The brand mark: a stack of four cards, knocked out of whatever tile it
   sits on. The tile is the caller's (indigo in the nav, a translucent
   indigo in the footer), and the mark's own depth is carried by per-card
   opacity in the markup — so one drawing works on every surface here and
   still reads correctly with JS off, where assets/js/brand.js never runs.

   Sized by HEIGHT alone against the mark's 323x392 ink box: setting both
   width and height would pin it to a box that does not match its aspect,
   and the stack would come out subtly squashed. */
.brand-mark {
  display: grid;
  place-items: center;
  width: 30px; height: 30px;
  border-radius: 9px;
  background: var(--indigo);
  color: #fff;
  box-shadow: 0 2px 8px rgba(101, 76, 244, 0.35);
  overflow: hidden;
}
.brand-mark__svg {
  display: block;
  height: 22px;
  width: auto;
  overflow: visible;
}

.nav-links {
  display: flex;
  gap: var(--sp-5);
  margin-inline: auto;
  font-size: var(--fs-small);
  font-weight: 500;
  color: var(--ink-soft);
}
.nav-links a {
  position: relative;
  display: inline-flex;
  align-items: center;
  min-height: 40px;
  transition: color var(--dur-fast) var(--ease-out);
}
.nav-links a::after {
  content: '';
  position: absolute;
  left: 0; right: 100%; bottom: 0;
  height: 1.5px;
  background: var(--indigo);
  transition: right var(--dur-base) var(--ease-out);
}
.nav-links a:hover { color: var(--ink); }
.nav-links a:hover::after { right: 0; }

.nav-actions { display: flex; align-items: center; gap: var(--sp-3); flex-shrink: 0; }

/* --- Buttons ------------------------------------------------------------ */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  padding: 0.8125rem 1.5rem;
  border-radius: var(--r-pill);
  font-size: var(--fs-small);
  font-weight: 700;
  letter-spacing: -0.01em;
  white-space: nowrap;
  transition: transform var(--dur-fast) var(--ease-out),
              box-shadow var(--dur-base) var(--ease-out),
              background var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out);
}
.btn-sm { padding: 0.5rem 1rem; font-size: 0.8125rem; }
.btn-block { width: 100%; }

.btn-indigo {
  background: var(--indigo);
  color: #fff;
  box-shadow: 0 1px 2px rgba(20,16,31,.12), 0 8px 20px rgba(101,76,244,.28);
}
.btn-indigo:hover { background: var(--indigo-deep); transform: translateY(-1px); box-shadow: 0 2px 4px rgba(20,16,31,.12), 0 14px 32px rgba(101,76,244,.34); }
.btn-indigo:active { transform: translateY(0); }

.btn-ghost {
  background: transparent;
  color: var(--ink);
  box-shadow: inset 0 0 0 1.5px var(--line-strong);
}
.btn-ghost:hover { box-shadow: inset 0 0 0 1.5px var(--ink-soft); background: rgba(20,16,31,.02); transform: translateY(-1px); }
.btn-lg { min-height: 52px; padding: 0.9375rem 1.625rem; font-size: 0.9375rem; }
/* On a charcoal card: paper, then indigo tint on hover. */
.btn-light {
  background: var(--surface);
  color: var(--ink);
  box-shadow: 0 1px 2px rgba(0,0,0,.2), 0 8px 24px rgba(0,0,0,.25);
}
.btn-light:hover { background: var(--indigo-tint); transform: translateY(-1px); }
/* The app's reveal colour. Used once per screen at most: it is the spark. */
.btn-amber {
  background: var(--amber);
  color: var(--charcoal);
  box-shadow: 0 1px 2px rgba(0,0,0,.2), 0 10px 28px rgba(244,203,61,.3);
}
.btn-amber:hover { background: #F7D65F; transform: translateY(-1px); box-shadow: 0 2px 4px rgba(0,0,0,.2), 0 14px 34px rgba(244,203,61,.38); }
/* A secondary action on a dark surface. */
.btn-quiet {
  background: transparent;
  color: var(--on-dark);
  box-shadow: inset 0 0 0 1.5px rgba(255,255,255,.28);
}
.btn-quiet:hover { background: rgba(255,255,255,.08); box-shadow: inset 0 0 0 1.5px rgba(255,255,255,.45); }
.btn:active { transform: translateY(0) scale(0.985); }
/* Icons inside a button move with the intent of the action: the arrow leans
   forward, the play mark steps in. */
.btn-arrow, .btn-play { flex-shrink: 0; transition: transform var(--dur-base) var(--ease-out); }
.btn:hover .btn-arrow { transform: translateX(3px); }
.btn:hover .btn-play { transform: scale(1.12); }

/* --- Motion toggle ------------------------------------------------------ */

.motion-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  font-size: var(--fs-micro);
  font-weight: 600;
  color: var(--ink-soft);
  /* 44px, not the 31px it was: this is a thumb-sized control and it was
     failing WCAG 2.5.8 (24px minimum) on every touch device. */
  min-height: 44px;
  padding: 0.4rem 0.9rem;
  border-radius: var(--r-pill);
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.motion-toggle:hover { background: rgba(20,16,31,.05); color: var(--ink); }
.motion-dot {
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--sage);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--sage) 22%, transparent);
  transition: background var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}
[data-motion="off"] .motion-dot { background: var(--ink-faint); box-shadow: 0 0 0 3px rgba(20,16,31,.08); }

/* --- Footer ------------------------------------------------------------- */

.site-footer {
  background: var(--charcoal);
  color: var(--on-dark-soft);
  padding-block: var(--sp-10) var(--sp-6);
  margin-top: var(--sp-11);
  font-size: var(--fs-small);
}
.footer-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.6fr) repeat(3, minmax(0, 1fr));
  gap: var(--sp-7);
  padding-bottom: var(--sp-8);
  border-bottom: 1px solid rgba(255,255,255,.09);
}
.footer-brand .brand { color: var(--on-dark); }
.footer-brand .brand-mark { background: rgba(101,76,244,.25); box-shadow: none; }
.footer-tag { margin-top: var(--sp-4); font-size: var(--fs-body); color: var(--on-dark); font-weight: 500; }
.footer-note { margin-top: var(--sp-2); max-width: 34ch; color: var(--on-dark-faint); }
.footer-note a {
  color: var(--indigo-lift);
  text-decoration: underline;
  text-underline-offset: 3px;
  /* Inline in a sentence, so WCAG 2.5.8 exempts it. Padded anyway: a 44px
     target costs no visual space here and stops the exemption being one
     stylesheet edit away from being load-bearing. */
  display: inline-block;
  padding-block: 10px;
  margin-block: -10px;
}

/* Was `.footer-col h2`, which put three footer column labels into the site's
   heading outline as level-2 siblings of the homepage's thirteen content
   headings. A screen-reader user navigating by heading got sixteen level-2
   stops, a third of which were nav labels. These are labels, not sections, so
   they are a <p> with this class. */
.footer-col-title {
  font-size: var(--fs-eyebrow);
  text-transform: uppercase;
  letter-spacing: var(--tracking-eyebrow);
  color: var(--on-dark-faint);
  font-weight: 700;
  margin-bottom: var(--sp-4);
}
.footer-col li + li { margin-top: 2px; }
/* 44px rows. Fourteen links at an 18px line box is a 26px tap pitch, which
   is below the 24px WCAG 2.5.8 floor even before the padding is considered,
   and well short of anything comfortable on a thumb. */
.footer-col a {
  display: flex;
  align-items: center;
  min-height: 44px;
  padding-inline: var(--sp-2);
  margin-inline: calc(var(--sp-2) * -1);
  border-radius: var(--r-sm);
  transition: color var(--dur-fast) var(--ease-out), background var(--dur-fast) var(--ease-out);
}
.footer-col a:hover { color: var(--on-dark); background: rgba(255, 255, 255, 0.05); }

.footer-bottom {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  padding-top: var(--sp-6);
  font-size: var(--fs-micro);
  color: var(--on-dark-faint);
}
.footer-motion .motion-toggle { color: var(--on-dark-soft); }
.footer-motion .motion-toggle:hover { background: rgba(255,255,255,.06); color: var(--on-dark); }

/* --- Forms -------------------------------------------------------------- */

.field { display: flex; flex-direction: column; gap: var(--sp-2); }
.field label { font-size: var(--fs-small); font-weight: 600; color: var(--ink); }

.field input[type='email'],
.field select {
  width: 100%;
  padding: 0.8125rem 1rem;
  border-radius: var(--r-md);
  border: 1.5px solid var(--line-strong);
  background: var(--surface);
  color: var(--ink);
  font-size: var(--fs-body);
  transition: border-color var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}
.field input[type='email']::placeholder { color: var(--ink-faint); }
.field input[type='email']:focus,
.field select:focus {
  outline: none;
  border-color: var(--indigo);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--indigo) 18%, transparent);
}
.field input:user-invalid { border-color: #C4405A; }
.field select {
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M2.5 4.5L6 8l3.5-3.5' fill='none' stroke='%235B5568' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0.9rem center;
  background-size: 12px;
  padding-right: 2.5rem;
}

.field-check {
  flex-direction: row;
  align-items: center;
  gap: var(--sp-3);
  min-height: 44px;
}
/* The label is the real target: it is `for`-bound, so the whole 44px row
   toggles the box. The 18px control itself stays a control, not a target. */
.field-check input { width: 24px; height: 24px; accent-color: var(--indigo); flex-shrink: 0; }
.field-check label {
  font-weight: 500;
  color: var(--ink-soft);
  font-size: var(--fs-small);
  line-height: 1.5;
  align-self: stretch;
  display: flex;
  align-items: center;
  cursor: pointer;
}

.honeypot { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* --- Analytics consent ----------------------------------------------------
   A floating card rather than a full-width strip, so it never sits on top of
   the footer's controls on a wide screen. On a phone it spans the width, and
   assets/js/consent.js pads the page bottom by its height while it is open
   so the last line of the page can still be scrolled clear of it. */
.consent {
  position: fixed;
  z-index: 9999;
  left: max(var(--sp-4), env(safe-area-inset-left));
  right: max(var(--sp-4), env(safe-area-inset-right));
  bottom: max(var(--sp-4), env(safe-area-inset-bottom));
  max-width: 440px;
  display: grid;
  gap: var(--sp-4);
  padding: var(--sp-5);
  border-radius: var(--r-lg);
  background: var(--charcoal);
  color: var(--on-dark);
  box-shadow: var(--sh-dark), 0 0 0 1px rgba(255,255,255,.06) inset;
  font-size: var(--fs-small);
  line-height: 1.55;
  animation: consentIn 420ms var(--ease-out) both;
}
.consent-text { margin: 0; color: var(--on-dark-soft); }
.consent-text a { color: var(--indigo-lift); text-decoration: underline; text-underline-offset: 3px; }
.consent-actions { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.consent .btn { min-height: 44px; }
html.consent-open body { padding-bottom: var(--consent-h, 0px); }
@keyframes consentIn { from { opacity: 0; transform: translateY(16px); } to { opacity: 1; transform: none; } }
@media (min-width: 641px) {
  .consent { left: max(var(--sp-5), env(safe-area-inset-left)); right: auto; bottom: var(--sp-5); }
}

.form-status {
  font-size: var(--fs-small);
  font-weight: 600;
  min-height: 1.4em;
}
.form-status[data-tone='ok'] { color: var(--sage-strong); }
.form-status[data-tone='error'] { color: #C4405A; }
.form-status[data-tone='info'] { color: var(--ink-soft); }

.form-preview-note,
.noscript-note {
  font-size: var(--fs-micro);
  color: var(--ink-soft);
  background: #FFF6DC;
  border: 1px solid #F0DFA8;
  color: #6B5312;
  padding: var(--sp-3);
  border-radius: var(--r-sm);
  line-height: 1.5;
}
.noscript-note a { text-decoration: underline; }

/* --- Inline prose links ------------------------------------------------
   The 24px WCAG 2.5.8 floor, applied to every link that sits inside a
   sentence. Strictly, 2.5.8 exempts inline targets, so this is a
   self-imposed floor rather than a compliance one, and it is worth it: a
   21px-tall link in running text is genuinely hard to hit on a phone.

   `min-height` rather than `padding`, deliberately. These links sit on
   their paragraph's line box, so vertical padding would push the line
   spacing apart and change the rhythm of the prose; an inline-block box of
   24px simply straddles the descender without moving anything. Padding is
   only added where the horizontal box would otherwise be under 24px,
   which is the other half of the same requirement.

   This rule lives in base.css rather than per-component because the
   failure was never local: `.act-note a`, `.compare-sources a`,
   `.answer-first-body a` and `.pull-quote a` each carried their own
   attempt at this, three of the four did not have one at all, and the
   homepage does not load content.css where two of them were declared. */
p a,
li a,
dd a,
figcaption a,
blockquote a,
.summary a {
  display: inline-block;
  min-height: 24px;
  min-width: 24px;
}

/* --- Scroll-reveal ------------------------------------------------------ */

/* Default is VISIBLE. The reveal only ever hides elements once JS has
   confirmed it will un-hide them again — a crawler, a no-JS browser, or a
   script that dies mid-flight all still get the full page. */
[data-reveal] { opacity: 1; }
html.js [data-reveal] {
  opacity: 0;
  transform: translate3d(0, 22px, 0);
  transition: opacity var(--dur-slow) var(--ease-out),
              transform var(--dur-slow) var(--ease-out);
  transition-delay: var(--d, 0s);
  will-change: opacity, transform;
}
html.js [data-reveal].is-in {
  opacity: 1;
  transform: none;
  /* Release the compositor layer once the transition is over.

     `will-change` is a standing promise to the browser, and this page puts it
     on 40+ elements at once, permanently. That is 40+ promoted layers held in
     memory for the life of the document, on a site whose stated audience is
     mid-range Android — the exact hardware where layer memory is the scarce
     resource. Promoting for the duration of the transition is what will-change
     is for. */
  will-change: auto;
}

/* --- Reduced motion ----------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  html.js [data-reveal] { opacity: 1; transform: none; transition: none; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* --- Smooth scroll (Lenis) ----------------------------------------------
   Lenis's own recommended rules, which were missing. Without the second one
   the page's `scroll-behavior: smooth` (the right thing without Lenis, for
   anchor jumps and for touch) animates every one of Lenis's per-frame
   position updates a second time, and the glide stutters and lags the hand. */
html.lenis, html.lenis body { height: auto; }
.lenis.lenis-smooth { scroll-behavior: auto !important; }
.lenis.lenis-smooth [data-lenis-prevent] { overscroll-behavior: contain; }
.lenis.lenis-stopped { overflow: clip; }
/* Paused: an ordinary scroll, and anchors jump. */
html[data-motion='off'] { scroll-behavior: auto; }

/* Scroll lock while the nav sheet is open. `overflow: hidden` on the root is
   what actually stops iOS Safari dragging the page behind the dialog; the
   `overscroll-behavior` stops the rubber-band chaining up into the URL bar. */
html.nav-open, html.nav-open body { overflow: hidden; overscroll-behavior: none; }

/* ══════════════════════════════════════════════════════════════════════
   NAVIGATION SHEET
   The header's own link list is `display: none` below 900px, which left
   every story act unreachable on a phone or a tablet. This is the
   replacement. Native <dialog> supplies focus trapping, Escape, ::backdrop
   and background inerting, so the only thing to build is the sheet itself.
   ══════════════════════════════════════════════════════════════════════ */

.nav-toggle {
  display: none;
  place-items: center;
  width: 44px;
  height: 44px;
  margin-right: -6px;
  border-radius: var(--r-sm);
  color: var(--ink);
  flex-shrink: 0;
}
.nav-toggle:hover { background: rgba(20, 16, 31, 0.05); }

.nav-toggle-bars { display: grid; gap: 5px; width: 20px; }
.nav-toggle-bars i {
  display: block;
  height: 1.8px;
  border-radius: 2px;
  background: currentColor;
  transition: transform var(--dur-base) var(--ease-out),
              opacity var(--dur-fast) var(--ease-out);
}
.nav-toggle[aria-expanded='true'] .nav-toggle-bars i:nth-child(1) { transform: translateY(6.8px) rotate(45deg); }
.nav-toggle[aria-expanded='true'] .nav-toggle-bars i:nth-child(2) { opacity: 0; }
.nav-toggle[aria-expanded='true'] .nav-toggle-bars i:nth-child(3) { transform: translateY(-6.8px) rotate(-45deg); }

.nav-sheet {
  width: 100%;
  max-width: none;
  max-height: 100dvh;
  height: 100dvh;
  margin: 0;
  padding: 0;
  border: 0;
  background: var(--charcoal);
  color: var(--on-dark);
  overflow-y: auto;
  overscroll-behavior: contain;
  /* Paint from the top edge on iOS, where a dialog is centred by default. */
  inset: 0 0 auto 0;
}

.nav-sheet::backdrop { background: rgba(14, 10, 32, 0.6); }

.nav-sheet[open] { animation: sheetIn 320ms var(--ease-out) both; }
.nav-sheet[open]::backdrop { animation: fadeIn 240ms var(--ease-out) both; }
@keyframes sheetIn { from { opacity: 0; transform: translateY(-2.5%); } to { opacity: 1; transform: none; } }
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }

.nav-sheet-inner {
  display: flex;
  flex-direction: column;
  min-height: 100%;
  padding:
    max(var(--sp-5), env(safe-area-inset-top))
    max(var(--gutter), env(safe-area-inset-right))
    max(var(--sp-6), env(safe-area-inset-bottom))
    max(var(--gutter), env(safe-area-inset-left));
}

.nav-sheet-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  padding-bottom: var(--sp-5);
  border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.nav-sheet-head .brand { color: var(--on-dark); }
.nav-sheet-head .brand-mark { background: rgba(101, 76, 244, 0.25); box-shadow: none; }

.nav-sheet-close {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  margin-right: -8px;
  border-radius: var(--r-sm);
  color: var(--on-dark-soft);
}
.nav-sheet-close:hover { background: rgba(255, 255, 255, 0.08); color: var(--on-dark); }

.nav-sheet-links { display: grid; padding-block: var(--sp-3); }
.nav-sheet-links a {
  display: flex;
  flex-direction: column;
  gap: 2px;
  /* 52px rows: comfortably past the 44px touch minimum with room for the
     secondary line, and a comfortable repeat-tap rhythm. */
  padding: var(--sp-3) var(--sp-2);
  border-bottom: 1px solid rgba(255, 255, 255, 0.07);
  border-radius: var(--r-sm);
  transition: background var(--dur-fast) var(--ease-out);
}
.nav-sheet-links a:hover { background: rgba(255, 255, 255, 0.05); }
.nav-sheet-links span { font-size: 1.0625rem; font-weight: 700; letter-spacing: -0.015em; }
.nav-sheet-links em { font-style: normal; font-size: var(--fs-micro); color: var(--on-dark-faint); }

.nav-sheet-cta { margin-top: auto; padding-block: 1rem; }
.nav-sheet-note {
  margin-top: var(--sp-4);
  text-align: center;
  font-size: var(--fs-micro);
  color: var(--on-dark-faint);
}

@media (max-width: 900px) {
  /* Only reachable with JS: a <dialog> needs script to open, so a toggle
     that cannot do anything is worse than no toggle at all. */
  html.js .nav-toggle { display: grid; }
  .nav-actions .btn-sm { display: none; }
}

@media (min-width: 901px) {
  /* Never leave a hidden dialog in the accessibility tree on a wide screen
     where the inline nav is showing instead. */
  .nav-sheet { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .nav-sheet[open], .nav-sheet[open]::backdrop { animation: none; }
}
