/* responsive.css — Aperio responsive layer.
   Load order: styles.css, pages.css, seo.css (SEO pages only), responsive.css, consent.css.
   This file only adds/overrides; it never duplicates full component styles from the three
   files above. See docs/HANDOFF-README.md §A2 for the spec this implements. */

/* ============================================================
   1. Skip link — must be the first child of <body>
   ============================================================ */
.skip-link{position:fixed;left:12px;top:-60px;z-index:200;background:var(--navy);color:var(--cream);padding:12px 20px;border-radius:6px;font-size:14px;font-weight:600;text-decoration:none;transition:top .15s ease}
/* A-09 / SC 1.4.11: was var(--teal) #1BA4B6 = 2.82:1 on --paper. outline-offset:2px puts
   the ring outside the link's own navy chip, so BOTH its adjacent colours are paper — the
   navy never touches it. This rule's specificity (0,2,0) beats the global a:focus-visible
   rule (0,1,1), so it has to carry a compliant colour itself. --teal-deep is 5.23:1. */
.skip-link:focus{top:12px;outline:2px solid var(--teal-deep);outline-offset:2px}

/* ============================================================
   2. Focus-visible — consistent 2px outline, 2px offset.
      The colour comes from --focus (styles.css :root = --teal-deep, 4.9:1 on paper/cream)
      because one fixed teal ring measured only 2.92:1 against --navy, under the 3:1 that
      SC 1.4.11 requires for a non-text UI indicator (04 §1.1). Every navy section
      re-declares --focus to --mustard (8.78:1 on navy) and the variable inherits down to
      whatever is focused inside it. Scroll-padding-top keeps a focused element from
      landing under the 68px sticky nav (04 §1.5, SC 2.4.11).

      A-01 (BLOCKER, SC 1.4.11) — WHY THE LIST BELOW IS NOT THE WHOLE FIX.
      --focus is an INHERITED custom property, so the mustard declared here for a navy
      section also reached every LIGHT surface nested inside it. Measured before:
        .cta form.addr input / .addr-aux / submit  ring #E8C547 on #fff    = 1.68:1
        .contact .lead-form input/select/textarea  ring #E8C547 on #FBF8F1 = 1.58:1
        .keyless .screen .notif button.u / .d      ring #E8C547 on #fff    = 1.68:1
      (3:1 required; 46 + 10 + 10 + 6 + 6 + 6 failing controls across the audited pages.)
      The reset is NOT written here as a list of "light things that live inside dark
      things" — that list goes stale the first time someone drops a new light component
      into a navy section. Instead each light surface declares --focus next to its own
      `background`, on the element that owns it: .addr and .screen (styles.css),
      .lead-form, .addr-list and .est-attach (pages.css). A declaration on the element
      always beats an inherited value, whatever the file order or specificity here.
      If you add a component with its own background, give it its own --focus.
   ============================================================ */
html{scroll-padding-top:76px}
.keyless,.cta,.contact,.ph.dark,.hero-tag,.cloud{--focus:var(--mustard)}
a:focus-visible,button:focus-visible,input:focus-visible,textarea:focus-visible,
select:focus-visible,summary:focus-visible,[tabindex]:focus-visible{
  outline:2px solid var(--focus,var(--teal-deep));outline-offset:2px;border-radius:4px
}
/* pill/segmented controls need a tighter radius so the outline doesn't look detached */
.seg a:focus-visible,.seg button:focus-visible{border-radius:3px}
.addr-aux:focus-visible{border-radius:50%}

