/* ============================================================================
   Brushler — marketing site
   Dark by default, light on request. Deliberate: this is software used at 5am in
   a truck and 9pm doing invoices, and the product screenshots are themselves
   dark-UI, so they sit in the page rather than glaring out of it.
   ========================================================================== */

:root {
  --brand: #22c1e0;
  --brand-2: #6fe3f5;
  --brand-deep: #0e7f9e;
  --warm: #f0a03c;
  --good: #35c98a;
  --max: 1200px;
  --r: 18px;
  --r-sm: 12px;
  --ease: cubic-bezier(.22,1,.36,1);
}

:root, :root[data-theme="dark"] {
  /* Tells the browser to render NATIVE controls dark — the <select> popup, scrollbars, date
     pickers. Without it the dropdown list paints on an OS-white sheet while the options inherit
     light text, which is white-on-white and unreadable. */
  color-scheme: dark;
  --bg: #060b10;
  --bg-2: #0a1219;
  --surface: rgba(255,255,255,.032);
  --surface-2: rgba(255,255,255,.055);
  --line: rgba(255,255,255,.09);
  --line-2: rgba(255,255,255,.055);
  --ink: #edf6f9;
  --ink-2: #9fb8c5;
  --ink-3: #6a8494;
  --glow: rgba(34,193,224,.15);
  --frame: #17242e;
  --frame-2: #2a4453;
  --shadow: 0 40px 90px -30px rgba(0,0,0,.9);
  --grain: .028;
}

:root[data-theme="light"] {
  color-scheme: light;
  /* Deliberately not white. A page that is mostly #fff glares on a phone in a truck at midday,
     and the product screenshots are dark UI — they sit far better on a soft blue-grey than they
     do punched out of paper white. */
  --bg: #eaf1f5;
  --bg-2: #e1ebf1;
  --surface: rgba(255,255,255,.62);
  --surface-2: rgba(255,255,255,.86);
  --line: rgba(12,58,78,.14);
  --line-2: rgba(12,58,78,.085);
  --ink: #0b202b;
  --ink-2: #42606e;
  --ink-3: #6a8593;
  --brand: #0d7a97;
  --brand-2: #12a3c4;
  --glow: rgba(13,122,151,.10);
  --frame: #d5e4ec;
  --frame-2: #b0c9d6;
  --shadow: 0 30px 70px -30px rgba(9,45,60,.26);
  --grain: 0;
}

* { box-sizing: border-box; }
/* No overflow-x here. Clipping on the root element changes which element scrolls, which
   silently stopped scroll events AND IntersectionObserver from firing — every reveal
   stayed at opacity 0 forever. body already carries overflow-x: hidden, which contains
   the decorative aurora without touching the scroll container. */
html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation: none !important; transition-duration: .01ms !important; }
}

body {
  margin: 0; background: var(--bg); color: var(--ink);
  font: 17px/1.65 "Inter","SF Pro Text","Segoe UI Variable Text","Segoe UI",system-ui,-apple-system,sans-serif;
  -webkit-font-smoothing: antialiased;
  /* NO overflow-x here. Setting it makes <body> a scroll container, which breaks position:sticky
     for the header and changes which element dispatches scroll events. The decorative auroras
     that used to need clipping are already contained by their own sections' overflow:hidden, so
     nothing actually overflows and the rule was only masking layout bugs. */
}
/* Film grain — kills the flat-gradient look that makes dark sites feel cheap. */
body::before {
  content: ""; position: fixed; inset: 0; z-index: 999; pointer-events: none; opacity: var(--grain);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

.wrap { max-width: var(--max); margin: 0 auto; padding: 0 26px; }
/* Anchor targets have to clear the sticky header, or jumping to one parks its heading behind
   the nav. --nav-h is measured at runtime, so this stays right across breakpoints. */
[id] { scroll-margin-top: calc(var(--nav-h, 72px) + 18px); }
a { color: var(--brand); text-decoration: none; }
img { max-width: 100%; display: block; }

h1,h2,h3,h4 { margin: 0; font-weight: 800; line-height: 1.08; letter-spacing: -.032em; }
h1 { font-size: clamp(2.7rem, 6.6vw, 4.6rem); }
h2 { font-size: clamp(2.05rem, 4.3vw, 3.15rem); }
h3 { font-size: 1.2rem; letter-spacing: -.018em; }
p { margin: 0 0 1em; }
.lede { font-size: clamp(1.08rem,2vw,1.3rem); color: var(--ink-2); line-height: 1.62; }
em { font-style: normal; color: var(--ink); font-weight: 650; }

/* ---------------- nav ---------------- */
.progress { position: fixed; top: 0; left: 0; height: 2px; width: 0; z-index: 100;
  background: linear-gradient(90deg,var(--brand),var(--brand-2)); transition: width .1s linear; }
nav.top {
  position: sticky; position: -webkit-sticky; top: 0; z-index: 90; isolation: isolate;
  background: color-mix(in srgb, var(--bg) 78%, transparent);
  backdrop-filter: blur(18px) saturate(1.5);
  border-bottom: 1px solid var(--line-2);
}
nav.top .wrap { display: flex; align-items: center; gap: 28px; padding: 13px 26px; }
/* Logo is the first thing that has to look like a real company — sized so the mark reads as a
   pool brush rather than a smudge, and the wordmark carries at a glance. */
.brand { display: flex; align-items: center; gap: 12px; font-weight: 800; font-size: 27px; letter-spacing: -.035em; color: var(--ink); }
.brand .mark { width: 32px; height: 41px; color: var(--brand); flex: 0 0 auto; }
@media (max-width: 560px) { .brand { font-size: 23px; } .brand .mark { width: 27px; height: 35px; } }
.navlinks { margin-left: auto; display: flex; gap: 26px; align-items: center; }
.navlinks a:not(.btn) { color: var(--ink-2); font-weight: 550; font-size: 14.5px; position: relative; }
/* Flex items shrink below their content width by default, which squeezed "The full
   picture" and "Sign in" into two lines each and made the header 95px tall on a
   1000px laptop. Nothing in the nav may wrap. */
.navlinks > * { flex: 0 0 auto; white-space: nowrap; }
.navlinks a:not(.btn)::after {
  content: ""; position: absolute; left: 0; bottom: -5px; height: 1.5px; width: 0;
  background: var(--brand); transition: width .25s var(--ease);
}
.navlinks a:not(.btn):hover { color: var(--ink); }
.navlinks a:not(.btn):hover::after { width: 100%; }
@media (max-width: 1010px) { .navlinks a:not(.btn):not(.keep) { display: none; } }

.themetoggle {
  width: 35px; height: 35px; border-radius: 10px; display: grid; place-items: center;
  background: var(--surface); border: 1px solid var(--line); color: var(--ink-2); cursor: pointer;
  transition: color .2s, border-color .2s, transform .2s var(--ease);
}
.themetoggle:hover { color: var(--brand); border-color: var(--brand); transform: rotate(-12deg); }
.themetoggle svg { width: 17px; height: 17px; }
:root[data-theme="light"] .icon-sun { display: none; }
:root:not([data-theme="light"]) .icon-moon { display: none; }

/* ---------------- buttons ---------------- */
.btn {
  display: inline-flex; align-items: center; gap: 9px; position: relative; overflow: hidden;
  background: linear-gradient(135deg,var(--brand),var(--brand-deep));
  color: #02141a; font-weight: 700; font-size: 15px; padding: 13px 24px; border-radius: 12px;
  border: 1px solid color-mix(in srgb, var(--brand) 60%, transparent); white-space: nowrap;
  /* Buttons must not inherit the body's 1.65 reading line-height. It made the nav CTA 53px tall,
     which dragged the whole header to 80px on desktop against 72px on mobile — and anything
     sticking below the header was positioned against the wrong number. */
  line-height: 1.2;
  transition: transform .2s var(--ease), box-shadow .25s var(--ease);
}
/* Only the FILLED button gets white text in light mode. Without the :not(.ghost) the
   transparent button inherits it too and becomes white-on-near-white. */
:root[data-theme="light"] .btn:not(.ghost) { color: #fff; }
.btn:hover { transform: translateY(-2px); box-shadow: 0 16px 40px -14px var(--brand); }
.btn.ghost { background: var(--surface); color: var(--ink); border-color: var(--line); }
.btn.ghost:hover { border-color: var(--brand); color: var(--brand); box-shadow: none; }
.btn.lg { font-size: 16.5px; padding: 16px 32px; border-radius: 13px; }

/* ---------------- hero ---------------- */
header.hero { position: relative; padding: 96px 0 96px; overflow: hidden; }
@media (max-width: 980px) { header.hero { padding: 60px 0 56px; } .hero-grid { gap: 40px; } }
.aurora { position: absolute; inset: -30% -10% auto -10%; height: 900px; z-index: -1; filter: blur(70px); opacity: .85; }
.aurora i { position: absolute; display: block; border-radius: 50%; }
.aurora i:nth-child(1){ width:640px;height:520px;left:52%;top:-90px;background:radial-gradient(circle,rgba(34,193,224,.42),transparent 68%);animation:drift1 22s ease-in-out infinite; }
.aurora i:nth-child(2){ width:560px;height:460px;left:2%;top:60px;background:radial-gradient(circle,rgba(14,127,158,.36),transparent 68%);animation:drift2 27s ease-in-out infinite; }
.aurora i:nth-child(3){ width:420px;height:380px;left:70%;top:300px;background:radial-gradient(circle,rgba(240,160,60,.14),transparent 68%);animation:drift3 31s ease-in-out infinite; }
@keyframes drift1 { 0%,100%{transform:translate(0,0)} 50%{transform:translate(-70px,50px)} }
@keyframes drift2 { 0%,100%{transform:translate(0,0)} 50%{transform:translate(60px,-40px)} }
@keyframes drift3 { 0%,100%{transform:translate(0,0)} 50%{transform:translate(-40px,-60px)} }

.hero-grid { display: grid; grid-template-columns: 1.02fr .98fr; gap: 50px; align-items: center; }
@media (max-width: 700px) { .hero-grid { grid-template-columns: 1fr; gap: 44px; } }

.eyebrow {
  display: inline-flex; align-items: center; gap: 9px; font-size: 12px; font-weight: 700;
  letter-spacing: .13em; text-transform: uppercase; color: var(--brand);
  background: var(--glow); border: 1px solid color-mix(in srgb,var(--brand) 28%,transparent);
  padding: 8px 15px; border-radius: 999px; margin-bottom: 26px;
}
.eyebrow .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--brand); box-shadow: 0 0 0 0 var(--brand); animation: pulse 2.6s infinite; }
@keyframes pulse { 0%{box-shadow:0 0 0 0 color-mix(in srgb,var(--brand) 70%,transparent)} 70%{box-shadow:0 0 0 9px transparent} 100%{box-shadow:0 0 0 0 transparent} }

