/* ============================================
   Shape Friends · shapefriends.com
   ============================================ */

/* Fonts are loaded via <link rel="stylesheet"> in each page's <head>
   so the browser can fetch them in parallel with this CSS, and via
   `display=optional` so the fallback font is committed for the session
   if the web font isn't ready in ~100ms — no mid-render swap flash
   (especially noticeable on the Playwrite GB J wordmark). */

/* ── Brand tokens ── */

:root {
  --cream:       #FDFCF6;
  --cream-dark:  #EEEEE8;
  --floor:       #FFD3C7;
  --ink:         #1A1814;
  --ink-light:   rgba(26, 24, 20, 0.55);
  --ink-faint:   rgba(26, 24, 20, 0.08);

  --green:   #4BB479;
  --pink:    #FB9B98;
  --orange:  #FB8D33;
  --blue:    #5ABEE0;
  --yellow:  #F6DB4D;
  --red:     #FA3360;
  --purple:  #BF8BED;
  --teal:    #75D4C2;
  --sky:     #5DD7E8;  /* Diamond — light cyan, "tilts and gleams" */

  --font-wordmark:    'Fredoka', sans-serif;
  --font-title:       'Fredoka', sans-serif;
  --font-body:        'Podkova', serif;
  --font-handwriting: 'Playwrite GB J', cursive;
  --font-accent:      'Cutive', serif;

  --max-width: 980px;
  --section-pad: 120px;

  /* Hero size — drives the hero tagline and the wordmark on every page.
     Stepped via media queries below so the subpage logo, hero tagline,
     and brand-script all scale together. */
  --hero-size: 112px;
  /* Size of the three hero characters that straddle the hero/philosophy
     boundary. Lives at :root so .philosophy can reference it for the
     negative-margin straddle pull. */
  --hero-friend-size: clamp(90px, 12vw, 160px);
}
@media (max-width: 1100px) { :root { --hero-size: 96px; } }
@media (max-width: 760px)  { :root { --hero-size: 72px; } }
@media (max-width: 520px)  { :root { --hero-size: 56px; } }
@media (max-width: 400px)  { :root { --hero-size: 44px; } }

/* ──────────────────────────────────────────────────────────────
   Higher-contrast palette — accessibility opt-in
   ──────────────────────────────────────────────────────────────
   The default brand palette is tuned for vibe (light, candy, kid-
   friendly) and trades some WCAG AA contrast on the large display
   text (hero tagline, trust title) for that visual identity. Users
   who need stronger contrast get a darker variant — each channel ×
   0.8, lifting most text into the AA-large band on cream.

   Activated via either:
   - The user has set "Increase contrast" in their OS accessibility
     settings (auto-applied via prefers-contrast: more), OR
   - The user clicked the "Higher contrast" footer toggle, which
     sets html[data-contrast="high"] and persists in localStorage.
*/
@media (prefers-contrast: more) {
  :root {
    --green:   #3C9061;
    --pink:    #C97C7A;
    --orange:  #C97129;
    --blue:    #4898B3;
    --yellow:  #C5AF3E;
    --red:     #C8294D;
    --purple:  #996FBE;
    --teal:    #5EAA9B;
    --sky:     #4AACBA;
  }
}
html[data-contrast="high"] {
  --green:   #3C9061;
  --pink:    #C97C7A;
  --orange:  #C97129;
  --blue:    #4898B3;
  --yellow:  #C5AF3E;
  --red:     #C8294D;
  --purple:  #996FBE;
  --teal:    #5EAA9B;
  --sky:     #4AACBA;
}

/* ──────────────────────────────────────────────────────────────
   Reduce motion — accessibility opt-in
   ──────────────────────────────────────────────────────────────
   Disables vestibular-trigger animations: the reveal-on-scroll
   fades, the carousel slide transition, the character mount fade,
   and the friends' procedural wobble + breath + cursor tracking
   (handled in characters.js, which reads either prefers-reduced-
   motion OR html[data-motion="reduced"]).

   Activated via either the OS preference or the footer toggle.
   Non-motion transitions (button colour hovers, app-store-button
   tints) are left alone — those aren't vestibular triggers. */
@media (prefers-reduced-motion: reduce) {
  .reveal { transition: none !important; }
  .character[data-friend] { transition: none !important; }
  .character svg { transition: none !important; }
  .friends-track { transition: none !important; }
}
html[data-motion="reduced"] .reveal,
html[data-motion="reduced"] .character[data-friend],
html[data-motion="reduced"] .character svg,
html[data-motion="reduced"] .friends-track {
  transition: none !important;
}

/* ── Reset ── */

*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

html {
  scroll-behavior: smooth;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  /* Belt-and-braces against horizontal scroll on mobile. `overflow-x: clip`
     prevents both the html and body from establishing a horizontal scroll
     container (unlike `hidden`, which still allows touch-panning content
     beyond the edge on some iOS WebKit versions). */
  overflow-x: clip;
}

body {
  font-family: var(--font-body);
  color: var(--ink);
  background: var(--cream);
  line-height: 1.6;
  overflow-x: clip;
}

::selection { background: rgba(246, 219, 77, 0.3); color: var(--ink); }

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

/* ── Layout ── */

.container { max-width: var(--max-width); margin: 0 auto; padding: 0 28px; }
section    { padding: var(--section-pad) 0; }

/* ── Brand script logo (handwritten "shape friends") ── */

