/* ============================================================================
   PEDALS & PADDLES BIKE REPAIR — production stylesheet
   Direction: "Garage Light" (concept B). Carried forward from
   design-preview/concept-b-garage-light.html — organised, not redesigned.

   CONTENTS
     01  Fonts (self-hosted WOFF2)
     02  Tokens
     03  Reset + base
     04  Type primitives
     05  Buttons
     05b Tracked type — optical inset correction        <- read before adding letter-spacing
     06  Review affordances  (.notice / .smp / .needs)   <- review mode only
     07  Header
     08  Hero
     09  Photo slots
     10  Section shell
     11  Pricing (tiers + à la carte)
     12  What I fix
     13  How it works
     14  Dark band
     15  About
     16  Booking form
     17  Footer
     18  Sticky mobile bar
     19  Breakpoints
     20  Motion + scroll
     21  Service-page prose (markdown-rendered)
     22  Promotions, kids line, à la carte units
     23  Review-mode feedback control                    <- review mode only

   HOUSE RULES, do not break them:
     * navy + orange are the only two UI colours. --flag-red / --flag-yel are BADGE ONLY.
     * orange never carries small text on the light ground. Use --orange-ink for orange text.
     * every number on the site is monospaced (.num, or a component that sets --mono).
     * one dark navy band per page, maximum.
   ============================================================================ */


/* ============================================================
   01 · FONTS — self-hosted WOFF2, latin + latin-ext subsets
   Archivo and IBM Plex Sans are variable; IBM Plex Mono is per weight.
   Only the display face is preloaded (see _Layout.cshtml).
   ============================================================ */

