/* ==========================================================================
   Highway Heroes Logistics — Design System
   hhlogs.com · one stylesheet, every page, EN + ES

   Brand rules that must not drift:
     · Sharp corners. No border-radius on layout surfaces. The logo is a
       shield with hard edges and the whole system follows it.
     · Numbers are always mono. The promise is "we show our work", so every
       figure on the site is typeset like data, never like marketing.
     · One primary action per screen: Talk to a Dispatcher.
     · Chrome (the logo's metal) appears as a gradient rule, never as a fill
       behind text.

   Contents
     01 Tokens              10 Recap card          19 Blog
     02 Reset + base        11 Trust strip         20 Long-form / legal
     03 Typography          12 Sections            21 Final CTA
     04 Motion utilities    13 Services            22 Footer
     05 Loader              14 Process             23 Chat widget
     06 Layout              15 Load board          24 Utilities
     07 Buttons             16 Gallery + video     25 Print
     08 Header              17 Calculators
     09 Hero                18 Forms
   ========================================================================== */

/* == 01 Tokens ============================================================ */
:root {
  /* Brand */
  --asphalt: #121A2C;
  --asphalt-2: #18213A;
  --asphalt-3: #0C1220;
  --steel: #3C4658;
  --accent: #D6303C;
  --accent-lo: #B0242F;
  --accent-hi: #F04552;
  --paper: #F6F6F8;
  --paper-2: #EAECF1;
  --white: #FFFFFF;
  --slate: #59626E;
  --signal: #2E9E6D;
  --warn: #E8A33D;
  --chrome: #D7DBE2;

  /* Back-compat with earlier prototype markup that used var(--amber). */
  --amber: var(--accent);

  /* Surfaces + lines */
  --line: rgba(247, 245, 241, 0.14);
  --line-soft: rgba(247, 245, 241, 0.08);
  --line-light: rgba(18, 26, 44, 0.10);
  --line-light-2: rgba(18, 26, 44, 0.20);
  --on-dark: rgba(247, 245, 241, 0.74);
  --on-dark-mute: rgba(247, 245, 241, 0.58);

  /* The logo's metal, reused as rules and edges. */
  --chrome-grad: linear-gradient(180deg, #FFFFFF 0%, #C9CFD9 42%, #8B93A2 58%, #EDF0F4 100%);
  --accent-grad: linear-gradient(135deg, var(--accent-hi), var(--accent-lo));

  /* Type
     Montserrat for display, Inter for everything else.
     There is no third webfont for data. Inter carries real tabular figures,
     so the "readout" character comes from uppercase + wide tracking + tabular
     numerals instead of a monospace face. One less font file to download. */
  --font-display: 'Montserrat', 'Montserrat Fallback', 'Helvetica Neue', Arial, sans-serif;
  --font-body: 'Inter', 'Inter Fallback', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  --font-mono: var(--font-body);

  /* Space + rhythm */
  --wrap: 1180px;
  --gutter: 24px;
  --section-y: clamp(64px, 8.5vw, 108px);
  --header-h: 84px;

  /* Shape.
     The original handoff sample specified sharp corners throughout. That was
     the client's reference, not our design. Softening the geometry is the
     single biggest thing separating this from that sample: rounded surfaces,
     pill actions, and real elevation instead of hairline boxes. */
  --r-xs: 8px;
  --r-sm: 12px;
  --r-md: 18px;
  --r-lg: 26px;
  --r-xl: 34px;
  --r-pill: 999px;

  /* Elevation — deeper and softer, so cards sit above the page instead of
     being drawn onto it. */
  --shadow-1: 0 2px 8px rgba(18, 26, 44, 0.05), 0 1px 2px rgba(18, 26, 44, 0.04);
  --shadow-2: 0 12px 32px rgba(18, 26, 44, 0.10), 0 2px 8px rgba(18, 26, 44, 0.06);
  --shadow-3: 0 28px 80px rgba(10, 14, 24, 0.40), 0 8px 24px rgba(10, 14, 24, 0.24);
  --shadow-lift: 0 20px 50px rgba(18, 26, 44, 0.16);
  --glow-accent: 0 0 0 1px rgba(214, 48, 60, 0.35), 0 10px 34px rgba(214, 48, 60, 0.28);

  /* Motion */
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
  /* Scroll reveals sit at 520ms with a strong ease-out: long enough to read
     as motion, short enough that a fast scroller is never waiting on it. */
  --t-fast: 160ms;
  --t-base: 280ms;
  --t-slow: 620ms;
}

/* == 02 Reset + base ====================================================== */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

html {
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--header-h) + 20px);
  -webkit-text-size-adjust: 100%;
}

/* Nothing on this site should ever scroll sideways. Two things would otherwise
   cause it: the closed mobile nav parked off-screen, and the oversized hero
   road plane. `clip` rather than `hidden` on purpose — `hidden` turns the
   element into a scroll container and breaks the sticky header. */
html, body { overflow-x: clip; }

body {
  font-family: var(--font-body);
  background: var(--paper);
  color: var(--asphalt);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-wrap: break-word;
}

img, svg, video, iframe { max-width: 100%; display: block; }
img { height: auto; }
a { color: inherit; }
[hidden] { display: none !important; }
/* Anchor offset is handled once, by scroll-padding-top on <html> above.
   Adding scroll-margin-top on :target as well makes the two stack and every
   in-page link lands a header-height too far down. */

::selection { background: var(--accent); color: var(--white); }

/* Links, buttons and other controls get the offset outline ring.
   Form fields are handled separately below: they already carry a border, and
   stacking an offset outline on top of it reads as two borders. */
a:focus-visible,
button:focus-visible,
summary:focus-visible,
[tabindex]:focus-visible {
  outline: 3px solid var(--accent-hi);
  outline-offset: 3px;
}

.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 300;
  background: var(--accent); color: var(--white);
  padding: 14px 22px; font-family: var(--font-mono); font-size: 13px; text-decoration: none;
}
.skip-link:focus { left: 0; }

/* == 03 Typography ========================================================
   Montserrat is not condensed, so headings run wider than the earlier
   Barlow Condensed setting. Sizes come down a step, tracking goes slightly
   negative, and line-height opens up to suit the larger x-height. */
h1, h2, h3, h4, .display {
  font-family: var(--font-display);
  text-transform: uppercase;
  letter-spacing: -0.02em;
  font-weight: 800;
  line-height: 1.08;
  text-wrap: balance;
}
h1 { font-size: clamp(33px, 4.6vw, 60px); }
h2 { font-size: clamp(25px, 3.4vw, 40px); }
h3 { font-size: clamp(18px, 2vw, 23px); letter-spacing: -0.015em; font-weight: 700; }
h4 { font-size: 16px; font-weight: 700; }
p { text-wrap: pretty; }

/* Figures and micro-labels. Tabular numerals keep columns from dancing as
   values update in the calculators and on the load board. */
.mono, .num {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-feature-settings: 'tnum' 1, 'cv05' 1;
}

.eyebrow {
  color: var(--accent);
  font-family: var(--font-body);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  margin-bottom: 20px;
  display: flex;
  align-items: center;
  gap: 12px;
}
.eyebrow::before { content: ''; width: 28px; height: 2px; background: var(--accent); flex: none; }

/* Chrome rule — the logo's metal used as a divider. */
.chrome-rule { height: 2px; background: var(--chrome-grad); opacity: 0.55; border: 0; }

/* Every readout on the site. Grouped here so the figures stay consistent
   wherever they appear: hero recap, load board, calculators, fact rows. */
.metric-value,
.recap-week,
.board-rpm, .board-age, .board-lane,
.calc-result .big,
.calc-break .mono,
.fact .v,
.hos-clock .v,
.ifta-due,
.trust-row,
.crumbs,
[data-count] {
  font-variant-numeric: tabular-nums;
  font-feature-settings: 'tnum' 1;
}

/* Small uppercase labels need extra weight to hold their shape in Inter at
   10–12px, where the old monospace face carried them on stroke width alone. */
.section-eyebrow,
.metric-label,
.recap-bar-label,
.board-title, .board-scope, .board-scan, .board-row.head, .board-rank,
.loader-text,
.crumbs,
.post-meta,
.foot-grid h2,
.calc-result .lbl,
.hos-clock .k,
.ifta-table th,
label,
.updated,
.trust-row,
.gal-cap,
.mile .mm,
.tool-link,
.read-more,
.ai-pick {
  font-weight: 600;
}

/* == 04 Motion utilities ================================================== */
/* Elements start hidden and are revealed by IntersectionObserver in hhl.js.
   .js-on is set on <html> so that without JS nothing is ever stuck invisible. */
.js-on [data-reveal] {
  opacity: 0;
  transform: translateY(22px);
  transition: opacity var(--t-slow) var(--ease-out), transform var(--t-slow) var(--ease-out);
  transition-delay: var(--reveal-delay, 0ms);
  will-change: opacity, transform;
}
.js-on [data-reveal='left']  { transform: translateX(-26px); }
.js-on [data-reveal='right'] { transform: translateX(26px); }
.js-on [data-reveal='scale'] { transform: scale(0.965); }
.js-on [data-reveal].is-in { opacity: 1; transform: none; }

/* == Scroll-driven accents ================================================
   Progressive enhancement, no JS and no library. Where the browser supports
   scroll-driven animations these run entirely off the compositor; where it
   does not, nothing happens and every element is already in its final state.

   The fallback direction matters and is the reason this is CSS rather than
   another JS-observed reveal. A first attempt wiped photographs open with
   clip-path driven by the same IntersectionObserver the text reveals use.
   Measured on the blog index, `is-in` never landed on those wrappers, so
   every card image sat at `clip-path: inset(0 0 100%)` — permanently
   invisible, on a page that still looked plausible because the captions were
   fine. A decorative effect must never be able to hide content: if the
   mechanism fails here, the image is simply not animated.

   Deliberately limited to two effects. The guidance this pass followed is one
   or two animated elements per view; past that the motion stops carrying
   meaning and competes with the content. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    /* Section photographs drift against the scroll. Small enough to read as
       depth rather than as an effect. */
    .svc-row-media img,
    .post-lead img,
    .pc-media img {
      animation: mediaDrift linear both;
      animation-timeline: view();
      animation-range: entry 0% exit 100%;
    }
    /* Oversized figures settle as they arrive, so the number is what the eye
       lands on rather than the box around it. */
    .stat-xl, .ph-fact .v, .mc-v, .mc-eq b {
      animation: figureSettle linear both;
      animation-timeline: view();
      animation-range: entry 10% entry 65%;
    }
  }
}
/* scale() holds the drift inside the frame — without it the translate would
   expose an edge at each end of the range. */
@keyframes mediaDrift {
  from { transform: scale(1.07) translateY(-1.5%); }
  to   { transform: scale(1.07) translateY(1.5%); }
}
@keyframes figureSettle {
  from { opacity: 0.4; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  .js-on [data-reveal] { opacity: 1 !important; transform: none !important; }
}

/* == 05 Loader ============================================================ */
/* Covers the first paint so the hero animation never appears half-built.
   Removed by hhl.js on window load, with a hard timeout as a safety net. */
#hhl-loader {
  position: fixed; inset: 0; z-index: 500;
  background: var(--asphalt);
  display: grid; place-items: center;
  transition: opacity 460ms var(--ease-out), visibility 460ms;
}
#hhl-loader.is-done { opacity: 0; visibility: hidden; pointer-events: none; }
.loader-inner { display: grid; place-items: center; gap: 26px; width: min(280px, 74vw); }
.loader-mark { width: 88px; height: 88px; position: relative; }
.loader-mark img { width: 100%; height: 100%; object-fit: contain; }
.loader-mark::after {
  content: ''; position: absolute; inset: -14px;
  border: 1px solid var(--line);
  animation: loaderPulse 1.6s var(--ease-in-out) infinite;
}
@keyframes loaderPulse {
  0%, 100% { transform: scale(0.92); opacity: 0.25; }
  50%      { transform: scale(1);    opacity: 0.7; }
}
.loader-track { width: 100%; height: 2px; background: var(--line-soft); overflow: hidden; }
.loader-fill { height: 100%; width: 0; background: var(--accent); transition: width 220ms linear; }
.loader-text {
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.18em;
  text-transform: uppercase; color: var(--on-dark-mute);
}
/* Without JS the loader must never trap the page. */
html:not(.js-on) #hhl-loader { display: none; }

/* == 06 Layout ============================================================ */
.wrap { max-width: var(--wrap); margin-inline: auto; padding-inline: var(--gutter); }
section { padding-block: var(--section-y); position: relative; }

.section-head { max-width: 660px; margin-bottom: clamp(36px, 5vw, 60px); }
.section-head h2 { margin-bottom: 16px; }
.section-head p { color: var(--slate); font-size: clamp(15px, 1.7vw, 17px); max-width: 580px; }

.section-eyebrow {
  font-family: var(--font-mono); font-size: 12px; color: var(--accent);
  background: var(--asphalt); display: inline-block; padding: 6px 13px;
  letter-spacing: 0.1em; margin-bottom: 20px; text-transform: uppercase;
}
.on-dark { background: var(--asphalt); color: var(--paper); }
.on-dark .section-eyebrow { background: var(--accent); color: var(--white); }
.on-dark .section-head h2 { color: var(--paper); }
.on-dark .section-head p { color: var(--on-dark); }

/* Three dark bands run back to back on the homepage. Left alone that is two
   full section paddings of empty navy between them, which reads as a gap
   rather than a rhythm. Adjacent dark bands share one boundary instead. */
.on-dark + .on-dark {
  padding-top: calc(var(--section-y) * 0.45);
  border-top: 1px solid var(--line-soft);
}
.on-dark + .on-dark::after {
  content: ''; position: absolute; top: -1px; left: 0;
  width: 120px; height: 2px; background: var(--accent); opacity: 0.85;
}

/* Fine grain over dark bands — stops large flat areas looking like plastic. */
.on-dark::before {
  content: ''; position: absolute; inset: 0; pointer-events: none; opacity: 0.5;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='0.035'/%3E%3C/svg%3E");
}
.on-dark > * { position: relative; z-index: 1; }

/* == 07 Buttons =========================================================== */
.btn {
  position: relative; overflow: hidden;
  display: inline-flex; align-items: center; justify-content: center; gap: 10px;
  padding: 16px 30px;
  font-family: var(--font-body); font-weight: 600; font-size: 15px; line-height: 1.2;
  text-decoration: none; border: 1px solid transparent; cursor: pointer;
  transition: transform var(--t-fast) var(--ease-out), background var(--t-fast), border-color var(--t-fast), box-shadow var(--t-base);
}
.btn-primary { background: var(--accent); color: var(--white); }
.btn-ghost { background: transparent; color: var(--paper); border-color: rgba(247, 245, 241, 0.34); }
.btn-dark { background: var(--asphalt); color: var(--white); }
.btn-sm { padding: 11px 20px; font-size: 14px; }

