/* ──────────────────────────────────────────────────────────────────────────
   Beam Drip — shared styles for the site pages (/, /learn, …).
   NOT used by the game. index.html is a single self-contained file on purpose:
   that is what keeps Devvit packaging simple, and it must not grow a dependency
   on this file. Tokens below are copied from the game's :root and from the
   delivered art, so the two match by intent rather than by import.

   Sampled from art/, not guessed: the drop is #7bc4e8 and the icon's border is
   #f3f3f3. Game tokens (--gold, --ink, --drop) are the game's own values.
   ────────────────────────────────────────────────────────────────────────── */

/* ⚠️ Font choices are a fixed menu, not free text. The editor sets --font-display and
   --font-body to one of these stacks by key; it can never inject a font-family value. Only
   Fredoka and Nunito are self-hosted — every other option is a system stack, so picking one
   costs no download and cannot fail to load. */
:root{
  --stack-brand:"Fredoka","Nunito",system-ui,sans-serif;
  --stack-body:"Nunito",system-ui,-apple-system,sans-serif;
  --stack-system:system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;
  --stack-serif:"Iowan Old Style",Charter,Georgia,"Times New Roman",serif;
  --stack-mono:ui-monospace,"SF Mono",Menlo,Consolas,monospace;
  --font-display:var(--stack-brand);
  --font-body:var(--stack-body);
}

@font-face{font-family:"Fredoka";src:url("/fonts/fredoka-latin.woff2") format("woff2");
  font-weight:400 700;font-style:normal;font-display:swap}
@font-face{font-family:"Nunito";src:url("/fonts/nunito-latin.woff2") format("woff2");
  font-weight:400 800;font-style:normal;font-display:swap}

:root{
  /* brand — from the game and the delivered art */
  --sky:#004aad;          /* the share image and app icon ground */
  --sky-deep:#14143a;     /* the game's body background */
  --drop:#7bc4e8;         /* the favicon drop */
  --drop-soft:#86dbf2;    /* the game's --drop */
  --gold:#ffd66b;
  --gold-deep:#e0a92f;
  --accent:#ef7a4e;
  /* ⚠️ Darker than the game's #0e9aa7, which measured 3.30:1 against this page's paper —
     under AA for the small uppercase .eyebrow labels that use it. The dark theme puts the
     brighter value back, because there it is the light one that fails. Caught by the
     editor's contrast checker, on the default palette, months after it shipped. */
  --teal:#0a7580;

  /* neutrals — biased a few degrees toward the brand blue rather than pure grey,
     so they read as chosen next to #004aad instead of defaulted */
  --ink:#1a2238;
  --ink-soft:#4a5570;
  /* ⚠️ Was #6b7590, which measured 4.47:1 on --paper and 4.09:1 on --paper-sunk — under AA
     for the footer and the small notes under the quiz, both of which are ordinary small
     text. Same shape of bug as --teal before it, and found the same way: by checking a
     token nobody had thought to check. */
  --ink-faint:#5f6d85;
  --paper:#fbfcfe;
  --paper-sunk:#eef2f8;   /* the alternating band behind .sunk sections */
  --card:#fbfcfe;        /* card ground, separate from the page so cards can lift or sink */
  /* ⚠️ The quiz cards have their OWN ground, deliberately not --card. They are a distinct
     thing on the page — an interactive sample, not a content card — and tying them to the
     card colour meant a page restyle dragged them along. Soft grey by default: the question
     ink holds 5.5:1 on it. */
  --quiz-card:#eef0f4;
  --rule:#dbe3ef;

  --radius:14px;
  --radius-lg:22px;
  --shadow:0 1px 2px rgba(20,25,50,.06), 0 8px 24px rgba(20,25,50,.08);
  --shadow-lift:0 2px 4px rgba(20,25,50,.08), 0 16px 40px rgba(20,25,50,.14);
  --wrap:1080px;
  --read:68ch;            /* long-form measure */
}

