/* ===== 00-foundation.css ===== */
/* ==========================================================================
   FARANG - farangapp.com
   00 FOUNDATION: tokens, base, primitives.
   Every colour here is lifted from the iOS app's Farang/Shared/Theme.swift.
   Nothing below this file may introduce a raw hex value.
   ========================================================================== */

/* Anton ships a single upright 400. The italic is a skew, applied on an
   inline-block wrapper - see .d > span. Browsers synthesise oblique
   inconsistently at display sizes, so we never rely on font-style. */
@font-face{
  font-family:'Anton';
  src:url('/assets/fonts/anton-latin-400.woff2') format('woff2');
  font-weight:400; font-style:normal; font-display:swap;
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,
    U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,
    U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}

/* Size-adjusted fallback so there is no layout shift while Anton loads. */
@font-face{
  font-family:'Anton Fallback';
  src:local('Haettenschweiler'), local('Arial Narrow Bold'), local('Impact');
  size-adjust:92%; ascent-override:98%; descent-override:22%; line-gap-override:0%;
}

:root{

  /* ============================================================
     COLOUR
     ============================================================ */

  /* Canvas and surfaces. Elevation is expressed by SURFACE, never by shadow. */
  --canvas:            #0A0A10;
  --canvas-band:       #0C0C13;
  --surface-well:      #13131A;
  --surface-chrome:    #1C1C25;
  --surface-card:      #22222D;
  --surface-nested:    #272732;

  /* Text. Two tiers only. Quieter is achieved with SIZE, not more transparency. */
  --text:              #F5F2EE;   /* warm off-white. NEVER #FFFFFF for text. */
  --text-muted:        #9C9BA5;

  /* The brand accent. Budget: max three visible rose elements per viewport. */
  --rose:              #FF6B7D;
  --rose-ink:          #13131A;
  --rose-08:           rgba(255,107,125,0.08);
  --rose-12:           rgba(255,107,125,0.12);
  --rose-25:           rgba(255,107,125,0.25);
  --rose-35:           rgba(255,107,125,0.35);

  /* Gradients. Strictly rationed - see the ROSE BUDGET note below. */
  --grad-sunset:       linear-gradient(135deg,#FF6B7D 0%,#FF9B7A 52%,#FFB88C 100%);
  --grad-deeprose:     linear-gradient(135deg,#8A2E3E 0%,#C74858 50%,#8A2E3E 100%);

  /* Borders. This IS the elevation system. */
  --line:              rgba(255,255,255,0.06);
  --line-hover:        rgba(255,255,255,0.12);
  --line-active:       rgba(255,107,125,0.35);

  /* Status. These do real functional work on this site, not decoration. */
  --ok:                #8AD4A0;
  --warn:              #F5D88B;
  --danger:            #E89B9B;
  --info:              #8FBBD9;

  /* Category accents. Identity only: 3px rails, sub-32px icons, 10px dots,
     badge fills. NEVER a fill larger than 40px. */
  --cat-visa:          #E24E4E;
  --cat-map:           #4A90E2;
  --cat-translate:     #A855F7;
  --cat-money:         #2ECC71;
  --cat-social:        #FF6B35;
  --cat-aunty:         #E8A93D;
  --cat-pin-a:         #D46A9C;
  --cat-pin-b:         #6BC9E8;

  /* Per-destination accent, rewritten by JS on spawn selection. */
  --dest:              var(--cat-visa);

  /* ============================================================
     TYPE
     ============================================================ */

  --ff-display: 'Anton','Anton Fallback',Haettenschweiler,'Arial Narrow',sans-serif;
  --ff-body: -apple-system,BlinkMacSystemFont,'SF Pro Text','Segoe UI',
             system-ui,Roboto,'Helvetica Neue',Arial,sans-serif;
  --ff-mono: ui-monospace,SFMono-Regular,'SF Mono',Menlo,Consolas,monospace;

  /* Display (Anton). Always uppercase, always skewed, ls -0.5px. */
  --fs-hero:      clamp(2.875rem, 9vw, 6.5rem);
  --fs-display-1: clamp(2rem, 5vw, 3.75rem);
  --fs-display-2: clamp(1.75rem, 4vw, 2.75rem);
  --fs-display-3: 1.375rem;
  --fs-display-4: 0.9375rem;
  --fs-num-xl:    clamp(2.5rem, 7vw, 5rem);
  --fs-num:       clamp(1.75rem, 4vw, 2.5rem);

  --lh-display:   0.90;
  --ls-display:   -0.5px;
  --skew-display: -9deg;

  /* Body (system stack). Weight never exceeds 600 in running text. */
  --fs-lead:   1.0625rem;
  --fs-body:   1rem;
  --fs-sm:     0.875rem;
  --fs-cap:    0.8125rem;
  --fs-micro:  0.75rem;
  --fs-label:  0.6875rem;
  --fs-label-s:0.625rem;

  --lh-body:   1.6;
  --lh-tight:  1.45;

  --fw-body:   400;
  --fw-med:    500;
  --fw-semi:   600;
  --fw-label:  700;

  --ls-label:  3px;
  --ls-tagline:3.5px;

  --measure:   62ch;
  --measure-narrow: 46ch;

  /* ============================================================
     SPACE - 4px base
     ============================================================ */
  --sp-1:4px;   --sp-2:8px;   --sp-3:12px;  --sp-4:16px;  --sp-5:20px;
  --sp-6:24px;  --sp-8:32px;  --sp-10:40px; --sp-12:48px; --sp-16:64px;
  --sp-20:80px; --sp-24:96px; --sp-30:120px; --sp-40:160px;

  --section-y:      clamp(80px, 11vw, 120px);
  --section-y-tight:clamp(56px, 8vw, 80px);
  --gutter:         clamp(20px, 5vw, 40px);
  --maxw:           1200px;
  --maxw-narrow:    760px;
  --hud-h:          64px;

  /* ============================================================
     RADII
     ============================================================ */
  --r-card:   14px;
  --r-nested: 10px;
  --r-chip:   8px;
  --r-sm:     6px;
  --r-pill:   999px;
  --r-logo:   8px;
  --r-device: 44px;
  --r-screen: 36px;

  /* ============================================================
     ELEVATION - no drop shadows on flat surfaces. On a near-black
     canvas a shadow reads as grime. Only floating objects get one.
     ============================================================ */
  --shadow-float:  0 12px 32px rgba(0,0,0,0.45);
  --shadow-device: 0 40px 80px rgba(0,0,0,0.60);
  --rim-rose:      inset 0 0 0 1px rgba(255,107,125,0.10);
  --glow-rose:     0 0 40px rgba(255,107,125,0.18);

  /* ============================================================
     MOTION - exactly two curves. No third curve is permitted.
     No spring overshoot above 1.02 anywhere: overshoot is what
     makes gamified sites read as toys.
     ============================================================ */
  --ease-out:   cubic-bezier(0.22, 1, 0.36, 1);
  --ease-state: cubic-bezier(0.4, 0, 0.2, 1);
  --dur-fast:   160ms;
  --dur-base:   240ms;
  --dur-slow:   420ms;
  --stagger:    60ms;

  /* ============================================================
     TEXTURE
     ============================================================ */
  --grain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23g)'/%3E%3C/svg%3E");
  --grid-line: rgba(255,255,255,0.02);
  --grid-size: 72px;

  /* ============================================================
     LAYERS
     ============================================================ */
  --z-bg:      0;
  --z-content: 1;
  --z-tracker: 80;
  --z-hud:     100;
  --z-toast:   120;
  --z-grain:   9999;
}

/* ---------------------------------------------------------------
   ROSE BUDGET - ENFORCED
   Maximum THREE #FF6B7D elements visible in any single viewport at
   any scroll position. If a reviewer can screenshot a scroll position
   and count four, that position is a bug and gets fixed.

   Rose is permitted on, and only on:
     the XP rail fill, the level chip, primary CTAs, active/selected
     states, the wordmark, the tagline, quest completion ticks, the
     Pro tier accents, earned-badge borders, and hover underlines.

   --grad-sunset appears in EXACTLY FOUR places on the entire site:
     1. the XP rail once XP passes 78
     2. the 2px top edge of the Pro pricing panel
     3. the Aunty avatar
     4. the final CTA primary button
   Nowhere else. Ever.

   --grad-deeprose appears in EXACTLY TWO places, both times as a low,
   heavily blurred radial bloom at 25-40% opacity:
     1. behind the hero device cluster
     2. behind the final CTA
   It is never a fill and never sits behind body text.

   The previous site's failure was a page bathed in coral. Here the rose
   is scarce enough that every appearance registers as a signal.
   --------------------------------------------------------------- */

*,*::before,*::after{box-sizing:border-box}
html{scroll-behavior:smooth; -webkit-text-size-adjust:100%}
@media (prefers-reduced-motion: reduce){ html{scroll-behavior:auto} }

body{
  margin:0; background:var(--canvas); color:var(--text);
  font-family:var(--ff-body); font-size:var(--fs-body);
  line-height:var(--lh-body); font-weight:var(--fw-body);
  -webkit-font-smoothing:antialiased; text-rendering:optimizeLegibility;
  overflow-x:hidden;
}

/* Fixed grain. Stops the flat near-black looking like an unstyled page. */
body::after{
  content:""; position:fixed; inset:0; pointer-events:none; z-index:var(--z-grain);
  background-image:var(--grain); opacity:.035; mix-blend-mode:overlay;
}

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

/* The 72px arcade grid. Applied ONLY to .has-grid: hero, final CTA, pricing. */
.has-grid{position:relative; isolation:isolate}
.has-grid::before{
  content:""; position:absolute; inset:0; pointer-events:none; z-index:-1;
  background-image:
    linear-gradient(var(--grid-line) 1px, transparent 1px),
    linear-gradient(90deg, var(--grid-line) 1px, transparent 1px);
  background-size:var(--grid-size) var(--grid-size);
  -webkit-mask-image:radial-gradient(ellipse 70% 70% at 50% 45%, #000 30%, transparent 100%);
          mask-image:radial-gradient(ellipse 70% 70% at 50% 45%, #000 30%, transparent 100%);
}

/* ---- THE LABEL. The strongest carried brand tic. Appears above every
   section without exception, formatted "LVL 03 / QUEST 02". */
.lbl{
  font-size:var(--fs-label); font-weight:var(--fw-label);
  letter-spacing:var(--ls-label); text-transform:uppercase;
  color:var(--text-muted); line-height:1; margin:0 0 var(--sp-4);
  display:flex; align-items:center; gap:var(--sp-3);
}
.lbl::after{content:""; flex:1; height:1px; background:var(--line)}
.lbl--rail{border-left:3px solid var(--rail,var(--rose)); padding-left:var(--sp-3)}
.lbl b{font-weight:var(--fw-label); color:var(--text)}

/* ---- ANTON DISPLAY. Skew on an inline-block wrapper, never the block. */
.d{
  font-family:var(--ff-display); font-weight:400; text-transform:uppercase;
  letter-spacing:var(--ls-display); line-height:var(--lh-display);
  color:var(--text); margin:0;
}
.d > span{
  display:inline-block; transform:skewX(var(--skew-display));
  transform-origin:0 100%; padding-right:.08em;
}
.d em{font-style:normal; color:var(--rose)}
.d--hero{font-size:var(--fs-hero)}
.d--1{font-size:var(--fs-display-1)}
.d--2{font-size:var(--fs-display-2)}
.d--3{font-size:var(--fs-display-3)}

/* ---- SURFACES */
.card{background:var(--surface-card); border:1px solid var(--line);
      border-radius:var(--r-card); padding:var(--sp-6)}
.well{background:var(--surface-well); border:1px solid var(--line);
      border-radius:var(--r-card)}
.nested{background:var(--surface-nested); border:1px solid transparent;
        border-radius:var(--r-nested)}

/* ---- PILLS AND BUTTONS */
.btn{display:inline-flex; align-items:center; justify-content:center; gap:var(--sp-2);
     min-height:52px; padding:0 var(--sp-6); border-radius:var(--r-pill);
     font-size:var(--fs-sm); font-weight:var(--fw-semi); text-decoration:none;
     border:1px solid transparent; cursor:pointer; font-family:inherit;
     transition:transform var(--dur-fast) var(--ease-state),
                border-color var(--dur-fast) var(--ease-state),
                filter var(--dur-fast) var(--ease-state)}
.btn--primary{background:var(--rose); color:var(--rose-ink)}
.btn--primary:hover{filter:brightness(1.06); transform:translateY(-2px)}
.btn--ghost{background:transparent; color:var(--text); border-color:var(--line-hover)}
.btn--ghost:hover{border-color:var(--rose-35)}
.btn--sm{min-height:40px; font-size:var(--fs-cap); padding:0 var(--sp-5)}
.btn svg{flex:0 0 auto}

.chip{display:inline-flex; align-items:center; gap:var(--sp-2);
      background:var(--surface-nested); color:var(--text-muted);
      border:1px solid transparent; border-radius:var(--r-pill);
      padding:7px var(--sp-4); font-size:var(--fs-micro); line-height:1;
      min-height:34px; cursor:pointer; font-family:inherit}
.chip[aria-pressed="true"],.chip.is-on{color:var(--text); border-color:var(--line-active)}

/* ---- DEVICE FRAME. Drawn in CSS. The screenshots already contain their
   own status bar and Dynamic Island, so the frame draws BEZEL ONLY -
   never a notch, never a gloss, never a reflection. */
.device{
  --dw:300px; width:var(--dw); flex:0 0 auto; position:relative;
  background:var(--surface-chrome); padding:10px;
  border-radius:var(--r-device);
  box-shadow:var(--shadow-device), var(--rim-rose);
}
.device::after{content:""; position:absolute; inset:0; border-radius:inherit;
  box-shadow:inset 0 0 0 1px rgba(255,255,255,0.07); pointer-events:none}
.device__screen{display:block; width:100%; height:auto; aspect-ratio:780/1696;
  object-fit:cover; border-radius:var(--r-screen); background:var(--surface-well)}

/* ---- HAIRLINE BAND */
.band{background:var(--canvas-band); border-top:1px solid var(--line);
      border-bottom:1px solid var(--line)}

/* ---- LAYOUT */
.wrap{max-width:var(--maxw); margin:0 auto; padding-inline:var(--gutter)}
.wrap--narrow{max-width:var(--maxw-narrow)}
.section{padding-block:var(--section-y)}
.section--tight{padding-block:var(--section-y-tight)}
.measure{max-width:var(--measure)}
.lead{font-size:var(--fs-lead); color:var(--text-muted); line-height:var(--lh-body)}

/* ---- FOCUS. One ring, everywhere, no exceptions. */
:focus-visible{outline:2px solid var(--rose); outline-offset:2px; border-radius:var(--r-sm)}
.btn--primary:focus-visible{outline-color:var(--text); box-shadow:0 0 0 4px rgba(10,10,16,.9)}

/* ---- SCREEN READER ONLY */
.sr{position:absolute; width:1px; height:1px; padding:0; margin:-1px;
    overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0}

/* ---- Wide content never makes the page scroll sideways. */
.scroller{overflow-x:auto; overscroll-behavior-x:contain;
          scrollbar-width:thin; -webkit-overflow-scrolling:touch}

/* ---- Global reduced-motion kill switch. Every animated thing on this site
   must still reach its final state with these rules applied. */
@media (prefers-reduced-motion: reduce){
  *,*::before,*::after{
    animation-duration:.001ms !important;
    animation-iteration-count:1 !important;
    transition-duration:.001ms !important;
    scroll-behavior:auto !important;
  }
}

/* ===== 10-hud-hero.css ===== */
/* ==========================================================================
   FARANG - farangapp.com
   10 HUD + HERO + STAMPS
   Mobile first. Every colour is a foundation token. No raw hex, no emoji.
   ========================================================================== */

/* The HUD is fixed, so in-page anchors must clear it. */
html{scroll-padding-top:calc(var(--hud-h) + var(--sp-4))}


/* ==========================================================================
   5.0  #hud - the persistent HUD
   ========================================================================== */

.hud{
  position:fixed; inset:0 0 auto 0; z-index:var(--z-hud);
  height:var(--hud-h);
  /* spec: rgba(28,28,37,0.72) = --surface-chrome at 72%, expressed as a token */
  background:rgba(28,28,37,0.72);
  background:color-mix(in srgb, var(--surface-chrome) 72%, transparent);
  -webkit-backdrop-filter:blur(20px) saturate(120%);
          backdrop-filter:blur(20px) saturate(120%);
  border-bottom:1px solid var(--line);
}
.hud__inner{
  display:flex; align-items:center; justify-content:space-between;
  gap:var(--sp-2); height:100%;
}

/* ---- Left: brand lockup */
.hud__brand{
  display:inline-flex; align-items:center; gap:var(--sp-2);
  text-decoration:none; min-height:44px; flex:0 0 auto;
}
.hud__logo{display:block; width:28px; height:28px; border-radius:var(--r-logo)}
.hud__word{
  font-size:20px; font-weight:900; letter-spacing:-0.7px;
  color:var(--rose); line-height:1;
}

/* ---- Centre: the XP module */
.hud__xp{
  display:flex; flex-direction:column; align-items:center; justify-content:center;
  gap:var(--sp-1); min-width:0; flex:0 1 auto;
}
.hud__level{
  display:inline-flex; align-items:center; gap:var(--sp-2);
  min-height:44px; padding-inline:var(--sp-1);
  text-decoration:none; white-space:nowrap;
  font-size:var(--fs-label); font-weight:var(--fw-label);
  letter-spacing:var(--ls-label); text-transform:uppercase;
  color:var(--text-muted); line-height:1;
}
.hud__level:hover .hud__nametext{color:var(--text)}
.hud__num{
  font-size:var(--fs-display-4); color:var(--text); line-height:1;
}
.hud__slash{color:var(--text-muted); opacity:.7}

/* The level name lives in an overflow box so the XP engine can roll it. */
.hud__name{display:block; overflow:hidden; height:1.05em; line-height:1.05em}
.hud__nametext{
  display:block; line-height:1.05em;
  transition:transform 120ms var(--ease-state), color var(--dur-fast) var(--ease-state);
}

/* ---- The rail. Inert at 0 until the XP engine writes scaleX on the fill. */
.hud__rail{
  position:relative; width:240px; max-width:38vw; height:3px;
  background:var(--line); border-radius:var(--r-pill);
  transition:box-shadow 90ms var(--ease-state);
}
.hud__rail.is-bloom{box-shadow:0 0 20px var(--rose-25)}
.hud__railfill{
  position:absolute; inset:0; border-radius:inherit;
  background:var(--rose);
  transform:scaleX(0); transform-origin:left center;
}
/* Past XP 78 the engine adds .is-max. */
.hud__railfill.is-max{background:var(--grad-sunset)}

/* ---- Right: the App Store pill. Visual box is 36px per spec, the hit area
   is padded out to 44px so it still clears the minimum touch target. */
.hud__cta{
  position:relative; flex:0 0 auto;
  min-height:36px; padding:0 var(--sp-4);
  font-size:var(--fs-cap); letter-spacing:.4px;
}
.hud__cta::after{
  content:""; position:absolute; left:0; right:0; top:50%;
  transform:translateY(-50%); height:44px;
}

/* ---- Mobile: 56px bar, compact LVL chip, rail on the bottom edge. */
@media (max-width:899.98px){
  :root{--hud-h:56px}
  .hud__slash,.hud__name{display:none}
  .hud__xp{gap:0}
  .hud__level{
    gap:var(--sp-1); min-height:44px; padding-inline:var(--sp-3);
    background:var(--surface-nested); border-radius:var(--r-pill);
  }
  .hud__rail{
    position:absolute; left:0; right:0; bottom:0;
    width:auto; max-width:none; height:2px; border-radius:0; background:var(--line);
  }
  .hud__cta{min-height:40px; padding:0 var(--sp-4)}
}
@media (max-width:359.98px){
  .hud__word{display:none}
}


/* ==========================================================================
   5.1  #hero
   ========================================================================== */

.hero{
  position:relative;
  min-height:100vh; min-height:100svh;
  background:var(--canvas);
  padding-top:calc(var(--hud-h) + var(--sp-6));
  padding-bottom:calc(var(--sp-20) + var(--sp-10));
  display:flex; align-items:center;
  overflow:hidden;
}

/* Layer 2: the Deep Rose bloom. Sits LOW - the light comes up from under
   the phones. Layer 1 (the 72px grid) is .has-grid::before, layer 3 is the
   page grain on body::after. */
.hero__bloom{
  position:absolute; z-index:-1; pointer-events:none;
  left:50%; top:78%;
  width:1400px; height:700px; max-width:190vw;
  transform:translate(-50%,-50%);
  background:var(--grad-deeprose);
  border-radius:50%;
  opacity:.32;
  filter:blur(120px);
  /* Falls off so the glow stays a light source under the phones and never
     becomes a fill sitting behind the body copy. */
  -webkit-mask-image:radial-gradient(ellipse 50% 50% at 50% 50%, #000 0%, transparent 72%);
          mask-image:radial-gradient(ellipse 50% 50% at 50% 50%, #000 0%, transparent 72%);
}

.hero__inner{
  position:relative; z-index:var(--z-content);
  width:100%;
  display:grid; gap:var(--sp-8);
  grid-template-areas:"stage" "copy";
}
/* The H1 is sized from the COLUMN, not the viewport: the three spans are
   deliberate lines and must never re-wrap. 1cqi = 1% of the copy column,
   and the longest line measures 7.7em plus skew overhang. */
.hero__copy{grid-area:copy; container-type:inline-size}
.hero__stage{grid-area:stage}

.hero__kicker{margin-bottom:var(--sp-5)}
.lbl.hero__kicker b{color:var(--text)}

.hero__h1{
  display:flex; flex-direction:column; align-items:flex-start;
  font-size:min(clamp(2.5rem, 12vw, 3.5rem), 12.4cqi);
  margin:0 0 var(--sp-6);
}
.hero__h1 > span{
  white-space:nowrap;
  animation:heroLine var(--dur-slow) var(--ease-out) both;
  animation-delay:calc(var(--i,0) * 80ms);
}
@keyframes heroLine{
  from{-webkit-clip-path:inset(0 0 100% 0); clip-path:inset(0 0 100% 0)}
  to{-webkit-clip-path:inset(-0.14em -0.24em -0.16em -0.06em);
             clip-path:inset(-0.14em -0.24em -0.16em -0.06em)}
}

.hero__sub{
  margin:0 0 var(--sp-8);
  font-size:var(--fs-lead); line-height:var(--lh-body);
  color:var(--text-muted); max-width:var(--measure-narrow);
}

.hero__cta{display:flex; flex-wrap:wrap; gap:var(--sp-3); margin-bottom:var(--sp-6)}
.hero__get{padding-inline:var(--sp-6)}
.hero__see{letter-spacing:1px; font-size:var(--fs-cap)}

.hero__fine{
  margin:0; font-size:var(--fs-cap); line-height:1.7; color:var(--text-muted);
}

/* ---- Device cluster. All frames share one grid cell so the side phones
   pivot around the centre phone. The rAF loop writes --py per frame; the
   static rotation lives on the inner .device so parallax cannot fight it. */
.hero__stage{
  position:relative;
  display:grid; place-items:center;
  justify-self:center;
}
.hero__stage > *{grid-area:1 / 1}

.hero__phone{transform:translate3d(0, var(--py, 0px), 0)}
.hero__phone--main{z-index:3}
.hero__phone--left{z-index:1}
.hero__phone--right{z-index:2}

.hero__dev--main{--dw:260px}
.hero__dev--side{--dw:240px}

/* Below 900px the cluster is the centre phone only, above the copy, no
   parallax, per the mobile table. */
.hero__phone--left,.hero__phone--right{display:none}

/* ---- The pre-rendered achievement toast, composited over the centre
   phone's lower third. Static, decorative, never announced. */
.hero__toast{
  position:relative; z-index:4;
  align-self:end; justify-self:center;
  display:flex; align-items:center; gap:var(--sp-3);
  width:min(264px, 88%);
  margin-bottom:19%;
  padding:var(--sp-3) var(--sp-4) var(--sp-3) var(--sp-5);
  background:var(--surface-card);
  border:1px solid var(--line);
  border-radius:var(--r-card);
  box-shadow:var(--shadow-float);
  pointer-events:none;
  overflow:hidden;
}
.hero__toastrail{
  position:absolute; left:0; top:0; bottom:0; width:3px;
  background:var(--cat-visa);
}
.hero__toasticon{flex:0 0 auto; color:var(--cat-visa)}
.hero__toasttext{display:flex; flex-direction:column; gap:5px; min-width:0}
.hero__toastlbl{
  font-size:var(--fs-label-s); font-weight:var(--fw-label);
  letter-spacing:var(--ls-label); text-transform:uppercase;
  color:var(--text-muted); line-height:1;
}
.hero__toastname{
  font-size:var(--fs-display-4); font-weight:var(--fw-semi);
  color:var(--text); line-height:1;
}

/* ---- Scroll hint */
.hero__hint{
  position:absolute; left:50%; bottom:var(--sp-6);
  transform:translateX(-50%);
  display:flex; flex-direction:column; align-items:center; gap:var(--sp-3);
  margin:0; z-index:var(--z-content);
}
.hero__hintlbl{
  font-size:var(--fs-label); font-weight:var(--fw-label);
  letter-spacing:var(--ls-label); text-transform:uppercase;
  color:var(--text-muted); line-height:1;
}
.hero__rule{
  position:relative; display:block; width:1px; height:40px;
  background:var(--line-hover);
}
.hero__dot{
  position:absolute; left:50%; top:0; width:6px; height:6px;
  margin-left:-3px; border-radius:var(--r-pill);
  background:var(--rose);
  animation:heroDot 1.8s var(--ease-state) infinite;
}
@keyframes heroDot{
  0%{transform:translateY(0); opacity:0}
  18%{opacity:1}
  82%{opacity:1}
  100%{transform:translateY(34px); opacity:0}
}

/* ---- Desktop: 12 columns, copy 1-6, cluster 7-12. */
@media (min-width:900px){
  .hero{
    padding-top:calc(var(--hud-h) + var(--sp-12));
    padding-bottom:var(--sp-20);
  }
  .hero__inner{
    grid-template-columns:repeat(12,1fr);
    grid-template-areas:"copy copy copy copy copy copy stage stage stage stage stage stage";
    align-items:center;
    gap:var(--sp-8);
  }
  .hero__h1{font-size:min(var(--fs-hero), 12.4cqi); margin-bottom:var(--sp-8)}
  .hero__phone--left,.hero__phone--right{display:block}
  .hero__dev--main{--dw:300px}
  .hero__dev--side{--dw:240px}
  .hero__phone--left .device{transform:rotate(-7deg) translate(-110px, 40px)}
  .hero__phone--right .device{transform:rotate(6deg) translate(110px, -30px)}
  .hero__toast{
    width:264px;
    margin-bottom:120px;
    transform:translateX(-18px);
  }
}


/* ==========================================================================
   5.2  #stamps - passport control
   ========================================================================== */

.stamps{position:relative}
.stamps__scroller{scroll-snap-type:x proximity}
.stamps__row{
  display:flex; align-items:center; gap:var(--sp-3);
  list-style:none; margin:0 auto; padding:var(--sp-5) var(--gutter);
  max-width:var(--maxw); width:max-content;
}
.stamps__chip{
  flex:0 0 auto; scroll-snap-align:center;
  display:flex; align-items:center; gap:var(--sp-2);
  padding:12px 18px;
  background:transparent;
  border:1px dashed var(--line-hover);
  border-radius:var(--r-sm);
  transform:rotate(var(--rot,0deg));
}
.stamps__fig{
  font-size:26px; line-height:1; color:var(--text);
}
.stamps__slash{
  font-size:var(--fs-cap); color:var(--text-muted); opacity:.55; line-height:1;
}
.stamps__lbl{
  font-size:var(--fs-label-s); font-weight:var(--fw-label);
  letter-spacing:var(--ls-label); text-transform:uppercase;
  color:var(--text-muted); line-height:1.4;
  max-width:128px;      /* holds every label to at most two lines */
}

/* The stamp-in. Chips are fully rendered without JS; the shared observer
   adds .in to the section and the settle plays once. */
.stamps.in .stamps__chip{
  animation:stampIn 220ms var(--ease-out) both;
  animation-delay:calc(var(--i,0) * 90ms);
}
@keyframes stampIn{
  0%{transform:rotate(calc(var(--rot,0deg) + 3deg)) scale(1.12);
     border-color:var(--rose-35); opacity:0}
  20%{opacity:1}
  100%{transform:rotate(var(--rot,0deg)) scale(1);
       border-color:var(--line-hover); opacity:1}
}

@media (min-width:720px){
  .stamps{min-height:96px; display:flex; align-items:center}
  .stamps__scroller{overflow:visible; width:100%}
  .stamps__row{
    width:100%; justify-content:center; flex-wrap:nowrap;
    gap:clamp(8px, 1.6vw, 20px);
  }
}


/* ==========================================================================
   REDUCED MOTION
   The foundation kill switch already flattens durations. These rules make
   sure nothing is left mid-travel and every end state is the resting one.
   ========================================================================== */
@media (prefers-reduced-motion: reduce){
  .hero__h1 > span{animation:none; -webkit-clip-path:none; clip-path:none}
  .hero__dot{animation:none; top:0; opacity:1}
  .hero__phone{transform:none}
  .stamps.in .stamps__chip{animation:none; transform:rotate(var(--rot,0deg))}
  .hud__nametext{transition:none}
  .hud__rail.is-bloom{box-shadow:none}
}

/* ===== 110-pricing-faq.css ===== */
/* ==========================================================================
   110 PRICING / PATCH NOTES / FAQ
   Sections 10, 11 and 12. Mobile first, enhanced upward with min-width.
   Every colour is a foundation token. No raw hex, no emoji, no em-dash.

   Rose accounting for these three sections:
     #pricing     - the Pro panel is the signal: sunset top edge (permitted
                    use 2 of 4), the ENDGAME chip, the GET PRO pill. The
                    kicker rail is --cat-money instead of rose so the panel
                    keeps the eye. The Pro feature ticks are rose per 5.10;
                    they are 16px hairline glyphs, not a wash.
     #patchnotes  - kicker rail, the NOW entry rail, the NOW dot. Three.
     #faq         - kicker rail and one rose word in the title. Two.
   ========================================================================== */


/* ==========================================================================
   10 / #pricing
   ========================================================================== */

.pricing__sprite{position:absolute; width:0; height:0; overflow:hidden}

/* Money, not brand: keeps a rose slot free for the Pro panel below. */
.pricing__kicker{--rail:var(--cat-money)}

/* Three explicit display lines, so the title never reflows into two. */
.pricing__title > span{display:block}

.pricing__body{margin:var(--sp-5) 0 0}

.pricing__grid{
  display:grid; gap:var(--sp-5);
  max-width:880px; margin:var(--sp-10) auto 0;
}

.pricing__tier{
  display:flex; flex-direction:column;
  padding:var(--sp-6);
  position:relative;
}

/* Below 760px the panels stack with PRO first. */
.pricing__pro{order:-1}

.tier__head{display:flex; align-items:center; gap:var(--sp-3); flex-wrap:wrap}
.tier__name{font-size:1.625rem}          /* 26px Anton italic */

.tier__chip{
  font-size:var(--fs-label-s); font-weight:var(--fw-label);
  letter-spacing:var(--ls-label); text-transform:uppercase; line-height:1;
  color:var(--rose); background:var(--rose-12);
  border:1px solid var(--rose-25); border-radius:var(--r-pill);
  padding:5px var(--sp-3);
}

/* The price line. Swap the two data-price spans to change the figures;
   nothing else in the section needs touching. */
.tier__price{
  display:flex; align-items:baseline; flex-wrap:wrap; gap:var(--sp-2);
  margin:var(--sp-4) 0 0;
}
.tier__fig{
  font-family:var(--ff-display); font-weight:400; letter-spacing:var(--ls-display);
  line-height:1; font-size:var(--fs-num); color:var(--text);
  display:inline-block; transform:skewX(var(--skew-display));
  transform-origin:0 100%; padding-right:.08em;
}
.tier__note{font-size:var(--fs-sm); color:var(--text-muted)}

.tier__hr{
  height:1px; border:0; background:var(--line);
  margin:var(--sp-6) 0;
}

.tier__plus{
  margin:0 0 var(--sp-4);
  font-size:var(--fs-label); font-weight:var(--fw-label);
  letter-spacing:var(--ls-label); text-transform:uppercase;
  color:var(--text-muted); line-height:1;
}

.tier__list{list-style:none; margin:0; padding:0; display:grid; gap:var(--sp-3)}
.tier__list li{
  display:grid; grid-template-columns:16px 1fr; gap:var(--sp-3);
  align-items:start;
  font-size:var(--fs-sm); color:var(--text); line-height:var(--lh-tight);
}
.tier__tick{width:16px; height:16px; margin-top:2px; color:var(--text-muted)}
.tier__list--pro .tier__tick{color:var(--rose)}

.tier__cta{margin-top:var(--sp-8); width:100%; align-self:stretch}

/* ---- PRO panel: the one permitted sunset edge in this section ---------- */
.pricing__pro{
  border-color:var(--rose-25);
  overflow:hidden;
  isolation:isolate;
}
/* Inner radial rose glow, dropping off from the top edge. Very low. */
.pricing__pro::before{
  content:""; position:absolute; inset:0; z-index:-1; pointer-events:none;
  background:radial-gradient(120% 70% at 50% 0%, var(--rose-12), transparent 70%);
  opacity:.5;
}
.pricing__edge{
  position:absolute; inset:0 0 auto 0; height:2px;
  background:var(--grad-sunset);
  transition:opacity var(--dur-base) var(--ease-state);
  pointer-events:none;
}

/* ---- Lock theatre. Purely decorative, applied by JS as .is-locked, and
   removed after 400ms or immediately on hover / focus. Default state is
   UNLOCKED, so JS off, crawlers and screen readers get the real panel. -- */
.pricing__hatch{
  position:absolute; inset:0; pointer-events:none;
  background:repeating-linear-gradient(45deg,
    var(--line) 0 1px, transparent 1px 8px);
  opacity:0;
  clip-path:inset(0 0 0 100%);
  transition:clip-path 320ms var(--ease-out), opacity 320ms var(--ease-out);
}
.pricing__pro.is-locked .pricing__hatch{opacity:1; clip-path:inset(0 0 0 0)}
.pricing__pro.is-locked .pricing__edge{opacity:.25}

.pricing__swap{display:grid; align-items:center; justify-items:center}
.pricing__swap > span{
  grid-area:1/1;
  transition:opacity var(--dur-fast) var(--ease-state);
}
.pricing__cta-lock{opacity:0}
.pricing__pro.is-locked .pricing__cta-real{opacity:0}
.pricing__pro.is-locked .pricing__cta-lock{opacity:1}

/* No hover means no theatre: on touch a 400ms delay on a tap target is
   just a delay. Forced at the CSS level so it holds even if JS runs. */
@media (hover:none){
  .pricing__hatch{display:none}
  .pricing__pro.is-locked .pricing__edge{opacity:1}
  .pricing__pro.is-locked .pricing__cta-real{opacity:1}
  .pricing__pro.is-locked .pricing__cta-lock{opacity:0}
}

.pricing__fine{
  margin:var(--sp-8) auto 0; max-width:var(--measure);
  text-align:center; font-size:var(--fs-cap); color:var(--text-muted);
  line-height:var(--lh-body);
}

@media (min-width:760px){
  .pricing__grid{grid-template-columns:1fr 1fr; gap:var(--sp-6)}
  .pricing__pro{order:0}
  .pricing__tier{padding:var(--sp-8)}
}


/* ==========================================================================
   11 / #patchnotes
   ========================================================================== */

.pnotes__title{margin-bottom:var(--sp-10)}

.pnotes__list{
  list-style:none; margin:0; padding:0; position:relative;
}
/* The connecting line. --draw is written by the shared rAF loop from the
   section's own intersection ratio and defaults to 1, so with JS off the
   line is simply already drawn. */
.pnotes__list::before{
  content:""; position:absolute; left:3.5px; top:6px; bottom:6px; width:1px;
  background:var(--line);
  transform:scaleY(var(--draw,1)); transform-origin:top;
}

.pnotes__item{position:relative; padding-left:var(--sp-8)}
.pnotes__item + .pnotes__item{margin-top:var(--sp-5)}

.pnotes__dot{
  position:absolute; left:0; top:22px;
  width:8px; height:8px; border-radius:var(--r-pill);
  background:var(--surface-nested);
  box-shadow:0 0 0 1px var(--line);
}
.pnotes__item--now .pnotes__dot{background:var(--rose); box-shadow:none}

.pnotes__card{
  padding:var(--sp-5);
  border-left:4px solid var(--rail,var(--line));
}

.pnotes__place{
  margin:0 0 var(--sp-2);
  font-size:var(--fs-label); font-weight:var(--fw-label);
  letter-spacing:var(--ls-label); text-transform:uppercase;
  color:var(--text); line-height:1.3;
}
.pnotes__body{
  margin:0; font-size:var(--fs-sm); color:var(--text-muted);
  line-height:var(--lh-body); max-width:var(--measure);
}


/* ==========================================================================
   12 / #faq
   ========================================================================== */

.faq__sprite{position:absolute; width:0; height:0; overflow:hidden}

.faq__title{margin-bottom:var(--sp-8)}

.faq__cols{display:grid; gap:0}
.faq__row{border-bottom:1px solid var(--line)}

.faq__q{
  display:flex; align-items:center; justify-content:space-between;
  gap:var(--sp-5);
  min-height:56px; padding:var(--sp-4) 0;
  font-size:var(--fs-body); font-weight:var(--fw-med); color:var(--text);
  line-height:var(--lh-tight);
  cursor:pointer; list-style:none;
}
.faq__q::-webkit-details-marker{display:none}
.faq__q::marker{content:""}
.faq__q:hover{color:var(--text)}

.faq__glyph{
  flex:0 0 auto; width:14px; height:14px; color:var(--text-muted);
  transition:transform var(--dur-base) var(--ease-state),
             color var(--dur-fast) var(--ease-state);
}
.faq__row[open] .faq__glyph{transform:rotate(45deg); color:var(--text)}
.faq__q:hover .faq__glyph{color:var(--text)}

/* Height animation hook: JS flips grid-template-rows between 0fr and 1fr.
   The default is 1fr, so the browser's own open/closed handling is correct
   with JS off. */
.faq__a{
  display:grid; grid-template-rows:1fr;
  transition:grid-template-rows 200ms var(--ease-state);
}
.faq__a-in{overflow:hidden}
.faq__a p{
  margin:0 0 var(--sp-5);
  font-size:0.9375rem;              /* 15px */
  color:var(--text-muted); line-height:var(--lh-body);
  max-width:56ch;
}

@media (min-width:860px){
  .faq__cols{grid-template-columns:1fr 1fr; gap:0 var(--sp-12); align-items:start}
}


/* ==========================================================================
   REDUCED MOTION
   The foundation already kills durations globally. These rules make sure
   the end state is the right one: the Pro panel renders unlocked, the
   timeline line is fully drawn, the FAQ opens instantly.
   ========================================================================== */
@media (prefers-reduced-motion: reduce){
  .pricing__hatch{display:none}
  .pricing__pro.is-locked .pricing__edge{opacity:1}
  .pricing__pro.is-locked .pricing__cta-real{opacity:1}
  .pricing__pro.is-locked .pricing__cta-lock{opacity:0}
  .pnotes__list::before{transform:none}
  .faq__row[open] .faq__glyph{transform:rotate(45deg)}
}

/* ===== 12-hud-nav.css ===== */
/* ==========================================================================
   12 HUD NAV - the section links that replaced the XP module.

   Loads after 10-hud-hero.css, which still carries .hud__inner, .hud__brand
   and .hud__cta. The .hud__xp / .hud__level / .hud__rail rules in that file
   are now dead: no markup uses them. They are left in place rather than
   surgically removed so the diff stays readable, and they cost nothing
   because nothing matches them.

   Links hide below 900px. The page is short enough to scroll on a phone,
   and the App Store pill is the only control that matters there.
   ========================================================================== */

.hud__nav{
  display:flex; align-items:center; gap:var(--sp-2);
  min-width:0; flex:0 1 auto;
}

.hud__link{
  display:inline-flex; align-items:center;
  min-height:40px; padding-inline:var(--sp-4);
  border-radius:var(--r-pill);
  text-decoration:none; white-space:nowrap;
  font-size:var(--fs-cap); font-weight:var(--fw-med);
  color:var(--text-muted);
  border:1px solid transparent;
  transition:color var(--dur-fast) var(--ease-state),
             border-color var(--dur-fast) var(--ease-state);
}
.hud__link:hover{color:var(--text); border-color:var(--line-hover)}
.hud__link:focus-visible{outline:2px solid var(--rose); outline-offset:2px}

@media (max-width:899.98px){
  .hud__nav{display:none}
}

/* ===== 140-cta-footer.css ===== */
/* ==========================================================================
   140  FINAL CTA + FOOTER
   Sections 5.13 and 5.14. Mobile first, enhanced upward with min-width.
   Every colour is a token. No raw hex, no raw rgba.
   ROSE ACCOUNTING for the #finalcta viewport:
     1  the headline word BUT DIFFERENT.        (.d em)
     2  the primary button                      (--grad-sunset, use 4 of 4)
     3  the Deep Rose bloom                     (--grad-deeprose, use 2 of 2)
   Locked badges, the summary, the reassurance row and RESET RUN are all
   muted on purpose so the count never reaches four.
   ROSE ACCOUNTING for the #footer viewport:
     1  the logo mark fill    2  the tagline    (+ one transient hover underline)
   ========================================================================== */


/* ==========================================================================
   FINAL CTA
   ========================================================================== */

#finalcta{
  min-height:calc(100svh - var(--hud-h));
  display:flex; align-items:center; justify-content:center;
  padding-block:var(--section-y);
  overflow:hidden;                 /* the bloom is wider than the page */
}

.cta__wrap{
  display:flex; flex-direction:column; align-items:center;
  text-align:center; width:100%;
  gap:var(--sp-6);
}

/* ---- Decorative layer 1: the Deep Rose bloom. Permitted use 2 of 2.
   Sits LOW and heavily blurred. Never a fill, never behind body text.
   Opacity is held at the enforced 25-40% band from the rose-budget rule. */
.cta__bloom{
  position:absolute; z-index:-1; pointer-events:none;
  left:50%; bottom:-14%;
  width:min(1400px, 150%); height:min(700px, 60%);
  transform:translateX(-50%);
  background:var(--grad-deeprose);
  border-radius:var(--r-pill);
  filter:blur(120px);
  opacity:.38;
}

/* ---- Decorative layer 2: one 300px device frame as pure texture.
   Rendered as a CSS background, not an <img>, so it carries no alt text
   and can never be announced. aria-hidden in the markup as well.
   Desktop only: at 390px a 300px frame is most of the screen and competes
   with the copy even at 8%, and phones skip the image fetch entirely. */
.cta__ghost{
  display:none;
  position:absolute; z-index:-1; pointer-events:none;
  left:50%; top:38%; transform:translate(-50%,-50%);
  --dw:300px;
  opacity:.08; filter:blur(2px);
  box-shadow:none;                 /* no rose rim, no device shadow on texture */
}
.cta__ghost::after{content:none}
.cta__ghost-shot{
  background-image:url('/assets/shots/social-map.webp');
  background-size:cover; background-position:top center;
}

/* ---- 1. RUN BANNER. 32px --surface-card pill in the label style. */
.cta__banner{
  display:inline-flex; flex-wrap:wrap; align-items:center; justify-content:center;
  gap:var(--sp-2);
  margin:0; min-height:32px; padding:0 var(--sp-4);
  background:var(--surface-card); border:1px solid var(--line);
  border-radius:var(--r-pill);
  font-size:var(--fs-label); font-weight:var(--fw-label);
  letter-spacing:var(--ls-label); text-transform:uppercase;
  line-height:1; color:var(--text-muted);
}
.cta__banner-lvl{font-weight:var(--fw-label); color:var(--text)}
.cta__banner-sep{color:var(--line-hover)}
/* The anchor keeps a 44px hit area without inflating the 32px pill. */
.cta__banner-link{
  display:inline-flex; align-items:center; min-height:44px;
  margin-block:-6px; padding-inline:var(--sp-1);
  color:var(--text-muted);
  text-decoration:underline; text-decoration-thickness:1px;
  text-decoration-color:var(--line-hover); text-underline-offset:4px;
  transition:color var(--dur-fast) var(--ease-state),
             text-decoration-color var(--dur-fast) var(--ease-state);
}
.cta__banner-link:hover{color:var(--text); text-decoration-color:var(--text-muted)}

/* Complete state. The tick is the only rose the banner may ever show, and it
   only appears at 100%, by which point the headline is the visitor's focus. */
.cta__banner-tick{display:inline-flex; width:14px; height:14px; color:var(--rose)}
.cta__banner-tick[hidden]{display:none}
.cta__banner-tick svg{width:100%; height:100%}
.cta__banner[data-complete="true"]{border-color:var(--line-active)}

/* ---- 2. THE TAGLINE AT FULL SIZE */
/* Flex column with align-items:center so each skewed line box is only as wide
   as its own text - transform-origin 0 100% then sits on the real glyphs
   instead of on a full-width block, which is what makes centred Anton skew
   correctly. Do not change this to display:block. */
.cta__title{
  display:flex; flex-direction:column; align-items:center;
  font-size:clamp(2.5rem, 8vw, 6rem);
  line-height:.90;
  margin:var(--sp-2) 0 0;
}

/* ---- 3. SUBHEAD */
.cta__sub{max-width:var(--measure); margin:0}

/* ---- 4. CTA CLUSTER */
.cta__cluster{
  display:flex; flex-direction:column; align-items:center; gap:var(--sp-6);
  width:100%; margin-top:var(--sp-2);
}

/* Permitted --grad-sunset use 4 of 4. Nowhere else in these two sections. */
.cta__store{
  width:100%; max-width:340px;
  min-height:52px; gap:var(--sp-3);
  background:var(--grad-sunset); color:var(--rose-ink);
  font-weight:var(--fw-semi);
}
.cta__store:hover{filter:brightness(1.06); transform:translateY(-2px)}
.cta__store:focus-visible{outline-color:var(--text)}
.cta__store-txt{display:flex; flex-direction:column; align-items:flex-start; line-height:1.15}
.cta__store-sm{font-size:var(--fs-micro); font-weight:var(--fw-med); opacity:.85}
.cta__store-lg{font-size:var(--fs-lead); font-weight:var(--fw-semi); letter-spacing:-.2px}

/* QR is desktop only - on a phone there is nothing to point at it with.
   Fills are set here, not as presentation attributes: var() is not supported
   inside SVG presentation attributes. Dark modules on a light plate so real
   scanners read it. */
.cta__qr{display:none}
.cta__qr-bg{fill:var(--text)}
.cta__qr-fg{fill:var(--canvas)}

/* ---- 5. BADGE SHELF. 2x4 on mobile, 4x2 from 900px.
   Slots stay near the specified 96px square rather than stretching to the
   full column, which turns them into empty billboards on a phone. */
.cta__shelf{
  display:grid; grid-template-columns:repeat(2, minmax(96px, 120px));
  justify-content:center;
  gap:var(--sp-3);
  width:100%;
  margin-top:var(--sp-4);
}

.cta__badge{
  position:relative;
  display:flex; flex-direction:column; align-items:center; justify-content:center;
  gap:var(--sp-2);
  aspect-ratio:1; min-height:96px;
  padding:var(--sp-3) var(--sp-2) var(--sp-6);
  background:var(--surface-card);
  border:1px solid var(--line);
  border-radius:var(--r-card);
  text-decoration:none;
  transition:border-color var(--dur-base) var(--ease-state),
             color var(--dur-base) var(--ease-state),
             transform var(--dur-fast) var(--ease-state);
}
a.cta__badge:hover{transform:translateY(-2px); border-color:var(--line-hover)}

.cta__badge-ico{display:block; width:32px; height:32px}
.cta__badge-ico svg{width:100%; height:100%; display:block}

.cta__badge-lbl{
  font-size:var(--fs-label-s); font-weight:var(--fw-label);
  letter-spacing:var(--ls-label); text-transform:uppercase;
  line-height:1.25; text-align:center;
  color:var(--text-muted);
  transition:color var(--dur-base) var(--ease-state);
}

.cta__badge-state{
  position:absolute; right:8px; bottom:8px;
  width:12px; height:12px; color:var(--text-muted);
}
.cta__badge-state svg{width:100%; height:100%; display:block}
.cta__badge-state[hidden]{display:none}

/* LOCKED. Faint glyph, readable label, padlock. Never a blank square. */
.cta__badge[data-state="locked"] .cta__badge-ico{color:var(--line-hover)}

/* EARNED. Category accent at full strength, active border, rose tick. */
.cta__badge[data-state="earned"]{border-color:var(--line-active)}
.cta__badge[data-state="earned"] .cta__badge-ico{color:var(--acc, var(--text))}
.cta__badge[data-state="earned"] .cta__badge-lbl{color:var(--text)}
.cta__badge-state--tick{color:var(--rose)}

/* Tooltip naming what unlocks it. Pointer devices only - on touch the tap
   goes straight to the section instead, per the mobile table. */
.cta__badge-tip{
  position:absolute; left:50%; bottom:calc(100% + 8px);
  transform:translateX(-50%);
  width:max-content; max-width:180px;
  padding:var(--sp-2) var(--sp-3);
  background:var(--surface-card); border:1px solid var(--line-hover);
  border-radius:var(--r-nested); box-shadow:var(--shadow-float);
  color:var(--text); font-size:var(--fs-micro); line-height:var(--lh-tight);
  text-transform:none; letter-spacing:normal;
  opacity:0; visibility:hidden; pointer-events:none; z-index:var(--z-content);
  transition:opacity var(--dur-fast) var(--ease-state),
             visibility var(--dur-fast) var(--ease-state);
}
@media (hover:hover) and (pointer:fine){
  .cta__badge:hover .cta__badge-tip,
  .cta__badge:focus-visible .cta__badge-tip{opacity:1; visibility:visible}
}

/* One border pulse on locked slots when the section enters view.
   --rose-12 is the nearest token to the specified 15%. */
@keyframes cta-lockpulse{
  0%,100%{border-color:var(--line)}
  50%    {border-color:var(--rose-12)}
}
#finalcta.in .cta__badge[data-state="locked"]{
  animation:cta-lockpulse var(--dur-slow) var(--ease-out) 1;
}

/* ---- 6. RUN SUMMARY */
.cta__sum{
  display:flex; flex-wrap:wrap; justify-content:center;
  gap:var(--sp-6) var(--sp-10);
  margin:var(--sp-4) 0 0;
}
.cta__sum-i{display:flex; flex-direction:column; align-items:center; gap:var(--sp-2)}
.cta__sum-k{
  font-size:var(--fs-label); font-weight:var(--fw-label);
  letter-spacing:var(--ls-label); text-transform:uppercase;
  color:var(--text-muted); line-height:1;
}
.cta__sum-v{margin:0; color:var(--text); line-height:1}
.cta__sum-v--num{font-size:var(--fs-display-3); font-weight:var(--fw-semi)}
.cta__sum-of{color:var(--text-muted); font-size:var(--fs-sm); font-weight:var(--fw-body)}

/* ---- 7. NUDGE */
.cta__nudge{
  margin:0; font-size:var(--fs-sm); color:var(--text-muted);
}
.cta__nudge[hidden]{display:none}

/* ---- 8. REASSURANCE ROW */
.cta__reassure{
  display:flex; flex-wrap:wrap; justify-content:center; align-items:center;
  gap:var(--sp-2) var(--sp-4);
  list-style:none; margin:0; padding:0;
  font-size:var(--fs-label); font-weight:var(--fw-label);
  letter-spacing:var(--ls-label); text-transform:uppercase;
  color:var(--text-muted); line-height:1.4;
}
.cta__reassure li{display:flex; align-items:center; gap:var(--sp-4)}
.cta__reassure li + li::before{content:"/"; color:var(--line-hover)}

/* ---- 9. RESET RUN. Deliberately the quietest thing on the screen. */
.cta__reset{
  display:inline-flex; align-items:center; justify-content:center;
  min-height:44px; padding:0 var(--sp-4);
  background:none; border:0; cursor:pointer; font-family:inherit;
  font-size:var(--fs-label); font-weight:var(--fw-label);
  letter-spacing:var(--ls-label); text-transform:uppercase;
  color:var(--text-muted);
  transition:color var(--dur-fast) var(--ease-state);
}
.cta__reset:hover{color:var(--text)}

/* ---- Desktop */
@media (min-width:900px){
  .cta__wrap{gap:var(--sp-8)}
  .cta__cluster{flex-direction:row; justify-content:center; gap:var(--sp-8)}
  .cta__store{width:auto; max-width:none; padding-inline:var(--sp-8)}

  .cta__qr{
    display:flex; flex-direction:column; align-items:center; gap:var(--sp-3);
  }
  .cta__qr-svg{
    display:block; width:120px; height:120px;
    border-radius:var(--r-chip);
  }
  .cta__qr-lbl{
    font-size:var(--fs-label-s); font-weight:var(--fw-label);
    letter-spacing:var(--ls-label); text-transform:uppercase;
    color:var(--text-muted); line-height:1;
  }

  .cta__ghost{display:block}
  .cta__shelf{
    grid-template-columns:repeat(4, 96px);
    gap:var(--sp-4);
  }
  .cta__badge{min-height:96px}
}

/* ---- Reduced motion: nothing moves, every state still lands. */
@media (prefers-reduced-motion: reduce){
  #finalcta.in .cta__badge[data-state="locked"]{animation:none}
  a.cta__badge:hover,
  .cta__store:hover{transform:none}
}


/* ==========================================================================
   FOOTER
   The legal hrefs in the markup are contractual. Do not restyle them into
   anything that could be intercepted, and never wire JS to them.
   ========================================================================== */

.ft{
  background:var(--surface-chrome);
  border-top:1px solid var(--line);
  padding-block:var(--sp-16);
}

.ft__cols{
  display:grid; grid-template-columns:1fr; gap:var(--sp-10);
}

/* ---- Column one */
.ft__lockup{
  display:inline-flex; align-items:center; gap:var(--sp-3);
  min-height:44px; text-decoration:none;
}
.ft__mark{display:block; width:28px; height:28px}
.ft__mark svg{display:block; width:100%; height:100%; border-radius:var(--r-logo)}
/* Set in CSS, not as presentation attributes: var() does not resolve there. */
.ft__mark-tile{fill:var(--rose)}
.ft__mark-arcs{stroke:var(--text)}
.ft__word{
  font-size:1.25rem; font-weight:900; letter-spacing:-.7px; color:var(--rose);
}

/* The tagline exactly as it renders on the app's login screen. */
.ft__tagline{
  margin:var(--sp-3) 0 0;
  font-size:var(--fs-label); font-weight:var(--fw-label);
  letter-spacing:var(--ls-tagline); text-transform:uppercase;
  color:var(--rose); line-height:1.5;
}

/* ---- Column heads */
.ft__head{
  margin:0 0 var(--sp-2);
  font-size:var(--fs-label); font-weight:var(--fw-label);
  letter-spacing:var(--ls-label); text-transform:uppercase;
  color:var(--text); line-height:1;
}

.ft__list{list-style:none; margin:0; padding:0}

/* Link: muted, hover --text with a 1px rose underline wiping in from the left. */
.ft__lk{
  position:relative; display:inline-flex; align-items:center;
  min-height:44px;
  color:var(--text-muted); text-decoration:none;
  font-size:var(--fs-sm);
  transition:color var(--dur-fast) var(--ease-state);
}
/* Short links like FAQ are narrower than 44px. Widen the HIT AREA with a
   pseudo-element rather than the box, so the underline still tracks the text. */
.ft__lk::before{
  content:""; position:absolute; top:0; bottom:0; left:50%;
  transform:translateX(-50%); width:max(100%, 44px);
}
.ft__lk::after{
  content:""; position:absolute; left:0; right:0; bottom:calc(50% - .85em);
  height:1px; background:var(--rose);
  transform:scaleX(0); transform-origin:left center;
  transition:transform var(--dur-fast) var(--ease-state);
}
.ft__lk:hover{color:var(--text)}
.ft__lk:hover::after{transform:scaleX(1)}

/* ---- The selected destination's phrase */
.ft__phrase{
  display:flex; flex-wrap:wrap; gap:var(--sp-2);
  margin:var(--sp-12) 0 0;
  font-size:var(--fs-cap); color:var(--text-muted); line-height:var(--lh-tight);
}
.ft__phrase-native{font-style:italic; color:var(--text)}

/* ---- Bottom bar */
.ft__bar{
  display:flex; flex-direction:column; align-items:flex-start; gap:var(--sp-6);
  margin-top:var(--sp-6); padding-top:var(--sp-6);
  border-top:1px solid var(--line);
}
.ft__bar-l{max-width:var(--measure)}
.ft__copy{
  margin:0;
  font-size:var(--fs-label); font-weight:var(--fw-label);
  letter-spacing:var(--ls-label); text-transform:uppercase;
  color:var(--text-muted); line-height:1.4;
}
.ft__made{
  margin:var(--sp-3) 0 0;
  font-size:var(--fs-cap); color:var(--text-muted); line-height:var(--lh-tight);
}
.ft__store{flex:0 0 auto; min-height:44px}

/* ---- Tablet: two columns */
@media (min-width:620px){
  .ft__cols{grid-template-columns:repeat(2, minmax(0,1fr)); gap:var(--sp-10) var(--sp-8)}
}

/* ---- Desktop: four columns, bottom bar splits left and right */
@media (min-width:900px){
  .ft{padding-block:64px}
  .ft__cols{
    grid-template-columns:1.4fr 1fr 1fr 1fr;
    gap:var(--sp-10);
  }
  .ft__bar{
    flex-direction:row; align-items:flex-end; justify-content:space-between;
    gap:var(--sp-10);
  }
}


/* ==========================================================================
   STANDALONE ROUTES - phone reading corrections
   /support, /privacy, /terms and /delete-account share the footer above, so
   the handful of document corrections that only bite on a phone live here
   next to it. The document styling itself belongs to legal.css, which is
   concatenated AFTER this file: everything below therefore either sets a
   property legal.css never declares, or carries a higher specificity on
   purpose. Nothing here fights a tie.
   ========================================================================== */

/* Aunty@farangapp.com and farangapp.com/delete-account are the longest
   unbroken strings in these documents and both sit inside body copy. Let
   them break mid-string rather than set the column's min-content width and
   push the page sideways at 320px. */
.legal a{overflow-wrap:anywhere}

/* Long legal paragraphs are the one place a single-word last line is really
   noticeable. Both properties degrade to nothing where unsupported. */
.legal h1,.legal h2,.legal h3{text-wrap:balance}
.legal p,.legal li{text-wrap:pretty}

@media (max-width:479px){
  /* 48px of air above every h2 is a desktop rhythm. On a phone the same
     document runs three to four times longer, so the gaps read as breaks in
     the argument rather than as structure. main.legal beats legal.css's
     .legal h2 on specificity, which is why the selector is written that way. */
  main.legal h2{margin-top:var(--sp-10)}
  main.legal h3{margin-top:var(--sp-6)}

  /* The contents card holds twelve 44px rows. At 320px the 20px card padding
     costs more than it earns, and the rows are what needs the width. */
  .legal .legal__toc{padding:var(--sp-4)}

  /* Same reasoning for the two full-width panels on /delete-account and the
     closing note: keep the 44px targets, spend less on the frame. */
  .legal .legal__panel,
  .legal .legal__foot-note{padding:var(--sp-4)}

  /* The cross-link row is five items now, so it wraps to three rows at
     320px. Trim the column gap so two short links can share a row; the 44px
     link height already separates the rows. */
  .legal .legal__nav{gap:var(--sp-1) var(--sp-4)}
}

/* ===== 20-reveal.css ===== */
/* ==========================================================================
   FARANG - farangapp.com
   20 REVEAL: the shared scroll-entrance and micro-interaction layer.

   THE ONE RULE THAT MATTERS
   Every hidden state in this file is gated behind `html.js-reveal`, and that
   class is added by parts/js/20-reveal.js at boot. If the script never runs,
   throws, is blocked, or the browser has no IntersectionObserver, the class
   is never added and NOTHING in here hides anything. The page renders at full
   opacity with every line of copy present. Never move a hidden state out from
   under `.js-reveal`.

   Two curves only, both from the foundation. No third curve is introduced.
   Only transform, opacity and clip-path are animated - nothing here can
   trigger layout.

   AUTHORING CONTRACT (what other part files may use):

     data-reveal              rise 20px + fade in, --dur-slow, --ease-out
     data-reveal="fade"       fade only, no travel
     data-reveal="wipe"       Anton clip-path wipe, per <span> line, 80ms apart
                              matching the hero H1 exactly
     style="--i:3"            stagger index, 60ms apart, capped at 6
     style="--rise:12px"      override the travel distance on one element

     data-countup             count the number already in this element's text
                              up to itself once it enters view. The final text
                              is restored verbatim, so the markup is always the
                              source of truth for the figure.

     data-lift                pointer hover lifts 4px and brightens the border
     data-settle              click plays the 160ms settle (.chip gets this
                              automatically)

   The script also adds `.in` to every <section> and the <footer> as they
   arrive. That is the hook 10-hud-hero.css (.stamps.in) and 140-cta-footer.css
   (#finalcta.in) were already written against.
   ========================================================================== */


/* --------------------------------------------------------------------------
   1. RISE AND FADE
   The hidden state carries no transition, so applying it is instant and
   silent. The transition is declared on the revealed state, which is what
   animates the change when the class flips.
   -------------------------------------------------------------------------- */

.js-reveal [data-reveal]{
  /* stagger, capped at six so a long list never crawls */
  --rd:calc(min(var(--i,0), 6) * var(--stagger));
  opacity:0;
  transform:translate3d(0, var(--rise,20px), 0);
}

.js-reveal [data-reveal="fade"]{ transform:none }

.js-reveal [data-reveal].is-revealed{
  opacity:1;
  transform:none;
  transition:opacity var(--dur-slow) var(--ease-out),
             transform var(--dur-slow) var(--ease-out);
  transition-delay:var(--rd);
}


/* --------------------------------------------------------------------------
   2. THE ANTON WIPE
   Display lines reveal by uncovering, never by fading. The inset values are
   the hero's, verbatim, so a heading further down the page and the H1 read as
   one system: the negative insets give the skew overhang and the descenders
   room so nothing is shaved off at the end of the wipe.
   The <span> keeps its skewX from .d > span - this only ever writes clip-path.
   -------------------------------------------------------------------------- */

.js-reveal [data-reveal="wipe"]{ opacity:1; transform:none }

.js-reveal [data-reveal="wipe"] > span{
  -webkit-clip-path:inset(0 0 100% 0);
          clip-path:inset(0 0 100% 0);
}

.js-reveal [data-reveal="wipe"].is-revealed > span{
  -webkit-clip-path:inset(-0.14em -0.24em -0.16em -0.06em);
          clip-path:inset(-0.14em -0.24em -0.16em -0.06em);
  transition:-webkit-clip-path var(--dur-slow) var(--ease-out),
                     clip-path var(--dur-slow) var(--ease-out);
  transition-delay:calc(var(--rd) + var(--ln,0) * 80ms);
}

/* 80ms per line. Six lines is more than any headline on this site has. */
.js-reveal [data-reveal="wipe"] > span:nth-child(1){--ln:0}
.js-reveal [data-reveal="wipe"] > span:nth-child(2){--ln:1}
.js-reveal [data-reveal="wipe"] > span:nth-child(3){--ln:2}
.js-reveal [data-reveal="wipe"] > span:nth-child(4){--ln:3}
.js-reveal [data-reveal="wipe"] > span:nth-child(5){--ln:4}
.js-reveal [data-reveal="wipe"] > span:nth-child(n+6){--ln:5}

/* A wipe on an element with no line spans wipes itself. If :has is missing
   the rule drops and that element simply stays visible, which is the correct
   way for this to fail. */
.js-reveal [data-reveal="wipe"]:not(:has(> span)){
  -webkit-clip-path:inset(0 0 100% 0);
          clip-path:inset(0 0 100% 0);
}
.js-reveal [data-reveal="wipe"].is-revealed:not(:has(> span)){
  -webkit-clip-path:inset(-0.14em -0.24em -0.16em -0.06em);
          clip-path:inset(-0.14em -0.24em -0.16em -0.06em);
  transition:-webkit-clip-path var(--dur-slow) var(--ease-out),
                     clip-path var(--dur-slow) var(--ease-out);
  transition-delay:var(--rd);
}


/* --------------------------------------------------------------------------
   3. THE LABEL RULE DRAWS IN
   .lbl::after is the hairline that runs off to the right of every section
   label. It is already flex:1 and 1px tall, so scaling it on X costs nothing
   and moves no layout. Scoped to <section> and <footer> because those are the
   only elements that ever receive .in - the run tracker's .lbl lives on <body>
   and must never be caught by this.
   -------------------------------------------------------------------------- */

.js-reveal section .lbl::after,
.js-reveal footer .lbl::after{
  transform:scaleX(0);
  transform-origin:0 50%;
}
.js-reveal section.in .lbl::after,
.js-reveal footer.in .lbl::after{
  transform:none;
  transition:transform var(--dur-slow) var(--ease-out) 120ms;
}


/* --------------------------------------------------------------------------
   4. HOVER LIFT
   Pointer devices only. A touch device has no hover state to reveal, and the
   spec replaces this with the 3px accent rail on mobile, which already ships.
   -------------------------------------------------------------------------- */

@media (hover:hover) and (pointer:fine){
  [data-lift]{
    transition:transform var(--dur-fast) var(--ease-state),
               border-color var(--dur-fast) var(--ease-state);
  }
  [data-lift]:hover{
    transform:translateY(-4px);
    border-color:var(--line-hover);
  }
  /* A lifting card that is still mid-reveal must not have its entrance
     transform stomped by the hover transform. */
  .js-reveal [data-reveal][data-lift]:not(.is-revealed):hover{ transform:translate3d(0, var(--rise,20px), 0) }
}


/* --------------------------------------------------------------------------
   5. THE SETTLE
   Applied by the script on click, removed 200ms later, so it never fires for
   a control that was already selected when the page loaded. 1.02 is the hard
   ceiling on overshoot for the whole site and this sits exactly on it, easing
   down rather than springing past.
   -------------------------------------------------------------------------- */

.fr-settle{ animation:fr-settle var(--dur-fast) var(--ease-state) 1 }
@keyframes fr-settle{
  from{ transform:scale(1.02) }
  to{ transform:scale(1) }
}


/* --------------------------------------------------------------------------
   6. REDUCED MOTION
   The script does not add .js-reveal at all when this is set, so section 1-3
   can never apply. These rules are the belt to that braces: if the preference
   is switched on after boot, every element is already at its end state before
   the next frame.
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce){
  .js-reveal [data-reveal],
  .js-reveal [data-reveal].is-revealed{
    opacity:1; transform:none; transition:none;
  }
  .js-reveal [data-reveal="wipe"] > span,
  .js-reveal [data-reveal="wipe"].is-revealed > span,
  .js-reveal [data-reveal="wipe"],
  .js-reveal [data-reveal="wipe"].is-revealed{
    -webkit-clip-path:none; clip-path:none; transition:none;
  }
  .js-reveal section .lbl::after,
  .js-reveal footer .lbl::after,
  .js-reveal section.in .lbl::after,
  .js-reveal footer.in .lbl::after{
    transform:none; transition:none;
  }
  [data-lift]:hover{ transform:none }
  .fr-settle{ animation:none }
}


/* --------------------------------------------------------------------------
   7. PRINT
   A printed page has no scroll, so nothing would ever intersect.
   -------------------------------------------------------------------------- */

@media print{
  .js-reveal [data-reveal]{ opacity:1 !important; transform:none !important }
  .js-reveal [data-reveal="wipe"] > span,
  .js-reveal [data-reveal="wipe"]{
    -webkit-clip-path:none !important; clip-path:none !important;
  }
  .js-reveal section .lbl::after,
  .js-reveal footer .lbl::after{ transform:none !important }
}

/* ===== 40-feature.css ===== */
/* ==========================================================================
   40 FEATURE - the one section pattern, used five times.

   The page is a scroll narrative: you are a farang, here is what you need,
   in the order you need it. Each feature gets exactly one section and is
   never explained twice, so this file is deliberately the only layout in
   the middle of the page.

   Copy on one side, one real screenshot on the other, alternating with
   .feat--flip. Nothing here is interactive; the screenshot IS the
   explanation, which is what keeps the section short.

   ROSE BUDGET: one word in the headline. The numbered kicker and the tick
   marks use the section's category accent, never rose.
   ========================================================================== */

.feat{padding-block:var(--section-y)}
.feat + .feat{padding-top:0}          /* sections read as one run, not five slabs */

.feat__inner{
  display:grid; gap:clamp(var(--sp-10), 7vw, var(--sp-24));
  grid-template-columns:1fr;
  align-items:center;
}
@media (min-width:900px){
  .feat__inner{grid-template-columns:minmax(0,1fr) auto}
  .feat--flip .feat__copy{order:2}
  .feat--flip .feat__shot{order:1}
}

/* ---------------------------------------------------------------- COPY */

.feat__copy{display:flex; flex-direction:column; align-items:flex-start}
.feat__copy .lbl{margin-bottom:var(--sp-5)}
.feat__copy .d{margin-bottom:var(--sp-5)}

.feat__lead{
  font-size:var(--fs-lead); color:var(--text-muted);
  line-height:var(--lh-body); max-width:38ch; margin:0;
}

/* Two or three of these at most. They are the only body copy allowed in a
   feature section beyond the single lead sentence. */
.feat__pts{
  list-style:none; margin:var(--sp-8) 0 0; padding:0;
  display:flex; flex-direction:column; gap:var(--sp-3);
  max-width:40ch;
}
.feat__pts li{
  display:grid; grid-template-columns:auto 1fr; gap:var(--sp-3);
  align-items:start;
  font-size:var(--fs-sm); color:var(--text-muted);
  line-height:var(--lh-tight);
}
/* Muted, not accented. Three coloured ticks beside a rose headline word
   put the section over the three-rose budget, and the ticks are the least
   important thing in it. The section accent stays on the kicker rail. */
.feat__tick{
  width:17px; height:17px; flex:0 0 auto; margin-top:2px;
  color:var(--text-muted); opacity:.75;
}

/* ---------------------------------------------------------------- SHOT */

.feat__shot{justify-self:center}
.feat__dev{--dw:clamp(240px, 30vw, 296px)}

/* ---------------------------------------------------------------- ALSO
   One compact row for the things that did not earn a section of their own.
   Icons and three words each - never a paragraph. */

.also{padding-block:var(--section-y-tight)}
.also__row{
  list-style:none; margin:0; padding:0;
  display:grid; gap:var(--sp-5);
  grid-template-columns:repeat(auto-fit, minmax(200px, 1fr));
}
.also__i{
  display:grid; grid-template-columns:auto 1fr; gap:var(--sp-4);
  align-items:center;
  padding:var(--sp-5);
  background:var(--surface-well);
  border:1px solid var(--line);
  border-radius:var(--r-card);
}
.also__ico{
  width:38px; height:38px; border-radius:var(--r-nested);
  display:grid; place-items:center;
  background:var(--surface-nested);
  color:var(--also-acc, var(--text-muted));
}
.also__t{display:flex; flex-direction:column; gap:2px; min-width:0}
.also__h{font-size:var(--fs-sm); font-weight:var(--fw-semi); color:var(--text)}
.also__d{font-size:var(--fs-micro); color:var(--text-muted); line-height:var(--lh-tight)}

/* ---------------------------------------------------------------- NARROW */

@media (max-width:899px){
  .feat{padding-block:var(--section-y-tight)}
  .feat__inner{gap:var(--sp-10)}
  .feat__shot{order:-1}            /* screenshot first, then the words */
  .feat--flip .feat__shot{order:-1}
  .feat__lead,.feat__pts{max-width:none}
}

/* ===== 45-flow.css ===== */
/* ==========================================================================
   45 FLOW - a feature shown as a sequence instead of a single screenshot.

   One static screenshot of a map does not explain a product. These sections
   put the copy on top and two to four real screens underneath, in order,
   each with a numbered caption, so the reader can see the actual path
   through the app rather than guess at it.

   .feat--flow overrides the two-column grid in 40-feature.css: at this
   width three phones need the full measure, so the copy stops competing
   with them for horizontal space.

   Below 900px the row becomes a scroller. The next phone stays part-visible,
   which is what tells you the sequence continues.
   ========================================================================== */

.feat--flow .feat__inner{
  grid-template-columns:1fr;
  align-items:start;
  gap:var(--sp-12);
}
@media (min-width:900px){
  .feat--flow .feat__inner{grid-template-columns:1fr}
  /* .feat--flip has nothing to flip here - the row is always full width. */
  .feat--flow .feat__copy{order:0}
  .feat--flow .feat__flow{order:0}
}

.feat--flow .feat__lead{max-width:52ch}

/* ---------------------------------------------------------------- ROW */

.feat__flow{
  display:grid;
  grid-auto-flow:column;
  grid-auto-columns:minmax(0,1fr);
  gap:clamp(var(--sp-4), 2.5vw, var(--sp-8));
  align-items:start;
  width:100%;
}

.flow__step{
  margin:0; min-width:0;
  display:flex; flex-direction:column; gap:var(--sp-4);
}

.flow__dev{--dw:100%; width:100%}

/* The arrow between steps. Drawn on the caption row rather than between
   the phones, so it never collides with a device corner. Hidden on the
   last step and whenever the row is scrolling rather than laid out. */
.flow__cap{
  display:grid; grid-template-columns:auto 1fr; gap:var(--sp-3);
  align-items:start;
  font-size:var(--fs-sm); color:var(--text-muted);
  line-height:var(--lh-tight);
}
.flow__n{
  display:grid; place-items:center;
  width:22px; height:22px; flex:0 0 auto;
  border-radius:50%;
  border:1px solid var(--line-hover);
  font-size:var(--fs-label-s); font-weight:var(--fw-label);
  color:var(--feat-acc, var(--text-muted));
  font-variant-numeric:tabular-nums;
}
.flow__t strong{
  display:block; color:var(--text);
  font-size:var(--fs-sm); font-weight:var(--fw-semi);
  margin-bottom:2px;
}

/* ---------------------------------------------------------------- NARROW */

@media (max-width:899px){
  .feat--flow .feat__inner{gap:var(--sp-8)}

  /* Escape the wrap so the scroller can bleed to both edges, then put the
     gutter back as padding. Without this the first and last phone sit
     awkwardly inset from the edge of the screen. */
  .feat__flowwrap{
    margin-inline:calc(var(--gutter) * -1);
    padding-inline:var(--gutter);
    overflow-x:auto; overscroll-behavior-x:contain;
    scrollbar-width:thin; -webkit-overflow-scrolling:touch;
    scroll-snap-type:x proximity;
  }
  .feat__flow{
    grid-auto-columns:minmax(200px, 1fr);
    width:max-content;
  }
  .flow__step{scroll-snap-align:start}
}

@media (min-width:900px){
  /* No scroller needed - the grid already fits inside the wrap. */
  .feat__flowwrap{overflow:visible}
}

/* ===== legal.css ===== */
/* ==========================================================================
   LEGAL - /privacy, /terms, /delete-account.  Spec 11.
   These three paths are hardcoded inside the shipped iOS app. Everything in
   this file is scoped under .legal or the .legal- chrome prefix so it can
   never touch the marketing page, which loads the same site.css.
   No game layer here. A legal page is a document, not a run.
   ========================================================================== */

/* ---- Skip link. Present on all three documents. ------------------------ */
.legal-skip{
  position:absolute; left:var(--sp-4); top:var(--sp-4); z-index:calc(var(--z-hud) + 1);
  transform:translateY(-200%);
  background:var(--surface-card); color:var(--text);
  border:1px solid var(--line-hover); border-radius:var(--r-pill);
  padding:0 var(--sp-5); min-height:44px; display:inline-flex; align-items:center;
  font-size:var(--fs-cap); font-weight:var(--fw-semi); text-decoration:none;
  transition:transform var(--dur-fast) var(--ease-state);
}
.legal-skip:focus-visible{transform:translateY(0)}

/* ---- The simplified static header. 64px, opaque, one hairline.
   No HUD, no XP rail, no CTA pill, no backdrop-filter. ------------------- */
.legal-hdr{
  position:fixed; inset:0 0 auto 0; z-index:var(--z-hud);
  height:var(--hud-h); background:var(--surface-chrome);
  border-bottom:1px solid var(--line);
}
.legal-hdr__inner{
  height:100%; display:flex; align-items:center; justify-content:space-between;
  gap:var(--sp-4);
}
.legal-hdr__brand{
  display:inline-flex; align-items:center; gap:var(--sp-3);
  min-height:44px; text-decoration:none; color:var(--text);
}
.legal-hdr__mark{display:block; flex:0 0 auto; border-radius:var(--r-logo)}
.legal-hdr__word{
  font-family:var(--ff-display); font-size:1.25rem; text-transform:uppercase;
  letter-spacing:var(--ls-display); line-height:1;
  display:inline-block; transform:skewX(var(--skew-display)); padding-right:.08em;
}
.legal-hdr__back{
  min-height:44px; text-transform:uppercase; letter-spacing:1.4px;
  font-size:var(--fs-micro); white-space:nowrap;
}
.legal-hdr__long{display:none}
@media (min-width:560px){ .legal-hdr__long{display:inline} }

/* ---- The document -------------------------------------------------------
   Spec 11.1. No .has-grid on legal pages; the grain layer stays on body. */
.legal{
  max-width:720px; margin:0 auto;
  padding:calc(var(--hud-h) + var(--sp-16)) var(--gutter) var(--sp-24);
}
.legal--narrow{max-width:640px}

.legal h1{
  font-family:var(--ff-display); text-transform:uppercase;
  font-size:var(--fs-display-1); letter-spacing:var(--ls-display);
  line-height:var(--lh-display); color:var(--text); margin:0 0 var(--sp-3);
}
.legal h1 span{display:inline-block; transform:skewX(var(--skew-display)); padding-right:.08em}

.legal h2{
  font-size:var(--fs-lead); font-weight:var(--fw-semi); color:var(--text);
  line-height:var(--lh-tight); letter-spacing:.02em;
  margin:var(--sp-12) 0 var(--sp-3);
}
.legal h3{
  font-size:var(--fs-body); font-weight:var(--fw-semi); color:var(--text);
  margin:var(--sp-8) 0 var(--sp-2);
}
.legal p,.legal li{
  font-size:var(--fs-body); line-height:var(--lh-body);
  color:var(--text-muted); max-width:var(--measure);
}
.legal p{margin:0 0 var(--sp-4)}
.legal a{color:var(--rose); text-underline-offset:3px}
.legal hr{border:0; border-top:1px solid var(--line); margin:var(--sp-12) 0}

/* The document label. Rose is spent on the mark, the links and the CTA here,
   so the rail runs quiet and the page stays inside the three-rose budget. */
.legal .lbl--rail{--rail:var(--line-hover)}

.legal .legal__meta{
  font-size:var(--fs-label); font-weight:var(--fw-label);
  letter-spacing:var(--ls-label); text-transform:uppercase;
  color:var(--text-muted); margin:0 0 var(--sp-8);
}
.legal__intro{color:var(--text-muted)}

/* ---- Table of contents. Not sticky. ------------------------------------ */
.legal__toc{
  background:var(--surface-card); border:1px solid var(--line);
  border-radius:var(--r-card); padding:var(--sp-5); margin:var(--sp-8) 0 var(--sp-4);
}
.legal .legal__toc-h{
  font-size:var(--fs-label); font-weight:var(--fw-label);
  letter-spacing:var(--ls-label); text-transform:uppercase;
  color:var(--text-muted); margin:0 0 var(--sp-3);
}
.legal__toc-list{
  list-style:none; margin:0; padding:0;
  display:grid; gap:2px;
}
@media (min-width:640px){ .legal__toc-list{grid-template-columns:1fr 1fr; column-gap:var(--sp-6)} }
.legal__toc-list a{
  display:flex; align-items:center; gap:var(--sp-2);
  min-height:44px; padding:0 var(--sp-2);
  color:var(--text); text-decoration:none;
  font-size:var(--fs-sm); border-radius:var(--r-sm);
  border-bottom:1px solid transparent;
}
.legal__toc-list a:hover{color:var(--text); border-bottom-color:var(--rose-35)}
.legal__toc-n{
  color:var(--text-muted); font-size:var(--fs-micro);
  font-variant-numeric:tabular-nums; min-width:1.5em;
}

/* ---- Sections ----------------------------------------------------------- */
.legal__sec{scroll-margin-top:calc(var(--hud-h) + var(--sp-4))}
.legal__num{color:var(--text-muted); font-weight:var(--fw-med); margin-right:.4em}
.legal__list{margin:0 0 var(--sp-4); padding:0; list-style:none}
.legal .legal__list li{position:relative; padding-left:var(--sp-5); margin-bottom:var(--sp-2)}
.legal .legal__list li::before{
  content:""; position:absolute; left:2px; top:.7em;
  width:6px; height:1px; background:var(--text-muted);
}

/* ---- Cross-link row + document footer ----------------------------------- */
.legal__nav{
  margin-top:var(--sp-16); padding-top:var(--sp-6);
  border-top:1px solid var(--line);
  display:flex; flex-wrap:wrap; gap:var(--sp-2) var(--sp-5);
}
.legal__nav a{
  display:inline-flex; align-items:center; min-height:44px;
  font-size:var(--fs-cap); color:var(--text-muted); text-decoration:none;
  border-bottom:1px solid transparent;
}
.legal__nav a:hover{color:var(--text); border-bottom-color:var(--rose-35)}
.legal__nav a[aria-current="page"]{color:var(--text)}

/* ---- /delete-account panels --------------------------------------------- */
.legal__panels{display:grid; gap:var(--sp-4); margin:var(--sp-8) 0}
.legal .legal__panel-h{
  font-size:var(--fs-label); font-weight:var(--fw-label);
  letter-spacing:var(--ls-label); text-transform:uppercase;
  color:var(--text); margin:0 0 var(--sp-4);
}
.legal__panel p:last-child{margin-bottom:0}

/* ---- The mailto composer ------------------------------------------------- */
.legal__field{margin-bottom:var(--sp-5)}
.legal__label{
  display:block; font-size:var(--fs-cap); font-weight:var(--fw-semi);
  color:var(--text); margin-bottom:var(--sp-2);
}
.legal__input{
  display:block; width:100%; min-height:52px;
  background:var(--surface-nested); color:var(--text);
  border:1px solid var(--line); border-radius:var(--r-nested);
  padding:0 var(--sp-4); font-family:inherit; font-size:var(--fs-body);
  transition:border-color var(--dur-fast) var(--ease-state);
}
.legal__input::placeholder{color:var(--text-muted)}
.legal__input:hover{border-color:var(--line-hover)}
.legal .legal__help{
  font-size:var(--fs-cap); color:var(--text-muted); margin:var(--sp-2) 0 0;
}
.legal__check{
  display:flex; gap:var(--sp-3); align-items:flex-start;
  min-height:44px; padding:var(--sp-2) 0; cursor:pointer;
  font-size:var(--fs-sm); color:var(--text-muted); line-height:var(--lh-body);
  max-width:var(--measure);
}
.legal__check input{
  flex:0 0 auto; width:22px; height:22px; margin:5px 0 0;
  accent-color:var(--rose); cursor:pointer;
}
.legal__actions{margin-top:var(--sp-5)}
.legal__actions .btn{width:100%}
@media (min-width:520px){ .legal__actions .btn{width:auto; min-width:260px} }
.legal__actions .btn[disabled]{opacity:.45; cursor:not-allowed; transform:none; filter:none}
.legal .legal__note{font-size:var(--fs-cap); color:var(--text-muted); margin:var(--sp-4) 0 0}
.legal__noscript{
  display:block; margin-top:var(--sp-4);
  border-top:1px solid var(--line); padding-top:var(--sp-4);
}
.legal__foot-note{
  background:var(--surface-well); border:1px solid var(--line);
  border-radius:var(--r-card); padding:var(--sp-5); margin-top:var(--sp-8);
}
.legal__foot-note p{margin:0}

/* ===== support.css ===== */
/* ==========================================================================
   SUPPORT - /support.  App Store Connect requires a support URL, so this
   route exists for Apple as much as for a person with a broken subscription.
   It reuses the .legal document chrome from legal.css wholesale; everything
   here is scoped under .support so it can never reach the marketing page or
   the three legal documents, which load the same site.css.
   No game layer, no JS. Loads after legal.css, before zz-mobile.css.
   ========================================================================== */

/* ---- Buttons inside the document.
   legal.css paints every descendant link rose (.legal a), which is right for
   running prose and wrong for a pill: it would put rose text on a rose fill.
   Anchors styled as buttons are handed their button colours back. ---------- */
.support a.btn{text-decoration:none}
.support a.btn--primary{color:var(--rose-ink)}
.support a.btn--ghost{color:var(--text)}

/* ---- The contact card. The one place rose is spent on this page besides
   the header mark and the inline links: address, then the primary pill. --- */
.support__contact{margin:var(--sp-8) 0 0; padding:var(--sp-6)}
.support .support__contact-h{
  font-size:var(--fs-label); font-weight:var(--fw-label);
  letter-spacing:var(--ls-label); text-transform:uppercase;
  color:var(--text-muted); margin:0 0 var(--sp-3);
}
.support .support__addr{margin:0 0 var(--sp-4)}
.support .support__addr a{
  font-family:var(--ff-mono); font-weight:var(--fw-semi);
  /* Clamped low at the small end so the address never widens 320px. */
  font-size:clamp(1.0625rem, 5.4vw, 1.625rem);
  line-height:var(--lh-tight); letter-spacing:-.01em;
  overflow-wrap:anywhere;
  /* Standalone link, so it carries a 44px tap target of its own. */
  display:inline-flex; align-items:center; min-height:44px;
  text-decoration:underline; text-decoration-color:var(--rose-35);
  text-decoration-thickness:1px; text-underline-offset:5px;
}
.support .support__addr a:hover{text-decoration-color:var(--rose)}
.support .support__contact-p{margin:0 0 var(--sp-5)}
.support .support__cta{margin:0}
.support__cta .btn{width:100%}
@media (min-width:520px){ .support__cta .btn{width:auto} }

/* ---- The self-serve block ------------------------------------------------
   Six answers, each a card. One column on a phone, two once there is room. */
.support .support__lead-h{margin-top:var(--sp-12)}

.support__grid{
  display:grid; gap:var(--sp-4);
  margin:var(--sp-6) 0 0; padding:0; list-style:none;
}
@media (min-width:760px){
  .support__grid{grid-template-columns:1fr 1fr; align-items:start}
}

.support__item{padding:var(--sp-5)}
.support .support__item-h{
  font-size:var(--fs-body); font-weight:var(--fw-semi); color:var(--text);
  line-height:var(--lh-tight); margin:0 0 var(--sp-3);
}
.support .support__item p{font-size:var(--fs-sm); margin:0 0 var(--sp-3)}
.support .support__item p:last-child{margin-bottom:0}

/* Rose is spent once on this page, on the contact card, because that is the
   only thing the page exists to do. Six cards can share one tall viewport,
   and three rose links inside them plus the fixed header mark would blow the
   three-rose budget, so links below the fold read as text with a hairline
   under them and only earn rose on hover - the treatment the table of
   contents in legal.css already uses. */
.support .support__item a,
.support .support__quiet a{
  color:var(--text);
  text-decoration:underline; text-decoration-color:var(--line-hover);
  text-decoration-thickness:1px; text-underline-offset:3px;
}
.support .support__item a:hover,
.support .support__quiet a:hover{text-decoration-color:var(--rose)}

/* The four taps. Numbered because the count is the point. */
.support__steps{
  margin:0 0 var(--sp-3); padding:0; list-style:none;
  counter-reset:support-step;
}
.support .support__steps li{
  counter-increment:support-step;
  position:relative; padding-left:var(--sp-8);
  font-size:var(--fs-sm); line-height:var(--lh-tight);
  min-height:28px; display:flex; align-items:center;
  margin-bottom:var(--sp-2);
}
.support .support__steps li:last-child{margin-bottom:0}
.support .support__steps li::before{
  content:counter(support-step);
  position:absolute; left:0; top:2px;
  width:22px; height:22px; border-radius:var(--r-pill);
  background:var(--surface-nested); color:var(--text);
  border:1px solid var(--line);
  font-size:var(--fs-label-s); font-weight:var(--fw-label);
  font-variant-numeric:tabular-nums;
  display:flex; align-items:center; justify-content:center;
}

/* ---- The App Store link. Ghost, so the rose budget stays at three. ------ */
.support .support__store{margin-top:var(--sp-2)}
@media (max-width:519px){ .support .support__store{width:100%} }

/* ===== zz-mobile.css ===== */
/* ==========================================================================
   ZZ MOBILE - loads last, so it wins.

   Two jobs:
   1. A defensive floor so no section can ever make the PAGE scroll sideways.
      Wide things scroll inside their own rail instead.
   2. Small-screen corrections found by measuring real layouts at 320, 360,
      390 and 430px rather than by guessing at breakpoints.

   Rule of thumb used throughout: nothing is hidden to make it fit. If content
   does not fit it either wraps, stacks, or scrolls inside a labelled rail.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. THE FLOOR
   -------------------------------------------------------------------------- */

/* overflow-x:clip, not hidden. `hidden` on <body> promotes the body to a
   scroll container, which breaks position:sticky and confuses iOS Safari's
   address-bar resize. `clip` trims the same overflow without that side
   effect. Declared on both elements so the clip applies whichever one the
   browser treats as the viewport propagator. */
html{overflow-x:clip}
body{overflow-x:clip}

/* Long unbroken strings (emails, URLs, Thai without spaces) must never be the
   thing that widens a column. */
body{overflow-wrap:break-word}

/* Grid and flex children default to min-width:auto, which means "never shrink
   below your content". That single default is the cause of almost every
   horizontal-overflow bug. Opt every layout child out of it, then let the
   specific cases below opt back in where an intrinsic minimum is wanted. */
.wrap > *, .card > *, .well > *, .nested > *{min-width:0}

/* --------------------------------------------------------------------------
   2. CORRECTIONS, MEASURED
   -------------------------------------------------------------------------- */

@media (max-width:899px){

  /* The kit converter card. Measured min-content was 388px inside a 350px
     column at 390px wide: a 338px input row plus 2x24px card padding. Tighten
     the padding and let the number input shrink to nothing, since it is
     flex:1 and the value stays legible. */
  .kit__conv{padding:var(--sp-4)}
  .kit__amt{padding-left:var(--sp-3); padding-right:var(--sp-2); gap:var(--sp-1)}
  .kit__amtin{flex:1 1 0; width:0; min-width:0}
  .kit__convwrap{flex-direction:column; gap:var(--sp-5)}

  /* Preset amount chips wrap rather than forcing the card wide. */
  .kit__presets{flex-wrap:wrap}
}

@media (max-width:719px){
  /* Stamps band: below the spec's 720px switch it is a scroll rail.
     It deliberately does NOT full-bleed. A negative inline margin here widens
     the body by one gutter, and because the fixed HUD and the XP tracker are
     sized from the body, the whole page then sits 20px wider than the viewport
     and every line of copy gets trimmed on the right. The rail lives inside
     the wrap instead: correct beats flush. */
  .stamps__scroller{
    margin-inline:0;
    padding-inline:0;
    scroll-padding-inline:0;
  }
}

/* The audit found the five stamp chips need ~1177px but the spec switched them
   out of their scroll rail at 720px, so between 720px and ~1100px the first and
   last chip were clipped with no way to reach them - the country count and the
   free-tier claim, i.e. the two facts the band exists to state. Keep the rail
   until there is genuinely room. */
@media (min-width:720px) and (max-width:1099px){
  .stamps__scroller{
    overflow-x:auto; overscroll-behavior-x:contain; -webkit-overflow-scrolling:touch;
    margin-inline:0; padding-inline:0;
  }
  .stamps__row{width:max-content; min-width:100%}
}

/* Nothing inside the fixed HUD or the floating XP tracker may exceed the
   viewport. Both are sized from the body, so an overflow here is what pushes
   the whole page sideways. */
@media (max-width:899px){
  .hud__inner, .xp-trk__bar{max-width:100vw}
  .xp-trk__bar{box-sizing:border-box}
  .xp-trk__more{max-width:100%; white-space:normal}
}

/* The Border Run mode toggle. flex:1 stopped the BOX overflowing but not the
   TEXT: at 320px the two labels need 130px and 120px inside 112px buttons, so
   they ran together as "SCHENGEN 90/180THAILAND TM.47" with the pill edge
   cutting the zero. Shrink the type and drop the tracking before the crossover
   at roughly 355px rather than truncating a label that names a legal rule. */
@media (max-width:379px){
  .qvisa__mode{
    font-size:var(--fs-label-s);
    letter-spacing:0;
    padding-inline:var(--sp-1);
  }
}

@media (max-width:359px){
  /* The brand link loses its wordmark below 360px, which left an anchor with
     no accessible name and a 28px-wide hit box. Keep the word, just shrink it,
     so the link keeps both its name and a 44px target. */
  .hud__word{display:inline !important; font-size:var(--fs-sm)}
  .hud__brand{min-height:44px; min-width:44px}
}

/* --------------------------------------------------------------------------
   3. TOUCH TARGETS
   Every genuinely interactive control clears 44px on the cross axis on a touch
   device. Only elements that actually do something are targeted: a decorative
   .chip that happens to be a <span> (the FREE / PRO tier tags that are not
   links) is left at its designed size, because inflating a label is not an
   accessibility win, it is just a bigger label.

   Inline text links inside a paragraph or a footer column keep their natural
   width. WCAG's target-size rule exempts links whose target is determined by
   the text itself; forcing them to 44px wide would wreck the column rhythm
   for no benefit. Their height is already 44.
   -------------------------------------------------------------------------- */
/* Keyed to viewport width rather than (hover:none). A narrow window is the
   thing that makes targets cramped, and width is testable in any browser,
   whereas a pointer query silently does nothing on a desktop check. Touch
   devices are narrow, so they get this too. */
@media (max-width:899px){
  a.btn, button.btn, .btn--sm,
  a.chip, button.chip,
  input[type="range"],
  summary{
    min-height:44px;
  }
  /* Inline-flex + centred so a taller hit box does not shift the label. */
  a.chip, button.chip{display:inline-flex; align-items:center; padding-block:0}
}

/* --------------------------------------------------------------------------
   4. iOS SAFARI
   -------------------------------------------------------------------------- */

/* Font sizes below 16px on a focusable input make iOS Safari zoom the page in
   and never zoom back out. Every text-entry field on this site is therefore
   at least 16px on touch, regardless of what the section CSS asked for. */
@media (hover:none){
  input[type="text"], input[type="email"], input[type="number"],
  input[type="search"], input[type="date"], textarea, select{
    font-size:max(16px, 1rem);
  }
}

/* Respect the notch and the home indicator when content runs to the edge. */
@supports (padding:max(0px)){
  .wrap{
    padding-left:max(var(--gutter), env(safe-area-inset-left));
    padding-right:max(var(--gutter), env(safe-area-inset-right));
  }
  #footer{padding-bottom:max(var(--sp-12), env(safe-area-inset-bottom))}
}