/* ============================================================
   3. Reduced motion — kill decorative animation/transition globally.
      .rv visibility is already handled in styles.css; count-ups are
      gated in site.js. This covers the hero-scene keyframes (pulse,
      notif slide-in, data streams, packet dots, cloud blip) plus any
      other transition/animation in the three base files.
   ============================================================ */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{
    animation-duration:.01ms!important;
    animation-iteration-count:1!important;
    animation-delay:0s!important;          /* .hero-tag .dot 1.6s, .notif .3s, .pk .9s/1.8s,
                                              .dots i .15s/.3s and the new .rv failsafe's
                                              2.5s all had to be reset — killing the
                                              duration alone still left the delay running */
    transition-duration:.01ms!important;
    transition-delay:0s!important;
    scroll-behavior:auto!important
  }
  /* offset-path travel is not a "duration" — stop the packet dots moving at all, rather
     than letting a .01ms run snap them to the end of the path */
  .pk{animation:none!important}
}

/* ============================================================
   4. Font fallback metrics — reduces CLS from the Google Fonts swap.
      Values below are ESTIMATES (typical Instrument Sans/Serif vs.
      Arial/Times metrics) — replace with measured output from a
      tool like Capsize or Fontaine before shipping.
   ============================================================ */
@font-face{
  font-family:'Instrument Sans Fallback';
  src:local('Arial');
  ascent-override:95%;
  descent-override:23%;
  line-gap-override:0%;
  size-adjust:104%;
}
@font-face{
  font-family:'Instrument Serif Fallback';
  src:local('Times New Roman'),local('Georgia');
  ascent-override:88%;
  descent-override:22%;
  line-gap-override:0%;
  size-adjust:114%;
}
/* --sans/--serif in styles.css :root already list these fallback names
   ahead of the generic family so they apply during the font swap. */

/* ============================================================
   5. Language toggle: <a> in .seg (was <button>) — make it identical
   ============================================================ */
.seg a{background:transparent;border-right:1px solid var(--rule);padding:7px 12px;font:inherit;font-size:12px;font-weight:600;color:var(--dim);cursor:pointer;text-decoration:none;display:inline-block;line-height:1.3}
.seg a:last-child{border-right:0}
.seg a:hover{color:var(--ink)}
.seg a.on{background:var(--navy);color:var(--cream)}
.seg a.on:hover{color:var(--cream)}
.nav-right .seg.lang a{padding:6px 9px;font-size:12px} /* 12px floor, was 11.5 (03 §M5) */
/* shell.js reuses the same .seg.lang markup for both the inline nav-bar
   toggle (.nav-right) and the one repeated inside .nav-mobile for the
   burger menu. The two rules above are scoped to .nav-right specifically
   — a blanket `.nav .seg.lang` selector would also hide/shrink the
   mobile-menu copy (it's a descendant of .nav too), making the language
   toggle unreachable on mobile and, before that, shrinking its touch
   target below the default .seg a size. */

/* ============================================================
   6. Nav: burger breakpoint is 1100px (was 900; 980 before that).
      WHY 1100 — measured, not guessed. French is the constraint: with the nowrap +
      tightened gaps now in styles.css the /fr bar's intrinsic width is
        2×--pad + logo 99 + 28 + .nav-links 549 + 28 + .nav-right 275 ≈ 979 + 2×--pad,
      and --pad is clamp(20px,4vw,56px), so it needs ≈1065px. Below that the FR labels
      ("Comment ça marche", "Pour les entreprises") wrapped to 2–3 lines and the
      "Réserver" button was pushed past the viewport edge between 901 and 925px
      (03 §B3). 1100 leaves ~33px of slack for font-swap metric differences. EN fits from
      ~950px but shares the breakpoint deliberately — one nav state per width, both langs.
      THE SAME NUMBER LIVES IN: pages.css (.nav-right .seg.lang hide; .burger/.nav-mobile
      display) and site.js's scroll-lock release (must become min-width:1101px).
      styles.css's 900px block is layout-only now and must stay at 900.
   ============================================================ */
@media (max-width:1100px){
  .nav-links,.nav-right .login{display:none}
}
/* Narrow phones: the bar is logo + "Book service" + burger and at 320px that needs 359px.
   It used to be resolved by hiding the button under 360px, which left the narrowest
   phones with no visible CTA at all if JS (and therefore the burger) failed. Shrink the
   row instead — measured 263px of content at 320px, 17px of slack. */
