/* ═══════════════════════════════════════════════════════════════════════════
   decysp site — shared stylesheet for every page.

   Every colour is lifted from the app itself:
     dark  = AppColorsDark   (Moody Navy, lib/core/constants/colors.dart)
     light = AppColorsLight  (warm paper)
     oled  = kOledColors     (lib/core/constants/oled_theme.dart)
   Module accents = ModuleColors. Gold = the kGold ramps.

   PERFORMANCE NOTES (why some things look the way they do)
   - The hero aurora is a blurred layer rotated with a TRANSFORM, not an
     animated gradient angle. Transforms run on the GPU compositor; animating
     the gradient itself repaints a huge blurred area every frame, which is
     what made the first version of this page feel laggy.
   - The film grain has no mix-blend-mode. Blending a fixed full-screen layer
     forces the browser to re-composite the entire page on every scroll
     frame. Plain low opacity looks 95% the same and costs almost nothing.
   - Below-the-fold sections use content-visibility:auto so the browser
     skips laying them out until they approach the viewport.
   ═══════════════════════════════════════════════════════════════════════ */

/* The woff2 files are latin subsets of the TTFs beside them — 89% smaller,
   which is most of what this site weighs. Every current browser takes the
   woff2; the full TTFs remain as the fallback and as the source of truth
   for re-subsetting (fonttools' pyftsubset, latin + punctuation ranges). */
@font-face{font-family:'Cormorant Garamond';src:url('fonts/CormorantGaramond-Regular.woff2') format('woff2'),url('fonts/CormorantGaramond-Regular.ttf') format('truetype');font-weight:400;font-style:normal;font-display:swap}
@font-face{font-family:'Cormorant Garamond';src:url('fonts/CormorantGaramond-LightItalic.woff2') format('woff2'),url('fonts/CormorantGaramond-LightItalic.ttf') format('truetype');font-weight:300;font-style:italic;font-display:swap}
@font-face{font-family:'Outfit';src:url('fonts/Outfit-Light.woff2') format('woff2'),url('fonts/Outfit-Light.ttf') format('truetype');font-weight:300;font-display:swap}
@font-face{font-family:'Outfit';src:url('fonts/Outfit-Regular.woff2') format('woff2'),url('fonts/Outfit-Regular.ttf') format('truetype');font-weight:400;font-display:swap}
@font-face{font-family:'Outfit';src:url('fonts/Outfit-Medium.woff2') format('woff2'),url('fonts/Outfit-Medium.ttf') format('truetype');font-weight:500;font-display:swap}
@font-face{font-family:'DM Mono';src:url('fonts/DMMono-Regular.woff2') format('woff2'),url('fonts/DMMono-Regular.ttf') format('truetype');font-weight:400;font-display:swap}
/* The two worlds' own voices (wiki melt only — see THE TWO WORLDS below).
   Both OFL, bundled like everything else; a browser only fetches a face
   when a rendered element asks for it, so these cost nothing until the
   page melts. MedievalSharp carries acre's carved-timber lettering;
   Outfit 700 is the fintech headline weight the base site never needs. */
@font-face{font-family:'MedievalSharp';src:url('fonts/MedievalSharp.woff2') format('woff2');font-weight:400;font-display:swap}
@font-face{font-family:'Outfit';src:url('fonts/Outfit-Bold.woff2') format('woff2');font-weight:700;font-display:swap}

:root{
  /* ── Moody Navy (AppColorsDark) ── */
  --bg:#0F172A; --surface:#1D2737; --card:#293345; --hover:#2A3547;
  --input:#0C121F; --border:rgba(72,87,112,.55); --border-strong:#485770;
  --text:#F1F5F9; --muted:#94A3B8; --faint:#64748B;
  --accent:#818CF8;
  --gold:#C9A96E; --gold-light:#E8D3B0; --gold-deep:#A07840;
  --gold-glow:rgba(201,169,110,.18); --gold-border:rgba(201,169,110,.28);
  --on-gold:#0F172A;
  --grain:.05; --shadow:0 24px 70px -30px rgba(0,0,0,.65);

  /* Time-of-day glow for the hero — set by JS from the visitor's clock,
     the same idea as the app's dawn/dusk/night skins. Defaults to gold. */
  --tod-glow:rgba(201,169,110,.10);

  /* ── ModuleColors, verbatim ── */
  --aurum:#10B981; --keeper:#3B82F6; --folio:#A855F7; --ledger:#F97316;
  --tome:#8B5CF6; --ink:#F59E0B; --hearth:#E8590A; --almanac:#7D9471;
  --estate:#A1724A; --ascent:#14B8A6; --reverie:#6D6A9F; --atelier:#64748B;

  --serif:'Cormorant Garamond',Georgia,serif;
  --sans:'Outfit',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;
  --mono:'DM Mono',ui-monospace,'SF Mono',Menlo,monospace;
  --shell:min(74rem,100% - 3rem);

  /* ── The motion family ─────────────────────────────────────────────────
     One physics for the whole site: SETTLING. Nothing slides in — things
     come to rest, slightly overdamped, like paper laid on a desk. No
     bounce anywhere (this site holds someone's mortgage); the premium
     read comes from every element obeying the same curve, not from any
     one effect. --settle is the signature; --ease/--ease-out are its
     quicker relatives for hovers and small state changes. */
  --ease:cubic-bezier(.35,.7,.25,1);
  --ease-out:cubic-bezier(.18,.85,.26,1);
  --settle:cubic-bezier(.32,.75,.22,1);

  /* ── Light from above ──────────────────────────────────────────────────
     A 1px near-white inner top edge on raised surfaces — the difference
     between a card that is painted and a card that is lit. Dark themes
     get the inner highlight; paper flips it to a soft under-shadow, since
     white-on-white light reads as nothing. */
  --edge:inset 0 1px 0 rgba(255,255,255,.05);
}

/* Warm paper (AppColorsLight). Module colours retuned darker — the emerald
   and amber that read on navy go invisible on paper. */
html[data-theme="light"]{
  --bg:#F2EFEA; --surface:#FFFFFF; --card:#FFFFFF; --hover:#FAF8F4;
  --input:#F7F4EF; --border:#E6E1D8; --border-strong:#D8D1C4;
  --text:#1C1A17; --muted:#6E675E; --faint:#8E877C;
  --accent:#5B6CF6;
  --gold:#8F6B2E; --gold-light:#C9AA73; --gold-deep:#A07840;
  --gold-glow:rgba(160,120,64,.14); --gold-border:rgba(160,120,64,.3);
  --on-gold:#FFF9EE;
  --grain:.03; --shadow:0 24px 60px -34px rgba(60,50,30,.35);
  --edge:0 1px 2px rgba(60,50,30,.08);

  --aurum:#047857; --keeper:#1D4ED8; --folio:#7E22CE; --ledger:#C2410C;
  --tome:#6D28D9; --ink:#B45309; --hearth:#B8430A; --almanac:#5D7452;
  --estate:#8A5A32; --ascent:#0F766E; --reverie:#55518A; --atelier:#4B5563;
}

/* OLED night (kOledColors). Pure black page; surfaces step up so hairlines
   have something to sit on. */
html[data-theme="oled"]{
  --bg:#000000; --surface:#0E0E11; --card:#121216; --hover:#16161A;
  --input:#08080A; --border:rgba(35,35,41,.9); --border-strong:#232329;
  --text:#E8E8ED; --muted:#9A9AA4; --faint:#6E6E78;
  --accent:#8B93FF;
  --gold:#C9A96E; --gold-light:#E8D3B0; --gold-deep:#A07840;
  --gold-glow:rgba(201,169,110,.14); --gold-border:rgba(201,169,110,.26);
  --on-gold:#0A0A0C;
  --grain:.06; --shadow:0 24px 70px -28px rgba(0,0,0,.9);
  --edge:inset 0 1px 0 rgba(255,255,255,.06);
}

*,*::before,*::after{box-sizing:border-box}
/* overflow-x:clip (not hidden) — kills sideways panning from the aurora's
   deliberate bleed without creating a scroll container, so the sticky
   header keeps working everywhere. */
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth;overflow-x:clip;
  /* the scrollbar wears the theme too — one of those touches nobody can
     name but everybody feels */
  scrollbar-color:var(--border-strong) transparent}
@media(prefers-reduced-motion:reduce){html{scroll-behavior:auto}}
body{
  margin:0;background:var(--bg);color:var(--text);
  font-family:var(--sans);font-size:16.5px;font-weight:300;line-height:1.7;
  -webkit-font-smoothing:antialiased;overflow-x:clip;
  transition:background .5s var(--ease),color .5s var(--ease);
}
a{color:inherit;text-decoration:none}
p{margin:0 0 1em}
img{max-width:100%;display:block}
:focus-visible{outline:2px solid var(--gold);outline-offset:3px;border-radius:4px}
::selection{background:var(--gold);color:var(--on-gold)}

h1,h2,h3{font-family:var(--serif);font-weight:400;letter-spacing:-.01em;line-height:1.08;margin:0;
  /* balance evens a heading's ragged edge; pretty spares prose its
     orphans. Both are hints — browsers without them lose nothing. */
  text-wrap:balance}
p,.lede,li{text-wrap:pretty}
.whisper{font-family:var(--serif);font-style:italic;font-weight:300}
.mono{font-family:var(--mono);font-size:.8rem;letter-spacing:.04em}

