/* ============================================================
   MIDNIGHT & GOLD — shared design system
   ============================================================ */

/* ---------- tokens ---------- */
:root{
  --bg:        #060608;
  --bg-soft:   #0c0c12;
  --panel:     rgba(255,255,255,.038);
  --panel-brd: rgba(233,213,160,.14);

  --gold-1:    #f6e7bd;
  --gold-2:    #dcb862;
  --gold-3:    #a3803a;
  --gold-glow: rgba(220,184,98,.30);

  --text:      #ece7dd;
  --muted:     #948c7d;
  --faint:     #5d574d;

  --serif: "Cormorant Garamond", "Iowan Old Style", "Apple Garamond", Garamond,
           "Times New Roman", Georgia, serif;
  --sans:  "Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto,
           "Helvetica Neue", Arial, sans-serif;

  --ease-out:  cubic-bezier(.16,1,.3,1);
  --ease-io:   cubic-bezier(.65,0,.35,1);
  --ease-soft: cubic-bezier(.4,0,.2,1);

  --pad: clamp(20px, 5vw, 64px);
  --maxw: 1180px;
}

/* ---------- reset ---------- */
*,*::before,*::after{ box-sizing:border-box }
html,body{ height:100% }
html{
  background:var(--bg);
  -webkit-text-size-adjust:100%;
  scroll-behavior:smooth;
}
body{
  margin:0;
  background:var(--bg);
  color:var(--text);
  font-family:var(--sans);
  font-size:16px;
  line-height:1.65;
  overflow-x:hidden;
  -webkit-font-smoothing:antialiased;
  -moz-osx-font-smoothing:grayscale;
  text-rendering:optimizeLegibility;
}
img,svg,canvas{ display:block; max-width:100% }
button,input{ font:inherit; color:inherit }
a{ color:inherit; text-decoration:none }
::selection{ background:var(--gold-2); color:#0a0a0c }

/* ---------- ambient background ---------- */
.aura{
  position:fixed; inset:0; z-index:0; pointer-events:none;
  contain:strict; transform:translateZ(0);
  background:
    radial-gradient(900px 620px at 18% -8%,  rgba(163,128,58,.20), transparent 62%),
    radial-gradient(820px 560px at 88% 8%,   rgba(120,96,180,.13), transparent 60%),
    radial-gradient(1000px 800px at 50% 118%,rgba(220,184,98,.11), transparent 62%),
    linear-gradient(180deg, #060608 0%, #0a0a10 48%, #050507 100%);
}
/* Film grain — STATIC.
   This used to be a 4x-viewport layer with mix-blend-mode:overlay animated on
   a steps() timer. That forced a full-screen blended repaint several times a
   second and was the single biggest source of jank. Held still and composited
   normally it costs one cheap paint, and looks identical at 3% opacity. */
.aura::after{
  content:""; position:absolute; inset:0;
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/></filter><rect width='160' height='160' filter='url(%23n)' opacity='.5'/></svg>");
  opacity:.028;
}
#dust{ position:fixed; inset:0; z-index:1; pointer-events:none;
       contain:strict; will-change:transform; transform:translateZ(0) }

/* ---------- preloader ---------- */
#loader{
  position:fixed; inset:0; z-index:9999;
  background:var(--bg);
  display:grid; place-items:center;
  transition:opacity .7s var(--ease-soft), visibility .7s;
}
#loader.done{ opacity:0; visibility:hidden }
.loader-in{ text-align:center; transform:translateY(0) }
.loader-mark{
  width:54px; height:54px; margin:0 auto 26px; position:relative;
}
.loader-mark span{
  position:absolute; inset:0; border-radius:50%;
  border:1px solid transparent; border-top-color:var(--gold-2);
  animation:spin 1.15s var(--ease-io) infinite;
}
.loader-mark span:nth-child(2){ inset:9px;  border-top-color:var(--gold-3); animation-duration:1.6s; animation-direction:reverse }
.loader-mark span:nth-child(3){ inset:18px; border-top-color:var(--gold-1); animation-duration:.9s }
@keyframes spin{ to{ transform:rotate(360deg) } }
.loader-bar{
  width:170px; height:1px; margin:0 auto; background:rgba(255,255,255,.10);
  position:relative; overflow:hidden;
}
.loader-bar i{
  position:absolute; inset:0 100% 0 0;
  background:linear-gradient(90deg,var(--gold-3),var(--gold-1));
  transition:right .35s var(--ease-out);
}
.loader-txt{
  margin-top:18px; font-size:10px; letter-spacing:.42em; text-transform:uppercase;
  color:var(--faint);
}

