/* The 3D "how it works" section's styles (src/partials/how-stage.html, docs/js/how.js) and the home
   page's phone call bar, which how.js drives. Moved out of main.css 2026-10-26: only / and /how-it-works/
   carry the section, and every other page was downloading ~5 KB (compressed) of these rules before it
   could show any text (writeups/SEO-AUDIT-2026-10-26.md, item 7). tools/build.js links this file on any
   page whose source includes {{partial:how-stage}}, in the body just before <section class="section
   how">, with ?v=<hash> so a change is a new URL. The call bar's rules are here too, and the call bar
   sits after the section on the home page. Rules keep main.css's order among themselves; nothing in
   main.css targets these classes. */

/* How it works. ONE markup (src/partials/how-stage.html, on / and /how-it-works/), two layouts (docs/js/how.js picks):
     default     — four cards with a still each, in the page's own light style. What reduced
                   motion, Save-Data, no WebGL, no JavaScript and the Claude pane get.
     .how--stage — the scene (.how-view) pins FULL-SCREEN and the step cards scroll over it;
                   docs/js/home3d/stage.js moves the camera with the scroll, and how.js turns the
                   site header transparent while it is over the scene (body.hdr-dark).
   Nothing in either is hidden until something fires: the cards are always visible text. */
.how { padding-bottom: var(--space-7); }
.how .section-head { margin-bottom: var(--space-6); }
.how-skip { display: none; margin: var(--space-4) 0 0; font-weight: 700; font-size: var(--fs-body-sm); }
/* Keyboard only (the site's .skip-link pattern): a way out of a pinned section for someone tabbing, but
   not a teal "skip this" as the last thing a phone reader sees before the part that teaches. */
.how--stage .how-skip { position: relative; height: 0; margin: 0; }
.how--stage .how-skip a { position: absolute; white-space: nowrap; left: 50%; top: var(--space-3); transform: translate(-50%, -200%); opacity: 0; pointer-events: none; }
.how--stage .how-skip a:focus-visible { transform: translate(-50%, 0); opacity: 1; pointer-events: auto; z-index: 5; }

.how-view { display: none; }
/* The whole loop, lit — the opening shot's own poster — above the four close-ups, so a visitor who gets
   the still layout sees the one picture that explains it all at once. The stage never shows it. */
.how-whole { max-width: var(--container-max); margin: 0 auto var(--space-6); padding: 0 var(--gutter); }
.how-whole img { display: block; width: 100%; height: auto; aspect-ratio: 16 / 9; object-fit: cover; border-radius: var(--radius-lg); background: #faf6ee; box-shadow: var(--shadow-md); }
.how--stage .how-whole { display: none; }
/* Phones: the portrait poster, cropped 4:5, so the loop is twice the size it is in a 16:9 strip. */
@media (max-width: 640px) { .how-whole img { aspect-ratio: 4 / 5; } }
.how-beats {
  list-style: none; margin: 0 auto; padding: 0 var(--gutter); max-width: var(--container-max);
  display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--space-6);
}
.how-beat {
  background: var(--surface-card); border: 1px solid var(--border-subtle); border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md); overflow: hidden; display: flex; flex-direction: column;
}
.how-still img { width: 100%; height: auto; aspect-ratio: 3 / 2; object-fit: cover; background: #faf6ee; }
/* v18: the last beat is a camera stop with no card (the sensors view). The still layout has no camera, so it
   has nothing to show there. */
.how:not(.how--stage) .how-beat--view { display: none; }

.how-card { padding: var(--space-5) var(--space-6) var(--space-6); }
/* "01 The pump" — the review page's step label, in the page's colours when it sits in a light card. */
.how-eyebrow {
  display: flex; align-items: center; gap: 0.75rem; margin: 0 0 var(--space-2);
  font-family: var(--font-mono); font-size: var(--fs-overline); font-weight: 700;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--sun-800);
}
.how-eyebrow::before { content: ""; width: 26px; height: 1px; background: currentColor; }
.how-eyebrow .num { color: var(--text-muted); }
.how-card h3 { margin: 0 0 var(--space-2); font-size: var(--fs-title); }
.how-card p:last-child { margin: 0; }
.how-more { margin: var(--space-6) 0 0; text-align: center; font-weight: 700; }

/* ── Stage layout ──────────────────────────────────────────────────────────────────────────────
   The view is the whole screen while the section is pinned: top 0, 100vh (the LARGE viewport, so a
   phone's toolbar sliding away uncovers more scene rather than a strip of page), under a header that
   has gone transparent. Keith, 2026-09-23: the white bar over the top of it and the frame around it
   were what made the scene read as muted — measured, the render itself is identical to the review
   page's, pixel for pixel; it was the presentation.
   v16 (2026-09-27): the scene is an architectural model in a dark slate studio, not a dusk photograph, so the
   section's darks went from warm (#12111b, the dusk sky) to the model's backdrop: #0a0c10 is scene.js LOOK.bg's
   zenith, drawn untone-mapped, so the canvas fading in over the stage does not shift its tint.
   v18 (2026-09-28): Keith picked v16w's look, the white model on the page's own cream, so the section is light
   again: #faf6ee (--sand-50) is LOOK.bg now, and the cards, chips, dots and the header over the scene went light
   with it (v16w's stage-light.css, carried into these rules rather than laid over them). */
