@import url('https://fonts.cdnfonts.com/css/pulang');

/* ============================================================
   DESIGN SYSTEM TOKENS
   Pulled from the "Color Palette", "Typographic Scale", and the
   *Hover State" component frames in the Figma file. Every page
   should read colors/type from these variables instead of
   hardcoding values, so the whole site stays in sync.

   NOTE: the Color Palette page labels body text "#3D3D3D," while the
   real nav/footer instances (as originally deployed) used pure black.
   Confirmed by direct request that #3D3D3D is correct everywhere —
   --color-text now matches --color-text-secondary.

   The 12-column grid (node 855:512) doesn't show up as layers —
   it's a Figma layout-grid guide on the frame, not real content,
   so values below are taken from that grid's settings panel
   rather than the code extraction.
============================================================ */
:root {
  /* Core */
  --color-bg: #fffdfd;
  --color-text: #3d3d3d;
  --color-text-secondary: #3d3d3d; /* documented "text-on-light" token — see note above */
  --color-text-muted: #1e1e1e; /* actual color used on the real footer copyright line */
  --color-text-on-dark: #fffdfd;

  /* Accent / secondary palette (assets, card borders, illustrations) */
  --color-accent-blonde: #ebe6a0;
  --color-accent-yellow: #f2e35d;
  --color-accent-lightblue: #bde6e6;
  --color-accent-purple: #8899fa;
  --color-accent-pink: #f8ccc9;
  --color-neutral-border: #919191; /* form borders */

  /* Nav + footer chrome */
  --color-nav-bg: #fffdfd; /* matches --color-bg so the fixed header doesn't show a seam against the page */
  --color-footer-gradient-start: #e6f0ff;
  --color-footer-gradient-end: #d8f5ff;
  --color-social-bg: #000000;

  /* Primary CTA (solid green button) */
  --color-primary-default: #649d35;
  --color-primary-hover: #44890b;
  --color-primary-active: #237b35;

  /* Secondary CTA (outlined button) */
  --color-secondary-border: #649d35;
  --color-secondary-text: #4b7d22;
  --color-secondary-hover-bg: #f2fff5;

  /* Tertiary CTA (underlined text button) */
  --color-tertiary-text: #6ec313;
  --color-tertiary-hover-bg: #f1f1f1;

  /* Discord CTA */
  --color-discord-default: #7289da;
  --color-discord-hover: #5569ae;
  --color-discord-active: #4559a0;

  /* Radius — observed from real components, not a documented scale */
  --radius-button: 16px;
  --radius-pill: 100px;

  /* 12-column desktop layout grid (Figma frame 855:512 — Stretch type,
     12 columns, auto width, 48px side margins, 20px gutter) */
  --grid-columns: 12;
  --grid-margin: 48px;
  --grid-gutter: 20px;

  /* Nav bar's own rendered height (80px logo + 12px top/bottom padding),
     used to offset content below it now that it's fixed. */
  --nav-height: 104px;

  /* Fonts */
  --font-heading: 'Pulang', 'Nunito', sans-serif;
  --font-body: 'Nunito', sans-serif;

  /* Type scale (desktop — Figma's mobile scale had gaps/inconsistencies
     in the source file and there's no breakpoint spec yet, so it isn't
     included here; ask if you want it added once a breakpoint exists) */
  /* Letter-spacing is in em, not px. Every value in the Figma type
     scale is a percentage of its own font size (2% everywhere, 5% on
     buttons), and 1em == 100%, so these are the same numbers the px
     versions produced at desktop sizes — but they now follow the font
     size automatically, so the mobile breakpoint only has to override
     the one tracking that actually changes (body, 2% -> 3%). */
  --text-title-size: 64px;   --text-title-lh: 1.2; --text-title-ls: 0.02em;
  --text-h1-size: 48px;      --text-h1-lh: 1.2;    --text-h1-ls: 0.02em;
  --text-h2-size: 36px;      --text-h2-lh: 1.4;    --text-h2-ls: 0.02em;
  --text-h3-size: 28px;      --text-h3-lh: 1.3;    --text-h3-ls: 0.02em;
  --text-subtitle1-size: 20px; --text-subtitle1-lh: 1.3; --text-subtitle1-ls: 0.02em;
  --text-subtitle2-size: 16px; --text-subtitle2-lh: 1.3; --text-subtitle2-ls: 0.02em;
  --text-body-size: 20px;    --text-body-lh: 1.5;  --text-body-ls: 0.02em;
  --text-button-size: 18px;  --text-button-lh: 1.3; --text-button-ls: 0.05em;
}

/* Matches Home's side margin at laptop widths (e.g. MacBook, ~1512px):
   Home centers a 1440px-max content box with 48px of padding inside it,
   which works out to 84px from the true edge at 1512px. This page's
   --grid-margin is a flat padding (no centering), so bump it to the same
   84px here. Placed before the phone block below so that one still wins
   at ≤640px. Covers .grid-12 (body content) and .hero__content (hero
   text) — nav/footer only read --grid-margin at ≤640px, so they're
   unaffected. */