.brand-script {
  font-family: var(--font-handwriting);
  font-weight: 400;
  font-size: clamp(12px, 1.4vw, 16px);
  color: var(--ink);
  line-height: 1;
  text-align: center;
  text-decoration: none;
  display: block;
}

.brand-script--link {
  /* Subpage wordmark — same size formula as the hero brand-script
     (`--hero-size * 0.4`) so the logo identity stays consistent across
     pages. The base .brand-script font-size is tiny because it's used
     inline in some contexts; the link variant gets the proper header
     presence. */
  font-size: calc(var(--hero-size) * 0.4);
  margin: 0 auto 64px;
  transition: opacity 0.2s;
}
.brand-script--link:hover { opacity: 0.7; }

/* ── Hero ── */

.hero {
  min-height: 100vh;
  display: flex; flex-direction: column;
  align-items: center;
  text-align: center;
  padding: 60px 24px 0;
  position: relative;
  /* Allow the friends row to dip below the hero into the green
     philosophy section without being clipped. */
  overflow: visible;
}

/* Centred content (logo + tagline) takes the upper space; the friends
   row sits at the bottom of the hero where it can straddle into the
   philosophy section below. */
.hero-content {
  flex: 1;
  display: flex; flex-direction: column;
  justify-content: center; align-items: center;
}

/* Subtle warm glow behind characters */
.hero::before {
  content: ''; position: absolute;
  width: 600px; height: 600px; border-radius: 50%;
  background: radial-gradient(circle, rgba(246, 219, 77, 0.06), transparent 70%);
  bottom: 10%; left: 50%; transform: translateX(-50%);
  pointer-events: none;
}

.hero::after {
  content: ''; position: absolute;
  width: 400px; height: 400px; border-radius: 50%;
  background: radial-gradient(circle, rgba(251, 155, 152, 0.05), transparent 70%);
  top: 15%; right: -5%;
  pointer-events: none;
}

.hero-content { position: relative; z-index: 1; max-width: 1200px; }

/* Hero text size: fixed steps at breakpoints, logo follows at 1:4 ratio.
   No vw scaling, so text stays confidently big across desktop widths. */
/* --hero-size lives at :root now (top of file) so both the hero
   wordmark and the subpage wordmark scale identically. */

/* Logo set to 0.4× hero font-size so the thin script visually matches
   ~1/4 of the heavy Fredoka letter heights. */
.hero .brand-script {
  font-size: calc(var(--hero-size) * 0.4);
  margin-bottom: calc(var(--hero-size) * 0.18);
  animation: hero-enter 1s cubic-bezier(0.16, 1, 0.3, 1) 0.05s both;
}

/* Big bubbly hero tagline: three colored phrases, wraps naturally */
.hero-tagline {
  font-family: var(--font-title); font-weight: 700;
  font-size: var(--hero-size);
  line-height: 1.05; letter-spacing: -0.015em;
  margin: 0 0 64px;
  animation: hero-enter 1s cubic-bezier(0.16, 1, 0.3, 1) 0.25s both;
}
/* Each phrase on its own line; if a phrase is too wide for the viewport
   it wraps within itself but stays in its own colour. */
.hero-tagline span { display: block; }

.hero-characters {
  display: flex;
  gap: clamp(24px, 6vw, 56px);
  align-items: flex-end; justify-content: center;
  animation: hero-enter 1s cubic-bezier(0.16, 1, 0.3, 1) 0.7s both;
  /* The actual straddle effect is created by negative margin-top on the
     .philosophy section below — pulling that section up has the same
     visual effect (characters appear to dip into the green) but doesn't
     get swallowed by the hero's min-height: 100vh. */
  position: relative;
  z-index: 2;
}

/* ── Characters (SVG) ── */

.character { display: inline-block; }

.character svg {
  width: 100%; height: 100%;
  transition: transform 0.3s ease;
  /* The procedural wobble + fillScale (port from iOS Characters) lets each
     shape paint slightly outside the viewBox so all variants read at the
     same visual mass. SwiftUI's frame doesn't clip; we replicate that here. */
  overflow: visible;
}

/* First-paint hide. Characters tagged for JS mount are kept invisible until
   characters.js rewrites their geometry to iOS-uniform positions, so we
   never see the static-HTML eye coordinates flash before the JS pass. The
   mount handler adds .sf-mounted to fade them in cleanly. */
.character[data-friend] { opacity: 0; transition: opacity 0.4s ease; }
.character[data-friend].sf-mounted { opacity: 1; }

.character:hover svg {
  transform: scale(1.08) rotate(-3deg);
}

.hero .character { width: var(--hero-friend-size); }

/* Breath is now driven by characters.js (per-friend personality, procedural
   sinusoid keyed to the personality-adjusted clock). The static CSS
   `breathe` keyframes here used to scale .character — leaving them on would
   compound with the JS scale on .sf-breath. */

/* Blink + pupil drift + cursor tracking are driven by characters.js. */

/* ── Philosophy ── */

/* Green-tinted "next room" — the hero friends dip down into here
   so the cross character bridges the cream/green colour change. */