@media (max-width:400px){
  .nav .wrap{gap:12px}
  .nav-right{gap:8px}
  .nav .btn{padding:0 12px;font-size:13.5px}
}
/* A-03 / SC 1.4.12 — the SECOND cause of the text-spacing overflow, which the audit did
   not isolate (it attributed all of it to the <h1>). With the WCAG override applied, the
   nav row alone pushed the page past the viewport at 320px: 8px on EN ("Book service"),
   41px on ES ("Agendar servicio"). Two things combined to make it unshrinkable —
   `.nav-right{flex-shrink:0}` (styles.css, correct at normal spacing: it stops the CTA
   being squeezed) and the shared `.btn{white-space:nowrap}`. Because .nav-right refused to
   shrink, letting the button wrap had no effect on its own; both had to give.
   Scoped to <=480px so the desktop/tablet bar and the measured 1100px burger breakpoint
   are untouched.
   EN at 320px is byte-identical before and after (logo 99, button 106.8, burger right edge
   300 — the row sums to 269.7 in a 280px content box, the "17px of slack" §6 above
   records). ES at 320px is the case that slack was never measured for: "Agendar servicio"
   made the row sum to 296.3px in that same 280px box, so the burger's right edge sat at
   316.3px — 16px INTO the page's right padding, 4px off the screen edge, which
   body{overflow-x:hidden} used to hide. With the shrink allowed the ES row sums to exactly
   280 and the burger lands on 300 like English. So this is a latent-bug fix at normal
   spacing as well as the 1.4.12 fix. */
@media (max-width:480px){
  .nav-right{flex-shrink:1;min-width:0}
  .nav .btn{white-space:normal;text-wrap:balance;height:auto;min-height:44px;padding-block:6px;line-height:1.2}
  .logo{min-width:0;overflow-wrap:anywhere}
}
.nav-mobile{
  max-height:calc(100vh - 68px);
  max-height:calc(100dvh - 68px);
  overflow-y:auto;
  -webkit-overflow-scrolling:touch;
  padding-left:calc(var(--pad) + env(safe-area-inset-left));
  padding-right:calc(var(--pad) + env(safe-area-inset-right));
  padding-bottom:calc(20px + env(safe-area-inset-bottom));
  overscroll-behavior:contain;
}
/* .nav-mobile is a column flex container; when its content is a few px
   taller than the max-height above, flexbox's default shrink behaviour
   kicks in before the scrollbar does. The plain <a> rows resist that
   (their automatic min-size is content-based), but the language .seg's
   own `overflow:hidden` (styles.css, for the pill's rounded corners)
   forces ITS automatic minimum to 0 per the flexbox spec — so it alone
   absorbed all the shrink and collapsed to ~2px, clipping "EN/FR" to a
   sliver. Rows in a scrollable menu should never shrink; the container
   should scroll instead. */
.nav-mobile > *{flex-shrink:0}
/* burger + login/right-side controls also need a min 44px hit target */
.burger,.nav-right .login,.nav .btn{min-height:44px}
.burger{min-width:44px}
/* the mobile panel's language pill is the last item in a scrollable menu — it must be a
   real 44px target, not the 33px the inherited .seg sizing gave it (03 §M5/H4) */
.nav-mobile .seg.lang a{min-height:44px;display:inline-flex;align-items:center;padding:0 16px;font-size:13px}

/* ============================================================
   6b. No-JS navigation fallback (04 §1.7 BLOCKER, second half).
      Below the burger breakpoint the only route to /how, /pricing, /fleet … is the
      .nav-mobile panel, which site.js reveals with body.menu-open. If site.js never
      runs, the burger is inert and the panel stays display:none.
      A CSS-only fallback is NOT possible against the current markup without changing
      the JS-enhanced behaviour: .nav-mobile is a sibling of .nav .wrap, so the only
      :has()-free hook is `.wrap:focus-within ~ .nav-mobile`, which would also open the
      panel when the logo or the Book-service button is focused — a live regression for
      keyboard users who DO have JS. The exact markup needed (a `<a href="#nav-mobile">`
      burger + `id="nav-mobile"`) is filed for the shell owner in
      docs/stress/fix-requests.md; the rules below are already written against it, so the
      fallback starts working the moment that markup lands and are inert until then.
      Mitigation that is live today: the server-rendered footer carries every nav
      destination, so no-JS visitors are not stranded, only inconvenienced.
   ============================================================ */