@media (max-width: 1800px) {
  :root { --grid-margin: 84px; }
}

/* ============================================================
   BASE / RESET
============================================================ */
*, *::before, *::after {
  box-sizing: border-box;
}

html, body {
  margin: 0;
  padding: 0;
}

body {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  background: var(--color-bg);
  color: var(--color-text);
  font-family: var(--font-body);
  padding-top: var(--nav-height); /* compensates for the fixed nav bar below */
}

/* Keeps anchor-linked sections (e.g. from the footer) from scrolling
   their heading up underneath the fixed nav bar. */
section[id] {
  scroll-margin-top: var(--nav-height);
}

img {
  display: block;
  max-width: 100%;
}

a {
  color: inherit;
  text-decoration: none;
}

ul {
  margin: 0;
  padding: 0;
  list-style: none;
}

/* ============================================================
   TYPE SCALE
   Matches the named text styles in the Figma "Typographic
   Scale" frame 1:1 — use these classes on headings/body copy
   so every page's type stays on-system.
============================================================ */
.text-title    { font-family: var(--font-heading); font-weight: 400; font-size: var(--text-title-size); line-height: var(--text-title-lh); letter-spacing: var(--text-title-ls); }
.text-h1       { font-family: var(--font-heading); font-weight: 400; font-size: var(--text-h1-size); line-height: var(--text-h1-lh); letter-spacing: var(--text-h1-ls); }
.text-h2       { font-family: var(--font-heading); font-weight: 400; font-size: var(--text-h2-size); line-height: var(--text-h2-lh); letter-spacing: var(--text-h2-ls); }
.text-h3       { font-family: var(--font-heading); font-weight: 400; font-size: var(--text-h3-size); line-height: var(--text-h3-lh); letter-spacing: var(--text-h3-ls); }
.text-subtitle-1 { font-family: var(--font-body); font-weight: 600; font-size: var(--text-subtitle1-size); line-height: var(--text-subtitle1-lh); letter-spacing: var(--text-subtitle1-ls); }
.text-subtitle-2 { font-family: var(--font-body); font-weight: 400; font-size: var(--text-subtitle2-size); line-height: var(--text-subtitle2-lh); letter-spacing: var(--text-subtitle2-ls); }
.text-body     { font-family: var(--font-body); font-weight: 400; font-size: var(--text-body-size); line-height: var(--text-body-lh); letter-spacing: var(--text-body-ls); }

/* ============================================================
   LAYOUT GRID
   12-column desktop grid — wrap your page content in .grid-12
   and place children with .col-span-N (1-12) to line up with
   the rest of the site. Fluid width with a fixed 48px side
   margin and 20px gutter at any screen size — matches how the
   nav/footer/hero stay flush against the true edges, so this
   stays aligned with them instead of drifting on wide screens.
============================================================ */
.grid-12 {
  display: grid;
  grid-template-columns: repeat(var(--grid-columns), 1fr);
  gap: var(--grid-gutter);
  width: 100%;
  padding: 0 var(--grid-margin);
}

.col-span-1  { grid-column: span 1; }
.col-span-2  { grid-column: span 2; }
.col-span-3  { grid-column: span 3; }
.col-span-4  { grid-column: span 4; }
.col-span-5  { grid-column: span 5; }
.col-span-6  { grid-column: span 6; }
.col-span-7  { grid-column: span 7; }
.col-span-8  { grid-column: span 8; }
.col-span-9  { grid-column: span 9; }
.col-span-10 { grid-column: span 10; }
.col-span-11 { grid-column: span 11; }
.col-span-12 { grid-column: span 12; }

/* Dev-only guide box — sized by .grid-12/.col-span-12 above, so its
   left/right edges land exactly on the grid's 48px margins. Not a
   real design-system component; delete along with the markup in
   index.html once the margins are understood. */
.margin-marker {
  box-sizing: border-box;
  height: 352px;
  margin: 124px 0; /* gap to the hero above and the footer/grass below */
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 24px;
  background: #d9d9d9;
  border: 2px dashed #8a8a8a;
  border-radius: 4px;
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.5;
  color: #4a4a4a;
}