/* Sweep of light across the primary CTA on hover. */
.btn-primary::after {
  content: ''; position: absolute; top: 0; left: -120%;
  width: 60%; height: 100%;
  background: linear-gradient(100deg, transparent, rgba(255, 255, 255, 0.28), transparent);
  transition: left 620ms var(--ease-out);
}
@media (hover: hover) {
  .btn-primary:hover { background: var(--accent-lo); transform: translateY(-2px); box-shadow: var(--glow-accent); }
  .btn-primary:hover::after { left: 130%; }
  .btn-ghost:hover { border-color: var(--paper); background: rgba(247, 245, 241, 0.07); }
  .btn-dark:hover { background: #0A0F1C; transform: translateY(-2px); }
}
.btn:active { transform: translateY(0); }
.cta-row { display: flex; gap: 14px; flex-wrap: wrap; }

.tool-link {
  font-family: var(--font-mono); font-size: 12px; text-transform: uppercase;
  letter-spacing: 0.07em; color: var(--asphalt); text-decoration: none;
  border-bottom: 2px solid var(--accent); padding-bottom: 3px;
  display: inline-flex; align-items: center; gap: 7px;
  transition: gap var(--t-fast) var(--ease-out), color var(--t-fast);
}
@media (hover: hover) { .tool-link:hover { color: var(--accent); gap: 12px; } }

/* == 08 Header ============================================================ */
/* The blur lives on a pseudo-element, not on the header itself.
   backdrop-filter makes an element the containing block for any
   position:fixed descendant. The mobile menu is a child of this header, so
   with the filter here its `inset: header-h 0 0 0` resolved against the
   85px-tall header instead of the viewport: the overlay collapsed to a thin
   strip showing one link, with the page visible behind it. Keeping the filter
   on ::before leaves the header itself an ordinary containing block. */
/* A floating pill, not a full-width bar.
 *
 * The bar spanned edge to edge with a hairline under it, which is the default
 * every site has had for a decade and which cuts the page in two right where
 * the hero photograph is strongest. Detaching it lets the image run under and
 * past it, and turns the nav into an object sitting on the page rather than a
 * lid clamped over it.
 *
 * The blur stays on ::before rather than on .site-header itself. backdrop-filter
 * makes an element the containing block for fixed-position descendants, and the
 * mobile sheet is a fixed descendant — with the filter on the header the sheet
 * resolved its inset against the header box and rendered 85px tall. */
.site-header {
  position: sticky; top: 0; z-index: 100;
  background: transparent; border-bottom: 0;
  padding: 14px var(--gutter) 0;
  transition: padding var(--t-base) var(--ease-out);
}
.site-header.is-stuck { padding-top: 8px; }

.navbar {
  position: relative;
  display: flex; align-items: center; justify-content: space-between; gap: 18px;
  padding: 10px 12px 10px 20px; max-width: var(--wrap); margin-inline: auto;
  min-height: 64px; border-radius: var(--r-pill);
  border: 1px solid rgba(247, 245, 241, 0.12);
  box-shadow: 0 18px 50px rgba(6, 10, 20, 0.32);
  transition: min-height var(--t-base) var(--ease-out), padding var(--t-base) var(--ease-out),
              box-shadow var(--t-base), border-color var(--t-base);
}
.navbar::before {
  content: ''; position: absolute; inset: 0; z-index: -1;
  border-radius: inherit;
  background: rgba(18, 26, 44, 0.72);
  backdrop-filter: saturate(160%) blur(18px);
  -webkit-backdrop-filter: saturate(160%) blur(18px);
  transition: background var(--t-base);
}
.site-header.is-stuck .navbar {
  min-height: 58px; padding-block: 7px;
  border-color: rgba(247, 245, 241, 0.18);
  box-shadow: 0 16px 44px rgba(6, 10, 20, 0.5);
}
.site-header.is-stuck .navbar::before { background: rgba(18, 26, 44, 0.9); }


.logo {
  display: flex; align-items: center; gap: 12px;
  font-family: var(--font-display); font-weight: 800; font-size: 19px;
  color: var(--paper); text-transform: uppercase; letter-spacing: -0.01em;
  text-decoration: none; flex: none; line-height: 1;
}
.logo img { height: 52px; width: auto; transition: height var(--t-base) var(--ease-out); }
.site-header.is-stuck .logo img { height: 42px; }
.logo .logo-sub {
  display: block; font-family: var(--font-mono); font-size: 9px;
  letter-spacing: 0.22em; color: var(--accent); margin-top: 3px; font-weight: 500;
}

nav.links { display: flex; gap: 26px; align-items: center; }
nav.links a {
  position: relative;
  color: var(--paper); text-decoration: none; font-size: 14px; font-weight: 500;
  opacity: 0.82; transition: opacity var(--t-fast); padding-block: 4px;
}
nav.links a::after {
  content: ''; position: absolute; left: 0; bottom: -2px; height: 2px; width: 0;
  background: var(--accent); transition: width var(--t-base) var(--ease-out);
}
@media (hover: hover) {
  nav.links a:hover { opacity: 1; }
  nav.links a:hover::after { width: 100%; }
}
nav.links a[aria-current='page'] { opacity: 1; }
nav.links a[aria-current='page']::after { width: 100%; }

.nav-cta {
  background: var(--accent); color: var(--white) !important;
  padding: 12px 22px; font-weight: 600; font-size: 14px;
  text-decoration: none; opacity: 1 !important; white-space: nowrap;
  transition: background var(--t-fast), box-shadow var(--t-base);
}
.nav-cta::after { display: none !important; }
@media (hover: hover) { .nav-cta:hover { background: var(--accent-lo); box-shadow: var(--glow-accent); } }

.lang-toggle {
  font-family: var(--font-mono) !important; font-size: 11px !important;
  letter-spacing: 0.1em; border: 1px solid rgba(247, 245, 241, 0.28);
  padding: 7px 11px !important; opacity: 0.9 !important;
}
.lang-toggle::after { display: none !important; }
@media (hover: hover) { .lang-toggle:hover { border-color: var(--accent); color: var(--accent); } }

.mobile-toggle {
  display: none; background: rgba(247, 245, 241, 0.06);
  border: 1px solid rgba(247, 245, 241, 0.20);
  border-radius: 50%;
  color: var(--paper); cursor: pointer; width: 44px; height: 44px; flex: none;
  align-items: center; justify-content: center;
  transition: background var(--t-fast), border-color var(--t-fast);
}
.mobile-toggle:active { background: rgba(247, 245, 241, 0.14); }
.mobile-toggle .bars,
.mobile-toggle .bars::before,
.mobile-toggle .bars::after {
  display: block; width: 21px; height: 2px; background: var(--paper);
  transition: transform var(--t-base) var(--ease-out), opacity var(--t-fast);
}
.mobile-toggle .bars { position: relative; }
.mobile-toggle .bars::before, .mobile-toggle .bars::after { content: ''; position: absolute; left: 0; }
.mobile-toggle .bars::before { top: -6px; }
.mobile-toggle .bars::after { top: 6px; }
.mobile-toggle[aria-expanded='true'] .bars { background: transparent; }
.mobile-toggle[aria-expanded='true'] .bars::before { transform: translateY(6px) rotate(45deg); }
.mobile-toggle[aria-expanded='true'] .bars::after { transform: translateY(-6px) rotate(-45deg); }

/* == Icon chip ============================================================
   The component that makes a row of icons read as a set rather than as
   decoration: a bordered box at a fixed size holding a 1.75-stroke line icon
   that inherits currentColor. Because the colour comes from the chip, a
   parent hover can move the icon, the border and the fill together.
   ======================================================================== */
/* The base is the LIGHT palette, because most cards on the site are white.
   A first pass defaulted to the dark palette and every chip on a white bento
   tile rendered white-on-white — present in the DOM, invisible on screen.
   Dark surfaces opt in below rather than the other way round. */
.icon-chip {
  flex: none; display: inline-grid; place-items: center;
  width: 36px; height: 36px; border-radius: var(--r-sm);
  border: 1px solid var(--line-light); background: var(--paper-2); color: var(--slate);
  transition: border-color var(--t-fast), background var(--t-fast), color var(--t-fast);
}
.icon-chip svg { display: block; }

/* Every dark surface the chip can land on. */
.on-dark .icon-chip,
nav.links .icon-chip,
.bento-tile.dark > .icon-chip,
.bento-tile.deep > .icon-chip,
.bento-tile.glass > .icon-chip,
.claim-card .icon-chip,
.icon-chip.is-dark {
  border-color: var(--line); background: rgba(247, 245, 241, 0.06); color: var(--on-dark);
}

/* The wrapper is invisible to layout on desktop, so the header's flex row is
   unchanged by it. It only becomes a real box in the mobile sheet below. */
.nav-grid { display: contents; }
.nav-grip, .nav-call, .nav-scrim { display: none; }
/* Desktop is a text bar. Chips and notes belong to the mobile sheet only —
   seven chips strung across a horizontal bar is clutter, not wayfinding. */
nav.links .icon-chip, nav.links .nav-note { display: none; }

/* == Mobile menu: a bottom sheet, not a full-screen takeover ===============
   It used to be a full-screen panel sliding in from the right, holding eight
   links at 19px in a single column: 623px of content that overflowed and
   scrolled on any phone shorter than an iPhone 14, and that read as a page
   change rather than a menu.

   A sheet fixes all of it. It rises from the bottom, where the thumb already
   is and where this site's own bottom bar taught the visitor to look. Two
   columns halve the height so it never scrolls. The page stays visible behind
   a scrim, so opening the menu does not feel like leaving the page.
   ======================================================================== */
@media (max-width: 980px) {
  .mobile-toggle { display: flex; }

  .nav-scrim {
    display: block; position: fixed; inset: 0; z-index: 108;
    background: rgba(8, 12, 22, 0.6);
    opacity: 0; visibility: hidden;
    transition: opacity var(--t-base), visibility var(--t-base);
  }
  body.nav-open .nav-scrim { opacity: 1; visibility: visible; }

  nav.links {
    position: fixed; left: 0; right: 0; bottom: 0; top: auto; z-index: 140;
    display: block;
    background: var(--asphalt);
    border-top: 1px solid var(--line);
    border-radius: var(--r-lg) var(--r-lg) 0 0;
    box-shadow: 0 -18px 50px rgba(6, 10, 20, 0.55);
    padding: 12px var(--gutter) calc(20px + env(safe-area-inset-bottom, 0px));
    /* A cap rather than a fixed height: the sheet is as tall as its contents
       and no taller, and if a future menu outgrows the screen it scrolls
       inside itself instead of pushing the page. */
    max-height: 86svh; overflow-y: auto;
    /* visibility, not just transform. A closed panel parked at translateY
       still occupies layout: it grows the document's scrollable area and its
       links stay in the tab order while the menu looks shut. */
    transform: translateY(100%);
    visibility: hidden;
    transition: transform var(--t-base) var(--ease-out), visibility var(--t-base);
  }
  nav.links.open { transform: translateY(0); visibility: visible; }

  /* The grab handle. It does nothing on its own — the scrim and the toggle
     both close the sheet — but it is the shape that tells a phone user this
     panel came from the bottom edge and can go back there. */
  .nav-grip {
    display: block; width: 38px; height: 4px; margin: 0 auto 14px;
    border-radius: var(--r-pill); background: rgba(247, 245, 241, 0.24);
  }

  /* One column, not two. With an icon chip and a note on every row there is
     no longer a reason to halve the width, and a single column lets each item
     say what is behind it instead of being a two-word tile. */
  .nav-grid { display: grid; grid-template-columns: 1fr; gap: 6px; }
  nav.links a {
    display: flex; align-items: center; gap: 13px;
    font-size: 15px; font-weight: 600; opacity: 1;
    padding: 11px 12px; border: 1px solid transparent;
    border-radius: var(--r-sm); background: transparent;
  }
  nav.links a::after { display: none; }
  /* Both of these are switched off for the desktop bar further up, so the
     sheet has to turn them back on explicitly. */
  nav.links .icon-chip { display: inline-grid; }
  nav.links .nav-note { display: block; }
  .nav-text { display: grid; gap: 2px; min-width: 0; }
  .nav-label { line-height: 1.25; }
  .nav-note {
    display: block; font-size: 12px; font-weight: 500; letter-spacing: 0;
    color: var(--on-dark-mute); line-height: 1.3;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  nav.links a[aria-current='page'] {
    color: var(--accent);
    border-color: rgba(214, 48, 60, 0.38);
    background: rgba(214, 48, 60, 0.10);
  }
  nav.links a[aria-current='page'] .icon-chip {
    border-color: rgba(214, 48, 60, 0.45); background: rgba(214, 48, 60, 0.14); color: var(--accent);
  }
  nav.links a[aria-current='page'] .nav-note { color: rgba(214, 48, 60, 0.75); }
  /* :active rather than :hover — this panel only exists on touch. */
  nav.links a:active { background: rgba(247, 245, 241, 0.06); }
  nav.links a:active .icon-chip { border-color: rgba(247, 245, 241, 0.28); color: var(--paper); }

  /* Both of these need the `nav.links a` prefix to outrank the tile styling
     above — a bare `.nav-cta` loses to it on specificity and the primary
     action came out looking like a seventh menu tile. */
  nav.links a.nav-cta {
    display: block; text-align: center; padding: 17px 20px; margin-top: 10px;
    background: var(--accent); border-color: transparent; border-radius: var(--r-pill);
    font-size: 15px;
  }
  /* A quiet text line, not another tile. */
  nav.links a.nav-call {
    display: flex; align-items: center; justify-content: center; gap: 8px;
    margin-top: 14px; padding: 0; border: 0; background: none; border-radius: 0;
    font-size: 13px; font-weight: 500; opacity: 0.68; text-decoration: none;
  }
  nav.links a.nav-call svg { flex: none; }
  .nav-call::after { display: none !important; }

  .lang-toggle { display: block; text-align: center; margin-top: 14px; }

  /* The sheet lives inside .site-header, which is a stacking context of its
     own (sticky + z-index: 100). So the sheet's z-index only orders it
     against its siblings there — against the rest of the page it is pinned at
     100, and the bottom bar at 130 painted straight over it. Raising the
     header while the menu is open is what actually lifts the sheet. */
  body.nav-open .site-header { z-index: 150; }

  body.nav-open { overflow: hidden; }
}

/* == 09 Hero (motion graphics) ============================================ */
/* No photography and no video file. The background is drawn entirely in CSS
   and SVG: a receding road plane, a radar sweep over the load sources, and
   load chips that surface and fade. Everything stops under reduced-motion. */
.hero {
  position: relative; overflow: hidden;
  background: var(--asphalt-3);
  color: var(--paper);
  margin-top: calc(var(--header-h) * -1);
  padding: calc(var(--header-h) + clamp(34px, 4vw, 56px)) 0 clamp(56px, 7vw, 84px);
  min-height: min(88vh, 840px);
  display: flex; align-items: center;
  isolation: isolate;
}
.hero > .wrap { width: 100%; }

/* Photography behind the hero.
   Uses the full-colour plate, not one of the pre-graded ones: those are
   already darkened for text-directly-on-image use, and stacking these
   gradients on top of one flattened the truck into solid navy. All the
   darkening happens here, so the contrast is tunable in one place. */
.hero-photo {
  position: absolute; inset: 0; z-index: 0;

  /* The path is relative to THIS FILE, not to the page.
     A url() inside a custom property resolves against the stylesheet where
     the var() is substituted, so setting --hero-img from an inline style
     attribute produced assets/css/assets/img/... and silently 404'd, leaving
     the hero flat navy with nothing in the console. Keep it here.

     Subject choice matters as much as the path. A close-up of a cab put
     another carrier's name across the hero, where it reads as if it were
     HHL's. The drone shot of a silo on a lowboy carries no third-party
     branding and says "we move the hard stuff" without saying anything we
     cannot back up. */
  --hero-img: url("../img/w/load-silo-lowboy-1600.jpg");

  /* Heavier on the left, where all the type sits. Every text colour used on
     top of this clears 4.5:1 against the darkest point of its own band. */
  background-image:
    linear-gradient(100deg, rgba(10,15,27,0.97) 0%, rgba(10,15,27,0.93) 32%, rgba(10,15,27,0.62) 62%, rgba(10,15,27,0.80) 100%),
    linear-gradient(0deg, rgba(10,15,27,0.94) 0%, rgba(10,15,27,0.30) 40%, rgba(10,15,27,0.55) 100%),
    var(--hero-img, none);
  background-size: cover, cover, cover;
  background-position: center, center, center 42%;
}
@media (max-width: 860px) {
  .hero-photo {
    /* Half the pixels for a background nobody zooms into. */
    --hero-img: url("../img/w/load-silo-lowboy-800.jpg");
    background-image:
      linear-gradient(180deg, rgba(12,18,32,0.82) 0%, rgba(12,18,32,0.92) 55%, rgba(12,18,32,0.97) 100%),
      var(--hero-img, none);
    background-size: cover, cover;
    background-position: center, center 30%;
  }
}

/* Two columns: the argument on the left, the proof on the right. */
.hero-inner {
  position: relative; z-index: 1;
  display: grid; grid-template-columns: 1.02fr 0.98fr;
  gap: clamp(32px, 4vw, 64px); align-items: center;
}
@media (max-width: 980px) { .hero-inner { grid-template-columns: 1fr; gap: 40px; } }

.hero h1 { margin-bottom: 20px; color: var(--paper); }
.hero h1 em {
  font-style: normal;
  background: linear-gradient(100deg, var(--accent-hi), var(--accent));
  -webkit-background-clip: text; background-clip: text; color: transparent;
}
.hero p.lede { font-size: clamp(16px, 1.9vw, 19px); color: var(--on-dark); max-width: 54ch; margin-bottom: 30px; }

/* Small proof row under the CTAs: the three claims we can actually stand on. */
.hero-proof {
  display: flex; flex-wrap: wrap; gap: 26px;
  margin-top: 34px; padding-top: 26px;
  border-top: 1px solid rgba(247, 245, 241, 0.14);
}
.hero-proof div { min-width: 96px; }
.hero-proof .v {
  font-size: clamp(22px, 2.6vw, 30px); font-weight: 700; letter-spacing: -0.03em;
  color: var(--paper); font-variant-numeric: tabular-nums; line-height: 1;
}
.hero-proof .k {
  font-size: 11px; font-weight: 600; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--on-dark-mute); margin-top: 8px;
}

/* The recap panel: the signature object, now a properly elevated card. */
.hero-card {
  position: relative;
  background: linear-gradient(165deg, rgba(30, 41, 70, 0.92), rgba(16, 23, 41, 0.94));
  border: 1px solid rgba(247, 245, 241, 0.14);
  box-shadow: var(--shadow-3);
  backdrop-filter: blur(22px) saturate(150%);
  -webkit-backdrop-filter: blur(22px) saturate(150%);
  padding: clamp(22px, 2.6vw, 32px);
}
.hero-card-top {
  display: flex; justify-content: space-between; align-items: center;
  gap: 12px; flex-wrap: wrap; padding-bottom: 18px;
  border-bottom: 1px solid rgba(247, 245, 241, 0.12);
}
.hero-card-top .who { font-size: 15px; font-weight: 600; color: var(--paper); }
.hero-card-top .when {
  font-size: 11px; font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--on-dark-mute);
}
.hero-metrics { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; padding: 24px 0 20px; }
.hero-metric .v {
  font-size: clamp(24px, 3vw, 34px); font-weight: 700; letter-spacing: -0.035em;
  line-height: 1; color: var(--paper); font-variant-numeric: tabular-nums; white-space: nowrap;
}
.hero-metric .v.good { color: var(--signal); }
.hero-metric .k {
  font-size: 10px; font-weight: 600; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--on-dark-mute); margin-top: 9px;
}
.hero-card .recap-bar-track { border-radius: var(--r-pill); height: 7px; }
.hero-card .recap-bar-fill { border-radius: var(--r-pill); }
.hero-card-foot {
  margin-top: 20px; padding-top: 16px;
  border-top: 1px solid rgba(247, 245, 241, 0.12);
  font-size: 13px; color: var(--on-dark-mute);
  display: flex; align-items: center; gap: 9px;
}

/* Two photo chips tucked under the card, so the fold shows real freight. */
.hero-thumbs { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-top: 12px; }
.hero-thumb {
  position: relative; overflow: hidden; border-radius: var(--r-md);
  aspect-ratio: 16 / 10; border: 1px solid rgba(247, 245, 241, 0.12);
}
.hero-thumb img { width: 100%; height: 100%; object-fit: cover; }
.hero-thumb span {
  position: absolute; left: 0; right: 0; bottom: 0; padding: 20px 12px 9px;
  background: linear-gradient(transparent, rgba(8, 12, 22, 0.9));
  font-size: 10px; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--paper);
}
.hero-bg { position: absolute; inset: 0; z-index: 0; pointer-events: none; }

/* Horizon wash. Sits where the road plane vanishes, so the two read as one
   scene rather than two unrelated effects. */
.hero-bg .glow {
  position: absolute; left: 50%; top: 70%; transform: translate(-50%, -50%);
  width: min(1200px, 140%); height: 480px;
  background: radial-gradient(ellipse at center, rgba(214, 48, 60, 0.34), rgba(214, 48, 60, 0.08) 44%, transparent 72%);
  filter: blur(14px);
}
/* Darkens the edges and the type area without flattening the road, which
   lives in the lower half. */
.hero-bg .vignette {
  position: absolute; inset: 0;
  background:
    radial-gradient(120% 80% at 50% 30%, transparent 42%, rgba(6, 10, 20, 0.5) 100%),
    linear-gradient(180deg, rgba(12, 18, 32, 0.88) 0%, rgba(12, 18, 32, 0.30) 46%, rgba(12, 18, 32, 0.55) 100%);
}

/* Receding road: a plane tilted in 3D with lane lines pulled toward camera. */
/* A steeper rotateX squeezes the whole plane into a few pixels at the bottom
   of the frame. 66deg keeps enough of it on screen to actually read as a road
   pulling toward the viewer. */