@media (max-width:1100px){
  .nav-mobile:target{display:flex}
}

/* ============================================================
   7. Address widget — stack under 480px, full-width button,
      44px minimum hit targets everywhere, safe-area edge padding.
      DOM order from addr.js: svg icon, input, .addr-aux, .btn,
      .addr-list, .addr-status (list/status appended after the form
      controls; status is already flex:1 0 100% so it always drops
      to its own line).
   ============================================================ */
.addr-aux{width:44px;height:44px;flex:0 0 44px}
.addr-opt{min-height:44px}
.addr .btn{min-width:44px}
.addr input{min-height:44px}

@media (max-width:480px){
  .addr{
    padding:10px 10px 10px 14px;
    row-gap:8px;
    padding-left:calc(14px + env(safe-area-inset-left));
    padding-right:calc(10px + env(safe-area-inset-right));
  }
  /* row 1: icon + input + aux (locate/clear) — DOM order already puts
     these first, so no `order` reshuffle is needed. Keep the input's
     flex-basis at 0% (matching styles.css's own `flex:1`): giving it
     `flex-basis:auto` here made its large intrinsic/UA-default width
     count as the line's "hypothetical" size, which pushed .addr-aux
     onto its own line during wrap detection even though there was
     visibly enough room once the input actually shrank. */
  .addr input{flex:1 1 0%}
  /* row 2: button, full width, own line */
  .addr .btn{flex:1 0 100%;justify-content:center;height:48px;margin-top:2px}
  /* status / suggestions list always full-width already (addr-list is
     position:absolute; addr-status is flex:1 0 100%) — just tidy spacing */
  .addr-status{padding-top:10px}
  .addr-list{left:calc(-1px - env(safe-area-inset-left) * 0);right:-1px}
}

/* ============================================================
   8. Overflow-x safety net — real fixes for elements that can push
      past the viewport at 320–360px, found by testing the prototype
      pages with responsive.css applied.
      NOTE (A-03): there is NO last-resort net any more — body{overflow-x:hidden}
      was removed from styles.css because it made text that overflowed under the
      WCAG 1.4.12 text-spacing override unreachable rather than merely untidy.
      These rules, and the per-section clips on .hero/.cta/.keyless, are now the
      whole story. If a new horizontal overflow appears, fix the element.
   ============================================================ */
/* CTA background van art must never force width beyond the section */
.cta{max-width:100vw}
/* phone mockup in the "keyless" section is a fixed 300px box */
.phone-wrap{max-width:100%}
.phone{max-width:100%}
/* long unbroken tokens (postal codes, engine codes) must be able to wrap */
.addr-opt small,.addr-opt b,.veh-row span,.spec td{overflow-wrap:anywhere}
.hoods li,.city-cloud a{overflow-wrap:anywhere}
/* pricing.html .pledger rows: pages.css sets both the label (b) and the
   value (span) to white-space:nowrap for a clean two-column read on
   desktop. Some labels ("Tire inspection — tread, pressure, age") are
   long enough that the un-wrappable pair forces the row (and the whole
   .pcols grid track, since it collapses to a single 1fr column under
   900px) past 398px wide — a hard overflow up to ~99px at 320px,
   regardless of body{overflow-x:hidden}. Let the row stack below 560px,
   where two nowrap columns can no longer both fit comfortably anyway. */
@media (max-width:640px){
  .pledger div{flex-direction:column;align-items:flex-start;gap:3px}
  .pledger div b,.pledger div span:last-child{white-space:normal}
  .pledger div span:last-child{text-align:left}
}