.grad { background: linear-gradient(102deg,var(--brand-2),var(--brand) 45%,var(--warm)); -webkit-background-clip: text; background-clip: text; color: transparent; }
.hero p.lede { margin: 24px 0 32px; max-width: 34em; }
.cta-row { display: flex; gap: 14px; flex-wrap: wrap; align-items: center; }
.cta-note { font-size: 13.5px; color: var(--ink-3); margin-top: 18px; }

/* phone cluster.
   width:100% is load-bearing — every child is position:absolute, so the cluster has no in-flow
   content to size against and collapses to zero width, which makes the children's percentage
   widths resolve against nothing and squashes the phones into slivers. */
/* Height is derived, not guessed. Every child is absolutely positioned, so the cluster cannot
   size to its content — but the phones are a known fraction of its WIDTH (46%) at a known image
   ratio (720:1565), so the height they need is ~1.09x the cluster width once the 8% top offset
   is included. aspect-ratio expresses exactly that and stays correct at every screen size,
   where the old fixed 620/500/430px values left dead space at some widths and clipped at others. */
.cluster { position: relative; width: 100%; aspect-ratio: 1 / 1.09; }
@media (max-width: 700px) { .cluster { max-width: 440px; margin: 0 auto; } }
.cluster .phone { position: absolute; width: 46%; }
.cluster .phone:nth-child(1) { left: 0; top: 8%; transform: rotate(-5deg); z-index: 2; animation: float1 9s ease-in-out infinite; }
.cluster .phone:nth-child(2) { right: 2%; top: 0; transform: rotate(4deg); z-index: 3; animation: float2 11s ease-in-out infinite; }
.cluster .phone:nth-child(3) { left: 24%; bottom: 0; transform: rotate(-1deg); z-index: 1; opacity: .55; width: 42%; }
@keyframes float1 { 0%,100%{transform:rotate(-5deg) translateY(0)} 50%{transform:rotate(-5deg) translateY(-14px)} }
@keyframes float2 { 0%,100%{transform:rotate(4deg) translateY(0)} 50%{transform:rotate(4deg) translateY(12px)} }

.phone {
  position: relative; border-radius: 32px; padding: 7px;
  background: linear-gradient(155deg,var(--frame-2),var(--frame));
  border: 1px solid var(--frame-2); box-shadow: var(--shadow);
}
/* Screenshots are all 1206x2622 (iPhone Pro), exported at 720 wide. Pinning the ratio here means
   a container that misbehaves can crop the image but can never STRETCH it — a distorted product
   screenshot reads as amateur faster than almost anything else on a marketing page. */
.phone img {
  border-radius: 26px;
  width: 100%; height: auto;
  aspect-ratio: 720 / 1565;
  object-fit: cover; object-position: top center;
}
.phone::after {
  content: ""; position: absolute; top: 14px; left: 50%; transform: translateX(-50%);
  width: 58px; height: 5px; border-radius: 4px; background: rgba(0,0,0,.55);
}
/* glass sheen */
.phone::before {
  content: ""; position: absolute; inset: 7px; border-radius: 26px; pointer-events: none; z-index: 2;
  background: linear-gradient(125deg, rgba(255,255,255,.11), transparent 42%);
}

/* ---------------- sections ---------------- */
section { padding: 108px 0; position: relative; }
section.tight { padding: 62px 0; }
.band { background: var(--bg-2); border-top: 1px solid var(--line-2); border-bottom: 1px solid var(--line-2); }
.section-head { max-width: 44em; margin-bottom: 60px; }
.section-head.center { margin-inline: auto; text-align: center; }
.kicker { font-size: 12.5px; font-weight: 700; letter-spacing: .15em; text-transform: uppercase; color: var(--brand); margin-bottom: 16px; }
.section-head p { color: var(--ink-2); font-size: 1.08rem; margin-top: 20px; }

/* ---------------- replaces ---------------- */
.replaces { display: grid; grid-template-columns: repeat(4,1fr); gap: 14px; }
/* min-width:0 is required, not cosmetic: a 1fr track defaults to min-width:auto, so the cells
   refuse to shrink below their longest unbreakable phrase and push the grid wider than the page.
   That was the last 30px of horizontal overflow at 320px. */
.replaces > div { min-width: 0; }
@media (max-width: 820px) { .replaces { grid-template-columns: repeat(2,1fr); } }
@media (max-width: 400px) { .replaces { grid-template-columns: 1fr; } }
.replaces div {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-sm);
  padding: 22px 20px; transition: border-color .25s, transform .25s var(--ease);
}
.replaces div:hover { border-color: color-mix(in srgb,var(--brand) 40%,var(--line)); transform: translateY(-3px); }
.replaces .from { color: var(--ink-3); font-size: 13.5px; text-decoration: line-through; margin-bottom: 7px; }
.replaces .to { font-weight: 700; font-size: 15.5px; letter-spacing: -.015em; }