/* ============================================================
   BUTTONS
   .btn is the shared base; pick one variant class per button.
   Hover/active colors come straight from the *Hover State*
   component frames in Figma.
============================================================ */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 12px 24px;
  border: none;
  border-radius: var(--radius-button);
  font-family: var(--font-body);
  font-weight: 700;
  font-size: var(--text-button-size);
  line-height: var(--text-button-lh);
  letter-spacing: var(--text-button-ls);
  white-space: nowrap;
  cursor: pointer;
  text-decoration: none;
  /* Same smooth hover fade as .btn-discord (now shared from here instead of
     duplicated on it) so every button variant — including DinoDex's View
     Full Catalog/Open Catalog Sheet and the getting-started drop type
     overlay's secondary CTA, which both use these shared classes — animates
     its hover color instead of snapping instantly. box-shadow is here too
     for .btn-secondary's hover "thicker border": a literal border-width
     transition (1px -> 2px) only has one whole pixel to cross, so it got
     rounded to the nearest device pixel for most of the transition and
     then snapped at the very end — looking delayed, not eased. An inset
     box-shadow isn't snapped to device pixels, so its spread animates
     genuinely smoothly instead. */
  transition: background-color 300ms ease, box-shadow 300ms ease;
}

.btn-primary {
  background: var(--color-primary-default);
  color: var(--color-text-on-dark);
}
.btn-primary:hover { background: var(--color-primary-hover); }
.btn-primary:active { background: var(--color-primary-active); }

/* Primary + trailing icon — Figma only defines Default/Press (no
   separate hover shade), so Press is used as the hover color. */
.btn-primary-icon {
  background: var(--color-primary-default);
  color: var(--color-text-on-dark);
}
.btn-primary-icon:hover { background: var(--color-primary-active); }
.btn-primary-icon img { width: 24px; height: 24px; transform: rotate(90deg); }

.btn-secondary {
  height: 48px;
  min-height: 48px;
  background: var(--color-bg);
  color: var(--color-secondary-text);
  border: 1px solid var(--color-secondary-border);
}
.btn-secondary:hover {
  background: var(--color-secondary-hover-bg);
  /* Stacks on top of the real 1px border instead of changing its width,
     so the apparent thickening (still ending up ~2px total) animates
     smoothly — see the transition comment on .btn above. */
  box-shadow: inset 0 0 0 1px var(--color-secondary-border);
}

.btn-tertiary {
  background: var(--color-bg);
  color: var(--color-tertiary-text);
  text-decoration: underline;
}
.btn-tertiary:hover { background: var(--color-tertiary-hover-bg); }

.btn-discord {
  height: 48px;
  min-height: 48px;
  padding: 12px 16px;
  background: var(--color-discord-default);
  color: var(--color-text-on-dark);
}
.btn-discord:hover { background: var(--color-discord-hover); }
.btn-discord:active { background: var(--color-discord-active); }
.btn-discord img { width: 24px; height: 24px; }

/* ============================================================
   NAV LINKS
   Transparent border by default, colored on hover — avoids the
   1px text jump you'd get from adding a border only on :hover.
============================================================ */
.main-nav__link {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: var(--text-body-size);
  line-height: var(--text-body-lh);
  letter-spacing: var(--text-body-ls);
  white-space: nowrap;
  border-bottom: 1px solid transparent;
  transition: border-bottom-color 300ms ease;
}
.main-nav__link:hover {
  border-bottom-color: var(--color-text);
}

/* Current-page indicator — same underline the hover state uses, just
   permanently on, plus bold. */
.main-nav__link--current,
.nav-dropdown__panel a[aria-current="page"] {
  font-weight: 700;
  border-bottom-color: var(--color-text);
}

.main-nav__chevron {
  width: 24px;
  height: 24px;
  transform: rotate(180deg);
  transition: transform 0.15s ease;
}

/* ============================================================
   HEADER / NAV
============================================================ */
.site-header {
  background: var(--color-nav-bg);
  flex-shrink: 0;
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 30;
}

.site-header__inner {
  width: 100%;
  padding: 12px 40px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  row-gap: 8px;
  gap: 40px;
}

.site-logo img {
  width: 80px;
  height: 80px;
  object-fit: cover;
}

.main-nav {
  display: flex;
  align-items: center;
  gap: 40px;
  flex-wrap: wrap;
}

/* Laptop range (1201-1800px, above the hamburger-menu breakpoint below) —
   tighter gaps so the 5 nav links + Discord button always have room to
   fit on one line without crowding the button past the page edge. */
@media (min-width: 1201px) and (max-width: 1800px) {
  .site-header__inner { gap: 24px; }
  .main-nav { gap: 24px; }
}

/* "Getting Started" dropdown trigger + overlay */
.nav-dropdown {
  position: relative;
}

.nav-dropdown:hover .main-nav__link {
  border-bottom-color: var(--color-text);
}
.nav-dropdown:hover .main-nav__chevron {
  transform: rotate(0deg);
}

.nav-dropdown__panel {
  position: absolute;
  top: 100%;
  left: 0;
  width: max-content;
  min-width: 100%;
  background: var(--color-nav-bg);
  padding: 12px;
  border-radius: 0 0 var(--radius-button) 0;
  box-shadow: 0 8px 16px rgba(0, 0, 0, 0.08);
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.15s ease;
}

