/* ============================================================================
   Rewind Bodyshop | core.css
   Shell + design system. New rw- vocabulary, deliberately unlike the Pivtech
   stylesheet. Component-oriented split: core.css (shell) + modules.css (blocks)
   + portal.css + coming-soon.css. Theme is LOCKED DARK.
   Vocabulary: rw-frame (container) | rw-bay (section) | rw-rail/rw-overlay/
   rw-topbar (nav) | rw-act (button) | rw-tick (sparing label) |
   data-rw-reveal (wind-in motion).
   ========================================================================== */

/* ---- Tokens -------------------------------------------------------------- */
:root {
  /* Dark ground ramp (no pure #000) */
  --rw-ink: #0b0b0d;          /* page background */
  --rw-coal: #131316;         /* alt section / elevated band */
  --rw-carbon: #1c1c21;       /* cards / panels */
  --rw-carbon-2: #25252b;     /* hover surface / inputs */

  /* Crimson ramp (one accent hue, luminance-tuned for roles) */
  --rw-crimson: #ec3224;      /* accent, borders, focus, large display crimson (>=24px / 3:1) */
  --rw-crimson-text: #ee4b3e; /* SMALL crimson text on dark grounds (>=4.5:1 AA on ink/coal/carbon) */
  --rw-crimson-deep: #c92b1f; /* solid fills w/ white text (6.1:1) + crimson text on light (5.9:1) */
  --rw-crimson-glow: rgba(236, 50, 36, .32);

  /* Warm off-white text on dark (no pure #fff) */
  --rw-bone: #f4f2ed;         /* headings / strong (≈17:1 on ink) */
  --rw-text: #d8d6d1;         /* body (≈12:1 on ink) */
  --rw-ash: #9a9aa2;          /* muted / labels (≈6.3:1 on ink, AA) */
  --rw-titanium: #c9c7c2;     /* warm metal accent text */

  /* On-light (rare light chips / AA safety) */
  --rw-crimson-ink: #c92b1f;
  --rw-steel-ink: #57575f;

  /* Hairlines */
  --rw-line: rgba(255, 255, 255, .10);
  --rw-line-strong: rgba(255, 255, 255, .20);
  --rw-line-crimson: rgba(236, 50, 36, .55);

  /* Type families (loaded via Google Fonts <link> per page) */
  --ff-display: "Bricolage Grotesque", "Hanken Grotesk", system-ui, sans-serif;
  --ff-body: "Hanken Grotesk", system-ui, -apple-system, sans-serif;
  --ff-mono: "Spline Sans Mono", ui-monospace, "SFMono-Regular", monospace;

  /* Fluid type scale */
  --fs-display: clamp(2.7rem, 1.6rem + 4.6vw, 5.4rem);
  --fs-h1: clamp(2.25rem, 1.5rem + 3vw, 3.9rem);
  --fs-h2: clamp(1.7rem, 1.25rem + 1.9vw, 2.6rem);
  --fs-h3: clamp(1.24rem, 1.04rem + .85vw, 1.68rem);   /* tracks h2 so the step never jars */
  --fs-lead: clamp(1.06rem, .98rem + .5vw, 1.28rem);
  --fs-body: 1.0625rem;       /* 17px */
  --fs-sm: .9rem;
  --fs-xs: .78rem;
  --fs-mono: .8rem;

  /* Spacing + rhythm (one clamped scale; component gaps come from here) */
  --bay-pad: clamp(4.25rem, 2.5rem + 7vw, 8.5rem);
  --frame-max: 1280px;
  --gutter: clamp(1.25rem, .5rem + 3vw, 3rem);
  --sp-1: clamp(.6rem, .5rem + .5vw, .95rem);      /* tight: card grids, mosaic */
  --sp-2: clamp(.9rem, .7rem + .9vw, 1.35rem);     /* standard: review/job grids */
  --sp-3: clamp(1.3rem, 1rem + 1.5vw, 2.1rem);     /* roomy: split layouts, panel gaps */
  --top-h: 64px;              /* sticky top header; tune in one place */

  /* Shape (engineered / tight, one consistent scale) */
  --r-1: 2px;   /* chips, inputs */
  --r-2: 4px;   /* buttons */
  --r-3: 8px;   /* cards, panels */

  /* Motion - ONE language (emil: built-in easings too weak). Roles:
     --rw-ease-out    : entrances, hover, most transitions
     --rw-ease-io     : the wind-back signature + dial sweep (mechanical in-out)
     --rw-ease-drawer : overlay open/close
     Durations: micro 120-160ms (press/hover) · reveal 480-560ms · wind-back 900-1100ms · dial 1200ms */
  --rw-ease-out: cubic-bezier(.23, 1, .32, 1);
  --rw-ease-io: cubic-bezier(.77, 0, .175, 1);
  --rw-ease-drawer: cubic-bezier(.32, .72, 0, 1);

  /* Elevation (tinted, never pure-black slab) */
  --sh-1: 0 1px 0 rgba(255, 255, 255, .04) inset, 0 10px 30px rgba(0, 0, 0, .45);
  --sh-2: 0 1px 0 rgba(255, 255, 255, .05) inset, 0 24px 60px rgba(0, 0, 0, .55);
  /* Depth layer: lit-from-above highlight + crimson edge-light on key media (no new hue) */
  --sh-top-light: inset 0 1px 0 rgba(255, 255, 255, .07), inset 0 46px 60px -60px rgba(255, 255, 255, .06);
  /* standard card elevation: top-light + two soft layered drops (never a black slab) */
  --sh-card: var(--sh-top-light), 0 6px 18px rgba(0, 0, 0, .28), 0 18px 40px rgba(0, 0, 0, .22);
  --rw-edge-crimson: 0 0 0 1px rgba(236, 50, 36, .32), -16px 0 46px -26px var(--rw-crimson-glow);

  /* z-index scale (documented, no spam) */
  --z-top: 40;
  --z-overlay: 60;
  --z-skip: 100;
}

/* ---- Reset --------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }
/* [hidden] must always win: author display rules (e.g. .rw-act inline-flex)
   otherwise override the UA's [hidden]{display:none} and "hidden" controls
   keep rendering (the portal header buttons did exactly that) */
[hidden] { display: none !important; }
/* the canvas itself is ink: any strip a mobile browser exposes outside the
   layout viewport (behind its own chrome, overscroll) then reads as the site's
   ground, never a foreign colour */
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; background: var(--rw-ink); }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

/* Window scrollbar (round 13, per Craig): ONE simple red scrollbar sitewide
   that must sit BELOW the header, never over it. A native window scrollbar
   always spans the full viewport, so when JS runs (html.js) the native bar
   is hidden and shell.js renders the custom .rw-scrollbar - a real track +
   draggable thumb pinned at top: --top-h (the portal measures its own
   header). No-JS keeps this styled native bar - full height is the only
   native option. */
html { scrollbar-width: thin; scrollbar-color: var(--rw-crimson-deep) var(--rw-ink); }
html::-webkit-scrollbar { width: 10px; height: 10px; }
html::-webkit-scrollbar-thumb { background: var(--rw-crimson-deep); border-radius: 5px; }
html::-webkit-scrollbar-thumb:hover { background: var(--rw-crimson); }
html::-webkit-scrollbar-track { background: var(--rw-ink); }
html.js { scrollbar-width: none; }
html.js::-webkit-scrollbar { width: 0; height: 0; }
/* the custom bar: JS keeps the thumb sized/positioned (initScrollbar); drag
   the thumb or click the track like any standard scrollbar. Hidden while the
   page has nothing to scroll. aria-hidden - native scrolling is untouched.
   The track is TRANSPARENT (2026-07-22, per Ellis): the thumb rides OVER
   the page as a pure overlay - the old ink track read as a dark gutter
   down the right edge of full-bleed sections. The invisible track still
   takes the pointer (click-to-jump + drag unchanged). */