/* Film grain — the page's one texture. Plain opacity, no blend mode: see
   the performance notes at the top of this file. */
body::after{
  content:"";position:fixed;inset:0;z-index:90;pointer-events:none;
  opacity:var(--grain);
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='.5'/%3E%3C/svg%3E");
}

/* Scroll progress — a gold thread across the top. It ties off with one
   glint when the page has been read to the end. */
#thread{position:fixed;top:0;left:0;height:2px;z-index:70;width:0;
  background:linear-gradient(90deg,var(--gold-deep),var(--gold-light));
  overflow:hidden;
  /* the one element that persists across page navigations: in browsers
     with cross-document view transitions the thread carries over rather
     than being cut and re-spun */
  view-transition-name:thread}
/* crossing a section boundary, it thickens for a beat (site.js) */
#thread.sect{animation:threadSect .7s var(--settle)}
@keyframes threadSect{35%{height:3.5px;box-shadow:0 1px 10px var(--gold-glow)}}
#thread.tied::after{content:"";position:absolute;inset:0;
  background:linear-gradient(90deg,transparent,rgba(255,255,255,.85),transparent);
  transform:translateX(-100%);animation:glint 1.1s ease-out forwards}
@keyframes glint{to{transform:translateX(100%)}}

.logo{border-radius:22%;background:#F6F3EC;flex:none;object-fit:cover}

/* ══════════════ HEADER ══════════════ */
.bar{width:var(--shell);margin-inline:auto;display:flex;align-items:center;gap:.9rem}
#hdr{
  position:sticky;top:0;z-index:40;padding:.8rem 0;
  background:color-mix(in srgb,var(--bg) 74%,transparent);
  backdrop-filter:blur(14px) saturate(1.25);
  -webkit-backdrop-filter:blur(14px) saturate(1.25);
  border-bottom:1px solid transparent;
  transition:border-color .3s var(--ease);
}
#hdr.stuck{border-bottom-color:var(--border)}
#hdr .logo{width:30px;height:30px}
/* view-transition-name: in browsers with cross-document view transitions,
   the mark and the wordmark physically travel between pages while the rest
   of the page crossfades — one element that persists through the house. */
#hdr .logo{view-transition-name:brandmark}
.wordmark{font-family:var(--serif);font-size:1.25rem;color:var(--gold);letter-spacing:.01em;
  view-transition-name:wordmark}
.sp{flex:1}
nav.links{display:flex;gap:1.4rem;font-size:.86rem;color:var(--muted)}
nav.links a{transition:color .2s;position:relative}
nav.links a:hover,nav.links a.here{color:var(--gold)}
/* the unread dot — a newspaper on the doorstep */
nav.links a.unread::after,#menu-btn.unread::after{
  content:"";position:absolute;top:-1px;right:-8px;width:5px;height:5px;
  border-radius:50%;background:var(--gold);box-shadow:0 0 6px var(--gold-glow)}
#menu-btn{position:relative}
#menu-btn.unread::after{top:4px;right:4px}
@media(max-width:840px){nav.links{display:none}}
.ghost{
  border:1px solid var(--border);background:transparent;color:var(--muted);
  border-radius:999px;padding:.42rem .9rem;font-size:.8rem;cursor:pointer;
  font-family:var(--sans);display:inline-flex;align-items:center;gap:.45rem;
  transition:color .2s,border-color .2s,transform .2s;
}
.ghost:hover{color:var(--text);border-color:var(--gold-deep);transform:translateY(-1px)}
#menu-btn .dots{letter-spacing:.14em}

/* ══════════════ SECTIONS / SHARED ══════════════ */
main{width:var(--shell);margin-inline:auto}
section{padding:6rem 0 2rem}
/* Skip layout for far-below-fold sections until they near the viewport.
   contain-intrinsic-size keeps the scrollbar honest. */
.cv{content-visibility:auto;contain-intrinsic-size:auto 760px}
.eyebrow{font-family:var(--mono);font-size:.72rem;letter-spacing:.32em;text-transform:uppercase;color:var(--gold);margin-bottom:1.1rem}
h2{font-size:clamp(2rem,4.5vw,3.1rem);margin-bottom:1rem}
/* The manual <br>s in headings are art direction for wide screens; on a
   phone they force bad breaks, so let the text wrap naturally instead. */
@media(max-width:700px){h2 br{display:none}}
/* Ledes speak in the whisper voice — the serif italic promoted from
   decoration to structure. Cormorant's small x-height wants the extra
   size; at sans 1.05rem this face would read smaller, not equal. */
.lede{color:var(--muted);max-width:44rem;
  font-family:var(--serif);font-style:italic;font-weight:300;
  font-size:1.28rem;line-height:1.6}
/* Reveal = settle. Short travel (14px, not a slide), one curve, done in
   .6s — long fades read as loading, short settles read as arriving. */
.r{opacity:0;transform:translateY(14px);transition:opacity .6s var(--settle),transform .6s var(--settle)}
.r.in{opacity:1;transform:none}
/* Grouped children arrive a breath behind their parent, left to right.
   The container carries .r for the observer and .rs for the choreography.
   The reveal is an ANIMATION, not a transition, on purpose: many of these
   children (buttons, download cards, theme cards) own a transition list
   for their hover states, and whichever rule wins the cascade would erase
   the other's properties — a transition-based reveal either popped in or
   poisoned every hover with its stagger delay. Animations coexist. */
.r.rs{transform:none}
.r.rs:not(.in)>*{opacity:0}
.r.in.rs>*{animation:settleIn .55s var(--settle) both}
@keyframes settleIn{from{opacity:0;translate:0 12px}}
.r.in.rs>*:nth-child(2){animation-delay:55ms}
.r.in.rs>*:nth-child(3){animation-delay:110ms}
.r.in.rs>*:nth-child(4){animation-delay:165ms}
.r.in.rs>*:nth-child(5){animation-delay:220ms}
.r.in.rs>*:nth-child(6){animation-delay:275ms}
.r.in.rs>*:nth-child(7){animation-delay:330ms}
.r.in.rs>*:nth-child(8){animation-delay:385ms}
.r.in.rs>*:nth-child(n+9){animation-delay:430ms}
@media(prefers-reduced-motion:reduce){
  .r,.r.rs:not(.in)>*{opacity:1;transform:none;translate:none;transition:none;animation:none}
  *,*::before,*::after{animation-duration:.001s !important;animation-iteration-count:1 !important;transition-duration:.001s !important}
}

/* ══════════════ SECTION DOT RAIL (desktop) ══════════════ */
#rail-nav{position:fixed;right:1.1rem;top:50%;translate:0 -50%;z-index:45;
  display:flex;flex-direction:column;gap:.7rem}
@media(max-width:1100px){#rail-nav{display:none}}
#rail-nav a{position:relative;width:9px;height:9px;border-radius:50%;
  background:var(--border-strong);opacity:.45;transition:.25s}
#rail-nav a:hover,#rail-nav a.here{background:var(--gold);opacity:1;transform:scale(1.25)}
#rail-nav a span{
  position:absolute;right:1.4rem;top:50%;translate:0 -50%;white-space:nowrap;
  font-family:var(--mono);font-size:.68rem;letter-spacing:.12em;color:var(--gold);
  background:var(--surface);border:1px solid var(--border);border-radius:999px;
  padding:.25rem .7rem;opacity:0;pointer-events:none;transition:opacity .2s;
}
#rail-nav a:hover span{opacity:1}

/* ══════════════ HERO ══════════════ */
#hero{min-height:calc(100svh - 60px);display:grid;grid-template-columns:1.1fr .9fr;
  align-items:center;gap:3rem;padding:3rem 0 4rem;position:relative}
@media(max-width:900px){#hero{grid-template-columns:1fr;padding-top:2rem;text-align:center;min-height:0}}
/* Aurora: a static conic gradient on a layer that ROTATES via transform.
   The wrapper clips nothing (overflow-x:clip on html handles the bleed). */
#aurora{position:absolute;inset:-10% -10%;z-index:-1;pointer-events:none;overflow:hidden;
  contain:strict}
#aurora::before{
  content:"";position:absolute;left:50%;top:40%;width:130vmax;height:130vmax;
  margin:-65vmax 0 0 -65vmax;border-radius:50%;
  background:conic-gradient(
    transparent 0deg, var(--tod-glow) 60deg, transparent 130deg,
    var(--season-glow,rgba(129,140,248,.06)) 200deg, transparent 270deg, var(--gold-glow) 330deg, transparent 360deg);
  filter:blur(60px);opacity:.55;
  animation:aurorot 48s linear infinite;will-change:transform;
}
@keyframes aurorot{to{transform:rotate(1turn)}}
/* Pause the hero's infinite animations once it has scrolled away — they
   would otherwise composite forever behind the rest of the page. */
.offstage #aurora::before,.offstage #phone{animation-play-state:paused}
.offstage #phone::after{animation-play-state:paused}

.h-title{font-size:clamp(2.7rem,6.4vw,4.6rem);line-height:1.04}
.h-title em{font-style:italic;font-weight:300;color:var(--gold)}
.h-sub{color:var(--muted);font-size:1.12rem;max-width:33rem;margin:1.4rem 0 2rem}
@media(max-width:900px){.h-sub{margin-inline:auto}}
.badges{display:flex;gap:.6rem;flex-wrap:wrap;margin-bottom:2.2rem}
@media(max-width:900px){.badges{justify-content:center}}
.badge{font-family:var(--mono);font-size:.72rem;letter-spacing:.06em;color:var(--muted);
  border:1px solid var(--border);border-radius:999px;padding:.34rem .8rem;
  display:inline-flex;align-items:center;gap:.45rem}