.hero-bg .road {
  position: absolute; left: 50%; bottom: -2%;
  width: 300%; height: 82%;
  transform: translateX(-50%) perspective(560px) rotateX(66deg);
  transform-origin: 50% 100%;
  background-image:
    repeating-linear-gradient(0deg, rgba(215, 219, 226, 0.42) 0 3px, transparent 3px 74px),
    repeating-linear-gradient(90deg, rgba(215, 219, 226, 0.16) 0 2px, transparent 2px 128px);
  animation: roadPull 1.5s linear infinite;
  -webkit-mask-image: linear-gradient(to top, #000 0%, rgba(0,0,0,0.55) 34%, transparent 74%);
          mask-image: linear-gradient(to top, #000 0%, rgba(0,0,0,0.55) 34%, transparent 74%);
}
@keyframes roadPull { to { background-position: 0 74px, 0 0; } }

/* Centre lane in brand red */
.hero-bg .lane {
  position: absolute; left: 50%; bottom: -2%;
  width: 9px; height: 82%;
  transform: translateX(-50%) perspective(560px) rotateX(66deg);
  transform-origin: 50% 100%;
  background: repeating-linear-gradient(0deg, var(--accent) 0 38px, transparent 38px 92px);
  animation: roadPull 1.5s linear infinite;
  opacity: 0.9;
  -webkit-mask-image: linear-gradient(to top, #000 0%, transparent 70%);
          mask-image: linear-gradient(to top, #000 0%, transparent 70%);
}

/* Radar. Sits low-right, in the gap under the recap card, so it reads as its
   own element instead of a shape hidden behind the card. */
.hero-bg .radar {
  position: absolute; right: 3%; bottom: 4%;
  width: min(280px, 26vw); aspect-ratio: 1;
  opacity: 0.62;
}
.hero-bg .radar .ring { fill: none; stroke: rgba(247, 245, 241, 0.13); stroke-width: 1; }
.hero-bg .radar .cross { stroke: rgba(247, 245, 241, 0.09); stroke-width: 1; }
.hero-bg .radar .blip { fill: var(--accent); animation: blip 3.4s ease-in-out infinite; }
.hero-bg .radar .blip:nth-of-type(2) { animation-delay: 0.7s; }
.hero-bg .radar .blip:nth-of-type(3) { animation-delay: 1.5s; }
.hero-bg .radar .blip:nth-of-type(4) { animation-delay: 2.2s; }
@keyframes blip { 0%, 70%, 100% { opacity: 0; r: 3; } 12% { opacity: 1; r: 5; } 40% { opacity: 0.35; r: 8; } }
.hero-bg .sweep {
  position: absolute; inset: 0;
  background: conic-gradient(from 0deg, rgba(46, 158, 109, 0.30), transparent 22%, transparent 100%);
  border-radius: 50%;
  animation: sweep 4.6s linear infinite;
  -webkit-mask-image: radial-gradient(circle, #000 68%, transparent 69%);
          mask-image: radial-gradient(circle, #000 68%, transparent 69%);
}
@keyframes sweep { to { transform: rotate(360deg); } }

/* Load chips drifting up out of the feed */
.hero-bg .chips { position: absolute; inset: 0; overflow: hidden; }
.hero-chip-fly {
  position: absolute;
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.04em;
  color: rgba(247, 245, 241, 0.5);
  background: rgba(24, 33, 58, 0.72);
  border: 1px solid var(--line);
  border-left: 2px solid var(--accent);
  padding: 6px 11px; white-space: nowrap;
  animation: chipFly 11s linear infinite;
  opacity: 0;
}
@keyframes chipFly {
  0%   { opacity: 0; transform: translateY(30px); }
  12%  { opacity: 1; }
  78%  { opacity: 1; }
  100% { opacity: 0; transform: translateY(-90px); }
}

@media (prefers-reduced-motion: reduce) {
  .hero-bg .road, .hero-bg .lane, .hero-bg .sweep, .hero-chip-fly, .hero-bg .radar .blip { animation: none; }
  .hero-chip-fly { opacity: 1; }
}
/* Below the two-column breakpoint the recap card stacks under the copy and
   there is no free quadrant left for the chips or the radar to live in. */
@media (max-width: 980px) {
  .hero-bg .radar { display: none; }
  .hero-bg .chips { display: none; }
}

.hero-grid {
  position: relative; z-index: 1;
  display: grid; grid-template-columns: 1.08fr 0.92fr;
  gap: clamp(36px, 5vw, 60px); align-items: center;
}
.hero h1 { margin-bottom: 22px; color: var(--paper); }
.hero h1 em { font-style: normal; color: var(--accent); }
.hero p.lede { font-size: clamp(16px, 2vw, 19px); color: var(--on-dark); max-width: 520px; margin-bottom: 34px; }
@media (max-width: 980px) { .hero-grid { grid-template-columns: 1fr; } }

/* Live strip under the hero CTAs */
.hero-live {
  display: flex; align-items: center; gap: 10px; margin-top: 30px;
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--on-dark-mute);
}
.hero-live .dot {
  width: 8px; height: 8px; border-radius: 50%; background: var(--signal); flex: none;
  box-shadow: 0 0 0 0 rgba(46, 158, 109, 0.6); animation: ping 2.4s ease-out infinite;
}
@keyframes ping { 70%, 100% { box-shadow: 0 0 0 10px rgba(46, 158, 109, 0); } }

/* Interior page hero */
/* The header is a floating pill now, so the hero runs underneath it and the
   photograph is uninterrupted. The negative margin pulls the band up behind
   the pill and the top padding puts the content back below it — the offset
   has to live on this rule rather than beside .site-header, because this
   padding shorthand appears later in the file and would otherwise win. */
.page-hero {
  position: relative; overflow: hidden;
  background: var(--asphalt); color: var(--paper);
  margin-top: calc(var(--header-h) * -1);
  padding: calc(var(--header-h) + clamp(40px, 5vw, 64px)) 0 clamp(44px, 6vw, 64px);
}
/* Interior heroes carry the same photo layer as the homepage, but quieter.
   Fading the whole layer with opacity was wrong: it faded the darkening
   gradients too, so the image washed back into the navy and vanished. The
   gradients stay at full strength and are simply set heavier here. */
.page-hero.has-plate { position: relative; }

/* The gradient used to run 0.96 / 0.90 / 0.66 / 0.86 across the whole width,
   which put a near-opaque sheet over the photograph on every one of eighteen
   pages. The image was still being downloaded and still being decoded; it just
   could not be seen.
 *
 * Contrast is only needed where the type is, so the wash is heavy on the left
 * and clears off the right, and the freight reads.
 *
 * The stops are measured, not eyeballed. Method: render the page with
 * .ph-copy/.ph-facts/.crumbs/.post-lead hidden, sample the brightest pixel in
 * the copy column strictly inside the hero's own bounds, and check the
 * smallest text colour against it. Across all ten plate pages these stops
 * measure 6.2:1 to 8.1:1, against the 4.5:1 body text needs.
 *
 * Two traps worth knowing before touching them. A sample box even one pixel
 * taller than the hero catches the boundary row against the light section
 * below and reports a false fail. And the culvert-pipe plate is the bright
 * one — how-it-works and carrier-setup use it, and a wash that clears too
 * early puts those two at 3.9:1 while every other page still looks fine. */
.page-hero.has-plate .hero-photo {
  z-index: 0; opacity: 1;
  background-image:
    linear-gradient(100deg,
      rgba(10,15,27,0.97) 0%,
      rgba(10,15,27,0.95) 38%,
      rgba(10,15,27,0.74) 60%,
      rgba(10,15,27,0.32) 82%,
      rgba(10,15,27,0.50) 100%),
    linear-gradient(0deg, rgba(10,15,27,0.88) 0%, rgba(10,15,27,0.16) 55%, rgba(10,15,27,0.42) 100%),
    var(--hero-img, none);
  background-size: cover, cover, cover;
  background-position: center, center, right 34%;
}

/* Film grain. A flat dark field at this size reads as cheap; a little noise
   over it reads as a surface. Generated inline so it costs no request, and
   sits under the content but over the photograph. */
.page-hero.has-plate::after {
  content: ''; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  opacity: 0.42; mix-blend-mode: overlay;
  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.9' numOctaves='3'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)' opacity='0.5'/%3E%3C/svg%3E");
}

/* The band no longer ends on a hard horizontal cut. A single accent hairline
   along the bottom edge, brightest under the headline and fading out to the
   right, gives the section a finish line instead of a stop. */
.page-hero.has-plate::before {
  content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 2px; z-index: 2;
  background: linear-gradient(90deg, var(--accent) 0%, rgba(214,48,60,0.35) 34%, transparent 72%);
}

/* Slow drift on the plate as the hero leaves. Same mechanism as the rest of
   the motion pass: where it is unsupported the plate is simply static. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .page-hero.has-plate .hero-photo {
      animation: plateDrift linear both;
      animation-timeline: view();
      animation-range: entry 100% exit 100%;
    }
  }
}
@keyframes plateDrift {
  from { transform: scale(1.06) translateY(0); }
  to   { transform: scale(1.06) translateY(3.5%); }
}
.page-hero h1 { color: var(--paper); margin-bottom: 16px; }
.page-hero p { color: var(--on-dark); max-width: 640px; font-size: clamp(16px, 2vw, 18px); }

/* Copy left, spec panel right. Aligned to the baseline of the copy rather
   than centred: the headline is the anchor of the band and the panel should
   read as sitting on the same line, not floating beside it. */
.ph-grid {
  display: grid; grid-template-columns: 1.15fr 0.85fr;
  gap: clamp(28px, 5vw, 72px); align-items: end;
}
.page-hero.is-solo .ph-grid { grid-template-columns: 1fr; }
@media (max-width: 900px) { .ph-grid { grid-template-columns: 1fr; gap: 28px; } }

/* Now that the photograph shows through, the panel has to hold its own
   against it rather than sit on flat navy. Heavier blur, a real edge, an
   accent hairline along the top, and the rows hairlined instead of gapped —
   one card, not four stacked chips. */
.ph-facts {
  position: relative; border-radius: var(--r-md); overflow: hidden;
  background: rgba(14, 21, 36, 0.62);
  border: 1px solid rgba(247, 245, 241, 0.14);
  backdrop-filter: blur(18px) saturate(150%);
  -webkit-backdrop-filter: blur(18px) saturate(150%);
  box-shadow: 0 24px 60px rgba(6, 10, 20, 0.42);
}
.ph-facts::before {
  content: ''; position: absolute; left: 0; right: 0; top: 0; height: 2px;
  background: linear-gradient(90deg, var(--accent), rgba(214, 48, 60, 0.15));
}
.ph-fact {
  display: flex; align-items: baseline; justify-content: space-between; gap: 18px;
  padding: 15px clamp(16px, 1.8vw, 22px);
  border-top: 1px solid rgba(247, 245, 241, 0.09);
}
.ph-fact:first-child { border-top: 0; }
.ph-fact .k {
  font-size: 10px; font-weight: 600; letter-spacing: 0.13em;
  text-transform: uppercase; color: var(--on-dark-mute);
}
.ph-fact .v {
  font-size: clamp(17px, 1.9vw, 22px); font-weight: 700; letter-spacing: -0.02em;
  color: var(--paper); font-variant-numeric: tabular-nums; white-space: nowrap;
}
/* Two across on a phone: four full-width rows of one figure each is a lot of
   hero for very little information. */
@media (max-width: 620px) {
  .ph-facts { grid-template-columns: 1fr 1fr; }
  .ph-fact { flex-direction: column; align-items: flex-start; gap: 5px; }
  .ph-fact .v { font-size: 18px; }
}

.ph-actions { margin-top: 28px; }

/* ---- Before / after ------------------------------------------------------
   Two panels and an arrow. The "after" side is the one carrying the claim, so
   it gets the accent and the elevation; the "before" side is deliberately
   flatter and quieter, because that is the state being argued against. */
.swap {
  display: grid; grid-template-columns: 1fr auto 1fr;
  gap: clamp(16px, 2.4vw, 30px); align-items: stretch;
}
.swap-side {
  padding: clamp(22px, 2.6vw, 32px);
  border: 1px solid var(--line); border-radius: var(--r-md);
  background: rgba(247, 245, 241, 0.03);
}
.swap-side.is-after {
  background: var(--asphalt-2);
  border-color: rgba(214, 48, 60, 0.42);
  box-shadow: var(--shadow-2);
}
.swap-side h3 { font-size: clamp(19px, 2.2vw, 25px); color: var(--paper); margin-bottom: 12px; }
.swap-side > p { font-size: 15px; color: var(--on-dark); line-height: 1.7; }
.swap-list { list-style: none; margin-top: 20px; padding-top: 18px; border-top: 1px solid var(--line); }
.swap-list li {
  position: relative; padding-left: 20px; margin-bottom: 10px;
  font-size: 13.5px; color: var(--on-dark-mute); line-height: 1.55;
}
.swap-list li::before {
  content: ''; position: absolute; left: 0; top: 8px;
  /* On the dark "before" panel --line-light-2 rendered as nothing at all.
     Quieter than the accent, but still visible. */
  width: 6px; height: 6px; background: rgba(247, 245, 241, 0.35); transform: rotate(45deg);
}
.is-after .swap-list li::before { background: var(--accent); }

.swap-arrow {
  display: grid; place-items: center; color: var(--accent);
  width: 44px; align-self: center;
}
.swap-arrow svg { width: 30px; height: 30px; }

/* The arrow points the wrong way once the panels stack, so it turns with
   them rather than being hidden — it is the only thing saying these two are
   a sequence and not a pair of alternatives. */
@media (max-width: 860px) {
  .swap { grid-template-columns: 1fr; }
  .swap-arrow { transform: rotate(90deg); width: auto; padding: 4px 0; }
}

.swap-caveat {
  margin-top: 26px; padding: 18px 22px;
  border-left: 3px solid var(--accent);
  background: rgba(247, 245, 241, 0.04);
  font-size: 14.5px; line-height: 1.7; color: var(--on-dark); max-width: 92ch;
}
.swap-caveat strong { color: var(--paper); }

/* ---- CTA variants ------------------------------------------------------
   See finalCta() in _build/partials.js for which page gets which and why. */

/* panel: the ask on the left, the two ways to reach a human on the right. */
.cta-panel { background: var(--asphalt); }
.cp-grid {
  display: grid; grid-template-columns: 1.05fr 0.95fr;
  gap: clamp(30px, 5vw, 72px); align-items: center;
}
@media (max-width: 900px) { .cp-grid { grid-template-columns: 1fr; gap: 28px; } }
.cp-lede { color: var(--on-dark); font-size: clamp(16px, 1.9vw, 18px); margin-top: 14px; max-width: 46ch; }

.cp-actions { display: grid; gap: 10px; }
.cp-action {
  display: flex; align-items: center; gap: 14px;
  padding: 16px 18px; text-decoration: none;
  background: rgba(247, 245, 241, 0.045);
  border: 1px solid var(--line); border-radius: var(--r-md);
  transition: border-color var(--t-base), background var(--t-base), transform var(--t-base) var(--ease-out);
}
.cp-action span { display: grid; gap: 3px; min-width: 0; }
.cp-k { font-size: 11px; font-weight: 600; letter-spacing: 0.12em; text-transform: uppercase; color: var(--on-dark-mute); }
.cp-v { font-size: clamp(17px, 1.9vw, 21px); font-weight: 700; color: var(--paper); letter-spacing: -0.01em; }
@media (hover: hover) {
  .cp-action:hover { border-color: rgba(214, 48, 60, 0.5); background: rgba(214, 48, 60, 0.09); transform: translateX(3px); }
  .cp-action:hover .icon-chip { border-color: rgba(214, 48, 60, 0.5); background: rgba(214, 48, 60, 0.14); color: var(--accent); }
}
.cp-note { font-size: 13px; color: var(--on-dark-mute); margin-top: 6px; line-height: 1.6; }

/* quiet: one line and one link, for the end of an article. */
.cta-quiet { background: var(--paper-2); }
.cq-inner {
  display: flex; align-items: center; justify-content: space-between;
  gap: clamp(20px, 3vw, 44px); flex-wrap: wrap;
  padding: clamp(24px, 3vw, 34px) clamp(24px, 3vw, 38px);
  background: var(--white); border: 1px solid var(--line-light);
  border-radius: var(--r-md); box-shadow: var(--shadow-1);
  border-left: 3px solid var(--accent);
}
.cq-inner h2 { font-size: clamp(20px, 2.4vw, 26px); margin-bottom: 8px; }
.cq-inner p { color: var(--slate); font-size: 15px; max-width: 58ch; line-height: 1.7; }
.cq-inner .btn { white-space: nowrap; }

/* ---- Blog post: a reading layout --------------------------------------
   The lead photo bleeds the full width and overlaps up into the hero, which
   is what makes the article feel like a piece rather than a page with a
   picture on it. */
.post-lead {
  position: relative; z-index: 2;
  max-width: 1100px; margin: -70px auto 0; padding-inline: var(--gutter);
}
.post-lead img {
  display: block; width: 100%; aspect-ratio: 16 / 9; object-fit: cover;
  border-radius: var(--r-lg); box-shadow: var(--shadow-3);
}
@media (max-width: 760px) { .post-lead { margin-top: -34px; } .post-lead img { aspect-ratio: 3 / 2; } }

.post-body-band { padding-top: clamp(40px, 5vw, 64px); }
.post-article { max-width: 720px; margin-inline: auto; }

/* 17px at a 68-character measure. The site's 15px body is right for tiles and
   tables and too small for 1,200 words of continuous prose. */
.prose-lg { max-width: none; }
.prose-lg p, .prose-lg li { font-size: 17px; line-height: 1.82; }
.prose-lg p { margin-bottom: 20px; }
.prose-lg h2 { font-size: clamp(24px, 3.2vw, 32px); margin: 52px 0 16px; }
.prose-lg h3 { font-size: 21px; margin: 32px 0 12px; }
/* First paragraph reads as a stand-first, so the article opens at a size that
   invites the read rather than starting flat. */
.prose-lg > p:first-of-type { font-size: 20px; line-height: 1.7; color: var(--slate); }

.post-tool {
  margin-top: 52px; padding: clamp(26px, 3vw, 36px);
  background: var(--asphalt); color: var(--paper);
  border-radius: var(--r-md); border-top: 3px solid var(--accent);
  display: grid; gap: 24px;
}
.post-tool h2 { font-size: clamp(20px, 2.4vw, 26px); color: var(--paper); margin-bottom: 10px; }
.post-tool p { font-size: 15px; color: var(--on-dark); line-height: 1.7; }
.pt-actions { display: flex; align-items: center; gap: 20px; flex-wrap: wrap; }
.pt-call { color: var(--on-dark-mute); font-size: 14px; text-decoration: none; }
@media (hover: hover) { .pt-call:hover { color: var(--paper); } }

.post-more { background: var(--paper-2); }
.post-more .section-head { margin-bottom: clamp(24px, 3vw, 36px); }

/* ---- Blog index: one grid, every post a peer ---------------------------
   The lead spans the full width with a landscape image; the rest sit beside
   each other underneath. Every card carries the same parts in the same order,
   so scanning is one movement rather than three different reading tasks. */
.post-grid {
  display: grid; grid-template-columns: repeat(2, 1fr);
  gap: clamp(18px, 2.2vw, 28px);
}
.post-card {
  display: flex; flex-direction: column; overflow: hidden;
  background: var(--white); border: 1px solid var(--line-light);
  border-radius: var(--r-md); box-shadow: var(--shadow-1);
  transition: transform var(--t-base) var(--ease-out), box-shadow var(--t-base), border-color var(--t-base);
}
.post-card.is-lead { grid-column: 1 / -1; flex-direction: row; align-items: stretch; }
@media (max-width: 900px) {
  .post-grid { grid-template-columns: 1fr; }
  .post-card.is-lead { flex-direction: column; }
}

.pc-media { display: block; overflow: hidden; background: var(--asphalt-2); }
/* <picture> is inline by default, so the <img> inside it sat on a text
   baseline and left a white gutter down the side of every card. */
.pc-media picture { display: block; height: 100%; }
.post-card.is-lead .pc-media { flex: 0 0 52%; }
.pc-media img { display: block; width: 100%; object-fit: cover; transition: transform 720ms var(--ease-out); }
/* Two different jobs, so two different rules rather than one with an
   override. In the grid the card's height follows its image, so the image
   sets the ratio. In the lead the image is a column beside the copy, so the
   card's height is already decided and the image fills it — an aspect-ratio
   there fights the stretch and leaves a white gutter around the photo. */
.post-card:not(.is-lead) .pc-media img { aspect-ratio: 16 / 10; height: auto; }
.post-card.is-lead .pc-media img { height: 100%; min-height: 340px; }

.pc-body { padding: clamp(20px, 2.4vw, 30px); display: flex; flex-direction: column; flex: 1; }
.post-card.is-lead .pc-body { justify-content: center; }
/* flex:1 on the excerpt is what pushes the CTA to the bottom of a grid card.
   In the lead the block is vertically centred instead, so the same rule just
   opens a hole between the paragraph and the link. */
.post-card.is-lead .excerpt { flex: 0 0 auto; }
.pc-body h3 { font-size: clamp(19px, 2.1vw, 24px); margin: 12px 0 10px; }
.post-card.is-lead .pc-body h3 { font-size: clamp(24px, 3vw, 34px); }
.pc-body h3 a { text-decoration: none; color: inherit; }
.pc-body .excerpt { font-size: 15px; color: var(--slate); line-height: 1.7; flex: 1; }
.pc-go {
  display: inline-flex; align-items: center; gap: 8px; margin-top: 18px;
  font-size: 12px; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--asphalt); transition: color var(--t-fast), gap var(--t-fast);
}
/* The whole card is the link target — the anchor sits on the heading and the
   media, and this is the affordance, so it must not read as a second button. */
@media (hover: hover) {
  .post-card:hover { transform: translateY(-4px); box-shadow: var(--shadow-lift); border-color: var(--line-light-2); }
  .post-card:hover .pc-media img { transform: scale(1.05); }
  .post-card:hover .pc-go { color: var(--accent); gap: 12px; }
}
.post-card:focus-within { border-color: var(--accent); }

/* ---- Dispatch vs DIY: verdict band + worked example --------------------- */
.verdict-band { background: var(--asphalt); color: var(--paper); }
.verdict {
  display: grid; grid-template-columns: 200px 1fr;
  gap: clamp(24px, 4vw, 60px); align-items: start;
}
@media (max-width: 820px) { .verdict { grid-template-columns: 1fr; gap: 18px; } }
.verdict-mark { padding-top: 8px; }
.verdict-lead {
  font-family: var(--font-display); font-weight: 700;
  font-size: clamp(21px, 2.6vw, 30px); line-height: 1.32; letter-spacing: -0.015em;
  color: var(--paper); margin-bottom: 20px;
}
.verdict-body p { color: var(--on-dark); font-size: clamp(15px, 1.8vw, 17px); line-height: 1.75; max-width: 66ch; }
.verdict-body p + p { margin-top: 14px; }
.verdict-p { color: var(--paper) !important; font-weight: 600; }

.math-split {
  display: grid; grid-template-columns: 1.05fr 0.95fr;
  gap: clamp(28px, 4.5vw, 66px); align-items: center;
}
@media (max-width: 900px) { .math-split { grid-template-columns: 1fr; gap: 30px; } }
.math-lede { color: var(--slate); font-size: clamp(16px, 1.9vw, 18px); margin-top: 14px; max-width: 48ch; }
.math-card {
  background: var(--asphalt); color: var(--paper);
  padding: clamp(24px, 2.8vw, 34px); border-radius: var(--r-md);
  border-top: 3px solid var(--accent); box-shadow: var(--shadow-2);
}
.mc-head { padding-bottom: 16px; border-bottom: 1px solid var(--line); margin-bottom: 18px; }

/* Both sides of the comparison the section asks you to run, side by side. */
.mc-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 1px; background: var(--line); }
.mc-col {
  display: grid; gap: 4px; padding: 16px 18px;
  background: var(--asphalt); font-variant-numeric: tabular-nums;
}
.mc-col.is-alt { background: rgba(214, 48, 60, 0.09); }
.mc-k { font-size: 10px; font-weight: 600; letter-spacing: 0.12em; text-transform: uppercase; color: var(--on-dark-mute); }
.mc-v { font-size: clamp(24px, 2.8vw, 32px); font-weight: 700; letter-spacing: -0.03em; color: var(--paper); }
.mc-sub { font-size: 12.5px; color: var(--on-dark-mute); }

/* The equation is the answer, so it gets the weight the old "$1,966" had. */
.mc-verdict { margin-top: 22px; padding-top: 20px; border-top: 1px solid var(--line); }
.mc-eq {
  display: flex; align-items: center; gap: clamp(10px, 1.6vw, 18px);
  flex-wrap: wrap; margin-bottom: 16px;
}
.mc-eq span { display: grid; }
.mc-eq b {
  font-size: clamp(19px, 2.2vw, 26px); font-weight: 700; letter-spacing: -0.02em;
  color: var(--paper); font-variant-numeric: tabular-nums;
}
.mc-eq i { font-style: normal; font-size: 10px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--on-dark-mute); }
.mc-eq em { font-style: normal; font-size: 18px; color: var(--on-dark-mute); }
.mc-eq .hot b { color: var(--accent-hi); }
.mc-verdict p { font-size: 14.5px; line-height: 1.72; color: var(--on-dark); }

.math-note { font-size: 12.5px; color: var(--on-dark-mute); line-height: 1.65; margin-top: 16px; }

/* Per-page hero plates.
   url() inside a custom property resolves against this stylesheet, not the
   page, which is why every one of these is written with ../img/ and why they
   live here rather than in an inline style.

   Only the freight photographs are in this list, and that is deliberate. The
   truck-* and equip-* shots all carry another carrier's identity in legible
   type — "Patterson Carriers LLC" with its MC and DOT numbers, "Chris Y
   Saulito Inc", an ITS Logistics trailer. At gallery-tile size under a caption
   that says "a carrier we dispatch" that is honest. Bled across a hero it
   reads as HHL's own truck and its own authority number, which it is not. */
.ph-img-flatbed    { --hero-img: url("../img/w/load-crated-flatbed-1600.jpg"); }
.ph-img-lowboy     { --hero-img: url("../img/w/load-silo-lowboy-1600.jpg"); }
.ph-img-reel       { --hero-img: url("../img/w/load-cable-reel-1600.jpg"); }
.ph-img-pipe       { --hero-img: url("../img/w/load-culvert-pipe-1600.jpg"); }
.ph-img-tanks      { --hero-img: url("../img/w/load-tanks-flatbed-1600.jpg"); }
@media (max-width: 860px) {
  .ph-img-flatbed   { --hero-img: url("../img/w/load-crated-flatbed-800.jpg"); }
  .ph-img-lowboy    { --hero-img: url("../img/w/load-silo-lowboy-800.jpg"); }
  .ph-img-reel      { --hero-img: url("../img/w/load-cable-reel-800.jpg"); }
  .ph-img-pipe      { --hero-img: url("../img/w/load-culvert-pipe-800.jpg"); }
  .ph-img-tanks     { --hero-img: url("../img/w/load-tanks-flatbed-800.jpg"); }
}

/* Lift the content above the photo layer — but not the photo layer itself.
   `.page-hero > *` outranks `.hero-photo` on specificity, so without the
   :not() it forced the background layer from absolute back to relative,
   collapsing it to zero height with no error anywhere. */
.page-hero > *:not(.hero-photo) { position: relative; z-index: 1; }

.crumbs {
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--on-dark-mute); margin-bottom: 22px;
}
.crumbs ol { list-style: none; display: flex; flex-wrap: wrap; gap: 9px; }
.crumbs li + li::before { content: '/'; margin-right: 9px; opacity: 0.45; }
.crumbs a { text-decoration: none; }
@media (hover: hover) { .crumbs a:hover { color: var(--accent); } }

/* == 10 Recap card ======================================================== */
.recap-card {
  position: relative;
  background: linear-gradient(165deg, rgba(24, 33, 58, 0.96), rgba(14, 20, 36, 0.96));
  border: 1px solid var(--line);
  padding: 30px;
  box-shadow: var(--shadow-3);
  backdrop-filter: blur(6px);
}
.recap-card::before {
  content: attr(data-label); position: absolute; top: -11px; left: 26px;
  background: var(--accent); color: var(--white); font-family: var(--font-mono);
  font-size: 10px; font-weight: 600; letter-spacing: 0.12em; padding: 5px 11px;
}
/* Chrome edge along the top, echoing the logo's metal. */
.recap-card::after {
  content: ''; position: absolute; left: 0; right: 0; top: 0; height: 2px;
  background: var(--chrome-grad); opacity: 0.5;
}
.recap-top {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: 12px; flex-wrap: wrap; margin-top: 10px;
  padding-bottom: 20px; border-bottom: 1px solid var(--line);
}
.recap-name { font-family: var(--font-display); font-size: 21px; font-weight: 600; letter-spacing: 0.01em; }
.recap-week { font-family: var(--font-mono); font-size: 12px; color: var(--on-dark-mute); }
.recap-metrics { display: grid; grid-template-columns: repeat(3, 1fr); gap: 18px; padding: 24px 0; }
.metric-label {
  font-family: var(--font-mono); font-size: 10px; text-transform: uppercase;
  letter-spacing: 0.1em; color: var(--on-dark-mute); margin-bottom: 8px;
}
.metric-value {
  font-family: var(--font-mono); font-size: clamp(23px, 3vw, 29px);
  font-weight: 600; color: var(--chrome); font-variant-numeric: tabular-nums;
}
.metric-value.good { color: var(--signal); }
.recap-bar-row { display: flex; align-items: center; gap: 12px; padding: 7px 0; }
.recap-bar-label { font-family: var(--font-mono); font-size: 10px; color: var(--on-dark-mute); width: 42px; flex: none; }
.recap-bar-track { flex: 1; height: 6px; background: rgba(247, 245, 241, 0.09); overflow: hidden; }
.recap-bar-fill {
  height: 100%; background: var(--accent-grad); width: 0;
  transition: width 1100ms var(--ease-out); transition-delay: var(--bar-delay, 0ms);
}
[data-bars].is-in .recap-bar-fill, .recap-card.is-in .recap-bar-fill { width: var(--bar-w, 0%); }
html:not(.js-on) .recap-bar-fill { width: var(--bar-w, 0%); }
.recap-foot {
  margin-top: 20px; padding-top: 18px; border-top: 1px solid var(--line);
  font-size: 13px; color: var(--on-dark-mute);
}

/* == 11 Trust strip ======================================================= */
.trust-strip { background: var(--paper); padding: 22px 0; border-bottom: 1px solid var(--line-light); }
.trust-row {
  display: flex; justify-content: space-between; flex-wrap: wrap; gap: 14px 28px;
  font-family: var(--font-mono); font-size: 12px; color: var(--slate);
  letter-spacing: 0.05em; text-transform: uppercase; list-style: none;
}
.trust-row li { display: flex; align-items: center; gap: 9px; }
.trust-row li::before {
  content: ''; width: 6px; height: 6px; background: var(--accent);
  transform: rotate(45deg); flex: none;
}

/* == 12 Sections ========================================================== */
/* Mile-marker divider between major bands. */
.mm-divider {
  display: flex; align-items: center; gap: 16px;
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--slate); margin-bottom: 26px;
}
.mm-divider::before, .mm-divider::after { content: ''; height: 1px; background: var(--line-light); flex: 1; }

