/* =========================================================
   DrawQuest / Pixel Studio — site styles
   Colors & fonts mirror DrawQuest/Extensions/Color+Theme.swift
   and Font+Theme.swift so the site matches the app exactly.
   ========================================================= */

@font-face {
  font-family: "BrushUp";
  src: url("../fonts/BrushUp.ttf") format("truetype");
  font-weight: normal;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "NunitoSans";
  src: url("../fonts/NunitoSans-Regular.ttf") format("truetype");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "NunitoSans";
  src: url("../fonts/NunitoSans-Bold.ttf") format("truetype");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

:root {
  /* Backgrounds */
  --bg: #fff9f0;
  --surface: #ffffff;
  --outline: #1b2b4b;

  /* Category colors */
  --blue: #2b7fc0;
  --green: #2d9e6a;
  --orange: #e8521c;

  /* Functional accents */
  --accent-orange: #f36f40;
  --accent-yellow: #eae208;
  --accent-green: #52be61;
  --accent-blue: #324182;

  --tracing-bg: #2b3a7a;

  /* Illustrated backgrounds (from brand illustration set) */
  --sky-blue: #0ea5e9;
  --sky-blue-deep: #0193e8;
  --sky-navy: #150f3c;
  --forest-dark: #0a4501;
  --about-blue: #0071bc;

  /* Text */
  --text-primary: #1b2b4b;
  --text-secondary: #4a6080;
  --text-on-color: #ffffff;

  --radius-card: 24px;
  --radius-sm: 16px;
  --border-w: 1.5px;
  --border-soft: 1px solid rgba(27, 43, 75, 0.08);
  --shadow-sm: 0 2px 10px rgba(27, 43, 75, 0.06);
  --shadow: 0 8px 28px rgba(27, 43, 75, 0.1);
  --shadow-lg: 0 20px 48px rgba(27, 43, 75, 0.16);
}

* {
  box-sizing: border-box;
}

html {
  scroll-behavior: smooth;
}

/* One continuous full-width background layer per section, sitting
   behind the contained (max-width) content. The section itself has no
   background of its own any more - this is the only copy, so there's
   nothing to misalign and nothing to fix-position (it scrolls normally
   with the page, like everything else). Purely decorative - never
   holds text/icons/buttons. */
.bg-full {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 50%;
  width: 100vw;
  margin-left: -50vw;
  z-index: -1;
  pointer-events: none;
}
.bg-full--header {
  background: #f8931f;
}
.bg-full--hero {
  background-image:
    url("../img/decor/treeline.png"),
    linear-gradient(
      115deg,
      var(--sky-blue-deep) 0%,
      var(--sky-blue-deep) 28%,
      var(--sky-navy) 62%,
      var(--sky-navy) 100%
    );
  background-repeat: no-repeat, no-repeat;
  background-position:
    bottom center,
    center;
  background-size:
    100vw 260px,
    cover;
}
/* One shared background spans the screenshots + books sections together
   (see .grass-field wrapper) so there's a single continuous "cover" paint -
   no seam from two sections independently covering their own, different
   heights with the same image. */
.grass-field {
  position: relative;
}
.bg-full--grass-field {
  /* Reserve the bottom 300px of .grass-field for .bg-full--grass-extension
     below instead of spanning the full inset:0 .bg-full gives by default -
     so THIS layer's own box height stays exactly what it always was
     (unaffected by #books' extra +300px padding-bottom), and `cover`
     below keeps computing the exact same scale/crop it always has. This
     is what "don't touch the middle" means in practice: this rule, and
     everything it paints, is byte-for-byte what it was before the
     extension existed. */
  bottom: auto;
  height: calc(100% - 300px);
  background-color: var(--forest-dark);
  background-image: url("../img/decor/grass-texture.png");
  /* `cover` (not a fixed vw size) on purpose: a fixed size can't track
     this section's content height, which doesn't scale linearly with
     viewport width (book grid, carousel, text wrapping all contribute
     independently) - at any width where the fixed-size image falls
     short of the actual box height, `background-repeat:no-repeat`
     leaves the remainder as flat `background-color`, a visible seam
     ("strap") where the texture just stops. `cover` always rescales to
     exactly fill the box, so that can never happen. */
  background-size: cover;
  background-position: center 300px;
  background-repeat: no-repeat;
}
/* Strip of forest green (same color as treeline.png's own bottom edge)
   right under the trees, within the carousel section - the image above
   is shifted down by this same 300px via background-position, so the
   grass image's own top (not some random middle slice) continues right
   where this strip ends. */
.bg-full--grass-field::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 300px;
  background: var(--forest-dark);
}
/* Continues the grass image exactly where .bg-full--grass-field's `cover`
   crop currently leaves off, filling the +300px added to #books'
   padding-bottom (see below) with the rest of the same source image
   instead of a flat color "strap". Because .bg-full--grass-field's crop
   is driven by `cover`, which rescales to whatever height this section's
   content happens to produce, there's no fixed vw/% formula that stays
   in sync with it at every width (that mismatch is exactly what caused
   the strap before) - so background-size/-position here are written as
   CSS custom properties that script.js measures and updates to match
   .bg-full--grass-field's actual current scale on load and on resize.
   The hard-coded values are only the unset-custom-property fallback. */
.bg-full--grass-extension {
  top: auto;
  bottom: 0;
  height: 300px;
  background-color: var(--forest-dark);
  background-image: url("../img/decor/grass-texture.png");
  background-repeat: no-repeat;
  background-size: var(--grass-ext-size, cover);
  background-position: var(--grass-ext-position, center top);
}
.bg-full--about {
  background: linear-gradient(165deg, var(--sky-blue-deep), var(--about-blue));
}
.bg-full--footer {
  background: var(--outline);
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text-primary);
  font-family:
    "NunitoSans",
    -apple-system,
    BlinkMacSystemFont,
    sans-serif;
  font-size: clamp(16px, calc(13.76px + 0.32vw), 20.8px);
  /* 1.5, not the old 1.55 - tightened slightly on request, but not
     further: 1.5x is the WCAG 1.4.12 floor for body text line-spacing,
     and this is a sans-serif body face (see the frontend-design skill's
     own note that sans-serif wants less leading than serif, but this is
     already about as little as text this size should get). */
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

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

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

h1,
h2,
h3,
h4 {
  font-family: "BrushUp", cursive;
  font-weight: normal;
  color: var(--text-primary);
  margin: 0 0 0.4em;
  line-height: 1.15;
  letter-spacing: 0.3px;
}

.container {
  width: 100%;
  padding: 0 clamp(40px, 5vw, 80px);
}

.section {
  padding: 72px 0;
  position: relative;
}
.section--tight {
  padding: 48px 0;
}

.eyebrow {
  display: inline-block;
  font-family: "NunitoSans";
  font-weight: 700;
  font-size: clamp(13px, calc(11.18px + 0.26vw), 16.9px);
  letter-spacing: 1.5px;
  text-transform: uppercase;
  color: var(--blue);
  background: rgba(43, 127, 192, 0.1);
  padding: 6px 14px;
  border-radius: 999px;
  margin-bottom: 14px;
}

.section-head {
  text-align: center;
  max-width: 680px;
  margin: 0 auto 44px;
}
.section-head h2 {
  font-size: clamp(28px, calc(19px + 1.5vw), 52px);
}
.section-head p {
  color: var(--text-secondary);
  font-size: clamp(17px, calc(14.62px + 0.34vw), 22.1px);
}

/* Buttons */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  font-family: "NunitoSans";
  font-weight: 700;
  font-size: clamp(15.5px, calc(13.33px + 0.31vw), 20.15px);
  letter-spacing: 0.2px;
  padding: 14px 28px;
  border-radius: 999px;
  border: none;
  cursor: pointer;
  transition:
    transform 0.15s ease,
    box-shadow 0.15s ease,
    background 0.15s ease;
  white-space: nowrap;
}
.btn:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow-lg);
}
.btn:active {
  transform: translateY(0);
}

.btn--primary {
  background: linear-gradient(135deg, var(--accent-orange), var(--orange));
  color: #fff;
  box-shadow: var(--shadow);
}
.btn--outline {
  background: var(--surface);
  color: var(--outline);
  box-shadow: var(--shadow-sm);
  border: 1.5px solid rgba(27, 43, 75, 0.14);
}
.btn--outline:hover {
  border-color: var(--outline);
}
.btn--sm {
  font-size: clamp(14px, calc(12.04px + 0.28vw), 18.2px);
  padding: 10px 20px;
}
.btn--block {
  width: 100%;
}

.btn--disabled {
  opacity: 0.55;
  cursor: default;
  pointer-events: none;
}

.btn[data-placeholder]:after {
  content: "";
}

/* =========================
   Header
   ========================= */
.site-header {
  position: sticky;
  top: 0;
  z-index: 500;
  box-shadow: 0 2px 14px rgba(0, 0, 0, 0.12);
}

.nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-top: 8px;
  padding-bottom: 8px;
}

.brand {
  display: flex;
  align-items: center;
  gap: 10px;
}
.brand img {
  height: clamp(44px, calc(37.84px + 0.88vw), 57.2px);
  width: auto;
}
.brand span {
  font-family: "BrushUp", cursive;
  font-size: clamp(22px, calc(18.92px + 0.44vw), 28.6px);
  color: var(--outline);
}

.nav-links {
  display: flex;
  align-items: center;
  gap: 6px;
  list-style: none;
  margin: 0;
  padding: 0;
}
.nav-links a {
  display: block;
  font-weight: 700;
  font-size: clamp(15px, calc(12.9px + 0.3vw), 19.5px);
  color: #fff;
  position: relative;
  padding: 10px 14px;
  border-radius: 10px;
}
.nav-links a:hover {
  color: var(--outline);
  background: rgba(255, 255, 255, 0.18);
}

.nav-actions {
  display: flex;
  align-items: center;
  gap: 14px;
}

.header-social {
  display: flex;
  align-items: center;
  gap: 10px;
}
.header-icon {
  width: 30px;
  height: 30px;
  display: flex;
  align-items: center;
  justify-content: center;
  filter: drop-shadow(0 2px 6px rgba(0, 0, 0, 0.2));
  transition: transform 0.15s ease;
}
.header-icon:hover {
  transform: translateY(-2px);
}
.header-icon img {
  width: 100%;
  height: 100%;
  display: block;
}
.header-icon[aria-disabled="true"] {
  cursor: default;
}

.nav-toggle {
  display: none;
  flex-direction: column;
  gap: 5px;
  background: none;
  border: none;
  cursor: pointer;
  padding: 8px;
}
.nav-toggle span {
  width: 24px;
  height: 3px;
  background: #fff;
  border-radius: 2px;
}

/* =========================
   Hero
   ========================= */
.hero {
  padding: 24px 0 266px;
  position: relative;
}
.hero::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image: url("../img/decor/stars-texture.png");
  background-repeat: no-repeat;
  background-position: right top;
  background-size: 115% auto;
  pointer-events: none;
  z-index: 0;
}
.hero::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 260px;
  background-image: url("../img/decor/treeline.png");
  background-repeat: no-repeat;
  background-position: bottom center;
  background-size: 100vw 260px;
  pointer-events: none;
  z-index: 1;
}
.hero .container {
  position: relative;
  z-index: 101;
}
/* At rest (page not scrolled), the hero visual sits above the sticky
   header instead of clipping behind it. Once scrolling starts, this
   override drops away and the header covers it normally, as it scrolls
   up and out of view like any other content. */
body:not(.is-scrolled) .hero .container {
  z-index: 600;
}
.hero-critters {
  position: absolute;
  inset: 0;
  /* Always below .hero .container's lower bound (101, see its
     body:not(.is-scrolled) override above) in both scroll states, so
     critters that are meant to sit behind the phone/text (like
     .critter-cloud2) stay behind consistently instead of popping in
     front once .container drops from 600 to 101 on scroll - still
     above the ground layer (.hero-bear, .hero-cloud-behind, the
     ::after treeline), which all sit at z-index 0-1. */
  z-index: 2;
  pointer-events: none;
  overflow: hidden;
  border-radius: inherit;
}
.critter {
  position: absolute;
  display: block;
}
.hero-critters .critter-cloud1 {
  width: clamp(130px, calc(111.8px + 2.6vw), 169px);
  top: 10px;
  left: -2%;
  opacity: 0.92;
}
.hero-critters .critter-cloud2 {
  width: clamp(500px, calc(430px + 10vw), 680px);
  top: 40px;
  left: 8%;
  opacity: 0.9;
}
.hero-critters .critter-bee1 {
  width: clamp(100px, calc(86px + 2vw), 130px);
  top: 20px;
  left: 13%;
  filter: drop-shadow(0 6px 10px rgba(0, 0, 0, 0.18));
}
.hero-critters .critter-cloud3 {
  width: clamp(140px, calc(120.4px + 2.8vw), 182px);
  top: -10px;
  left: 44%;
  opacity: 0.92;
}
/* Mobile-only fourth cloud (desing/Website-mobile_02 shows one drifting
   in from the left edge, tucked behind/below the store-pill button) -
   no desktop equivalent, so hidden by default and only given a size/
   position in the mobile media query below. */