/* ---------------- flow rail ---------------- */
.flow { position: relative; display: grid; gap: 74px; }
.flow::before {
  content: ""; position: absolute; left: 50%; top: 40px; bottom: 40px; width: 1px;
  background: linear-gradient(180deg,transparent,var(--line),var(--line),transparent);
}
@media (max-width: 700px) { .flow::before { left: 17px; } .flow { gap: 58px; } }

/* 260px keeps the flow phones close to the rail thumbnails, which reads far more compact
   than the near-400px originals without losing legibility of the screens. */
.step { display: grid; grid-template-columns: 1fr 300px; gap: 56px; align-items: center; }
.step:nth-child(even) { grid-template-columns: 300px 1fr; }
.step:nth-child(even) .step-copy { order: 2; }
@media (max-width: 700px) {
  .step, .step:nth-child(even) { grid-template-columns: 1fr; gap: 34px; padding-left: 46px; }
  .step:nth-child(even) .step-copy { order: 0; }
  .stepgal { max-width: 320px; }
}
.step-n {
  position: relative; display: inline-grid; place-items: center; width: 40px; height: 40px;
  border-radius: 12px; background: var(--bg); border: 1px solid color-mix(in srgb,var(--brand) 40%,transparent);
  color: var(--brand); font-weight: 800; font-size: 15.5px; margin-bottom: 20px;
  box-shadow: 0 0 0 6px var(--bg), 0 0 30px -4px var(--glow);
}
/* superseded by the sticky rules below */
.step h3 { font-size: 1.58rem; margin-bottom: 13px; }
.step p { color: var(--ink-2); }
.step ul { margin: 20px 0 0; padding: 0; list-style: none; display: grid; gap: 10px; }
.step li { position: relative; padding-left: 27px; color: var(--ink-2); font-size: 15.3px; }
.step li::before {
  content: ""; position: absolute; left: 1px; top: 8px; width: 13px; height: 7px;
  border-left: 2px solid var(--good); border-bottom: 2px solid var(--good); transform: rotate(-45deg);
}

/* ---------------- spotlight ---------------- */
.spotlight { display: grid; grid-template-columns: 1fr 300px; gap: 62px; align-items: center; }
.spotlight.flip { grid-template-columns: 300px 1fr; }
.spotlight.flip .sp-copy { order: 2; }
@media (max-width: 700px) {
  .spotlight, .spotlight.flip { grid-template-columns: 1fr; gap: 38px; }
  .spotlight.flip .sp-copy { order: 0; }
  .spotlight .phone { max-width: 280px; margin: 0 auto; }
}
.statrow { display: grid; grid-template-columns: repeat(3,1fr); gap: 16px; margin-top: 34px; }
@media (max-width: 640px) { .statrow { grid-template-columns: 1fr; } }
.stat { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-sm); padding: 20px; }
.stat b { display: block; font-size: 1.5rem; letter-spacing: -.035em; color: var(--brand); margin-bottom: 4px; }
.stat span { font-size: 13px; color: var(--ink-3); line-height: 1.45; display: block; }

/* ---------------- bento ---------------- */
.bento { display: grid; grid-template-columns: repeat(6,1fr); gap: 18px; }
.bento .cell {
  grid-column: span 2; background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r); padding: 28px 26px; position: relative; overflow: hidden;
  transition: border-color .25s, transform .25s var(--ease);
}
.bento .cell:hover { border-color: color-mix(in srgb,var(--brand) 45%,var(--line)); transform: translateY(-3px); }
.bento .cell.wide { grid-column: span 3; }
.bento .cell h3 { margin-bottom: 11px; font-size: 1.08rem; }
.bento .cell p { color: var(--ink-2); font-size: 14.7px; margin: 0; line-height: 1.6; }
.ficon {
  width: 38px; height: 38px; border-radius: 11px; display: grid; place-items: center; margin-bottom: 18px;
  background: var(--glow); color: var(--brand); border: 1px solid color-mix(in srgb,var(--brand) 22%,transparent);
}
.ficon svg { width: 19px; height: 19px; }
@media (max-width: 980px) { .bento { grid-template-columns: repeat(4,1fr); } .bento .cell.wide { grid-column: span 2; } }
@media (max-width: 640px) { .bento { grid-template-columns: 1fr; } .bento .cell, .bento .cell.wide { grid-column: span 1; } }

/* ---------------- marquee / draggable rail ----------------
   Driven by JS rather than a CSS keyframe: it drifts on its own, but you can grab it, throw it,
   and it carries momentum. touch-action:pan-y keeps vertical page scrolling working on a phone
   while horizontal drags belong to the rail. */
.marquee {
  overflow: hidden; cursor: grab; touch-action: pan-y;
  -webkit-user-select: none; user-select: none;
  /* Fade in PIXELS, not percent. At 7% a 1440px viewport dissolved 100px off each end, so the
     outermost screen was always half-gone; at 34px it reads as an edge hint at every width. */
  mask-image: linear-gradient(90deg, transparent 0, #000 34px, #000 calc(100% - 34px), transparent 100%);
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 34px, #000 calc(100% - 34px), transparent 100%);
}
.marquee.dragging { cursor: grabbing; }
/* The gutter is what actually fixes the clipped end photos: it parks the first and last screen
   clear of the fade, so at either travel limit that screen is whole rather than half-dissolved.
   It counts toward scrollWidth, which is what minX is derived from, so the stops stay exact. */
.marquee-track {
  display: flex; gap: 20px; width: max-content; will-change: transform;
  padding-inline: 44px;
}
.marquee .phone { width: 208px; flex: 0 0 auto; border-radius: 24px; padding: 5px; cursor: zoom-in; }
.marquee.dragging .phone { cursor: grabbing; }
.marquee img { pointer-events: none; -webkit-user-drag: none; }

/* scrubber
   The bar is a TRACK, not a hit target: cursor:default and no pointer handler on it, so a stray
   click near the rail can't teleport the strip. Everything is driven by the thumb, which is
   deliberately narrow (fine control over sixty-odd screens) and tall (easy to grab and to see
   at a glance). touch-action:none on the thumb because the rail above it claims pan-y — without
   this, dragging the thumb on a phone would scroll the page instead. */
.scrubber {
  position: relative; height: 8px; border-radius: 999px; background: var(--surface-2);
  border: 1px solid var(--line); max-width: 660px; margin: 34px auto 8px; cursor: default;
}
.scrubber .thumb {
  position: absolute; top: -10px; height: 28px; border-radius: 999px; min-width: 24px;
  background: linear-gradient(135deg,var(--brand),var(--brand-deep));
  border: 1px solid color-mix(in srgb,var(--brand) 55%,transparent);
  box-shadow: 0 4px 14px -4px var(--brand); cursor: grab; touch-action: none;
  transition: transform .18s var(--ease), box-shadow .18s var(--ease);
}
.scrubber .thumb:hover { transform: scale(1.07); box-shadow: 0 5px 18px -4px var(--brand); }
.scrubber.dragging .thumb { cursor: grabbing; transform: scale(1.07); }
/* Grip: three upright bars, the right way round for a tall thumb. */
.scrubber .thumb::after {
  content: ""; position: absolute; inset: 0; margin: auto; width: 2px; height: 11px;
  border-radius: 2px; background: rgba(255,255,255,.6);
  box-shadow: -5px 0 0 rgba(255,255,255,.6), 5px 0 0 rgba(255,255,255,.6);
}
.marquee .phone img { border-radius: 20px; }
.marquee .phone::after { width: 42px; height: 4px; top: 10px; }
.marquee .phone::before { inset: 5px; border-radius: 20px; }

/* ---------------- compare ---------------- */
.cmp-scroll { overflow-x: auto; border: 1px solid var(--line); border-radius: var(--r); background: var(--surface); }
.cmp { width: 100%; border-collapse: collapse; font-size: 15px; min-width: 660px; }
.cmp th, .cmp td { padding: 17px 18px; border-bottom: 1px solid var(--line-2); text-align: left; vertical-align: top; }
.cmp tbody tr:last-child td { border-bottom: 0; }
.cmp thead th { font-size: 12px; letter-spacing: .1em; text-transform: uppercase; color: var(--ink-3); font-weight: 700; }
.cmp thead th.us { color: var(--brand); }
.cmp td.feat { font-weight: 650; color: var(--ink); width: 38%; }
.cmp td small { display: block; color: var(--ink-3); font-weight: 400; margin-top: 4px; font-size: 12.8px; line-height: 1.45; }
.cmp .yes { color: var(--good); font-weight: 650; }
.cmp .no { color: var(--ink-3); }
.cmp tbody tr:hover { background: var(--surface-2); }

/* ---------------- faq ---------------- */
.faq { display: grid; gap: 12px; max-width: 50em; margin: 0 auto; }
.faq details { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-sm); padding: 0 22px; transition: border-color .2s; }
.faq details[open] { border-color: color-mix(in srgb,var(--brand) 35%,var(--line)); }
.faq summary { cursor: pointer; padding: 19px 0; font-weight: 650; list-style: none; display: flex; justify-content: space-between; gap: 18px; align-items: center; }
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after { content: "+"; color: var(--brand); font-size: 23px; font-weight: 300; line-height: 1; transition: transform .25s var(--ease); }
.faq details[open] summary::after { content: "\2212"; }
.faq details p { color: var(--ink-2); font-size: 15.2px; padding-bottom: 20px; margin: 0; }