/* == 13 Services ========================================================== */
.services-grid {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 1px;
  background: var(--line-light); border: 1px solid var(--line-light);
}
.service-tile {
  position: relative; background: var(--paper); padding: 34px 28px;
  transition: background var(--t-base), transform var(--t-base) var(--ease-out);
  overflow: hidden;
}
.service-tile::before {
  content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 3px;
  background: var(--accent); transform: scaleY(0); transform-origin: top;
  transition: transform var(--t-base) var(--ease-out);
}
@media (hover: hover) {
  .service-tile:hover { background: var(--white); }
  .service-tile:hover::before { transform: scaleY(1); }
}
.service-tile .num { font-family: var(--font-mono); font-size: 12px; color: var(--accent); margin-bottom: 16px; display: block; }
.service-tile h3 { font-size: 25px; margin-bottom: 10px; }
.service-tile p { font-size: 14px; color: var(--slate); }
@media (max-width: 900px) { .services-grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 560px) { .services-grid { grid-template-columns: 1fr; } }

/* Detailed equipment cards with photography */
.svc-detail { display: grid; grid-template-columns: repeat(2, 1fr); gap: 26px; }
.svc-block {
  background: var(--white); border: 1px solid var(--line-light);
  box-shadow: var(--shadow-1); overflow: hidden;
  transition: box-shadow var(--t-base), transform var(--t-base) var(--ease-out);
  display: flex; flex-direction: column;
}
@media (hover: hover) { .svc-block:hover { box-shadow: var(--shadow-2); transform: translateY(-3px); } }
.svc-media { position: relative; aspect-ratio: 16 / 9; overflow: hidden; background: var(--asphalt-2); }
.svc-media img { width: 100%; height: 100%; object-fit: cover; transition: transform 700ms var(--ease-out); }
@media (hover: hover) { .svc-block:hover .svc-media img { transform: scale(1.05); } }
.svc-media .num {
  position: absolute; top: 0; left: 0; z-index: 1;
  background: var(--accent); color: var(--white);
  font-family: var(--font-mono); font-size: 12px; padding: 7px 12px;
}
.svc-body { padding: 28px; flex: 1; display: flex; flex-direction: column; }
.svc-block h3 { font-size: 26px; margin-bottom: 12px; }
.svc-block p { font-size: 14px; color: var(--slate); margin-bottom: 16px; }
.svc-block ul { list-style: none; font-size: 14px; margin-top: auto; }
.svc-block li { padding: 7px 0 7px 24px; position: relative; color: var(--asphalt); }
.svc-block li::before {
  content: ''; position: absolute; left: 0; top: 15px;
  width: 8px; height: 2px; background: var(--accent);
}
.svc-block a { color: var(--asphalt); font-weight: 600; }
@media (max-width: 760px) { .svc-detail { grid-template-columns: 1fr; } }

/* == 14 Process (mile markers) ============================================ */
/* == Band rhythm ==========================================================
   Dark bands are punctuation. The rule this file now follows: never more
   than two dark bands in a row, and never a dark run longer than about two
   viewport heights. Measured before the change, the homepage ran 5,735px of
   unbroken navy across three sections and every interior page was
   dark → light → dark → dark. That is what made the site feel heavy and
   samey regardless of what was inside the sections.
   ======================================================================== */
.process-strip { background: var(--asphalt); color: var(--paper); }

/* Light variant: the band is paper, the tiles inside stay dark. The section
   keeps its visual weight, but the page gets a breath between dark runs. */
.process-strip.is-light { background: var(--paper-2); color: var(--asphalt); }
.process-strip.is-light .section-head h2 { color: var(--asphalt); }
.process-strip.is-light .section-head p { color: var(--slate); }
.process-strip.is-light .pin-dots span { background: rgba(18, 26, 44, 0.16); }
.process-strip.is-light .pin-dots span.on { background: var(--accent); }
.process-strip.is-light .pin-progress { background: rgba(18, 26, 44, 0.10); }
.mile-list { display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(26px, 3vw, 40px); }
.mile { position: relative; border-left: 2px solid var(--accent); padding-left: 24px; }
.mile::before {
  content: ''; position: absolute; left: -6px; top: 0;
  width: 10px; height: 10px; background: var(--accent); transform: rotate(45deg);
}
.mile .mm { font-family: var(--font-mono); font-size: 13px; color: var(--accent); display: block; margin-bottom: 12px; letter-spacing: 0.08em; }
.mile h3 { font-size: 23px; margin-bottom: 10px; color: var(--paper); }
.mile p { font-size: 14px; color: var(--on-dark); }
@media (max-width: 760px) { .mile-list { grid-template-columns: 1fr; } }

.steps-inline { display: grid; grid-template-columns: repeat(3, 1fr); gap: 30px; margin-top: 52px; }
.steps-inline .mile h3 { color: var(--asphalt); font-size: 20px; }
.steps-inline .mile p { color: var(--slate); }
@media (max-width: 760px) { .steps-inline { grid-template-columns: 1fr; } }

/* == 15 Load board ======================================================== */
.board-band { background: var(--asphalt); color: var(--paper); }
/* Full width, not capped at 960. The board is the evidence for the section's
   claim, and a table of six columns squeezed into two thirds of the page while
   the last third sits empty made it look like a widget rather than the point. */
.board {
  background: linear-gradient(165deg, rgba(24, 33, 58, 0.96), rgba(14, 20, 36, 0.98));
  border: 1px solid var(--line); box-shadow: var(--shadow-3);
}
.board-head { display: flex; justify-content: space-between; align-items: center; gap: 12px; padding: 16px 22px; border-bottom: 1px solid var(--line); }
.board-title { font-family: var(--font-mono); font-size: 12px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--on-dark-mute); }
/* Replaces the pulsing "LIVE FEED" badge. Nothing here is live, and a site
   whose argument is "we show you the real numbers" should not lead with a fake
   telemetry light. What a carrier actually needs to know to read the table is
   whose truck it is scored against, so that is what the slot says now. */
.board-scope {
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.06em;
  color: var(--on-dark-mute); text-align: right;
}
.board-scan {
  display: flex; align-items: center; gap: 12px; padding: 11px 22px;
  border-bottom: 1px solid var(--line);
  font-family: var(--font-mono); font-size: 11px; color: var(--on-dark-mute); letter-spacing: 0.06em;
}
.scan-bar { flex: 1; height: 3px; background: rgba(247, 245, 241, 0.08); position: relative; overflow: hidden; }
.scan-bar::after {
  content: ''; position: absolute; top: 0; left: -30%; width: 30%; height: 100%;
  background: linear-gradient(90deg, transparent, var(--accent), transparent);
  animation: scan 2.6s linear infinite;
}
@keyframes scan { to { left: 110%; } }
.board-row {
  display: grid;
  grid-template-columns: 34px minmax(0, 1.9fr) 0.62fr 0.42fr 0.62fr 0.72fr;
  gap: 14px; align-items: center;
  padding: 14px 22px; border-bottom: 1px solid var(--line-soft); font-size: 14px;
}
.board-row:last-of-type { border-bottom: 0; }
.board-row.head {
  font-family: var(--font-mono); font-size: 10px; text-transform: uppercase;
  letter-spacing: 0.1em; color: var(--on-dark-mute); padding: 11px 22px;
  border-bottom: 1px solid var(--line);
}
/* Figures right-aligned so the decimal points line up down the column. A rate
   table where $2.74 and $2.96 do not stack is harder to compare than it needs
   to be, which is the one job this table has. */
.board-row > *:nth-child(n+3) { text-align: right; font-variant-numeric: tabular-nums; }

.board-rank { font-family: var(--font-mono); font-size: 11px; color: rgba(247, 245, 241, 0.34); }
.board-lane { font-weight: 600; color: var(--paper); display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
/* Deliberately not colour-coded by value. Green on the highest rate said
   "this is the good one" about the $3.12 Laredo load, while the row beneath
   the table explains at length that it is the wrong load. Two signals
   disagreeing is worse than one signal: the accent bar and the AI PICK badge
   carry the recommendation, and the rate column just reports the rate. */
.board-rpm { font-family: var(--font-mono); font-weight: 600; color: var(--chrome); }
.board-net { font-weight: 700; color: var(--paper); }

/* The recommended row carries a left accent rather than a filled background:
   at 5 rows a filled row reads as "selected", and this is a recommendation. */
.board-row.is-pick { position: relative; background: rgba(214, 48, 60, 0.07); }
.board-row.is-pick::before {
  content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 3px; background: var(--accent);
}
.board-row.is-pick .board-rank { color: var(--accent); }

.ai-pick {
  font-family: var(--font-mono); font-size: 9px; background: var(--accent); color: var(--white);
  padding: 3px 8px; letter-spacing: 0.08em; display: inline-block;
}

/* The reasoning behind the pick. This is the part that turns an assertion into
   a demonstration, so it gets its own band inside the board rather than being
   folded into the footnote. */
.board-why {
  padding: 20px 22px; border-top: 1px solid var(--line);
  background: rgba(247, 245, 241, 0.035);
}
.board-why p { font-size: 14.5px; line-height: 1.72; color: var(--on-dark); max-width: 88ch; }
.board-note { padding: 14px 22px; font-size: 12.5px; color: var(--on-dark-mute); border-top: 1px solid var(--line-soft); }

@media (max-width: 760px) {
  .board-row { grid-template-columns: 26px minmax(0, 1.5fr) 0.7fr 0.8fr; gap: 10px; padding: 13px 16px; font-size: 13px; }
  .board-row .hide-m { display: none; }
  .board-head, .board-scan, .board-why, .board-note { padding-inline: 16px; }
  .board-head { flex-direction: column; align-items: flex-start; gap: 6px; }
  .board-scope { text-align: left; }
}

/* == 16 Gallery + video =================================================== */
.gallery-band { background: var(--asphalt); }
.gal-grid { display: grid; grid-template-columns: repeat(4, 1fr); grid-auto-rows: 200px; gap: 14px; }
.gal-item { position: relative; overflow: hidden; border: 1px solid var(--line); margin: 0; }
.gal-item img { width: 100%; height: 100%; object-fit: cover; transition: transform 720ms var(--ease-out); }
@media (hover: hover) { .gal-item:hover img { transform: scale(1.06); } }
.gal-item.wide { grid-column: span 2; }
.gal-item.tall { grid-row: span 2; }
.gal-item.hero-tile { grid-column: span 2; grid-row: span 2; }
.gal-cap {
  position: absolute; left: 0; right: 0; bottom: 0; padding: 30px 16px 12px;
  background: linear-gradient(transparent, rgba(8, 12, 22, 0.9));
  color: var(--paper); font-family: var(--font-mono); font-size: 11px;
  letter-spacing: 0.07em; text-transform: uppercase;
  transform: translateY(6px); opacity: 0.9;
  transition: transform var(--t-base) var(--ease-out), opacity var(--t-base);
}
@media (hover: hover) { .gal-item:hover .gal-cap { transform: translateY(0); opacity: 1; } }
@media (max-width: 900px) { .gal-grid { grid-template-columns: repeat(2, 1fr); grid-auto-rows: 168px; } .gal-item.hero-tile { grid-column: span 2; } }
@media (max-width: 560px) { .gal-grid { grid-auto-rows: 140px; } }

.reviews-band { background: var(--paper); }

/* These are 9:16 phone videos. Making one "featured" by giving it more columns
   also makes it proportionally taller, so it hung far below the others and the
   row read as broken rather than hierarchical. Portrait media wants a set of
   equals, so all five share a row and the positioning statement gets its own
   full-width band underneath. */
/* Head and claim share a row, so the statement about paid badges sits beside
   the promise instead of being tacked on under the videos, and the right half
   of the band stops being empty. */
.head-split {
  display: grid; grid-template-columns: 1.1fr 0.9fr;
  gap: clamp(28px, 4vw, 60px); align-items: end;
  margin-bottom: clamp(28px, 3.4vw, 42px);
}
@media (max-width: 900px) { .head-split { grid-template-columns: 1fr; gap: 26px; align-items: start; } }
.head-lede { color: var(--slate); font-size: clamp(16px, 1.9vw, 18px); margin-top: 14px; max-width: 46ch; }
.claim-card {
  background: var(--asphalt); color: var(--paper);
  padding: clamp(22px, 2.4vw, 28px); border-radius: var(--r-md);
  border-top: 3px solid var(--accent);
}
.claim-card p { font-size: 14.5px; color: var(--on-dark); line-height: 1.7; margin-bottom: 16px; }
.claim-card .tool-link { color: var(--paper); }
@media (hover: hover) { .claim-card .tool-link:hover { color: var(--accent-hi); } }

/* Five 9:16 videos. They were in a 5-column grid that a stray `.narrow`
   utility (max-width: 800px) was capping, so each frame came out 144px wide:
   a phone video shrunk to a postage stamp with a caption printed over the
   speaker's own burned-in subtitles.

   Now the caption lives under the frame, and the rail is a snap row at every
   width — same gesture on a desktop trackpad as on a phone, and it grows with
   the video count instead of dividing a fixed width by it. */
.review-rail {
  display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr);
  gap: 16px; align-items: start;
}
.short-card { display: block; }
.short-card .short-frame { position: relative; }
.short-card .sc-n {
  position: absolute; z-index: 2; top: 10px; left: 10px;
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.08em;
  font-variant-numeric: tabular-nums; color: var(--paper);
  background: rgba(8, 12, 22, 0.62); padding: 4px 8px; border-radius: var(--r-pill);
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
}