.badge i{width:6px;height:6px;border-radius:50%;background:var(--gold);display:inline-block}
.ctas{display:flex;gap:.9rem;flex-wrap:wrap}
@media(max-width:900px){.ctas{justify-content:center}}
.btn{
  display:inline-flex;align-items:center;gap:.5rem;border-radius:999px;cursor:pointer;
  padding:.78rem 1.5rem;font-size:.92rem;font-family:var(--sans);font-weight:400;
  transition:transform .2s var(--ease),box-shadow .25s var(--ease),filter .2s;
}
.btn.gold{background:linear-gradient(140deg,var(--gold-light),var(--gold) 45%,var(--gold-deep));
  color:var(--on-gold);border:0;box-shadow:0 10px 34px -12px var(--gold-glow);
  position:relative;overflow:hidden}
/* The sheen follows the cursor (site.js sets --mx), so the button reads
   as metal rather than paint. Hover-only; never renders on touch. */
.btn.gold::after{content:"";position:absolute;inset:0;pointer-events:none;
  background:radial-gradient(65% 130% at var(--mx,50%) 0%,rgba(255,255,255,.28),transparent 62%);
  opacity:0;transition:opacity .25s var(--ease)}
.btn.gold:hover::after{opacity:1}
.btn.gold:hover{transform:translateY(-2px);box-shadow:0 16px 44px -12px var(--gold-glow);filter:saturate(1.05)}
.btn.line{background:transparent;border:1px solid var(--border);color:var(--muted)}
.btn.line:hover{border-color:var(--gold-deep);color:var(--text);transform:translateY(-2px)}
/* Buttons press DOWN before their action fires — the settle family's
   response to being touched. Placed after the hovers so it wins them. */
.btn.gold:active,.btn.line:active,.btn:active{transform:translateY(1px) scale(.985);transition-duration:.08s}
.ghost:active{transform:translateY(1px);transition-duration:.08s}

/* Floating hub screenshot */
.h-visual{display:grid;place-items:center;perspective:1200px}
#phone{
  width:min(330px,78vw);border-radius:34px;overflow:hidden;position:relative;
  border:1px solid var(--gold-border);box-shadow:var(--shadow),0 0 90px -30px var(--gold-glow);
  transform-style:preserve-3d;transition:transform .18s ease-out;
  animation:float 7s ease-in-out infinite;
}
@keyframes float{0%,100%{translate:0 0}50%{translate:0 -12px}}
#phone img{width:100%}
#phone::after{ /* moving sheen */
  content:"";position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(115deg,transparent 30%,rgba(255,255,255,.06) 46%,transparent 60%);
  background-size:260% 100%;animation:sheen 7.5s ease-in-out infinite;
}
@keyframes sheen{0%,55%{background-position:120% 0}90%,100%{background-position:-60% 0}}

/* Stats strip */
#stats{display:grid;grid-template-columns:repeat(4,1fr);gap:1px;background:var(--border);
  border:1px solid var(--border);border-radius:18px;overflow:hidden;margin-top:1rem}
@media(max-width:700px){#stats{grid-template-columns:repeat(2,1fr)}}
.stat{background:var(--surface);padding:1.6rem 1.2rem;text-align:center;box-shadow:var(--edge)}
.stat b{display:block;font-family:var(--serif);font-weight:400;font-size:2.4rem;color:var(--gold);line-height:1}
.stat span{font-size:.8rem;color:var(--faint);letter-spacing:.08em;text-transform:uppercase;font-family:var(--mono)}

/* ══════════════ LATEST-UPDATE TEASER ══════════════ */
#teaser{display:block;margin-top:1rem;border:1px solid var(--gold-border);border-radius:18px;
  background:linear-gradient(120deg,var(--gold-glow),transparent 55%),var(--surface);
  padding:1.5rem 1.7rem;box-shadow:var(--edge);
  transition:transform .3s var(--ease),box-shadow .3s}
#teaser:hover{transform:translateY(-3px);box-shadow:var(--edge),0 18px 50px -26px var(--gold-glow)}
#teaser .t-date{font-family:var(--mono);font-size:.68rem;letter-spacing:.22em;text-transform:uppercase;color:var(--gold)}
#teaser .t-title{font-family:var(--serif);font-size:1.65rem;margin:.25rem 0 .35rem}
#teaser .t-body{color:var(--muted);font-size:.95rem;max-width:52rem;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
#teaser .t-more{margin-top:.7rem;font-size:.86rem;color:var(--gold)}

/* ══════════════ MODULES ══════════════ */
#mods-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(15.5rem,1fr));gap:1rem;margin-top:2.6rem}
/* A subtle masonry rhythm: even cards sit a step lower, so the grid reads
   as rooms arranged rather than cells filled. Done with the translate
   property, which composes with the hover's transform untouched. Off on
   phones, where the grid is one column and the offset would just be a gap. */
@media(min-width:700px){#mods-grid .mod:nth-child(even){translate:0 12px}}
/* Looking at one room dims the other eleven — the card under the cursor
   gets the light. Hover-capable pointers only. */
@media(hover:hover){
  #mods-grid:hover .mod:not(:hover){opacity:.7;filter:saturate(.82)}
  #mods-grid .mod{transition:transform .3s var(--ease),border-color .3s var(--ease),
    box-shadow .3s var(--ease),opacity .35s var(--ease),filter .35s var(--ease)}
}
/* Each glyph draws itself as its card arrives — every shape carries
   pathLength="1" (set in site.js), so one dasharray rule strokes them all.
   The draw starts a beat after the card lands and takes longer than the
   settle, so the ink is still flowing as the room comes to rest. */
#mods-grid .glyph svg *{stroke-dasharray:1;stroke-dashoffset:1;
  transition:stroke-dashoffset 1s var(--settle) .15s}
#mods-grid .mod.in .glyph svg *{stroke-dashoffset:0}
/* This override must live BELOW the draw rules — same selector, so the
   cascade decides by order, and an earlier copy of it would silently lose. */
@media(prefers-reduced-motion:reduce){
  #mods-grid .glyph svg *{stroke-dashoffset:0;transition:none}
}
.mod{
  --c:var(--gold);
  position:relative;background:var(--card);border:1px solid var(--border);border-radius:18px;
  padding:1.5rem 1.4rem 1.4rem;overflow:hidden;cursor:pointer;text-align:left;
  font-family:var(--sans);color:var(--text);width:100%;box-shadow:var(--edge);
  transition:transform .3s var(--ease),border-color .3s var(--ease),box-shadow .3s var(--ease);
}
.mod:hover{transform:translateY(-5px);border-color:color-mix(in srgb,var(--c) 45%,var(--border));
  box-shadow:var(--edge),0 20px 50px -24px color-mix(in srgb,var(--c) 35%,transparent)}
.mod:active{transform:translateY(-2px) scale(.99);transition-duration:.08s}
.mod::before{
  content:"";position:absolute;top:-40%;right:-30%;width:70%;aspect-ratio:1;border-radius:50%;
  background:radial-gradient(circle,color-mix(in srgb,var(--c) 16%,transparent),transparent 70%);
  opacity:0;transition:opacity .4s;
}
.mod:hover::before{opacity:1}
.mod .glyph{width:38px;height:38px;margin-bottom:1rem;color:var(--c);opacity:.95}
.mod .glyph svg{width:100%;height:100%;stroke:currentColor;fill:none;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round}
.mod h3{font-family:var(--sans);font-weight:500;font-size:1.02rem;color:var(--c);letter-spacing:.02em;margin-bottom:.15rem}
.mod .whisper{color:var(--muted);font-size:1.02rem}
.mod .plain{position:absolute;top:1.2rem;right:1.2rem;font-family:var(--mono);font-size:.62rem;
  letter-spacing:.14em;text-transform:uppercase;color:var(--faint);opacity:0;transition:opacity .3s}
.mod:hover .plain{opacity:1}
.mods-hint{margin-top:1rem;font-family:var(--mono);font-size:.72rem;letter-spacing:.1em;color:var(--faint)}

/* Module detail sheet */
#sheet{position:fixed;inset:0;z-index:82;display:none;place-items:center;padding:1.5rem;
  background:color-mix(in srgb,var(--bg) 82%,transparent);backdrop-filter:blur(14px)}
#sheet.on{display:grid}
.sheet-card{
  --c:var(--gold);
  width:min(34rem,100%);max-height:86svh;overflow-y:auto;position:relative;
  background:var(--card);border:1px solid color-mix(in srgb,var(--c) 40%,var(--border));
  border-radius:22px;padding:2.1rem 2.2rem 1.8rem;box-shadow:var(--edge),var(--shadow);
}
@media(max-width:560px){#sheet{align-items:end;padding:0}
  .sheet-card{border-radius:22px 22px 0 0;max-height:88svh}}