@media (prefers-color-scheme: dark){
  :root:not([data-theme="light"]){
    --ink:#e8edf7; --ink-soft:#a8b4cc; --ink-faint:#7d8ba3;   /* was #8490aa, 4.48:1 on --paper */
    --paper:#0f1430; --paper-sunk:#171d3f; --card:#141a3a; --rule:#2a3258;
    --quiz-card:#1a2036;
    --teal:#0e9aa7;   /* 5.33:1 here; the light value would be 3.33:1 */
    --shadow:0 1px 2px rgba(0,0,0,.4), 0 8px 24px rgba(0,0,0,.4);
    --shadow-lift:0 2px 4px rgba(0,0,0,.5), 0 16px 40px rgba(0,0,0,.55);
  }
}
:root[data-theme="dark"]{
  --ink:#e8edf7; --ink-soft:#a8b4cc; --ink-faint:#7d8ba3;   /* was #8490aa, 4.48:1 on --paper */
  --paper:#0f1430; --paper-sunk:#171d3f; --card:#141a3a; --rule:#2a3258;
  --quiz-card:#1a2036;
  --teal:#0e9aa7;
  --shadow:0 1px 2px rgba(0,0,0,.4), 0 8px 24px rgba(0,0,0,.4);
  --shadow-lift:0 2px 4px rgba(0,0,0,.5), 0 16px 40px rgba(0,0,0,.55);
}

*{box-sizing:border-box}
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth}
@media (prefers-reduced-motion: reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{animation-duration:.01ms!important;animation-iteration-count:1!important;
    transition-duration:.01ms!important}
}
body{margin:0;background:var(--paper);color:var(--ink);
  font-family:var(--font-body);font-size:17px;line-height:1.62;
  -webkit-font-smoothing:antialiased}

/* ---- type ---- */
h1,h2,h3{font-family:var(--font-display);font-weight:700;
  line-height:1.15;text-wrap:balance;margin:0 0 .4em}
h1{font-size:clamp(2.1rem,5.2vw,3.5rem);letter-spacing:-.018em}
h2{font-size:clamp(1.5rem,3.1vw,2.1rem);letter-spacing:-.012em;margin-top:2.2em}
/* ⚠️ A section's FIRST heading gets no top margin — the section's own padding already
   separates it. Without this the two stack: measured, a .sunk section put its heading 42px
   from the top while every other section put it at 94px, because only the sunk ones were
   being patched with an inline margin-top:0 in the renderer. That is where the "lots of
   extra space" came from, and it was invisible as long as you only compared sections of the
   same kind. Handles both shapes the renderer emits: h2 inside .wrap, and h2 directly
   inside a section that is itself .wrap. */
section > h2:first-child,
section > .wrap > h2:first-child{margin-top:0}
h3{font-size:1.16rem;font-weight:600;margin-top:1.7em}
p{margin:0 0 1.05em;max-width:var(--read)}
a{color:var(--sky);text-underline-offset:.16em;text-decoration-thickness:.07em}
/* ⚠️ :where() so these contribute ZERO specificity, leaving them at plain `a` (0,0,1).
   Written as `:root[data-theme="dark"] a` they scored (0,2,1) and beat BOTH
   `.btn--primary{color:#3a2c05}` (0,1,0) and `a.card{color:inherit}` (0,2,0) — so in dark
   mode every button label and every card-as-link turned pale blue. Measured on the primary
   button: rgb(134,219,242) on rgb(255,214,107), 1.12:1. Not "a bit low" — invisible.

   :where() is the fix rather than :not(.btn):not(.card) because the next component that
   colours its own links should not have to be remembered here. */