/* Below the point where five across stops being legible, scroll instead of
   shrink. A 9:16 video under about 180px wide shows nothing useful. */
@media (max-width: 1080px) {
  .review-rail {
    display: flex; overflow-x: auto; scroll-snap-type: x mandatory;
    margin-inline: calc(var(--gutter) * -1);
    padding-inline: var(--gutter); scroll-padding-inline: var(--gutter);
    scrollbar-width: none;
  }
  .review-rail::-webkit-scrollbar { display: none; }
  .short-card { flex: 0 0 clamp(190px, 44vw, 240px); scroll-snap-align: start; }
}


.shorts-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(196px, 1fr)); gap: 18px; }
.short-frame {
  aspect-ratio: 9 / 16; border: 1px solid var(--line-light-2); background: var(--asphalt);
  overflow: hidden; box-shadow: var(--shadow-1);
  transition: box-shadow var(--t-base), transform var(--t-base) var(--ease-out);
}
@media (hover: hover) { .short-frame:hover { box-shadow: var(--shadow-2); transform: translateY(-3px); } }
.short-frame iframe { width: 100%; height: 100%; border: 0; }

/* YouTube facade: nothing loads from youtube.com until the visitor clicks. */
.yt-facade {
  position: relative; width: 100%; height: 100%;
  border: 0; padding: 0; margin: 0; cursor: pointer;
  background: var(--asphalt-2) center / cover no-repeat;
  display: flex; align-items: center; justify-content: center;
}
.yt-facade::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(10, 14, 24, 0.12), rgba(10, 14, 24, 0.62));
}
.yt-facade .play {
  position: relative; z-index: 1; width: 62px; height: 62px; border-radius: 50%;
  background: var(--accent); display: flex; align-items: center; justify-content: center;
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.4);
  transition: transform var(--t-base) var(--ease-out);
}
.yt-facade .play::after {
  content: ''; border-style: solid; border-width: 10px 0 10px 17px;
  border-color: transparent transparent transparent var(--white); margin-left: 4px;
}
@media (hover: hover) { .yt-facade:hover .play { transform: scale(1.12); } }

/* == 17 Calculators ======================================================= */
.tools-band { background: var(--paper-2); }
.tools-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px; }
.tool-card {
  background: var(--paper); border: 1px solid var(--line-light); padding: 28px;
  display: flex; flex-direction: column; position: relative; overflow: hidden;
  transition: transform var(--t-base) var(--ease-out), box-shadow var(--t-base);
}
.tool-card::before {
  content: ''; position: absolute; inset: 0 0 auto 0; height: 3px;
  background: var(--accent-grad); transform: scaleX(0); transform-origin: left;
  transition: transform var(--t-base) var(--ease-out);
}
@media (hover: hover) {
  .tool-card:hover { transform: translateY(-4px); box-shadow: var(--shadow-2); }
  .tool-card:hover::before { transform: scaleX(1); }
}
.tool-card .tool-ico { color: var(--accent); margin-bottom: 16px; }
.tool-card h3 { font-size: 23px; margin-bottom: 10px; }
.tool-card p { font-size: 14px; color: var(--slate); margin-bottom: 20px; flex: 1; }
@media (max-width: 900px) { .tools-grid { grid-template-columns: 1fr; } }

/* Calculators as one column of full-width rows.
   As a four-across grid every blurb was squeezed into the same narrow box and
   four different tools read as one undifferentiated set. A row gives each its
   number, icon, name, a sentence with room, and its own action. */
.tools-stack { display: grid; gap: 12px; }
.tool-line {
  display: grid; grid-template-columns: auto auto 1fr auto;
  align-items: center; gap: clamp(16px, 2.4vw, 30px);
  padding: clamp(18px, 2.2vw, 26px) clamp(20px, 2.6vw, 32px);
  background: var(--paper); border: 1px solid var(--line-light);
  text-decoration: none; color: inherit; position: relative; overflow: hidden;
  transition: transform var(--t-base) var(--ease-out), box-shadow var(--t-base), border-color var(--t-base);
}
/* The accent wipes in from the left edge on hover, the same gesture the tool
   cards used, kept so the interaction language does not change. */
.tool-line::before {
  content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 3px;
  background: var(--accent-grad); transform: scaleY(0); transform-origin: top;
  transition: transform var(--t-base) var(--ease-out);
}
@media (hover: hover) {
  .tool-line:hover { box-shadow: var(--shadow-2); border-color: var(--line-light-2); transform: translateX(3px); }
  .tool-line:hover::before { transform: scaleY(1); }
  .tool-line:hover .tl-cta { color: var(--accent); gap: 12px; }
}
.tl-num {
  font-family: var(--font-mono); font-size: 12px; letter-spacing: 0.1em;
  color: var(--line-light-2); font-variant-numeric: tabular-nums;
}
.tl-ico { color: var(--accent); display: grid; place-items: center; }
.tl-h { display: block; font-family: var(--font-display); font-weight: 700; font-size: clamp(19px, 2vw, 23px); letter-spacing: -0.01em; }
.tl-p { display: block; font-size: 14px; color: var(--slate); margin-top: 5px; max-width: 62ch; line-height: 1.6; }
.tl-cta {
  display: inline-flex; align-items: center; gap: 8px; white-space: nowrap;
  font-size: 13px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--asphalt); transition: color var(--t-fast), gap var(--t-fast);
}
/* Below tablet the arrow label is redundant — the whole row is the link — and
   the number column is decoration that costs width the sentence needs. */
@media (max-width: 760px) {
  .tool-line { grid-template-columns: auto 1fr; gap: 14px; }
  .tl-num, .tl-cta { display: none; }
}

.calc-card {
  background: var(--white); border: 1px solid var(--line-light);
  padding: clamp(22px, 4vw, 38px); box-shadow: var(--shadow-1);
}
.calc-grid { display: grid; grid-template-columns: 1.05fr 0.95fr; gap: 34px; align-items: start; }
@media (max-width: 860px) { .calc-grid { grid-template-columns: 1fr; } }

.calc-fields { display: grid; gap: 18px; }
.calc-fields .row-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
@media (max-width: 520px) { .calc-fields .row-2 { grid-template-columns: 1fr; } }

.calc-result {
  background: var(--asphalt); color: var(--paper); padding: 30px;
  position: sticky; top: calc(var(--header-h) + 20px);
  border-top: 3px solid var(--accent);
}
@media (max-width: 860px) { .calc-result { position: static; } }
.calc-result .lbl {
  font-family: var(--font-mono); font-size: 10px; text-transform: uppercase;
  letter-spacing: 0.12em; color: var(--on-dark-mute); margin-bottom: 8px;
}
.calc-result .big {
  font-family: var(--font-mono); font-size: clamp(36px, 6.5vw, 50px);
  font-weight: 600; color: var(--accent); line-height: 1; font-variant-numeric: tabular-nums;
}
.calc-result .big.pos { color: var(--signal); }
.calc-result .big.neg { color: var(--accent-hi); }
.calc-break { margin-top: 22px; border-top: 1px solid var(--line); padding-top: 16px; font-size: 13px; }
.calc-break div { display: flex; justify-content: space-between; gap: 14px; padding: 6px 0; color: var(--on-dark); }
.calc-break .mono { color: var(--paper); }
.calc-break .total { border-top: 1px solid var(--line); margin-top: 10px; padding-top: 12px; font-weight: 600; }
.calc-break .mono.pos { color: var(--signal); }
.calc-break .mono.neg { color: #FF7A82; }
.calc-verdict {
  margin-top: 20px; padding: 14px 16px; border-left: 3px solid var(--signal);
  background: rgba(46, 158, 109, 0.12); font-size: 13px; color: var(--paper);
}
.calc-verdict.bad { border-left-color: var(--accent-hi); background: rgba(240, 69, 82, 0.14); }
.calc-verdict.warn { border-left-color: var(--warn); background: rgba(232, 163, 61, 0.14); }

/* IFTA state rows */
.ifta-table { width: 100%; border-collapse: collapse; font-size: 14px; }
.ifta-table th {
  text-align: left; font-family: var(--font-mono); font-size: 10px;
  text-transform: uppercase; letter-spacing: 0.08em; color: var(--slate);
  padding: 10px 8px; border-bottom: 1px solid var(--line-light);
  font-weight: 600;
}
.ifta-table td { padding: 7px 8px; border-bottom: 1px solid var(--line-light); vertical-align: middle; }
.ifta-table input, .ifta-table select { padding: 9px 10px; font-size: 14px; }
.ifta-table .col-num { width: 22%; }
.ifta-table .col-act { width: 44px; }
.row-remove {
  background: none; border: 1px solid var(--line-light-2); color: var(--slate);
  width: 32px; height: 32px; cursor: pointer; font-size: 18px; line-height: 1;
  transition: border-color var(--t-fast), color var(--t-fast);
}
@media (hover: hover) { .row-remove:hover { border-color: var(--accent); color: var(--accent); } }
.ifta-scroll { overflow-x: auto; }

/* HOS clocks */
.hos-clocks { display: grid; grid-template-columns: repeat(3, 1fr); gap: 18px; margin-top: 8px; }
@media (max-width: 620px) { .hos-clocks { grid-template-columns: 1fr; } }
.hos-clock { background: rgba(247, 245, 241, 0.05); border: 1px solid var(--line); padding: 18px; }
.hos-clock .k { font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--on-dark-mute); margin-bottom: 10px; }
.hos-clock .v { font-family: var(--font-mono); font-size: 26px; font-weight: 600; color: var(--chrome); }
.hos-clock .track { height: 5px; background: rgba(247, 245, 241, 0.1); margin-top: 12px; overflow: hidden; }
.hos-clock .fill { height: 100%; background: var(--signal); transition: width var(--t-base) var(--ease-out); }
.hos-clock .fill.warn { background: var(--warn); }
.hos-clock .fill.over { background: var(--accent-hi); }

/* == 18 Forms ============================================================= */
.form-card {
  background: var(--white); border: 1px solid var(--line-light);
  padding: clamp(22px, 4vw, 40px); max-width: 820px; box-shadow: var(--shadow-1);
}
.form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
.form-grid .full { grid-column: 1 / -1; }
@media (max-width: 640px) { .form-grid { grid-template-columns: 1fr; } }

label {
  display: block; font-family: var(--font-mono); font-size: 10px;
  text-transform: uppercase; letter-spacing: 0.1em; color: var(--slate); margin-bottom: 8px;
}
.req { color: var(--accent); }

input, select, textarea {
  width: 100%; padding: 14px 15px;
  border: 1px solid var(--line-light-2); background: var(--white);
  font-family: var(--font-body); font-size: 16px; /* 16px keeps iOS from zooming */
  color: var(--asphalt); border-radius: 0;
  transition: border-color var(--t-fast), box-shadow var(--t-fast);
}
textarea { resize: vertical; min-height: 100px; }
select {
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8'%3E%3Cpath fill='none' stroke='%2359626E' stroke-width='1.6' d='M1 1.5l5 5 5-5'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 15px center; padding-right: 40px;
}
/* One focus indicator, not three. The field's own border turns accent and a
   single 3px ring sits flush against it — no offset outline on top.
   `outline: 2px solid transparent` is not decorative: Windows High Contrast
   mode discards box-shadow but forces transparent outlines to a system
   colour, so the ring survives where the shadow would not. */
input:focus-visible, select:focus-visible, textarea:focus-visible {
  outline: 2px solid transparent;
  outline-offset: 0;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(214, 48, 60, 0.30);
}
input:focus, select:focus, textarea:focus { border-color: var(--accent); }
input:disabled, select:disabled, textarea:disabled, button:disabled { opacity: 0.55; cursor: not-allowed; }
input[type='number'] { font-family: var(--font-mono); }

input[aria-invalid='true'], select[aria-invalid='true'], textarea[aria-invalid='true'] {
  border-color: var(--accent); background: #FDF4F5;
}
.field-error {
  display: none; font-family: var(--font-mono); font-size: 11px;
  color: var(--accent-lo); margin-top: 7px; text-transform: none; letter-spacing: 0;
}
.field-error.show { display: block; }
.field-hint { font-size: 12px; color: var(--slate); margin-top: 7px; }

.form-note { font-size: 13px; color: var(--slate); margin-top: 16px; line-height: 1.65; }
.consent { display: flex; align-items: flex-start; gap: 12px; margin-top: 20px; }
.consent input { width: auto; flex: none; margin-top: 4px; accent-color: var(--accent); }
.consent label {
  text-transform: none; font-family: var(--font-body); font-size: 13px;
  letter-spacing: 0; color: var(--slate); margin-bottom: 0; line-height: 1.6;
}
.consent a { color: var(--asphalt); font-weight: 600; }

/* Honeypot: off-screen for people, inviting to naive bots. */
.hp-field { position: absolute !important; left: -9999px !important; width: 1px; height: 1px; overflow: hidden; }

.form-status {
  display: none; margin-top: 24px; padding: 18px 20px;
  border-left: 3px solid var(--signal); background: #EEF7F2;
  font-size: 14px; color: var(--asphalt); line-height: 1.6;
}
.form-status.error { border-left-color: var(--accent); background: #FDF4F5; }
.form-status.warn { border-left-color: var(--warn); background: #FDF7EC; }
.form-status.show { display: block; animation: statusIn 380ms var(--ease-out); }
@keyframes statusIn { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: none; } }
.form-status strong {
  display: block; font-family: var(--font-display); text-transform: uppercase;
  font-size: 19px; margin-bottom: 5px; letter-spacing: 0.01em;
}

/* Submit button spinner */
.btn .spin {
  width: 15px; height: 15px; border: 2px solid rgba(255, 255, 255, 0.35);
  border-top-color: var(--white); border-radius: 50%; animation: spin 700ms linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* == 19 Blog ============================================================== */
.post-meta {
  font-family: var(--font-mono); font-size: 11px; text-transform: uppercase;
  letter-spacing: 0.08em; color: var(--slate); margin-bottom: 14px;
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
}
.post-meta .tag { background: var(--accent); color: var(--white); padding: 4px 9px; }
.post-body {
  font-size: 15px; color: var(--asphalt); line-height: 1.8;
  border-top: 1px solid var(--line-light); padding-top: 20px; margin-top: 18px;
}
.post-body p { margin-bottom: 15px; }
.post-body p:last-child { margin-bottom: 0; }
.post-body a { color: var(--accent-lo); font-weight: 600; }
.read-more {
  font-family: var(--font-mono); font-size: 12px; text-transform: uppercase;
  letter-spacing: 0.07em; color: var(--asphalt); background: none; border: none;
  border-bottom: 2px solid var(--accent); cursor: pointer; padding: 0 0 3px;
  transition: color var(--t-fast);
}
@media (hover: hover) { .read-more:hover { color: var(--accent); } }

/* Previous / next links at the foot of a blog post. */
.post-nav {
  display: grid; grid-template-columns: 1fr 1fr; gap: 16px;
  margin-top: 48px; padding-top: 32px; border-top: 1px solid var(--line-light);
}
@media (max-width: 620px) { .post-nav { grid-template-columns: 1fr; } }
.post-nav-link {
  display: block; padding: 20px 22px; text-decoration: none;
  border: 1px solid var(--line-light); border-radius: var(--r-md);
  background: var(--white);
  transition: border-color var(--t-fast), transform var(--t-base) var(--ease-out), box-shadow var(--t-base);
}
@media (hover: hover) {
  .post-nav-link:hover { border-color: var(--accent); transform: translateY(-2px); box-shadow: var(--shadow-2); }
}
.post-nav-link.align-end { text-align: right; }
.post-nav-link .k {
  display: block; font-size: 10px; font-weight: 600; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--accent); margin-bottom: 8px;
}
.post-nav-link .t { display: block; font-size: 15px; font-weight: 600; color: var(--asphalt); line-height: 1.45; }

/* Two-column comparison. Deliberately not a feature table with ticks down our
   column and crosses down theirs: both sides carry real advantages and real
   costs, and a comparison nobody believes persuades nobody. */
/* stretch, not start: one column has more points than the other, and two
   cards ending at different heights read as an unfinished layout rather than
   as an argument with two sides. */
.compare { display: grid; grid-template-columns: 1fr 1fr; gap: 22px; align-items: stretch; }
@media (max-width: 860px) { .compare { grid-template-columns: 1fr; } }
.compare-col {
  background: var(--white); border: 1px solid var(--line-light);
  border-radius: var(--r-lg); padding: clamp(24px, 3vw, 34px);
  box-shadow: var(--shadow-1);
}
.compare-col.featured { border-color: var(--accent); box-shadow: var(--shadow-2); }
.compare-head { padding-bottom: 20px; margin-bottom: 20px; border-bottom: 1px solid var(--line-light); }
.compare-head h2 { font-size: clamp(22px, 2.6vw, 28px); }
.compare-list { list-style: none; display: grid; gap: 2px; }
.compare-list li {
  position: relative; padding: 13px 0 13px 34px;
  font-size: 15px; line-height: 1.6; color: var(--asphalt);
  border-top: 1px solid var(--line-light);
}
.compare-list li:first-child { border-top: 0; }
.compare-list li::before {
  content: ''; position: absolute; left: 2px; top: 18px;
  width: 16px; height: 16px; border-radius: 50%;
}
.compare-list li.good::before {
  background: rgba(46, 158, 109, 0.14);
  box-shadow: inset 0 0 0 2px var(--signal);
}
.compare-list li.bad::before {
  background: rgba(214, 48, 60, 0.12);
  box-shadow: inset 0 0 0 2px var(--accent);
}
/* A dot alone does not carry meaning for a screen reader, so name it. */
.compare-list li.good::after,
.compare-list li.bad::after {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap;
}
.compare-list li.good::after { content: 'Advantage: '; }
.compare-list li.bad::after { content: 'Cost: '; }

/* == 20 Long-form / legal ================================================= */
.prose { max-width: 780px; }
.prose h2 { font-size: clamp(22px, 3vw, 30px); margin: 44px 0 14px; }
.prose h2:first-child { margin-top: 0; }
.prose h3 { font-size: 19px; margin: 26px 0 10px; }
.prose p, .prose li { font-size: 15px; line-height: 1.8; color: var(--asphalt); }
.prose p { margin-bottom: 15px; }
.prose ul, .prose ol { margin: 0 0 18px 22px; }
.prose li { margin-bottom: 9px; }
.prose a { color: var(--accent-lo); font-weight: 600; }
.legal-note {
  border-left: 3px solid var(--accent); background: var(--paper-2);
  padding: 18px 20px; font-size: 14px; color: var(--slate); margin-bottom: 36px; line-height: 1.7;
}
.updated {
  font-family: var(--font-mono); font-size: 12px; color: var(--slate);
  text-transform: uppercase; letter-spacing: 0.08em; margin-bottom: 30px;
}

/* Was a 1px-gap grid whose background showed through as hairlines. Once the
   tiles gained radius those hairlines became visible grey bars between the
   cards, so the divider trick is gone and the gap is a real gap. */
.facts-row {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--bento-gap);
  background: transparent; border: 0;
  margin-top: 40px;
}
.fact { background: var(--white); border: 1px solid var(--line-light); padding: 30px; }
.on-dark .fact, .process-strip .fact {
  background: var(--asphalt-2); border-color: var(--line);
}
.on-dark .fact .v, .process-strip .fact .v { color: var(--paper); }
.on-dark .fact .k, .process-strip .fact .k { color: var(--on-dark-mute); }
.fact .v {
  font-family: var(--font-mono); font-size: clamp(28px, 4vw, 36px);
  font-weight: 600; color: var(--asphalt); font-variant-numeric: tabular-nums;
}
.fact .k { font-family: var(--font-mono); font-size: 11px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--slate); margin-top: 8px; }
@media (max-width: 640px) { .facts-row { grid-template-columns: 1fr; } }