.sheet-card .glyph{width:44px;height:44px;color:var(--c);margin-bottom:1rem}
.sheet-card .glyph svg{width:100%;height:100%;stroke:currentColor;fill:none;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round}
.sheet-card h3{font-family:var(--sans);font-weight:500;font-size:1.3rem;color:var(--c)}
.sheet-card .whisper{color:var(--muted);font-size:1.15rem;margin-bottom:1.1rem}
.sheet-card .s-plain{position:absolute;top:1.6rem;right:1.6rem;font-family:var(--mono);
  font-size:.64rem;letter-spacing:.14em;text-transform:uppercase;color:var(--faint)}
.sheet-card p{color:var(--muted);font-size:.96rem}
.sheet-card ul{margin:.2rem 0 1em;padding-left:1.15rem;color:var(--muted);font-size:.95rem}
.sheet-card li{margin:.35rem 0}
.sheet-card li::marker{color:var(--c)}
.sheet-x{position:absolute;top:.9rem;right:.9rem;width:2rem;height:2rem;border-radius:50%;
  border:1px solid var(--border);background:transparent;color:var(--muted);cursor:pointer;
  font-size:1rem;line-height:1;display:none}
@media(max-width:560px){.sheet-x{display:block}.sheet-card .s-plain{display:none}}

/* ══════════════ GALLERY (A look inside) ══════════════ */
#rail-wrap{position:relative;margin-top:2.6rem}
#rail{
  display:flex;gap:1.4rem;overflow-x:auto;scroll-snap-type:x mandatory;
  padding:1rem .2rem 1.6rem;scrollbar-width:none;cursor:grab;
  /* a soft fade at both ends, so the strip visibly continues rather
     than cropping — the film doesn't end at the projector's gate */
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 2.5rem,#000 calc(100% - 2.5rem),transparent);
  mask-image:linear-gradient(90deg,transparent,#000 2.5rem,#000 calc(100% - 2.5rem),transparent);
}
#rail::-webkit-scrollbar{display:none}
#rail.drag{cursor:grabbing;scroll-snap-type:none}
.shot{
  --c:var(--gold);
  flex:0 0 min(300px,74vw);scroll-snap-align:center;border-radius:24px;overflow:hidden;
  border:1px solid var(--border);position:relative;background:var(--surface);
  transition:transform .35s var(--ease),border-color .35s,box-shadow .35s;
}
.shot:hover{transform:translateY(-6px);border-color:color-mix(in srgb,var(--c) 50%,var(--border));
  box-shadow:0 26px 60px -28px color-mix(in srgb,var(--c) 45%,transparent)}
.shot img{width:100%;aspect-ratio:1290/2796;object-fit:cover;transition:filter .35s var(--ease)}
/* the screen appears lit from within, not spotlit from outside */
.shot:hover img{filter:brightness(1.05)}
.shot figcaption{
  position:absolute;left:0;right:0;bottom:0;padding:2.2rem 1.1rem .9rem;
  display:flex;justify-content:space-between;align-items:baseline;
  background:linear-gradient(transparent,rgba(6,10,18,.82));color:#F1F5F9;
  font-size:.86rem;
}
html[data-theme="light"] .shot figcaption{background:linear-gradient(transparent,rgba(28,26,23,.8));color:#FFF}
.shot figcaption b{font-weight:500;color:var(--c);filter:brightness(1.25)}
.shot figcaption span{font-family:var(--mono);font-size:.66rem;letter-spacing:.12em;text-transform:uppercase;opacity:.75}
#dots{display:flex;gap:.5rem;justify-content:center;margin-top:.4rem}
#dots i{width:7px;height:7px;border-radius:50%;background:var(--border-strong);opacity:.5;transition:.3s;cursor:pointer}
#dots i.on{background:var(--gold);opacity:1;transform:scale(1.25)}
.rail-btn{
  position:absolute;top:42%;z-index:5;width:2.6rem;height:2.6rem;border-radius:50%;
  border:1px solid var(--border);background:color-mix(in srgb,var(--bg) 80%,transparent);
  backdrop-filter:blur(8px);color:var(--text);cursor:pointer;font-size:1rem;
  transition:border-color .2s,transform .2s;
}
.rail-btn:hover{border-color:var(--gold-deep);transform:scale(1.08)}
.rail-btn.prev{left:-.9rem}.rail-btn.next{right:-.9rem}
@media(max-width:700px){.rail-btn{display:none}}

/* Lightbox */
#lb{position:fixed;inset:0;z-index:80;display:none;place-items:center;
  background:color-mix(in srgb,var(--bg) 88%,transparent);backdrop-filter:blur(18px);padding:2rem}
#lb.on{display:grid}
#lb img{max-height:88svh;max-width:min(92vw,420px);border-radius:26px;border:1px solid var(--gold-border);box-shadow:var(--shadow)}
#lb .x{position:absolute;top:1.2rem;right:1.4rem;font-size:1.6rem;background:none;border:0;color:var(--muted);cursor:pointer}
#lb .x:hover{color:var(--text)}

/* ══════════════ WIDGETS ══════════════ */
#widgets-grid{columns:3;column-gap:1.2rem;margin-top:2.6rem}
@media(max-width:900px){#widgets-grid{columns:2}}
@media(max-width:560px){#widgets-grid{columns:1}}
.widget{break-inside:avoid;margin-bottom:1.2rem;border-radius:22px;overflow:hidden;
  border:1px solid var(--border);box-shadow:var(--edge),var(--shadow);
  transition:transform .35s var(--ease),border-color .35s;position:relative}
.widget:hover{transform:translateY(-5px) scale(1.012);border-color:var(--gold-border)}
.widget img{width:100%}

/* ══════════════ SYNC ══════════════ */
#sync-stage{margin-top:2.8rem;border:1px solid var(--border);border-radius:24px;
  background:var(--surface);padding:2.6rem 1.6rem;overflow:hidden;position:relative}
#sync-svg{width:100%;max-width:46rem;margin-inline:auto;display:block}
#sync-svg .dev{fill:var(--card);stroke:var(--border-strong)}
#sync-svg .scr{fill:var(--input)}
#sync-svg .lbl{font-family:var(--mono);font-size:9px;letter-spacing:.14em;fill:var(--faint);text-transform:uppercase}
#sync-svg .sas{font-family:var(--mono);font-size:15px;letter-spacing:.34em;fill:var(--gold)}
#sync-svg .wire{stroke:var(--gold);stroke-width:1.6;fill:none;stroke-dasharray:5 7;animation:wire 1.6s linear infinite;opacity:.85}
@keyframes wire{to{stroke-dashoffset:-24}}
#sync-svg .pulse{fill:var(--gold);animation:pulse 1.6s ease-in-out infinite}
@keyframes pulse{0%,100%{opacity:.25}50%{opacity:1}}
.spec-row{display:flex;gap:.6rem;flex-wrap:wrap;justify-content:center;margin-top:2rem}
.spec{font-family:var(--mono);font-size:.72rem;letter-spacing:.08em;color:var(--gold);
  border:1px solid var(--gold-border);background:var(--gold-glow);border-radius:999px;padding:.4rem .95rem}
.sync-notes{display:grid;grid-template-columns:repeat(auto-fit,minmax(15rem,1fr));gap:1rem;margin-top:2.2rem}
.note{background:var(--card);border:1px solid var(--border);border-radius:16px;padding:1.3rem 1.3rem 1rem;box-shadow:var(--edge)}
.note b{display:block;font-weight:500;font-size:.95rem;margin-bottom:.3rem;color:var(--text)}
.note p{color:var(--muted);font-size:.9rem;margin:0}

/* ══════════════ PRIVACY ══════════════ */
#priv-list{margin-top:2.4rem;border:1px solid var(--border);border-radius:20px;overflow:hidden}
.priv{display:grid;grid-template-columns:1fr auto;gap:.4rem 1.5rem;padding:1.35rem 1.6rem;
  background:var(--surface);border-bottom:1px solid var(--border);align-items:center}
.priv:last-child{border-bottom:0}
.priv b{font-weight:500;font-size:.98rem}
.priv p{margin:0;color:var(--muted);font-size:.88rem;grid-column:1}
.pill{font-family:var(--mono);font-size:.66rem;letter-spacing:.1em;text-transform:uppercase;
  border-radius:999px;padding:.3rem .75rem;white-space:nowrap;grid-row:span 2;justify-self:end}
.pill.off{color:var(--aurum);border:1px solid color-mix(in srgb,var(--aurum) 40%,transparent);background:color-mix(in srgb,var(--aurum) 9%,transparent)}
.pill.opt{color:var(--ink);border:1px solid color-mix(in srgb,var(--ink) 40%,transparent);background:color-mix(in srgb,var(--ink) 9%,transparent)}
@media(max-width:560px){.priv{grid-template-columns:1fr}.pill{grid-row:auto;justify-self:start;margin-top:.4rem}}

/* ══════════════ THEMES (made yours) ══════════════ */
#theme-cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(15rem,1fr));gap:1.2rem;margin-top:2.6rem}
.tcard{border-radius:20px;overflow:hidden;border:1px solid var(--border);cursor:pointer;
  background:var(--card);text-align:left;padding:0;font-family:var(--sans);box-shadow:var(--edge);
  transition:transform .3s var(--ease),border-color .3s,box-shadow .3s}
.tcard:hover{transform:translateY(-4px)}
.tcard:active{transform:translateY(-2px) scale(.99);transition-duration:.08s}
.tcard.on{border-color:var(--gold);box-shadow:var(--edge),0 0 0 1px var(--gold),0 18px 50px -24px var(--gold-glow)}
.tprev{height:8.6rem;position:relative;padding:1rem}
.tprev .mini{position:absolute;inset:1rem;border-radius:12px;padding:.8rem .9rem;display:flex;flex-direction:column;gap:.45rem}
.tprev .row{height:.55rem;border-radius:4px;width:70%}
.tprev .row.s{width:45%}
.tmeta{padding:1rem 1.2rem 1.1rem;border-top:1px solid var(--border)}
.tmeta b{font-weight:500;font-size:.95rem;color:var(--text);display:block}
.tmeta span{font-size:.8rem;color:var(--faint)}

/* ══════════════ UPDATES FEED (updates.html) ══════════════ */
#feed{margin-top:2.6rem;position:relative;padding-left:2rem}
#feed::before{content:"";position:absolute;left:.5rem;top:.6rem;bottom:.6rem;width:1px;
  background:linear-gradient(var(--gold-border),transparent 96%)}