/* Long FR call-to-action labels vs the shared `.btn{white-space:nowrap}`. Measured at
   320px on /fr/fleet: "Envoyer cette estimation à l'équipe flottes" is a 341px unbreakable
   box in a 280px column (41px over the viewport); /fr/dealers' "Parler à l'équipe
   concessionnaires" is 283px. nowrap is right for the nav and the address widget (short
   labels, fixed rows) and stays there — content CTAs get to wrap instead. */
@media (max-width:560px){
  .ctas .btn,.calc-out .btn,.lead-form .btn{
    white-space:normal;text-wrap:balance;text-align:center;justify-content:center;
    height:auto;min-height:48px;padding-block:12px;max-width:100%
  }
}

/* ------------------------------------------------------------
   8b. Footer grid. styles.css had `.foot{grid-template-columns:1fr 1fr}` below 900px:
   plain `1fr` (not `minmax(0,1fr)`) lets a track grow to its min-content, and the widest
   unbreakable token in column 2 is `hello@aperiomobility.com` (~166px). At 320px the two
   tracks computed to 114.3px + 165.9px + 40px gap = 320.16px inside a 280px content box,
   so the FR footer ran 20px past the viewport at every width 300–336 and EN overshot its
   own wrap by 10px (03 §B4/L4). auto-fit + minmax(min(100%,150px),1fr) picks the column
   count from the space available (4 at ≥900, 3 at ~640–900, 2 at ~440–640, 1 below) and
   the tracks can now actually shrink; overflow-wrap lets the email break.
   ------------------------------------------------------------ */
.foot{grid-template-columns:repeat(auto-fit,minmax(min(100%,150px),1fr))}
.foot a,.foot p,.foot-bottom{overflow-wrap:anywhere}
@media (max-width:400px){.foot{grid-template-columns:1fr}}

/* ------------------------------------------------------------
   8c. Touch targets below the burger breakpoint (03 §H4, SC 2.5.8/2.5.5).
   Footer links were 21px line boxes on a 30px pitch, footer-bottom links 16px, breadcrumb
   links 16px, the .seg pills 32px and the calculator's preset/unit buttons 33px/21px.
   Each gets a 44px box; .foot a's bottom margin drops from 9px to 0 so the column height
   is unchanged (21+9=30 → 44+0=44 is taller, so the list also loses its own extra gap).
   ------------------------------------------------------------ */
/* standalone .arrow CTAs measured 23px tall — 1px under the 24×24 SC 2.5.8 minimum, at
   every width, so this one is not breakpoint-scoped. 44px would need a spacing redesign
   of .svc-card / .local-side; 24px clears AA with no visible change. */
.arrow{min-height:24px}
/* A-11 / F-12 — the rest of the sub-24px targets, at ALL widths (the 44px block below only
   runs under the 1100px burger breakpoint, so desktop was still short). Measured at 1280px
   on /: .nav-dd-btn 100.6×21.8, .nav-links a ~21, .foot a 264.4×21, .foot-bottom a 154×16
   and 99.2×16, .crumbs a 16. SC 2.5.8 is a WCAG 2.2 criterion and therefore advisory here,
   but it costs nothing to clear it now.
   min-height (not padding) is used wherever the element is already a flex item or
   inline-flex, because it grows the hit box without moving anything: .foot a keeps its
   display:block and its 9px margin, so the column's pitch goes 30px → 33px and nothing
   reflows. Only the genuinely inline runs need a display change to make min-height apply. */