@media (prefers-color-scheme: dark){ :where(:root:not([data-theme="light"])) a{color:var(--drop-soft)} }
:where(:root[data-theme="dark"]) a{color:var(--drop-soft)}
a:hover{text-decoration-thickness:.13em}
:focus-visible{outline:3px solid var(--gold);outline-offset:3px;border-radius:4px}
.eyebrow{font-family:var(--font-display);font-size:.79rem;font-weight:600;letter-spacing:.15em;
  text-transform:uppercase;color:var(--teal);margin:0 0 .6em}
.lede{font-size:1.16rem;color:var(--ink-soft);max-width:58ch}

/* ---- layout ---- */
.wrap{width:min(100% - 2.4rem, var(--wrap));margin-inline:auto}
.wrap--read{width:min(100% - 2.4rem, 46rem)}
section{padding-block:clamp(2.3rem,5vw,3.8rem)}
.sunk{background:var(--paper-sunk)}
.grid{display:grid;gap:1.1rem;grid-template-columns:repeat(auto-fit,minmax(15rem,1fr))}

/* ---- components ---- */
.card{background:var(--card);border:1px solid var(--rule);border-radius:var(--radius);
  padding:1.3rem 1.4rem;box-shadow:var(--shadow);
  transition:transform .16s ease, box-shadow .16s ease}
a.card{display:block;color:inherit;text-decoration:none}
a.card:hover{transform:translateY(-3px);box-shadow:var(--shadow-lift)}
.card h3{margin-top:0}
.card p{margin-bottom:0;color:var(--ink-soft);font-size:.96rem}

.btn{display:inline-flex;align-items:center;gap:.5rem;
  font-family:var(--font-display);font-weight:600;font-size:1.02rem;
  padding:.72rem 1.5rem;border-radius:999px;border:2px solid transparent;
  text-decoration:none;cursor:pointer;
  transition:transform .08s ease, box-shadow .16s ease, background .16s ease}