/* ---------------- final cta ---------------- */
.finalcta { text-align: center; padding: 116px 0; position: relative; overflow: hidden; }
.finalcta .cta-row { justify-content: center; }

/* ---------------- footer ---------------- */
footer { background: var(--bg-2); border-top: 1px solid var(--line-2); padding: 60px 0 40px; font-size: 14.6px; }
.fcols { display: grid; grid-template-columns: 1.7fr 1fr 1fr 1fr; gap: 36px; margin-bottom: 42px; }
@media (max-width: 780px) { .fcols { grid-template-columns: 1fr 1fr; } }
footer h4 { font-size: 12px; letter-spacing: .13em; text-transform: uppercase; color: var(--ink-3); margin: 0 0 15px; font-weight: 700; }
footer ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; }
footer a { color: var(--ink-2); }
footer a:hover { color: var(--brand); }
.fbot { border-top: 1px solid var(--line-2); padding-top: 26px; color: var(--ink-3); display: flex; flex-wrap: wrap; gap: 14px; justify-content: space-between; font-size: 13.6px; }

/* ---------------- reveal ---------------- */
.reveal { opacity: 0; transform: translateY(22px); transition: opacity .7s var(--ease), transform .7s var(--ease); }
.reveal.in { opacity: 1; transform: none; }

/* ---------------- lead form ---------------- */
.lead { max-width: 720px; margin: 0 auto; text-align: left; }
.frow { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-bottom: 16px; }
@media (max-width: 620px) { .frow { grid-template-columns: 1fr; } }
.field { display: block; margin-bottom: 16px; }
.field > span { display: block; font-size: 13.5px; font-weight: 650; color: var(--ink-2); margin-bottom: 7px; }
.field > span b { color: var(--brand); font-weight: 700; }
.field input, .field select, .field textarea {
  width: 100%; font: inherit; font-size: 15.5px; color: var(--ink);
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-sm);
  padding: 13px 15px; transition: border-color .18s, box-shadow .18s;
}
.field textarea { resize: vertical; min-height: 96px; line-height: 1.55; }
.field input::placeholder, .field textarea::placeholder { color: var(--ink-3); opacity: .75; }
.field input:focus, .field select:focus, .field textarea:focus {
  outline: none; border-color: var(--brand);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand) 20%, transparent);
}
.field select {
  appearance: none; cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' fill='none' stroke='%236f8996' stroke-width='2' stroke-linecap='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 15px center; padding-right: 40px;
}
.field select option { background: var(--bg-2); color: var(--ink); }
.field input[aria-invalid="true"], .field select[aria-invalid="true"] { border-color: #e2584d; }
/* Honeypot: moved off-screen rather than display:none — some bots skip hidden fields but still
   fill positioned ones, and screen readers get aria-hidden on the wrapper. */
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
.fsubmit { display: flex; align-items: center; gap: 18px; flex-wrap: wrap; margin-top: 8px; }
.fnote { font-size: 13px; color: var(--ink-3); }
.fmsg { margin: 16px 0 0; font-size: 14.6px; min-height: 1.2em; }
.fmsg.ok { color: var(--good); }
.fmsg.err { color: #e2584d; }
.lead.sent { text-align: center; }
button.btn { cursor: pointer; font-family: inherit; }
button.btn[disabled] { opacity: .6; cursor: progress; }

/* ---------------- value copy ---------------- */
/* The "what it's worth" line under each flow step. Accented rather than boxed so it reads as a
   continuation of the step, not a callout the eye learns to skip. */
.payoff {
  margin: 18px 0 0; padding: 12px 0 12px 16px;
  border-left: 2px solid color-mix(in srgb, var(--brand) 55%, transparent);
  color: var(--ink-2); font-size: 14.8px; line-height: 1.58;
}
.payoff b { color: var(--ink); font-weight: 700; }

.twocol { display: grid; grid-template-columns: 1fr 1fr; gap: 22px; }
@media (max-width: 700px) { .twocol { grid-template-columns: 1fr; } }
.vcard {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r);
  padding: 32px 30px; transition: border-color .25s;
}
.vcard:hover { border-color: color-mix(in srgb, var(--brand) 40%, var(--line)); }
.vcard h3 { font-size: 1.35rem; margin-bottom: 12px; }
.vlede { color: var(--ink-2); font-size: 15.4px; margin-bottom: 22px; }
.vcard ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 14px; }
.vcard li { position: relative; padding-left: 27px; color: var(--ink-2); font-size: 15.1px; line-height: 1.58; }
.vcard li b { color: var(--ink); font-weight: 650; }
.vcard li::before {
  content: ""; position: absolute; left: 1px; top: 8px; width: 13px; height: 7px;
  border-left: 2px solid var(--good); border-bottom: 2px solid var(--good); transform: rotate(-45deg);
}

/* nav sign-in — quieter than the primary CTA, but findable, because existing customers
   arriving at the marketing site should not have to hunt for the way in. */
.signin {
  color: var(--ink-2); font-weight: 650; font-size: 14.5px; padding: 9px 15px;
  border: 1px solid var(--line); border-radius: 10px; transition: color .2s, border-color .2s;
}
.signin:hover { color: var(--brand); border-color: var(--brand); }

/* five footer columns now that sign-in has its own */
.fcols { grid-template-columns: 1.6fr 1fr 1fr 1fr 1fr; }
@media (max-width: 980px) { .fcols { grid-template-columns: 1fr 1fr 1fr; } }
@media (max-width: 640px) { .fcols { grid-template-columns: 1fr 1fr; } }

/* ---------------- real logo ----------------
   The brand artwork is an etched illustration, not line art. It ships as transparent PNGs whose
   ALPHA carries the etching, and is painted with mask + currentColor-style background — so the
   real texture is preserved while the colour still follows the theme. Using <img> instead would
   lock it to one colour and look wrong in light mode. */
.brand .mark {
  display: block; flex: 0 0 auto;
  width: 46px; height: 36px; background: var(--brand);
  -webkit-mask: url("/brand/logo-brush.png") center/contain no-repeat;
  mask: url("/brand/logo-brush.png") center/contain no-repeat;
}
.brand:hover .mark { background: var(--brand-2); }
@media (max-width: 560px) { .brand .mark { width: 38px; height: 30px; } }

