/* ============================================================
   Ascent · The DeFi Residency
   Visual spec from Figma (Xperimenting for X · node 1225:27918):
   mesh-gradient shader panels (js/mesh.js), Coanda throughout,
   "// " section labels, ghost Apply pills, hairline access table,
   windowed game panel, glow footer.
   Motion: transform/opacity only, strong ease-out.
   ============================================================ */

/* Coanda, licensed from CoType Foundry (order #4892) — Website licence,
   Regular + Bold, 10k monthly page views, ascentresidency.com and its
   subdomains. WOFF2 first; WOFF is the fallback the foundry ships for
   older engines. Licence text in licenses/, never deployed. */
@font-face {
  font-family: "Coanda";
  src: url("../assets/fonts/coanda-regular.woff2") format("woff2"),
       url("../assets/fonts/coanda-regular.woff") format("woff");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Coanda";
  src: url("../assets/fonts/coanda-bold.woff2") format("woff2"),
       url("../assets/fonts/coanda-bold.woff") format("woff");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

:root {
  --bg: #010101;            /* page background, straight from the Figma frame */
  --ink: #efedea;
  --muted: #8a8a8f;
  --rule: rgba(239, 237, 234, 0.14);
  --chip-bg: #16151b;
  --chip-line: #37373c;
  --radius-pill: 99px;
  /* accents taken from the mesh palette, so focus and selection sit in the
     same blue family as the panels */
  --accent: #2726a5;
  --accent-hi: #7d86ff;
  --panel-inset: 20px;    /* uniform gutter: 1472x910 panel in a 1512x950 frame.
                             Measured off frame1-full.jpg — the top and side gaps
                             are equal (7 vs 8 screen px at 3.44 design px each).
                             The 8s in Figma's Dev Mode panel are the corner radii. */
  --panel-radius: 8px;
  --font-display: "Coanda", "Handjet", monospace;
  --font-body: "Space Grotesk", system-ui, -apple-system, sans-serif;
  --ease-out: cubic-bezier(0.23, 1, 0.32, 1);
  --ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);
  --ease-in: cubic-bezier(0.4, 0, 1, 1);
  /* Static stand-ins for the mesh shader — visible only until WebGL takes
     over, and permanently where WebGL is unavailable. Control colours are
     the corners of the shader's 4x4 grid. */
  --mesh-fallback:
    radial-gradient(90% 70% at 72% 18%, #585ed3 0%, rgba(88, 94, 211, 0) 62%),
    radial-gradient(80% 70% at 30% 40%, #2726a5 0%, rgba(39, 38, 165, 0) 68%),
    linear-gradient(150deg, #0b255d 0%, #181678 52%, #151976 100%);
}

* { box-sizing: border-box; }

html {
  scroll-behavior: smooth;
  scroll-snap-type: y proximity;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3 { margin: 0; font-weight: 400; }
p { margin: 0; }
a { color: inherit; }
img { max-width: 100%; }

::selection { background: var(--accent); color: #fff; }

:focus-visible {
  outline: 2px solid var(--accent-hi);
  outline-offset: 2px;
}

.snap {
  min-height: 100svh;
  scroll-snap-align: start;
}

/* ---------- Mesh gradient shader surface ----------
   The canvas carries the Figma "Mesh gradient" fill (js/mesh.js). The CSS
   gradient underneath shows through only until the first WebGL frame, and
   stays put on machines with no WebGL2. */

.mesh-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  border-radius: inherit;
  opacity: 0;
  transition: opacity 420ms var(--ease-out);
}
.mesh-canvas.mesh-ready { opacity: 1; }
.mesh-canvas.mesh-unsupported { display: none; }

/* ---------- Film grain ----------
   One fixed emulsion layer over the whole page, so the black sections carry
   the same texture as the blue panels. The shader adds its own finer grain
   on top of the gradients. */

.grain {
  position: fixed;
  inset: -150%;
  z-index: 50;
  pointer-events: none;
  opacity: 0.42;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.82' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)' opacity='0.55'/%3E%3C/svg%3E");
  will-change: transform;
  animation: grain-shift 900ms steps(1, end) infinite;
}
@keyframes grain-shift {
  0%   { transform: translate(0, 0); }
  12%  { transform: translate(-6%, 3%); }
  25%  { transform: translate(4%, -5%); }
  37%  { transform: translate(-3%, -4%); }
  50%  { transform: translate(5%, 4%); }
  62%  { transform: translate(-5%, 2%); }
  75%  { transform: translate(3%, 5%); }
  87%  { transform: translate(-4%, -3%); }
  100% { transform: translate(0, 0); }
}

/* ---------- Shared bits ---------- */

/* Label is full ink; the // marker sits at ~0.32 against it, matching
   the design (a single rgba on the whole string made the slashes as
   loud as the words). */
.slabel {
  font-family: var(--font-display);
  font-size: clamp(18px, 1.6vw, 24px);
  color: var(--ink);
}
.slabel::before {
  content: "// ";
  opacity: 0.32;
}

.chips {
  display: flex;
  gap: 10px;
  justify-content: center;
}
.chip {
  font-family: var(--font-display);
  font-size: clamp(13px, 1.05vw, 15px);
  line-height: 1;
  color: rgba(239, 237, 234, 0.92);
  background: transparent;
  border: 1px solid rgba(239, 237, 234, 0.34);
  border-radius: var(--radius-pill);
  padding: 10px 20px 11px;
}

/* Every Apply in the design is the same ghost pill: hairline border,
   transparent fill, light Coanda label. Measured off design-refs/access.png:
   125x42 at a 1512 frame, with the "Apply" label 48px wide — hence 17.5px
   type and 37px side padding. min-height keeps it a 44px touch target. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(15px, 1.16vw, 17.5px);
  letter-spacing: 0;
  line-height: 1;
  text-decoration: none;
  border-radius: var(--radius-pill);
  padding: 12px 37px 13px;
  color: rgba(239, 237, 234, 0.92);
  background: transparent;
  border: 1px solid rgba(239, 237, 234, 0.34);
  transition:
    transform 220ms var(--ease-out),
    background-color 260ms var(--ease-in),
    color 220ms var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  /* Stroke holds; the state reads through a white wash instead. */
  .btn:hover {
    transform: translateY(-1px);
    color: #fff;
    background: rgba(255, 255, 255, 0.14);
  }
}
.btn:active { transform: translateY(0) scale(0.975); }

/* ---------- Hero ----------
   In the design the hero is an inset panel: 1472x910 inside a 1512 frame,
   i.e. a 20px gutter with an 8px corner radius, sitting on the page black. */

.hero {
  padding: var(--panel-inset);
  background: var(--bg);
}
.hero-panel {
  position: relative;
  isolation: isolate;
  height: calc(100svh - var(--panel-inset) * 2);
  min-height: 520px;
  border-radius: var(--panel-radius);
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;   /* Ascent 03 centres the stack in the panel:
                                Frame 2147232298 sits at x486 y173.5, 500x562
                                in 1472x910 - dead centre on both axes. */
  padding: 24px;
  background: var(--mesh-fallback);
}
/* the design's dashed-ellipse texture, exported from Figma — a whisper
   over the mesh, not a sunburst */
.hero-ellipse {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0.16;
  mix-blend-mode: screen;   /* the rays are light-blue and only ever add light;
                               overlay collapsed them against the saturated field */
  /* The shader's top scrim lives underneath this layer, so without a matching
     mask the rays escape it and read as a bold sunburst across the darkened
     top edge. This mirrors topScrim (0.60 → 1.0 by 34%) in js/mesh.js. */
  -webkit-mask-image: linear-gradient(to bottom, rgba(0,0,0,0.6) 0%, #000 34%);
  mask-image: linear-gradient(to bottom, rgba(0,0,0,0.6) 0%, #000 34%);
  pointer-events: none;
}

/* Wordmark, top centre. Measured off the frame: ~118px wide with its top
   edge ~60px down the panel, so it clears the chips rather than sitting on
   their line. Centred with auto margins instead of a translate, so it does
   not fight the intro's translateY. */
.hero-logo {
  position: absolute;
  top: 56px;
  left: 0;
  right: 0;
  z-index: 2;
  display: block;
  width: clamp(92px, 7.8vw, 118px);
  height: auto;
  margin-inline: auto;
}

.hero-center {
  /* position:relative with no z-index. Source order already paints this
     above the absolutely-positioned mesh canvas, so the z-index it used to
     carry bought nothing. */
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 22px;                  /* the design stack's auto-layout gap */
  width: min(500px, 100%);    /* and its width */
  /* No perspective. It was left over from the old image lockup and nothing
     uses a 3D transform any more. */
}

/* The particle scene moved into the hero, directly above the headline.
   Stack maths: 562 = 312 (scene) + 22 + 165 (headline) + 22 + 41 (Apply),
   so the scene is 500x312. Same screen/key treatment as the statement
   copy - the filter and the blend stay on different elements, or the
   blend isolates and Unicorn's lifted black comes back. */
.hero-scene {
  /* Zoom for the particle mark. The scene draws itself to fill its canvas,
     so the mark's size is the canvas's size. Raise this one number to make
     the logo bigger. */
  --scene-zoom: 1.6;
  position: relative;
  width: 100%;
  aspect-ratio: 500 / 312;
  mix-blend-mode: screen;
  pointer-events: none;
}
/* The canvas is grown past its layout box and re-centred on it, rather than
   the box being grown. The box keeps the design's 500x312, so the headline,
   Apply and countdown below it do not move by a pixel. Growing the canvas
   rather than CSS-scaling it also means the mark is redrawn at the larger
   size instead of being resampled soft. */
/* No SVG filter here. It used to key Unicorn's opaque ~#0e0e0e clear colour
   to transparent, but iOS Safari does not reliably apply filter: url(#...)
   to an element containing a WebGL canvas - it left the canvas opaque, and
   since it is grown to 160% that black rectangle cut the panel into bands.
   `screen` on the wrapper already handles a near-black floor: screening
   #0e0e0e lifts by about 5%, which is invisible against the mesh. */
.hero-scene .scene-canvas {
  position: absolute;
  width: calc(var(--scene-zoom) * 100%);
  height: calc(var(--scene-zoom) * 100%);
  left: calc((100% - var(--scene-zoom) * 100%) / 2);
  top: calc((100% - var(--scene-zoom) * 100%) / 2);
  /* No filter. The scene used to clear to an opaque ~#0e0e0e, which needed
     an SVG luminance-to-alpha filter to key out - and iOS Safari does not
     apply filter: url(#...) over a WebGL canvas, so the mark showed as a
     black box on iPhone. The Unicorn scene now publishes with a transparent
     background, so the canvas composites correctly on its own and the
     filter is gone. Do not reintroduce it. */
  mix-blend-mode: normal;
}

/* Live text now, not the defi-residency.svg lockup.
   Coanda Regular 44px / 124% / 0 tracking; 2.91vw hits 44px at 1512. */
.hero-headline {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(26px, 2.91vw, 44px);
  line-height: 1.24;
  letter-spacing: 0;
  text-align: center;
  color: var(--ink);
}

/* Chips: top right, stacked. Frame 2147232249 is x1253 y16, 203x66 hug in a
   1472-wide panel - 16px off both edges, 8px between the two. */
.chips-hero {
  position: absolute;
  top: 16px;
  right: 16px;
  z-index: 2;
  flex-direction: column;
  align-items: flex-end;
  gap: 8px;
}

/* Coanda Bold 18px, centred, baseline 42px off the panel's bottom edge
   (design text is x595 y846, 281x22 in 1472x910). */
.hero-countdown {
  position: absolute;
  left: 24px;
  right: 24px;
  bottom: 42px;
  z-index: 2;
  text-align: center;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(14px, 1.19vw, 18px);
  color: rgba(239, 237, 234, 0.62);
}
.countdown-value { font-variant-numeric: tabular-nums; }

/* ---------- Ecosystem partners: hairline grid ---------- */

.partners {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  /* 8.6vw resolves to the design's 130px gutter at 1512 (1512 - 1252 = 260,
     halved), so desktop is unchanged - the inner is already capped at 1252 -
     while narrow screens finally get a margin instead of running the grid
     edge to edge. */
  padding: clamp(48px, 8vh, 96px) clamp(20px, 8.6vw, 130px);
  overflow: hidden;
  background: var(--bg);
}

/* The design's striped-arc layer, exported from Figma. It is authored
   1512x820 and reads as two corner arcs — `contain` keeps the arcs circular
   instead of stretching them into ovals the way `cover` did. */
.access-bg {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 100%;
  min-width: 1100px;
  height: auto;
  transform: translate(-50%, -50%);
  opacity: 0.9;
  pointer-events: none;
  /* Keeps the arcs turning without breaking the frame. The two arcs sit
     ~700-800px out from the image centre, so a full revolution would swing
     them past the 820px-tall artwork and leave the section bare for part of
     the cycle. 4.8deg of travel is ~63px of arc at that radius — plainly
     moving, never enough to restage the composition.
     ease-in-out so it eases at the turns instead of ticking back. */
  animation: access-ring-drift 48s var(--ease-in-out) infinite;
  will-change: transform;
}
@keyframes access-ring-drift {
  0%, 100% { transform: translate(-50%, -50%) rotate(-2.4deg); }
  50%      { transform: translate(-50%, -50%) rotate(2.4deg); }
}

.partners-inner {
  position: relative;
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 0;            /* header sits straight on the grid, sharing one rule */
  align-items: center;
}

/* 1252-wide column in a 1512 section = 130px margins; header 110 tall,
   grid rows 110 tall, five across.
   Real dashed borders on all four sides of every cell, not the hand-rolled
   repeating-gradient rules the old access table used. Cells pull themselves
   1px right and down so each shared edge is ONE dashed line rather than two
   stacked, and a short final row simply stops instead of leaving a stray
   container edge running across the empty space. */
.partners-inner { max-width: 1252px; }
.phead {
  display: flex;
  align-items: center;
  justify-content: center;
  height: clamp(74px, 12.2svh, 110px);
  width: 100%;
  border: 1px dashed #2c2c2c;
  margin-bottom: -1px;
}
.pgrid {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  width: 100%;
}
.pcell {
  display: flex;
  align-items: center;
  justify-content: center;
  height: clamp(74px, 12.2svh, 110px);
  border: 1px dashed #2c2c2c;
  margin: 0 -1px -1px 0;
}
/* 18 cells over 5 columns leaves 3 on the last row; the frame centres them
   under columns 2-4 rather than letting them sit left. */
.pcell:nth-child(16) { grid-column-start: 2; }
.plogo {
  font-family: var(--font-display);
  font-size: clamp(14px, 1.32vw, 20px);
  color: var(--muted);
  white-space: nowrap;
}
@media (hover: hover) and (pointer: fine) {
  .plogo { transition: color 200ms var(--ease-out); }
  .pcell:hover .plogo { color: var(--ink); }
}

/* ---------- Leave with (pinned blue step-scroller) ----------
   The section is several viewports tall; the panel pins while scroll
   progress steps the highlight through the four points, then the next
   section rides up over it. */

.leave {
  height: 320svh;
}
.leave-sticky {
  position: sticky;
  top: 0;
  height: 100svh;
  display: flex;
  overflow: hidden;
  isolation: isolate;
  background: var(--mesh-fallback);
}
/* Label left, list right, both on the vertical centre line, pushed apart.
   The 180px side margin is measured off the design frame (label left edge
   at 128px in a 1080-wide capture, x1.4 to the 1512 design = 179px).
   11.9vw hits 180px at 1512 and eases down on narrower screens.
   Vertical padding is symmetric so `center` is the true centre. */
.leave-inner {
  position: relative;
  flex: 1;
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
  gap: clamp(32px, 6vw, 96px);
  padding: clamp(40px, 8vh, 88px) clamp(24px, 11.9vw, 180px);
  /* Stop at the design frame. The side padding caps at 180px but the
     viewport does not, so past 1512 the pair drifted apart unbounded —
     875px of empty mesh at 1920, reading as two disconnected clusters
     instead of one row. */
  max-width: 1512px;
  margin-inline: auto;
  width: 100%;
}
/* the label is two short words — never let the row squeeze it into a wrap */
.leave-inner .slabel { white-space: nowrap; }
.leave-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: clamp(10px, 1.6vh, 18px);
  max-width: 40em;
}
/* The design sets the inactive lines in a low-opacity fill, NOT an outline.
   `-webkit-text-stroke` produced hollow, ragged glyphs that fought the
   Coanda letterforms; --lit is driven per item from the scroll pin. */
.leave-list li {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(18px, 1.85vw, 28px);   /* 1.85vw lands on 28px at the 1512 design width */
  line-height: 1.45;
  color: rgba(239, 237, 234, calc(0.3 + 0.7 * var(--lit, 0)));
  padding-left: 16px;
  margin-left: -18px;
  border-left: 2px solid rgba(239, 237, 234, calc(var(--lit, 0) * 0.9));
  transform: translateX(calc(var(--lit, 0) * 2px));
  will-change: color, transform;
}
/* the section after rides up over the pinned panel */
.faq {
  position: relative;
  background: var(--bg);
}

/* ---------- Common questions + game ---------- */

.faq {
  display: grid;
  /* design: ~103px page margin at 1512, game window ~36% of the
     content row (shipped 76px / 42% read as a squeezed FAQ + a
     panel ~21% too wide). Same inset on all four sides. */
  grid-template-columns: minmax(0, 1fr) minmax(0, 36%);
  gap: clamp(32px, 5vw, 72px);
  align-items: stretch;
  min-height: 100svh;
  padding: clamp(48px, 6.8vw, 104px);
  max-width: 1512px;
  margin: 0 auto;
}

/* Top-anchored, not space-between. Pinned to the bottom, the list grew
   UPWARDS as an answer opened, so the row you clicked slid out from under
   the cursor and the minus was no longer where the plus had been. Anchored
   at the top it only ever extends downwards and the clicked row holds
   still. This also closes the dead band that space-between opened up
   between the label and the first question on tall screens. */
.faq-col {
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  /* Big, but FIXED. space-between used to open this gap by filling the
     leftover height, which is why it also dragged the list around whenever
     an answer opened. A viewport-proportional gap keeps the same airy look
     while staying constant, so rows never move. */
  gap: clamp(40px, 34vh, 360px);
  min-width: 0;
}

.faq-list { display: flex; flex-direction: column; }
/* the padding lives on summary, not details, so the clickable box is a
   full-height touch target rather than just the line box */
.faq-list details { padding: 0; }
.faq-list summary {
  cursor: pointer;
  list-style: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  padding: 11px 0;
  min-height: 44px;
  box-sizing: border-box;
  font-family: var(--font-display);
  font-size: clamp(16px, 1.45vw, 20px);
  color: var(--ink);
  transition: color 150ms var(--ease-out);
}
.faq-list summary::-webkit-details-marker { display: none; }
.faq-list summary::after {
  content: "+";
  font-family: var(--font-display);
  font-size: clamp(26px, 2.4vw, 32px);
  font-weight: 400;
  line-height: 0.8;
  width: 1em;
  text-align: center;
  color: var(--muted);
  flex-shrink: 0;
}
.faq-list details[open] summary::after {
  content: "–";
  font-size: clamp(30px, 2.8vw, 36px);
  line-height: 0.65;
}
/* With one answer open, the closed questions step back so the open row
   reads as the focus. :has means no JS and no state to keep in sync. */
.faq-list:has(details[open]) details:not([open]) summary { color: rgba(239, 237, 234, 0.45); }
.faq-list:has(details[open]) details:not([open]) summary::after { color: rgba(239, 237, 234, 0.28); }
@media (hover: hover) and (pointer: fine) {
  .faq-list summary:hover { color: #fff; }
  .faq-list:has(details[open]) details:not([open]) summary:hover { color: var(--ink); }
}
/* Was a flat 14px, which left the desktop answers SMALLER than the 16px
   the mobile block sets - the wrong way round. Scales now, and keeps a
   clear step below the question. */
.faq-list details p {
  padding: 6px 0 4px;
  color: var(--muted);
  font-family: var(--font-display);
  font-size: clamp(15px, 1.22vw, 17.5px);
  line-height: 1.55;
  max-width: 34em;
}

/* the game as a graphite handheld — Pocket/Color proportions, not a
   beige 1989 brick. The shell is CSS (so the screen and pad stay real
   HTML); lighting is vector gradients, so it scales clean. */
.game-col {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 14px;
  min-width: 0;
  align-items: center;
}
.gb {
  --gb-hi: #2c2c34;
  --gb-mid: #1a1a20;
  --gb-lo: #101014;
  --gb-ink: #0b0b0e;
  width: 100%;
  max-width: 360px;
}
.gb-body {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 3%;
  aspect-ratio: 360 / 593;
  padding: 4.5% 4% 5%;
  border-radius: 12% 12% 16% 16% / 6% 6% 8% 8%;
  background:
    radial-gradient(120% 70% at 18% -4%, rgba(255, 255, 255, 0.14), transparent 46%),
    radial-gradient(90% 50% at 86% 108%, rgba(0, 0, 0, 0.45), transparent 52%),
    linear-gradient(165deg, var(--gb-hi) 0%, var(--gb-mid) 46%, var(--gb-lo) 100%);
  box-shadow:
    inset 1px 1px 0 rgba(255, 255, 255, 0.16),
    inset -2px -3px 0 rgba(0, 0, 0, 0.5),
    inset 0 0 0 1px rgba(255, 255, 255, 0.04),
    0 1px 0 #070709,
    0 22px 48px rgba(0, 0, 0, 0.48);
}
.gb-slot {
  position: absolute;
  top: 0;
  left: 22%;
  right: 22%;
  height: 5px;
  border-radius: 0 0 3px 3px;
  background: linear-gradient(#070709, #16161a);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06);
}

/* recessed screen housing */
.gb-bezel {
  position: relative;
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  padding: 1.5%;
  border-radius: 6% / 5%;
  background:
    linear-gradient(180deg, #0d0d12 0%, #14141a 100%);
  box-shadow:
    inset 0 2px 3px rgba(0, 0, 0, 0.65),
    inset 0 -1px 0 rgba(255, 255, 255, 0.06),
    0 1px 0 rgba(255, 255, 255, 0.05);
}
.gb-led {
  position: absolute;
  top: 3%;
  left: 3%;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: #3ddc68;
  box-shadow: 0 0 6px rgba(61, 220, 104, 0.7);
}
.gb-foot {
  display: block;
  width: 16px;
  height: auto;
  margin: 2% auto 0;
  padding: 4px 5px 3px;
  border-radius: 5px;
  background: linear-gradient(165deg, #26262e, #121218);
  box-shadow:
    inset 1px 1px 0 rgba(255, 255, 255, 0.14),
    inset -1px -1px 0 rgba(0, 0, 0, 0.55),
    0 1px 0 rgba(255, 255, 255, 0.04);
  opacity: 0.75;
}
.tetris-panel {
  position: relative;
  flex: 1 1 auto;
  min-height: 300px;
  overflow: hidden;
  background: #050506;
  border-radius: 3px;
  box-shadow:
    inset 0 0 0 1px rgba(239, 237, 234, 0.06),
    inset 0 10px 18px rgba(0, 0, 0, 0.35);
}
.tetris-panel::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: repeating-linear-gradient(
    0deg,
    rgba(255, 255, 255, 0.028) 0 1px,
    transparent 1px 3px
  );
}

/* D-pad, A/B, start/select, speaker */
.gb-deck {
  position: relative;
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-areas:
    "pad ab"
    "pad speaker";
  align-items: center;
  flex: 0 0 auto;
  min-height: 118px;
  padding: 2% 2% 5% 2%;
}
.gb-pad {
  grid-area: pad;
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  grid-template-rows: 1fr 1fr 1fr;
  width: clamp(112px, 34%, 132px);
  aspect-ratio: 1;
  justify-self: start;
}
.gb-up    { grid-area: 1 / 2; border-radius: 5px 5px 1px 1px; }
.gb-left  { grid-area: 2 / 1; border-radius: 5px 1px 1px 5px; }
.gb-right { grid-area: 2 / 3; border-radius: 1px 5px 5px 1px; }
.gb-down  { grid-area: 3 / 2; border-radius: 1px 1px 5px 5px; }
.gb-pad::after {
  content: "";
  grid-area: 2 / 2;
  background: linear-gradient(160deg, #2a2a32 0%, #141418 100%);
  pointer-events: none;
}
.gb-arm {
  position: relative;
  border: 0;
  padding: 0;
  cursor: pointer;
  color: transparent;
  background: linear-gradient(160deg, #2a2a32 0%, #141418 100%);
  box-shadow:
    inset 1px 1px 0 rgba(255, 255, 255, 0.16),
    inset -1px -1px 0 rgba(0, 0, 0, 0.55),
    0 2px 0 #070709;
  -webkit-tap-highlight-color: transparent;
}
/* A small halo only. At -8px the diagonally adjacent arms overlapped in an
   8x8 corner square, so a thumb near a diagonal hit whichever arm happened to
   paint last - the "hitting the area around it" problem. The arms themselves
   are now big enough not to need the padding. */
.gb-arm::before {
  content: "";
  position: absolute;
  inset: -2px;
}
.gb-arm:active {
  transform: translateY(1px);
  box-shadow:
    inset 1px 1px 2px rgba(0, 0, 0, 0.6),
    0 0 0 #070709;
}
.gb-ab {
  grid-area: ab;
  display: flex;
  flex-direction: row;
  justify-content: flex-end;
  align-items: center;
  gap: 12px;
  justify-self: end;
}
.gb-face {
  position: relative;
  width: clamp(44px, 13vw, 54px);
  aspect-ratio: 1;
  border: 0;
  padding: 0;
  border-radius: 50%;
  cursor: pointer;
  background: radial-gradient(circle at 35% 30%, #3a3a44, #1a1a20 62%);
  box-shadow:
    inset 1px 1px 0 rgba(255, 255, 255, 0.18),
    inset -2px -2px 0 rgba(0, 0, 0, 0.45),
    0 3px 0 #070709;
  color: rgba(239, 237, 234, 0.72);
  font-family: var(--font-display);
  font-size: 12px;          /* also clears the 12px legibility floor */
  letter-spacing: 0.02em;
  -webkit-tap-highlight-color: transparent;
}
.gb-face span { display: block; }
.gb-a {
  background: radial-gradient(circle at 35% 30%, #3d3d52, #1c1c2a 62%);
}
.gb-face:active {
  transform: translateY(2px);
  box-shadow:
    inset 1px 1px 2px rgba(0, 0, 0, 0.55),
    0 0 0 #070709;
}

.gb-speaker {
  grid-area: speaker;
  justify-self: end;
  align-self: end;
  width: 72%;
  height: 22px;
  margin-top: 8px;
  background: repeating-linear-gradient(
    -28deg,
    transparent 0 5px,
    rgba(0, 0, 0, 0.55) 5px 7px,
    rgba(255, 255, 255, 0.04) 7px 8px
  );
  mask-image: linear-gradient(90deg, transparent, #000 14%, #000 86%, transparent);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 14%, #000 86%, transparent);
  pointer-events: none;
}

#tetris {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  touch-action: none;
  cursor: pointer;
}
#tetris:focus-visible { outline: none; }

.tetris-hud {
  position: absolute;
  top: 14px;
  right: 16px;
  font-family: var(--font-display);
  font-size: 14px;
  color: rgba(239, 237, 234, 0.6);
  pointer-events: none;
}

.game-caption {
  font-family: var(--font-display);
  font-size: 13.5px;
  color: var(--muted);
  text-align: center;
}

/* ---------- Overlay ---------- */

.tetris-overlay {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  width: 100%;
  border: none;
  cursor: pointer;
  background: transparent;
  color: var(--ink);
  font-family: var(--font-display);
  transition: opacity 220ms var(--ease-out);
}
.pause-bars {
  width: 18px;
  height: 22px;
  border-left: 5px solid rgba(239, 237, 234, 0.9);
  border-right: 5px solid rgba(239, 237, 234, 0.9);
}
.tetris-overlay.hidden {
  opacity: 0;
  pointer-events: none;
}
.tetris-overlay-title {
  font-weight: 400;
  font-size: clamp(17px, 1.6vw, 22px);
  text-shadow: 0 1px 8px rgba(11, 10, 16, 0.8);
}
.tetris-overlay-sub {
  font-size: 12.5px;
  color: var(--muted);
  text-shadow: 0 1px 8px rgba(11, 10, 16, 0.8);
}

/* ---------- Footer ---------- */

.scene-footer {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  background: #04040a;
  display: flex;
  flex-direction: column;
}
/* The mesh sits underneath, dimmed right down, so the wordmark glow has a
   deep blue field to bloom out of rather than flat black. It is masked at the
   top edge so it rises out of the black FAQ section above instead of meeting
   it at a hard seam. */
.mesh-footer {
  z-index: 0;
  -webkit-mask-image: linear-gradient(to bottom, transparent 0%, #000 34%);
  mask-image: linear-gradient(to bottom, transparent 0%, #000 34%);
}
.chips-footer {
  position: relative;
  z-index: 2;
  padding-top: clamp(36px, 7vh, 64px);
}
.scene-canvas {
  position: absolute;
  inset: 0;
  z-index: 1;
  width: 100%;
  height: 100%;
  min-height: 100%;
  mix-blend-mode: screen;
}
/* The overlay spans the whole footer so both children can be placed against
   the wordmark, which is a Unicorn canvas at inset:0 — CSS has no idea how
   big the rendered glyphs are, so the only stable anchor is a percentage of
   the footer. Measured off the design frame: wordmark centre 50.6%, Apply
   centre 61.7%, credit bar baseline 93.4%.
   pointer-events is handed back to the two interactive children only, so the
   overlay does not blanket the scene underneath it. */
.footer-overlay {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
}
.footer-apply {
  position: absolute;
  top: calc(59% + 13px); /* design puts the pill centre at 61.7%; the 13px
                            is a hand nudge on top of that */
  left: 0;
  right: 0;
  display: flex;
  justify-content: center;
  pointer-events: auto;
}
/* Twitter · By Superteam India · Instagram, spread the full width.
   Plain space-between, which leaves the wordmark a few px left of true
   centre because "Twitter" is narrower than "Instagram" — the design
   frame sits off-centre by the same amount, so this is not a bug. */
.footer-bar {
  position: absolute;
  left: clamp(24px, 2.3vw, 36px);
  right: clamp(24px, 2.3vw, 36px);
  bottom: clamp(24px, 4.1vh, 37px);   /* link box is 44px tall, so this centres the type on the design's 93.4% */
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  pointer-events: auto;
}
.footer-line {
  display: block;
  width: min(257px, 72vw);
  height: auto;
  /* the bar re-enables pointer events for its links; without this the
     decorative wordmark inherits them and becomes an opaque hit region
     that swallows clicks meant for the Apply pill behind it */
  pointer-events: none;
}
.footer-social-link {
  display: inline-flex;
  align-items: center;
  min-height: 44px;          /* touch target; the type itself is only ~20px */
  font-family: var(--font-display);
  font-size: clamp(15px, 1.5vw, 20px);
  color: var(--muted);
  text-decoration: none;
  white-space: nowrap;
  transition: color 180ms var(--ease-out), transform 180ms var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .footer-social-link:hover {
    color: #fff;
    transform: translateY(-1px);
  }
}

/* ---------- Intro choreography (JS swaps body.intro → body.intro-go) ---------- */

body.intro .hero-logo { opacity: 0; transform: translateY(-10px); }
body.intro .hero-countdown { opacity: 0; transform: translateY(10px); }
body.intro .hero-headline { opacity: 0; transform: translateY(22px) scale(0.985); }
body.intro .hero-center .btn { opacity: 0; transform: translateY(14px); }
body.intro .chips-hero { opacity: 0; transform: translateY(12px); }

body.intro-go .hero-headline {
  opacity: 1; transform: none;
  transition: opacity 700ms var(--ease-out) 60ms, transform 700ms var(--ease-out) 60ms;
}
body.intro-go .hero-center .btn {
  opacity: 1; transform: none;
  transition: opacity 600ms var(--ease-out) 280ms, transform 600ms var(--ease-out) 280ms;
}
body.intro-go .hero-logo {
  opacity: 1; transform: none;
  transition: opacity 600ms var(--ease-out) 420ms, transform 600ms var(--ease-out) 420ms;
}
body.intro-go .hero-countdown {
  opacity: 1; transform: none;
  transition: opacity 600ms var(--ease-out) 520ms, transform 600ms var(--ease-out) 520ms;
}
body.intro-go .chips-hero {
  opacity: 1; transform: none;
  transition: opacity 600ms var(--ease-out) 520ms, transform 600ms var(--ease-out) 520ms;
}

/* ---------- Reveal on scroll (re-arms on exit; staggers children) ---------- */

.reveal {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity 500ms var(--ease-out), transform 500ms var(--ease-out);
}
.reveal.visible { opacity: 1; transform: none; }

.reveal :is(.pcell, .leave-list li, .faq-list details) {
  transition: opacity 450ms var(--ease-out), transform 450ms var(--ease-out);
}
.reveal:not(.visible) :is(.pcell, .leave-list li, .faq-list details) {
  opacity: 0;
  transform: translateY(10px);
}
.reveal.visible :is(.pcell, .leave-list li, .faq-list details) { opacity: 1; }
/* once the list is in, kill the reveal transition so --lit can snap */
.reveal.visible .leave-list li { transition: none; }
.reveal.visible :is(.pcell, .leave-list li, .faq-list details):nth-child(2) { transition-delay: 50ms; }
.reveal.visible :is(.pcell, .leave-list li, .faq-list details):nth-child(3) { transition-delay: 100ms; }
.reveal.visible :is(.pcell, .leave-list li, .faq-list details):nth-child(4) { transition-delay: 150ms; }
.reveal.visible :is(.pcell, .leave-list li, .faq-list details):nth-child(5) { transition-delay: 200ms; }
.reveal.visible :is(.pcell, .leave-list li, .faq-list details):nth-child(6) { transition-delay: 250ms; }
.reveal.visible :is(.pcell, .leave-list li, .faq-list details):nth-child(7) { transition-delay: 300ms; }
.reveal.visible :is(.pcell, .leave-list li, .faq-list details):nth-child(8) { transition-delay: 350ms; }

/* ---------- Responsive ---------- */

@media (max-width: 900px) {
  .faq {
    grid-template-columns: 1fr;
    align-items: start;
  }
  /* the tall desktop gap is dead space on a phone */
  .faq-col { gap: clamp(28px, 7vh, 64px); }
  /* five across does not fit a phone; two columns keeps the wordmarks legible */
  .pgrid { grid-template-columns: repeat(2, 1fr); }
  .plogo { font-size: 16px; }
  .leave {
    height: auto;
  }
  .leave-sticky {
    position: relative;
    height: auto;
    min-height: 100svh;
  }
  /* the side-by-side row needs more width than this has — go back to a stack */
  .leave-inner {
    flex-direction: column;
    align-items: flex-start;
    justify-content: center;
    gap: clamp(24px, 5vh, 40px);
    padding: clamp(48px, 9vh, 96px) clamp(24px, 6vw, 96px);
  }
  .leave-list { align-self: stretch; }
  .leave-list li { font-size: clamp(18px, 4.8vw, 22px); }
  .faq-list summary { font-size: 17px; }
  .faq-list details p { font-size: 16px; }
  .access-table .cell { font-size: 18px; }
  .slabel { font-size: clamp(20px, 5vw, 24px); }
}

/* Three across stops fitting well before the phone breakpoint: drop the
   wordmark onto its own centred row and keep the two links side by side. */
@media (max-width: 700px) {
  .footer-bar {
    flex-wrap: wrap;
    justify-content: center;
    gap: 14px 28px;
  }
  .footer-line { order: -1; }
  /* Break the line AFTER the wordmark with a zero-height spacer rather than
     giving the image flex-basis:100% — that sets the image's main size, so
     the 257px mark stretched to the full bar width (652px at the 700px
     breakpoint) and out-shouted the Ascent logo above it. The spacer shares
     order:-1 and follows in document order, so it lands between the mark
     and the two links. */
  .footer-bar::after {
    content: "";
    order: -1;
    flex-basis: 100%;
    height: 0;
  }
}

/* Phone landscape. Anchoring Apply to 59% of the height only works while
   there IS height: at 667x375 the pill lands on top of the credit bar and
   the overlap kills the CTA. Below this the footer goes back to normal
   flow, where the two simply stack and can never collide. */
@media (max-height: 500px) {
  .footer-overlay {
    position: relative;
    inset: auto;
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: flex-end;
    gap: 18px;
    padding: 0 clamp(24px, 2.3vw, 36px) clamp(24px, 4.1vh, 37px);
    pointer-events: auto;
  }
  .footer-apply,
  .footer-bar {
    position: static;
    inset: auto;
    width: 100%;
  }
}

/* Narrow: a centred wordmark and a right-stacked chip column fight for the
   same band - at 390 the mark runs straight through "Zo House, Bengaluru".
   Send the wordmark to the left edge so the two share the row instead. */
@media (max-width: 700px) {
  .hero-logo {
    left: 16px;
    right: auto;
    margin-inline: 0;
    top: 18px;
  }
}

@media (max-width: 560px) {
  /* keep the chips clear of the sound toggle — the room has to come from
     INSIDE the panel, or the section grows past one screen and leaves a
     black seam under the panel's rounded bottom edge */
  .hero-panel { padding-bottom: 64px; }
  .hero-headline { font-size: clamp(26px, 6.6vw, 34px); }
  .chips-hero { top: 12px; right: 12px; }
  .gb { max-width: 300px; }
  .partners { padding-bottom: 72px; }   /* breathing room under the last row */
  .faq { padding-bottom: 72px; }
  /* single column here, so nine stacked rows at desktop height is a lot of
     scroll on a short screen */
  .pcell, .phead { height: 62px; }
}

/* ---------- Short viewports ----------
   The 500x312 particle scene is the tallest thing in the hero stack. Under
   ~620px of height the stack bottoms out inside the 520px min-height panel
   and the countdown prints straight through the Apply pill. Shrink the scene
   rather than moving the countdown, so the design's proportions survive
   wherever there is actually room for them. */
@media (max-height: 620px) {
  /* squashing the scene into a 70px band just renders a smudge - it is
     decorative, so drop it and let the headline own the panel */
  .hero-scene { display: none; }
  .hero-center { gap: 18px; }
  .hero-countdown { bottom: 14px; }
}

/* ---------- Small screens: size the deck for thumbs ----------
   The pad and faces are sized off the shell at desktop, which lands the arms
   near 31px - under the 44px target and easy to miss between.
   Keyed on width as well as pointer type: headless Chrome reports
   `pointer: fine` at every viewport, so a pointer-only query is invisible to
   the QA captures and would ship unverified. */
@media (max-width: 900px), (pointer: coarse) {
  .gb { max-width: 340px; }
  /* give the pad the wider column so its arms clear 44px */
  .gb-deck { grid-template-columns: 1.25fr 1fr; min-height: 152px; }
  .gb-pad { width: min(100%, 156px); }
  .gb-face { width: clamp(56px, 17vw, 64px); font-size: 12.5px; }
}

/* ---------- Motion preferences ---------- */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; scroll-snap-type: none; }
  .reveal,
  .reveal :is(.pcell, .leave-list li, .faq-list details) {
    opacity: 1;
    transition: none;
  }
  .reveal { transform: none; }
  .btn, .tetris-overlay { transition: none; }
  /* keep the emulsion, drop the shimmer */
  .grain { animation: none; }
  .access-bg { animation: none; }
  .mesh-canvas { transition: none; }
}