/* Archivo — variable wght 400..900, wdth 100%..125% */
@font-face{
  font-family:'Archivo';font-style:normal;font-display:swap;
  font-weight:400 900;font-stretch:100% 125%;
  src:url('/fonts/archivo-var-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face{
  font-family:'Archivo';font-style:normal;font-display:swap;
  font-weight:400 900;font-stretch:100% 125%;
  src:url('/fonts/archivo-var-latin-ext.woff2') format('woff2');
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}

/* IBM Plex Sans — variable wght */
@font-face{
  font-family:'IBM Plex Sans';font-style:normal;font-display:swap;font-weight:100 700;
  src:url('/fonts/plex-sans-var-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face{
  font-family:'IBM Plex Sans';font-style:normal;font-display:swap;font-weight:100 700;
  src:url('/fonts/plex-sans-var-latin-ext.woff2') format('woff2');
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}

/* IBM Plex Mono — 400 / 500 / 600 */
@font-face{
  font-family:'IBM Plex Mono';font-style:normal;font-display:swap;font-weight:400;
  src:url('/fonts/plex-mono-400-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face{
  font-family:'IBM Plex Mono';font-style:normal;font-display:swap;font-weight:400;
  src:url('/fonts/plex-mono-400-latin-ext.woff2') format('woff2');
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}
@font-face{
  font-family:'IBM Plex Mono';font-style:normal;font-display:swap;font-weight:500;
  src:url('/fonts/plex-mono-500-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face{
  font-family:'IBM Plex Mono';font-style:normal;font-display:swap;font-weight:500;
  src:url('/fonts/plex-mono-500-latin-ext.woff2') format('woff2');
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}
@font-face{
  font-family:'IBM Plex Mono';font-style:normal;font-display:swap;font-weight:600;
  src:url('/fonts/plex-mono-600-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face{
  font-family:'IBM Plex Mono';font-style:normal;font-display:swap;font-weight:600;
  src:url('/fonts/plex-mono-600-latin-ext.woff2') format('woff2');
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}


/* ============================================================
   02 · TOKENS
   ============================================================ */
:root{
  /* --- warm canvas --------------------------------------
     STILL alabaster, deliberately NOT neutral grey and never #FFF — the house
     wf-design-standards rule forbids a white-lab ground.

     Revised 2026-08-01 (owner: "the general background color is too yellow").
     The base was never the problem; the RAMP was. Warmth, measured as (R − B),
     used to CLIMB as the ramp darkened — bone +8, bone-2 +14, bone-3 +18,
     rule +20 — so every surface the eye actually lands on (tier rows, panels,
     hairlines) was 2–2.5x warmer than the canvas it sat on, and the page read
     yellow even though the canvas alone looked fine.

     The ramp below is roughly HALF the old spread and stays near-constant as it
     darkens (+5 / +7 / +9 / +9 / +12) instead of escalating. Keep it that way:
     if you add a surface, match the warmth of its neighbours, don't out-warm them. */
  /* Warmth pass 2 (2026-08-01). Pass 1 halved the ramp (+8→+5 at the ground) and the owner
     still read the body background as yellow, so this takes the ground to +2 — essentially a
     neutral off-white that keeps a trace of warmth rather than an alabaster that keeps a trace
     of neutrality. The ramp below stays FLAT: warmth must not climb as tones darken, which is
     what made pass 1 read warm even after the ground was corrected.
     Still not #FFF — `wf-design-standards` forbids a white-lab canvas — but this is the floor.
     Two rgba() literals track --bone and were changed with it: .hdr (.92) and .bar (.96). */
  --bone:      #F7F6F5;   /* page ground — neutral off-white (R−B +2) */
  --bone-2:    #F0EFED;   /* recessed surface, photo slots   (R−B +3) */
  --bone-3:    #E6E4E1;   /* concrete, deeper fill           (R−B +5) */
  --rule:      #D4D2CE;   /* hairlines                       (R−B +6) */
  --rule-soft: #E3E1DE;   /*                                 (R−B +5) */

  /* --- ink ---------------------------------------------- */
  --ink:       #16181A;   /* headlines + body */
  --ink-70:    #444A4E;
  --ink-55:    #5A6064;   /* secondary prose — 5.9:1 on bone */

  /* --- brand -------------------------------------------- */
  --navy:      #163259;   /* from the badge — primary UI */
  --navy-lift: #1E4275;
  --orange:    #E8601C;   /* structural accent ONLY — fills, rules, ticks, focus rings */
  --orange-ink:#A6390A;   /* dried-ink orange — the ONLY orange safe for text on bone (5.9:1) */
  --on-navy:   #F3F0EA;
  --on-navy-2: #A9BAD1;   /* 6.5:1 on navy */

  /* badge-only, never UI */
  --flag-red:  #C8102E;
  --flag-yel:  #FFC72C;

  /* --- type --------------------------------------------- */
  --display: "Archivo", system-ui, sans-serif;
  --text:    "IBM Plex Sans", system-ui, sans-serif;
  --mono:    "IBM Plex Mono", ui-monospace, monospace;

  --gut:  clamp(20px, 5vw, 64px);
  --max:  1240px;
  --sect: clamp(72px, 11vw, 148px);

  /* --- price columns ------------------------------------
     Every price list on this site right-aligns into a FIXED column, so the
     numbers share one x-position and the eye can scan them. Widths are sized to
     the longest published value plus its unit — $275 for a tier, "per wheel"
     under an à la carte price. Widen these if a longer price or unit ever ships;
     do NOT switch a list back to auto-width. See section 11. */
  --price-col:      5.4rem;   /* à la carte / dot-leader menus */
  --price-col-tier: 7.5rem;   /* tune-up tiers + the kids line */
}
@media (min-width:900px){
  :root{ --price-col-tier:210px }
}


/* ============================================================
   03 · RESET + BASE
   ============================================================ */
*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
body{
  margin:0;
  background:var(--bone);
  color:var(--ink);
  font-family:var(--text);
  font-size:17px;
  line-height:1.62;
  -webkit-font-smoothing:antialiased;
  overflow-x:hidden;
}
img,svg{max-width:100%}
img{height:auto}
a{color:inherit}

/* focus — visible on every interactive element */
:focus-visible{
  outline:2px solid var(--orange);
  outline-offset:3px;
  border-radius:1px;
}
.band :focus-visible{outline-color:var(--orange)}

.wrap{width:100%;max-width:var(--max);margin:0 auto;padding-inline:var(--gut)}
.sr{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}

/* skip link — first focusable thing on the page */
.skip{
  position:absolute;left:-9999px;top:0;z-index:200;
  background:var(--navy);color:var(--on-navy);
  font-family:var(--mono);font-size:.78rem;letter-spacing:.1em;text-transform:uppercase;
  --trk:.1em; --pad-x:20px;
  padding-block:14px;text-decoration:none;
}
.skip:focus{left:0}


/* ============================================================
   04 · TYPE PRIMITIVES
   ============================================================ */
/* Display type.
   Revised 2026-08-01 (owner: "title feels a bit intense, can we back it down a bit
   and fix some vertical height on words").

   TWO separate problems were in here, and only one of them was taste.

   1. THE LINES OVERLAPPED. MEASURED, not guessed: Archivo's font box (ascent + descent)
      is 1.09em at wdth 112, so any line-height below 1.09 puts the descenders of one line
      inside the caps of the next. The h1 ran 0.94 and the gap between rendered lines
      measured −3px — the "p" of "Bike repair in" was sitting in "Castle Rock". That is a
      defect, not a look. The tokens below clear 1.09 with 1.1–3.8px of real gap at
      360 / 404 / 768 / 1280.

      1.09 IS A HARD FLOOR. If you lower either token, re-measure — nothing else on this
      site would catch it. Method: Range.getClientRects() over the text nodes, then
      next.top − previous.bottom.

   2. IT WAS MAXED ON EVERY AXIS AT ONCE — weight 800 AND wdth 112 AND −0.042em AND a
      sub-1.0 leading. Each is defensible alone; stacked they shout. Weight and tracking
      came off; the WIDTH AXIS STAYED, because wdth 112 is what gives this direction its
      editorial character and it was never the aggressive part. */
:root{
  --lh-display: 1.16;   /* HARD FLOOR 1.09 — below that Archivo's lines collide */
  --lh-h1:      1.14;
}
.display{
  font-family:var(--display);
  font-variation-settings:"wdth" 112;
  font-weight:700;
  line-height:var(--lh-display);
  letter-spacing:-0.024em;
  text-wrap:balance;
  margin:0;
}
h1.display{font-size:clamp(2.35rem, 6.4vw, 4.75rem);letter-spacing:-0.028em;line-height:var(--lh-h1);text-wrap:pretty}
@media (max-width:899px){ h1.display{font-size:clamp(2.2rem, 8.8vw, 3.9rem)} }
h2.display{font-size:clamp(1.9rem, 5.2vw, 3.3rem)}
h3{
  font-family:var(--text);font-weight:600;
  font-size:1.18rem;line-height:1.3;letter-spacing:-0.01em;margin:0;
}
.num{font-family:var(--mono);font-variant-numeric:tabular-nums;font-feature-settings:"tnum"}

/* section index label — mono, orange tick */
.ix{
  font-family:var(--mono);font-size:.72rem;font-weight:500;
  letter-spacing:.2em;text-transform:uppercase;color:var(--ink-55);
  display:flex;align-items:center;gap:10px;margin:0 0 22px;
}
.ix::before{content:"";width:9px;height:9px;background:var(--orange);flex:none}
.band .ix{color:var(--on-navy-2)}

.lede{font-size:clamp(1.06rem,2.2vw,1.28rem);line-height:1.55;color:var(--ink-70);max-width:46ch;margin:0}
.muted{color:var(--ink-55)}


/* ============================================================
   05 · BUTTONS
   ============================================================ */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:10px;
  font-family:var(--text);font-weight:600;font-size:1rem;line-height:1;
  padding:17px 26px;border:1.5px solid var(--navy);background:var(--navy);color:var(--on-navy);
  text-decoration:none;cursor:pointer;border-radius:2px;
  min-height:44px;
  transition:background .18s ease, transform .12s ease, border-color .18s ease;
}
.btn:hover{background:var(--navy-lift);border-color:var(--navy-lift)}
.btn:active{transform:translateY(1px)}
.btn.ghost{background:transparent;color:var(--ink);border-color:var(--ink);}
.btn.ghost:hover{background:var(--ink);color:var(--bone);border-color:var(--ink)}
.btn[disabled]{opacity:.55;cursor:not-allowed;transform:none}
.band .btn.ghost{color:var(--on-navy);border-color:var(--on-navy)}
.band .btn.ghost:hover{background:var(--on-navy);color:var(--navy)}


/* ============================================================
   05b · TRACKED TYPE — OPTICAL INSET CORRECTION
   Read this before you put letter-spacing on anything with a background,
   a border, or a rule sized to its own text.

   THE BUG IT FIXES. letter-spacing adds its space AFTER every character,
   INCLUDING THE LAST ONE, and that trailing space is part of the element's
   content width. So inside a filled chip with symmetrical padding the gap on
   the right is padding + one tracking-unit while the gap on the left is just
   padding. The type sits visibly shoved toward the left of its box. At the
   sizes this site tracks at (.14em–.24em on 9–11px mono) that is 1–3px — small
   enough that it reads as "off" rather than obviously broken, which is exactly
   why it survived to production.

   Measured on the live site before this fix: .needs was 4px 9px at .14em
   tracking, .promo-flag 5px 9px at .17em.

   HOW IT WORKS. A component declares TWO custom properties beside its own
   letter-spacing and declares NO horizontal padding of its own:

       .promo-flag{ letter-spacing:.17em; --trk:.17em; --pad-x:9px; padding-block:5px }

   and the single rule below takes the tracking back out of the right side, so
   the OPTICAL insets match. The maths lives here once. Never hand-tune a
   padding-right on a component to compensate — that is how the two drift.

   --trk is the tracking of the LAST INLINE RUN in the box, which is not always
   the element's own letter-spacing: .needs ends with its <i> hint, and the hint
   is tracked tighter than the label. Set --trk to what actually sits against
   the right padding.

   IMPORTANT: a component in this list must set padding-block / --pad-x, never
   the `padding` shorthand — the shorthand resets padding-inline and silently
   undoes the correction no matter where it appears in the file.
   ============================================================ */
.needs,.promo-flag,.pop-flag,.mini,.hero .eyebrow .tagx,.skip{
  padding-inline:var(--pad-x) max(0px, calc(var(--pad-x) - var(--trk)));
}

/* Same defect, different geometry: a rule drawn to the width of shrink-to-fit text
   runs one tracking-unit past the last glyph. Measured on .fdesc: 140.45px of rule
   against a last glyph ending at 136.81px. A negative margin does NOT fix this — the
   element keeps its own max-content width — so the rule moves to a pseudo-element
   that can be sized short of it. */
.fdesc{position:relative;border-top:0}
.fdesc::before{
  content:"";position:absolute;left:0;top:0;
  width:calc(100% - var(--trk));
  border-top:1px solid var(--rule);
}


/* ============================================================
   06 · REVIEW AFFORDANCES
   All three render ONLY while brand.config.json _reviewMode is true.
   ============================================================ */

/* the strip above the header */
.notice{
  background:var(--ink);color:#EDE8E0;
  font-family:var(--mono);font-size:.7rem;letter-spacing:.09em;
  text-transform:uppercase;line-height:1.5;
}
.notice .wrap{display:flex;flex-wrap:wrap;gap:6px 18px;align-items:center;padding-block:9px}
.notice b{color:var(--orange);font-weight:600;letter-spacing:.14em}
.notice .wrap > span{color:#A49C90}   /* direct child — see the note on .facts li > span */
.notice i{
  font-style:normal;
  border-bottom:2px dotted var(--orange);
}

/* draft value — dotted orange underline */
.smp{
  text-decoration:underline;
  text-decoration-style:dotted;
  text-decoration-color:var(--orange);
  text-decoration-thickness:2px;
  text-underline-offset:4px;
}

/* confirm value — a marker chip a non-technical reader cannot miss.
   The literal string NEEDS_CONFIRMATION is never rendered; this is.

   Horizontal padding comes from section 05b via --pad-x / --trk. --trk is .09em
   rather than .16em because the run that actually sits against the right padding
   is the <i> hint, which is tracked tighter than the label.

   Vertical padding went 4px -> 6px on 2026-08-01: against a 1.5 line-height this
   chip wraps to two and three lines constantly (the hint is a whole sentence), and
   4px made the wrapped block look pinched top and bottom. row-gap keeps the wrapped
   lines from touching. */
.needs{
  display:inline-flex;flex-wrap:wrap;align-items:baseline;gap:2px 7px;
  vertical-align:baseline;
  background:var(--ink);color:var(--orange);
  font-family:var(--mono);font-size:.62rem;font-weight:600;
  letter-spacing:.16em;text-transform:uppercase;line-height:1.5;
  --trk:.09em; --pad-x:9px;
  padding-block:6px;border-radius:2px;
  white-space:normal;
  text-decoration:none;
}
.needs i{font-style:normal;color:#CFC7BA;letter-spacing:.09em;font-weight:400}
.band .needs,.notice .needs{background:var(--bone);color:var(--orange-ink)}
.band .needs i,.notice .needs i{color:var(--ink-55)}

/* keep chips from inheriting an oversized display/mono context.
   NOTE: set --trk alongside any letter-spacing override, or 05b compensates by
   the wrong amount. */
.facts .needs,.tier .needs,.alc .needs,.hours .needs,.fphone .needs,.rates .needs{
  font-size:.6rem;letter-spacing:.14em;
}

/* --- COMPACT VARIANT ------------------------------------------------------
   The full chip is right in prose and wrong in a slot built for four characters.
   Measured before this existed: the hero .facts rail is a 3-column grid of ~119px
   cells holding "1–2 days" and "$0", and the third cell — brand.yearsExperience,
   confirm-tier — rendered the full chip at 110px wide and SIX LINES TALL, forcing
   the whole row to 123px. The owner spotted it immediately.

   So: same component, same colours, same type, one glyph. It still reads as an
   open question, it still carries the whole hint (title attribute + .sr text, so
   hover and screen readers both get the real explanation), and its box is shorter
   than the line box of every slot it sits in, so the row keeps its rhythm.

   Use it in a stat, a heading, a table cell, a nav item, a button or a fixed-width
   price column. Use the standard chip anywhere there is prose room — the point of
   the marker system is that the client SEES the gap while reading, and a bare "?"
   in running text would not carry that. */
.needs.tight{
  gap:0;
  --pad-x:7px; --trk:.14em;
  padding-block:2px;
  letter-spacing:.14em;
  line-height:1.35;
}


/* ============================================================
   07 · HEADER
   ============================================================ */
.hdr{
  position:sticky;top:0;z-index:60;
  /* --bone at .92 — keep these two numbers in step with the token in section 02 */
  background:rgba(247,246,245,.92);
  backdrop-filter:saturate(160%) blur(10px);
  border-bottom:1px solid var(--rule);
}
.hdr .wrap{display:flex;align-items:center;gap:18px;min-height:70px}
.lockup{display:flex;align-items:center;gap:11px;text-decoration:none;flex:none;min-height:44px}
/* The mark is the client's raster artwork now (see _Mark.cshtml), so this targets both the
   <img> and any future inline SVG — one rule, so the lockup can't change size depending on
   which form the mark happens to take. */
.lockup svg,.lockup .mark{width:42px;height:42px;display:block;flex:none}
.lockup .wm{
  display:block;font-family:var(--display);font-variation-settings:"wdth" 108;
  font-weight:800;font-size:.9rem;letter-spacing:-0.012em;line-height:1.05;
}
/* NOT a case for the 05b correction, though it looks like one. This rule is drawn to the
   width of the LOCKUP (the wordmark above is ~120px, the descriptor ~70px), not to the width
   of this text — measured overshoot 51.9px against 1.6px of tracking. It is lockup geometry
   and it is meant to be that wide. Leave it alone. */
.lockup .desc{
  display:block;font-family:var(--mono);font-size:.575rem;font-weight:500;
  letter-spacing:.235em;padding-top:3px;margin-top:3px;
  border-top:1px solid var(--rule);color:var(--ink-55);
}
.nav{display:none;margin-left:auto;gap:26px}
.nav a{
  text-decoration:none;font-size:.94rem;color:var(--ink-70);
  padding:6px 0;border-bottom:2px solid transparent;
}
.nav a:hover{color:var(--ink);border-color:var(--orange)}
.nav a[aria-current="page"]{color:var(--ink);border-color:var(--orange)}
/* The three repair-page links are the first thing to go when the header runs out of room.
   Tap-to-call and tap-to-text are guaranteed at every breakpoint and never yield to a nav
   link; the mobile menu panel carries the full list below 900px. */
.nav .nav-more{display:none}
@media (min-width:1180px){ .nav .nav-more{display:inline-block} }
.hcta{display:flex;align-items:center;gap:8px;margin-left:auto}
.nav + .hcta{margin-left:0}
.mini{
  display:inline-flex;align-items:center;justify-content:center;min-height:44px;
  font-family:var(--mono);font-size:.76rem;font-weight:500;letter-spacing:.09em;
  text-transform:uppercase;text-decoration:none;border-radius:2px;
  --trk:.09em; --pad-x:14px;
  padding-block:10px;
  border:1.5px solid var(--rule);color:var(--ink);white-space:nowrap;
}
.mini:hover{border-color:var(--ink)}

/* Inline tap-to-call / tap-to-text links are REAL tap targets wherever they appear in prose,
   so they carry the 44px minimum like every other control. (A link inline in a sentence is
   otherwise exempt — these are not; they're the primary conversion action on a phone.) */
a.num[href^="tel:"],a.num[href^="sms:"]{
  display:inline-block;padding-block:12px;line-height:1.2;
}
.mini.solid{background:var(--ink);border-color:var(--ink);color:var(--bone)}
.mini.solid:hover{background:var(--navy);border-color:var(--navy)}


/* ============================================================
   08 · HERO
   ============================================================ */
.hero{padding-top:clamp(46px,9vw,104px);padding-bottom:clamp(38px,6vw,64px)}
.hero .eyebrow{
  font-family:var(--mono);font-size:.76rem;font-weight:500;letter-spacing:.2em;
  text-transform:uppercase;color:var(--ink-55);margin:0 0 26px;
  display:flex;flex-wrap:wrap;align-items:center;gap:12px;
}
.hero .eyebrow .dot{width:9px;height:9px;background:var(--orange);flex:none}
.hero .eyebrow .tagx{
  border:1px solid var(--rule);font-size:.66rem;letter-spacing:.16em;color:var(--ink-55);
  --trk:.16em; --pad-x:8px;
  padding-block:3px;
}
h1 .city{
  display:inline-block;
  box-shadow:inset 0 -0.13em 0 0 var(--orange);
  padding-bottom:.02em;
}
.deck{
  font-family:var(--display);font-variation-settings:"wdth" 106;font-weight:600;
  font-size:clamp(1.2rem,2.3vw,1.75rem);line-height:1.15;letter-spacing:-0.025em;
  color:var(--ink-70);margin:clamp(20px,2.6vw,30px) 0 0;max-width:22ch;
}
.hero-sub{
  margin:clamp(26px,4vw,42px) 0 0;
  display:grid;gap:clamp(24px,4vw,52px);
}
.hero-cta{display:flex;flex-wrap:wrap;gap:12px;margin-top:28px}

/* mono fact rail */
.facts{
  margin:clamp(40px,6vw,64px) 0 0;padding:0;list-style:none;
  display:grid;grid-template-columns:repeat(3,1fr);
  border-top:1px solid var(--rule);
}
/* Padding fixed 2026-08-03. Two faults, both visible in the owner's screenshot:
   NO LEFT PADDING, so the value in cells 2 and 3 sat hard against the divider rule to its
   left; and NO BOTTOM PADDING, so the label ran straight into the bottom of the cell while
   the dividers carried on past it. The left padding is added on `li + li` only — the first
   cell must stay flush with the page's left edge so the rail lines up with the H1 above it.
   Clamped rather than fixed because these are three ~119px columns on a phone, where a hard
   20px would push "1–2 days" onto two lines. */
.facts li{padding:20px 16px 22px 0;border-right:1px solid var(--rule-soft)}
.facts li + li{padding-left:clamp(12px,2.6vw,22px)}
.facts li:last-child{border-right:0}
.facts b{
  display:block;font-family:var(--mono);font-weight:600;
  font-size:clamp(1.15rem,3.4vw,1.65rem);letter-spacing:-0.02em;
}
/* DIRECT child only. As a bare `.facts span` this also captured the spans INSIDE a review
   marker sitting in the value cell — the compact chip's glyph came out grey, at caption size,
   with a 6px top margin. Any rule in here that targets a bare element rather than a class
   will do the same thing to the next component that lands in this rail. */
.facts li > span{display:block;font-size:.82rem;color:var(--ink-55);line-height:1.35;margin-top:6px}


/* ============================================================
   09 · PHOTOS + PHOTO SLOTS
   A slot holds either an approved photograph (docs/PHOTOS.md) or the typographic
   placeholder naming the shot still needed. NO stock imagery ships on this site.
   ============================================================ */

/* real photographs — the only <img> on the site comes through _Photo.cshtml */
.photo{margin:0;position:relative;background:var(--bone-2);border:1px solid var(--rule)}
.photo img{display:block;width:100%;height:100%;object-fit:cover}
.photo figcaption{
  font-family:var(--mono);font-size:.66rem;letter-spacing:.13em;text-transform:uppercase;
  color:var(--ink-55);padding:11px 2px 0;
}
/* the .hero-slot / .about-slot modifiers set the frame; the image fills it */
.photo.wide{aspect-ratio:21/9}
.photo.three-two{aspect-ratio:3/2}
.photo.four-three{aspect-ratio:4/3}
.photo.four-five{aspect-ratio:4/5}
.photo.square{aspect-ratio:1/1}
/* the same modifiers on an unfilled slot, so the page doesn't reflow when a photo lands */
.slot.wide{aspect-ratio:21/9}
.slot.three-two{aspect-ratio:3/2}
.slot.four-three{aspect-ratio:4/3}
.slot.four-five{aspect-ratio:4/5}
.slot.square{aspect-ratio:1/1}

.slot{
  position:relative;background:var(--bone-2);
  border:1px solid var(--rule);
  display:flex;flex-direction:column;justify-content:space-between;
  padding:clamp(18px,3vw,30px);
  overflow:hidden;
}
.slot::before{
  content:"";position:absolute;inset:0;
  background:repeating-linear-gradient(135deg,transparent 0 13px,rgba(22,24,26,.035) 13px 14px);
  pointer-events:none;
}
.slot > *{position:relative}
.slot .cap{
  font-family:var(--mono);font-size:.68rem;font-weight:500;letter-spacing:.19em;
  text-transform:uppercase;color:var(--ink-55);
  display:flex;align-items:center;gap:9px;
}
.slot .cap::before{content:"";width:9px;height:9px;background:var(--orange);flex:none}
.slot .brief{
  font-family:var(--display);font-variation-settings:"wdth" 106;font-weight:600;
  font-size:clamp(1.02rem,2.3vw,1.5rem);line-height:1.2;letter-spacing:-0.02em;
  color:var(--ink-70);max-width:26ch;margin:24px 0 0;
}
.slot .foot{
  font-family:var(--mono);font-size:.66rem;letter-spacing:.13em;text-transform:uppercase;
  color:var(--ink-55);margin-top:26px;padding-top:12px;border-top:1px solid var(--rule);
  display:flex;flex-wrap:wrap;gap:6px 28px;justify-content:space-between;
}
/* crop marks — reads as a framed, deliberate empty slot */
.slot .marks{position:absolute;inset:11px;pointer-events:none}
.slot .marks i{position:absolute;width:16px;height:16px;border:1.5px solid rgba(22,24,26,.22)}
.slot .marks i:nth-child(1){top:0;left:0;border-right:0;border-bottom:0}
.slot .marks i:nth-child(2){top:0;right:0;border-left:0;border-bottom:0}
.slot .marks i:nth-child(3){bottom:0;left:0;border-right:0;border-top:0}
.slot .marks i:nth-child(4){bottom:0;right:0;border-left:0;border-top:0}
.hero-slot{aspect-ratio:4/3;margin-top:clamp(44px,6vw,72px)}


/* ============================================================
   10 · EDITORIAL SECTION SHELL
   ============================================================ */
.sect{padding-block:var(--sect);border-top:1px solid var(--rule)}
.sect-head{margin-bottom:clamp(38px,5vw,66px)}
.sect-head p{margin:18px 0 0}

/* interior page header — the non-home equivalent of .hero */
.phead{padding-top:clamp(40px,7vw,84px);padding-bottom:clamp(30px,5vw,56px)}
.phead .lede{margin-top:clamp(18px,2.4vw,26px)}
.phead .hero-cta{margin-top:26px}


/* ============================================================
   11 · PRICING — numbered editorial menu (NOT cards)
   ============================================================ */
.tiers{border-top:1.5px solid var(--ink)}
/* The price column is FIXED, not auto. Each .tier is its own grid, so an auto
   column sizes to that tier's own price stack and the three tiers ended up with
   three different price positions on a phone. See the alignment note under .alc. */
.tier{
  position:relative;
  display:grid;grid-template-columns:minmax(0,1fr) var(--price-col-tier);gap:6px 16px;
  padding:clamp(26px,4vw,40px) 0;
  border-bottom:1px solid var(--rule);
}
/* Serial, inline inside the h3. It is a marker, not a column — as its own grid cell it
   stretched to the price stack's height (228x127px for two characters at 404px) and pushed
   the tier name into a row below its own price. Sized off the heading so it scales with it. */
.tier .tno{
  font-family:var(--mono);font-size:.44em;font-weight:600;letter-spacing:.14em;
  color:var(--ink-55);margin-right:.7em;vertical-align:.28em;
}
/* Auto row placement, deliberately — no explicit grid-row anywhere in this component.
   The flag (when present) takes a full-width row of its own, then .tbody and .tcol-price
   share the next one. Tiers with and without a flag therefore align their name and price
   identically, and a tier with no flag leaves no empty track behind. */
.tier .promo-flag,
.tier .pop-flag{grid-column:1 / -1;justify-self:start}
.tier .tbody{grid-column:1;min-width:0}
.tier .tcol-price{grid-column:2;text-align:right}
/* Display face, so it takes the same leading floor as .display — "Full Tune-Up &
   Overhaul" wraps to two lines on a phone and at line-height 1 the descenders
   landed in the next line's caps. See section 04. */
.tier h3{
  font-family:var(--display);font-variation-settings:"wdth" 110;font-weight:700;
  font-size:clamp(1.65rem,4.4vw,2.35rem);letter-spacing:-0.028em;line-height:var(--lh-display);
}
.tier .best{margin:12px 0 0;color:var(--ink-55);font-size:.98rem;max-width:44ch}
.tier .price{
  font-family:var(--mono);font-weight:600;font-variant-numeric:tabular-nums;
  font-size:clamp(1.6rem,4.4vw,2.15rem);letter-spacing:-0.045em;line-height:1;
}
.tier .dur{
  font-family:var(--mono);font-size:.75rem;letter-spacing:.11em;text-transform:uppercase;
  color:var(--ink-55);margin-top:9px;
}
.tier ul{
  margin:20px 0 0;padding:0;list-style:none;
  display:grid;gap:7px 30px;font-size:.95rem;color:var(--ink-70);
}
.tier ul li{display:flex;gap:10px;line-height:1.45}
.tier ul li::before{
  content:"";width:7px;height:1.5px;background:var(--ink-55);flex:none;margin-top:.72em;
}
/* NO BACKGROUND on the marked rows — deliberately, and it must stay that way.
   `.tier` runs `padding: … 0` because this is a RULED EDITORIAL LIST, not a set of cards:
   every row's text aligns to the same page grid and no row has a box. Painting a gradient
   onto .pop/.promo broke that on two counts at once —
     1. a background turns the row into a visible box, and a box with zero horizontal
        padding sits its text flush against its own left edge, which is exactly what it
        looked like;
     2. the wash was an orange tint over the ground, and it was a large part of what kept
        reading as "yellow" after the neutral ramp had already been corrected twice.
   Adding horizontal padding instead would have indented these rows relative to the
   unmarked ones and broken the list's left alignment — the wrong fix.
   The row is already marked twice over: an orange rule and an orange badge. That is enough. */
.tier.pop::before,
.tier.promo::before{
  content:"";position:absolute;left:0;right:0;top:-1px;height:3px;width:auto;
  background:var(--orange);
}
.pop-flag{
  display:inline-block;font-family:var(--mono);font-size:.66rem;font-weight:600;
  letter-spacing:.17em;text-transform:uppercase;
  --trk:.17em; --pad-x:9px;
  background:var(--ink);color:var(--bone);padding-block:5px;margin-bottom:12px;
}
.tier .excl{
  margin:16px 0 0;font-size:.88rem;color:var(--ink-55);max-width:52ch;
}
.tier .excl b{font-weight:600;color:var(--ink-70)}
.parts-note{
  margin:30px 0 0;font-size:.97rem;color:var(--ink-55);max-width:66ch;
  padding-left:18px;border-left:2px solid var(--rule);
}

/* à la carte — dotted-leader menu. Only services.seed.json > aLaCarte publishes here.
   aLaCarteUnconfirmed NEVER renders anywhere on this site.

   PRICE ALIGNMENT — read before touching this. Revised 2026-08-01 (owner: "the price
   jumps around a lot visually on the pricing page, can we make it so they all line up").

   The rows used to be a pure flex line — name, leader, then price and unit FLOWING
   INLINE after it. So the price landed wherever the row happened to leave it:
   a row with no unit right-aligned at one x, a row with "per brake" pushed the price
   ~90px left, and a row whose name wrapped to two lines put it somewhere else again.
   Measured across the 12 prices on /services at 404px: SIX distinct left edges and TWO
   distinct right edges. Nothing shared a column, so the eye could not scan the price
   list, which is the entire job of publishing prices (CLAUDE.md calls it the single
   biggest trust lever this business has).

   Now the row is a grid with a FIXED price column, so the price right edge is the row
   right edge on every row at every width — one x-position, no exceptions.

   The unit ("per brake") sits BENEATH the price, right-aligned, on every row that has
   one. Deliberately not "inline when it fits": a unit that is sometimes beside the
   number and sometimes below it reintroduces exactly the raggedness this fixes. His
   brake pricing is per BRAKE, not per wheel, so the unit still has to sit with the
   number rather than in the note. */
.alc{margin-top:clamp(56px,8vw,90px)}
.alc h3{
  font-family:var(--display);font-variation-settings:"wdth" 110;font-weight:700;
  font-size:clamp(1.3rem,3.2vw,1.8rem);letter-spacing:-0.026em;margin-bottom:22px;
}
.alc ul{list-style:none;margin:0;padding:0;border-top:1px solid var(--rule);
  display:grid;gap:0;column-gap:clamp(30px,5vw,72px)}
.alc li{padding:14px 0;border-bottom:1px solid var(--rule-soft)}
.alc .l1{
  display:grid;
  grid-template-columns:minmax(0,1fr) var(--price-col);
  column-gap:12px;align-items:baseline;
}
.alc .nmw{grid-column:1;grid-row:1;display:flex;align-items:baseline;gap:10px;min-width:0}
.alc .nm{font-size:.99rem;line-height:1.3}
.alc .dots{flex:1;border-bottom:1px dotted var(--rule);transform:translateY(-4px);min-width:14px}
.alc .pr{
  grid-column:2;grid-row:1;
  font-family:var(--mono);font-weight:600;font-size:1.02rem;font-variant-numeric:tabular-nums;
  text-align:right;
}
.alc .nt{font-size:.8rem;color:var(--ink-55);margin-top:3px;line-height:1.35}

/* rates + fees rail */
.rates{list-style:none;margin:clamp(40px,6vw,60px) 0 0;padding:0;
  display:grid;gap:0;border-top:1px solid var(--rule)}
.rates li{padding:14px 0;border-bottom:1px solid var(--rule-soft);
  display:flex;align-items:baseline;gap:12px;flex-wrap:wrap}
.rates .rl{font-size:.99rem}
.rates .rv{font-family:var(--mono);font-weight:600;font-variant-numeric:tabular-nums;margin-left:auto}


/* ============================================================
   12 · WHAT I FIX
   ============================================================ */
.fixes{display:grid;gap:1px;background:var(--rule);border:1px solid var(--rule)}
.fix{
  background:var(--bone);padding:clamp(22px,3vw,30px);
  text-decoration:none;display:block;
  transition:background .16s ease;
}
.fix:hover{background:var(--bone-2)}
.fix .k{
  font-family:var(--mono);font-size:.72rem;font-weight:600;letter-spacing:.16em;
  color:var(--ink-55);display:block;margin-bottom:16px;
}
.fix .t{
  display:block;font-family:var(--display);font-variation-settings:"wdth" 108;
  font-weight:700;font-size:1.22rem;letter-spacing:-0.022em;line-height:1.15;
}
.fix .d{display:block;font-size:.92rem;color:var(--ink-55);margin-top:9px;line-height:1.45}
.fix .arw{
  display:inline-block;margin-top:16px;font-family:var(--mono);font-size:.9rem;color:var(--ink-55);
  transition:transform .16s ease,color .16s ease;
}
.fix:hover .arw{transform:translateX(5px);color:var(--orange-ink)}


/* ============================================================
   13 · HOW IT WORKS
   ============================================================ */
.steps{display:grid;gap:clamp(30px,4vw,44px)}
.step{border-top:2px solid var(--ink);padding-top:20px}
.step .sn{
  font-family:var(--mono);font-size:.74rem;font-weight:600;letter-spacing:.2em;
  text-transform:uppercase;color:var(--ink-55);display:block;margin-bottom:14px;
}
.step h3{font-size:1.28rem;margin-bottom:10px}
.step p{margin:0;color:var(--ink-55);font-size:.97rem}


/* ============================================================
   14 · DARK BAND — the single contrast moment. One per page, maximum.
   ============================================================ */
.band{background:var(--navy);color:var(--on-navy);padding-block:var(--sect)}
.band h2{color:var(--on-navy)}
.band .lede{color:var(--on-navy-2)}
.areas-lbl{
  font-family:var(--mono);font-size:.68rem;letter-spacing:.16em;text-transform:uppercase;
  color:var(--on-navy-2);margin:clamp(34px,5vw,52px) 0 14px;
}
.areas{
  list-style:none;margin:0;padding:0;
  display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
  gap:0;border-top:1px solid rgba(243,240,234,.22);
}
.areas li{
  font-family:var(--mono);font-size:.95rem;letter-spacing:.02em;
  padding:14px 8px 14px 0;border-bottom:1px solid rgba(243,240,234,.14);
}
.band-note{
  margin:30px 0 0;font-size:.92rem;color:var(--on-navy-2);
  padding-left:16px;border-left:2px solid var(--orange);max-width:52ch;
}


/* ============================================================
   15 · ABOUT
   ============================================================ */
.about-slot{aspect-ratio:1/1}


/* ============================================================
   16 · BOOKING FORM
   Every field has a real visible label. A placeholder is never the label.
   ============================================================ */
.form{margin-top:clamp(30px,4vw,44px);max-width:640px}
.f-row{display:grid;gap:18px;margin-bottom:18px}
.field label{
  display:block;font-family:var(--mono);font-size:.72rem;font-weight:500;
  letter-spacing:.15em;text-transform:uppercase;color:var(--ink-55);margin-bottom:8px;
}
.field .req{color:var(--ink);font-weight:600}
.field .opt{color:var(--ink-55);font-weight:400;letter-spacing:.1em}
.field input,.field select,.field textarea{
  width:100%;font-family:var(--text);font-size:1rem;color:var(--ink);
  background:#FCFBFA;border:1.5px solid var(--rule);border-radius:2px;
  padding:14px 14px;line-height:1.4;min-height:44px;
  transition:border-color .15s ease, background .15s ease;
}
.field textarea{min-height:132px;resize:vertical}
.field select{
  appearance:none;
  background-image:linear-gradient(45deg,transparent 50%,var(--ink-55) 50%),linear-gradient(135deg,var(--ink-55) 50%,transparent 50%);
  background-position:calc(100% - 20px) calc(50% + 1px),calc(100% - 14px) calc(50% + 1px);
  background-size:6px 6px,6px 6px;background-repeat:no-repeat;
  padding-right:44px;
}
.field input:hover,.field select:hover,.field textarea:hover{border-color:#BCB7B0}
.field input:focus,.field select:focus,.field textarea:focus{border-color:var(--ink);background:#fff;outline:none}
.field input:focus-visible,.field select:focus-visible,.field textarea:focus-visible{outline:2px solid var(--orange);outline-offset:2px}

/* inline errors, below the field they belong to */
.field .err{
  display:block;margin-top:8px;font-size:.86rem;line-height:1.4;
  color:var(--orange-ink);font-family:var(--text);letter-spacing:0;text-transform:none;
}
.field .err:empty{display:none}
.field input[aria-invalid="true"],
.field select[aria-invalid="true"],
.field textarea[aria-invalid="true"]{border-color:var(--orange-ink);background:#FDF6F2}

.err-summary{
  margin:0 0 26px;padding:16px 18px;border:1.5px solid var(--orange-ink);
  background:#FDF6F2;border-radius:2px;font-size:.95rem;color:var(--ink);
}
.err-summary p{margin:0 0 8px;font-weight:600}
.err-summary ul{margin:0;padding-left:20px}

.hp{position:absolute;left:-9999px;width:1px;height:1px;opacity:0}
.f-actions{display:flex;flex-wrap:wrap;align-items:center;gap:16px;margin-top:26px}
.f-actions .hint{font-size:.85rem;color:var(--ink-55)}
.spin{
  width:15px;height:15px;border:2px solid rgba(243,240,234,.4);border-top-color:var(--on-navy);
  border-radius:50%;animation:sp .7s linear infinite;display:none;
}
.btn.busy .spin{display:block}
@keyframes sp{to{transform:rotate(360deg)}}

/* /thanks confirmation */
.okmsg{
  margin-top:22px;padding:16px 18px;
  border:1.5px solid var(--navy);background:#EDF1F6;color:var(--navy);
  font-size:.95rem;display:flex;align-items:flex-start;gap:12px;border-radius:2px;
}
.okmsg svg{flex:none;margin-top:2px}


/* ============================================================
   17 · FOOTER
   ============================================================ */
.ftr{border-top:1px solid var(--rule);padding-top:clamp(52px,7vw,80px);padding-bottom:clamp(40px,6vw,64px);background:var(--bone-2)}
.fgrid{display:grid;gap:clamp(34px,5vw,48px)}
.fgrid h4{
  font-family:var(--mono);font-size:.72rem;font-weight:600;letter-spacing:.18em;
  text-transform:uppercase;color:var(--ink-55);margin:0 0 16px;
}
.fgrid p{margin:0 0 10px;font-size:.95rem;color:var(--ink-70);max-width:38ch}
.fgrid nav a{display:block;text-decoration:none;color:var(--ink-70);font-size:.95rem;padding:11px 0}
.fgrid nav a:hover{color:var(--ink);text-decoration:underline;text-underline-offset:3px}
.fname{
  font-family:var(--display);font-variation-settings:"wdth" 110;font-weight:700;
  font-size:1.28rem;letter-spacing:-0.026em;line-height:var(--lh-display);margin:0 0 6px !important;color:var(--ink) !important;
}
/* The rule above this text is drawn by .fdesc::before in section 05b, not by a border,
   so it can stop at the last glyph instead of running .24em past it. */
.fdesc{
  font-family:var(--mono);font-size:.62rem;letter-spacing:.24em;color:var(--ink-55);
  --trk:.24em;
  display:inline-block;padding-top:5px;margin-bottom:16px !important;
}
.hours{font-family:var(--mono);font-size:.93rem;line-height:2;margin:0;font-variant-numeric:tabular-nums}
.hours span.d{display:inline-block;min-width:74px;color:var(--ink-55)}
.hours em{font-style:normal}
.fphone{font-family:var(--mono);font-size:1.12rem;font-weight:600;letter-spacing:-0.02em}
/* Tap targets, fixed 2026-08-03. The footer phone and email were 19px tall — the two most
   important targets down here, and the site's NAP, sized like body copy. They are standalone
   contact links rather than links inside a sentence, so the WCAG 2.5.8 inline-text exception
   does not cover them. inline-block + vertical padding takes both past 44px without moving
   anything else, because the padding grows into leading the rows already had. */
.fphone a,.ftr a[href^="mailto:"]{display:inline-block;padding-block:11px;color:var(--ink)}
.fphone{margin-block:-4px}
.legal{
  margin-top:clamp(40px,6vw,60px);padding-top:20px;border-top:1px solid var(--rule);
  font-family:var(--mono);font-size:.66rem;letter-spacing:.13em;text-transform:uppercase;
  color:var(--ink-55);line-height:1.8;
}


/* ============================================================
   18 · STICKY MOBILE BAR
   ============================================================ */
.bar{
  position:fixed;left:0;right:0;bottom:0;z-index:70;
  display:grid;grid-template-columns:auto auto minmax(0,1fr);gap:8px;
  padding:10px var(--gut);
  padding-bottom:calc(10px + env(safe-area-inset-bottom,0px));
  background:rgba(247,246,245,.96);   /* --bone at .96 */
  backdrop-filter:blur(10px);border-top:1px solid var(--rule);
}
.bar a,.bar button{
  display:flex;align-items:center;justify-content:center;gap:8px;min-height:44px;
  font-family:var(--text);font-weight:600;font-size:.95rem;
  padding:12px 14px;text-decoration:none;border-radius:2px;cursor:pointer;
  border:1.5px solid var(--ink);color:var(--ink);background:transparent;
}
.bar a.solid{background:var(--navy);border-color:var(--navy);color:var(--on-navy)}
body{padding-bottom:86px}

/* Mobile navigation.
   Concept B was a single-page concept, so it had no mobile nav. On a multi-page site the
   header cannot carry one at 360px without pushing out tap-to-call/tap-to-text, which are
   guaranteed at every breakpoint — so the nav lives in the thumb-reachable bottom bar. */
.menupanel{
  position:fixed;left:0;right:0;bottom:0;z-index:69;
  padding:14px var(--gut) calc(80px + env(safe-area-inset-bottom,0px));
  background:var(--bone);border-top:1px solid var(--rule);
  box-shadow:0 -18px 40px rgba(22,24,26,.14);
}
.menupanel[hidden]{display:none}
.menupanel a{
  display:block;text-decoration:none;color:var(--ink);font-size:1.02rem;
  padding:14px 0;border-bottom:1px solid var(--rule-soft);min-height:44px;
}
.menupanel a:last-child{border-bottom:0}
.menupanel a[aria-current="page"]{font-weight:600;box-shadow:inset 3px 0 0 0 var(--orange);padding-left:12px}
.bar .burger{width:15px;height:11px;position:relative;flex:none}
.bar .burger::before,.bar .burger::after,.bar .burger i{
  content:"";position:absolute;left:0;right:0;height:1.5px;background:currentColor;
}
.bar .burger::before{top:0}
.bar .burger i{top:50%;margin-top:-.75px}
.bar .burger::after{bottom:0}


/* ============================================================
   19 · BREAKPOINTS
   ============================================================ */
/* small phones — compress the header lockup so nothing overflows at 360px */
@media (max-width:559px){
  .hdr .wrap{gap:10px;min-height:62px}
  .lockup{gap:8px}
  .lockup svg,.lockup .mark{width:34px;height:34px}
  .lockup .wm{font-size:.735rem;font-variation-settings:"wdth" 100}
  .lockup .desc{font-size:.5rem;letter-spacing:.2em;margin-top:2px;padding-top:2px}
  .hcta{gap:6px}
  .mini{--pad-x:11px;--trk:.07em;padding-block:13px;font-size:.685rem;letter-spacing:.07em}
}

@media (min-width:560px){
  .f-row.two{grid-template-columns:1fr 1fr}
  .tier ul{grid-template-columns:1fr 1fr}
  .fixes{grid-template-columns:1fr 1fr}
}

@media (min-width:900px){
  .nav{display:flex;gap:17px;font-size:.9rem}
  .hero-grid{
    display:grid;grid-template-columns:minmax(0,7fr) minmax(0,5fr);
    gap:clamp(36px,5vw,80px);align-items:end;
  }
  .hero-sub{grid-template-columns:minmax(0,1fr);align-content:end;padding-bottom:6px}
  .hero-slot{aspect-ratio:21/9;margin-top:clamp(56px,7vw,92px)}
  .facts{grid-template-columns:repeat(3,1fr)}

  .split{display:grid;grid-template-columns:minmax(0,4fr) minmax(0,8fr);gap:clamp(36px,5vw,80px)}
  .split-head{position:sticky;top:100px;align-self:start}
  .sect-head{margin-bottom:0}

  /* Two columns, not three — the serial is inline in the h3 at every width now, so the
     52px column it used to own is gone. Content and price, which is what the row is for. */
  .tier{grid-template-columns:minmax(0,1fr) var(--price-col-tier);gap:6px clamp(20px,3vw,40px);align-items:start}
  .tier .tbody{grid-column:1;padding-top:0}
  .tier .tcol-price{grid-column:2;text-align:right;padding-top:4px}
  .tier .price{font-size:2.1rem}
  /* NO desktop override of the accent — it stays a top rule at every width.
     It used to become a 4px bar at left:-26px here. Once the background wash came off,
     that bar had nothing tying it to its row: it read as a stray orange mark floating in
     the margin beside the list. A top rule matches the language the list already speaks —
     .tiers has a top border, every .tier has a bottom border. The marked row simply gets
     a heavier, orange one. */

  .alc ul{grid-template-columns:1fr 1fr}
  .fixes{grid-template-columns:1fr 1fr}
  .steps{grid-template-columns:repeat(3,1fr)}
  .areas{grid-template-columns:repeat(3,minmax(0,1fr))}
  .about-grid{display:grid;grid-template-columns:minmax(0,5fr) minmax(0,7fr);gap:clamp(36px,5vw,72px);align-items:center}
  .fgrid{grid-template-columns:minmax(0,2fr) minmax(0,1fr) minmax(0,1fr)}
  .band-wide{display:grid;grid-template-columns:minmax(0,5fr) minmax(0,7fr);gap:clamp(36px,5vw,80px);align-items:start}
  .bar,.menupanel{display:none}
  body{padding-bottom:0}
}

@media (min-width:1100px){
  .form{max-width:none}
  .f-row.two{grid-template-columns:1fr 1fr}
}


/* ============================================================
   20 · MOTION + SCROLL
   ============================================================ */
section[id]{scroll-margin-top:86px}
@media (prefers-reduced-motion:no-preference){
  html{scroll-behavior:smooth}
}
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{
    animation-duration:.001ms !important;animation-iteration-count:1 !important;
    transition-duration:.001ms !important;scroll-behavior:auto !important;
  }
}


/* ============================================================
   21 · SERVICE-PAGE PROSE (markdown-rendered)
   Everything Content/MarkdownHtml.cs can emit. Its own breakpoints live in this
   section rather than in 19, so the whole markdown surface is readable in one place.

   The symptom -> cause -> cost table is the highest-value element on a service page
   and the whole reason these pages rank. It must be genuinely readable at 360px,
   which means STACKING it into labelled rows — not shunting a five-inch grid
   sideways behind a scrollbar and hoping.
   ============================================================ */
.prose{max-width:74ch}
.prose > p{margin:0 0 1.15em;color:var(--ink-70);font-size:1.02rem}
.prose > p:last-child{margin-bottom:0}
.prose strong{font-weight:600;color:var(--ink)}
.prose a{color:var(--ink);text-decoration:underline;text-decoration-thickness:1.5px;text-underline-offset:3px;text-decoration-color:var(--orange)}
.prose a:hover{color:var(--orange-ink)}
/* display face -> takes the 1.09em leading floor from section 04 */
.prose h3{
  font-family:var(--display);font-variation-settings:"wdth" 108;font-weight:700;
  font-size:clamp(1.24rem,3vw,1.62rem);letter-spacing:-0.024em;line-height:var(--lh-display);
  margin:clamp(34px,4.5vw,52px) 0 16px;
}
.prose h3:first-child{margin-top:0}

.prose-list{margin:0 0 1.3em;padding:0;list-style:none;display:grid;gap:10px}
.prose-list li{display:flex;gap:12px;line-height:1.5;color:var(--ink-70);font-size:.99rem}
.prose-list li::before{content:"";width:8px;height:1.5px;background:var(--orange);flex:none;margin-top:.78em}

/* --- tables -------------------------------------------------------------- */
/* Two containment rules, and the order matters. The 3+-column tables STACK below 760px
   (see .mdtable-stack) so they never need to scroll. The two-column price tables stay
   tables and are allowed to scroll INSIDE this wrapper if a long "per brake (pads extra)"
   cell won't fit — what must never happen is the page body scrolling sideways. */
.tablewrap{margin:0 0 1.5em;max-width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}
.mdtable{width:100%;border-collapse:collapse;font-size:.95rem;line-height:1.5}
.mdtable th{
  text-align:left;font-family:var(--mono);font-size:.68rem;font-weight:600;
  letter-spacing:.16em;text-transform:uppercase;color:var(--ink-55);
  padding:0 18px 12px 0;border-bottom:1.5px solid var(--ink);vertical-align:bottom;
}
.mdtable th:last-child,.mdtable td:last-child{padding-right:0}
.mdtable td{
  padding:15px 18px 15px 0;border-bottom:1px solid var(--rule-soft);
  vertical-align:top;color:var(--ink-70);
}
.mdtable tr:last-child td{border-bottom:1px solid var(--rule)}

/* --- symptom tables, readability pass 2026-08-03 -------------------------------
   These are the highest-value content on a service page: someone stands next to a
   noisy bike, finds their symptom, and reads across to the price. As first built,
   all three columns rendered at the same size, weight and colour, so there was
   nothing to scan BY — the eye had to read every cell of a ten-row grid to find
   one line. Three changes, in order of how much they do:

   1. THE SYMPTOM COLUMN LEADS. It is the column you arrive knowing, so it is set
      in full ink at body size while the cause column stays secondary. That single
      contrast step is what turns the grid into a list you can run your eye down.
   2. THE MONEY IS FINDABLE. Prices sit mid-sentence in the third column
      ("Caliper brake adjustment $25 per brake, or $30 on discs"); .cost lifts them
      into mono tabular figures so they read as figures, not prose.
   3. ROOM TO BREATHE. Row padding was 15px against cells that routinely run to
      three lines; the rows ran together. Wider padding and a per-row hover band
      make it obvious which line you are reading across. */
.mdtable-stack td:first-child{
  color:var(--ink);font-weight:500;
}
.mdtable .cost{
  font-family:var(--mono);font-variant-numeric:tabular-nums;font-feature-settings:"tnum";
  font-size:.94em;font-weight:600;color:var(--ink);white-space:nowrap;
}
@media (min-width:760px){
  .mdtable-stack td{padding-top:20px;padding-bottom:20px;padding-right:26px}
  .mdtable-stack th{padding-right:26px}
  /* Reading a wide row across three columns is where these tables lose people.
     The band follows the row under the cursor and costs nothing when unused. */
  .mdtable-stack tbody tr{transition:background-color .12s ease}
  .mdtable-stack tbody tr:hover{background:var(--bone-2)}
  /* the hover band would otherwise stop dead at the last cell's text edge */
  .mdtable-stack tbody td:first-child{box-shadow:-14px 0 0 0 transparent}
  .mdtable-stack tbody tr:hover td:first-child{box-shadow:-14px 0 0 0 var(--bone-2)}
}
@media (prefers-reduced-motion:reduce){
  .mdtable-stack tbody tr{transition:none}
}
/* the two-column price tables read as a menu, so the number is mono and right-aligned.
   A real table column already gives every price one shared right edge; tabular-nums
   makes the DIGITS line up inside it too, which is the same rule .alc follows. */
.mdtable:not(.mdtable-stack) td:last-child{
  font-family:var(--mono);font-variant-numeric:tabular-nums;font-feature-settings:"tnum";
  color:var(--ink);
  white-space:nowrap;text-align:right;
}
/* the cost column of a stacked symptom -> cause -> cost table */
.mdtable-stack td:last-child{font-variant-numeric:tabular-nums}
.mdtable:not(.mdtable-stack) th:last-child{text-align:right}
/* on a phone the price column is allowed to wrap rather than force a scrollbar */
@media (max-width:599px){
  .mdtable:not(.mdtable-stack) td:last-child{white-space:normal}
  .mdtable:not(.mdtable-stack) td{padding-right:12px}
}

/* 3+ columns: stack below 760px. Each cell announces its own column, so the
   symptom / cause / cost relationship survives at 360px with no sideways scroll. */
@media (max-width:759px){
  .mdtable-stack thead{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
  .mdtable-stack,.mdtable-stack tbody,.mdtable-stack tr,.mdtable-stack td{display:block;width:100%}
  .mdtable-stack tr{
    border:1px solid var(--rule);border-left:3px solid var(--orange);
    background:var(--bone-2);
    padding:clamp(14px,3.5vw,20px);margin-bottom:14px;
  }
  .mdtable-stack td{padding:0 0 14px;border-bottom:0}
  .mdtable-stack tr td:last-child{padding-bottom:0}
  .mdtable-stack td::before{
    content:attr(data-th);display:block;
    font-family:var(--mono);font-size:.62rem;font-weight:600;letter-spacing:.16em;
    text-transform:uppercase;color:var(--ink-55);margin-bottom:5px;
  }
  /* the cost cell is the answer people came for — give it the weight */
  .mdtable-stack td:last-child{
    color:var(--ink);border-top:1px solid var(--rule);padding-top:12px;
  }
}
/* wide screens: a long first column would otherwise take half the page.
   Rebalanced 2026-08-03. The old 30/32/38 split gave the widest column to the SHORTEST
   content: symptoms and causes are a phrase each, while "what it takes" carries a price
   plus its conditions and was wrapping to four lines against a three-line neighbour.
   27/31/42 evens the row heights out, which is most of why the grid now reads calmly. */
@media (min-width:760px){
  .mdtable-stack th:first-child,.mdtable-stack td:first-child{width:27%}
  .mdtable-stack th:nth-child(2),.mdtable-stack td:nth-child(2){width:31%}
}

/* --- FAQ ----------------------------------------------------------------- */
.faq{padding:22px 0;border-bottom:1px solid var(--rule-soft)}
.faq:first-child{padding-top:0}
.faq:last-child{border-bottom:0}
.faq h3{font-family:var(--text);font-weight:600;font-size:1.08rem;letter-spacing:-0.01em;margin:0 0 9px;line-height:1.35}
.faq p{margin:0;color:var(--ink-55);font-size:.98rem}

/* --- the "> NEEDS CONFIRMATION:" blockquotes from the drafted copy --------
   Review scaffolding: renders only while _reviewMode is true and disappears with it. */
.rvnote{
  margin:0 0 1.5em;padding:16px 18px;
  background:var(--bone-3);border-left:3px solid var(--orange);
}
.rvnote p{margin:10px 0 0;font-size:.9rem;line-height:1.5;color:var(--ink-70)}
.rvnote .needs{font-size:.6rem;letter-spacing:.14em}

/* related-services rail at the foot of a service page */
.related{display:grid;gap:14px;margin-top:clamp(28px,4vw,40px)}
@media (min-width:640px){ .related{grid-template-columns:1fr 1fr} }
.related a{
  display:block;text-decoration:none;padding:20px;border:1px solid var(--rule);background:var(--bone-2);
  transition:background .16s ease,border-color .16s ease;
}
.related a:hover{background:var(--bone-3);border-color:var(--ink-55)}
.related .rl{
  display:block;font-family:var(--mono);font-size:.66rem;font-weight:600;letter-spacing:.16em;
  text-transform:uppercase;color:var(--ink-55);margin-bottom:9px;
}
.related .rt{
  display:block;font-family:var(--display);font-variation-settings:"wdth" 108;font-weight:700;
  font-size:1.1rem;letter-spacing:-0.02em;line-height:1.2;color:var(--ink);
}


/* ============================================================
   22 · PROMOTIONS, KIDS LINE, À LA CARTE UNITS
   The tier price is ONE number plus a subordinate parts line. Ranges are gone —
   the owner quotes single prices and a range misrepresents how he sells.
   ============================================================ */

/* the price stack inside .tier .tcol-price */
.tier .parts-sub{
  font-family:var(--mono);font-size:.7rem;letter-spacing:.11em;text-transform:uppercase;
  color:var(--ink-55);margin-top:7px;
}
.tier .was{
  display:block;font-family:var(--mono);font-size:.78rem;letter-spacing:.06em;
  color:var(--ink-55);margin-top:8px;
}

/* Promotional flag. Badge only — and note the colour pairing: orange ground with INK text,
   not white. Orange on white is 3.4:1 and this is 0.66rem uppercase; ink on orange is 5.2:1.
   The house rule that orange never carries small text applies to the badge too. */
.promo-flag{
  display:inline-block;font-family:var(--mono);font-size:.66rem;font-weight:600;
  letter-spacing:.17em;text-transform:uppercase;
  --trk:.17em; --pad-x:9px;
  background:var(--orange);color:var(--ink);padding-block:5px;margin-bottom:12px;
}
.promo-ends{
  display:block;font-family:var(--mono);font-size:.68rem;letter-spacing:.12em;
  text-transform:uppercase;color:var(--ink-55);margin-top:10px;
}
/* .tier.promo intentionally has NO background — see the note on .tier.promo::before in
   section 11. A background on a zero-horizontal-padding row is what put the text flush
   against its own edge, and the orange wash was also feeding the "still too yellow" read.
   ::before is defined alongside .pop in section 11 and overridden once at 900px there —
   one rule, two modifiers, so the accent can never drift between them. */

/* kids tune-up — a distinct line UNDER the tier block, deliberately not a fourth
   column: a fourth column breaks the three-tier design and buries the tiers that
   carry the revenue. */
.kids{
  display:grid;grid-template-columns:minmax(0,1fr) var(--price-col-tier);gap:8px 20px;align-items:baseline;
  padding:22px 0;border-bottom:1px solid var(--rule);
}
.kids h3{font-size:1.1rem;font-weight:600}
.kids .cut{
  font-family:var(--mono);font-size:.76rem;letter-spacing:.06em;
  color:var(--ink-55);margin:6px 0 0;
}
.kids .kp{text-align:right}
.kids .price{
  font-family:var(--mono);font-weight:600;font-variant-numeric:tabular-nums;
  font-size:1.5rem;letter-spacing:-0.04em;line-height:1;
}
.kids .parts-sub{
  font-family:var(--mono);font-size:.7rem;letter-spacing:.11em;text-transform:uppercase;
  color:var(--ink-55);margin-top:7px;
}

/* à la carte unit — "per brake" is part of the price, not a footnote. Getting the
   unit wrong misquotes people, so it stays with the number: directly beneath it,
   right-aligned to the same column edge, on every row that has one. */
.alc .u{
  grid-column:2;grid-row:2;
  font-style:normal;font-family:var(--mono);font-weight:400;font-size:.72rem;
  letter-spacing:.08em;color:var(--ink-55);white-space:nowrap;
  text-align:right;margin-top:3px;
}


/* ============================================================
   23 · REVIEW-MODE FEEDBACK CONTROL
   Renders ONLY while brand.config.json _reviewMode is true — same flag that
   drives noindex and the .notice strip — so it leaves the site completely at
   launch with no separate switch to remember.

   It is a mailto: link, not a form, and that is deliberate rather than lazy:
   there is no SendGrid key yet and App Service local storage is not durable
   across redeploys, so a server-backed feedback form would accept the client's
   comments and silently lose them. A mailto costs nothing, works today, and
   fails visibly if it fails at all.

   Placement rules it has to honour:
     * never over the sticky bottom bar (tap-to-call / Book), so below 900px it
       sits ABOVE the bar rather than on it;
     * never over the tap-to-call buttons in the header;
     * z-index below .bar and .menupanel, so opening the mobile menu covers it
       instead of it floating on top of the nav;
     * 44px tap target, visible focus ring, no motion under prefers-reduced-motion.
   ============================================================ */
.rvfb{
  position:fixed;z-index:66;
  right:var(--gut);
  bottom:calc(84px + env(safe-area-inset-bottom,0px));
  max-width:calc(100vw - (var(--gut) * 2));
  display:inline-flex;align-items:center;gap:9px;
  min-height:44px;padding:11px 16px;
  background:var(--ink);color:var(--bone);
  border:1.5px solid var(--ink);border-radius:2px;
  box-shadow:0 6px 22px rgba(22,24,26,.24);
  font-family:var(--mono);font-size:.68rem;font-weight:600;
  letter-spacing:.13em;text-transform:uppercase;line-height:1.3;
  text-decoration:none;
  transition:background .16s ease,border-color .16s ease;
}
.rvfb:hover{background:var(--navy);border-color:var(--navy)}
.rvfb .dot{width:9px;height:9px;background:var(--orange);flex:none}
@media (min-width:900px){
  .rvfb{bottom:24px;right:24px}
}
/* The global prefers-reduced-motion rule already flattens the transition; this is
   here so the intent survives if that rule is ever narrowed. */
@media (prefers-reduced-motion:reduce){
  .rvfb{transition:none}
}