/* ---------------- mobile nav ---------------- */
.navtoggle {
  display: none; width: 40px; height: 40px; border-radius: 10px; place-items: center;
  background: var(--surface); border: 1px solid var(--line); color: var(--ink); cursor: pointer;
}
.navtoggle svg { width: 20px; height: 20px; }
.navtoggle .close { display: none; }
.navtoggle[aria-expanded="true"] .open { display: none; }
.navtoggle[aria-expanded="true"] .close { display: block; }
@media (max-width: 1010px) {
  .navtoggle { display: grid; }
  .navlinks {
    /* Was a hardcoded 64px while the header is actually 72 — the panel opened 8px high and sat
       over the header's bottom edge. --nav-h is measured and written by app.js. */
    position: fixed; inset: var(--nav-h, 72px) 0 auto; padding: 18px 26px 26px;
    background: var(--bg); border-bottom: 1px solid var(--line);
    flex-direction: column; align-items: stretch; gap: 4px;
    transform: translateY(-12px); opacity: 0; pointer-events: none;
    transition: opacity .22s var(--ease), transform .22s var(--ease);
    max-height: calc(100dvh - var(--nav-h, 72px)); overflow-y: auto;
  }
  .navlinks.open { transform: none; opacity: 1; pointer-events: auto; }
  /* the toggle is inside .navlinks in source order, so pull it back out visually */
  nav.top .wrap { position: relative; }
  .navlinks a:not(.btn):not(.keep), .navlinks a { display: block !important; }
  .navlinks a:not(.btn) { padding: 13px 2px; font-size: 16.5px; border-bottom: 1px solid var(--line-2); }
  .navlinks a:not(.btn)::after { display: none; }
  .navlinks .btn, .navlinks .signin { margin-top: 12px; justify-content: center; text-align: center; }
  .navactions { margin-left: auto; display: flex; align-items: center; gap: 10px; }
}
.navactions { display: contents; }
@media (max-width: 1010px) { .navactions { display: flex; } }

/* ---------------- lightbox ---------------- */
.lb {
  position: fixed; inset: 0; z-index: 200; display: none;
  background: rgba(3,8,12,.94); backdrop-filter: blur(8px);
  /* Mobile browser chrome (Safari's bottom bar, Chrome's collapsing address bar) changes the
     usable height as you scroll, and 100vh does not follow it — the photo would overflow under
     the bar and the thumbnails would fall off screen. dvh tracks the DYNAMIC viewport, and the
     JS below also pins --lb-h from visualViewport for engines that lag on dvh. */
  height: 100dvh; height: var(--lb-h, 100dvh);
  padding: max(14px, env(safe-area-inset-top)) 14px max(14px, env(safe-area-inset-bottom));
  flex-direction: column; align-items: center; justify-content: center;
  overscroll-behavior: contain;
}
.lb.open { display: flex; }
/* min-height:0 lets the stage actually shrink inside the flex column; without it the image's
   intrinsic height wins and pushes the thumbnail strip off the bottom, which is what made the
   thumbnails look like they were sliding under the photo. */
/* FLEX, not grid. This was `display:grid; place-items:center`, and the image's max-height:100%
   was silently ignored: a grid row is auto-sized, so the image's containing block had no
   definite height, and a percentage max-height against an indefinite height resolves to none.
   The image rendered at its natural ratio — 954px tall inside a 354px stage — overflowing the
   bottom by 600px and sitting under the thumbnail strip. A flex container that has already been
   given a definite height by the column above it resolves that percentage properly. */
.lb-stage {
  flex: 1 1 auto; min-height: 0; width: 100%; position: relative;
  display: flex; align-items: center; justify-content: center;
  touch-action: pan-y pinch-zoom; z-index: 1;
}
.lb-stage img {
  max-height: 100%; max-width: min(100%, 440px); width: auto; height: auto;
  min-height: 0;                  /* a flex item's automatic minimum size would re-inflate it */
  object-fit: contain;
  border-radius: 18px; border: 1px solid rgba(255,255,255,.14);
  box-shadow: 0 40px 90px -30px #000;
}
.lb-close, .lb-nav {
  position: absolute; display: grid; place-items: center; cursor: pointer;
  background: rgba(255,255,255,.09); border: 1px solid rgba(255,255,255,.16);
  color: #fff; border-radius: 999px; transition: background .18s, transform .18s var(--ease);
}
.lb-close:hover, .lb-nav:hover { background: rgba(255,255,255,.2); }
.lb-close { top: 14px; right: 14px; width: 42px; height: 42px; font-size: 20px; line-height: 1; }
.lb-nav { top: 50%; transform: translateY(-50%); width: 46px; height: 46px; }
.lb-nav.prev { left: 10px; } .lb-nav.next { right: 10px; }
.lb-nav svg { width: 20px; height: 20px; }
.lb-nav[hidden] { display: none; }
.lb-count { position: absolute; top: 20px; left: 20px; color: rgba(255,255,255,.6); font-size: 13.5px; letter-spacing: .04em; }
/* flex:0 0 auto + its own stacking context: the strip keeps its height no matter how tall the
   photo is, and stays ABOVE the stage rather than being painted under it. */
.lb-thumbs {
  display: flex; gap: 8px; margin-top: 14px; overflow-x: auto; max-width: 100%;
  padding: 4px; scrollbar-width: none; flex: 0 0 auto; position: relative; z-index: 2;
  overscroll-behavior-x: contain;
}
.lb-thumbs::-webkit-scrollbar { display: none; }
.lb-thumbs button {
  flex: 0 0 auto; width: 46px; padding: 0; border-radius: 8px; overflow: hidden; cursor: pointer;
  border: 2px solid transparent; background: none; opacity: .5; transition: opacity .18s, border-color .18s;
}
.lb-thumbs button.active { opacity: 1; border-color: var(--brand); }
.lb-thumbs button:hover { opacity: 1; }
.lb-thumbs img { width: 100%; height: auto; display: block; aspect-ratio: 720/1565; object-fit: cover; object-position: top; }
@media (max-width: 560px) { .lb-nav { width: 40px; height: 40px; } .lb-thumbs button { width: 38px; } }

/* ---------------- step numbers ----------------
   Sticky on MOBILE ONLY. On desktop the number sits beside its own copy in a two-column layout,
   so there is nothing for it to track — pinning it there just detaches it from the text it
   labels. On a phone the steps are one long column and the number genuinely helps.

   Alignment: `left` on a position:sticky element is a stickiness THRESHOLD, not a placement
   offset — setting it moved nothing, which is why the numbers sat in the text column instead of
   on the rail. The spine is at 17px and the badge is 40px wide, so its left edge belongs at
   17 - 20 = -3px from the flow's edge, i.e. -49px back from the step's 46px content inset.
   That is a margin, and margins do move things. */
@media (max-width: 700px) {
  /* A sticky element can only travel inside its own containing block, and .step-copy ends where
     the gallery begins — which is why the number let go halfway down the step. display:contents
     dissolves that wrapper so the copy's children become children of .step directly, making the
     WHOLE step (text and gallery) the travel range. The number now holds until the next one
     takes over. */
  .step { display: block; padding-left: 46px; }
  .step-copy { display: contents; }
  .step-n {
    position: sticky; position: -webkit-sticky;
    /* Clears BOTH sticky layers above it: the header and the stuck chapter bar under it. Both
       are measured at runtime — at a hardcoded 76px the numbers slid behind the tab bar. */
    top: calc(var(--nav-h, 72px) + var(--tabbar-h, 56px) + 8px); z-index: 4;
    margin: 0 0 16px -49px;
  }
  .stepgal { margin-top: 24px; }
}