.philosophy {
  text-align: center;
  background: color-mix(in srgb, var(--green) 14%, var(--cream));
  /* flow-root prevents the hero-characters' negative margin from
     collapsing through this section. */
  display: flow-root;
  /* Top padding leaves room for the friends to occupy the top portion
     without crowding the text. Bottom padding is smaller than the full
     --section-pad (the bridge text inside already provides most of the
     transition into .friends), but NOT zero — the bridge needs visible
     breathing room WITHIN the green section so it doesn't butt against
     the green/cream colour seam. (.friends and .apps can use
     padding-bottom: 0 because they share the cream background with the
     next section; .philosophy has a colour change so it needs to seat
     the bridge properly.) */
  padding: clamp(70px, 11vw, 140px) 0 clamp(48px, 5vw, 80px);
  /* Pull this section up so the hero-characters row (sitting at the
     hero's bottom edge) genuinely straddles the cream/green boundary
     instead of just sitting on top of it. We pull by ~55% of friend size
     to compensate for the slightly taller heart character; pure 50% would
     leave the heart only partially crossing. */
  margin-top: calc(-1 * var(--hero-friend-size) * 0.55);
}

.philosophy-text {
  max-width: 720px; margin: 0 auto;
  font-family: var(--font-body); font-weight: 500;
  font-size: clamp(22px, 3.2vw, 30px);
  line-height: 1.7;
  color: var(--ink);
}

/* ── Friends ── */

.friends {
  text-align: center;
  background: linear-gradient(180deg, var(--cream) 0%, var(--cream-dark) 50%, var(--cream) 100%);
  /* The .friends-coda below the carousel already provides the visual
     breather into the next section, so collapse this section's own
     bottom padding to avoid stacking section-pad + section-pad + coda
     margin = a giant void on mobile. */
  padding-bottom: 0;
}

.friends h2 {
  font-family: var(--font-title);
  font-size: clamp(34px, 7vw, 56px);
  margin-bottom: 12px;
}

.friends-subtitle {
  /* Narrator voice — Podkova at a lighter weight + faded ink instead of
     the script font. Playwrite is now reserved for the .brand-script
     wordmark, so the page reads with one less typeface in the mix. */
  font-family: var(--font-body); font-weight: 400;
  color: var(--ink-light);
  font-size: clamp(16px, 2.5vw, 22px);
  margin-bottom: 60px;
}

/* Each friend becomes a proper "character card": brand-tinted backdrop,
   big featured character, confident descriptor. Treats the first
   introduction to the cast as a real reveal, not a small icon list. */
.friend {
  text-align: center;
  background: color-mix(in srgb, var(--accent, var(--green)) 14%, white);
  border-radius: 32px;
  /* Tightened from 44px top so cards aren't visually tall+empty. */
  padding: 32px 24px 28px;
  display: flex;
  flex-direction: column;
  align-items: center;
  /* Centre content (was flex-end which pushed everything to the bottom
     and left an awkward empty band at the top). */
  justify-content: center;
  box-shadow:
    0 16px 40px rgba(26, 24, 20, 0.08),
    inset 0 1px 0 rgba(255, 255, 255, 0.7);
  transition: transform 0.4s cubic-bezier(0.16, 1, 0.3, 1),
              box-shadow 0.4s ease;
}
.friend:hover {
  transform: translateY(-6px);
  box-shadow:
    0 24px 56px rgba(26, 24, 20, 0.12),
    inset 0 1px 0 rgba(255, 255, 255, 0.7);
}

.friend .character {
  width: clamp(120px, 16vw, 200px);
  max-width: 100%;
}

/* Friend descriptor — a label, not a title. Tuned tight enough that the
   longest 5-word descriptors ("Looks good at every angle", "Soft, warm,
   full of hugs") fit on ONE line at every carousel breakpoint, so cards
   don't go inconsistent heights from text wrapping. */
.friend-desc {
  font-family: var(--font-title); font-weight: 600;
  font-size: clamp(14px, 1.1vw, 16px);
  line-height: 1.3;
  letter-spacing: -0.005em;
  color: color-mix(in srgb, var(--accent, var(--ink)) 35%, var(--ink));
  margin-top: 18px;
  /* Belt-and-braces: if a viewport ever gets narrow enough that a
     descriptor would still wrap (e.g. a translated string longer than
     the English ones), don't let the wrap distort the card height — the
     `white-space: nowrap` here would clip with ellipsis instead. We
     omit nowrap on purpose so wrapping is graceful, but with the font
     size above all current descriptors fit in 1 line. */
}

/* ───────────────────────────────────────────────────────────────
   Section bridges. Small storybook narrator lines that pass the
   reader from one section to the next, so the page reads like
   turning pages of a kids' book rather than a stack of separate
   marketing blocks. Same handwriting font as the section
   subtitles so it all feels like one warm voice.
   ─────────────────────────────────────────────────────────────── */
.friends-coda,
.section-bridge {
  /* Narrator voice — Podkova at lighter weight; distinguished from body
     prose by smaller size, --ink-light colour, generous line-height,
     and centred alignment (set below). */
  font-family: var(--font-body); font-weight: 400;
  font-size: clamp(20px, 2.2vw, 28px);
  line-height: 1.5; letter-spacing: 0.005em;
  color: var(--ink-light);
  text-align: center;
  max-width: 640px;
  margin-left: auto;
  margin-right: auto;
}

/* Friends carousel ends with a generous transition into "Our apps".
   Lower clamp floor (was 96) so mobile doesn't get a chunky 96px gap. */
.friends-coda {
  margin-top: clamp(56px, 12vw, 144px);
}

/* End-of-section bridges (philosophy → friends, apps → trust).
   Generous top margin so the line reads as a proper narrator's
   pause rather than a tacked-on label after the body text. */