.hero-critters .critter-cloud4 {
  display: none;
}
.critter-bat {
  filter: drop-shadow(0 2px 5px rgba(0, 0, 0, 0.45));
}
.hero-critters .critter-bat2 {
  width: clamp(34px, calc(29.24px + 0.68vw), 44.2px);
  top: 12%;
  left: 93%;
  opacity: 1;
}
.hero-critters .critter-bat3 {
  width: clamp(24px, calc(20.64px + 0.48vw), 31.2px);
  top: 7%;
  left: 95%;
  opacity: 0.9;
}
.hero-critters .critter-bat4 {
  width: clamp(24px, calc(20.64px + 0.48vw), 31.2px);
  top: 9%;
  left: 98%;
  opacity: 0.85;
}
.hero-critters .critter-bat6 {
  width: clamp(30px, calc(25.8px + 0.6vw), 39px);
  top: 23%;
  left: 97%;
  opacity: 0.9;
}
/* bat8-12 used to live here, positioned as top:%/left:% of .hero-critters
   (i.e. of .hero's own box). That tracked .hero's CONSTANT 950px height
   fine vertically, but they decorate the open sky pocket between the
   phone and screens cluster - the same pocket .hero-tools and
   .hero-cloud-behind sit in - and that pocket's actual position/size is
   governed by .hero-phone-frame's clamp()'d width (nonlinear: frozen at
   its max above ~1058px of .hero-visual's cqw, still shrinking below
   it). A flat %-of-.hero position doesn't track that nonlinear curve,
   so this cluster drifted out of the pocket (and into .hero-tools' text)
   at in-between desktop widths while .hero-tools (see its own note
   below) drifted the opposite way. Fix: nest them in .hero-phone-frame
   like .hero-cloud-behind, positioned/sized as a % of ITS own box, which
   already scales exactly with the phone at every width - see that
   element's comment for why this works. Values below reproduce the
   original hand-tuned look pixel-for-pixel at 1920px (where the frame
   sits at its clamp max, same as the old fixed-px values were tuned
   against), derived from each bat's rotated bounding-box center so the
   rotate() transforms below keep the same visual anchor post-move.
   Moved out of index.html's .hero-critters block into .hero-phone-frame. */
.hero-phone-frame .critter-bat8 {
  position: absolute;
  top: 82.6%;
  left: 107.55%;
  width: clamp(23.4px, 3.03cqw, 32px);
  height: auto;
  opacity: 0.95;
  pointer-events: none;
}
.hero-phone-frame .critter-bat9 {
  position: absolute;
  top: 87.8%;
  left: 89.4%;
  width: clamp(36.5px, 4.73cqw, 50px);
  height: auto;
  opacity: 0.85;
  transform: rotate(20deg);
  pointer-events: none;
}
.hero-phone-frame .critter-bat10 {
  position: absolute;
  top: 75.7%;
  left: 107.55%;
  width: clamp(21.9px, 2.84cqw, 30px);
  height: auto;
  opacity: 0.85;
  transform: rotate(20deg);
  pointer-events: none;
}
.hero-phone-frame .critter-bat11 {
  position: absolute;
  top: 75.7%;
  left: 93.9%;
  width: clamp(21.9px, 2.84cqw, 30px);
  height: auto;
  opacity: 0.9;
  transform: rotate(-20deg);
  pointer-events: none;
}
.hero-phone-frame .critter-bat12 {
  position: absolute;
  top: 84.4%;
  left: 121.2%;
  width: clamp(36.5px, 4.73cqw, 50px);
  height: auto;
  transform: rotate(20deg);
  pointer-events: none;
}
.hero-critters .critter-bat13 {
  width: clamp(55px, calc(47.3px + 1.1vw), 71.5px);
  top: 61%;
  left: 95%;
}

.hero-critters .critter-bat14 {
  width: clamp(50px, calc(43px + 1vw), 65px);
  top: 73%;
  left: 98%;
  opacity: 0.85;
}
.hero-critters .critter-owl {
  width: clamp(210px, calc(180.6px + 4.2vw), 273px);
  left: -8px;
  /* bottom:25px is the no-JS fallback (matches the old fixed look).
     script.js measures the store-pill button's actual position (which
     moves as the paragraph above it reflows to more/fewer lines) and
     sets an inline `top` + `bottom:auto` once it runs, keeping a
     constant gap between this owl and the button instead of the two
     drifting apart as .hero's fixed-950px-tall box and the button's
     reflow-dependent position change independently. See
     syncOwlGap() for the reasoning. */
  bottom: 25px;
  transform: scaleX(-1);
  filter: drop-shadow(0 10px 14px rgba(0, 0, 0, 0.25));
}

.hero-bear {
  position: absolute;
  width: clamp(195px, calc(167.7px + 3.9vw), 253.5px);
  left: 47%;
  bottom: 80px;
  z-index: 0;
  pointer-events: none;
  filter: drop-shadow(0 6px 10px rgba(0, 0, 0, 0.2));
}

/* Nested in .hero-phone-frame (same reasoning as the bat/bird note
   below): this cloud needs to sit a constant distance below the orange
   phone at every width, which top:%/bottom:px relative to .hero (fixed
   950px tall) can't do once the phone itself starts shrinking inside a
   box that doesn't shrink with it - confirmed the gap between the two
   went from 75px at 1512px wide to 232px at 1024px. Position is cqw (%
   of the frame's own width) on every axis so it scales and stays
   anchored to the phone exactly, the same as the bat/bird above it. */
.hero-phone-frame .hero-cloud-behind {
  position: absolute;
  top: 90%;
  left: -10%;
  width: 90%;
  height: auto;
  opacity: 0.9;
  pointer-events: none;
}

/* The bat/bird decorating the orange phone, and the two bats decorating
   the screens cluster, used to be positioned with top:%/px relative to
   .hero (fixed at 950px tall) or .hero-critters (also pinned to .hero's
   height) so they could escape .hero-critters' own stacking context and
   sit above the phone artwork (see the stacking-context note at the top
   of this file). That worked for z-index, but .hero's height is
   deliberately CONSTANT while the phone/screens images shrink
   continuously as the viewport narrows (width:%, capped by their own
   max-width clamp()s) - so a top:% of .hero drifts further and further
   below the phone/screens as they shrink, ending up visibly detached at
   narrow desktop widths (confirmed: the bird's top edge was 112px below
   the phone's bottom edge at 1000px wide, having sat neatly over the
   phone at 1920px).

   The fix: nest each decoration inside the actual image box it needs to
   track (a wrapper div around the phone image, and the already-existing
   .hero-screens div around the screen images), and position it with
   top/left as a % of THAT box. Since those boxes scale exactly with the
   artwork (the wrapper shrink-wraps the phone img; .hero-screens' own
   height is tied to its width via aspect-ratio), the decoration is
   geometrically locked to the same spot on the artwork at every width -
   no curve-fitting, no reference widths to go stale. It also sidesteps
   the z-index escape problem entirely: nested here, each decoration is
   just a later-painted sibling of the image it sits on, so plain source
   order (or a small local z-index) puts it on top with no need to beat
   .container's scroll-toggled baseline from outside. */
.hero-phone-frame {
  position: absolute;
  /* Both this width and .hero-screens' width below are expressed in cqw
     (this element's own % of .hero-visual's container-query width), not
     vw, so the two never drift apart or cap at different points as the
     viewport changes (that was the earlier bug: gap went from -2% of
     .hero-visual's width at 1920px to -4.1% at 1500px instead of
     staying constant).
     The % values (40/55, phone/screens below) are chosen to hold the
     SAME overlap - (phone% + screens% + .hero-screens' `right`%) - 1 -
     as the original hand-tuned 35/48/22 combo: (0.35+0.48+0.22-1 =
     0.05, a +5%-of-.hero-visual-width gap - enough to keep the phone
     and screens visually separate instead of the raw design's -16%
     clash, see .hero-screens' own note). 40+55+... with `right` pulled
     in to 10% below reproduces that exact same 0.05 - bigger phone and
     screens, filling the space `right: 22%` used to reserve as empty
     sky, with no more overlap than the original tuning had. Only the
     *scale* changed, not the relationship between the pieces, so this
     is safe to apply at every desktop width, not just a narrow band.

     Ceiling raised again (423px -> 465px, ~10%) once the headline/lede
     shrank (see .hero-title-sub/-main and .hero p.lede above) freed up
     real sky at wide desktop widths where this was already maxed out -
     floor and the 40cqw rate are untouched, so narrower widths (where
     this isn't at its ceiling yet) are unaffected; only >~1163px of
     .hero-visual's own width (465/0.4) sees it bigger than before. */
  width: clamp(309px, 40cqw, 465px);
  left: 0;
  top: 26px;
  z-index: 6;
}
.hero-phone-frame .hero-phone-mockup {
  position: static;
  width: 100%;
  height: auto;
  display: block;
  filter: drop-shadow(0 20px 30px rgba(0, 0, 0, 0.3));
}
.hero-phone-frame .hero-bird-front {
  position: absolute;
  top: 77.5%;
  left: -10.6%;
  width: 48.7%;
  height: auto;
  transform: scaleX(-1);
  filter: drop-shadow(0 8px 12px rgba(0, 0, 0, 0.2));
  pointer-events: none;
}
.hero-phone-frame .hero-bat-front {
  position: absolute;
  top: 55.2%;
  left: 77%;
  width: 32.75%;
  height: auto;
  transform: rotate(-20deg);
  pointer-events: none;
  filter: drop-shadow(0 2px 5px rgba(0, 0, 0, 0.45));
}
/* Same reasoning as the bat/bird/cloud above: nested in .hero-phone-frame
   and positioned in cqw so this bee stays above the phone's top edge by
   a constant margin at every width, instead of drifting down INTO the
   phone as it shrinks (it was overlapping the phone's top by 30px at
   1512px wide, and by over 100px at 1024px). */
.hero-phone-frame .critter-bee3 {
  position: absolute;
  top: 6%;
  left: 59.5%;
  width: 18%;
  height: auto;
  transform: scaleX(-1);
  filter: drop-shadow(0 6px 10px rgba(0, 0, 0, 0.18));
}
.hero-screens .hero-bat-front-2 {
  position: absolute;
  top: -5%;
  left: 96%;
  width: 15.7%;
  height: auto;
  z-index: 6;
  pointer-events: none;
  transform: rotate(20deg);
}
.hero-screens .hero-bat-front-3 {
  position: absolute;
  top: 8.3%;
  left: 105%;
  width: 10%;
  height: auto;
  z-index: 6;
  pointer-events: none;
  transform: rotate(-20deg);
}
/* Mobile-only group, scattered below the screens fan (see their own
   rules in the 1024px query) - no desktop equivalent, so hidden by
   default like .critter-cloud4 above. */
.hero-screens .hero-bat-front-4,
.hero-screens .hero-bat-front-5,
.hero-screens .hero-bat-front-6,
.hero-screens .hero-bat-front-7,
.hero-screens .hero-bat-front-8,
.hero-screens .hero-bat-front-9,
.hero-screens .hero-bat-front-10,
.hero-screens .hero-bat-front-11,
.hero-screens .hero-bat-front-12 {
  display: none;
}

