/* Natmeg — shared site styles.
 *
 * Structure, spacing and type live here. COLOUR does not: assets/themes.css is generated from the
 * game's own palettes by tools/sync-web-themes.py and supplies every brand token, so a page picks
 * its look with one attribute:
 *
 *     <html data-theme="ocean">      ocean / jungle / crayon
 *     <html>                         meadow, the game's default, on bare :root
 *
 * Tokens it provides, and the palette role each comes from:
 *
 *     --felt        feltColor        the table you play on (plus --felt-deep / --felt-lift)
 *     --cream       wash             ball wash; the text colour over felt
 *     --sage        pageBg           the theme's page ground
 *     --gold        active           the launched ball; the primary call to action
 *     --violet      anchorTint       the two anchor balls
 *     --sky         aimTrail         the prediction dots
 *     --frozen      frozen           a ball sitting out a turn
 *     --clay        coneColor        obstacles
 *     --pickup      pickupColor      bonuses
 *     --felt-vignette                how hard the rim darkens; ocean 0.20, jungle 0.08
 *
 * Load themes.css BEFORE this file. Retune a palette in the game and the site follows on the next
 * deploy: the old hardcoded green here (#2E7D32) had drifted a full brand generation behind.
 */

/* Display font is synced FROM the game's WORDMARK font by tools/sync-web-font.sh (single source of
   truth: scripts/Ui.cs WordmarkFontPath). The path + family here are deliberately font-agnostic. */
@font-face {
  font-family: "Natmeg Display";
  src: url("/assets/fonts/display.ttf") format("truetype");
  font-weight: 500;
  font-display: swap;
}

:root {
  /* Brand colours are NOT here. assets/themes.css is generated from the game's own palettes and
     defines --felt / --cream / --gold / --violet / --sky / --clay / --pickup / --frozen, with
     meadow on bare :root and the others behind [data-theme]. Load it BEFORE this file. */

  /* Surfaces: neutral, shared by every theme, and the only place light/dark mode is decided. */
  --ink: #3b402e;
  --muted: #6a7059;
  --card: #ffffff;
  --page: #f4f2e5;
  --border: rgba(59, 64, 46, 0.14);
  --shadow: 0 14px 44px rgba(39, 89, 51, 0.16);
  --shadow-sm: 0 4px 14px rgba(39, 89, 51, 0.1);

  /* Two families, not one. The display face is a game font: lovely for the wordmark and headings,
     tiring for a page of privacy-policy prose, which is what the site used to set it in. */
  --font-display: "Natmeg Display", ui-rounded, "SF Pro Rounded", "Segoe UI", system-ui, sans-serif;
  --font-text: ui-rounded, "SF Pro Rounded", -apple-system, BlinkMacSystemFont, "Segoe UI",
    system-ui, sans-serif;
}

@media (prefers-color-scheme: dark) {
  :root {
    --ink: #eceadb;
    --muted: #9aa48c;
    --card: #1e2c21;
    --page: #141d17;
    --border: rgba(236, 234, 219, 0.13);
    --shadow: 0 14px 44px rgba(0, 0, 0, 0.5);
    --shadow-sm: 0 4px 14px rgba(0, 0, 0, 0.35);
  }
}

* { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  font-family: var(--font-text);
  color: var(--ink);
  background: var(--page);
  line-height: 1.62;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, .btn, .tagline, .wordmark {
  font-family: var(--font-display);
  font-weight: 500;
}

a { color: var(--felt); }
@media (prefers-color-scheme: dark) { a { color: #8fd39c; } }

.wrap {
  max-width: 720px;
  margin: 0 auto;
  padding: 32px 20px 64px;
}

/* ---- Landing hero ----
   The felt gradient is the table you play on; the grain overlay is the same idea as the game's
   feltGrain (0.038), just cheap enough to ship as a gradient stack rather than a texture. */
.hero {
  position: relative;
  overflow: hidden;
  text-align: center;
  /* The bottom padding is sized for the SCENE, not the content. Meadow scatters flowers from
     uv.y 0.72 and crayon's daisies sit at 0.80-0.90, so anything the reader has to click has to
     finish above roughly 0.78 of the band's height or it competes with planting. */
  padding: 60px 20px clamp(120px, 19vw, 196px);
  background:
    radial-gradient(120% 90% at 50% 0%, var(--felt) 0%, var(--felt-deep) 68%, var(--felt-deep) 100%);
  color: var(--cream);
}
/* feltVignette, straight from the active theme's palette: darkens the rim so the band reads as a
   lit space. Only used on the flat-felt fallback; a painted scene draws its own (see .has-scene). */
.hero::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: radial-gradient(100% 75% at 50% 40%, transparent 55%,
      rgba(0, 0, 0, calc(var(--felt-vignette, 0.12) * 1)) 100%);
}
.hero > * { position: relative; z-index: 1; }
/* The WebGL backdrop is a child too, so it has to be pulled back under the content. Higher
   specificity than the rule above rather than relying on source order. */
.hero > .hero-bg, .doc-hero > .hero-bg {
  position: absolute;
  inset: 0;
  z-index: 0;
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
}

.hero .logo {
  width: 124px; height: 124px;
  border-radius: 28px;
  box-shadow: var(--shadow);
}
.hero h1, .wordmark {
  font-size: clamp(42px, 9vw, 66px);
  margin: 18px 0 6px;
  letter-spacing: -0.5px;
  line-height: 1.05;
}
.hero .tagline {
  font-size: clamp(18px, 4.5vw, 22px);
  opacity: 0.94;
  margin: 0 auto;
  max-width: 30ch;
}


/* ---- Dancing wordmark ----
   wordmark.js overlays a canvas that repaints the word every frame (the port of the game's
   WordmarkDance). The real <h1> text stays in the DOM for screen readers and search engines and is
   simply made invisible; if the script never runs, or the visitor asked for reduced motion, the
   heading renders normally and nothing here applies. */
.wordmark-live {
  position: relative;
  cursor: pointer;
  user-select: none;
  -webkit-user-select: none;
  -webkit-tap-highlight-color: transparent;
}
/* Hidden from sight, not from the accessibility tree: a canvas cannot be read aloud, so the real
   word stays in the DOM and only stops being painted. clip-path rather than display:none, which
   would take it out of the tree along with the pixels. */
.wordmark-text {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
.wordmark-canvas {
  position: absolute;
  left: 0;
  top: 50%;
  transform: translateY(-50%);
  display: block;
  pointer-events: none;
}

/* ---- Theme motifs ----
   The same plant sprites the campaign overworld scatters beside the road, standing along the foot
   of the hero. Rendered from assets/motifs.js (see tools/sync-web-themes.py) so a page never names
   a file. Purely decorative: aria-hidden, non-interactive, and absent entirely when JS is off. */
.motif-row {
  position: absolute;
  left: 0; right: 0; bottom: -2px;
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 4px;
  padding: 0 clamp(4px, 3vw, 40px);
  pointer-events: none;
  z-index: 0;
  overflow: hidden;
}
.motif-row img {
  width: clamp(38px, 9vw, 74px);
  height: auto;
  display: block;
  opacity: 0.9;
  filter: drop-shadow(0 6px 10px rgba(0, 0, 0, 0.22));
  transform-origin: bottom center;
}
/* Stagger the row so it reads as scattered planting rather than a rank of identical stems. */
.motif-row img:nth-child(odd)  { transform: scale(0.86) rotate(-3deg); }
.motif-row img:nth-child(3n)   { transform: scale(1.06) rotate(2deg); opacity: 0.98; }
.motif-row img:nth-child(4n)   { transform: scale(0.78) rotate(4deg); opacity: 0.8; }
/* Split layout: two groups pushed to the edges, centre left clear for the social row. */
.motif-row--split { justify-content: space-between; }
.motif-side {
  display: flex;
  align-items: flex-end;
  gap: clamp(2px, 1.6vw, 16px);
}

/* Below the widest phones the far ends fall off; thin the planting rather than squash everything. */
@media (max-width: 520px) {
  .motif-row > img:nth-child(n + 6) { display: none; }
  .motif-side img:nth-child(n + 3) { display: none; }
}

/* Doc pages carry a slimmer band so the legal text stays the subject. */
.doc-hero {
  position: relative;
  overflow: hidden;
  /* Generous top padding is not decoration: the scene puts features near the top of its UV space
     (crayon's smiley sun at y=0.14, jungle's leaf canopy above y=0.18) and on a short band those
     land exactly where an h1 would. In the game the page is a tall phone and the title clears them
     naturally; here the band has to buy that room back. */
  padding: clamp(96px, 15vw, 132px) 20px clamp(62px, 9vw, 84px);
  margin-bottom: 30px;
  background: radial-gradient(120% 120% at 50% 0%, var(--felt-lift) 0%, var(--felt) 46%, var(--felt-deep) 100%);
  color: var(--cream);
  text-align: center;
}
.doc-hero h1 { font-size: clamp(30px, 7vw, 42px); margin: 0; color: var(--cream); }
.doc-hero .updated { color: var(--cream); opacity: 0.72; margin: 6px 0 0; }
.doc-hero .motif-row img { width: clamp(30px, 7vw, 56px); opacity: 0.82; }
.doc-hero > * { position: relative; z-index: 1; }
.doc-hero .motif-row { z-index: 0; }


/* ---- Store buttons ---- */
.stores {
  display: flex;
  gap: 14px;
  justify-content: center;
  flex-wrap: wrap;
  margin-top: 28px;
}
.btn {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 14px 28px;
  border-radius: 999px;
  font-size: 17px;
  text-decoration: none;
  background: var(--cream);
  color: var(--felt-deep);
  box-shadow: var(--shadow-sm);
  transition: transform 0.12s ease, box-shadow 0.12s ease;
}
.btn:hover { transform: translateY(-2px); box-shadow: var(--shadow); }
.btn svg { width: 20px; height: 20px; flex: none; }
/* Primary call to action: the gold of the active ball. */
.btn.primary { background: var(--gold); color: #3a2e08; }
.hero .coming-soon { margin-top: 18px; font-size: 15px; color: var(--cream); opacity: 0.78; }

/* Secondary path under the badges. /get still earns its place for the platform redirect and the
   desktop QR hand-off, but it is no longer the landing page's main call to action. */
.stores-more { margin: 16px 0 0; font-size: 15px; }
.stores-more a { color: var(--cream); opacity: 0.82; text-underline-offset: 3px; }
.has-scene .stores-more a { color: var(--ink-on-scene); }

/* Visible keyboard focus ring (gold reads on both felt and paper). */
a:focus-visible, .btn:focus-visible, .socials a:focus-visible {
  outline: 3px solid var(--gold);
  outline-offset: 3px;
}

/* ---- Content sections ---- */
.section { padding: 44px 20px; }
.section.narrow { max-width: 640px; margin: 0 auto; }
.section h2 { font-size: 27px; margin: 0 0 12px; }
.muted { color: var(--muted); }
.gap { margin-top: 32px; }
.features {
  display: grid;
  gap: 18px;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  max-width: 760px;
  margin: 0 auto;
}
.feature {
  position: relative;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 18px;
  padding: 22px 20px 20px;
  box-shadow: var(--shadow-sm);
}
/* A coloured cap per card, cycling the three ball colours from the palette. */
.feature::before {
  content: "";
  position: absolute;
  top: 0; left: 22px; right: 22px;
  height: 3px;
  border-radius: 0 0 3px 3px;
  background: var(--gold);
}
.feature:nth-child(2)::before { background: var(--violet); }
.feature:nth-child(3)::before { background: var(--sky); }
.feature h2 { margin: 0 0 6px; font-size: 19px; }
.feature p { margin: 0; color: var(--muted); font-size: 15px; }

/* ---- Legal / doc pages ---- */
.doc h1 { font-size: 34px; margin: 0 0 4px; }
.doc .updated { color: var(--muted); font-size: 14px; margin: 0 0 28px; }
.doc h2 { font-size: 22px; margin: 32px 0 8px; }
.doc h3 { font-size: 18px; margin: 20px 0 4px; }
.doc ul { padding-left: 22px; }
.doc code {
  background: var(--border);
  padding: 2px 6px;
  border-radius: 6px;
  font-size: 0.9em;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}

/* ---- Social row ---- */
.socials {
  display: flex;
  gap: 16px;
  justify-content: center;
  margin: 26px 0 0;
  position: relative;
  z-index: 2;   /* above the motif band, which shares the hero's lower edge */
}
.socials a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  color: inherit;
  background: rgba(239, 236, 220, 0.14);
  transition: transform 0.12s ease, background 0.12s ease;
}
.socials a:hover { transform: translateY(-2px); background: rgba(239, 236, 220, 0.26); }
.socials svg { width: 22px; height: 22px; display: block; }
/* On light footers the pill needs a darker wash to stay visible. */
.footer .socials a { background: var(--border); }




/* ---- Store badges ----
   The vendors' OWN artwork, from assets/badges/ (provenance + rules in the README there). Neither
   may be recoloured, redrawn, distorted or given effects, so the only styling here is size, spacing
   and the clear space each one requires. Nothing touches the pixels.

   The two files are not equivalent: Apple ships a bare SVG, Google ships a PNG with its own clear
   space already around the artwork. Matching them by box height alone would leave Google's looking
   noticeably smaller, so Apple's gets the padding Apple's guidelines ask for (a tenth of the badge
   height) and Google's gets a taller box to compensate for the margin already inside it. Google
   also requires its badge to be no smaller than any other store badge beside it, which this keeps. */
.storebadge {
  display: inline-flex;
  align-items: center;
  text-decoration: none;
  transition: transform 0.12s ease;
}
.storebadge:hover { transform: translateY(-2px); }
.storebadge img { display: block; width: auto; }
.storebadge:focus-visible { outline: 3px solid var(--gold); outline-offset: 4px; }

.storebadge.is-apple img { height: 46px; margin: 4.6px; }   /* clear space = height / 10 */
.storebadge.is-play img  { height: 68px; }                  /* clear space already in the PNG */

/* Before a listing is public. NOT the official badge dimmed: the badge may not be altered, and
   both vendors licence it to promote an app you can actually download. This is our own chip. */
.storesoon {
  display: inline-flex;
  align-items: center;
  padding: 13px 20px;
  border-radius: 999px;
  font-size: 15px;
  color: var(--cream);
  background: rgba(20, 24, 15, 0.55);
  border: 1px solid rgba(244, 242, 229, 0.28);
}
.has-scene .storesoon {
  color: var(--ink-on-scene);
  background: rgba(255, 255, 255, 0.28);
  border-color: rgba(0, 0, 0, 0.14);
}

@media (prefers-reduced-motion: reduce) {
  .storebadge { transition: none; }
  .storebadge:hover { transform: none; }
}

/* ---- Desktop hand-off on /get (QR + typeable URL) ---- */
.handoff {
  margin: 30px auto 0;
  max-width: 340px;
}
.handoff[hidden] { display: none; }
.qr {
  display: block;
  width: 168px;
  height: 168px;
  margin: 0 auto;
  padding: 12px;
  border-radius: 16px;
  /* Fixed light card, never themed: the QR's modules are a fixed dark, and inheriting ocean's white
     ink or jungle's black would stop it scanning on half the worlds. */
  background: #fbfaf3;
  box-shadow: var(--shadow-sm);
}
.handoff-copy {
  margin: 14px 0 0;
  font-size: 15px;
  color: var(--cream);
  opacity: 0.9;
}
.handoff-copy .plainlink {
  color: inherit;
  font-weight: 600;
  text-underline-offset: 3px;
}

/* ---- Corner controls (sound + world) ----
   Fixed TOP-right on every page, where they are actually noticed; bottom-right was easy to miss.
   Small and out of the reading column, and they sit over the scene, so they carry their own solid
   surface rather than inheriting the band's colour. */
.site-controls {
  position: fixed;
  right: max(14px, env(safe-area-inset-right));
  top: max(14px, env(safe-area-inset-top));
  z-index: 40;
  display: flex;
  align-items: center;
  gap: 8px;
}
.site-control {
  width: 42px;
  height: 42px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 50%;
  background: var(--card);
  color: var(--ink);
  cursor: pointer;
  box-shadow: var(--shadow-sm);
  transition: transform 0.12s ease, background 0.12s ease;
}
.site-control:hover { transform: translateY(-2px); }
/* Over a painted scene the pill is the only thing separating the icon from the sky, so firm it up
   a little; the neutral --card alone reads washed out against a bright meadow morning. */
.site-control, .site-control-toast { backdrop-filter: saturate(1.2) blur(6px); }
.site-control svg { width: 20px; height: 20px; display: block; }
.site-control:focus-visible { outline: 3px solid var(--gold); outline-offset: 3px; }

/* The toast names the world you just switched to, or explains why the music stayed silent.
   Ordered before the buttons in the flex row so it grows leftwards, away from the screen edge. */
.site-control-toast {
  order: -1;
  max-width: 0;
  overflow: hidden;
  white-space: nowrap;
  opacity: 0;
  font-size: 14px;
  color: var(--ink);
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 999px;
  box-shadow: var(--shadow-sm);
  transition: max-width 0.28s ease, opacity 0.18s ease, padding 0.28s ease;
  padding: 7px 0;
}
.site-controls.has-toast .site-control-toast {
  max-width: 15rem;
  opacity: 1;
  padding: 7px 14px;
}

@media (prefers-reduced-motion: reduce) {
  .site-control, .site-control-toast { transition: none; }
  .site-control:hover { transform: none; }
}

/* ---- Footer ---- */
.footer {
  text-align: center;
  padding: 34px 20px;
  color: var(--muted);
  font-size: 14px;
  border-top: 1px solid var(--border);
}
.footer p a { color: var(--muted); margin: 0 10px; }

.back {
  display: inline-block;
  margin-bottom: 24px;
  color: var(--muted);
  text-decoration: none;
  font-size: 15px;
}

@media (prefers-reduced-motion: reduce) {
  .btn, .socials a { transition: none; }
  .btn:hover, .socials a:hover { transform: none; }
}

/* ---- A band wearing the game's scene ----
   LAST IN THE FILE ON PURPOSE. Every rule here is a same-specificity override of a default set
   earlier (.doc-hero h1, .hero .coming-soon, .handoff-copy all pin a colour for the flat-felt
   case), so it is source order that decides, and this block has to win. Twice now a rule was
   added above and quietly kept cream text on a daylit sky. Add new scene overrides HERE.

   They exist because the scene is BRIGHT: meadow, jungle and crayon all render a daylit sky and
   the cream that reads against flat felt disappears into it. --ink-on-scene is the theme's own
   answer, taken from the palette's pillInactiveText; ocean, being deep water, gets white back out
   of the same token. site.js adds .has-scene only once the backdrop has actually painted, so every
   failure path keeps the flat gradient and the cream that suits it. */
.has-scene {
  color: var(--ink-on-scene);
}
/* The shader draws its own corner_darken, so the CSS vignette would double it. */
.has-scene::after { display: none; }

/* .doc-hero h1 pins cream for the flat-felt case and outranks the bare .has-scene above, so the
   heading has to be named here too or it keeps cream over a daylit sky. */
.has-scene h1,
.has-scene .tagline,
.has-scene .coming-soon,
.has-scene .handoff-copy,
.has-scene .updated { color: var(--ink-on-scene); }

/* A whisper of lift so the wordmark holds its edge over a gradient sky rather than sitting flat
   on it. Deliberately tiny: anything heavier reads as a drop shadow from a different decade. */
.has-scene h1 { text-shadow: 0 1px 2px rgba(255, 255, 255, 0.28); }

/* The translucent-cream pill vanishes on a pale sky; over a scene the wash goes the other way. */
.has-scene .socials a { background: rgba(0, 0, 0, 0.09); }
.has-scene .socials a:hover { background: rgba(0, 0, 0, 0.16); }
