/* ============================================================
   THUSO — PRODUCT THEATRE
   Two independent pillars:
     · MOBILE PILLAR  = base styles (app-shell UX, thumb-first)
     · DESKTOP PILLAR = @media (min-width:1024px) (cinematic theatre)
   ============================================================ */

/* ---------- Fonts ---------- */
/* Two faces for the whole site: Poppins Regular and Bold, both from Google Fonts (Theo, 2026-09-13).
   Every weight in this sheet is 400 or 700 to match. */
@font-face{font-family:'Poppins';font-style:normal;font-weight:400;font-display:swap;src:url('../fonts/poppins-400.woff2') format('woff2')}
@font-face{font-family:'Poppins';font-style:normal;font-weight:700;font-display:swap;src:url('../fonts/poppins-700.woff2') format('woff2')}

/* ---------- Tokens ---------- */
:root{
  /* LIGHT THEME (default) — Thuso paper */
  --bg0:#EEF5F1;
  --bg1:#E3EFE8;
  --bg2:#D8E9E0;
  --band:#E2EFE9;
  --card:#FFFFFF;
  --card2:#F6FBF8;
  --line:rgba(12,60,48,.13);
  --line2:rgba(12,60,48,.26);
  --ink:#0B2B23;
  --sub:#3F5F55;
  --faint:#587569;
  --acc:#14735F;           /* interactive/accent text on canvas (AA on paper) */
  --acc-soft:rgba(20,115,95,.13);
  --hov-sh:rgba(13,52,42,.14);
  --teal:#1E9E82;
  --mint:#2FD9AD;
  --aqua:#54E8C2;
  --emerald:#0A5A47;
  /* Buttons carry the IDENTICAL ramp as the highlighted heading text below -
     same three stops, same 100deg. If one is ever retuned, retune both. */
  --grad:linear-gradient(100deg,#1FA486,#14735F 55%,#0D5B49);
  --grad-text:linear-gradient(100deg,#1FA486,#14735F 55%,#0D5B49);
  --buco:#F5921E;
  --timbercity:#1E7A3C;
  --citiwood:#00915E;
  --hhl:#E1251B;
  --sb:var(--acc); /* switchboard active brand accent */
  --font-d:'Poppins',ui-sans-serif,system-ui,sans-serif;
  --font-b:'Poppins',ui-sans-serif,system-ui,-apple-system,'Segoe UI',Roboto,sans-serif;
  --r-lg:28px; --r-md:20px; --r-sm:14px;
  --ds:1; /* device scale */
}

/* DARK STAGE THEME — the emerald night, scoped.
   Applies to: .act-dark sections (the "stages"), every device/app screen,
   the footer, and the floating hero chips. */
.act-dark,.float-chip{
  --bg0:#0D342A;
  --bg1:#104033;
  --bg2:#134A3B;
  --band:#082A21;
  --card:rgba(255,255,255,.05);
  --card2:rgba(255,255,255,.085);
  --line:rgba(160,228,203,.17);
  --line2:rgba(160,228,203,.3);
  --ink:#F1F8F4;
  --sub:#AECBC0;
  --faint:#8FAEA2;
  --acc:#2FD9AD;
  --acc-soft:rgba(47,217,173,.13);
  --hov-sh:rgba(0,0,0,.35);
  --grad-text:linear-gradient(100deg,var(--aqua),var(--mint) 45%,var(--teal));
  --buco:#FF9E2E;
  --timbercity:#52C978;
  --citiwood:#25C68F;
  --hhl:#FF6A5E;
}
.act-dark{
  position:relative;
  background:linear-gradient(180deg,#0C3128,#0D342A 42%,#0A2C23);
  color:var(--ink);
}

/* ---------- Reset / base ---------- */
*,*::before,*::after{box-sizing:border-box;-webkit-tap-highlight-color:transparent}
html{scroll-behavior:smooth}
/* jump links land clear of the fixed header - it is 64px on phones and 76px on desktop (mobile audit, 2026-09-14) */
html{scroll-padding-top:76px}
@media (min-width:1024px){html{scroll-padding-top:88px}}
html,body{overflow-x:clip}
body{
  margin:0;
  background:var(--bg0);
  color:var(--ink);
  font-family:var(--font-b);
  font-size:16px;
  line-height:1.6;
  -webkit-font-smoothing:antialiased;
  text-rendering:optimizeLegibility;
}
img{max-width:100%;display:block}
a{color:inherit;text-decoration:none}
button{font:inherit;color:inherit;background:none;border:0;cursor:pointer;padding:0}
ul{margin:0;padding:0;list-style:none}
h1,h2,h3,h4,p{margin:0}
::selection{background:var(--mint);color:#04120E}
:focus-visible{outline:2px solid var(--acc);outline-offset:3px;border-radius:6px}
body.no-scroll{overflow:hidden}
/* the open menu: body overflow alone let the page behind scroll, because html is the scroller (mobile audit, 2026-09-14) */
html.menu-open{overflow:hidden}

.skip{position:fixed;top:-60px;left:16px;z-index:100;background:var(--mint);color:#04120E;font:700 14px var(--font-d);padding:10px 16px;border-radius:10px;transition:top .2s}
.skip:focus{top:12px}

::-webkit-scrollbar{width:11px}
::-webkit-scrollbar-thumb{background:rgba(13,52,42,.3);border-radius:8px;border:3px solid #EEF5F1}
::-webkit-scrollbar-track{background:#EEF5F1}

/* ---------- Shared atoms ---------- */
.container{max-width:1240px;margin-inline:auto;padding-inline:clamp(20px,5vw,32px)}
.center{text-align:center}
.center .store-pills,.store-pills.center{justify-content:center}

.kicker{
  font:700 11px/1.4 var(--font-d);
  letter-spacing:.22em;
  text-transform:uppercase;
  color:var(--acc);
  margin-bottom:14px;
}
.kicker.center{text-align:center}

.display{
  font:700 clamp(2.55rem,5vw + 1.1rem,4.9rem)/1.04 var(--font-d);
  letter-spacing:-.02em;
}
.h1{font:700 clamp(2.1rem,2.9vw,3.3rem)/1.08 var(--font-d);letter-spacing:-.015em}
.h2{font:700 clamp(1.85rem,2.5vw,2.7rem)/1.12 var(--font-d);letter-spacing:-.015em}
/* phones: no single word left alone on a headline's last line (Features left "it." on its own, 2026-09-14) */
@media (max-width:1023px){.display,.h1,.h2{text-wrap:pretty}}
/* Headings in two weights (Theo, 2026-09-13): Poppins Regular, the <b> part Bold */
.h1.h-mix,.h2.h-mix,.display.h-mix{font-weight:400}
.h-mix b{font-weight:700}
/* Features page (2026-09-14): each section's number in its feature colour, as on the home page's cards */
.kicker.feat-n{font-size:12.5px;letter-spacing:.16em;color:var(--fcol,var(--acc));margin-bottom:10px}

.grad-text{
  background:var(--grad-text);
  -webkit-background-clip:text;
  background-clip:text;
  color:transparent;
}

.hero-copy .display{font-size:clamp(1.95rem,12vw,3.2rem)} /* mobile: two lines — sized so "build better." fits at 360 */
.hero-copy .display br{display:none}
.lede{color:var(--sub);font-size:clamp(1.02rem,1.1vw,1.16rem);line-height:1.65;max-width:36em;margin-top:20px;text-wrap:pretty}
.lede.center{margin-inline:auto}
.lede-wide{max-width:none} /* single-line sublines — the 36em measure would force a wrap */
/* Keeps a phrase whole so it wraps as ONE unit rather than being split mid-way.
   Used to control WHERE a heading breaks without a hard <br>, which would force
   the same break on a phone where it is not wanted. Desktop only: on narrow
   screens the heading should wrap wherever it needs to. */
@media (min-width:1024px){ .nowrap-d{white-space:nowrap} }
.brandline{font:700 clamp(1.55rem,3.2vw,2.2rem)/1.2 var(--font-d);letter-spacing:-.015em;color:var(--ink);margin-top:28px}
.section-sub{color:var(--sub);font-size:1.04rem;line-height:1.65;max-width:34em;margin-top:16px}

.ticks{margin-top:24px;display:grid;gap:12px}
.ticks li{
  position:relative;padding-left:34px;color:var(--sub);font-size:1rem;
}
.ticks li::before{
  content:"✓";position:absolute;left:0;top:1px;width:22px;height:22px;
  display:grid;place-items:center;border-radius:50%;
  background:var(--acc-soft);color:var(--acc);
  font:700 12px var(--font-d);
}

.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;font:700 15px var(--font-d);text-transform:uppercase;letter-spacing:.06em;border-radius:10px;transition:transform .25s,box-shadow .25s,filter .25s}
.btn-grad{background:var(--grad);color:#FFFFFF;padding:15px 26px;box-shadow:0 10px 28px rgba(13,91,73,.28)}
.btn-grad:hover{transform:translateY(-2px);filter:brightness(1.06);box-shadow:0 14px 38px rgba(47,217,173,.38)}
.btn-sm{padding:11px 20px;font-size:14px}
/* the header's Get the app: a long, low rectangle rather than a near-square block */
.head-cta .btn{padding:10px 34px;font-size:13px;border-radius:8px}

.text-link{
  display:inline-block;margin-top:18px;color:var(--acc);font:700 15px var(--font-d);
  border-bottom:1px solid transparent;transition:border-color .2s;
}
.text-link:hover{border-bottom-color:var(--acc)}
/* Chevron tip, drawn rather than typed. A "›" glyph sits at a different
   height and weight in every font; two borders rotated 45deg track the link's
   own font-size and colour exactly, at any size, in both themes. */
.chev{
  display:inline-block;width:.36em;height:.36em;
  margin-left:.5em;position:relative;top:-.07em;
  border-top:2px solid currentColor;
  border-right:2px solid currentColor;
  transform:rotate(45deg);
  transition:transform .22s;
}


.store-pills{display:flex;flex-wrap:wrap;gap:12px;margin-top:28px}
 /* Official Apple and Google badges. Both companies supply this artwork and both
   forbid redrawing their marks or restyling the badge, so the images are used
   exactly as supplied and are only ever SIZED - by height, never width, because
   the two have different aspect ratios and that is correct. Required clear space
   is held by the row gap. */
/* The badge files carry a transparent margin of 4/168 of their height on every side (2026-09-15). Cropped
   tight, the grey border had nothing to fade into and scaled down with its outer edge cut off. The img is
   drawn that much bigger and the link pulled back by the same amount, so the badge keeps its size and
   place. --bh is the badge's visible height; set it, never the img height. */
.store-badge{--bh:42px;display:inline-flex;margin:calc(var(--bh) * -4 / 168);border-radius:9px;transition:transform .25s,opacity .25s}
.store-badge:hover{transform:translateY(-2px);opacity:.9}
.store-badge img{height:calc(var(--bh) * 176 / 168);width:auto;display:block}

.dev-note{font-size:12px;color:var(--faint);margin-top:16px}
.dev-note.center{text-align:center}

/* Reveal on scroll (JS adds .is-in) — visible fallback via <noscript> */
.rv{opacity:0;transform:translateY(24px);transition:opacity .8s cubic-bezier(.2,.7,.2,1) var(--d,0s),transform .8s cubic-bezier(.2,.7,.2,1) var(--d,0s)}
.rv.is-in{opacity:1;transform:none}
/* An inner page's first screen does not wait for site.js: its reveals play on first paint, so the
   headline shows about a second sooner on a slow phone (mobile audit, 2026-09-14). At rest they are
   plain visible elements; the animation only supplies the way in. */
.page-hero .rv{opacity:1;transform:none;animation:rv-up .8s cubic-bezier(.2,.7,.2,1) var(--d,0s) backwards}
@keyframes rv-up{from{opacity:0;transform:translateY(24px)}}
/* site.js never ran (failed to load or broke on boot - see thusoFail in each page head): show everything */
html.js-failed .rv{opacity:1;transform:none;transition:none;animation:none}

/* Pillar gates */
.dt-only{display:none !important}

/* ---------- Atmosphere ---------- */
.atmo{position:fixed;inset:0;z-index:-1;overflow:hidden;background:linear-gradient(180deg,#F4FAF7 0%,var(--bg0) 44%,#E7F1EC 100%)}
.aurora{position:absolute;border-radius:50%;filter:none;pointer-events:none}
.aurora-a{
  width:78vmax;height:78vmax;top:-32vmax;left:-24vmax;
  background:radial-gradient(closest-side,rgba(47,217,173,.11),transparent 70%);
  animation:drift-a 68s ease-in-out infinite alternate;
}
.aurora-b{
  width:70vmax;height:70vmax;top:22%;right:-30vmax;
  background:radial-gradient(closest-side,rgba(30,158,130,.09),transparent 70%);
  animation:drift-b 84s ease-in-out infinite alternate;
}
.aurora-c{
  width:60vmax;height:60vmax;bottom:-20vmax;left:8vw;
  background:radial-gradient(closest-side,rgba(84,232,194,.07),transparent 72%);
  animation:drift-c 76s ease-in-out infinite alternate;
}
@keyframes drift-a{to{transform:translate(9vmax,7vmax) scale(1.12)}}
@keyframes drift-b{to{transform:translate(-8vmax,-6vmax) scale(1.08)}}
@keyframes drift-c{to{transform:translate(6vmax,-8vmax) scale(1.15)}}
.grain{
  position:absolute;inset:0;opacity:.035;pointer-events:none;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)' opacity='.6'/%3E%3C/svg%3E");
}
.cursor-glow{display:none}

/* ---------- Header ---------- */
.site-head{position:fixed;top:0;left:0;right:0;z-index:50;transition:background .3s ease,box-shadow .3s ease}
.site-head.is-solid{
  /* opaque, and NO backdrop-filter: the blur promoted the header to its own
     compositing layer (killing subpixel antialiasing) and the translucency let
     the dark phone bleed through — together that made the logo look fuzzy the
     moment you scrolled. */
  background:#F2F8F5;
  box-shadow:0 1px 0 rgba(12,60,48,.08),0 18px 40px rgba(13,52,42,.12);
}
.head-inner{max-width:1240px;margin-inline:auto;height:64px;display:flex;align-items:center;justify-content:space-between;padding-inline:clamp(20px,5vw,32px)}
.head-logo img{height:30px;width:auto}
.burger{
  width:44px;height:44px;
  /* gap 6, NOT 5. The bar stride is height+gap; at 5 that is 7 CSS px, which lands on
     half device pixels under Windows display scaling and rounds each bar differently -
     measured 3/2/2 at 1.25 and 4/4/3 at 1.75, i.e. visibly unequal lines. THE STRIDE MUST
     STAY 8 (height 3 + gap 5): 8 is whole at 1.25 / 1.5 / 1.75 / 2, so all three bars share
     one sub-pixel phase and render identically. If you change the height or the gap, keep
     them summing to 8 - AND update the .is-open translateY below to match the stride, or
     the two diagonals of the X stop crossing at the same point and it looks skew. */
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:5px;
  position:relative;z-index:60;border-radius:12px;
}
.burger span{display:block;width:24px;height:3px;background:var(--ink);border-radius:2px;transition:transform .3s,opacity .25s}
.burger.is-open span:nth-child(1){transform:translateY(8px) rotate(45deg)}
.burger.is-open span:nth-child(2){opacity:0}
.burger.is-open span:nth-child(3){transform:translateY(-8px) rotate(-45deg)}

/* ---------- Mobile menu (sheet sits UNDER the header so the burger/X stays usable) ---------- */
.mob-menu{
  position:fixed;inset:0;z-index:49;
  background:linear-gradient(180deg,rgba(244,250,247,.97),rgba(233,243,238,.985) 60%);
  backdrop-filter:blur(22px);-webkit-backdrop-filter:blur(22px);
  display:flex;flex-direction:column;justify-content:space-between;
  padding:104px 30px calc(34px + env(safe-area-inset-bottom));
  opacity:0;pointer-events:none;visibility:hidden;
  transition:opacity .32s ease,visibility 0s .32s;
  overflow-y:auto;overscroll-behavior:contain;
}
.mob-menu::before{
  content:"";position:absolute;inset:0;pointer-events:none;
  background:radial-gradient(58% 34% at 85% 0%,rgba(30,158,130,.12),transparent 70%);
}
.mob-menu.is-open{opacity:1;pointer-events:auto;visibility:visible;transition:opacity .32s ease}
.mob-links{display:grid;counter-reset:mnav;position:relative}
.mob-links a{
  counter-increment:mnav;
  display:flex;align-items:baseline;gap:15px;
  padding:11px 0;
  font:400 clamp(1.65rem,6.6vw,2.1rem)/1.2 var(--font-d);letter-spacing:-.01em;color:var(--ink);
  border-bottom:1px solid var(--line);
  opacity:0;transform:translateY(16px);
  transition:opacity .4s ease,transform .4s ease,color .2s;
}
.mob-links a:last-child{border-bottom:0}
.mob-links a::before{
  content:counter(mnav,decimal-leading-zero);
  font:700 12px var(--font-d);letter-spacing:.12em;color:var(--faint);
}
.mob-links a[aria-current="page"]{color:var(--acc)}
.mob-links a[aria-current="page"]::before{color:var(--acc)}
.mob-links a:active{color:var(--acc)}
.mob-menu.is-open .mob-links a{opacity:1;transform:none}
.mob-menu.is-open .mob-links a:nth-child(1){transition-delay:.05s}
.mob-menu.is-open .mob-links a:nth-child(2){transition-delay:.1s}
.mob-menu.is-open .mob-links a:nth-child(3){transition-delay:.15s}
.mob-menu.is-open .mob-links a:nth-child(4){transition-delay:.2s}
.mob-menu.is-open .mob-links a:nth-child(5){transition-delay:.25s}
.mob-menu.is-open .mob-links a:nth-child(6){transition-delay:.3s}
.mob-menu-foot{position:relative;padding-top:26px;opacity:0;transform:translateY(12px);transition:opacity .4s ease,transform .4s ease}
.mob-menu.is-open .mob-menu-foot{opacity:1;transform:none;transition-delay:.32s}
.mob-menu-note{font:700 11px var(--font-d);letter-spacing:.2em;text-transform:uppercase;color:var(--acc);margin-bottom:14px}
/* the nav above is left-aligned, but the footer reads as its own block - centred */
.mob-menu-foot{text-align:center}
.mob-menu-foot .store-pills{margin-top:0;justify-content:center}

/* ============================================================
   DEVICE + APP SCREENS  (design size 300 × 620, scaled by --ds)
   ============================================================ */
.device{position:relative;display:inline-block}
.device-frame{
  position:relative;
  width:calc(300px*var(--ds) + 16px);
  height:calc(620px*var(--ds) + 16px);
  border-radius:calc(46px*var(--ds) + 8px);
  background:linear-gradient(160deg,#131E19,#0A130F);
  border:1px solid rgba(255,255,255,.1);
  box-shadow:0 26px 64px rgba(10,40,32,.30),inset 0 0 0 1px rgba(255,255,255,.035); /* eased back from .6/90px - too heavy on the light paper */
  /* an illustration of the app, not text to copy: a stray drag across the hero
     painted the screen in the mint selection colour */
  -webkit-user-select:none;user-select:none;
}
.device-island{
  position:absolute;top:calc(8px + 11px*var(--ds));left:50%;transform:translateX(-50%);
  width:calc(92px*var(--ds));height:calc(25px*var(--ds));
  background:#020705;border-radius:999px;z-index:4;
}
.device-screen,.stage-screen{
  position:absolute;inset:8px;
  border-radius:calc(46px*var(--ds));
  overflow:hidden;background:#FFFFFF;
}
.device-screen>.scr,.stage-screen>.scr{
  width:300px;height:620px;
  zoom:var(--ds); /* laid out at final size, so the type stays crisp - see screens.css */
}
/* the client's screenshots in a flat device (2026-09-14): fill the screen from the top, so the status
   bar stays whole and the slight difference in shape comes off the bottom */
.device-screen>.device-shot{display:block;width:100%;height:100%;object-fit:cover;object-position:50% 0}
/* ...except the screens whose app menu sits at the very bottom - Materials (04 SHOP) and Credit Accounts (05 MANAGE)
   on the Features page (Theo, 2026-09-18: "the app menu/console at the bottom of the screen cuts out some of the
   buttons text, can we just add some padding at the bottom so it lifts the menu/console up a bit"). The shots are
   iPhone 15 Pro shape (2.17:1) in a 2.07:1 screen, so filling from the top cut 21px off the bottom at 360px wide -
   right through the menu's labels. These show nearly the whole shot instead, still from the top. The WHOLE shot
   (object-fit:contain alone) put the menu "a bit too high now" (Theo, same day) - 23px of the app's own space under
   the labels - so the shot's box stands 2.3% taller than the screen and the screen trims that off the bottom: half
   the old 21px, 10px at 360 wide, about 16px left under the labels, clear of the rounded corners. It scales with the
   phone. The shot narrows by about 2px a side into the screen's white. (Measured options at 360x663: trim 0 / 7 /
   10 / 13 / 21px - 21 is the old cut; 13 still clears the corners if he wants it a touch lower.) */
.device-screen>.device-shot.shot-whole{object-fit:contain;height:102.3%}

/* Screen chrome ------------------------------------------------
   The in-app UI runs LIGHT, matching the real Thuso app. Screen-scoped
   tokens (--s-*) so it retunes in one place, and ONE accent throughout —
   the live app currently mixes emerald, turquoise and indigo. */
.scr{
  --s-bg:#FFFFFF;
  --s-surface:#F2F7F4;
  --s-ink:#10241E;
  --s-sub:#4A6459;
  --s-faint:#7C968B;
  --s-line:rgba(16,36,30,.10);
  --s-acc:#0F6B58;
  --s-acc-soft:rgba(15,107,88,.09);
  display:flex;flex-direction:column;gap:12px;
  padding:12px 16px 18px;
  text-align:left; /* never inherit centring from a carousel slide — apps read left */
  background:var(--s-bg);
  color:var(--s-ink);font-family:var(--font-b);
  position:relative;
}
.scr::after{ /* home indicator */
  content:"";position:absolute;bottom:7px;left:50%;transform:translateX(-50%);
  width:88px;height:4px;border-radius:4px;background:rgba(16,36,30,.2);
}
.scr-status{display:flex;align-items:center;justify-content:space-between;padding:2px 6px 0;min-height:26px}
.st-time{font-size:12.5px;font-weight:700}
.st-ic{display:flex;align-items:flex-end;gap:3px}
.st-ic i{display:block;width:4px;border-radius:1.5px;background:rgba(16,36,30,.72)}
.st-ic i:nth-child(1){height:6px}
.st-ic i:nth-child(2){height:9px}
.st-ic i:nth-child(3){height:12px}
.scr-head{display:flex;align-items:center;justify-content:space-between;gap:10px;min-height:26px}
.scr-title{font:700 15.5px var(--font-d);letter-spacing:-.01em}
.scr-pill{font:700 10px var(--font-d);letter-spacing:.1em;text-transform:uppercase;color:var(--s-acc);background:var(--s-acc-soft);padding:5px 10px;border-radius:999px}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.35}}

/* solid emerald action, like the app's own primary buttons */
.app-cta{
  margin-top:auto;height:46px;border-radius:14px;
  background:var(--s-acc);color:#fff;
  display:grid;place-items:center;
  font:700 14.5px var(--font-d);
  box-shadow:0 8px 20px rgba(15,107,88,.22);
}
.app-label{font:700 10.5px var(--font-d);letter-spacing:.16em;text-transform:uppercase;color:var(--s-faint)}
.app-search{
  display:flex;align-items:center;gap:9px;
  background:var(--s-surface);border:1px solid var(--s-line);border-radius:14px;
  padding:11px 14px;color:var(--s-faint);font-size:13px;
}
.app-search svg{width:16px;height:16px;fill:var(--s-faint);flex:0 0 auto}

/* floating action button — on every main screen of the real app */
.app-fab{
  position:absolute;right:16px;bottom:58px;z-index:3;
  width:44px;height:44px;border-radius:50%;
  background:var(--s-acc);color:#fff;
  display:grid;place-items:center;
  font:400 25px/1 var(--font-b);
  box-shadow:0 10px 22px rgba(15,107,88,.32);
}

.drv-ava{
  width:34px;height:34px;border-radius:50%;flex:0 0 auto;
  background:var(--s-acc-soft);color:var(--s-acc);
  display:inline-flex;align-items:center;justify-content:center;
  font:700 12px/1 var(--font-d);letter-spacing:.02em;
}
.bdot{width:9px;height:9px;border-radius:50%;flex:0 0 auto}
.bdot-buco{background:var(--buco)}
.bdot-timbercity{background:var(--timbercity)}
.bdot-citiwood{background:var(--citiwood)}
.bdot-hhl{background:var(--hhl)}

/* scr-home */
.app-greet .ag-hi{font:700 20px var(--font-d);letter-spacing:-.01em}
.app-greet .ag-sub{font-size:13px;color:var(--s-sub);margin-top:2px}
.app-brandgrid{display:grid;grid-template-columns:repeat(4,1fr);gap:8px}
.ab-tile{
  background:var(--s-surface);border:1px solid var(--s-line);border-radius:14px;
  padding:10px 4px 8px;display:flex;flex-direction:column;align-items:center;gap:6px;
}
.ab-tile img{width:32px;height:32px;object-fit:contain}
.ab-tile span{font-size:9.5px;color:var(--s-sub)}
.app-reorder{
  display:flex;align-items:center;justify-content:space-between;gap:10px;
  background:var(--s-surface);border:1px solid var(--s-line);border-radius:14px;padding:12px 14px;
}
.ar-title{font-size:13.5px;font-weight:700}
.ar-sub{font-size:11.5px;color:var(--s-faint);margin-top:1px}
.ar-btn{font:700 12px var(--font-d);color:#fff;background:var(--s-acc);padding:7px 14px;border-radius:999px}
.app-reward{
  display:flex;align-items:center;gap:8px;
  font:700 12px var(--font-b);color:var(--s-acc);
  background:var(--s-acc-soft);border:1px solid rgba(15,107,88,.2);
  border-radius:12px;padding:9px 12px;
}
.chip-dot{width:8px;height:8px;border-radius:50%;background:var(--mint);flex:0 0 auto;animation:pulse 1.6s ease-in-out infinite}
.scr .chip-dot{background:var(--s-acc)} /* hero float-chips keep mint on their dark pill */
.app-promo{
  display:flex;flex-direction:column;gap:2px;
  background:var(--s-surface);border:1px solid var(--s-line);
  border-radius:14px;padding:12px 14px;
}
.app-promo strong{font-size:13px;font-weight:700}
.app-promo span{font-size:11.5px;color:var(--s-faint)}
.app-tabbar{
  margin-top:auto;display:flex;justify-content:space-between;
  border-top:1px solid var(--s-line);padding:10px 2px 0;
}
.app-tabbar span{font-size:9px;color:var(--s-faint);display:flex;flex-direction:column;align-items:center;gap:4px;flex:1;text-align:center}
.app-tabbar span::before{content:"";width:16px;height:16px;border-radius:5px;background:rgba(16,36,30,.16)}
.app-tabbar span.is-on{color:var(--s-acc);font-weight:700}
.app-tabbar span.is-on::before{background:var(--s-acc)}

/* scr-cart */
.cart-item,.mk-row,.tr-row,.rw-row{
  display:flex;align-items:center;gap:10px;
  padding:10px 0;border-bottom:1px solid var(--s-line);
}
.ci-main,.mk-main,.tr-main,.rw-main{flex:1;min-width:0}
.ci-main strong,.mk-main strong,.tr-main strong,.rw-main strong{display:block;font-size:13.5px;font-weight:700;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.ci-main span,.mk-main span,.tr-main span,.rw-main span{display:block;font-size:11.5px;color:var(--s-faint);margin-top:1px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.ci-price{font-size:13.5px;font-weight:700}
.cart-deliver{
  margin-top:auto;display:flex;align-items:center;gap:8px;
  font-size:12px;color:var(--s-sub);
  background:var(--s-surface);border:1px solid var(--s-line);border-radius:12px;padding:10px 12px;
}
.cart-deliver svg{width:14px;height:14px;fill:var(--s-acc);flex:0 0 auto}
.cart-deliver strong{color:var(--s-ink);font-weight:700}
.cart-total{display:flex;align-items:baseline;justify-content:space-between;padding-top:2px}
.cart-total span{font-size:13px;color:var(--s-sub)}
.cart-total strong{font:700 20px var(--font-d)}

/* scr-deliver */
.dl-addr{
  display:flex;align-items:center;gap:10px;
  background:var(--s-surface);border:1px solid var(--s-line);border-radius:14px;padding:12px 14px;
}
.dl-addr svg{width:16px;height:16px;fill:var(--s-acc);flex:0 0 auto}
.dl-addr div{flex:1;min-width:0}
.dl-addr div strong{display:block;font-size:13.5px;font-weight:700}
.dl-addr div span{display:block;font-size:11.5px;color:var(--s-faint)}
.dl-change{font:700 12px var(--font-d);color:var(--s-acc)}
.dl-opts{display:grid;grid-template-columns:1fr 1fr;gap:8px}
.dl-opt{
  background:var(--s-surface);border:1.5px solid var(--s-line);
  border-radius:14px;padding:11px 12px;
}
.dl-opt strong{display:block;font-size:13.5px;font-weight:700}
.dl-opt span{display:block;font-size:11px;color:var(--s-faint);margin-top:1px}
.dl-opt.is-on{border-color:var(--s-acc);background:var(--s-acc-soft)}

/* scr-track */
@keyframes route-in{to{stroke-dashoffset:0}}
.ripple{transform-origin:195px 60px;animation:ripple 2.8s ease-out infinite}
.ripple.r2{animation-delay:1.4s}
@keyframes ripple{0%{transform:scale(.5);opacity:.75}100%{transform:scale(2.6);opacity:0}}
.scr-live{display:flex;align-items:center;gap:6px;font:700 10px var(--font-d);letter-spacing:.12em;text-transform:uppercase;color:var(--s-acc)}
.live-dot{width:7px;height:7px;border-radius:50%;background:var(--s-acc);animation:pulse 1.6s ease-in-out infinite}
.track-map{flex:1;min-height:225;border-radius:16px;overflow:hidden;position:relative;border:1px solid var(--s-line)}
.track-map svg{position:absolute;inset:0;width:100%;height:100%}
.route-core{stroke-dasharray:300;stroke-dashoffset:300;animation:route-in 2.6s .4s cubic-bezier(.4,0,.2,1) forwards}
  .route-core{animation:none;stroke-dashoffset:0}
.eta-card{background:var(--s-surface);border:1px solid var(--s-line);border-radius:16px;padding:13px 14px;display:grid;gap:11px}
.eta-top{display:flex;align-items:center;justify-content:space-between}
.eta-top span{font-size:13.5px;font-weight:700}
.eta-min{font:700 20px var(--font-d);color:var(--s-acc)}
.eta-driver{display:flex;align-items:center;gap:10px}
.eta-driver div{flex:1;min-width:0}
.eta-driver div strong{display:block;font-size:13px;font-weight:700}
.eta-driver div span{display:block;font-size:11px;color:var(--s-faint)}
.eta-steps{display:flex;gap:6px}
.eta-steps span{
  flex:1;text-align:center;font-size:10px;color:var(--s-faint);
  border-top:2.5px solid rgba(16,36,30,.12);padding-top:6px;
}
.eta-steps .is-done{border-color:rgba(15,107,88,.45);color:var(--s-sub)}
.eta-steps .is-on{border-color:var(--s-acc);color:var(--s-acc);font-weight:700}

/* scr-markup */
.mk-client{display:flex;align-items:center;gap:10px;background:var(--s-surface);border:1px solid var(--s-line);border-radius:14px;padding:11px 13px}
.mk-client div{flex:1;min-width:0}
.mk-client div strong{display:block;font-size:13.5px;font-weight:700}
.mk-client div span{display:block;font-size:11.5px;color:var(--s-faint)}
.mk-profit{font-size:13.5px;font-weight:700;color:var(--s-acc)}
.mk-total{margin-top:auto;background:var(--s-surface);border:1px solid var(--s-line);border-radius:14px;padding:12px 14px;display:grid;gap:7px}
.mk-total>div{display:flex;align-items:baseline;justify-content:space-between}
.mk-total span{font-size:12.5px;color:var(--s-sub)}
.mk-total strong{font-size:15px;font-weight:700}
.mk-total .mk-yours span{color:var(--s-acc)}
.mk-total .mk-yours strong{font:700 19px var(--font-d);color:var(--s-acc)}
.mk-note{font-size:10.5px;color:var(--s-faint);text-align:center;margin-top:-4px}

/* scr-trade */
.tr-side{display:flex;flex-direction:column;align-items:flex-end;gap:4px}
.tr-star{font-size:12px;font-weight:700}
.tr-ver{font:700 9px var(--font-d);letter-spacing:.1em;text-transform:uppercase;color:var(--s-acc);background:var(--s-acc-soft);padding:3.5px 8px;border-radius:999px}

/* scr-card — the virtual card stays a dark object, as it is in real life */
.vcard{
  position:relative;aspect-ratio:1.586;border-radius:16px;overflow:hidden;
  background:linear-gradient(135deg,#0E4A3B,#17836B 55%,#1E9E82);
  padding:16px;display:flex;flex-direction:column;justify-content:space-between;
  box-shadow:0 12px 26px rgba(16,36,30,.22);
}
.vc-ripples span{
  position:absolute;border:1.5px solid rgba(255,255,255,.25);border-radius:50%;
  top:50%;right:-40px;transform:translateY(-50%);
}
.vc-ripples span:nth-child(1){width:110px;height:110px}
.vc-ripples span:nth-child(2){width:180px;height:180px;opacity:.6}
.vc-ripples span:nth-child(3){width:250px;height:250px;opacity:.3}
.vc-top img{height:19px;width:auto}
.vc-bottom{display:flex;align-items:center;justify-content:space-between;font-size:12px;color:rgba(255,255,255,.92)}
.vc-tag{font:700 9px var(--font-d);letter-spacing:.12em;text-transform:uppercase;background:rgba(255,255,255,.18);padding:4px 9px;border-radius:999px}
.rw-balance{display:flex;align-items:baseline;justify-content:space-between;padding:2px 2px 0}
.rw-balance span{font-size:12.5px;color:var(--s-sub)}
.rw-balance strong{font:700 22px var(--font-d);color:var(--s-acc)}
.rw-amt{font-size:13px;font-weight:700;color:var(--s-acc)}

/* ============================================================
   MOBILE PILLAR — app-shell experience (base)
   ============================================================ */

main{position:relative;z-index:1}

/* --- Hero --- */
.hero{padding:calc(64px + 7vh) 0 34px}
.hero-inner{padding-inline:clamp(20px,5.5vw,32px);max-width:1240px;margin-inline:auto;display:flex;flex-direction:column}
.hero-copy .lede{margin-top:clamp(22px,4vh,44px)} /* enough air to separate headline from body, without the hole */
.hero-trust{margin-top:26px;font-size:13.5px;color:var(--faint)}
.hero-trust strong{color:var(--sub);font-weight:700}
.hero-stage{position:relative;order:-1;margin:0 0 30px;display:flex;flex-direction:column;align-items:center;--ds:.92;
  --phone-half:calc(150px*var(--ds) + 8px)} /* .device-frame is 300px*--ds + 16px, centred */
.hero-halo{
  position:absolute;top:46%;left:50%;transform:translate(-50%,-50%);
  width:min(120vw,520px);aspect-ratio:1;border-radius:50%;
  background:radial-gradient(closest-side,rgba(30,158,130,.13),transparent 70%);
  pointer-events:none;
}
/* The hero phones hold still - home, about and download (Theo, 2026-09-11); on the
   home page the pills float instead. The old dev-float bob also moved the frame that
   measureFly()/measureCentre() read by up to 9px, and knocked the house off the
   title line. Do not put a float back on .device-hero. */
.float-chip{
  position:absolute;z-index:3;
  display:flex;align-items:center;gap:8px;
  background:rgba(8,30,23,.82);backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);
  border:1px solid var(--line2);border-radius:999px;
  padding:9px 15px;font:700 12.5px var(--font-b);color:var(--ink);
  box-shadow:0 12px 30px rgba(0,0,0,.35);
  white-space:nowrap;
  animation:chip-float 6s ease-in-out infinite alternate;
}
/* The destination pin owns the upper right of the map, so the tall pill sits left
   and only one pill is anchored right. Bands verified 360px-1920px.
   Pills hang off the PHONE's edge, not the stage's - the stage keeps its width while
   --ds shrinks the device, so a stage-anchored pill drifts off the phone entirely. */
.chip-a{top:20%;left:auto;right:calc(50% + var(--phone-half) - 91px*var(--ds))}
.chip-long{display:none} /* the full sentence duplicates the ETA card below it and overruns the phone on mobile */
.chip-short{display:inline}
.chip-b{top:45%;right:auto;left:calc(50% + var(--phone-half) - 130px*var(--ds))}
.chip-c{top:56%;left:auto;right:calc(50% + var(--phone-half) - 120px*var(--ds));bottom:auto}
@keyframes chip-float{to{transform:translateY(-8px)}}
/* Each hero pill floats on its own path, height and clock, so they never move as a
   pair (Theo: "float but not 100% identical"). The periods don't divide, so they
   drift in and out of step and never repeat together; the negative delays start
   them mid-float, apart, from the first frame. Transform only - the scroll loop
   writes their opacity, never their transform. Translation only, no rotation, so
   the text stays crisp. */
.chip-b{animation:chip-float-b 5.3s ease-in-out -1.7s infinite alternate}
.chip-c{animation:chip-float-c 7.1s ease-in-out -4.2s infinite alternate}
@keyframes chip-float-b{
  0%{transform:translate(0,0)}
  55%{transform:translate(2px,-7px)}
  100%{transform:translate(-1px,-11px)}
}
@keyframes chip-float-c{
  0%{transform:translate(0,0)}
  40%{transform:translate(-2px,-3px)}
  100%{transform:translate(1px,-7px)}
}

/* --- Brands strip --- */
.brands{padding:64px 0 58px}
.brands .h2{margin-top:2px}
.brand-grid{display:none}
.marquee{margin-top:34px;overflow:hidden;-webkit-mask-image:linear-gradient(90deg,transparent,#000 12%,#000 88%,transparent);mask-image:linear-gradient(90deg,transparent,#000 12%,#000 88%,transparent)}
.marquee-track{display:flex;width:max-content;animation:marq 28s linear infinite}
.marquee-group{display:flex;align-items:center;gap:44px;padding-right:44px}
.mq-logo{height:54px;width:auto;background:var(--card);border:1px solid var(--line);border-radius:14px;padding:10px 16px;box-sizing:content-box}
.mq-dot{width:6px;height:6px;border-radius:50%;background:var(--mint);opacity:.45}
@keyframes marq{to{transform:translateX(-50%)}}
.brands-sub{color:var(--sub);font-size:1rem;max-width:34em;margin-top:30px}
.brands-sub.center{margin-inline:auto;text-align:center}

/* --- Theatre (mobile carousel) --- */
.theatre-m{padding:56px 0 30px}
/* Two lines, never three: "Everything you need." then "Right where you need it." The second line needs
   11.53em, so under ~400px the size follows the screen width (23px at 320, 27px at 360, 29.6px from 400).
   The subline is held to one line the same way (Theo, 2026-09-15). */
.theatre-m .h2 b{display:block}
.theatre-m .h2{font-size:min(1.85rem, calc((100vw - 44px) / 11.8))}
.theatre-m .section-sub{white-space:nowrap;font-size:min(1.04rem, calc((100vw - 44px) / 17))}
.thm-scroller{
  display:flex;gap:16px;
  overflow-x:auto;scroll-snap-type:x mandatory;
  overscroll-behavior-x:contain;   /* a hard swipe at either end never turns into the browser's back gesture */
  padding:30px 20px 12px;
  scrollbar-width:none;
}
.thm-scroller::-webkit-scrollbar{display:none}
/* Touch screens: one swipe, one card. The browser's own sideways fling carried a hard flick two to four
   cards on and would not be stopped, so sideways drags are driven by site.js and the row only pans up
   and down natively (pinch zoom kept). Snapping off here, or it fights the scripted drag (2026-09-15). */
@media (pointer:coarse){ .thm-scroller{scroll-snap-type:none;touch-action:pan-y pinch-zoom} }
.thm-slide{
  flex:0 0 84%;max-width:330px;
  scroll-snap-align:center;
  /* mouse, trackpad and keyboard: stop at every card (touch screens are paged by site.js, below) */
  scroll-snap-stop:always;
  background:var(--card);border:1px solid var(--line);border-radius:var(--r-lg);
  padding:26px 20px 28px;
  display:flex;flex-direction:column;align-items:center;text-align:center;
  --ds:.7;
}
/* direct children only — these must never reach inside the app screen */
/* The step label carries the same six-step spine as the desktop act and the
   features page, so the three surfaces tell one story. More specific than
   .thm-slide>p (two classes beats one class + one element), so it wins. */
.thm-slide>.thm-step{font:700 11px var(--font-d);letter-spacing:.16em;text-transform:uppercase;color:var(--fcol,var(--acc));margin-top:18px}
/* the client's screenshots, shown whole (contain), never cropped */
.thm-shot{display:block;width:100%;height:100%;object-fit:contain;background:#fff}
.thm-slide>h3{font:700 1.3rem/1.25 var(--font-d);margin-top:7px;letter-spacing:-.01em}
.thm-slide>p{color:var(--sub);font-size:.96rem;margin-top:10px}
/* the slide says it in bullets (Theo, 2026-09-16). The slide's copy is centred, so the list is a LEFT-aligned
   block centred as a unit - centring the items themselves scatters the dots. Accent dot, not --fcol. */
.thm-slide>.thm-list{list-style:none;margin:10px auto 0;padding:0;display:grid;gap:7px;width:max-content;max-width:100%;text-align:left}
.thm-slide>.thm-list li{position:relative;padding-left:16px;color:var(--sub);font-size:.96rem;line-height:1.45}
.thm-slide>.thm-list li::before{content:"";position:absolute;left:0;top:.55em;width:6px;height:6px;border-radius:50%;background:var(--fcol,var(--acc))}
.thm-dots{display:flex;justify-content:center;gap:8px;margin-top:18px}
/* the same way out of the set as the desktop act's "View all features" (Theo, 2026-09-16) - the carousel had none */
.thm-more{margin-top:24px}
/* was rgba(255,255,255,.18) — white dots on a light page, so the inactive ones
   were invisible and the carousel looked like it had a single dot */
.thm-dots span{width:7px;height:7px;border-radius:50%;background:rgba(24,34,30,.16);transition:background .3s,transform .3s}
.thm-dots span.is-on{background:var(--acc);transform:scale(1.25)}

/* --- Switchboard --- */
.switch{padding:72px 0;transition:background .7s ease}
.switch-inner{max-width:1240px;margin-inline:auto;padding-inline:clamp(20px,5.5vw,32px)}
.switch[data-brand="buco"]{--sb:var(--buco)}
.switch[data-brand="timbercity"]{--sb:var(--timbercity)}
.switch[data-brand="citiwood"]{--sb:var(--citiwood)}
.switch[data-brand="hhl"]{--sb:var(--hhl)}
.switch-chips{display:flex;flex-wrap:wrap;gap:9px;margin-top:26px}
.switch-chips.center{justify-content:center}
.s-chip{
  display:flex;align-items:center;gap:8px;
  font:700 12.5px var(--font-d);color:var(--sub);text-transform:uppercase;letter-spacing:.05em;
  background:var(--card);border:1px solid var(--line);
  border-radius:999px;padding:10px 16px;
  transition:border-color .3s,color .3s,background .3s,transform .2s;
}
.s-chip:hover{transform:translateY(-1px);color:var(--ink)}
.s-chip.is-on{color:var(--ink);border-color:color-mix(in srgb,var(--sb) 55%,transparent);background:color-mix(in srgb,var(--sb) 12%,transparent)}
.s-dot{width:8px;height:8px;border-radius:50%}
.s-chip[data-brand="thuso"] .s-dot{background:var(--acc)}
.s-chip[data-brand="buco"] .s-dot{background:var(--buco)}
.s-chip[data-brand="timbercity"] .s-dot{background:var(--timbercity)}
.s-chip[data-brand="citiwood"] .s-dot{background:var(--citiwood)}
.s-chip[data-brand="hhl"] .s-dot{background:var(--hhl)}
.switch-stage{position:relative;margin-top:32px;display:grid;place-items:center}
.switch .section-sub{margin-inline:auto;max-width:46em}

/* WHY THUSO (2026-09-30, Chris: on the home page above the six steps) - phones, and desktop without the intro. The
   act's why beat replaces it under html.has-intro (below, with .switch). */
.why{padding:72px 0;text-align:center}
.why .section-sub{margin-inline:auto;max-width:38em}
.why-list{list-style:none;margin:26px auto 0;padding:0;max-width:24em;display:grid;gap:10px;text-align:left}
.why-list li{position:relative;padding-left:22px;font-weight:600;color:var(--ink)}
.why-list li::before{content:"";position:absolute;left:0;top:.5em;width:9px;height:9px;border-radius:50%;background:var(--acc)}
.why .why-more{margin-top:26px}
/* PARTNERS GRID (2026-09-14, mobile audit): the old flat brand-switch phone became the desktop act's four
   cards. Resting card, lit card (brand bar down the left edge, brand-tinted border and name) and sizes
   all follow .bact-card. site.js lights one at a time while the section is on screen. */
.brand-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;max-width:560px;margin:30px auto 0}
.brand-card{
  position:relative;display:flex;flex-direction:column;align-items:flex-start;gap:4px;
  padding:18px 16px 18px 20px;border-radius:19px;
  background:var(--card);border:1px solid var(--line);box-shadow:0 10px 30px rgba(12,60,48,.07);
  /* the reveal's own fade and rise are kept, with the lit-card changes added */
  transition:opacity .8s cubic-bezier(.2,.7,.2,1) var(--d,0s),transform .8s cubic-bezier(.2,.7,.2,1) var(--d,0s),
             border-color .45s ease,box-shadow .45s ease;
}
.brand-card img{width:48px;height:48px;object-fit:contain;border-radius:11px;margin-bottom:9px}
.brand-card .bc-name{font:700 17px var(--font-d);color:var(--ink);line-height:1.2;transition:color .45s ease}
.brand-card .bc-role{font:400 13px/1.35 var(--font-b);color:var(--faint)}
.brand-card[data-brand="buco"]{--bcol:var(--buco)}
.brand-card[data-brand="timbercity"]{--bcol:var(--timbercity)}
.brand-card[data-brand="citiwood"]{--bcol:var(--citiwood)}
.brand-card[data-brand="hhl"]{--bcol:var(--hhl)}
.brand-card::before{
  content:"";position:absolute;left:0;top:16px;bottom:16px;width:4px;
  border-radius:0 4px 4px 0;background:var(--bcol,var(--acc));opacity:0;transition:opacity .35s ease;
}
.brand-card.is-on{border-color:color-mix(in srgb,var(--bcol) 58%,transparent);box-shadow:0 16px 40px color-mix(in srgb,var(--bcol) 22%,transparent)}
.brand-card.is-on::before{opacity:1}
.brand-card.is-on .bc-name{color:color-mix(in srgb,var(--bcol) 72%,var(--ink))}
@media (min-width:600px){.brand-grid{grid-template-columns:repeat(4,minmax(0,1fr));max-width:1000px;gap:16px}}
/* The brand mark now pulses INSIDE a phone, the way the desktop act shows it.
   Same .pin-img hooks, so site.js setBrand() needs no change. */
.device-brand{--ds:.6}
.brand-screen{
  position:absolute;inset:8px;border-radius:calc(46px*var(--ds));
  overflow:hidden;display:grid;place-items:center;
  background:#FFFFFF;   /* no coloured glow around the mark (Theo, 2026-09-13) */
}
/* two classes, so these beat the bare .halo-N / .switch-pin sizes and their
   desktop overrides without needing !important */
.brand-screen .halo{border-color:rgba(12,40,32,.08)}   /* neutral, like the desktop act's phone */
.brand-screen .halo-1{width:104px;height:104px}
.brand-screen .halo-2{width:150px;height:150px}
.brand-screen .halo-3{width:198px;height:198px}
.brand-screen .switch-pin{display:block;width:78px;height:78px}
.brand-screen .pin-img{filter:drop-shadow(0 10px 20px rgba(10,40,32,.22))}
.bs-name{position:absolute;left:0;right:0;bottom:34px;height:46px;pointer-events:none}
.bs-n{
  position:absolute;inset:0;display:flex;flex-direction:column;
  align-items:center;justify-content:center;gap:2px;
  opacity:0;transform:translateY(6px);transition:opacity .45s ease,transform .45s ease;
  font:700 15px var(--font-d);color:var(--ink);
}
.bs-n em{font:700 11.5px var(--font-d);font-style:normal;color:var(--sub)}
.switch[data-brand="thuso"] .bs-thuso,
.switch[data-brand="buco"] .bs-buco,
.switch[data-brand="timbercity"] .bs-timbercity,
.switch[data-brand="citiwood"] .bs-citiwood,
.switch[data-brand="hhl"] .bs-hhl{opacity:1;transform:none}
.halo{
  position:absolute;border-radius:50%;
  border:1px solid color-mix(in srgb,var(--sb) 30%,transparent);
  transition:border-color .7s;
  animation:halo-pulse 3.6s ease-in-out infinite;
}
.halo-1{width:190px;height:190px}
.halo-2{width:280px;height:280px;animation-delay:1s;opacity:.7}
.halo-3{width:380px;height:380px;animation-delay:2s;opacity:.4}
/* the rings behind the house breathe (Theo, 2026-10-01: "used to have pulsing circles... doesnt function anymore" -
   it was running, but 4.5% over 5s reads as still). 10% over 3.6s, the rings a second apart. Shared by the partners
   section's own rings (.halo) on phones and no-intro desktops. */
@keyframes halo-pulse{0%,100%{transform:scale(1)}50%{transform:scale(1.10)}}
.switch-stage::before{
  content:"";position:absolute;width:min(92vw,420px);height:min(92vw,420px);border-radius:50%;
  background:radial-gradient(closest-side,color-mix(in srgb,var(--sb) 15%,transparent),transparent 72%);
  transition:background .7s;
}
.switch-pin{position:relative;width:132px;height:132px}
.pin-img{
  position:absolute;inset:0;width:100%;height:100%;object-fit:contain;
  opacity:0;transform:scale(.92);transition:opacity .5s,transform .5s;
  filter:drop-shadow(0 18px 34px rgba(0,0,0,.45));
}
.pin-img.is-on{opacity:1;transform:none}

/* --- First (ledger band) --- */
.first{padding:76px 0 82px;position:relative;overflow:hidden}
.first:not(.act-dark){background:transparent}
.first-head{max-width:560px}
/* The heading scales on 2.5vw up to a 43px ceiling, but this box was pinned at
   560px - so past ~1700px wide the line needed 571px and wrapped. Wider on
   desktop, where the type is at full size. Only a kicker and the h2 live here,
   so nothing is made harder to read by the wider measure. */
@media (min-width:1024px){.first-head{max-width:680px}}
.first-grid{display:grid;gap:34px;margin-top:44px}
.first-num{
  font:700 42px/1 var(--font-d);
  color:transparent;-webkit-text-stroke:1.5px color-mix(in srgb,var(--acc) 80%,transparent);
  display:block;
}
.first-item h3{font:700 1.25rem var(--font-d);margin-top:14px}
.first-item p{color:var(--sub);font-size:.98rem;margin-top:8px;max-width:30em}

/* --- Audiences --- */
.aud{padding:78px 0 30px}
.aud-grid{display:grid;gap:18px;margin-top:40px}
.aud-card{
  background:var(--card);border:1px solid var(--line);border-radius:var(--r-lg);
  padding:30px 26px 32px;
  transition:transform .35s,border-color .35s,box-shadow .35s,background .35s;
}
.aud-card:hover{transform:translateY(-6px);border-color:var(--line2);background:var(--card2);box-shadow:0 24px 60px var(--hov-sh)}
.aud-tag{font:700 10.5px var(--font-d);letter-spacing:.2em;text-transform:uppercase;color:var(--acc);margin-bottom:14px}
.aud-card h3{font:700 1.35rem/1.3 var(--font-d);letter-spacing:-.01em}
.aud-card p{color:var(--sub);font-size:.98rem;margin-top:12px}
/* phones and tablets (About, 2026-09-15): a card title never leaves one word alone ("Quote in minutes, not /
   evenings."), and tablets get two columns like the brand cards under them - one column of six wide cards was
   mostly empty. Capped at 1023, so the desktop three columns are untouched. */
@media (max-width:1023px){.aud-card h3{text-wrap:balance}}
@media (min-width:600px) and (max-width:1023px){.aud-grid{grid-template-columns:repeat(2,minmax(0,1fr))}}

/* --- Loyalty --- */
.loyal{padding:78px 0}
.loyal-inner{max-width:1240px;margin-inline:auto;padding-inline:clamp(20px,5.5vw,32px);display:flex;flex-direction:column;gap:40px}
.loyal-copy{order:-1}
.loyal-stage{position:relative;display:grid;place-items:center;padding:22px 0}
.cardstage{position:relative;display:flex;flex-direction:column;align-items:center;perspective:1100px}
.loyal-glow{
  position:absolute;width:min(110vw,480px);aspect-ratio:1;border-radius:50%;
  background:radial-gradient(closest-side,rgba(47,217,173,.15),transparent 70%);
  /* it reaches over the brand chips below the card: without this it ate every tap but the last chip's
     (found while re-flowing the chips, 2026-09-17). Decorative, so nothing here should take a tap. */
  pointer-events:none;
}
.tilt{
  position:relative;width:min(64vw,290px);border-radius:18px;
  transform-style:preserve-3d;will-change:transform;
  transition:transform .18s ease-out;
  box-shadow:0 46px 90px -34px rgba(0,0,0,.65),0 0 0 1px rgba(47,217,173,.16);
}
.tilt img{width:100%;height:auto;border-radius:18px;display:block}
.tilt-sheen{
  display:none;position:absolute;inset:0;border-radius:18px;pointer-events:none;
  background:linear-gradient(115deg,transparent 30%,rgba(255,255,255,.13) 46%,transparent 62%);
  background-size:260% 100%;background-position:var(--shx,80%) 0;
}
/* shown only where a pointer can drive it — on touch it was a static white haze */
.loyal-stage.has-tilt .tilt-sheen{display:block}
/* "Scan in store, at any till" — sits flat while the card tilts behind it */
.scan-chip{
  position:static;margin-top:16px; /* mobile: reads as a caption under the card */
  display:flex;align-items:center;gap:9px;white-space:nowrap;
  background:rgba(6,24,20,.92);border:1px solid rgba(47,217,173,.35);border-radius:13px;
  padding:11px 15px;color:#fff;font:700 12.5px var(--font-d);
  box-shadow:0 18px 40px -14px rgba(0,0,0,.5);
}
.scan-chip svg{width:17px;height:17px;color:var(--mint);flex:0 0 auto}

/* --- Finale --- */
/* The pre-footer CTA is a tinted PANEL floating on the page rather than a
   full-bleed stripe - with the footer now light too, the page stays one calm
   surface end to end and the green button carries the emphasis. */
.finale,.cta-band{padding:44px 0 56px}
.finale > .container,
.cta-band > .container{
  /* The still panel: what shows before the canvas paints, and what stays under
     reduced motion or without WebGL - the same ramp the flow runs through.
     MOBILE VALUES. 118deg over a phone-shaped panel puts most of its area in
     the back half of the ramp, so the panel read as one flat near-black. A
     steeper angle runs the ramp down the long side instead, and the stops are
     lifted. Lightest is #10604F: white contrast 7.5, so still far clear of 4.5.
     Desktop restores the original 118deg ramp in the min-width:1024 block. */
  background:linear-gradient(155deg,#257F80,#1B5F5A 50%,#14413E); /* brand-gradient family; lightest #257F80 holds white at 4.75 */
  border:1px solid rgba(255,255,255,.11);
  border-radius:28px;
  /* the container is edge-to-edge on a phone, which clipped the corners off;
     this keeps a gutter at every width and still centres under the 1240 cap */
  width:calc(100% - 32px);
  padding-block:clamp(52px,7vw,78px);
  /* Grounding, not a band. Measured against a no-shadow render, in green-channel
     units of darkening straight down from the panel edge:
         0 30px 70px .30   50 at the edge, still 23 at +45px, reaching 93px
                           -> a broad slab whose terminus reads as a LINE
         0 16px 40px .15   24 at the edge, gone by +45px
                           -> too faint; Theo read it as no shadow at all
         0 12px 30px .26   42 at the edge, gone by +45px, reaching 40px
         0 12px 30px .32   52 at the edge, gone by +45px, reaching 41px   <- this
     The line came from the long TAIL, not from the weight at the edge. .32 puts
     the weight back to where the original had it (52 vs 50) on LESS THAN HALF
     the reach, so it grounds the panel without a terminus to read as an edge.
     Opacity is the dial here; leave the 12px offset and 30px blur alone, they
     are what keep the reach at ~40px. */
  box-shadow:0 12px 30px rgba(20,65,62,.32);
  overflow:hidden;   /* keeps the canvas inside the rounded corners */
  /* All copy is white. No blade is allowed above the luminance where white
     holds 4.5, so nothing here is tinted or dimmed to compensate - that is the
     shader's job, not the copy's.
     Headings take their colour by INHERITANCE from body, which resolved
     var(--ink) to the page value long before this rule - inherited colour is a
     resolved colour, not a live var reference - so it has to be re-resolved. */
  color:var(--ink);
  --ink:#FFFFFF;
  --sub:#FFFFFF;
  --acc:#FFFFFF;
  --faint:#FFFFFF;
}
/* On this panel the emphasised word is WHITE like the rest of the heading.
   .grad-text paints a gradient into the glyphs and sets color:transparent, so
   both have to be undone - overriding the --grad-text token alone would still
   leave transparent text. */
.finale .grad-text,
.cta-band .grad-text{background:none;color:#FFFFFF}
/* the lede and the small print carry their weight, not a lighter colour */
.finale .lede,.cta-band .lede{opacity:.86}
.finale .dev-note,.cta-band .dev-note{opacity:.72}
/* The dark panels need no inversion any more. Apple's badge carries a white
   outline and Google's a grey one precisely so they hold on any background, so
   they are left bare. The old white-card treatment existed only because those
   were OUR pills; you may not put Apple's badge in your own container. */
/* Everything sits above the lava - EXCEPT the lava. This selector is more
   specific than .lava, so without the :not() it forced the canvas back into
   flow at its intrinsic pixel height, and the ResizeObserver fed that straight
   back in: canvas grows -> panel grows -> canvas grows. */
.finale > .container > *:not(.lava),
.cta-band > .container > *:not(.lava){position:relative;z-index:1}

/* --- the lava -----------------------------------------------------------
   Injected by JS, so no-WebGL and reduced-motion simply keep the still panel
   above. Fades up on its first painted frame rather than popping in. */
.lava{
  position:absolute;inset:0;z-index:0;
  display:block;width:100%;height:100%;
  border-radius:inherit;pointer-events:none;
  opacity:0;transition:opacity .8s ease;
}
.lava.is-lit{opacity:1}

.finale{position:relative;overflow:hidden}
.finale-glow{display:none} /* clipped by overflow:hidden -> visible seam on light */
.finale .container{position:relative}
.finale .lede{margin-inline:auto}

/* --- Footer --- */
.site-foot{
  position:relative;z-index:1;
  /* Colourless: the page's own atmosphere carries straight through, so the
     footer is the same paper as everything above it. The brand bar across the
     top is now the only divider it needs. */
  background:transparent;
  border-top:0;   /* the footer flows straight out of the page */
  /* the bottom clears the mobile Get the app bar AND the fade above it (was 104px - the last line sat
     under the fade, mobile audit 2026-09-14). Desktop overrides this; the bar is phones only. */
  padding:60px 0 calc(150px + env(safe-area-inset-bottom));
}
.foot-strip{display:flex;flex-direction:column;align-items:flex-start;gap:14px}
/* the same lockup at the same size as the header's */
.foot-logo img{height:30px;width:auto}
.foot-strip a,.foot-strip span{font:400 14.5px/1.4 var(--font-b);color:var(--sub);transition:color .2s}
.foot-strip a:hover{color:var(--acc)}
.foot-legal{border-top:1px solid var(--line);margin-top:30px;padding-top:22px;display:flex;flex-wrap:wrap;gap:10px 24px;justify-content:space-between;align-items:center}
.foot-legal p{color:var(--faint);font-size:13px}
/* the social handles (Chris, 2026-10-05): Facebook, Instagram, YouTube, TikTok, between the copyright and the credit */
.foot-social{display:flex;align-items:center;gap:10px}
.foot-social a{display:grid;place-items:center;width:38px;height:38px;border-radius:50%;border:1px solid var(--line);color:var(--sub);
  transition:color .2s,border-color .2s,transform .2s}
.foot-social a:hover{color:var(--acc);border-color:var(--acc);transform:translateY(-2px)}
.foot-social a:focus-visible{outline:2px solid var(--acc);outline-offset:3px}
.foot-social svg{width:17px;height:17px;fill:currentColor}
/* desktop: the icons dead centre between the copyright and the credit, whatever their widths */
@media (min-width:1024px){.foot-legal{display:grid;grid-template-columns:1fr auto 1fr}.foot-legal .foot-links{justify-self:end}}
.foot-links a{color:var(--sub)}
.foot-links a:hover{color:var(--acc)}

/* --- Mobile action bar --- */
/* The bar is now a column: a small handle sitting on top of the glass panel. The
   handle tucks the whole thing off the bottom edge and brings it back. */
.actionbar{
  position:fixed;left:12px;right:12px;bottom:calc(12px + env(safe-area-inset-bottom));z-index:45;
  display:flex;flex-direction:column;align-items:center;
  /* hidden on first paint, slides up once the user starts scrolling */
  transform:translateY(calc(100% + 26px));opacity:0;pointer-events:none;
  transition:transform .38s cubic-bezier(.2,.7,.2,1),opacity .28s ease;
}
.actionbar.is-shown{transform:none;opacity:1;pointer-events:auto}
/* out of the way while someone types in the contact form (Theo, 2026-09-15): browsers that push the page up for
   the keyboard put the bar straight onto the message box (103px of it on an iPhone SE). Back on blur. */
body:has(.c-form :focus) .actionbar{transform:translateY(calc(100% + 26px));opacity:0;pointer-events:none}
/* a soft fade behind the bar: whatever scrolls under it fades out before it reaches the tab, so the
   tab never sits on top of a heading (mobile review, 2026-09-14). Gone while the bar is tucked. */
.actionbar::before{
  content:"";position:absolute;z-index:-1;pointer-events:none;
  left:-12px;right:-12px;top:-40px;bottom:calc(-12px - env(safe-area-inset-bottom));
  background:linear-gradient(to bottom,color-mix(in srgb,var(--bg0) 0%,transparent),color-mix(in srgb,var(--bg0) 90%,transparent) 46px,color-mix(in srgb,var(--bg0) 96%,transparent));
  transition:opacity .3s ease;
}
.actionbar.is-tucked::before{opacity:0}
/* tucked: drop by everything except the handle, plus the bottom inset, so the handle
   ends up flush with the bottom edge and the panel is off screen */
.actionbar.is-shown.is-tucked{
  transform:translateY(calc(100% - 28px + 12px + env(safe-area-inset-bottom)));
}
.ab-panel{
  width:100%;display:flex;gap:10px;padding:10px;
  background:rgba(246,251,248,.93);backdrop-filter:blur(18px);-webkit-backdrop-filter:blur(18px);
  border:1px solid var(--line);border-radius:20px;
  box-shadow:0 18px 50px rgba(13,52,42,.2);
}
.ab-handle{
  width:74px;height:28px;flex:0 0 auto;margin-bottom:-1px;
  display:grid;place-items:center;cursor:pointer;
  background:rgba(246,251,248,.93);backdrop-filter:blur(18px);-webkit-backdrop-filter:blur(18px);
  border:1px solid var(--line);border-bottom:0;border-radius:14px 14px 0 0;
  box-shadow:0 -6px 16px rgba(13,52,42,.08);
}
.ab-handle:focus-visible{outline:2px solid var(--acc);outline-offset:2px}
/* chevron: points DOWN while the bar is up (tap to hide), UP once it is tucked */
.ab-chev{
  width:10px;height:10px;margin-top:-3px;
  border-right:2px solid var(--sub);border-bottom:2px solid var(--sub);
  transform:rotate(45deg);transition:transform .3s ease;
}
.actionbar.is-tucked .ab-chev{transform:rotate(-135deg);margin-top:3px}
.ab-btn{
  height:50px;border-radius:10px;
  display:flex;align-items:center;justify-content:center;gap:9px;
  font:700 15px var(--font-d);text-transform:uppercase;letter-spacing:.06em;
}
.ab-primary{flex:1;background:var(--grad);color:#FFFFFF;box-shadow:0 8px 22px rgba(13,91,73,.3)}

/* --- Small phones --- */
@media (max-width:560px){
  .scan-chip{padding:9px 12px;font-size:11.5px}
}

@media (max-width:1023px){
  /* .brands and .switch are ONE beat on desktop - the act folds them together so
     the four brands are shown once. On mobile they stayed two full sections, both
     headlined about the four brands, one straight after the other. Read them as a
     single block instead: .brands keeps the framing and the logos, .switch drops
     its duplicate heading and contributes the part .brands cannot - the live
     colour switch.
     Done in CSS, NOT by moving markup, because the desktop fold is gated on
     html.has-intro - and has-intro is never set under reduced-motion, Save-Data,
     no mix-blend-mode or no JS. On those desktop paths BOTH sections still render
     and each still needs its own heading. Moving the markup would break that. */
  /* The hero eyebrow wrapped to two lines on every phone: at 11px/.22em it needs
     454px and a 360px screen gives the eyebrow a 309px box. Shortened by one word
     in the markup and tightened here: 10px/.10em needs 300px, so it holds one line
     from 360px up. Do not widen the tracking again without re-measuring. */
  .hero-copy .kicker{font-size:10px;letter-spacing:.10em}
}

@media (max-width:380px){
  .thm-slide{--ds:.64}
  .store-badge{--bh:38px}
}

/* Mobile height ladder — the hero phone has to leave room for the kicker and the
   headline inside the first screen. Without this the headline itself was clipped
   on iPhone 12/13/14/15, iPhone X/11 Pro and 360x800 Android. Must come after the
   width rules above: equal specificity, source order decides. */
@media (max-width:1023px) and (max-height:940px){ .hero-stage{--ds:.78} }
@media (max-width:1023px) and (max-height:870px){ .hero-stage{--ds:.70} }
@media (max-width:1023px) and (max-height:820px){ .hero-stage{--ds:.64} }
@media (max-width:1023px) and (max-height:760px){
  .hero{padding-top:76px} /* the 7vh top pad was eating the headline's room */
  .hero-stage{--ds:.58;margin-bottom:22px}
  .hero-stage .dev-note{margin-top:10px}
}
@media (max-width:1023px) and (max-height:700px){ .hero-stage{--ds:.54} }
@media (max-width:1023px) and (max-height:640px){ .hero-stage{--ds:.46} }
@media (max-width:1023px) and (max-height:580px){ .hero-stage{--ds:.40} }

/* ============================================================
   DESKTOP PILLAR — cinematic theatre (min-width: 1024px)
   ============================================================ */
@media (min-width:1024px){

  .dt-only{display:revert !important}
  .mb-only{display:none !important}
  .head-cta.dt-only{display:flex !important}
  .head-nav.dt-only{display:flex !important}

  .cursor-glow{
    display:block;position:fixed;z-index:0;pointer-events:none;
    width:680px;height:680px;border-radius:50%;
    background:radial-gradient(closest-side,rgba(47,217,173,.075),transparent 70%);
    top:0;left:0;opacity:0;transition:opacity .6s;
    will-change:transform;
  }
  .cursor-glow.is-live{opacity:1}

  /* Header */
  .head-inner{height:76px}
  .head-logo img{height:33px}
  .store-badge{--bh:46px}
  .head-nav{display:flex;gap:34px}
  .head-nav a{font:400 14.5px var(--font-d);color:var(--sub);transition:color .2s;position:relative}
  .head-nav a::after{
    content:"";position:absolute;left:0;bottom:-6px;width:0;height:2px;border-radius:2px;
    background:var(--grad);transition:width .28s;
  }
  .head-nav a:hover{color:var(--ink)}
  .head-nav a:hover::after{width:100%}

  /* Hero — full-viewport cinema */
  .hero{padding:0;min-height:100vh;display:flex;align-items:center}
  .hero-inner{
    display:grid;grid-template-columns:1.08fr .92fr;align-items:center;gap:40px;
    width:100%;padding-top:100px;padding-bottom:60px;
  }
  /* bigger on desktop to fill the page (Theo, 2026-09-14): "build better." is ~5.7x the size wide,
     so 5.8rem still holds each line inside the 613px column at 1240 */
  .hero-copy .display{font-size:clamp(3.4rem,6.8vw,5.8rem)}
  .hero-copy .display br{display:revert}
  .hero-copy .lede{margin-top:22px}
  .hero-trust{margin-top:38px;font-size:17px}
  .hero-copy .store-pills{margin-top:34px;gap:14px}
  .hero-copy .store-badge{--bh:58px}
  .hero-stage{order:0;margin:0;--ds:1.02} /* grid: must reset the mobile order or the columns swap */
  .hero-halo{width:620px;top:50%}
  /* left / right / left zigzag, evenly spaced; clears the pin, the LIVE badge
     and the ETA card at both ends of the float, down to 1024x768 */
  .chip-a{top:20%;left:auto;right:calc(50% + var(--phone-half) - 110px*var(--ds))}
  .chip-long{display:inline}
  .chip-short{display:none}
  .chip-b{top:40%;right:auto;left:calc(50% + var(--phone-half) - 69px*var(--ds));bottom:auto}
  .chip-c{top:56%;left:auto;right:calc(50% + var(--phone-half) - 67px*var(--ds));bottom:auto}
  .float-chip{font-size:13px;padding:10px 17px}
  .dev-note{margin-top:20px}

  /* Brands — static grid, no slider on desktop */
  .brands{padding:96px 0 88px}
  .brand-grid.dt-only{
    display:grid !important;
    grid-template-columns:repeat(4,minmax(0,1fr));gap:22px;
    max-width:1240px;margin:48px auto 0;padding-inline:32px;
  }
  .bg-item{
    background:var(--card);border:1px solid var(--line);border-radius:22px;
    padding:32px 18px 26px;
    display:flex;flex-direction:column;align-items:center;gap:18px;
    transition:transform .3s,border-color .3s,background .3s;
  }
  .bg-item:hover{transform:translateY(-5px);border-color:var(--line2);background:var(--card2)}
  .bg-item img{height:64px;width:auto;object-fit:contain}
  .bg-item span{font:700 13.5px var(--font-d);letter-spacing:.08em;text-transform:uppercase;color:var(--sub)}
  .brands-sub{margin-top:40px;font-size:1.05rem}

  /* Theatre — sticky product stage */
  .theatre{position:relative}
  .th-grid{
    max-width:1240px;margin-inline:auto;padding-inline:32px;
    display:grid;grid-template-columns:44px minmax(0,1fr) 470px;gap:0 56px;
  }
  .th-rail{
    position:sticky;top:0;align-self:start;
    height:100vh;display:flex;flex-direction:column;justify-content:center;gap:18px;
  }
  .th-dot{
    display:flex;align-items:center;gap:10px;
    font:700 12px var(--font-d);color:var(--faint);
    transition:color .3s;
  }
  .th-dot::before{
    content:"";width:6px;height:6px;border-radius:50%;
    background:rgba(255,255,255,.18);transition:background .3s,transform .3s,box-shadow .3s;
  }
  .th-dot.is-on{color:var(--acc)}
  .th-dot.is-on::before{background:var(--acc);transform:scale(1.4);box-shadow:0 0 12px rgba(47,217,173,.8)}
  .th-dot:hover{color:var(--sub)}
  .th-chapters{display:block}
  .th-ch{
    min-height:100vh;display:flex;flex-direction:column;justify-content:center;
    max-width:560px;padding:8vh 0;
    opacity:.2;transition:opacity .5s ease;
  }
  .th-ch.is-on{opacity:1}
  .th-ch .lede{margin-top:18px}
  .th-stagecol{position:relative}
  .th-stage{
    position:sticky;top:0;height:100vh;
    display:flex;flex-direction:column;align-items:center;justify-content:center;
  }
  .device-stage{--ds:1.04}
  .stage-screen{
    opacity:0;transform:translateY(18px) scale(.985);
    transition:opacity .5s ease,transform .5s ease;
    pointer-events:none;
  }
  .stage-screen.is-active{opacity:1;transform:none}
  .th-stage .dev-note{margin-top:14px}

  /* Switchboard */
  .switch{padding:110px 0}
  .why{padding:110px 0}
  /* the stage is sized by the phone in it - no fixed height. .switch-inner no
     longer exists: one merged section, everything inside .container */
  .switch-stage{margin-top:38px}
  .switch-stage::before{width:560px;height:560px}
  .device-brand{--ds:.72}
  .brand-screen .halo-1{width:126px;height:126px}
  .brand-screen .halo-2{width:182px;height:182px}
  .brand-screen .halo-3{width:240px;height:240px}
  .brand-screen .switch-pin{width:94px;height:94px}
  .bs-name{bottom:44px}
  .bs-n{font-size:17px}
  .halo-1{width:230px;height:230px}
  .halo-2{width:350px;height:350px}
  .halo-3{width:480px;height:480px}
  .switch-pin{width:170px;height:170px}
  .switch-chips{margin-top:32px;gap:10px}
  .s-chip{padding:11px 18px}

  /* First */
  .first{padding:110px 0 120px}
  .first-grid{grid-template-columns:repeat(4,minmax(0,1fr));gap:48px 40px;margin-top:60px}
  .first-num{font-size:52px}

  /* Audiences */
  .aud{padding:110px 0 40px}
  .aud-grid{grid-template-columns:repeat(3,minmax(0,1fr));gap:26px;margin-top:52px}
  .aud-card{padding:36px 32px 38px}

  /* Loyalty */
  .loyal{padding:110px 0}
  .loyal-inner{flex-direction:row;align-items:center;gap:64px}
  .loyal-stage{flex:1;order:-1;padding:30px 0}
  .loyal-copy{flex:1;order:0}
  .tilt{width:330px}
  .cardstage{display:inline-block}
  .scan-chip{position:absolute;margin-top:0;right:-66px;bottom:26px} /* hangs past the card so its own "scan in store" art stays readable */

  /* Finale */
  .finale,.cta-band{padding:72px 0 92px}
  /* the original wide-panel ramp, unchanged - see the mobile note above */
  .finale > .container,
  .cta-band > .container{background:linear-gradient(118deg,#1F6E68,#19554F 48%,#14413E)}

  /* Footer */
  .site-foot{padding:56px 0 36px}
  /* one long strip: logo, email, then the legal links, spread across the width */
  .foot-strip{flex-direction:row;align-items:center;justify-content:space-between;gap:16px 28px}
  .foot-logo img{height:33px}
}

/* Wide cinema */
@media (min-width:1400px){
  .hero-inner{gap:64px}
  .hero-stage{--ds:1.1}
  .device-stage{--ds:1.1}
}

/* ============================================================
   HEIGHT-AWARE DEVICE SCALE (desktop)
   The phone is a fixed 620px-tall design. The hero and the theatre stage are
   locked to 100vh, so on a laptop (or any short viewport) the device outgrows
   the screen and gets clipped. These steps keep it inside the viewport.
   MUST stay after the width rules above — same specificity, source order wins.
   ============================================================ */
@media (min-width:1024px) and (max-height:960px){
  .hero-stage,.device-stage,.dl-stage{--ds:1}
}
@media (min-width:1024px) and (max-height:880px){
  .hero-stage,.device-stage,.dl-stage{--ds:.95}
}
@media (min-width:1024px) and (max-height:820px){
  .hero-stage,.device-stage,.dl-stage{--ds:.88}
}
@media (min-width:1024px) and (max-height:760px){
  .hero-stage,.device-stage,.dl-stage{--ds:.8}
  .hero{padding-top:0}
}
@media (min-width:1024px) and (max-height:700px){
  .hero-stage,.device-stage,.dl-stage{--ds:.72}
}
@media (min-width:1024px) and (max-height:640px){
  .hero-stage,.device-stage,.dl-stage{--ds:.64}
}


/* ============================================================
   INNER PAGES — shared components (mobile pillar base)
   ============================================================ */

.page-hero{padding:calc(64px + 9vh) 0 44px}
.page-hero .lede{margin-top:18px}
/* The Features title sits on TWO LINES AT EVERY WIDTH, one phrase per line (Theo, 2026-09-17: "must be on
   two lines on desktop"): "Everything you need." / "Right where you need it." It is the only page title
   with two phrases, and `:has(+ .feat)` is what picks that page out. */
.page-hero:has(+ .feat) .display.h-mix b{display:block}
.inline-link{color:var(--acc);border-bottom:1px solid rgba(47,217,173,.4)}
.inline-link:hover{border-bottom-color:var(--acc)}
.center-sub{margin-inline:auto;text-align:center}

/* Feature sections */
.feat{padding:54px 0}
.feat-grid{max-width:1240px;margin-inline:auto;padding-inline:clamp(20px,5.5vw,32px);display:flex;flex-direction:column;gap:36px}
.feat-stage{display:flex;justify-content:center;--ds:.78}
/* The phones on Features, About and Download, on phones (Theo, 2026-09-15). Each fits whole between the header and
   the Get the app bar: at their fixed sizes the Features phones (500px) were 23px too tall for that gap on most
   phones and 133px on an iPhone SE, the About/Download hero phone (574px) 97px and 207px, so every one showed cut
   off. 206px = the header (64) + the bar and its tab (110) + 32 of air; 400px is the floor and --fh-max each
   phone's old size. The frame is drawn off a length unit (--u): --ds is a plain number and can't come from svh. */
@media (max-width:1023px){
  .feat-stage .device-frame,.dl-stage .device-hero .device-frame{
    --fh:clamp(400px, calc(100vh - 206px), var(--fh-max));
    --u:calc((var(--fh) - 16px) / 620);
    width:calc(300 * var(--u) + 16px);height:var(--fh);border-radius:calc(46 * var(--u) + 8px);
  }
  .feat-stage .device-frame{--fh-max:500px}
  .dl-stage .device-hero .device-frame{--fh-max:574px}
  .feat-stage .device-island,.dl-stage .device-hero .device-island{top:calc(8px + 11 * var(--u));width:calc(92 * var(--u));height:calc(25 * var(--u))}
  .feat-stage .device-screen,.dl-stage .device-hero .device-screen{border-radius:calc(46 * var(--u))}
  /* TWO lines, one per phrase (Theo, 2026-09-17): "Everything you need." then "Right where you need it." The long
     phrase measures 11.41em, so the size follows the room the container leaves (24px at 320, 27px at 360, 32px at
     412) and only caps at the display size once there is room - an iPad keeps its 56px. Balance is the safety net
     if a phrase ever has to wrap anyway. The 98px of empty page before 01 comes down to 64. */
  :root{--page-title-fs:min(clamp(2.55rem, 5vw + 1.1rem, 4.9rem), calc((100vw - 2 * clamp(20px, 5vw, 32px)) / 11.7))}
  /* the Features title's own break now lives with `.page-hero` above, so it holds at every width. Every
     other page title holds ONE line: the cap here gives that line 11.7em, so a new title has to measure
     less than that at the mixed weight - "Here to help." is 6.1em, "Talk to us." 4.8em, and the 13.02em
     title FAQ carried for an hour on 2026-09-17 is why this note exists. */
  .page-hero .display.h-mix{font-size:var(--page-title-fs);text-wrap:balance}
  /* and the section titles never outgrow it (Theo, 2026-09-17): at their own 2.1rem they sat BIGGER than the page
     title once it came down to fit. They keep 2.1rem wherever the title has room to be larger - an iPad - and
     otherwise follow it, so the two sizes stay in step. */
  .feat h2.h1{font-size:min(2.1rem, var(--page-title-fs))}
  .page-hero:has(+ .feat){padding-bottom:10px}
  /* "No wallet? No / problem." at 360: the bold half stays in one piece (home page and Features) */
  #vcards .h-mix b{white-space:nowrap}
}
/* svh, not vh: the height with the browser bar showing (the vh line above is the fallback) */
@supports (height:1svh){
  @media (max-width:1023px){ .feat-stage .device-frame,.dl-stage .device-hero .device-frame{--fh:clamp(400px, calc(100svh - 206px), var(--fh-max))} }
}
.fstrip{display:flex;align-items:center;flex-wrap:wrap;gap:10px;margin-top:26px}
.fs-cell{background:var(--card);border:1px solid var(--line);border-radius:14px;padding:10px 16px}
.fs-cell span{display:block;font:700 10px var(--font-d);letter-spacing:.14em;text-transform:uppercase;color:var(--faint)}
.fs-cell strong{font:700 16px var(--font-d);color:var(--ink)}
.fs-profit strong{color:var(--acc)}
.fs-arrow{color:var(--faint);font-size:15px}

/* Mini cards (built for site) */
.mini{padding:66px 0 40px}
.mini-grid{display:grid;gap:14px;margin-top:38px}
.mini-card{background:var(--card);border:1px solid var(--line);border-radius:var(--r-md);padding:24px 22px;transition:transform .3s,border-color .3s,background .3s}
.mini-card:hover{transform:translateY(-4px);border-color:var(--line2);background:var(--card2)}
.mini-card h3{font:700 1.05rem var(--font-d)}
.mini-card p{color:var(--sub);font-size:.94rem;margin-top:8px}

/* Story (about) */

/* Group / brand cards */
.group{padding:66px 0 30px}
.brandcards{display:grid;gap:14px;margin-top:40px}
.brandcard{background:var(--card);border:1px solid var(--line);border-radius:var(--r-lg);padding:26px 24px;transition:transform .3s,border-color .3s,background .3s}
.brandcard:hover{transform:translateY(-4px);border-color:var(--line2);background:var(--card2)}
/* the logo IS the plaque: each brand's own rounded tile at the size the grey mat
   used to be, with no mat behind it (Theo, 2026-09-11 - "dull background") */
.bc-plaque{display:inline-flex;border-radius:14px;overflow:hidden}
.bc-plaque img{width:64px;height:64px;object-fit:contain;display:block}
.brandcard h3{font:700 1.2rem var(--font-d);margin-top:16px}
.brandcard p{color:var(--sub);font-size:.95rem;margin-top:8px}

/* Adaptive mark */
.adaptive{padding:60px 0 84px}
.adaptive-grid{display:grid;gap:32px}
.pin-row{display:flex;flex-wrap:wrap;justify-content:center;align-items:center;gap:16px;background:var(--card);border:1px solid var(--line);border-radius:var(--r-lg);padding:28px 22px}
.pin-row img{width:60px;height:60px;object-fit:contain;filter:drop-shadow(0 10px 22px rgba(0,0,0,.4))}

/* Values */
.values{padding:60px 0 40px}
.values-grid{display:grid;gap:14px;margin-top:38px}
.value-card{background:var(--card);border:1px solid var(--line);border-radius:var(--r-md);padding:22px}
.value-card h3{font:700 1.02rem var(--font-d);color:var(--acc)}
.value-card p{color:var(--sub);font-size:.94rem;margin-top:8px}

/* CTA band */
.cta-band{position:relative;overflow:hidden} /* matches .finale on the home page */
.cta-glow{display:none} /* clipped by overflow:hidden -> visible seam on light */
.cta-band .container{position:relative}

/* FAQ accordions */
.faqs{padding:8px 0 60px}
/* .faq-wrap sits ON the .container element, so it must NOT set max-width at all -
   doing so overrides the 1240px container and the rows run the whole window.
   The old 840px cap is simply gone; the container governs. */
.faq-group{font:700 13px var(--font-d);letter-spacing:.2em;text-transform:uppercase;color:var(--acc);margin:44px 0 16px}
.faq-group:first-child{margin-top:6px}
.acc{background:var(--card);border:1px solid var(--line);border-radius:18px;margin-bottom:12px;transition:background .3s,border-color .3s}
.acc[open]{background:var(--card);border-color:var(--line2)} /* stays white when open - no grey fill */
.acc summary{cursor:pointer;list-style:none;display:flex;align-items:center;justify-content:space-between;gap:16px;padding:20px 22px;font:700 1.1rem/1.45 var(--font-b);color:var(--ink)}
.acc summary::-webkit-details-marker{display:none}
.acc summary:hover{color:var(--acc)}
.acc-ic{position:relative;flex:0 0 auto;width:24px;height:24px;border-radius:50%;background:var(--acc-soft)}
.acc-ic::before,.acc-ic::after{content:"";position:absolute;top:50%;left:50%;background:var(--acc);border-radius:2px;transition:transform .3s}
.acc-ic::before{width:10px;height:2px;transform:translate(-50%,-50%)}
.acc-ic::after{width:2px;height:10px;transform:translate(-50%,-50%)}
.acc[open] .acc-ic::after{transform:translate(-50%,-50%) rotate(90deg)}
.acc-body{padding:0 22px 22px;color:var(--sub);font-size:1.04rem;line-height:1.7;max-width:62em}
.acc-body p + p{margin-top:12px}
.acc-body strong{color:var(--ink);font-weight:700}
/* FAQ on phones (Theo, 2026-09-15). A question never leaves one word alone ("How do I shop or view / materials?").
   Touch screens keep :hover on whatever was tapped, so a tapped question stayed green after closing it. And a
   question opened by a link (the home page's "Show me how", faq.html#faq-credit) holds still while it fades in:
   the page scrolled it into place while the reveal still had it 24px low, then it slid up under the header. */
@media (max-width:1023px){.acc summary{text-wrap:balance}}
@media (hover:none){.acc summary:hover{color:var(--ink)}}
.acc.rv:target{transform:none;transition:opacity .8s cubic-bezier(.2,.7,.2,1) var(--d,0s)}
@media (min-width:1024px){
  .faq-group{font-size:14px;margin:52px 0 18px}
  .acc summary{padding:24px 28px;font-size:1.26rem}
  .acc-body{padding:0 28px 26px;font-size:1.1rem}
  .acc-ic{width:28px;height:28px}
  .acc-ic::before{width:12px}
  .acc-ic::after{height:12px}
}

/* Forms */
.ea-form{margin-top:30px;max-width:540px}
.ea-label{display:block;font:700 11px var(--font-d);letter-spacing:.2em;text-transform:uppercase;color:var(--acc);margin-bottom:12px}
.ea-row{display:flex;flex-wrap:wrap;gap:10px}
.ea-input{flex:1;min-width:210px;background:rgba(255,255,255,.05);border:1px solid var(--line);border-radius:999px;padding:14px 22px;color:var(--ink);font:15px var(--font-b)}
.ea-input::placeholder{color:var(--faint)}
.ea-input:focus{outline:none;border-color:var(--acc);box-shadow:0 0 0 3px rgba(47,217,173,.18)}
.ea-btn{border:0}
.ea-consent{font-size:12px;color:var(--faint);margin-top:14px;line-height:1.55;max-width:46em}

.form-wrap{margin-top:54px;background:var(--card);border:1px solid var(--line);border-radius:var(--r-lg);padding:30px 24px 34px}
.c-form{display:grid;gap:18px;margin-top:24px}
.f-row{display:grid;gap:18px}
.f-field label{display:block;font:700 11px var(--font-d);letter-spacing:.16em;text-transform:uppercase;color:var(--faint);margin-bottom:9px}
.f-field input,.f-field select,.f-field textarea{width:100%;background:rgba(255,255,255,.05);border:1px solid var(--line);border-radius:14px;padding:13px 16px;color:var(--ink);font:15px var(--font-b)}
.f-field textarea{resize:vertical;min-height:120px}
/* touch screens: at 15px iOS zooms the whole page when a field is tapped (mobile audit, 2026-09-14) */
@media (pointer:coarse){.f-field input,.f-field select,.f-field textarea{font-size:16px}}
.f-field select{appearance:none;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8'%3E%3Cpath d='M1 1l5 5 5-5' stroke='%237C9B8F' stroke-width='2' fill='none' stroke-linecap='round'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right 16px center}
.f-field input:focus,.f-field select:focus,.f-field textarea:focus{outline:none;border-color:var(--acc);box-shadow:0 0 0 3px rgba(47,217,173,.18)}
.c-form .btn{justify-self:start;border:0}
/* phones: the send button spans the form - a 176px block off to the left was the one narrow thing on the page */
@media (max-width:599px){.c-form .btn{justify-self:stretch}}

/* Download page */
.dl-hero{padding-bottom:20px}
.dl-grid{display:flex;flex-direction:column;gap:44px}
.dl-stage{position:relative;display:flex;flex-direction:column;align-items:center;--ds:.9}
.qr-card{display:flex;align-items:center;gap:16px;margin-top:30px;background:var(--card);border:1px solid var(--line);border-radius:18px;padding:16px 20px}
.qr-img{width:104px;height:104px;flex:0 0 auto;border-radius:10px;display:block;background:#FFFFFF}
.qr-card p{font-size:14px;color:var(--sub);line-height:1.5}
.qr-card small{color:var(--faint);font-size:11.5px}
/* phones and tablets are the device you would scan WITH - the store buttons above do the job there (2026-09-14) */
@media (max-width:1023px){.qr-card{display:none}}

/* Download hero on phones (Theo, 2026-09-17): the phone comes first, above "Get the app", and the store
   buttons sit centred under the copy. The id keeps this off the About hero, which shares .dl-hero. */
@media (max-width:1023px){
  #dlHero .dl-stage{order:-1}
  #dlHero .store-pills{justify-content:center}
  /* with the phone first, the page-hero's own 9vh sat as dead space above it - the same air the home hero
     leaves under the header instead (Theo, 2026-09-17) */
  #dlHero{padding-top:calc(64px + clamp(12px, 3.4svh, 40px))}
}

/* Steps */
.steps{padding:64px 0 30px}
.steps-grid{display:grid;gap:14px;margin-top:38px}
.step-card{background:var(--card);border:1px solid var(--line);border-radius:var(--r-lg);padding:26px 24px}
.step-num{display:grid;place-items:center;width:42px;height:42px;border-radius:50%;background:var(--grad);color:#FFFFFF;font:700 18px var(--font-d);margin-bottom:16px;box-shadow:0 8px 20px rgba(47,217,173,.25)}
.step-card h3{font:700 1.15rem var(--font-d)}
.step-card p{color:var(--sub);font-size:.95rem;margin-top:8px}
/* phones and tablets (Download, 2026-09-15): the steps heading and the closing panel's heading balance their lines
   ("The whole yard, in / your pocket." left "in" hanging; the Features and About closing headings come out the
   same), and from iPad mini width the three steps sit side by side - three wide strips were mostly empty. Not
   below 744: at 700 the step titles wrap. Capped at 1023, so desktop is untouched. */
@media (max-width:1023px){.steps .h2,.cta-band .h2{text-wrap:balance}}
@media (min-width:744px) and (max-width:1023px){.steps-grid{grid-template-columns:repeat(3,minmax(0,1fr))}}

/* Contact */
.contact{padding:10px 0 40px}
.contact-grid{display:grid;gap:14px}
.contact-card{background:var(--card);border:1px solid var(--line);border-radius:var(--r-lg);padding:26px 24px;transition:transform .3s,border-color .3s,background .3s}
.contact-card:hover{transform:translateY(-4px);border-color:var(--line2);background:var(--card2)}
.contact-card h3{font:700 1.18rem var(--font-d)}
/* phones and tablets: no word left alone ("Quotes, accounts and / volume") */
@media (max-width:1023px){.contact-card h3{text-wrap:balance}}
.contact-card p{color:var(--sub);font-size:.95rem;margin-top:8px}
.contact-card .text-link{margin-top:14px}

/* Mid breakpoint — comfortable tablets */
@media (min-width:600px){
  .mini-grid{grid-template-columns:1fr 1fr}
  .brandcards{grid-template-columns:1fr 1fr}
  .values-grid{grid-template-columns:1fr 1fr}
  .f-row{grid-template-columns:1fr 1fr}
}

/* ============================================================
   INNER PAGES — desktop pillar
   ============================================================ */
@media (min-width:1024px){
  .page-hero{padding:190px 0 70px}
  .feat{padding:84px 0}
  .feat-grid{display:grid;grid-template-columns:minmax(0,1fr) 470px;align-items:center;gap:64px}
  .feat-stage{--ds:.92}
  .feat-flip .feat-grid{grid-template-columns:470px minmax(0,1fr)}
  .feat-flip .feat-stage{order:-1}
  .mini{padding:110px 0 60px}
  .mini-grid{grid-template-columns:repeat(4,minmax(0,1fr));gap:20px;margin-top:52px}
  .group{padding:100px 0 50px}
  .brandcards{grid-template-columns:repeat(4,minmax(0,1fr));gap:20px;margin-top:52px}
  .adaptive{padding:90px 0 124px}
  .adaptive-grid{grid-template-columns:1fr 1fr;gap:64px;align-items:center}
  .pin-row{padding:40px 30px;gap:26px}
  .pin-row img{width:72px;height:72px}
  .values{padding:90px 0 60px}
  .values-grid{grid-template-columns:repeat(4,minmax(0,1fr));gap:20px;margin-top:52px}
  .cta-band{padding:150px 0 170px}
  .faqs{padding:16px 0 90px}
  .dl-hero{padding-bottom:40px}
  .dl-grid{display:grid;grid-template-columns:1.05fr .95fr;align-items:center;gap:48px}
  .dl-stage{--ds:.98}
  .steps{padding:100px 0 50px}
  .steps-grid{grid-template-columns:repeat(3,minmax(0,1fr));gap:24px;margin-top:52px}
  .contact{padding:20px 0 70px}
  /* both columns share a height so the form ends level with the last card;
     the message box takes up whatever slack that creates */
  .contact .container{display:grid;grid-template-columns:400px minmax(0,1fr);gap:28px;align-items:stretch}
  .form-wrap{display:flex;flex-direction:column}
  .c-form{flex:1;display:flex;flex-direction:column}
  .c-form .f-msg{flex:1;display:flex;flex-direction:column}
  .c-form .f-msg textarea{flex:1;resize:none}
  .contact-grid{grid-template-columns:1fr;gap:18px}
  .form-wrap{margin-top:0;padding:42px 46px 48px;max-width:none}
}

/* ============================================================
   Hero fly-in — the app loads, scrubbed inside the hero
   Gated by <html class="has-intro"> (inline head script; never
   present for no-JS, reduced-motion, Save-Data or browsers
   without multiply blending — those get the normal hero).
   The phone opens centred on its pulsing mark (on desktop with
   the title card either side of it), the products fly into it
   while the loader fills, the Materials screen fills in, then the
   phone moves across and the copy arrives. The hero pins inside
   a tall wrapper while scroll drives all of it.
   ============================================================ */
.hero-fly,.hero-film-ui{display:none}
/* The fly-in replaced the truck film (2026-09-11). A fixed layer ABOVE the page -
   main is z-index 1, this is 5, the header 50 - so the products pass over the phone
   and shrink into its screen. Every .fly is positioned by the scroll loop, which
   writes transform AND opacity on every frame; nothing else may touch either. */
html.has-intro .hero-fly{
  display:block;position:fixed;inset:0;z-index:5;pointer-events:none;overflow:hidden;
}
.fly{
  position:absolute;left:0;top:0;height:auto;max-width:none;width:var(--fw,220px);
  opacity:0;will-change:transform,opacity;transform-origin:50% 50%;
  /* the shadow is the browser's, not baked into the photo, so it travels with the
     item and never implies a floor for something that is flying */
  filter:drop-shadow(0 22px 26px rgba(10,40,32,.20)) drop-shadow(0 6px 10px rgba(10,40,32,.12));
}
/* The title card: one line split either side of the phone while it loads, desktop
   only - on mobile the phone fills the width and there is no room beside it.
   Placed and sized by measureFly() from the phone's loading pose (--tt-x, --tt-y,
   --tt-size); the scroll loop writes each half's opacity and transform every frame.
   It sits first in the layer, so the products fly in front of it. */
.hero-title{display:none}
@media (min-width:1024px){
  html.has-intro .hero-title{
    display:block;position:absolute;inset:0;margin:0;
    font:700 var(--tt-size,64px)/1.02 var(--font-d);letter-spacing:-.02em;color:var(--ink);
  }
  /* rises in just after the phone, once the boot hold lifts (see below) */
  html.has-intro:not(.hero-boot) .hero-title{animation:tt-in .9s cubic-bezier(.2,.8,.2,1) .18s both}
  .ht-l,.ht-r{position:absolute;top:var(--tt-y,50%);white-space:nowrap;opacity:0;will-change:transform,opacity}
  .ht-l{right:var(--tt-x,60%);text-align:right}
  /* the same gradient the hero heading puts on "better." */
  .ht-r{
    left:var(--tt-x,60%);text-align:left;
    background:var(--grad-text);-webkit-background-clip:text;background-clip:text;color:transparent;
  }
}
@keyframes tt-in{from{opacity:0;transform:translateY(18px)}to{opacity:1;transform:none}}
/* The boot hold. Added by the head gate on a fresh visit and lifted by the hero
   code two frames after its first render. Without it the first frame the script
   painted eased into place: the phone appeared showing the Materials screen and
   the pills, and the splash faded in over them - a visible flash on every first
   visit. Held, that first frame is painted exactly, with nothing transitioning,
   and the phone and title wait so they arrive as one entrance whatever the
   script's timing. The !important is needed: the scroll loop writes the stage's
   opacity inline. */
html.hero-boot .hero-wrap .hero-stage{opacity:0 !important}
html.hero-boot .hero-title{opacity:0}
html.hero-boot .house-screen,
html.hero-boot .house-screen::before,
html.hero-boot .hs-img,
html.hero-boot .hs-name,
html.hero-boot .hs-halo,
html.hero-boot .hs-load,
html.hero-boot .hs-load i,
html.hero-boot .hero-wrap .float-chip,
html.hero-boot .hero-wrap .dev-note{transition:none !important}
/* the entrance once the hold lifts: an animation, because it has to win over the
   inline opacity for its half second; backwards fill, so it lets go after */
html.has-intro:not(.intro-seen):not(.hero-boot) .hero-wrap .hero-stage{animation:stage-in .55s ease backwards}
@keyframes stage-in{from{opacity:0}to{opacity:1}}
html.has-intro .hero-wrap{height:300vh}
@media (min-width:1024px){html.has-intro .hero-wrap{height:660vh}} /* 300vh film + 360vh act: brands, WHY THUSO, then features (data-brand-run in index.html must match) */
html.has-intro .hero-wrap .hero{position:sticky;top:0}
html.has-intro .hero-wrap .hero-stage{transition:none}
/* The film drives window.scrollTo every frame; behavior:auto defers to the
   page's smooth scrolling, which would restart the animation each frame and
   leave the page standing still. While the film owns the page, scrolling is
   instant. */
html.has-intro{scroll-behavior:auto}
/* the hero's own reveal must never animate over the film's choreography */
html.has-intro .hero-wrap .hero-copy{transition:none}
html.has-intro .hero-wrap .film-copy .rv{transition:none}
/* pre-JS first paint on a fresh visit: phone hidden; on desktop the copy
   waits on the right, on mobile the film plays alone and the copy waits
   hidden (the engine takes over with exact values immediately) */
html.has-intro:not(.intro-seen) .hero-wrap .hero-stage{opacity:0}
/* the copy arrives AFTER the products have loaded - at every width now */
html.has-intro:not(.intro-seen) .hero-wrap .hero-copy{opacity:0}
/* portrait keeps the headline on screen from the first paint - only the
   phone waits offstage */
html.has-intro .hero-film-ui{
  display:flex;position:fixed;left:clamp(20px,6vw,96px);
  bottom:calc(clamp(18px,6vh,60px) + env(safe-area-inset-bottom));
  /* centre, not flex-start: the console is the wider child, so a left-aligned cue
     sat 16px off its centre. The console keeps its own left edge either way. */
  z-index:60;flex-direction:column;align-items:center;gap:12px;
}
.hf-cue{
  display:flex;align-items:center;gap:8px;
  font:700 13.5px/1 var(--font-d);letter-spacing:.06em;color:#31524A;
}
.hf-cue svg{width:13px;height:13px;color:#31524A;animation:ipNudge 2.2s ease-in-out infinite}
@keyframes ipNudge{0%,100%{transform:translateY(-1px)}50%{transform:translateY(2px)}}
.intro-prog{
  display:flex;align-items:center;gap:10px;padding:10px 14px;border-radius:999px;
  background:rgba(255,255,255,.74);border:1px solid var(--line);
  backdrop-filter:blur(6px);
}
.ip-toggle{width:24px;height:24px;display:grid;place-items:center;color:var(--ink);opacity:.82;border-radius:50%}
.ip-toggle:hover{opacity:1}
.ip-toggle svg{width:11px;height:11px;fill:currentColor}
.ip-pause{display:none}
.hero-film-ui.is-rolling .ip-play{display:none}
.hero-film-ui.is-rolling .ip-pause{display:block}
.ip-dots{display:flex;gap:6px}
.ip-dots i{width:22px;height:4px;border-radius:999px;background:rgba(24,34,30,.15);transition:background .3s ease}
.ip-dots i.is-on{background:rgba(20,28,25,.85)}
.hf-skip{
  margin-left:6px;font:700 12px/1 var(--font-d);color:var(--sub);text-transform:uppercase;
  letter-spacing:.06em;padding:7px 12px;border-radius:999px;
  border:1px solid var(--line);transition:color .2s,border-color .2s;
}
.hf-skip:hover{color:var(--ink);border-color:var(--line2)}
/* The film chrome is position:fixed at z-index 60; the mobile menu is 49, so the
   cue and the scrubber printed through the open menu. Hide it while the menu is
   up. NOTE: use visibility, NOT opacity - the scroll loop owns ui.style.opacity
   and writes it inline every frame, so an opacity rule here would never win. */
html.has-intro body.no-scroll .hero-film-ui{visibility:hidden}
@media (max-width:1023px){
  /* the phone is centred during the intro, so the controls sit at the bottom.
     The mobile action bar only appears past introOff + 150, so they never meet. */
  html.has-intro .hero-film-ui{
    left:50%;transform:translateX(-50%);align-items:center;
    top:auto;bottom:calc(clamp(14px,3vh,28px) + env(safe-area-inset-bottom));
  }
  .hf-cue{font-size:12.5px}
  .intro-prog{padding:8px 12px;gap:8px}
  .ip-dots i{width:18px}
}

/* ============================================================
   Reduced motion
   ============================================================ */
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  .hero-fly,.hero-film-ui{display:none !important}
  .rv,.page-hero .rv{transition:none;opacity:1;transform:none;animation:none}
  .aurora,.float-chip,.tilt,.chip-dot,.live-dot,.halo{animation:none !important}
  .marquee-track{animation:none}
  .ripple{animation:none;opacity:.35}
  .stage-screen,.th-ch,.pin-img,.mob-links a,.mob-menu-foot,.mob-menu{transition-duration:.01s;transition-delay:0s !important}
}


/* ============================================================
   BRAND ACT — the pinned hero's second act (desktop only)
   The phone glides from the right column to the middle of the
   screen; the four houses gather two a side; the mark inside the
   phone wears each brand's colours in turn and that brand's card
   lights up with it. Every position is scroll-scrubbed and
   reversible, exactly like the film before it.
   Folds the old .brands + .switch sections into one beat so the
   four brands are shown once, not three times. Mobile is
   untouched and keeps both original sections.
   ============================================================ */
.bact{display:none}
.house-screen{display:none}
/* Desktop-only, like the house screen. Without a base rule it inherited
   .device-screen's white background at full opacity and sat over the hero
   phone on mobile as a blank white panel. */
.feat-screen{display:none}

@media (min-width:1024px){
  /* the two sections this act replaces */
  html.has-intro .brands,
  html.has-intro .switch{display:none}
  html.has-intro .why{display:none}   /* the act's why beat carries it beside the phone */

  /* the act writes opacity on these directly; the float keyframe animates
     transform only, so the two never fight */
  html.has-intro .hero-wrap .float-chip,
  html.has-intro .hero-wrap .dev-note{transition:opacity .25s linear}

  /* the phone outranks the act layer, so a card or a line of copy can never
     be painted over it while it is crossing the screen */
  html.has-intro .hero-wrap .hero-stage{position:relative;z-index:4}

  html.has-intro .bact{
    display:block;position:absolute;inset:0;z-index:2;
    opacity:0;pointer-events:none;
    /* set by JS from the live phone box so everything hangs off the
       real centre rather than a guessed one */
    --ph-half:280px;   /* half the SCALED phone height */
    --ph-halfw:140px;  /* half the SCALED phone width  */
    /* wide enough for every subline on ONE line (Theo, 2026-10-01): the longest, "Handles, hinges & locks", is
       152px of text in 50px of padding, so the floor is 236; capped by the room beside the phone, not the phone's
       own width, which at 1530 squeezed the cards to 200 and wrapped two of them */
    --bc-w:min(clamp(236px,18.4vw,272px),calc(50vw - var(--ph-halfw) - 90px));
  }
  html.has-intro .bact .kicker{margin:0 0 6px}
  html.has-intro .bact .h2{margin:0;font-size:clamp(1.5rem,2.5vw,2.3rem);line-height:1.1}

  .bact-head{
    position:absolute;left:50%;
    bottom:calc(50% + var(--ph-half) - var(--ph-drop,0px) + 46px);
    width:min(980px,90vw);text-align:center;
    /* it is anchored to where the phone LANDS, so while the phone is still on
       its way down it would sit across it - everything waits for the landing
       and the descent belongs to the phone alone */
    opacity:0;transform:translate(-50%,-10px);
    transition:opacity .55s ease,transform .55s cubic-bezier(.22,.9,.3,1);
  }
  .bact.cards-in .bact-head{opacity:1;transform:translate(-50%,0)}

  /* --- the four houses, two a side --- */
  .bact-card{
    position:absolute;width:var(--bc-w);
    display:flex;flex-direction:column;align-items:flex-start;gap:4px;
    padding:21px 23px 21px 27px;border-radius:19px;
    background:var(--card);border:1px solid var(--line);
    box-shadow:0 10px 30px rgba(12,60,48,.07);
    font:inherit;text-align:left;cursor:pointer;-webkit-appearance:none;appearance:none;
    /* Parked at the phone's own centre, where the phone hides them outright:
       the card layer sits UNDER the phone, so they need no fade - they simply
       come out from behind its edges. --tx/--ty are measured live in JS, so
       this works for any number of cards. visibility keeps a parked card out
       of the tab order. */
    visibility:hidden;opacity:0;
    transform:translate(var(--tx,0px),var(--ty,0px)) scale(var(--ts,.86));
    /* going back in, the card fades over the last of its travel, so the slow end of the ease never
       leaves a sliver showing past the phone's edges (Theo, 2026-09-14) */
    transition:visibility 0s linear .6s,opacity .22s ease .14s,border-color .45s ease,
               background .45s ease,box-shadow .45s ease,
               transform .6s cubic-bezier(.24,.86,.32,1);
  }
  /* dealt out one after another; pulled back in together */
  .bact.cards-in .bact-card{
    visibility:visible;pointer-events:auto;transform:none;opacity:1;
    transition:visibility 0s,opacity .1s ease,border-color .45s ease,background .45s ease,
               box-shadow .45s ease,transform .6s cubic-bezier(.24,.86,.32,1);
    transition-delay:calc(var(--i,0) * 75ms);
  }

  /* THE PHONE IS MOVING - no card may be on screen.
     The retreat is a 600ms transition and a flick can cross the whole gap
     between the two beats in far less than that, so the phone would leave with
     a card still animating over the hero. This cuts them dead, with no
     transition to play out. It sits AFTER .cards-in deliberately: equal
     specificity, so source order decides and the cut wins.
     At any normal scroll speed the sequenced retreat has already finished by
     the time this could apply, so it never actually fires; on a flick it
     happens far too fast to see. */
  .bact.cards-cut .bact-card,
  .bact.cards-cut .feat-card{
    transition:none !important;
    visibility:hidden;pointer-events:none;opacity:0;
    transform:translate(var(--tx,0px),var(--ty,0px)) scale(var(--ts,.86));
  }
  /* hover wears the brand, the same 60 % border / 12 % tint the pills use */
  .bact.cards-in .bact-card:hover{
    border-color:color-mix(in srgb,var(--bcol,var(--acc)) 60%,transparent);
    background:var(--card); /* white inside; the brand shows in the edge and the glow */
    box-shadow:0 16px 38px color-mix(in srgb,var(--bcol,var(--acc)) 18%,transparent);
    transform:translateY(-2px);
  }
  .bact-card:focus-visible{outline:2px solid var(--acc);outline-offset:3px}
  .bact-card img{width:52px;height:52px;object-fit:contain;border-radius:11px;margin-bottom:9px}
  .bc-name{font:700 18.5px var(--font-d);color:var(--ink);line-height:1.2}
  .bc-role{font:400 13px/1.35 var(--font-b);color:var(--faint);white-space:nowrap}

  .bc-l{right:calc(50% + var(--ph-halfw) + 42px)}
  .bc-r{left:calc(50% + var(--ph-halfw) + 42px)}
  .bc-t{bottom:calc(50% - var(--ph-drop,0px) + 14px)}
  .bc-b{top:calc(50% + var(--ph-drop,0px) + 14px)}

  /* the lit card wears its own brand, the same treatment the
     switchboard chips used */
  .bact-card[data-brand="buco"]{--bcol:var(--buco)}
  .bact-card[data-brand="timbercity"]{--bcol:var(--timbercity)}
  .bact-card[data-brand="citiwood"]{--bcol:var(--citiwood)}
  .bact-card[data-brand="hhl"]{--bcol:var(--hhl)}
  /* the selected card is marked with a bar down its left edge, in its own brand */
  .bact-card::before{
    content:"";position:absolute;left:0;top:16px;bottom:16px;width:4px;
    border-radius:0 4px 4px 0;background:var(--bcol,var(--acc));
    opacity:0;transition:opacity .35s ease;
  }
  .bact.cards-in .bact-card.is-on::before{opacity:1}
  .bact.cards-in .bact-card.is-on{
    border-color:color-mix(in srgb,var(--bcol) 58%,transparent);
    background:var(--card);
    box-shadow:0 16px 40px color-mix(in srgb,var(--bcol) 22%,transparent);
    transform:translateY(-3px);
  }
  .bact-card.is-on .bc-name{color:color-mix(in srgb,var(--bcol) 72%,var(--ink))}

  /* sits between the tracking map and the house: the phone wears the theatre's
     first screen as it crosses to the features slot, so at the swap both
     phones show the same thing and the change cannot be seen */
  html.has-intro .feat-screen{display:block;opacity:0;z-index:2;transition:opacity .35s ease;pointer-events:none}
  html.has-intro .feat-screen.is-shown{opacity:1}
}

/* ---- The mark inside the phone -------------------------------------------
   The pulsing Thuso mark. It used to live only in the desktop brand act; since
   2026-09-11 it also OPENS the hero at every width as the splash the products load
   into, so it is lifted out of the desktop media query. Visibility is still
   class-driven (.is-shown). NEVER write its opacity inline - see brandActReset(). */
html.has-intro .house-screen{
  display:grid;position:absolute;inset:8px;z-index:3;
  border-radius:calc(46px*var(--ds));overflow:hidden;
  place-items:center;align-content:center;gap:calc(30px*var(--ds));
  background:var(--card);pointer-events:none;
  /* opaque: switched by class, never crossfaded against another screen */
  opacity:0;transition:opacity .4s ease;
}
html.has-intro .house-screen.is-shown{opacity:1}

.house-screen::before{
  content:"";position:absolute;width:132%;aspect-ratio:1;border-radius:50%;
  background:radial-gradient(circle,color-mix(in srgb,var(--hcol,var(--acc)) 22%,transparent) 0%,transparent 68%);
  transition:background .55s ease;
}
.hs-halo{
  grid-area:1/1;position:relative;display:block;border-radius:50%;
  border:calc(1px*var(--p3k,1)) solid color-mix(in srgb,var(--hcol,var(--acc)) 34%,transparent);
  background:radial-gradient(closest-side,color-mix(in srgb,var(--hcol,var(--acc)) 13%,transparent),transparent 74%);
  transition:border-color .55s ease,background .55s ease;
  animation:halo-pulse 3.6s ease-in-out infinite;
  pointer-events:none;
}
.hs-halo-1{width:calc(150px*var(--ds));height:calc(150px*var(--ds))}
.hs-halo-2{width:calc(212px*var(--ds));height:calc(212px*var(--ds));opacity:.7;animation-delay:1s}
.hs-halo-3{width:calc(268px*var(--ds));height:calc(268px*var(--ds));opacity:.42;animation-delay:2s}

.hs-img{
  grid-area:1/1;position:relative;z-index:1;
  width:calc(150px*var(--ds));height:calc(150px*var(--ds));object-fit:contain;
  opacity:0;transform:scale(.9);
  transition:opacity .5s ease,transform .5s ease;
  /* DEPTH, not decoration (Theo, 2026-10-01, after three tries at rings: "still not a huge fan"; the screen "was flat
     and boring"): a soft neutral shadow under the mark so it sits on the screen (the icons carry alpha, so it follows
     the shape), and a slow float on the separate translate property so the crossfade's transform and the bump/pop
     scale are untouched. Never a coloured glow (2026-09-13). */
  filter:drop-shadow(0 calc(12px*var(--ds)) calc(16px*var(--ds)) rgba(12,40,32,.17));
}
/* THE MARK, ANIMATED - Erin's icon animation itself (2026-10-01), played on the phone's white screen; the clip's
   background is white too, so it drops in seamlessly. On the hero's house beat (both platforms) a cut that starts past
   the assembly plays from zero at 0.75x, so the light sweeps through the mark just before the line splits:
   assets/video/thuso-mark-hero-720.mp4 on desktop, -540 on phones, never seeked (Pages ignores Range requests). No
   poster and no background on the video: until it has a frame it is transparent, and the mark image underneath is what
   shows. The drawn flash that stood here for an hour (radial arcs) was not the clip; Theo: "it doesnt match the video".
   The act's Meet Thuso beat looped a cut of it too, until Theo asked for another screen there (.hs-shot below). */
/* 1.3x (Theo, 2026-10-01: at 1.5x "the icon becomes very big at the end"): the mark rests at ~87pu and Erin's growth
   takes it to ~134pu - a third bigger than the original 96, not half the screen. Played at 0.75x speed (site.js). */
.mk-video{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;transform:scale(1.3);pointer-events:none;
  visibility:hidden;transition:visibility 0s linear .5s}
/* THE CLIP EXISTS ONLY WHILE ITS MOMENT IS ON SCREEN (2026-10-01). Chrome always draws a <video> on a layer of its
   own, and everything stacked above it in the phone's screen is then split onto layers too - which is how the
   screen's corners came to depend on a step Theo's Chrome got wrong after a scroll back. Hidden, the video is not
   painted at all, so the rest of the time the screen draws as one flat picture, as it did before the clip. It shows
   the moment the house does (desktop .is-on, phones .mh-on) and goes 0.5s after it, once the house has faded out.
   Never while the act owns the phone (a visible child would show through the hidden house). */
.p3-screen:not(.act-owns) .p3-house.is-on .mk-video,.p3-screen:not(.act-owns) .p3-house.mh-on .mk-video{visibility:visible;transition:none}
/* THE MEET THUSO SCREEN (2026-10-01): the app's welcome screen (index.html .hs-shot) over the whole house screen while
   the copy stands. It carries its own status bar, so it sits over .p3-sb (z-index 2) as well as the mark. */
.house-screen .hs-shot{position:absolute;inset:0;z-index:3;display:block;width:100%;height:100%;object-fit:cover;border-radius:inherit;opacity:0;transition:opacity .45s ease;pointer-events:none}
.house-screen.why-on .hs-shot{opacity:1}
/* Written every frame by the hero's scroll loop, 0 otherwise:
   --bump  the house gives a little as each product lands in it
   --pop   loading done - the house grows and settles back (0 -> 1 -> 0); the rings
           go with it at half the stretch, so the outer one stays inside the screen
   The separate scale property, so neither fights nor waits on the transform
   transition above, or on the rings' pulse animation (which is transform too). */
.hs-img.is-on{opacity:1;transform:none;scale:calc(1 + var(--bump,0) + var(--pop,0) * .2)}
.house-screen .hs-halo{scale:calc(1 + var(--pop,0) * .1)}
.hs-name{
  grid-area:2/1;position:relative;margin:0;
  font:700 calc(17px*var(--ds)) var(--font-d);
  color:var(--hcol,var(--acc));transition:color .5s ease;
}
/* what the brand does, tight under its name - the cards' own words (site.js bRole) */
.hs-role{
  grid-area:3/1;position:relative;margin:calc(-22px*var(--ds)) 0 0;
  font:400 calc(12.5px*var(--ds))/1.3 var(--font-b);color:var(--sub);text-align:center;
  max-width:calc(220px*var(--ds));
}
html.has-intro .house-screen.is-loading .hs-role{display:none}
/* --hcol has to live on the SCREEN itself: .bact is a sibling of the phone,
   not an ancestor, so nothing set there ever inherits down into the device. */
.house-screen[data-brand="thuso"]{--hcol:var(--acc)}
.house-screen[data-brand="buco"]{--hcol:var(--buco)}
.house-screen[data-brand="timbercity"]{--hcol:var(--timbercity)}
.house-screen[data-brand="citiwood"]{--hcol:var(--citiwood)}
.house-screen[data-brand="hhl"]{--hcol:var(--hhl)}
/* The loader: a slim track under the brand name, filled by the scroll loop through
   --load on the <i>. Only shown while the products are loading (.is-loading); the
   brand act uses the same screen without it. */
.hs-load{
  grid-area:3/1;position:relative;display:block;margin-top:calc(-12px*var(--ds));
  width:calc(128px*var(--ds));height:calc(4px*var(--p3k,1));border-radius:999px;overflow:hidden;
  background:color-mix(in srgb,var(--hcol,var(--acc)) 16%,transparent);
  opacity:0;transition:opacity .3s ease;
}
.hs-load i{
  position:absolute;inset:0;border-radius:inherit;background:var(--hcol,var(--acc));
  transform-origin:0 50%;transform:scaleX(var(--load,0));transition:background .5s ease;
}
html.has-intro .house-screen.is-loading .hs-load{opacity:1}
/* The hero's loading layout: the house and its loader and nothing else - no name
   under it (Theo, 2026-09-11). The loader takes the name's row. The brand act uses
   the same screen without .is-loading, so it keeps its brand names. */
html.has-intro .house-screen.is-loading .hs-name{display:none}
html.has-intro .house-screen.is-loading .hs-load{grid-area:2/1;margin-top:calc(-4px*var(--ds))}

@media (prefers-reduced-motion:reduce){
  .bact-card,.hs-img,.hs-name,.hs-halo,.house-screen::before{transition:none}
  .hs-halo,.hs-img{animation:none}
}


/* ============================================================
   HANDOFF - the phone leaves the brand act and takes up the
   features position. The theatre is pulled up by one viewport so
   its sticky stage engages at the exact moment the hero unpins;
   without that there is a full screen of scroll where the hero is
   sliding away and nothing is happening. The theatre's own phone
   stays invisible until the hero's phone has arrived on top of it,
   so the swap cannot be seen.
   ============================================================ */
/* (the theatre handoff lived here - the 01-06 theatre is gone and the phone
   now holds the middle for the feature beat instead of travelling to it) */


/* ============================================================
   FEATURE BEAT — the second half of the pinned act (desktop)
   The brand cards clear, the phone holds the middle, and the six
   features gather round it. The selected card opens with its
   highlight and puts that feature on the phone. Replaces the old
   01–06 sticky theatre outright.
   Cards live in two flex COLUMNS, not fixed slots, so the set can
   be added to or cut without moving anything.
   ============================================================ */
.fact-col,.fact-head,.fact-more{display:none}

@media (min-width:1024px){
  /* The column is only a container - the CARDS animate, one after another, so
     they read as arriving in sequence rather than all appearing together. */
  html.has-intro .fact-col{
    display:flex;flex-direction:column;gap:10px;
    /* centred on the PHONE, not on the hero. The phone settles --ph-drop
       below the hero's middle, so a plain top:50% hung the columns that far
       above it and they read as sitting at the phone's shoulders. The brand
       cards already followed the drop; these never did. */
    position:absolute;top:calc(50% + var(--ph-drop,0px));transform:translateY(-50%);
    /* never wider than the phone it sits beside - JS keeps --ph-halfw in step
       with the live phone box, so this holds at every --ds step */
    /* the same width both sides, as wide as the room beside the phone allows (Theo, 2026-09-14) */
    width:min(clamp(250px,24vw,350px),calc(50vw - var(--ph-halfw) - 72px));
    /* NO visibility:hidden here. Hiding the column killed the cards instantly
       the moment .feats-in came off, so the retreat animated behind a hidden
       parent and read as them simply vanishing. Visibility is handled per card
       below, on a delay, exactly as the brand cards do it. */
    pointer-events:none;
  }
  html.has-intro .fact-l{right:calc(50% + var(--ph-halfw) + 40px)}
  html.has-intro .fact-r{left:calc(50% + var(--ph-halfw) + 40px)}
  html.has-intro .bact.feats-in .fact-col{pointer-events:auto}

  /* --- the card itself (restored: I cut these out when rewriting the block
     by position, which is why they came back as plain text) --- */
  .feat-card{
    position:relative;
    display:block;width:100%;text-align:left;font:inherit;cursor:pointer;
    -webkit-appearance:none;appearance:none;
    padding:20px 22px 20px 26px;border-radius:18px;
    background:var(--card);border:1px solid var(--line);
    box-shadow:0 12px 30px rgba(12,60,48,.08);
  }
  .feat-card::before{
    content:"";position:absolute;left:0;top:14px;bottom:14px;width:4px;
    border-radius:0 4px 4px 0;background:var(--fcol,var(--acc));
    opacity:0;transition:opacity .35s ease;
  }
  .feat-card.is-on::before{opacity:1}
  .fc-n{
    display:block;font:700 12.5px var(--font-d);letter-spacing:.16em;
    color:var(--fcol,var(--faint));margin-bottom:5px;
  }
  .fc-tag{display:none}
  .fc-title{display:block;font:700 19px/1.25 var(--font-d);color:var(--ink)}
  /* the highlight stays shut until this is the card being shown */
  .fc-more{
    display:grid;grid-template-rows:0fr;opacity:0;
    transition:grid-template-rows .45s cubic-bezier(.22,.9,.3,1),opacity .35s ease;
  }
  .fc-more>span{overflow:hidden;display:block;font:400 14px/1.55 var(--font-b);color:var(--sub)}
  /* the card says it in bullets now (Theo, 2026-09-16): the home page short and to the point, the Features page
     keeps the full copy. Spans, not a <ul>: a list is not allowed inside a button. The dot is the brand accent,
     not --fcol - the lime and yellow cards would be invisible at 5px. */
  .fc-b{display:block;position:relative;padding-left:14px;margin-top:6px}
  .fc-b:first-child{margin-top:0}
  .fc-b::before{content:"";position:absolute;left:0;top:.52em;width:5px;height:5px;border-radius:50%;background:var(--fcol,var(--acc))}
  .feat-card.is-on{
    border-color:var(--fcol,var(--acc));
    background:var(--card);
    box-shadow:0 14px 34px color-mix(in srgb,var(--fcol,var(--acc)) 16%,transparent);
  }
  .feat-card.is-on .fc-n{color:var(--fcol,var(--acc))}
  .feat-card.is-on .fc-more{grid-template-rows:1fr;opacity:1}
  .feat-card.is-on .fc-more>span{padding-top:7px}
  /* same prefix as the deal rule, so the lift cannot fight the transform */
  html.has-intro .bact.feats-in .feat-card:hover{
    border-color:color-mix(in srgb,var(--fcol,var(--acc)) 55%,transparent);
    transform:translateY(-2px);
  }
  .feat-card:focus-visible{outline:2px solid var(--acc);outline-offset:3px}

  html.has-intro .feat-card{
    /* the 0.6s delay on visibility is what makes the retreat visible: the card
       stays on screen for the whole travel back behind the phone, and only
       then disappears. Without it the card is gone before it has moved. */
    visibility:hidden;opacity:0;
    transform:translate(var(--tx,0px),var(--ty,0px)) scale(var(--ts,.86));
    transition:visibility 0s linear .6s,opacity .22s ease .14s,
               transform .6s cubic-bezier(.24,.86,.32,1),
               border-color .4s ease,background .4s ease,box-shadow .4s ease;
  }
  html.has-intro .bact.feats-in .feat-card{
    visibility:visible;transform:none;opacity:1;
    transition:visibility 0s,opacity .1s ease,
               transform .6s cubic-bezier(.24,.86,.32,1),
               border-color .4s ease,background .4s ease,box-shadow .4s ease;
    transition-delay:calc(var(--i,0) * 75ms);
  }

  html.has-intro .fact-head{
    display:block;position:absolute;left:50%;
    bottom:calc(50% + var(--ph-half) - var(--ph-drop,0px) + 46px);
    width:min(980px,90vw);text-align:center;
    opacity:0;transform:translate(-50%,-46px);   /* drops in from above */
    transition:opacity .55s ease,transform .6s cubic-bezier(.22,.9,.3,1);
  }
  html.has-intro .bact.feats-in .fact-head{opacity:1;transform:translate(-50%,0)}
  html.has-intro .fact-head .kicker{margin:0 0 6px}
  html.has-intro .fact-head .h2{margin:0;font-size:clamp(1.5rem,2.5vw,2.3rem);line-height:1.1}

  /* --- THE WHY BEAT (2026-09-30): the phone slides left and this stands on its right, centred on the phone, the
     pair centred on the screen as a group (site.js sets --why-dx and --why-w from the viewport). It comes in on a
     short slide from the right once the phone has landed, and is cut dead while the phone is moving, as the cards
     are. Chris asked for Why Thuso on the home page above the six steps; the words are Theo's. --- */
  html.has-intro .wact{
    position:absolute;top:calc(50% + var(--ph-drop,0px));
    /* 140px off the phone, a block up to 640 (Theo, 2026-10-01: "very close to each other... a lot of open space on
       the sides"); site.js centres phone and block as a pair with the same 140 */
    left:calc(50% + var(--why-dx,-390px) + var(--ph-halfw) + 140px);width:var(--why-w,600px);
    opacity:0;visibility:hidden;pointer-events:none;transform:translate(36px,-50%);
    transition:visibility 0s linear .6s,opacity .22s ease .14s,transform .6s cubic-bezier(.24,.86,.32,1);
  }
  html.has-intro .bact.why-in .wact{
    visibility:visible;opacity:1;pointer-events:auto;transform:translate(0,-50%);
    transition:visibility 0s,opacity .3s ease .08s,transform .65s cubic-bezier(.24,.86,.32,1) .08s;
  }
  html.has-intro .bact.why-cut .wact{transition:none !important;visibility:hidden;opacity:0;pointer-events:none;transform:translate(36px,-50%)}
  /* the heading at the act's own size (.bact .h2 - the same as "Partners" above it and "Everything you need" below);
     the paragraph a touch smaller so it holds FOUR lines beside the phone (Theo, 2026-10-01: it ran to five) */
  html.has-intro .wact .h2{margin:0 0 14px}
  html.has-intro .wact-p{margin:0;color:var(--sub);font-size:clamp(.95rem,1.05vw,1rem);line-height:1.6}
  html.has-intro .wact-list{list-style:none;margin:20px 0 0;padding:0;display:grid;gap:9px;text-align:left}
  html.has-intro .wact-list li{position:relative;padding-left:22px;font-weight:600;color:var(--ink);font-size:1rem}
  html.has-intro .wact-list li::before{content:"";position:absolute;left:0;top:.5em;width:9px;height:9px;border-radius:50%;background:var(--acc)}
  html.has-intro .wact-more{margin-top:22px}

  html.has-intro .fact-more{
    display:inline-flex;position:absolute;left:50%;
    top:calc(50% + var(--ph-half) + var(--ph-drop,0px) + 20px);
    opacity:0;transform:translate(-50%,42px);pointer-events:none;   /* rises from below */
    transition:opacity .55s ease,transform .6s cubic-bezier(.22,.9,.3,1);
  }
  html.has-intro .bact.feats-live .fact-more{opacity:1;transform:translate(-50%,0);pointer-events:auto}
}

@media (prefers-reduced-motion:reduce){
  .fact-col,.feat-card,.fc-more,.fact-head,.fact-more{transition:none}
}


/* ---- Projects (Build) -------------------------------------------------- */
.pj-card{
  margin-top:12px;padding:13px;border-radius:14px;
  background:var(--s-surface);border:1px solid var(--s-line);
}
.pj-top{display:flex;justify-content:space-between;align-items:flex-start;gap:10px}
.pj-top strong{display:block;font:700 13.5px var(--font-d);color:var(--s-ink)}
.pj-top span{font:400 11px var(--font-b);color:var(--s-faint)}
.pj-pct{font:700 15px var(--font-d);color:var(--s-acc)}
.pj-bar{margin-top:10px;height:6px;border-radius:999px;background:var(--s-line);overflow:hidden}
.pj-bar i{display:block;height:100%;border-radius:999px;background:var(--s-acc)}
.pj-meta{display:flex;justify-content:space-between;margin-top:8px}
.pj-meta span{font:400 10.5px var(--font-b);color:var(--s-faint)}

.pj-task{display:flex;align-items:flex-start;gap:10px;padding:9px 2px;border-bottom:1px solid var(--s-line)}
.pj-task:last-of-type{border-bottom:0}
.pj-task strong{display:block;font:700 12.5px var(--font-d);color:var(--s-ink)}
.pj-task span{font:400 10.5px var(--font-b);color:var(--s-faint)}
.pj-tick{
  flex:0 0 auto;width:17px;height:17px;margin-top:1px;border-radius:6px;
  border:1.5px solid var(--s-line);background:#fff;position:relative;
}
.pj-task.is-done .pj-tick{background:var(--s-acc);border-color:var(--s-acc)}
.pj-task.is-done .pj-tick::after{
  content:"";position:absolute;left:5px;top:2px;width:4px;height:8px;
  border:solid #fff;border-width:0 2px 2px 0;transform:rotate(45deg);
}
.pj-task.is-done strong{color:var(--s-sub)}

/* ---- Chat (Connect) ---------------------------------------------------- */
.scr-chat .scr-title{display:flex;align-items:center;gap:8px}
.ch-ava{width:24px;height:24px;font-size:9.5px}
.ch-day{margin:12px 0 2px;text-align:center;font:700 10px var(--font-b);color:var(--s-faint)}
.ch-in,.ch-out{margin-top:8px;max-width:80%}
.ch-in p,.ch-out p{margin:0;font:400 12px/1.45 var(--font-b)}
.ch-in p{
  background:var(--s-surface);border:1px solid var(--s-line);color:var(--s-ink);
  padding:9px 12px;border-radius:14px 14px 14px 4px;
}
.ch-out{margin-left:auto}
.ch-out p{
  background:var(--s-acc);color:#fff;
  padding:9px 12px;border-radius:14px 14px 4px 14px;
}
.ch-img p{border-radius:0 0 4px 14px}
/* The message says "Main wall, ready to go", so this is the wall - drawn
   rather than photographed, so it costs nothing to load and carries no licence.
   Layers, back to front: tile field, grout both ways, a nudge on alternate rows
   so it reads as laid tile and not graph paper, then the light across it. */
.ch-thumb{
  display:block;height:74px;border-radius:14px 14px 0 0;
  background:
    /* daylight across the wall, and a little shadow in the far corner */
    radial-gradient(120% 90% at 22% 8%, rgba(255,255,255,.62), rgba(255,255,255,0) 62%),
    linear-gradient(148deg, rgba(255,255,255,.30), rgba(9,42,34,.10) 88%),
    /* grout */
    repeating-linear-gradient(90deg, rgba(12,60,48,.16) 0 1px, transparent 1px 21px),
    repeating-linear-gradient(180deg, rgba(12,60,48,.16) 0 1px, transparent 1px 16px),
    /* alternate rows nudged, so it reads as laid tile and not graph paper */
    repeating-linear-gradient(180deg, transparent 0 16px,
      rgba(12,60,48,.05) 16px 32px),
    /* the tile itself */
    linear-gradient(160deg,#E4EDE9,#CBD9D4);
  border:1px solid var(--s-line);border-bottom:0;
}
.ch-quote{
  margin-top:10px;padding:11px 12px;border-radius:14px;
  background:var(--s-acc-soft);border:1px solid color-mix(in srgb,var(--s-acc) 30%,transparent);
}
.ch-qtop{display:flex;justify-content:space-between;align-items:center}
.ch-qtop strong{font:700 12.5px var(--font-d);color:var(--s-ink)}
.ch-appr{font:700 9.5px var(--font-d);letter-spacing:.08em;text-transform:uppercase;color:var(--s-acc)}
.ch-quote>span{display:block;margin-top:3px;font:400 10.5px var(--font-b);color:var(--s-sub)}
.ch-bar{
  margin-top:auto;display:flex;align-items:center;justify-content:space-between;
  height:40px;padding:0 6px 0 14px;border-radius:999px;
  background:var(--s-surface);border:1px solid var(--s-line);
}
.ch-bar span{font:400 12px var(--font-b);color:var(--s-faint)}
/* Centred by FLEX, not by hardcoded offsets. left:10px/top:9px was eyeballed
   against one size and sat low and right of the middle. */
.ch-send{
  width:28px;height:28px;border-radius:50%;background:var(--s-acc);
  flex:0 0 auto;display:flex;align-items:center;justify-content:center;
}
.ch-send::after{
  content:"";width:6px;height:6px;
  border:solid #fff;border-width:2px 2px 0 0;transform:rotate(45deg);
  /* a chevron's ink sits toward its apex, so the geometric centre reads right
     of true - pull it back a pixel so it looks centred rather than measures it */
  margin-left:-1px;
}
.scr-chat{display:flex;flex-direction:column}


/* --- How it works ------------------------------------------------------
   Deliberately not cards: the audiences section directly above is already a
   grid of white cards on the paper, and a second one read as the same section
   twice. This is a ruled index instead: hairlines separate one row from the
   next, and the outlined numerals carry the sequence on their own.
   The .first* rules above are NOT dead - about.html still uses them. */
.how{padding:58px 0 8px}
/* phones: each phrase on its own line (Theo, 2026-09-16), "Need a hand?" then "Here's how to get it done." The
   second line needs ~12.8em, so under ~420px the size follows the screen, exactly as the theatre heading does. */
@media (max-width:1023px){
  #how-h b{display:block}
  #how-h{font-size:min(1.85rem, calc((100vw - 44px) / 12.8))}
}
.how-head{margin-bottom:6px}
.how-head .lede{margin-top:14px}

.how-steps{list-style:none;margin:30px 0 0;padding:0}
.how-step{
  position:relative;
  display:grid;grid-template-columns:auto 1fr;gap:2px 18px;
  padding:22px 0;
}
/* separated row from row, not joined numeral to numeral */
.how-step + .how-step{border-top:1px solid var(--line)}

.how-num{
  grid-row:1 / span 2;align-self:start;
  width:38px;text-align:center;
  font:700 22px/1.45 var(--font-d);
  color:transparent;-webkit-text-stroke:1.6px var(--hcol,var(--acc));
}
.how-body h3{font:700 1.06rem var(--font-d);color:var(--ink);letter-spacing:-.01em}
.how-body p{color:var(--sub);font-size:.94rem;line-height:1.6;margin-top:5px}
/* the placeholder reads as unfinished ON PURPOSE - it must not be mistaken
   for copy if this page is built before the real flow arrives */
.how-todo{color:var(--faint);font-style:italic}

/* Stretched link: the ONLY link in the row, but its ::after covers the whole
   row - so the entire row is clickable while the tab order and screen readers
   still see one well-labelled link rather than a nest of them. */
.how-go{
  grid-column:2;justify-self:start;margin-top:12px;
  display:inline-flex;align-items:center;
  font:700 12.5px var(--font-d);color:var(--ink);text-transform:uppercase;letter-spacing:.06em;white-space:nowrap;
  padding:9px 16px;border-radius:999px;
  /* the row's colour carries the button; the label stays ink so the yellow rows still read */
  border:1px solid var(--hcol,var(--acc));
  background:color-mix(in srgb,var(--hcol,var(--acc)) 16%,transparent);
  transition:background .22s,border-color .22s,transform .22s;
}
.how-go::after{content:"";position:absolute;inset:0;border-radius:var(--r-sm)}
.how-step:hover .how-go{background:transparent;border-color:var(--hcol,var(--acc))}
/* translate in the chevron's OWN rotated frame: (2.5,-2.5) at 45deg comes
   out as 3.5px straight right, and keeps the rotation that draws it */
.how-step:hover .how-go .chev{transform:rotate(45deg) translate(2.5px,-2.5px)}
.how-go:focus-visible{outline:2px solid var(--acc);outline-offset:3px}

/* Ready for Erin's walkthroughs. It sits ABOVE the stretched layer so it stays
   independently clickable once the videos land. */
.how-watch{
  position:relative;z-index:1;grid-column:2;justify-self:start;
  display:inline-flex;align-items:center;gap:9px;margin-top:12px;
  font:700 13.5px var(--font-d);color:var(--acc);white-space:nowrap;
}
/* play badge, drawn rather than shipped as an asset: circle is ::before, the
   triangle ::after over it (pseudo-elements cannot nest) */
.how-watch::before{content:"";flex:0 0 auto;width:19px;height:19px;border-radius:50%;background:var(--acc-soft)}
.how-watch::after{
  content:"";position:absolute;left:7px;top:50%;transform:translateY(-50%);
  width:0;height:0;border-left:6px solid var(--acc);
  border-top:4px solid transparent;border-bottom:4px solid transparent;
}
.how-watch:hover{text-decoration:underline}
.how-cta{margin-top:34px;text-align:center}

@media (min-width:1024px){
  .how{padding:92px 0 16px}
  /* Head stays LEFT-ALIGNED - the two sections above are both centred, so this
     alone stops it reading as another one of them. The lede takes its own full
     width rather than a side column: at lede size that sentence needs ~850px
     and any half-width column is ~570px, so splitting the head would have
     meant shrinking the type to about 13px to hold one line. */
  .how-head{margin-bottom:10px}
  .how-head .lede{
    margin:16px 0 0;max-width:none;font-size:1rem;
  }
  .how-steps{margin-top:44px}
  .how-step{
    grid-template-columns:74px minmax(200px,1fr) minmax(112px,auto);
    align-items:center;gap:0 30px;padding:26px 16px 26px 0;
  }
  .how-num{grid-row:auto;align-self:center;width:74px;font-size:34px;line-height:1}
  /* the description column carries the longest line in the set (79 chars), so
     it gets the room - trimming copy to fit a narrow column just moves the
     problem to whichever row is longest next */
  .how-body{display:grid;grid-template-columns:minmax(160px,.62fr) minmax(0,1.55fr);gap:0 28px;align-items:center}
  .how-body p{margin:0}
  .how-go{grid-column:3;margin-top:0;justify-self:end}
  .how-watch{grid-column:3;margin-top:0;justify-self:end;margin-right:14px}
  .how-cta{margin-top:48px}
}


/* --- Legal pages (privacy, terms) --------------------------------------
   Long-form reading, so the measure is tight and the rhythm is generous.
   .legal-todo is meant to look UNFINISHED - a stub clause that reads like
   finished copy is worse than an obviously empty one. */
.legal{padding:8px 0 70px}
.legal-flag{
  max-width:70ch;margin:0 0 40px;padding:18px 20px;border-radius:var(--r-sm);
  background:var(--acc-soft);border:1px solid color-mix(in srgb,var(--acc) 30%,transparent);
  color:var(--ink);font-size:.95rem;line-height:1.6;
}
.legal-flag strong{color:var(--acc)}
.legal-body{max-width:70ch}
.legal-sec{padding:24px 0;border-top:1px solid var(--line)}
.legal-sec:first-child{border-top:0;padding-top:0}
.legal-sec h2{font:700 1.12rem var(--font-d);color:var(--ink);letter-spacing:-.01em}
.legal-sec p{margin-top:9px;font-size:.95rem;line-height:1.7;color:var(--sub)}
.legal-todo{
  color:var(--faint);font-style:italic;
  border-left:2px solid var(--line2);padding-left:14px;
}
.legal-updated{margin-top:34px;font-size:.88rem;color:var(--faint)}
@media (min-width:1024px){
  .legal{padding:16px 0 96px}
  .legal-sec{padding:30px 0}
  .legal-sec h2{font-size:1.2rem}
}


/* ============================================================
   vCARD SWITCHER (2026-09-10) — five brand cards on the one tilting
   stage. Everything is scoped to #vcards so the brand switchboard is
   untouched, and the selected chip reuses the existing .s-chip.is-on
   rule by setting --sb to that brand's colour. No font is declared
   here: the chips and copy keep the site's own Poppins / body stack.
   ============================================================ */
#vcards .tilt .vc-img{position:absolute;inset:0;width:100%;height:auto;opacity:0;transform:translateX(28px);
  transition:opacity .5s ease,transform .65s cubic-bezier(.22,.9,.3,1)}
#vcards .tilt .vc-img.is-on{position:relative;opacity:1;transform:none}
#vcards .tilt .vc-img.is-off{transform:translateX(-28px)}   /* the card leaving goes the other way */
/* centred: heading, the cards and their pills, one line under them (Theo, 2026-09-13) */
.loyal-c .h2{margin:0}
.loyal-c .loyal-stage{padding:34px 0 6px}
.loyal-c .section-sub{margin:24px auto 0;max-width:none}
@media (min-width:1024px){
  .loyal-c{padding:104px 0 96px}
  .loyal-c .loyal-stage{padding:44px 0 6px}
}
#vcards .card-chips{margin-top:28px;justify-content:center}
#vcards .s-chip{cursor:pointer}
#vcards .s-chip[data-brand="thuso"]{--sb:var(--acc)}
#vcards .s-chip[data-brand="buco"]{--sb:var(--buco)}
#vcards .s-chip[data-brand="timbercity"]{--sb:var(--timbercity)}
#vcards .s-chip[data-brand="citiwood"]{--sb:var(--citiwood)}
#vcards .s-chip[data-brand="hhl"]{--sb:var(--hhl)}
/* two rows, never three (Theo, 2026-09-17). Under 375px the five chips broke 2/2/1; the chip now gives back a
   little width as the screen narrows - text and side padding only, so the row stays 38-40px tall to tap - and
   they sit 3 + 2 from 320px up. A tablet still takes all five on one line. */
@media (max-width:1023px){
  #vcards .card-chips{gap:clamp(6px,2vw,9px)}
  #vcards .s-chip{font-size:clamp(10.5px,3.05vw,12.5px);padding:10px clamp(10px,3.7vw,16px);gap:clamp(6px,1.9vw,8px);letter-spacing:.04em}
  #vcards .s-dot{width:7px;height:7px}
}
@media (prefers-reduced-motion:reduce){#vcards .tilt .vc-img{transition:none;transform:none}}


/* ============================================================
   WHY THUSO hero — the h1 holds one line. Measured at 1440: the line
   needs 740px and the default column gives 592px, so the phone column
   is pinned (its frame is 310px) and the display size trimmed. Clears
   at 1024 / 1100 / 1280 / 1440 / 1920 with room to spare.
   ============================================================ */
@media (min-width:1024px){
  #whyHero .dl-grid{grid-template-columns:minmax(0,1fr) 360px;gap:48px}
  #whyHero .display{font-size:clamp(2.4rem,4.7vw,4.3rem)}
}

/* ============================================================
   ONE TYPE SCALE ON PHONES AND TABLETS (Theo, 2026-09-17: "the headers differ on pages")
   Every page had grown its own sizes: titles 27-43px, section headings 25/27/30/41, card titles
   17/18/19/21/22, kickers 11px or 13px with two trackings, body 15/16/17. One set of tokens now, and
   LAST in the file on purpose - each component sets its own size earlier, so this has to come after them.
   The title size is the one the longest title needs ("Right where you need it." measures 11.41em, so two
   phrases fit from 320px up); every page title takes it, and section headings match, told apart by weight
   and the green kicker. The home HERO headline is the page's statement and keeps its own size, as does
   "#how-h", whose second phrase is longer still and fits itself. Colour was already consistent.
   ============================================================ */
@media (max-width:1023px){
  :root{--t-card:1.2rem;--t-body:1rem;--t-lede:1.06rem}
  .page-hero .display,.page-hero .h1{font-size:var(--page-title-fs)}
  /* section headings match the title on phones and stop at their own 2.1rem, so a tablet's 56px title still
     leads and "QUOTE." does not shout */
  .h2,.feat h2.h1,.finale .display,.cta-band .h2{font-size:min(var(--page-title-fs), 2.1rem)}
  .aud-card h3,.brandcard h3,.step-card h3,.contact-card h3,.thm-slide>h3,.how-step h3,
  .mini-card h3,.value-card h3,.acc summary{font-size:var(--t-card)}
  /* the card label needs the card in the selector: `.aud-card p` (two classes) already beat a bare .aud-tag */
  .kicker,.kicker.feat-n,.aud-card .aud-tag,.contact-card .aud-tag,.faq-group{font-size:11px;letter-spacing:.22em}
  .lede,.section-sub{font-size:var(--t-lede)}
  .aud-card p:not(.aud-tag),.brandcard p,.step-card p,.contact-card p:not(.aud-tag),
  .acc-body,.acc-body p,.thm-list li,.how-body p,.mini-card p,.value-card p{font-size:var(--t-body)}
}

/* ===== THE LEGAL PAGES (2026-10-05) ======================================================================================
   Chris's brief: a T&Cs page listing Thuso's own terms, The Building Company's five documents (PDFs on thebuildingco.com
   behind his go. links) and a Data Subject Request page with PrivIQ's form. terms.html, app-terms.html and
   data-subject-request.html. */
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
.legal{padding:4px 0 88px}
.legal-hero{padding-bottom:36px}
.legal-back{display:inline-flex;align-items:center;gap:6px;margin-bottom:18px;font:600 13.5px/1 var(--font-d);color:var(--acc)}
.legal-back svg{width:16px;height:16px}
.legal-back:hover span{text-decoration:underline;text-underline-offset:3px}
/* the T&Cs page: one row per document, in Chris's order */
.legal-list{list-style:none;margin:0;padding:0;display:grid;gap:12px;max-width:780px}
.legal-list a{display:flex;align-items:center;gap:14px;min-height:64px;padding:18px 22px;background:var(--card);border:1px solid var(--line);
  border-radius:18px;color:var(--ink);transition:border-color .25s,box-shadow .25s,transform .25s}
.legal-list a:hover{border-color:var(--line2);box-shadow:0 12px 28px rgba(12,40,32,.08);transform:translateY(-2px)}
.legal-list a:focus-visible{outline:2px solid var(--acc);outline-offset:3px}
.lg-name{flex:1;font:600 clamp(1rem,1.1vw,1.08rem)/1.35 var(--font-d)}
.lg-kind{flex:none;padding:5px 9px;border-radius:999px;background:var(--acc-soft);color:var(--acc);font:700 10.5px/1 var(--font-d);letter-spacing:.14em}
.lg-go{flex:none;width:18px;height:18px;color:var(--acc)}
/* a document page: one reading column, the wording exactly as supplied */
.legal-meta{list-style:none;margin:20px 0 0;padding:0;display:flex;flex-wrap:wrap;gap:6px 24px;color:var(--sub);font-size:.95rem}
.legal-doc{max-width:780px}
.legal-doc h2{font:700 clamp(1.12rem,1.3vw,1.28rem)/1.3 var(--font-d);color:var(--ink);margin:40px 0 12px;scroll-margin-top:96px}
.legal-doc p,.legal-doc li{color:var(--sub);font-size:1.02rem;line-height:1.72}
.legal-doc p + p,.legal-doc ul + p{margin-top:12px}
.legal-doc ul{list-style:disc;margin:12px 0 0;padding-left:1.25em}
.legal-doc li + li{margin-top:6px}
.legal-doc li::marker{color:var(--acc)}
.legal-doc strong{color:var(--ink);font-weight:700}
.legal-doc a{overflow-wrap:anywhere}
.ld-notice{padding:20px 22px;background:var(--card);border:1px solid var(--line);border-left:4px solid var(--acc);border-radius:16px}
.ld-notice p{color:var(--ink)}
.ld-end{margin-top:44px;padding-top:22px;border-top:1px solid var(--line)}
/* the Data Subject Request page: PrivIQ's form renders into #dsar-form.
   PrivIQ'S FORM uses Bootstrap's class names (row, col-sm-6, form-group, form-control, btn, btn-group, active) but brings
   no Bootstrap, and the site's own resets (links, buttons) and its .btn class reach into it: links read as plain text and
   the buttons as bare words. This layer, scoped to the box, gives it the site's look. */
.dsar-form{max-width:860px;min-height:120px;color:var(--sub);font-size:1rem;line-height:1.6}
.dsar-form>h1{font:700 clamp(1.3rem,1.7vw,1.65rem)/1.25 var(--font-d);color:var(--ink);margin:0 0 14px}
.dsar-form p{margin:0 0 10px}
.dsar-form strong{color:var(--ink)}
.dsar-form a{color:var(--acc);text-decoration:underline;text-underline-offset:3px}
.dsar-form a:hover{color:var(--ink)}
.dsar-form form{margin-top:22px;padding:clamp(20px,3vw,34px);background:var(--card);border:1px solid var(--line);border-radius:var(--r-md)}
.dsar-form h4{font:700 1.08rem/1.3 var(--font-d);color:var(--ink);margin:28px 0 14px}
.dsar-form h5{font:600 .95rem/1.4 var(--font-d);color:var(--ink);margin:0 0 12px}
.dsar-form .row:not([style*="none"]){display:flex;flex-wrap:wrap;gap:0 18px}
.dsar-form .col-sm-6{flex:1 1 230px;min-width:0}
.dsar-form .col-sm-12{flex:1 1 100%}
.dsar-form .text-center{display:flex;flex-wrap:wrap;justify-content:center;gap:12px;margin-top:18px}
.dsar-form .form-group{margin:0 0 16px}
.dsar-form .control-label{display:block;margin:0 0 6px;font:600 .9rem/1.4 var(--font-d);color:var(--ink)}
.dsar-form .control-label+br{display:none}
.dsar-form .form-control{display:block;width:100%;min-height:46px;padding:10px 14px;border:1px solid var(--line2);border-radius:12px;
  background:#fff;color:#0B2B23;font:400 1rem/1.4 var(--font-b);transition:border-color .2s,box-shadow .2s}
.dsar-form textarea.form-control{min-height:110px;resize:vertical}
.dsar-form .form-control:focus{outline:0;border-color:var(--acc);box-shadow:0 0 0 3px var(--acc-soft)}
.dsar-form .iti{display:block;width:100%}
.dsar-form .iti--allow-dropdown .form-control{padding-left:52px}
.dsar-form .btn-group{display:flex;flex-wrap:wrap;gap:8px}
.dsar-form .btn-group .btn{min-height:42px;padding:9px 16px;border:1px solid var(--line2);border-radius:999px;background:#fff;color:var(--ink);
  font:600 .9rem/1 var(--font-d);text-transform:none;letter-spacing:0;transition:border-color .2s,background .2s,color .2s}
.dsar-form .btn-group .btn:hover{border-color:var(--acc)}
.dsar-form .btn-group .btn.active{background:var(--acc);border-color:var(--acc);color:#fff}
.dsar-form .btn-group .btn .bi{display:none}
.dsar-form .btn-group .btn.active .bi{display:inline-block}
.dsar-form .btn-primary{background:var(--grad);color:#fff;padding:14px 28px;box-shadow:0 10px 28px rgba(13,91,73,.28)}
.dsar-form .btn-primary:hover{transform:translateY(-2px);filter:brightness(1.06)}
.dsar-form .btn-info{padding:13px 24px;border:1px solid var(--line2);background:#fff;color:var(--ink)}
.dsar-form .btn-info:hover{border-color:var(--acc)}
.dsar-note{margin:0;padding:18px 20px;border:1px dashed var(--line2);border-radius:14px;color:var(--sub)}