/* Sits in the open dark-sky pocket below the orange phone mockup and
   left of the screens cluster, above the bear. `left` stays in cqw -
   i.e. as a % of .hero-visual's own width - same reasoning as
   .hero-phone-frame: .hero-visual's width is the one thing both the
   phone and screens cluster ultimately scale from.

   `top` was tried two ways and both anchored it to the wrong thing:
   plain cqw (44.17cqw, the original) is linear in .hero-visual's width,
   but the phone's own height is a clamp() of that width - nonlinear -
   so the text drifted away from the phone as the window narrowed. Tying
   `top` directly to the phone's clamp() formula instead (a later
   attempt) fixed that drift but turned out wrong in the other
   direction: once the phone/screens genuinely get tight for space
   (around the 1024-1100px band), text glued to the phone's bottom edge
   gets uncomfortably close to it and to the bat cluster in the same
   pocket (see .hero-phone-frame .critter-bat8 etc. above).

   Fix: don't anchor to the phone at all - anchor to the bear instead,
   the same way .hero-bear itself anchors to .hero (`bottom: 110px` off
   .hero's own CONSTANT ~950px height, see the "Don't grow .hero's
   height" note at the top of this file). .hero-visual's own box is
   equally constant here: top offset from .hero is a fixed 24px and its
   min-height is a fixed 660px at every desktop width (confirmed via
   getBoundingClientRect at 1280/1920), so a plain `bottom: Npx` off
   .hero-visual's bottom behaves exactly like .hero-bear's fixed anchor
   - it doesn't move at all as the viewport narrows, regardless of how
   much the phone/screens shrink, which keeps a constant buffer above
   the bear's head instead of chasing the phone into the cramped zone. */
.hero-tools {
  position: absolute;
  bottom: 30px;
  left: 33.28cqw;
  max-width: 200px;
  z-index: 10;
  color: #fff;
}
.hero-tools p {
  margin: 0 0 6px;
  font-size: clamp(16px, calc(13.76px + 0.32vw), 20.8px);
  text-shadow: 0 2px 8px rgba(0, 0, 0, 0.5);
}

.hero-tools strong {
  color: var(--accent-white);
}
.hero-tools-star {
  display: inline-block;
  width: 40px;
  height: auto;
  vertical-align: -4px;
  margin-left: 10px;
}
.hero-tools-icons {
  display: block;
  width: 160px;
  height: auto;
  margin-top: 8px;
}

.hero-grid {
  display: grid;
  grid-template-columns: minmax(0, 0.6fr) minmax(0, 1.4fr);
  gap: 24px;
  align-items: start;
}
.hero-grid > * {
  min-width: 0;
}
.hero-grid > div:first-child {
  position: relative;
  padding-top: 90px;
  /* The headline below is sized in cqw (this container's own inline
     size), not vw, because vw and this column's actual width don't
     scale in lockstep - the column is 0.6fr of (viewport minus a
     clamp()'d padding minus the grid gap), which isn't a fixed
     fraction of vw once the padding clamp is mid-range (roughly
     981-1600px wide). A vw-sized headline drifts wider than its own
     column there and visibly overflows into .hero-visual/the phone
     mockup next to it (confirmed at 1024-1280px). Sizing from the
     column's own rendered width instead makes that impossible by
     construction, at any width. */
  container-type: inline-size;
}

.hero-title {
  margin: 0 0 20px;
  line-height: 1;
  text-transform: uppercase;
}
.hero-title-sub {
  display: block;
  font-size: clamp(40px, 15.8cqw, 91px);
  color: #fff;
  /* em, not a flat 5px: the offset needs to stay a small fraction of the
     letterforms to read as a tight drop-shadow. A fixed 5px is ~5.5% of
     the 91px desktop ceiling (reads fine, tight to the letters) but
     ~18-20% of the ~25-28px this shrinks to on mobile - at that ratio it
     stops looking like a shadow and reads as a second, offset copy of
     the word instead ("double sign"). 0.055em is 5px at the 91px
     ceiling (pixel-matches the old look there) and scales down with the
     text at every size in between. */
  text-shadow: 0.055em 0.055em 0 var(--accent-yellow);
  margin-bottom: 6px;
}
.hero-title-main {
  display: block;
  font-size: clamp(40px, 15.8cqw, 91px);
  color: #fff;
  text-shadow: 0.055em 0.055em 0 var(--accent-yellow);
}
.hero-title-accent {
  color: var(--accent-yellow);
  text-shadow: 0.055em 0.055em 0 #fff;
}

.hero p.lede {
  /* Was clamp(13px,...,20px) - smaller than body's own
     clamp(16px,...,20.8px) at every width for no documented reason.
     Matched to body's so hero copy isn't quietly smaller than the
     rest of the site's paragraphs. */
  font-size: clamp(16px, calc(13.76px + 0.32vw), 20.8px);
  color: rgba(255, 255, 255, 0.9);
  max-width: 48ch;
  margin-bottom: 28px;
  text-align: justify;
}


.store-pill {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  background: rgba(255, 255, 255, 0.15);
  backdrop-filter: blur(6px);
  border: 1.5px solid rgba(255, 255, 255, 0.85);
  color: #fff;
  border-radius: 999px;
  padding: clamp(12px, 1vw, 18px) clamp(26px, 2vw, 36px);
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.15);
  font-weight: 700;
  font-size: clamp(14px, calc(12.04px + 0.28vw), 18.2px);
}
/* The real App Store badge icon isn't the Apple company logo (a plain
   apple silhouette, what the old inline SVG here drew) - it's its own
   distinct glyph (a stylized "A" with a leaf/heart at the apex), see
   the template (desing/Website-mobile_02.jpg). Extracted from the
   existing navy appstore.png (used elsewhere for the header's outlined
   App Store icon) via connected-component separation - that asset's
   navy ring-badge outline and the "A" glyph are two disconnected
   shapes sharing the same navy+alpha encoding, so isolating the
   glyph's own connected region and recoloring its alpha mask to white
   (rather than redrawing it by eye) gets the exact real glyph instead
   of an approximation. See assets/img/decor/appstore-a-white.png. */
.store-pill-icon {
  width: 30px;
  height: auto;
  flex-shrink: 0;
}
.store-pill-text {
  text-align: left;
  line-height: 1.25;
}
.store-pill-text small {
  display: block;
  font-size: clamp(12.5px, calc(10.75px + 0.25vw), 16.25px);
  font-weight: 600;
  opacity: 0.92;
}
/* text-transform:uppercase (not literal caps in the markup, "App
   Store") to match the template's "APP STORE" treatment while keeping
   the actual copy - "Coming soon to the" - as-is; this button is
   aria-disabled pending the real App Store listing, so the template's
   "Available on the" wording doesn't apply here, just its look. */
.store-pill strong {
  display: block;
  font-family: "BrushUp", cursive;
  font-size: clamp(19px, calc(16.34px + 0.38vw), 24.7px);
  letter-spacing: 0.3px;
  text-transform: uppercase;
}
.store-pill[aria-disabled="true"] {
  cursor: default;
}
/* Nested in the text column (.hero-grid > div:first-child, already a
   container - see its container-type note above) instead of
   .hero-critters, and positioned in cqw: this bee sits right at the
   text column's bottom-right corner, in the gap before the phone. Its
   old position (top:450px/left:30% of .hero-critters, pinned to .hero's
   constant 950px height) didn't track the text column at all, just
   happened to roughly line up with it at one width. */
.hero-grid > div:first-child .critter-bee2 {
  position: absolute;
  /* `bottom: 30.2%` was meant to track the store-pill button below it
     (percent-of-the-column's-own-height instead of a width-based guess,
     to survive the paragraph above wrapping to a different number of
     lines) - but the column's total height is exactly what changes in
     steps as it wraps, so 30.2% of it isn't actually constant either:
     confirmed via getBoundingClientRect, the bee drifted away from the
     button as the viewport narrowed and had dropped entirely below the
     phone's own bottom edge by 1025px wide ("drops when you downsize").
     script.js' syncBeeGap() now measures the button's actual position on
     load/resize (>1024px) and sets `top` in px directly, same fix as
     syncOwlGap() for the owl below - this `bottom` value only matters as
     the pre-JS/no-JS fallback. */
  bottom: 30.2%;
  left: 99.26cqw;
  width: 15cqw;
  height: auto;
  filter: drop-shadow(0 6px 10px rgba(0, 0, 0, 0.18));
}

/* Hero phone visual */
.hero-visual {
  position: relative;
  display: block;
  min-width: 0;
  max-width: 100%;
  min-height: 660px;
  container-type: inline-size;
}
.hero-screens {
  position: absolute;
  /* See .hero-phone-frame's note above: cqw (not vw) keeps this in sync
     with the phone at every width. 55/10 (not the original 48/22, and
     not the raw design's 60/12) keeps the SAME +5%-of-.hero-visual-width
     overlap as 48/22 did (see the math in .hero-phone-frame's note) -
     bigger, and using the empty sky `right: 22%` used to reserve,
     without clashing any more than the original 48/22 tuning did.
     Ceiling raised 625px -> 688px alongside .hero-phone-frame's, same
     reasoning (freed by the smaller headline/lede) and same floor/rate
     left untouched. .hero-caption-layer's width must stay identical to
     this value - see its own rule. */
  width: clamp(447px, 55cqw, 688px);
  aspect-ratio: 766 / 1002;
  right: 10%;
  top: 10px;
  z-index: 1;
}
.hero-screen {
  position: absolute;
  width: 31%;
  height: 51.5%;
  border-radius: 15% / 7%;
  transform-origin: top left;
  transform: rotate(-25.44deg);
  box-shadow: 0 12px 26px rgba(0, 0, 0, 0.4);
  object-fit: cover;
  display: block;
}
.hero-screen-8 {
  left: 0%;
  top: 18.6%;
  z-index: 1;
}
.hero-screen-7 {
  left: 25.4%;
  top: 0%;
  z-index: 2;
}
.hero-screen-6 {
  left: 29.9%;
  top: 66.9%;
  z-index: 3;
}
.hero-screen-9 {
  left: 69%;
  top: 10.3%;
  z-index: 4;
}
.hero-screen-10 {
  left: 56.2%;
  top: 48.5%;
  z-index: 5;
}

.hero-caption {
  position: absolute;
  margin: 0;
  color: #fff;
  font-size: clamp(19px, calc(16.34px + 0.38vw), 24.7px);
  line-height: 1.3;
  max-width: 170px;
  z-index: 103;
  text-shadow: 0 2px 8px rgba(0, 0, 0, 0.5);
}
.hero-caption strong {
  color: var(--accent-white);
}
/* These two used to be nested directly inside .hero-screens, positioned
   as a % of its box so they'd track the shrinking screens cluster. That
   tracking was right, but nesting them there trapped their z-index:103
   (inherited from .hero-caption) inside .hero-screens' own stacking
   context (.hero-screens has z-index:1) - a child's z-index can't escape
   its parent's stacking context (see the note on .hero-critters at the
   top of this file), so 103 only ever won against the screen tiles
   (z-index 1-5) *inside* that context. Against .hero-phone-frame, a
   SIBLING context rooted at z-index:6, what mattered was .hero-screens'
   own 1 vs .hero-phone-frame's 6 - .hero-screens lost outright, captions
   included, so the orange phone mockup's image (opaque near its base)
   painted over this text whenever the phone's bounding box swung over
   that corner (visible at ~1025-1200px wide, where the 0.4fr/1.6fr grid
   override - see Responsive below - changes how much the phone/screens
   overlap there; invisible at 1920px+ purely by accident of where the
   phone's opaque pixels happened to fall).

   Fix: wrap both in .hero-caption-layer (see its own rule below), a
   sibling of .hero-screens and .hero-phone-frame instead of a child of
   either, with a z-index that beats .hero-phone-frame's 6 outright. The
   wrapper duplicates .hero-screens' own box geometry exactly (same
   width/aspect-ratio/position formula, see that rule's comment), so
   these two keep the exact %-of-box values they had when nested directly
   in .hero-screens - no repositioning needed, just a different (and
   correctly stacked) container. */
.hero-caption--top {
  top: 0%;
  right: 80.5%;
  max-width: 100px;
  font-size: clamp(14px, calc(12.04px + 0.28vw), 18.2px);
  text-align: left;
}
.hero-caption--top strong {
  color: #fff;
}
.hero-caption--mid {
  top: 66%;
  left: -4%;
  text-align: left;
}
.hero-caption--right {
  top: 55.8%;
  font-size: clamp(14px, calc(12.04px + 0.28vw), 18.2px);
  right: -30.2%;
  text-align: left;
}
/* Duplicates .hero-screens' own box (position/width/aspect-ratio/right/
   top) on purpose - see the comment on .hero-caption--top above for why
   this needs to be a separate element rather than nested inside
   .hero-screens itself. Keep this in sync with .hero-screens' rule if
   that one ever changes; pointer-events:none so it never blocks clicks
   on the screens underneath, and the z-index only needs to beat
   .hero-phone-frame's 6. */