/* ---------- page shell ---------- */
.page{
  position:relative; z-index:2;
  min-height:100vh; min-height:100dvh;
  display:flex; flex-direction:column;
  padding:var(--pad);
}
.page-main{
  flex:1; display:flex; flex-direction:column;
  align-items:center; justify-content:center;
  text-align:center; gap:clamp(12px,2.1vh,26px);
  width:100%; max-width:var(--maxw); margin:0 auto;
  padding:18px 0;
}
/* chapters that carry a large interactive centrepiece breathe tighter
   so the Continue button always stays above the fold */
body[data-compact] .page-main{ gap:clamp(9px,1.5vh,18px) }
body[data-compact] .lede{ font-size:clamp(.9rem,1.75vh,1.04rem); line-height:1.62 }
body[data-compact] .chapter-no{ font-size:clamp(2.1rem,6.4vh,3.6rem) }
body[data-compact] h2.title{ font-size:clamp(1.55rem,4.1vh,2.6rem) }

/* ---------- top bar ---------- */
.topbar{
  display:flex; align-items:center; justify-content:space-between;
  gap:16px; width:100%; max-width:var(--maxw); margin:0 auto;
  font-size:10px; letter-spacing:.34em; text-transform:uppercase; color:var(--faint);
}
.mark{ display:flex; align-items:center; gap:10px; color:var(--muted) }
.mark i{
  width:7px; height:7px; border-radius:50%; display:block;
  background:var(--gold-2); box-shadow:0 0 12px var(--gold-glow);
  animation:beat 2.6s var(--ease-io) infinite;
}
@keyframes beat{ 0%,100%{transform:scale(1);opacity:.85} 50%{transform:scale(1.5);opacity:1} }

/* ---------- progress rail ---------- */
.rail{ display:flex; align-items:center; gap:9px }
.rail b{
  width:22px; height:2px; border-radius:2px; display:block;
  background:rgba(255,255,255,.14); position:relative; overflow:hidden;
  transition:background .5s var(--ease-soft);
}
.rail b::after{
  content:""; position:absolute; inset:0; transform:scaleX(0); transform-origin:left;
  background:linear-gradient(90deg,var(--gold-3),var(--gold-1));
  transition:transform .8s var(--ease-out);
}
.rail b.on::after{ transform:scaleX(1) }
.rail b.now{ box-shadow:0 0 10px var(--gold-glow) }

/* ---------- typography ---------- */
.eyebrow{
  margin:0; font-size:11px; letter-spacing:.46em; text-transform:uppercase;
  color:var(--gold-2); opacity:.9;
}
.chapter-no{
  font-family:var(--serif); font-size:clamp(3.4rem,12vw,7rem);
  line-height:.85; color:transparent; margin:0;
  -webkit-text-stroke:1px rgba(220,184,98,.30);
  letter-spacing:-.02em;
}
/* Gold gradient headline.
   IMPORTANT: this used to animate `background-position` on a
   background-clip:text element. That re-rasterises every glyph on every
   frame and measured a ~38fps loss on its own. The gradient is now static
   and the moving highlight is a separate GPU-composited overlay that only
   ever translates — same look, effectively free. */
