/* ══════════════════════════════════════════════════════════════════════════
   ASTRAL — MOTION LAYER / VISUAL REDESIGN
   ══════════════════════════════════════════════════════════════════════════

   This file loads AFTER assets/css/style.css and only ever adds to it (or
   overrides one rule where the redesign genuinely replaces it). Nothing in
   style.css was deleted: every class the page controllers in app.js and
   mobile-pages.js spit out still resolves, because those templates are
   markup we don't control from here.

   Three sources feed this layer:

   1. Lenis          — smooth scroll. `html.lenis` rules at the bottom of the
                       nav section are the vendor's own required styles.
   2. GSAP           — scroll-driven reveals, parallax, page transitions and
                       the split-text hero. Owned by assets/js/fx.js.
   3. React Bits     — the effects are *ports*, not the React package: Aurora,
                       Particles, ClickSpark, SpotlightCard, Magnet, CountUp,
                       Marquee, TiltedCard and DecryptedText, re-implemented
                       for vanilla DOM because this site has no React and no
                       bundler. Same ideas, zero framework.

   Rules for future edits:
   - Never set a transform on `.view` and leave it there — that turns the view
     into a containing block and every `position:fixed` child (the top light,
     the backdrop scrims) starts scrolling with the page instead. fx.js
     clears its transforms; keep it that way.
   - Anything animated here has a partner under `prefers-reduced-motion` (the
     end of this file) and under `html.no-motion`, which Settings → This
     device sets.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── 1. tokens ─────────────────────────────────────────────────────────── */

:root{
  /* The palette is unchanged — still gold on black. What changed is depth:
     the greys now carry a trace of the gold so surfaces read as one material
     instead of neutral grey boxes sitting on a warm page. */
  --bg:#040403;
  --bg-2:#0a0908;
  --bg-3:#12100c;
  --bg-4:#1d1913;
  --border:#2b2419;
  --border-soft:#1a1611;

  --gold:#d4af37;
  --gold-bright:#f6e3a6;
  --gold-deep:#a0741c;

  /* Radii grow with the size of the thing they wrap. */
  --r:12px;
  --r-lg:18px;
  --r-xl:26px;

  --shadow:0 26px 60px -34px rgb(0 0 0 / 90%);

  /* Two easings, both GSAP-friendly cubic-beziers: `--ease-out` for anything
     that enters (fast out, long settle), `--ease-spring` for anything the
     pointer touches. */
  --ease-out:cubic-bezier(.16,1,.3,1);
  --ease-spring:cubic-bezier(.34,1.56,.64,1);
  --transition:.24s var(--ease-out);
  --dur:500ms;
  --dur-slow:900ms;

  --spot-x:50%;
  --spot-y:50%;
}

/* ── 2. page canvas & typography ───────────────────────────────────────── */

html{
  /* Lenis needs the document to be the scroll container and nothing else to
     fight it. scroll-behavior:smooth would double up with Lenis' easing. */
  scroll-behavior:auto;
  background:#040403;
}