.about-story p { font-size: 16px; color: var(--asphalt); max-width: 680px; margin-bottom: 20px; line-height: 1.8; }

/* FAQ: sticky intro rail on the left, numbered accordion on the right.
   A bare <details> stack read as an afterthought next to the rest of the
   page; this gives the section its own structure and a place to put the
   "still stuck, here is the phone number" card where it actually helps. */
.faq-layout { display: grid; grid-template-columns: 340px 1fr; gap: 56px; align-items: start; }
@media (max-width: 1000px) { .faq-layout { grid-template-columns: 1fr; gap: 32px; } }

.faq-aside { position: sticky; top: calc(var(--header-h) + 28px); }
@media (max-width: 1000px) { .faq-aside { position: static; } }
.faq-help {
  margin-top: 28px; background: var(--asphalt); color: var(--paper);
  padding: 26px; border-top: 3px solid var(--accent);
}
.faq-help .k { font-size: 10px; font-weight: 600; letter-spacing: 0.14em; text-transform: uppercase; color: var(--on-dark-mute); margin-bottom: 10px; }
.faq-help p { font-size: 14px; color: var(--on-dark); margin-bottom: 18px; line-height: 1.6; }
.faq-help .num-lg { font-size: clamp(22px, 2.6vw, 28px); font-weight: 600; letter-spacing: -0.03em; font-variant-numeric: tabular-nums; }
.faq-help .num-lg a { text-decoration: none; }
@media (hover: hover) { .faq-help .num-lg a:hover { color: var(--accent-hi); } }

.faq-list { counter-reset: faq; border-top: 1px solid var(--line-light); }
.faq-item {
  counter-increment: faq;
  border-bottom: 1px solid var(--line-light);
  transition: background var(--t-base);
}
.faq-item[open] { background: var(--white); }
.faq-item summary {
  list-style: none; cursor: pointer; position: relative;
  display: grid; grid-template-columns: 46px 1fr 32px; align-items: baseline; gap: 12px;
  padding: 26px 20px 26px 16px;
  font-family: var(--font-display); font-size: clamp(17px, 2.1vw, 21px);
  text-transform: none; font-weight: 700; line-height: 1.3; letter-spacing: -0.015em;
  transition: color var(--t-fast);
}
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary::before {
  content: '0' counter(faq);
  font-family: var(--font-body); font-size: 12px; font-weight: 600;
  letter-spacing: 0.08em; color: var(--accent); font-variant-numeric: tabular-nums;
}
@media (hover: hover) { .faq-item summary:hover { color: var(--accent); } }

/* Chevron in its own grid cell rather than absolutely positioned, so it can
   never collide with a long question. */
.faq-icon { position: relative; width: 32px; height: 32px; justify-self: end; align-self: center; }
.faq-icon::before, .faq-icon::after {
  content: ''; position: absolute; top: 50%; left: 50%;
  background: var(--accent); transition: transform var(--t-base) var(--ease-out);
}
.faq-icon::before { width: 14px; height: 2px; transform: translate(-50%, -50%); }
.faq-icon::after  { width: 2px; height: 14px; transform: translate(-50%, -50%); }
.faq-item[open] .faq-icon::after { transform: translate(-50%, -50%) scaleY(0); }

.faq-item .answer {
  padding: 0 20px 28px 74px;
  font-size: 15px; color: var(--slate); line-height: 1.8; max-width: 72ch;
}
.faq-item .answer a { color: var(--accent-lo); font-weight: 600; }
@media (max-width: 560px) {
  .faq-item summary { grid-template-columns: 34px 1fr 26px; padding: 22px 8px 22px 8px; gap: 10px; }
  .faq-item .answer { padding: 0 8px 24px 52px; }
}

/* == 21 Final CTA ========================================================= */
.final-cta {
  position: relative; overflow: hidden; text-align: center;
  background: linear-gradient(150deg, #C22733, #8E1B24);
}
.final-cta::before {
  content: ''; position: absolute; inset: 0; opacity: 0.16;
  background-image: repeating-linear-gradient(115deg, transparent 0 38px, rgba(255, 255, 255, 0.5) 38px 40px);
}
.final-cta > * { position: relative; z-index: 1; }
.final-cta h2 { color: var(--white); font-size: clamp(30px, 5.2vw, 56px); margin-bottom: 18px; }
.final-cta p {
  font-size: clamp(16px, 2vw, 18px); margin-bottom: 34px;
  color: rgba(255, 255, 255, 0.9); max-width: 540px; margin-inline: auto;
}
.final-cta .btn-dark { background: var(--asphalt); color: var(--white); }
.final-cta .cta-row { justify-content: center; }
.final-cta .btn-ghost { border-color: rgba(255, 255, 255, 0.5); color: var(--white); }

/* == 22 Footer ============================================================ */
.site-footer { background: var(--asphalt-3); color: var(--on-dark); padding: 64px 0 30px; font-size: 14px; }
.foot-grid {
  display: grid; grid-template-columns: 1.5fr 1fr 1fr 1.15fr; gap: 36px;
  padding-bottom: 40px; border-bottom: 1px solid var(--line);
}
.foot-grid h2 {
  color: var(--paper); font-family: var(--font-mono); font-size: 11px;
  letter-spacing: 0.12em; text-transform: uppercase; margin-bottom: 18px; font-weight: 600;
}
.foot-grid ul { list-style: none; }
.foot-grid li { margin-bottom: 11px; }
.foot-grid a { text-decoration: none; opacity: 0.85; transition: opacity var(--t-fast), color var(--t-fast); }
@media (hover: hover) { .foot-grid a:hover { opacity: 1; color: var(--accent); } }
/* Contact column: an icon, a quiet label, then the value. The label matters
   as much as the icon — a phone glyph alone cannot say which of two numbers
   is sales and which is the 24/7 line. */
.foot-contact li { display: flex; gap: 12px; margin-bottom: 18px; align-items: flex-start; }
.foot-contact svg { width: 17px; height: 17px; flex: none; margin-top: 2px; color: var(--accent); }
.foot-contact .fc-k {
  display: block; font-size: 10px; font-weight: 600; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--on-dark-mute); margin-bottom: 3px;
}
.foot-contact a { color: var(--paper); opacity: 0.92; }
.foot-contact address { font-style: normal; color: var(--paper); opacity: 0.92; line-height: 1.55; }

.foot-blurb { max-width: 300px; opacity: 0.72; margin-bottom: 22px; }
.foot-social { display: flex; gap: 14px; }
.foot-social a {
  opacity: 0.75; display: grid; place-items: center;
  width: 40px; height: 40px; border: 1px solid var(--line);
  transition: opacity var(--t-fast), border-color var(--t-fast), transform var(--t-fast) var(--ease-out);
}
@media (hover: hover) { .foot-social a:hover { opacity: 1; border-color: var(--accent); transform: translateY(-2px); } }
.foot-bottom {
  padding-top: 26px; display: flex; justify-content: space-between;
  flex-wrap: wrap; gap: 14px; font-size: 13px; opacity: 0.75;
}
.foot-bottom nav { display: flex; gap: 18px; flex-wrap: wrap; }
@media (max-width: 860px) { .foot-grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 520px) { .foot-grid { grid-template-columns: 1fr; gap: 30px; } }