.hero-caption-layer {
  position: absolute;
  width: clamp(447px, 55cqw, 688px);
  aspect-ratio: 766 / 1002;
  right: 10%;
  top: 10px;
  z-index: 20;
  pointer-events: none;
}

/* =========================
   Screenshots / carousels
   ========================= */
#screenshots {
  position: relative;
  padding: 6px 0 6px;
}

.shots-block {
  margin-top: 0;
}
/* Description (left) and the iPhone/iPad tabs (right), sharing one row
   below the carousel - see .shots-detail/.shots-tabs below. Stacks on
   narrow screens (760px query) where there isn't room for both side by
   side without squeezing the tab labels. */
.shots-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 24px;
  margin-top: 28px;
}
.shots-tabs {
  display: inline-flex;
  flex-shrink: 0;
  background: rgba(255, 255, 255, 0.18);
  backdrop-filter: blur(6px);
  border-radius: 999px;
  padding: 4px;
}
.shots-tab {
  font-family: "NunitoSans";
  font-weight: 700;
  font-size: clamp(14.5px, calc(12.47px + 0.29vw), 18.85px);
  padding: 9px 22px;
  border-radius: 999px;
  cursor: pointer;
  border: none;
  background: transparent;
  color: rgba(255, 255, 255, 0.85);
  transition:
    background 0.15s ease,
    color 0.15s ease,
    box-shadow 0.15s ease;
}
.shots-tab.is-active {
  background: var(--surface);
  color: var(--outline);
  box-shadow: var(--shadow-sm);
}

/* App blurb, top-left of .shots-head - sits on the same dark grass-field
   background as .shots-tabs next to it, so the copy needs the same
   white-on-dark treatment rather than the default .section-head (tuned
   for the light --bg sections), which would be unreadable here. */
.shots-detail {
  flex: 1;
  min-width: 0;
  max-width: 920px;
  margin: 0;
  text-align: justify;
}
.shots-detail p {
  color: rgba(255, 255, 255, 0.92);
  /* Matched to body's clamp(16px,...,20.8px) - was clamp(14px,...,
     17.5px), smaller than body at every width for no documented
     reason. */
  font-size: clamp(16px, calc(13.76px + 0.32vw), 20.8px);
  margin: 0 0 12px;
}
.shots-detail p:last-child {
  margin-bottom: 0;
}

.carousel-wrap {
  position: relative;
}
/* Set by script.js only on the iPhone screenshots wrap, only once its 6
   (narrow, portrait) shots already fit the track without overflowing -
   at that point a left-aligned, always-scrollable strip with arrows
   that do nothing just reads as broken UI. Centers the strip and drops
   the arrows instead. The iPad wrap never gets this class - its shots
   are wider (landscape) and still need to scroll even on wide screens,
   so it keeps its normal left-aligned, arrow-driven carousel. */
.carousel-wrap.is-fit .carousel {
  justify-content: center;
}
.carousel-wrap.is-fit .carousel-nav {
  display: none;
}
.carousel {
  display: flex;
  /* 20px (was 4px) so each tile reads as a separate screenshot. */
  gap: 20px;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  padding: 6px 6px 18px;
  /* No -webkit-overflow-scrolling:touch - obsolete (native since iOS 13)
     and a suspect in the unresolved rounded-corners bug, see CLAUDE.md. */
  scrollbar-width: thin;
  scrollbar-color: var(--outline) transparent;
}
.carousel::-webkit-scrollbar {
  height: 8px;
}
.carousel::-webkit-scrollbar-thumb {
  background: rgba(27, 43, 75, 0.25);
  border-radius: 8px;
}

.carousel figure {
  flex: 0 0 auto;
  scroll-snap-align: start;
  margin: 0;
  /* border-radius/clip-path here never actually clipped on real iPhone/
     iPad Safari (worked on desktop only) - confirmed 2026-10-14 it's
     tied to whether the carousel track is actually scrolling (iPhone
     tiles fit without overflow and rounded fine; iPad tiles overflow,
     momentum-scroll, and that's where WebKit drops the clip). Three
     CSS-only attempts (will-change, translateZ+backface-visibility,
     then a real mask-image corner-cutout) all failed on-device, so the
     clip/compositing layer itself isn't reliable here at all. Real fix
     now lives in the asset: the source images are .webp with the
     rounded corners baked in as true alpha transparency (see
     assets/img/screenshots/{iphone,ipad}/*.webp and CLAUDE.md), so
     nothing in WebKit needs to clip anything at paint time. These
     CSS properties stay only as an inexpensive belt-and-suspenders
     fallback (e.g. a future non-pre-rounded image dropped in here). */
  border-radius: 20px;
  overflow: hidden;
  clip-path: inset(0 round 20px);
}
/* 20px (was 10px, barely visible) so corners actually read as rounded. */
.carousel img {
  height: clamp(480px, calc(412.8px + 9.6vw), 624px);
  width: auto;
  max-width: none;
  border-radius: 20px;
}

.carousel-nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 42px;
  height: 42px;
  border-radius: 50%;
  background: var(--surface);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  box-shadow: var(--shadow);
  z-index: 2;
}
.carousel-nav--prev {
  left: -8px;
}
.carousel-nav--next {
  right: -8px;
}
.carousel-nav svg {
  width: 18px;
  height: 18px;
}

/* Clicking a screenshot opens it in the shared sample-pages lightbox
   (see script.js' screenshot-lightbox block and #lightbox below) - same
   popup used by the book "Show sample pages" buttons. */
.carousel figure img {
  cursor: pointer;
}

/* =========================
   Books
   ========================= */
#books {
  position: relative;
  /* Just enough room for .critter-motorcycle to ride without clipping
     or overlapping .books-grid above it: the bike's own height
     (width * 0.67, its native aspect ratio) tops out at ~322px at this
     breakpoint's clamp ceiling (--moto-w maxes at 480px), so 360px
     leaves a consistent ~35-90px gap above it depending on viewport
     width, not the ~230px dead grass strip a flat 500px left. This is
     independent of .bg-full--grass-extension's own fixed 300px height
     below (see that rule's comment) - extension is always anchored to
     #books' actual bottom edge and sized on its own, so shrinking this
     padding doesn't require touching it. */
  padding-bottom: 360px;
}
#books .container {
  position: relative;
  z-index: 2;
}
/* Text and colors match the design reference exactly (sampled from both
   desing/Website-desktop_001.jpg and Website-mobile_02.jpg - same
   treatment in both): "How to Draw" white, "Book Series" the site's
   existing --accent-yellow, both sharing the same navy drop shadow
   (#324182, rgb(50,65,130) - also the exact navy already baked into
   the header's Amazon/TikTok/App Store icons, confirmed by sampling
   those too, though there's no shared CSS variable for it). text-shadow
   is inherited, so setting it once on the h2 covers the yellow span
   below without repeating it. */
#books .section-head h2 {
  color: #fff;
  text-shadow: 3px 3px 0 #324182;
  /* ~1.3x the shared .section-head h2 clamp(28px,...,52px) - asked to
     read bigger than the other section headings at every width, not
     just on desktop, so the floor/ceiling/vw-rate are all scaled up
     together rather than just raising the ceiling. Reverted back down
     on phone specifically (<700px) - see the override below - where
     the bigger size crowded .critter-trex above the book grid; tablet
     (700-1024px) and desktop both keep this larger size, confirmed
     good at both. */
  font-size: clamp(36px, calc(25px + 2vw), 68px);
}
#books .section-head h2 .books-title-accent {
  color: var(--accent-yellow);
}
#books .section-head p {
  color: rgba(255, 255, 255, 0.9);
}

.books-critters {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  overflow: hidden;
}
/* Drives back and forth across the section with a mid-ride flip; `left`
   is a fixed 0 reference point for the animation's vw-based `translate`
   values. --moto-w duplicates the width as a custom property so the
   @keyframes below can calc() off it - keep both in sync if it changes. */
.books-critters .critter-motorcycle {
  --moto-w: clamp(320px, calc(270px + 7vw), 480px);
  width: var(--moto-w);
  bottom: 0;
  left: 0;
  filter: drop-shadow(0 8px 12px rgba(0, 0, 0, 0.25));
  animation:
    motorcycle-move 7s linear infinite,
    motorcycle-flip 7s linear infinite;
  will-change: translate, rotate;
}
/* Position and rotation are two separate animations (driving the
   standalone `translate`/`rotate` properties, not `transform` - CSS
   Transforms Level 2) so the flip's timing window can be tuned
   independently of the ride's own left-to-right motion. Both
   animations share the same 7s duration and loop in lockstep, so the
   flip always lands at the same point in the ride; both are constant-
   speed (`linear`). */
@keyframes motorcycle-move {
  /* calc(50vw - var(--moto-w)/2) centers the bike at true page-middle;
     +/-100vw clears it fully past either edge at any viewport. */
  0% {
    translate: calc(50vw - (var(--moto-w) / 2) + 100vw) 0;
  }
  100% {
    translate: calc(50vw - (var(--moto-w) / 2) - 100vw) 0;
  }
}
/* Rides right-to-left, matching the artwork's own facing direction. The
   flip is centered on 50% of the shared 7s duration (page's horizontal
   middle, see motorcycle-move), 45-55% window for a quick snap. Constant
   speed (linear) - an eased version was tried and rejected. */
@keyframes motorcycle-flip {
  0% {
    rotate: 0deg;
  }
  45% {
    rotate: 0deg;
  }
  55% {
    rotate: 360deg;
  }
  100% {
    rotate: 360deg;
  }
}
@media (prefers-reduced-motion: reduce) {
  .books-critters .critter-motorcycle {
    animation: none;
    left: 2%;
  }
}
/* Top-right corner, above the book cards. */
.books-critters .critter-trex {
  width: clamp(290px, calc(244px + 6.5vw), 420px);
  top: 0.5%;
  right: 3%;
  filter: drop-shadow(0 8px 12px rgba(0, 0, 0, 0.2));
}

.books-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 48px;
  max-width: 1300px;
  /* Top margin big enough to win the collapse against .section-head's
     own margin-bottom:44px (adjacent block margins collapse to
     whichever is larger, they don't add) - the active card's
     scale(1.18) (see .book-card.is-active) grows upward from center as
     well as down, and without enough clearance here it overlaps the
     section description text above it. */
  margin: 70px auto 0;
}

.book-card {
  border-radius: var(--radius-card);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  cursor: pointer;
  /* One shadow for the whole card (cover + body together), not one on
     the cover alone - .book-cover used to carry its own box-shadow AND
     border-radius on top of this, which (since the cover is a flush
     square sitting directly above the body) read as its own separate
     floating box with a seam where the body's rectangle started below
     it, rather than one card with a photo on top. */
  box-shadow: var(--shadow);
  transition:
    transform 0.3s ease,
    box-shadow 0.3s ease,
    opacity 0.3s ease;
}
.book-card:hover:not(.is-active) {
  transform: translateY(-4px);
}
/* The carousel's "chosen" book - defaults to the middle card (see
   script.js' books-carousel block), picked by clicking any card.
   Scaled up in place rather than actually reordering/scrolling the
   grid, since all 3 cards fit on one row at desktop widths - "a
   carousel that looks almost like now" reads as this grid with a
   focused center item, not a scroll track. The other two shrink and
   fade slightly to push the size contrast further than scaling the
   active one alone would. Below 1024px wide this gives way to a
   one-book-at-a-time mobile layout instead - see .book-card:not(.is-
   active) in the 1024px media query. */
.book-card:not(.is-active) {
  transform: scale(0.88);
  opacity: 0.85;
}
.book-card.is-active {
  transform: scale(1.18);
  box-shadow: 0 22px 44px rgba(0, 0, 0, 0.35);
  z-index: 2;
}
.book-cover {
  aspect-ratio: 1 / 1;
  overflow: hidden;
}
.book-cover img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.book-body {
  padding: 22px 22px 24px;
  display: flex;
  flex-direction: column;
  flex: 1;
  background: rgba(27, 43, 75, 0.42);
  border-radius: 0 0 var(--radius-card) var(--radius-card);
}
.book-body p.desc {
  color: rgba(255, 255, 255, 0.9);
  font-size: clamp(15px, calc(12.9px + 0.3vw), 19.5px);
  text-align: center;
  flex: 1;
  margin-bottom: 20px;
}
.book-actions {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.amazon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  background: linear-gradient(135deg, var(--accent-orange), var(--orange));
  color: #fff;
  border: none;
  border-radius: 999px;
  padding: 13px 20px;
  font-family: "BrushUp", cursive;
  font-weight: 400;
  letter-spacing: 0.4px;
  font-size: clamp(16px, calc(13.76px + 0.32vw), 20.8px);
  cursor: pointer;
  box-shadow: var(--shadow);
  transition:
    transform 0.15s ease,
    box-shadow 0.15s ease;
}
.amazon-btn:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow-lg);
}
.amazon-btn svg {
  width: 18px;
  height: 18px;
}

