/* billed — shared design system. Extracted from index.html at v104.
   Previously this lived inline in BOTH index.html and support.html; 826 of
   support.html's 955 CSS lines were a duplicate that had already drifted:
   it was missing the v103 mobile-menu glass and the mobile footer grid, and
   carried a superseded hero gradient. One file now, so that cannot recur.
   Page-specific CSS stays inline on its own page (see support.html). */

  /* ===========================================================
     v97 — SELF-HOSTED FONTS. No CDN, no external font request.

     Montserrat: ONE variable file spans weights 100-900, replacing the six
     static weights we used to pull from Google Fonts. 37 KB, served from
     /assets/fonts/ and preloaded in <head>.

     Tabler icons: the full webfont is 760 KB for ~5000 glyphs. This site uses
     33. Subset to just those and it is 7 KB — small enough to inline, so the
     icons can never arrive late or flash as empty squares.
     To ADD an icon: a subset does not contain it. Re-run the subsetting step
     with the new name included, or the glyph renders as a blank box.
     =========================================================== */
  @font-face{font-family:Montserrat;font-style:normal;font-display:swap;font-weight:100 900;src:url(/assets/fonts/montserrat-latin-var.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
  @font-face{font-family:"tabler-icons";font-style:normal;font-weight:400;font-display:block;src:url(data:font/woff2;base64,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) format("woff2")}
  .ti{font-family:"tabler-icons" !important;speak:none;font-style:normal;font-weight:normal;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}
  .ti-arrow-guide:before{content:"\f22a"}.ti-bell-ringing:before{content:"\ed07"}.ti-briefcase:before{content:"\ea46"}.ti-calendar:before{content:"\ea53"}.ti-chart-bar:before{content:"\ea59"}.ti-check:before{content:"\ea5e"}.ti-chevron-down:before{content:"\ea5f"}.ti-circle-check:before{content:"\ea67"}.ti-circle-check-filled:before{content:"\f704"}.ti-clock:before{content:"\ea70"}.ti-clock-hour-4:before{content:"\f319"}.ti-database-export:before{content:"\ee6e"}.ti-download:before{content:"\ea96"}.ti-file-invoice:before{content:"\eb67"}.ti-gauge:before{content:"\eab1"}.ti-hand-click:before{content:"\ef4f"}.ti-layout-grid:before{content:"\edba"}.ti-loader-2:before{content:"\f226"}.ti-lock:before{content:"\eae2"}.ti-mail:before{content:"\eae5"}.ti-mail-fast:before{content:"\f069"}.ti-map-pin:before{content:"\eae8"}.ti-message-2:before{content:"\eaec"}.ti-paperclip:before{content:"\eb02"}.ti-photo:before{content:"\eb0a"}.ti-plus:before{content:"\eb0b"}.ti-receipt:before{content:"\edfd"}.ti-send:before{content:"\eb1e"}.ti-tag:before{content:"\eb34"}.ti-tags:before{content:"\ef86"}.ti-users:before{content:"\ebf2"}.ti-world:before{content:"\eb54"}.ti-x:before{content:"\eb55"}

  /* ===========================================================
     billed website v220
     Themes mirror the app: data-theme = dark|light · data-variant = glass(Modern)|minimal
     =========================================================== */
  :root, [data-theme="light"]{
    --bg-primary:#ffffff; --bg-secondary:#f5f5f4; --bg-tertiary:#f2f2f2;
    --text-primary:#111111; --text-secondary:#555555; --text-tertiary:#999999;
    --border-light:rgba(0,0,0,0.08); --border-med:rgba(0,0,0,0.15); --border-strong:rgba(0,0,0,0.25);
    /* Every divider on the page reads from --rule: FAQ rows, the footer's top edge, its
       column rules, its base rule, and the step dividers over the photograph. Retune all
       of them from this one value. Separate from --border-* because those also carry card
       and input edges, which should not move when a divider is adjusted. */
    --rule:rgba(0,0,0,0.15);
    --card-bg: rgba(255,255,255,0); --card-edge:rgba(255,255,255,0.85);
    --card-shine: 0 0 0 0 transparent;
    --card-drop: 0 11px 17px rgba(0,0,0,0.15);
    --band-bg:rgba(255,255,255,0.45);
    --glass-blur: 15px; --card-bw:1px;
    --scrim:rgba(250,250,252,0.60); --nav-bg:rgba(242,242,242,0.72);
    --accent-ink:#6c6c6c; --logo-ink:#141414;
    --ph-fig:rgba(0,0,0,0.13); --ph-bg1:#e9e9e7; --ph-bg2:#dedede;
    --bg-grad:radial-gradient(72% 62% at 14% 4%,#ffffff 0%,transparent 56%),radial-gradient(62% 56% at 95% 28%,#f9f9fc 0%,transparent 58%),radial-gradient(88% 78% at 50% 120%,#d6d6dd 0%,transparent 60%),radial-gradient(52% 46% at 82% 92%,#dedee5 0%,transparent 62%),linear-gradient(158deg,#f4f4f5 0%,#ededee 54%,#e4e4e8 100%);
    --brand-bg:url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%22147.01%22 height=%22130%22 viewBox=%220 0 147.01 130%22%3E%3Cg fill=%22%23000000%22%3E%3Cpath d=%22M73.5,60c-8.27,0-15,6.73-15,15s6.73,15,15,15,15-6.73,15-15-6.73-15-15-15Z%22%3E%3C/path%3E%3Cpath d=%22M113.5,35c0-8.28,6.72-15,15-15,1.18,0,2.32.15,3.42.41C127.93,8.55,116.72,0,103.5,0h-60C26.93,0,13.5,13.43,13.5,30v70c0,16.57,13.43,30,30,30h60c16.57,0,30-13.43,30-30v-50.87c-1.57.55-3.24.87-5,.87-8.28,0-15-6.72-15-15ZM73.5,110c-5.37,0-10.45-1.21-15-3.37v3.37h-20V20h20v23.37c4.55-2.16,9.63-3.37,15-3.37,19.33,0,35,15.67,35,35s-15.67,35-35,35Z%22%3E%3C/path%3E%3Cpolygon points=%22143.51 20.07 143.47 20.03 142.65 20.85 141.46 22.05 139.94 23.57 139.12 24.4 138.51 25 136.41 27.11 135.58 27.93 133.51 30 132.05 31.47 128.51 35 124.97 31.46 123.51 32.92 121.44 35 123.51 37.07 124.97 38.54 126.43 40 128.51 42.07 130.58 40 132.05 38.54 133.51 37.07 135.58 35 138.51 32.07 139.12 31.47 139.94 30.64 140.58 30 142.65 27.93 143.47 27.11 143.51 27.07 145.58 25 146.19 24.39 147.01 23.57 143.51 20.07%22%3E%3C/polygon%3E%3C/g%3E%3C/svg%3E"); --noise-op:0.07; --brand-w:min(62vw,58vh);
  }
  [data-theme="dark"]{
    --bg-primary:#1c1c1e; --bg-secondary:#2c2c2e; --bg-tertiary:#141414;
    --text-primary:#f5f5f5; --text-secondary:#a0a0a0; --text-tertiary:#666666;
    --border-light:rgba(255,255,255,0.08); --border-med:rgba(255,255,255,0.15); --border-strong:rgba(255,255,255,0.25);
    --rule:rgba(255,255,255,0.15);
    --card-bg: rgba(42,42,47,0.19); --card-edge:rgba(255,255,255,0.12);
    --card-shine: 0 0 0 0 transparent;
    --card-drop: 0 11px 17px rgba(0,0,0,0.15);
    --band-bg:rgba(28,28,32,0.42);
    --glass-blur: 15px; --card-bw:1px;
    --scrim:rgba(0,0,0,0.55); --nav-bg:rgba(20,20,20,0.62);
    --accent-ink:#d9ff00; --logo-ink:#d9ff00;
    --ph-fig:rgba(255,255,255,0.11); --ph-bg1:#1f1f22; --ph-bg2:#161618;
    --bg-grad:radial-gradient(72% 62% at 14% 4%,#41414c 0%,transparent 56%),radial-gradient(62% 56% at 95% 28%,#35353f 0%,transparent 58%),radial-gradient(88% 78% at 50% 120%,#060608 0%,transparent 60%),radial-gradient(52% 46% at 82% 92%,#0a0a0c 0%,transparent 62%),linear-gradient(158deg,#1f1f24 0%,#161619 54%,#0e0e11 100%);
    --brand-bg:url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%22147.01%22 height=%22130%22 viewBox=%220 0 147.01 130%22%3E%3Cg fill=%22%23ffffff%22%3E%3Cpath d=%22M73.5,60c-8.27,0-15,6.73-15,15s6.73,15,15,15,15-6.73,15-15-6.73-15-15-15Z%22%3E%3C/path%3E%3Cpath d=%22M113.5,35c0-8.28,6.72-15,15-15,1.18,0,2.32.15,3.42.41C127.93,8.55,116.72,0,103.5,0h-60C26.93,0,13.5,13.43,13.5,30v70c0,16.57,13.43,30,30,30h60c16.57,0,30-13.43,30-30v-50.87c-1.57.55-3.24.87-5,.87-8.28,0-15-6.72-15-15ZM73.5,110c-5.37,0-10.45-1.21-15-3.37v3.37h-20V20h20v23.37c4.55-2.16,9.63-3.37,15-3.37,19.33,0,35,15.67,35,35s-15.67,35-35,35Z%22%3E%3C/path%3E%3Cpolygon points=%22143.51 20.07 143.47 20.03 142.65 20.85 141.46 22.05 139.94 23.57 139.12 24.4 138.51 25 136.41 27.11 135.58 27.93 133.51 30 132.05 31.47 128.51 35 124.97 31.46 123.51 32.92 121.44 35 123.51 37.07 124.97 38.54 126.43 40 128.51 42.07 130.58 40 132.05 38.54 133.51 37.07 135.58 35 138.51 32.07 139.12 31.47 139.94 30.64 140.58 30 142.65 27.93 143.47 27.11 143.51 27.07 145.58 25 146.19 24.39 147.01 23.57 143.51 20.07%22%3E%3C/polygon%3E%3C/g%3E%3C/svg%3E"); --noise-op:0.07; --brand-w:min(62vw,58vh);
  }
  /* ---- MINIMAL: flat surfaces, no blur, no shine, no ambient colour ---- */
  [data-variant="minimal"]{
    --card-bg:var(--bg-secondary); --card-edge:var(--border-med);
    --card-shine: 0 0 0 0 transparent; --card-drop:none;
    --band-bg:var(--bg-primary);
    --glass-blur:0px; --card-bw:0.5px;
    --bg-grad:none; --noise-op:0;
  }
  :root{ --lime:#d9ff00; --pink:#f80080; --blue:#00a6ff; --r:30px; --maxw:1120px;
         --ease:cubic-bezier(.22,.72,.28,1);
         --ease-quad:cubic-bezier(.455,.03,.515,.955); }

  *{box-sizing:border-box}

  /* ===== Custom scrollbar — ported from the billed app ===== */
  html{scrollbar-gutter:stable}
  /* WebKit (Chrome/Safari/Edge): 6px pill thumb, transparent track, no arrow buttons */
  ::-webkit-scrollbar{width:6px;height:6px}
  ::-webkit-scrollbar-track{background:transparent}
  ::-webkit-scrollbar-thumb{background:rgba(0,0,0,0.18);border-radius:200px}
  ::-webkit-scrollbar-thumb:hover{background:rgba(0,0,0,0.32)}
  ::-webkit-scrollbar-corner{background:transparent}
  ::-webkit-scrollbar-button{display:none!important;width:0!important;height:0!important}
  /* dark themes flip the thumb to light */
  [data-theme="dark"] ::-webkit-scrollbar-thumb{background:rgba(255,255,255,0.18)}
  [data-theme="dark"] ::-webkit-scrollbar-thumb:hover{background:rgba(255,255,255,0.32)}
  /* Firefox only — gated off WebKit, where scrollbar-width/color would force the native
     Fluent scrollbar (arrow buttons) and disable every ::-webkit rule above. */
  @supports not selector(::-webkit-scrollbar){
    *{scrollbar-width:thin;scrollbar-color:rgba(0,0,0,0.18) transparent}
    [data-theme="dark"] *{scrollbar-color:rgba(255,255,255,0.18) transparent}
  }
  /* mobile / touch: hide the bar entirely (native overlay scrolling takes over) */
  @media(max-width:900px),(orientation:portrait) and (pointer:coarse){
    *{scrollbar-width:none!important;-ms-overflow-style:none!important}
    *::-webkit-scrollbar{display:none!important;width:0!important;height:0!important}
  }
  /* keep the horizontal carousels' bars hidden regardless (they use arrows + dots) */
  .who-strip::-webkit-scrollbar,.gal-track::-webkit-scrollbar{display:none!important}
  /* scroll-behavior:smooth removed — it fights Lenis, which handles anchors
     itself via anchors:{offset}. scroll-padding-top stays: it still governs
     find-in-page, keyboard navigation and the native path used on touch and
     under prefers-reduced-motion, where Lenis never starts. */
  html{scroll-padding-top:100px}
  body{
    margin:0; background:var(--bg-tertiary); color:var(--text-primary);
    font-family:Montserrat,-apple-system,'Segoe UI',Roboto,sans-serif;
    -webkit-font-smoothing:antialiased; line-height:1.6; overflow-x:hidden;
    transition:background .4s var(--ease), color .4s var(--ease);
  }
  a{color:inherit;text-decoration:none}
  /* clearly-a-link inline CTA, e.g. 'Find out more →' on spot sections / feature cards */
  .more-link{color:var(--lime);font-weight:600;text-decoration:none;white-space:nowrap}
  .more-link::after{content:'\2192';display:inline-block;margin-left:6px;transition:transform .22s var(--ease)}
  .more-link:hover{text-decoration:underline;text-underline-offset:3px}
  .more-link:hover::after{transform:translateX(4px)} img,svg{max-width:100%;display:block}
  :focus-visible{outline:2px solid var(--accent-ink);outline-offset:3px;border-radius:6px}
  .wrap{max-width:var(--maxw);margin:0 auto;padding:0 24px;position:relative;z-index:1}
  /* Pretitle library — identical in every section */
  .eyebrow{font-size:13px;font-weight:700;letter-spacing:1.8px;text-transform:uppercase;color:var(--accent-ink);margin:0 0 14px;line-height:1.3}
  /* .how sits on a dark photograph in every theme, so its label is lime unconditionally
     rather than following --accent-ink (which is neutral grey on the light themes). */
  .how .eyebrow{color:var(--lime)}

  /* ---- Page background: same two layers the app uses ---- */
  body::before{content:'';position:fixed;inset:0;z-index:-2;pointer-events:none;background:var(--bg-grad);transition:background .4s var(--ease)}
  body::after{content:'';position:fixed;inset:0;z-index:-1;pointer-events:none;background-image:var(--brand-bg);background-size:var(--brand-w) calc(var(--brand-w) * 0.8843);background-position:center center;background-repeat:no-repeat;opacity:var(--noise-op);transition:opacity .4s var(--ease)}

  /* ---- Nav: minimal at rest, expands when the pointer enters the bar ---- */
  header.nav{position:fixed;top:0;left:0;right:0;z-index:200;
    background:transparent;border-bottom:1px solid transparent;
    -webkit-backdrop-filter:blur(0px);backdrop-filter:blur(0px);
    transition:background .45s var(--ease),border-color .45s var(--ease),backdrop-filter .45s var(--ease),box-shadow .45s var(--ease)}
  /* frosted once you leave the hero */
  /* Light theme, nav still transparent, sitting on a dark hero photo: the
     light tokens vanish against it. Restore the dark theme's nav values --
     copied from the [data-theme="dark"] block, not invented -- and only while
     the bar is transparent. Once .scrolled (or hover/focus) paints --nav-bg
     behind it, the normal light tokens are correct again and take over. */
  [data-theme="light"].photo-hero header.nav:not(.scrolled):not(:hover):not(:focus-within){
    --text-primary:#f5f5f5; --text-secondary:#a0a0a0; --text-tertiary:#666666;
    --accent-ink:#d9ff00; --logo-ink:#d9ff00;
    --border-light:rgba(255,255,255,0.08); --border-med:rgba(255,255,255,0.15)}
  header.nav.scrolled{background:var(--nav-bg);border-bottom-color:var(--border-light);
    -webkit-backdrop-filter:blur(20px) saturate(1.4);backdrop-filter:blur(20px) saturate(1.4)}
  /* frosted whenever the bar is engaged */
  header.nav:hover,header.nav:focus-within{background:var(--nav-bg);border-bottom-color:var(--border-light);
    -webkit-backdrop-filter:blur(22px) saturate(1.5);backdrop-filter:blur(22px) saturate(1.5);
    box-shadow:0 18px 44px -26px rgba(0,0,0,.7)}

  .nav-in{display:grid;grid-template-columns:1fr auto 1fr;align-items:center;height:62px;
    transition:height .42s var(--ease)}
  .nav-in > .brand{justify-self:start}
  .nav-in > .nav-links{justify-self:center}
  .nav-in > .nav-cta{justify-self:end}
  header.nav:hover .nav-in,header.nav:focus-within .nav-in{height:98px}

  /* brand: icon at rest -> full wordmark when the bar expands */
  .brand{position:relative;display:flex;align-items:center;height:26px;transition:height .42s var(--ease)}
  .brand .logo-mark,.brand .logo-full{color:var(--logo-ink);transform-origin:left center;
    transition:opacity .34s var(--ease),transform .42s var(--ease),color .4s var(--ease),height .42s var(--ease)}
  .brand .logo-mark svg,.brand .logo-full svg{width:100%;height:100%;display:block}
  .brand .logo-mark{height:28px;width:32px}
  .brand .logo-full{position:absolute;left:0;top:50%;height:26px;width:75px;opacity:0;pointer-events:auto;
    transform:translateY(-50%) scale(.92);
    transition:opacity .34s var(--ease),transform .42s var(--ease),height .42s var(--ease),width .42s var(--ease),color .4s var(--ease)}
  header.nav:hover .brand,header.nav:focus-within .brand{height:44px}
  header.nav:hover .logo-mark,header.nav:focus-within .logo-mark{opacity:0;transform:scale(.8)}
  /* matches the expanded CTA pill height so the two lime elements balance */
  header.nav:hover .logo-full,header.nav:focus-within .logo-full{opacity:1;height:44px;width:126px;transform:translateY(-50%) scale(1)}
  /* Expand the anchor hit area to cover the wordmark so the whole logo is clickable, not just the icon. */
  .nav:hover .brand,header.nav:focus-within .brand{min-width:126px}
  .ft-brand .brand{height:auto}

  /* links: quiet, then open up */
  .nav-links{display:flex;align-items:center;gap:26px;transition:gap .42s var(--ease)}
  header.nav:hover .nav-links,header.nav:focus-within .nav-links{gap:38px}
  .nav-links a.link{position:relative;color:var(--text-primary);font-size:13px;font-weight:600;letter-spacing:.2px;opacity:.55;
    transition:opacity .34s var(--ease),font-size .42s var(--ease),letter-spacing .42s var(--ease),color .2s}
  header.nav:hover .nav-links a.link,header.nav:focus-within .nav-links a.link{opacity:.85;font-size:15px;letter-spacing:.5px}
  .nav-links a.link:hover{opacity:1}
  .nav-links a.link::after{content:'';position:absolute;left:0;right:0;bottom:-7px;height:2px;background:var(--accent-ink);border-radius:2px;
    transform:scaleX(0);transform-origin:center center;transition:transform .3s var(--ease)}
  .nav-links a.link:hover::after,.nav-links a.link.active::after{transform:scaleX(1)}
  .nav-links a.link.active{opacity:1;color:var(--accent-ink)}

  /* German labels are long enough to close the gap between the links and the login
     block entirely ("FAQ" ran into "Anmelden"). A guaranteed left margin keeps the two
     groups apart whatever the language. */
  /* Longer localised labels (Iniciar sesión, Funcionalidades) must not wrap. */
  .nav-cta{display:flex;align-items:center;gap:14px;margin-left:28px;white-space:nowrap;transition:gap .42s var(--ease)}
  .nav-login{white-space:nowrap}
  header.nav:hover .nav-cta,header.nav:focus-within .nav-cta{gap:20px}
  .nav-login{color:var(--text-primary);font-size:13px;font-weight:600;opacity:.45;
    transition:opacity .34s var(--ease),font-size .42s var(--ease)}
  header.nav:hover .nav-login,header.nav:focus-within .nav-login{opacity:.85;font-size:14.5px}
  .nav-login:hover{opacity:1}

  /* CTA: bare lime text at rest -> full pill when the bar expands */
  .cta{display:inline-flex;align-items:center;justify-content:center;font-family:inherit;font-weight:700;
    font-size:13px;letter-spacing:.3px;white-space:nowrap;border-radius:200px;cursor:pointer;
    background:transparent;color:var(--accent-ink);padding:0;border:1px solid transparent;
    transition:background .4s var(--ease),color .34s var(--ease),padding .42s var(--ease),
               font-size .42s var(--ease),border-color .34s,transform .2s var(--ease)}
  header.nav:hover .cta,header.nav:focus-within .cta{background:var(--lime);color:#141414;padding:12px 26px;font-size:14.5px}
  header.nav:hover .cta:hover{background:#e6ff33;transform:scale(1.04)}

  /* touch devices have no hover — show the open state */
  @media (hover:none){
    .nav-in{height:70px}
    .brand{height:36px} .brand .logo-mark{opacity:0} .brand .logo-full{opacity:1;height:36px;width:103px}
    .nav-links a.link,.nav-login{opacity:.9}
    .cta{background:var(--lime);color:#141414;padding:10px 20px}
  }

  .btn{display:inline-flex;align-items:center;justify-content:center;gap:9px;font-family:inherit;font-weight:700;border-radius:200px;cursor:pointer;border:1px solid transparent;font-size:14px;padding:11px 22px;white-space:nowrap;transform-origin:center center;transition:transform .22s var(--ease),background .2s,border-color .2s}
  .btn-primary{background:var(--lime);color:#141414}
  .btn-primary:hover{background:#e6ff33;transform:scale(1.04)}
  .btn-primary:active{transform:scale(.99)}
  .btn-ghost{background:var(--card-bg);color:var(--text-primary);border-color:var(--border-med);backdrop-filter:blur(var(--glass-blur));-webkit-backdrop-filter:blur(var(--glass-blur))}
  .btn-ghost:hover{border-color:var(--border-strong);transform:scale(1.04)}
  .btn-lg{font-size:15px;padding:15px 32px}
  /* Bars are drawn in CSS rather than set as an icon-font glyph, for two reasons: the
     Tabler webfont is a third-party request that can fail, and a glyph cannot animate
     into an X. */
  .menu-btn{display:none;background:none;border:0;color:var(--text-primary);cursor:pointer;
    width:44px;height:44px;padding:0;align-items:center;justify-content:center}
  .mb-bars{position:relative;display:block;width:26px;height:18px}
  .mb-bars span{position:absolute;left:0;width:100%;height:2px;border-radius:2px;background:currentColor;
    transition:top .34s var(--ease-quad),transform .34s var(--ease-quad),opacity .18s linear}
  .mb-bars span:nth-child(1){top:0}
  .mb-bars span:nth-child(2){top:8px}
  .mb-bars span:nth-child(3){top:16px}
  /* Open: the outer bars slide to the middle and cross, the centre one fades out. Sliding
     first and rotating in the same transition is what makes it read as a morph. */
  .menu-btn.open .mb-bars span:nth-child(1){top:8px;transform:rotate(45deg)}
  .menu-btn.open .mb-bars span:nth-child(2){opacity:0}
  .menu-btn.open .mb-bars span:nth-child(3){top:8px;transform:rotate(-45deg)}

  /* Hidden outright at desktop widths — the overlay styles live in the 640px query, so the
     menu cannot be opened at all where .nav-links is the real navigation. */
  .mmenu{display:none}
  html.menu-open{overflow:hidden}

  /* ---- Surfaces ---- */
  .glass{
    background:var(--card-bg);
    border:var(--card-bw) solid var(--card-edge);
    border-radius:var(--r);
    backdrop-filter:blur(var(--glass-blur)) saturate(1.5);
    -webkit-backdrop-filter:blur(var(--glass-blur)) saturate(1.5);
    box-shadow:var(--card-shine), var(--card-drop);
    transition:background .4s var(--ease),border-color .3s var(--ease),box-shadow .3s var(--ease),transform .3s var(--ease),backdrop-filter .4s;
  }
  section{position:relative;padding:88px 0;z-index:1}
  .band{background:var(--band-bg);backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px);transition:background .4s var(--ease)}
  [data-variant="minimal"] .band{backdrop-filter:none;-webkit-backdrop-filter:none}
  .sec-head{max-width:640px;margin:0 0 88px}
  .sec-head.center{margin-left:auto;margin-right:auto;text-align:center}
  /* Requested clause breaks, desktop only. Below 900px the first half stops fitting on one
     line and the forced break would itself create a ragged three-line heading, so it is
     dropped and text-wrap:balance takes the heading back over. */
  .hbr{display:none}

  /* Headings: let the browser balance the line lengths. The manual <br class="hbr"> breaks
     suit the English wording; translated headings are longer and were wrapping into three
     ragged lines with a stranded word, so those markets rely on balancing instead. */
  h1,h2,.sec-head h2,.hero-content h1{text-wrap:balance}
  @media(min-width:900px){ .hbr{display:inline} }
  /* text-wrap:balance evens the line lengths, which is what stops a heading dropping a
     single word onto its own last line. It is used INSTEAD of hard <br> tags because the
     requirement covers mobile and tablet too, and a fixed break cannot follow a fluid
     width — it would fix desktop and create new orphans everywhere else. */
  .sec-head h2{font-size:clamp(28px,3.6vw,40px);line-height:1.1;font-weight:800;letter-spacing:-1px;margin:0 0 14px;
    text-wrap:balance}
  .sec-head p:not(.eyebrow){font-size:17px;color:var(--text-secondary);margin:0}

  /* ---- Photo placeholders ---- */
  .ph{position:relative;border-radius:var(--r);overflow:hidden;background:var(--bg-secondary);border:1px solid var(--border-light)}
  .ph svg{width:100%;height:100%}
  .ph-lab{position:absolute;left:12px;bottom:11px;font-size:10.5px;font-weight:600;color:var(--text-tertiary);background:var(--bg-tertiary);border:1px solid var(--border-light);padding:5px 10px;border-radius:100px}

  /* ---- Hero ---- */
  .hero{position:relative;min-height:100vh;min-height:100svh;display:flex;align-items:center;justify-content:center;text-align:center;overflow:hidden;padding:130px 24px 96px;z-index:1}
  .hero-bg{position:absolute;inset:0;z-index:0;overflow:hidden;background:var(--ph-bg1)}
  .hero-bg svg{width:100%;height:100%}
  .hero-bg video{width:100%;height:100%;object-fit:cover;display:block}
  .hero-poster{position:absolute;inset:0;background-size:cover;background-position:center;display:none;
    background-image:url(/assets/hero-wide-poster.jpg)}
  /* v102: the hero is full-height, so a portrait viewport crops a 16:9 frame to a
     narrow slice. Portrait gets the 9:16 cut of the same footage instead. */
  @media (orientation: portrait){
    .hero-poster{background-image:url(/assets/hero-tall-poster.jpg)}
  }
  @media (prefers-reduced-motion: reduce){
    .hero-bg video{display:none}
    .hero-poster{display:block}
  }
  /* The linear tint no longer starts at the top: it is fully clear to 36% and only begins
     ramping around the middle of the h1, so the video reads at full brightness above the
     text. The radial is a small pool kept behind the copy for legibility, not a wash. */
  .hero-scrim{position:absolute;inset:0;background:
      radial-gradient(76% 48% at 50% 54%, rgba(0,0,0,.26), transparent 74%),
      linear-gradient(to bottom, rgba(0,0,0,0) 50%, rgba(0,0,0,.09) 57%, rgba(0,0,0,.19) 64%, rgba(0,0,0,.30) 71%, rgba(0,0,0,.41) 78%, rgba(0,0,0,.53) 85%, rgba(0,0,0,.64) 92%, rgba(0,0,0,.75) 100%)}
  /* No z-index here. .hero already carries z-index:1, and a second stacking context nested
   inside it puts the hero's own background out of reach of backdrop-filter — any frosted
   element in the hero silently rendered as a flat tint. Paint order is preserved by DOM
   order instead: .hero-bg, then .hero-scrim, then this. The end-of-page CTA never had the
   problem because .final has no z-index, so it only ever made one stacking context. */
.hero-content{position:relative;max-width:860px;margin:0 auto}
  /* Hero keeps one fixed colourway in every theme — the video never changes */
  .hero h1{font-size:clamp(40px,7vw,76px);line-height:1.02;font-weight:800;letter-spacing:-1.8px;margin:0 auto 24px;max-width:15ch;color:#ffffff}
  .hero h1 .done{color:#d9ff00}
  .hero .eyebrow{color:#d9ff00}
  .hero p.sub{font-size:19px;color:#ffffff;max-width:560px;margin:0 auto 34px}
  .hero-cta{display:flex;align-items:center;justify-content:center;gap:16px;flex-wrap:wrap}
  .hero .btn-ghost{background:rgba(255,255,255,.06);border-color:rgba(255,255,255,.34);color:#ffffff;
    /* Same material as every other glass surface on the site. This was the only rule
       anywhere using blur(26px) saturate(1.6) — a one-off that did not paint. */
    -webkit-backdrop-filter:blur(var(--glass-blur)) saturate(1.5);
    backdrop-filter:blur(var(--glass-blur)) saturate(1.5)}
  .hero .btn-ghost:hover{background:rgba(255,255,255,.13);border-color:rgba(255,255,255,.58)}
  /* after the entrance plays the animation is stripped (see script) so the button is no
     longer inside an animated-opacity layer and its blur can sample the video behind it */
  .hero-content.settled > *{opacity:1!important;animation:none!important}
  .hero-note{margin:22px 0 0;font-size:13px;color:rgba(255,255,255,.82);display:flex;align-items:center;justify-content:center;gap:8px;flex-wrap:wrap}
/* Desktop / landscape-tablet: left-align the hero. Placed AFTER the base hero rules so
   source order lets it win at equal specificity. Mobile + portrait tablet stay centered. */
@media(min-width:821px){
  /* Generous left inset so the block breathes instead of hugging the screen edge.
     Scales a little with viewport via clamp; the right stays open over the video. */
  .hero{justify-content:flex-start;text-align:left;padding-left:clamp(48px,7vw,120px)}
  .hero-content{margin:0;max-width:680px}
  .hero h1{margin-left:0;margin-right:0}
  /* .sub had margin:0 auto — the auto was centering it ~60px in, out of line with the
     headline. Pin it left so it starts on the same edge as the title. */
  .hero p.sub{margin-left:0;margin-right:0}
  .hero-cta{justify-content:flex-start}
  .hero-note{justify-content:flex-start}
}
  .hero-note i{color:#d9ff00}

  /* Small text inherits the 1.6 body-copy rhythm, which reads loose on 12-14px labels and
     notes — most visible when one wraps to two lines. 1.45 keeps them legible but tight. */
  .hero-note,.hero-install,.trust,.theme-hint,.tagl,.in-note,.in-you-note,.in-sum,
  .send-note,.pr-alt,.pr-sum,.mini li,.faq-cat-title,.hn-sep,.num,
  .card p,.why p,.ft-brand p{line-height:1.45}
  .hero-tag{position:absolute;right:18px;bottom:16px;z-index:2;font-size:10.5px;font-weight:600;color:var(--text-tertiary);background:var(--bg-tertiary);border:1px solid var(--border-light);padding:5px 11px;border-radius:100px;opacity:.85}
  .scroll-cue{position:absolute;bottom:26px;left:50%;transform:translateX(-50%);z-index:2;color:rgba(255,255,255,.55);font-size:24px;animation:bob 2.2s ease-in-out infinite}
  @keyframes bob{0%,100%{transform:translate(-50%,0)}50%{transform:translate(-50%,8px)}}
  /* hero entrance */
  .hero-content > *{animation:heroUp .9s var(--ease) backwards}
  /* The button row is excluded from the entrance fade. An element mid-animation is its own
     backdrop root, so while it faded the frosted pills inside it could not resolve — they
     appeared flat for the first second or so. The row simply appears; the copy still
     staggers in around it. */
  .hero-content > .hero-cta{animation:none}
  /* animation-fill-mode:forwards holds the final frame AND keeps the element composited.
     Clearing it once the last child has landed restores a normal paint path. */
  .hero.settled .hero-content > *,
  .hero-content.settled > *{opacity:1!important;animation:none!important;transform:none!important}
  .hero-content > *:nth-child(1){animation-delay:.10s}
  .hero-content > *:nth-child(2){animation-delay:.20s}
  .hero-content > *:nth-child(3){animation-delay:.30s}
  .hero-content > *:nth-child(4){animation-delay:.40s}
  .hero-content > *:nth-child(5){animation-delay:.50s}
  @keyframes heroUp{from{opacity:0}to{opacity:1}}

  /* ---- Statement ---- */
  /* z-index:2 beats the global `section{z-index:1}` on line ~170. Without it the next
     section is a later sibling at the SAME z-index and wins the tie, painting its
     translucent band background over the bottom of the bleeding phone. */
  .statement{padding:92px 0;z-index:2}

  /* ---- Who it's for: full-bleed photo strip + floating glass card ----
     DOM order matters: the strip comes FIRST so it is painted before the card.
     backdrop-filter samples only what is already painted behind it, so a card
     placed earlier in the DOM would blur the page background and not the photos.
     For the same reason the card must never carry .rv — the reveal animates
     opacity, which would make the card its own backdrop root and kill the blur.
     The card is absolutely positioned instead, and pulled up over the strip edge.
     --who-pad is shared by the padding and the card offset: if they drift apart the
     card stops straddling the strip edge. It also buys clearance from the statement
     phones, which bleed 28px down into the top of this section. */
  .who{--who-pad:234px;padding:var(--who-pad) 0 0;position:relative}

  /* Flex, not grid, so a hovered tile can widen and its neighbours give way.
     Height is set on the strip rather than aspect-ratio on the tile: with
     aspect-ratio the strip would change height as a tile grows. 31.25vw keeps
     each of the four tiles at 4:5 at rest. */
  .who-strip{display:flex;height:clamp(420px,31.25vw,700px)}
  /* The reveal lives on .tile-in, NOT on .tile. .rv sets the `transition` shorthand and
     sits later in this stylesheet, so a .rv on the tile silently wiped out the flex-grow
     transition and the hover snapped. The observer also writes transition-delay inline,
     which would have delayed the hover by up to 210ms. Keeping them on separate elements
     avoids both. */
  .tile{position:relative;margin:0;flex:1 1 0;min-width:0;height:100%;overflow:hidden;
    transition:flex-grow .6s var(--ease-quad)}
  .tile:hover{flex-grow:1.3}
  .tile-in{position:absolute;inset:0}
  .tile img{display:block;width:100%;height:100%;object-fit:cover;
    filter:grayscale(1) contrast(1.03);transition:filter .6s var(--ease-quad)}
  .tile:hover img{filter:grayscale(0) contrast(1)}
  /* Scrim and caption stay fixed across all four themes, like the hero. */
  /* Side padding is fluid rather than stepped: a breakpoint would leave a band where the
     padding has jumped back to 30px but the title font has not yet reached its 32px clamp,
     and the two-line fit fails inside it. */
  .tile figcaption{position:absolute;left:0;right:0;bottom:0;z-index:2;
    padding:34px clamp(18px,2vw,30px) 30px;
    background:linear-gradient(to top,rgba(0,0,0,.88),rgba(0,0,0,.46) 48%,transparent)}
  /* Same scale as .spot-text h3 and the standard 17px body — no ad-hoc sizes here. */
  /* 2.35vw, not 3vw. Each title is hard-broken after its ampersand, and the longest
     resulting half — 'Consultants &' — measures 7.43em in Montserrat 800. For that half to
     stay on one line the tile must give it 7.43 x font-size of room, and it has to hold
     while a SIBLING is hovered, when this tile shrinks to 23.3% of the strip. 3vw failed
     that from about 1200px down and the title spilled to three lines.
     The 19px floor only bites below ~810px, where the 820px query takes over anyway. */
  .tile figcaption h3{color:var(--lime);font-size:clamp(19px,2.35vw,32px);font-weight:800;
    letter-spacing:-.6px;line-height:1.12;margin:0 0 8px}
  .tile figcaption p{color:rgba(255,255,255,.82);font-size:17px;line-height:1.5;margin:0;max-width:26ch}

  .who-card{position:absolute;top:var(--who-pad);left:50%;transform:translate(-50%,-70%);z-index:3;
    width:min(660px,calc(100% - 48px));padding:34px 40px 36px;text-align:center}
  /* The card wraps the global .sec-head so its type is identical to every other
     section heading. Only the outer spacing is overridden. */
  .who-card .sec-head{margin:0;max-width:none}
  .who-card .sec-head p:not(.eyebrow){color:var(--text-primary)}

  @media(max-width:920px){
    /* Horizontal swipe strip; card returns to normal flow above it via order.
       v103: was 820px. .st-grid/.st-phones switch to their stacked layout at 920px,
       so between 821-920px (portrait tablets) the phones stacked while this section
       kept its DESKTOP layout -- the who-card stayed absolutely positioned and pulled
       up by -70%, which is what opened the gap under the mockups, and the tiles stayed
       a static 4-up row instead of the swipe strip. These two must move together. */
    /* 80px here sat on top of .statement's own bottom padding, which measured 102px of
       dead space between the phone mockups and the card. The card already overlaps the
       strip below it, so this only needs to clear the phones. */
    .who{display:flex;flex-direction:column;padding:28px 0 0}
    /* Paired with the .who padding above. Both need to change from the SAME breakpoint —
       the who-section switches to its stacked/strip layout at 820px, and leaving the
       statement's larger desktop padding in place until 640px left a 120px hole at
       tablet widths. */
    .statement{padding-bottom:32px}
    /* Overlap the strip so the blur has photos behind it. z-index is doing the real work:
       flex `order` reorders painting too, so without it the card paints BEFORE the strip
       and backdrop-filter would sample the page background instead of the photos. */
    .who-card{position:relative;top:auto;left:auto;z-index:3;transform:none;order:-1;width:calc(100% - 40px);margin:0 auto -52px}
    .who-strip{order:1;height:clamp(400px,97.5vw,560px);
      overflow-x:auto;scroll-snap-type:x mandatory;scrollbar-width:none}
    .who-strip::-webkit-scrollbar{display:none}
    /* 78% of the viewport is much wider than a quarter-strip tile, so there is room to
       spare here — don't inherit the tightened desktop title size. */
    /* 78% of a 390px phone is a 304px tile; 78% of a 912px tablet is 711px, which
       shows barely one tile and reads as broken rather than swipeable. The clamp
       keeps phones exactly as they were and caps the tablet at ~1.6 tiles in view. */
    .tile{flex:0 0 clamp(280px,78%,560px);scroll-snap-align:center}
    .tile figcaption h3{font-size:clamp(24px,3vw,32px)}
    .tile:hover{flex-grow:0}
  }

  .st-grid{display:grid;grid-template-columns:1.02fr .98fr;gap:54px;align-items:center}
  .st-text h2{font-size:clamp(30px,4vw,44px);font-weight:800;letter-spacing:-1.2px;line-height:1.06;margin:0 0 18px}
  .st-lead{font-size:17px;color:var(--text-secondary);margin:0 0 28px;max-width:520px}
  .st-lead .hl{color:var(--accent-ink);font-weight:700}
  /* The three facts use the same frosted panel and hairline dividers as the "How it works"
     steps, so the page repeats one pattern instead of introducing another. Equal columns
     mean a label that wraps — which happens in German, Spanish and Portuguese — can never
     leave the three sitting at different heights. */
  .st-stats{--st-gap:32px;display:grid;grid-template-columns:repeat(3,1fr);gap:var(--st-gap);
    padding:22px 24px}
  .st-stat{position:relative;min-width:0}
  .st-stat:not(:first-child)::before{content:'';position:absolute;top:-2px;bottom:-2px;
    left:calc(var(--st-gap) / -2);width:1px;background:rgba(255,255,255,.15)}
  .st-stat b{display:block;font-size:22px;font-weight:800;letter-spacing:-.5px;line-height:1.1;
    color:var(--lime)}
  .st-stat span{display:block;font-size:13px;line-height:1.35;color:var(--text-secondary);
    margin-top:6px}
  /* Solid lime, not glass. These opt OUT of the card tokens entirely rather than
     overriding them, so --card-bg / --card-shine / --card-drop stay untouched. The ink is
     fixed near-black in all four themes because the lime itself never changes. */
  
  
  
  /* ---- Phone mockups (statement) ---- */
  /* No auto margins here: auto margins on a grid item cancel stretch alignment, the box
     shrink-wraps to its content, and both children are absolutely positioned -> width 0.
     Use justify-self to centre/start instead.
     The box hugs the phone group rather than the column, so the % offsets below stay stable.
     margin-block is negative and larger than .statement's 92px padding: the group therefore
     claims less height than it occupies and bleeds ~28px past the section edge top and bottom.
     .wrap already carries z-index:1, which paints this over the next section's background. */
  .st-phones{position:relative;width:100%;max-width:392px;justify-self:start;aspect-ratio:1/1.84;margin-block:-120px}
  .pm{position:absolute;width:auto;filter:drop-shadow(0 40px 55px rgba(0,0,0,.5))}
  @media(hover:hover){
    /* Restating the full transition list is deliberate. .rv sets `transition` as a
       shorthand, which resets transition-property to just opacity+transform, so `translate`
       would jump instead of easing. These two rules reproduce the reveal timings exactly
       (.rv-down = .85s, .rv-up = 1s, both inherited from .rv's duration override) and add
       translate on top. IF THOSE REVEAL DURATIONS EVER CHANGE, CHANGE THEM HERE TOO. */
    .st-phones .pm-back{transition:opacity .85s var(--ease),transform .85s var(--ease),translate .9s var(--ease-quad)}
    .st-phones .pm-front{transition:opacity 1s var(--ease),transform 1s var(--ease),translate .9s var(--ease-quad)}
    /* Each phone answers to its own pointer. The front phone's box overlaps the back one by
       only 60 of its 219px, and neither image has transparent side margins, so ~72% of the
       back phone stays directly hoverable. */
    .st-phones .pm:hover{translate:0 -22px}
  }
  @media(prefers-reduced-motion: reduce){
    /* The global reduced-motion rule collapses durations to .01ms, which would turn the
       lift into an instant jump. Better to have no lift at all. */
    .st-phones .pm:hover{translate:none}
  }
  .pm-back{left:0;top:0;height:76.6%;z-index:1}
  .pm-front{left:40.3%;top:20.5%;height:79.5%;z-index:2}
  @media(max-width:920px){ .st-grid{grid-template-columns:1fr;gap:30px} .st-phones{max-width:330px;justify-self:center;margin-block:0} }

  /* ---- People ---- */
  .people{display:grid;grid-template-columns:repeat(4,1fr);gap:16px}
  .person .ph{aspect-ratio:3/4}
  .person h3{font-size:15px;font-weight:700;margin:14px 0 4px}
  .person p{font-size:13.5px;color:var(--text-secondary);margin:0}

  /* ---- Cards (even grid) ---- */
  .feat-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:16px;align-items:stretch}
  /* Deliberately identical to .gal-dot so the two swipeable rows on this page behave and
     look the same. Hidden until the grid becomes a strip. */
  .feat-dots,.pr-dots{display:none;gap:9px;justify-content:center;margin-top:22px}
  .feat-dot,.pr-dot{width:9px;height:9px;padding:0;border:0;border-radius:99px;cursor:pointer;
    background:var(--border-strong);transition:background .45s var(--ease-quad),width .45s var(--ease-quad)}
  .feat-dot.on,.pr-dot.on{background:var(--accent-ink);width:28px}

  .card{position:relative;padding:26px 24px;height:100%;display:flex;flex-direction:column}
  .card .ic{width:46px;height:46px;border-radius:13px;background:rgba(217,255,0,.10);border:1px solid rgba(217,255,0,.26);display:flex;align-items:center;justify-content:center;color:var(--accent-ink);font-size:22px;margin-bottom:17px;transform-origin:center center;transition:transform .32s var(--ease),background .3s}
  .card:hover .ic{transform:scale(1.08);background:rgba(217,255,0,.18)}

  /* ---- One shared hover for every card-like surface: inner lime glow in / out ---- */
  .hov{position:relative;transform-origin:center center;transition:transform .32s var(--ease),border-color .32s var(--ease)}
  .hov::after{content:'';position:absolute;inset:0;border-radius:inherit;pointer-events:none;opacity:0;transition:opacity .32s var(--ease);box-shadow:inset 0 0 30px -6px rgba(217,255,0,.55), inset 0 0 0 1px rgba(217,255,0,.40)}
  /* Minimal: clean lime outline only — no inner glow */
  [data-variant="minimal"] .hov::after{box-shadow:inset 0 0 0 1px rgba(217,255,0,.90)}
  /* The chip behind each feature icon is a hard-coded lime tint rather than a token. With
     the glyph now grey, a pale lime pill around it looks like an oversight, so the chip
     goes neutral too on light themes. */
  [data-theme="light"] .card .ic{background:rgba(17,17,17,.045);border-color:rgba(17,17,17,.12)}
  .hov:hover{transform:scale(1.02)}
  .hov:hover::after{opacity:1}
  .card h3{font-size:16px;font-weight:700;margin:0 0 12px;letter-spacing:-.2px}
  .card p{font-size:14px;color:var(--text-secondary);margin:0}
  /* Shared responsive card grid — set --cols per use (in-why=3, pr-groups=2); stacks to one
     column on narrow screens. NOTE: .feat-grid is a separate component (horizontal carousel on
     mobile), so it deliberately does not use this. */
  .card-grid{display:grid;grid-template-columns:repeat(var(--cols,3),minmax(0,1fr));gap:16px}
  @media(max-width:820px){ .card-grid{grid-template-columns:1fr} }
  .card.accent-pink .ic{background:rgba(248,0,128,.10);border-color:rgba(248,0,128,.26);color:var(--pink)}
  .card.accent-pink:hover .ic{background:rgba(248,0,128,.18)}
  .card.accent-blue .ic{background:rgba(0,166,255,.10);border-color:rgba(0,166,255,.26);color:var(--blue)}
  .card.accent-blue:hover .ic{background:rgba(0,166,255,.18)}
  .chips{display:flex;flex-wrap:wrap;gap:7px;margin-top:auto;padding-top:14px}
  .chip{font-size:10.5px;font-weight:600;color:var(--text-secondary);background:var(--bg-secondary);border:1px solid var(--border-light);border-radius:100px;padding:4px 10px}

  /* ---- Screenshot frame ---- */
  .shot img{display:block;width:100%;height:100%;object-fit:cover}

  /* ---- Screenshot gallery ----
     Native scroll-snap does the swiping on touch; the arrows tween scrollLeft in JS so
     the motion uses easeInOutQuad rather than the browser's uncontrollable smooth scroll.
     The nav buttons deliberately use a SOLID background, not glass: .gal carries .rv,
     whose opacity animation would make it a backdrop root and silently kill any
     backdrop-filter on a descendant. */
  /* Full-bleed peek gallery: the track spans the window, slides are narrower than it,
     so the neighbours stay visible on both sides and only clip at the window edge.
     The inline padding equals the empty space beside a centred slide, which is what
     lets the first and last slides sit centred rather than flush.
     padding-block is NOT decoration: overflow-x:auto forces overflow-y to compute to
     auto, so the track clips vertically — without headroom the .hov scale(1.02) was
     shaving the top off the screenshot. */
  .gal{--slide-w:min(1120px,76vw);position:relative;margin-top:-18px}
  .gal-track{display:flex;gap:26px;overflow-x:auto;scroll-snap-type:x mandatory;scrollbar-width:none;
    padding:18px calc((100vw - var(--slide-w))/2);
    scroll-padding-inline:calc((100vw - var(--slide-w))/2)}
  .gal-track::-webkit-scrollbar{display:none}
  .gal-slide{flex:0 0 var(--slide-w);scroll-snap-align:center;margin:0;min-width:0}
  .gal-slide .shot{aspect-ratio:2240/1263}
  /* index 'See it in action' shows real app screenshots at their native shape (~1.68),
     uncropped: contain them in one uniform frame; the dark padding blends with the app bg. */
  #galTrack .shot{aspect-ratio:2240/1335;background:#0e0e11}
  #galTrack .shot img{object-fit:contain}
  .gal-cap{margin-top:18px;text-align:center;font-size:17px;color:var(--text-secondary)}
  .gal-cap strong{color:var(--text-primary);font-weight:700}
  /* <button> does not inherit color — set it explicitly or these render near-black. */
  .gal-nav{position:absolute;top:calc(50% - 34px);transform:translateY(-50%);z-index:4;
    width:54px;height:54px;border-radius:50%;display:grid;place-items:center;cursor:pointer;
    background:var(--bg-tertiary);border:1px solid var(--border-med);color:var(--text-primary);
    font-family:inherit;font-size:30px;line-height:0;padding:0 0 4px;
    box-shadow:0 10px 30px -12px rgba(0,0,0,.7);
    transition:opacity .45s var(--ease-quad),transform .45s var(--ease-quad),border-color .45s var(--ease-quad)}
  .gal-nav:hover:not([disabled]){transform:translateY(-50%) scale(1.09);border-color:rgba(217,255,0,.55)}
  .gal-nav[disabled]{opacity:.28;cursor:default}
  /* Sit the arrows on the centre slide's own edges, not the window's. The button is 54px
     wide, so subtracting 27px straddles it half on the image and half on the gap.
     max() keeps them on screen if the viewport is narrower than the slide. */
  .gal-nav.prev{left:max(8px,calc((100vw - var(--slide-w))/2 - 27px))}
  .gal-nav.next{right:max(8px,calc((100vw - var(--slide-w))/2 - 27px))}
  .gal-dots{display:flex;gap:9px;justify-content:center;margin-top:22px}
  .gal-dot{width:9px;height:9px;padding:0;border:0;border-radius:99px;cursor:pointer;
    background:var(--border-strong);transition:background .45s var(--ease-quad),width .45s var(--ease-quad)}
  .gal-dot.on{background:var(--accent-ink);width:28px}
  @media(max-width:700px){ .gal-nav{display:none} .gal{--slide-w:86vw} .gal-track{gap:16px} }

  .shot{width:100%;background:var(--bg-secondary);border:1px solid var(--border-med);border-radius:var(--r);overflow:hidden;box-shadow:0 44px 90px -55px rgba(0,0,0,.9);position:relative}
  .showcase .shot{max-width:960px;margin:0 auto}

  /* ---- Spotlights ---- */
  .spot{display:grid;grid-template-columns:1fr 1.1fr;gap:20px;align-items:stretch;padding:26px 0}
  /* The copy sits in a frosted card beside the photo, matching the step-by-step rows on
     the install page. .spot-text must NOT carry .rv — the reveal animates opacity, which
     would make the card its own backdrop root and kill the blur (see the notes on .gal
     and the pricing card). The reveal stays on the .spot row instead. */
  .spot-text{background:var(--card-bg);border:var(--card-bw) solid var(--card-edge);
    border-radius:var(--r);backdrop-filter:blur(var(--glass-blur)) saturate(1.5);
    -webkit-backdrop-filter:blur(var(--glass-blur)) saturate(1.5);
    box-shadow:var(--card-shine),var(--card-drop);
    padding:34px 32px;display:flex;flex-direction:column;justify-content:center}
  .spot.rev .spot-text{order:2}
  .spot-img{width:100%;height:auto;display:block;border-radius:var(--r)}
  .spot-text h3{font-size:clamp(24px,3vw,32px);font-weight:800;letter-spacing:-.6px;margin:0 0 14px;line-height:1.12;
    text-wrap:balance}
  /* :not(.eyebrow) or this (0,1,1) selector beats .eyebrow (0,1,0) and the pretitle
     renders grey at 16px instead of lime at 13px. Identical to the .sec-head p case. */
  .spot-text p:not(.eyebrow){font-size:16px;color:var(--text-secondary);margin:0 0 18px}
  .mini{list-style:none;padding:0;margin:0}
  .mini li{display:flex;gap:10px;align-items:flex-start;font-size:14px;margin:9px 0}
  .mini li i{color:var(--accent-ink);margin-top:3px}

  /* ---- Themes ---- */
  .theme-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:16px}
  .theme-card{font-family:inherit;color:var(--text-primary);text-align:left;cursor:pointer;padding:0;overflow:hidden}
  .theme-card[aria-pressed="true"]{border-color:var(--lime);box-shadow:0 0 0 1px var(--lime), var(--card-drop)}
  .swatch{height:116px;padding:14px;display:flex;flex-direction:column;justify-content:flex-end}
  .sw-bar{height:9px;border-radius:5px;display:block}
  .sw-card{border-radius:10px;padding:10px;display:flex;flex-direction:column;gap:6px}
  .theme-meta{padding:13px 15px 15px;display:flex;align-items:center;justify-content:space-between;gap:8px}
  .theme-meta h3{font-size:14px;font-weight:700;margin:0}
  .theme-meta .tick{color:var(--lime);font-size:18px;opacity:0;transform:scale(.6);transition:opacity .25s,transform .25s var(--ease)}
  .theme-card[aria-pressed="true"] .theme-meta .tick{opacity:1;transform:scale(1)}
  .theme-hint{font-size:13.5px;color:var(--text-tertiary);text-align:center;margin:24px 0 0;display:flex;align-items:center;justify-content:center;gap:8px}

  /* ---- Steps ---- */
  /* ---- How it works: full-height, over a photograph ----
     Type in this section is theme-INDEPENDENT. It always sits on a photo, so it stays
     light in all four theme combinations — the same rule the who-tiles and the final CTA
     already follow. Nothing here reads a --text-* token. */
  .how{position:relative;overflow:hidden;padding:88px 0 76px;
    min-height:100vh;min-height:100svh;
    display:flex;flex-direction:column;justify-content:space-between;gap:56px}
  /* An <img> rather than a background-image, matching the final CTA: object-fit and
     background-size compute their crops differently, and every full-bleed photo on this
     page uses the img form so they all crop by one rule. */
  /* The reveal scale lives on the CONTAINER and the parallax offset on the plates inside
     it. Two elements, so the two effects compose instead of overwriting each other's
     transform. */
  .how-bg{position:absolute;inset:0;z-index:0;
    transform:scale(1.14);transform-origin:center center;
    transition:transform 1.8s var(--ease)}
  /* Identical element type, object-fit, object-position and box on all three plates means
     they crop pixel-for-pixel identically at any width — the same rule the final CTA scenes
     rely on — so the scene stays registered however the section is sized.
     The 1.10 overscale is headroom for the parallax, not styling: at scale 1 a translated
     plate would drag its own edge into frame. 10% gives 5% of the box on each side, which
     at the tightest realistic window (~1000x563) is 28px vertically against the 17px the
     front plate ever travels. */
  .how-layer{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
    object-position:center;
    transform:scale(1.10);transform-origin:center center;
    transition:translate .6s var(--ease)}
  /* One-shot reveal. The observer adds .reveal the first time the section comes into view
     and then disconnects, so the photo eases down from oversized to its natural crop once
     and stays settled — it does not replay on the way back up.
     Two reasons this is safe to scale: .how already clips (overflow:hidden), so the
     oversized start never bleeds into the neighbouring sections; and nothing inside .how is
     glass, so this transform cannot become a containing block that shifts a backdrop-filter
     the way it would inside a card. */
  .how.reveal .how-bg{transform:scale(1)}
  /* Used for one frame while the reveal re-arms, so the jump back to the oversized start
     is instant rather than a visible 1.8s scale-up. */
  .how-bg.rearm{transition:none}
  @media(prefers-reduced-motion: reduce){
    /* No reveal scale and no parallax offset — the plates simply sit registered. */
    .how-bg,.how.reveal .how-bg{transform:none;transition:none}
    .how-layer{translate:none !important}
  }

  /* Two stacked gradients. The first is the requested black-to-transparent lift off the
     base, clear by a little over half height. The second is a much gentler wash from the
     top so the heading holds contrast on a bright photo — drop that second line if the
     final photo is dark enough not to need it. */
  /* Two stacked gradients, and both are tuned against THIS photograph by measurement, not
     by eye: white on the worst-lit patch behind the heading (the sunlit cabinet, upper
     right) measured only 2.90:1 at 1280px with a lighter veil. The strengths below hold
     every text element above 4.5:1 at 1920/1440/1280/390.
     - to top   = the base lift that was asked for, so the steps read.
     - to bottom = a top wash purely for heading legibility. If the photo is ever swapped
       for a darker one, this second gradient is the one to soften first. */
  .how-veil{position:absolute;inset:0;z-index:1;pointer-events:none;
    background:linear-gradient(to bottom,rgba(0,0,0,.55) 0%,rgba(0,0,0,.34) 18%,rgba(0,0,0,.12) 38%,rgba(0,0,0,0) 56%)}
  .how-top,.how-bottom{position:relative;z-index:2}
  /* justify-content on .how does the vertical spacing, so the heading drops its margin. */
  .how .sec-head{margin:0 auto}
  .how .sec-head h2{color:#fff}
  .how .sec-head .eyebrow{color:var(--lime)}

  .steps{--step-gap:56px;display:grid;grid-template-columns:repeat(3,1fr);gap:var(--step-gap)}
  .how .steps{padding:34px 34px 26px;background:rgba(18,18,22,.34)}
  .how .steps .step:first-child{padding-top:0}
  .step{position:relative;padding-top:8px}
  /* The rule sits at the exact centre of the grid gap, so the space either side of it is
     equal by construction — no hand-tuned padding to drift out of sync with the gap.
     Change --step-gap alone and the dividers stay centred. */
  /* Flat 1px, no end-fade, matching every other divider. The value is written out rather
     than read from --rule because this section keeps one fixed colourway on a dark
     photograph in all four themes — --rule would flip to black on the light themes. It is
     the dark-theme value of --rule exactly: keep the two in step. */
  .how .step:not(:first-child)::before{content:'';position:absolute;z-index:1;
    top:0;bottom:0;left:calc(var(--step-gap) / -2);width:1px;
    background:rgba(255,255,255,.15)}
  .step .num{font-size:14px;font-weight:800;color:var(--accent-ink);letter-spacing:1px}
  .step h3{font-size:19px;font-weight:700;margin:14px 0 8px;letter-spacing:-.3px}
  .step p{font-size:15px;color:var(--text-secondary);margin:0}
  .how .step .num{color:var(--lime)}
  .how .step h3{color:#fff}
  .how .step p{color:rgba(255,255,255,.80)}


  /* ---- Pricing ---- */
  .price-wrap{max-width:560px;margin:0 auto}
  .price{position:relative;border-radius:var(--r);padding:42px;text-align:center;overflow:hidden;border-color:rgba(217,255,0,.30)}
  .bill-toggle{display:inline-flex;padding:4px;gap:4px;border-radius:200px;background:var(--bg-secondary);border:1px solid var(--border-light);margin:0 0 22px}
  .bt{font-family:inherit;font-size:13px;font-weight:700;color:var(--text-secondary);background:transparent;border:0;
      padding:8px 18px;border-radius:200px;cursor:pointer;transition:background .28s var(--ease),color .28s var(--ease)}
  .bt.active{background:var(--lime);color:#141414}
  .price .amt{font-size:64px;font-weight:900;letter-spacing:-2px;line-height:1}
  /* "9 €" puts a literal space between figure and symbol. At 64px that space
     renders ~17px wide and the pair reads as two separate words. Tightened on
     the figure element only, so the price strings themselves are untouched —
     check_prices.py asserts them literally. No effect on A$12 / US$8.50 /
     £7.50 / C$12, none of which contain a space. */
  .price .amt [data-price-amt]{word-spacing:-.12em}
  /* Toggling the period crossfades the figure instead of snapping the text. .pf-out lifts
     it away, the text is replaced at the midpoint, .pf-in drops it back in from below.
     Both live on descendants of .price, never on an ancestor of it: a transform or a
     fractional opacity on .price itself would break its own backdrop-filter. */
  .price .amt,.price .flat{transition:opacity .19s var(--ease-quad),transform .19s var(--ease-quad)}
  .price .amt.pf-out,.price .flat.pf-out{opacity:0;transform:translateY(-9px)}
  .price .amt.pf-in,.price .flat.pf-in{opacity:0;transform:translateY(9px);transition:none}
  .price .amt small{font-size:18px;font-weight:600;color:var(--text-secondary);letter-spacing:0}
  .price .flat{font-size:15px;color:var(--accent-ink);font-weight:700;margin:10px 0 4px}
  .price ul{list-style:none;padding:0;margin:26px 0 30px;text-align:left;display:inline-block}
  .price ul li{display:flex;gap:11px;align-items:flex-start;font-size:15px;margin:12px 0}
  .price ul li i{color:var(--accent-ink);font-size:18px;margin-top:2px;flex-shrink:0}
  .price ul li span.mut{color:var(--text-secondary)}
  .price .btn{width:100%} .price .trust{margin:16px 0 0;font-size:13px;color:var(--text-secondary)}

  /* ---- Why ---- */
  .why-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;align-items:stretch}
  .why-dots{display:none;gap:9px;justify-content:center;margin-top:22px}
  .why-dot{width:9px;height:9px;padding:0;border:0;border-radius:99px;cursor:pointer;
    background:var(--border-strong);transition:background .45s var(--ease-quad),width .45s var(--ease-quad)}
  .why-dot.on{background:var(--accent-ink);width:28px}
  .why{padding:28px;height:100%}
  /* width:fit-content is what makes transform-origin:center actually centre on the icon.
     As a plain block the <i> stretched the full card width (289px at 1440 for a 24px glyph),
     so "center" resolved ~130px to the right of the glyph and the hover scale threw it
     sideways instead of growing it in place. Kept as a block, not inline-block, so the
     margin-bottom and line box are unchanged. */
  .why i{font-size:24px;color:var(--accent-ink);margin-bottom:14px;display:block;width:fit-content;
    transform-origin:center center;transition:transform .32s var(--ease)}
  .why:hover i{transform:scale(1.1)}
  .why h3{font-size:16px;font-weight:700;margin:0 0 8px} .why p{font-size:14px;color:var(--text-secondary);margin:0}

  /* ---- FAQ (animated open/close) ---- */
  .faq-wrap{max-width:780px;margin:0 auto}
  details.faq{border-bottom:1px solid var(--rule)}
  details.faq summary{list-style:none;cursor:pointer;display:flex;align-items:center;justify-content:space-between;gap:20px;padding:22px 4px;font-size:17px;font-weight:600;letter-spacing:-.2px;transition:color .2s}
  details.faq summary::-webkit-details-marker{display:none}
  details.faq summary:hover{color:var(--accent-ink)}
  details.faq summary i{color:var(--text-tertiary);font-size:22px;flex-shrink:0;transition:transform .38s var(--ease),color .25s}
  details.faq[open]:not(.closing) summary i{transform:rotate(135deg);color:var(--accent-ink)}
  .faq-body{display:grid;grid-template-rows:0fr;transition:grid-template-rows .38s var(--ease),opacity .3s ease;opacity:0}
  details.faq[open]:not(.closing):not(.opening) .faq-body{grid-template-rows:1fr;opacity:1}
  .faq-body > div{overflow:hidden}
  .faq-body p{margin:0 0 22px;color:var(--text-secondary);font-size:15px;max-width:640px}

  /* ---- Final CTA ---- */
  /* ---- Final CTA: layered composite ----
     Stack, bottom to top: background room photo -> tagline SVG -> girl cutout ->
     front plate (scene 2 only) -> copy.
     The girl is the SAME figure as the background, on transparent, so she masks the
     tagline exactly where she overlaps — the lime letters read as passing behind her.
     Fixed colourway (lime + white on the dark room), like the hero — no theme tokens. */
  .final{position:relative;min-height:100svh;display:flex;flex-direction:column;
    justify-content:flex-end;align-items:center;text-align:center;overflow:hidden;padding:0}
  /* Background + girl are BOTH <img> now, sharing one rule below. Same element type +
     same object-fit + same object-position + same dimensions => they crop pixel-for-pixel
     identically at any width. (A background-size:cover div and an object-fit:cover img can
     round their crops differently — that mismatch was the drift.) */
  .final-bg{z-index:0}
  .final-scrim{position:absolute;inset:0;z-index:1;
    background:linear-gradient(to bottom, transparent 0%, transparent 58%, rgba(10,10,10,.72) 100%)}
  /* tagline band, vertically centred on the girl's upper body, behind her (z-index:2) */
  .final-tagline{position:absolute;left:0;right:0;top:15%;z-index:2;display:flex;justify-content:center;
    padding:0 3vw;pointer-events:none}
  .final-tagline svg{width:min(1340px,86vw);height:auto;display:block;
    /* start hidden and pushed down; .reveal lifts it. clip-path hides the descend below its band */
    transform:translateY(46px);opacity:0;transition:transform 1s var(--ease-quad),opacity .9s var(--ease-quad)}
  .final.reveal .final-tagline svg{transform:none;opacity:1}
  /* girl cutout: same width/anchor as the background so she lines up over her own photo */
  /* --- Scene carousel: two stacks (backgrounds z0, girls z3) cross-fade together. ---
     Each stack carries its own mouse-parallax offset (--px/--py), set per scene by the
     script. Scenes whose figure is baked into the background photo get the SAME vector on
     both stacks, so the cutout never leaves its own copy; the scene that ships an empty
     plate gets a separate, larger vector on the cutout. Only the active scene is opaque,
     which is why switching whole-stack vectors is enough. */
  .final-bgs, .final-girls, .final-fronts{position:absolute;inset:0;
    transform:translate3d(var(--px,0),var(--py,0),0);
    transition:transform .5s cubic-bezier(.16,.62,.28,1)}
  .final-girls{z-index:3}
  /* Explicit z-index rather than leaning on DOM order, so the front plate cannot be
     reordered out of place by a later markup edit. .final-copy moves to 5 to stay on top. */
  .final-fronts{z-index:4}
  .scene-bg, .scene-girl, .scene-front{position:absolute;inset:0;width:100%;height:100%;
    object-fit:cover;object-position:center;pointer-events:none;
    opacity:0;transform:scale(1.06);will-change:opacity,transform;
    /* INACTIVE: fade out over 1.2s. The scale reset is DELAYED by 1.2s (transform 0s ease 1.2s)
       so the outgoing image HOLDS its grown scale through the whole fade and only snaps back
       once it is fully invisible — this removes the size "jump" during the cross-fade. */
    transition:opacity 1.2s ease, transform 0s ease 1.2s}
  .scene-bg.active, .scene-girl.active, .scene-front.active{opacity:1;transform:scale(1.13);
    /* ACTIVE: fade in over 1.2s and Ken-Burns zoom 1.06 -> 1.13 over 7s (no delay). */
    transition:opacity 1.2s ease, transform 7s ease-out}
  @media(prefers-reduced-motion: reduce){
    .scene-bg, .scene-girl, .scene-front,
    .scene-bg.active, .scene-girl.active, .scene-front.active{transform:none;transition:opacity 1.2s ease}
  }
  /* The tagline band carries the parallax; the SVG inside keeps its reveal transform.
     On the layered scene this band sits between the two photo depths, so the lettering
     reads as hanging in the room rather than pinned to either plane. */
  .final-tagline{transform:translate3d(var(--px,0),var(--py,0),0);
    transition:transform .5s cubic-bezier(.16,.62,.28,1)}
  /* scene dots */
  .final-dots{display:flex;gap:10px;justify-content:center;margin-top:26px}
  .fdot{width:9px;height:9px;padding:0;border:0;border-radius:50%;cursor:pointer;
    background:rgba(255,255,255,.4);transition:background .25s, transform .25s}
  .fdot:hover{background:rgba(255,255,255,.7)}
  .fdot.active{background:#d9ff00;transform:scale(1.25)}
  @media(prefers-reduced-motion: reduce){
    .scene-bg.active, .scene-girl.active, .scene-front.active{animation:none;transform:scale(1.08)}
  }
  .final-copy{position:relative;z-index:5;padding-bottom:min(9vh,90px);max-width:640px}
  .final-copy p{font-size:clamp(17px,2vw,20px);color:#fff;margin:0 0 26px;
    text-shadow:0 2px 18px rgba(0,0,0,.6)}
  /* Deliberately the same clamp, weight, tracking and leading as .sec-head h2, so the CTA
     headline reads at the same rank as every other section heading on the page. Kept as a
     separate rule rather than reusing .sec-head because this section is fixed white on a
     photograph and takes no theme tokens. If .sec-head h2 is ever resized, resize this too. */
  .final-h{font-size:clamp(28px,3.6vw,40px);line-height:1.1;font-weight:800;
    letter-spacing:-1px;color:#fff;margin:0 0 12px;
    text-shadow:0 2px 18px rgba(0,0,0,.6);text-wrap:balance}
  @media(max-width:700px){
    /* The section was a full 100svh with only ~286px of content in it, leaving well over
       500px of empty photograph on a phone. Shorter, with a floor so the tagline and copy
       never crowd each other on a small handset. */
    .final{min-height:max(560px,72svh)}
    .final-tagline{top:12%}
    .final-tagline svg{width:88vw}
    .final-copy{padding-bottom:44px}
  }

  /* ---- Footer ---- */
  footer.ft{border-top:1px solid var(--rule);padding:58px 0 40px;background:var(--band-bg);position:relative;z-index:1;-webkit-backdrop-filter:blur(var(--glass-blur)) saturate(1.4);backdrop-filter:blur(var(--glass-blur)) saturate(1.4);transition:background .4s var(--ease),backdrop-filter .4s var(--ease)}
[data-variant="minimal"] footer.ft{-webkit-backdrop-filter:none;backdrop-filter:none}
/* Minimal sets --glass-blur to 0, so without this the menu keeps a 72% tint with no
   blur behind it -- the page reads through sharply, which is the one thing minimal is
   not meant to do. Same treatment the footer and .band already get. */
[data-variant="minimal"] .mmenu{background:var(--bg-primary);
  -webkit-backdrop-filter:none;backdrop-filter:none}


  .ft-grid{display:flex;justify-content:space-between;gap:40px;flex-wrap:wrap;margin-bottom:40px}
  .ft-brand{max-width:320px} .ft-brand p{color:var(--text-tertiary);font-size:14px;margin:20px 0 0}
  /* currentColor + --logo-ink, matching the nav logo: lime on dark themes, near-black on
     light ones. The 'billed' letters are knockouts, so they simply show the page behind. */
  .ft-logo{display:block;width:210px;color:var(--logo-ink);transform-origin:left center;
    transition:transform .3s var(--ease),color .4s var(--ease)}
  .ft-logo:hover{transform:scale(1.03)}
  .ft-logo svg{width:100%;height:auto;display:block}
  .ft-cols{--ft-gap:64px;display:flex;gap:var(--ft-gap);flex-wrap:wrap}
  .ft-col{position:relative}
  .ft-col h4{font-size:12px;font-weight:700;letter-spacing:1px;text-transform:uppercase;color:var(--text-tertiary);margin:0 0 16px}
  .ft-col a{display:block;color:var(--text-secondary);font-size:15px;margin:10px 0;transition:color .2s}
  .ft-col a:hover{color:var(--accent-ink)}
  /* Same construction as the step dividers: the line sits at the centre of the flex gap,
     so the space either side is equal by construction. Hidden below 640px, where three
     columns wrap and a rule pinned to the gap would land in the wrong place. */
  .ft-col + .ft-col::before{content:'';position:absolute;top:1px;bottom:1px;
    left:calc(var(--ft-gap) / -2);width:1px;background:var(--rule)}
  /* .ft-col a sets display:block and a vertical margin, and outranks .btn on specificity,
     so the pill has to be restored explicitly here. */
  .ft-col .btn{display:inline-flex;margin:2px 0 0;font-size:13px;padding:10px 20px}
  .ft-col .btn:hover{color:var(--text-primary)}
  .ft-base{border-top:1px solid var(--rule);padding-top:26px;display:flex;justify-content:space-between;flex-wrap:wrap;gap:10px;align-items:center}
  .ft-base p{font-size:13px;color:var(--text-tertiary);margin:0}
  /* The sign-off is the wordmark tagline, so it tracks the logo rather than the accent —
     otherwise it would go grey on light themes while the logo above it went black. */
  .ft-base .tagl{color:var(--logo-ink);font-weight:700}

  /* ---- Reveal ---- */
  .rv{opacity:0;transform:scale(.965);transform-origin:center center;transition:opacity .7s var(--ease),transform .7s var(--ease)}
  .rv.in{opacity:1;transform:none}
  /* Directional reveal. Must stay AFTER .rv / .rv.in above — same specificity, source
     order is what decides. Note the reveal observer writes transition-delay inline,
     so the stagger between the two phones comes from there, not from here. */
  .rv-down{transform:translateY(-42px) scale(.985);transition-duration:.85s}
  .rv-up{transform:translateY(48px) scale(.985);transition-duration:1s}
  .rv-down.in,.rv-up.in{transform:none}

  @media (prefers-reduced-motion: reduce){
    html{scroll-behavior:auto}
    *,*::before,*::after{animation:none!important;transition-duration:.01ms!important}
    .rv{opacity:1;transform:none}
    .rv-down,.rv-up{transform:none}
    /* Once the entrance animation has finished its work it must be fully cleared, not left
   running. An element with an animated opacity/transform is its own backdrop root, so any
   frosted child (.btn-ghost in a .hero-cta) silently renders as a flat tint. The home page
   never showed this because its JS adds .settled; the other pages have no such script, so
   the animation stayed applied forever and their hero pills never frosted. */
.hero-content > *{opacity:1;transform:none;animation:none}
  }

  @media(max-width:920px){
    /* Swipe strip rather than a stack. Twelve cards at one column ran past 2000px, which
       is the length problem this solves; the same gesture already drives the who-it's-for
       tiles and the screenshot gallery, so it is a familiar move on this page.
       display:flex overrides the grid, and grid-template-columns is cleared so the
       declaration can't linger if the display value is ever changed back. */
    /* Edge to edge, exactly like .gal-track: the negative inline margin cancels .wrap's
       24px padding (50% - 50vw resolves to -24px here), then the inline padding centres a
       card and lets its neighbours peek in from BOTH sides rather than being cropped off
       one edge. scroll-padding keeps snapping aligned to that same centre. */
    .feat-grid{--feat-w:min(320px,78vw);
      display:flex;grid-template-columns:none;gap:14px;
      overflow-x:auto;scroll-snap-type:x mandatory;scrollbar-width:none;
      overscroll-behavior-x:contain;
      margin-inline:calc(50% - 50vw);
      padding:2px calc((100vw - var(--feat-w))/2) 4px;
      scroll-padding-inline:calc((100vw - var(--feat-w))/2)}
    .feat-grid::-webkit-scrollbar{display:none}
    .feat-grid > .card{flex:0 0 var(--feat-w);scroll-snap-align:center;height:auto}
    .feat-dots,.pr-dots{display:flex}
    /* The reveal observer never fires for cards parked off-screen to the RIGHT of a
       horizontal scroller, so they would sit at opacity 0 forever. Both existing strips on
       this page avoid .rv on their scrolling children for the same reason; here the class
       stays on the markup and is simply neutralised at these widths. */
    .feat-grid > .card.rv{opacity:1;transform:none}
    /* Tablet portrait: match the features strip instead of a 2-col grid. */
    .why-grid{--why-w:min(340px,64vw);display:flex;grid-template-columns:none;gap:14px;
      overflow-x:auto;scroll-snap-type:x mandatory;scrollbar-width:none;overscroll-behavior-x:contain;
      margin-inline:calc(50% - 50vw);
      padding:2px calc((100vw - var(--why-w))/2) 4px;
      scroll-padding-inline:calc((100vw - var(--why-w))/2)}
    .why-grid::-webkit-scrollbar{display:none}
    .why-grid > .why{flex:0 0 var(--why-w);scroll-snap-align:center;height:auto}
    .why-grid > .why.rv{opacity:1;transform:none}
    .why-dots{display:flex}
    .people{grid-template-columns:repeat(2,1fr)}
    .theme-grid{grid-template-columns:repeat(2,1fr)}
    .steps{--step-gap:28px;grid-template-columns:1fr;gap:var(--step-gap);padding:26px 22px}
    /* One column, so the rule turns horizontal and sits in the centre of the row gap. */
    .how{padding:60px 0 24px;gap:40px}
    /* Portrait crops to the middle 26% of the frame — the brightest part of this kitchen —
       and the stacked steps cover much more height, so both gradients reach further.
       Measured: lime "STEP 01" was 2.36:1 against the desktop veil at 390px. */
    .how-veil{background:linear-gradient(to bottom,rgba(0,0,0,.60) 0%,rgba(0,0,0,.38) 18%,rgba(0,0,0,.14) 38%,rgba(0,0,0,0) 56%)}
    .how .step:not(:first-child)::before{left:0;right:0;width:auto;height:1px;
      top:calc(var(--step-gap) / -2);bottom:auto;
      background:rgba(255,255,255,.15)}
    .spot{grid-template-columns:1fr;gap:16px}
  .spot-text{padding:26px 22px} .spot.rev .spot-text{order:0}
  }
  @media(max-width:640px){
    .nav-links{display:none} .menu-btn{display:inline-flex} .nav-cta .nav-login{display:none}
    /* Full-screen overlay rather than a dropdown panel: the links are centred in the
       viewport with nothing else competing, and each is a large tap target.
       Kept in the flow with opacity+visibility instead of display, so it can transition. */
    /* z-index below header.nav's 200, so the logo and the X stay visible and tappable
       over the overlay. */
    .mmenu{display:flex;position:fixed;inset:0;z-index:150;flex-direction:column;
      justify-content:center;align-items:center;gap:2px;
      /* v103: glass, not a flat panel. --bg-primary first as the opaque fallback for
         anything without color-mix; the mix keeps enough opacity for the links to stay
         legible over a bright hero frame while the page still reads through it. */
      background:var(--bg-primary);
      background:color-mix(in srgb, var(--bg-primary) 72%, transparent);
      -webkit-backdrop-filter:blur(calc(var(--glass-blur) * 1.7)) saturate(1.5);
      backdrop-filter:blur(calc(var(--glass-blur) * 1.7)) saturate(1.5);
      border-top:0;padding:0;
      opacity:0;visibility:hidden;pointer-events:none;
      transition:opacity .34s var(--ease-quad),visibility .34s var(--ease-quad),
                 backdrop-filter .45s var(--ease-quad)}
    .mmenu.open{opacity:1;visibility:visible;pointer-events:auto}
    .mmenu a{display:block;padding:13px 24px;text-align:center;color:var(--text-primary);
      font-weight:800;font-size:clamp(27px,7.5vw,40px);letter-spacing:-1px;line-height:1.15;
      opacity:0;transform:translateY(16px);
      transition:opacity .4s var(--ease),transform .4s var(--ease),color .2s}
    .mmenu.open a{opacity:1;transform:none}
  .mmenu a.active{color:var(--accent-ink)}
    /* Small stagger so the list arrives rather than appearing all at once. */
    .mmenu.open a:nth-child(1){transition-delay:.05s}
    .mmenu.open a:nth-child(2){transition-delay:.10s}
    .mmenu.open a:nth-child(3){transition-delay:.15s}
    .mmenu.open a:nth-child(4){transition-delay:.20s}
    .mmenu.open a:nth-child(5){transition-delay:.25s}
    .feat-grid{grid-template-columns:1fr}
    /* Full-bleed swipe strip, identical construction to .feat-grid. */
    .why-grid{--why-w:min(320px,78vw);display:flex;grid-template-columns:none;gap:14px;
      overflow-x:auto;scroll-snap-type:x mandatory;scrollbar-width:none;overscroll-behavior-x:contain;
      margin-inline:calc(50% - 50vw);
      padding:2px calc((100vw - var(--why-w))/2) 4px;
      scroll-padding-inline:calc((100vw - var(--why-w))/2)}
    .why-grid::-webkit-scrollbar{display:none}
    .why-grid > .why{flex:0 0 var(--why-w);scroll-snap-align:center;height:auto}
    .why-grid > .why.rv{opacity:1;transform:none}
    .why-dots{display:flex}
    .people{grid-template-columns:1fr 1fr}
    .price{padding:32px 22px} section{padding:60px 0} .sec-head{margin-bottom:60px} .ft-cols{--ft-gap:40px}
    /* Mobile footer: brand full-width on top, then link columns in a tidy 2-up grid so
       Product/Company share a row and Get started sits below — no big empty right gap. */
    .ft-grid{flex-direction:column;gap:36px;margin-bottom:32px}
    .ft-brand{max-width:none}
    .ft-cols{display:grid;grid-template-columns:1fr 1fr;gap:32px 24px;width:100%}
    .ft-col .btn{width:100%;justify-content:center}
    .ft-base{flex-direction:column;gap:6px;align-items:center}
    .ft-col + .ft-col::before{display:none}
    /* .nav-links is display:none at this width, so it leaves the grid entirely and
       .nav-cta auto-places into column 2 of `1fr auto 1fr` — stranding it mid-bar with
       an empty third column to its right. Two columns puts it hard right. */
    .nav-in{grid-template-columns:1fr auto}
    /* more top padding than bottom pushes the centred hero copy down, showing more video */
    .hero{padding:230px 20px 60px}
    /* stat cards hug their text and align left with the copy above them */
    .st-stats{--st-gap:18px;grid-template-columns:1fr;padding:20px 20px}
    .st-stat:not(:first-child)::before{left:0;right:0;width:auto;height:1px;
      top:calc(var(--st-gap) / -2);bottom:auto}
    /* object-fit:cover only crops horizontally on a portrait viewport, so object-position
       does nothing vertically. Oversizing the video and pulling it up is what actually
       trims the empty headroom above the character. */
    .hero-bg video{position:absolute;left:0;top:-8%;width:100%;height:112%}
    /* keep the trailing clause on one line instead of orphaning a word */
    /* each clause is its own flex item, so a phrase wraps whole instead of orphaning
       a word. The .hero-note flex-wrap was already there; it just had one long text node. */
    /* close the gap between the phone mockups and the who-it's-for card */
    .statement{padding-bottom:32px}
    /* This is the rule that actually wins at phone widths — the 820px block above is
       overridden here. Together with .statement's bottom padding it produced 102px of
       dead space between the phone mockups and the card. */
    .who{padding-top:16px}
  }

  /* --- v97: hero video gating -------------------------------------------
     Default state is POSTER, not video: if the JS below never runs, or decides
     against the video, the still image is what shows. .video-on is added only
     once the file is actually attached. --------------------------------- */
  .hero-bg video{display:none}
  .hero-poster{display:block}
  .hero-bg.video-on video{display:block}
  .hero-bg.video-on .hero-poster{display:none}

  /* --- v103: footer ---------------------------------------------------------
     Below 920px the footer was still running its desktop flex row. .ft-cols wrapped
     onto its own line, which left the brand block alone on line one with a wide dead
     area beside it -- the "broken" look on a portrait tablet. Centring the whole thing
     makes the wrap intentional instead of accidental, and centres the logo on phones. */
  @media(max-width:920px){
    .ft-grid{flex-direction:column;align-items:flex-start;gap:34px;margin-bottom:34px}
    .ft-brand{max-width:none;text-align:left}
    /* transform-origin must follow the logo, or the hover scale pushes it off-centre. */
    .ft-logo{margin-inline:0;transform-origin:left center}
    .ft-brand p{margin:16px 0 0;max-width:340px}
    .ft-cols{display:grid;grid-template-columns:repeat(3,minmax(0,max-content));
      justify-content:flex-start;gap:0 44px;width:auto;text-align:left}
    .ft-col{display:flex;flex-direction:column;align-items:flex-start}
    .ft-col .btn{width:auto}
    .ft-col + .ft-col::before{display:none}
    .ft-base{flex-direction:column;gap:8px;align-items:flex-start;text-align:left}
  }
  /* Three columns stop fitting once "Terms of Service" can't sit beside the others. */
  @media(max-width:640px){
    /* App + Company sit side-by-side, centred as a pair, text left-aligned, with a vertical
       divider between them. Grid items stretch to the taller column, so the divider spans the
       full height. "Get started" spans the row below and centres. */
    .ft-cols{grid-template-columns:max-content max-content;column-gap:40px;row-gap:34px;
      width:auto;justify-content:start;text-align:left}
    .ft-col{align-items:flex-start}
    .ft-col:nth-child(2)::before{display:block;left:-20px;top:2px;bottom:2px}
    .ft-col:nth-child(3){grid-column:1 / -1;align-items:flex-start;text-align:left;justify-self:start}
    .ft-col:nth-child(3)::before{display:none}
    .ft-col .btn{width:100%;max-width:280px;justify-content:center}
  }

/* ---- Region switcher + suggestion bar -------------------------------------
   The switcher sits in the footer: always reachable, and it doesn't crowd a nav
   that already carries five links plus two CTAs on mobile. */
  /* sits as the last item in the Company column, so it reads as part of the list */
  .ft-region{display:flex;justify-content:flex-start;margin:14px 0 0}
  .rg{position:relative}
  .rg-btn{display:inline-flex;align-items:center;gap:9px;white-space:nowrap;padding:9px 14px;border-radius:99px;
    border:1px solid var(--card-edge);background:transparent;color:var(--text-secondary);
    font:inherit;font-size:14px;cursor:pointer;transition:color .2s,border-color .2s}
  .rg-btn:hover{color:#fff;border-color:var(--border-strong)}
  .rg-btn .ti{font-size:14px;transition:transform .25s var(--ease)}
  .rg.open .rg-btn .ti{transform:rotate(180deg)}
  /* Flags are pre-clipped to a circle in the artwork — no border-radius here. */
  .rg-flag{width:20px;height:20px;flex:0 0 20px;display:block}
  .rg-menu{position:absolute;bottom:calc(100% + 8px);left:0;min-width:max-content;padding:6px;
    border-radius:14px;border:1px solid var(--card-edge);background:var(--bg-elevated,#17171b);
    box-shadow:0 20px 50px -20px rgba(0,0,0,.8);opacity:0;visibility:hidden;transform:translateY(6px);
    transition:opacity .2s var(--ease),transform .2s var(--ease),visibility .2s;z-index:60}
  .rg.open .rg-menu{opacity:1;visibility:visible;transform:none}
  /* .ft-col a sets display:block (it styles the footer link lists) and the switcher now
     lives in that column — without matching specificity the flag stacks above the label. */
  .ft-col .rg-item,
  .rg-item{display:flex;align-items:center;gap:10px;padding:10px 12px;border-radius:10px;margin:0;
    color:var(--text-secondary);font-size:14px;line-height:1.45;white-space:nowrap}
  .rg-item:hover{background:rgba(255,255,255,.06);color:#fff}
  .rg-item.on{color:var(--accent-ink)}

  /* Suggestion bar: a strip, not a modal — it shouldn't block the page it's
     asking about. Shown once; an explicit choice or "Stay here" silences it. */
  .rg-bar{position:fixed;left:16px;right:16px;bottom:16px;z-index:200;
    opacity:0;transform:translateY(12px);transition:opacity .3s var(--ease),transform .3s var(--ease)}
  .rg-bar.in{opacity:1;transform:none}
  .rg-bar-in{display:flex;align-items:center;gap:14px;flex-wrap:wrap;
    max-width:720px;margin:0 auto;padding:14px 18px;border-radius:18px;
    border:1px solid var(--card-edge);background:rgba(22,22,26,.92);
    backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);
    box-shadow:0 28px 60px -28px rgba(0,0,0,.9)}
  .rg-bar-in p{margin:0;flex:1 1 240px;font-size:14px;line-height:1.45;color:var(--text-secondary)}
  .rg-bar-x{background:none;border:0;color:var(--text-secondary);font:inherit;font-size:14px;
    cursor:pointer;padding:8px 6px;text-decoration:underline;text-underline-offset:3px}
  .rg-bar-x:hover{color:#fff}
  .btn-sm{padding:10px 18px;font-size:14px}

  /* Consent bar reuses the region bar's frame; it sits above it if both ever appear. */
  .ck-bar{z-index:210}
  .ck-bar .rg-bar-in p a{color:var(--accent-ink);text-decoration:underline;text-underline-offset:3px}
  @media(max-width:640px){
    .rg-bar{left:10px;right:10px;bottom:10px}
    .rg-bar-in{gap:10px;padding:14px}
    .rg-bar-in p{flex:1 1 100%}
  }

/* Discreet "how to install" link that sits in the note line under a hero CTA. billed has
   no app-store button, so this is what replaces it -- it should read as a helpful aside,
   not as an apology for a missing download. */
.hero-install{color:var(--text-secondary);text-decoration:underline;
  text-underline-offset:3px;text-decoration-color:var(--border-strong);
  transition:color .2s,text-decoration-color .2s}
.hero-install:hover{color:var(--accent-ink);text-decoration-color:var(--accent-ink)}
.hn-sep{opacity:.5}


/* ============================================================================
   PARALLAX PLATES — one definition for the whole site.
     <div class="hero-bg">                  (or .final-bg)
       <div class="plates">
         <img class="plate plate-back"  src="...">
         <img class="plate plate-front" src="...">
       </div>
     </div>
   .reveal on the section settles the scale; shared.js drives the mousemove
   translate. This used to be copy-pasted into three pages under three selector
   prefixes (.lb-hero, .lb-final, .in-hero), which is how they drifted apart.
   ============================================================================ */
.plates{position:absolute;inset:0;
  transform:scale(1.12);transform-origin:center center;
  transition:transform 1.9s var(--ease)}
.reveal .plates{transform:scale(1)}
.plate{position:absolute;inset:0;width:100%;height:100%;
  object-fit:cover;object-position:center;display:block;
  transform:scale(1.05);transform-origin:center center;
  transition:translate .75s cubic-bezier(.22,.61,.36,1)}
@media(prefers-reduced-motion:reduce){
  .plates,.reveal .plates{transform:none;transition:none}
}

/* Google Play badge (v223). Google's own SVG, unmodified, served as an <img> so the file
   stays byte-identical to the one they ship and its internal .st1/.st5 classes cannot leak
   into the page. Height is fixed and the surrounding margin gives the clear space Google's
   brand guidelines ask for (a quarter of the badge height on every side). */
.play-badge{display:inline-block;line-height:0;margin:14px 0}
.play-badge img{height:54px;width:auto;max-width:100%;display:block}
.play-get{margin-top:22px}
.play-get p{margin:0 0 10px;font-size:14px;color:var(--text-tertiary)}

  /* ---- Play badge row (one placement, every price card) ----------------
     Google's brand guideline asks for clear space around the badge of at
     least a quarter of its height. The badge renders 48px tall, so the
     anchor carries 12px of padding on every side. Served as <img> and never
     inlined, and the SVGs themselves are untouched. */
  .play-row{display:flex;align-items:center;justify-content:space-between;gap:16px;
    margin:18px 0 0;padding:18px 0 0;border-top:1px solid var(--card-edge)}
  /* the label may shrink; the badge may not -- the global img{max-width:100%}
     otherwise squeezes the badge below 48px on the narrower homepage card */
  .play-row > span{font-size:13px;color:var(--text-secondary);flex:1 1 auto;min-width:0}
  .play-row .play-badge{flex:0 0 auto;display:block;padding:12px;margin:-12px}
  .play-row .play-badge img{height:48px;width:auto;max-width:none;display:block}
  @media(max-width:520px){
    .play-row{flex-direction:column;align-items:center;text-align:center;gap:12px}
    .play-row .play-badge{margin:-12px 0}
  }

  /* ---- Hero small print sits on the hero photo, which is dark in BOTH
     themes, so it cannot use --text-tertiary (#666 in dark). Reuse the
     value .hero-note already uses. line-height stays at the site's
     deliberate 1.45. */
  .hero .hero-install{color:rgba(255,255,255,.82)}

  /* ---- The theme picker stores a choice site-wide, so a visitor in light
     theme lands on the pricing page with light tokens over the dark hero
     photo and the card becomes unreadable. Inside .pr-hero only, restore the
     dark theme's values -- copied verbatim from [data-theme="dark"] above.
     The homepage card sits on the normal page background and is untouched. */
  [data-theme="light"] .pr-hero .pr-card{
    --text-primary:#f5f5f5; --text-secondary:#a0a0a0; --text-tertiary:#666666;
    --border-light:rgba(255,255,255,0.08); --border-med:rgba(255,255,255,0.15);
    --border-strong:rgba(255,255,255,0.25); --rule:rgba(255,255,255,0.15);
    --bg-primary:#1c1c1e; --bg-secondary:#2c2c2e; --bg-tertiary:#141414;
    --card-bg:rgba(42,42,47,0.19); --card-edge:rgba(255,255,255,0.12);
    --accent-ink:#d9ff00; --logo-ink:#d9ff00;
    color:var(--text-primary)}