/* ---------------- per-step gallery ----------------
   A real track, not a src swap. The slides sit side by side and the track follows the pointer
   1:1 while you drag, then eases to the nearest slide on release — so the movement you make is
   the movement you see. Swapping the image on pointerup (what this did before) reads as a
   snap because nothing moves until the gesture is already over.

   Slides are 82% of the frame so the neighbours peek at the edges; at the first and last slide
   only one side has anything to show, which is itself the signal that you are at an end. */
.stepgal { display: grid; gap: 12px; }

.gal-main {
  position: relative; overflow: hidden; cursor: grab;
  touch-action: pan-y;                 /* vertical scroll stays with the page */
  -webkit-user-select: none; user-select: none;
}
.gal-main.dragging { cursor: grabbing; }
.gal-track { display: flex; gap: 12px; will-change: transform; }
.gal-track.animating { transition: transform .42s cubic-bezier(.22,1,.36,1); }
.gal-slide { flex: 0 0 82%; }
.gal-slide img { -webkit-user-drag: none; pointer-events: none; }

.gal-frame {
  position: relative; padding: 7px; border: 1px solid var(--frame-2); border-radius: 30px;
  background: linear-gradient(155deg, var(--frame-2), var(--frame)); box-shadow: var(--shadow);
  transition: opacity .35s var(--ease), transform .35s var(--ease);
  opacity: .42; transform: scale(.94);
}
.gal-slide.current .gal-frame { opacity: 1; transform: none; }
.gal-frame::after {
  content: ""; position: absolute; top: 14px; left: 50%; transform: translateX(-50%);
  width: 52px; height: 5px; border-radius: 4px; background: rgba(0,0,0,.55);
}
/* aspect-ratio and object-fit together mean a missing sizing rule can only ever crop, never
   stretch — this block was deleted once and the screenshots rendered at 3x their height. */
.gal-frame img {
  width: 100%; height: auto; display: block; border-radius: 24px;
  aspect-ratio: 720 / 1565; object-fit: cover; object-position: top center;
}

.gal-thumbs { display: flex; gap: 8px; }
.gal-thumbs button {
  flex: 1 1 0; min-width: 0; padding: 0; border-radius: 10px; overflow: hidden; cursor: pointer;
  background: none; border: 2px solid transparent; opacity: .45;
  transition: opacity .2s, border-color .2s, transform .2s var(--ease);
}
.gal-thumbs button:hover { opacity: .85; transform: translateY(-2px); }
.gal-thumbs button.active { opacity: 1; border-color: var(--brand); }
.gal-thumbs img {
  width: 100%; height: auto; display: block; border-radius: 8px;
  aspect-ratio: 720 / 900; object-fit: cover; object-position: top center;
}
.stepgal[data-single] .gal-thumbs { display: none; }
.stepgal[data-single] .gal-slide { flex: 0 0 100%; }
.stepgal[data-single] .gal-frame { opacity: 1; transform: none; }

@media (max-width: 700px) { .stepgal { max-width: 340px; } }
@media (max-width: 560px) { .stepgal { max-width: 100%; } }


/* ================= sub-page furniture =================
   The site is three pages now: home, the walkthrough and contact. Everything below exists
   because the walkthrough was ten steps down one page — a lot of scrolling to reach the part
   you came for — and the contact form was a wall at the bottom of the home page. */

/* A hero with no phone cluster beside it: centred copy, and on the walkthrough, the chapter
   tabs sitting directly underneath. */
header.hero.pagehero { padding: 84px 0 56px; }
header.hero.pagehero.short { padding: 84px 0 44px; }
.pagehero h1 { font-size: clamp(2.1rem, 5vw, 3.4rem); }
.pagehero .lede { max-width: 40em; margin-bottom: 0; }
@media (max-width: 980px) { header.hero.pagehero, header.hero.pagehero.short { padding: 54px 0 38px; } }

/* ---------------- walkthrough chapter tabs ----------------
   A compact row of labels, and it looks the SAME resting as it does stuck. The first version
   was a two-row card grid at rest that collapsed to one row on stick — a visible jolt partway
   down the page, right when you were reading. Now only the SURFACE changes: the bar picks up a
   background, a blur and a hairline as it catches under the header, and nothing moves.

   It has to live outside header.hero to stick at all — that header sets overflow:hidden to
   contain the aurora, and an ancestor with overflow:hidden silently disables position:sticky on
   everything inside it. Same class of bug as the body{overflow-x} one that broke the header. */
.wtabsentinel { height: 1px; margin-bottom: -1px; }
.wtabbar {
  position: sticky; position: -webkit-sticky;
  top: var(--nav-h, 72px);  /* measured by app.js — the header is not a fixed height */
  z-index: 60;              /* under the nav's 90, over the page */
  padding: 14px 0;
  border-bottom: 1px solid transparent;
  transition: background .24s var(--ease), box-shadow .24s var(--ease), border-color .24s var(--ease);
}
.wtabbar.stuck {
  background: color-mix(in srgb, var(--bg) 86%, transparent);
  backdrop-filter: blur(18px) saturate(1.5);
  border-bottom-color: var(--line-2);
  box-shadow: 0 14px 30px -26px rgba(0,0,0,.85);
}
.wtabs {
  display: flex; gap: 8px; justify-content: center;
  overflow-x: auto; scroll-behavior: smooth;
  scrollbar-width: none; -ms-overflow-style: none;
}
.wtabs::-webkit-scrollbar { display: none; }
/* Centring a flex row that overflows makes its start unreachable, so hand back to flex-start
   at the width where eight chapters stop fitting. */
@media (max-width: 1080px) { .wtabs { justify-content: flex-start; } }

.wtab {
  position: relative; flex: 0 0 auto; text-align: left; cursor: pointer; white-space: nowrap;
  background: var(--surface); border: 1px solid var(--line); border-radius: 999px;
  padding: 9px 16px; color: var(--ink-2); font: inherit;
  transition: border-color .2s var(--ease), background .2s var(--ease),
              color .2s var(--ease), box-shadow .2s var(--ease);
}
.wtab:hover { border-color: color-mix(in srgb, var(--brand) 45%, transparent); }
.wtab:focus-visible { outline: 2px solid var(--brand); outline-offset: 3px; }
.wtab .wtab-l {
  display: block; font-weight: 700; font-size: 13.8px; color: var(--ink); letter-spacing: -.012em;
}
/* The chapter blurbs stay in the markup but are never rendered — the labels carry it, and the
   compact bar is what makes the sticky behaviour bearable. */
.wtab .wtab-d { display: none; }
@media (max-width: 620px) { .wtab { padding: 8px 13px; } .wtab .wtab-l { font-size: 13.2px; } }

.wtab.active {
  background: color-mix(in srgb, var(--brand) 15%, var(--surface));
  border-color: color-mix(in srgb, var(--brand) 62%, transparent);
  box-shadow: 0 8px 22px -14px var(--brand);
}
.wtab.active .wtab-l { color: var(--brand); }
.wtab.active .wtab-d { color: var(--ink-2); }
/* The nib that ties the selected card to the panel below it. */
/* The nib that used to tie the selected card to its panel is gone with the cards — on a pill
   in a scrolling row it would point at nothing. */
@media (max-width: 620px) { .wtab .wtab-l { font-size: 14px; } }

/* [hidden] loses to any display declaration, so say it explicitly rather than relying on the
   UA sheet — a panel that stays visible would stack all four chapters on top of each other. */
.wpanel { padding: 54px 0 20px; }
.wpanel[hidden] { display: none; }
@media (max-width: 980px) { .wpanel { padding: 38px 0 12px; } }

/* ---------------- walkthrough teaser (home) ----------------
   The tour card and the swipe phone share this row: the card says what the walkthrough is, the
   phone lets you take either action without scrolling to the bottom for it. */