.showmore-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  background: var(--outline);
  color: #fff;
  border: 1.5px solid rgba(255, 255, 255, 0.3);
  border-radius: 999px;
  padding: 11px 20px;
  font-family: "BrushUp", cursive;
  font-weight: 400;
  letter-spacing: 0.4px;
  font-size: clamp(15.5px, calc(13.33px + 0.31vw), 20.15px);
  cursor: pointer;
  box-shadow: var(--shadow);
  transition:
    transform 0.15s ease,
    box-shadow 0.15s ease;
}
.showmore-btn:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow-lg);
}
.showmore-btn svg {
  width: 16px;
  height: 16px;
  transition: transform 0.2s;
}

/* =========================
   About
   ========================= */
.about-section {
  position: relative;
}
.about-section::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 6px;
  background: var(--outline);
  z-index: 2;
}
.about-critters {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  overflow: hidden;
}
.about-critters .critter-about-cloud1 {
  width: clamp(150px, calc(129px + 3vw), 195px);
  top: 10%;
  left: 4%;
  opacity: 0.5;
}
.about-critters .critter-about-cloud2 {
  width: clamp(110px, calc(94.6px + 2.2vw), 143px);
  top: 18%;
  right: 6%;
  opacity: 0.4;
}
.about-grid {
  position: relative;
  z-index: 1;
}
.about-copy {
  max-width: 760px;
  margin: 0 auto;
  text-align: center;
}
.about-copy h2 {
  font-size: clamp(28px, calc(20px + 1.34vw), 49.4px);
  color: #fff;
}
.about-copy p {
  color: rgba(255, 255, 255, 0.88);
  font-size: clamp(16.5px, calc(14.19px + 0.33vw), 21.45px);
  margin-bottom: 16px;
}
.about-copy p:last-child {
  margin-bottom: 0;
}
.about-copy strong {
  color: var(--accent-yellow);
}

.about-stats {
  display: flex;
  justify-content: center;
  gap: 28px;
  margin-top: 28px;
  flex-wrap: wrap;
}
.about-stats div {
  text-align: center;
}
.about-stats strong {
  display: block;
  font-family: "BrushUp", cursive;
  font-size: clamp(26px, calc(22.36px + 0.52vw), 33.8px);
  color: var(--accent-yellow);
}
.about-stats span {
  font-size: clamp(13px, calc(11.18px + 0.26vw), 16.9px);
  color: rgba(255, 255, 255, 0.78);
  font-weight: 700;
}

/* =========================
   Lightbox
   ========================= */
.lightbox {
  position: fixed;
  inset: 0;
  background: rgba(27, 43, 75, 0.86);
  display: none;
  align-items: center;
  justify-content: center;
  z-index: 1000;
  padding: 24px;
}
.lightbox.is-open {
  display: flex;
}
.lightbox-inner {
  position: relative;
  max-width: 900px;
  width: 100%;
  background: var(--surface);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-lg);
  padding: 20px;
}
.lightbox-title {
  font-family: "NunitoSans";
  font-weight: 700;
  font-size: clamp(17px, calc(14.62px + 0.34vw), 22.1px);
  margin-bottom: 12px;
  padding-right: 40px;
}
.lightbox-stage {
  position: relative;
}
.lightbox-stage img {
  width: 100%;
  max-height: 68vh;
  object-fit: contain;
  border-radius: 12px;
  background: var(--bg);
}
.lightbox-close {
  position: absolute;
  top: 14px;
  right: 14px;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: var(--outline);
  color: #fff;
  border: none;
  cursor: pointer;
  font-size: clamp(18px, calc(15.48px + 0.36vw), 23.4px);
  line-height: 1;
  z-index: 2;
}
.lightbox-arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 46px;
  height: 46px;
  border-radius: 50%;
  background: #fff;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: var(--shadow);
}
.lightbox-arrow--prev {
  left: 8px;
}
.lightbox-arrow--next {
  right: 8px;
}
.lightbox-arrow svg {
  width: 20px;
  height: 20px;
}
.lightbox-count {
  text-align: center;
  font-size: clamp(13px, calc(11.18px + 0.26vw), 16.9px);
  font-weight: 700;
  color: var(--text-secondary);
  margin-top: 12px;
}

/* Screenshot viewer - deliberately NOT the card/title/counter-style
   .lightbox above: just a dark backdrop and the pictures themselves,
   enlarged (bigger than the carousel thumbnails) and horizontally
   scrollable/swipeable, opening already scrolled to the one clicked. */
.shots-lightbox {
  position: fixed;
  inset: 0;
  background: rgba(27, 43, 75, 0.92);
  display: none;
  z-index: 1000;
}
.shots-lightbox.is-open {
  display: block;
}
.shots-lightbox-track {
  display: flex;
  align-items: center;
  gap: 24px;
  width: 100%;
  height: 100%;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  padding: 0 10vw;
  scrollbar-width: none;
}
.shots-lightbox-track::-webkit-scrollbar {
  display: none;
}
/* Wraps each img. border-radius/overflow/clip-path here are just the
   desktop-era fallback - see .carousel figure's note and CLAUDE.md:
   the real iOS rounding now comes from the .webp assets' own baked
   alpha-channel corners, not from anything this wrapper clips. Sized
   via fit-content to hug whatever size the img's own height:85vh/
   width:auto/max-width:85vw resolves to, aspect ratio included. */
.shots-lightbox-frame {
  flex: 0 0 auto;
  width: fit-content;
  height: fit-content;
  max-width: 85vw;
  border-radius: 16px;
  overflow: hidden;
  clip-path: inset(0 round 16px);
  scroll-snap-align: center;
  display: flex;
}
.shots-lightbox-track img {
  display: block;
  height: 85vh;
  width: auto;
  max-width: 85vw;
  object-fit: contain;
}
.shots-lightbox-close {
  position: absolute;
  top: 20px;
  right: 24px;
  background: none;
  border: none;
  color: #fff;
  font-size: clamp(24px, calc(20px + 1vw), 34px);
  line-height: 1;
  cursor: pointer;
  z-index: 2;
}

/* =========================
   Footer
   ========================= */
.site-footer {
  position: relative;
  color: #fff;
  padding: 56px 0 28px;
}
.footer-grid {
  display: grid;
  grid-template-columns: 1.4fr 1fr 1fr 1fr;
  gap: 32px;
  margin-bottom: 40px;
}
.footer-brand .brand span {
  color: #fff;
}
/* Scoped to .brand (logo wrapper) - a bare .footer-brand img also
   matched the .social-row icons and stretched them (height:40px with
   no matching width, winning over .header-icon img's 100%/100%). */
.footer-brand .brand img {
  height: 40px;
  margin-bottom: 14px;
}
.footer-brand p {
  color: rgba(255, 255, 255, 0.72);
  /* Matched to body's clamp(16px,...,20.8px) - was clamp(14px,...,
     18.2px), smaller than body at every width for no documented
     reason. */
  font-size: clamp(16px, calc(13.76px + 0.32vw), 20.8px);
  max-width: 32ch;
}

.footer-col h4 {
  font-family: "BrushUp", cursive;
  font-size: clamp(16px, calc(13.76px + 0.32vw), 20.8px);
  color: var(--accent-yellow);
  margin-bottom: 14px;
}
.footer-col ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 10px;
}
.footer-col a {
  font-size: clamp(14.5px, calc(12.47px + 0.29vw), 18.85px);
  color: rgba(255, 255, 255, 0.85);
}
.footer-col a:hover {
  color: #fff;
  text-decoration: underline;
}

/* Footer icons now reuse .header-icon directly (see its own rule) -
   Amazon/TikTok/App Store are each already self-contained navy badge
   PNGs (own rounded shape and color baked in, same assets the header
   uses), so the footer no longer needs its own circle-background +
   white-SVG treatment this rule used to provide for a lone TikTok
   glyph. */
.social-row {
  display: flex;
  gap: 10px;
  margin-top: 4px;
}

.footer-bottom {
  border-top: 1px solid rgba(255, 255, 255, 0.18);
  padding-top: 22px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: 12px;
  font-size: clamp(13px, calc(11.18px + 0.26vw), 16.9px);
  color: rgba(255, 255, 255, 0.65);
}
.footer-bottom a {
  color: rgba(255, 255, 255, 0.85);
}

.back-to-top {
  position: fixed;
  right: 22px;
  bottom: 22px;
  width: 48px;
  height: 48px;
  border-radius: 50%;
  background: linear-gradient(135deg, var(--accent-orange), var(--orange));
  border: none;
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  box-shadow: var(--shadow);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s ease;
  z-index: 90;
}
.back-to-top.is-visible {
  opacity: 1;
  pointer-events: auto;
}
.back-to-top svg {
  width: 20px;
  height: 20px;
}

/* =========================
   Simple legal / support pages
   ========================= */
.page-hero {
  padding: 64px 0 40px;
  text-align: center;
}
.page-hero .eyebrow {
  margin-bottom: 10px;
}
.page-hero h1 {
  font-size: clamp(32px, calc(21.6px + 1.74vw), 59.8px);
}
.page-hero p {
  color: var(--text-secondary);
  max-width: 56ch;
  margin: 0 auto;
}

.legal-card {
  background: var(--surface);
  border: var(--border-soft);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow);
  padding: 40px clamp(20px, 5vw, 56px);
  max-width: 880px;
  margin: 0 auto 72px;
}
.legal-card h2 {
  font-size: clamp(22px, calc(18.92px + 0.44vw), 28.6px);
  color: var(--accent-blue);
  margin-top: 36px;
}
.legal-card h2:first-child {
  margin-top: 0;
}
.legal-card p,
.legal-card li {
  color: var(--text-secondary);
  /* Matched to body's clamp(16px,...,20.8px) - was clamp(15.5px,...,
     20.15px), marginally smaller than body for no documented reason. */
  font-size: clamp(16px, calc(13.76px + 0.32vw), 20.8px);
}
.legal-card ul {
  padding-left: 20px;
}
.legal-card .updated {
  display: inline-block;
  font-size: clamp(13px, calc(11.18px + 0.26vw), 16.9px);
  font-weight: 700;
  color: var(--text-secondary);
  background: var(--bg);
  border-radius: 999px;
  padding: 6px 14px;
  margin-bottom: 28px;
}

/* Down to a single card (all three used to end in the same email) -
   capped narrower than the old 3-up 880px grid so it doesn't stretch
   edge to edge as one lonely wide card. */
.contact-grid {
  display: grid;
  max-width: 420px;
  margin: 0 auto 64px;
}
.contact-card {
  background: var(--surface);
  border: var(--border-soft);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow);
  padding: 28px 24px;
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  transition:
    transform 0.2s ease,
    box-shadow 0.2s ease;
}
.contact-card:hover {
  transform: translateY(-4px);
  box-shadow: var(--shadow-lg);
}
.contact-card .icon-circle {
  width: 54px;
  height: 54px;
  border-radius: 50%;
  background: linear-gradient(135deg, var(--accent-orange), var(--orange));
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0 auto 14px;
}
.contact-card .icon-circle svg {
  width: 24px;
  height: 24px;
}
.contact-card h3 {
  font-size: clamp(18px, calc(15.48px + 0.36vw), 23.4px);
  margin-bottom: 8px;
}
.contact-card p {
  color: var(--text-secondary);
  font-size: clamp(14.5px, calc(12.47px + 0.29vw), 18.85px);
  margin-bottom: 14px;
  flex: 1;
}
.contact-card .btn {
  margin-top: auto;
}