.nav-dropdown:hover .nav-dropdown__panel,
.nav-dropdown:focus-within .nav-dropdown__panel {
  opacity: 1;
  visibility: visible;
}

.nav-dropdown__panel a {
  display: block;
  font-size: var(--text-body-size);
  letter-spacing: var(--text-body-ls);
  white-space: nowrap;
  border-bottom: 1px solid transparent;
  transition: border-bottom-color 300ms ease;
}
.nav-dropdown__panel a:hover {
  border-bottom-color: var(--color-text);
}

/* ============================================================
   PAGE CONTENT
   Intentionally unstyled/unconstrained — flex: 1 0 auto lets it
   grow to whatever height its content needs (sticky-footer
   pattern), and there is no max-height/overflow:hidden anywhere
   in this file, so long content is never cut off.
============================================================ */
.page-content {
  flex: 1 0 auto;
  width: 100%;
}

/* ============================================================
   HERO
   Full-width banner with a bottom-left title/subtitle over a
   darkening gradient. This is example page content (like the
   Figma "Frame 48" DinoDex hero) — swap .hero__bg's image and
   the text for your own page, or delete the whole section if
   your page doesn't use this pattern. Title/subtitle sizing
   matches .text-title / .text-subtitle-1 from the type scale;
   the text block's side margins match the 12-col grid (48px).
============================================================ */
.hero {
  position: relative;
  width: 100%;
  height: 600px;
  overflow: hidden;
  display: flex;
  align-items: flex-end;
}

.hero__bg {
  position: absolute;
  inset: 0;
  z-index: 0;
  background: var(--color-text-secondary); /* dark gray placeholder — drop an <img> back in here to swap in a real photo/illustration */
}

.hero__bg picture {
  display: contents;
}

.hero__bg img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 68%; /* matches the pan/crop set in Figma for this image */
}

.hero__gradient {
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(to bottom, rgba(102, 102, 102, 0) 0%, rgba(0, 0, 0, 0.7) 75%);
  pointer-events: none;
}

.hero__content {
  position: relative;
  z-index: 2;
  width: 100%;
  padding: 16px var(--grid-margin);
}

.hero__title {
  margin: 0 0 8px;
  font-family: var(--font-heading);
  font-weight: 400;
  font-size: var(--text-title-size);
  line-height: var(--text-title-lh);
  letter-spacing: var(--text-title-ls);
  color: var(--color-text-on-dark);
  text-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25);
}

.hero__subtitle {
  margin: 0;
  font-family: var(--font-body);
  font-weight: 600;
  font-size: var(--text-subtitle1-size);
  line-height: var(--text-subtitle1-lh);
  letter-spacing: var(--text-subtitle1-ls);
  color: var(--color-text-on-dark);
  text-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25);
}

/* ============================================================
   FOOTER
============================================================ */
.site-footer {
  width: 100%;
  flex-shrink: 0;
  background: linear-gradient(to top, var(--color-footer-gradient-start), var(--color-footer-gradient-end));
}

/* The grass art is a single ~657px-wide clump, not a seamless tile — Figma
   built the strip by overlapping 3 copies 452px apart (205px of overlap
   each), not by repeating it edge-to-edge. Plain background-repeat shows
   that seam as gaps with the footer's blue peeking through, so this
   reproduces the same manual overlap instead, via stacked background
   layers, repeated enough times to cover very wide screens. */
.footer-grass {
  width: 100%;
  height: 152px;
  background-color: var(--color-bg);
  background-repeat: no-repeat;
  background-size: 657px 152px;
  background-image:
    url('assets/grass.svg'), url('assets/grass.svg'), url('assets/grass.svg'),
    url('assets/grass.svg'), url('assets/grass.svg'), url('assets/grass.svg'),
    url('assets/grass.svg'), url('assets/grass.svg'), url('assets/grass.svg'),
    url('assets/grass.svg'), url('assets/grass.svg'), url('assets/grass.svg');
  background-position:
    -226px bottom, 226px bottom, 678px bottom, 1130px bottom, 1582px bottom, 2034px bottom,
    2486px bottom, 2938px bottom, 3390px bottom, 3842px bottom, 4294px bottom, 4746px bottom;
  /* Forces its own pixel-snapped GPU layer — without this the tiled
     SVG blades get a hairline rasterization seam at the top. */
  transform: translateZ(0);
}

.footer-content {
  position: relative;
  width: 100%;
  padding: 40px 28px;
}

/* Decorative footer illustrations — positioned to match the 1440px desktop
   comp. Not part of the responsive spec yet; revisit once one exists. */
.footer-deco {
  position: absolute;
  pointer-events: none;
}

/* Left-anchored — these sit near the left-aligned columns/copyright,
   so they stay pinned to the true left edge at any width. */
.footer-deco--flower-tl    { left: 0px;   top: 0px;   width: 40px; height: 40px; }
.footer-deco--flower-mid   { left: 478px; top: 226px; width: 40px; height: 40px; }