.nav-links a,.nav-dd-btn{min-height:24px}
.foot a{min-height:24px}
.foot-bottom a,.foot-bottom .linklike{min-height:24px;display:inline-flex;align-items:center}
.crumbs a,.crumbs span{min-height:24px;display:inline-flex;align-items:center}
@media (max-width:1100px){
  .foot a{min-height:44px;display:flex;align-items:center;margin-bottom:0}
  .foot-bottom a{min-height:44px;display:inline-flex;align-items:center}
  .crumbs a,.crumbs span{min-height:44px;display:inline-flex;align-items:center}
  .crumbs ol{padding:0}
  .seg a,.seg button{min-height:44px;display:inline-flex;align-items:center}
  .presets button{min-height:44px}
  .nav .logo{min-height:44px}                 /* was 99×30 (03 §L3) */
  .city-cloud a{min-height:44px;display:inline-flex;align-items:center} /* was 69–111×39 */
  /* the km/mi toggles carry inline styles from the page partial (font/background/padding),
     but not `display` — they are plain inline buttons, so min-width/min-height are ignored
     until they get a flex display. 21×21px measured before this. */
  .calc button.unit{display:inline-flex;align-items:center;justify-content:center;min-width:44px;min-height:44px}
}

/* ============================================================
   9. Grids — fluid patterns + a fix for the fold-unfolded (673–884px)
      squeeze on stat tiles / veh-rows previously reported.
   ============================================================ */
/* stat tiles: never let two columns get narrower than ~200px each */
.stats{grid-template-columns:repeat(auto-fit,minmax(min(100%,200px),1fr))}
.ph .stats{grid-template-columns:repeat(auto-fit,minmax(min(100%,200px),1fr))}
@media (max-width:380px){.stats,.ph .stats{grid-template-columns:1fr}}

/* svc-grid / make-grid already use auto-fit/auto-fill with a sane minmax —
   just lower the minimum slightly so 2 columns fit gracefully at ~344px */
.svc-grid{grid-template-columns:repeat(auto-fit,minmax(min(100%,220px),1fr))}
.make-grid{grid-template-columns:repeat(auto-fill,minmax(min(100%,150px),1fr))}
.near-list{grid-template-columns:repeat(auto-fill,minmax(min(100%,180px),1fr))}

/* veh-row: the fixed 5-column grid (name 2fr / years / grade / price / arrow)
   gets cramped well before the prototype's 640px cutoff — a Fold-unfolded
   width (673–884px) still gets the full 5-track grid today, and four text
   columns plus 4×16px gaps don't have room at that width. Extend the
   existing "drop the price column" behaviour up through the whole
   fold-unfolded band, then drop one more column at true phone widths. */
@media (max-width:920px){
  .veh-row{grid-template-columns:minmax(0,1.7fr) minmax(0,1fr) minmax(0,1fr) 18px;column-gap:12px}
  .veh-row span:nth-of-type(3){display:none} /* price column, same as prototype's <640 behaviour */
  .veh-row.th span:nth-of-type(3){display:none}
}
@media (max-width:400px){
  .veh-row{grid-template-columns:minmax(0,1.6fr) minmax(0,1fr) 16px;column-gap:8px}
  .veh-row span:nth-of-type(2){display:none} /* oil grade column, too tight at phone widths */
  .veh-row.th span:nth-of-type(2){display:none}
}

/* .near and .two already collapse cleanly to a single column at the 900px
   nav breakpoint (see styles.css/pages.css) and are two EXPLICIT tracks
   with different fr weights by design (near is .8fr/1.4fr) — not a
   repeated pattern, so auto-fit/minmax doesn't apply without breaking the
   intended proportions. No squeeze exists in the 673–884px band for these
   two (they're already single-column below 900px), so left untouched. */

/* ============================================================
   10. Spec tables — wrapped by the build as
       <div class="spec"><div class="spec-scroll"><table>…</table></div></div>
       Horizontal scroll lives on .spec-scroll; .spec keeps the section
       spacing/border. Sticky first column + scroll-shadow edge fade.
   ============================================================ */
