/* ==========================================================================
   "upfithq" — UpfitHQ (upfithq.app)

   Written for this site against the shared block markup. Not the foundation
   sheet recoloured and not servmate's: those are a memorial charity and a
   company somebody rings at two in the morning. This is a product, sold to a
   man who owns a shop.

   THE ONE IDEA, and every decision below follows from it:

   ⚠ HE HAS BEEN SOLD SOFTWARE BEFORE. DO NOT SELL — SHOW.
   The reader runs a police upfit shop on a whiteboard, a clipboard and
   QuickBooks, and three people have already told him a subscription would fix
   it. He is not shopping for enthusiasm. He is looking for evidence that
   whoever built this has stood on a shop floor. So:

     • THE SITE LOOKS LIKE THE SOFTWARE. Dark ground, squared corners, hard
       rules, instrument-panel spacing — the same room the product lives in.
       A bright brochure ABOUT a dark tool is the tell that nobody shipped it.

     • AMBER IS THE ONLY COLOUR THAT ACTS. It is on buttons, on the rule under
       a section heading, and on a figure. It is never a decorative fill, a
       border for its own sake, or a heading colour. The moment amber appears
       on something that does nothing, the sheet has lost its argument.

     • NUMBERS ARE SET IN MONO. Figures, part numbers, and the small labels.
       That is the vocabulary of a parts list, and it reads as a system rather
       than a pitch — which is the whole claim the site is making.

     • NO STOCK PHOTOGRAPHY, EVER. Every image here is a screen of the real
       thing. If there is no screen there is no image, which is why the hero
       carries itself on type and paints before anything downloads.

   Rajdhani and DM Sans are the product's own faces, so the site and the
   software are visibly the same object. JetBrains Mono is the parts list.
   ========================================================================== */

:root {
  /* The ground. Near-black with a blue cast, not pure black: black reads as
     luxury goods, and this is equipment. */
  --pit:        #0b0e13;
  --pit-2:      #11151d;
  --pit-3:      #171d27;
  --pit-4:      #1e2531;

  /* Rules and edges. */
  --steel:      #29323f;
  --steel-lit:  #3a4657;

  /* ⚠ THE ONLY COLOUR THAT ACTS. Safety orange, and the demo shop's own
     accent, so a visitor who clicks through sees the same colour arrive. */
  --amber:      #f26419;
  --amber-lit:  #ff8341;
  --amber-deep: #b8460d;

  /* Text. */
  --paper:      #f2f4f7;
  --paper-2:    #dfe4ea;
  --ash:        #98a2b3;
  --ash-dim:    #6b7585;

  /* A cool secondary, used for nothing that acts — panel edges, the diagram
     tint, a hover ground. Deliberately desaturated so it cannot compete. */
  --cool:       #4a9fd4;

  /* The names the shared markup and the token layer already use, mapped onto
     this palette so an inline style from a block resolves to something from
     it rather than to a default. */
  --blue:         var(--cool);
  --blue-lit:     #6fb8e4;
  --red:          var(--amber);
  --red-lit:      var(--amber-lit);
  --blue-rgb:     74, 159, 212;
  --blue-lit-rgb: 111, 184, 228;
  --red-rgb:      242, 100, 25;

  --ink:        var(--paper);
  --surface:    var(--pit-2);
  --rule-dark:  var(--steel);
  --font-mono:  var(--mono);

  --display: 'Rajdhani', 'Arial Narrow', sans-serif;
  --body:    'DM Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --mono:    'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, monospace;

  /* ⚠ SET AGAINST THE SCREEN IT IS READ ON. 1180 is a laptop number; on
     a 3440-wide monitor it is a third of the width and the whole site
     reads as a strip down the middle. 1280 keeps prose at a sane measure
     (the text caps are per-element) while giving the drawing room. */
  /* The top bar’s height, named once so the drawer can hang below it
     instead of over the burger that opens it. */
  --header-h: 75px;   /* 74px of bar plus its 1px rule */
  --wrap:    1360px;
  --gut:     clamp(1.25rem, 4vw, 2.5rem);

  /* Squared. One small radius exists so a photo does not look like a mistake
     beside a hard-ruled panel, and nothing else uses it. */
  --r:       3px;
}

/* --------------------------------------------------------------------------
   0. Reset and page
   -------------------------------------------------------------------------- */

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  background: var(--pit);
  color: var(--paper);
  font-family: var(--body);
  font-size: 1.0625rem;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

img, svg, video { max-width: 100%; height: auto; display: block; }
a { color: var(--amber-lit); text-decoration-thickness: 1px; text-underline-offset: 3px; }
a:hover { color: var(--paper); }

:focus-visible {
  outline: 2px solid var(--amber-lit);
  outline-offset: 3px;
}

.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 200;
  background: var(--amber); color: #120800;
  padding: 0.75rem 1.25rem; font-weight: 700;
}
.skip-link:focus { left: 0; }

.wrap { width: 100%; max-width: var(--wrap); margin-inline: auto; padding-inline: var(--gut); }
.wrap-narrow { width: 100%; max-width: 760px; margin-inline: auto; padding-inline: var(--gut); }

.hp { position: absolute !important; left: -9999px !important; }

/* --------------------------------------------------------------------------
   1. Type
   -------------------------------------------------------------------------- */

h1, h2, h3, h4 {
  font-family: var(--display);
  font-weight: 700;
  line-height: 1.04;
  letter-spacing: -0.005em;
  margin: 0 0 0.6em;
  text-wrap: balance;
}

/* ⚠ UPPERCASE, HUGE, AND LEANING. The reference Kian sent sets its name in
   enormous slanted condensed capitals across a photograph, and that slant is
   most of why it reads as urgent rather than corporate — it is the same lean
   that is on the side of every ambulance. Rajdhani has no italic, so the
   skew is applied; a real oblique would be better and a second family would
   cost the "the site looks like the software" argument the whole sheet rests
   on.
   ⚠ THE SKEW IS ON THE LINES, NOT THE BLOCK. Skewing the h1 skews its
   layout box too, so the left edge no longer lines up with the lead beneath
   it and the whole hero looks knocked out of true. */
.h-hero {
  font-size: clamp(3rem, 11vw, 9.5rem);
  line-height: 0.84;
  letter-spacing: -0.02em;
  text-transform: uppercase;
  margin: 0 0 1.75rem;
}
.hero__title-line {
  display: block;
  transform: skewX(-7deg);
  transform-origin: 0 100%;
}
.hero__title-line em {
  font-style: normal;
  color: var(--amber);
}

.h1 { font-size: clamp(2.25rem, 5vw, 3.5rem); }
.h2 { font-size: clamp(1.85rem, 3.6vw, 2.7rem); }
.h3 { font-size: clamp(1.25rem, 2vw, 1.5rem); line-height: 1.2; }

/* ⚠ THE EYEBROW IS MONO AND SMALL CAPS-ISH, not a coloured tag. It is the
   label on an instrument, which is the whole visual argument. */
.eyebrow {
  font-family: var(--mono);
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--amber);
  margin: 0 0 0.9rem;
}

.lead, .lede {
  font-size: clamp(1.125rem, 1.8vw, 1.3125rem);
  line-height: 1.6;
  color: var(--paper-2);
  max-width: 62ch;
  margin: 0 0 1.5rem;
}

p { margin: 0 0 1.15em; max-width: 70ch; }
p:last-child { margin-bottom: 0; }

.muted, .sub { color: var(--ash); }
.sub { font-size: 0.9375rem; }