.rw-scrollbar {
  position: fixed; top: var(--top-h); bottom: 0; right: 0; width: 10px;
  z-index: calc(var(--z-top) - 1); background: transparent;
  touch-action: none;
}
.rw-scrollbar i {
  position: absolute; left: 1px; right: 1px; top: 0; border-radius: 5px;
  background: var(--rw-crimson-deep); min-height: 40px; cursor: default;
  will-change: transform;   /* the thumb moves by transform (GPU), never by top */
}
.rw-scrollbar i:hover, .rw-scrollbar.is-dragging i { background: var(--rw-crimson); }
/* 2026-07-22 (per Ellis): the bar auto-hides at rest SITEWIDE, matching the
   portal - it rests invisible and fades in only while the page is actually
   scrolling (.is-live from shell.js's 900ms idle timer), while the thumb is
   dragged, or when the pointer sits over the track. The invisible track
   still takes the pointer. No-JS keeps the styled native bar above. */
.rw-scrollbar { opacity: 0; transition: opacity .35s var(--rw-ease-out); }
/* asymmetric fade: answer the first scroll near-instantly (~120ms), retreat
   gently (350ms) once the page settles */
.rw-scrollbar.is-live, .rw-scrollbar.is-dragging, .rw-scrollbar:hover {
  opacity: 1; transition-duration: .12s;
}
@media (prefers-reduced-motion: reduce) { .rw-scrollbar { transition: none; } }

/* SMOOTHNESS (2026-07-27, per Ellis): where the browser has scroll-driven
   animations the thumb is placed by the COMPOSITOR, not by JS. shell.js /
   portal.js add html.rw-sb-css when the timeline is supported and publish the
   thumb's travel distance as --rw-thumb-span on the COLD path only (resize,
   content-height change), so a scroll frame runs NO script at all. Reason: a
   scroll handler can only ever place the thumb a frame behind the scroll it
   follows, and Safari/iOS coalesce scroll events through momentum - that
   residual lag is what still read as choppy after the 2026-07-23 reflow fix.
   Browsers without the timeline (Firefox: still flagged) keep the JS path.
   scroll(root block) is REQUIRED, never a bare scroll(): body is a scroll
   container of its own (overflow-x below), so scroll() would bind the
   timeline to the wrong box. */
@keyframes rw-thumb-run {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(0, var(--rw-thumb-span, 0px), 0); }
}
@supports (animation-timeline: scroll(root block)) {
  html.rw-sb-css .rw-scrollbar i {
    animation: rw-thumb-run linear both;
    animation-timeline: scroll(root block);
    animation-duration: auto;      /* progress-driven, not time-driven */
  }
  /* while the thumb is dragged, JS owns the transform 1:1 with the pointer */
  html.rw-sb-css .rw-scrollbar.is-dragging i { animation: none; }
  /* the reduced-motion blanket further down sets animation-duration:.001ms on
     everything, which would park a progress-timeline thumb at the track bottom
     forever. A scroll-progress animation is not unrequested motion - it IS the
     scroll - so restore auto inside the gate. */
  @media (prefers-reduced-motion: reduce) {
    html.rw-sb-css .rw-scrollbar i { animation-duration: auto !important; }
  }

  /* INK CAP (2026-08-04, per Ellis - the FOURTH pass at the strip above the
     header, see the .rw-top::before slab note below for the history). While
     Safari's collapsed URL pill is up, iOS paints the band above the layout
     viewport from the SCROLLING CONTENT LAYER ONLY - chrome-pinned layers
     (the sticky bar and anything drawn from it, slab included) are clipped at
     the layout viewport's top edge, which is why the pure-CSS slab never
     showed on device. So the cover has to live IN the scrolling layer: this
     pseudo sits 100lvh tall at the document top and a scroll-driven animation
     (the same scroll(root block) machinery as the thumb above, same
     html.rw-sb-css gate, same cold-path publishing) translates it down by
     exactly the scroll offset, keeping its bottom edge welded to the layout
     viewport's top - i.e. the header's top - at every scroll position. The
     compositor owns it, so it tracks momentum with zero script and zero lag
     (the round-12 JS veil's failure). The travel is calc'd from --rw-doc-h
     (document scrollHeight, published by shell.js/portal.js measure() on the
     cold path) minus 100svh - SVH, NOT LVH (2026-08-06, per Ellis,
     UPDATEDNAV): the timeline's progress is scroll / (scrollHeight -
     viewport), and mobile engines resolve that viewport as the SMALL,
     chrome-expanded layout viewport (the ICB, which iOS and Android hold at
     the small size while the chrome collapses). The first cut subtracted
     100lvh, so the travel ran (lvh - svh) short of the timeline's range and
     the cap's bottom edge undershot the scroll by progress x (lvh - svh) - a
     thin strip of page content above the header that GREW with scroll depth.
     With svh the offset equals the scroll position exactly, welding the cap's
     bottom edge to the header top at every depth (where svh = lvh - desktop -
     nothing changes). z-index below the header (the glass keeps sampling real
     content), above everything the strip could expose. Browsers without the
     timeline, no-JS, or a failed thumb probe: no cap, no harm - the artifact
     simply remains the pre-existing one. */
  html.rw-sb-css body::before {
    content: ""; position: absolute; top: -100lvh; left: 0; width: 100%;
    height: 100lvh; background: var(--rw-ink);
    z-index: calc(var(--z-top) - 1); pointer-events: none;
    animation: rw-inkcap-run linear both;
    animation-timeline: scroll(root block);
    animation-duration: auto;
  }
  /* same reduced-motion carve-out as the thumb: parking this at the .001ms
     end-fill would drop an ink plate over content near the page foot */
  @media (prefers-reduced-motion: reduce) {
    html.rw-sb-css body::before { animation-duration: auto !important; }
  }
}
@keyframes rw-inkcap-run {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(0, calc(var(--rw-doc-h, 0px) - 100svh), 0); }
}

body {
  background: var(--rw-ink);
  color: var(--rw-text);
  font-family: var(--ff-body);
  font-size: var(--fs-body);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  font-synthesis: none;
  overflow-x: hidden;            /* belt-and-braces against overhang */
  /* no padding-top: the sticky header occupies flow (round 11) */
}

/* Film grain: fixed, pointer-events-none overlay (design-taste 6.E). Static,
   ultra-low opacity - adds material tooth to the flat dark grounds without
   touching text contrast. One composited layer; never on a scrolling box. */
body::after {
  content: ""; position: fixed; inset: 0; z-index: 90; pointer-events: none; opacity: .04;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: 160px 160px;
}

img, picture, svg, video { display: block; max-width: 100%; }
img { height: auto; }
button, input, select, textarea { font: inherit; color: inherit; }
button { background: none; border: 0; cursor: pointer; }
a { color: inherit; text-decoration: none; }
ul[role="list"], ol[role="list"] { list-style: none; padding: 0; }
:where(h1, h2, h3, h4) { line-height: 1.04; font-weight: 600; }

/* Focus: visible crimson ring on both grounds */
:focus-visible {
  outline: 2px solid var(--rw-crimson);
  outline-offset: 2px;
  border-radius: var(--r-1);
}
:focus:not(:focus-visible) { outline: none; }