.spec{overflow-x:visible}
.spec-scroll{
  position:relative;
  overflow-x:auto;
  -webkit-overflow-scrolling:touch;
  /* scroll-shadow technique: two scrolling gradients matched to the page
     background sit over two fixed gradients that only show once there is
     more content to reveal in that direction */
  background:
    linear-gradient(to right, var(--paper) 30%, rgba(251,248,241,0)) ,
    linear-gradient(to left, var(--paper) 30%, rgba(251,248,241,0)) 100% 0,
    radial-gradient(farthest-side at 0 50%, rgba(11,43,60,.16), rgba(11,43,60,0)),
    radial-gradient(farthest-side at 100% 50%, rgba(11,43,60,.16), rgba(11,43,60,0)) 100% 0;
  background-repeat:no-repeat;
  background-size:40px 100%, 40px 100%, 14px 100%, 14px 100%;
  background-attachment:local, local, scroll, scroll;
}
.sec.tint .spec-scroll{
  background-image:
    linear-gradient(to right, var(--cream) 30%, rgba(245,240,229,0)),
    linear-gradient(to left, var(--cream) 30%, rgba(245,240,229,0)) ,
    radial-gradient(farthest-side at 0 50%, rgba(11,43,60,.16), rgba(11,43,60,0)),
    radial-gradient(farthest-side at 100% 50%, rgba(11,43,60,.16), rgba(11,43,60,0));
}
.spec-scroll table td:first-child,.spec-scroll table th:first-child{
  position:sticky;left:0;background:var(--paper);z-index:2;
  box-shadow:1px 0 0 var(--rule-soft);
}
.sec.tint .spec-scroll table td:first-child,.sec.tint .spec-scroll table th:first-child{background:var(--cream)}
.spec-scroll table th{font-size:13px} /* was 12px — never below 13px */
.spec-scroll table td{font-size:13.5px}
/* A-07 / SC 2.1.1 — the CSS half. The scroller is only reachable by Tab in Chromium 127+
   ("keyboard-focusable scrollers"); Safari does not implement that, so the price columns
   would be unreachable for a keyboard or switch user there. The markup half
   (tabindex="0" + role="region" + aria-label) is filed for the template owner in
   docs/accessibility/REQUESTS-MARKUP-JS.md as REQ-01. This rule is the visible focus
   indicator for it and works either way — via Chromium's implicit focusability today, and
   via the explicit tabindex once that lands. outline-offset is NEGATIVE here on purpose:
   the scroller is flush with the page grid, so a +2px ring would be clipped by the
   section's own overflow and would also collide with the sticky first column. */
.spec-scroll:focus-visible{
  outline:2px solid var(--focus,var(--teal-deep));
  outline-offset:-2px;
  border-radius:4px
}
/* Chromium paints a focus ring on a scroller that merely received focus by click/drag;
   :focus-visible above already limits us to keyboard focus, but be explicit. */
.spec-scroll:focus:not(:focus-visible){outline:none}

/* ============================================================
   10b. Home hero live-scene: same composition at every width, just
        scaled. Previously pages.css re-cropped the scene under 900px
        (different aspect-ratio, mascot-fg width, sky inset, cloud
        width) so phones showed a different arrangement than desktop.
        Now the desktop geometry (all %-based) is kept everywhere and
        the whole art box is just capped + centred on narrow layouts,
        so mascot / van / skyline / streams / cloud stay in the same
        relative places at every size.
   ============================================================ */
.hero-art{container-type:inline-size}
@media (max-width:900px){
  /* single-column layout (see styles.css 900px breakpoint): the art
     would otherwise stretch to the full column width, which reads as
     oversized on tablets. Cap it and centre it instead of re-cropping. */
  /* 543px, not 560: 543 is the desktop reference width the .cloud scale below divides by,
     and capping the art 17px wider meant the card stopped growing at scale(1) while the
     art kept going — the card was 42.1% of the art in the 673–900 band vs 43.5%
     everywhere else (03 §L1). Cap and reference now agree, so the composition is
     identical at every width. Desktop is untouched (the art is ~538px there anyway). */
  .hero-art{width:100%;max-width:543px;margin-inline:auto} /* width:100% is required: inline-size containment + auto margins would otherwise collapse the box to 0 */
}
/* The live scene's mascot-fg is intentionally 112% wide with a -48%
   translateX so it bleeds past the (overflow:visible) art box — that's
   fine because .hero itself clips horizontally (styles.css: .hero{
   overflow-x:clip}), so the bleed never reaches the page edge, even at
   320–375px where the capped 560px art can't fit the overhang alongside
   normal page padding. */