.btn--primary{background:var(--gold);color:#3a2c05;box-shadow:0 3px 0 var(--gold-deep)}
.btn--primary:hover{background:#ffe08c}
.btn--primary:active{transform:translateY(2px);box-shadow:0 1px 0 var(--gold-deep)}
.btn--ghost{border-color:currentColor;color:var(--ink)}
.btn--ghost:hover{background:var(--paper-sunk)}
.btn[aria-disabled="true"]{opacity:.62;cursor:not-allowed;box-shadow:none}
.btn[aria-disabled="true"]:active{transform:none}

/* The button row. Wraps by default so two buttons never collide at a narrow width —
   they stack instead, which is what the mobile layout was already doing correctly. */
.ctaRow{display:flex;flex-wrap:wrap;gap:.6rem;align-items:center;margin-bottom:1.05em}
.ctaRow--left{justify-content:flex-start}
.ctaRow--center{justify-content:center}
.ctaRow--right{justify-content:flex-end}
.skyband .ctaRow{justify-content:center}
.skyband .ctaRow--left{justify-content:flex-start}
.skyband .ctaRow--right{justify-content:flex-end}

.note{border-left:3px solid var(--drop);background:var(--paper-sunk);
  padding:.85rem 1.1rem;border-radius:0 var(--radius) var(--radius) 0;
  font-size:.96rem;color:var(--ink-soft)}
.note p:last-child{margin-bottom:0}

/* ---- site chrome ---- */
.siteNav{display:flex;align-items:center;gap:1.3rem;padding-block:1.05rem;
  border-bottom:1px solid var(--rule)}
/* ⚠️ nowrap. "Beam Drip" was breaking across two lines under 480px, which took the whole
   bar to 102px and made the links look like they were colliding with it. */
.siteNav .brand{display:flex;align-items:center;gap:.55rem;font-family:var(--font-display);
  font-weight:700;font-size:1.12rem;color:var(--ink);text-decoration:none;white-space:nowrap}
.siteNav .brand img{width:30px;height:30px;border-radius:8px}
.siteNav nav{margin-left:auto;display:flex;gap:1.15rem;flex-wrap:wrap}
.siteNav nav a{color:var(--ink-soft);text-decoration:none;font-weight:700;font-size:.95rem}
.siteNav nav a:hover,.siteNav nav a[aria-current="page"]{color:var(--sky)}
@media (prefers-color-scheme: dark){
  :where(:root:not([data-theme="light"])) .siteNav nav a:hover,
  :where(:root:not([data-theme="light"])) .siteNav nav a[aria-current="page"]{color:var(--drop-soft)} }

/* ---- narrow screens ----
   Measured, not guessed: at every width from 320 to 414 the bar was 102px tall, the brand
   wrapped to two lines and the four links made two rows; from 480 up it was a clean 65px.
   So the layout changes below 480 rather than at some round number. The links become one
   centred row that can scroll sideways if a page ever has more of them — a hidden link is
   worse than a scrollable one, and both beat a second row shoving the bar into the page. */
@media (max-width: 479px){
  .siteNav{flex-direction:column;align-items:center;gap:.45rem;padding-block:.7rem}
  .siteNav .brand{font-size:1.05rem}
  .siteNav nav{margin-left:0;justify-content:center;gap:1rem;flex-wrap:nowrap;
    max-width:100%;overflow-x:auto;scrollbar-width:none;-webkit-overflow-scrolling:touch}
  .siteNav nav::-webkit-scrollbar{display:none}
  .siteNav nav a{font-size:.9rem;white-space:nowrap}
}

.siteFoot{border-top:1px solid var(--rule);padding-block:1.7rem;margin-top:3rem;
  color:var(--ink-faint);font-size:.9rem}
.siteFoot ul{list-style:none;margin:0 0 .6rem;padding:0;display:flex;gap:1.1rem;flex-wrap:wrap}
.siteFoot a{color:var(--ink-soft)}

/* Wide content must scroll inside its own box; the page body never scrolls sideways. */
.scrollx{overflow-x:auto;-webkit-overflow-scrolling:touch}
img{max-width:100%;height:auto}
.visually-hidden{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;
  clip:rect(0 0 0 0);white-space:nowrap;border:0}

/* ── the opening band ──────────────────────────────────────────────────────
   A landscape echo of the game's intro. The sky stops are copied verbatim from
   drawSky() in index.html so the site and the game are the same sky, not two
   guesses at it. Everything moves slowly and stops entirely under
   prefers-reduced-motion — this sits at the very top of the page, which is the
   worst possible place for motion somebody did not ask for. */
.skyband{position:relative;overflow:hidden;isolation:isolate;
  background:linear-gradient(180deg,#26265f 0%,#5560a8 50%,#9a8fc0 80%,#f0a97e 100%);
  padding-block:clamp(2rem,5vw,3.4rem);text-align:center}
/* The beam: a soft wedge of light rising from the horizon, the same idea as the
   sunbeam the whole game is played with. */
.skyband__beam{position:absolute;left:50%;bottom:-12%;width:min(34rem,74%);height:150%;
  transform:translateX(-50%);pointer-events:none;z-index:-1;
  background:radial-gradient(ellipse 52% 60% at 50% 100%,rgba(255,240,190,.55),rgba(255,214,107,.18) 45%,transparent 72%);
  animation:beamBreathe 7s ease-in-out infinite}
@keyframes beamBreathe{0%,100%{opacity:.82;transform:translateX(-50%) scaleX(.97)}
  50%{opacity:1;transform:translateX(-50%) scaleX(1.05)}}
.skyband__clouds{position:absolute;inset:0;pointer-events:none;z-index:-1}
/* Flatter, softer and fainter. At 9px blur a 240x54 lozenge still reads as a rectangle
   with rounded ends rather than cloud; the blur has to be a large fraction of the
   SHORT side before the silhouette disappears. */
/* ⚠️ The game's cloud, not a lozenge. drawCloud() in index.html paints eight circles —
   three shaded behind, five light in front — and these are those eight, offset so the
   design-space origin lands inside the box (x +51, y +33). Positions and radii are in em,
   so font-size alone scales a whole cloud; 1em = one game unit, and font-size:1px is the
   game's own size.

   The pill shape came from doing this with one border-radius:999px span and hiding it under
   22px of blur. Blur is down to 3px now, because the silhouette is doing the work instead. */
.skyband__clouds span{
  --light:rgba(240,243,252,.95);
  --shade:rgba(198,207,238,.9);
  position:absolute;display:block;width:102em;height:67em;
  filter:blur(3px) drop-shadow(0 2px 6px rgba(20,20,58,.18));
  background:
    radial-gradient(circle 19em at 65em 40em, var(--light) 98%, transparent 100%),
    radial-gradient(circle 19em at 39em 40em, var(--light) 98%, transparent 100%),
    radial-gradient(circle 21em at 81em 36em, var(--light) 98%, transparent 100%),
    radial-gradient(circle 27em at 51em 27em, var(--light) 98%, transparent 100%),
    radial-gradient(circle 21em at 23em 35em, var(--light) 98%, transparent 100%),
    radial-gradient(circle 22em at 51em 45em, var(--shade) 98%, transparent 100%),
    radial-gradient(circle 18em at 83em 42em, var(--shade) 98%, transparent 100%),
    radial-gradient(circle 20em at 21em 41em, var(--shade) 98%, transparent 100%);
}
/* ⚠️ Tops stay in the upper band — see the contrast note below. font-size is the scale. */
/* ⚠️ font-size is min(px, vw) so the clouds SHRINK with the viewport. Fixed px failed at
   375px: the clouds kept their size while the band got shorter and the title wrapped to two
   lines and rose to meet them — measured, a cloud overlapped the title by 61px. Positioning
   by percentage while sizing by px is the trap; tie both to the viewport and it holds. */
.skyband__clouds span:nth-child(1){font-size:min(1.5px,.235vw);top:3%;left:-14%;animation:drift 58s linear infinite}
.skyband__clouds span:nth-child(2){font-size:min(1.05px,.165vw);top:15%;left:-22%;animation:drift 82s linear infinite 7s;opacity:.9}
.skyband__clouds span:nth-child(3){font-size:min(1.9px,.3vw);top:17%;left:-30%;animation:drift 104s linear infinite 3s}
/* Clouds are white, so one drifting behind the copy LIGHTENS the ground under white text:
   measured, a .42 pill took the lede from 5.50:1 to 3.22:1, under AA — and these are more
   opaque than that pill was. Behind the logo there is nothing to fail. Keep the bottom of
   every cloud ABOVE ~50% of the band if these are ever retuned — cloud 3 sat at 36% and
   its bottom landed at 57%, exactly on the title's top edge. A cloud is much taller than
   its `top` suggests, because font-size scales all 67em of it. */
@keyframes drift{to{transform:translateX(160vw)}}
/* ⚠️ A scrim, because measurement said so. White at 94% on the mid-sky (rgb(129,126,183))
   is 3.75:1 — under the 4.5:1 WCAG AA needs for body text. The sky is prettiest exactly
   where it is lightest, which is where the copy sits, so darkening the ground beneath the
   text is the fix that keeps both. 25% of the game's own #14143a measures 5.46:1. */
.skyband::after{content:"";position:absolute;inset:auto 0 0;height:78%;z-index:-1;
  pointer-events:none;
  background:linear-gradient(180deg,rgba(20,20,58,0) 0%,rgba(20,20,58,.25) 42%,rgba(20,20,58,.25) 100%)}
.skyband__inner{position:relative;display:flex;flex-direction:column;align-items:center;gap:.55rem}
.skyband__logo{width:min(19.5rem,56vw);height:auto;
  filter:drop-shadow(0 10px 26px rgba(10,10,40,.45));
  animation:logoRise 1.1s cubic-bezier(.2,.9,.3,1) both}
@keyframes logoRise{from{opacity:0;transform:translateY(16px) scale(.94)}to{opacity:1;transform:none}}
/* ⚠️ Light text on the sky, not the page tokens — this band keeps its own colours in
   both themes, because the gradient is the same either way. */
/* A step down from the page-wide h1 scale. The logo IS the headline here; the words
   underneath restate it for crawlers and for anyone who cannot see the image, so they
   do not need to compete with it for size. */
.skyband__title{color:#fff;font-size:clamp(1.7rem,3.9vw,2.6rem);
  text-shadow:0 3px 0 rgba(0,0,0,.22),0 7px 18px rgba(0,0,0,.34);margin-bottom:.1em}
.skyband__lede{color:rgba(255,255,255,.94);max-width:46ch;
  text-shadow:0 1px 8px rgba(10,10,40,.5)}
.skyband .btn--ghost{color:#fff;border-color:rgba(255,255,255,.75)}
.skyband .btn--ghost:hover{background:rgba(255,255,255,.14)}
@media (prefers-reduced-motion: reduce){
  .skyband__beam,.skyband__clouds span,.skyband__logo{animation:none}
  .skyband__logo{opacity:1;transform:none}}

/* ── picture cards ─────────────────────────────────────────────────────── */
.picCards{list-style:none;margin:1.1rem 0 0;padding:0;display:grid;gap:1.1rem;
  grid-template-columns:repeat(auto-fit,minmax(15rem,1fr))}
.picCard{background:var(--card);border:1px solid var(--rule);border-radius:var(--radius-lg);
  overflow:hidden;box-shadow:var(--shadow);display:flex;flex-direction:column}
/* ⚠️ aspect-ratio plus the width/height attributes on the <img> together reserve the box
   before the image arrives. Without both, three square images popping in shove the whole
   page down as they load — the layout shift Google measures and penalises. */
.picCard img{display:block;width:100%;height:auto;aspect-ratio:1/1;object-fit:cover}
.picCard h3{margin:.85rem 1rem .2rem;font-size:1.02rem}
.picCard p{margin:0 1rem 1rem;font-size:.94rem;color:var(--ink-soft)}
/* ⚠️ A card with no heading needs its own space under the picture. The paragraph's top
   margin is 0 because normally an h3 sits above it and supplies the gap — take the heading
   away and the text lands flush against the image. Covers both card shapes. */
.picCard img + p,
.card--pic img + p,
.card--pic img + h3{margin-top:.85rem}

/* A link card with a picture on top. Shares .card's ground and border so the two kinds sit
   together without looking like different components. */
.card--pic{padding:0;overflow:hidden}
.card--pic img{display:block;width:100%;height:auto;aspect-ratio:1/1;object-fit:cover}
.card--pic h3{margin:.85rem 1rem .2rem}
.card--pic p{margin:0 1rem 1.1rem}
/* ⚠️ An inset thumbnail rather than a full-bleed header. Measured, a full-width square in a
   353px card gave a 351px image — taller than the paragraph it introduces, which reads as an
   illustration with a caption rather than a card with an icon. Used where the body text is
   the point and the picture is support. */
.card--pic--sm{padding:1.1rem 0 0}
.card--pic--sm img{width:min(9rem,42%);margin:0 1rem;border-radius:var(--radius)}

/* Hero with a picture: side by side where there is room, stacked where there is not.
   The image is capped rather than fluid — a square illustration beside a paragraph should
   not grow with the viewport until it dwarfs the words it introduces. */
.hero--pic{display:flex;align-items:center;gap:clamp(1rem,3vw,2.2rem);flex-wrap:wrap}
.hero--pic .hero__pic{flex:0 0 auto;width:min(13rem,38vw)}
.hero--pic .hero__pic img{display:block;width:100%;height:auto;border-radius:var(--radius-lg)}
.hero--pic .hero__text{flex:1 1 20rem;min-width:0}