/* Right-anchored — these were positioned relative to the right edge of
   the 1440px comp, so they're pinned via `right` (1440 - left - width)
   instead of `left`, and track the true right edge at any width now
   that .footer-content is fluid rather than capped at 1440. */
.footer-deco--cloud1-back  { right: 294px; top: 61px;   width: 65px; height: 34px; }
.footer-deco--cloud1-front { right: 292px; top: 59px;   width: 64px; height: 34px; }
.footer-deco--cloud2-back  { right: 165px; top: 20px;   width: 85px; height: 45px; }
.footer-deco--cloud2-front { right: 162px; top: 18px;   width: 84px; height: 45px; }
.footer-deco--cloud3-back  { right: 65px;  top: 78px;   width: 61px; height: 33px; }
.footer-deco--cloud3-front { right: 62px;  top: 77px;   width: 61px; height: 33px; }
.footer-deco--dino-single  { right: 213px; top: 173px;  width: 85px; height: 132px; }

/* Laptop-size nudge only (1001-1800px) — the cloud nearest "About Us"
   sat too high against the column header at this width. */
@media (max-width: 1800px) {
  .footer-deco--cloud1-back  { top: 111px; }
  .footer-deco--cloud1-front { top: 109px; }
}
/* bottom: 0 (not 9px) so the green dino's feet sit flush with the
   footer's bottom edge, matching the blue dino (dino-single) above. */
.footer-deco--dino-duo     { right: 126px; bottom: 0; width: 81px; height: 107px; }
/* bottom: 0 (not 7px) to sit flush with the footer's bottom edge, matching
   the dinos above instead of floating above it. */
.footer-deco--flower-small-1 { right: 28px; bottom: 0; width: 39px; height: 43px; }
.footer-deco--flower-small-2 { right: 70px; bottom: 0; width: 29px; height: 32px; }

.footer-columns {
  position: relative;
  z-index: 1;
  display: flex;
  flex-wrap: wrap;
  gap: 57px;
  margin-bottom: 48px;
}

.footer-column__title {
  margin: 0 0 12px;
  font-family: var(--font-heading);
  font-size: 20px;
  color: var(--color-text);
}