.section-bridge {
  margin-top: clamp(56px, 11vw, 140px);
}

/* ─────────────────────────────────────────────────────────────
   Friends carousel: classic 3-up slider with prev/next buttons
   and infinite wrap-around in both directions.
   ───────────────────────────────────────────────────────────── */

.friends-carousel {
  display: flex;
  align-items: center;
  gap: clamp(16px, 2.5vw, 32px);
  /* Free-flowing width so the carousel uses the full section width.
     Side padding keeps the cards off the viewport edges. */
  padding: 0 clamp(24px, 4vw, 64px);
  margin: 0 auto;
  --gap: clamp(20px, 2.4vw, 32px);
  --visible: 3;
}

/* More room → more cards on screen. JS reads --visible and resizes the
   friends so the carousel always fills the available width cleanly.
   Breakpoints chosen so the resulting card width always leaves enough
   horizontal room for 5-word descriptors to fit on a single line. */
@media (min-width: 1400px) { .friends-carousel { --visible: 4; } }
@media (min-width: 1720px) { .friends-carousel { --visible: 5; } }
@media (min-width: 2040px) { .friends-carousel { --visible: 6; } }
@media (min-width: 2400px) { .friends-carousel { --visible: 7; } }

.friends-nav {
  flex-shrink: 0;
  width: 60px; height: 60px;
  border-radius: 50%;
  background: white;
  border: 1.5px solid rgba(26, 24, 20, 0.10);
  color: var(--ink);
  cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  transition: background 0.25s, border-color 0.25s,
              color 0.25s, transform 0.25s, box-shadow 0.25s;
  box-shadow: 0 4px 16px rgba(26, 24, 20, 0.06);
}
.friends-nav svg { width: 26px; height: 26px; }

.friends-nav:hover {
  background: var(--ink);
  color: var(--cream);
  border-color: var(--ink);
  transform: scale(1.06);
  box-shadow: 0 10px 24px rgba(26, 24, 20, 0.16);
}
.friends-nav:active { transform: scale(0.96); }
.friends-nav:disabled { opacity: 0.4; cursor: not-allowed; }

.friends-viewport {
  flex: 1;
  /* Flex items default to min-width: auto = the intrinsic content
     width. The track is enormous (18 cards), so without this the
     viewport would grow to fit everything. */
  min-width: 0;
  /* Padding stays still while the track translates inside, giving a
     stable buffer zone where card box-shadows can render before
     reaching the viewport's overflow-clip boundary. */
  padding: 0 40px;
  /* Clip horizontally (the carousel translates cards out of view
     to the left/right) at the viewport's outer edge — past the
     padding, so shadows inside the padding zone are unclipped.
     Let card drop shadows extend above and below unclipped. */
  overflow-x: clip;
  overflow-y: visible;
  /* Drag/swipe support: claim horizontal gestures, leave vertical
     page scroll to the browser. */
  touch-action: pan-y;
  cursor: grab;
  -webkit-user-select: none;
          user-select: none;
}
.friends-carousel.is-dragging .friends-viewport { cursor: grabbing; }
/* While dragging, ignore pointer events on the cards' inner SVGs so
   the gesture isn't hijacked by the browser's default image-drag. */
.friends-carousel.is-dragging .friend * { pointer-events: none; }

.friends-track {
  display: flex;
  gap: var(--gap);
  will-change: transform;
}

.friends-track .friend {
  flex-shrink: 0;
  /* JS sets width to (viewport - 2*gap) / 3 so 3 friends fit exactly */
}

@media (max-width: 1000px) {
  .friends-carousel {
    --visible: 2;
    /* Tighten the carousel side padding so two cards fit comfortably without
       getting crushed between nav buttons. The 1000px breakpoint (bumped
       up from 880) means 3-up only kicks in once cards are wide enough
       for 5-word descriptors to fit on one line. */
    padding: 0 16px;
    gap: 12px;
  }
  .friends-nav { width: 52px; height: 52px; }
  .friend { padding: 36px 22px 28px; border-radius: 28px; }
}

@media (max-width: 560px) {
  .friends-carousel {
    --visible: 1;
    /* Drop the carousel's own side padding to a hairline — the viewport
       padding below is what gives the cards their breathing room now. */
    padding: 0;
    gap: 0;
  }
  /* Hide nav buttons on small screens — swipe is the natural mobile
     gesture and the buttons were eating ~130px of horizontal real estate.
     The .friends-dots below take over as the swipe affordance. */
  .friends-nav { display: none; }
  /* Bigger viewport padding (was 28px) so a clearer slice of the
     neighbour cards peeks in from each side — combined with the dots
     below, the carousel reads as obviously swipeable. */
  .friends-viewport { padding: 0 40px; }
  .friend {
    padding: 28px 18px 22px;
    border-radius: 22px;
    /* Drop flex-end → centre so the character + descriptor sit in the
       middle of the card instead of clinging to the bottom. */
    justify-content: center;
  }
  /* Allow the character to grow into the wider card. */
  .friend .character { width: clamp(120px, 38vw, 180px); }
  .friend-desc { font-size: 16px; margin-top: 18px; }
}

/* ── Pagination dots — mobile swipe affordance ──────────────────
   Visible only on mobile, where the nav arrows are hidden and the dot
   row tells the user there are nine friends to swipe between. Each dot
   is also tappable for direct navigation. */