h1.display{
  position:relative; display:inline-block;
  font-family:var(--serif); font-weight:300;
  font-size:clamp(2.5rem,8.2vw,6.4rem);
  line-height:1.02; letter-spacing:-.025em; margin:0;
  background:linear-gradient(96deg,var(--gold-1) 0%,#fffaf0 26%,var(--gold-2) 54%,var(--gold-1) 82%,var(--gold-3) 100%);
  -webkit-background-clip:text; background-clip:text; color:transparent;
}
h2.title{
  font-family:var(--serif); font-weight:300;
  font-size:clamp(1.9rem,5.4vw,3.5rem);
  line-height:1.1; letter-spacing:-.015em; margin:0; color:#fbf6ea;
}
.lede{
  margin:0; max-width:56ch; color:var(--muted);
  font-size:clamp(.98rem,2.1vw,1.14rem); line-height:1.78;
}
.serif-quote{
  font-family:var(--serif); font-style:italic; font-weight:300;
  font-size:clamp(1.25rem,3.4vw,2rem); line-height:1.5;
  color:#e9dfc8; max-width:24ch; margin:0 auto;
}
.rule{
  width:64px; height:1px; margin:2px auto; border:0;
  background:linear-gradient(90deg,transparent,var(--gold-2),transparent);
}

/* ---------- buttons ---------- */
.btn{
  --bh:54px;
  position:relative; display:inline-flex; align-items:center; justify-content:center;
  gap:11px; height:var(--bh); padding:0 34px; border:0; border-radius:999px;
  font-size:13.5px; font-weight:600; letter-spacing:.11em; text-transform:uppercase;
  cursor:pointer; overflow:hidden; isolation:isolate;
  color:#100d06; background:linear-gradient(135deg,var(--gold-1),var(--gold-2) 55%,var(--gold-3));
  box-shadow:0 12px 34px rgba(180,140,60,.24), inset 0 1px 0 rgba(255,255,255,.45);
  transition:transform .4s var(--ease-out), box-shadow .4s var(--ease-out), opacity .3s;
  -webkit-tap-highlight-color:transparent;
}
.btn::after{ /* shine sweep */
  content:""; position:absolute; top:0; bottom:0; width:42%; left:-60%;
  background:linear-gradient(100deg,transparent,rgba(255,255,255,.62),transparent);
  transform:skewX(-18deg); transition:left .75s var(--ease-out);
}
.btn:hover{ transform:translateY(-3px); box-shadow:0 20px 44px rgba(190,150,70,.34), inset 0 1px 0 rgba(255,255,255,.5) }
.btn:hover::after{ left:120% }
.btn:active{ transform:translateY(-1px) scale(.985) }
.btn[disabled]{ opacity:.34; pointer-events:none; filter:grayscale(.5) }

.btn.ghost{
  color:var(--text); background:transparent;
  border:1px solid rgba(233,213,160,.26); box-shadow:none;
}
.btn.ghost:hover{
  background:rgba(233,213,160,.07); border-color:rgba(233,213,160,.5);
  box-shadow:0 10px 30px rgba(0,0,0,.3);
}
.btn.ghost::after{ display:none }

.btn-row{ display:flex; gap:14px; flex-wrap:wrap; justify-content:center; align-items:center }

/* text link with animated underline */
.tlink{
  position:relative; font-size:11px; letter-spacing:.3em; text-transform:uppercase;
  color:var(--muted); padding-bottom:4px; cursor:pointer; background:none; border:0;
}
.tlink::after{
  content:""; position:absolute; left:0; right:0; bottom:0; height:1px;
  background:var(--gold-2); transform:scaleX(0); transform-origin:right;
  transition:transform .5s var(--ease-out);
}
.tlink:hover{ color:var(--gold-1) }
.tlink:hover::after{ transform:scaleX(1); transform-origin:left }

/* ---------- field ---------- */
.field{ position:relative; width:min(400px,86vw) }
.field input{
  width:100%; height:60px; padding:0 22px; text-align:center;
  background:rgba(255,255,255,.045); border:1px solid rgba(233,213,160,.18);
  border-radius:14px; outline:none; letter-spacing:.03em; font-size:17px;
  transition:border-color .4s var(--ease-soft), background .4s, box-shadow .4s;
}
.field input::placeholder{ color:var(--faint); letter-spacing:.14em; font-size:14px; text-transform:uppercase }
.field input:focus{
  border-color:rgba(233,213,160,.6); background:rgba(255,255,255,.07);
  box-shadow:0 0 0 4px rgba(220,184,98,.10), 0 16px 40px rgba(0,0,0,.4);
}

/* ---------- glass panel ---------- */
/* Glass panel.
   backdrop-filter forces the compositor to re-blur everything behind the
   element on every frame — with drifting dust and confetti behind it, that
   never stops. It measured as the last big cost after the text sheen
   (41 -> 60fps). Over this near-black background a translucent fill plus a
   soft inner highlight is visually indistinguishable and costs nothing. */
.panel{
  background:linear-gradient(180deg,rgba(255,255,255,.055),rgba(255,255,255,.028));
  border:1px solid var(--panel-brd); border-radius:20px;
  box-shadow:0 28px 70px rgba(0,0,0,.45), inset 0 1px 0 rgba(255,255,255,.07);
}

/* Opt in to true glass only where the device can clearly afford it and the
   user has not asked for reduced motion. */
@media (min-width:1100px) and (prefers-reduced-motion:no-preference){
  .field input{
    backdrop-filter:blur(6px); -webkit-backdrop-filter:blur(6px);
  }
}

/* ---------- entrance animation system ---------- */
[data-anim]{ opacity:0; will-change:transform,opacity }
[data-anim="up"]    { transform:translate3d(0,34px,0) }
[data-anim="down"]  { transform:translate3d(0,-26px,0) }
[data-anim="fade"]  { transform:none }
[data-anim="scale"] { transform:scale(.93) }
[data-anim="left"]  { transform:translate3d(-34px,0,0) }
[data-anim="blur"]  { transform:translate3d(0,22px,0); filter:blur(9px) }
[data-anim].in{
  opacity:1; transform:none; filter:none;
  transition:opacity .95s var(--ease-out), transform 1.05s var(--ease-out), filter .95s var(--ease-out);
}
/* release the compositor layer once the entrance has played */
[data-anim].settled{ will-change:auto }

/* word-by-word reveal.
   `overflow:clip` masks the slide-up but is removed once the word has
   landed, so descenders (g, y, p) and the text gradient stay intact. */
.word{ display:inline-block; overflow:clip; vertical-align:top; padding-bottom:.06em }
@supports not (overflow:clip){ .word{ overflow:hidden } }
.words-settled .word{ overflow:visible }
.word > i{
  display:inline-block; font-style:inherit;
  transform:translateY(105%); opacity:0;
  transition:transform 1s var(--ease-out), opacity .8s var(--ease-out);
}
/* NOTE: must settle on `none`, not `translateY(0)`. Any transform value
   creates a stacking context, which detaches the glyphs from the parent's
   -webkit-background-clip:text gradient and renders them invisible. */
.reveal-on .word > i{ transform:none; opacity:1 }

/* ---------- floating scroll/next hint ---------- */
.hint{
  display:flex; flex-direction:column; align-items:center; gap:9px;
  font-size:9.5px; letter-spacing:.36em; text-transform:uppercase; color:var(--faint);
}
.hint i{
  width:1px; height:34px; display:block; position:relative; overflow:hidden;
  background:rgba(255,255,255,.12);
}
.hint i::after{
  content:""; position:absolute; left:0; width:1px; height:14px;
  background:var(--gold-2); animation:trickle 2.2s var(--ease-io) infinite;
}
@keyframes trickle{ 0%{top:-16px} 100%{top:36px} }

/* ---------- page transition curtain (fallback) ---------- */
#curtain{
  position:fixed; inset:0; z-index:9998; pointer-events:none;
  background:var(--bg); opacity:0;
  transition:opacity .42s var(--ease-soft);
}
#curtain.up{ opacity:1 }