.post{position:relative;margin-bottom:3rem}
.post::before{content:"";position:absolute;left:-1.86rem;top:.55rem;width:9px;height:9px;border-radius:50%;
  background:var(--gold);box-shadow:0 0 0 4px var(--gold-glow)}
.post .date{font-family:var(--mono);font-size:.7rem;letter-spacing:.22em;text-transform:uppercase;color:var(--gold);margin-bottom:.35rem}
.post .rt{color:var(--faint);margin-left:.5rem;letter-spacing:.14em;text-transform:none}
.post h3{font-size:1.75rem;margin-bottom:.7rem}
.post .body{color:var(--muted);max-width:46rem;font-size:.98rem}
.post .body strong{color:var(--text);font-weight:500}
.post .body em{font-family:var(--serif);font-style:italic;font-size:1.06em}
.post .body code{font-family:var(--mono);font-size:.85em;background:var(--input);border:1px solid var(--border);border-radius:6px;padding:.1em .4em}
.post .body ul{margin:.4rem 0 1em;padding-left:1.2rem}
.post .body li{margin:.3rem 0}
.post .body img{border-radius:16px;border:1px solid var(--border);margin:.8rem 0;max-width:26rem}
.post .body a{color:var(--gold);border-bottom:1px solid var(--gold-border)}
.m-tint{font-weight:400}
.post .anch{
  background:none;border:0;cursor:pointer;color:var(--faint);
  font-family:var(--serif);font-size:.95rem;padding:0 .3rem;margin-left:.35rem;
  opacity:0;transition:opacity .25s,color .2s;vertical-align:baseline;
}
.post:hover .anch,.post .anch:focus-visible{opacity:1}
.post .anch:hover{color:var(--gold)}
@media(pointer:coarse){.post .anch{opacity:.6}}
.post:target{scroll-margin-top:5rem}
.post:target::before{box-shadow:0 0 0 5px var(--gold-glow),0 0 18px var(--gold)}

/* ══════════════ DOWNLOAD ══════════════ */
#dl-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(13rem,1fr));gap:1rem;margin-top:2.6rem}
.dl{background:var(--card);border:1px solid var(--border);border-radius:18px;padding:1.6rem 1.4rem;
  display:flex;flex-direction:column;gap:.2rem;box-shadow:var(--edge);
  transition:transform .3s var(--ease),border-color .3s}
.dl:hover{transform:translateY(-4px);border-color:var(--gold-border)}
.dl b{font-weight:500;font-size:1rem}
.dl span{color:var(--faint);font-size:.82rem}
.dl .soon{margin-top:1rem;font-family:var(--mono);font-size:.68rem;letter-spacing:.12em;
  text-transform:uppercase;color:var(--faint)}
.dl a.get{margin-top:1rem;color:var(--gold);font-size:.88rem}
.dl a.get:hover{text-decoration:underline}

/* ══════════════ FOOTER ══════════════
   The last thing a visitor sees decides whether the site was finished or
   abandoned. A hairline that fades at its ends instead of a hard border,
   the tagline given room, links in three columns that answer hover with
   the same settle as everything above. */
footer{position:relative;border-top:0;margin-top:7rem;padding:3.6rem 0 3.8rem}
footer::before{content:"";position:absolute;top:0;left:50%;translate:-50% 0;
  width:min(74rem,100% - 3rem);height:1px;
  background:linear-gradient(90deg,transparent,var(--gold-border) 20%,var(--gold-border) 80%,transparent)}
footer .bar{align-items:flex-start;gap:2rem;flex-wrap:wrap}
footer .logo{width:34px;height:34px}
.f-tag{font-family:var(--serif);font-style:italic;color:var(--muted);font-size:1.2rem;margin-top:.6rem}
.f-small{color:var(--faint);font-size:.78rem;margin-top:.4rem}
/* Grouped columns: each with a quiet mono header, so eleven links read
   as a table of contents instead of a pile. */
footer nav.f-cols{display:flex;gap:3.4rem;flex-wrap:wrap;font-size:.85rem;color:var(--muted)}
.f-col{display:flex;flex-direction:column;gap:.5rem;min-width:8.5rem}
.f-h{font-family:var(--mono);font-weight:400;font-size:.62rem;letter-spacing:.28em;
  text-transform:uppercase;color:var(--faint);margin-bottom:.35rem}
footer nav a{transition:color .2s var(--ease),translate .25s var(--ease)}
footer nav a:hover{color:var(--gold);translate:2px 0}
@media(max-width:560px){footer nav.f-cols{gap:2.2rem}}

/* ══════════════ PALETTE (press / or the ⌘ button) ══════════════ */
#pal{position:fixed;inset:0;z-index:85;display:none;place-items:start center;padding-top:14vh;
  background:color-mix(in srgb,var(--bg) 70%,transparent);backdrop-filter:blur(10px)}
#pal.on{display:grid}
.pal-box{width:min(26rem,92vw);background:var(--surface);border:1px solid var(--border);
  border-radius:16px;overflow:hidden;box-shadow:var(--shadow)}
#pal-q{width:100%;border:0;background:var(--input);color:var(--text);padding:.95rem 1.2rem;
  font-family:var(--sans);font-size:.95rem;outline:none;border-bottom:1px solid var(--border)}
#pal-list{max-height:19rem;overflow-y:auto;padding:.4rem}
.pal-h{font-family:var(--mono);font-size:.62rem;letter-spacing:.28em;text-transform:uppercase;
  color:var(--faint);padding:.65rem .8rem .3rem}
.pal-h:not(:first-child){border-top:1px solid var(--border);margin-top:.35rem}
.pal-it{display:flex;justify-content:space-between;align-items:center;width:100%;text-align:left;
  background:none;border:0;color:var(--muted);font-family:var(--sans);font-size:.9rem;
  padding:.6rem .8rem;border-radius:9px;cursor:pointer}
.pal-it:hover,.pal-it.sel{background:var(--hover);color:var(--text)}
.pal-it .k{font-family:var(--mono);font-size:.66rem;color:var(--faint)}

/* ══════════════ CROSS-PAGE TRANSITIONS ══════════════
   One rule: same-origin navigations crossfade in browsers that support
   cross-document view transitions. Everyone else just navigates. */
@view-transition{navigation:auto}

/* The theme switch bleeds — see setTheme() in site.js for the why. While
   html.theming is on, every element cross-fades its colours; setTheme sets
   each one's transition-delay from its position down the screen, so the fade
   front travels top → bottom. The veil is the OLD ground colour, one fixed
   layer behind the content, 220% tall with a soft gradient top; it slides
   down and out on a transform (composited, no repaint), carrying the page
   background down with the front. Elements off screen switch instantly; nobody
   sees them. Same-page view transitions are deliberately not used here. */
html.theme-snap,html.theme-snap *{transition:none !important}
html.theming body *{
  transition-property:background-color,color,border-color !important;
  transition-duration:.42s !important;
  transition-timing-function:cubic-bezier(.4,0,.2,1) !important;
}
#theme-veil{
  /* Starts with its feathered top edge AT the top of the viewport (the header
     changes at delay 0 anyway) and slides fully out below. Timed so the solid
     edge passes each row a beat before that row's text finishes fading: the
     ground must never be behind the ink, or light text sits on light for a
     frame (seen 2026-09-05 when the veil started 120vh up). */
  position:fixed;left:0;right:0;top:0;height:118vh;z-index:-1;pointer-events:none;
  background:linear-gradient(to bottom,transparent 0,var(--veil) 16vh,var(--veil) 100%);
  transform:translateY(0);will-change:transform;
  transition:transform var(--travel,.85s) cubic-bezier(.35,.1,.35,1);
}
#theme-veil.go{transform:translateY(118vh)}

/* ══════════════ OPTIONAL GATE (config.js → gate.enabled) ══════════════
   The inline script in each page's <head> adds .gated before first paint,
   so the content never flashes. site.js builds #gate and removes .gated
   on unlock. */