.friends-dots {
  display: none;
}
@media (max-width: 560px) {
  .friends-dots {
    display: flex;
    justify-content: center;
    gap: 10px;
    margin-top: 28px;
    padding: 0 24px;
    flex-wrap: wrap;
  }
}
.friends-dot {
  appearance: none;
  -webkit-appearance: none;
  width: 9px; height: 9px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: rgba(26, 24, 20, 0.20);
  cursor: pointer;
  transition: background 0.25s ease, transform 0.25s ease;
}
.friends-dot[aria-current="true"] {
  background: var(--ink);
  transform: scale(1.3);
}
.friends-dot:hover { background: rgba(26, 24, 20, 0.40); }
.friends-dot[aria-current="true"]:hover { background: var(--ink); }
/* Larger hit target than the visual size so dots are easy to tap. */
.friends-dot::before {
  content: '';
  position: absolute;
  inset: -8px;
}
.friends-dot { position: relative; }

/* ── Apps ── */

.apps {
  text-align: center;
  /* Like .friends, the trailing .section-bridge inside this section is
     the visual breather into trust — so we don't stack section-pad on
     top of the bridge's already-generous margin-top. */
  padding-bottom: 0;
}

.apps h2 {
  font-family: var(--font-title);
  font-size: clamp(34px, 7vw, 56px);
  margin-bottom: 12px;
}

.apps-subtitle {
  font-family: var(--font-body); font-weight: 400;
  color: var(--ink-light);
  font-size: clamp(16px, 2.5vw, 22px);
  margin-bottom: 56px;
}

.apps-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 40px;
  max-width: 1000px;
  margin: 0 auto;
}

/* App card: feels like an iOS app listing. Big squircle icon on top,
   description + CTA below. */
.app-card {
  background: transparent;
  padding: 0;
  text-align: center;
}

/* The "app icon": rounded squircle (iOS 22% radius), brand-tinted bg.
   Contains a unique illustration on top and the colourful wordmark
   below — like the home-screen icon of a real app. Generous content,
   tight padding so the graphic dominates the canvas (the way real
   App Store icons do).

   `container-type: inline-size` turns this into a CSS container so
   the children (illustration + wordmark) can size relative to the
   icon, not the viewport. This is what lets the wordmark scale up
   on a big icon instead of being capped at a tiny pixel value. */
.app-icon {
  aspect-ratio: 1;
  border-radius: 22.37%;
  background: color-mix(in srgb, var(--card-accent) 12%, white);
  display: flex;
  align-items: center;
  justify-content: center;
  /* Now the icon contains only the illustration (wordmark moved out to
     become a sibling under the card), so the icon can be a single-child
     centred container with comfortable padding around the graphic. */
  padding: 10%;
  box-shadow:
    0 18px 48px rgba(26, 24, 20, 0.10),
    0 4px 12px rgba(26, 24, 20, 0.05),
    inset 0 1px 0 rgba(255, 255, 255, 0.6);
  margin-bottom: 20px;
  transition:
    transform 0.45s cubic-bezier(0.16, 1, 0.3, 1),
    box-shadow 0.45s ease;
}

.app-card:hover .app-icon {
  transform: translateY(-8px) rotate(-1.5deg);
  box-shadow:
    0 28px 64px rgba(26, 24, 20, 0.14),
    0 6px 16px rgba(26, 24, 20, 0.06),
    inset 0 1px 0 rgba(255, 255, 255, 0.6);
}

/* Object illustration above the wordmark (cupcake / book / note).
   Sized relative to the icon container so it dominates the canvas
   the way real app icon graphics do. */
.app-illustration {
  /* Bigger now that the wordmark moved out — illustration dominates the
     icon canvas the way a real iOS icon graphic would. */
  width: 80%;
  height: auto;
  flex-shrink: 0;
  /* Tiny continuous breathing animation, organic and calm */
  animation: app-illustration-bob 5.5s ease-in-out infinite;
}
.app-card:nth-child(2) .app-illustration { animation-delay: 1.2s; }
.app-card:nth-child(3) .app-illustration { animation-delay: 2.4s; }

@keyframes app-illustration-bob {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-4px); }
}

.app-card-wordmark {
  font-family: var(--font-wordmark); font-weight: 700;
  /* Sits below the icon as a sibling, so it scales by viewport (cqi
     no longer applies — the icon was the container before). */
  font-size: clamp(26px, 3.4vw, 38px);
  line-height: 0.95; letter-spacing: -0.02em;
  text-align: center;
  margin-bottom: 18px;
}
.app-card-wordmark span { display: block; }

/* Ratio lock on narrow viewports: don't let the 1:1 icon become
   absurdly tall when the grid collapses to a single column. */
@media (max-width: 720px) {
  .app-card { max-width: 340px; margin: 0 auto; }
}

.app-card-desc {
  font-family: var(--font-body);
  font-size: 17px; color: var(--ink-light);
  margin-bottom: 22px; line-height: 1.55;
  max-width: 320px; margin-left: auto; margin-right: auto;
}

/* ---- App Store badge ---- */

/* Native-feeling system font stack for the badge */
.appstore-btn {
  font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Text', 'Helvetica Neue', sans-serif;
  -webkit-font-smoothing: antialiased;
}

/* Apple App Store badge: black pill, apple glyph, two-line text.
   Mimics Apple's official "Download on the App Store" badge style. */