.how--stage .how-skip { display: block; }
.how--stage .how-stage { position: relative; background: #faf6ee; margin-top: var(--space-5); --focus-ring: var(--ink-900, #1e1a14); }
.how--stage .how-view {
  display: block; position: sticky; top: 0; height: 100vh; overflow: hidden; background: #faf6ee;
}
.how-poster img, .how-canvas { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; }
.how-canvas { opacity: 0; transition: opacity 0.8s var(--ease-out); }
.how-view.is-live .how-canvas { opacity: 1; }
/* (v18: no film grain. It was the dark studio's; v16w dropped it on the white model.) */
/* Chips: the review page's glass pills with a glowing dot. */
.how-labels { position: absolute; inset: 0; pointer-events: none; z-index: 1; }
.how-lbl {
  position: absolute; left: 0; top: 0; opacity: 0; white-space: nowrap;
  display: inline-flex; align-items: center; gap: 10px; padding: 9px 14px 9px 11px;
  font-family: var(--font-mono); font-size: 0.72rem; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--ink-800, #2e2922); background: rgba(255, 255, 255, 0.86); border: 1px solid rgba(30, 26, 20, 0.12);
  border-radius: var(--radius-pill);
  -webkit-backdrop-filter: blur(14px) saturate(1.1); backdrop-filter: blur(14px) saturate(1.1);
  box-shadow: 0 8px 24px rgba(66, 59, 48, 0.14);
  will-change: transform, opacity;
}
/* A dot's colour is the temperature of the water it points at: blue going up, orange coming back —
   including "Back into your pool", which is warm water arriving (it was teal in v14). v16: the cool dot is the
   cool line's own blue (scene.js LOOK.cool); teal now means the pool water. */
.how-lbl i { width: 8px; height: 8px; border-radius: 50%; background: var(--ink-700, #4a4338); flex: none; box-shadow: 0 0 0 3px rgba(74, 67, 56, 0.18); }
.how-lbl--cool i { background: #1f6fe3; box-shadow: 0 0 0 3px rgba(31, 111, 227, 0.18); }
.how-lbl--warm i { background: #f05a1a; box-shadow: 0 0 0 3px rgba(240, 90, 26, 0.18); }
/* data-dot chips are placed by their dot, not their middle (stage.js): the dot sits ON the part. --flip
   mirrors the pill so the dot is on its right, for a part near the right edge of the view. */
.how-lbl--flip { flex-direction: row-reverse; padding: 9px 11px 9px 14px; }
/* The side dots, from the review page. Desktop only, as there: a hover affordance, and on a phone the
   thumb's side of the screen belongs to the scroll. */
.how-dots {
  position: absolute; right: 26px; top: 50%; transform: translateY(-50%); z-index: 2;
  display: grid; gap: 14px; opacity: 0; transition: opacity 0.35s var(--ease-out);
}
/* Only while the view is pinned (stage.js sets how--pinned): as the section let go, the rail and the
   current step's name rode up into the header beside "Get a free quote". */
.how--live.how--pinned .how-dots { opacity: 1; }
.how-dots a { position: relative; display: block; width: 10px; height: 10px; border-radius: 50%; background: rgba(30, 26, 20, 0.18); transition: background 0.3s, transform 0.3s var(--ease-out); }
.how-dots a span {
  position: absolute; right: 22px; top: 50%; transform: translateY(-50%); white-space: nowrap;
  font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--ink-600, #5e5649); opacity: 0; transition: opacity 0.25s; pointer-events: none;
}
.how-dots a:hover span, .how-dots a:focus-visible span, .how-dots a.is-active span { opacity: 1; }
.how-dots a.is-active { background: var(--sun-500); transform: scale(1.35); box-shadow: 0 0 0 4px rgba(232, 90, 42, 0.25); }
@media (max-width: 860px) { .how-dots { display: none; } }
/* The cards scroll over the pinned view: pulled up by the view's own height, with a lead-in so the
   opening shot shows before the first card arrives, and a tail so the last one lands before the view
   lets go (arithmetic in stage.js, schedule()). */
.how--stage .how-beats {
  /* Lead-in 92svh: the opening shot (the whole loop, lit) HOLDS for the first 30% of a screen of scroll
     before the camera heads for the pump (stage.js, schedule()), and the first card still has its 62svh
     to arrive. Tail 34svh: the last step's camera lands with its card centred, half a screen before the
     end of the beats, and the view has to stay pinned through that plus a moment's hold. */
  display: block; max-width: none; padding: 92svh 0 34svh; position: relative; margin-top: -100vh;
}
.how--stage .how-beat {
  min-height: 62svh; background: none; border: 0; box-shadow: none; overflow: visible; border-radius: 0;
  display: flex; flex-direction: row; align-items: center;
  max-width: calc(var(--container-max) + 120px); margin: 0 auto; padding: 0 var(--gutter);
}
.how--stage .how-still { display: none; }
/* The review page's glass cards: cream glass over the white model (v18; slate glass on v16's dark studio, warm
   glass before that), big display type, the orange step label. */
.how--stage .how-card {
  width: min(540px, 100%); padding: clamp(26px, 3.2vw, 42px); border-radius: var(--radius-xl);
  background: linear-gradient(160deg, rgba(255, 253, 249, 0.86), rgba(250, 246, 238, 0.66));
  border: 1px solid rgba(30, 26, 20, 0.08);
  -webkit-backdrop-filter: blur(18px) saturate(1.1); backdrop-filter: blur(18px) saturate(1.1);
  box-shadow: 0 24px 60px rgba(66, 59, 48, 0.14), inset 0 1px 0 rgba(255, 255, 255, 0.7);
}
.how--stage .how-eyebrow { margin-bottom: 18px; font-size: 0.72rem; color: var(--sun-600, #dc4620); }
.how--stage .how-eyebrow .num { color: var(--ink-500, #7a7163); opacity: 1; }
.how--stage .how-card h3 {
  margin: 0 0 20px; color: var(--ink-900, #1e1a14); font-family: var(--font-display); font-weight: 800;
  font-size: clamp(2rem, 4.2vw, 3.7rem); line-height: 1.04; letter-spacing: -0.025em; text-wrap: balance;
}
/* :not(.how-eyebrow): the step label is a <p> too, and this rule (0,2,1) outranked .how-eyebrow's (0,2,0),
   which turned the small orange "01 The pump" into a 17px grey line that read like a step counter. */
.how--stage .how-card p:not(.how-eyebrow) { color: var(--ink-700, #4a4338); font-size: 1.08rem; line-height: 1.6; max-width: 46ch; }
.how--stage .how-more { margin-top: var(--space-6); }
/* The review page's choreography: the step you are on is lit, the others step back. Only once the
   scene is running (.how--live), which is only after the visitor has scrolled — never a resting state
   a crawler could see. */
.how--live .how-beat .how-card { opacity: 0; transform: translateY(28px); transition: opacity 0.45s cubic-bezier(0.65, 0, 0.35, 1), transform 0.45s cubic-bezier(0.65, 0, 0.35, 1); }
.how--live .how-beat.is-active .how-card { opacity: var(--hf, 1); transform: none; transition: opacity 0.7s var(--ease-out), transform 0.9s var(--ease-out); }
/* Leaving under the header (--hf, stage.js): the fade follows the scroll, so no opacity transition behind it. */
.how--live .how-beat.is-active .how-card.is-leaving { transition: transform 0.9s var(--ease-out); }
@media (prefers-reduced-motion: reduce) { .how--live .how-beat .how-card { transform: none; } }

/* ── The header over the pinned scene (/ and /how-it-works/) ───────────────────────────────────────────────────
   body.hdr-dark is set by docs/js/how.js while the header sits over the pinned 3D scene:
   the review page's see-through top bar, so the picture runs to the top of the screen. Never while the
   phone menu is open — its panel is white and wants the ordinary header above it. */
body.hdr-dark:not(:has(#mobile-menu:not([hidden]))) .site-header {
  /* v18: over the white model the header keeps its dark text, on a cream fade instead of the dark one. */
  background: linear-gradient(180deg, rgba(250, 246, 238, 0.92) 0%, rgba(250, 246, 238, 0) 100%);
  /* border-box + no-repeat: the gradient is sized to the padding box by default and REPEATS into the
     1px transparent border, which drew its top edge as a line under the header. */
  background-origin: border-box; background-repeat: no-repeat;
  -webkit-backdrop-filter: none; backdrop-filter: none; border-bottom-color: transparent;
}

/* Phone call bar — only ≤860px, and only once no on-page button row is on screen (how.js; home only). */
.callbar { display: none; }
@media (max-width: 860px) {
  .callbar {
    display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-2);
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 40;
    padding: 10px var(--space-3) calc(10px + env(safe-area-inset-bottom, 0px));
    background: rgba(250, 246, 238, 0.97); border-top: 1px solid var(--border-default);
    box-shadow: 0 -6px 20px rgba(46, 40, 31, 0.12);
    transform: translateY(110%); visibility: hidden;
    /* visibility follows the slide out, so a parked bar is not a pair of invisible links in the tab order */
    transition: transform 280ms var(--ease-out), visibility 0s linear 280ms;
  }
  .callbar.is-up { transform: none; visibility: visible; transition: transform 280ms var(--ease-out), visibility 0s; }
  /* The open phone menu carries both of the bar's links itself. */
  body:has(#mobile-menu:not([hidden])) .callbar { transform: translateY(110%); visibility: hidden; }
  .callbar a {
    /* gap, not a space in the markup: a flex container drops whitespace at the edges of its items */
    display: flex; align-items: center; justify-content: center; gap: 0.3em; min-height: 48px; padding: 0 var(--space-3);
    border-radius: var(--radius-pill); font-weight: 700; font-size: 0.9375rem; text-decoration: none; white-space: nowrap;
  }
  .callbar-call { color: var(--text-strong); border: 2px solid var(--ink-700); }
  .callbar-quote { background: var(--brand); color: var(--brand-on); }
  /* The bar is the phone's quote button on this page, so the header's goes (the burger menu keeps
     the link) — one screen, one of each button. Room for the bar under the footer's last line. */
  .has-callbar .sps-btn-quote { display: none; }
  .has-callbar .site-footer { padding-bottom: calc(76px + env(safe-area-inset-bottom, 0px)); }
  /* ...and the step cards settle above the bar, not behind it. */
  .has-callbar .how--stage .how-beat { padding-bottom: calc(var(--space-4) + 70px + env(safe-area-inset-bottom, 0px)); }
  /* Over the pinned scene the bar goes dark glass, like the header above it (body.cb-dark, how.js): a
     cream slab across the foot of every frame was the phone's version of the white header Keith asked
     to have off the scene. Same buttons, same place; the quote pill stays orange. */
  .callbar { transition: transform 280ms var(--ease-out), visibility 0s linear 280ms, background-color 0.3s, border-color 0.3s; }
  .callbar.is-up { transition: transform 280ms var(--ease-out), visibility 0s, background-color 0.3s, border-color 0.3s; }
  body.cb-dark .callbar {
    background: rgba(250, 246, 238, 0.78); border-top-color: rgba(30, 26, 20, 0.1); box-shadow: none;   /* (v18: cream glass over the white model) */
    -webkit-backdrop-filter: blur(16px) saturate(1.2); backdrop-filter: blur(16px) saturate(1.2);
  }
}
@media (max-width: 360px) { .callbar-num { display: none; } }

@media (max-width: 640px) {
  .how-beats { grid-template-columns: 1fr; gap: var(--space-5); }
  .how-card { padding: var(--space-4) var(--space-5) var(--space-5); }
}

/* A portrait screen (phones, most tablets upright) puts the card across the foot of the view,
   so the subject has the space above it — the framing v14's KEY_PORTRAIT was tuned for. */
@media (orientation: portrait), (max-width: 640px) {
  /* 50svh a step on a portrait screen: a flick per step. The lead-in is longer than the desktop's
     because a portrait step's camera lands when its card reaches the FOOT of the screen (stage.js), so
     the first card has further to come before the opening shot hands over; +30svh of it is the opening
     shot's hold, as on the desktop. */
  .how--stage .how-beats { padding: 120svh 0 10svh; }
  .how--stage .how-beat { align-items: flex-end; min-height: 50svh; padding-bottom: var(--space-4); }
  /* The review page's phone card: full width, 22px corners, 16px body. */
  .how--stage .how-card { width: 100%; padding: 22px; border-radius: 22px; }
  .how--stage .how-eyebrow { margin-bottom: 12px; }
  .how--stage .how-card h3 { font-size: 1.75rem; margin-bottom: 10px; }
  .how--stage .how-card p:not(.how-eyebrow) { font-size: 1rem; line-height: 1.55; }
}
/* A phone on its side (390px tall): the card is centred in the room between the header and the call bar,
   and compact, as v14's were. Before this the phones' call-bar clearance (86px under each step, meant for
   a portrait card at the foot) pushed every centred card up under the header at its own keyframe. */
@media (orientation: landscape) and (max-height: 500px) {
  .how--stage .how-beat, .has-callbar .how--stage .how-beat { align-items: center; padding-top: 72px; padding-bottom: calc(70px + env(safe-area-inset-bottom, 0px)); }
  .how--stage .how-card { width: min(440px, 100%); padding: 18px 22px; border-radius: 20px; }
  .how--stage .how-eyebrow { margin-bottom: 8px; }
  .how--stage .how-card h3 { font-size: 1.5rem; margin-bottom: 8px; }
  .how--stage .how-card p:not(.how-eyebrow) { font-size: 0.95rem; line-height: 1.45; }
}
