/* ─────────────────────────────────────────────────────────────
   AION · every stylesheet index.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; }
}

/* ══ base.css ══════════════════════════════════════════ */
/* ============================================================
   base.css
   Reset, document setup, atmospheric layers (gradient, grain).
   ============================================================ */

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

html, body {
  width: 100%;
  height: 100%;
  background: var(--void);
  color: var(--bone);
  overflow: hidden;
  font-family: var(--font-display);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: geometricPrecision;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  /* Double-tap zoom off, pinch zoom left alone. This replaces
     `user-scalable=no`, which took both away — and took them away only on
     Android, since iOS has ignored that flag since iOS 10. The Gate is a
     canvas you tap on; a stray double-tap should not throw the astrolabe to
     300% mid-composition. Wanting to magnify the text you are about to make
     permanent is a different thing, and it still works. */
  touch-action: manipulation;
}

button {
  font: inherit;
  color: inherit;
  background: none;
  border: none;
  cursor: pointer;
}

a { color: inherit; }

/* The [hidden] attribute must always wins over component display
   rules (e.g. `.mm-mobile-block { display: grid }`). Without this,
   `display: grid|flex|inline-block` on a hidden element overrides
   the user-agent `display: none`. */
[hidden] { display: none !important; }

/* The cosmic backdrop (.atmosphere, nebula, vignette, grain) now
   lives in the shared styles/atmosphere.css so every page breathes
   the same void. */

/* ── Layer -1 · ambient background video ───────────────────
   A living galactic field drifting far behind everything. Two
   layers crossfade (driven by ui/core-trailer.js · startBgTrailer)
   and are deeply dimmed + softened so the instrument stays the
   sole focus. */
.bg-stage {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  overflow: hidden;
  background: #000;
}
.bg-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity 2.2s ease-in-out;
  /* Same warm grade as the core, so background and heart share one
     tone — ancient gold, not stock colour. */
  filter: brightness(0.82) saturate(1.0) sepia(0.2) hue-rotate(-6deg) contrast(1.06) blur(2px);
  transform: scale(1.08);   /* hide blur edges; subtle depth */
}
.bg-video.is-active { opacity: 0.5; }   /* present, but still behind the instrument */
.bg-veil {
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse at 50% 50%,
    rgba(0, 0, 0, 0.18) 0%,
    rgba(0, 0, 0, 0.44) 70%,
    rgba(0, 0, 0, 0.70) 100%);
}

/* ── Layer 1 · cosmos
   Canvas star field. Painted by `ui/starfield.js`. */
#cosmos {
  position: fixed;
  inset: 0;
  z-index: var(--z-cosmos);
  pointer-events: none;
}

/* ── Universal opacity-fade utility ─────────────────────── */
.opacity-fade {
  opacity: 0;
  animation: fade 2s ease-out 2.4s forwards;
}

/* ══ 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; }
}

/* ══ instrument.css ══════════════════════════════════════════ */
/* ============================================================
   instrument.css
   The astrolabe. Four temporal rings around a luminous core.
   ============================================================ */

.stage {
  position: fixed;
  inset: 0;
  z-index: var(--z-instrument);
  display: grid;
  place-items: center;
  /* Reserve the header and footer bands so the astrolabe is centred
     in the space BETWEEN them, never beneath the wordmark or the
     BEAR WITNESS bar. */
  padding: 60px 0 140px;
  pointer-events: none;
  perspective: 1500px;          /* depth for the parallax tilt */
}

.instrument {
  position: relative;
  /* Cap by the height left between header and footer so the rings
     never spill onto the wordmark or the BEAR WITNESS bar. */
  width: min(90vmin, 720px, calc(100svh - 200px));
  aspect-ratio: 1;
  pointer-events: auto;
  transform-style: preserve-3d;
  transition: transform 0.35s cubic-bezier(0.22, 1, 0.36, 1);
  will-change: transform;
}

.instrument svg.astro {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
}

/* ── Spin pivots ─────────────────────────────────────────
   Four rings, four cadences. The ratios honour their
   corresponding constants. Year is glacial; hour is brisk;
   the rete drifts retrograde, an axis against the tide. */

.pivot              { transform-origin: 500px 500px; transform-box: view-box; }
.pivot.year         { animation: spin  900s linear infinite; }
.pivot.month        { animation: spin  300s linear infinite; }
.pivot.day          { animation: spin   90s linear infinite; }
.pivot.hour         { animation: spin   18s linear infinite; }
.pivot.rete         { animation: spin 1800s linear infinite reverse; }

/* When the ceremony begins, the instrument quickens — the
   user feels the moment dilating around them. */
.instrument.ceremony .pivot.year   { animation-duration:  18s; }
.instrument.ceremony .pivot.month  { animation-duration:  12s; }
.instrument.ceremony .pivot.day    { animation-duration:   8s; }
.instrument.ceremony .pivot.hour   { animation-duration:   4s; }
.instrument.ceremony .ring,
.instrument.ceremony .ring-h       { stroke: var(--gold-soft) !important; }

/* ── SVG primitives ─────────────────────────────────────── */

.tick   { stroke: var(--bone); stroke-width: 0.5; }
.ring   { fill: none; stroke: var(--bone-dim); stroke-width: 0.8; transition: stroke 1s ease;
          filter: drop-shadow(0 0 1.5px rgba(212, 173, 79, 0.25)); }
.ring-h { fill: none; stroke: var(--bone-faint); stroke-width: 0.4; transition: stroke 1s ease; }

.glyph  { fill: var(--bone-dim); font-family: var(--font-display); font-style: italic; }
.num    { fill: var(--bone-dim); font-family: var(--font-mono);    font-weight: 300; }
.const  { fill: var(--gold);     font-family: var(--font-display); font-style: italic;
          filter: drop-shadow(0 0 5px rgba(212, 173, 79, 0.55)); }
.cu     { fill: var(--bone-faint); font-family: var(--font-cuneiform); font-weight: 400; letter-spacing: 0.05em; }
.fa     { fill: var(--bone-faint); font-family: var(--font-cuneiform); font-weight: 400; } /* legacy alias */

.marker        { fill: var(--bone); }
.pointer-gold  { fill: var(--gold); filter: drop-shadow(0 0 6px var(--gold-soft)); }

/* Reveal cascade on first paint */
.reveal      { opacity: 0; animation: fade 1.8s ease-out forwards; }
.reveal.r1   { animation-delay: 0.55s; }
.reveal.r2   { animation-delay: 0.95s; }
.reveal.r3   { animation-delay: 1.35s; }
.reveal.r4   { animation-delay: 1.75s; }
.reveal.r5   { animation-delay: 2.20s; }

/* ════════════════════════════════════════════════════════
   Living instrument touches · barely perceptible
   The astrolabe should feel inhabited. A breath here, a
   photon there. Each element is small enough to never
   demand attention; together they animate the void.
   ════════════════════════════════════════════════════════ */

/* Ambient halo behind the instrument · slow breath. */
.ambient-halo {
  position: absolute;
  inset: -8%;
  border-radius: 50%;
  pointer-events: none;
  z-index: -1;
  background: radial-gradient(
    circle,
    rgba(212, 173, 79, 0.06) 0%,
    rgba(212, 173, 79, 0.02) 40%,
    transparent 70%
  );
  animation: ambient-breath 9s ease-in-out infinite;
}

/* Photons travelling along each ring's orbit. Different
   speeds matched to the ring's own cadence. The eye
   barely registers them, but the instrument feels alive. */
.orbit-particle {
  position: absolute;
  top: 0;
  left: 0;
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: var(--gold);
  box-shadow: 0 0 5px var(--gold-soft);
  pointer-events: none;
  opacity: 0;
  animation: orbit-particle linear infinite;
}