.gated body > :not(#gate){visibility:hidden}
#gate{
  position:fixed;inset:0;z-index:100;display:grid;place-items:center;
  background:var(--bg);
  background-image:radial-gradient(58% 42% at 50% 40%,var(--gold-glow),transparent 70%);
  padding:1.5rem;visibility:visible;
  transition:opacity .8s var(--ease),transform .8s var(--ease);
}
#gate.open{opacity:0;transform:scale(1.05);pointer-events:none}
.gate-in{width:min(21rem,100%);text-align:center}
.gate-logo{width:78px;height:78px;margin:0 auto 1.5rem;animation:breathe 5.5s ease-in-out infinite}
@keyframes breathe{
  0%,100%{box-shadow:0 0 0 1px var(--gold-border),0 10px 44px -14px var(--gold-glow)}
  50%{box-shadow:0 0 0 1px var(--gold-border),0 16px 70px -10px var(--gold-glow),0 0 40px var(--gold-glow)}
}
.gate-word{font-family:var(--serif);font-size:2.3rem;color:var(--gold);letter-spacing:.01em;margin-bottom:.2rem}
.gate-sub{color:var(--faint);font-size:.85rem;margin-bottom:2.3rem}
.field{position:relative}
#pw{
  width:100%;padding:.95rem 3.1rem .95rem 1.1rem;
  background:var(--input);border:1px solid var(--border);border-radius:12px;
  color:var(--text);font-family:var(--mono);font-size:1rem;
  letter-spacing:.28em;text-align:center;
  transition:border-color .25s var(--ease);
}
#pw::placeholder{letter-spacing:.05em;font-family:var(--sans);color:var(--faint)}
#pw:focus{outline:none;border-color:var(--gold-deep)}
#go{
  position:absolute;right:.4rem;top:50%;transform:translateY(-50%);
  width:2.25rem;height:2.25rem;border:0;border-radius:9px;cursor:pointer;
  background:var(--gold);color:var(--on-gold);font-size:1rem;line-height:1;
  transition:transform .2s var(--ease),filter .2s var(--ease);
}
#go:hover{transform:translateY(-50%) scale(1.08);filter:brightness(1.07)}
.gate-msg{min-height:1.4rem;margin-top:1rem;font-size:.83rem;color:#E07A5F}
.shake{animation:shake .4s}
@keyframes shake{10%,90%{transform:translateX(-2px)}20%,80%{transform:translateX(4px)}30%,50%,70%{transform:translateX(-7px)}40%,60%{transform:translateX(7px)}}

/* ══════════════ PLATFORM ICONS ON DOWNLOAD CARDS ══════════════ */
.dl .plat{width:26px;height:26px;color:var(--faint);margin-bottom:.6rem;transition:color .3s}
.dl:hover .plat{color:var(--gold)}
.dl .plat svg{width:100%;height:100%;fill:currentColor}
.dl a.get svg{width:14px;height:14px;fill:currentColor;vertical-align:-2px;margin-right:.4rem}

/* ══════════════ PROSE PAGES (guide, privacy, why) ══════════════ */
.prose{max-width:46rem;color:var(--muted);font-size:1rem}
.prose h3{font-family:var(--serif);font-size:1.7rem;color:var(--text);margin:2.6rem 0 .8rem}
.prose h4{font-family:var(--sans);font-weight:500;font-size:1rem;color:var(--text);margin:1.8rem 0 .4rem}
.prose strong{color:var(--text);font-weight:500}
.prose em{font-family:var(--serif);font-style:italic;font-size:1.06em}
.prose ul{padding-left:1.2rem;margin:.4rem 0 1.2em}
.prose li{margin:.35rem 0}
.prose li::marker{color:var(--gold)}
.prose .pill{grid-row:auto;margin-left:.6rem;vertical-align:2px}
.prose-date{font-family:var(--mono);font-size:.7rem;letter-spacing:.22em;text-transform:uppercase;color:var(--faint);margin-bottom:2rem}
.prose blockquote{
  margin:1.6rem 0;padding:1rem 1.4rem;border-left:2px solid var(--gold);
  background:var(--surface);border-radius:0 14px 14px 0;
  font-family:var(--serif);font-style:italic;font-size:1.12rem;color:var(--text);
}

/* FAQ — native details/summary, styled quiet */
.faq{margin-top:2.4rem;border:1px solid var(--border);border-radius:20px;overflow:hidden;box-shadow:var(--edge)}
.faq details{border-bottom:1px solid var(--border);background:var(--surface)}
.faq details:last-child{border-bottom:0}
.faq summary{
  cursor:pointer;list-style:none;display:flex;justify-content:space-between;align-items:center;
  gap:1rem;padding:1.15rem 1.5rem;font-weight:400;font-size:.98rem;color:var(--text);
  transition:background .2s;
}
.faq summary::-webkit-details-marker{display:none}
.faq summary:hover{background:var(--hover)}
.faq summary::after{content:"+";font-family:var(--serif);font-size:1.3rem;color:var(--gold);flex:none;transition:rotate .35s var(--settle)}
.faq details[open] summary::after{rotate:45deg}
.faq .a{padding:0 1.5rem 1.25rem;color:var(--muted);font-size:.93rem;max-width:40rem}
/* Answers ease open on the same curve as everything else, where the
   browser supports animating details. Elsewhere they open instantly,
   which is exactly what they did before. */
@supports (interpolate-size:allow-keywords){
  :root{interpolate-size:allow-keywords}
  .faq details::details-content{height:0;overflow:clip;
    transition:height .35s var(--settle),content-visibility .35s allow-discrete}
  .faq details[open]::details-content{height:auto}
}

/* Contact card */
.contact-card{
  margin-top:2.4rem;border:1px solid var(--gold-border);border-radius:20px;
  background:linear-gradient(120deg,var(--gold-glow),transparent 55%),var(--surface);
  padding:1.7rem 1.9rem;max-width:34rem;box-shadow:var(--edge);
}
.contact-card b{font-weight:500;display:block;margin-bottom:.3rem}
.contact-card p{color:var(--muted);font-size:.92rem;margin:0 0 .8rem}
.contact-card a.mail{font-family:var(--mono);font-size:.95rem;color:var(--gold);border-bottom:1px solid var(--gold-border)}

/* ══════════════ INSTALL BAR ══════════════ */
#install-bar{
  position:fixed;left:50%;bottom:1.6rem;translate:-50% 120%;z-index:94;
  display:flex;align-items:center;gap:.8rem;max-width:min(30rem,92vw);
  background:var(--surface);border:1px solid var(--gold-border);border-radius:16px;
  padding:.8rem 1.1rem;box-shadow:var(--shadow);font-size:.86rem;color:var(--muted);
  transition:translate .6s var(--ease-out);
}
#install-bar.on{translate:-50% 0}
#install-bar .logo{width:26px;height:26px}
#install-bar button{
  border:0;border-radius:999px;padding:.42rem .95rem;cursor:pointer;
  font-family:var(--sans);font-size:.8rem;white-space:nowrap;
}
#install-bar .ib-go{background:var(--gold);color:var(--on-gold)}
#install-bar .ib-no{background:transparent;color:var(--faint)}
#install-bar .ib-no:hover{color:var(--text)}

/* ══════════════ TOAST (easter eggs, demo notes) ══════════════ */
#toast{
  position:fixed;left:50%;bottom:2.2rem;translate:-50% 0;z-index:95;
  font-family:var(--mono);font-size:.78rem;letter-spacing:.06em;color:var(--gold);
  background:var(--surface);border:1px solid var(--gold-border);border-radius:999px;
  padding:.6rem 1.2rem;box-shadow:var(--shadow);white-space:nowrap;
  opacity:0;pointer-events:none;transition:opacity .5s var(--ease),translate .5s var(--ease);
}
#toast.on{opacity:1;translate:-50% -6px}

/* ══════════════ WIKI (wiki.html) ══════════════
   A filing cabinet, not a walkthrough: a sticky index on the left, the
   articles on the right, and a search box that filters both. On a phone the
   index becomes a wrapped row of pills above the articles — same element,
   same links, no second markup. Every colour here is an existing token;
   module articles borrow their accent the same way .mod cards do (--c). */
#wiki-wrap{display:grid;grid-template-columns:15rem minmax(0,1fr);gap:3.2rem;margin-top:2.6rem;align-items:start}
#wiki-side{position:sticky;top:4.4rem;display:flex;flex-direction:column;gap:1rem}
#wiki-q{width:100%;background:var(--input);border:1px solid var(--border);border-radius:12px;
  color:var(--text);font-family:var(--sans);font-size:.9rem;font-weight:300;
  padding:.65rem .95rem;outline:none;transition:border-color .25s var(--ease)}
#wiki-q::placeholder{color:var(--faint)}
#wiki-q:focus{border-color:var(--gold-deep)}
#wiki-q::-webkit-search-cancel-button{-webkit-appearance:none}
#wiki-toc{display:flex;flex-direction:column;gap:.1rem;
  max-height:calc(100svh - 13rem);overflow-y:auto;scrollbar-width:thin}
#wiki-toc .wt-h{font-family:var(--mono);font-size:.62rem;letter-spacing:.28em;
  text-transform:uppercase;color:var(--faint);padding:.95rem 0 .4rem}
#wiki-toc a{--c:var(--gold);font-size:.88rem;color:var(--muted);padding:.26rem .65rem;
  border-left:2px solid transparent;border-radius:0 8px 8px 0;
  transition:color .2s,border-color .2s,background .2s}
#wiki-toc a:hover{color:var(--text);background:var(--hover)}
#wiki-toc a.here{color:var(--c);border-left-color:var(--c)}
@media(max-width:900px){
  #wiki-wrap{grid-template-columns:1fr;gap:1.6rem}
  #wiki-side{position:static}
  #wiki-toc{flex-direction:row;flex-wrap:wrap;gap:.45rem;max-height:none;overflow:visible}
  #wiki-toc .wt-h{display:none}
  #wiki-toc a{border:1px solid var(--border);border-radius:999px;padding:.3rem .8rem;font-size:.8rem}
  #wiki-toc a.here{border-color:var(--c)}
}
.wiki-art{--c:var(--gold);border-top:1px solid var(--border);padding:2.5rem 0 1.2rem;
  scroll-margin-top:4.6rem}