strong, b { font-weight: 700; color: #fff; }

.rule-top { border-top: 1px solid var(--steel); padding-top: 2rem; }

.link-arrow {
  font-family: var(--mono);
  font-size: 0.8125rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--amber-lit);
  text-decoration: none;
  white-space: nowrap;
}
.link-arrow::after { content: ' →'; }
.link-arrow:hover { color: #fff; }

/* --------------------------------------------------------------------------
   2. Sections
   -------------------------------------------------------------------------- */

.section { padding: clamp(3.5rem, 8vw, 6.5rem) 0; }
.section--tight, .section.pad { padding-block: clamp(2.5rem, 5vw, 4rem); }

/* ⚠ THE RENDERER'S TONE VOCABULARY IS light | tint | dark | darker, and it
   emits '', section--tone, section--dark, section--dark2 (TONE_CLASS in
   schema/blocks.js). An earlier version of this sheet styled `.tone-default`
   and `.tone-muted`, names I invented — so three sections had no ground at
   all and fell through to the body. Nothing reported it: an unknown tone
   renders as the light one, which on a dark site looks deliberate.

   ⚠ AND EACH TONE CARRIES ITS OWN RAISED SURFACE, rather than every component
   hard-coding a colour. A card is only visible because it differs from what
   it sits on, so a card painted --pit-2 disappears the moment its section is
   --pit-2. Declaring the pair here means a new component reads `--raised` and
   is automatically right on all four grounds, instead of needing an override
   per tone — which is what the ~26 section--dark rules in the other three
   sheets are. */
.section {
  --ground: var(--pit);
  --raised: var(--pit-2);
  background: var(--ground);
}
.section--tone  { --ground: var(--pit-2); --raised: var(--pit-3); }
.section--dark  { --ground: var(--pit-3); --raised: var(--pit-4); }
/* The darkest ground steps its cards DOWN rather than up — there is nothing
   above --pit-4, and a raised surface has to differ in some direction. */
.section--dark2 { --ground: var(--pit-4); --raised: var(--pit-2); }

.sechead { margin-bottom: clamp(2rem, 4vw, 3rem); }
.sechead__text { max-width: 60ch; }

/* ⚠ The short amber rule under a section heading is the one decorative use of
   the colour, and it is allowed because it is a MEASURE — it marks where the
   section starts, the way a scale mark does.

   ⚠ AND IT IS CHEVRON STRIPED, which is the one ornament on this site that is
   borrowed from the trade rather than from design. Kian, 25 Sep 2026: "our
   audience is people that deal with first responders everyday … they are
   rugged and a little crude too. The website should feel that way." Chevron
   striping is on the back of every ambulance and every fire truck these
   people build; a reader recognises it before they read a word of the
   heading. It stays SMALL and it stays on the measure mark — a page banded
   with hazard tape would be costume, not character. */
.sechead .h2::after {
  content: '';
  display: block;
  width: 92px; height: 12px;
  margin-top: 1.1rem;
  background-image: repeating-linear-gradient(
    115deg,
    var(--amber) 0 7px,
    #0c0f14 7px 14px);
  background-color: var(--amber);
}

/* --------------------------------------------------------------------------
   3. Buttons
   -------------------------------------------------------------------------- */

/* ⚠ A BARE .btn IS THE FILLED ONE. That is the renderer's contract, not a
   guess: render/html.js buttons() says "the first is filled" and gives the
   first button `btn` with no modifier, every one after it `btn btn--ghost`.
   This theme had it the other way round — `.btn` outlined and `.btn--ghost`
   unstyled — so both buttons in every pair rendered identically and no page
   had a visible primary action. Found by listing the classes the rendered
   pages use against the classes this file styles; `btn--ghost` came back as
   styled nowhere, which is the tell. */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 0.5rem;
  min-height: 52px;
  padding: 0.85rem 1.6rem;
  border: 1px solid var(--amber);
  border-radius: 0;
  background: var(--amber);
  color: #150800;
  font-family: var(--body);
  font-size: 1rem;
  font-weight: 700;
  line-height: 1.2;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  transition: background-color .14s ease, border-color .14s ease, color .14s ease;
}
.btn:hover { background: var(--amber-lit); border-color: var(--amber-lit); color: #150800; }

/* Secondary: the same shape, no fill. */
.btn--ghost {
  background: transparent;
  border-color: var(--steel-lit);
  color: var(--paper);
}
.btn--ghost:hover { background: var(--pit-4); border-color: var(--ash-dim); color: #fff; }

/* `ink` is the renderer's outline for a light ground. This site has none —
   all four tones are dark — so it reads as the ghost, deliberately. */
.btn--ink { background: transparent; border-color: var(--ash-dim); color: var(--paper); }
.btn--ink:hover { background: var(--pit-4); color: #fff; }

/* Kept as aliases: a block that asks for the primary by name still gets it. */
.btn--primary, .btn.is-primary {
  background: var(--amber);
  border-color: var(--amber);
  color: #150800;
}
.btn--primary:hover, .btn.is-primary:hover {
  background: var(--amber-lit);
  border-color: var(--amber-lit);
  color: #150800;
}

.btn--sm { min-height: 40px; padding: 0.45rem 1rem; font-size: 0.9375rem; }
.btn--block { display: flex; width: 100%; }

.btn-row { display: flex; flex-wrap: wrap; gap: 0.85rem; margin-top: 2rem; }

/* --------------------------------------------------------------------------
   4. Announcement, top bar, header, nav
   -------------------------------------------------------------------------- */

.announce { background: var(--amber-deep); color: #fff; font-size: 0.9375rem; }
.announce__inner { padding: 0.6rem 0; display: flex; gap: 0.75rem; align-items: center; justify-content: center; text-align: center; }
.announce a { color: #fff; }

.topbar { background: var(--pit-2); border-bottom: 1px solid var(--steel); font-size: 0.875rem; color: var(--ash); }
.topbar__inner { display: flex; flex-wrap: wrap; gap: 1rem; align-items: center; justify-content: space-between; padding: 0.5rem 0; }
.topbar__meta { display: flex; flex-wrap: wrap; gap: 1.25rem; }
.topbar__social { display: flex; gap: 0.75rem; }
.topbar a { color: var(--ash); text-decoration: none; }
.topbar a:hover { color: var(--paper); }

.header {
  position: sticky; top: 0; z-index: 90;
  background: rgba(11, 14, 19, 0.92);
  backdrop-filter: saturate(140%) blur(10px);
  border-bottom: 1px solid var(--steel);
}
.header__inner {
  display: flex; align-items: center; gap: 1.5rem;
  min-height: 74px;
}

/* ⚠ THE WORDMARK IS TYPE, NOT AN IMAGE, and that is on purpose until there is
   a real mark. A missing logo file is a broken-image box on the first screen;
   a wordmark is never broken. If a mark arrives, it goes here and this rule
   keeps working for the alt text. */
.brand {
  display: inline-flex; align-items: center; gap: 0.6rem;
  font-family: var(--display);
  font-size: 1.5rem;
  font-weight: 700;
  letter-spacing: 0.01em;
  color: var(--paper);
  text-decoration: none;
  white-space: nowrap;
}
.brand img { max-height: 34px; width: auto; }
.brand:hover { color: #fff; }

.nav { margin-left: auto; }
.nav__list { display: flex; gap: 0.25rem; list-style: none; margin: 0; padding: 0; align-items: center; }
.nav__link {
  display: inline-block;
  padding: 0.6rem 0.85rem;
  color: var(--paper-2);
  font-size: 0.9375rem;
  font-weight: 500;
  text-decoration: none;
  border-bottom: 2px solid transparent;
}
.nav__link:hover { color: #fff; border-bottom-color: var(--amber); }
.nav__item--has-menu { position: relative; }
.nav__menu {
  position: absolute; left: 0; top: 100%; min-width: 220px;
  background: var(--pit-3); border: 1px solid var(--steel);
  list-style: none; margin: 0; padding: 0.4rem;
  display: none;
}
.nav__item--has-menu:hover .nav__menu,
.nav__item--has-menu:focus-within .nav__menu { display: block; }
.nav__menu a { display: block; padding: 0.55rem 0.75rem; color: var(--paper-2); text-decoration: none; font-size: 0.9375rem; }
.nav__menu a:hover { background: var(--pit-4); color: #fff; }
.chev { margin-left: 0.35rem; opacity: 0.6; }

/* ⚠ .header__cta IS THE BOX THE BURGER LIVES IN, NOT JUST THE DEMO BUTTON.
   blocks.js puts both inside it, and this sheet hid the whole box below
   980px — so every phone got a header with a wordmark and no way into the
   menu at all. The burger's own `display: inline-flex` was intact and its
   rect was 0x0, which is what an ancestor set to display:none looks like
   from the inside, and why reading the burger's own style said nothing was
   wrong. Kian found it on his phone; my own 390px capture had shown it and
   I had not looked at the header.
   The box is always on. The inline demo BUTTON is what goes away on a
   phone — the drawer already carries it, along with every nav link. */
.header__cta {
  display: inline-flex; align-items: center; gap: 0.6rem;
  margin-left: auto;
}
.header__cta .btn { display: none; }
@media (min-width: 980px) { .header__cta .btn { display: inline-flex; } }

.nav__toggle, .burger {
  display: inline-flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 5px; padding: 0;
  width: 46px; height: 46px; margin-left: auto;
  background: transparent; border: 1px solid var(--steel-lit); color: var(--paper);
  cursor: pointer;
}
/* THIS SHEET REPLACES THE SHARED ONE, SO IT HAS TO DRAW THE LINES ITSELF.
   The shared theme styles `.burger span`; this sheet never did, so the three
   spans had no size and the menu button on a phone was an empty box, top
   right of the first screen (November 5, 2026). Drawn in currentColor so they
   turn amber with the border while the menu is open. */
.burger span {
  display: block; width: 20px; height: 2px;
  background: currentColor;
  transition: transform .2s ease, opacity .15s ease;
}
body.nav-open .burger span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
body.nav-open .burger span:nth-child(2) { opacity: 0; }
body.nav-open .burger span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }
@media (prefers-reduced-motion: reduce) { .burger span { transition: none; } }
@media (min-width: 980px) { .nav__toggle, .burger { display: none; } }
@media (max-width: 979px) { .nav { display: none; } }

/* ⚠ THE SHARED SCRIPT TOGGLES `body.nav-open` AND `aria-hidden`, NOTHING
   ELSE. An earlier version of this sheet opened on `.drawer[open]` and
   `.is-open` — plausible names that the script never sets, so the burger did
   nothing and the drawer sat off-screen. Read theme/assets/js/site.js before
   inventing a state selector. */
/* ⚠ THE DRAWER HUNG OVER THE BURGER THAT OPENS IT. At `inset: 0` and
   z-index 120, with the scrim at 110 and the header at 90, the drawer and
   the scrim both covered the top bar — so the toggle that closes it was
   buried under the thing it had just opened. There is no close button and
   no outside-tap handler, and Escape is not a gesture on a phone, so the
   only way out was tapping a link, which navigates. Kian: "it doesn’t
   close once open."

   The panel hangs BELOW the bar now and the bar outranks both it and the
   scrim, so the burger stays visible and tappable and turns amber while
   open. The ground is a step lighter with a real shadow, because at
   --pit-2 against --pit it barely read as a panel at all. */
.drawer {
  position: fixed; inset: var(--header-h) 0 0 auto;
  width: min(340px, 86vw); z-index: 120;
  background: var(--pit-3);
  border-left: 1px solid var(--steel-lit);
  transform: translateX(100%);
  visibility: hidden;
  transition: transform .2s ease, visibility 0s linear .2s;
  padding: 1.25rem; overflow-y: auto;
  overscroll-behavior: contain;
}
body.nav-open .header { z-index: 130; }
body.nav-open .burger { border-color: var(--amber); color: var(--amber); }
/* THE SHADOW ONLY EXISTS WHILE IT IS OPEN. Closed, the panel sits just
   past the right edge and a shadow 64px wide spilled back onto the page: a
   dark smudge down the right side of every page on a computer, where the
   menu is never even used (November 5, 2026). Hidden, too, so its links are
   not tabbed into while it is off screen.
   KEYED ON aria-hidden AS WELL AS nav-open: the shared script also sets
   nav-open to lock scrolling behind the screenshot lightbox, and the menu
   must not slide in behind a photo nobody asked it for. */
body.nav-open .drawer[aria-hidden="false"] {
  transform: translateX(0);
  visibility: visible;
  box-shadow: -20px 0 44px rgba(0, 0, 0, 0.55);
  transition: transform .2s ease, visibility 0s;
}

/* The scrim is drawn by this sheet because the markup has no element for it.
   It sits under the drawer and over everything else. */
body.nav-open::after {
  content: ''; position: fixed; inset: 0; z-index: 110;
  background: rgba(5, 7, 10, 0.8);
  touch-action: none;
}
body.nav-open { overflow: hidden; }

.drawer__list { list-style: none; margin: 0; padding: 0; }
.drawer__list a {
  display: flex; align-items: baseline; gap: 0.75rem;
  padding: 0.9rem 0.25rem;
  border-bottom: 1px solid var(--steel);
  color: var(--paper); text-decoration: none; font-size: 1.0625rem;
}
/* The drawer numbers its own items — the shared markup emits <span class="num">
   — and mono suits it, since this sheet already treats a figure as a reading
   off an instrument. */
.drawer__list .num {
  font-family: var(--mono); font-size: 0.75rem; font-weight: 500;
  color: var(--amber); letter-spacing: 0.06em;
}

/* --------------------------------------------------------------------------
   5. Hero — type on a dark ground, no photograph required
   -------------------------------------------------------------------------- */

.hero { position: relative; background: var(--pit); overflow: hidden; }

/* The instrument-panel grid. Very faint, and it is the only ornament on the
   page: it reads as a drawing surface, which is what the product is. */
.hero::before {
  content: '';
  position: absolute; inset: 0;
  background-image:
    linear-gradient(to right, rgba(255,255,255,0.028) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(255,255,255,0.028) 1px, transparent 1px);
  background-size: 64px 64px;
  mask-image: radial-gradient(ellipse 80% 70% at 50% 0%, #000 30%, transparent 78%);
  pointer-events: none;
}

.hero__inner { position: relative; padding: clamp(4rem, 9vw, 7rem) 0; }

/* ⚠ .hero__copy IS THE .wrap ELEMENT — blocks.js writes
   `<div class="wrap hero__copy">`. So a max-width here does not narrow the
   text inside the wrap, it REPLACES the wrap: at 46rem the hero became a
   736px column centred in the viewport while every section below it was a
   1280px wrap with its content on the left edge. On a 1440 screen that is a
   slight indent and looks like a choice; on Kian's ultrawide the hero floated
   in the middle of the screen, narrower and further right than the rest of
   the page, and read as small and timid. Cap the TEXT, never the wrap. */
.hero__copy { max-width: var(--wrap); }
.hero .h-hero { max-width: 16ch; }
.hero .lead { max-width: 56ch; color: var(--paper-2); font-size: clamp(1.1875rem, 1.5vw, 1.4375rem); }

/* ⚠ THE HERO IS ONE SCREEN, FULL BLEED, WITH THE TYPE ON TOP OF IT.
   Kian sent upfitnationusa.com: "I think this is more what I meant when I
   said 'in your face'." What that site does is two things and no more — a
   tightly cropped photograph of a lightbar filling the whole viewport, and
   enormous slanted condensed uppercase type sitting on it. (Its COPY is
   thin; the lesson is the picture and the type, not the words.)

   So the drawing stops being a framed plate under the headline and becomes
   the ground the headline sits on: edge to edge, cropped, darkened enough to
   read through. The red and blue still come through, which is the only part
   of a technical drawing that reads at a glance from across a room.

   ⚠ THE ONE INGREDIENT THIS CANNOT HAVE IS A PHOTOGRAPH. There is no stock
   photography on this site by choice, and no rights to a real build: every
   image here is a screen of the software. A shot of an actual vehicle on a
   lift would finish this hero, and it is the one thing to ask Kian for. */
.hero {
  position: relative;
  display: flex; flex-direction: column; justify-content: center;
  min-height: min(92vh, 940px);
  overflow: hidden;
}
.hero__media {
  position: absolute; inset: 0; order: 0; z-index: 0;
  width: 100%; max-width: none; margin: 0; padding: 0;
}
.hero__media img {
  width: 100%; height: 100%;
  object-fit: cover; object-position: center 42%;
  border: 0; padding: 0; background: none;
  opacity: 0.34;
  filter: contrast(1.08) saturate(1.35);
}
/* The scrim. Heavier at the top and bottom so the nav and the buttons keep
   their contrast, lighter across the middle where the vehicle is. */
.hero::after {
  content: '';
  position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: linear-gradient(180deg,
    rgba(11,14,19,0.86) 0%,
    rgba(11,14,19,0.52) 38%,
    rgba(11,14,19,0.62) 68%,
    rgba(11,14,19,0.95) 100%);
}
.hero__inner { position: relative; z-index: 2; }
.hero:not(.hero--bare) .hero__inner { background: none; }

/* --------------------------------------------------------------------------
   6. Page hero, breadcrumbs
   -------------------------------------------------------------------------- */

.pagehero { position: relative; background: var(--pit-2); border-bottom: 1px solid var(--steel); overflow: hidden; }
.pagehero__inner { position: relative; padding: clamp(2.75rem, 7vw, 5rem) 0 clamp(2.25rem, 5vw, 3.5rem); }
.pagehero__media { position: absolute; inset: 0; }
.pagehero__media img { width: 100%; height: 100%; object-fit: cover; opacity: 0.18; }
.pagehero .lead { max-width: 58ch; }

.crumbs {
  display: flex; flex-wrap: wrap; gap: 0.5rem;
  list-style: none; margin: 0 0 1.25rem; padding: 0;
  font-family: var(--mono); font-size: 0.75rem; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--ash-dim);
}
.crumbs li + li::before { content: '/ '; color: var(--steel-lit); }
.crumbs a { color: var(--ash); text-decoration: none; }
.crumbs a:hover { color: var(--amber-lit); }

/* --------------------------------------------------------------------------
   7. Stat band — the figures, in mono, because they are measurements
   -------------------------------------------------------------------------- */

.statband { background: var(--pit-3); border-block: 1px solid var(--steel); }
.statband.pad { padding-block: clamp(2rem, 4vw, 3rem); }
.statband:not(.pad) { padding-block: clamp(2rem, 4vw, 3rem); }

.statband__grid {
  display: grid; gap: 1px;
  grid-template-columns: repeat(2, 1fr);
  background: var(--steel);
  border: 1px solid var(--steel);
}
@media (min-width: 760px) { .statband__grid { grid-template-columns: repeat(4, 1fr); } }

.stat { background: var(--pit-3); padding: 1.5rem 1.25rem; text-align: center; }
.stat__num, .num {
  display: block;
  font-family: var(--mono);
  font-size: clamp(1.9rem, 4vw, 2.75rem);
  font-weight: 500;
  line-height: 1;
  color: var(--amber);
  letter-spacing: -0.02em;
}
.stat .unit { color: var(--amber); }
.stat__label, .stat .label {
  display: block; margin-top: 0.6rem;
  font-size: 0.875rem; line-height: 1.35; color: var(--ash);
  text-transform: none; letter-spacing: 0;
}
/* Words rather than figures: smaller, so a long one still fits a phone. */
.statband--labels .stat__num, .stat__num.words {
  font-size: clamp(1.0625rem, 2vw, 1.25rem);
  font-family: var(--body); font-weight: 700;
  color: var(--paper);
}

/* --------------------------------------------------------------------------
   8. Cards
   -------------------------------------------------------------------------- */

.cards {
  display: grid; gap: 1px;
  grid-template-columns: 1fr;
  background: var(--steel);
  border: 1px solid var(--steel);
}
@media (min-width: 680px)  { .cards { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1000px) { .cards { grid-template-columns: repeat(3, 1fr); } }
.cards--2 { grid-template-columns: 1fr; }
@media (min-width: 760px) { .cards--2 { grid-template-columns: repeat(2, 1fr); } }

.card {
  background: var(--raised, var(--pit-2));
  display: flex; flex-direction: column;
  transition: background-color .14s ease;
}
/* The hover lifts off whatever ground the card is on, rather than at one
   fixed colour — on a --dark section a fixed --pit-4 hover would be the
   card's resting colour and the hover would do nothing. */
.card:hover { background: var(--steel); }
.card__media img { width: 100%; aspect-ratio: 16 / 10; object-fit: cover; }
.card__body { padding: 1.75rem 1.5rem; flex: 1; }
.card__body .h3 { margin-bottom: 0.6rem; }
.card__body p { color: var(--ash); font-size: 0.9875rem; }
.card__tag {
  display: inline-block; margin-bottom: 0.75rem;
  font-family: var(--mono); font-size: 0.6875rem; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--amber);
}
.card a { text-decoration: none; }
.flip { display: flex; flex-direction: column; }

/* --------------------------------------------------------------------------
   9. Spec list — numbered capabilities
   -------------------------------------------------------------------------- */

.specs { display: grid; gap: 0; border-top: 1px solid var(--steel); }
.spec {
  display: grid; gap: 0.35rem 1.5rem;
  grid-template-columns: auto 1fr;
  padding: 1.75rem 0;
  border-bottom: 1px solid var(--steel);
}
@media (min-width: 860px) { .spec { grid-template-columns: 4.5rem 22rem 1fr; align-items: baseline; } }

.spec__num {
  font-family: var(--mono); font-size: 0.9375rem; font-weight: 500;
  color: var(--amber); letter-spacing: 0.05em;
  grid-row: 1;
}
.spec__title { font-family: var(--display); font-size: 1.3125rem; font-weight: 700; line-height: 1.2; margin: 0; }
.spec__body { color: var(--ash); grid-column: 2 / -1; margin: 0; }
@media (min-width: 860px) { .spec__body { grid-column: 3; } }

/* --------------------------------------------------------------------------
   10. Split — screen beside text, and text alone
   -------------------------------------------------------------------------- */

.split { display: grid; gap: clamp(2rem, 5vw, 4rem); align-items: center; }
@media (min-width: 900px) {
  .split { grid-template-columns: 1fr 1fr; }
  .split[data-image-side="left"] .split__media { order: -1; }
}
.split--bare { grid-template-columns: 1fr; max-width: 70ch; }

/* ⚠ A SCREENSHOT IS FRAMED LIKE A SCREEN, not floated like a photograph. The
   thin steel edge and the dark mat say "this is the software", which is the
   only claim an image on this site is allowed to make. */
.split__media, .framed, .shot {
  border: 1px solid var(--steel-lit);
  background: var(--pit);
  padding: 6px;
}
.split__media img, .framed img, .shot img { width: 100%; }
.shot--tall img { aspect-ratio: 9 / 16; object-fit: cover; object-position: top; }
.shot__cap, .split__media figcaption {
  display: block; padding: 0.7rem 0.25rem 0.1rem;
  font-family: var(--mono); font-size: 0.75rem; letter-spacing: 0.06em;
  color: var(--ash-dim);
}

.split .h2 { margin-bottom: 1rem; }
.split .note { margin-top: 1.25rem; font-size: 0.875rem; color: var(--ash-dim); }

/* The points beside the photo, and the checklist / infolist variants. */
.checklist, .infolist { list-style: none; margin: 1.75rem 0 0; padding: 0; }
.checklist li, .infolist li {
  position: relative; padding: 0 0 0 1.75rem; margin-bottom: 0.85rem;
  color: var(--paper-2);
}
.checklist li::before, .infolist li::before {
  content: ''; position: absolute; left: 0; top: 0.62em;
  width: 9px; height: 9px; background: var(--amber);
}

/* --------------------------------------------------------------------------
   10b. Plans — what it costs

   Same hairline idiom as .cards: a 1px gap over --steel with a 1px border,
   so the columns are separated by a rule rather than by shadows or radius.

   ⚠ THE PRICE IS AMBER AND MONO, and that is the sheet’s own rule rather
   than an exception to it: amber marks a button, the rule under a heading,
   and a FIGURE. Every number on this site is mono, because that is the
   vocabulary of a parts list.

   ⚠ AND THE FEATURED PLAN IS MARKED BY A RULE, NOT BY A FILL. A pricing
   table with one column shouting in colour is the brochure tell this whole
   sheet is written against. The top rule and a small tag are enough to say
   which one is the whole product.
   -------------------------------------------------------------------------- */

.plans {
  display: grid; gap: 1px;
  grid-template-columns: 1fr;
  background: var(--steel);
  border: 1px solid var(--steel);
  margin-top: 2.25rem;
}
@media (min-width: 820px) { .plans--2 { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 820px) { .plans--3 { grid-template-columns: repeat(3, 1fr); } }

.plan {
  background: var(--raised, var(--pit-2));
  padding: 2rem 1.75rem 2.25rem;
  display: flex; flex-direction: column;
}
/* A 2px amber rule along the top edge, inside the cell, so it reads as part
   of the panel rather than as a border around it. */
.plan--lead { box-shadow: inset 0 2px 0 0 var(--amber); }

.plan__head { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; }
.plan__name {
  font-family: var(--display); font-weight: 700;
  font-size: 1.5rem; line-height: 1.1; letter-spacing: 0.01em;
  color: var(--paper); margin: 0;
}
.plan__tag {
  font-family: var(--mono); font-size: 0.6875rem; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--amber); white-space: nowrap;
}

.plan__price {
  font-family: var(--mono); font-weight: 600;
  font-size: clamp(2.25rem, 4.5vw, 2.875rem); line-height: 1;
  color: var(--amber); margin: 1.1rem 0 0;
  font-variant-numeric: tabular-nums;
}
.plan__period {
  font-size: 0.875rem; font-weight: 400; letter-spacing: 0.04em;
  color: var(--ash); white-space: nowrap;
}

.plan__lead { color: var(--ash); font-size: 0.9875rem; margin: 1.1rem 0 0; }

.plan__label {
  font-family: var(--mono); font-size: 0.6875rem; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--ash-dim);
  margin: 1.9rem 0 -0.9rem; padding-top: 1.5rem;
  border-top: 1px solid var(--steel);
}

/* The list grows and the button stays on the floor of the panel, so two
   plans with different numbers of lines still line their buttons up. */
.plan .checklist { flex: 1; }
.plan__btn { align-self: flex-start; margin-top: 2rem; }

.plans__note {
  color: var(--ash-dim); font-size: 0.875rem; margin-top: 1.5rem; max-width: 62ch;
}

/* Add-ons: bought on top of a plan, so a quieter row under the plans. Same
   panel, a smaller name and price, and the amber tag says which plans can
   have it. */
.addons { margin-top: 2.75rem; }
.addons__heading {
  font-family: var(--display); font-weight: 700; font-size: 1.25rem; line-height: 1.15;
  color: var(--paper); margin: 0;
}
.addons__lead { color: var(--ash); font-size: 0.9375rem; margin: 0.5rem 0 0; max-width: 62ch; }
.addons .plans { margin-top: 1.25rem; }
.plan--addon { padding: 1.6rem 1.6rem 1.9rem; }
.plan--addon .plan__name { font-size: 1.25rem; }
.plan--addon .plan__price { font-size: clamp(1.75rem, 3.4vw, 2.125rem); margin-top: 0.85rem; }
.plan--addon .checklist { margin-top: 1.1rem; }

/* --------------------------------------------------------------------------
   11. Steps — numbered process
   -------------------------------------------------------------------------- */

.steps { display: grid; gap: 1px; background: var(--steel); border: 1px solid var(--steel); margin-top: 1rem; }
@media (min-width: 760px)  { .steps { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1060px) { .steps { grid-template-columns: repeat(4, 1fr); } }

.step { background: var(--raised, var(--pit-2)); padding: 1.75rem 1.5rem; }
.step .num, .step__num {
  display: block; margin-bottom: 0.9rem;
  font-family: var(--mono); font-size: 0.8125rem; font-weight: 500;
  letter-spacing: 0.14em; color: var(--amber);
}
.step .h3 { margin-bottom: 0.5rem; font-size: 1.1875rem; }
.step p { color: var(--ash); font-size: 0.9375rem; }

/* Inside a split, the same list runs vertically rather than as a grid. */
.split .steps { grid-template-columns: 1fr; border: 0; background: transparent; gap: 0; }
.split .step { background: transparent; padding: 1.1rem 0; border-bottom: 1px solid var(--steel); }
.split .step:last-child { border-bottom: 0; }

/* --------------------------------------------------------------------------
   12. Quotes
   -------------------------------------------------------------------------- */

.quotes { display: grid; gap: 1.5rem; }
@media (min-width: 860px) { .quotes { grid-template-columns: repeat(2, 1fr); } }
.quote { border-left: 3px solid var(--amber); padding: 0.25rem 0 0.25rem 1.5rem; background: transparent; }
.quote__text { font-size: 1.125rem; line-height: 1.55; color: var(--paper); margin: 0 0 0.9rem; }
.quote__by { font-weight: 700; font-size: 0.9375rem; }
.quote__detail { display: block; color: var(--ash); font-weight: 400; font-size: 0.875rem; }
.quotes__source { margin-top: 1.5rem; font-size: 0.8125rem; color: var(--ash-dim); }

/* --------------------------------------------------------------------------
   13. Brand strip
   -------------------------------------------------------------------------- */

.brandstrip { padding-block: clamp(2rem, 4vw, 3rem); }
.brandstrip__label {
  font-family: var(--mono); font-size: 0.75rem; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--ash-dim); text-align: center; margin-bottom: 1.5rem;
}
.brandstrip__logos { display: flex; flex-wrap: wrap; gap: clamp(1.5rem, 5vw, 3.5rem); align-items: center; justify-content: center; }
.brandstrip__logos img { max-height: 42px; width: auto; opacity: 0.7; filter: grayscale(1); }
.brandstrip__logos img:hover { opacity: 1; filter: none; }

/* --------------------------------------------------------------------------
   14. Gallery, lightbox, team — carried for the day screens arrive
   -------------------------------------------------------------------------- */

.gallery { display: grid; gap: 1px; background: var(--steel); border: 1px solid var(--steel); grid-template-columns: repeat(2, 1fr); }
@media (min-width: 860px) { .gallery { grid-template-columns: repeat(3, 1fr); } }
.gallery button, .gallery a {
  display: flex; flex-direction: column; padding: 0; border: 0;
  background: var(--pit-2); cursor: pointer; text-align: left;
}

/* ⚠ A SCREENSHOT IS CONTAINED, NEVER COVERED. `object-fit: cover` on a 4:3
   cell takes a 3000x1885 screen and throws away its sides, so each tile was a
   vertical sliver of a dark UI — unreadable, and read as broken rather than
   as cropped. Contained on the page's own ground, the whole screen is there
   and the letterboxing looks like a screen in a frame, which is what it is.
   ⚠ AND EVERY TILE IS THE SAME SHAPE. `.shot--tall` gives its image a 9:16
   aspect, which in a three-column grid makes that ROW as tall as a portrait
   phone and leaves its neighbours floating in dead space — the overlapping
   mess Kian photographed. The portrait phone shot is contained like the rest;
   it does not get to set the row height. */
.gallery img {
  width: 100%; aspect-ratio: 16 / 10;
  object-fit: contain; object-position: center;
  background: var(--pit);
}
.gallery .shot--tall img { aspect-ratio: 16 / 10; object-fit: contain; }
.gallery .shot__cap { margin-top: auto; }

.lightbox {
  position: fixed; inset: 0; z-index: 200; display: none;
  background: rgba(5, 7, 10, 0.95);
  align-items: center; justify-content: center; padding: 2rem;
}
.lightbox[open], .lightbox.is-open { display: flex; }
.lightbox img { max-height: 82vh; width: auto; }
.lightbox__cap { position: absolute; bottom: 1.5rem; left: 0; right: 0; text-align: center; color: var(--ash); font-size: 0.875rem; }
.lightbox__close, .lightbox__nav {
  position: absolute; width: 48px; height: 48px;
  background: transparent; border: 1px solid var(--steel-lit); color: #fff; cursor: pointer;
}
.lightbox__close { top: 1.25rem; right: 1.25rem; }
.lightbox__nav--prev { left: 1.25rem; top: 50%; transform: translateY(-50%); }
.lightbox__nav--next { right: 1.25rem; top: 50%; transform: translateY(-50%); }

.team { display: grid; gap: 1.5rem; grid-template-columns: repeat(2, 1fr); }
@media (min-width: 860px) { .team { grid-template-columns: repeat(4, 1fr); } }
.member__photo img { width: 100%; aspect-ratio: 1; object-fit: cover; border: 1px solid var(--steel); }
.member__name { font-family: var(--display); font-size: 1.125rem; font-weight: 700; margin: 0.9rem 0 0.15rem; }
.member__role { color: var(--ash); font-size: 0.875rem; }

/* --------------------------------------------------------------------------
   15. Contact and forms
   -------------------------------------------------------------------------- */

.contact-grid { display: grid; gap: clamp(2rem, 5vw, 3.5rem); }
@media (min-width: 940px) { .contact-grid { grid-template-columns: 1fr 1.15fr; align-items: start; } }

.infolist { margin-top: 0; }

.form { display: grid; gap: 1.1rem; }
.field-row { display: grid; gap: 1.1rem; }
@media (min-width: 620px) { .field-row { grid-template-columns: repeat(2, 1fr); } }

.field { display: grid; gap: 0.4rem; }
.field label, .label {
  font-family: var(--mono); font-size: 0.75rem; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--ash);
}
.req { color: var(--amber); }

.field input, .field select, .field textarea {
  width: 100%;
  padding: 0.85rem 0.9rem;
  background: var(--pit);
  border: 1px solid var(--steel-lit);
  border-radius: 0;
  color: var(--paper);
  font-family: var(--body);
  font-size: 1rem;
  min-height: 52px;
}
.field textarea { min-height: 140px; resize: vertical; }
.field input:focus, .field select:focus, .field textarea:focus {
  outline: none; border-color: var(--amber); box-shadow: inset 0 0 0 1px var(--amber);
}
.field input::placeholder, .field textarea::placeholder { color: var(--ash-dim); }

.field--check { grid-template-columns: auto 1fr; align-items: start; gap: 0.75rem; }
.field--check input { width: 20px; min-height: 20px; height: 20px; margin-top: 0.25rem; accent-color: var(--amber); }
.field--check label { font-family: var(--body); font-size: 0.9375rem; text-transform: none; letter-spacing: 0; color: var(--paper-2); }

.field__error { color: var(--amber-lit); font-size: 0.8125rem; }
.form__note { font-size: 0.8125rem; color: var(--ash-dim); max-width: 52ch; }
.form__status { padding: 0.9rem 1rem; border: 1px solid var(--steel-lit); font-size: 0.9375rem; }
.form__status[data-state="ok"] { border-color: var(--amber); color: var(--paper); }

.map { border: 1px solid var(--steel); filter: grayscale(0.4) contrast(1.1); }
.map iframe { display: block; width: 100%; border: 0; }

.filters { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-bottom: 1.5rem; }
.filter {
  padding: 0.5rem 1rem; min-height: 40px;
  background: transparent; border: 1px solid var(--steel-lit); color: var(--paper-2);
  font-size: 0.9375rem; cursor: pointer;
}
.filter.picked, .filter[aria-pressed="true"] { background: var(--amber); border-color: var(--amber); color: #150800; font-weight: 700; }

/* --------------------------------------------------------------------------
   16. Call to action
   -------------------------------------------------------------------------- */

/* ⚠ THE CTA SETS ITS OWN GROUND, because cta_band has no tone field and the
   markup is `class="section cta"` — so it inherits .section's --ground and
   lands on the same colour as whatever it follows, which is exactly what a
   band must not do. A fallback cannot save it: --ground is always defined by
   then. It is declared, not defaulted. */
.section.cta, .cta { --ground: var(--pit-3); }
.cta { position: relative; background: var(--ground); border-block: 1px solid var(--steel); overflow: hidden; }
.cta__media { position: absolute; inset: 0; }
.cta__media img { width: 100%; height: 100%; object-fit: cover; opacity: 0.16; }
.cta__inner { position: relative; padding: clamp(3rem, 7vw, 5rem) 0; text-align: center; }
.cta__inner .lead { margin-inline: auto; }
.cta__inner .btn-row { justify-content: center; }

/* --------------------------------------------------------------------------
   17. Footer
   -------------------------------------------------------------------------- */

.footer { background: var(--pit-2); border-top: 1px solid var(--steel); padding-top: clamp(3rem, 6vw, 4.5rem); }
.footer__grid { display: grid; gap: 2.5rem; }
@media (min-width: 860px) { .footer__grid { grid-template-columns: 1.4fr repeat(3, 1fr); } }

.footer__brand .brand { margin-bottom: 1rem; }
.footer__brand p { color: var(--ash); font-size: 0.9375rem; max-width: 42ch; }

.footer h3, .footer .h3 {
  font-family: var(--mono); font-size: 0.75rem; font-weight: 500;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--ash-dim);
  margin-bottom: 1rem;
}
.footer ul { list-style: none; margin: 0; padding: 0; }
.footer li { margin-bottom: 0.6rem; }
.footer a { color: var(--paper-2); text-decoration: none; font-size: 0.9375rem; }
.footer a:hover { color: var(--amber-lit); }

.footer__badges { display: flex; flex-wrap: wrap; gap: 1.25rem; align-items: center; margin-top: 1.5rem; }
.footer__badges img { max-height: 40px; width: auto; opacity: 0.75; }

.footer__social { display: flex; gap: 0.75rem; margin-top: 1.25rem; }

.footer__bar {
  margin-top: clamp(2.5rem, 5vw, 3.5rem);
  border-top: 1px solid var(--steel);
  padding: 1.5rem 0;
  display: flex; flex-wrap: wrap; gap: 1rem; justify-content: space-between;
  font-size: 0.8125rem; color: var(--ash-dim);
}
.footer__credit a { color: var(--ash); font-size: 0.8125rem; }

/* --------------------------------------------------------------------------
   18. The call bar — a phone shortcut on small screens
   -------------------------------------------------------------------------- */

.callbar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 110;
  display: none;
  background: var(--amber); color: #150800;
  padding: 0.85rem 1rem; text-align: center;
  font-weight: 700; text-decoration: none;
}
@media (max-width: 720px) { .callbar { display: block; } body { padding-bottom: 56px; } }

/* --------------------------------------------------------------------------
   19. Motion, 404
   -------------------------------------------------------------------------- */

.reveal { opacity: 0; transform: translateY(10px); transition: opacity .5s ease, transform .5s ease; }
.reveal.is-in, .reveal.shown { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .reveal { opacity: 1; transform: none; transition: none; }
}

.lost { padding: clamp(4rem, 10vw, 7rem) 0; text-align: center; }
.lost .h1 { margin-bottom: 1rem; }

/* --------------------------------------------------------------------------
   19b. The states the shared script sets
   --------------------------------------------------------------------------
   ⚠ EVERY CLASS HERE IS SET BY theme/assets/js/site.js, NOT BY THIS SHEET, so
   a sheet that omits one does not fail loudly — it just stops responding. The
   theme test enumerates themes/ and demands all of them precisely because
   this list is invisible from inside the CSS.
   -------------------------------------------------------------------------- */

/* Screen-reader-only. Must actually be off-screen, not merely small. */
.sr-only {
  position: absolute !important;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  clip-path: inset(50%); overflow: hidden; white-space: nowrap;
}

/* The header once the page has scrolled past the hero. A hairline and a
   slightly denser ground — the sticky bar should read as an edge, not as a
   second navigation appearing. */
.header.is-stuck {
  background: rgba(11, 14, 19, 0.97);
  border-bottom-color: var(--steel-lit);
}

/* The current page in the nav and the drawer. Amber is allowed here because
   it marks where you are, which is a reading, not decoration. */
.nav__link[aria-current="page"],
.drawer__list a[aria-current="page"] {
  color: #fff;
  border-bottom-color: var(--amber);
}
.drawer__list a[aria-current="page"] .num { color: #fff; }

/* Reveal-on-scroll staggering. The script sets data-delay; honour it, and
   drop it entirely when the reader has asked for less motion. */
.reveal[data-delay="1"] { transition-delay: .06s; }
.reveal[data-delay="2"] { transition-delay: .12s; }
.reveal[data-delay="3"] { transition-delay: .18s; }
.reveal[data-delay="4"] { transition-delay: .24s; }
.is-visible { opacity: 1 !important; transform: none !important; }
@media (prefers-reduced-motion: reduce) {
  .reveal[data-delay] { transition-delay: 0s !important; }
}

/* ⚠ is-hidden MUST BEAT AN AUTHOR display, the way [hidden] has to. A panel
   the script hides while this sheet gives it `display: grid` stays on screen,
   and every check that reads the property rather than the computed style will
   still say it is hidden. */
.is-hidden { display: none !important; }

/* A field the script has marked invalid. Amber, because it is the colour that
   means "this needs you" everywhere else on the page. */
.has-error input, .has-error select, .has-error textarea {
  border-color: var(--amber);
  box-shadow: inset 0 0 0 1px var(--amber);
}
.has-error .field__error { display: block; }

/* --------------------------------------------------------------------------
   20. Calendar — carried so the block is never unstyled here
   -------------------------------------------------------------------------- */

.cal { border: 1px solid var(--steel); background: var(--pit-2); }
.cal__title, .cal__month-name { font-family: var(--display); font-size: 1.25rem; font-weight: 700; padding: 1rem; }
.cal__body { display: grid; grid-template-columns: repeat(7, 1fr); gap: 1px; background: var(--steel); }
.cal__dow { background: var(--pit-3); padding: 0.5rem; text-align: center; font-family: var(--mono); font-size: 0.6875rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ash-dim); }
.cal__day, .cal__empty { background: var(--pit-2); padding: 0.6rem; min-height: 68px; font-size: 0.875rem; }
.cal__empty { background: var(--pit); }
.cal__date { font-family: var(--mono); color: var(--ash); }
.cal__night { border-left: 3px solid var(--steel-lit); padding-left: 0.4rem; margin-top: 0.3rem; font-size: 0.8125rem; }
/* The three states a date can be in. Amber is the one that can still be had,
   because that is the only one the reader can act on. */
.cal__night--open   { border-left-color: var(--amber); }
.cal__night--booked { border-left-color: var(--steel-lit); opacity: 0.75; }
.cal__night--past   { border-left-color: var(--steel); opacity: 0.45; }
.cal__place { color: var(--ash); display: block; }
.cal__status { font-family: var(--mono); font-size: 0.6875rem; letter-spacing: 0.06em; text-transform: uppercase; color: var(--amber); }
.cal__list { list-style: none; margin: 0; padding: 1rem; }
.cal__note { padding: 1rem; color: var(--ash-dim); font-size: 0.8125rem; }

/* ─── The drawing wants the whole width ──────────────────────────────────
   ⚠ THE FIVE-VIEW SHEET IS 1.93:1, AND HALF A COLUMN MAKES IT UNREADABLE.
   Every other photo on this site is a portrait phone or a screen that reads
   fine at ~520px; this one is a technical drawing whose whole job is to be
   looked at, and beside a long copy column it rendered about 520x270 with
   700px of empty ground above it. Measured in a band capture, not guessed.

   So this one section stacks: the copy at a readable measure, the drawing
   full width beneath it. Keyed on the section id rather than a modifier
   class because the block schema has no field for it — image_side is
   left|right and nothing else — and inventing a field to express "this
   image is wide" would put a layout decision in the content. */
#the-drawing .split { grid-template-columns: 1fr; align-items: start; }
#the-drawing .split__media { order: 2; margin-top: clamp(1.5rem, 3vw, 2.5rem); }

/* ⚠ CAP THE PROSE, NOT THE COLUMN. Capping the container would drag the
   points row in with it and leave a third of the section empty. */
#the-drawing .split .eyebrow,
#the-drawing .split .h2,
#the-drawing .split .lead,
#the-drawing .split > div:not(.split__media) > p { max-width: 68ch; }

/* The points read as columns once the copy is not sharing the row with a
   picture. ⚠ !important because blocks.js writes `grid-template-columns:1fr`
   as an INLINE style on that div — it is forcing one column for the normal
   case, where the points sit in a narrow half-width column beside a photo.
   A theme cannot outrank an inline style any other way, and editing the
   shared renderer to suit one section of one site would be the worse
   trade. */
@media (min-width: 860px) {
  #the-drawing .split .steps {
    grid-template-columns: repeat(3, 1fr) !important;
    gap: 2rem !important;
    margin-top: 2.25rem !important;
  }
  #the-drawing .split .step {
    border-bottom: 0;
    border-top: 1px solid var(--steel);
    padding-top: 1.25rem !important;
  }
}

/* ─── Gaps found by listing rendered classes against this file ───────────
   Every rule below is a class the renderer emits that this theme had never
   styled. Each was invisible in a different way, which is why the audit was
   worth more than another look at a screenshot. */

/* ⚠ image_side HAS NEVER WORKED ON THIS SITE. blocks.js expresses it as the
   class `split--flip`; this file was listening for `[data-image-side]`, an
   attribute the renderer does not write. So every split put its photo on the
   left whatever the content said. `order: 2` moves the media after the copy,
   which is what "photo on the right" means in a two-column grid. */
@media (min-width: 900px) {
  .split--flip .split__media { order: 2; }
}

/* ⚠ THE CONTACT ICONS WERE RENDERING AT ~380px. An inline <svg> with a
   viewBox and no width is sized by its container, and the global
   `svg { max-width: 100%; height: auto }` then stretches it to the full
   column. Two near-black 380px icons pushed the contact page's left column
   down by a thousand pixels. They are 20px and amber now — and the list's
   own ::before bullet is dropped where an icon is present, because a bullet
   AND an icon is two markers for one row. */
.infolist li svg, .checklist li svg {
  width: 20px; height: 20px; flex: 0 0 20px;
  margin-top: 0.2rem;
  fill: var(--amber);
}
.infolist li:has(svg)::before, .checklist li:has(svg)::before { content: none; }
.infolist .value { color: var(--paper); font-weight: 500; }
.infolist .value a { color: var(--amber); }

/* ⚠ AN EMPTY STATUS BOX IS STILL A BOX. .form__status carries a border and
   padding so a submitted form can say what happened; before anybody submits
   it is empty, and it rendered as a blank bordered rectangle above the first
   field that reads as an input nobody labelled. */
.form__status:empty { display: none; }

/* The wordmark a site with no logo gets in the header and footer. */
/* ─── The wordmark ──────────────────────────────────────────────────────
   Kian, 25 Sep 2026: "UpfitHQ in the top left is BORINGGGG, let's make it
   better." It was the company name set in the display face and nothing else,
   which is the CMS's honest fallback for a site with no logo — and reads as
   a placeholder next to a hero shouting in slanted capitals.

   ⚠ IT IS A LOCKUP, NOT A FONT CHOICE, AND IT IS BUILT FROM THE PAGE'S OWN
   VOCABULARY. Capitals and the same -7deg lean as the headline, so the mark
   and the masthead are visibly one thing; and a chevron-striped tab in front
   of it, the same striping as the section measure marks. Chevron striping is
   what is on the back of every rig these readers build, so the mark reads as
   a decal rather than as a typeface.

   ⚠ NO MARKUP CHANGE, DELIBERATELY. blocks.js writes a single
   `<span class="brand__word">` holding the company name, and the honest way
   to get two-tone type or a real symbol is an uploaded logo — not a renderer
   that guesses where to split somebody's name. Everything here is CSS on
   what the shared markup already emits, so the day Kian has a logo it simply
   replaces this and nothing has to be unpicked. */
.brand__word {
  font-family: var(--display);
  font-weight: 700;
  font-size: 1.55rem;
  line-height: 1;
  text-transform: uppercase;
  letter-spacing: 0.015em;
  transform: skewX(-7deg);
  display: inline-block;
}
/* ⚠ THE TAB HANGS OFF THE WORD, NOT OFF .brand. The header wraps the mark
   in <a class="brand">; the FOOTER emits a bare <span class="brand__word">
   with no wrapper at all, so a rule hung on .brand gave the mark a tab in
   one place and not the other — which reads as a bug rather than as
   restraint. On the word it also inherits the skew, so the tab and the
   letters lean together. */
.brand__word::before {
  content: '';
  display: inline-block;
  width: 17px; height: 0.92em;
  margin-right: 0.45rem;
  vertical-align: -0.06em;
  background-image: repeating-linear-gradient(
    115deg,
    var(--amber) 0 5px,
    #0c0f14 5px 10px);
  background-color: var(--amber);
}
/* On the footer the tab would compete with the heading beneath it. */
.footer__brand .brand__word { font-size: 1.35rem; }
/* ™ after the name, header and footer alike (October 6, 2026). ™ needs no
   registration; ® only once the USPTO has registered the mark. It sits on the
   cap line at 40% of the word and inherits the skew, so it reads as part of
   the mark rather than as a footnote. */
.brand__word::after {
  content: '\2122';
  font-size: 0.4em;
  font-weight: 600;
  letter-spacing: 0;
  vertical-align: top;
  margin-left: 0.12em;
}

/* A list row that carries an icon is a ROW, not an indented bullet. The
   checklist's absolute ::before and its 1.75rem indent are for the bullet
   variant; with an icon the same li has to lay out as flex, or the icon
   stacks above the label (svg is display:block globally). */
.infolist li:has(svg), .checklist li:has(svg) {
  display: flex; align-items: flex-start; gap: 0.75rem;
  padding-left: 0;
}

/* ⚠ THE RENDERER ALREADY WRITES THE ARROW, as <span class="chev">&rarr;</span>
   inside the link. This sheet's ::after added a second one, so every one of
   these read "OPEN THE LIVE DEMO → →". Keep the ::after for the links that
   have no chev — html.js's own link-arrow writes none. */
.link-arrow:has(.chev)::after { content: none; }

/* ==========================================================================
   21. The trade's own vocabulary
   Kian: "Anymore BADASS-IRY we can do lol". Three moves, and the test each
   one had to pass is the same: is this something these readers actually
   build, or is it costume? Diamond plate, sirens and a scrolling news ticker
   all failed it. These did not.
   ========================================================================== */

/* ─── 1. A lightbar across the top of the page ──────────────────────────
   Three pixels, red on the left, blue on the right, alternating slowly. It
   is the one thing every person on this site has wired a hundred times, and
   it reads before a single word does.

   ⚠ IT IS THE PRODUCT'S OWN MOTIF, NOT A NEW ONE. The app's sign-in screen
   has run exactly this alternation — bar-red / bar-blue, 2.6s — since it was
   built, and the argument this whole sheet rests on is that the site and the
   software are visibly the same object.

   ⚠ AND IT IS SLOW ON PURPOSE. A real lightbar flashes several times a
   second; at that rate this would be a migraine on a page somebody reads for
   three minutes, and genuinely unsafe for a photosensitive reader. 2.6s per
   cycle reads as "emergency vehicle" without ever reading as a warning. Off
   entirely under prefers-reduced-motion. */
.header::before,
.header::after {
  content: '';
  position: absolute; top: 0; height: 3px;
  z-index: 2; pointer-events: none;
}
.header::before { left: 0;  right: 50%; background: #e8362f; }
.header::after  { left: 50%; right: 0;  background: #2f6ae8; }
@media (prefers-reduced-motion: no-preference) {
  .header::before { animation: bar-red  2.6s ease-in-out infinite; }
  .header::after  { animation: bar-blue 2.6s ease-in-out infinite; }
}
@keyframes bar-red  { 0%, 44% { opacity: 1; }    50%, 94% { opacity: 0.22; } 100% { opacity: 1; } }
@keyframes bar-blue { 0%, 44% { opacity: 0.22; } 50%, 94% { opacity: 1; }    100% { opacity: 0.22; } }

/* ─── 2. Section headings shout ─────────────────────────────────────────
   Only the hero was in capitals, so everything under it went quiet at the
   first scroll. The LEAN stays exclusive to the hero and the wordmark —
   skewing an h2 skews its layout box, so its left edge stops lining up with
   the eyebrow above and the lead below, and the section reads knocked out of
   true rather than urgent. Capitals harden it; the slant would break it. */
.h2, .cta .h1, .pagehero .h1 { text-transform: uppercase; letter-spacing: -0.005em; }

/* ─── 3. A chevron band before the call to action ───────────────────────
   Full bleed, edge to edge, the same striping as the wordmark tab and the
   section marks. It is the back of a rig, and it is the one place on the
   page where the motif gets to be big — immediately before the only thing
   the page is actually asking for. */
.cta::before {
  content: '';
  position: absolute; top: 0; left: 0; right: 0; height: 14px;
  z-index: 2;
  background-image: repeating-linear-gradient(
    115deg,
    var(--amber) 0 12px,
    #0c0f14 12px 24px);
  background-color: var(--amber);
}


/* ---------------------------------------------------------------------------
   Whelen part number decoder
   A part number is a code, so it is set in the mono face everywhere it
   appears — in the box you type into and in every answer. Reading "M6H"
   against "M6M" in a proportional face is how somebody orders the wrong one.
   --------------------------------------------------------------------------- */
.wd { background: var(--raised); border: 1px solid var(--rule-dark); border-radius: var(--r); padding: 1.5rem; margin-top: 2rem; }
.wd__label { display: block; font-family: var(--display); font-size: .8125rem; font-weight: 600; letter-spacing: .09em; text-transform: uppercase; color: var(--ash); margin-bottom: .6rem; }
.wd__row { display: flex; gap: .75rem; flex-wrap: wrap; }
.wd__input { flex: 1 1 14rem; min-width: 0; font-family: var(--font-mono); font-size: 1.125rem; letter-spacing: .04em; text-transform: uppercase; padding: .85rem 1rem; color: var(--ink); background: var(--pit); border: 1px solid var(--rule-dark); border-radius: var(--r); }
.wd__input:focus-visible { outline: 2px solid var(--amber); outline-offset: 2px; border-color: var(--amber); }
.wd__input::placeholder { color: var(--ash-dim); text-transform: none; letter-spacing: normal; }
.wd__go { flex: 0 0 auto; }

.wd__out:not(:empty) { margin-top: 1.5rem; }
.wd__count { font-family: var(--display); font-size: .8125rem; font-weight: 600; letter-spacing: .09em; text-transform: uppercase; color: var(--amber); margin: 0 0 .75rem; }
.wd__msg { color: var(--ash); margin: 0; }
.wd__msg code { font-family: var(--font-mono); color: var(--ink); background: var(--pit); padding: .1em .4em; border-radius: 3px; }

.wd__list { list-style: none; margin: 0; padding: 0; display: grid; gap: .5rem; grid-template-columns: 1fr; }
@media (min-width: 620px) { .wd__list { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 960px) { .wd__list { grid-template-columns: repeat(3, 1fr); } }
.wd__list li { display: flex; flex-direction: column; gap: .15rem; background: var(--pit); border: 1px solid var(--rule-dark); border-radius: var(--r); padding: .7rem .9rem; }
.wd__pn { font-family: var(--font-mono); font-size: 1rem; font-weight: 500; color: var(--ink); letter-spacing: .04em; }
.wd__col { font-size: .8125rem; color: var(--ash); }

/* The legend is the half a crawler reads and the half that works with no
   script, so it is ordinary content rather than a panel of the tool. */
.wd__legends { display: grid; gap: 1.5rem; grid-template-columns: 1fr; margin-top: 2.5rem; }
@media (min-width: 760px) { .wd__legends { grid-template-columns: repeat(3, 1fr); } }
.wd__legend-title { font-family: var(--font-mono); font-size: .9375rem; font-weight: 500; color: var(--amber); margin: 0 0 .25rem; }
.wd__legend-sub { font-size: .8125rem; color: var(--ash-dim); margin: 0 0 .75rem; }
.wd__codes { display: grid; grid-template-columns: auto 1fr; gap: .3rem .9rem; margin: 0; font-size: .875rem; }
.wd__codes dt { font-family: var(--font-mono); color: var(--ink); font-weight: 500; }
.wd__codes dd { margin: 0; color: var(--ash); }
.wd__note { font-size: .8125rem; color: var(--ash-dim); margin-top: 2rem; }