/* native cross-document view transitions */
@view-transition{ navigation:auto }
::view-transition-old(root){ animation:vtOut .42s var(--ease-soft) both }
::view-transition-new(root){ animation:vtIn  .62s var(--ease-out)  both }
@keyframes vtOut{ to{ opacity:0; transform:scale(.985) translateY(-10px); filter:blur(5px) } }
@keyframes vtIn { from{ opacity:0; transform:scale(1.012) translateY(14px); filter:blur(7px) } }
::view-transition-group(aura){ animation-duration:.6s }

/* first paint of each document */
.boot .page-main{ opacity:0 }

/* ---------- footer ---------- */
.footnote{
  width:100%; max-width:var(--maxw); margin:0 auto;
  display:flex; align-items:center; justify-content:space-between; gap:16px;
  font-size:9.5px; letter-spacing:.3em; text-transform:uppercase; color:var(--faint);
}

/* ---------- utilities ---------- */
.stack{ display:flex; flex-direction:column; align-items:center; gap:inherit }
.hidden{ display:none !important }
.nowrap{ white-space:nowrap }
.gold{ color:var(--gold-1) }

@media (max-width:640px){
  .footnote span:last-child{ display:none }
  .topbar{ font-size:9px; letter-spacing:.26em }
}

/* ---------- low-power tier ----------
   Set by the quality governor in app.js when the device measures slow.
   Drops the ambient extras that cost paint but add least to the feel. */
[data-perf="low"] .aura::after{ display:none }          /* film grain */
[data-perf="low"] .card-note::before{ display:none }     /* card sheen  */
[data-perf="low"] .mark i{ animation:none }
[data-perf="low"] .seal .pulse{ animation:none }
[data-perf="low"] .stat::before{ display:none }
[data-perf="low"] .panel{ box-shadow:0 18px 40px rgba(0,0,0,.45) }

/* ---------- accessibility ---------- */
:focus-visible{ outline:2px solid var(--gold-2); outline-offset:3px; border-radius:8px }

@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{
    animation-duration:.001ms !important; animation-iteration-count:1 !important;
    transition-duration:.001ms !important; scroll-behavior:auto !important;
  }
  [data-anim]{ opacity:1 !important; transform:none !important; filter:none !important }
  .word > i{ transform:none !important; opacity:1 !important }
  .boot .page-main{ opacity:1 }
}