.faq-list {
  max-width: 760px;
  margin: 0 auto 72px;
  display: grid;
  gap: 14px;
}
.faq-item {
  background: var(--surface);
  border: var(--border-soft);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
  transition: box-shadow 0.2s ease;
}
.faq-item:hover {
  box-shadow: var(--shadow);
}
.faq-item summary {
  cursor: pointer;
  list-style: none;
  padding: 18px 22px;
  font-family: "NunitoSans";
  font-weight: 700;
  font-size: clamp(15.5px, calc(13.33px + 0.31vw), 20.15px);
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
}
.faq-item summary::-webkit-details-marker {
  display: none;
}
.faq-item summary .plus {
  flex: 0 0 auto;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: rgba(27, 43, 75, 0.08);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: clamp(16px, calc(13.76px + 0.32vw), 20.8px);
}
.faq-item[open] summary .plus {
  background: var(--accent-orange);
  color: #fff;
}
.faq-item p {
  padding: 0 22px 20px;
  margin: 0;
  color: var(--text-secondary);
  /* Matched to body's clamp(16px,...,20.8px) - was clamp(15px,...,
     19.5px), smaller than body at every width for no documented
     reason. */
  font-size: clamp(16px, calc(13.76px + 0.32vw), 20.8px);
}

/* =========================
   Responsive
   ========================= */
/* Shrinks the gap between the hero and the carousel below, desktop only
   (mobile has its own padding-bottom override at 760px). The owl/bee are
   JS-anchored to the store-pill button (syncOwlGap/syncBeeGap in
   script.js), not to .hero's height, so they don't move with this. */
@media (min-width: 1025px) {
  .hero {
    padding-bottom: 176px;
  }
}

/* Below ~1400px wide, .hero-visual's own column (0.6fr of the row) gets
   tight enough that even the bigger phone/screens formula above (see
   their own notes) still leaves the headline comfortably more room than
   the screenshots need. Give the visual column most of the row instead
   - the text column's own cqw-based font-size (see .hero-grid >
   div:first-child above) shrinks automatically as its column narrows,
   so this also does the "shrink the text" half of the ask for free. */
@media (max-width: 1400px) and (min-width: 1025px) {
  .hero-grid {
    grid-template-columns: minmax(0, 0.4fr) minmax(0, 1.6fr);
  }
  /* The text column's own cqw-based headline size (clamp(40px,
     15.8cqw, 91px), see .hero-grid > div:first-child above) has a
     40px floor tuned for the 0.6fr column the reference widths give
     it - at 0.4fr here the column is narrow enough that 40px no
     longer fits and "DRAWQUEST" clips/overflows past it. Lower the
     floor so it keeps shrinking with the column instead of hitting
     that wall. */
  .hero-title-sub,
  .hero-title-main {
    font-size: clamp(23px, 15.8cqw, 91px);
  }
}

