/* Static, overlap-free instances (scripts/build-fonts.py): -webkit-text-stroke shows no seams. */
@font-face { font-family: 'Unbounded'; font-weight: 500; font-display: swap; src: url('../fonts/unbounded-500.woff2') format('woff2'); }
@font-face { font-family: 'Unbounded'; font-weight: 800; font-display: swap; src: url('../fonts/unbounded-800.woff2') format('woff2'); }
@font-face { font-family: 'Unbounded'; font-weight: 900; font-display: swap; src: url('../fonts/unbounded-900.woff2') format('woff2'); }
@font-face { font-family: 'Inter Tight'; font-weight: 400; font-display: swap; src: url('../fonts/inter-tight-400.woff2') format('woff2'); }
@font-face { font-family: 'Inter Tight'; font-weight: 500; font-display: swap; src: url('../fonts/inter-tight-500.woff2') format('woff2'); }
@font-face { font-family: 'Inter Tight'; font-weight: 600; font-display: swap; src: url('../fonts/inter-tight-600.woff2') format('woff2'); }

*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { -webkit-text-size-adjust: 100%; background: var(--bg); }
body { overflow-x: hidden; -webkit-font-smoothing: antialiased; background-color: var(--bg); color: var(--fg); font-family: var(--t); transition: background-color .6s ease; }
img, svg { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }
:focus-visible { outline: 2px solid var(--o); outline-offset: 3px; }
/* Visually hidden, still read by screen readers. */
.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }

/* Smooth scroll: Lenis 1.3.26 dist/lenis.css */
html.lenis, html.lenis body { height: auto; }
.lenis:not(.lenis-autoToggle).lenis-stopped { overflow: clip; }
.lenis [data-lenis-prevent], .lenis [data-lenis-prevent-wheel], .lenis [data-lenis-prevent-touch], .lenis [data-lenis-prevent-vertical], .lenis [data-lenis-prevent-horizontal] { overscroll-behavior: contain; }
.lenis.lenis-smooth iframe { pointer-events: none; }
.lenis.lenis-autoToggle { transition-property: overflow; transition-duration: 1ms; transition-behavior: allow-discrete; }

/* Scene palettes. Motion: the page itself changes colour (one repaint per switch, not per frame) when a
   section's top crosses the middle of the screen. Static: every section carries its own background. */
/* A plain background-color on body, not an inherited custom property: the switch restyles body alone. */
html[data-palette="dark"] body { background-color: var(--p-dark); }
html[data-palette="navy"] body { background-color: var(--p-navy); }
html[data-palette="orange"] body { background-color: var(--p-orange); }
[data-palette="dark"] { color: var(--p-dark-ink); }
[data-palette="navy"] { color: var(--p-navy-ink); }
[data-palette="orange"] { color: var(--p-orange-ink); }
html:not(.js) [data-palette="navy"] { background: var(--p-navy); }
html:not(.js) [data-palette="orange"] { background: var(--p-orange); }
@media (prefers-reduced-motion: reduce) { body { transition: none; } }

/* Data failed to load: blocks drawn from data stay out instead of showing empty shells. */
.nodata [data-needs-data] { display: none; }

