/* ─────────────────────────────────────────────────────────────
   AION · every stylesheet wall.html loads, in one request.
   GENERATED by _tools/seo/css-bundle.mjs — do not edit.
   Edit the sheets in this directory and rebuild.

   ONE PER PAGE, not one for the site: base.css sets
   `html,body{overflow:hidden}` for the Gate, and giving that to
   the other pages made them unscrollable — the declaration sat
   below the fold with no way to reach it. A sheet a page did not
   load is not harmless when you hand it over.
   ───────────────────────────────────────────────────────────── */

/* ══ fonts.css ══════════════════════════════════════════ */
/* ─────────────────────────────────────────────────────────────
   GENERATED by _tools/seo/fonts.mjs — do not edit by hand.

   Self-hosted, for the reason tokens.css already gives about the Old
   Persian face: a project published to outlive every company involved
   should not ask one of them for its letters on every page load.

   Cormorant Garamond and IBM Plex Mono are both SIL Open Font License
   1.1; the licences ship beside the files in site/fonts/.

   font-display: swap is kept from the original request — text paints
   immediately in the fallback stack and reflows once, which is the
   right trade on a gateway that can be cold.
   ───────────────────────────────────────────────────────────────── */
@font-face {
  font-family: "Cormorant Garamond";
  font-style: italic;
  font-weight: 300;
  font-display: swap;
  src: url("../fonts/cormorant-garamond-300-italic-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Cormorant Garamond";
  font-style: italic;
  font-weight: 300;
  font-display: swap;
  src: url("../fonts/cormorant-garamond-300-italic-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Cormorant Garamond";
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/cormorant-garamond-400-italic-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Cormorant Garamond";
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/cormorant-garamond-400-italic-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Cormorant Garamond";
  font-style: normal;
  font-weight: 300;
  font-display: swap;
  src: url("../fonts/cormorant-garamond-300-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Cormorant Garamond";
  font-style: normal;
  font-weight: 300;
  font-display: swap;
  src: url("../fonts/cormorant-garamond-300-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Cormorant Garamond";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/cormorant-garamond-400-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Cormorant Garamond";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/cormorant-garamond-400-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Cormorant Garamond";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("../fonts/cormorant-garamond-500-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Cormorant Garamond";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("../fonts/cormorant-garamond-500-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Cormorant Garamond";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("../fonts/cormorant-garamond-600-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Cormorant Garamond";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("../fonts/cormorant-garamond-600-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 200;
  font-display: swap;
  src: url("../fonts/ibm-plex-mono-200-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 200;
  font-display: swap;
  src: url("../fonts/ibm-plex-mono-200-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 300;
  font-display: swap;
  src: url("../fonts/ibm-plex-mono-300-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 300;
  font-display: swap;
  src: url("../fonts/ibm-plex-mono-300-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/ibm-plex-mono-400-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/ibm-plex-mono-400-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("../fonts/ibm-plex-mono-500-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("../fonts/ibm-plex-mono-500-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ══ tokens.css ══════════════════════════════════════════ */
/* ============================================================
   tokens.css
   Design tokens — every visible value in the system flows
   from this file. Components reference variables; they do
   not hard-code values.
   ============================================================ */

/* ── [hidden] must mean hidden ─────────────────────────────
   The user-agent rule is `[hidden] { display: none }`, and ANY author
   `display` declaration outranks it — author normal beats UA normal
   whatever the specificity. So `.wv3 { display: grid }` beside
   `<section class="wv3" hidden>` left the section on screen.

   base.css has carried this guard for a long time, but base.css is
   loaded by index.html alone. tokens.css is the one stylesheet every
   page loads, so the guard belongs here. wall.css had already patched
   two symptoms of it one selector at a time (`.vt-row[hidden]`,
   `.vt-note[hidden]`) — that is the shape of a rule missing from the
   base layer. */
[hidden] { display: none !important; }

/* ── Old Persian Cuneiform · self-hosted for permanence ────
   The script in which Cyrus and Darius wrote their
   declarations on stone. AION inherits the lineage:
   carved letters meant to outlast empires. */
@font-face {
  font-family: "Noto Sans Old Persian";
  src: url("../fonts/NotoSansOldPersian-Regular.ttf") format("truetype");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

:root {
  /* ── Palette ─────────────────────────────────────────────
     The AION palette is restrained: pure void, warm bone,
     a single sacred gold (representing φ, the architect's
     percentage), one ember for living elements, and crimson
     reserved exclusively for errors. */

  --void:        #000000;
  --abyss:       #02040a;
  --bone:        #ede8d8;
  --bone-dim:    rgba(237, 232, 216, 0.55);
  --bone-faint:  rgba(237, 232, 216, 0.28);
  --bone-ghost:  rgba(237, 232, 216, 0.10);
  --bone-trace:  rgba(237, 232, 216, 0.04);
  --gold:        #d4ad4f;
  --gold-deep:   #a8842f;
  --gold-soft:   rgba(212, 173, 79, 0.55);
  --gold-glow:   rgba(212, 173, 79, 0.18);
  --gold-trace:  rgba(212, 173, 79, 0.04);
  --ember:       #ff7a3d;
  --crimson:     #b53a3a;
  --jade:        #5fb392;

  /* ── Typography ─────────────────────────────────────── */

  /* Both families are self-hosted, like the Old Persian face above and for the
     same reason — see styles/fonts.css and _tools/seo/fonts.mjs. Nothing on
     this site asks a third party for its letters (O9, closed 2026-08-13).
     The fallbacks stay: a face that fails to load must degrade to something
     chosen, not to whatever the browser reaches for. */
  --font-display:   "Cormorant Garamond", Georgia, "Times New Roman", serif;
  --font-mono:      "IBM Plex Mono", ui-monospace, "SF Mono", Menlo, monospace;
  --font-cuneiform: "Noto Sans Old Persian", "Noto Sans", "Times New Roman", serif;

  /* ── Z-index ladder ─────────────────────────────────── */

  --z-atmosphere:  0;
  --z-cosmos:      1;
  --z-instrument:  4;
  --z-chrome:      6;
  --z-grain:      50;
  --z-modal:     100;
  --z-toast:     200;

  /* ── Focus ──────────────────────────────────────────── */

  --focus-ring:    var(--gold);
  --focus-width:   2px;
  --focus-offset:  3px;
}

/* ════ FOCUS · where the keyboard is ══════════════════════
   Measured before this existed: on the Hall, 31 of 32 focusable controls
   showed no ring at all, and on the Gate 6 of 7. Someone navigating by
   keyboard — because they prefer to, or because they must — could tab
   through the whole site without ever seeing where they were.

   Four stylesheets also carried a bare `outline: none` on inputs, removing
   the browser's own ring and putting nothing back. Each of those had a
   border-colour change beside it, which is a hint, not a focus indicator:
   it is invisible to anyone who cannot distinguish those two golds.

   `:focus-visible`, not `:focus` — a mouse click on a button should not
   leave a ring behind it. That is the whole reason the pseudo-class exists.

   This lives in tokens.css because it is the one stylesheet all eight pages
   load. A rule that ships on seven pages out of eight is a rule that is
   wrong on the eighth. ════════════════════════════════════════════════ */

:where(a, button, input, textarea, select, summary, [tabindex]):focus-visible {
  outline: var(--focus-width) solid var(--focus-ring);
  outline-offset: var(--focus-offset);
  border-radius: 2px;
}

/* `:where()` above keeps specificity at zero, so any component can still
   style its own focus without fighting this.

   The eight `outline: none` rules in this project are all written as `:focus`,
   which also fires on a mouse click — that is why they were written, and it is
   also why they take the ring away from the keyboard. This wins them back for
   `:focus-visible` alone, by class rather than by listing every id: a list is
   a thing that goes one short, and it did — `.wv3-input` and one more were
   missing from the first attempt, found by measuring every page rather than
   by reading the CSS. */
[class*="-input"]:focus-visible,
[class*="-secret"]:focus-visible,
[class*="-ref-link"]:focus-visible,
[class*="-text"]:focus-visible,
#cmpText:focus-visible,
input:focus-visible,
textarea:focus-visible {
  outline: var(--focus-width) solid var(--focus-ring) !important;
  outline-offset: 1px;
}

/* Anything that scrolls should be reachable by keyboard, and a scrollable
   region only receives focus if it can hold it. */
@media (prefers-reduced-motion: no-preference) {
  :where(html) { scroll-behavior: smooth; }
}

/* ════ TOUCH · the target, not the letters ════════════════
   Measured at 390px, the width of a phone: the back link on every page is
   13px tall, `.soul` is 11px, `.rail-room` is 23px. WCAG 2.2 asks for 24×24
   (SC 2.5.8) and a finger asks for more.

   The letters are not the problem and must not change. Fine monospace at wide
   tracking IS this project's face — an instrument's engraving, not a web UI —
   and setting a 16px minimum on it would be vandalism dressed as compliance.

   So the *target* grows and the *type* does not: a pseudo-element centred on
   the link, stretched to 24px tall, outside the flow so no layout moves. The
   link looks identical and is twice as easy to hit. ═══════════════════════ */

.wall-back, .wall-codex, .witness-back, .codex-back, .mc-back, .mc-codex,
.wall-archive a, .rail-room, .bezel-nav a, .ld-nav a, .codex-lang a,
.witness-nav a, .ld-bar-nav a, .ld-footer-nav a,
.soul, .link, .link-mute {
  position: relative;
}
.wall-back::after, .wall-codex::after, .witness-back::after, .codex-back::after,
.mc-back::after, .mc-codex::after, .wall-archive a::after, .rail-room::after,
.codex-lang a::after, .witness-nav a::after, .ld-bar-nav a::after,
.ld-footer-nav a::after, .soul::after, .link::after, .link-mute::after {
  content: "";
  position: absolute;
  /* Both axes. The first version stretched height only, and `.soul` — an
     11×11 dot on the Gate — stayed 11px wide and therefore still unhittable.
     A target is the smaller of its two sides. */
  left: 50%; top: 50%;
  translate: -50% -50%;
  min-width: 24px;  width: 100%;
  min-height: 24px; height: 100%;
  /* Behind the text, and it must never eat a click meant for something else. */
  z-index: -1;
}

/* `.bezel-nav a` already draws its own underline in ::after — give that one
   the room instead of a second pseudo-element it does not have. */
.bezel-nav a::after, .ld-nav a::after { content: none; }
.bezel-nav a, .ld-nav a { padding-block: 6px; }

/* ════ TYPE · a floor, on small screens only ══════════════
   Nine classes render below 9px on a phone, three at 8px. That is legible on
   a 27-inch display at arm's length and not on a phone in daylight. The floor
   is raised only where the screen is small, so the desktop composition —
   which was designed at these sizes and looked at — is untouched.

   The `html` prefix is load-bearing, not decoration. tokens.css is the FIRST
   stylesheet every page loads, so on equal specificity it loses every tie to
   the page's own sheet — the first version of this block set nothing at all,
   and measured as having changed nothing. ════════════════════════════════ */

@media (max-width: 560px) {
  html .stat-key, html .rail-count, html .room-count,
  html .placard-by, html .placard-when, html .placard-sealed,
  html .manifesto-source, html .codex-mute, html .wv3-split,
  html .meta-key, html .ld-eyebrow, html .ld-footer-fine,
  html .key, html .sep,
  /* The chain badge's label is a bare <span> with no class of its own, so it
     has to be reached through its parent. Found by measuring, not by reading
     the markup — a class list will always miss the elements that have none. */
  html .chain-status, html .chain-status span,
  /* The Hall's pulse (P2) and the sealed room's countdown (P3) arrived after
     this list was written and inherited the same 9px the rest of the hall's
     metadata had before the floor existed. A rule that covers nine classes
     out of eleven is wrong on the two. */
  html .pulse-title, html .pulse-amt, html .pulse-when {
    font-size: 10px;
  }
  html .placard-id, html .coll, html .rail-when { font-size: 10.5px; }

  /* The composer is where a person writes the thing they came here to write,
     on the device most of them will use. Its labels — "For whom", "A Wallet",
     "A future date" — were rendering at 9px, which is the smallest type on
     the site being asked to carry the most important decision on it. */
  html .cmp-choice-label,
  html .cmp-premium,
  html .cmp-meta, html .cmp-meta span { font-size: 10.5px; }
  html .cmp-seg button, html .cmp-seg .on { font-size: 11px; }
  html .wallet-pill, html .wallet-pill span { font-size: 10px; }
}

/* ══ atmosphere.css ══════════════════════════════════════════ */
/* ============================================================
   atmosphere.css
   The shared cosmic backdrop — loaded by every page so the
   homepage, the wall, and the witness viewer all breathe the
   same void: a drifting coloured nebula, a depth vignette, and
   a film of grain over it all. The star field (canvas #cosmos)
   is painted on top by ui/starfield.js.
   ============================================================ */

/* ── Drifting nebula + vignette ───────────────────────────── */
.atmosphere {
  position: fixed;
  inset: 0;
  z-index: var(--z-atmosphere, 0);
  pointer-events: none;
  background:
    radial-gradient(ellipse at 50% 46%,
      rgba(40, 24, 80, 0.16) 0%,
      rgba(8, 4, 20, 0.06) 34%,
      transparent 64%),
    radial-gradient(ellipse at 50% 56%,
      rgba(150, 110, 50, 0.06) 0%,
      transparent 50%);
}

.atmosphere::before {
  content: "";
  position: absolute;
  inset: -16%;
  pointer-events: none;
  background:
    radial-gradient(34% 30% at 27% 29%, rgba(64, 42, 128, 0.13), transparent 70%),
    radial-gradient(30% 27% at 75% 35%, rgba(22, 84, 96, 0.10), transparent 70%),
    radial-gradient(33% 30% at 62% 75%, rgba(156, 104, 42, 0.09), transparent 70%),
    radial-gradient(26% 24% at 31% 73%, rgba(96, 40, 96, 0.09), transparent 70%);
  filter: blur(30px);
  animation: nebula-drift 52s ease-in-out infinite,
             nebula-hue   60s ease-in-out infinite;
}

.atmosphere::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: radial-gradient(ellipse at 50% 50%,
    transparent 38%,
    rgba(0, 0, 0, 0.42) 76%,
    rgba(0, 0, 0, 0.80) 100%);
}

/* ── Film grain · analogue warmth atop digital perfection ─── */
.grain {
  position: fixed;
  inset: 0;
  z-index: var(--z-grain, 50);
  pointer-events: none;
  opacity: 0.04;
  mix-blend-mode: screen;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='220' height='220'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.92' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 1  0 0 0 0 1  0 0 0 0 1  0 0 0 0.55 0'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>");
}

/* Keyframes (nebula-drift / nebula-hue) live in motion.css, which
   the homepage already loads. The wall and witness pages inline a
   copy below so they animate without pulling the whole motion set. */
@keyframes nebula-drift {
  0%   { transform: translate(-2%, -1%) scale(1.06); }
  50%  { transform: translate(2%,  2%)  scale(1.14); }
  100% { transform: translate(-2%, -1%) scale(1.06); }
}
@keyframes nebula-hue {
  0%, 100% { filter: blur(30px) hue-rotate(-18deg) saturate(1); }
  50%      { filter: blur(30px) hue-rotate( 22deg) saturate(1.2); }
}

/* ── Page-enter · emerge from the dark ─────────────────────
   A black veil that fades out on load, so arriving from the Hall's
   zoom transition feels continuous — you emerge from the glow. */
.page-enter {
  position: fixed;
  inset: 0;
  z-index: 500;
  pointer-events: none;
  background: #000;
  animation: page-enter-fade 1s ease-out forwards;
}
@keyframes page-enter-fade {
  from { opacity: 1; }
  to   { opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .atmosphere::before { animation: none !important; }
  .page-enter { animation: none !important; opacity: 0 !important; }
}

/* ══ wall.css ══════════════════════════════════════════ */
/* ============================================================
   wall.css
   The Hall — a museum of declarations. Rooms by month, a guide
   rail down the side, and one vitrine for the work being looked
   at. Everything else in here serves those three.
   ============================================================ */

* { margin: 0; padding: 0; box-sizing: border-box; }

html, body {
  width: 100%;
  min-height: 100%;
  background: var(--void);
  color: var(--bone);
  font-family: var(--font-display);
  -webkit-font-smoothing: antialiased;
  text-rendering: geometricPrecision;
  /* `clip`, never `hidden`.
     `overflow-x: hidden` forces the other axis to `auto`, which makes body a
     scroll container — and a sticky element sticks to its nearest scrolling
     ancestor. That ancestor would be a body that does not itself scroll, so
     the guide rail never stuck at any width, on any device. `clip` holds back
     the same horizontal overflow without creating a scroll container. */
  overflow-x: clip;
}

a { color: inherit; text-decoration: none; }
em { font-style: italic; color: var(--bone); }

/* Cosmos backdrop */
#cosmos {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
}

/* Grain */
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  opacity: 0.04;
  mix-blend-mode: screen;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='220' height='220'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.92' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 1  0 0 0 0 1  0 0 0 0 1  0 0 0 0.55 0'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>");
}

/* ── Header ──────────────────────────────────────────── */

.wall-header {
  position: relative;
  z-index: 2;
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  padding: 28px 32px 14px;
  gap: 16px;
}

.wall-back, .wall-codex {
  font-family: var(--font-mono);
  font-weight: 200;
  font-size: 10px;
  letter-spacing: 0.32em;
  color: var(--bone-dim);
  text-shadow: 0 0 6px rgba(237, 232, 216, 0.18);
  transition: color 0.4s ease, letter-spacing 0.4s ease, text-shadow 0.4s ease;
}
.wall-back:hover  { color: var(--gold);  letter-spacing: 0.40em; text-shadow: 0 0 10px rgba(212,173,79,0.7); }
.wall-right {
  justify-self: end;
  display: flex;
  align-items: center;
  gap: 16px;
}
.wall-codex { color: #ff6b5b; text-shadow: 0 0 7px rgba(255,107,91,0.4); }
.wall-codex:hover { color: #ff8d80; letter-spacing: 0.40em; text-shadow: 0 0 12px rgba(255,107,91,0.85); }

.wall-brand {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(20px, 2.6vw, 28px);
  letter-spacing: 0.6em;
  padding-left: 0.6em;
  color: var(--gold);
  text-shadow: 0 0 12px rgba(212, 173, 79, 0.45), 0 0 28px rgba(212, 173, 79, 0.20);
  text-align: center;
}

.wall-subtitle {
  position: relative;
  z-index: 2;
  text-align: center;
  font-family: var(--font-display);
  font-weight: 300;
  font-size: clamp(14px, 1.6vw, 17px);
  color: var(--bone-dim);
  line-height: 1.9;
  letter-spacing: 0.05em;
  margin: 18px auto 28px;
  max-width: 640px;
  padding: 0 24px;
}
.wall-subtitle .wall-cu {
  font-family: var(--font-cuneiform);
  color: var(--gold-soft);
  letter-spacing: 0.18em;
}

/* ── Stats strip ─────────────────────────────────────── */

.wall-stats {
  position: relative;
  z-index: 2;
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: 36px;
  padding: 0 24px 28px;
}
.stat { text-align: center; min-width: 90px; }
.stat-key {
  font-family: var(--font-mono);
  font-weight: 200;
  font-size: 9px;
  letter-spacing: 0.36em;
  color: var(--bone-faint);
  margin-bottom: 6px;
}
.stat-val {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 400;
  font-size: 22px;
  color: var(--gold);
  letter-spacing: 0.04em;
}

/* ── Collections · the seven meanings ────────────────── */

.hall-collections {
  position: relative;
  z-index: 3;
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: 8px;
  padding: 0 24px 26px;
  border-bottom: 1px solid var(--bone-trace);
  margin-bottom: 30px;
}

.coll {
  appearance: none;
  cursor: pointer;
  background: transparent;
  border: 1px solid var(--bone-ghost);
  border-radius: 2px;
  padding: 7px 13px 6px;
  font-family: var(--font-mono);
  font-weight: 200;
  font-size: 9.5px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--bone-dim);
  transition: color 0.35s ease, border-color 0.35s ease, box-shadow 0.35s ease;
}
.coll:hover {
  color: color-mix(in srgb, var(--glow, #d4ad4f) 85%, var(--bone));
  border-color: color-mix(in srgb, var(--glow, #d4ad4f) 45%, transparent);
}
.coll[aria-pressed="true"] {
  color: color-mix(in srgb, var(--glow, #d4ad4f) 90%, var(--bone));
  border-color: color-mix(in srgb, var(--glow, #d4ad4f) 62%, transparent);
  box-shadow: 0 0 16px color-mix(in srgb, var(--glow, #d4ad4f) 18%, transparent),
              inset 0 0 12px color-mix(in srgb, var(--glow, #d4ad4f) 8%, transparent);
}
.coll:focus-visible { outline: 1px solid var(--gold); outline-offset: 2px; }

/* ── Layout · the rail beside the rooms ──────────────── */

.wall-main {
  position: relative;
  z-index: 2;
  padding: 0 24px 60px;
}

.wall-status {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.32em;
  color: var(--bone-faint);
  padding: 80px 0;
  text-align: center;
}
.wall-status.error { color: var(--crimson); }

.hall-empty {
  text-align: center;
  padding: 70px 0;
  font-family: var(--font-display);
  font-style: italic;
  font-size: 15px;
  color: var(--bone-faint);
}

.hall-layout {
  display: grid;
  grid-template-columns: 190px minmax(0, 1fr);
  gap: clamp(24px, 4vw, 56px);
  max-width: 1280px;
  margin: 0 auto;
  align-items: start;
}

/* ── The guide rail ──────────────────────────────────── */

.hall-rail {
  position: sticky;
  top: 28px;
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding-right: 14px;
  border-right: 1px solid var(--bone-trace);
  max-height: calc(100vh - 56px);
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: var(--bone-ghost) transparent;
}

.rail-room {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  padding: 7px 10px 6px 12px;
  font-family: var(--font-mono);
  font-weight: 200;
  font-size: 9.5px;
  letter-spacing: 0.18em;
  color: var(--bone-faint);
  border-left: 1px solid transparent;
  transition: color 0.35s ease, border-color 0.35s ease, background 0.35s ease;
}
.rail-room:hover { color: var(--bone-dim); background: rgba(237,232,216,0.02); }
.rail-room.here {
  color: var(--gold);
  border-left-color: var(--gold);
  background: var(--gold-trace);
  text-shadow: 0 0 10px var(--gold-glow);
}
.rail-count { font-size: 8.5px; color: var(--bone-faint); }
.rail-room.here .rail-count { color: var(--gold-soft); }

/* ── Rooms ───────────────────────────────────────────── */

.hall-rooms { min-width: 0; }

.room { scroll-margin-top: 24px; padding-bottom: 46px; }

.room-label {
  display: flex;
  align-items: baseline;
  gap: 16px;
  padding-bottom: 12px;
  margin-bottom: 26px;
  border-bottom: 1px solid var(--bone-trace);
  font-weight: 400;
}
.room-when {
  font-family: var(--font-display);
  font-size: clamp(15px, 1.9vw, 19px);
  letter-spacing: 0.34em;
  color: var(--gold-soft);
}
.room-count {
  margin-left: auto;
  font-family: var(--font-mono);
  font-weight: 200;
  font-size: 9px;
  letter-spacing: 0.24em;
  color: var(--bone-faint);
}

/* ── The pulse (P2) ──────────────────────────────────────────────────
   Two columns above the rooms: what was honoured, and what arrived.
   Deliberately quiet — it is a noticeboard at the door of a museum, not
   a feed. If it competes with the works it has failed. */
.hall-pulse {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr));
  gap: 20px 34px;
  margin: 0 0 34px;
  padding: 18px 20px;
  border-top: 1px solid var(--bone-trace);
  border-bottom: 1px solid var(--bone-trace);
}
.pulse-title {
  margin: 0 0 10px;
  font-family: var(--font-mono);
  font-weight: 200;
  font-size: 9px;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: var(--bone-faint);
}
.pulse-item {
  position: relative;
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: baseline;
  gap: 4px 10px;
  padding: 5px 0;
  text-decoration: none;
  border-bottom: 1px solid color-mix(in srgb, var(--bone-trace) 55%, transparent);
}
.pulse-item:last-child { border-bottom: 0; }
.pulse-item b {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 400;
  font-size: 14px;
  color: color-mix(in srgb, var(--glow, #d4ad4f) 42%, var(--bone));
}
.pulse-item:hover b { color: var(--glow, var(--gold)); }
.pulse-amt, .pulse-when {
  justify-self: end;
  font-family: var(--font-mono);
  font-weight: 200;
  font-size: 9px;
  letter-spacing: 0.14em;
  color: var(--bone-faint);
}
.pulse-amt { color: var(--gold-soft); }
/* A stranger's words, quoted as theirs — set apart from our own type so it
   is never mistaken for the site speaking. */
.pulse-note {
  grid-column: 1 / -1;
  font-family: var(--font-display);
  font-style: italic;
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--bone-dim);
  quotes: "\201C" "\201D";
}
/* The target grows and the type does not — styles/tokens.css, same rule. */
.pulse-item::after {
  content: ""; position: absolute; left: 50%; top: 50%;
  translate: -50% -50%;
  min-width: 24px; width: 100%;
  min-height: 24px; height: 100%;
  z-index: -1;
}

/* ── The room that is not a month (P3) ───────────────────────────────
   A capsule holding words nobody alive has read is the strongest thing
   this project makes, and it used to hang among the readable works with
   a line of type on it. Its room stands at the head of the hall, and is
   marked as different without being loud: a colder light, a rule above
   it, and one sentence saying what is in it. */
.room-sealed {
  position: relative;
  margin-bottom: 10px;
  padding-top: 26px;
}
.room-sealed .room-when {
  color: color-mix(in srgb, #6fb4ff 52%, var(--bone));
  letter-spacing: 0.2em;
}
.room-sealed .room-label {
  border-bottom-color: color-mix(in srgb, #6fb4ff 22%, transparent);
}
.room-note {
  max-width: 62ch;
  margin: -14px 0 26px;
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 300;
  font-size: 14px;
  line-height: 1.7;
  color: var(--bone-dim);
}
.rail-sealed .rail-when { color: color-mix(in srgb, #6fb4ff 46%, var(--bone-faint)); }
.rail-sealed.here .rail-when { color: color-mix(in srgb, #6fb4ff 70%, var(--bone)); }

/* The year, behind the distance. "opens in 10 years" is what a person
   wants; the date is the footnote to it, not the headline. */
.placard-year { opacity: 0.55; }

.room-works {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(clamp(150px, 20vw, 210px), 1fr));
  gap: clamp(22px, 3vw, 40px);
}

/* ── One work ────────────────────────────────────────── */

.work { display: flex; flex-direction: column; align-items: center; min-width: 0; }

/* An <a>, not a button — see wall.js. A crawler follows this; the vitrine
   only intercepts a plain left-click. */
.work-frame {
  position: relative;
  display: block;
  width: 100%;
  aspect-ratio: 1;
  cursor: pointer;
  text-decoration: none;
  -webkit-tap-highlight-color: transparent;
  transition: transform 0.5s cubic-bezier(.22, 1, .36, 1);
}
.work-frame:hover { transform: translateY(-5px); }
.work-frame:focus-visible { outline: 1px solid var(--gold); outline-offset: 6px; }

/* The star — a work's placeholder, and the reason a hall of hundreds loads.
   It is a point of light in the work's own colour, holding the exact square
   the plate will occupy, so nothing reflows when the plate arrives. */
.work-star {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background:
    radial-gradient(circle at 50% 50%,
      color-mix(in srgb, var(--glow, #d4ad4f) 92%, white) 0%,
      color-mix(in srgb, var(--glow, #d4ad4f) 70%, transparent) 4%,
      color-mix(in srgb, var(--glow, #d4ad4f) 18%, transparent) 12%,
      transparent 46%);
  opacity: 0.85;
  animation: star-breathe 4.5s ease-in-out infinite;
  transition: opacity 0.7s ease, transform 0.7s ease;
}
@keyframes star-breathe {
  0%, 100% { opacity: 0.6; }
  50%      { opacity: 0.95; }
}

.work-plate {
  position: absolute;
  inset: 0;
  display: block;
  opacity: 0;
  transform: scale(0.93);
  transition: opacity 0.85s ease, transform 0.85s cubic-bezier(.22, 1, .36, 1);
  filter: drop-shadow(0 0 16px color-mix(in srgb, var(--glow, #d4ad4f) 24%, transparent));
}
.work-plate svg { width: 100%; height: 100%; display: block; border-radius: 3px; }

/* The star opens into the work. */
.work-frame.lit .work-star  { opacity: 0; transform: scale(2.2); animation: none; }
.work-frame.lit .work-plate { opacity: 1; transform: scale(1); }

.work-frame:hover .work-plate {
  filter: drop-shadow(0 0 30px color-mix(in srgb, var(--glow, #d4ad4f) 52%, transparent));
}

/* ── The coin ─────────────────────────────────────────────────────────
 *
 * A rim in the declaration's own colour, and the words underneath it.
 *
 * Two things were true about the Hall and both are fixed here. A wall of
 * plates is **unreadable at a glance** — gold circles, all of them, and no way
 * to tell a vow from a love letter without opening one. And the words, which
 * are the entire point of the project, were behind a click: the Hall showed
 * the art and hid the writing.
 *
 * `--glow` is already the intent's colour, set per work in `wall.js` from
 * `INTENT_COLORS`. Nothing new had to be invented; it was simply never drawn.
 *
 * **The plate itself is untouched.** Every one of these rules is on a ring
 * *around* the artwork or on an overlay above it. The SVG comes from
 * `AionRenderer` on chain and no CSS here may alter a pixel of it — what a
 * wallet renders and what this page renders must stay the same picture.
 */
.work-coin {
  position: absolute;
  inset: -2.5%;
  border-radius: 50%;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.7s ease;
  /* Two rings: a hairline that states the colour, and a soft one outside it
     that reads as light caught on an edge rather than as a border. */
  box-shadow:
    inset 0 0 0 1px color-mix(in srgb, var(--glow, #d4ad4f) 55%, transparent),
    0 0 0 1px color-mix(in srgb, var(--glow, #d4ad4f) 14%, transparent),
    0 0 22px -6px color-mix(in srgb, var(--glow, #d4ad4f) 40%, transparent);
}
.work-frame.lit .work-coin { opacity: 0.85; }
.work-frame:hover .work-coin,
.work-frame:focus-visible .work-coin { opacity: 1; }

/* The shine — a band of light that crosses the rim once, and stops.
 *
 * 600ms, not five seconds. The plate is the thing being looked at, and an
 * animation that keeps it moving is an animation that keeps it from being
 * read. This passes, and leaves. */
.work-coin::after {
  content: "";
  position: absolute;
  inset: -1px;
  border-radius: 50%;
  background: conic-gradient(from 0deg,
    transparent 0deg,
    color-mix(in srgb, var(--glow, #d4ad4f) 70%, white) 8deg,
    transparent 26deg,
    transparent 360deg);
  opacity: 0;
  /* Only the rim is lit: the middle is masked out so the shine never crosses
     the artwork. */
  -webkit-mask: radial-gradient(circle, transparent 0 96%, #000 96.5%);
  mask: radial-gradient(circle, transparent 0 96%, #000 96.5%);
}
.work-frame:hover .work-coin::after,
.work-frame:focus-visible .work-coin::after { animation: coin-shine 0.6s ease-out; }

@keyframes coin-shine {
  0%   { opacity: 0;    transform: rotate(0deg); }
  15%  { opacity: 0.9; }
  100% { opacity: 0;    transform: rotate(300deg); }
}

/* The words, on the coin.
 *
 * They fade up over the plate rather than beside it, because a caption beside
 * a picture is read as a label and these are somebody's own sentences. Held
 * inside the circle, on a wash dark enough to read against any sky. */
.work-words {
  position: absolute;
  inset: 8%;
  border-radius: 50%;
  display: grid;
  place-items: center;
  padding: 14%;
  text-align: center;
  /* The clamp on `q` bounds the line count, not the height: six lines of a
     long declaration on a narrow screen are taller than the circle holding
     them, and without this the words climb out of the coin and across the
     wall. Clipped here, at the round edge, so nothing can ever leave. */
  overflow: hidden;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.45s ease;
  background: radial-gradient(circle at 50% 50%,
    rgba(7, 7, 13, 0.92) 0%, rgba(7, 7, 13, 0.86) 62%, rgba(7, 7, 13, 0) 100%);
}
.work-words q {
  quotes: none;
  /* People write with emoji and the wall has to show what they wrote, so the
     colour faces are named before the stack runs out. Declaration #2 ends in
     🫡 and on the machine this was built on it draws as a tofu box — measured,
     not assumed: that build of Segoe UI Emoji predates Emoji 14 and has no
     such glyph, and no font list can conjure one. Current Windows, macOS, iOS
     and Android all have it. Naming the emoji faces does not rescue that
     machine; it stops every *other* engine from settling for the generic
     `serif` inside --font-display and drawing a box where a glyph exists. */
  font: italic 400 clamp(10px, 1.35vw, 14px)/1.5
        var(--font-display, Georgia, serif),
        "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif;
  color: #ede8d8;
  display: -webkit-box;
  -webkit-line-clamp: 5;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* The words belong to the coin, not to the window.
 *
 * Sized in `vw` they drift out of step with the tile that holds them — the
 * grid changes column count at its own breakpoints, so at some widths the
 * text grew while its circle shrank. Measured in `cqi` the type is a fixed
 * fraction of the plate it is written on, and five lines fit inside the round
 * edge at every width the wall can take. The `vw` rule above stays as the
 * floor for engines without container queries. */
@supports (font-size: 1cqi) {
  .work-frame { container-type: inline-size; }
  .work-words q { font-size: clamp(9px, 7cqi, 15px); }
}
/* A sealed capsule has no words to show and must never look as though it does. */
.work-words.is-sealed q {
  color: color-mix(in srgb, var(--glow, #d4ad4f) 70%, #ede8d8);
  font-style: normal;
  font-size: clamp(10px, 1.2vw, 13px);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
/* Only once there is something to read.
 *
 * The wash exists to make the words legible over the plate. Raised on an empty
 * `q` it just dims the artwork for nothing — which is what happens for the
 * third of a second the fetch is in flight, and permanently if the content
 * cannot be reached at all. `has-words` is set when text lands, so the words
 * fade in with the wash instead of arriving into a dark hole. */
.work-frame:hover .work-words.has-words,
.work-frame:focus-visible .work-words.has-words { opacity: 1; }

/* `.peek` forces the open state without a pointer. Nothing on the wall sets
   it — a tap opens the vitrine, which shows the declaration in full — but it
   is how the state is inspected, and how a future view could pin one open. */
.work-frame.peek .work-coin { opacity: 1; }
.work-frame.peek .work-words.has-words { opacity: 1; }

/* Motion is an enhancement; the words are not.
 *
 * With reduced motion the shine does not run and the rim does not fade — but
 * the writing still appears, because withholding the content of the page from
 * someone who asked for less movement would be the wrong reading of that
 * request entirely. */
@media (prefers-reduced-motion: reduce) {
  .work-coin { transition: none; }
  .work-coin::after { display: none; }
  .work-words { transition: none; }
}

/* ── The placard ─────────────────────────────────────── */

.placard {
  margin-top: 13px;
  text-align: center;
  display: flex;
  flex-direction: column;
  gap: 3px;
  max-width: 100%;
}
.placard-title {
  font-family: var(--font-display);
  font-style: italic;
  font-size: clamp(14px, 1.5vw, 17px);
  letter-spacing: 0.03em;
  color: color-mix(in srgb, var(--glow, #d4ad4f) 72%, var(--bone));
  text-shadow: 0 0 12px color-mix(in srgb, var(--glow, #d4ad4f) 30%, transparent);
}
.placard-id {
  font-family: var(--font-mono);
  font-weight: 300;
  font-size: 9.5px;
  letter-spacing: 0.22em;
  color: var(--gold-soft);
}
.placard-by {
  font-family: var(--font-mono);
  font-weight: 200;
  font-size: 8.5px;
  letter-spacing: 0.14em;
  color: var(--bone-dim);
}
.placard-when {
  font-family: var(--font-mono);
  font-weight: 200;
  font-size: 8.5px;
  letter-spacing: 0.22em;
  color: var(--bone-faint);
}
.placard-sealed {
  margin-top: 2px;
  font-family: var(--font-mono);
  font-weight: 200;
  font-size: 8px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--glow, #6fb4ff) 60%, var(--bone-faint));
}

/* ── The vitrine ─────────────────────────────────────── */

.vitrine {
  border: 0;
  padding: 0;
  background: transparent;
  color: var(--bone);
  max-width: 100vw;
  max-height: 100vh;
  width: 100%;
  height: 100%;
  overflow: auto;
  overscroll-behavior: contain;
}
.vitrine::backdrop {
  background: radial-gradient(circle at center, rgba(6, 4, 14, 0.86) 0%, rgba(0, 0, 0, 0.96) 76%);
  backdrop-filter: blur(6px);
}

.vt-body {
  min-height: 100%;
  display: grid;
  /* The placard is a label: a definite width, and the work takes everything
     left. Sharing the width in a fixed ratio put a 408px label beside a 552px
     painting at 1440. Capping the label's track instead was worse at 800,
     where the track claimed its whole 360px maximum and left the work 280 —
     the label wider than the work it describes. */
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: clamp(24px, 4vw, 60px);
  padding: clamp(24px, 5vh, 64px) clamp(52px, 7vw, 110px);
  /* Wide enough that a large screen actually shows a large work — at 1220 the
     vitrine capped the plate at 580px on a 1920px display while 78vh allowed
     842. The height cap on .vt-plate is what stops it now, which is the right
     thing to be stopped by. */
  max-width: 1600px;
  margin: 0 auto;
}

.vt-plate {
  width: 100%;
  /* Capped by max-WIDTH, not max-height. A definite `width: 100%` wins over
     `aspect-ratio`, so a max-height would have clamped the height alone and
     quietly stretched the plate out of square. */
  max-width: min(100%, 78vh);
  aspect-ratio: 1;
  margin: 0 auto;
  filter: drop-shadow(0 0 44px color-mix(in srgb, var(--glow, #d4ad4f) 28%, transparent));
}
.vt-plate svg { width: 100%; height: 100%; display: block; border-radius: 4px; }

.vt-placard { min-width: 0; width: clamp(260px, 30vw, 360px); }
.vt-title {
  font-family: var(--font-display);
  font-style: italic;
  font-size: clamp(24px, 3.4vw, 38px);
  letter-spacing: 0.03em;
  color: color-mix(in srgb, var(--glow, #d4ad4f) 76%, var(--bone));
  text-shadow: 0 0 20px color-mix(in srgb, var(--glow, #d4ad4f) 34%, transparent);
}
.vt-sub {
  margin-top: 6px;
  padding-bottom: 18px;
  border-bottom: 1px solid var(--bone-trace);
  font-family: var(--font-mono);
  font-weight: 200;
  font-size: 10px;
  letter-spacing: 0.28em;
  color: var(--bone-dim);
}

/* The declaration, in the vitrine.
 *
 * It goes above the provenance rows, not below them, because it is the thing
 * and they are the footnotes — a placard that led with WITNESS, INSCRIBED,
 * SKY, BLOCK and never showed a word of the writing had its priorities the
 * wrong way round. Larger than the rows and in the display face: this is the
 * one piece of the page a person actually wrote.
 *
 * No quotation marks. The words are set apart by their own space and the mark
 * on the left, and typing `"` around a stranger's sentence puts punctuation in
 * their mouth that may not survive translation into every language the Hall
 * will hold. */
.vt-words {
  margin: 20px 0 0;
  /* Logical, not physical: the element carries `dir="auto"`, so on a Persian
     or Arabic declaration the whole block flips and the rule has to travel to
     the other edge with it. `border-left` would pin it to the side the words
     no longer begin on. */
  padding-block-start: 2px;
  padding-inline-start: 16px;
  border-inline-start: 2px solid color-mix(in srgb, var(--glow, #d4ad4f) 46%, transparent);
  font-family: var(--font-display), "Apple Color Emoji", "Segoe UI Emoji",
               "Noto Color Emoji", serif;
  font-size: clamp(15px, 1.7vw, 19px);
  line-height: 1.72;
  color: var(--bone);
  /* A declaration can be long, and the dialog must not grow past the screen
     because of it. Its own scroll, so the plate and the rows stay in place. */
  max-height: 34vh;
  overflow-y: auto;
  overscroll-behavior: contain;
  /* A long declaration scrolls, and the platform's default bar is a bright
     grey slab down the side of a black placard. Thin and in the work's own
     colour instead. `scrollbar-color` covers Firefox and current Chrome; the
     WebKit rules below cover Safari, which still ignores it. */
  scrollbar-width: thin;
  scrollbar-color: color-mix(in srgb, var(--glow, #d4ad4f) 34%, transparent) transparent;
  /* Somebody else's text, of unknown shape: a single unbroken 400-character
     "word" is a thing people type, and it must not push the placard wide. */
  overflow-wrap: anywhere;
  white-space: pre-line;
}
.vt-words[hidden] { display: none; }
.vt-words::-webkit-scrollbar { width: 6px; }
.vt-words::-webkit-scrollbar-track { background: transparent; }
.vt-words::-webkit-scrollbar-thumb {
  border-radius: 3px;
  background: color-mix(in srgb, var(--glow, #d4ad4f) 30%, transparent);
}

.vt-rows { margin-top: 18px; display: flex; flex-direction: column; gap: 7px; }
.vt-row {
  display: flex;
  justify-content: space-between;
  gap: 18px;
  font-family: var(--font-mono);
  font-weight: 200;
  font-size: 10px;
  letter-spacing: 0.1em;
}
.vt-row[hidden] { display: none; }
.vt-row dt { color: var(--bone-faint); white-space: nowrap; letter-spacing: 0.22em; }
.vt-row dd { color: var(--bone); text-align: right; word-break: break-all; }

.vt-note {
  margin-top: 20px;
  padding: 12px 14px;
  border-left: 1px solid color-mix(in srgb, var(--glow, #d4ad4f) 40%, transparent);
  background: rgba(237, 232, 216, 0.02);
  font-family: var(--font-display);
  font-style: italic;
  font-size: 13.5px;
  line-height: 1.75;
  color: var(--bone-dim);
}
.vt-note[hidden] { display: none; }

.vt-enter {
  display: inline-block;
  margin-top: 26px;
  padding: 11px 20px 10px;
  border: 1px solid color-mix(in srgb, var(--glow, #d4ad4f) 50%, transparent);
  border-radius: 2px;
  font-family: var(--font-mono);
  font-weight: 300;
  font-size: 10px;
  letter-spacing: 0.3em;
  color: color-mix(in srgb, var(--glow, #d4ad4f) 88%, var(--bone));
  transition: box-shadow 0.4s ease, border-color 0.4s ease, letter-spacing 0.4s ease;
}
.vt-enter:hover {
  letter-spacing: 0.36em;
  border-color: color-mix(in srgb, var(--glow, #d4ad4f) 80%, transparent);
  box-shadow: 0 0 26px color-mix(in srgb, var(--glow, #d4ad4f) 24%, transparent);
}

.vt-count {
  margin-top: 16px;
  font-family: var(--font-mono);
  font-weight: 200;
  font-size: 9px;
  letter-spacing: 0.3em;
  color: var(--bone-faint);
}

/* Walking the collection — ‹ › on the edges, and the arrow keys. */
.vt-close, .vt-step {
  position: fixed;
  z-index: 2;
  appearance: none;
  background: transparent;
  border: 1px solid var(--bone-ghost);
  border-radius: 50%;
  color: var(--bone-dim);
  cursor: pointer;
  display: grid;
  place-items: center;
  transition: color 0.35s ease, border-color 0.35s ease, box-shadow 0.35s ease;
}
.vt-close { top: 22px; right: 24px; width: 34px; height: 34px; font-size: 13px; }
.vt-step  { top: 50%; transform: translateY(-50%); width: 42px; height: 42px; font-size: 22px; line-height: 1; }
.vt-prev  { left: 14px; }
.vt-next  { right: 14px; }
.vt-close:hover, .vt-step:hover {
  color: var(--gold);
  border-color: var(--gold-soft);
  box-shadow: 0 0 18px var(--gold-glow);
}
.vt-close:focus-visible, .vt-step:focus-visible { outline: 1px solid var(--gold); outline-offset: 3px; }

/* ── Leaving for the astrolabe ───────────────────────── */

.hall-transition {
  position: fixed;
  inset: 0;
  z-index: 998;
  pointer-events: none;
  background: radial-gradient(circle at center, rgba(8, 5, 18, 0.5) 0%, #000 78%);
  opacity: 0;
  transition: opacity 0.6s ease;
}
.hall-transition.show { opacity: 1; }

.hall-transition-plate {
  z-index: 999;
  pointer-events: none;
  transform-origin: center center;
  will-change: transform, filter;
  filter: drop-shadow(0 0 18px color-mix(in srgb, var(--glow, #d4ad4f) 36%, transparent));
}
.hall-transition-plate.blooming {
  filter: drop-shadow(0 0 52px color-mix(in srgb, var(--glow, #d4ad4f) 85%, transparent));
}
.hall-transition-plate svg { width: 100%; height: 100%; display: block; }

/* ── Footer ──────────────────────────────────────────── */

.wall-footer {
  position: relative;
  z-index: 2;
  text-align: center;
  padding: 60px 24px 80px;
  border-top: 1px solid var(--bone-trace);
}
.wall-explainer {
  max-width: 600px;
  margin: 0 auto 32px;
  font-family: var(--font-display);
  font-style: italic;
  font-size: 14px;
  line-height: 1.85;
  color: var(--bone-dim);
}
.wall-archive {
  margin: 0 auto 28px;
  font-family: var(--font-mono);
  font-weight: 200;
  font-size: 10px;
  letter-spacing: 0.24em;
}
.wall-archive a {
  color: var(--bone-faint);
  transition: color 0.4s ease, letter-spacing 0.4s ease;
}
.wall-archive a:hover { color: var(--gold); letter-spacing: 0.3em; }

.footer-tomb {
  display: grid;
  place-items: center;
  margin: 0 auto 12px;
  width: 56px;
  height: 56px;
  opacity: 0.6;
}
.footer-tomb .tomb-mark { width: 100%; height: 100%; }
.footer-text {
  font-family: var(--font-cuneiform);
  font-size: 14px;
  letter-spacing: 0.22em;
  color: var(--gold-soft);
}

/* ── Responsive ──────────────────────────────────────── */

/* Below the rail's own width there is no room beside the rooms for it, so it
   becomes a strip above them that scrolls sideways — still the whole hall at
   a glance, still one tap to any room. */
@media (max-width: 900px) {
  /* Block, not a one-column grid.
     A sticky element cannot leave its containing block, and a grid item's
     containing block is its grid AREA — which here would be the rail's own
     43px row at the very top. It scrolled away at the first flick and never
     came back: a guide rail that is only visible when you do not need it.
     As a block child of the full-height layout it sticks for real. */
  .hall-layout { display: block; }
  .hall-rail {
    margin-bottom: 20px;
    position: sticky;
    top: 0;
    flex-direction: row;
    gap: 6px;
    padding: 10px 0;
    border-right: 0;
    border-bottom: 1px solid var(--bone-trace);
    overflow-x: auto;
    max-height: none;
    /* Opaque enough that the room labels do not ghost through it as they
       scroll under, and without its own scrollbar — a 4px grey bar across a
       gold guide rail is not part of the design. */
    background: linear-gradient(to bottom, rgba(0,0,0,0.97), rgba(0,0,0,0.9));
    backdrop-filter: blur(6px);
    scrollbar-width: none;
    z-index: 4;
  }
  .hall-rail::-webkit-scrollbar { display: none; }
  .rail-room {
    flex: 0 0 auto;
    gap: 7px;
    border-left: 0;
    border-bottom: 1px solid transparent;
    padding: 5px 10px;
  }
  .rail-room.here { border-left: 0; border-bottom-color: var(--gold); }
  .room { scroll-margin-top: 58px; }
}

@media (max-width: 720px) {
  .wall-header { grid-template-columns: auto 1fr; padding: 20px 18px 10px; }
  .wall-codex { display: none; }
  .wall-brand { font-size: 18px; letter-spacing: 0.4em; padding-left: 0.4em; text-align: right; }
  .wall-stats { gap: 22px; }
  .stat-val { font-size: 18px; }
  .stat { min-width: 70px; }
  .wall-main { padding: 0 14px 48px; }
  .room-works { grid-template-columns: repeat(auto-fill, minmax(132px, 1fr)); gap: 20px; }
  .vt-body {
    grid-template-columns: minmax(0, 1fr);
    padding: 62px 20px 40px;
    gap: 26px;
  }
  .vt-plate { max-width: min(100%, 52vh); }
  .vt-step { top: auto; bottom: 16px; transform: none; }
  .vt-prev { left: 20px; }
  .vt-next { right: 20px; }
}

/* ── Stillness, when it is asked for ─────────────────── */

@media (prefers-reduced-motion: reduce) {
  .work-star { animation: none; }
  .work-frame,
  .work-plate,
  .work-star { transition-duration: 0.01ms; }
  .work-frame:hover { transform: none; }
}

/* ══ chrome.css ══════════════════════════════════════════ */
/* ============================================================
   chrome.css
   Top bezel, bottom footer, the four-phase indicator,
   action buttons, and the wallet status pill.
   ============================================================ */

/* ════ BEZEL · top status bar ═══════════════════════════ */

.bezel {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: var(--z-chrome);
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: start;
  gap: 16px;
  padding:
    max(14px, env(safe-area-inset-top))
    24px
    16px;
  font-family: var(--font-mono);
  font-weight: 300;
  font-size: 10px;
  letter-spacing: 0.24em;
  color: var(--bone-dim);
  pointer-events: none;
}

.bezel > * { pointer-events: auto; }

.bezel-left {
  display: inline-flex;
  align-items: center;
  gap: 22px;
  flex-wrap: nowrap;
  padding-top: 3px;   /* optical align with the wordmark's cap height */
}
.bezel-nav {
  display: inline-flex;
  gap: 16px;
}
.bezel-nav a {
  position: relative;
  font-family: var(--font-mono);
  font-weight: 300;
  font-size: 10px;
  letter-spacing: 0.32em;
  color: var(--bone-dim);
  text-decoration: none;
  text-shadow: 0 0 6px rgba(237, 232, 216, 0.18);
  transition: color 0.4s ease, letter-spacing 0.4s ease, text-shadow 0.4s ease;
}
.bezel-nav a:hover { letter-spacing: 0.40em; }

/* An understated underline draws in from the centre on hover, in
   the link's own colour. Restraint at rest; intent on intent. */
.bezel-nav a::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -5px;
  height: 1px;
  background: currentColor;
  opacity: 0.75;
  transform: scaleX(0);
  transform-origin: center;
  transition: transform 0.45s cubic-bezier(0.22, 1, 0.36, 1);
}
.bezel-nav a:hover::after { transform: scaleX(1); }

/* Each header item carries its own jewel tone — gold, violet,
   crimson — so the chrome reads as a small constellation rather
   than a grey row. Glow intensifies on hover. */
.bezel-nav a[href*="wall"]  { color: #a98bff; text-shadow: 0 0 7px rgba(169, 139, 255, 0.40); }
.bezel-nav a[href*="codex"] { color: #ff6b5b; text-shadow: 0 0 7px rgba(255, 107,  91, 0.40); }
.bezel-nav a[href*="wall"]:hover  { color: #c4b2ff; text-shadow: 0 0 13px rgba(169, 139, 255, 0.85); }
.bezel-nav a[href*="codex"]:hover { color: #ff8d80; text-shadow: 0 0 13px rgba(255, 107,  91, 0.85); }

/* On phones the three-up header has no room for the nav inline, so
   WALL · CODEX drop to a centred line just beneath it — still one
   tap away, never hidden. */
@media (max-width: 720px) {
  .bezel-nav {
    position: absolute;
    top: 44px;
    left: 0;
    right: 0;
    justify-content: center;
    gap: 30px;
  }
}

.chain-status {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  white-space: nowrap;
  color: rgba(212, 173, 79, 0.82);             /* gold — the chain's own tone */
  text-shadow: 0 0 7px rgba(212, 173, 79, 0.32);
}
.chain-status .dot {
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--gold);
  box-shadow: 0 0 6px var(--gold-soft);
  animation: blink 2.4s ease-in-out infinite;
}

.brand-block {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  position: relative;
}

.brand {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(20px, 2.9vw, 30px);
  letter-spacing: 1.2em;
  padding-left: 1.2em; /* compensate for trailing letter-spacing visual centering */
  text-align: center;
  white-space: nowrap;
  position: relative;
  /* A star forged of gold — a moving glint sweeps the letters
     while a soft corona glows around them. */
  background: linear-gradient(110deg,
    #d4ad4f 28%, #fff4d6 47%, #ffe9b8 53%, #d4ad4f 72%);
  background-size: 240% 100%;
  -webkit-background-clip: text;
          background-clip: text;
  -webkit-text-fill-color: transparent;
          color: transparent;
  filter:
    drop-shadow(0 0 10px rgba(212, 173, 79, 0.45))
    drop-shadow(0 0 26px rgba(212, 173, 79, 0.22));
  animation: brand-shimmer 7.5s ease-in-out infinite;
}
/* The breathing corona behind the wordmark. */
.brand-block::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 42%;
  width: 240%;
  height: 220%;
  transform: translate(-50%, -50%);
  pointer-events: none;
  z-index: -1;
  background: radial-gradient(ellipse at center,
    rgba(212, 173, 79, 0.16) 0%,
    rgba(212, 173, 79, 0.05) 38%,
    transparent 64%);
  animation: brand-halo 6s ease-in-out infinite;
}

/* A barely-perceptible halo behind the brand. Breathes in
   sympathy with the central seed. */
.brand::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: 200%;
  height: 200%;
  transform: translate(-50%, -50%);
  pointer-events: none;
  z-index: -1;
  background: radial-gradient(
    ellipse at center,
    rgba(212, 173, 79, 0.10) 0%,
    transparent 50%
  );
  animation: brand-halo 6s ease-in-out infinite;
}

/* ── Wallet pill (right) ─────────────────────────────── */

.wallet-pill {
  justify-self: end;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 7px 14px 6px;
  border: 1px solid rgba(212, 173, 79, 0.28);
  border-radius: 999px;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.18em;
  color: rgba(212, 173, 79, 0.88);             /* gold — the action's tone */
  text-shadow: 0 0 6px rgba(212, 173, 79, 0.30);
  cursor: pointer;
  transition: border-color 0.4s ease, color 0.4s ease, background 0.4s ease;
  white-space: nowrap;
}
.wallet-pill:hover {
  border-color: var(--bone-faint);
  color: var(--bone);
}
.wallet-pill.connected {
  border-color: var(--gold-soft);
  color: var(--gold);
  background: var(--gold-trace);
}
.wallet-pill.wrong-chain {
  border-color: var(--crimson);
  color: var(--crimson);
}
.wallet-pill .glyph {
  font-size: 11px;
  letter-spacing: 0;
  color: inherit;
}

/* The pill wallet-gate.js brings with it, on pages that have no bezel to
   seat one in. The Gate's own pill is laid out by its grid and never gets
   this class. */
.wg-pill {
  position: fixed;
  top: 18px;
  right: 20px;
  z-index: 60;
  background: rgba(4, 3, 10, 0.72);
  backdrop-filter: blur(8px);
}
@media (max-width: 720px) {
  .wg-pill { top: 12px; right: 12px; font-size: 9px; padding: 6px 11px 5px; }
}

/* Seated in a page's own header rather than floating over it. */
.wg-pill-inline { justify-self: auto; }
@media (max-width: 720px) {
  .wg-pill-inline { font-size: 9px; padding: 6px 10px 5px; letter-spacing: 0.14em; }
}

/* ════ FOOTER · phases + data + actions ═════════════════ */

.footer {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: var(--z-chrome);
  padding:
    14px
    24px
    max(18px, env(safe-area-inset-bottom));
  display: grid;
  gap: 14px;
  text-align: center;
  pointer-events: none;
}
.footer > * { pointer-events: auto; }

/* ── Universal phase indicator
   Four icons, no language. The user's progress through the
   ritual is legible to anyone, regardless of literacy. */
.phases {
  display: inline-flex;
  justify-content: center;
  align-items: center;
  gap: 28px;
  justify-self: center;
}

/* Each phase carries its own colour and breathes on a staggered
   cadence — the four steps of the rite glow in sequence so they
   announce themselves rather than hiding in the dark. */
.phase {
  display: flex;
  align-items: center;
  gap: 10px;
  opacity: 0.5;
  animation: phase-breath 5.5s ease-in-out infinite;
  transition: opacity 0.6s ease;
}
.phase[data-phase="wallet"]  { --c: #d4ad4f; animation-delay: 0s;    }  /* gold  */
.phase[data-phase="network"] { --c: #6fb4ff; animation-delay: 0.7s;  }  /* azure */
.phase[data-phase="witness"] { --c: #ff7a3d; animation-delay: 1.4s;  }  /* ember */
.phase[data-phase="eternal"] { --c: #5fb392; animation-delay: 2.1s;  }  /* jade  */

.phase svg {
  width: 22px;
  height: 22px;
  fill: none;
  stroke: var(--c, var(--bone));
  stroke-width: 1.3;
  filter: drop-shadow(0 0 4px var(--c));
  transition: filter 0.4s ease, transform 0.4s ease;
}

/* The current step burns bright and steady in its own colour. */
.phase.active {
  opacity: 1 !important;
  animation: none;
}
.phase.active svg {
  filter: drop-shadow(0 0 8px var(--c)) drop-shadow(0 0 18px var(--c));
  transform: scale(1.14);
}
.phase.done { opacity: 0.8; animation: none; }

.phase .connector {
  width: 22px;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--bone-faint), transparent);
}

@keyframes phase-breath {
  0%, 100% { opacity: 0.4; }
  50%      { opacity: 0.85; }
}

/* ── Data strip ──────────────────────────────────────── */

.data-strip {
  font-family: var(--font-mono);
  font-weight: 300;
  font-size: 10px;
  letter-spacing: 0.22em;
  color: var(--bone-dim);
  display: inline-flex;
  flex-wrap: wrap;
  gap: 14px;
  justify-content: center;
  align-items: center;
  justify-self: center;
}
.data-strip b   { color: var(--bone); font-weight: 400; }
.data-strip .sep { color: var(--bone-ghost); }
.data-strip .key { color: var(--bone-faint); }

/* ════ ACTION BUTTONS ═══════════════════════════════════ */

.action-rail {
  display: flex;
  gap: 12px;
  justify-content: center;
  flex-wrap: wrap;
}

.action {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 12px 22px;
  border: 1px solid var(--bone-ghost);
  border-radius: 999px;
  font-family: var(--font-mono);
  font-weight: 300;
  font-size: 11px;
  letter-spacing: 0.28em;
  color: var(--bone);
  text-transform: uppercase;
  cursor: pointer;
  transition: all 0.4s ease;
  white-space: nowrap;
  text-decoration: none;
  position: relative;
}
.action:hover {
  border-color: var(--gold-soft);
  color: var(--gold);
  background: var(--gold-trace);
}

/* The primary action — BEAR WITNESS — is rendered with a
   subtle halo and a column of light descending onto it from
   above on first appearance. The user should feel the action
   has arrived from somewhere higher than the page. */
.action.primary {
  padding: 14px 30px;
  border-color: rgba(212, 173, 79, 0.55);
  color: #ffe9b8;
  text-shadow: 0 0 10px rgba(212, 173, 79, 0.40);
  /* Warm gold fill + a sheen band that sweeps across on hover. */
  background:
    linear-gradient(120deg, transparent 32%, rgba(255, 240, 205, 0.18) 48%, transparent 64%) no-repeat,
    radial-gradient(130% 200% at 50% -10%, rgba(212, 173, 79, 0.26) 0%, rgba(212, 173, 79, 0.07) 55%, transparent 75%);
  background-size: 260% 100%, 100% 100%;
  background-position: 175% 0, center;
  box-shadow:
    0 0 28px rgba(212, 173, 79, 0.16),
    inset 0 0 18px rgba(212, 173, 79, 0.08);
  transition:
    background-position 0.9s ease,
    transform 0.4s cubic-bezier(0.22, 1, 0.36, 1),
    box-shadow 0.4s ease,
    border-color 0.4s ease,
    color 0.4s ease;
}
.action.primary:hover {
  transform: translateY(-2px);
  border-color: rgba(255, 230, 160, 0.92);
  color: #fff6dd;
  background-position: -60% 0, center;
  box-shadow:
    0 0 46px rgba(212, 173, 79, 0.36),
    inset 0 0 22px rgba(212, 173, 79, 0.14);
}
.action.primary:active { transform: translateY(0) scale(0.99); }
.action.primary::before {
  content: "";
  position: absolute;
  left: 50%;
  top: -180px;
  width: 1px;
  height: 180px;
  background: linear-gradient(
    180deg,
    transparent 0%,
    rgba(212, 173, 79, 0.45) 60%,
    rgba(212, 173, 79, 0.95) 100%
  );
  transform-origin: 50% 100%;
  pointer-events: none;
  animation: descend-light 2.2s ease-out 5.0s 1 forwards;
  opacity: 0;
}
.action.primary::after {
  content: "";
  position: absolute;
  inset: -8px;
  border-radius: 999px;
  background: radial-gradient(
    ellipse at 50% 50%,
    rgba(212, 173, 79, 0.18) 0%,
    transparent 70%
  );
  pointer-events: none;
  z-index: -1;
  animation: ambient-breath 6s ease-in-out infinite;
}
.action.primary:hover {
  border-color: var(--gold);
  background: rgba(212, 173, 79, 0.16);
  letter-spacing: 0.32em;
  box-shadow:
    0 0 32px rgba(212, 173, 79, 0.20),
    inset 0 0 24px rgba(212, 173, 79, 0.08);
}
.action.ghost {
  border-color: var(--bone-trace);
  color: var(--bone-faint);
}
.action[disabled] { opacity: 0.4; cursor: not-allowed; }

/* ── Responsive chrome ──────────────────────────────── */

@media (max-width: 720px) {
  .brand       { letter-spacing: 1em; padding-left: 1em; font-size: 17px; }
  .data-strip  { font-size: 9px; letter-spacing: 0.18em; gap: 10px; }
  .phases      { gap: 18px; }
  .phase .connector { width: 14px; }
  .phase svg   { width: 18px; height: 18px; }
  .footer      { padding: 10px 16px max(14px, env(safe-area-inset-bottom)); }
  .bezel       { padding-left: 14px; padding-right: 14px; font-size: 9px; letter-spacing: 0.18em; }
  .wallet-pill { padding: 6px 10px 5px; font-size: 9px; }
  .action      { padding: 11px 18px; font-size: 10px; letter-spacing: 0.22em; }
}
@media (max-width: 380px) {
  .brand       { letter-spacing: 0.7em; padding-left: 0.7em; }
  .phases      { gap: 12px; }
  .data-strip  { gap: 8px; }
}

/* ══ modals.css ══════════════════════════════════════════ */
/* ============================================================
   modals.css
   Modal system. Backdrop blur, rising entrance, scrollable
   bodies, accessible close. Used for: wallet picker, mobile
   prompt, ceremony confirm, witnessing spinner, eternal seal.
   ============================================================ */

.modal-veil {
  position: fixed;
  inset: 0;
  z-index: var(--z-modal);
  display: none;
  background: rgba(0, 0, 0, 0.78);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  align-items: center;
  justify-content: center;
  padding: 24px;
  animation: veil-in 0.4s ease forwards;
}
.modal-veil.show { display: flex; }

.modal {
  position: relative;
  width: min(420px, 100%);
  max-height: 90vh;
  overflow-y: auto;
  border: 1px solid var(--bone-ghost);
  background: linear-gradient(180deg, rgba(15, 12, 8, 0.96), rgba(0, 0, 0, 0.96));
  padding: 32px 28px;
  text-align: center;
  animation: modal-rise 0.5s cubic-bezier(.2, .8, .2, 1) forwards;
}

.modal h2 {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 400;
  font-size: 22px;
  letter-spacing: 0.08em;
  color: var(--bone);
  margin-bottom: 6px;
}
.modal .subtitle {
  font-family: var(--font-cuneiform);
  font-weight: 400;
  font-size: 13px;
  letter-spacing: 0.18em;
  color: var(--bone-faint);
  margin-bottom: 24px;
}
.modal .subtitle::before,
.modal .subtitle::after {
  content: "";
  display: inline-block;
  width: 24px;
  height: 1px;
  background: var(--bone-ghost);
  vertical-align: middle;
  margin: 0 12px;
}
.modal .body {
  font-family: var(--font-mono);
  font-weight: 300;
  font-size: 11px;
  letter-spacing: 0.16em;
  color: var(--bone-dim);
  line-height: 1.9;
  margin-bottom: 20px;
}
.modal .body b { color: var(--bone); }
.modal .body a { color: var(--gold); text-decoration: none; }
.modal .body a:hover { text-decoration: underline; }

.modal .actions {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 8px;
}

.modal .close {
  position: absolute;
  top: 10px;
  right: 14px;
  font-family: var(--font-mono);
  font-size: 14px;
  color: var(--bone-faint);
  cursor: pointer;
  background: none;
  border: none;
  padding: 6px 8px;
  transition: color 0.3s ease;
}
.modal .close:hover { color: var(--bone); }

/* ── Wallet list ─────────────────────────────────────── */

.wallet-list {
  display: grid;
  gap: 10px;
  margin-top: 14px;
}

.wallet-option {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 14px 16px;
  border: 1px solid var(--bone-ghost);
  background: rgba(255, 255, 255, 0.01);
  cursor: pointer;
  text-align: left;
  transition: all 0.3s ease;
  width: 100%;
}
.wallet-option:hover {
  border-color: var(--gold-soft);
  background: var(--gold-trace);
}
.wallet-option img {
  width: 28px;
  height: 28px;
  border-radius: 4px;
}
.wallet-option .wn {
  font-family: var(--font-display);
  font-size: 17px;
  color: var(--bone);
  flex: 1;
}
.wallet-option .wm {
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.18em;
  color: var(--bone-faint);
}
.wallet-option .icon-fallback {
  width: 28px;
  height: 28px;
  border-radius: 4px;
  background: var(--gold-glow);
  display: grid;
  place-items: center;
  color: var(--gold);
  font-family: var(--font-mono);
  font-size: 14px;
}

/* ── Ceremony details ────────────────────────────────── */

.ceremony-details {
  display: grid;
  gap: 12px;
  margin: 18px 0 24px;
  padding: 18px;
  border: 1px solid var(--bone-ghost);
  background: var(--gold-trace);
}
.ceremony-details .row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.2em;
}
.ceremony-details .row .k { color: var(--bone-faint); }
.ceremony-details .row .v { color: var(--bone); font-weight: 400; }
.ceremony-details .row .v.gold {
  color: var(--gold);
  font-size: 13px;
  letter-spacing: 0.14em;
}

/* ── Witnessing spinner — three nested spinning rings ── */

.spinner-block {
  display: grid;
  place-items: center;
  padding: 28px 0;
}
.spinner-rings {
  width: 90px;
  height: 90px;
  position: relative;
}
.spinner-rings svg {
  width: 100%;
  height: 100%;
  position: absolute;
  inset: 0;
}
.spinner-rings .sr-circle {
  fill: none;
  stroke: var(--gold);
  stroke-width: 1.2;
  stroke-dasharray: 4 8;
  animation: spin 6s linear infinite;
  transform-origin: 50% 50%;
}
.spinner-rings .sr-circle.sr-2 {
  animation-duration: 4s;
  animation-direction: reverse;
  stroke: var(--bone);
  opacity: 0.5;
}
.spinner-rings .sr-circle.sr-3 {
  animation-duration: 3s;
  stroke: var(--ember);
  opacity: 0.6;
}

/* ── Eternal seal ────────────────────────────────────── */

.eternal-seal {
  width: 180px;
  height: 180px;
  margin: 14px auto 36px;
  position: relative;
}
.eternal-seal svg {
  width: 100%;
  height: 100%;
  overflow: visible;
}
.eternal-seal .moment-num {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 400;
  font-size: 36px;
  color: var(--gold);
  letter-spacing: 0.05em;
  text-shadow: 0 0 12px var(--gold-soft);
}
.eternal-seal .moment-label {
  position: absolute;
  bottom: -22px;
  left: 50%;
  transform: translateX(-50%);
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.32em;
  color: var(--bone-faint);
  white-space: nowrap;
}

/* ── Mobile prompt — open in MetaMask ────────────────── */

.mm-mobile-block {
  display: grid;
  gap: 14px;
  padding: 18px;
  border: 1px solid var(--gold-soft);
  background: var(--gold-trace);
  margin: 14px 0 18px;
  text-align: left;
}
.mm-mobile-block .mm-row {
  display: flex;
  align-items: center;
  gap: 14px;
}
.mm-mobile-block .mm-icon {
  width: 36px;
  height: 36px;
  flex-shrink: 0;
}
.mm-mobile-block .mm-text {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.16em;
  color: var(--bone-dim);
  line-height: 1.7;
}
.mm-mobile-block .mm-text b {
  color: var(--bone);
  display: block;
  margin-bottom: 4px;
  font-family: var(--font-display);
  font-style: italic;
  font-size: 16px;
  letter-spacing: 0.04em;
  font-weight: 400;
}

/* ── Toast ──────────────────────────────────────────── */

.toast {
  position: fixed;
  top: 80px;
  left: 50%;
  transform: translateX(-50%);
  z-index: var(--z-toast);
  padding: 12px 20px;
  border: 1px solid var(--crimson);
  background: rgba(60, 10, 10, 0.92);
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.14em;
  color: var(--bone);
  max-width: 92vw;
  text-align: center;
  display: none;
  animation: toast-in 0.4s ease forwards;
}
.toast.show { display: block; }
.toast.info {
  border-color: var(--gold-soft);
  background: rgba(40, 30, 10, 0.92);
}

/* ── Responsive ──────────────────────────────────────── */

@media (max-width: 720px) {
  .modal { padding: 26px 22px; }
  .modal h2 { font-size: 19px; }
}

/* ══ tomb.css ══════════════════════════════════════════ */
/* ============================================================
   tomb.css
   The Pasargadae mark. Renders the Tomb of Cyrus silhouette
   as a watermark and as a feature element. The mark itself
   is built by `src/ui/tomb.js` — these rules govern placement.
   ============================================================ */

/* ── Corner watermark ──────────────────────────────────
   Appears bottom-left of the viewport. Minimal, just a
   trace of where AION inherits its lineage from. */

.tomb-watermark {
  position: fixed;
  left: 24px;
  bottom: 18px;
  z-index: var(--z-chrome);
  width: 36px;
  height: 36px;
  opacity: 0;
  pointer-events: none;
  animation: fade 2s ease-out 5.0s forwards;
}
.tomb-watermark .tomb-mark {
  width: 100%;
  height: 100%;
}

/* Subtle inscription beneath the watermark — only on hover
   of a parent or first reveal. Tells the user what they see. */
.tomb-watermark::after {
  content: "PASARGADAE · 540 BCE";
  position: absolute;
  left: 50%;
  bottom: -14px;
  transform: translateX(-50%);
  font-family: var(--font-mono);
  font-weight: 200;
  font-size: 7px;
  letter-spacing: 0.32em;
  color: var(--bone-ghost);
  white-space: nowrap;
  opacity: 0;
  transition: opacity 0.6s ease;
}
.tomb-watermark:hover::after { opacity: 1; }

@media (max-width: 720px) {
  .tomb-watermark { display: none; }
}

/* ── Tomb in the eternal seal modal ────────────────────
   When a witness is sealed, the tomb appears beside the
   moment number — the user is now in the same lineage. */

.eternal-tomb {
  display: grid;
  place-items: center;
  margin: 18px auto 6px;
  width: 56px;
  height: 56px;
  opacity: 0.7;
}
.eternal-tomb .tomb-mark {
  width: 100%;
  height: 100%;
}
.eternal-tomb-caption {
  font-family: var(--font-mono);
  font-weight: 200;
  font-size: 8px;
  letter-spacing: 0.36em;
  color: var(--bone-faint);
  text-align: center;
  margin-bottom: 18px;
}

/* ── Reduced motion ─────────────────────────────────── */

@media (prefers-reduced-motion: reduce) {
  .tomb-watermark { animation: none !important; opacity: 0.5; }
}

/* ══ firstrun.css ══════════════════════════════════════════ */
/* ============================================================
   firstrun.css
   The first-visit guide — three cards that point at what they
   are talking about. Built by src/ui/firstrun.js.

   It has to belong to this site, not to a tour library: gold on
   void, hand-set mono for the labels, Cormorant for the words.
   A generic blue tooltip on this page would read as an advert
   somebody else injected.
   ============================================================ */

.fr-root {
  position: fixed;
  inset: 0;
  z-index: 900;         /* over the instrument, under toast (200 is page-level) */
  pointer-events: none; /* only the card and the veil take clicks */
}

/* A veil, not a blackout. The page is the subject; the card is a note in the
   margin of it, and a visitor should be able to see what is being pointed at. */
.fr-veil {
  position: absolute;
  inset: 0;
  pointer-events: auto;
  background: radial-gradient(circle at 50% 45%,
    rgba(2, 2, 8, 0.30) 0%, rgba(2, 2, 8, 0.62) 70%);
  backdrop-filter: blur(1.5px);
  animation: fr-fade 0.6s ease both;
}

.fr-card {
  position: absolute;
  pointer-events: auto;
  width: min(340px, calc(100vw - 28px));
  padding: 20px 22px 16px;
  background: linear-gradient(180deg, rgba(10, 8, 18, 0.97), rgba(5, 4, 12, 0.97));
  border: 1px solid color-mix(in srgb, var(--gold) 34%, transparent);
  border-radius: 3px;
  box-shadow: 0 18px 60px rgba(0, 0, 0, 0.7),
              0 0 40px color-mix(in srgb, var(--gold) 10%, transparent);
  animation: fr-rise 0.5s cubic-bezier(.22, 1, .36, 1) both;
}

.fr-dots { display: flex; gap: 6px; margin-bottom: 14px; }
.fr-dot {
  width: 18px; height: 2px;
  background: var(--bone-ghost);
  transition: background 0.4s ease;
}
.fr-dot.on { background: var(--gold); box-shadow: 0 0 8px var(--gold-glow); }

.fr-title {
  margin: 0 0 8px;
  font-family: var(--font-display);
  font-weight: 400;
  font-style: italic;
  font-size: 20px;
  letter-spacing: 0.02em;
  color: var(--gold);
  text-shadow: 0 0 14px var(--gold-glow);
}

.fr-body {
  margin: 0 0 18px;
  font-family: var(--font-display);
  font-weight: 300;
  font-size: 15px;
  line-height: 1.65;
  color: var(--bone-dim);
}

.fr-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; }

.fr-skip, .fr-next {
  appearance: none;
  cursor: pointer;
  font-family: var(--font-mono);
  font-weight: 300;
  font-size: 10px;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  transition: color 0.35s ease, border-color 0.35s ease, box-shadow 0.35s ease;
}

.fr-skip {
  background: none;
  border: 0;
  padding: 8px 2px;
  color: var(--bone-faint);
}
.fr-skip:hover { color: var(--bone-dim); }

.fr-next {
  background: transparent;
  border: 1px solid color-mix(in srgb, var(--gold) 45%, transparent);
  border-radius: 2px;
  padding: 9px 18px 8px;
  color: var(--gold);
}
.fr-next:hover {
  border-color: var(--gold-soft);
  box-shadow: 0 0 22px color-mix(in srgb, var(--gold) 20%, transparent);
}

/* What the card is pointing at. A ring, not a spotlight cut out of an overlay:
   the element keeps its own appearance and simply gains a mark. */
.fr-lit {
  position: relative;
  z-index: 901;
  outline: 1px solid color-mix(in srgb, var(--gold) 60%, transparent);
  outline-offset: 6px;
  border-radius: 3px;
  animation: fr-pulse 2.4s ease-in-out infinite;
}

@keyframes fr-fade { from { opacity: 0 } to { opacity: 1 } }
@keyframes fr-rise {
  from { opacity: 0; transform: translateY(10px) }
  to   { opacity: 1; transform: none }
}
@keyframes fr-pulse {
  0%, 100% { outline-color: color-mix(in srgb, var(--gold) 30%, transparent) }
  50%      { outline-color: color-mix(in srgb, var(--gold) 75%, transparent) }
}

/* Stillness when it is asked for: the cards remain, the movement does not. */
.fr-root.fr-still .fr-veil,
.fr-root.fr-still .fr-card { animation: none; }
.fr-root.fr-still .fr-lit  { animation: none; outline-color: var(--gold-soft); }

@media (prefers-reduced-motion: reduce) {
  .fr-veil, .fr-card, .fr-lit { animation: none !important; }
}

/* On a phone the card sits at the bottom, where a thumb is. */
@media (max-width: 700px) {
  .fr-card { width: calc(100vw - 24px); padding: 18px 18px 14px; }
  .fr-title { font-size: 18px; }
  .fr-body { font-size: 14.5px; }
}