@media (max-width: 1024px) {
  /* Desktop's clamp(40px, 5vw, 80px) floors out at 40px a side on any
     mobile width (5vw never reaches 40px below 800px wide), which eats
     a lot of a narrow phone's width before content even starts. Mobile
     gets its own, smaller fixed side margin instead. */
  .container {
    padding: 0 20px;
  }
  /* The mobile design reference (desing/Website-mobile_02) shows no
     nav links and no hamburger in the header at all - just the logo
     and the 3 social icons. Hide both the link row and the toggle
     button that would otherwise reveal them (see the 760px query's
     .nav.is-open rules, unreachable now that nothing below 1024px can
     open it) rather than keep a menu entry point the reference doesn't
     have. */
  .nav-links,
  .nav-toggle {
    display: none;
  }
  /* .hero-screens' fanned screenshots are rotated (transform: rotate()
     on .hero-screen, see its base rule) - rotation inflates an
     element's rendered bounding box past its own CSS width/left%, which
     went unnoticed while .hero-screens had plenty of margin to the
     viewport edge on desktop, but overflows the actual viewport now
     that .hero-screens runs close to full mobile width (confirmed via
     a real scrollWidth > innerWidth, not the usual few px body's own
     overflow-x:hidden already absorbs). .hero has no max-width (full-
     bleed by design, see CLAUDE.md), so clipping here only affects
     decorative overflow, not .hero-screens' own intentional full-bleed
     breakout (92vw, well inside .hero's own full viewport width). */
  .hero {
    overflow-x: hidden;
  }
  /* Desktop's hero gradient runs at 115deg (mostly left-to-right, a
     touch of downward tilt) to match the diagonal in the desktop design
     reference - the mobile design reference (desing/Website-mobile_02)
     uses a straight top-to-bottom gradient instead, light blue up top
     fading to navy at the bottom, so switch the angle here rather than
     reuse the desktop one at a width it wasn't tuned for. */
  .bg-full--hero {
    background-image:
      url("../img/decor/treeline.png"),
      linear-gradient(
        180deg,
        var(--sky-blue-deep) 0%,
        var(--sky-blue-deep) 28%,
        var(--sky-navy) 62%,
        var(--sky-navy) 100%
      );
  }
  /* .hero::before's stars texture is anchored right/top on desktop,
     where the whole hero is one short, wide strip and "top" lands
     correctly in the (also top-anchored) navy part of the diagonal
     gradient. Here the hero is much taller (phone+screens stacked
     below the text) and the gradient is vertical with navy only in the
     bottom ~40%, so "top" instead lands the stars over the light-blue
     sky behind the phone, where the reference doesn't show any.
     Anchoring to bottom instead puts them over the navy section
     (.hero-screens) where the reference does show them. */
  .hero::before {
    top: auto;
    height: 50%;
    background-repeat: repeat;
    background-position: right bottom;
    background-size: auto;
  }
  /* The mobile design reference (desing/Website-mobile_02) keeps the
     headline/text block and the orange phone side by side at the top -
     text in the top-left, phone in the top-right - with .hero-screens
     (the dark "bat drawing" screens fan) below both, spanning the full
     width, not sharing the phone's column (too cramped to read there)
     and not swapped out for the #screenshots carousel further down (an
     earlier pass assumed it was a duplicate of that carousel's content
     - it isn't, the reference shows this fan as part of the hero
     itself).
     .hero-phone-frame and .hero-screens are ordinarily position:
     absolute within .hero-visual, sized/placed as a % of ITS box - that
     only works when .hero-visual's own box is tall enough to contain
     both, which isn't knowable up front since the text column's height
     (paragraph line count, mainly) varies with viewport width and
     content, and .hero-grid's align-items:start means .hero-visual
     never stretches to match it. An earlier pass tried computing
     .hero-visual's needed height by hand (clamp()/calc() formulas) -
     fragile, and wrong the moment the two columns' relative heights
     changed. display:contents dissolves .hero-visual's own box instead,
     so its children (.hero-phone-frame, .hero-screens, .hero-tools)
     become direct children of .hero-grid and can be placed with plain
     grid-column/grid-row like any other grid item - letting Grid do the
     "below whichever column is taller" sizing itself instead of a
     guessed-up-front formula. */
  /* 0.92fr/1.08fr - matched against the actual mobile design reference
     (desing/Website-mobile_02.jpg, measured at its native ~1024px-wide
     scale: the text block's own right edge sits at ~46% of the content
     box, the phone starts right after it and is allowed to run past the
     padding on the right, not confined to a hard column boundary). An
     earlier pass pushed this to 0.72/1.28 (36/64) to fix the phone
     looking stuck/undersized - that was really a .hero-phone-frame max-
     width ceiling problem (see its own comment below), not a column-
     share problem, and 0.72/1.28 overcorrected: the text column ended up
     far narrower than the reference (wrapping every ~20 characters
     instead of ~45), and the store-pill button's column got so narrow
     its 2-line text wrapped to 3 and border-radius:999px rendered it as
     a near-circle instead of a pill (see its own mobile override
     below). 0.92/1.08 matches the reference's proportions and - as a
     side effect - gives the phone a bit less width at every size below
     the clamp ceiling, which is also what was asked for. */
  /* 1.4fr/0.6fr, NOT meant to size the phone (see .hero-phone-frame's
     own comment below for why it's decoupled from this column's width
     now) - it only needs to (a) give the text column a healthy width on
     its own, since on an actual narrow phone (confirmed on a real
     iPhone 16, 393px wide) even the more-generous-than-before 0.92fr
     share from the last pass only worked out to ~136px, barely wider
     than before it - a proportional fr split can't give a narrow
     viewport an absolutely-wide column, only the SAME viewport-relative
     one - and (b) mark where the phone's own left edge starts. */
  .hero-grid {
    grid-template-columns: minmax(0, 1.4fr) minmax(0, 0.6fr);
    gap: 16px 0;
  }
  /* Was 90px (desktop's gap below the sticky header) - at mobile the
     phone sits immediately below the header on its own row:1 (top:0
     below), so the text column needs to start at the same height to
     align with it instead of starting ~90px lower, which read as the
     whole text block sitting oddly low relative to the phone. */
  .hero-grid > div:first-child {
    padding-top: 0;
  }
  .hero-visual {
    display: contents;
  }
  /* Both switch from position:absolute (sized as a % of .hero-visual's
     box) to position:relative + grid placement now that .hero-visual
     is display:contents - position:relative (not static) so they're
     still valid containing blocks for their own %-positioned children
     (.hero-bird-front etc., .hero-screen-6 etc.) exactly as before. */
  .hero-phone-frame {
    position: relative;
    grid-column: 2;
    grid-row: 1;
    /* width:100% (of column 2) + a max-width ceiling - the previous
       approach - ties the phone's size to how much the text column
       (1.4fr above) left over, which on a real narrow phone is barely
       anything. Decoupled instead: an intrinsic width of its own
       (not width:100%) plus justify-self:start (not the old
       margin-left:auto) pins its LEFT edge to column 2's left edge -
       same boundary text's right edge already respects via the gap, so
       it still can't intrude leftward into the text - while letting it
       grow past column 2's own (narrow) right edge on the right,
       further right than before and past the viewport edge if its
       formula asks for more room than is actually left, same bleed-past-
       the-padding technique .hero-screens below uses, and fine per the
       request ("it can be partially cut on the right"). A later request
       asked for the phone moved further left still - the negative
       margin-left pulls its left edge back out of column 2 and into the
       gap (headline text sits well above this row, so there's nothing
       to collide with at this row's height). */
    width: clamp(200px, 54vw, 360px);
    justify-self: start;
    margin-left: -28px;
    top: 0;
  }
  .hero-screens {
    position: relative;
    grid-column: 1 / -1;
    grid-row: 2;
    /* Breaks out to the full viewport width (edge to edge, matching the
       reference) via the same left:50%/translateX(-50%) technique
       .bg-full uses elsewhere, rather than stopping at .hero-grid's own
       (padded, narrower) width - left pulled in from 50% (perfectly
       centered) to 40% to shift the whole cluster left, matching the
       reference's off-center placement instead of centering it. */
    left: 40%;
    transform: translateX(-50%);
    /* Not a full 100vw - several of the fanned screens inside (rotated
       via transform, which inflates their rendered bounding box past
       their own CSS width/left%) and .hero-caption--right (deliberately
       positioned at right:-30.2%, past .hero-screens' own right edge by
       design) overflowed the actual viewport once .hero-screens itself
       was this close to full width, confirmed via a real scrollWidth >
       innerWidth (not the few harmless px body's overflow-x:hidden
       absorbs elsewhere) - 92vw leaves enough margin for both. */
    width: 92vw;
    /* 650px (tuned for phones) was also capping this flat on tablet,
       reading as stuck-small while the rest of the scene grew. 880px
       still leaves margin against the viewport (~944px content width
       at 1024px) for the rotated tiles' inflated bounding box. */
    max-width: 880px;
    right: auto;
    top: auto;
    margin-top: 56px;
  }
  /* .hero-caption-layer duplicates .hero-screens' box on desktop (see
     its own rule), so it needs the exact same mobile override too -
     same grid cell, same breakout-to-92vw geometry - or the two drift
     apart and the captions inside stop lining up with the screens they
     describe. */
  .hero-caption-layer {
    position: relative;
    grid-column: 1 / -1;
    grid-row: 2;
    left: 40%;
    transform: translateX(-50%);
    width: 92vw;
    max-width: 880px;
    right: auto;
    top: auto;
    margin-top: 56px;
  }
  /* Still overflows even with .hero-screens' own margin above - this
     caption is positioned well past its box on purpose (see its base
     rule) to sit in open space past the cluster's edge, which existed
     on desktop but doesn't once the cluster is this close to the full
     viewport width. Pull it back inside instead of losing the margin
     fix for every other child just to fit this one.
     On a narrow phone the cluster itself fills almost the entire 92vw
     box (confirmed via getBoundingClientRect: screens span -15.6px to
     346px at 393px wide, leaving almost no side margin), so `right: 2%`
     alone still landed this caption directly on top of the highest
     z-index tile (hero-screen-10, the purple coloring screen - measured
     at x:187-299/y:758-1002, caption measured at x:169-339/y:793-830,
     a direct overlap) instead of beside it - unreadable white text over
     a busy image, which is what was reported. There's no open sky left
     *beside* the cluster at this width, but there is a ~137px gap of
     open sky *above* it (.hero-phone-frame's own bottom sits at 392px,
     .hero-screens' top at 529px) - matching the design reference, where
     both captions actually sit in that neck between the phone row and
     the fan, not over the fan itself. Pulling this one up out of the
     caption-layer box entirely (negative top) moves it into that gap;
     the smaller font-size below keeps it from needing much room. */
  .hero-caption--right {
    top: -60px;
    right: 1%;
    max-width: clamp(110px, calc(85px + 6.34vw), 150px);
    font-size: clamp(12px, calc(9.5px + 0.63vw), 16px);
  }
  /* Same overflow, opposite edge - .hero-screens shifted left (see its
     `left: 40%` above, moving the whole cluster left per the design
     reference) pushed this caption's right:80.5% (of .hero-screens' own
     width) far enough that its LEFT edge went negative, clipped by
     .hero's own overflow-x:hidden (confirmed via getBoundingClientRect,
     left:-21px). Pull it in the same way as --right above, and lift it
     into the same open-sky gap above the cluster (see --right's comment)
     with a smaller font so it reads clearly instead of grazing the
     fanned screens' top edge. `left` needs rechecking if the max-width
     above ever changes - it's tuned to clear the worst drift point
     between .hero-screens' width and its centering transform. */
  .hero-caption--top {
    top: -30px;
    right: auto;
    left: 10%;
    max-width: clamp(110px, calc(85px + 6.34vw), 150px);
    font-size: clamp(12px, calc(9.5px + 0.63vw), 16px);
  }
  /* Was display:none below 1024px (it was positioned as a %-of-the-gap
     between .hero-phone-frame and .hero-screens, a pocket that only
     exists in the desktop two-column layout - on mobile those two stack
     instead, so the old position:absolute/left:33.28cqw/bottom:-20px
     values had nowhere sensible to land and this whole block - the
     "Earn stars..." line and the 5 tool icons - was just dropped).
     The mobile design reference (desing/Website-mobile_02.jpg) shows
     this same block in the open sky below the screens fan, full width,
     stacked above the forest/winter/wild cards row - so instead of
     reviving the old %-of-gap position, give it its own full-width grid
     row below .hero-screens (row 3 - .hero-visual is display:contents
     here, same as .hero-phone-frame/.hero-screens above, so this just
     needs position:relative + explicit placement like they have) and
     scale the text/icons down to the size that reads well at this
     width instead of the desktop clamp() ceiling. */
  .hero-tools {
    display: block;
    position: relative;
    grid-column: 1 / -1;
    grid-row: 3;
    left: auto;
    bottom: auto;
    margin-top: -60px;
    /* clamp() floors match the old flat phone values, so this only grows
       past ~430px wide instead of staying phone-sized up to 1024px. */
    max-width: clamp(120px, calc(95px + 6.34vw), 160px);
  }
  .hero-tools p {
    font-size: clamp(12px, calc(9.5px + 0.63vw), 16px);
  }
  .hero-tools-star {
    width: clamp(26px, calc(20px + 1.43vw), 35px);
    vertical-align: -6px;
    margin-left: 6px;
  }
  .hero-tools-icons {
    width: clamp(110px, calc(85px + 6.34vw), 150px);
  }
  /* Was left-aligned here: at the old ~half-viewport-wide text column,
     text-align:justify's inter-word stretching was visibly uneven on
     such short lines. The column was widened considerably since (see
     .hero-grid's 1.4fr/0.6fr split and the smaller side margins above),
     so justify now has enough line length to distribute evenly - and
     the user specifically asked for the justified look back, matching
     the base (desktop) rule instead of overriding it.
     Re-enabling it surfaced a separate, pre-existing overlap though:
     the paragraph's own max-width (48ch, desktop's value) runs right up
     to - and past - the phone's actual left edge at this width, since
     .hero-phone-frame's margin-left:-28px (added per an earlier "move
     the phone further left" request) pulls the phone in from column 2
     without column 1's text ever being told to stop short of it.
     text-align:left mostly hid this (lines only reached the true edge
     when a line happened to wrap right there); justify stretches EVERY
     line to that edge, so the phone cutting off the last few letters of
     several lines became obvious. Confirmed via getBoundingClientRect
     at 393px wide: paragraph right edge 267px vs phone left edge 239px,
     a real 28px overlap. Narrower max-width here keeps the column clear
     of the phone instead of shrinking the phone's leftward pull back. */
  .hero p.lede {
    max-width: 205px;
  }
  /* The headline's desktop font-size (clamp(40px, 15.8cqw, 91px)) has
     a 40px floor tuned for desktop's 0.6fr text column - at this much
     narrower ~half-viewport mobile column (149px at 394px wide), 40px
     doesn't fit "DrawQuest" without overflowing well past the column
     into the phone's column, confirmed via screenshot. Lowering the
     floor (same fix already used for the 1025-1400px range above) lets
     it keep shrinking with the column instead of hitting that wall.
     (This rule was still carrying the pre-shrink 20.3cqw/117px mid and
     ceiling after the base rule above was scaled down - fixed to match,
     since a stale higher ceiling here meant mobile screens just below
     1024px rendered the old, oversized headline again.) */
  .hero-title-sub,
  .hero-title-main {
    font-size: clamp(16px, 11.5cqw, 91px);
  }
  /* The store-pill's desktop sizing assumes a column wide enough for
     "Coming soon to the APP STORE" to wrap to 2 lines - in a real
     narrow phone's ~150px-wide half-column it wraps to 3, and
     border-radius:999px's heavy corner rounding at this height:width
     ratio visually clips the bottom line against the curve even though
     it's within the box. Shrinking the icon/padding/font gets it back
     to 2 lines there. clamp() floors match those same flat values, so
     it only grows past phone widths, toward the tablet column's 280-
     460px of room. */
  .store-pill {
    gap: clamp(6px, calc(4px + 0.48vw), 9px);
    padding: clamp(7px, calc(4.5px + 0.63vw), 11px)
      clamp(12px, calc(7px + 1.27vw), 20px);
  }
  .store-pill-icon {
    width: clamp(16px, calc(11px + 1.27vw), 24px);
  }
  .store-pill-text small {
    font-size: clamp(9px, calc(6.5px + 0.63vw), 13px);
  }
  .store-pill strong {
    font-size: clamp(12px, calc(8.9px + 0.79vw), 17px);
  }
  /* Books - desktop shows all 3 covers side by side in one row, with
     the "chosen" one (see .book-card.is-active in script.js' books-
     carousel block) scaled up in place. The mobile design reference
     shows all 3 as well, just stacked one full-size book per row
     instead of 3 across - not a single-card switcher (an earlier pass
     here hid the other two and added .books-dots to switch between
     them, which doesn't match: the reference scrolls through all 3,
     it doesn't hide any). Every card gets the "is-active" full
     treatment here since there's no shrunk/dim state to contrast
     against once they're stacked instead of side by side. */
  .books-grid {
    grid-template-columns: 1fr;
  }
  .book-card {
    max-width: 420px;
    margin: 0 auto;
    transform: none !important;
    opacity: 1 !important;
  }
  .footer-grid {
    grid-template-columns: 1fr 1fr;
  }

  /* .hero-visual stays in its normal (narrower) 2-column spot below
     1024px (see .hero-grid above), but it's still a lot narrower than
     its 1.4fr desktop share, and now only holds the phone (.hero-screens
     is hidden above). The night-sky bat swarm and bee1 are positioned
     with desktop top:%/px values tuned for that wider, phone+screens
     box - left alone, they'd land wherever that math happens to put
     them in the narrower phone-only one. Hide the swarm entirely here
     rather than chase their position through a layout they were never
     designed for, and give cloud1/bee1 a small fixed spot in the sky
     above the phone instead of their desktop top value. */
  .hero-critters .critter-bat2,
  .hero-critters .critter-bat3,
  .hero-critters .critter-bat4,
  .hero-critters .critter-bat6,
  .hero-phone-frame .critter-bat8,
  .hero-phone-frame .critter-bat9,
  .hero-phone-frame .critter-bat10,
  .hero-phone-frame .critter-bat11,
  .hero-phone-frame .critter-bat12,
  .hero-critters .critter-bat13,
  .hero-critters .critter-bat14,
  .hero-grid > div:first-child .critter-bee2,
  /* Re-hidden - a brief attempt at showing this (it's visible in the
     static design reference) looked wrong once actually rendered next
     to the phone on mobile and was asked to be removed again. */
  .hero-phone-frame .hero-bat-front {
    display: none;
  }
  /* Unlike the bat swarm/cloud2/cloud3 above, these are already
     positioned in % of .hero-phone-frame's/.hero-screens' own box (see
     their base rules), not the desktop-only top:%% of .hero - the
     whole point of that earlier refactor was so they'd track the phone/
     screens correctly at any size, so they don't need new coordinates
     here, just to not be hidden. The mobile design reference (desing/
     Website-mobile_02) shows the bird, a cloud and bats scattered
     around the phone/screens like this. */
  .hero-phone-frame .hero-bird-front,
  .hero-phone-frame .hero-cloud-behind,
  .hero-phone-frame .critter-bee3,
  .hero-screens .hero-bat-front-2,
  .hero-screens .hero-bat-front-3 {
    display: block;
  }
  /* Bigger than the desktop base rule's 90% here specifically - on a
     narrow phone the base size reads as a thin wisp rather than the
     puffy cloud the design reference shows tucked under the phone.
     left adjusted more negative to keep it roughly centered under the
     phone now that it's wider. */
  .hero-phone-frame .hero-cloud-behind {
    width: 125%;
    left: -24%;
  }
  /* Scattered group below the screens fan (mobile only - see their
     desktop-hidden base rule). .hero-screens itself already bleeds
     past the viewport's left edge here (left:40%/translateX(-50%),
     see its own rule above - confirmed via getBoundingClientRect: box
     left sits at -19.6px at 393px wide), so a further negative `left`
     on top of that (the first version of -4/-5 used -6%/6% of the
     BOX, i.e. still well left of the box's own negative edge) pushed
     most of each bat off the actual viewport - only a sliver was ever
     visible. Kept all of them at a positive % of the box that still
     lands a few px inside the real (0 to innerWidth) visible area,
     confirmed per-bat via getBoundingClientRect after placing. */
  .hero-screens .hero-bat-front-4,
  .hero-screens .hero-bat-front-5,
  .hero-screens .hero-bat-front-6,
  .hero-screens .hero-bat-front-7,
  .hero-screens .hero-bat-front-8,
  .hero-screens .hero-bat-front-9,
  .hero-screens .hero-bat-front-10,
  .hero-screens .hero-bat-front-11,
  .hero-screens .hero-bat-front-12 {
    display: block;
  }
  .hero-screens .hero-bat-front-4 {
    position: absolute;
    top: 68%;
    left: 9%;
    width: 14%;
    height: auto;
    z-index: 6;
    transform: rotate(18deg);
  }
  .hero-screens .hero-bat-front-5 {
    position: absolute;
    top: 79%;
    left: 22%;
    width: 11%;
    height: auto;
    z-index: 6;
    transform: rotate(-12deg);
  }
  .hero-screens .hero-bat-front-6 {
    position: absolute;
    top: 97%;
    left: 44%;
    width: 12%;
    height: auto;
    z-index: 6;
    transform: rotate(8deg);
  }
  .hero-screens .hero-bat-front-7 {
    position: absolute;
    top: 60%;
    left: 100%;
    width: 11%;
    height: auto;
    z-index: 6;
    transform: rotate(20deg);
  }
  .hero-screens .hero-bat-front-8 {
    position: absolute;
    top: 96%;
    left: 90%;
    width: 13%;
    height: auto;
    z-index: 6;
    transform: rotate(14deg);
  }
  /* Small tight cluster right above the "Earn stars" line (.hero-tools
     sits at ~91% of this box - 975px of 546-1019px at 393px wide, see
     getBoundingClientRect) - smaller than -4 through -8 above (6-7% of
     the box vs 11-15%) so they read as a little swarm rather than
     repeating the bigger scattered bats. */
  .hero-screens .hero-bat-front-9 {
    position: absolute;
    top: 80%;
    left: 4%;
    width: 7%;
    height: auto;
    z-index: 6;
    transform: rotate(10deg);
  }
  .hero-screens .hero-bat-front-10 {
    position: absolute;
    top: 85%;
    left: 16%;
    width: 6%;
    height: auto;
    z-index: 6;
    transform: rotate(-14deg);
  }
  .hero-screens .hero-bat-front-11 {
    position: absolute;
    top: 55%;
    left: 10%;
    width: 7%;
    height: auto;
    z-index: 6;
    transform: rotate(16deg);
  }
  .hero-screens .hero-bat-front-12 {
    position: absolute;
    top: 87%;
    left: 37%;
    width: 6%;
    height: auto;
    z-index: 6;
    transform: rotate(-9deg);
  }
  /* The mobile design reference shows a cloud and a bee together above
     the headline, on the text column's side of the hero - cloud1 was
     already shown here (just tucked up near the header), bee1 was
     hidden entirely (its old desktop spot put it under the phone
     cluster, which sits at a much higher z-index). Re-enabling and
     repositioning both to sit together above "App DrawQuest" instead,
     matching the reference. */
  .hero-critters .critter-cloud1 {
    top: 8px;
    left: -6%;
    /* clamp() floor matches the old flat 110px, growing only past phone
       widths - same for the other clouds/bee1/owl/bear below. */
    width: clamp(110px, calc(85px + 6.34vw), 150px);
  }
  /* The reference's second cloud isn't a big backdrop behind the whole
     headline - re-measured against desing/Website-mobile_02.jpg
     directly: it's a smaller, puffier cloud sitting just right of/below
     the bee, roughly between "APP" and "DRAWQUEST" and the phone, not
     overlapping the letters themselves. Sized and moved down/right to
     match instead of spanning the full text block. */
  .hero-critters .critter-cloud2 {
    display: block;
    width: clamp(170px, calc(133px + 9.51vw), 230px);
    top: 155px;
    left: 30%;
    opacity: 0.85;
  }
  /* Reference also shows a cloud up in the top-right sky, above/behind
     the phone - reusing cloud3 (a desktop-only element, hidden at
     mobile until now) for it with its own mobile position instead of
     adding a new element. */
  .hero-critters .critter-cloud3 {
    display: block;
    width: clamp(200px, calc(156px + 11.09vw), 270px);
    top: 15px;
    left: 66%;
    opacity: 0.85;
  }
  /* And a fourth cloud drifting in from the left edge, tucked behind/
     below the store-pill button - no desktop equivalent for this one,
     see critter-cloud4's own base rule. Top is a fixed px guess at
     where the button typically sits once the paragraph above it has
     wrapped; being a soft, low-opacity decorative cloud, being a bit
     off underneath the button at other paragraph lengths isn't
     noticeable the way a sharp-edged element would be. */
  .hero-critters .critter-cloud4 {
    display: block;
    width: clamp(190px, calc(150px + 10.3vw), 255px);
    top: 330px;
    left: -22%;
    opacity: 0.8;
  }
  .hero-critters .critter-bee1 {
    display: block;
    width: clamp(80px, calc(63px + 4.44vw), 108px);
    top: 30px;
    left: 32%;
  }
  .hero-critters .critter-owl {
    width: clamp(150px, calc(116px + 8.72vw), 205px);
  }
  /* Must stay behind the treeline (don't raise z-index above
     .hero::after's 1 - tried once, explicitly rejected: the ask is
     peeking from behind the trees, not standing in front). `bottom:95px`
     is tuned empirically (screenshotted at 761/834/1024px) to clear
     enough of the jagged treeline silhouette for a believable small peek
     at every width in this clamp's range. */
  .hero-bear {
    width: clamp(150px, calc(116px + 8.72vw), 205px);
    left: 50%;
    bottom: 95px;
  }
  .books-critters .critter-trex {
    width: 220px;
  }
  .books-critters .critter-motorcycle {
    --moto-w: 220px;
    width: var(--moto-w);
  }
  /* Rescaled from the 360px desktop value to match this breakpoint's
     much smaller 220px moto (~147px tall) - see the base #books rule's
     comment for the sizing logic. */
  #books {
    padding-bottom: 180px;
  }

  /* Screenshots carousel - now that .hero-screens shows these same dark
     "bat drawing" screens prominently in the hero itself (see its own
     media query rules), this carousel is a secondary gallery, not the
     primary showcase - an earlier pass sized it at 85vw (near-fullscreen,
     one image dominating the whole viewport), which duplicated the
     hero's own visual weight instead of reading as a smaller "see more"
     strip.
     Sized by height (not width, despite the width-driven comment this
     replaced) - iPhone shots are portrait and much taller/narrower than
     iPad's, so equal width gave the iPhone tab tall images and the iPad
     tab noticeably shorter ones (181x394 vs 181x242, measured) even
     though both were "46vw". Equal height instead makes both tabs read
     as the same size, each keeping its own natural width - the same
     approach the desktop carousel already uses (clamp(480px,...,624px)
     height, width:auto), just smaller. */
  /* padding-inline was 7.5vw, left over from when a single large card
     centered itself via scroll-snap - several shots fit in view at once
     now (see .carousel img below), the strip doesn't start centered,
     and that same 7.5vw just reads as a dead gap before the first
     image. 16px (matching the section's own container gutter) instead. */
  .carousel {
    gap: 12px;
    padding: 6px 16px 18px;
  }
  .carousel figure {
    scroll-snap-align: start;
  }
  /* Touch-swiping a horizontally-scrollable strip is an obvious,
     native gesture at mobile widths - unlike the iPhone-tab-only
     is-fit check above (which only hides arrows once content happens
     to not overflow), this hides them unconditionally on both tabs,
     arrows or not, relying on swipe as the only way to move the
     carousel here. */
  .carousel-nav {
    display: none;
  }
  .carousel img {
    height: clamp(260px, 72vw, 360px);
    width: auto;
    max-width: none;
    /* Matches .carousel figure's radius so they clip at the same curve. */
    border-radius: 20px;
  }
}