.appstore-btn {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  background: #000;
  color: #fff;
  text-decoration: none;
  padding: 9px 16px 9px 14px;
  border-radius: 10px;
  transition: transform 0.2s ease, background 0.2s ease, box-shadow 0.2s ease;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12);
}
.appstore-btn:hover {
  transform: translateY(-1px);
  background: #1c1c1e;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.18);
}
.appstore-btn:active {
  transform: translateY(0);
  background: #2a2a2c;
}

.appstore-btn-glyph {
  width: 26px; height: 26px;
  fill: currentColor;
  flex-shrink: 0;
}

.appstore-btn-text {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  line-height: 1;
  text-align: left;
}

.appstore-btn-small {
  font-size: 10.5px;
  font-weight: 400;
  letter-spacing: 0.01em;
  margin-bottom: 3px;
  opacity: 0.92;
}

.appstore-btn-large {
  font-size: 17px;
  font-weight: 600;
  letter-spacing: -0.01em;
}

/* Ghost variant for not-yet-shipping apps — outlined, less emphatic */
.appstore-btn--ghost {
  background: transparent;
  color: var(--ink);
  border: 1.5px solid rgba(26, 24, 20, 0.18);
  box-shadow: none;
}
.appstore-btn--ghost:hover {
  background: rgba(26, 24, 20, 0.04);
  border-color: rgba(26, 24, 20, 0.28);
  box-shadow: none;
}
.appstore-btn--ghost:active {
  background: rgba(26, 24, 20, 0.06);
}

/* ── Trust ── */

.trust {
  text-align: center;
  /* Extra cream room at the bottom so the footer friends have space
     to peek up into without crowding the trust grid above. */
  padding-bottom: calc(var(--section-pad) + 80px);
}

/* Big bold trust statement: each word a different brand colour */
.trust-title {
  font-family: var(--font-title); font-weight: 700;
  font-size: clamp(44px, 6.5vw, 96px);
  line-height: 1.05; letter-spacing: -0.025em;
  margin-bottom: 28px;
}
.trust-title span { display: inline-block; }

/* Colour-coded subtitle: body font, sized for reading, not for display.
   Three short statements separated by a soft dot. */
.trust-subtitle {
  font-family: var(--font-body); font-weight: 500;
  font-size: clamp(16px, 1.6vw, 20px);
  line-height: 1.4; letter-spacing: 0.01em;
  margin-bottom: 64px;
}
.trust-subtitle span {
  display: inline-block;
}
.trust-subtitle span + span::before {
  content: '·';
  color: var(--ink-light);
  margin: 0 0.7em;
  font-weight: 400;
}

.trust-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 16px;
  max-width: 680px;
  margin: 0 auto;
  text-align: left;
}

.trust-item {
  display: flex; align-items: center; gap: 16px;
  padding: 20px 24px;
  border-radius: 20px;
  background: rgba(255, 255, 255, 0.5);
  border: 1px solid var(--ink-faint);
  transition: background 0.2s, transform 0.2s;
}
.trust-item:hover {
  background: rgba(255, 255, 255, 0.85);
  transform: translateY(-2px);
}

.trust-icon {
  width: 52px; height: 52px;
  border-radius: 16px;
  display: flex; align-items: center; justify-content: center;
  flex-shrink: 0;
}
.trust-icon svg { width: 26px; height: 26px; }

.trust-item-text {
  font-family: var(--font-title);
  font-weight: 600; font-size: 18px; line-height: 1.3;
}

/* Staggered reveal for trust grid — same soft gummy curve, no blur */
.trust-grid .trust-item {
  opacity: 0;
  transform: translateY(14px);
  transition:
    opacity 0.9s cubic-bezier(0.22, 1, 0.36, 1),
    transform 1.1s cubic-bezier(0.16, 1.08, 0.3, 1);
}
.trust-grid.visible .trust-item {
  opacity: 1;
  transform: translateY(0);
}
.trust-grid .trust-item:nth-child(1) { transition-delay: 0.00s; }
.trust-grid .trust-item:nth-child(2) { transition-delay: 0.06s; }
.trust-grid .trust-item:nth-child(3) { transition-delay: 0.12s; }
.trust-grid .trust-item:nth-child(4) { transition-delay: 0.18s; }
.trust-grid .trust-item:nth-child(5) { transition-delay: 0.24s; }
.trust-grid .trust-item:nth-child(6) { transition-delay: 0.30s; }

/* ── Footer ── */

footer {
  --footer-friend-size: clamp(72px, 9vw, 130px);
  background: var(--floor);
  /* zero top padding so the friends' negative margin can pull them
     above the cream/pink boundary. flow-root creates a new block
     formatting context, preventing the friends' margin-top from
     collapsing into the footer's own margin. */
  padding: 0 24px 56px;
  text-align: center;
  position: relative;
  overflow: visible;
  display: flow-root;
}

/* Friends standing on the pink floor: their top half pokes up into
   the cream section above. The flex container's top sits exactly at
   the cream/pink boundary, then a negative margin pulls it up by half
   the character height so the boundary cuts each character through
   the middle. Generous bottom margin keeps the footer-links from
   crowding the characters from below. */
.footer-friends {
  display: flex;
  justify-content: center;
  align-items: flex-end;
  gap: clamp(14px, 2.5vw, 36px);
  margin-top: calc(-1 * var(--footer-friend-size) / 2);
  margin-bottom: 72px;
  position: relative;
  z-index: 1;
}

.footer-friends .character {
  width: var(--footer-friend-size);
}

.footer-links {
  display: flex; justify-content: center; gap: 36px;
  list-style: none; margin-bottom: 20px;
}