.tourgrid { display: grid; grid-template-columns: 1fr 330px; gap: 46px; align-items: center; }
@media (min-width: 701px) and (max-width: 1000px) { .tourgrid { grid-template-columns: 1fr 270px; gap: 30px; } }
@media (max-width: 700px) { .tourgrid { grid-template-columns: 1fr; gap: 34px; } }
.tourgrid .swipe-phone { max-width: 330px; width: 100%; margin: 0 auto; }
.tourgrid .swipe-help { text-align: center; grid-column: 1 / -1; margin: 4px 0 0; }
/* Not a link any more — it explains the phone next to it rather than going anywhere, so it
   keeps the lift on hover but loses the pointer cursor that promises a destination. */
.tourcard {
  display: flex; align-items: center; justify-content: space-between; gap: 28px;
  padding: 30px 32px; border-radius: var(--r); cursor: default;
  background: var(--surface); border: 1px solid var(--line);
  transition: border-color .25s var(--ease), transform .25s var(--ease), box-shadow .25s var(--ease);
}
.tourcard:hover {
  border-color: color-mix(in srgb, var(--brand) 50%, transparent);
  transform: translateY(-3px); box-shadow: 0 26px 60px -34px var(--brand);
}
.tourcard h3 { font-size: 1.36rem; letter-spacing: -.02em; margin-bottom: 9px; color: var(--ink); }
.tourcard p { color: var(--ink-2); max-width: 52em; font-size: 15.3px; }
.tourcard .kicker { margin-bottom: 11px; }
.tourgo {
  flex: 0 0 auto; display: grid; place-items: center; width: 48px; height: 48px;
  border-radius: 50%; background: var(--surface-2); border: 1px solid var(--line); color: var(--brand);
  transition: transform .25s var(--ease), background .25s var(--ease);
}
.tourgo svg { width: 21px; height: 21px; }
.tourcard:hover .tourgo { background: color-mix(in srgb, var(--brand) 16%, transparent); }
.tourcard:hover .tourgo svg { animation: tournudge 1.1s var(--ease) infinite; }
@keyframes tournudge { 0%,100%{transform:none} 30%{transform:translateX(3px)} 65%{transform:translateX(-3px)} }
@media (prefers-reduced-motion: reduce) { .tourcard:hover .tourgo svg { animation: none; } }
@media (max-width: 700px) {
  .tourcard { flex-direction: column; align-items: flex-start; gap: 20px; padding: 26px 22px; }
}

/* ---------------- contact page ---------------- */
.cpoints {
  list-style: none; padding: 0; margin: 44px auto 0; max-width: 760px;
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px;
}
@media (max-width: 800px) { .cpoints { grid-template-columns: 1fr; max-width: 560px; } }
.cpoints li {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-sm);
  padding: 18px 19px; color: var(--ink-3); font-size: 13.8px; line-height: 1.55;
}
.cpoints b { display: block; color: var(--ink); font-size: 14.6px; margin-bottom: 5px; font-weight: 700; }

/* ---------------- proof grid ----------------
   Deliberately 2-up rather than the bento's 6-col. This section sits directly above the bento,
   and at the same rhythm the two would read as one long block of cards instead of a claim
   section followed by a feature list. Wider cards also suit sentences over labels. */
.proof { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
@media (max-width: 760px) { .proof { grid-template-columns: 1fr; } }
.pcard {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r);
  padding: 27px 26px; transition: border-color .25s, transform .25s var(--ease);
}
.pcard:hover { border-color: color-mix(in srgb,var(--brand) 45%,var(--line)); transform: translateY(-3px); }
.pcard h3 { margin-bottom: 10px; font-size: 1.06rem; }
.pcard p { color: var(--ink-2); font-size: 14.7px; margin: 0; line-height: 1.6; }
.proofnote {
  margin: 30px auto 0; max-width: 720px; text-align: center;
  color: var(--ink-3); font-size: 14px; line-height: 1.6;
}
.proofnote b { color: var(--ink-2); font-weight: 600; }

.cta-row.center { justify-content: center; }

/* Current page in the nav — a quiet marker, not a second button. */
.navlinks a.here { color: var(--brand); }

/* ---------------- tablet ----------------
   An iPad has room for a copy column and a phone beside it; stacking at 980 threw away half the
   screen and made the whole page read like a very long phone page. Between 701 and 1000 the
   PHONE COLUMN narrows instead of the layout collapsing. */
@media (min-width: 701px) and (max-width: 1000px) {
  .hero-grid { grid-template-columns: 1fr .8fr; gap: 28px; }
  .cluster { max-width: none; }
  .spotlight { grid-template-columns: 1fr 244px; gap: 34px; }
  .spotlight.flip { grid-template-columns: 244px 1fr; }
  .step { grid-template-columns: 1fr 244px; gap: 32px; }
  .step:nth-child(even) { grid-template-columns: 244px 1fr; }
  .statrow { gap: 12px; }
  .stat { padding: 15px 14px; }
  .stat b { font-size: 1.25rem; }
}

/* ---------------- spotlight action row ----------------
   The pitch section is where the natural scroll lands, so it carries the action itself rather
   than only a link deeper into the site. */
.sp-actions { display: flex; align-items: center; gap: 18px; flex-wrap: wrap; margin-top: 28px; }
.sp-link { font-weight: 600; font-size: 14.6px; color: var(--brand); }
.sp-link:hover { text-decoration: underline; }
@media (max-width: 460px) { .sp-actions { gap: 14px; } .sp-actions .btn { width: 100%; justify-content: center; } }

/* ================= swipe CTA =================
   A reproduction of the dashboard, not a screenshot of one — the whole point is that the stop
   follows your finger, and a static frame cannot. Everything is proportioned off the 720x1565
   export in CONTAINER units, so the app holds its proportions at any width instead of needing a
   breakpoint per size.

   The app's own gesture and its own icons: pull a stop RIGHT and the location arrow appears on
   the left, pull it LEFT and the walking figure appears on the right, each on a panel a quarter
   of the row wide, icon only, exactly as the app does it. Nothing depends on reading an
   unlabelled square — the copy above and the buttons below say what each direction does. */
.swipecta .swipe-stage { max-width: 430px; margin: 0 auto; }
.swipe-phone {
  position: relative; border-radius: 34px; padding: 8px;
  background: linear-gradient(155deg, var(--frame-2), var(--frame));
  border: 1px solid var(--frame-2); box-shadow: var(--shadow);
}
.swipe-phone::before {
  content: ""; position: absolute; inset: 8px; border-radius: 27px; pointer-events: none; z-index: 5;
  background: linear-gradient(125deg, rgba(255,255,255,.09), transparent 46%);
}
/* The screen stays dark in light mode: it is a phone running a dark-UI app, not page furniture. */
.swipe-screen {
  container-type: inline-size;
  border-radius: 27px; overflow: hidden; background: #1c1c1e; text-align: left;
  color: #eee; line-height: 1.25; font-weight: 500;
}