::selection { background: var(--rw-crimson-deep); color: #fff; }

/* ---- Base type ----------------------------------------------------------- */
h1, .rw-display {
  font-family: var(--ff-display);
  font-optical-sizing: auto;
  letter-spacing: -.02em;
}
h2, h3 { font-family: var(--ff-display); font-optical-sizing: auto; letter-spacing: -.015em; color: var(--rw-bone); }
strong, b { color: var(--rw-bone); font-weight: 600; }
p { max-width: 68ch; }
.rw-mono { font-family: var(--ff-mono); font-variant-numeric: tabular-nums; }
.tnum { font-variant-numeric: tabular-nums; }

/* Accessibility helpers */
.rw-vh {
  position: absolute !important; width: 1px; height: 1px; padding: 0;
  margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.rw-skip {
  position: fixed; top: .5rem; left: .5rem; z-index: var(--z-skip);
  background: var(--rw-crimson-deep); color: #fff; padding: .7rem 1.1rem;
  border-radius: var(--r-2); font-weight: 600; transform: translateY(-160%);
  white-space: nowrap;
  /* Invisible as well as off-viewport: the translate alone can still show
     while mobile chrome shifts fixed layers during rubber-band overscroll. */
  opacity: 0; pointer-events: none;
  transition: transform .18s var(--rw-ease-out), opacity .18s var(--rw-ease-out);
}
.rw-skip:focus { transform: translateY(0); opacity: 1; pointer-events: auto; }

/* ---- Layout primitives --------------------------------------------------- */
.rw-frame { width: 100%; max-width: var(--frame-max); margin-inline: auto; padding-inline: var(--gutter); }
.rw-frame--wide { max-width: 1480px; }
.rw-frame--narrow { max-width: 760px; }

.rw-bay { padding-block: var(--bay-pad); position: relative; }
.rw-bay--coal { background: var(--rw-coal); }
.rw-bay--tight { padding-block: clamp(2.5rem, 1.5rem + 4vw, 4.5rem); }

.rw-rule { height: 1px; border: 0; background: var(--rw-line); }

/* Sparing section label: shift-light blades + plain-language label.
   NOT an eyebrow micro-label, NOT numbered, NOT uppercase-tracked-mono.
   Use on at most 1 of every 3 sections. */
.rw-tick {
  display: inline-flex; align-items: center; gap: .65rem;
  font-family: var(--ff-mono); font-size: var(--fs-mono);
  color: var(--rw-ash); letter-spacing: .02em; margin-bottom: 1.1rem;
}
/* Shift-light blades: slanted crimson marks that MARCH leftward forever (the
   rewind direction). A light sweep rides underneath for shimmer. Shared by
   every tick (.rw-tick / .rw-phead__tick / .rw-hero__motto). CSS-only so it
   also runs no-JS; reduced-motion freezes to solid blades. */
.rw-tick::before, .rw-phead__tick::before, .rw-hero__motto::before {
  content: ""; width: 34px; height: 12px; flex: none;
  background: linear-gradient(90deg, var(--rw-crimson-deep), var(--rw-crimson-text) 55%, var(--rw-crimson-deep)) 0 0 / 200% 100%;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='12'%3E%3Cpath d='M0 12 L4 12 L9 0 L5 0 Z'/%3E%3C/svg%3E") 0 0 / 10px 12px repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='12'%3E%3Cpath d='M0 12 L4 12 L9 0 L5 0 Z'/%3E%3C/svg%3E") 0 0 / 10px 12px repeat;
  animation: rw-blades .9s linear infinite;
}
/* INTEGER period (round 11): the mask is a 10px-wide SVG blade tile, and the
   loop slides exactly one tile (-10px), so frame 0 and the loop restart are
   pixel-identical. The old repeating-linear-gradient marched a fractional
   9.9304px horizontal period, which WebKit quantised - every 0.9s replay
   jumped by up to a device pixel. Do not revert to gradient maths. */
@keyframes rw-blades { to { -webkit-mask-position: -10px 0; mask-position: -10px 0; } }
@media (prefers-reduced-motion: reduce) {
  .rw-tick::before, .rw-phead__tick::before, .rw-hero__motto::before {
    animation: none; background: var(--rw-crimson);
  }
}

.rw-lead { font-size: var(--fs-lead); color: var(--rw-text); max-width: 60ch; }
.rw-eq { color: var(--rw-bone); }

/* ---- Buttons (rw-act) ---------------------------------------------------- */
.rw-act {
  display: inline-flex; align-items: center; justify-content: center; gap: .55em;
  font-family: var(--ff-body); font-weight: 600; font-size: .95rem; line-height: 1;
  letter-spacing: .005em; padding: .95em 1.45em; border-radius: var(--r-2);
  background: var(--rw-crimson-deep); color: #fff; border: 1px solid transparent;
  min-height: 44px; cursor: pointer; white-space: nowrap;
  transition: transform .16s var(--rw-ease-out), box-shadow .16s var(--rw-ease-out),
              background-color .16s var(--rw-ease-out);
}
.rw-act__arrow { display: inline-block; transition: transform .16s var(--rw-ease-out); }
.rw-act:active { transform: scale(.97); }
.rw-act--line {
  background: transparent; color: var(--rw-bone); border-color: var(--rw-line-strong);
}
.rw-act--lg { padding: 1.1em 1.7em; font-size: 1rem; }
.rw-act[disabled], .rw-act:disabled { opacity: .5; cursor: not-allowed; }

@media (hover: hover) and (pointer: fine) {
  .rw-act:hover { background: var(--rw-crimson); box-shadow: 0 0 0 4px var(--rw-crimson-glow); }
  .rw-act:hover .rw-act__arrow { transform: translateX(3px); }
  .rw-act--line:hover {
    background: rgba(236, 50, 36, .10); border-color: var(--rw-crimson);
    box-shadow: none; color: var(--rw-bone);
  }
}

/* ---- Forms base ---------------------------------------------------------- */
.rw-field { display: grid; gap: .4rem; margin-bottom: 1.15rem; }
.rw-field label { font-size: var(--fs-sm); font-weight: 600; color: var(--rw-bone); }
.rw-field .rw-hint { font-size: var(--fs-xs); color: var(--rw-ash); }
.rw-field > .rw-hint { max-width: 60ch; }   /* keep standalone helper lines readable */
.rw-input, .rw-select, .rw-textarea {
  width: 100%; background: var(--rw-carbon); color: var(--rw-bone);
  border: 1px solid var(--rw-line-strong); border-radius: var(--r-1);
  padding: .8rem .9rem; font-size: 1rem; min-height: 44px;
  transition: border-color .16s var(--rw-ease-out), box-shadow .16s var(--rw-ease-out);
}
.rw-textarea { min-height: 130px; resize: vertical; line-height: 1.5; }
.rw-input::placeholder, .rw-textarea::placeholder { color: #8a8a92; }
.rw-input:focus, .rw-select:focus, .rw-textarea:focus {
  outline: none; border-color: var(--rw-crimson); box-shadow: 0 0 0 3px var(--rw-crimson-glow);
}
/* autofill keeps the brand surface (never the UA yellow/blue tint), so
   anything layered over a field - like the password toggle - stays visible */
input:-webkit-autofill, input:-webkit-autofill:hover,
textarea:-webkit-autofill, select:-webkit-autofill {
  -webkit-box-shadow: inset 0 0 0 1000px var(--rw-carbon);
  -webkit-text-fill-color: var(--rw-bone);
  caret-color: var(--rw-bone);
}
/* focused / flagged-invalid autofill: restack the crimson ring in front of
   the carbon fill - both write box-shadow, so the plain fill erased it */
input:-webkit-autofill:focus, textarea:-webkit-autofill:focus,
select:-webkit-autofill:focus,
input[aria-invalid="true"]:-webkit-autofill,
textarea[aria-invalid="true"]:-webkit-autofill {
  -webkit-box-shadow: 0 0 0 3px var(--rw-crimson-glow), inset 0 0 0 1000px var(--rw-carbon);
  -webkit-text-fill-color: var(--rw-bone);
  caret-color: var(--rw-bone);
}
/* engines that only honour the unprefixed pseudo-class (kept separate: an
   unrecognised selector would drop the whole rule above) */
input:autofill {
  -webkit-text-fill-color: var(--rw-bone);
  caret-color: var(--rw-bone);
}
/* invalid highlight (forms.js sets aria-invalid on the first failing field) */
.rw-input[aria-invalid="true"], .rw-select[aria-invalid="true"], .rw-textarea[aria-invalid="true"] {
  border-color: var(--rw-crimson); box-shadow: 0 0 0 3px var(--rw-crimson-glow);
}
.rw-input[disabled] { opacity: .55; cursor: not-allowed; }
/* File inputs read as a machined drop bay: dashed surround + crimson Choose
   button. Styling the NATIVE input (::file-selector-button) keeps it fully
   functional and accessible with zero JS; used on estimate + the portal. */
.rw-input[type="file"] {
  border: 1px dashed var(--rw-line-strong); background: rgba(255, 255, 255, .02);
  border-radius: var(--r-2); padding: .85rem; min-height: 0;
  font-size: var(--fs-sm); color: var(--rw-ash); cursor: pointer;
  transition: border-color .16s var(--rw-ease-out), background-color .16s var(--rw-ease-out);
}
/* the button->filename gap lives on the button. The legacy alias
   (::-webkit-file-upload-button) is kept as its OWN duplicate rules: one
   unknown pseudo in a selector list drops the whole rule, so they can't share */
.rw-input[type="file"]::file-selector-button {
  font: inherit; font-size: var(--fs-sm); font-weight: 600; color: #fff;
  background: var(--rw-crimson-deep); border: 0; border-radius: var(--r-2);
  padding: .55rem 1rem; margin-right: 1.6rem; cursor: pointer;
  transition: background-color .16s var(--rw-ease-out), box-shadow .16s var(--rw-ease-out);
}
.rw-input[type="file"]::-webkit-file-upload-button {
  font: inherit; font-size: var(--fs-sm); font-weight: 600; color: #fff;
  background: var(--rw-crimson-deep); border: 0; border-radius: var(--r-2);
  padding: .55rem 1rem; margin-right: 1.6rem; cursor: pointer;
  transition: background-color .16s var(--rw-ease-out), box-shadow .16s var(--rw-ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .rw-input[type="file"]:not([disabled]):hover { border-color: var(--rw-crimson); background: rgba(236, 50, 36, .06); }
  .rw-input[type="file"]:not([disabled]):hover::file-selector-button { background: var(--rw-crimson); box-shadow: 0 0 0 4px var(--rw-crimson-glow); }
  .rw-input[type="file"]:not([disabled]):hover::-webkit-file-upload-button { background: var(--rw-crimson); box-shadow: 0 0 0 4px var(--rw-crimson-glow); }
}
.rw-input[type="file"][disabled] { cursor: not-allowed; }
.rw-input[type="file"][disabled]::file-selector-button { opacity: .6; cursor: not-allowed; box-shadow: none; }
.rw-input[type="file"][disabled]::-webkit-file-upload-button { opacity: .6; cursor: not-allowed; box-shadow: none; }

/* Machined drop bay v2 (round 5). The native input's UA strings ("No file
   chosen") cannot be styled, so the bay renders its OWN face and file list:
   with JS the native input stretches invisibly over the face (click AND OS
   drag-drop keep working with zero wiring; change events feed the list, which
   forms.js / portal.js render). No-JS hides the face and shows the styled
   native input above. Focus ring rides :focus-within. */
.rw-file { position: relative; }
.js .rw-file .rw-file__input {
  position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0;
  cursor: pointer; z-index: 1; padding: 0; border: 0; min-height: 0;
}
html:not(.js) .rw-file__face { display: none; }
.rw-file__face {
  display: grid; justify-items: center; gap: .3rem; text-align: center;
  border: 1px dashed var(--rw-line-strong); border-radius: var(--r-2);
  background: rgba(255, 255, 255, .02); padding: 1.6rem 1.2rem 1.45rem;
  transition: border-color .16s var(--rw-ease-out), background-color .16s var(--rw-ease-out),
              box-shadow .16s var(--rw-ease-out);
}
.rw-file__face svg { width: 26px; height: 26px; color: var(--rw-crimson-text); margin-bottom: .25rem; }
.rw-file__face b { color: var(--rw-bone); font-size: var(--fs-sm); font-weight: 600; }
.rw-file__face b span { color: var(--rw-ash); font-weight: 400; }
.rw-file__meta {
  font-family: var(--ff-mono); font-size: var(--fs-xs); color: var(--rw-ash);
  letter-spacing: .02em; margin-top: .45rem;
}
@media (hover: hover) and (pointer: fine) {
  .js .rw-file:hover .rw-file__face { border-color: var(--rw-crimson); background: rgba(236, 50, 36, .06); }
}
.rw-file:focus-within .rw-file__face { border-color: var(--rw-crimson); box-shadow: 0 0 0 3px var(--rw-crimson-glow); }
.rw-file__list { list-style: none; margin: .6rem 0 0; padding: 0; display: grid; gap: .35rem; }
.rw-file__list[hidden] { display: none; }
.rw-file__list li {
  display: flex; align-items: baseline; gap: .7rem; min-width: 0;
  font-size: var(--fs-sm); color: var(--rw-text);
  border: 1px solid var(--rw-line); border-left: 2px solid var(--rw-crimson);
  border-radius: var(--r-1); background: var(--rw-carbon); padding: .45rem .7rem;
}
.rw-file__list li b {
  color: var(--rw-bone); font-weight: 500; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.rw-file__list li .rw-mono { margin-left: auto; flex: none; font-size: var(--fs-xs); color: var(--rw-ash); }
.rw-file__bar { display: flex; align-items: center; gap: .9rem; margin-top: .6rem; }
.rw-file__bar[hidden] { display: none; }
.rw-file__count { font-family: var(--ff-mono); font-size: var(--fs-xs); color: var(--rw-ash); }
.rw-file__clear {
  color: var(--rw-crimson-text); font-weight: 600; font-size: var(--fs-xs);
  padding: .35rem 0; position: relative; z-index: 2;
}
.rw-file__clear:active { transform: scale(.97); }
@media (hover: hover) and (pointer: fine) { .rw-file__clear:hover { color: var(--rw-crimson); } }
.rw-honeypot { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* ---- Custom listbox select (ui.js enhancement of select.rw-select) --------
   The native select stays in the DOM (hidden) for form submission + no-JS. */
.rw-lb { position: relative; }
.rw-select--native {
  position: absolute !important; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.rw-lb__btn {
  display: flex; align-items: center; justify-content: space-between; gap: .8rem;
  width: 100%; text-align: left; background: var(--rw-carbon); color: var(--rw-bone);
  border: 1px solid var(--rw-line-strong); border-radius: var(--r-1);
  padding: .8rem .9rem; font-size: 1rem; min-height: 44px; cursor: pointer;
  transition: border-color .16s var(--rw-ease-out), box-shadow .16s var(--rw-ease-out);
}
.rw-lb__btn[aria-expanded="true"], .rw-lb__btn:focus-visible {
  outline: none; border-color: var(--rw-crimson); box-shadow: 0 0 0 3px var(--rw-crimson-glow);
}
.rw-lb__value { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rw-lb__caret {
  flex: none; width: 8px; height: 8px; margin-right: .1rem;
  border-right: 2px solid var(--rw-ash); border-bottom: 2px solid var(--rw-ash);
  transform: rotate(45deg) translateY(-2px);
  transition: transform .16s var(--rw-ease-out);
}
.rw-lb__btn[aria-expanded="true"] .rw-lb__caret { transform: rotate(-135deg) translateY(-2px); }
.rw-lb__list {
  position: absolute; z-index: 30; top: calc(100% + 4px); left: 0; right: 0;
  max-height: 280px; overflow-y: auto; padding: .35rem;
  background: var(--rw-carbon); border: 1px solid var(--rw-line-strong);
  border-radius: var(--r-2); box-shadow: var(--sh-card);
  transform-origin: top; transform: scale(1); opacity: 1;
  transition: transform .16s var(--rw-ease-out), opacity .16s var(--rw-ease-out);
}
.rw-lb__list.is-entering { transform: scale(.97); opacity: 0; transition: none; }
/* opened UPWARD by ui.js placePop() when the list will not fit below the
   trigger and there is more room above (2026-09-06); the inline max-height
   it sets caps the list to the room it actually has */
.rw-lb__list.is-up { top: auto; bottom: calc(100% + 4px); transform-origin: bottom; }
.rw-lb__list[hidden] { display: none; }
.rw-lb__group {
  font-family: var(--ff-mono); font-size: var(--fs-xs); color: var(--rw-ash);
  padding: .55rem .6rem .25rem; letter-spacing: .03em;
}
.rw-lb__opt {
  padding: .55rem .6rem; border-radius: var(--r-1); color: var(--rw-text);
  font-size: var(--fs-sm); cursor: pointer;
}
.rw-lb__opt.is-active { background: rgba(236, 50, 36, .12); color: var(--rw-bone); }
.rw-lb__opt[aria-selected="true"] {
  color: var(--rw-bone); font-weight: 600;
  background: linear-gradient(90deg, rgba(236, 50, 36, .16), rgba(236, 50, 36, .06));
}
@media (prefers-reduced-motion: reduce) { .rw-lb__list { transition: opacity .12s ease; } }
/* two-up field row (was only in portal.css; contact/estimate use it too) */
.rw-grid2 { display: grid; gap: 0 1rem; grid-template-columns: 1fr; align-items: start; }
@media (min-width: 620px) {
  .rw-grid2 { grid-template-columns: 1fr 1fr; }
  /* columns sized to their content so the pairing reads deliberate:
     email runs longer than a name; a year is four digits beside a reg plate */
  .rw-grid2--id { grid-template-columns: .85fr 1.15fr; }
  .rw-grid2--reg { grid-template-columns: 1.4fr .6fr; }
}
.rw-formnote[role="status"], .rw-formnote[role="alert"] {
  border-radius: var(--r-2); padding: .85rem 1rem; font-size: var(--fs-sm);
  margin: .5rem 0 1.15rem;   /* bottom matches the .rw-field rhythm - never touches the submit */
}
.rw-formnote--ok { background: rgba(46, 160, 87, .14); border: 1px solid rgba(46,160,87,.4); color: #cdeccd; }
.rw-formnote--err { background: rgba(236, 50, 36, .14); border: 1px solid var(--rw-line-crimson); color: #f6c9d3; }

/* Payment note (2026-09-08, per Ellis): the "We accept Klarna" line under the
   submit button of every customer form (contact + estimate). Mono readout in
   bone with a short crimson dash, the same instrument language as the ticks,
   so it reads as a fact about the workshop rather than an advert.
   2026-09-09, per Ellis: the word Klarna is now Klarna's OWN badge
   (assets/logo/klarnaBadge.svg, their official black variant, shipped
   unaltered). Its container is #0A0B09 against coal/carbon grounds, so the
   mark reads as a dark plate with the white wordmark on it - an instrument
   key, not a pink advert.
   The line is a flex row, so "We accept" and the badge MUST share one
   wrapper (.rw-paynote__take): bare text nodes become gap-separated flex
   items and the badge would drift away from the words it belongs to. Same
   trap the hero motto's <span> exists to avoid. */
.rw-paynote {
  display: flex; align-items: center; flex-wrap: wrap; gap: .35rem .65rem; margin-top: 1rem;
  font-family: var(--ff-mono); font-size: var(--fs-mono); color: var(--rw-bone);
  letter-spacing: .02em;
}
.rw-paynote::before {
  content: ""; flex: none; width: .9rem; height: 2px; background: var(--rw-crimson);
  box-shadow: 0 0 8px var(--rw-crimson-glow);
}
.rw-paynote__take { display: inline-flex; align-items: center; gap: .5rem; white-space: nowrap; }
/* height drives it; width auto keeps Klarna's 45x25 ratio whatever the mono
   size resolves to. The intrinsic width/height attributes stay on the image
   element so the line never reflows once the SVG lands.
   1.95em is measured, not guessed: Klarna's wordmark fills only ~a third of
   its badge height, so at the badge heights that look "right" numerically the
   word itself reads smaller than the type beside it. 1.95em is where the
   wordmark's cap height matches the mono cap height. */
.rw-paynote__take img { display: block; width: auto; height: 1.95em; }
.rw-paynote__note { color: var(--rw-ash); }

/* Send progress (2026-07-22): determinate bar shown by forms.js while an
   estimate's photos convert + upload. Machined panel language: coal ground,
   hairline border, crimson fill, mono readout. Injected by JS only - the
   no-JS path never renders it. */
.rw-sendbar {
  background: var(--rw-coal); border: 1px solid var(--rw-line);
  border-radius: var(--r-2); padding: .8rem 1rem; margin: .5rem 0 1.15rem;
  opacity: 0; transform: translateY(4px);
  transition: opacity .2s var(--rw-ease-out), transform .2s var(--rw-ease-out);
}
.rw-sendbar.is-in { opacity: 1; transform: translateY(0); }
@media (prefers-reduced-motion: reduce) { .rw-sendbar { transform: none !important; opacity: 1; transition: none; } }
.rw-sendbar__head {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: 1rem; margin-bottom: .55rem; font-size: var(--fs-sm); color: var(--rw-text);
}
.rw-sendbar__head .rw-mono {
  font-family: var(--ff-mono); font-size: var(--fs-xs); color: var(--rw-ash);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.rw-sendbar__track {
  height: 6px; border-radius: 3px; overflow: hidden;
  background: rgba(255, 255, 255, .1);
}
.rw-sendbar__track i {
  display: block; height: 100%; width: 0;
  background: var(--rw-crimson-deep);
  box-shadow: 0 0 10px var(--rw-crimson-glow);
  transition: width .25s var(--rw-ease-out);
}
@media (prefers-reduced-motion: reduce) { .rw-sendbar__track i { transition: none; } }

/* ============================================================================
   NAVIGATION  (sticky top cockpit bar | overlay menu <880)
   Instrument-cluster header: brand left, binnacle nav right (mono labels, a
   crimson "you are here" lamp over the current link) and the Estimate CTA.
   Deliberately not Pivtech's centred row. No-JS: translucent bar, lamp intact.
   ========================================================================== */
.rw-top {
  position: sticky; top: 0; z-index: var(--z-top);
  background: rgba(11, 11, 13, .74);
  -webkit-backdrop-filter: blur(14px) saturate(1.35);
  backdrop-filter: blur(14px) saturate(1.35);
  border-bottom: 1px solid var(--rw-line);
}
/* Ink slab above the bar (2026-07-22, per Ellis): while scrolling, iOS
   Safari exposes a strip of the RAW DOCUMENT above the layout viewport
   behind its collapsed chrome - page content was visibly scrolling above
   the header. The html ink canvas only covers true overscroll, not this.
   This opaque slab rides the sticky bar one whole viewport upward so that
   strip always reads as the site ground (Pivtech's white block, in ink).
   2026-08-04: on device this slab turned out NEVER to paint in that strip -
   iOS clips chrome-pinned layers (sticky/fixed and their pseudos) at the
   layout viewport's top edge and fills the band from the scrolling content
   layer alone. The working cover is the scroll-driven INK CAP on
   body::before (see the scrollbar timeline block above), which lives in the
   scrolling layer where iOS actually paints. The slab stays as a free
   belt-and-braces for engines that DO draw sticky overflow up there. */
.rw-top::before {
  content: "";
  position: absolute; left: 0; right: 0; bottom: 100%;
  height: 100vh; height: 100lvh;
  background: var(--rw-ink);
}
.rw-top__inner {
  max-width: var(--frame-max); margin-inline: auto; padding-inline: var(--gutter);
  min-height: var(--top-h); display: flex; align-items: center;
  gap: clamp(1rem, 2.5vw, 2.2rem);
}
.rw-top__brand { display: block; flex: none; width: 112px; }
.rw-top__brand img { width: 100%; height: auto; }

/* binnacle nav + header CTA: hidden on small screens, shown from 880px */
.rw-top__nav { display: none; }
.rw-top__cta {
  display: none; align-items: center; justify-content: center;
  margin-left: auto; padding: .62em 1.1em; min-height: 40px;
  border-radius: var(--r-2); white-space: nowrap;
  background: var(--rw-crimson-deep); color: #fff; font-weight: 600; font-size: .88rem;
  transition: background-color .16s var(--rw-ease-out), box-shadow .16s var(--rw-ease-out);
}
@media (min-width: 640px) { .rw-top__cta { display: inline-flex; } }
@media (hover: hover) and (pointer: fine) {
  .rw-top__cta:hover { background: var(--rw-crimson); box-shadow: 0 0 0 4px var(--rw-crimson-glow); }
}

.rw-menu-btn {
  display: inline-flex; align-items: center; gap: .5rem; min-height: 44px; padding: .4rem .6rem;
  margin-left: auto;
  font-weight: 600; font-size: .95rem; color: var(--rw-bone); border-radius: var(--r-2);
}
@media (min-width: 640px) { .rw-menu-btn { margin-left: 0; } }  /* CTA owns the push */
.rw-menu-btn__bars { display: inline-flex; flex-direction: column; gap: 4px; width: 20px; }
.rw-menu-btn__bars span { height: 2px; background: var(--rw-crimson); border-radius: 2px; }
/* close variant: the SAME component in the overlay's top row, so the tap
   target sits exactly where Menu was; the bars become an X of equal size */
.rw-menu-btn.rw-menu-btn--close { display: inline-flex; margin-left: auto; }
.rw-menu-btn__bars--x { position: relative; height: 14px; justify-content: center; }
.rw-menu-btn__bars--x span { position: absolute; left: 0; right: 0; top: 50%; }
.rw-menu-btn__bars--x span:first-child { transform: translateY(-50%) rotate(45deg); }
.rw-menu-btn__bars--x span:last-child { transform: translateY(-50%) rotate(-45deg); }

@media (min-width: 880px) {
  .rw-menu-btn { display: none; }
  .rw-top__nav {
    display: flex; align-items: center; margin-left: auto;
    gap: clamp(1.05rem, .4rem + 1.6vw, 2rem);
  }
  .rw-top__nav a {
    position: relative; font-family: var(--ff-mono); font-size: .75rem; font-weight: 500;
    letter-spacing: .05em; color: var(--rw-ash); padding: 1.35rem 0;   /* full-height target */
    transition: color .16s var(--rw-ease-out);
  }
  /* hover underline blooms from the centre outward (unhurried, per Craig) */
  .rw-top__nav a::after {
    content: ""; position: absolute; left: 0; right: 0; bottom: 1rem; height: 2px;
    background: var(--rw-crimson); transform: scaleX(0); transform-origin: 50%;
    transition: transform .3s var(--rw-ease-out);
  }
  .rw-top__nav a:hover, .rw-top__nav a[aria-current="page"] { color: var(--rw-bone); }
  /* "you are here" indicator lamp above the current label (one per header) */
  .rw-top__nav a[aria-current="page"]::before {
    content: ""; position: absolute; top: .85rem; left: 50%; width: 5px; height: 5px;
    border-radius: 50%; background: var(--rw-crimson); box-shadow: 0 0 8px var(--rw-crimson-glow);
    transform: translateX(-50%);
  }
  .rw-top__cta { margin-left: .2rem; }
}
@media (min-width: 880px) and (hover: hover) and (pointer: fine) {
  .rw-top__nav a:hover::after { transform: scaleX(1); }
}

/* --- Full-screen overlay menu --- */
/* The panel is LARGE-viewport ink (round 11): 100lvh reaches at or below the
   lowest chrome line, so the menu extends further down ALL the time, browser
   controls showing or not (iOS's classic 100vh is the lvh-equivalent
   fallback). The old dvh sizing + negative-inset pseudo bleeds are gone: iOS
   clamps negative clip-path insets and clips fixed-layer painting, which is
   why round 10's bleeds never showed on device. */
.rw-overlay {
  position: fixed; top: 0; left: 0; right: 0; z-index: var(--z-overlay);
  height: 100vh; height: 100lvh;
  background: var(--rw-ink);
  clip-path: inset(0 0 100% 0);
  opacity: 0; visibility: hidden; pointer-events: none;
  transition: clip-path .42s var(--rw-ease-drawer), opacity .42s var(--rw-ease-out),
              visibility 0s linear .42s;
}
.rw-overlay.is-open {
  clip-path: inset(0 0 0 0); opacity: 1; visibility: visible; pointer-events: auto;
  transition: clip-path .46s var(--rw-ease-drawer), opacity .3s var(--rw-ease-out);
}
/* the frame is the USABLE menu: sized to the visible viewport (dvh) so the
   contact strip + CTA never hide behind browser controls, while the panel's
   lvh ink runs on beneath them */
.rw-overlay__frame {
  height: 100vh; height: 100dvh;
  display: grid; grid-template-rows: auto 1fr auto;
  padding: env(safe-area-inset-top, 0px) var(--gutter) max(1.4rem, env(safe-area-inset-bottom));
}
/* while the menu is FULLY open, the page behind goes blank: iOS paints page
   content underneath its own chrome where no fixed layer can reach, so the
   only way to guarantee that strip reads as menu ink is for the page itself
   to be ink there. Applied by shell.js AFTER the open wipe finishes, removed
   BEFORE the close wipe starts. */
body.rw-veiled main, body.rw-veiled .rw-footer { visibility: hidden; }
/* top row mirrors the fixed bar exactly (row height + brand width), so the
   Close control lands where the Menu control was - the thumb never moves */
.rw-overlay__top { display: flex; align-items: center; min-height: var(--top-h); }
.rw-overlay__top img { width: 112px; }
/* the body stretches to fill the row and the links scale with viewport height,
   so the open menu occupies the whole screen with intent - no dead zones */
.rw-overlay__body {
  align-self: stretch; min-height: 0; display: grid; align-content: center;
  align-items: center; gap: 2.5rem; grid-template-columns: 1fr;
}
.rw-overlay__nav { display: flex; flex-direction: column; gap: clamp(.35rem, 2vh, 1rem); }
.rw-overlay__nav a {
  font-family: var(--ff-display); color: var(--rw-bone);
  font-size: min(clamp(2rem, 7.5vh, 3.8rem), 12vw);
  letter-spacing: -.02em; line-height: 1.08; width: max-content;
  transition: color .16s var(--rw-ease-out), transform .2s var(--rw-ease-out);
}
.rw-overlay__nav a[aria-current="page"] { color: var(--rw-crimson); }
.rw-overlay__figure { display: none; }
/* contact strip: email with the phone stacked directly beneath it in crimson;
   the CTA keeps the right edge */
.rw-overlay__contact { display: grid; grid-template-columns: 1fr auto; gap: .35rem 1.5rem;
  align-items: center; border-top: 1px solid var(--rw-line); padding-top: 1.4rem; }
.rw-overlay__contact a { color: var(--rw-text); font-size: var(--fs-sm); width: max-content; }
.rw-overlay__contact a[href^="tel:"] { color: var(--rw-crimson-text); font-weight: 600; }
.rw-overlay__contact .rw-act { grid-column: 2; grid-row: 1 / span 2; }

@media (hover: hover) and (pointer: fine) {
  .rw-overlay__nav a:hover { color: var(--rw-crimson); transform: translateX(6px); }
}
/* narrow phones (round 5): email + the estimate CTA cannot share a row
   without pushing past the right edge - stack the strip, CTA full width */
@media (max-width: 479px) {
  .rw-overlay__contact { grid-template-columns: 1fr; }
  .rw-overlay__contact .rw-act { grid-column: 1; grid-row: auto; width: 100%; margin-top: .5rem; }
}
@media (min-width: 720px) {
  .rw-overlay__body { grid-template-columns: 1.1fr .9fr; gap: 3rem; }
  .rw-overlay__figure { display: block; align-self: stretch; border-radius: var(--r-3); overflow: hidden; }
  .rw-overlay__figure img { width: 100%; height: 100%; object-fit: cover; }
}

body.rw-no-scroll { overflow: hidden; }

/* ============================================================================
   FOOTER  (asymmetric contact panel; not a 4-column link grid)
   ========================================================================== */
.rw-footer {
  position: relative; border-top: 1px solid var(--rw-line);
  background: linear-gradient(180deg, var(--rw-coal), var(--rw-ink) 88%);
}
/* the rewind seam returns along the footer's top edge, bookending the hero's */
.rw-footer::before {
  content: ""; position: absolute; top: -1px; left: 0; right: 0; height: 2px; pointer-events: none;
  background: linear-gradient(90deg, transparent, var(--rw-crimson) 16%, var(--rw-crimson) 84%, transparent);
  box-shadow: 0 0 18px var(--rw-crimson-glow);
}

/* pre-footer estimate strip */
.rw-footer__cta {
  display: grid; gap: 1.6rem; align-items: center; padding-block: clamp(3rem, 6vw, 5rem);
  border-bottom: 1px solid var(--rw-line);
}
.rw-footer__cta h2 { font-size: clamp(2rem, 1.3rem + 2.8vw, 3.5rem); color: var(--rw-bone); max-width: 16ch;
  letter-spacing: -.02em; }
.rw-footer__cta-actions { display: flex; flex-wrap: wrap; gap: .8rem; }
@media (min-width: 860px) {
  .rw-footer__cta { grid-template-columns: 1.4fr auto; }
  .rw-footer__cta-actions { justify-content: flex-end; }
}

/* footer body: oversized wordmark + contact definition list */
.rw-footer__body { display: grid; gap: 2.5rem; padding-block: clamp(2.5rem, 5vw, 4rem); }
.rw-footer__mark { display: block; width: clamp(170px, 26vw, 280px); opacity: .96; }
.rw-footer__mark img { width: 100%; height: auto; }
/* wordmark + motto as one signature column */
.rw-footer__ident { display: grid; gap: .9rem; justify-items: start; align-content: end; }
.rw-footer__motto {
  font-family: var(--ff-display); font-size: clamp(1.2rem, 1rem + 1vw, 1.7rem);
  color: var(--rw-titanium); letter-spacing: -.015em; line-height: 1.1;
}
.rw-footer__motto em { font-style: italic; color: var(--rw-crimson-text); }
/* social row: bordered rounded-square tiles under the motto, crimson on hover.
   44px targets, flush with the ident column edge. */
.rw-footer__social { display: flex; gap: .55rem; margin-top: .2rem; }
.rw-footer__social a {
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; border-radius: var(--r-3);
  border: 1px solid var(--rw-line-strong); color: var(--rw-ash);
  transition: color .16s var(--rw-ease-out), background-color .16s var(--rw-ease-out),
              border-color .16s var(--rw-ease-out);
}
.rw-footer__social svg { width: 20px; height: 20px; }
.rw-footer__social a:active { transform: scale(.97); }
@media (hover: hover) and (pointer: fine) {
  .rw-footer__social a:hover {
    color: var(--rw-crimson-text); background: rgba(236, 50, 36, .08);
    border-color: var(--rw-line-crimson);
  }
}
.rw-footer__contact { display: grid; gap: 1.1rem 2.5rem; }
.rw-footer__contact dl { display: grid; grid-template-columns: auto 1fr; gap: .35rem 1.1rem; align-items: baseline;
  border-left: 2px solid var(--rw-crimson); padding-left: 1.2rem; }
.rw-footer__contact dt { font-family: var(--ff-mono); font-size: var(--fs-xs); color: var(--rw-ash); }
.rw-footer__contact dd { color: var(--rw-text); font-size: var(--fs-sm); }
/* email + phone read highlighted, echoing the LaingLogic credit mark */
.rw-footer__contact dd a {
  color: var(--rw-bone); font-weight: 600;
  border-bottom: 2px solid var(--rw-crimson); padding-bottom: 1px;
}
.rw-footer__contact a:hover { color: var(--rw-crimson-text); }
@media (min-width: 860px) {
  .rw-footer__body { grid-template-columns: 1fr auto; align-items: end; }
  .rw-footer__contact { grid-template-columns: 1fr 1fr; }
}

/* single thin meta line */
.rw-footer__meta {
  display: flex; flex-wrap: wrap; align-items: center; gap: .6rem 1.4rem;
  padding-block: 1.4rem; border-top: 1px solid var(--rw-line);
  font-size: var(--fs-xs); color: var(--rw-ash);
}
.rw-footer__meta nav { display: flex; flex-wrap: wrap; gap: .2rem 1.4rem; }
.rw-footer__meta a { display: inline-block; padding-block: .45rem; }   /* touch target */
.rw-footer__meta a:hover { color: var(--rw-bone); }
/* the copyright line reads fully white (bone - this site never uses pure #fff) */
.rw-footer__meta > span:first-child { color: var(--rw-bone); }
.rw-footer__meta .rw-footer__credit { margin-left: auto; }
/* the LaingLogic mark reads highlighted: bone text over a crimson underline */
.rw-footer__credit b {
  color: var(--rw-bone); font-weight: 600;
  border-bottom: 2px solid var(--rw-crimson); padding-bottom: 1px;
}
/* the credit is a LINK (round 8, -> lainglogic.com): hover lights ONLY the
   LaingLogic mark - the "Fuelled by" prefix holds the meta row's ash */
.rw-footer__meta a.rw-footer__credit:hover { color: var(--rw-ash); }
a.rw-footer__credit:hover b { color: var(--rw-crimson-text); border-bottom-color: var(--rw-crimson-text); }

/* ============================================================================
   MOTION  (wind-in signature; reduced-motion safe)
   ========================================================================== */
/* Gated behind .js so NO-JS keeps all content visible (no-JS safe). The .js
   class is set synchronously in <head> before paint, so there is no flash. */
.js [data-rw-reveal] {
  opacity: 0; clip-path: inset(0 0 16% 0);
  transition: opacity .56s var(--rw-ease-out), clip-path .56s var(--rw-ease-out);
}
.js [data-rw-reveal].is-in { opacity: 1; clip-path: inset(0 0 0 0); }
.js [data-rw-reveal][data-rw-reveal="left"] { clip-path: inset(0 16% 0 0); }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important; animation-iteration-count: 1 !important;
    transition-duration: .001ms !important; scroll-behavior: auto !important;
  }
  [data-rw-reveal] { opacity: 1 !important; clip-path: inset(0 0 0 0) !important; }
}

/* Press feedback (tactile) - subtle scale on :active across shell interactives.
   Instantaneous (no transition), so reduced-motion needs no special-casing.
   Module elements (svc rows, chips, FAQ summaries) get theirs in modules.css. */
.rw-top__nav a:active, .rw-overlay__nav a:active,
.rw-menu-btn:active,
.rw-top__cta:active { transform: scale(.97); }

/* Utilities */
.rw-center { text-align: center; }
.rw-muted { color: var(--rw-ash); }
.rw-crimson { color: var(--rw-crimson-text); }
.rw-nowrap { white-space: nowrap; }

/* ============================================================================
   MERGED: cookie-consent.css (lucid ns=cc) - round 7 audit fix PERF-001..010
   (4th render-blocking head stylesheet). The standalone cookie-consent.css
   stays on disk as the lucid artifact but is NO LONGER LINKED.
   HAZARD: a future `lucid --implement-cookies` re-run regenerates that file
   AND re-adds its <link> to every head - after any re-run: re-apply the
   brand restyle, re-merge here, and re-remove the links (see CLAUDE.md).
   ========================================================================== */
/* lucid implement-cookies v1.6.0 - cookie-consent.css (variant-shared UI styles)
   Site-native tokens resolved from the source stylesheet; per-theme
   colours are contrast-clamped (text >= 4.5:1, UI accents >= 3:1) and
   declared below in the machine-readable cc-contrast-tokens block the
   check_cookie_consent.py gate re-verifies. Do not edit by hand. */

/* cc-contrast-tokens
   theme=dark surface=#0d0d10 text-primary=rgba(255,255,255,0.92) text-secondary=rgba(255,255,255,0.72) accent=#ec3224 accent-text=#fb3762
*/

.cc-cookie, .cc-cookie-overlay {
  --cc-surface: #0d0d10;
  --cc-text-primary: rgba(255,255,255,0.92);
  --cc-text-secondary: rgba(255,255,255,0.72);
  --cc-accent: #ec3224;
  --cc-accent-text: #fb3762;
  font-family: var(--ff-body);
}

.cc-cookie {
  position: fixed; left: 0; right: 0; bottom: 0;
  display: flex; justify-content: center; padding: 16px;
  z-index: 2147483000;
  pointer-events: none;
}
.cc-cookie-card {
  position: relative; overflow: hidden; pointer-events: auto;
  width: 100%; max-width: 640px;
  display: flex; align-items: center; gap: 20px;
  padding: 18px 20px; border-radius: 4px;
  background-color: var(--cc-surface);
  background-image: linear-gradient(135deg, rgba(255,255,255,0.07) 0%, rgba(255,255,255,0.02) 50%, rgba(255,255,255,0.05) 100%);
  border: 1px solid rgba(255,255,255,0.12); border-top: 2px solid #ec3224;
  backdrop-filter: blur(24px) saturate(1.6);
  -webkit-backdrop-filter: blur(24px) saturate(1.6);
  box-shadow: 0 24px 64px rgba(0,0,0,0.5), inset 0 1px 0 rgba(255,255,255,0.08);
  transform: translateY(24px); opacity: 0;
  transition: transform 0.5s cubic-bezier(0.22,1,0.36,1), opacity 0.4s ease;
}
.cc-cookie.cc-cookie-open .cc-cookie-card { transform: translateY(0); opacity: 1; }

.cc-cookie-body { position: relative; z-index: 1; flex: 1 1 auto; min-width: 0; }
.cc-cookie-title { font-size: 13px; font-weight: 700; letter-spacing: 0.02em; color: var(--cc-text-primary); margin: 0 0 5px; }
.cc-cookie-text { font-size: 12.5px; line-height: 1.55; color: var(--cc-text-secondary); margin: 0; }
.cc-cookie-link { color: var(--cc-accent-text); text-decoration: none; }
.cc-cookie-link:hover { text-decoration: underline; }
.cc-cookie-actions { position: relative; z-index: 1; display: flex; gap: 10px; flex: 0 0 auto; }
.cc-cookie-btn {
  font-family: inherit; font-size: 13px; font-weight: 700; letter-spacing: 0.01em;
  padding: 11px 18px; border-radius: 6px; cursor: pointer;
  min-height: 44px; white-space: nowrap;
  transition: transform 0.2s ease, background 0.2s ease, border-color 0.2s ease;
}
.cc-cookie-options, .cc-cookie-save { background: transparent; color: var(--cc-text-secondary); border: 1px solid rgba(255,255,255,0.12); }
.cc-cookie-options:hover, .cc-cookie-save:hover { color: var(--cc-text-primary); transform: translateY(-1px); }
.cc-cookie-reject { background: transparent; color: var(--cc-text-primary); border: 1px solid rgba(255,255,255,0.12); }
.cc-cookie-reject:hover { transform: translateY(-1px); }
.cc-cookie-accept { background: #c92b1f; color: #ffffff; border: none; }
.cc-cookie-accept:hover { transform: translateY(-1px); }
.cc-cookie-prefs-link { background: none; border: none; padding: 0; cursor: pointer; font: inherit; text-decoration: none; }


.cc-cookie-overlay {
  position: fixed; inset: 0; z-index: 2147483001;
  display: flex; align-items: center; justify-content: center; padding: 24px;
  background: rgba(46, 4, 15, 0.66);   /* faint dark-red wash over the page */
  backdrop-filter: blur(24px) saturate(1.6);
  -webkit-backdrop-filter: blur(24px) saturate(1.6);
  opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity 0.35s ease, visibility 0s linear 0.35s;
}
.cc-cookie-overlay.cc-cookie-overlay-open { opacity: 1; visibility: visible; pointer-events: auto; transition: opacity 0.35s ease, visibility 0s linear 0s; }
.cc-cookie-panel {
  position: relative; width: 100%; max-width: 460px;
  max-height: calc(100vh - 48px); max-height: calc(100dvh - 48px); overflow-y: auto;
  padding: 28px 26px 24px; border-radius: 6px;
  background-color: var(--cc-surface);
  background-image: linear-gradient(135deg, rgba(255,255,255,0.07) 0%, rgba(255,255,255,0.02) 50%, rgba(255,255,255,0.05) 100%);
  border: 1px solid rgba(255,255,255,0.12); border-top: 2px solid #ec3224;
  backdrop-filter: blur(24px) saturate(1.6);
  -webkit-backdrop-filter: blur(24px) saturate(1.6);
  box-shadow: 0 24px 64px rgba(0,0,0,0.5), inset 0 1px 0 rgba(255,255,255,0.08);
  transform: scale(0.96); opacity: 0;
  transition: transform 0.4s cubic-bezier(0.22,1,0.36,1), opacity 0.3s ease;
}
.cc-cookie-overlay.cc-cookie-overlay-open .cc-cookie-panel { transform: scale(1); opacity: 1; }

.cc-cookie-overlay-title { font-size: 15px; margin-bottom: 0; }
.cc-cookie-overlay-sub { font-size: 12.5px; line-height: 1.55; color: var(--cc-text-secondary); margin: 8px 0 0; }
.cc-cookie-opts { display: flex; flex-direction: column; gap: 12px; margin: 20px 0 22px; }
.cc-cookie-opt { display: flex; align-items: center; gap: 16px; padding: 14px 16px; border-radius: 6px; background: rgba(255,255,255,0.03); border: 1px solid rgba(255,255,255,0.08); }
.cc-cookie-opt-text { flex: 1 1 auto; min-width: 0; }
.cc-cookie-opt-name { font-size: 13px; font-weight: 700; color: var(--cc-text-primary); margin-bottom: 3px; }
.cc-cookie-opt-desc { font-size: 12px; line-height: 1.5; color: var(--cc-text-secondary); }
.cc-cookie-switch { position: relative; flex: 0 0 auto; width: 46px; height: 28px; padding: 0; border: none; background: none; cursor: pointer; -webkit-tap-highlight-color: transparent; }
.cc-cookie-switch-track { display: block; width: 46px; height: 28px; border-radius: 999px; background: rgba(255,255,255,0.12); border: 1px solid rgba(255,255,255,0.18); transition: background 0.25s ease, border-color 0.25s ease; }
.cc-cookie-switch-thumb { position: absolute; top: 4px; left: 4px; width: 20px; height: 20px; border-radius: 50%; background: #fff; box-shadow: 0 2px 6px rgba(0,0,0,0.35); transition: transform 0.25s cubic-bezier(0.22,1,0.36,1); }
.cc-cookie-switch[aria-checked="true"] .cc-cookie-switch-track { background: var(--cc-accent); border-color: transparent; }
.cc-cookie-switch[aria-checked="true"] .cc-cookie-switch-thumb { transform: translateX(18px); }
.cc-cookie-switch[disabled] { cursor: default; opacity: 0.7; }
.cc-cookie-switch:focus-visible { outline: 2px solid var(--cc-accent); outline-offset: 3px; border-radius: 999px; }
.cc-cookie-btn:focus-visible, .cc-cookie-prefs-link:focus-visible, .cc-cookie-overlay-close:focus-visible { outline: 2px solid var(--cc-accent); outline-offset: 2px; }
.cc-cookie-overlay-actions { display: flex; gap: 10px; }
.cc-cookie-overlay-actions .cc-cookie-btn { flex: 1 1 0; padding: 11px 12px; }
.cc-cookie-overlay-close { position: absolute; top: 12px; right: 12px; width: 32px; height: 32px; min-height: 32px; display: flex; align-items: center; justify-content: center; padding: 0; border-radius: 8px; border: 1px solid rgba(255,255,255,0.08); background: transparent; color: var(--cc-text-secondary); cursor: pointer; }
.cc-cookie-overlay-close:hover { color: var(--cc-text-primary); }
.cc-cookie-overlay-close svg { width: 14px; height: 14px; }
html.cc-cookie-noscroll, html.cc-cookie-noscroll body { overflow: hidden; }

@media (max-width: 640px) {
  .cc-cookie { padding: 10px 10px calc(20px + env(safe-area-inset-bottom, 0px)); left: 0; right: 0; bottom: 0; justify-content: center; }
  .cc-cookie-card { flex-direction: column; align-items: stretch; gap: 14px; padding: 18px; width: 100%; max-width: 640px; }
  .cc-cookie-actions { width: 100%; }
  .cc-cookie-btn { flex: 1 1 0; }
  .cc-cookie-overlay { padding: 16px; }
  .cc-cookie-panel { padding: 24px 20px 20px; }
  .cc-cookie-overlay-actions { flex-direction: column; }
}
@media (prefers-reduced-motion: reduce) {
  .cc-cookie-card { transition: opacity 0.3s ease; transform: none; }
  .cc-cookie.cc-cookie-open .cc-cookie-card { transform: none; }
  .cc-cookie-overlay { transition: opacity 0.2s ease; }
  .cc-cookie-panel { transition: opacity 0.2s ease; transform: none; }
  .cc-cookie-overlay.cc-cookie-overlay-open .cc-cookie-panel { transform: none; }
  .cc-cookie-switch-thumb { transition: none; }
}

/* ── Spacing and layout that used to be style attributes (HDR-039, 2026-09-22) ──
   style-src carries no 'unsafe-inline' any more (_headers), so the eight
   one-off adjustments the pages wrote inline are classes here. !important
   keeps the precedence an inline style had over every page rule. */
.rw-mt-08 { margin-top: .8rem !important; }
.rw-mt-1 { margin-top: 1rem !important; }
.rw-mt-14 { margin-top: 1.4rem !important; }
.rw-mb-0 { margin-bottom: 0 !important; }
.rw-ml-14 { margin-left: 1.4rem !important; }
.rw-wrap { flex-wrap: wrap !important; }
.rw-note-sm { font-size: var(--fs-sm) !important; margin-bottom: 1rem !important; }