.footer-column__links {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.footer-column__links a {
  font-size: 20px;
  letter-spacing: 0.4px;
  border-bottom: 1px solid transparent;
  transition: border-bottom-color 300ms ease;
}
.footer-column__links a:hover {
  border-bottom-color: var(--color-text);
}

.footer-bottom {
  position: relative;
  z-index: 1;
  max-width: 822px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 16px;
}

.footer-copyright {
  margin: 0;
  font-size: 16px;
  color: var(--color-text-muted);
}

.footer-social {
  display: flex;
  align-items: center;
  gap: 8px;
}

.footer-social__icon {
  width: 32px;
  height: 32px;
  border-radius: var(--radius-pill);
  background: var(--color-social-bg);
  display: flex;
  align-items: center;
  justify-content: center;
}

/* ============================================================
   TABLET + MOBILE
   Two tiers:
     <= 1200px  the nav collapses to the slide-in menu (matches the
                tablet breakpoint already used by flash-sales.css
                and getting-started.css)
     <=  640px  the mobile type scale and the 393px layout

   nav.js appends the menu markup on every viewport, so the base
   rules below must hide it explicitly — without them it renders as
   loose unstyled content beneath the footer on desktop.
============================================================ */

body {
  /* "clip" here, not "hidden" — hidden forces this element's own
     overflow-y to compute as "auto" (CSS Overflow spec: any
     overflow-x other than visible forces the other axis off
     visible too), which makes body its own independent scroll
     container. Any page whose content is even 1px taller than
     body's own box (any content filling it, not just scrollbars)
     then gets a second, body-level scrollbar nested next to the
     page's real one. "clip" clips the same horizontal bleed but
     doesn't force that — confirmed on the DinoDex page, which hit
     exactly this with a 1px mismatch. */
  overflow-x: clip;
}

.nav-toggle {
  display: none;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  width: 44px;
  height: 44px;
  padding: 0;
  border: none;
  background: none;
  cursor: pointer;
}

.nav-toggle span {
  display: block;
  width: 26px;
  height: 3px;
  border-radius: 2px;
  background: var(--color-text);
}

.mobile-menu,
.mobile-menu__overlay {
  display: none;
}

/* ============================================================
   TABLET AND BELOW — slide-in menu
============================================================ */
@media (max-width: 1200px) {
  /* Swapped for the menu, but only on pages that load nav.js —
     without this guard a page missing the script tag would be left
     with no navigation at all. */
  .has-mobile-menu .main-nav,
  .has-mobile-menu .site-header__inner > .btn-discord {
    display: none;
  }

  .has-mobile-menu .nav-toggle {
    display: flex;
    align-items: center;
  }

  /* Fallback for pages without nav.js: the nav scrolls sideways
     rather than wrapping, which would push the fixed header taller
     than --nav-height and hide content beneath it. Remove once every
     page includes nav.js. */
  body:not(.has-mobile-menu) .main-nav {
    flex: 1 1 auto;
    min-width: 0;
    flex-wrap: nowrap;
    overflow-x: auto;
    scrollbar-width: none;
  }
  body:not(.has-mobile-menu) .main-nav::-webkit-scrollbar {
    display: none;
  }

  .mobile-menu__overlay {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 40;
    background: rgba(0, 0, 0, 0.35);
    opacity: 0;
    visibility: hidden;
    transition: opacity 300ms ease-in-out, visibility 300ms ease-in-out;
  }
  .mobile-menu__overlay.is-open {
    opacity: 1;
    visibility: visible;
  }

  /* Enters from the right. 300ms ease-in-out matches Figma's Smart
     Animate default duration and its Ease In And Out curve.
     Full-bleed on a phone; a 393px drawer on a tablet, which is the
     width the menu was designed at. */
  .mobile-menu {
    display: flex;
    position: fixed;
    top: 0;
    right: 0;
    z-index: 50;
    flex-direction: column;
    width: min(100%, 393px);
    height: 100%;
    padding: 0 20px 40px;
    background: var(--color-bg);
    overflow-y: auto;
    transform: translateX(100%);
    transition: transform 300ms ease-in-out;
  }
  .mobile-menu.is-open {
    transform: translateX(0);
  }

  .mobile-menu__close {
    position: absolute;
    /* Matches the hamburger's own vertical center in the header, so the
       X lands exactly where the hamburger was. Computed instead of a
       flat value so it stays correct if --nav-height ever changes. */
    top: calc((var(--nav-height) - 44px) / 2);
    right: 8px;
    width: 44px;
    height: 44px;
    padding: 0;
    border: none;
    background: none;
    cursor: pointer;
  }
  .mobile-menu__close::before,
  .mobile-menu__close::after {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    width: 22px;
    height: 3px;
    border-radius: 2px;
    background: var(--color-text);
  }
  .mobile-menu__close::before { transform: translate(-50%, -50%) rotate(45deg); }
  .mobile-menu__close::after  { transform: translate(-50%, -50%) rotate(-45deg); }

  /* Same centering formula as the close button, just for the logo's
     own height, so the two land on the same row — the logo at the
     panel's left edge, the X at its right. */
  .mobile-menu__logo {
    position: absolute;
    top: calc((var(--nav-height) - 50px) / 2);
    left: 20px;
  }
  .mobile-menu__logo img {
    width: 50px;
    height: 50px;
    object-fit: cover;
  }

  /* margin-top is measured from the panel's own top edge, but the close
     button is absolutely positioned and overlaps part of it, so the gap
     actually visible below the close button is margin-top minus the
     close button's own bottom edge (~63px at the 82px nav-height
     baseline), not the full 116px. margin-bottom has no such overlap, so
     it's set to that same visible amount (116 - 63 = 53px) to match. */
  .mobile-menu__nav {
    margin-top: 116px;
    margin-bottom: 53px;
    display: flex;
    flex-direction: column;
  }

  /* 24px is the mobile H1 size. It is written out rather than taken
     from --text-h1-size because the menu is one component shared by
     both tiers, and above 640px that token is still the desktop 48px. */
  .mobile-menu__link {
    display: flex;
    align-items: center;
    gap: 16px;
    padding: 21px 0;
    border: none;
    background: none;
    text-align: left;
    cursor: pointer;
    color: var(--color-text-secondary);
    font-family: var(--font-body);
    font-weight: 400;
    font-size: 24px;
    line-height: 1.2;
    letter-spacing: 0.02em;
  }

  /* Current page: bold + underlined, same treatment as the desktop nav.
     The border sits on the text span (a shrink-wrapped flex item), not
     the link itself, so it hugs the word instead of the full row width. */
  .mobile-menu__link--current {
    font-weight: 700;
  }

  .mobile-menu__link--current .mobile-menu__link-text {
    border-bottom: 3px solid currentColor;
  }

  /* Split button: the chevron button is a separate, independently-
     tappable control from the link, placed right next to the text
     instead of pushed to the row's far end. */
  .mobile-menu__trigger-row {
    display: flex;
    align-items: center;
    justify-content: flex-start;
    gap: 8px;
  }
  .mobile-menu__trigger-link {
    flex: none;
    border-bottom: 3px solid transparent;
  }
  /* Expanded is underlined — keyed off the group's own open state
     rather than the chevron button's, since the link and the toggle
     are now two separate elements. Skipped when the trigger is also the
     current-page link (e.g. opening "Getting Started" while on Flash
     Sales): that case already gets its own text-hugging underline from
     .mobile-menu__link--current below, and this border would stack a
     second, lower one under the same word otherwise. */
  .mobile-menu__group.is-open .mobile-menu__trigger-link:not(.mobile-menu__link--current) {
    border-bottom-color: currentColor;
  }

  .mobile-menu__chevron-btn {
    flex: none;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    padding: 0;
    border: none;
    background: none;
    cursor: pointer;
  }

  .mobile-menu__chevron {
    width: 24px;
    height: 24px;
    /* The asset points up; 180deg is the collapsed (down) state. */
    transform: rotate(180deg);
    transition: transform 300ms ease-in-out;
  }
  .mobile-menu__chevron-btn[aria-expanded="true"] .mobile-menu__chevron {
    transform: rotate(0deg);
  }

  .mobile-menu__sub {
    max-height: 0;
    overflow: hidden;
    transition: max-height 300ms ease-in-out;
  }

  /* Indented under its parent to read as a sub-item, now that it's the
     dropdown's only entry rather than a duplicate of the trigger. */
  .mobile-menu__sublink {
    padding: 11px 0 11px 32px;
  }

  /* Shape matches Rules' mobile-menu CTA buttons. */
  .mobile-menu__cta {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    align-self: flex-start;
    height: 48px;
    padding: 12px 16px;
    border-radius: 16px;
    background: var(--color-discord-default);
    color: var(--color-text-on-dark);
    font-family: var(--font-body);
    font-weight: 700;
    font-size: 16px;
    line-height: 1.3;
    letter-spacing: 0.8px;
    text-decoration: none;
    white-space: nowrap;
  }
  .mobile-menu__cta img { width: 24px; height: 24px; }
  .mobile-menu__cta + .mobile-menu__cta { margin-top: 12px; }
  .mobile-menu__cta:last-of-type { margin-bottom: 40px; }
  /* Same shape as the Discord button, recolored with the same gradient
     blobs (blue/purple/pink/orange/yellow over a red base) as the
     Instagram CTA on the home page's closing section, so the two match. */
  .mobile-menu__cta--instagram {
    background:
      radial-gradient(circle at 15% 20%, #0c6acf 0%, transparent 42%),
      radial-gradient(circle at 35% 10%, #a139bf 0%, transparent 45%),
      radial-gradient(circle at 55% 35%, #e732b1 0%, transparent 48%),
      radial-gradient(circle at 75% 60%, #ff8f41 0%, transparent 50%),
      radial-gradient(circle at 92% 85%, #ffde46 0%, transparent 55%),
      #ff4c5b;
  }

  body.menu-open {
    overflow: hidden;
  }
}

/* ============================================================
   MOBILE — 640px and below
============================================================ */
@media (max-width: 640px) {
  :root {
    --grid-margin: 20px;

    /* Mobile type scale. Sizes plus the two line-heights that change;
       tracking is inherited from the em tokens except body (2% -> 3%). */
    --text-title-size: 48px;
    --text-h1-size: 24px;
    --text-h2-size: 20px;      --text-h2-lh: 1.2;
    --text-h3-size: 18px;
    --text-subtitle1-size: 18px;
    --text-body-size: 18px;    --text-body-ls: 0.03em;
    --text-button-size: 16px;  --text-button-lh: normal;
  }

  /* Every grid child goes full-width; the 12-column desktop layout has
     no meaning at 393px. Pages override individual items where the
     mobile design keeps them side by side. */
  [class*="col-span-"] {
    grid-column: 1 / -1;
  }

  /* ---------- Header ---------- */
  /* Was a flat 160px that centered the logo, splitting the space evenly
     above and below — but the notch/Dynamic Island only needs clearance
     at the TOP, so that symmetric bottom padding was pure excess height.
     Now: real safe-area inset at the top, a thin 16px everywhere else. */
  .has-mobile-menu {
    --nav-height: calc(env(safe-area-inset-top, 0px) + 82px);
  }

  .has-mobile-menu .site-header__inner {
    height: var(--nav-height);
    padding: calc(env(safe-area-inset-top, 0px) + 16px) var(--grid-margin) 16px;
    gap: 0;
  }

  body:not(.has-mobile-menu) {
    /* Header collapses to logo height + padding, so the offset body
       reserves for the fixed header has to shrink to match. */
    --nav-height: 70px;
  }
  body:not(.has-mobile-menu) .site-header__inner {
    height: auto;
    padding: 8px var(--grid-margin);
    gap: 16px;
  }

  .site-logo img {
    width: 50px;
    height: 50px;
  }

  /* ---------- Footer ---------- */
  .site-footer {
    background: #DEF3FF;
  }

  .footer-grass {
    height: 52px;
    background-size: 285px 52px;
    background-position:
      -98px bottom, 98px bottom, 294px bottom, 490px bottom, 686px bottom, 882px bottom,
      1078px bottom, 1274px bottom, 1470px bottom, 1666px bottom, 1862px bottom, 2058px bottom;
  }

  /* Deep bottom padding leaves the dino art its own band beneath the
     copyright, as in the frame, instead of colliding with the icons. */
  .footer-content {
    padding: 32px var(--grid-margin) 150px;
  }

  .footer-columns {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 36px 16px;
    margin-bottom: 32px;
  }

  /* The frame puts the social icons above the copyright; column-reverse
     gets that order without changing the shared markup. */
  .footer-bottom {
    flex-direction: column-reverse;
    align-items: flex-start;
    gap: 16px;
  }

  /* Small black icon-only circles become the same Discord/Instagram pill
     CTA buttons used in the mobile nav overlay (.mobile-menu__cta),
     stacked instead of side-by-side. The label text is CSS-generated so
     the shared HTML doesn't need touching — only this breakpoint sees
     it, desktop keeps the plain icon circles. */
  .footer-social {
    flex-direction: column;
    align-items: flex-start;
    gap: 12px;
  }
  .footer-social__icon {
    width: auto;
    height: 48px;
    padding: 12px 16px;
    border-radius: 16px;
    gap: 8px;
    background: var(--color-discord-default);
    color: var(--color-text-on-dark);
    font-family: var(--font-body);
    font-weight: 700;
    font-size: 16px;
    line-height: 1.3;
    letter-spacing: 0.8px;
    white-space: nowrap;
  }
  .footer-social__icon[href*="instagram"] {
    background:
      radial-gradient(circle at 15% 20%, #0c6acf 0%, transparent 42%),
      radial-gradient(circle at 35% 10%, #a139bf 0%, transparent 45%),
      radial-gradient(circle at 55% 35%, #e732b1 0%, transparent 48%),
      radial-gradient(circle at 75% 60%, #ff8f41 0%, transparent 50%),
      radial-gradient(circle at 92% 85%, #ffde46 0%, transparent 55%),
      #ff4c5b;
  }
  .footer-social__icon[href*="discord"]::after { content: "Join Our Discord"; }
  .footer-social__icon[href*="instagram"]::after { content: "Check Our Instagram"; }

  /* Mobile placements from the 393 frame. Note the asset names are
     misleading: dino-single is the blue one, dino-duo the green. */
  .footer-deco--dino-single {
    left: var(--grid-margin);
    right: auto;
    /* The desktop rule pins this one with `top`, which would otherwise
       win over `bottom` and strand it at the top of the footer. */
    top: auto;
    bottom: 24px;
    width: 56px;
    height: auto;
  }
  .footer-deco--dino-duo {
    left: 82px;
    right: auto;
    bottom: 24px;
    width: 56px;
    height: auto;
  }
  .footer-deco--flower-small-2 {
    left: 162px;
    right: auto;
    bottom: 30px;
    width: 26px;
    height: auto;
  }
  .footer-deco--flower-small-1 {
    left: 196px;
    right: auto;
    bottom: 30px;
    width: 24px;
    height: auto;
  }

  .footer-deco--cloud2-back,
  .footer-deco--cloud2-front { right: 16px; top: 120px; }
  .footer-deco--cloud3-back,
  .footer-deco--cloud3-front { right: 40px; top: 250px; }

  /* Positioned for the 1440 comp with no mobile placement; hidden
     rather than guessed at. */
  .footer-deco--flower-tl,
  .footer-deco--flower-mid,
  .footer-deco--cloud1-back,
  .footer-deco--cloud1-front {
    display: none;
  }
}

/* Respect a reduced-motion preference: the menu still opens and closes,
   it just does not animate. Matches getting-started.css. */
@media (prefers-reduced-motion: reduce) {
  .mobile-menu,
  .mobile-menu__overlay,
  .mobile-menu__sub,
  .mobile-menu__chevron {
    transition: none;
  }
}

.footer-social__icon img {
  width: 24px;
  height: 24px;
}

/* ============================================================
   SCROLL-REVEAL (fade + rise as content enters the viewport)
   Shared across every page. A page only needs:

       <script src="reveal.js" defer></script>

   then add class="reveal" to whatever content blocks should fade
   in (section headers, card grids, etc. — see reveal.js for the
   exact mechanism). Ported from the Rules page, which keeps its
   own copy of this same CSS/JS rather than loading these shared
   files, so its existing behavior is untouched.

   Scoped under html.js (added by reveal.js) so a page that hasn't
   added the script tag yet — or has JS disabled — shows .reveal
   content normally instead of leaving it invisible forever. */
.js .reveal {
  opacity: 0;
  transform: translateY(24px);
  transition: opacity 0.6s ease, transform 0.6s ease;
}
.js .reveal.is-visible {
  opacity: 1;
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  .reveal {
    opacity: 1;
    transform: none;
    transition: none;
  }
}