.wiki-art:first-child{border-top:0;padding-top:.3rem}
.wa-head{display:flex;align-items:flex-start;gap:1rem;margin-bottom:1rem}
.wa-head .glyph{width:34px;height:34px;color:var(--c);flex:none;margin-top:.15rem}
.wa-head .glyph svg{width:100%;height:100%;stroke:currentColor;fill:none;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round}
.wa-head h3{font-family:var(--sans);font-weight:500;font-size:1.25rem;color:var(--c);letter-spacing:.02em;line-height:1.3}
.wa-head .wa-plain{font-family:var(--mono);font-size:.62rem;letter-spacing:.14em;
  text-transform:uppercase;color:var(--faint);margin-left:.7rem;vertical-align:2px}
.wa-head .whisper{color:var(--muted);font-size:1.02rem}
/* the § mirrors the one on updates entries: quiet until hovered, always
   faintly present on touch screens where there is no hover */
.wiki-art .anch{background:none;border:0;cursor:pointer;color:var(--faint);
  font-family:var(--serif);font-size:1rem;padding:0 .3rem;margin-left:auto;
  opacity:0;transition:opacity .25s,color .2s}
.wiki-art:hover .anch,.wiki-art .anch:focus-visible{opacity:1}
.wiki-art .anch:hover{color:var(--gold)}
@media(pointer:coarse){.wiki-art .anch{opacity:.6}}
.wiki-art:target .wa-head .glyph{filter:drop-shadow(0 0 12px var(--gold-glow))}
.wa-body{color:var(--muted);max-width:44rem;font-size:.97rem}
.wa-body strong{color:var(--text);font-weight:500}
.wa-body em{font-family:var(--serif);font-style:italic;font-size:1.06em}
.wa-body code{font-family:var(--mono);font-size:.85em;background:var(--input);border:1px solid var(--border);border-radius:6px;padding:.1em .4em}
.wa-body h4{font-family:var(--sans);font-weight:500;font-size:.95rem;color:var(--text);margin:1.5rem 0 .4rem}
.wa-body ul{margin:.4rem 0 1em;padding-left:1.2rem}
.wa-body li{margin:.32rem 0}
.wa-body li::marker{color:var(--c)}
.wa-body a{color:var(--gold);border-bottom:1px solid var(--gold-border)}
#wiki-empty{display:none;color:var(--faint);font-family:var(--serif);font-style:italic;font-size:1.15rem;padding:2.4rem 0}

/* ══════════════ SKIP LINK ══════════════
   Invisible until it is the one thing a keyboard user needs. */
.skip{position:fixed;top:.6rem;left:.6rem;z-index:120;translate:0 -300%;
  background:var(--gold);color:var(--on-gold);border-radius:999px;
  padding:.55rem 1.1rem;font-size:.85rem;transition:translate .25s var(--ease)}
.skip:focus-visible{translate:0 0}

/* ══════════════ WIKI SEARCH MARKS ══════════════ */
mark.wk{background:var(--gold-glow);color:inherit;border-radius:3px;
  padding:.02em .12em;box-shadow:inset 0 -1px 0 var(--gold-border)}

/* the wiki borrows the prose blockquote for its > lines */
.wa-body blockquote{
  margin:1.4rem 0;padding:.9rem 1.3rem;border-left:2px solid var(--gold);
  background:var(--surface);border-radius:0 14px 14px 0;
  font-family:var(--serif);font-style:italic;font-size:1.12rem;color:var(--text);
}

/* small mono tool links (print the manual, and friends) */
.tool-link{font-family:var(--mono);font-size:.72rem;letter-spacing:.12em;
  text-transform:uppercase;color:var(--faint);border-bottom:1px solid var(--border);
  transition:color .2s,border-color .2s;cursor:pointer}
.tool-link:hover{color:var(--gold);border-color:var(--gold-border)}

/* ══════════════ THE KEYS (?) ══════════════ */
#keys{position:fixed;inset:0;z-index:86;display:grid;place-items:center;
  background:color-mix(in srgb,var(--bg) 70%,transparent);backdrop-filter:blur(10px)}
.keys-box{width:min(19rem,92vw);background:var(--surface);border:1px solid var(--border);
  border-radius:16px;box-shadow:var(--shadow);padding:.6rem .9rem 1rem}
.key-row{display:flex;justify-content:space-between;align-items:center;
  padding:.45rem .3rem;font-size:.88rem;color:var(--muted)}
.key-row kbd{font-family:var(--mono);font-size:.75rem;color:var(--gold);
  border:1px solid var(--border);border-bottom-width:2px;border-radius:7px;
  padding:.15rem .55rem;background:var(--input)}
.key-note{padding:.6rem .3rem 0;font-size:.76rem;color:var(--faint);
  border-top:1px solid var(--border);margin-top:.4rem}

/* ══════════════ THE TWO WORLDS (wiki.html) ══════════════
   Reading a game's wiki entry, the page dresses itself in that game's
   look — site.js toggles these classes from the scrollspy. Each world is
   its token set overridden on body (so every descendant follows) PLUS a
   layer of component immersion: acre becomes the old-school MMO's
   website — dark timber ground, parchment article panels with ink-brown
   text, carved yellow headings on banner strips; yield becomes the
   clean fintech site — a white page, navy 700-weight sans headlines,
   turquoise accents, soft-shadow cards. The melt is the long
   transitions at the bottom of this section. Scroll past, and the
   house's own theme settles back on its own. */

/* ── acre: the tokens — timber dark, with the classic yellow ── */
body.world-acre{
  --bg:#221A11; --surface:#2E2418; --card:#3A2C1B; --hover:#443320;
  --input:#171209; --border:rgba(126,95,53,.55); --border-strong:#6B5233;
  --text:#F2E3C8; --muted:#C9AB7C; --faint:#98805C;
  --accent:#B8874A;
  --gold:#FFB83F; --gold-light:#FFD98A; --gold-deep:#C77E1E;
  --gold-glow:rgba(255,184,63,.16); --gold-border:rgba(255,184,63,.35);
  --on-gold:#221A11; --grain:.12;
  --shadow:0 24px 70px -28px rgba(0,0,0,.85);
  --edge:inset 0 1px 0 rgba(255,255,255,.05);
}
/* ── yield: the tokens — SoFi.com, measured from the site itself:
   the big flat teal band (#00A2C7 exactly), white type on it, white
   cards carrying near-black #212121, dark indigo #201747 for headings,
   #00819D for links on white. The page BECOMES the teal band; the
   articles are the white sections. ── */
body.world-yield{
  --bg:#00A2C7; --surface:#0FB0D3; --card:#FFFFFF; --hover:#17B6D8;
  --input:#FFFFFF; --border:rgba(255,255,255,.35); --border-strong:rgba(255,255,255,.6);
  --text:#FFFFFF; --muted:#D9F4FA; --faint:rgba(255,255,255,.66);
  --accent:#201747;
  --gold:#FFFFFF; --gold-light:#CFF2FA; --gold-deep:#E5FAFF;
  --gold-glow:rgba(255,255,255,.18); --gold-border:rgba(255,255,255,.45);
  --on-gold:#00A2C7; --grain:0;
  --shadow:0 24px 60px -30px rgba(0,60,80,.4);
  --edge:0 1px 2px rgba(0,60,80,.08);
}

/* ── the world's own ground: a fixed layer that fades in behind the
   content. Glows and vignettes only — the solid colour underneath is
   body's own --bg, so the crossfade stays one smooth motion. ── */
#world-decor{position:fixed;inset:0;z-index:-1;pointer-events:none;
  opacity:0;transition:opacity .9s var(--settle)}
body.world-acre #world-decor{opacity:1;background:
  radial-gradient(110% 70% at 50% 0%,rgba(255,184,63,.06),transparent 60%),
  radial-gradient(140% 110% at 50% 115%,rgba(0,0,0,.55),transparent 55%)}
body.world-yield #world-decor{opacity:1;background:
  radial-gradient(75% 55% at 70% -10%,rgba(255,255,255,.22),transparent 60%),
  radial-gradient(80% 55% at 15% 112%,rgba(0,80,105,.35),transparent 60%)}

/* ── metric-neutral scaffolding ─────────────────────────────────────
   THE ONE RULE OF THE MELT: it may repaint anything and move NOTHING.
   The first version gave the panels real padding and borders, and every
   article changed height — which moved the acre entry relative to the
   scrollspy's trigger line, which toggled the melt, which moved it
   back: the scroll fought itself trying to stay aligned. So the panels
   and banner strips are absolutely-positioned layers painted BEHIND the
   text (::before, z-index:-1, opacity 0 until a world raises them), and
   every font swap keeps the base font-size so line boxes hold their
   height. If a future world needs padding, it cannot have it. */
.wiki-art{position:relative}
.wiki-art::before{content:"";position:absolute;inset:1rem -1.5rem -.1rem;
  z-index:-1;opacity:0;pointer-events:none;border:2px solid transparent;
  background-color:transparent;
  transition:opacity .7s var(--settle),background-color .7s var(--settle),
    border-color .7s var(--settle),box-shadow .7s var(--settle)}