/* Header: white through difference blend, so it reads on dark type and on the orange block alike. */
.top { position: fixed; inset: 0 0 auto; z-index: 30; display: flex; justify-content: space-between; align-items: center; padding: 16px var(--side); padding-top: max(16px, env(safe-area-inset-top)); font: 500 13px/1 var(--t); mix-blend-mode: difference; color: #fff; }
.top .wa { border: 1px solid currentColor; border-radius: 999px; padding: 9px 14px; }
.brand img { height: 20px; width: auto; }
/* Colour logos cannot go through the blend: a solid plate instead. */
/* Difference turns the white header blue over orange; there it takes the scene's ink instead. */
html[data-palette="orange"] .top { mix-blend-mode: normal; color: var(--p-orange-ink); }
.top--solid { mix-blend-mode: normal; background: var(--bg); color: var(--fg); }

.ns { padding: 72px var(--side) 0; font-size: 15px; color: var(--mut); }
.ns a { color: var(--fg); border-bottom: 1px solid var(--line-3); }

/* Chapter 1: hero. Motion: three layers on one pinned stage (words -> inside the O -> out from between
   100 and 000). Static: the same three layers one under another. */
/* No background of its own: the page colour lives on body, so a switch never leaves a dark band here. */
.hero { position: relative; }
.words { display: flex; flex-direction: column; justify-content: center; padding: 0 clamp(12px, 3vw, 32px); font-family: var(--d); text-transform: uppercase; }
.line { line-height: .88; white-space: nowrap; }
.l1 { font-weight: 800; font-size: clamp(26px, 7.2vw, 120px); letter-spacing: -.04em; }
.l2 { font-weight: 900; font-size: clamp(60px, 16vw, 270px); letter-spacing: -.06em; }
.l3 { font-weight: 500; font-size: clamp(13px, 4.1vw, 52px); letter-spacing: -.01em; padding-top: .3em; }
.l3 i { font-style: normal; color: var(--o); }
.o { display: inline-block; }
/* Zero-size marker after the O: its offsetTop is the baseline the glyph is measured from. */
.bl { display: inline-block; width: 0; height: 0; }

/* The slot. Without photos: giant words. With photos (data/photos.json): the phone, words become its caption. */
.beat { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3vh; pointer-events: none; }
.beat img { width: auto; height: min(56vh, 120vw); }
.flip { font: 900 clamp(24px, 6.4vw, 72px)/1 var(--d); text-transform: uppercase; letter-spacing: -.05em; text-align: center; }
.flip i { font-style: normal; color: var(--o); }

.count { padding: 0 var(--side); text-align: center; }
/* Min 48px (reference: 56px) so 100 000 fits a 320px screen; from 375px up the size is the reference's. */
/* Centred by its ink: the 1's wide left bearing and the tracking past the last 0 sit the boxes .215em right of
   the glyphs, so the padding takes twice that off the right. */
.reels { display: flex; justify-content: center; padding-right: .43em; font: 900 clamp(48px, 15vw, 230px)/1 var(--d); letter-spacing: -.06em; }
/* The empty gap must keep its width: the pull-out scale is measured from it. */
.reel, .reels .gap { flex: none; }
/* Clip only top and bottom: negative tracking pushes the glyph ink past the box on the right. */
.reel { height: 1em; clip-path: inset(0 -.3em); }
.reel > div { display: flex; flex-direction: column; }
.reel span { height: 1em; text-align: right; }
.reels .gap { width: .22em; }
.count p { margin-top: 18px; overflow: hidden; font-size: clamp(16px, 1.6vw, 22px); color: var(--mut); }
.count p span { display: block; }
/* The label in the display face, tracked wide (the padding balances the tracking after the last letter); the
   rest on the next line in the text face. */
.count .c-who { padding-left: .32em; font: 500 clamp(18px, 2.2vw, 30px)/1.2 var(--d); letter-spacing: .32em; color: var(--fg); }
.count .c-sub { margin-top: 10px; font: 400 clamp(15px, 1.6vw, 22px)/1.35 var(--t); }
.count em { font-style: normal; color: var(--o); }

/* Static: the layers in flow, no first-screen lock. */
html:not(.js) .hero { display: grid; gap: 7vh; padding: 104px 0 14vh; }
html:not(.js) .flip { display: flex; flex-wrap: wrap; justify-content: center; gap: 0 .35em; padding: 0 var(--side); }

/* Motion: the pinned stage. */
.js .hero { height: 100vh; height: 100svh; overflow: hidden; }
.js .words, .js .beat, .js .count { position: absolute; inset: 0; }
.js .words { z-index: 2; }
.js .beat { z-index: 3; }
.js .count { z-index: 2; display: flex; flex-direction: column; justify-content: center; opacity: 0; visibility: hidden; }
.js .line { clip-path: inset(0 -.3em); }
.js .flip { position: relative; width: 100%; height: 1em; clip-path: inset(0 -.3em); font-size: clamp(38px, 10.5vw, 168px); }
.js .photo .flip { font-size: clamp(24px, 6.4vw, 72px); }
.js .flip span { position: absolute; inset: 0; }
/* Hidden from the first paint; the script shows each one in the tick it sets its start state. */
.js .line > div, .js .flip span, .js .beat img { visibility: hidden; }

/* Chapter 2: model names stream past, then the price list. */
.marquee { position: relative; z-index: 1; padding: 6vh 0 18vh; overflow: hidden; display: grid; gap: 1vw; transition: color .6s ease; }
/* Pulled up over the tail of the pinned hero: the names roll in while 100 000 is still leaving.
   Set by marquee.animate once the pin exists; earlier the rows would sit on the first screen. */
.marquee.pulled { margin-top: -40vh; }
.track { display: flex; align-items: center; gap: .4em; white-space: nowrap; font: 900 clamp(48px, 9vw, 150px)/1 var(--d); text-transform: uppercase; letter-spacing: -.05em; will-change: transform; }
.track .ol { color: transparent; -webkit-text-stroke: 1.5px var(--fg); transition: -webkit-text-stroke-color .6s ease; }
.track .hot { color: var(--o); }
.track .dot { flex: none; width: .2em; height: .2em; border-radius: 50%; background: var(--o); }
.track .m-ph { flex: none; height: 1.1em; width: auto; }

.prices { padding: 0 var(--side) 18vh; }
.prices .head { display: flex; justify-content: space-between; align-items: baseline; gap: 16px; padding-bottom: 14px; border-bottom: 1px solid var(--line); font: 500 13px/1.3 var(--t); color: var(--mut); }
.prices .head a { color: var(--fg); border-bottom: 1px solid var(--line-3); }
.pr { display: flex; justify-content: space-between; align-items: flex-end; gap: 12px; padding: 18px 0; border-bottom: 1px solid var(--line); }
.pr small { display: block; margin-bottom: 6px; font: 500 12px/1 var(--t); color: var(--mut); }
.pr .n { clip-path: inset(0 -.3em); }
.pr .n span { display: block; font: 800 clamp(22px, 7vw, 60px)/1 var(--d); text-transform: uppercase; letter-spacing: -.035em; }
.pr .r { display: flex; align-items: center; gap: 10px; }
.pr .p { font: 500 clamp(14px, 1.6vw, 22px)/1 var(--t); color: var(--o); white-space: nowrap; font-variant-numeric: tabular-nums; }
.pr .go { font: 500 18px/1 var(--t); font-style: normal; }
/* Motion: the row crossing the middle of the screen is lit; hover does the same on desktop.
   No CSS transition on the name's transform: GSAP drives it for the rise. */
.js .pr .n span { opacity: .4; transition: opacity .35s ease; }
.js .pr .go { opacity: 0; transform: translateX(-8px); transition: opacity .35s ease, transform .35s cubic-bezier(.2, .8, .2, 1); }
.js .pr.on .n span, .js .pr:hover .n span { opacity: 1; }
.js .pr.on .go, .js .pr:hover .go { opacity: 1; transform: none; }

/* Scene: collage. Motion: a screen and a half scrolling past (no pin), the phrase in the centre, the photos on two invisible ovals
   (js/chapters/collage.js sets each photo's centre, scale and opacity). Static: the phrase over a grid of
   the photos. Sizes in cqi of the stage, so the scene composes the same at every width. */
.collage { overflow: hidden; overflow: clip; padding: 12vh var(--side) 16vh; }
.c-stage { container-type: inline-size; display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
.c-say { grid-column: 1 / -1; margin-bottom: 4vh; font: 500 clamp(30px, 9cqi, 96px)/1.04 var(--d); letter-spacing: -.035em; text-align: center; text-wrap: balance; }
.ph { margin: 0; aspect-ratio: 4 / 5; overflow: hidden; border-radius: clamp(10px, 1.4cqi, 18px); background: var(--line-2); }
.ph img { display: block; width: 100%; height: 100%; object-fit: cover; }
/* Before the pins exist the collage sits about a screen down, inside the lazy-load distance, and its photos
   would load alongside the fonts; display:none lazy images wait. Once ready, the collage is screens away. */
html.js:not(.ready) .ph img { display: none; }
@media (min-width: 900px) { .c-stage { grid-template-columns: repeat(6, 1fr); } }
/* Motion */
/* Tall enough for the whole dial with its photos, the scroll's spread and a margin (js/chapters/collage.js
   dialSize: R 78 % of the width, photos 30 %), so the scene never cuts it. */
.js .collage { height: 150vh; height: max(150svh, calc(160vw + 200px)); padding: 0; }
/* The gap between the last marquee row and the ring is halved (user 2026-10-23): the marquee gives up half its bottom
   padding, the scene is pulled up by half of its own slack (phone: the height minus the ring's 1.3125 widths, over two). */
.js .marquee { padding-bottom: 9vh; }
.js .collage { margin-top: calc((max(150vh, 160vw + 200px) - 131.25vw) / -4); margin-top: calc((max(150svh, 160vw + 200px) - 131.25vw) / -4); }
.js .c-stage { --pw: 25cqi; position: relative; display: block; height: 100%; }
.js .c-say { position: absolute; inset: 50% 28% auto; z-index: 1; margin: 0; translate: 0 -50%; font-size: 8cqi; }
/* Each photo's box is centred on its point (GSAP moves the point); opacity 0 until the dial places it. */
.js .ph { position: absolute; left: 0; top: 0; width: var(--pw); margin: calc(var(--pw) * -.625) 0 0 calc(var(--pw) * -.5); opacity: 0; will-change: transform; }
/* The edge and the sheen carry their own radius (the photo clips them anyway): `border-radius: inherit` of the photo's
   container-unit radius made every style recalc 20-30x slower while the ring turned. */
/* Details of the ring (js/chapters/collage.js builds the dial scale; it exists in motion mode only): ticks round the
   ring, a thin light edge and a sheen on every photo. The dial layer moves by transform only. */
.js .c-dial { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; pointer-events: none; will-change: transform; }
.js .c-dial .tk { stroke: var(--line-3); stroke-width: 1; vector-effect: non-scaling-stroke; }
.js .c-dial .tk.maj { stroke: var(--mut); stroke-width: 1.4; opacity: 1; }
.js .ph::before { content: ""; position: absolute; inset: 0; z-index: 1; border-radius: clamp(10px, 1.4vw, 18px); box-shadow: inset 0 1px 0 rgba(255, 255, 255, .22), inset 0 0 0 1px rgba(255, 255, 255, .07); pointer-events: none; }
.js .ph::after { content: ""; position: absolute; inset: 0; z-index: 1; border-radius: clamp(10px, 1.4vw, 18px); pointer-events: none; opacity: var(--so, 0); background: linear-gradient(110deg, transparent 38%, rgba(255, 255, 255, .22) 50%, transparent 62%); background-size: 300% 100%; background-position: calc(100% - var(--s, .5) * 100%) 0; }
@media (min-width: 900px) {
  .js .collage { height: max(150vh, calc(2 * min(36vw, 62vh) + 1.25 * min(13vw, 210px) + 380px)); height: max(150svh, calc(2 * min(36vw, 62svh) + 1.25 * min(13vw, 210px) + 380px)); }
  .js .c-stage { --pw: min(13cqi, 210px); }
  .js .collage { margin-top: -95px; }
  .js .c-say { inset-inline: 24%; font-size: min(5.2cqi, 9.4vh); }
}

/* Scene: quote, a poster. The black-and-white portrait fills the scene, darkened into the page at both
   edges; the quote sits over its lower part, the small credit line under it. The layout holds without
   the figure (a client removes the portrait, spec §5.6). Motion: the portrait settles from a slow zoom,
   the words light up one by one, a marker draws under «как она работает», the text floats over the photo. */
.quote { position: relative; overflow: hidden; overflow: clip; display: grid; align-content: end; gap: 22px; min-height: 120vh; min-height: 120svh; padding: 0 var(--side) 10vh; }
.qt-ph { position: absolute; inset: 0; margin: 0; overflow: hidden; overflow: clip; }
.qt-ph img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 18%; filter: grayscale(1) contrast(1.05) brightness(.62); transform-origin: 50% 30%; background: var(--line-2); }
.qt-ph::after { content: ''; position: absolute; inset: 0; pointer-events: none; background: linear-gradient(to top, var(--bg) 8%, rgba(10, 10, 10, .75) 38%, rgba(10, 10, 10, 0) 70%), linear-gradient(to bottom, var(--bg), rgba(10, 10, 10, 0) 18%); }
/* The credit sits over the photo: a dark backing keeps it at 4.5:1 over the brightest part (bare it measured 1.9:1).
   The box is offset by its padding, so the text keeps to the page gutter. */
.qt-ph figcaption { position: absolute; z-index: 1; top: 72px; right: calc(var(--side) - 10px); max-width: 22em; padding: 6px 10px; border-radius: 8px; background: rgba(10, 10, 10, .72); text-align: right; font: 400 12px/1.4 var(--t); color: var(--mut); }
.qt-ph a { color: var(--fg); border-bottom: 1px solid currentColor; }
.qt-tx { position: relative; display: grid; gap: 22px; max-width: 980px; }
/* A short soft shadow lifts the text off the photo: it reads as lying on top of it while it floats. */
.qt p { font: 500 clamp(26px, 4.4vw, 68px)/1.12 var(--d); letter-spacing: -.03em; text-wrap: pretty; text-shadow: 0 .05em .3em rgba(0, 0, 0, .5); }
.qt .hl { color: var(--o); background: linear-gradient(var(--o), var(--o)) 0 100% / 100% .06em no-repeat; }
.qt-by { font: 500 15px/1.3 var(--t); color: var(--mut); }
/* Wide screens: the whole portrait (face and the phone in his hand) on the left at full height, fading
   into the page on its right; the quote on the right, just over the photo's edge. */
@media (min-width: 900px) {
  .quote { min-height: 100vh; min-height: 100svh; align-content: center; padding: 14vh var(--side); }
  .qt-ph { right: auto; width: auto; max-width: 60%; aspect-ratio: 4 / 5; }
  .qt-ph img { object-position: 50% 30%; }
  .qt-ph::after { background: linear-gradient(to left, var(--bg), rgba(10, 10, 10, 0) 40%), linear-gradient(to top, var(--bg), rgba(10, 10, 10, 0) 25%), linear-gradient(to bottom, var(--bg), rgba(10, 10, 10, 0) 18%); }
  .qt-ph figcaption { top: 12vh; right: auto; left: calc(var(--side) - 10px); max-width: 26em; text-align: left; }
  .qt-tx { margin-left: 44%; max-width: 46em; }
  .qt p { font-size: clamp(26px, 3.3vw, 60px); }
}


/* Small labels above a scene's heading (stores, reviews, FAQ). */
.label { font: 500 13px/1.3 var(--t); color: var(--mut); }

/* Chapter 4: 2GIS. The whole network, no city singled out. */
.rev { padding: 0 var(--side) 18vh; }
.cities { margin-top: 6vh; }
.cr { position: relative; display: flex; justify-content: space-between; align-items: center; gap: 12px; padding: 18px 0; }
.cr::after { content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 1px; background: var(--line-2); transform-origin: left; transform: scaleX(var(--draw, 1)); }
.cr .c { clip-path: inset(0 -.3em); }
.cr .c span { display: block; font: 800 clamp(22px, 6.4vw, 56px)/1 var(--d); text-transform: uppercase; letter-spacing: -.035em; }
.cr .s { text-align: right; }
.cr .s b { display: block; font: 800 clamp(18px, 4vw, 36px)/1 var(--d); color: var(--o); letter-spacing: -.03em; }
.cr .s small { display: block; margin-top: 6px; font: 500 12px/1 var(--t); color: var(--mut); white-space: nowrap; }

/* Scene: stores on the map. The pins are the city switch; the card follows the chosen city. */
.stores { padding: 14vh var(--side) 18vh; overflow-x: hidden; overflow-x: clip; } /* the floor's light pool reaches past the map: no sideways scroll (hidden for Safari < 16) */
.stores .label { font: 500 clamp(28px, 4.4vw, 64px)/1.05 var(--d); letter-spacing: -.03em; color: var(--fg); }
.pick { margin-bottom: 14px; font: 500 14px/1.3 var(--t); color: var(--mut); }
.map { position: relative; width: 100%; aspect-ratio: 274 / 150; margin: 3vh 0 5vh; }
/* The silhouette is the glass. It floats: a black page hides a plain shadow, so the lift is a warm pool of light on the
   floor with a blurred dark copy of the outline inside it. The blur sits on the wrapper, the mask on its child (on one
   element the filter would run before the mask and cut the blur). */
.floor, .glass, .kz, .routes, .pins, .pool, .shade, .shade i { position: absolute; }
.floor, .glass, .kz, .routes, .pins, .shade, .shade i { inset: 0; }
.kz, .routes { width: 100%; height: 100%; }
.pool { inset: -25% -15%; background: radial-gradient(closest-side, rgba(255, 122, 47, .10), rgba(255, 122, 47, .04) 55%, transparent); }
.shade { filter: blur(18px); opacity: .85; }
.shade i { transform: translateY(5%) scale(.96); background: #000; -webkit-mask-image: url(../assets/kz-mask.svg); mask-image: url(../assets/kz-mask.svg); -webkit-mask-size: 100% 100%; mask-size: 100% 100%; -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat; }
.routes { overflow: visible; pointer-events: none; }
.rt { fill: none; stroke: rgba(245, 242, 238, .45); stroke-width: .5; stroke-linecap: round; stroke-dasharray: 1; }
.js .rt { stroke-dashoffset: 1; }
/* Rings at the tapped city and the arrival pulse at the others; sparks run along the arcs. Played by the motion only. */
.wv { fill: none; stroke: var(--o); stroke-width: 1.2; vector-effect: non-scaling-stroke; transform-box: fill-box; transform-origin: center; opacity: 0; }
.sp { fill: var(--o); opacity: 0; }
/* The button's 44px dot box is centred on the store; the name sits under it. */
.pin { position: absolute; left: var(--x); top: var(--y); display: flex; flex-direction: column; align-items: center; transform: translate(-50%, -22px); color: var(--mut); font: 600 clamp(11px, 1.2vw, 15px)/1 var(--t); white-space: nowrap; transition: color .3s ease; -webkit-tap-highlight-color: transparent; }
.pin .dot { position: relative; flex: none; width: 44px; height: 44px; }
.pin .dot::before { content: ''; position: absolute; inset: 17px; border-radius: 50%; background: var(--fg); transition: transform .4s cubic-bezier(.3, 1.6, .5, 1), background-color .3s ease; }
.pin span { margin-top: -10px; }
.pin:hover, .pin[aria-pressed="true"] { color: var(--fg); }
.pin[aria-pressed="true"] .dot::before { background: var(--o); transform: scale(1.6); }
.card { padding-top: 22px; border-top: 1px solid rgba(245, 242, 238, .16); }
.card .meta { font: 500 14px/1.3 var(--t); color: var(--mut); }
.card .addr { margin: 10px 0 6px; font: 800 clamp(24px, 6.6vw, 72px)/1.05 var(--d); text-transform: uppercase; letter-spacing: -.02em; }
.card .addr2 { min-height: 1.3em; font: 500 15px/1.3 var(--t); color: var(--mut); }
.card .wa-big { display: inline-flex; margin-top: 22px; padding: 16px 22px; border-radius: 999px; background: var(--o); color: var(--on-o); font: 800 clamp(13px, 3.8vw, 16px)/1 var(--d); text-transform: uppercase; letter-spacing: -.01em; white-space: nowrap; }
.card .acts { display: flex; flex-wrap: wrap; gap: 14px 22px; margin-top: 22px; font: 500 15px/1 var(--t); }
.card .acts a { padding-bottom: 5px; border-bottom: 1px solid rgba(245, 242, 238, .3); }
@media (min-width: 900px) {
  .stores { display: grid; grid-template-columns: 1.5fr 1fr; grid-template-areas: "label label" "map card"; align-items: center; column-gap: 5vw; }
  .stores .label { grid-area: label; }
  .map { grid-area: map; margin: 4vh 0; }
  .side { grid-area: card; }
  .card .addr { font-size: clamp(26px, 3.4vw, 52px); }
}
@media (prefers-reduced-motion: reduce) { .pin, .pin .dot::before { transition: none; } }

/* Chapter 6: FAQ, a quiet pause. Plain <details> works without JS; JS only smooths the height. */
.faq { max-width: 1100px; padding: 0 var(--side) 18vh; }
.faq .label { margin-bottom: 8px; }
.q { border-bottom: 1px solid var(--line); }
.q summary { display: flex; justify-content: space-between; align-items: center; gap: 16px; padding: 20px 0; list-style: none; cursor: pointer; font: 500 clamp(16px, 2vw, 26px)/1.3 var(--d); letter-spacing: -.02em; }
.q summary::-webkit-details-marker { display: none; }
.pm { position: relative; flex: none; width: 16px; height: 16px; }
.pm::before, .pm::after { content: ''; position: absolute; left: 0; right: 0; top: 50%; height: 2px; margin-top: -1px; background: var(--o); transition: transform .35s cubic-bezier(.2, .8, .2, 1); }
.pm::after { transform: rotate(90deg); }
/* Motion toggles .on before the height animation; the static page follows [open]. */
.q.on .pm::after, html:not(.js) .q[open] .pm::after { transform: rotate(0deg); }
.q .a { overflow: hidden; }
.q .a p { max-width: 60ch; padding-bottom: 22px; font-size: 16px; line-height: 1.5; color: var(--mut); }
@media (prefers-reduced-motion: reduce) { .pm::before, .pm::after { transition: none; } }

/* Finale: the orange footer. The running call on top, the giant brand, the chosen city's links. */
.foot { container-type: inline-size; display: grid; gap: 18px; padding: 0 0 calc(40px + env(safe-area-inset-bottom)); overflow: hidden; background: var(--p-orange); color: var(--p-orange-ink); font: 500 13px/1.4 var(--t); }
.cta { display: block; padding: 5vh 0; overflow: hidden; border-bottom: 1px solid rgba(18, 7, 0, .2); font: 900 clamp(40px, 10vw, 150px)/1 var(--d); text-transform: uppercase; letter-spacing: -.05em; }
/* Endless run: two identical halves, translate by exactly one half. Spacing is padding on every word,
   not flex gap, so the halves are equal and the loop has no seam. */
.cta-track { display: flex; width: max-content; white-space: nowrap; }
.cta-track span { padding-right: .45em; }
/* Runs only in motion mode: the static page (and reduced motion, which never gets .js) keeps it still. */
.js .cta-track { animation: cta-run 12s linear infinite; }
@keyframes cta-run { to { transform: translateX(-50%); } }
/* The brand fills the width between the side margins: the inked word is 3.92em wide at zero tracking
   with the X nudged (Unbounded has no Y-X kerning, the arms would touch). Clipped top and bottom only,
   the letters rise inside it. */
.giant { display: block; padding: 2vh var(--side) 0; font: 900 calc((100cqi - 2 * var(--side)) / 3.92)/.82 var(--d); text-transform: uppercase; white-space: nowrap; clip-path: inset(0 -.3em); }
.giant i { margin-left: .05em; font-style: normal; }
.f-links { display: flex; flex-wrap: wrap; gap: 14px 24px; padding: 2vh var(--side) 0; font: 600 15px/1 var(--t); }
.f-links a { padding-bottom: 4px; border-bottom: 1px solid rgba(18, 7, 0, .35); }
.f-line { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 8px 24px; padding: 0 var(--side); }
.foot .disc, .foot .credit { padding: 0 var(--side); color: var(--p-orange-mut); }

/* Phone bottom bar. Near-opaque instead of backdrop blur, which costs a repaint of everything under it
   on every scrolled frame on mid-range phones. */
.bar { position: fixed; left: 0; right: 0; bottom: 0; z-index: 40; display: flex; gap: 10px; padding: 10px 12px calc(10px + env(safe-area-inset-bottom)); background: var(--bar); border-top: 1px solid #1f1f1f; transform: translateY(110%); transition: transform .45s cubic-bezier(.2, .8, .2, 1); }
.bar.show { transform: none; }
.bar a { flex: 1; display: grid; place-items: center; height: 48px; border-radius: 999px; font: 800 14px/1 var(--d); text-transform: uppercase; letter-spacing: -.01em; }
.bar .b-wa { background: var(--o); color: var(--on-o); }
.bar .b-tel { border: 1px solid var(--line-3); }
@media (min-width: 900px) { .bar { display: none; } }
@media (prefers-reduced-motion: reduce) { .bar { transition: none; } }

/* Demo toast: what a link would do, instead of leaving the page. Rides above the phone bar. */
.toast { position: fixed; left: 0; right: 0; bottom: calc(16px + env(safe-area-inset-bottom)); z-index: 50; display: flex; gap: 10px; width: fit-content; max-width: calc(100% - 24px); margin: 0 auto; padding: 12px 16px; border: 1px solid var(--line-3); border-radius: 14px; background: #141414; color: var(--fg); font: 400 14px/1.4 var(--t); opacity: 0; transform: translateY(12px); pointer-events: none; transition: opacity .35s ease, transform .35s cubic-bezier(.2, .8, .2, 1), translate .45s cubic-bezier(.2, .8, .2, 1); }
.toast::before { content: ''; flex: none; width: 8px; height: 8px; margin-top: 6px; border-radius: 50%; background: var(--o); }
.toast.show { opacity: 1; transform: none; }
@media (max-width: 899px) { .bar.show ~ .toast { translate: 0 calc(-68px - env(safe-area-inset-bottom)); } }
@media (prefers-reduced-motion: reduce) { .toast { transition: none; } }

/* Desktop cursor (js/cursor.js): GSAP moves the box, CSS grows the dot. Difference keeps it visible on
   every scene colour; the word for a link sits in its own tag beside the pointer. */
html.has-cursor, html.has-cursor * { cursor: none; }
.cursor { position: fixed; left: 0; top: 0; z-index: 60; width: 0; height: 0; pointer-events: none; mix-blend-mode: difference; opacity: 0; transition: opacity .25s ease; }
.cursor.seen { opacity: 1; }
.cursor::before { content: ''; position: absolute; width: 14px; height: 14px; margin: -7px 0 0 -7px; left: 0; top: 0; border-radius: 50%; background: #fff; transition: transform .35s cubic-bezier(.2, .8, .2, 1); }
/* Over anything clickable the dot opens into a full circle that inverts what is under it. */
.cursor.on::before { transform: scale(3.4); }
/* The word for a link, in a tag beside the pointer (never over the thing pointed at); no blend, so it reads
   on every page colour. Buttons that say what they do get no tag (js/cursor.js cursorState). */
.cursor-tag { position: fixed; left: 0; top: 0; z-index: 60; padding: 6px 10px; border-radius: 999px; background: #0A0A0A; color: #F5F2EE; box-shadow: 0 0 0 1px rgba(245, 242, 238, .18); font: 600 11px/1 var(--t); white-space: nowrap; pointer-events: none; opacity: 0; transform-origin: 0 0; scale: .85; transition: opacity .2s ease, scale .25s cubic-bezier(.2, .8, .2, 1); }
.cursor-tag.seen { opacity: 1; scale: 1; }