body{
  background:
    radial-gradient(120% 80% at 50% -12%,rgb(212 175 55 / 10%) 0%,transparent 55%),
    radial-gradient(90% 60% at 100% 8%,rgb(212 175 55 / 5%) 0%,transparent 60%),
    linear-gradient(180deg,#050504 0%,#040403 40%,#030302 100%);
  background-attachment:fixed;
  font-feature-settings:"ss01","cv11";
  letter-spacing:-.005em;
}

/* A fluid display scale. Every heading on the site is a variance of these, so
   the type grows on a 4K monitor instead of staying phone-sized. */
h1,h2,h3,h4{letter-spacing:-.03em;}

::selection{background:rgb(212 175 55 / 32%);color:#fff8e2;}

/* Focus ring consistent with the gold system. */
:where(a,button,input,select,textarea,[tabindex]):focus-visible{
  outline:2px solid var(--gold);
  outline-offset:2px;
  border-radius:6px;
}

/* Scrollbar picks up the gold on hover instead of staying flat grey. */
::-webkit-scrollbar{width:10px;}
::-webkit-scrollbar-track{background:#080706;}
::-webkit-scrollbar-thumb{
  background:linear-gradient(180deg,#3a3226,#221d16);
  border-radius:99px;border:2px solid #080706;
}
::-webkit-scrollbar-thumb:hover{background:linear-gradient(180deg,#6d5a2c,#3a3226);}

/* ── 3. global fixed effects ───────────────────────────────────────────── */

/* Reaction to the redesign: a 2px gold thread across the top of the screen
   that reports scroll progress. It is the cheapest possible "this page is
   alive" signal, and it never reflows. */
.scroll-progress{
  position:fixed;inset:0 0 auto 0;height:2px;z-index:260;
  pointer-events:none;background:transparent;
}
.scroll-progress i{
  display:block;height:100%;width:100%;
  transform:scaleX(0);
  background:linear-gradient(90deg,rgb(212 175 55 / 0%),var(--gold) 40%,var(--gold-bright));
  box-shadow:0 0 14px rgb(212 175 55 / 65%);
  transform-origin:0 50%;
  /* No will-change: `width` is not a compositable property, so hinting it only
     promised the compiler something it could not deliver. The bar scales, it
     never resizes. */
}

/* Film grain. One inline SVG turbulence tile, tiled by the browser, at 3.5%
   — enough to keep large black areas from banding on an OLED, invisible
   otherwise. `mix-blend-mode:overlay` is what stops it reading as a grey
   veil over photographs. */
.fx-grain{
  position:fixed;inset:0;z-index:250;pointer-events:none;
  opacity:.045;mix-blend-mode:overlay;
  background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.82' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size:140px 140px;
}

/* ClickSpark + cursor trail canvases. Fixed, full-viewport, never
   interactive — they sit above everything except toasts. */
.fx-spark{
  position:fixed;inset:0;width:100%;height:100%;z-index:255;
  pointer-events:none;
}

/* A two-part cursor: a hard gold dot that tracks exactly, and a ring that
   lags behind it. The lag is what makes it feel like weight. Hidden entirely
   on touch devices and when motion is off (pointer:fine gate). */
.fx-cursor{display:none;position:fixed;inset:0;z-index:258;pointer-events:none;}
@media (hover:hover) and (pointer:fine){
  /* Gated on fx-ready, which only fx.js sets once it has booted: without it a
     failed module would leave a gold dot parked in the top-left corner of the
     screen with nothing driving it. */
  html.fx-ready .fx-cursor{display:block;}
}
.fx-cursor-dot,.fx-cursor-ring{
  position:absolute;top:0;left:0;border-radius:50%;
  will-change:transform;
}
.fx-cursor-dot{
  width:5px;height:5px;margin:-2.5px 0 0 -2.5px;
  background:var(--gold-bright);
  box-shadow:0 0 10px rgb(246 227 166 / 80%);
}
.fx-cursor-ring{
  width:34px;height:34px;margin:-17px 0 0 -17px;
  border:1px solid rgb(212 175 55 / 55%);
  background:radial-gradient(circle,rgb(212 175 55 / 10%),transparent 70%);
  transition:width .3s var(--ease-out),height .3s var(--ease-out),
             margin .3s var(--ease-out),border-color .3s,opacity .3s;
}
/* `.is-over` is set by fx.js when the pointer is on something clickable: the
   ring swells and softens, so the page answers before you press anything. */
.fx-cursor.is-over .fx-cursor-ring{
  width:56px;height:56px;margin:-28px 0 0 -28px;
  border-color:rgb(246 227 166 / 85%);
  background:radial-gradient(circle,rgb(212 175 55 / 16%),transparent 70%);
}
.fx-cursor.is-down .fx-cursor-ring{
  width:24px;height:24px;margin:-12px 0 0 -12px;
  border-color:var(--gold-bright);
}
/* The native cursor is deliberately NOT hidden. This is an accompanying
   highlight, not a replacement — nobody can end up with no pointer at all
   because a script failed. (`.is-over` gives the "this is clickable"
   feedback that a hidden cursor would normally carry.) */
.fx-cursor{opacity:1;}

/* ── 4. Lenis (vendor-required) ────────────────────────────────────────── */

html.lenis,html.lenis body{height:auto;}
.lenis.lenis-smooth{scroll-behavior:auto!important;}
.lenis.lenis-smooth [data-lenis-prevent]{overscroll-behavior:contain;}
.lenis.lenis-stopped{overflow:hidden;}
/* Lenis is what stops the page scrolling behind an open drawer, panel or
   modal. Without it - lite mode, or a vendor file that never loaded - nothing
   did, and the page scrolled under the dialog. fx.js sets this class in that
   case (see lockScroll). */
html.scroll-locked,html.scroll-locked body{overflow:hidden;}
.lenis.lenis-smooth iframe{pointer-events:none;}

/* ── 5. nav ────────────────────────────────────────────────────────────── */

.nav-shell{
  padding:16px 20px 0;
  transition:transform .55s var(--ease-out),padding .4s var(--ease-out);
}
/* Set while scrolling down; the nav returns the moment you scroll up. */
.nav-shell.is-hidden{transform:translateY(-135%);}
.nav-shell.is-stuck{padding-top:10px;}

.nav-bar{
  max-width:560px;
  padding:5px;
  border:1px solid rgb(212 175 55 / 16%);
  /* Was blur(24px) saturate(1.4). This bar is fixed over a page that moves
     under it, so the blur had to be recomputed every frame of every scroll -
     the single most expensive thing in the design, and at this opacity the
     blur was almost invisible anyway. Solid surface, same silhouette. */
  background:rgb(8 7 6 / 93%);
  box-shadow:
    0 18px 44px -26px rgb(0 0 0 / 95%),
    inset 0 1px 0 rgb(255 255 255 / 7%),
    0 0 0 1px rgb(0 0 0 / 40%);
  transition:box-shadow .5s var(--ease-out),background .5s var(--ease-out),border-color .5s;
}
.nav-shell.is-stuck .nav-bar{
  background:rgb(8 7 6 / 96%);
  border-color:rgb(212 175 55 / 26%);
  box-shadow:
    0 22px 50px -24px rgb(0 0 0 / 100%),
    inset 0 1px 0 rgb(255 255 255 / 9%),
    0 0 34px -8px rgb(212 175 55 / 22%);
}

/* The sliding pill behind the active route. Its x/width are written by
   fx.js — CSS only owns how it looks. Without JS it simply never appears,
   and `.nav-slot.is-active` below is still legible on its own. */
.nav-indicator{
  position:absolute;top:5px;bottom:5px;left:0;width:0;
  border-radius:999px;
  background:linear-gradient(135deg,rgb(212 175 55 / 26%),rgb(212 175 55 / 9%));
  border:1px solid rgb(212 175 55 / 42%);
  box-shadow:
    0 0 22px -2px rgb(212 175 55 / 30%),
    inset 0 1px 0 rgb(255 255 255 / 14%);
  opacity:0;
  pointer-events:none;
  transition:opacity .35s var(--ease-out);
  z-index:0;
}
.nav-indicator.is-on{opacity:1;}

.nav-slot{
  position:relative;z-index:1;
  padding:9px 12px;
  font-size:12.5px;
  letter-spacing:.01em;
  border-radius:999px;
  transition:color .3s var(--ease-out);
}
.nav-slot:hover{color:var(--text);}
/* The indicator replaces the solid chip that used to sit here, so the active
   slot keeps only its colour. */
.nav-slot.is-active{
  background:none;
  box-shadow:none;
  color:var(--gold-bright);
  text-shadow:0 0 18px rgb(212 175 55 / 45%);
}

.icon-btn,.avatar-btn{
  background:rgb(8 7 6 / 92%);
  border-color:rgb(212 175 55 / 14%);
  transition:transform .4s var(--ease-spring),border-color .3s,color .3s,box-shadow .4s;
}
.icon-btn:hover,.avatar-btn:hover{
  transform:translateY(-2px);
  border-color:rgb(212 175 55 / 55%);
  box-shadow:0 8px 24px -14px rgb(212 175 55 / 80%);
}
.brand-mark{position:relative;}
.brand-mark::after{
  content:"";position:absolute;inset:-8px;border-radius:50%;
  background:radial-gradient(circle,rgb(212 175 55 / 30%),transparent 68%);
  opacity:0;transition:opacity .4s var(--ease-out);
}
.brand:hover .brand-mark::after{opacity:1;}
.brand-mark .icon{transition:transform .8s var(--ease-out);}
.brand:hover .brand-mark .icon{transform:rotate(90deg);}

/* Drawer + notification panel: opaque, deeper shadow, springier open.
   These two are the only surfaces that sit directly over a page, and the page
   behind them is the hero - a photograph with 46px white type on it. At 96%
   the menu let that headline ghost through the panel and the list read as if
   it were printed on top of the page. Glass was the old answer here; the blur
   is gone (see §15), so the fill has to do the work on its own. No alpha. */
.drawer,.panel{
  background:linear-gradient(170deg,#14110c,#060605 88%);
  border-color:rgb(212 175 55 / 18%);
  box-shadow:0 30px 70px -30px #000,inset 0 1px 0 rgb(255 255 255 / 7%);
}
.drawer{transition:opacity .24s var(--ease-out),transform .4s var(--ease-spring);}
.panel{transition:opacity .24s var(--ease-out),transform .4s var(--ease-spring);}
.drawer-link{position:relative;overflow:hidden;}
.drawer-link::before{
  content:"";position:absolute;left:0;top:50%;transform:translateY(-50%);
  width:2px;height:0;border-radius:2px;background:var(--gold);
  transition:height .35s var(--ease-out);
}
.drawer-link:hover{color:var(--text-subtle);}
.drawer-link.is-active::before{height:56%;}
.drawer-link.is-active{color:var(--gold-bright);}

/* ── 6. hero ───────────────────────────────────────────────────────────── */

/* The hero is no longer a photograph with a scrim: it is four stacked layers,
   each moving at its own rate (fx.js drives 2 and 3 with ScrollTrigger):

     0  .hero-art      the photograph, oversized 12% so parallax never
                       reveals an edge, slowly drifting (ken burns)
     1  .hero-canvas   Aurora + drifting dust, the React Bits port
     2  .hero-veil     the black ramp that keeps the copy readable
     3  .hero-inner    the copy
     —  .hero-scroll   the cue, pinned to the bottom

   Negative z-indices are deliberately avoided: `.hero` is `isolation:isolate`,
   and a negative-z child inside an isolated context is exactly the bug that
   once made the season art lay out as a flex sibling. Every layer here is
   positioned with an explicit non-negative z-index instead. */
#view-home .hero.hero-fx{
  position:relative;
  width:100vw;
  margin-left:calc(50% - 50vw);
  margin-top:-64px;
  min-height:min(940px,100svh);
  padding:164px 20px 104px;
  display:flex;align-items:center;justify-content:center;text-align:center;
  overflow:hidden;
  isolation:isolate;
  background-color:#040403;
  background-image:none;
}

/* The old `.hero::before` (a dotted grid under a black gradient) is retired:
   its job is now done by .hero-grid and .hero-veil, which can move
   independently. Content:none is what actually removes it — a hero-specific
   `#view-home .hero::before` rule in style.css still sets a background. */
#view-home .hero.hero-fx::before{content:none;}

/* A fine ruled grid that only shows through the upper third — structure
   without texture. */
.hero-fx .hero-grid{
  position:absolute;inset:0;z-index:0;pointer-events:none;
  background-image:
    linear-gradient(90deg,rgb(212 175 55 / 7%) 1px,transparent 1px),
    linear-gradient(180deg,rgb(212 175 55 / 7%) 1px,transparent 1px);
  background-size:88px 88px;
  mask-image:radial-gradient(ellipse 70% 62% at 50% 34%,#000 0%,transparent 78%);
  -webkit-mask-image:radial-gradient(ellipse 70% 62% at 50% 34%,#000 0%,transparent 78%);
  opacity:.6;
}

.hero-fx .hero-art{
  position:absolute;inset:-12%;z-index:0;pointer-events:none;
  background:url('/assets/img/hero.jpg') center 34%/cover no-repeat;
  filter:saturate(.78) contrast(1.08) brightness(.7);
  /* The 34s `heroDrift` loop used to live here. It kept a full-screen,
     filtered layer animating forever, on top of (and overriding) the
     scroll parallax fx.js already applies to this same element - a CSS
     animation beats an inline transform, so the drift was the only thing
     that ever ran. Static photo + the GSAP parallax is the same depth for
     a fraction of the cost. */
  transform:scale(1.04);
}

.hero-fx .hero-canvas{
  position:absolute;inset:0;z-index:1;width:100%;height:100%;
  pointer-events:none;
  opacity:.9;
  mix-blend-mode:screen;
}

/* The veil does three jobs: darken the top so the floating nav stays
   readable over the photo, darken the bottom so the page below joins without
   a seam, and keep a pool of contrast in the middle for the headline. */
.hero-fx .hero-veil{
  position:absolute;inset:0;z-index:2;pointer-events:none;
  background:
    radial-gradient(ellipse 58% 44% at 50% 46%,rgb(4 4 3 / 76%) 0%,rgb(4 4 3 / 22%) 68%,transparent 100%),
    linear-gradient(180deg,rgb(4 4 3 / 90%) 0%,rgb(4 4 3 / 34%) 26%,rgb(4 4 3 / 40%) 58%,rgb(4 4 3 / 96%) 100%);
}

/* Pointer-tracked spotlight. `--hx/--hy` are written by fx.js on a rAF
   throttle; the gradient itself is pure CSS so it costs one composited layer
   and nothing else. It only fades in once a real pointer has moved (the
   `.is-pointer` class), so a touch device never sees a stray blob. */
.hero-fx .hero-spot{
  position:absolute;inset:0;z-index:2;pointer-events:none;
  background:radial-gradient(
    460px circle at var(--hx,50%) var(--hy,42%),
    rgb(255 232 170 / 16%) 0%,
    rgb(255 232 170 / 6%) 34%,
    transparent 70%
  );
  mix-blend-mode:screen;
  opacity:0;
  transition:opacity .7s var(--ease-out);
}
.hero-fx.is-pointer .hero-spot{opacity:1;}

.hero-fx::after{
  content:"";position:absolute;inset:0;z-index:2;pointer-events:none;
  background:radial-gradient(ellipse 88% 70% at 50% 50%,transparent 52%,rgb(0 0 0 / 62%) 100%);
}

#view-home .hero .hero-inner{
  position:relative;z-index:3;
  width:min(100%,820px);max-width:820px;
  padding:0;
}

.hero-kicker{
  display:inline-flex;align-items:center;gap:10px;
  margin-bottom:22px;
  color:var(--gold);
  font-size:11px;font-weight:800;
  letter-spacing:.22em;text-transform:uppercase;
}
.hero-kicker::before{
  content:"";width:34px;height:1px;
  background:linear-gradient(90deg,transparent,var(--gold));
}
.hero-kicker::after{
  content:"";width:34px;height:1px;
  background:linear-gradient(90deg,var(--gold),transparent);
}

/* Split headline. Each `.ht-line` is the mask, each `.ht-word` is the thing
   that moves — clipping per line is what makes the words rise out of the
   type rather than fade in on top of it. */
#view-home .hero .hero-title{
  display:block;
  max-width:820px;
  margin:0 auto 20px;
  font-size:clamp(40px,7.4vw,88px);
  line-height:.94;
  letter-spacing:-.05em;
  font-weight:800;
  text-wrap:balance;
}
/* The shadow goes on the plain words only. text-shadow paints *after* an
   element's own background, and these glyphs ARE a background (clipped to the
   text) — so a shadow on the .grad word lands on top of the gold and turns it
   muddy brown. */
.hero-title .ht-word:not(.grad){
  text-shadow:0 2px 30px rgb(0 0 0 / 55%),0 1px 3px rgb(0 0 0 / 35%);
}
.hero-title .ht-line{
  display:block;
  overflow:hidden;
  /* Room for descenders inside the mask. fx.js drops the overflow again when
     the intro finishes, so this never clips a resting headline. */
  padding-bottom:.14em;
  margin-bottom:-.12em;
}
.hero-title .ht-word{
  display:inline-block;
}
#view-home .hero .hero-title .grad{
  /* Every stop stays bright: the animation sweeps a highlight across the
     word, it doesn't fade it down and up. An earlier version ended on #a97e1f
     and the "gradient" spent half its cycle looking muddy brown. */
  background:linear-gradient(100deg,#e6c264 0%,#f6dfa0 32%,#fffbe8 50%,#f6dfa0 68%,#e6c264 100%);
  background-size:230% 100%;
  -webkit-background-clip:text;background-clip:text;color:transparent;
  filter:drop-shadow(0 8px 30px rgb(212 175 55 / 30%));
  animation:shine 12s ease-in-out infinite;
}
@keyframes shine{
  0%,100%{background-position:0% 50%;}
  50%{background-position:100% 50%;}
}

#view-home .hero p{
  max-width:520px;
  margin-bottom:26px;
  color:rgb(245 243 238 / 76%);
  font-size:15.5px;line-height:1.65;
}
#view-home .hero p code{
  padding:1px 6px;border-radius:6px;
  background:rgb(212 175 55 / 12%);
  border:1px solid rgb(212 175 55 / 22%);
  color:var(--gold-bright);font-size:.92em;
}
#view-home .hero p strong{color:var(--text);font-weight:700;}

/* ── platform switch: a real sliding thumb ─────────────────────────────── */
.platform-switch{
  position:relative;
  padding:4px;
  border:1px solid rgb(212 175 55 / 18%);
  background:rgb(6 6 5 / 90%);
  box-shadow:inset 0 1px 0 rgb(255 255 255 / 6%);
  margin-bottom:18px;
}
.platform-thumb{
  position:absolute;top:4px;bottom:4px;left:0;width:0;
  border-radius:999px;
  background:linear-gradient(140deg,var(--gold-bright),var(--gold) 45%,var(--gold-deep));
  box-shadow:
    0 6px 20px -8px rgb(212 175 55 / 80%),
    inset 0 1px 0 rgb(255 255 255 / 55%);
  opacity:0;
  transition:transform .5s var(--ease-spring),width .5s var(--ease-spring),opacity .3s;
  pointer-events:none;
}
.platform-thumb.is-on{opacity:1;}
.platform-tab{
  position:relative;z-index:1;
  padding:8px 22px;
  font-size:12.5px;font-weight:800;letter-spacing:.02em;
  color:rgb(245 243 238 / 58%);
}
.platform-tab:hover{color:var(--text);}
.platform-tab.is-active{
  background:none;
  color:#150f02;
  text-shadow:0 1px 0 rgb(255 255 255 / 22%);
}

/* ── hero actions ─────────────────────────────────────────────────────── */
.hero-actions{gap:12px;}

#view-home .hero-trust{
  gap:20px;margin-top:24px;
  color:rgb(245 243 238 / 58%);
  font-size:11.5px;font-weight:600;letter-spacing:.01em;
}
#view-home .hero-trust span{
  gap:8px;
  transition:color .3s;
}
#view-home .hero-trust span:hover{color:var(--text);}
.trust-dot{
  width:5px;height:5px;
  box-shadow:0 0 0 3px rgb(212 175 55 / 12%),0 0 12px rgb(212 175 55 / 60%);
  animation:trustPulse 3.2s ease-in-out infinite;
}
.hero-trust span:nth-child(2) .trust-dot{animation-delay:.7s;}
.hero-trust span:nth-child(3) .trust-dot{animation-delay:1.4s;}
@keyframes trustPulse{
  0%,100%{opacity:.55;transform:scale(1);}
  50%{opacity:1;transform:scale(1.25);}
}

/* Chips under the fold of the hero — glass pills with a gold hairline. */
#view-home #heroChips{
  justify-content:center;
  margin-top:28px;
  gap:9px;
}
.chip{
  padding:7px 15px;
  border-radius:999px;
  border:1px solid rgb(212 175 55 / 16%);
  background:rgb(12 11 9 / 55%);
  color:rgb(245 243 238 / 70%);
  font-size:11.5px;font-weight:700;letter-spacing:.02em;
  transition:border-color .3s,color .3s,background .3s,transform .3s var(--ease-spring);
}
.chip:hover{color:var(--text);border-color:rgb(212 175 55 / 48%);background:rgb(212 175 55 / 8%);transform:translateY(-2px);}
.chip.active{background:linear-gradient(140deg,var(--gold-bright),var(--gold));color:#150f02;border-color:transparent;}

/* Scroll cue. A hairline that drains downward, forever, in the one place a
   first-time visitor looks when the hero stops filling the fold. */
.hero-scroll{
  position:absolute;left:50%;bottom:26px;z-index:4;
  transform:translateX(-50%);
  display:flex;flex-direction:column;align-items:center;gap:10px;
  color:rgb(245 243 238 / 42%);
  font-size:9.5px;font-weight:800;letter-spacing:.28em;text-transform:uppercase;
  transition:color .3s;
}
.hero-scroll:hover{color:var(--gold);}
.hero-scroll-line{
  position:relative;display:block;width:1px;height:46px;overflow:hidden;
  background:rgb(212 175 55 / 18%);
}
.hero-scroll-line::after{
  content:"";position:absolute;left:0;top:-100%;width:100%;height:100%;
  background:linear-gradient(180deg,transparent,var(--gold),transparent);
  animation:scrollCue 2.4s cubic-bezier(.7,0,.3,1) infinite;
}
@keyframes scrollCue{
  0%{top:-100%;}
  60%,100%{top:100%;}
}

/* ── 7. marquee band ───────────────────────────────────────────────────── */

/* Pinned directly under the hero, bleeding the full width: it separates the
   bright hero from the dark page body and gives the eye something continuous
   to follow while scrolling. Purely decorative — aria-hidden, and the loop is
   a duplicate of the same list, so screen readers would read everything twice
   if it weren't. */
.hero-marquee{
  position:relative;
  width:100vw;margin-left:calc(50% - 50vw);
  overflow:hidden;
  border-top:1px solid rgb(212 175 55 / 14%);
  border-bottom:1px solid rgb(212 175 55 / 14%);
  background:linear-gradient(180deg,rgb(212 175 55 / 5%),rgb(4 4 3 / 0%));
  padding:15px 0;
  mask-image:linear-gradient(90deg,transparent,#000 12%,#000 88%,transparent);
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 12%,#000 88%,transparent);
  z-index:1;
}
.mq-track{
  display:flex;align-items:center;gap:0;width:max-content;
  will-change:transform;
  /* The loop is a CSS transform on the compositor, not a GSAP tween driven
     from the main thread. The old version wrote a new xPercent 60 times a
     second for as long as the page was open, competing with scroll for the
     same frame budget on exactly the devices that had none to spare. The
     track is two identical .mq-group halves, so -50% is seamless. */
  animation:mqScroll 42s linear infinite;
}
@keyframes mqScroll{
  from{transform:translate3d(0,0,0);}
  to{transform:translate3d(-50%,0,0);}
}
.mq-group{
  display:flex;align-items:center;gap:26px;padding-right:26px;
  white-space:nowrap;
}
.mq-item{
  display:inline-flex;align-items:center;gap:26px;
  font-family:'Space Grotesk',sans-serif;
  font-size:13px;font-weight:700;letter-spacing:.34em;text-transform:uppercase;
  color:rgb(245 243 238 / 52%);
}
/* The separator between marquee phrases. It was the ✦ character, which some
   phones draw out of the colour-emoji font; it is the site's own star shape
   now, masked and filled with gold like the star rows. */
.mq-item::after{
  content:"";inline-size:9px;block-size:9px;flex:0 0 auto;
  background:var(--gold);
  -webkit-mask:var(--star-mask) center / contain no-repeat;
  mask:var(--star-mask) center / contain no-repeat;
  opacity:.75;
}

/* ── 8. sections & headings ────────────────────────────────────────────── */

.section{padding:64px 0;}
.section-kicker{
  display:inline-flex;align-items:center;gap:9px;
  margin-bottom:10px;
  font-size:10px;font-weight:800;letter-spacing:.2em;
}
.section-kicker::before{
  content:"";width:18px;height:1px;background:currentColor;opacity:.7;
}
.section-head{margin-bottom:28px;align-items:flex-end;}
.section-head h2{
  font-size:clamp(24px,3.1vw,36px);
  font-weight:800;
  line-height:1.06;
}
.section-head p{
  color:var(--text-muted);
  font-size:13.5px;
  margin-top:8px;
  max-width:56ch;
  line-height:1.6;
}

/* Section headings that fx.js has split into lines get the same mask-rise
   treatment as the hero, one line at a time. */
.section-head h2 .fx-line{display:block;overflow:hidden;}
.section-head h2 .fx-line>span{display:inline-block;}

/* ── 9. buttons ────────────────────────────────────────────────────────── */

.btn{
  position:relative;
  padding:12px 22px;
  border-radius:999px;
  font-size:13px;
  font-weight:700;
  letter-spacing:.01em;
  overflow:hidden;
  isolation:isolate;
  transition:transform .35s var(--ease-spring),border-color .3s,color .3s,
             background .3s,box-shadow .4s var(--ease-out),filter .3s;
}
.btn:active{transform:scale(.965);}

/* A sheen that sweeps across the button on hover. `::before` and not
   `::after`, because `::after` is already the loading spinner
   (.btn.is-busy) in style.css. This mirrors the no-motion rule that hides
   .btn::before. */
.btn::before{
  content:"";position:absolute;inset:-1px;z-index:2;pointer-events:none;
  border-radius:inherit;
  background:linear-gradient(105deg,transparent 28%,rgb(255 255 255 / 45%) 48%,transparent 66%);
  transform:translateX(-130%);
  opacity:0;
  transition:transform .85s var(--ease-out),opacity .3s;
  mix-blend-mode:overlay;
}
.btn:hover::before{transform:translateX(130%);opacity:1;}

.btn-primary{
  background:linear-gradient(135deg,var(--gold-bright) 0%,var(--gold) 46%,var(--gold-deep) 100%);
  color:#150f02;
  box-shadow:
    0 14px 34px -16px rgb(212 175 55 / 78%),
    inset 0 1px 0 rgb(255 255 255 / 50%),
    inset 0 -1px 0 rgb(0 0 0 / 18%);
}
.btn-primary:hover{
  transform:translateY(-2px);
  box-shadow:
    0 20px 44px -16px rgb(212 175 55 / 88%),
    inset 0 1px 0 rgb(255 255 255 / 60%);
}

.btn-secondary{
  /* No backdrop-filter: these appear by the dozen in lists, and a blur on
     each one makes the browser snapshot the page behind every single copy. */
  background:rgb(255 255 255 / 4%);
  border-color:rgb(212 175 55 / 22%);
  color:var(--text);
  box-shadow:inset 0 1px 0 rgb(255 255 255 / 6%);
}
.btn-secondary:hover{
  background:rgb(212 175 55 / 10%);
  border-color:rgb(212 175 55 / 62%);
  transform:translateY(-2px);
  box-shadow:0 16px 34px -22px rgb(212 175 55 / 90%),inset 0 1px 0 rgb(255 255 255 / 10%);
}

.btn-ghost{border-radius:999px;}
.btn-ghost:hover{background:rgb(212 175 55 / 8%);color:var(--gold-bright);}
.btn-gold{
  background:linear-gradient(135deg,#fff3cf,var(--gold) 50%,#8c6d1f);
  color:#170f00;
  box-shadow:0 14px 34px -18px rgb(212 175 55 / 90%),inset 0 1px 0 rgb(255 255 255 / 55%);
}
.btn-sm{padding:8px 15px;font-size:11.5px;}
.btn .icon{width:15px;height:15px;}

/* ── 10. surfaces ──────────────────────────────────────────────────────── */

/* Every card on the site shares one material: a soft vertical gradient, a
   gold hairline that only shows at the edges, and a one-pixel top highlight
   that reads as a lit edge. Per-card art and scrims still layer underneath
   (they use ::before/::after, which this leaves alone). */
.card,
.perk-card,
.stat-pill,
.plan-card,
.home-group-card,
.tier-node,
.item-card,
.auth-card,
.lb-list,
.quest-row,
.ach-card{
  position:relative;
  background:linear-gradient(165deg,rgb(22 19 14 / 92%),rgb(7 7 6 / 96%));
  border:1px solid rgb(212 175 55 / 13%);
  box-shadow:
    0 24px 54px -40px rgb(0 0 0 / 100%),
    inset 0 1px 0 rgb(255 255 255 / 5%);
  transition:transform .5s var(--ease-out),border-color .4s,box-shadow .5s var(--ease-out);
}
.card:hover,
.perk-card:hover,
.plan-card:hover,
.home-group-card:hover,
.stat-pill:hover,
.tier-node:hover{
  border-color:rgb(212 175 55 / 38%);
  transform:translateY(-4px);
  box-shadow:
    0 34px 70px -40px rgb(0 0 0 / 100%),
    0 0 40px -18px rgb(212 175 55 / 34%),
    inset 0 1px 0 rgb(255 255 255 / 9%);
}
/* Tiles that genuinely do nothing don't get the lift — same reasoning as the
   cursor rules in style.css. */
.perk-card:not([data-char]):not([data-item]):not(a):not(button):hover,
.card-tile:hover,
.tier-node:hover{
  transform:none;
}

/* The pointer-following highlight (SpotlightCard). One child span injected by
   fx.js into any hover target; the host needs `position:relative` (all of
   them already have it) and the gradient is clipped to the host's radius by
   `border-radius:inherit`. */
.fx-spot{
  position:absolute;inset:0;z-index:4;
  border-radius:inherit;
  pointer-events:none;
  opacity:0;
  transition:opacity .45s var(--ease-out);
  background:radial-gradient(
    260px circle at var(--mx,50%) var(--my,50%),
    rgb(255 226 150 / 15%) 0%,
    rgb(255 226 150 / 5%) 34%,
    transparent 68%
  );
}
.fx-spot-host:hover>.fx-spot{opacity:1;}
/* No `overflow:hidden` on the host: the spot rounds its own corners with
   `border-radius:inherit`, and clipping the host would swallow the badges and
   ribbons that deliberately hang outside a card (see .plan-ribbon). */

/* ── season hero ──────────────────────────────────────────────────────── */
/* The scrim's last stop is the page colour itself (#040403), so the photograph
   dissolves into the page body rather than ending on a visible horizontal cut. */
.season-hero>.season-hero-veil{
  /* Darker through the middle where the copy sits, and it still lands on the
     page colour (#040403) at the bottom so there is no visible seam. */
  background:linear-gradient(180deg,rgb(4 4 3 / 30%) 0%,rgb(4 4 3 / 46%) 46%,rgb(4 4 3 / 62%) 72%,#040403 100%);
}
.season-hero .hero-inner h1{letter-spacing:-.045em;}
/* The season copy sits on a photograph, not on the flat page, so the plain
   words need their own backdrop. Not on the h1: it is background-clipped gold
   and a shadow there would muddy it (same trap as the home headline). */
.season-hero .hero-inner p,
.season-hero .hero-inner .hero-badge{text-shadow:0 1px 14px rgb(0 0 0 / 70%);}

/* A head followed by nothing but an empty host is a heading with no content:
   "Battle pass progress" on a between-seasons page. */
.section:has(> .section-head + #seasonMine:empty),
.section:has(> .section-head + #seasonLore:empty){display:none;}

/* ── feature (mode) cards ─────────────────────────────────────────────── */
.grid-4{
  perspective:1400px;
  gap:18px;
}
.feature-card{
  min-height:250px;
  border-radius:var(--r-xl);
  border-color:rgb(212 175 55 / 16%);
  transform-style:preserve-3d;
  box-shadow:0 30px 60px -44px #000;
  transition:border-color .45s,box-shadow .5s var(--ease-out);
}
.feature-card:hover{
  transform:none; /* tilt is applied per-frame by fx.js */
  border-color:rgb(212 175 55 / 52%);
  box-shadow:0 40px 80px -46px #000,0 0 60px -26px rgb(212 175 55 / 40%);
}
.feature-card::after{
  background:linear-gradient(180deg,rgb(0 0 0 / 0%) 0%,rgb(0 0 0 / 26%) 42%,rgb(0 0 0 / 92%) 100%);
}
.feature-content{
  padding:22px;
  transform:translateZ(34px);
  transition:transform .5s var(--ease-out);
}
.feature-content h3{
  font-size:18px;
  letter-spacing:-.02em;
  margin-bottom:6px;
}
/* The description is dimmed, not hidden. Hiding it entirely on a tile that
   isn't hovered makes the card say one word and nothing else — and there is
   no hover at all on a phone. It lifts to full strength with the tilt. */
.feature-content p{
  max-width:230px;
  font-size:12.5px;
  color:rgb(245 243 238 / 62%);
  transition:color .4s var(--ease-out);
}
.feature-card:hover .feature-content p{color:rgb(245 243 238 / 92%);}
.feature-icon{
  top:16px;right:16px;
  width:36px;height:36px;
  border-color:rgb(212 175 55 / 34%);
  background:rgb(4 4 3 / 55%);
  transform:translateZ(24px);
  transition:transform .5s var(--ease-spring),border-color .3s;
}
.feature-card:hover .feature-icon{transform:translateZ(50px) scale(1.08);border-color:var(--gold);}
/* ── stats ────────────────────────────────────────────────────────────── */
.stat-row{gap:14px;}
.stat-pill{
  padding:20px 20px 18px;
  overflow:hidden;
}
.stat-pill::before{
  content:"";position:absolute;left:0;right:0;top:0;height:1px;
  background:linear-gradient(90deg,transparent,rgb(212 175 55 / 55%),transparent);
  opacity:.6;
}
.stat-pill .label{
  font-size:10px;letter-spacing:.18em;
  color:rgb(245 243 238 / 42%);
}
.stat-pill .value{
  font-size:38px;
  line-height:1;
  margin-top:10px;
  font-family:'Space Grotesk',sans-serif;
  font-weight:800;
  letter-spacing:-.04em;
  font-variant-numeric:tabular-nums;
}
.stat-pill .value.gold{
  background:linear-gradient(140deg,var(--gold-bright),var(--gold) 55%,var(--gold-deep));
  -webkit-background-clip:text;background-clip:text;color:transparent;
  filter:drop-shadow(0 6px 18px rgb(212 175 55 / 22%));
}
.stat-pill .value.acc{color:var(--text);}
.stat-pill .subtext{color:rgb(245 243 238 / 40%);font-size:11.5px;}

/* ── home group cards ─────────────────────────────────────────────────── */
.home-group-card{border-radius:var(--r-lg);}
/* 1:1. These are square avatars - a portrait frame crops them to a thin strip
   and leaves a band of empty box under the picture, which is where the
   fallback initials used to end up peeking out. Square in, square out. */
.home-group-thumb{
  position:relative;isolation:isolate;
  display:grid;place-items:center;
  width:86px;aspect-ratio:1;height:auto;flex:0 0 86px;
  border-color:rgb(212 175 55 / 22%);
  border-radius:14px;
  background:
    radial-gradient(120% 100% at 30% 0%,rgb(212 175 55 / 26%),transparent 62%),
    linear-gradient(150deg,#17130a,#0a0908);
}
/* The pin-thin grid from the hero, at 11px, fading out before the edges.
   Negative z-index: it belongs behind the artwork, not over a photograph. */
.home-group-thumb::before{
  content:'';position:absolute;inset:0;border-radius:inherit;pointer-events:none;
  z-index:-1;
  background-image:
    linear-gradient(rgb(212 175 55 / 13%) 1px,transparent 1px),
    linear-gradient(90deg,rgb(212 175 55 / 13%) 1px,transparent 1px);
  background-size:11px 11px;
  -webkit-mask-image:radial-gradient(72% 72% at 50% 45%,#000 28%,transparent 100%);
  mask-image:radial-gradient(72% 72% at 50% 45%,#000 28%,transparent 100%);
}
.home-group-mark{
  position:relative;z-index:1;
  font-family:'Space Grotesk',sans-serif;font-size:21px;font-weight:700;
  letter-spacing:.06em;color:#f4dfa2;
  text-shadow:0 2px 14px rgb(212 175 55 / 45%);
  transition:transform var(--transition);
}
/* Art loaded means no initials at all. Stacking the photo over the monogram is
   not enough on its own: if the photo is ever laid out as a grid row instead of
   an absolutely positioned layer - a half-applied stylesheet, a browser that
   skips `inset`, a cached older sheet - the monogram drops to a second row and
   reads as a stray caption under the picture. So the <img> marks the frame as
   it loads (`data-art`) and the initials step aside. Before it loads, and if it
   fails, they are still the frame's content - the <img> removes itself on
   error, so a dead URL leaves exactly the old gold monogram behind. */
.home-group-thumb[data-art] .home-group-mark{display:none;}
/* The group art, full-bleed in the square. */
.home-group-thumb img{
  position:absolute;inset:0;z-index:2;
  width:100%;height:100%;object-fit:cover;object-position:center;
  border-radius:inherit;
}
@media (max-width:620px){.home-group-thumb{width:72px;flex:0 0 72px;}}
.home-group-card:hover .home-group-mark{transform:scale(1.1) rotate(-2deg);}
.home-group-card:hover .home-group-thumb img{transform:scale(1.09);}
@media (prefers-reduced-motion:reduce){.home-group-card:hover .home-group-mark{transform:none;}}
.home-group-label{letter-spacing:.16em;}

/* ── character cards ──────────────────────────────────────────────────── */
/* .char-card is a column flex container, so every child stretches to the full
   width — which turns an inline rarity pill into a full-width bar. Pills and
   badges are labels: they hug their text. */
.char-card .rarity-pill,
.char-card .char-status .badge,
.char-card .badge{align-self:flex-start;}
.char-card{gap:2px;}
.char-card .media-frame{margin-bottom:10px;}
.char-card h4{font-size:14.5px;}

/* ── empty containers ─────────────────────────────────────────────────── */
/* Several controllers render into a host only when the API has something to
   say (the season's "your progress" card, a board's tab row). Left alone, an
   empty host is a visible sliver of border with nothing inside it. */
.tab-row:empty,
.card:empty,
.card-glow:empty{display:none;}
/* A heading whose only content is an empty grid is a promise with nothing
   behind it — collapse the whole section (e.g. gem packages on a bot that has
   none configured). :has() degrades to today's rendering, no JS needed. */
.section:has(> .section-head:has(+ .grid-4:empty)){display:none;}
.grid-4:empty,
.grid-3:empty,
.grid-2:empty{display:none;}

/* Three cards in a four-column grid leaves a hole on the right that reads as a
   loading failure. Let the cards fill the row instead — desktop only, because
   the mobile breakpoints already collapse these grids to 1–2 columns. */
@media (min-width:760px){
  .grid-4:has(> :nth-child(3):last-child){grid-template-columns:repeat(3,minmax(0,1fr));}
  .grid-3:has(> :nth-child(2):last-child){grid-template-columns:repeat(2,minmax(0,1fr));}
}

/* ── tab rows ─────────────────────────────────────────────────────────── */
/* Underlined tabs → a glass pill group, the same material as the nav. Only
   the shape changes; `.tab-btn.active` keeps its meaning. */
.tab-row{
  display:inline-flex;width:auto;max-width:100%;
  gap:4px;padding:4px;margin-bottom:22px;
  border:1px solid rgb(212 175 55 / 14%);
  border-radius:999px;
  background:rgb(255 255 255 / 4%);
  box-shadow:inset 0 1px 0 rgb(255 255 255 / 5%);
  overflow-x:auto;
  scrollbar-width:none;
}
.tab-row::-webkit-scrollbar{height:0;}
.tab-btn{
  display:inline-flex;align-items:center;gap:7px;
  padding:8px 16px;
  border:none;
  border-radius:999px;
  font-size:12.5px;
  font-weight:700;
  white-space:nowrap;
  transition:background .3s,color .3s,box-shadow .3s;
}
/* A tab can lead with a line icon (the shop shelves do). It sits a touch
   quieter than the label and brightens with the active state. */
.tab-btn .tab-ico{width:15px;height:15px;flex:0 0 auto;opacity:.75;transition:opacity .3s,color .3s;}
.tab-btn:hover .tab-ico{opacity:.95;}
.tab-btn.active .tab-ico{opacity:1;color:var(--gold-bright);}
.tab-btn .gold-stars{font-size:.8em;margin-left:5px;}
.tab-btn:hover{background:rgb(255 255 255 / 4%);}
.tab-btn.active{
  background:linear-gradient(140deg,rgb(212 175 55 / 26%),rgb(212 175 55 / 8%));
  color:var(--gold-bright);
  box-shadow:inset 0 0 0 1px rgb(212 175 55 / 42%),inset 0 1px 0 rgb(255 255 255 / 12%),
             0 0 24px -10px rgb(212 175 55 / 70%);
}
/* The leaderboard's tab row shares a line with the search field. */
.lb-toolbar .tab-row{flex:0 1 auto;margin-bottom:0;}
.lb-search{
  padding:11px 18px;
  border-radius:999px;
  background:rgb(255 255 255 / 3%);
  border-color:rgb(212 175 55 / 16%);
  transition:border-color .3s,background .3s,box-shadow .3s;
}
.lb-search:focus{
  border-color:rgb(212 175 55 / 60%);
  background:rgb(212 175 55 / 4%);
  box-shadow:0 0 0 4px rgb(212 175 55 / 9%);
}

/* Any heading that app.js nests inside a card is a card heading, not a page
   heading — .section-head's display size is far too loud in that context. */
.card .section-head h2{font-size:clamp(18px,1.9vw,23px);}
.card .section-head p{font-size:12.5px;}

/* ── list rows (leaderboard) ──────────────────────────────────────────── */
.lb-list{
  border-radius:var(--r-lg);
  padding:8px;
}
.lb-row{
  border-radius:12px;
  transition:background .3s,border-color .3s,transform .35s var(--ease-out);
}
.lb-row:hover{
  background:rgb(212 175 55 / 6%);
  border-color:rgb(212 175 55 / 22%);
  transform:translateX(3px);
}
.lb-row.is-you{
  background:linear-gradient(90deg,rgb(212 175 55 / 12%),rgb(212 175 55 / 3%));
  border-color:rgb(212 175 55 / 32%);
}
.lb-rank.top1{text-shadow:0 0 16px rgb(212 175 55 / 70%);}
.lb-avatar{
  border-color:rgb(212 175 55 / 24%);
  transition:box-shadow .3s,border-color .3s;
}
.lb-row:hover .lb-avatar{
  border-color:rgb(212 175 55 / 60%);
  box-shadow:0 0 0 3px rgb(212 175 55 / 10%);
}
.lb-score{
  font-variant-numeric:tabular-nums;
  letter-spacing:-.01em;
}

/* ── faq ──────────────────────────────────────────────────────────────── */
#homeFaq{padding:6px 22px;}
.faq-q{
  font-size:14px;
  transition:color .3s;
  gap:16px;
}
.faq-q:hover{color:var(--gold-bright);}
.faq-q .arrow{
  display:grid;place-items:center;
  width:24px;height:24px;flex:0 0 24px;
  border-radius:50%;
  border:1px solid rgb(212 175 55 / 22%);
  font-size:15px;line-height:1;
  transition:transform .4s var(--ease-spring),border-color .3s,color .3s;
}
.faq-item.open .arrow{transform:rotate(135deg);border-color:var(--gold);color:var(--gold);}
.faq-a{transition:max-height .45s var(--ease-out),opacity .35s;}
.faq-a p{padding-right:36px;}

/* ── premium / plans ──────────────────────────────────────────────────── */
.plan-card.featured{
  background:
    radial-gradient(120% 70% at 50% 0%,rgb(212 175 55 / 12%),transparent 60%),
    #060605;
  box-shadow:
    0 0 0 1px rgb(212 175 55 / 26%),
    0 30px 70px -40px #000,
    0 0 70px -22px rgb(212 175 55 / 26%);
}
.plan-ribbon{
  box-shadow:0 8px 26px -8px rgb(212 175 55 / 80%);
  letter-spacing:.08em;
}
.plan-price{font-size:40px;letter-spacing:-.04em;}

/* ── auth ─────────────────────────────────────────────────────────────── */
.auth-wrap{padding-top:96px;}
.auth-card{
  border-radius:24px;
  padding:34px 30px;
  background:
    radial-gradient(120% 60% at 50% 0%,rgb(212 175 55 / 8%),transparent 62%),
    linear-gradient(170deg,rgb(20 17 12 / 94%),rgb(7 7 6 / 98%));
  box-shadow:
    0 40px 90px -50px #000,
    inset 0 1px 0 rgb(255 255 255 / 7%);
}
.auth-card h2{font-size:26px;letter-spacing:-.03em;}
.field input,.field select{
  padding:13px 15px;
  border-radius:12px;
  background:rgb(255 255 255 / 3%);
  border-color:rgb(212 175 55 / 16%);
  transition:border-color .3s,box-shadow .3s,background .3s;
}
.field input:focus,.field select:focus{
  background:rgb(212 175 55 / 4%);
  border-color:rgb(212 175 55 / 60%);
  box-shadow:0 0 0 4px rgb(212 175 55 / 10%);
}
.otp-row input{
  border-radius:14px;
  background:rgb(255 255 255 / 3%);
  font-size:24px;
  transition:border-color .3s,box-shadow .3s,transform .25s var(--ease-spring);
}
.otp-row input:focus{transform:translateY(-2px);}
.otp-row input.is-filled{border-color:rgb(212 175 55 / 65%);box-shadow:0 0 20px -8px rgb(212 175 55 / 80%);}

.auth-wrap{position:relative;}
.auth-wrap::before{
  content:"";position:absolute;left:50%;top:24%;transform:translate(-50%,-50%);
  width:min(720px,88vw);height:min(520px,60vh);pointer-events:none;z-index:-1;
  /* The gradient already fades to nothing; the blur only added a filter pass
     over a 720x520 box. */
  background:radial-gradient(ellipse at center,rgb(212 175 55 / 12%),transparent 68%);
}

/* ── modal ────────────────────────────────────────────────────────────── */
.modal-card{
  border-radius:26px;
  border-color:rgb(212 175 55 / 20%);
  background:linear-gradient(170deg,rgb(20 17 12 / 98%),rgb(6 6 5 / 99%));
  box-shadow:
    0 50px 110px -50px #000,
    inset 0 1px 0 rgb(255 255 255 / 8%);
  transition:transform .45s var(--ease-spring),opacity .3s;
}
.modal.open .modal-card{transform:none;}
.modal-scrim{background:rgb(2 2 2 / 86%);}

/* ── toasts ───────────────────────────────────────────────────────────── */

/* On a phone the toast stack anchored bottom-right sits on top of the dock.
   Lift it above the bar and let it use the full width instead. */
@media (max-width:820px){
  .toast-wrap{
    left:12px;right:12px;
    bottom:calc(96px + env(safe-area-inset-bottom,0px));
    align-items:stretch;
  }
  .toast{min-width:0;font-size:12.5px;padding:11px 13px;}
}
.toast{
  background:linear-gradient(160deg,rgb(22 19 14 / 96%),rgb(8 8 7 / 98%));
  border-color:rgb(212 175 55 / 22%);
  border-radius:14px;
  box-shadow:0 24px 50px -30px #000,inset 0 1px 0 rgb(255 255 255 / 7%);
}

/* ── tier track ───────────────────────────────────────────────────────── */
.tier-track{padding:8px 2px 18px;gap:16px;}
.tier-node{overflow:hidden;}
.tier-node.current{
  border-color:var(--gold);
  box-shadow:0 0 0 1px rgb(212 175 55 / 34%),0 0 40px -12px rgb(212 175 55 / 50%),var(--shadow);
}

/* ── 11. view transitions ─────────────────────────────────────────────── */

/* style.css fades each view in via a keyframe. GSAP takes over once it boots
   (it animates opacity and y on the incoming view and clears the props), so
   the keyframe is switched off to stop the two fighting. */
.view.active{animation:none;}

/* Event slides point at remote art (ibb.co). When that request fails — a
   blocked hotlink, a dead link, a phone on a bad connection — the 16/10 media
   box was a pure black rectangle. Under the photo there is now the same warm
   light the hero uses, so a missing image still reads as artwork. */
.ev-media{
  background:
    radial-gradient(120% 100% at 28% 0%,rgb(212 175 55 / 24%),transparent 60%),
    radial-gradient(90% 80% at 82% 100%,rgb(212 175 55 / 11%),transparent 70%),
    linear-gradient(155deg,#1a150c,#080706);
}
.ev-media img{background:transparent;}

/* ── 12. mobile dock ──────────────────────────────────────────────────── */

/* The floating bar becomes a dock: a deeper blur, a stronger gold edge, and
   an active slot that lifts out of the row. */
.mobile-bar{
  border-color:rgb(212 175 55 / 22%);
  background:linear-gradient(180deg,rgb(16 14 11 / 88%),rgb(6 6 5 / 94%));
  box-shadow:
    0 -10px 40px -20px rgb(0 0 0 / 90%),
    0 18px 44px -18px rgb(0 0 0 / 85%),
    inset 0 1px 0 rgb(255 255 255 / 9%);
}
.mbar-slot{border-radius:16px;}
.mbar-slot.is-active{
  color:var(--gold-bright);
  background:linear-gradient(180deg,rgb(212 175 55 / 18%),rgb(212 175 55 / 6%));
  box-shadow:inset 0 1px 0 rgb(255 255 255 / 10%),0 0 22px -10px rgb(212 175 55 / 70%);
}
.mbar-slot.is-active::before{background:var(--gold-bright);box-shadow:0 0 10px rgb(212 175 55 / 90%);}
.mbar-ico{transition:transform .35s var(--ease-spring);}
.mbar-slot.is-active .mbar-ico{transform:translateY(-1px) scale(1.08);}

/* ── 13. responsive ──────────────────────────────────────────────────── */

@media (max-width:1100px){
  #view-home .hero.hero-fx{min-height:min(860px,96svh);}
  .feature-card{min-height:230px;}
}

@media (max-width:900px){
  .section{padding:52px 0;}
  .section-head h2{font-size:clamp(22px,4.4vw,30px);}
  .stat-pill .value{font-size:32px;}
}

@media (max-width:760px){
  .nav-shell{padding:10px 12px 0;}

  #view-home .hero.hero-fx{
    width:100%;
    margin-left:0;
    margin-top:-52px;
    min-height:min(820px,94svh);
    padding:112px 16px 84px;
  }
  #view-home .hero .hero-inner{width:100%;max-width:100%;}
  #view-home .hero .hero-title{
    font-size:clamp(34px,10.4vw,50px);
    line-height:.98;
    letter-spacing:-.045em;
    margin-bottom:16px;
  }
  .hero-kicker{
    /* One line at any phone width. The copy is fixed, so it is sized to fit
       rather than left to wrap around the two rules either side of it. */
    font-size:clamp(7.5px,2.15vw,9.5px);
    letter-spacing:.12em;
    margin-bottom:16px;
    gap:8px;
    white-space:nowrap;
  }
  .hero-kicker::before,.hero-kicker::after{width:14px;flex:0 0 14px;}
  #view-home .hero p{font-size:14px;line-height:1.6;margin-bottom:20px;}
  .hero-fx .hero-art{background-position:62% 30%;}
  .hero-fx .hero-veil{
    background:
      radial-gradient(ellipse 90% 40% at 50% 40%,rgb(4 4 3 / 58%) 0%,transparent 76%),
      linear-gradient(180deg,rgb(4 4 3 / 86%) 0%,rgb(4 4 3 / 34%) 30%,rgb(4 4 3 / 96%) 100%);
  }
  #view-home .hero-trust{
    gap:10px 14px;margin-top:18px;font-size:10px;
    flex-wrap:wrap;justify-content:center;
  }
  /* Four chips wrap 3 + 1 at this width, which leaves a lonely orphan on its
     own line. A 2×2 grid reads as a set instead. */
  #view-home #heroChips{
    margin-top:20px!important;gap:7px;
    display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
  }
  #view-home #heroChips .chip{justify-content:center;text-align:center;}
  .chip{padding:6px 12px;font-size:10.5px;}
  .hero-scroll{display:none;}

  .hero-marquee{padding:12px 0;}
  .mq-item{font-size:11px;letter-spacing:.26em;gap:20px;}
  .mq-group{gap:20px;padding-right:20px;}

  .section{padding:40px 0;}
  .section-head{margin-bottom:18px;}
  .section-head p{font-size:12.5px;}

  .grid-4,.grid-3{gap:12px;}
  .feature-card{min-height:200px;border-radius:20px;}
  .feature-content{padding:16px;}
  .feature-content h3{font-size:15px;}
  .feature-content p{font-size:11.5px;}
  #view-home .stat-pill{padding:15px 14px;border-radius:16px;}
  #view-home .stat-pill .value{font-size:26px;margin-top:7px;}
  .auth-card{padding:26px 20px;border-radius:22px;}
  .auth-wrap{padding-top:76px;}
}

@media (max-width:420px){
  #view-home .hero .hero-title{font-size:clamp(30px,10vw,38px);}
  #view-home .hero-trust{font-size:9.5px;}
  .mq-item{font-size:10px;letter-spacing:.2em;}
}

/* Short, wide viewports (landscape phones, small laptops) don't have the
   height for a full-viewport hero — cap it and let the copy set the size. */
@media (max-height:640px) and (min-width:761px){
  #view-home .hero.hero-fx{min-height:auto;padding-top:130px;padding-bottom:70px;}
  #view-home .hero .hero-title{font-size:clamp(36px,5vw,56px);}
}

/* Coarse pointers get no hover-only affordances and no cursor ring. */
@media (hover:none){
  .feature-card:hover{transform:none;}
  .fx-spot{display:none;}
}

/* ── 14. reduced motion ──────────────────────────────────────────────── */

@media (prefers-reduced-motion:reduce){
  .hero-fx .hero-art{animation:none;}
  .mq-track{animation:none;}
  /* Note `background-image`, not the `background` shorthand: the shorthand
     resets background-clip to border-box, which turns the gradient word into
     a solid gold block with transparent text. */
  #view-home .hero .hero-title .grad{
    animation:none;
    background-image:var(--grad-1);
    -webkit-background-clip:text;background-clip:text;
  }
  .hero-scroll-line::after{animation:none;top:0;opacity:.6;}
  .trust-dot{animation:none;}
  .fx-cursor{display:none;}
  .fx-grain{display:none;}
  .btn::before{display:none;}
  .card,.perk-card,.stat-pill,.plan-card,.home-group-card,.tier-node,.lb-row,
  .feature-content p,.feature-icon,.mbar-ico{
    transition-duration:.001ms!important;
  }
  .card:hover,.perk-card:hover,.plan-card:hover,.home-group-card:hover,
  .stat-pill:hover,.btn:hover,.btn-primary:hover,.btn-secondary:hover{
    transform:none;
  }
}

/* Set by fx.js when Settings → This device turns motion off mid-session:
   the canvases and the cursor ring go away, CSS keeps the layout. */
html.fx-paused .fx-cursor,
html.fx-paused .fx-spark,
html.fx-paused .fx-grain,
html.fx-paused .hero-canvas{display:none;}
html.fx-paused .scroll-progress i{transition:none;}

/* ── community join row (sign-up, step one) ───────────────────────────── */

/* Two gold chips, one per chat, for a player who has no account yet: the
   point is that they can walk into a group and ask a human before they ever
   hand over a phone number. */
.join-lead{
  margin:16px 0 10px;
  font-size:12px;
  color:rgb(245 243 238 / 52%);
  letter-spacing:.01em;
}
.join-row{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:9px;}
.join-btn{
  display:flex;align-items:center;gap:9px;
  padding:9px 10px;
  border-radius:13px;
  border:1px solid rgb(212 175 55 / 32%);
  background:linear-gradient(150deg,rgb(212 175 55 / 13%),rgb(212 175 55 / 3%));
  box-shadow:inset 0 1px 0 rgb(255 255 255 / 5%);
  transition:transform .3s var(--ease-spring),border-color .3s,background .3s,box-shadow .3s;
}
.join-btn:hover{
  transform:translateY(-2px);
  border-color:rgb(212 175 55 / 68%);
  background:linear-gradient(150deg,rgb(212 175 55 / 20%),rgb(212 175 55 / 6%));
  box-shadow:0 14px 28px -20px rgb(212 175 55 / 95%),inset 0 1px 0 rgb(255 255 255 / 8%);
}
.join-ico{
  display:grid;place-items:center;flex:0 0 auto;
  width:28px;height:28px;border-radius:9px;
  background:linear-gradient(150deg,rgb(212 175 55 / 28%),rgb(212 175 55 / 8%));
  border:1px solid rgb(212 175 55 / 34%);
  color:var(--gold-bright);
}
.join-ico .icon{width:15px;height:15px;}
.join-txt{display:flex;flex-direction:column;min-width:0;line-height:1.25;}
.join-txt strong{
  font-size:11.5px;font-weight:800;letter-spacing:.01em;
  color:var(--gold-bright);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.join-txt em{font-style:normal;font-size:10px;color:rgb(245 243 238 / 45%);}

/* The auth card is ~400px at its widest and these two chips are already
   tight; below 420px they stack rather than squeeze the labels. */
@media (max-width:420px){
  .join-row{grid-template-columns:minmax(0,1fr);}
}

/* ── 15. performance layer ────────────────────────────────────────────── */

/* `html.perf-lite` is set by the inline <head> script before the first paint
   (see index.html) when the device is a phone, a slow connection, or a
   machine with very few cores or little memory. Settings → This device can
   override it in either direction.

   The rule this section follows: keep everything that carries meaning —
   layout, colour, type, spacing, hover/focus states, and the entrance of
   content — and drop everything that only produces motion or glass. Nothing
   here changes the DOM, so every route and every interaction is identical.

   These rules are all scoped to `html.perf-lite`, so the full design is
   untouched on machines that can afford it. */

/* Glass is gone entirely in lite mode. A backdrop-filter forces the browser
   to re-snapshot whatever is behind the element; on a page that scrolls under
   a fixed bar that is the most expensive thing in the whole design. The
   backgrounds above are opaque enough that the visual difference is a
   slightly flatter panel, nothing more. */
html.perf-lite *,
html.perf-lite *::before,
html.perf-lite *::after{
  backdrop-filter:none!important;
  -webkit-backdrop-filter:none!important;
}

/* Full-viewport blended layers: one is a fixed noise tile composited with
   `overlay` on every frame, the other two drive pointer decorations that a
   touch screen cannot express anyway. */
html.perf-lite .fx-grain,
html.perf-lite .fx-spark,
html.perf-lite .fx-cursor{display:none!important;}

/* The aurora canvas stays, but as a still picture drawn once — blending it
   per frame is what cost the fill rate, not the gradient itself. */
html.perf-lite .hero-canvas{mix-blend-mode:normal;}

/* Nothing is left promoted to its own compositor layer for the whole visit.
   `will-change` is a loan against GPU memory; a phone pays for every layer
   it is handed, and these were permanent. */
html.perf-lite .reveal,
html.perf-lite .hero-art,
html.perf-lite .ht-word,
html.perf-lite .nav-shell,
html.perf-lite .feature-card,
html.perf-lite .card,
html.perf-lite .stat-pill,
html.perf-lite .perk-card,
html.perf-lite .plan-card{will-change:auto!important;}

/* Looping decorative animation: an infinite keyframe keeps the page repainting
   whether or not anyone is looking. All of it is garnish — the elements are
   identical at rest, just frozen at their resting frame. */
html.perf-lite .grad,
html.perf-lite .gold-fx,
html.perf-lite .boot-sun-mark,
html.perf-lite .boot-sun-halo,
html.perf-lite .route-sun-mark,
html.perf-lite .gold-stars,
html.perf-lite .trust-dot,
html.perf-lite .skel,
html.perf-lite .hero-scroll-line::after,
html.perf-lite .xp-bar i::after{animation:none!important;}
/* (.mq-track is deliberately NOT in that list. It is a single compositor
   transform, free on the main thread, and it is the band that makes the page
   feel alive — the point of lite mode is to drop cost, not character.) */
/* (.btn.is-busy::after is deliberately NOT in that list: it is the spinner
   that says a request is in flight, so it is information, not decoration.) */

/* No GSAP means no intro timeline, and the intro is what lifts the headline
   out of its line masks and then releases them. Nothing is masking while
   nothing is moving, so the resting headline gets its descenders back -
   without this the "y" in "your" is clipped by the mask. */
html.perf-lite .hero-title .ht-line{overflow:visible;padding-bottom:0;margin-bottom:0;}

/* GSAP is not loaded in lite mode, so the view swap has to be CSS again —
   design rule 11 disabled the keyframe because GSAP used to own it. */
html.perf-lite .view.active{animation:fadeIn .16s ease;}
/* Entrances stay (they carry the "content arrived" meaning), just quicker and
   with a shorter delay ladder. */
html.perf-lite .reveal,
html.perf-lite.js .reveal{transition-duration:.34s;transition-delay:0ms;}

/* The scroll-progress thread is a transform on one 2px element: cheap, keep
   it, but it does not need its own layer hint. */
html.perf-lite .scroll-progress i{will-change:auto;}

/* An OS-level "reduce motion" beats lite mode's small fade as well - the two
   settings are independent, and the stricter one always wins. */
@media (prefers-reduced-motion:reduce){
  html.perf-lite .view.active{animation:none;}
}