/* ---- status bar ---- */
.ast { display: flex; justify-content: space-between; align-items: center; background: #445c7d;
       color: #fff; padding: 7.5cqw 5cqw 2.4cqw; font-size: 2.6cqw; font-weight: 700; }
.ast-r { display: flex; align-items: center; gap: 1.2cqw; }
.ast .sig { width: 3cqw; height: 2.2cqw; border-radius: .4cqw;
            background: linear-gradient(to right, #fff 0 60%, rgba(255,255,255,.45) 60%); }
.ast .bat { font-style: normal; font-size: 2cqw; background: rgba(255,255,255,.85); color: #33475c;
            border-radius: 1cqw; padding: .2cqw 1cqw; }

/* ---- blue header ---- */
.ahead { background: #445c7d; color: #fff; display: flex; align-items: flex-end; gap: 3cqw;
         padding: 1.6cqw 3.9cqw 4.8cqw; }
.ahead-t { flex: 1 1 auto; font-size: 5cqw; font-weight: 800; letter-spacing: -.035em; }
.ahead-t span { display: block; font-size: 2.15cqw; font-weight: 500; letter-spacing: 0;
                margin-top: .5cqw; opacity: .92; }
.ahead-a { display: flex; align-items: flex-end; gap: 3.4cqw; }
.aa { display: grid; justify-items: center; gap: .5cqw; }
.aa svg { width: 3.6cqw; height: 3.6cqw; }
.aa b { font-size: 2.1cqw; font-weight: 500; }

/* ---- month + week strip ---- */
.aweek { background: #2c2c2e; border-bottom: 1px solid #3a3a3c; padding: 2.6cqw 3.9cqw 4.05cqw; }
.aweek-m { display: flex; align-items: center; justify-content: space-between; font-size: 2.95cqw;
           font-weight: 600; color: #f0f0f0; padding: .6cqw 1cqw 1.6cqw; }
.aweek-m .ac { font-size: 4.2cqw; color: #e8e8e8; line-height: 1; }
.aweek-d { display: grid; grid-template-columns: repeat(7, 1fr); }
.aweek-d span { display: grid; justify-items: center; gap: .9cqw; padding: 1.6cqw 0 2cqw; border-radius: 1.3cqw; }
.aweek-d b { font-size: 2cqw; font-weight: 500; color: #cfd4d8; }
.aweek-d i { font-style: normal; font-size: 3cqw; font-weight: 700; color: #f2f2f2; }
.aweek-d .dim b, .aweek-d .dim i { color: #8a8a8a; }
.aweek-d .on { background: #3f5570; }

/* ---- the technician card ---- */
.abody { padding: 3.75cqw 3.9cqw; }
.acard { border: 1px solid #3a3a3c; border-radius: 2.2cqw; overflow: hidden; background: #2c2c2e; }
.ach { display: flex; align-items: center; gap: 3cqw; padding: 2.8cqw 3cqw; }
/* The crew ring is a water gauge, which is the right metaphor for a pool app and is what the
   real one does: it fills to the share of stops finished and the surface moves. Two oversized
   discs with a slightly-off border-radius, rotating at different speeds in opposite directions —
   their top edges undulate against each other and read as water. */
.ring { position: relative; overflow: hidden; flex: 0 0 auto; display: grid; place-items: center;
        width: 12.5cqw; height: 12.5cqw; border-radius: 50%;
        border: .35cqw solid #4f7ea6; color: #cfe4f2; font-size: 2.1cqw; font-weight: 700; }
.ring b { position: relative; z-index: 2; }
.ring i {
  position: absolute; left: 50%; margin-left: -90%; width: 180%; aspect-ratio: 1;
  top: calc(100% - var(--fill, 40%));
  border-radius: 44%; background: rgba(91,155,213,.5);
  animation: ringtide 7s linear infinite;
}
.ring i + i { border-radius: 47%; background: rgba(91,155,213,.32);
              animation-duration: 11s; animation-direction: reverse; }
@keyframes ringtide { to { transform: rotate(1turn); } }
@media (prefers-reduced-motion: reduce) { .ring i { animation: none; border-radius: 45%; } }
.ach b { flex: 1 1 auto; font-size: 3.3cqw; font-weight: 700; color: #f4f4f4; letter-spacing: -.02em; }
.ach .chev { color: #7fa9c8; font-size: 3cqw; line-height: 1; }
.mapbtn { flex: 0 0 auto; display: grid; place-items: center; width: 8.3cqw; height: 8.3cqw;
          border-radius: 1.5cqw; background: #3a3a3c; color: #9ab6c9; }
.mapbtn svg { width: 4.2cqw; height: 4.2cqw; }

/* ---- stop rows ---- */
.ar { position: relative; display: flex; align-items: center; gap: 3cqw; padding: 4.15cqw 3cqw;
      border-top: 1px solid #3a3a3c; background: #2c2c2e; }
.arn { flex: 0 0 auto; display: grid; place-items: center; width: 7.8cqw; height: 7.8cqw;
       border-radius: 50%; background: #242426; border: 1px solid #47474a; color: #7fa0b8;
       font-size: 2.7cqw; font-weight: 600; }
.arn.now { border-color: var(--brand); color: var(--brand); }
.ari { min-width: 0; flex: 1 1 auto; }
.ari b { display: block; font-size: 3.15cqw; font-weight: 700; color: #f2f2f2; letter-spacing: -.015em; }
.arm { display: flex; align-items: center; gap: 1.6cqw; margin: .9cqw 0; }
.arm i { font-style: italic; font-size: 2.6cqw; color: #b4b4b4; }
.arp { font-size: 2.05cqw; font-weight: 800; letter-spacing: .04em; border-radius: .9cqw;
       padding: .8cqw 1.5cqw; background: #b7b6b9; color: #1c1c1e; }
.arp.go { background: #ff9b05; color: #1c1c1e; }
.arp.ok { background: rgba(75,211,155,.16); color: #4bd39b; }
.arn.ok { border-color: #2f7d5e; color: #4bd39b; background: rgba(75,211,155,.08); }
.ari em { display: block; font-style: normal; font-size: 2.6cqw; color: #a9a9a9;
          white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* ---- the live row ---- */
.ar.live { display: block; padding: 0; overflow: hidden; background: #445c7d; }
.aact { position: absolute; top: 0; bottom: 0; width: 25%; display: grid; place-items: center;
        background: #445c7d; color: #fff; text-decoration: none; }
.aact svg { width: 5.6cqw; height: 5.6cqw; }
.aact.left { left: 0; }
.aact.right { right: 0; }
.aface {
  position: relative; z-index: 2; width: 100%; display: flex; align-items: center; gap: 3cqw;
  padding: 4.15cqw 3cqw; background: #2c2c2e; cursor: grab;
  /* Reads as the same row as the others, just flagged as the one you can grab. */
  box-shadow: inset 0 0 0 .38cqw color-mix(in srgb, var(--brand) 55%, transparent);
  touch-action: pan-y;                   /* vertical scrolling still belongs to the page */
  -webkit-user-select: none; user-select: none;
}
.aface:focus-visible { outline: .5cqw solid var(--brand); outline-offset: -.5cqw; }
.ar.live.dragging .aface { cursor: grabbing; }
/* Only the side being pulled toward shows. Both panels are absolutely placed and overlap, so
   lighting both would let the later one paint over the other and reveal the wrong action. */
.aact { opacity: 0; }
.ar.live.go-right .aact.left,
.ar.live.go-left  .aact.right { opacity: 1; }
.ar.live.armed .aact svg { transform: scale(1.12); transition: transform .16s var(--ease); }

/* Two nudges the first time it comes into view, so the row reads as draggable without a line of
   instructions having to do that job. Cancelled the moment it is touched. */
@keyframes swipenudge {
  0%, 100% { transform: translate3d(0,0,0); }
  38%      { transform: translate3d(13%,0,0); }
  72%      { transform: translate3d(-8%,0,0); }
}
.ar.live.hint .aface { animation: swipenudge 1.6s var(--ease) 2; }
@media (prefers-reduced-motion: reduce) { .ar.live.hint .aface { animation: none; } }

/* ---- tab bar ---- */
.atabs { display: grid; grid-template-columns: repeat(4, 1fr); background: #242426;
         border-top: 1px solid #3a3a3c; padding: 2.4cqw 0 3.6cqw; }
.atabs span { display: grid; justify-items: center; gap: .8cqw; color: #9a9a9a; }
.atabs svg { width: 3.6cqw; height: 3.6cqw; }
.atabs b { font-size: 2cqw; font-weight: 500; }
.atabs .on { color: #5b9bd5; }

.swipe-help { text-align: center; color: var(--ink-3); font-size: 12.8px; margin: 18px 0 22px; }