.wa-head{position:relative}
.wa-head::before{content:"";position:absolute;inset:-.6rem -1.5rem -.5rem;
  z-index:-1;opacity:0;pointer-events:none;
  border-bottom:2px solid transparent;background-color:transparent;
  transition:opacity .7s var(--settle),background-color .7s var(--settle),
    border-color .7s var(--settle)}

/* ── acre: the parchment sheets ─────────────────────────────────────
   Ink-brown text and maroon links, re-scoped through the same tokens so
   every rule inside just follows; the sheet and its timber banner are
   painted layers, so the page never reflows. */
body.world-acre #wiki-list .wiki-art{
  --text:#2E2212; --muted:#4E3A20; --faint:#6E552F;
  --gold:#7A2E12; --gold-border:rgba(122,46,18,.45);
  --gold-glow:rgba(122,46,18,.1);
  --input:#E0CD9C; --border:rgba(185,156,100,.7); --border-strong:#9A7F4C;
  border-top-color:transparent;
}
body.world-acre #wiki-list .wiki-art::before{
  opacity:1;background-color:#EAD9AC;
  border-color:#5C4326;border-radius:6px;
  box-shadow:inset 0 0 46px rgba(122,90,40,.4),0 12px 34px -16px rgba(0,0,0,.75);
}
body.world-acre .wa-head::before{
  opacity:1;background-color:#2A1F12;
  border-bottom-color:#5C4326;border-radius:4px 4px 0 0;
}
body.world-acre .wa-head h3{
  font-family:'MedievalSharp',var(--serif);font-weight:400;
  letter-spacing:.04em;
  color:#FFB83F;text-shadow:2px 2px 0 rgba(0,0,0,.9);
}
body.world-acre .wa-head .whisper{color:#C9AB7C}
body.world-acre .wa-head .anch{color:#98805C}
body.world-acre .wa-body h4{
  font-family:'MedievalSharp',var(--serif);font-weight:400;
  color:#5C2E0E;letter-spacing:.03em;
}
body.world-acre .wa-body li::marker{content:'◆  ';color:#7A2E12;font-size:.8em}
body.world-acre .wa-body blockquote{
  background:rgba(122,46,18,.08);border-left-color:#7A2E12;color:#2E2212}
body.world-acre .wa-body code{background:#E0CD9C;border-color:#B99C64;color:#5C2E0E}
/* outline, not border — a border would grow the image by 2px and shift
   everything below it */
body.world-acre .wa-body img{outline:2px solid #5C4326;outline-offset:-1px;border-radius:4px}
/* the search well goes carved: bevel faked with inset shadows so the
   border width (and the sidebar's height) never changes */
body.world-acre #wiki-q{
  background:#E4D2A2;color:#2E2212;border-color:#8A6B3F;border-radius:4px;
  box-shadow:inset 2px 2px 0 rgba(60,40,15,.45),inset -2px -2px 0 rgba(255,240,200,.5)}
body.world-acre #wiki-q::placeholder{color:#8A6F42}

/* ── yield: the SoFi page ───────────────────────────────────────────
   The page is the teal band; the articles are its white sections. Type
   on teal is white; inside a card it re-scopes to the measured palette:
   near-black body, indigo headings, teal links. */
body.world-yield #wiki-list .wiki-art{
  --text:#212121; --muted:#4F5459; --faint:#8A9095;
  --gold:#00819D; --gold-border:rgba(0,129,157,.4);
  --gold-glow:rgba(0,162,199,.1);
  --input:#F4F7F8; --border:#E4E9EC; --border-strong:#CDD6DA;
  border-top-color:transparent;
}
body.world-yield #wiki-list .wiki-art::before{
  opacity:1;background-color:#FFFFFF;
  border-color:transparent;border-radius:18px;
  box-shadow:0 22px 50px -24px rgba(0,60,80,.5);
}
body.world-yield .wa-head h3{
  font-family:var(--sans);font-weight:700;letter-spacing:-.02em;
  color:#201747;
}
body.world-yield .wa-head .whisper{
  font-family:var(--sans);font-style:normal;color:#4F5459}
body.world-yield .wa-body h4{
  font-family:var(--sans);font-weight:700;letter-spacing:-.01em;color:#201747}
body.world-yield .wa-body li::marker{content:'✓  ';color:#00A2C7;font-weight:700}
body.world-yield .wa-body a{color:#00819D;border-bottom-color:rgba(0,162,199,.4)}
body.world-yield .wa-body blockquote{
  font-family:var(--sans);font-style:normal;
  background:#EAF7FA;border-left-color:#00A2C7;border-radius:12px;color:#201747}
body.world-yield .wa-body img{border-color:#E4E9EC;border-radius:16px}
body.world-yield #wiki-q{border-radius:999px;background:#FFFFFF;color:#212121;
  border-color:#FFFFFF;box-shadow:0 8px 22px -10px rgba(0,60,80,.4)}
body.world-yield #wiki-q::placeholder{color:#8A9095}
/* colour only up here — a font swap on the page headline could re-wrap
   it and shift every article below, which is the bug the scaffolding
   above exists to prevent */
body.world-yield .eyebrow{color:#201747}
/* the TOC's current entry reads as their indigo pill */
body.world-yield #wiki-toc a.here{color:#FFFFFF;border-left-color:#201747}

/* ── the melt itself: the ground crossfades slowly; the type follows.
   The long transition lives on the world classes, so entering a world
   eases in — and on body's base rule below, so leaving eases out. ── */
body{transition:background .9s var(--settle),color .9s var(--settle)}
body.world-acre,body.world-yield{
  transition:background .9s var(--settle),color .9s var(--settle)}
.wiki-art,.wa-body,.wa-body blockquote,.wa-body code,.wa-head,.wa-head h3,
.wa-head .whisper,#wiki-q,.eyebrow,h2,.lede{
  transition:color .7s var(--settle),background-color .7s var(--settle),
    border-color .7s var(--settle),box-shadow .7s var(--settle)}
/* the header's glass is mixed from --bg, so it melts on the same clock */
#hdr{transition:border-color .3s var(--ease),background-color .9s var(--settle)}
@media(prefers-reduced-motion:reduce){
  body,.wiki-art,.wa-body,#world-decor{transition:none}
}

/* screenshots inside wiki entries — phone captures, kept modest */
.wa-body img{border-radius:18px;border:1px solid var(--border);
  margin:1.2rem 0;max-width:min(19rem,100%);
  box-shadow:var(--edge),var(--shadow)}

/* ══════════════ PRINT — the paper manual ══════════════
   Printed, the wiki becomes a typeset handbook of the whole app, and any
   page drops its chrome. Ink on paper regardless of theme. */
@media print{
  #hdr,#rail-nav,footer,#thread,#pal,#keys,#toast,#install-bar,
  #wiki-side,.anch,.skip,.mods-hint,#stars{display:none !important}
  body{background:#fff;color:#111}
  body::after{display:none}
  main{width:100%}
  .r{opacity:1 !important;transform:none !important}
  .lede,.wa-head .whisper{color:#444}
  .wa-body,.wa-body h4,.prose,.prose h3{color:#111}
  .wiki-art{border-top:1px solid #ccc;break-inside:avoid-page}
  /* screen accents are tuned for the theme behind them; paper gets ink */
  .wa-head h3,h1,h2,h3{color:#111}
  .wa-body a{color:#111;border-bottom:1px dotted #999}
  #wiki-wrap{display:block;margin-top:1rem}
  mark.wk{background:none;box-shadow:none}
}

/* ══════════════ SCROLL CHOREOGRAPHY ══════════════
   Driven by --hp (hero progress, 0→1), set once per frame by site.js's
   rAF-throttled scroll handler. Transform and opacity only — nothing here
   repaints, honouring the performance notes at the top of this file. The
   whole block sits behind prefers-reduced-motion:no-preference; without
   it the hero simply scrolls, as it always did. This section is LAST in
   the file on purpose: it modulates elements styled above, and several of
   its selectors win their ties by order. */
@media(prefers-reduced-motion:no-preference){
  /* the hero recedes rather than scrolls away: text drifts up at a
     fraction of scroll speed and dims; the phone eases back in depth;
     the aurora quietens */
  #hero>div:not(#aurora):not(.h-visual){
    transform:translateY(calc(var(--hp,0)*30px));
    opacity:calc(1 - var(--hp,0)*.45)}
  .h-visual.in{
    transform:translateY(calc(var(--hp,0)*48px)) scale(calc(1 - var(--hp,0)*.06));
    opacity:calc(1 - var(--hp,0)*.55);
    /* transform must follow the scroll frame-for-frame — only opacity
       keeps a transition, or the phone would swim after the wheel */
    transition:opacity .45s var(--settle)}
  #aurora{opacity:calc(1 - var(--hp,0)*.85)}

  /* section eyebrows settle their tracking as they arrive — wide when
     unseen, closing to spec as the section takes the stage */
  .eyebrow.r{letter-spacing:.45em}
  .eyebrow.r.in{letter-spacing:.32em;
    transition:opacity .6s var(--settle),transform .6s var(--settle),
      letter-spacing 1s var(--settle)}
  /* the headline lands a breath after its eyebrow */
  h2.r.in{transition-delay:70ms}
}