/* == 23 Chat widget ======================================================= */
.hero-fab {
  position: fixed; right: 22px; bottom: 22px; z-index: 120;
  background: var(--accent); color: var(--white); border: none; cursor: pointer;
  padding: 15px 22px; font-family: var(--font-display); font-weight: 700; font-size: 16px;
  text-transform: uppercase; letter-spacing: 0.04em;
  box-shadow: 0 8px 28px rgba(0, 0, 0, 0.32);
  display: flex; align-items: center; gap: 9px;
  transition: background var(--t-fast), transform var(--t-fast) var(--ease-out);
}
@media (hover: hover) { .hero-fab:hover { background: var(--accent-lo); transform: translateY(-2px); } }
.hero-panel {
  position: fixed; right: 22px; bottom: 88px; z-index: 121;
  width: 360px; max-width: calc(100vw - 44px);
  background: var(--white); border: 1px solid var(--line-light-2);
  box-shadow: var(--shadow-3);
  display: none; flex-direction: column; max-height: min(72vh, 580px);
}
.hero-panel.open { display: flex; animation: panelIn 320ms var(--ease-out); }
@keyframes panelIn { from { opacity: 0; transform: translateY(14px) scale(0.98); } to { opacity: 1; transform: none; } }
.hero-head { background: var(--asphalt); color: var(--paper); padding: 16px 20px; position: relative; }
.hero-head .t { font-family: var(--font-display); font-weight: 700; font-size: 19px; text-transform: uppercase; }
.hero-head .s { font-size: 12px; color: var(--on-dark-mute); max-width: 255px; margin-top: 2px; }
.hero-close {
  position: absolute; top: 10px; right: 10px; background: none; border: none;
  color: var(--paper); font-size: 24px; line-height: 1; cursor: pointer; padding: 4px 9px; opacity: 0.65;
}
@media (hover: hover) { .hero-close:hover { opacity: 1; } }
.hero-msgs { padding: 18px; overflow-y: auto; flex: 1; display: flex; flex-direction: column; gap: 11px; }
.hero-msg { font-size: 14px; line-height: 1.55; padding: 11px 15px; max-width: 88%; animation: msgIn 260ms var(--ease-out); }
@keyframes msgIn { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
.hero-msg.bot { background: #EDEFF4; color: var(--asphalt); align-self: flex-start; }
.hero-msg.user { background: var(--asphalt); color: var(--paper); align-self: flex-end; }
.hero-typing { display: flex; gap: 5px; align-self: flex-start; padding: 14px 16px; background: #EDEFF4; }
.hero-typing span { width: 6px; height: 6px; border-radius: 50%; background: var(--slate); animation: typing 1.2s ease-in-out infinite; }
.hero-typing span:nth-child(2) { animation-delay: 0.16s; }
.hero-typing span:nth-child(3) { animation-delay: 0.32s; }
@keyframes typing { 0%, 60%, 100% { opacity: 0.25; transform: translateY(0); } 30% { opacity: 1; transform: translateY(-4px); } }
.hero-chips { padding: 0 18px 18px; display: flex; flex-wrap: wrap; gap: 8px; }
.hero-chip {
  border: 1px solid var(--accent); background: var(--white); color: var(--asphalt);
  font-size: 12px; font-family: var(--font-mono); padding: 8px 13px; cursor: pointer;
  transition: background var(--t-fast), color var(--t-fast); text-align: left; text-decoration: none;
}
@media (hover: hover) { .hero-chip:hover { background: var(--accent); color: var(--white); } }
.hero-chip.primary { background: var(--accent); color: var(--white); font-weight: 600; }
@media (hover: hover) { .hero-chip.primary:hover { background: var(--accent-lo); } }
@media (max-width: 560px) {
  .hero-panel { right: 10px; left: 10px; width: auto; bottom: 80px; }
  .hero-fab { right: 12px; bottom: 12px; padding: 13px 17px; font-size: 15px; }
}

/* Consent bar. Bottom-anchored and never a full-screen blocker: a carrier
   looking up a phone number should not have to answer a cookie question to
   see it. */
.consent-bar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 150;
  background: var(--asphalt); border-top: 2px solid var(--accent);
  transform: translateY(110%); transition: transform 340ms var(--ease-out);
  box-shadow: 0 -10px 40px rgba(6, 10, 20, 0.4);
}
.consent-bar.is-in { transform: none; }
.consent-inner {
  max-width: var(--wrap); margin-inline: auto;
  padding: 18px var(--gutter);
  display: flex; align-items: center; justify-content: space-between; gap: 22px; flex-wrap: wrap;
}
.consent-inner p { font-size: 13.5px; color: var(--on-dark); max-width: 70ch; line-height: 1.6; }
.consent-inner a { color: var(--paper); font-weight: 600; }
.consent-actions { display: flex; gap: 10px; flex: none; }
@media (max-width: 620px) {
  .consent-inner { padding-bottom: 22px; }
  .consent-actions { width: 100%; }
  .consent-actions .btn { flex: 1; }
  /* Keep the chat button clear of the bar while it is up. */
  .consent-bar.is-in ~ .hero-fab { bottom: 130px; }
}

/* Offline notice on the calculator pages. */
.offline-note {
  position: fixed; left: 50%; bottom: 22px; z-index: 140;
  transform: translate(-50%, 140%); transition: transform 320ms var(--ease-out);
  background: var(--asphalt); color: var(--paper);
  padding: 14px 22px; border-radius: var(--r-pill);
  border: 1px solid var(--line); box-shadow: var(--shadow-3);
  font-size: 13.5px; font-weight: 500; max-width: calc(100vw - 32px); text-align: center;
}
.offline-note.is-in { transform: translate(-50%, 0); }

/* == Mobile carousels =====================================================
   Nine gallery photos or five videos stacked one per row turn into a very
   long scroll on a phone, and most of it never gets seen. Below the tablet
   breakpoint those grids become a swipeable snap row instead.

   Built on scroll-snap, so it works with no JavaScript at all: the row is
   swipeable the moment the CSS loads. The dots are an enhancement added by
   hhl-motion.js, and their absence costs nothing.
   ======================================================================== */
@media (max-width: 760px) {
  .m-carousel {
    display: flex !important;
    grid-template-columns: none !important;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    /* Full-bleed so cards run to the screen edge, which reads as swipeable. */
    margin-inline: calc(var(--gutter) * -1);
    padding-inline: var(--gutter);
    scroll-padding-inline: var(--gutter);
    gap: 12px;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .m-carousel::-webkit-scrollbar { display: none; }

  .m-carousel > * {
    flex: 0 0 var(--slide, 78%);
    scroll-snap-align: start;
    /* Spans from the bento grid mean nothing in a flex row. */
    grid-column: auto !important;
    grid-row: auto !important;
  }
  /* A sliver of the next card is the affordance that says "there is more". */
  .m-carousel.m-wide > * { --slide: 86%; }
  .m-carousel.m-narrow > * { --slide: 46%; }

  /* In a grid the row track gave every tile its height. In a flex row nothing
     does, so a short tile would collapse to its text and the row would look
     ragged. Give the slides one shared height per carousel. */
  .m-carousel > .bento-tile { min-height: 190px; }
  .m-carousel.m-wide > .bento-tile { min-height: 0; aspect-ratio: 5 / 4; }
  .m-carousel.m-narrow > .short-frame { min-height: 0; }
  .m-carousel > .tool-card { min-height: 210px; }
  /* Milestones carry no surface of their own, just a rule down the left, so
     they need the padding a tile gets from its own box. */
  .m-carousel > .mile { min-height: 150px; padding-right: 8px; }

  /* Items that are not slides (a full-width note, a CTA) leave the row. */
  .m-carousel > .no-slide { display: none; }

  .carousel-after { margin-top: 16px; }

  .carousel-dots {
    display: flex; justify-content: center; gap: 7px; margin-top: 16px;
  }
  .carousel-dots button {
    width: 7px; height: 7px; padding: 0; border: 0; border-radius: 50%;
    background: var(--line-light-2); cursor: pointer;
    transition: background var(--t-fast), width var(--t-fast);
  }
  .on-dark .carousel-dots button { background: rgba(247, 245, 241, 0.25); }
  .carousel-dots button[aria-current='true'] { background: var(--accent); width: 20px; border-radius: var(--r-pill); }
}
/* Above the breakpoint the dots are meaningless: everything is already visible. */
@media (min-width: 761px) { .carousel-dots { display: none; } }

/* == Mobile pass ==========================================================
   Measured, not guessed. A probe at 390x844 reported the homepage stacking to
   roughly sixteen screens of scrolling, with every bento tile taking a full
   row of its own regardless of how little was in it.

   Three moves fix most of it: pair up the short tiles, tighten the vertical
   rhythm, and stop the display type from eating a third of the fold.
   ======================================================================== */
@media (max-width: 620px) {
  /* A tile holding a number and a two-word label does not deserve a whole
     row. .m-half opts those into the two-column track the grid already has. */
  .bento > .m-half { grid-column: span 1; }
  .bento > .m-half .stat-lg { font-size: 26px; }
  .bento > .m-half .tile-note { font-size: 12px; }

  /* Sections were sized for a desktop rhythm; on a phone the same figure is
     a third of the viewport of empty space between every band. */
  :root { --section-y: 52px; }
  .section-head { margin-bottom: 24px; }
  .bento-tile { padding: 18px; }

  /* clamp() vw terms bottom out fine on desktop but leave headlines large
     enough on a phone that two of them fill the screen on their own. */
  h1 { font-size: clamp(30px, 8.6vw, 38px); }
  h2 { font-size: clamp(24px, 6.6vw, 30px); }
  .stat-xl { font-size: 40px; }

  /* Desktop gaps between a block and the one under it. */
  .mt-56 { margin-top: 30px; }
  .mt-32 { margin-top: 22px; }

  /* Each step of the process is two tiles: the marker and the copy. Side by
     side on desktop that reads as one card. Stacked at full width it read as
     six unrelated boxes, and a marker tile 100px tall holding two words was
     the worst of them. Flatten the marker into a header strip on its step. */
  .pin-panel .bento { gap: 0; }
  .pin-panel .bento-tile.sp-3 {
    flex-direction: row; align-items: center; justify-content: space-between;
    gap: 14px; min-height: 0; padding: 11px 18px;
    border-bottom: 0; border-radius: var(--r-md) var(--r-md) 0 0;
  }
  .pin-panel .bento-tile.sp-3 .tile-k { margin-bottom: 0; }
  .pin-panel .bento-tile.sp-3 .stat-xl { font-size: 24px; line-height: 1; }
  .pin-panel .bento-tile.sp-9 { border-radius: 0 0 var(--r-md) var(--r-md); }
}

@media (max-width: 760px) {
  /* Side by side, not stacked. Two 16:10 chips at half width are 98px tall
     together; stacked they were 196px of hero for the same two photos. */
  .hero-thumbs { grid-template-columns: 1fr 1fr; }

  /* The hero has a photo, a headline, a lede, two buttons, three proof
     figures, a recap card and two thumbnails. It does not also need to be
     880px before any of that is laid out. */
  .hero { min-height: 0; padding-block: 40px 44px; }
  .hero-inner { gap: 28px; }
  .hero p.lede { margin-bottom: 22px; }
  .hero-proof { gap: 18px 26px; margin-top: 24px; padding-top: 20px; }

  /* Full-width buttons in a stack, because a thumb misses a 140px pill that
     is sharing a row with another one. */
  .hero .cta-row { display: grid; gap: 10px; }
  .hero .cta-row .btn { width: 100%; }

  /* At 12px none of the three badges fit two to a line, so each took a line
     of its own and the set cost 110px of the fold. Two points smaller and
     they pair up. */
  .pill-row { gap: 7px; margin-bottom: 20px; }
  .pill { font-size: 10.5px; padding: 6px 12px; letter-spacing: 0.05em; }
}

/* == Mobile bottom navigation =============================================
   The audience is on a phone, often in a cab or at a pump, using one thumb.
   The two actions that matter (call someone, start setup) were both buried
   behind a hamburger. This puts them in the thumb zone.

   Five slots, with Call raised in the centre because on a phone a call is
   the highest-intent thing a carrier can do.
   ======================================================================== */
.bottom-nav {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 130;
  display: none;
  /* Solid, not translucent-plus-backdrop-filter. iOS Safari has long-standing
     repaint bugs with position:fixed + backdrop-filter: the bar flickers or
     fails to paint entirely while scrolling. At 0.96 opacity the blur was
     doing nothing visible anyway. */
  background: #141C2F;
  border-top: 1px solid var(--line);
  /* max(), not a bare env(): on a phone with no safe-area inset the value is
     0 and the row sits flush against the screen edge. */
  padding-bottom: max(env(safe-area-inset-bottom, 0px), 6px);
  box-shadow: 0 -8px 30px rgba(6, 10, 20, 0.4);
  /* Own compositing layer, which settles iOS repaint during momentum scroll. */
  transform: translateZ(0);
}

.bottom-nav-inner {
  display: grid; grid-template-columns: repeat(5, 1fr);
  align-items: end; max-width: 560px; margin-inline: auto;
}

.bn-item {
  display: flex; flex-direction: column; align-items: center; gap: 5px;
  padding: 10px 2px 9px; text-decoration: none;
  color: var(--on-dark-mute); background: none; border: 0; cursor: pointer;
  font-family: inherit; font-size: 10px; font-weight: 600;
  letter-spacing: 0.03em; text-transform: uppercase;
  /* 48px minimum touch target even though the label is small. */
  min-height: 56px;
  /* Grid children default to min-width:auto and will happily overflow the
     track rather than shrink. */
  min-width: 0;
  transition: color var(--t-fast);
}
/* One line, always. A wrapped label makes one column taller than the rest and
   the whole bar looks broken. */
.bn-item span:last-child {
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
@media (max-width: 380px) {
  .bn-item { font-size: 9px; letter-spacing: 0; }
  .bn-item svg { width: 19px; height: 19px; }
}
.bn-item svg { width: 21px; height: 21px; display: block; }
.bn-item[aria-current='page'] { color: var(--paper); }
.bn-item[aria-current='page'] svg { color: var(--accent); }
@media (hover: hover) { .bn-item:hover { color: var(--paper); } }

/* The raised call button. */
.bn-call {
  position: relative;
  color: var(--white);
}
.bn-call .bn-ring {
  display: grid; place-items: center;
  width: 54px; height: 54px; border-radius: 50%;
  background: var(--accent-grad);
  box-shadow: 0 6px 20px rgba(214, 48, 60, 0.45);
  margin-top: -22px;
  border: 3px solid rgba(18, 26, 44, 0.96);
  transition: transform var(--t-fast) var(--ease-out);
}
.bn-call svg { width: 23px; height: 23px; }
.bn-call:active .bn-ring { transform: scale(0.94); }

@media (max-width: 980px) {
  .bottom-nav { display: block; }

  /* Nothing at the bottom of the page should sit under the bar. */
  /* Measured height of the bar, not a guess: 75px + a little breathing room. */
  body { padding-bottom: calc(84px + env(safe-area-inset-bottom, 0px)); }

  /* Everything else anchored to the bottom stacks above it. The extra 24px on
     the consent bar clears the raised Call button, which sits proud of the
     bar's top edge by design. */
  .hero-fab { bottom: calc(88px + env(safe-area-inset-bottom, 0px)); }
  .consent-bar { bottom: calc(90px + env(safe-area-inset-bottom, 0px)); }
  .consent-bar.is-in ~ .hero-fab { bottom: calc(230px + env(safe-area-inset-bottom, 0px)); }
  .offline-note { bottom: calc(92px + env(safe-area-inset-bottom, 0px)); }

  /* The sheet covers the bottom bar on its own, so that one stays put —
     hiding it made the bar blink out a beat before the sheet arrived. The
     rest float above the page on higher z-indexes than the header the menu
     lives in, so without this they sit on top of the menu's own CTA. */
  body.nav-open .consent-bar,
  body.nav-open .hero-fab,
  body.nav-open .offline-note { display: none; }
}

/* Scroll progress rail along the top of the viewport */
.scroll-rail { position: fixed; top: 0; left: 0; height: 3px; width: 0; background: var(--accent); z-index: 200; transition: width 80ms linear; }

/* == 23b Bento system =====================================================
   The site's core claim is that HHL shows you your numbers. A modular tile
   grid is the honest form for that: every figure gets its own surface with
   its own label, at a size you can read from across the cab.

   12 columns on desktop, 6 on tablet, 2 on phone. Tiles declare their span
   with .sp-* / .rw-* and collapse predictably.
   ======================================================================== */
:root { --bento-gap: 14px; }

.bento {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: var(--bento-gap);
  grid-auto-rows: minmax(112px, auto);
}
@media (max-width: 1000px) { .bento { grid-template-columns: repeat(6, 1fr); } }
@media (max-width: 620px)  { .bento { grid-template-columns: repeat(2, 1fr); gap: 10px; } }

.sp-2 { grid-column: span 2; } .sp-3 { grid-column: span 3; }
.sp-4 { grid-column: span 4; } .sp-5 { grid-column: span 5; }
.sp-6 { grid-column: span 6; } .sp-7 { grid-column: span 7; }
.sp-8 { grid-column: span 8; } .sp-9 { grid-column: span 9; }
.sp-12 { grid-column: 1 / -1; }
.rw-2 { grid-row: span 2; } .rw-3 { grid-row: span 3; } .rw-4 { grid-row: span 4; }

@media (max-width: 1000px) {
  .sp-7, .sp-8, .sp-9, .sp-5 { grid-column: span 6; }
  .sp-4 { grid-column: span 3; }
  .sp-3 { grid-column: span 3; }
  .sp-2 { grid-column: span 2; }
}
@media (max-width: 620px) {
  .sp-3, .sp-4, .sp-5, .sp-6, .sp-7, .sp-8, .sp-9 { grid-column: 1 / -1; }
  .sp-2 { grid-column: span 1; }
  .rw-2, .rw-3, .rw-4 { grid-row: span 1; }
}

.bento-tile {
  position: relative; overflow: hidden;
  background: var(--white);
  border: 1px solid var(--line-light);
  padding: clamp(20px, 2.4vw, 30px);
  display: flex; flex-direction: column; justify-content: space-between;
  transition: border-color var(--t-base), transform var(--t-base) var(--ease-out);
}
.bento-tile > * { position: relative; z-index: 1; }
.bento-tile.dark { background: var(--asphalt-2); color: var(--paper); border-color: var(--line); }
.bento-tile.deep { background: var(--asphalt-3); color: var(--paper); border-color: var(--line); }
.bento-tile.accent { background: var(--accent-grad); color: var(--white); border-color: transparent; }
.bento-tile.accent .tile-k { color: rgba(255, 255, 255, 0.78); }
.bento-tile.flush { padding: 0; }

/* Frosted panel over the dark hero field. */
.bento-tile.glass {
  background: rgba(24, 33, 58, 0.55);
  backdrop-filter: blur(20px) saturate(150%);
  -webkit-backdrop-filter: blur(20px) saturate(150%);
  border: 1px solid rgba(247, 245, 241, 0.11);
  color: var(--paper);
}

/* The chip inside a bento tile sits above the label and moves with the
   card: hovering the tile carries the border, fill and icon with it, which is
   what makes six of them read as one set rather than six separate widgets. */
.bento-tile > .icon-chip { margin-bottom: 14px; }
@media (hover: hover) {
  .bento-tile:hover > .icon-chip {
    border-color: rgba(214, 48, 60, 0.45);
    background: rgba(214, 48, 60, 0.12);
    color: var(--accent);
  }
}
.bento-tile.accent > .icon-chip { border-color: rgba(255,255,255,0.3); background: rgba(255,255,255,0.14); color: #fff; }

/* Tile chrome */
.tile-k {
  font-size: 10px; font-weight: 600; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--slate); margin-bottom: 12px;
}
.dark .tile-k, .deep .tile-k, .glass .tile-k { color: var(--on-dark-mute); }
.tile-note { font-size: 13px; color: var(--slate); margin-top: 10px; }
.dark .tile-note, .deep .tile-note, .glass .tile-note { color: var(--on-dark-mute); }

/* The oversized readouts the whole design is built around. */
/* nowrap matters: "$2.61" breaking to "$2.3 / 1" in a narrow tile reads as a
   different number, which is the one thing this design cannot afford. */
.stat-xl {
  font-size: clamp(34px, 5vw, 72px); font-weight: 600; line-height: 0.92;
  letter-spacing: -0.045em; font-variant-numeric: tabular-nums; color: var(--asphalt);
  white-space: nowrap;
}
.dark .stat-xl, .deep .stat-xl, .glass .stat-xl { color: var(--paper); }
.stat-xl.good { color: var(--signal); }
.stat-xl.hot { color: var(--accent); }
.stat-lg {
  font-size: clamp(24px, 2.5vw, 36px); font-weight: 600; line-height: 1;
  letter-spacing: -0.035em; font-variant-numeric: tabular-nums; white-space: nowrap;
}

/* Cursor spotlight. --mx/--my are written by hhl-motion.js. */
.spot::after {
  content: ''; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background: radial-gradient(340px circle at var(--mx, 50%) var(--my, 50%), rgba(214, 48, 60, 0.16), transparent 62%);
  opacity: 0; transition: opacity var(--t-base);
}
@media (hover: hover) {
  .spot:hover::after { opacity: 1; }
  .bento-tile:hover { border-color: rgba(214, 48, 60, 0.45); }
}

/* Media tile with parallax inner image */
.tile-media { position: absolute; inset: 0; z-index: 0; overflow: hidden; }
/* --obj-pos is set per photo so the subject survives the crop instead of
   defaulting to dead centre. The extra height is the parallax travel. */
.tile-media img {
  width: 100%; height: 118%;
  object-fit: cover; object-position: var(--obj-pos, center center);
  will-change: transform;
}
.tile-media::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(8, 12, 22, 0.15), rgba(8, 12, 22, 0.82));
}
.bento-tile.has-media { color: var(--paper); border-color: var(--line); justify-content: flex-end; min-height: 220px; }

/* == 23c Marquee ==========================================================
   Duplicated track translated by exactly -50%, so the loop is seamless.
   ======================================================================== */
.marquee {
  overflow: hidden; position: relative;
  border-block: 1px solid var(--line-light);
  background: var(--paper);
  padding: 16px 0;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
}
.marquee.on-dark-strip { background: var(--asphalt); border-color: var(--line); }
.marquee-track {
  display: flex; width: max-content; gap: 0;
  animation: marqueeRun var(--marquee-speed, 46s) linear infinite;
  will-change: transform;
}
.marquee:hover .marquee-track { animation-play-state: paused; }
@keyframes marqueeRun { to { transform: translateX(-50%); } }
.marquee-item {
  display: flex; align-items: center; gap: 12px;
  padding: 0 28px; white-space: nowrap;
  font-size: 13px; font-weight: 600; letter-spacing: 0.04em;
  color: var(--slate); font-variant-numeric: tabular-nums;
}
.on-dark-strip .marquee-item { color: var(--on-dark); }
.marquee-item .rate { color: var(--accent); }
.on-dark-strip .marquee-item .rate { color: var(--accent-hi); }
.marquee-item::after {
  content: ''; width: 5px; height: 5px; background: var(--accent);
  transform: rotate(45deg); opacity: 0.7; margin-left: 16px;
}

/* == 23d Advanced motion ==================================================
   Word-by-word headline reveals, parallax layers, pinned sections and page
   transitions. Every one is inert under prefers-reduced-motion, and the
   heavier ones are dropped on small screens where the frame budget is
   tighter and the audience is usually on mobile data.
   ======================================================================== */

/* Headline reveal. hhl-motion.js splits the text into .word spans. */
.js-on .reveal-text .word {
  display: inline-block;
  opacity: 0;
  transform: translateY(0.42em);
  transition: opacity 620ms var(--ease-out), transform 620ms var(--ease-out);
  transition-delay: calc(var(--w, 0) * 42ms);
}
.js-on .reveal-text.is-in .word { opacity: 1; transform: none; }

/* Parallax layers, driven by hhl-motion.js writing --py. */
.js-on [data-parallax] { transform: translate3d(0, var(--py, 0px), 0); }

/* Pinned storytelling stage.

   Written the safe way round on purpose. The DEFAULT is an ordinary stacked
   section that everyone can read: normal height, panels one after another,
   fully visible. Pinning is opt-in, applied by hhl-motion.js only when the
   viewport is big enough and motion is welcome. Had it been the other way
   round, a JS failure would leave three panels stacked invisibly on one grid
   cell inside a 300vh-tall empty section. */
.pin-wrap { position: relative; }
.pin-stage { padding-block: var(--section-y); }
.pin-panel { margin-bottom: clamp(20px, 3vw, 34px); }
.pin-dots, .pin-progress { display: none; }

.pin-wrap.is-pinned { min-height: calc(var(--pin-steps, 3) * 100svh); }
.pin-wrap.is-pinned .pin-stage {
  position: sticky; top: 0; min-height: 100svh;
  display: grid; align-content: center; overflow: hidden; padding-block: 0;
}
.pin-wrap.is-pinned .pin-panel {
  grid-area: 1 / 1; margin-bottom: 0;
  opacity: 0; transform: translateY(28px) scale(0.985);
  transition: opacity 520ms var(--ease-out), transform 520ms var(--ease-out);
  pointer-events: none;
}
.pin-wrap.is-pinned .pin-panel.is-active { opacity: 1; transform: none; pointer-events: auto; }
.pin-wrap.is-pinned .pin-dots { display: grid; }
.pin-wrap.is-pinned .pin-progress { display: block; }
.pin-progress {
  position: absolute; left: 0; right: 0; bottom: 0; height: 3px;
  background: rgba(247, 245, 241, 0.10);
}
.pin-progress i { display: block; height: 100%; width: 0; background: var(--accent); transition: width 240ms linear; }
/* display is owned by the .is-pinned rules above; setting it here would
   win on source order and leave the dots showing when unpinned. */
.pin-dots { position: absolute; right: clamp(16px, 3vw, 40px); top: 50%; transform: translateY(-50%); gap: 14px; }
.pin-dots span { width: 9px; height: 9px; background: rgba(247, 245, 241, 0.22); transform: rotate(45deg); transition: background var(--t-base), transform var(--t-base); }
.pin-dots span.on { background: var(--accent); transform: rotate(45deg) scale(1.4); }


/* Aurora wash behind dark bands */
.aurora { position: relative; isolation: isolate; }
.aurora::before {
  content: ''; position: absolute; inset: -20% -10%; z-index: 0; pointer-events: none;
  background:
    radial-gradient(38% 46% at 18% 28%, rgba(214, 48, 60, 0.24), transparent 70%),
    radial-gradient(34% 40% at 82% 68%, rgba(46, 158, 109, 0.14), transparent 72%),
    radial-gradient(30% 38% at 62% 12%, rgba(120, 150, 220, 0.12), transparent 70%);
  filter: blur(46px);
  animation: auroraDrift 22s ease-in-out infinite alternate;
}
.aurora > * { position: relative; z-index: 1; }
@keyframes auroraDrift {
  from { transform: translate3d(-3%, -2%, 0) scale(1); }
  to   { transform: translate3d(3%, 2%, 0) scale(1.08); }
}

@media (prefers-reduced-motion: reduce) {
  .marquee-track { animation: none; }
  .aurora::before { animation: none; }
  .js-on .reveal-text .word { opacity: 1 !important; transform: none !important; }
  .js-on [data-parallax] { transform: none !important; }
  .pin-panel { opacity: 1; transform: none; pointer-events: auto; }
}

/* Cross-page transitions where the browser supports them. */
@view-transition { navigation: auto; }
@media (prefers-reduced-motion: no-preference) {
  ::view-transition-old(root) { animation: vtOut 180ms var(--ease-in-out) both; }
  ::view-transition-new(root) { animation: vtIn 320ms var(--ease-out) both; }
}
@keyframes vtOut { to { opacity: 0; } }
@keyframes vtIn { from { opacity: 0; transform: translateY(10px); } }

/* == 23e Shape layer ======================================================
   Applied in one place rather than scattered through every component, so the
   geometry of the whole site can be re-tuned from the radius tokens above.
   This is the layer that most visibly separates our design from the sharp,
   hairline-boxed reference we were handed.
   ======================================================================== */

/* Actions read as pills. */
.btn, .nav-cta, .hero-fab, .hero-chip, .row-remove, .lang-toggle {
  border-radius: var(--r-pill);
}
.ai-pick, .post-meta .tag, .section-eyebrow, .tile-k.chip, .pill {
  border-radius: var(--r-pill);
}

/* Surfaces. */
.bento-tile, .tool-card, .tool-feature, .tool-row, .svc-block, .form-card,
.calc-card, .calc-result, .post-card, .fact, .legal-note,
.faq-help, .form-rail-inner, .board, .recap-card, .hero-panel, .short-frame,
.svc-row-media, .consent-bar, .form-status, .calc-verdict, .hos-clock,
.tool-sample, .service-tile, .gal-item {
  border-radius: var(--r-md);
}
.board, .form-card, .calc-card, .hero-card {
  border-radius: var(--r-lg);
}

/* Inputs. */
input, select, textarea { border-radius: var(--r-sm); }
.consent-bar { border-radius: var(--r-lg) var(--r-lg) 0 0; }

/* Media inside rounded surfaces has to be clipped or the corners show
   through the photograph. */
.bento-tile, .svc-block, .short-frame, .svc-row-media,
.tile-media, .gal-item { overflow: hidden; }

/* Grid children with their own radius need the parent gap back, otherwise
   1px-gap bento borders look like a broken table. */
.services-grid { gap: var(--bento-gap); background: transparent; border: 0; }
.services-grid .service-tile { border: 1px solid var(--line-light); }
.facts-row { gap: var(--bento-gap); background: transparent; border: 0; }
.facts-row .fact { border: 1px solid var(--line-light); }

/* Elevation that matches the new geometry. */
.form-card, .calc-card, .tool-feature { box-shadow: var(--shadow-2); }
@media (hover: hover) {
  .bento-tile:hover, .tool-card:hover, .svc-block:hover { box-shadow: var(--shadow-lift); }
}

/* Reusable pill for badges and eyebrow chips. */
.pill {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 8px 16px; font-size: 12px; font-weight: 600;
  letter-spacing: 0.06em; text-transform: uppercase;
  background: rgba(247, 245, 241, 0.10);
  border: 1px solid rgba(247, 245, 241, 0.18);
  color: var(--paper);
  backdrop-filter: blur(10px);
}
.pill.light { background: var(--white); border-color: var(--line-light); color: var(--asphalt); }
.pill .dot-sm { width: 6px; height: 6px; border-radius: 50%; background: var(--signal); flex: none; }
.pill-row { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 26px; }

/* == 24 Per-page layouts ==================================================
   The shared system supplies colour, type and components. Composition is
   deliberately different on every page so the site does not read as one
   template with the words swapped out.
   ======================================================================== */

/* ---- Services: alternating full-bleed rows + sticky equipment index ----- */
.svc-layout { display: grid; grid-template-columns: 200px 1fr; gap: 56px; align-items: start; }
@media (max-width: 1020px) { .svc-layout { grid-template-columns: 1fr; gap: 32px; } }

.svc-index { position: sticky; top: calc(var(--header-h) + 28px); }
.svc-index h2 {
  font-family: var(--font-body); font-size: 11px; font-weight: 600;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--slate);
  margin-bottom: 16px; padding-bottom: 12px; border-bottom: 1px solid var(--line-light);
}
.svc-index ol { list-style: none; counter-reset: eq; }
.svc-index li { counter-increment: eq; }
.svc-index a {
  display: flex; gap: 12px; align-items: baseline;
  padding: 10px 0; text-decoration: none; font-size: 15px; font-weight: 500;
  color: var(--slate); border-bottom: 1px solid var(--line-light);
  transition: color var(--t-fast), padding-left var(--t-fast) var(--ease-out);
}
.svc-index a::before {
  content: '0' counter(eq); font-size: 11px; font-weight: 600;
  color: var(--accent); letter-spacing: 0.06em;
}
@media (hover: hover) { .svc-index a:hover { color: var(--asphalt); padding-left: 6px; } }
@media (max-width: 1020px) {
  .svc-index { position: static; }
  .svc-index ol { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0 20px; }
}
@media (max-width: 560px) { .svc-index ol { grid-template-columns: repeat(2, 1fr); } }

.svc-row {
  display: grid; grid-template-columns: 1.05fr 0.95fr; gap: 44px; align-items: center;
  padding: clamp(36px, 5vw, 56px) 0; border-top: 1px solid var(--line-light);
}
.svc-row:first-of-type { border-top: 0; padding-top: 0; }
/* Alternation is an explicit class rather than :nth-of-type. The list now has
   a break element sitting among the rows, and a structural selector that
   depends on sibling order silently re-phases the whole page the moment
   anything is inserted into it. */
.svc-row.is-flipped .svc-row-media { order: -1; }
@media (max-width: 860px) {
  .svc-row { grid-template-columns: 1fr; gap: 26px; }
  .svc-row.is-flipped .svc-row-media { order: 0; }
}
.svc-row-media { position: relative; overflow: hidden; aspect-ratio: 4 / 3; background: var(--asphalt-2); }
.svc-row-media img { width: 100%; height: 100%; object-fit: cover; transition: transform 800ms var(--ease-out); }
@media (hover: hover) { .svc-row:hover .svc-row-media img { transform: scale(1.04); } }

/* The number moved out of the photo corner and into the copy, where it reads
   as an index entry that matches the sticky list rather than a sticker
   dropped on the picture. */
.svc-copy .eq-num {
  display: block; font-family: var(--font-mono); font-size: 12px;
  letter-spacing: 0.14em; color: var(--accent); margin-bottom: 10px;
}
.svc-row h3 { font-size: clamp(26px, 3.4vw, 38px); margin-bottom: 14px; }
.svc-row h3 a { text-decoration: none; color: inherit; }
@media (hover: hover) { .svc-row h3 a:hover { color: var(--accent); } }

/* Mid-list band. Breaks a 2,768px run of six near-identical rows and carries
   the one thing that is true of all of them. */
.svc-break {
  display: grid; grid-template-columns: 1fr auto; gap: clamp(22px, 3vw, 44px);
  align-items: center; margin: clamp(28px, 4vw, 48px) 0;
  padding: clamp(26px, 3.2vw, 40px);
  background: var(--asphalt); color: var(--paper);
  border-radius: var(--r-md); border-top: 3px solid var(--accent);
}
@media (max-width: 760px) { .svc-break { grid-template-columns: 1fr; } }
.svc-break h3 { font-size: clamp(21px, 2.6vw, 28px); color: var(--paper); margin-bottom: 12px; }
.svc-break p { font-size: 15px; color: var(--on-dark); line-height: 1.72; max-width: 62ch; }
.svc-break .btn { white-space: nowrap; }

/* Scrollspy on the jump list: the row you are reading is marked in the index.
   Set by hhl.js; without JS the list is an ordinary set of anchors. */
.svc-index a.is-current { color: var(--asphalt); font-weight: 600; }
.svc-index a.is-current::before { color: var(--accent); }
.svc-row .lede { font-size: 16px; color: var(--slate); margin-bottom: 22px; max-width: 46ch; }
.svc-row ul { list-style: none; display: grid; gap: 2px; }
.svc-row li {
  position: relative; padding: 11px 0 11px 26px; font-size: 14px;
  border-top: 1px solid var(--line-light);
}
.svc-row li::before {
  content: ''; position: absolute; left: 0; top: 18px;
  width: 10px; height: 2px; background: var(--accent);
}

/* ---- Tools hub: one feature tile, three supporting ---------------------- */
.tools-board { display: grid; grid-template-columns: 1.25fr 1fr; gap: 22px; align-items: stretch; }
@media (max-width: 900px) { .tools-board { grid-template-columns: 1fr; } }
.tools-board .stack { display: grid; gap: 22px; align-content: start; }

.tool-feature {
  position: relative; overflow: hidden;
  background: var(--asphalt); color: var(--paper);
  padding: clamp(28px, 4vw, 44px);
  display: flex; flex-direction: column; justify-content: space-between;
  min-height: 340px; border-top: 3px solid var(--accent);
}
.tool-feature h3 { color: var(--paper); font-size: clamp(28px, 3.6vw, 40px); margin-bottom: 14px; }
.tool-feature p { color: var(--on-dark); font-size: 15px; max-width: 42ch; margin-bottom: 28px; }
.tool-feature .tool-ico { color: var(--accent); margin-bottom: 20px; }
.tool-feature .tool-link { color: var(--paper); }
@media (hover: hover) { .tool-feature .tool-link:hover { color: var(--accent); } }
/* Sample readout so the tile shows what the tool produces, not just names it. */
.tool-sample {
  display: flex; gap: 26px; flex-wrap: wrap;
  border-top: 1px solid var(--line); padding-top: 20px; margin-top: 8px;
}
.tool-sample div { min-width: 92px; }
.tool-sample .k {
  font-size: 10px; font-weight: 600; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--on-dark-mute); margin-bottom: 6px;
}
.tool-sample .v { font-size: 24px; font-weight: 600; color: var(--chrome); font-variant-numeric: tabular-nums; }
.tool-sample .v.good { color: var(--signal); }