/* .cloud card scales as ONE UNIT with the art, exactly like the mascot
   image does: it keeps its desktop box (236px, 11px type) and is scaled
   by (art width / 543px desktop reference). tan(atan2(a,b)) is the
   cross-browser way to divide two lengths into a unitless number.
   The card is decorative (.live is aria-hidden), so small type on
   phones is fine — the composition must match desktop. */
.cloud{
  width:236px;max-width:none;
  transform-origin:0 0;
  transform:scale(min(1,tan(atan2(100cqw,543px))));
}
/* Safari <17.4 (and any engine without trig functions in a calculation) drops the
   declaration above entirely and would paint the full 236px card over a 280px art box on
   an old iPhone. Step the same art-width/543 ratio with container queries instead — those
   are Safari 16+. If even container queries are missing, the flat scale(.5) below still
   keeps the card inside the narrowest art box we ship. */
@supports not (transform:scale(tan(atan2(1px,1px)))){
  .cloud{transform:scale(.5)}
  @container (min-width:300px){.cloud{transform:scale(.55)}}
  @container (min-width:340px){.cloud{transform:scale(.63)}}
  @container (min-width:380px){.cloud{transform:scale(.70)}}
  @container (min-width:420px){.cloud{transform:scale(.77)}}
  @container (min-width:460px){.cloud{transform:scale(.85)}}
  @container (min-width:500px){.cloud{transform:scale(.92)}}
  @container (min-width:543px){.cloud{transform:scale(1)}}
}

/* ============================================================
   11. Breadcrumbs wrap gracefully on small screens
   ============================================================ */
.crumbs ol{row-gap:4px}
.crumbs li{max-width:100%;overflow-wrap:anywhere}

/* ============================================================
   12. Foldables — dual-screen / hinge awareness. Kept modest: only
       nudge the hero layout so text + address form stay in one segment.
   ============================================================ */
@media (horizontal-viewport-segments:2){
  .hero .wrap{grid-template-columns:env(viewport-segment-width 0 0) 1fr;column-gap:calc(env(viewport-segment-width 1 0, 0px) - env(viewport-segment-left 1 0, 0px) + 1px)}
  .hero-copy{padding-right:24px}
  .cta .wrap{grid-template-columns:env(viewport-segment-width 0 0) 1fr}
}
@media (vertical-viewport-segments:2){
  /* Single-column so text + address form read top-to-bottom in the first
     segment; the art is kept (not hidden) and simply falls into the
     second segment below the hinge, per product's preference for the
     scene to stay visible rather than disappear on foldables. */
  .hero .wrap{grid-template-columns:1fr}
  .cta .van{display:none}
}

/* ============================================================
   13. Misc small-viewport polish
   ============================================================ */
@media (max-width:360px){
  .big{font-size:clamp(56px,16vw,96px)}
  .ph h1{font-size:clamp(38px,10vw,64px)}
}

/* ============================================================
   13. Pricing hero price card. styles.css (max-width:1180px) turns every
       .price-card into a 2-col grid — written for the home .price section,
       but it also hit the narrow .ph column on /pricing and crushed the
       list to one word per line (prototype bug). Inside .ph the card is
       always a simple stack, with the same check-list look as home.
   ============================================================ */
.ph .price-card{display:block}
.ph .price-card .big{font-size:clamp(72px,9vw,132px)}
.ph .price-card ul{list-style:none;margin:26px 0 0;padding:0;display:grid;gap:12px}
.ph .price-card li{display:flex;gap:12px;font-size:16px;align-items:flex-start}
.ph .price-card li::before{content:'';width:18px;height:18px;flex-shrink:0;margin-top:3px;border-radius:50%;background:var(--price-accent) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M7 12.5l3 3 7-7' stroke='%23fff' stroke-width='2.4' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center/contain no-repeat}