.footer-links a {
  font-family: var(--font-body); font-weight: 500; font-size: 16px;
  color: var(--ink); text-decoration: none; transition: opacity 0.2s;
}
.footer-links a:hover { opacity: 0.7; }

.footer-tagline {
  font-family: var(--font-body); font-weight: 400;
  font-size: 15px; color: var(--ink-light); margin-bottom: 10px;
}

.footer-copy {
  font-family: var(--font-accent);
  font-size: 12px; color: var(--ink-light);
}

/* Accessibility opt-ins. Two toggles (Higher contrast + Reduce motion)
   sit quietly under the copyright line — discoverable for the users
   who want them, without competing with the brand identity. Each one
   also auto-activates when the corresponding OS preference is set
   (prefers-contrast: more / prefers-reduced-motion: reduce). */
.footer-a11y {
  display: flex;
  justify-content: center;
  gap: 4px;
  flex-wrap: wrap;
  margin-top: 12px;
}
.footer-a11y-toggle {
  appearance: none;
  -webkit-appearance: none;
  border: 0;
  background: transparent;
  font-family: var(--font-accent);
  font-size: 12px;
  color: var(--ink-light);
  cursor: pointer;
  padding: 4px 12px;
  border-radius: 999px;
  transition: color 0.2s, background 0.2s;
}
.footer-a11y-toggle:hover {
  color: var(--ink);
  background: var(--ink-faint);
}
.footer-a11y-toggle[aria-pressed="true"] {
  color: var(--ink);
  background: var(--ink-faint);
}
.footer-a11y-toggle[aria-pressed="true"]::before {
  content: '✓ ';
  margin-right: 2px;
}

/* ── Privacy & Support (inner pages) ── */

.page {
  padding-top: 56px;
  /* Extra bottom room so the footer friends have space to peek up into
     without crowding the last paragraph / form on subpages. Same buffer
     the .trust section uses on the home page. */
  padding-bottom: calc(var(--section-pad) + 80px);
  min-height: 100vh;
}

.page-title {
  font-family: var(--font-title);
  font-size: clamp(38px, 8vw, 60px);
  /* 16px gap to subtitle — matches the friends/apps title→subtitle spacing
     proportionally (12px on a smaller heading). 8px was too tight for an h1. */
  margin-bottom: 16px;
  text-align: center;
}

.page-subtitle {
  font-family: var(--font-body); font-weight: 400;
  font-size: 18px; color: var(--ink-light);
  text-align: center; margin-bottom: 56px;
}

.page-content { max-width: 640px; margin: 0 auto; }

.page-content h2 {
  font-family: var(--font-title);
  font-size: 26px; margin-top: 44px; margin-bottom: 14px;
}

.page-content p {
  font-size: 18px; line-height: 1.75;
  margin-bottom: 18px; color: var(--ink);
}

.page-content strong { font-family: var(--font-body); font-weight: 800; }

.page-content ul {
  list-style: none;
  padding: 0;
  margin: 0 0 18px;
}
.page-content li {
  font-size: 18px; line-height: 1.6;
  margin-bottom: 16px;
  padding-left: 22px;
  position: relative;
  color: var(--ink);
}
.page-content li::before {
  content: '';
  position: absolute;
  left: 0; top: 0.65em;
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--green);
}
.page-content code {
  font-family: ui-monospace, 'SF Mono', Menlo, monospace;
  font-size: 0.9em;
  background: var(--ink-faint);
  padding: 1px 6px;
  border-radius: 4px;
}

.page-content a {
  color: var(--green); text-decoration: underline;
  text-decoration-color: rgba(75, 180, 121, 0.3);
  text-underline-offset: 3px;
}
.page-content a:hover { text-decoration-color: var(--green); }

/* ── Contact form ── */

.contact-form { max-width: 480px; margin: 0 auto; }

/* ── 404 page ──
   Lives at /404.html. Shares .page layout with privacy/support so the
   wordmark, page-title, page-subtitle, and footer all match the rest
   of the site. Adds the big "404" mark, a friends row that visually
   "searches" alongside the visitor (their idle-drift eyes + cursor
   tracking sells the bit), and a prominent take-me-home pill. */
.error-code {
  font-family: var(--font-title);
  font-weight: 700;
  font-size: clamp(120px, 24vw, 240px);
  line-height: 0.92;
  letter-spacing: -0.04em;
  color: var(--orange);
  text-align: center;
  margin: clamp(24px, 4vw, 48px) 0 clamp(16px, 2vw, 24px);
}
.error-friends {
  display: flex;
  justify-content: center;
  align-items: flex-end;
  gap: clamp(24px, 6vw, 56px);
  margin: clamp(40px, 6vw, 72px) 0 clamp(32px, 5vw, 56px);
}
.error-friends .character {
  width: clamp(80px, 14vw, 140px);
}
.error-home {
  display: inline-block;
  font-family: var(--font-title);
  font-weight: 600;
  font-size: clamp(16px, 1.4vw, 19px);
  color: var(--cream);
  background: var(--ink);
  padding: 16px 32px;
  border-radius: 999px;
  text-decoration: none;
  margin: 0 auto;
  transition: transform 0.15s ease, background 0.2s ease, box-shadow 0.2s ease;
  box-shadow: 0 8px 24px rgba(26, 24, 20, 0.18);
}
.error-home:hover {
  transform: translateY(-2px);
  box-shadow: 0 12px 32px rgba(26, 24, 20, 0.25);
}
.error-home:active {
  transform: translateY(0);
}
/* Centre the take-me-home pill since it's an inline-block in a flow ctx. */
.error-page .container {
  text-align: center;
}
.error-page .page-title {
  margin-bottom: 12px;
}