.tool-row {
  display: grid; grid-template-columns: 52px 1fr auto; gap: 20px; align-items: center;
  background: var(--white); border: 1px solid var(--line-light); padding: 24px 26px;
  text-decoration: none; transition: transform var(--t-base) var(--ease-out), box-shadow var(--t-base), border-color var(--t-base);
}
@media (hover: hover) {
  .tool-row:hover { transform: translateX(4px); box-shadow: var(--shadow-2); border-color: var(--accent); }
}
.tool-row .tool-ico { color: var(--accent); margin: 0; }
.tool-row h3 { font-size: 20px; margin-bottom: 4px; }
.tool-row p { font-size: 13px; color: var(--slate); margin: 0; }
.tool-row .go { color: var(--accent); font-size: 20px; line-height: 1; }
@media (max-width: 480px) { .tool-row { grid-template-columns: 1fr; gap: 12px; } .tool-row .go { display: none; } }

/* ---- Contact: full-height split screen ---------------------------------- */
/* ---- Form layout: elevated card + sticky rail ---------------------------
   Used by every form on the site. It started as the carrier-setup layout;
   Contact used to be a full-bleed split screen with the fields sitting
   straight on the page background, so the same act — sending HHL your
   details — looked like two different products. Now there is one pattern and
   one place to change it.

   The rail sticks because on a nine-field form the answer to "what happens
   after I press this" scrolls off the top exactly when the visitor starts
   wondering about it.
   ------------------------------------------------------------------------ */
.form-layout { display: grid; grid-template-columns: 1fr 320px; gap: 48px; align-items: start; }
@media (max-width: 1020px) { .form-layout { grid-template-columns: 1fr; gap: 32px; } }

.form-card-title { font-size: clamp(23px, 2.8vw, 30px); margin-bottom: 26px; }
.form-card .eyebrow { margin-bottom: 12px; }

.form-rail { position: sticky; top: calc(var(--header-h) + 28px); }
@media (max-width: 1020px) { .form-rail { position: static; } }
.form-rail-inner { background: var(--asphalt); color: var(--paper); padding: 30px 26px; border-top: 3px solid var(--accent); }
.form-rail h2 {
  font-family: var(--font-body); font-size: 10px; font-weight: 600;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--on-dark-mute); margin-bottom: 22px;
}
.rail-step { position: relative; padding: 0 0 26px 30px; }
.rail-step:last-of-type { padding-bottom: 0; }
.rail-step::before {
  content: ''; position: absolute; left: 4px; top: 6px;
  width: 9px; height: 9px; background: var(--accent); transform: rotate(45deg);
}
.rail-step::after {
  content: ''; position: absolute; left: 8px; top: 20px; bottom: 4px;
  width: 1px; background: var(--line);
}
.rail-step:last-of-type::after { display: none; }
.rail-step .k { font-size: 10px; font-weight: 600; letter-spacing: 0.12em; text-transform: uppercase; color: var(--accent); margin-bottom: 6px; }
.rail-step h3 { font-size: 17px; color: var(--paper); margin-bottom: 5px; }
/* Headings are uppercased site-wide, which is wrong for an address:
   SALES@HHLOGS.COM reads as a different string from the one you type. */
.rail-step h3.lower { text-transform: none; letter-spacing: 0; font-size: 16px; }
/* Contact's rail puts the phone numbers in the h3 slot, so they have to be
   tappable without looking like body links. */
.rail-step h3 a { color: var(--paper); text-decoration: none; }
@media (hover: hover) { .rail-step h3 a:hover { color: var(--accent-hi); } }
.rail-step p { font-size: 13px; color: var(--on-dark-mute); line-height: 1.6; }
.rail-step p a { color: var(--on-dark); }
.rail-help { margin-top: 26px; padding-top: 22px; border-top: 1px solid var(--line); font-size: 13px; color: var(--on-dark-mute); }
.rail-help a { color: var(--paper); font-weight: 600; }

/* ---- About: milestone timeline + pull quote ----------------------------- */
.pullquote {
  max-width: 900px; margin: 0 auto clamp(40px, 5vw, 60px);
  font-family: var(--font-display); font-weight: 700; text-transform: none;
  font-size: clamp(22px, 3.4vw, 36px); line-height: 1.3; letter-spacing: -0.02em;
  text-align: center; color: var(--asphalt); position: relative; padding: 0 clamp(16px, 4vw, 48px);
}
.pullquote::before, .pullquote::after {
  content: ''; display: block; width: 64px; height: 3px;
  background: var(--accent); margin: 0 auto 26px;
}
.pullquote::after { margin: 26px auto 0; }
.pullquote cite { display: block; font-family: var(--font-body); font-style: normal; font-size: 13px; font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase; color: var(--slate); margin-top: 22px; }

.timeline { display: grid; grid-template-columns: repeat(4, 1fr); gap: 0; margin-top: 8px; }
@media (max-width: 900px) { .timeline { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 520px) { .timeline { grid-template-columns: 1fr; } }
.tl-item { position: relative; padding: 34px 24px 0 0; border-top: 2px solid var(--line); }
.tl-item::before {
  content: ''; position: absolute; top: -7px; left: 0;
  width: 12px; height: 12px; background: var(--accent); transform: rotate(45deg);
}
.tl-item .k { font-size: 11px; font-weight: 600; letter-spacing: 0.12em; text-transform: uppercase; color: var(--accent); margin-bottom: 10px; }
.tl-item h3 { font-size: 19px; margin-bottom: 8px; }
.tl-item p { font-size: 14px; color: var(--slate); }
.on-dark .tl-item { border-top-color: var(--line); }
.on-dark .tl-item h3 { color: var(--paper); }
.on-dark .tl-item p { color: var(--on-dark); }

/* ---- Blog: featured post + compact list --------------------------------- */


/* ---- Legal: sticky contents rail ---------------------------------------- */
.legal-layout { display: grid; grid-template-columns: 240px 1fr; gap: 56px; align-items: start; }
@media (max-width: 1020px) { .legal-layout { grid-template-columns: 1fr; gap: 28px; } }
.legal-toc { position: sticky; top: calc(var(--header-h) + 28px); font-size: 14px; }
@media (max-width: 1020px) { .legal-toc { position: static; border: 1px solid var(--line-light); padding: 22px; background: var(--white); } }
.legal-toc h2 {
  font-family: var(--font-body); font-size: 10px; font-weight: 600;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--slate);
  margin-bottom: 14px; padding-bottom: 12px; border-bottom: 1px solid var(--line-light);
}
.legal-toc ol { list-style: none; counter-reset: toc; }
.legal-toc li { counter-increment: toc; }
.legal-toc a {
  display: flex; gap: 10px; padding: 7px 0; text-decoration: none;
  color: var(--slate); font-size: 13.5px; line-height: 1.45;
  transition: color var(--t-fast);
}
.legal-toc a::before { content: counter(toc); font-weight: 600; color: var(--accent); min-width: 16px; }
@media (hover: hover) { .legal-toc a:hover { color: var(--asphalt); } }

/* == 25 Utilities ========================================================= */
.visually-hidden {
  position: absolute !important; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.text-center { text-align: center; }
.narrow { max-width: 800px; }
.mt-16 { margin-top: 16px; }
.mt-24 { margin-top: 24px; }
.mt-32 { margin-top: 32px; }
.mt-56 { margin-top: 56px; }

/* == 25 Print ============================================================= */
@media print {
  .site-header, .hero-fab, .hero-panel, .final-cta, .skip-link,
  .board-scan, #hhl-loader, .scroll-rail, .hero-bg { display: none !important; }
  body { background: #fff; color: #000; }
  .hero, .page-hero, .process-strip, .board-band, .gallery-band, .site-footer, .on-dark {
    background: #fff !important; color: #000 !important;
  }
  .hero h1, .page-hero h1, .on-dark .section-head h2, .mile h3, .foot-grid h2 { color: #000 !important; }
  a[href^='http']::after { content: ' (' attr(href) ')'; font-size: 10px; }
  section { padding-block: 22px; page-break-inside: avoid; }
  .js-on [data-reveal] { opacity: 1 !important; transform: none !important; }
}

/* == Fleet ring ===========================================================
   A 3D carousel: every card sits on the surface of a cylinder at
   rotateY(θ) translateZ(r), inside one perspective container. The parent
   carries a single rotateY, so spinning the whole ring is one composited
   transform rather than 48 animated elements.

   θ comes from --i and --n, so the ring adapts to however many photos exist.
   The radius has to grow with the count or the cards overlap into a solid
   band: r ≈ (card width × n) / 2π, expressed here as a calc so nobody has to
   recompute it when the client sends the rest of the fleet.
   ======================================================================== */
.fleet-band { background: var(--asphalt); overflow: hidden; }

/* -- Floor: no JS, no 3D. A plain grid that works and opens every photo. -- */
.ring-track {
  list-style: none;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 10px; max-width: var(--wrap); margin-inline: auto; padding-inline: var(--gutter);
}
.ring-hit {
  display: block; width: 100%; padding: 0; cursor: pointer;
  background: var(--asphalt-2); border: 1px solid var(--line);
  border-radius: var(--r-sm); overflow: hidden;
}
.ring-hit img { display: block; width: 100%; aspect-ratio: 4 / 3; object-fit: cover; }
.ring-focus, .ring-lightbox { display: none; }

/* -- Enhanced: the actual ring. Only once JS confirms it can run. -------- */
@media (min-width: 981px) {
  .js-on .ring-stage[data-fleet-ring] {
    position: relative;
    height: 560px; margin-top: clamp(56px, 6vw, 92px);
    display: grid; place-items: center;
    /* clip, not hidden: the ring's far side is meant to run off the edges. */
    overflow: clip;
  }

  .js-on .ring-viewport {
    position: absolute; inset: 0;
    perspective: 1250px; perspective-origin: 50% 38%;
  }

  .js-on .ring-track {
    /* 62%, not 46%. Measured: the cards spanned 438px but sat 99px above the
       stage centre, so they overhung the top edge by 38px and crowded the
       heading above. overflow:clip does not save you here — a preserve-3d
       subtree paints outside its clipping ancestor. Move the track instead. */
    position: absolute; left: 50%; top: 62%;
    display: block; width: 0; height: 0; padding: 0; margin: 0; gap: 0;
    transform-style: preserve-3d;
    /* 20deg, not 9. At a shallow angle the ring reads as a drum seen edge-on;
       tilting further looks down into it, which is what makes the far side
       visible above the near side and gives the ellipse its shape. */
    transform: rotateX(20deg) rotateY(var(--spin, 0deg));
    will-change: transform;
  }

  .js-on .ring-card {
    /* Radius is fixed; the CARD is derived from it. Deriving the radius from
       the card count flattens the ring into a straight band (that was the
       first bug here). Fixing both instead means every extra photo eats into
       the same circumference, and at 48 the cards ended up twice as wide as
       their slot — a solid belt with nothing readable in it.

       So: hold the radius, and size the card to the arc it actually gets.
       1.35 is the overlap factor — each card is 35% wider than its slot, so
       neighbours tuck behind each other without swallowing each other. That
       ratio now holds at any photo count. */
    --radius: clamp(380px, 32vw, 540px);
    --slot: calc(6.2832 * var(--radius) / var(--n));
    --card-w: calc(var(--slot) * 1.35);
    position: absolute; left: calc(var(--card-w) / -2); top: -58px;
    width: var(--card-w);
    transform: rotateY(calc(var(--i) * (360deg / var(--n)))) translateZ(var(--radius));
    transition: opacity var(--t-base);
  }

  .js-on .ring-card .ring-hit img { aspect-ratio: 3 / 4; }
  .js-on .ring-card .ring-hit {
    border-radius: var(--r-xs);
    box-shadow: 0 10px 26px rgba(6, 10, 20, 0.5);
    transition: transform var(--t-base) var(--ease-out), border-color var(--t-base), box-shadow var(--t-base);
  }
  .js-on .ring-card .ring-hit:hover,
  .js-on .ring-card .ring-hit:focus-visible {
    transform: scale(1.14); border-color: var(--accent);
    box-shadow: 0 16px 40px rgba(6, 10, 20, 0.6), 0 0 0 1px var(--accent);
    z-index: 2;
  }
  /* The far half of the ring is behind the centre card, so it steps back
     rather than competing with it. */
  .js-on .ring-card.is-back .ring-hit { opacity: 0.42; }

  /* -- Centre preview -------------------------------------------------- */
  .js-on .ring-focus {
    display: block; position: relative; z-index: 3; margin: 0;
    width: 300px; pointer-events: none;
    opacity: 0; transform: translateY(10px) scale(0.97);
    transition: opacity var(--t-base), transform var(--t-base) var(--ease-out);
  }
  .js-on .ring-focus.is-on { opacity: 1; transform: none; }
  .js-on .ring-focus img {
    display: block; width: 100%; aspect-ratio: 4 / 3; object-fit: cover;
    border-radius: var(--r-md); border: 1px solid rgba(247, 245, 241, 0.18);
    box-shadow: 0 30px 70px rgba(6, 10, 20, 0.72);
    background: var(--asphalt-2);
  }
  .js-on .ring-focus figcaption {
    display: grid; gap: 3px; justify-items: center; text-align: center; margin-top: 14px;
  }
  .js-on .rf-k { font-size: 10px; font-weight: 600; letter-spacing: 0.14em; text-transform: uppercase; color: var(--on-dark-mute); }
  .js-on .rf-v { font-family: var(--font-display); font-weight: 700; font-size: 16px; line-height: 1.35; color: var(--paper); max-width: 30ch; }
  .js-on .rf-cta { font-size: 11px; font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase; color: var(--accent); }

  .js-on .ring-stage.is-grabbing { cursor: grabbing; }
  .js-on .ring-stage { cursor: grab; }
}

/* Rotation is the point of the ring; if it is unwelcome, the grid is fine. */
@media (prefers-reduced-motion: reduce) {
  .js-on .ring-stage[data-fleet-ring] { height: auto; display: block; overflow: visible; }
  .js-on .ring-viewport { position: static; perspective: none; }
  .js-on .ring-track {
    position: static; width: auto; height: auto; transform: none;
    display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    gap: 10px; padding-inline: var(--gutter);
  }
  .js-on .ring-card { position: static; width: auto; transform: none; }
  .js-on .ring-focus { display: none; }
}

.ring-note {
  margin-top: clamp(24px, 3vw, 38px); font-size: 13px;
  color: var(--on-dark-mute); text-align: center; max-width: 70ch; margin-inline: auto;
}

/* -- Lightbox ------------------------------------------------------------
   Everything here is set explicitly rather than leaning on the UA stylesheet.
   A modal <dialog> centres itself with inset:0 + margin:auto, and the first
   version of this only declared display:grid — which left the panel pinned to
   the top-left, the backdrop covering part of the screen, and the close button
   escaping to the viewport corner because the dialog was not acting as its
   containing block. Declaring the box outright fixes all three. */
.ring-lightbox {
  position: fixed; inset: 0; margin: auto;
  width: min(1080px, 92vw); max-width: 92vw;
  height: fit-content; max-height: 92svh;
  padding: clamp(14px, 2vw, 20px);
  background: var(--asphalt); color: var(--paper);
  border: 1px solid var(--line); border-radius: var(--r-lg);
  box-shadow: 0 40px 120px rgba(4, 7, 14, 0.7);
  overflow: visible;
}
.ring-lightbox:not([open]) { display: none; }
.ring-lightbox[open] { display: grid; justify-items: center; gap: 12px; }
.ring-lightbox::backdrop { background: rgba(6, 10, 20, 0.88); backdrop-filter: blur(6px); }
.ring-lightbox img {
  display: block; max-width: 100%; max-height: 78svh;
  width: auto; height: auto; border-radius: var(--r-md);
}
.rl-caption { font-size: 13px; color: var(--on-dark); text-align: center; max-width: 70ch; }
/* Sits on the panel's corner, not the viewport's — the dialog is the
   positioned ancestor now that position is declared above. */
.rl-close {
  position: absolute; top: -14px; right: -14px; z-index: 2;
  display: grid; place-items: center; width: 42px; height: 42px;
  border-radius: 50%; cursor: pointer; color: var(--paper);
  background: var(--asphalt-2); border: 1px solid rgba(247, 245, 241, 0.28);
  box-shadow: 0 8px 24px rgba(4, 7, 14, 0.6);
  transition: background var(--t-fast), border-color var(--t-fast), transform var(--t-fast);
}
@media (hover: hover) { .rl-close:hover { background: var(--accent); border-color: var(--accent); transform: scale(1.06); } }