.orbit-particle.p-year  { animation-duration: 220s; offset-path: circle(46.5% at 50% 50%); }
.orbit-particle.p-month { animation-duration: 110s; offset-path: circle(37.6% at 50% 50%); animation-delay: -30s; }
.orbit-particle.p-day   { animation-duration:  55s; offset-path: circle(28.7% at 50% 50%); animation-delay: -15s; }
.orbit-particle.p-hour  { animation-duration:  22s; offset-path: circle(20.8% at 50% 50%); animation-delay:  -7s; }

/* ════════════════════════════════════════════════════════
   The Living Core — a cosmic portal at the heart
   An ancient astrolabe with a living future burning inside it.
   The footage is screen-blended so only its light shows, framed
   by a gold rim and faded into the void at its edge.
   ════════════════════════════════════════════════════════ */

.core-portal {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 46%;
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  overflow: hidden;
  pointer-events: none;
  opacity: 0;
  animation: fade 3s ease-out 2.2s forwards;
  -webkit-mask: radial-gradient(circle, #000 56%, transparent 80%);
          mask: radial-gradient(circle, #000 56%, transparent 80%);
}

.core-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;                       /* crossfade target set by .is-active */
  mix-blend-mode: screen;          /* only the cosmic light shows */
  /* Unified cinematic grade — a warm amber push pulls the disparate
     clips into one ancient-gold tone, so they read as deliberate
     footage, not stock. */
  filter: contrast(1.14) saturate(1.04) brightness(0.9) sepia(0.18) hue-rotate(-6deg);
  transition: opacity 1.4s ease-in-out;
}
.core-video.is-active { opacity: 0.46; }   /* one level down — the film sits behind the rite */

/* A gold breath laid over the core, tying it to the instrument's
   own light. */
.core-grade {
  position: absolute;
  inset: 0;
  pointer-events: none;
  mix-blend-mode: soft-light;
  background: radial-gradient(circle at 50% 50%,
    rgba(212, 173, 79, 0.30) 0%,
    rgba(168, 110, 40, 0.16) 45%,
    rgba(40, 24, 60, 0.30) 100%);
}

/* Deepen the rim so the footage dissolves into the void. */
.core-veil {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  pointer-events: none;
  background: radial-gradient(circle,
    transparent 30%,
    rgba(6, 4, 16, 0.45) 70%,
    rgba(6, 4, 16, 0.92) 100%);
}

/* A faint gold ring marking the portal's edge. */
.core-rim {
  position: absolute;
  inset: 14%;
  border-radius: 50%;
  pointer-events: none;
  border: 1px solid rgba(212, 173, 79, 0.18);
  box-shadow:
    0 0 18px rgba(212, 173, 79, 0.14) inset,
    0 0 22px rgba(212, 173, 79, 0.10);
}

/* ════════════════════════════════════════════════════════
   The Sigil — center seal · universal language CTA
   A closed eye that opens, becomes a mark, becomes you.
   ════════════════════════════════════════════════════════ */

.sigil-mount {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 18%;
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  pointer-events: none;
  opacity: 0;
  /* The central light is a small sun whose colour drifts slowly
     through the cosmic palette — gold, amber, blue, green, white —
     like a living star. */
  animation: fade 2.4s ease-out 2.6s forwards,
             star-hue 34s linear infinite;
}

/* The corona — a true sun, not a lamp: a white-hot heart fading
   through warm light into the dark. */
.sigil-mount::before {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 460%;
  height: 460%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  pointer-events: none;
  z-index: -2;
  background: radial-gradient(circle,
    rgba(255, 252, 242, 0.55) 0%,
    rgba(255, 226, 158, 0.36) 12%,
    rgba(232, 180, 90, 0.18) 30%,
    rgba(180, 120, 50, 0.07) 52%,
    transparent 72%);
  filter: blur(4px);
  animation: core-bloom 6s ease-in-out infinite;
}

/* The radiant sunburst — slow turning rays, masked to a ring
   so they read as emanation, not a wheel. */
.sigil-mount::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 340%;
  height: 340%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  pointer-events: none;
  z-index: -3;
  opacity: 0.6;
  background: repeating-conic-gradient(from 0deg,
    transparent 0deg 4.6deg,
    rgba(255, 214, 140, 0.10) 4.6deg 6deg);
  -webkit-mask: radial-gradient(circle, transparent 26%, #000 44%, transparent 74%);
          mask: radial-gradient(circle, transparent 26%, #000 44%, transparent 74%);
  animation: core-rays 120s linear infinite;
}

.sigil {
  width: 100%;
  height: 100%;
  display: grid;
  place-items: center;
  position: relative;
  pointer-events: auto;
  cursor: pointer;
  border: none;
  background: none;
  padding: 0;
  border-radius: 50%;
  transition: transform 0.4s ease;
}
.sigil:hover  { transform: scale(1.04); }
.sigil:active { transform: scale(0.98); }
.sigil:disabled { cursor: wait; opacity: 0.7; }

.sigil svg {
  width: 100%;
  height: 100%;
  overflow: visible;
}

.sigil .halo {
  fill: none;
  stroke: var(--gold-soft);
  stroke-width: 0.8;
  opacity: 0.55;
  animation: halo 4s ease-in-out infinite;
  transform-origin: 50% 50%;
  transform-box: fill-box;
}

.sigil .iris-outer { fill: rgba(0, 0, 0, 0.55); stroke: var(--bone-faint); stroke-width: 0.6; }
.sigil .pupil      { fill: var(--bone); animation: heart 4.2s ease-in-out infinite; transform-origin: 50% 50%; transform-box: fill-box; }

/* ── Block pulse · the chain's heartbeat ─────────────
   Applied briefly to .pupil when a new Base block arrives.
   The instrument blinks once with the network. */
.sigil .pupil.block-pulse {
  animation: heart 4.2s ease-in-out infinite, block-pulse 0.8s ease-out 1;
}

@keyframes block-pulse {
  0%   { filter: drop-shadow(0 0 0 rgba(212, 173, 79, 0)); }
  30%  { filter: drop-shadow(0 0 8px rgba(212, 173, 79, 0.95)); fill: var(--gold); }
  100% { filter: drop-shadow(0 0 0 rgba(212, 173, 79, 0)); }
}

/* ── The Soul · the hidden manifest button
   A pinpoint of deep gold at the very heart of the sigil.
   Discoverable on hover. Click reveals the Codex Aion.
   Sits above the witness button (z-stacked) and stops
   propagation so the surrounding eye keeps its CTA role. */
.soul {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 14%;
  height: 14%;
  transform: translate(-50%, -50%);
  z-index: 5;
  display: grid;
  place-items: center;
  pointer-events: auto;
  cursor: pointer;
  border: none;
  background: none;
  padding: 0;
  border-radius: 50%;
  opacity: 0;
  animation: fade 2s ease-out 3.4s forwards;
}

.soul-dot {
  width: 30%;
  height: 30%;
  border-radius: 50%;
  background: radial-gradient(circle, #ffe8a8 0%, #d4ad4f 50%, transparent 100%);
  filter: drop-shadow(0 0 4px rgba(255, 230, 160, 0.7));
  transition: transform 0.5s ease, filter 0.5s ease;
}
.soul:hover .soul-dot {
  transform: scale(1.6);
  filter: drop-shadow(0 0 10px rgba(255, 230, 160, 0.9));
}

.soul-hint {
  position: absolute;
  top: 110%;
  left: 50%;
  transform: translateX(-50%) translateY(-4px);
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 300;
  font-size: 10px;
  letter-spacing: 0.36em;
  color: var(--gold);
  white-space: nowrap;
  opacity: 0;
  transition: opacity 0.6s ease, transform 0.6s ease;
  pointer-events: none;
}
.soul:hover .soul-hint,
.soul:focus-visible .soul-hint {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}

.sigil .seal-text {
  font-family: var(--font-mono);
  font-weight: 400;
  font-size: 12px;
  fill: var(--gold);
  letter-spacing: 0.1em;
  text-anchor: middle;
  opacity: 0;
  transition: opacity 0.6s ease;
}

/* Sealed state — after a successful witness */
.sigil.sealed .seal-text { opacity: 1; }
.sigil.sealed .pupil {
  animation: none;
  transform: scale(0.4);
  fill: var(--gold);
  filter: drop-shadow(0 0 4px var(--gold-soft));
}

/* ── Responsive sigil sizing ─────────────────────────────
   On narrow screens the rings tighten and the central sigil
   needs to grow proportionally to remain a comfortable tap
   target (~56px minimum). */
@media (max-width: 720px) {
  .sigil-mount { width: 22%; }
  /* Room for the two-line header (status + dropped nav) and the
     taller mobile footer, so the astrolabe never collides. */
  .stage { padding: 78px 0 150px; }
}
@media (max-width: 380px) {
  .sigil-mount { width: 26%; }
}

/* ══ 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; }
}

/* ══ composer.css ══════════════════════════════════════════ */
/* ============================================================
   composer.css — the Inscription. Not a cramped modal: a
   full-screen, breathing, cinematic rite. The cosmos behind the
   Gate glows through; the words are the hero; every control has
   room to breathe.
   ============================================================ */

/* The mood colour of the chosen intent. Registered so it can animate. */
@property --intent {
  syntax: "<color>";
  inherits: true;
  initial-value: #d4ad4f;
}

/* ── The stage (overrides the generic dark modal-veil) ─────────── */
.composer-veil.modal-veil {
  --intent: #d4ad4f;
  display: none;
  padding: 0;
  align-items: stretch;
  justify-content: stretch;
  background:
    radial-gradient(130% 100% at 50% 24%, rgba(10, 8, 14, 0.42), rgba(2, 1, 4, 0.90) 78%);
  backdrop-filter: blur(20px) saturate(1.12);
  -webkit-backdrop-filter: blur(20px) saturate(1.12);
  transition: --intent 0.8s ease;
}
.composer-veil.modal-veil.show { display: block; }

/* an aura tinted by the chosen intent — Love blushes the whole rite rose */
.cmp-aura {
  position: fixed;
  inset: 0;
  pointer-events: none;
  background:
    radial-gradient(46vmax 46vmax at 50% 34%, color-mix(in srgb, var(--intent) 18%, transparent), transparent 60%),
    radial-gradient(72vmax 52vmax at 50% 118%, color-mix(in srgb, var(--intent) 8%, transparent), transparent 62%);
  animation: cmp-aura-breathe 9s ease-in-out infinite;
}
@keyframes cmp-aura-breathe { 0%,100% { opacity: .72; } 50% { opacity: 1; } }

.cmp-x {
  position: fixed;
  top: 22px; right: 26px;
  z-index: 5;
  width: 42px; height: 42px;
  border-radius: 50%;
  border: 1px solid var(--bone-trace);
  background: rgba(0,0,0,0.3);
  color: var(--bone-dim);
  font-size: 22px; line-height: 1;
  cursor: pointer;
  transition: color .3s, border-color .3s, transform .3s;
}
.cmp-x:hover { color: var(--bone); border-color: var(--gold-soft); transform: rotate(90deg); }

.cmp-scroll {
  position: relative;
  height: 100%;
  overflow-y: auto;
  display: grid;
  place-items: center;
  padding: clamp(56px, 9vh, 100px) 24px clamp(40px, 8vh, 80px);
}
.cmp-inner {
  position: relative;
  width: min(640px, 100%);
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  animation: cmp-enter 0.7s cubic-bezier(.2, .8, .2, 1) both;
}
@keyframes cmp-enter { from { opacity: 0; transform: translateY(22px); } to { opacity: 1; transform: none; } }

/* ── Heading ───────────────────────────────────────────────────── */
.cmp-eyebrow {
  font-family: var(--font-mono);
  font-weight: 300;
  font-size: 10px;
  letter-spacing: 0.42em;
  padding-left: 0.42em;
  color: var(--gold-soft);
  margin-bottom: 16px;
}
.cmp-heading {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 400;
  font-size: clamp(28px, 5vw, 44px);
  line-height: 1.15;
  color: var(--bone);
  margin: 0 0 16px;
}

/* The plain, warm explainer — so a first-timer instantly knows what this is. */
.cmp-lead {
  max-width: 484px;
  margin: 0 0 8px;
  font-family: var(--font-display);
  font-weight: 300;
  font-size: clamp(14.5px, 1.9vw, 16.5px);
  line-height: 1.75;
  color: var(--bone-dim);
}
.cmp-lead strong { color: var(--gold-soft); font-weight: 400; }

/* Quiet numbered guides, so the rite reads as three simple steps. */
.cmp-step {
  display: flex;
  align-items: center;
  gap: 10px;
  justify-content: center;
  margin: 24px 0 16px;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--bone-faint);
}
.cmp-step-n {
  display: inline-grid;
  place-items: center;
  width: 20px; height: 20px;
  border-radius: 50%;
  border: 1px solid rgba(212,173,79,0.4);
  color: var(--gold-soft);
  font-size: 10px; letter-spacing: 0;
}

/* ── Intent chips ──────────────────────────────────────────────── */
.cmp-intents {
  display: flex;
  flex-wrap: wrap;
  gap: 9px;
  justify-content: center;
  margin-bottom: 6px;
}
.cmp-chip {
  --c: #d4ad4f;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: 0.1em;
  color: var(--bone-dim);
  padding: 10px 16px;
  border: 1px solid var(--bone-trace);
  border-radius: 999px;
  background: rgba(0,0,0,0.16);
  cursor: pointer;
  transition: color .35s, border-color .35s, background .35s, box-shadow .35s, transform .35s;
  white-space: nowrap;
}
.cmp-chip-g { font-size: 14px; color: color-mix(in srgb, var(--c) 70%, var(--bone-dim)); transition: color .35s, transform .35s; }
.cmp-chip:hover { color: var(--bone); border-color: color-mix(in srgb, var(--c) 32%, var(--bone-trace)); transform: translateY(-1px); }
.cmp-chip:hover .cmp-chip-g { color: var(--c); }
.cmp-chip.on {
  color: #fff;
  border-color: color-mix(in srgb, var(--c) 68%, transparent);
  background: color-mix(in srgb, var(--c) 14%, transparent);
  box-shadow: 0 0 26px color-mix(in srgb, var(--c) 24%, transparent), inset 0 0 14px color-mix(in srgb, var(--c) 9%, transparent);
}
.cmp-chip.on .cmp-chip-g { color: var(--c); animation: cmp-glyph-pulse 2.6s ease-in-out infinite; }

/* the chosen glyph comes alive — each intent in its own way */
@keyframes cmp-glyph-pulse { 0%,100% { transform: scale(1); } 50% { transform: scale(1.16); } }
@keyframes cmp-heartbeat { 0%,100% { transform: scale(1); } 12% { transform: scale(1.34); } 24% { transform: scale(1.05); } 38% { transform: scale(1.24); } 54% { transform: scale(1); } }
@keyframes cmp-twinkle { 0%,100% { opacity: .75; transform: rotate(0) scale(1); } 50% { opacity: 1; transform: rotate(22deg) scale(1.18); } }
@keyframes cmp-sway { 0%,100% { transform: rotate(-8deg); } 50% { transform: rotate(8deg); } }
.composer-veil[data-intent="0"] .cmp-chip.on .cmp-chip-g { animation: cmp-twinkle 3s ease-in-out infinite; }
.composer-veil[data-intent="2"] .cmp-chip.on .cmp-chip-g { animation: cmp-heartbeat 1.25s ease-in-out infinite; }
.composer-veil[data-intent="5"] .cmp-chip.on .cmp-chip-g { animation: cmp-sway 2.8s ease-in-out infinite; transform-origin: 50% 25%; }

/* ── The writing surface — the tablet ──────────────────────────────
   This is the one moment the whole project exists for: a person putting
   permanent words into the world. It was a bare textarea with a hairline
   under it — a form field, on the page where something is carved.

   So: a recessed panel with a struck corner at each end, the way the
   Notary figure in the art itself is drawn ("a ruled tablet with a
   struck seal at one corner"). Restrained on purpose — fine gold on
   near-black IS this project's face, and a card with a glow and a
   gradient would be a different project's. */
.cmp-write {
  position: relative;
  width: 100%;
  margin-bottom: 6px;
  padding: 22px 20px 14px;
  border: 1px solid var(--bone-trace);
  background:
    /* the light the words are written by — it answers the intent */
    radial-gradient(120% 150% at 50% -30%,
      color-mix(in srgb, var(--intent, #d4ad4f) 7%, transparent), transparent 62%),
    linear-gradient(180deg, rgba(0,0,0,0.34), rgba(0,0,0,0.16));
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,0.028),        /* the lit top edge of a cut */
    inset 0 22px 44px -30px rgba(0,0,0,0.9);      /* and the depth below it */
  transition: border-color .5s ease, box-shadow .5s ease;
}

/* Four struck corners. Drawn as background gradients rather than four
   elements, so the markup stays a textarea and a caption. */
.cmp-write::before {
  content: "";
  position: absolute;
  inset: 7px;
  pointer-events: none;
  background:
    linear-gradient(to right, var(--gold-soft, #d4ad4f) 0 14px, transparent 14px) 0 0 / 100% 1px no-repeat,
    linear-gradient(to bottom, var(--gold-soft, #d4ad4f) 0 14px, transparent 14px) 0 0 / 1px 100% no-repeat,
    linear-gradient(to left,  var(--gold-soft, #d4ad4f) 0 14px, transparent 14px) 100% 0 / 100% 1px no-repeat,
    linear-gradient(to bottom, var(--gold-soft, #d4ad4f) 0 14px, transparent 14px) 100% 0 / 1px 100% no-repeat,
    linear-gradient(to right, var(--gold-soft, #d4ad4f) 0 14px, transparent 14px) 0 100% / 100% 1px no-repeat,
    linear-gradient(to top,   var(--gold-soft, #d4ad4f) 0 14px, transparent 14px) 0 100% / 1px 100% no-repeat,
    linear-gradient(to left,  var(--gold-soft, #d4ad4f) 0 14px, transparent 14px) 100% 100% / 100% 1px no-repeat,
    linear-gradient(to top,   var(--gold-soft, #d4ad4f) 0 14px, transparent 14px) 100% 100% / 1px 100% no-repeat;
  opacity: 0.34;
  transition: opacity .5s ease;
}

/* The cuneiform AION has used since the Codex: 𐎠𐎭𐎶 — "I am". It sits at
   the head of the tablet the way a seal sits at the head of a stele. */
/* Inside the tablet, not straddling its edge. Sitting on the border needed
   a background to mask the line behind it, and a patch of flat black over
   a lit panel reads as a sticker — measured, not guessed: the ::after was
   pure black while nothing around it was. */
.cmp-write::after {
  content: "𐎠𐎭𐎶";
  position: absolute;
  top: 6px; left: 50%; translate: -50% 0;
  font-family: var(--font-cuneiform);
  font-size: 10px;
  letter-spacing: 0.34em;
  color: var(--bone-faint);
  opacity: 0.55;
  pointer-events: none;
  transition: color .5s ease, opacity .5s ease;
}

/* Writing lights the tablet. `focus-within` and not `:focus`, so the
   frame stays lit while the caret is in it. */
.cmp-write:focus-within {
  /* Mixed with `transparent`, not with `--bone-trace`.
     `color-mix` averages the ALPHA channel too, and --bone-trace is
     rgba(…,0.04) — so mixing into it produced a focused border at alpha
     0.039 against a resting 0.04. Measured in the browser: the focus
     indicator was invisible, on a field whose outline this rule replaces.
     Mixing into `transparent` sets the alpha deliberately instead. */
  border-color: color-mix(in srgb, var(--intent, #d4ad4f) 48%, transparent);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,0.04),
    inset 0 22px 44px -30px rgba(0,0,0,0.9),
    0 26px 60px -44px color-mix(in srgb, var(--intent, #d4ad4f) 85%, transparent);
}
.cmp-write:focus-within::before { opacity: 0.7; }
.cmp-write:focus-within::after {
  color: color-mix(in srgb, var(--intent, #d4ad4f) 70%, var(--gold-soft));
  opacity: 1;
}

#cmpText {
  position: relative;
  width: 100%;
  min-height: 116px;
  max-height: 320px;
  resize: none;
  background: transparent;
  border: none;
  color: var(--bone);
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 300;
  font-size: clamp(19px, 2.6vw, 25px);
  line-height: 1.6;
  text-align: center;
  padding: 6px 6px 10px;
}
#cmpText::placeholder { color: var(--bone-faint); font-style: italic; }
/* The frame carries the focus state now — a second ring inside it would
   be two rings. The visible indicator is the lit tablet above, which is
   larger and clearer than an outline, not weaker. */
#cmpText:focus { outline: none; }

.cmp-meta {
  position: relative;
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid color-mix(in srgb, var(--bone-trace) 70%, transparent);
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.1em;
  color: var(--bone-faint);
}
.cmp-meta #cmpHash { color: var(--gold-soft); word-break: break-all; text-align: left; }

/* ── The two choices ───────────────────────────────────────────── */
.cmp-choices {
  width: 100%;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 18px;
}
.cmp-choice-label {
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: var(--bone-faint);
  margin-bottom: 10px;
}
.cmp-seg {
  display: flex;
  border: 1px solid var(--bone-trace);
  border-radius: 8px;
  overflow: hidden;
  background: rgba(0,0,0,0.18);
}
.cmp-seg button {
  flex: 1;
  font-family: var(--font-mono);
  font-size: 9.5px;
  letter-spacing: 0.06em;
  color: var(--bone-dim);
  background: transparent;
  border: none;
  border-right: 1px solid var(--bone-trace);
  padding: 11px 4px;
  cursor: pointer;
  transition: color .3s, background .3s;
}
.cmp-seg button:last-child { border-right: none; }
.cmp-seg button:hover { color: var(--bone); }
.cmp-seg button.on { color: #ffe9b8; background: var(--gold-trace); }

.cmp-reveal-inputs { width: 100%; display: flex; flex-direction: column; gap: 10px; margin-top: 14px; }
.cmp-input {
  width: 100%;
  background: rgba(0,0,0,0.30);
  border: 1px solid var(--bone-trace);
  border-radius: 8px;
  color: var(--bone);
  font-family: var(--font-mono);
  font-size: 12.5px;
  padding: 12px 14px;
  text-align: center;
  animation: cmp-slip 0.4s ease both;
}
.cmp-input:focus { outline: none; border-color: rgba(212,173,79,0.5); }
.cmp-input[hidden] { display: none; }
@keyframes cmp-slip { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: none; } }

/* A plain, practical word about the chosen audience. */
.cmp-help {
  max-width: 460px;
  margin: 14px auto 0;
  font-family: var(--font-mono);
  font-weight: 300;
  font-size: 10.5px;
  line-height: 1.7;
  letter-spacing: 0.04em;
  color: var(--bone-dim);
}

/* Live social proof — the cosmos is already witnessing. */
.cmp-souls {
  margin: 4px 0 0;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.1em;
  color: color-mix(in srgb, var(--intent) 60%, var(--bone-dim));
  transition: color .6s ease;
}
.cmp-souls b { color: color-mix(in srgb, var(--intent) 85%, var(--bone)); font-weight: 400; }

/* STEAM — the price is climbing; inscribe before the next step. */
.cmp-steam {
  margin: 6px 0 0;
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: 0.08em;
  color: var(--ember, #ff7a5c);
}
.cmp-steam b { color: #ffb98a; font-weight: 400; }

/* ── The vow — the emotional summary, the line that sells it ───── */
.cmp-vow {
  margin: 34px 0 10px;
  max-width: 520px;
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 400;
  font-size: clamp(16px, 2.2vw, 20px);
  line-height: 1.6;
  color: color-mix(in srgb, var(--intent) 80%, var(--bone));
  text-shadow: 0 0 26px color-mix(in srgb, var(--intent) 22%, transparent);
  transition: color .6s ease;
}

/* ── The seal CTA ──────────────────────────────────────────────── */
.cmp-seal {
  position: relative;
  margin-top: 18px;
  display: inline-flex;
  align-items: center;
  gap: 16px;
  font-family: var(--font-mono);
  font-size: 13px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: #fff6dd;
  padding: 18px 40px;
  border-radius: 999px;
  border: 1px solid rgba(212,173,79,0.6);
  background:
    linear-gradient(120deg, transparent 30%, rgba(255,240,205,0.20) 48%, transparent 66%) no-repeat,
    radial-gradient(130% 220% at 50% -20%, rgba(212,173,79,0.30) 0%, rgba(212,173,79,0.08) 55%, transparent 78%);
  background-size: 260% 100%, 100% 100%;
  background-position: 180% 0, center;
  box-shadow: 0 0 30px rgba(212,173,79,0.18), inset 0 0 20px rgba(212,173,79,0.08);
  cursor: pointer;
  overflow: hidden;
  transition: transform .4s cubic-bezier(.2,.8,.2,1), box-shadow .4s, background-position 1s ease, border-color .4s;
}
.cmp-seal:hover {
  transform: translateY(-2px);
  border-color: rgba(255,230,160,0.9);
  background-position: -70% 0, center;
  box-shadow: 0 0 52px rgba(212,173,79,0.4), inset 0 0 24px rgba(212,173,79,0.14);
}
.cmp-seal[disabled] { opacity: 0.6; cursor: default; transform: none; }
.cmp-seal-price { color: var(--gold); letter-spacing: 0.1em; }
.cmp-seal-price b { color: #ffe9b8; }

.cmp-premium {
  margin-top: 14px;
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.1em;
  color: var(--bone-faint);
}

/* A calm word of reassurance — permanence is the point, but consent is safe. */
/* ── What an inscription gives you (P4) ──────────────────────────────
   Three facts the contract enforces, at the one moment a person is
   deciding whether to make one. Quiet — this is not a sales panel, it
   is the answer to "what am I actually getting", which the site had
   never given. The bold lead is the claim; the rest is the mechanism. */
.cmp-gives {
  max-width: 440px;
  margin: 18px auto 0;
  padding: 15px 18px;
  list-style: none;
  border: 1px solid var(--bone-trace);
  border-radius: 4px;
  background: rgba(212,173,79,0.028);
  display: grid;
  gap: 10px;
  font-family: var(--font-display);
  font-weight: 300;
  font-size: 13px;
  line-height: 1.62;
  color: var(--bone-dim);
  text-align: left;
}
.cmp-gives b { font-weight: 400; color: var(--bone); }
.cmp-gives li { position: relative; padding-left: 15px; }
.cmp-gives li::before {
  content: "✶";
  position: absolute; left: 0; top: 0.15em;
  font-size: 9px; color: var(--gold-soft, #d4ad4f);
}

.cmp-reassure {
  max-width: 440px;
  margin: 12px auto 0;
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 300;
  font-size: 12.5px;
  line-height: 1.6;
  color: var(--bone-faint);
}

/* ── Status ────────────────────────────────────────────────────── */
.cmp-status {
  margin-top: 16px;
  max-width: 520px;
  font-family: var(--font-mono);
  font-size: 10px;
  line-height: 1.8;
  letter-spacing: 0.05em;
  color: var(--bone-dim);
  word-break: break-all;
}
.cmp-status.ok  { color: var(--jade, #5fb392); }
.cmp-status.err { color: var(--ember, #ff6b5b); }
.cmp-status .cmp-prepared {
  display: block;
  margin-top: 10px;
  padding: 14px 16px;
  border: 1px dashed var(--bone-trace);
  border-radius: 9px;
  color: var(--bone-faint);
  text-align: left;
}
.cmp-status .cmp-prepared b { color: var(--gold-soft); }

@media (max-width: 540px) {
  .cmp-choices { grid-template-columns: 1fr; gap: 16px; }
  .cmp-x { top: 14px; right: 16px; }
}

/* ── The Gate's call to the rite — a living invitation ─────────────
   The entry button breathes, so its presence matches the weight of
   what waits behind it. */
#witnessBtn {
  animation: cta-breathe 5.5s ease-in-out infinite;
}
#witnessBtn:hover { animation: none; }
@keyframes cta-breathe {
  0%, 100% { box-shadow: 0 0 22px rgba(212,173,79,0.12), inset 0 0 14px rgba(212,173,79,0.05); }
  50%      { box-shadow: 0 0 44px rgba(212,173,79,0.30), inset 0 0 22px rgba(212,173,79,0.12); }
}

/* ── The sealing rite — the magnificent payoff ─────────────────── */
.cmp-inner.receding {
  opacity: 0;
  transform: scale(.96);
  transition: opacity .45s ease, transform .45s ease;
  pointer-events: none;
}
.cmp-rite {
  grid-area: 1 / 1;            /* stack over .cmp-inner in the scroll grid */
  position: relative;
  width: min(560px, 100%);
  display: flex; flex-direction: column; align-items: center; text-align: center;
  padding: clamp(40px, 8vh, 80px) 24px;
  opacity: 0; transform: scale(.92);
  transition: opacity .9s ease, transform .9s cubic-bezier(.2,.8,.2,1);
}
.cmp-rite.show { opacity: 1; transform: none; }

.cmp-rite-seal {
  position: relative;
  width: clamp(180px, 40vmin, 300px);
  aspect-ratio: 1;
  margin-bottom: 34px;
  filter:
    drop-shadow(0 0 34px color-mix(in srgb, var(--intent) 60%, transparent))
    drop-shadow(0 0 90px color-mix(in srgb, var(--intent) 30%, transparent));
}
.cmp-rite.show .cmp-rite-seal { animation: cmp-rite-bloom 1.7s cubic-bezier(.2,.8,.2,1) both; }
.cmp-rite-seal .seal-svg { width: 100%; height: 100%; display: block; animation: cmp-rite-spin 90s linear infinite; }
@keyframes cmp-rite-bloom { 0% { transform: scale(.15); opacity: 0; } 55% { opacity: 1; } 100% { transform: scale(1); opacity: 1; } }
@keyframes cmp-rite-spin { to { transform: rotate(360deg); } }

.cmp-rite-seal::before, .cmp-rite-seal::after {
  content: ""; position: absolute; inset: 8%;
  border-radius: 50%;
  border: 1px solid color-mix(in srgb, var(--intent) 45%, transparent);
  opacity: 0;
}
.cmp-rite.show .cmp-rite-seal::before { animation: cmp-rite-ring 3s ease-out 0.6s infinite; }
.cmp-rite.show .cmp-rite-seal::after  { animation: cmp-rite-ring 3s ease-out 1.6s infinite; }
@keyframes cmp-rite-ring { 0% { transform: scale(.8); opacity: .7; } 100% { transform: scale(1.9); opacity: 0; } }

.cmp-rite-title {
  font-family: var(--font-display); font-style: italic; font-weight: 400;
  font-size: clamp(24px, 4.4vw, 40px); line-height: 1.2;
  color: var(--bone); margin: 0;
  text-shadow: 0 0 30px color-mix(in srgb, var(--intent) 24%, transparent);
}
.cmp-rite-sub {
  margin: 14px 0 0;
  font-family: var(--font-display); font-style: italic; font-weight: 300;
  font-size: clamp(13px, 1.8vw, 16px);
  color: color-mix(in srgb, var(--intent) 75%, var(--bone-dim));
}
.cmp-rite-result { margin-top: 22px; width: 100%; }
.cmp-rite-result .cmp-prepared {
  display: inline-block; text-align: left;
  font-family: var(--font-mono); font-size: 10px; line-height: 1.9; letter-spacing: 0.05em;
  color: var(--bone-faint);
  padding: 14px 18px; border: 1px dashed var(--bone-trace); border-radius: 10px;
}
.cmp-rite-result .cmp-prepared b { color: var(--gold-soft); }
.cmp-rite-note { display: block; margin-top: 8px; color: var(--bone-faint); }

/* The referral hook in the rite — the viral engine. */
.cmp-ref {
  margin: 22px auto 0;
  max-width: 460px;
  padding: 16px 18px;
  border: 1px solid color-mix(in srgb, var(--intent) 40%, var(--bone-trace));
  border-radius: 12px;
  background: color-mix(in srgb, var(--intent) 6%, transparent);
  text-align: center;
}
.cmp-ref-h {
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.16em; text-transform: uppercase;
  color: color-mix(in srgb, var(--intent) 80%, var(--bone)); margin-bottom: 10px;
}
.cmp-ref-row { display: flex; gap: 8px; align-items: stretch; }
.cmp-ref-link {
  flex: 1; min-width: 0;
  background: rgba(0,0,0,0.4); border: 1px solid var(--bone-trace); border-radius: 7px;
  color: var(--bone-dim); font-family: var(--font-mono); font-size: 11px; padding: 9px 11px;
  text-overflow: ellipsis;
}
.cmp-ref-link:focus { outline: none; border-color: color-mix(in srgb, var(--intent) 50%, transparent); }
.cmp-ref-copy {
  font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.12em; text-transform: uppercase;
  color: #fff6dd; padding: 0 16px; border-radius: 7px; cursor: pointer; white-space: nowrap;
  border: 1px solid color-mix(in srgb, var(--intent) 55%, transparent);
  background: color-mix(in srgb, var(--intent) 16%, transparent);
  transition: box-shadow .3s;
}
.cmp-ref-copy:hover { box-shadow: 0 0 18px color-mix(in srgb, var(--intent) 28%, transparent); }
.cmp-ref-note {
  margin-top: 10px; font-family: var(--font-display); font-style: italic; font-weight: 300;
  font-size: 12.5px; line-height: 1.55; color: var(--bone-faint);
}

.cmp-rite-actions { margin-top: 28px; display: flex; gap: 14px; flex-wrap: wrap; justify-content: center; }
.cmp-rite-link, .cmp-rite-again {
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.16em; text-transform: uppercase;
  padding: 12px 24px; border-radius: 999px; cursor: pointer; transition: color .3s, border-color .3s, box-shadow .3s, transform .3s;
}
.cmp-rite-link {
  color: #fff6dd; border: 1px solid color-mix(in srgb, var(--intent) 60%, transparent);
  background: color-mix(in srgb, var(--intent) 14%, transparent);
}
.cmp-rite-link:hover { box-shadow: 0 0 26px color-mix(in srgb, var(--intent) 30%, transparent); transform: translateY(-2px); }
.cmp-rite-again { color: var(--bone-dim); border: 1px solid var(--bone-trace); background: transparent; }
.cmp-rite-again:hover { color: var(--bone); border-color: var(--bone-ghost); }

/* ── The gift link — shown once, never stored ──────────────────────
   Deliberately heavier than the rest of the rite: this is the one
   moment where losing a piece of text loses the gift forever, so it
   should feel like being handed a key rather than shown a receipt. */

.cmp-gift {
  margin-top: 18px;
  padding: 16px 18px;
  border: 1px solid rgba(212, 173, 79, 0.34);
  border-radius: 4px;
  background: rgba(212, 173, 79, 0.05);
  text-align: left;
}

.cmp-gift-title {
  font-family: var(--font-display, "Cinzel", serif);
  font-size: 12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--gold-soft, #d4ad4f);
  margin: 0 0 10px;
}

.cmp-gift-url {
  width: 100%;
  padding: 9px 11px;
  font-family: var(--font-mono, "IBM Plex Mono", monospace);
  font-size: 12px;
  color: var(--bone, #ede8d8);
  background: rgba(0, 0, 0, 0.42);
  border: 1px solid rgba(212, 173, 79, 0.24);
  border-radius: 3px;
  margin-bottom: 10px;
}

.cmp-gift-note,
.cmp-gift-warn {
  font-family: var(--font-body, "Cormorant Garamond", serif);
  font-size: 14px;
  line-height: 1.5;
  margin: 10px 0 0;
}

.cmp-gift-note { color: var(--bone, #ede8d8); opacity: 0.8; }
.cmp-gift-warn { color: #e0a06a; opacity: 0.9; }

/* ══ intro.css ══════════════════════════════════════════ */
/* ============================================================
   intro.css
   The wordless prologue. A moment is born; it begins to fade;
   an eye opens; the eye sees; the moment is sealed in gold.
   No words. No language barrier. The user understands what
   "witness" means before the first label is read.
   ============================================================ */

.intro {
  position: fixed;
  inset: 0;
  z-index: 30;        /* above instrument, below modals & toasts */
  display: grid;
  place-items: center;
  pointer-events: none;
  background: radial-gradient(
    ellipse at 50% 50%,
    rgba(0, 0, 0, 0) 0%,
    rgba(0, 0, 0, 0.4) 60%,
    rgba(0, 0, 0, 0.85) 100%
  );
  animation: intro-fade-out 1.4s ease-out 4.6s forwards;
}

.intro.skip {
  animation: intro-fade-out 0.6s ease-out forwards;
}

.intro-stage {
  width: min(50vmin, 420px);
  aspect-ratio: 1;
  position: relative;
}
.intro-stage svg {
  width: 100%;
  height: 100%;
  overflow: visible;
}

/* ── The mote · a passing moment of light ───────────── */

.intro-mote {
  fill: #ffffff;
  filter: drop-shadow(0 0 6px rgba(255, 255, 255, 0.9));
  opacity: 0;
  transform-origin: 100px 100px;
  transform-box: view-box;
  animation:
    intro-mote-appear 0.9s ease-out 0.4s forwards,
    intro-mote-pulse  1.4s ease-in-out 1.3s 1,
    intro-mote-dim    0.7s ease-in 2.1s forwards,
    intro-mote-seal   0.6s cubic-bezier(.2,.8,.2,1) 3.6s forwards;
}

.intro-mote-glow {
  fill: rgba(255, 255, 255, 0.18);
  opacity: 0;
  transform-origin: 100px 100px;
  transform-box: view-box;
  animation:
    intro-glow-appear 1.0s ease-out 0.6s forwards,
    intro-glow-dim    0.7s ease-in   2.1s forwards,
    intro-glow-bloom  0.8s cubic-bezier(.2,.8,.2,1) 3.5s forwards;
}

/* ── The eye · the witness opens ────────────────────── */

.intro-eye-top, .intro-eye-bot {
  fill: none;
  stroke: rgba(237, 232, 216, 0.85);
  stroke-width: 0.7;
  stroke-linecap: round;
  stroke-dasharray: 220;
  stroke-dashoffset: 220;
  opacity: 0;
  animation:
    intro-eye-fade 0.4s ease-out 2.4s forwards,
    intro-eye-draw 1.0s cubic-bezier(.4,.0,.2,1) 2.4s forwards,
    intro-eye-dissolve 0.7s ease-out 4.0s forwards;
}

.intro-iris {
  fill: none;
  stroke: rgba(212, 173, 79, 0.6);
  stroke-width: 0.6;
  opacity: 0;
  transform-origin: 100px 100px;
  transform-box: view-box;
  animation:
    intro-iris-appear  0.8s ease-out 3.0s forwards,
    intro-iris-dissolve 0.7s ease-out 4.0s forwards;
}

.intro-iris-pupil {
  fill: rgba(212, 173, 79, 0.15);
  opacity: 0;
  transform-origin: 100px 100px;
  transform-box: view-box;
  animation:
    intro-pupil-appear 0.6s ease-out 3.2s forwards,
    intro-pupil-dissolve 0.6s ease-out 4.0s forwards;
}

/* ── The radiant ring · the seal is inscribed ───────── */

.intro-radiant {
  fill: none;
  stroke: rgba(212, 173, 79, 0.9);
  stroke-width: 0.5;
  opacity: 0;
  transform-origin: 100px 100px;
  transform-box: view-box;
  animation: intro-radiant 1.4s cubic-bezier(.2,.6,.2,1) 3.7s forwards;
}

.intro-radiant.r2 { animation-delay: 3.95s; }
.intro-radiant.r3 { animation-delay: 4.20s; }

/* ── The Tomb of Cyrus · the lineage frame ──────────────
   Emerges briefly as the seal completes — a silhouette of
   Pasargadae rising into view, then dissolving with the
   rest of the prologue. The visitor learns, without words,
   that AION is the inheritor of an older tradition. */

.intro-tomb {
  fill: none;
  stroke: rgba(212, 173, 79, 0.85);
  stroke-width: 0.6;
  stroke-linejoin: round;
  opacity: 0;
  animation:
    intro-tomb-appear 0.8s ease-out 3.8s forwards,
    intro-tomb-dissolve 0.6s ease-in 4.5s forwards;
}

@keyframes intro-tomb-appear {
  from { opacity: 0; transform: translate(70px, 110px) scale(0.6); }
  to   { opacity: 1; transform: translate(70px, 100px) scale(0.6); }
}

@keyframes intro-tomb-dissolve {
  from { opacity: 1; }
  to   { opacity: 0; }
}

/* ── Skip control ─────────────────────────────────── */

.intro-skip {
  position: absolute;
  bottom: 32px;
  right: 32px;
  pointer-events: auto;
  font-family: var(--font-mono);
  font-weight: 200;
  font-size: 9px;
  letter-spacing: 0.32em;
  color: var(--bone-faint);
  background: none;
  border: none;
  padding: 10px 14px;
  cursor: pointer;
  opacity: 0;
  animation: fade 1s ease-out 1.8s forwards;
  transition: color 0.4s ease, letter-spacing 0.4s ease;
}
.intro-skip:hover {
  color: var(--bone-dim);
  letter-spacing: 0.40em;
}
.intro-skip::after { content: "  →"; }

@media (max-width: 480px) {
  .intro-skip { bottom: 22px; right: 22px; }
}

/* ── Pause the astrolabe reveal until intro completes ─ */

.intro-active .reveal,
.intro-active .sigil-mount,
.intro-active .opacity-fade {
  animation-play-state: paused !important;
}

/* ── Reduced motion · skip the prologue ─────────────── */

@media (prefers-reduced-motion: reduce) {
  .intro { display: none !important; }
  .intro-active .reveal,
  .intro-active .sigil-mount,
  .intro-active .opacity-fade { animation-play-state: running !important; }
}

/* ══ manifesto.css ══════════════════════════════════════════ */
/* ============================================================
   manifesto.css
   The contemplative whisper. Inscriptions from across 2500
   years of witness-making appear, hold, and dissolve. A
   golden hairline draws inward from a central pinpoint
   beneath each. A meditation, not a banner.
   ============================================================ */

.manifesto {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 178px;          /* sits above the footer's action rail */
  z-index: var(--z-instrument);
  display: grid;
  place-items: center;
  pointer-events: none;
  text-align: center;
  /* Wrapper is always visible. JS-driven phrase content
     handles the fade-in/out per line. */
}

.manifesto-phrase {
  position: relative;
  display: grid;
  place-items: center;
  gap: 6px;
  padding: 0 24px;
  min-height: 76px;
}

/* ── The hairline that draws inward beneath each phrase ─ */
.manifesto-rule {
  width: 0;
  height: 1px;
  background: linear-gradient(
    90deg,
    transparent 0%,
    var(--gold-soft) 30%,
    var(--gold-soft) 70%,
    transparent 100%
  );
  opacity: 0;
  transition: width 1.4s cubic-bezier(.4,0,.2,1),
              opacity 0.6s ease;
}
.manifesto-phrase.entering .manifesto-rule {
  width: 200px;
  opacity: 1;
}
.manifesto-phrase.exiting .manifesto-rule {
  width: 0;
  opacity: 0;
  transition: width 0.9s ease-in,
              opacity 0.6s ease 0.3s;
}

/* ── Primary text — English inscription ─────────────── */
.manifesto-en {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 400;
  font-size: clamp(15px, 1.9vw, 19px);
  letter-spacing: 0.10em;
  color: rgba(237, 232, 216, 0.86);
  white-space: nowrap;
  opacity: 0;
  transform: translateY(6px);
  transition: opacity 1.4s ease, transform 1.4s ease;
}

/* ── Cuneiform seal · the inscription's mark ───────── */
.manifesto-cu {
  font-family: var(--font-cuneiform);
  font-weight: 400;
  font-size: clamp(13px, 1.6vw, 16px);
  letter-spacing: 0.20em;
  color: rgba(212, 173, 79, 0.65);
  white-space: nowrap;
  opacity: 0;
  transform: translateY(4px);
  transition: opacity 1.4s ease 0.2s, transform 1.4s ease 0.2s;
}

/* ── Source attribution ─────────────────────────────── */
.manifesto-source {
  font-family: var(--font-mono);
  font-weight: 200;
  font-size: clamp(9px, 1.0vw, 10px);
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: rgba(237, 232, 216, 0.34);
  white-space: nowrap;
  opacity: 0;
  transform: translateY(4px);
  transition: opacity 1.4s ease 0.4s, transform 1.4s ease 0.4s;
}

.manifesto-phrase.entering .manifesto-en,
.manifesto-phrase.entering .manifesto-cu,
.manifesto-phrase.entering .manifesto-source {
  opacity: 1;
  transform: translateY(0);
}
.manifesto-phrase.exiting .manifesto-en,
.manifesto-phrase.exiting .manifesto-cu,
.manifesto-phrase.exiting .manifesto-source {
  opacity: 0;
  transform: translateY(-6px);
  transition: opacity 1.0s ease, transform 1.0s ease;
}

/* ── The pinpoint of light from which the rule emanates ─ */
.manifesto-pinpoint {
  position: absolute;
  top: 24%;
  left: 50%;
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: var(--gold);
  box-shadow: 0 0 6px var(--gold-soft);
  transform: translate(-50%, -50%) scale(0);
  opacity: 0;
  transition: transform 0.6s ease, opacity 0.6s ease;
  pointer-events: none;
}
.manifesto-phrase.entering .manifesto-pinpoint {
  transform: translate(-50%, -50%) scale(1);
  opacity: 1;
  transition-delay: 0.1s;
}
.manifesto-phrase.exiting .manifesto-pinpoint {
  transform: translate(-50%, -50%) scale(0);
  opacity: 0;
}

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

@media (max-width: 720px) {
  .manifesto { bottom: 156px; }
  .manifesto-phrase { min-height: 68px; padding: 0 14px; gap: 4px; }
  .manifesto-phrase.entering .manifesto-rule { width: 150px; }
  .manifesto-en { white-space: normal; max-width: 90vw; }
}

@media (max-width: 480px) {
  .manifesto { bottom: 148px; }
  .manifesto-en { font-size: 14px; letter-spacing: 0.06em; }
  .manifesto-cu { font-size: 12px; }
  .manifesto-source { font-size: 8px; letter-spacing: 0.22em; }
}

@media (max-height: 700px) {
  /* On short viewports the manifesto presses the astrolabe;
     give it a quieter footprint. */
  .manifesto-source { display: none; }
  .manifesto-phrase { min-height: 48px; }
}

@media (prefers-reduced-motion: reduce) {
  .manifesto-en,
  .manifesto-cu,
  .manifesto-source,
  .manifesto-rule,
  .manifesto-pinpoint {
    transition: opacity 0.3s linear !important;
    transform: none !important;
  }
}

/* ══ 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; }
}

/* ══ motion.css ══════════════════════════════════════════ */
/* ============================================================
   motion.css
   All keyframe animations. Consolidated so that anyone
   tuning the rhythm of the system can find every cadence
   in one file.
   ============================================================ */

@keyframes spin {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}

@keyframes fade {
  to { opacity: 1; }
}

@keyframes blink {
  0%, 100% { opacity: 0.35; }
  50%      { opacity: 1; }
}

@keyframes halo {
  0%, 100% { opacity: 0.35; transform: scale(1); }
  50%      { opacity: 0.8;  transform: scale(1.06); }
}

@keyframes heart {
  0%, 100% { transform: scale(0.85); opacity: 0.85; }
  50%      { transform: scale(1.15); opacity: 1; }
}

@keyframes veil-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

@keyframes modal-rise {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: translateY(0); }
}

@keyframes toast-in {
  from { opacity: 0; transform: translate(-50%, -8px); }
  to   { opacity: 1; transform: translate(-50%, 0); }
}

/* ════ Intro keyframes — the wordless prologue ═════════════
   Each frame teaches a step of "what is witnessing":
   1. a moment exists
   2. it would fade
   3. an eye opens
   4. it sees
   5. the moment is sealed
   ═════════════════════════════════════════════════════════ */

@keyframes intro-mote-appear {
  from { opacity: 0; transform: scale(0.4); }
  to   { opacity: 1; transform: scale(1); }
}

@keyframes intro-mote-pulse {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.4); }
}

@keyframes intro-mote-dim {
  from { opacity: 1; transform: scale(1); }
  to   { opacity: 0.18; transform: scale(0.6); }
}

@keyframes intro-mote-seal {
  0%   { fill: #ffffff;            transform: scale(0.6); opacity: 0.18; }
  40%  { fill: #d4ad4f;            transform: scale(2.4); opacity: 1; }
  100% { fill: #d4ad4f;            transform: scale(1.0); opacity: 1; }
}

@keyframes intro-glow-appear {
  from { opacity: 0; transform: scale(0.6); }
  to   { opacity: 0.6; transform: scale(1); }
}

@keyframes intro-glow-dim {
  from { opacity: 0.6; transform: scale(1); }
  to   { opacity: 0.1; transform: scale(0.8); }
}

@keyframes intro-glow-bloom {
  0%   { fill: rgba(255,255,255,0.18); opacity: 0.1; transform: scale(0.8); }
  50%  { fill: rgba(212,173,79,0.4);   opacity: 1;   transform: scale(2.6); }
  100% { fill: rgba(212,173,79,0.18);  opacity: 0.5; transform: scale(1.4); }
}

@keyframes intro-eye-fade   { to { opacity: 1; } }
@keyframes intro-eye-draw   { to { stroke-dashoffset: 0; } }
@keyframes intro-eye-dissolve { to { opacity: 0; stroke: rgba(212,173,79,0.4); } }

@keyframes intro-iris-appear {
  from { opacity: 0; transform: scale(0); }
  to   { opacity: 1; transform: scale(1); }
}

@keyframes intro-iris-dissolve {
  to { opacity: 0; transform: scale(1.6); }
}

@keyframes intro-pupil-appear {
  from { opacity: 0; transform: scale(2); }
  to   { opacity: 1; transform: scale(0.6); }
}

@keyframes intro-pupil-dissolve {
  to { opacity: 0; transform: scale(2); }
}

@keyframes intro-radiant {
  0%   { opacity: 0;    stroke-width: 1;   transform: scale(0.1); }
  20%  { opacity: 0.9;  stroke-width: 0.8; transform: scale(0.6); }
  100% { opacity: 0;    stroke-width: 0.2; transform: scale(2.6); }
}

@keyframes intro-fade-out {
  from { opacity: 1; }
  to   { opacity: 0; visibility: hidden; }
}

/* ── Light particles travelling along ring orbits ── */
@keyframes orbit-particle {
  from { offset-distance:   0%; opacity: 0; }
  10%  {                       opacity: 0.9; }
  90%  {                       opacity: 0.9; }
  to   { offset-distance: 100%; opacity: 0; }
}

/* ── Subtle breathing of the ambient halo ──── */
@keyframes ambient-breath {
  0%, 100% { opacity: 0.04; }
  50%      { opacity: 0.10; }
}

/* ── Brand halo · the divine descent ───────── */
@keyframes brand-halo {
  0%, 100% { opacity: 0.15; transform: scaleX(1); }
  50%      { opacity: 0.32; transform: scaleX(1.1); }
}

/* ── Brand shimmer · starlight sweeping the wordmark ─ */
@keyframes brand-shimmer {
  0%, 100% { background-position: 125% 0; }
  50%      { background-position: -25% 0; }
}

/* ── Action button · descending light ──────── */
@keyframes descend-light {
  0%   { opacity: 0; transform: translateY(-180px) scaleY(0); }
  40%  { opacity: 0.7; transform: translateY(-30px) scaleY(1); }
  100% { opacity: 0; transform: translateY(20px) scaleY(0.6); }
}

/* ── Luminous core · the heart that cannot be ignored ──────
   A breathing bloom and a slow radiant sunburst behind the
   central sigil. The eye of the instrument now emits light. */
@keyframes core-bloom {
  0%, 100% { opacity: 0.55; transform: translate(-50%, -50%) scale(1); }
  50%      { opacity: 0.95; transform: translate(-50%, -50%) scale(1.14); }
}
@keyframes core-rays {
  from { transform: translate(-50%, -50%) rotate(0deg); }
  to   { transform: translate(-50%, -50%) rotate(360deg); }
}

/* The star's colour drifts gold → green → blue → gold, the way a
   sun would read across temperatures. The white-hot core barely
   shifts; only the corona takes on colour. */
@keyframes star-hue {
  0%, 100% { filter: hue-rotate(0deg); }
  33%      { filter: hue-rotate(70deg); }
  66%      { filter: hue-rotate(150deg); }
}

/* ── Drifting nebula · the void breathes with colour ─────── */
@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); }
}

/* The nebula's colour slowly migrates — violet → teal → amber →
   back. The cosmos is never quite the same twice. */
@keyframes nebula-hue {
  0%, 100% { filter: blur(30px) hue-rotate(-18deg) saturate(1); }
  50%      { filter: blur(30px) hue-rotate( 22deg) saturate(1.2); }
}

/* ── Reduced motion
   Honour the user's preference. Static elements remain
   composed; only kinetic embellishments fall away. */

@media (prefers-reduced-motion: reduce) {
  .pivot,
  .pupil,
  .halo,
  .chain-status .dot,
  .reveal,
  .opacity-fade,
  .sigil-mount,
  .sigil-mount::before,
  .sigil-mount::after,
  .atmosphere::before {
    animation: none !important;
    opacity: 1 !important;
  }

  /* Chrome embellishments — stop the motion but keep each element's
     own colour/opacity state (the phases must stay distinguishable). */
  .brand,
  .brand-block::after,
  .brand::after,
  .phase,
  .action.primary::after {
    animation: none !important;
  }
}

/* ══ 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; }
}