/* Tablet band (iPad portrait widths and similar, 700-1024px) within the
   1024px mobile layout above. The plain mobile rules size the text
   column as a % of the full viewport (`1.4fr` of the grid), which keeps
   growing past the text's own `max-width: 48ch` cap - opening a dead
   gap before the phone, which was also bleeding off the right edge from
   its own disconnected vw-based width. Fix: cap column 1 near the
   text's natural width, let column 2 (`1fr`) absorb the rest, and size
   the phone at 100% of that column instead of its own formula. Below
   700px (real phones) untouched. */
@media (min-width: 700px) and (max-width: 1024px) {
  .hero-grid {
    grid-template-columns: minmax(0, clamp(280px, 45vw, 460px)) minmax(
        0,
        1fr
      );
  }
  .hero-grid > div:first-child {
    padding-top: clamp(20px, 5vw, 56px);
  }
  /* The plain mobile rule's 205px cap guarded against the phone bleeding
     into the text column - no longer happens here (see .hero-phone-frame
     below), so let the paragraph fill the now-wider column instead. */
  .hero p.lede {
    max-width: none;
  }
  .hero-phone-frame {
    width: 100%;
    max-width: 480px;
    justify-self: stretch;
    margin-left: 0;
  }
  /* Desktop's 15.8cqw rate, not the phone-tuned 11.5cqw below - this
     column is wide enough now (see above) to use the same rate as
     desktop instead of staying pinned to a phone-era formula. */
  .hero-title-sub,
  .hero-title-main {
    font-size: clamp(36px, 15.8cqw, 91px);
  }
  /* Re-shown at this breakpoint only (the plain ≤1024px rule above
     hides it - that's still correct below 700px, where this column is
     too narrow for any of this to fit) and repositioned: at this
     band's width the store-pill button sits well clear of the phone
     (unlike narrower phone widths), leaving an empty sky pocket next
     to it, below the paragraph and above the bird - reported as
     looking bare. left/top are calc(Apx+Bvw), fitted from real
     getBoundingClientRect measurements of that pocket's center at this
     range's two edges (700px and 1024px) rather than reusing bee2's
     desktop cqw position, which is relative to the text column (a
     different, narrower box here) and anchored by JS that explicitly
     no-ops below 1024px (see syncBeeGap() in script.js) - a static fit
     is enough for a small decorative gap-filler like this one. */
  .hero-grid > div:first-child .critter-bee2 {
    display: block;
    width: clamp(70px, calc(55px + 4vw), 95px);
    top: calc(163px + 20vw);
    left: calc(18px + 25.5vw);
    bottom: auto;
  }
}

/* Real phones only (below the 700px tablet band above) - #books'
   heading was enlarged to ~1.3x the shared .section-head h2 size (see
   that rule's own comment), confirmed good on tablet/desktop, but on a
   narrow phone the bigger text wraps taller and crowds .critter-trex
   sitting above the book grid. Reverted to the original shared
   clamp(28px,...,52px) at this width only. */
@media (max-width: 699px) {
  #books .section-head h2 {
    font-size: clamp(28px, calc(19px + 1.5vw), 52px);
  }
}

@media (max-width: 760px) {
  /* Not enough room for the description and the iPhone/iPad tabs side
     by side - stack them, tabs above the text (flex `order`, markup
     stays text-first for screen readers). */
  .shots-head {
    flex-direction: column;
    align-items: flex-start;
    gap: 16px;
  }
  .shots-tabs {
    order: -1;
  }
  .shots-detail {
    max-width: none;
  }
  .books-grid {
    grid-template-columns: 1fr;
  }
  .footer-grid {
    grid-template-columns: 1fr 1fr;
  }
  .footer-brand {
    grid-column: 1 / -1;
  }
  .section {
    padding: 56px 0;
  }

  .hero {
    padding-bottom: 120px;
  }
  .hero::after {
    height: 160px;
    background-size: 100vw 160px;
    background-repeat: no-repeat;
  }
  .bg-full--hero {
    background-size:
      100vw 160px,
      cover;
  }
  .hero-critters .critter-owl {
    width: 120px;
  }
  /* This and the 480px query's .hero-bear rule both come AFTER the
     1024px query's in the cascade, so at any width <=760px they're what
     actually wins for `bottom` - changing only the 1024px query's value
     (as was tried) has no visible effect below 760px wide, since this
     rule (then the 480px one, narrower still) overrides it back.
     Lowered so more of the bear sits inside the treeline's own 160px
     band (.hero::after above) where its opaque tree-silhouette pixels
     actually are, instead of just within the band's empty-sky upper
     portion - a smaller `bottom` alone doesn't guarantee more coverage,
     since the treeline image isn't a solid-color strip. Sampled same as
     the 1024px query (this breakpoint's own x: left:80%, treeline 160px
     here): opaque starts ~127px above .hero's bottom edge, so `bottom`
     is set to leave just the top ~12% of the bear's height above it. */
  .hero-bear {
    width: 120px;
    left: 80%;
    bottom: 60px;
  }

  .books-critters .critter-trex {
    width: 160px;
  }
  .books-critters .critter-motorcycle {
    --moto-w: 160px;
    width: var(--moto-w);
  }
  /* See the 1024px query's #books rule - same logic, rescaled for this
     breakpoint's 160px moto (~107px tall). */
  #books {
    padding-bottom: 140px;
  }

  .about-critters .critter-about-cloud1,
  .about-critters .critter-about-cloud2 {
    width: 90px;
  }
}

@media (max-width: 480px) {
  .footer-grid {
    grid-template-columns: 1fr;
  }

  .hero {
    padding-bottom: 90px;
  }
  .hero::after {
    height: 120px;
    background-size: 100vw 120px;
    background-repeat: no-repeat;
  }
  .bg-full--hero {
    background-size:
      100vw 120px,
      cover;
  }
  /* critter-cloud1 used to be hidden alongside the owl here too - pre-
     dates the hero restructuring above, where cloud1 is now paired
     with bee1 above the headline (see the 1024px query) to match the
     design reference. Leaving owl hidden (unrelated, near the
     treeline/bottom of the hero, not part of this pairing). */
  .hero-critters .critter-owl {
    display: none;
  }
  /* Last (narrowest) of the three .hero-bear overrides - see the 760px
     query's comment above for why `bottom` has to be set here directly
     rather than relying on a wider breakpoint's value. Treeline here is
     only 120px tall, so this needs to sit lower still than the 760px
     value for the bear to read as behind the trees rather than above
     them. */
  .hero-bear {
    width: 100px;
    left: 80%;
    bottom: 60px;
  }

  .books-critters .critter-trex {
    width: 125px;
  }
  .books-critters .critter-motorcycle {
    --moto-w: 125px;
    width: var(--moto-w);
  }
}