.form-group { margin-bottom: 24px; }

.form-group label {
  display: block; font-family: var(--font-title); font-weight: 600;
  font-size: 16px; margin-bottom: 8px; color: var(--ink);
}

.form-group input,
.form-group textarea {
  width: 100%; font-family: var(--font-body); font-size: 17px;
  padding: 14px 18px;
  border: 2px solid rgba(26, 24, 20, 0.10);
  border-radius: 16px; background: white; color: var(--ink);
  outline: none; transition: border-color 0.2s, box-shadow 0.2s;
}

.form-group input:focus,
.form-group textarea:focus {
  border-color: var(--green);
  box-shadow: 0 0 0 4px rgba(75, 180, 121, 0.10);
}

.form-group textarea { min-height: 160px; resize: vertical; }

.form-submit {
  display: inline-block; font-family: var(--font-title); font-size: 17px;
  background: var(--ink); color: var(--cream);
  border: none; padding: 16px 40px;
  border-radius: 100px; cursor: pointer;
  transition: background 0.2s, transform 0.2s;
}
.form-submit:hover { background: #3d2208; transform: scale(1.03); }

.form-note {
  font-family: var(--font-accent); font-size: 13px;
  color: var(--ink-light); margin-top: 18px;
}

/* ── Animations ── */

@keyframes breathe {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.03); }
}

@keyframes blink {
  0%, 93%, 100% { transform: scaleY(1); }
  95.5%, 97%    { transform: scaleY(0.05); }
}

@keyframes hero-enter {
  from { opacity: 0; transform: translateY(24px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* ── Scroll-linked fluidity ─────────────────────────────────────
   On browsers that support scroll-driven animations (Chrome/Edge
   115+, Firefox behind a flag, Safari coming soon), elements that
   opt in respond CONTINUOUSLY to scroll position rather than
   triggering once. Gives the page a fluid, calm-river feel.
   Falls back to the IntersectionObserver reveal elsewhere. */
@supports (animation-timeline: view()) {

  /* Hero friends gently breathe in as you scroll out of the hero,
     so the transition into the green section feels organic. */
  .hero-characters {
    animation: hero-friends-drift linear both;
    animation-timeline: view();
    animation-range: exit 0% exit 100%;
  }
  @keyframes hero-friends-drift {
    from { transform: translateY(0) scale(1); }
    to   { transform: translateY(12px) scale(0.985); }
  }

  /* Section bridges drift in continuously as they enter the viewport
     instead of waiting for the threshold-based observer to fire. */
  .section-bridge,
  .friends-coda {
    animation: bridge-arrive linear both;
    animation-timeline: view();
    animation-range: entry 0% entry 60%;
  }
  @keyframes bridge-arrive {
    from { opacity: 0; transform: translateY(28px); }
    to   { opacity: 1; transform: translateY(0); }
  }
}

/* ── Scroll reveal (blur-in) ── */

/* Soft "gummy" reveal. No blur — that was the harsh part. The transform
   uses a tiny springy curve so things settle in like jelly rather than
   snapping into place. Two separate timings so opacity catches up
   slightly before the position finishes, which reads as a calm arrival. */
.reveal {
  opacity: 0;
  transform: translateY(28px);
  transition:
    opacity 1.1s cubic-bezier(0.22, 1, 0.36, 1),
    transform 1.3s cubic-bezier(0.16, 1.08, 0.3, 1);
}
.reveal.visible {
  opacity: 1;
  transform: translateY(0);
}

/* App card stagger */
.app-card.reveal:nth-child(1) { transition-delay: 0.00s; }
.app-card.reveal:nth-child(2) { transition-delay: 0.08s; }
.app-card.reveal:nth-child(3) { transition-delay: 0.16s; }

/* ── Responsive ── */

@media (max-width: 768px) {
  :root { --section-pad: 80px; }

  /* Keep bottom padding at 0 so the hero-characters row sits at the
     hero's bottom edge and can straddle the cream/green boundary into
     the .philosophy section. */
  .hero { padding: 80px 20px 0; }

  .apps-grid {
    grid-template-columns: 1fr;
    max-width: 380px; margin: 0 auto;
  }

  .trust-grid {
    grid-template-columns: 1fr;
    max-width: 380px; margin: 0 auto;
  }

  .friends-grid { max-width: 380px; }
}

@media (max-width: 480px) {
  .hero .character { width: clamp(70px, 24vw, 110px); }
  /* `.friend .character` is now driven by the carousel-specific 560px
     rule above so it can grow into the wider single-card layout. */

  .app-card { padding: 36px 24px 32px; }
  .app-card-wordmark { font-size: 28px; }

  .trust-item { padding: 16px 18px; }
  .trust-icon { width: 44px; height: 44px; }
  .trust-item-text { font-size: 16px; }

  /* Footer friends — shrink the row so 5 friends + gaps fit comfortably
     even at iPhone-SE width (320px). Previous floor was 72px which
     overflowed at <380px. */
  footer { --footer-friend-size: clamp(44px, 14vw, 72px); padding: 0 16px 56px; }
  .footer-friends { gap: clamp(6px, 2vw, 14px); }
}
