/* ============================================================
   Reliable Garage Door Repair Service LLC  ::  stylesheet

   This file is GENERATED BY CONCATENATION and is not hand edited.
   It is, in order:

     partials/foundry/base.css      the vault discipline layer
     partials/foundry/foundry.css   the Foundry design, as approved
     partials/foundry/site.css      the six components a 45 page site
                                    needs that a one page specimen never had

   Foundry is the design Porter approved for this client (owner decision,
   2026-09-12), replacing Press. foundry.css is the client variant from
   web-templates/clients/designs/foundry-rgd and carries every change made during
   that review: the photo hero, the real badge, the palette sampled from the
   badge, the logo-matched display face, the phone-first hero CTA and the
   service tile hover backdrop.

   Theme: data-theme="navy", set on <body> in partials/head.html. The client
   palette is declared as body[data-theme] so that it beats [data-theme="navy"]'s
   own --mark; the attribute must be present or none of it applies.
   Ornament: 2, the design default.

   Two rules are load-bearing and a change here can break them silently:
     1. --ornament lives on <body>, never :root, or the dial stops moving
        the page.
     2. Contrast on this palette is measured, not eyeballed. Every value in
        the palette block and in the hover backdrop scrim carries the ratio
        it was chosen for. Do not nudge one without re-measuring.

   To change anything below, edit partials/foundry/*.css and rebuild:
     cat partials/foundry/base.css partials/foundry/foundry.css \
         partials/foundry/site.css > partials/styles.css
   then restore this header.
   ============================================================ */
/* ============================================================
   VAULT BASE  ::  the discipline layer every design shares
/* ============================================================
   VAULT BASE  ::  the discipline layer every design shares

   Harvested from a-lign.studio (see docs/a-lign-technique-dossier.md).
   Their polish turned out to cost under 1KB of CSS and zero JavaScript;
   the 406KB of animation machinery on top was not the lesson.

   This file is buyer-NEUTRAL on purpose. It carries no personality: no
   colours, no type faces, no spacing values, no component looks. Each
   design keeps all of that. What lives here is the invisible hand that
   makes four visibly different designs feel like one studio made them.

   Every design links this FIRST, then its own stylesheet.
   ============================================================ */

:root {
  /* ---- 1.1 One ease, one duration, everywhere ----
     a-lign registers a single symmetric ease and one default duration, and
     every effect on the site inherits them. That, more than any individual
     animation, is why unrelated effects feel like one hand made them.

     Theirs is 600ms. Ours is 200ms deliberately: a homeowner is in a hurry,
     and motion on a trade site should read as responsiveness, not as
     choreography. Their rule survives even though their number does not.

     The rule: anything driven by scroll position gets --ease-linear (the
     scrollbar IS the easing). Anything triggered gets --ease. */
  --ease: cubic-bezier(.65, 0, .35, 1);
  --ease-linear: linear;
  --dur: 200ms;
  --dur-slow: 320ms;      /* only for things that physically travel further */

  /* ---- 1.5 Measures in ch, not px ----
     Nothing on their site runs the full container width. A measure is a
     property of the TEXT, so it belongs in ch and never needs a breakpoint. */
  --measure-display: 16ch;   /* headlines: short enough to land in one breath */
  --measure-heading: 24ch;
  --measure-body: 62ch;
}

/* ---- 1.11 A complete reduced-motion kill switch ----
   Their site honours the preference but leaves some scroll-driven state
   behind. Ours does it properly: nothing animates, nothing transitions,
   nothing scroll-behaves, and critically NO ELEMENT IS LEFT HIDDEN waiting
   for an animation that will never run. A reveal that starts at opacity 0
   and is never revealed is a blank page, which is worse than no animation.

   `!important` is deliberate here. This block has to beat anything a design
   sets, including inline styles, because the alternative is a page a
   motion-sensitive reader cannot use. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto !important; }
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
  /* anything a design hides pre-animation must be visible, not merely still */
  [data-reveal] { opacity: 1 !important; transform: none !important; }
}

/* ---- Viewport units: svh, never vh ----
   `100vh` on a phone is the viewport WITHOUT the browser chrome, so a
   full-height hero is cut off by the address bar on first paint and then
   jumps when it retracts. `svh` is the small viewport, which is the one
   actually visible. */
.u-screen { min-height: 100svh; }

/* ---- 1.10 The image plate ----
   Every image sits on a neutral plate so a slow photo leaves a considered
   grey rectangle rather than a white hole that collapses the layout. Each
   design supplies the colour; this supplies the behaviour. */
.plate-img { background: var(--plate, rgba(0, 0, 0, .06)); display: block; }
.plate-img > img,
.plate-img > svg { display: block; width: 100%; }

/* ---- Measures as utilities ----
   Designs apply these rather than re-deriving a max-width per component. */
.measure { max-width: var(--measure-body); }
.measure-heading { max-width: var(--measure-heading); }
.measure-display { max-width: var(--measure-display); }

/* ---- Skip link + screen-reader text ----
   Identical in all four designs, so it lives once. Colours come from the
   design via --ink / --paper. */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
/* ============================================================
   FOUNDRY  ::  heritage / sign-painter

   The sixth design, and the first that is NOT a new buyer. It shares
   Artsy's buyer (the capable individual who owns the equipment) and
   argues for him differently: Artsy says "I am strong", Foundry says
   "I have been here thirty years."

   That is a legitimate second design only because it is structurally
   different, not recoloured. Compare the mechanics:

     Artsy     sheared bands          Foundry  no skew anywhere
     Artsy     hard offset shadow     Foundry  DOUBLE RULES, no shadow
     Artsy     giant ghost word       Foundry  an arched emblem
     Artsy     dark-first             Foundry  warm cream stock
     Artsy     uppercase grotesk      Foundry  slab + arched script

   Suits trades that trade on longevity and craft: auto detailing,
   custom fabrication, barbers, BBQ, breweries, heritage builders,
   anyone whose truck already has gold leaf on the door.

   Personality token: --ornament. How much sign-painter is on show.
   0 is a clean modern shop with heritage bones; 3 is full gold-leaf.

   Requires css/base.css.
   ============================================================ */

:root {
  --ornament: 2;              /* 0 plain, 1 restrained, 2 default, 3 full */
  --wrap: 1120px;
  --gutter: 22px;

  --display: 'Iowan Old Style', 'Palatino Linotype', Palatino, Georgia, serif;
  --body: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto,
          'Helvetica Neue', Arial, sans-serif;

  --stock: #F7F1E4;          /* warm press stock, never white */
  --stock-2: #EFE6D3;
  --ink: #241F1A;            /* ink-on-stock about 13:1 */
  --ink-2: #5E554A;          /* 5.6:1 on stock */
  --rule: #CFC0A4;

  /* Dual-gated like Dispatch: must carry white on a fill AND read as text
     on the cream stock, so there is never a second link colour. */
  --mark: #7B2233;
  --mark-2: #5C1926;
}

[data-theme="oxblood"] { --mark:#7B2233; --mark-2:#5C1926; }
[data-theme="navy"]    { --mark:#1F3A5F; --mark-2:#152A46; }
[data-theme="forest"]  { --mark:#23503A; --mark-2:#183A29; }
[data-theme="rust"]    { --mark:#8A3B18; --mark-2:#682C11; }
[data-theme="coal"]    { --mark:#2E2A26; --mark-2:#1C1916; }

/* Derived on body so an inline --ornament actually feeds them. */
body {
  --o: var(--ornament);
  --rule-gap: calc(2px + var(--o) * 1px);      /* the double-rule separation */
  --rule-w: calc(1px + var(--o) * 0.5px);
  --emblem: calc(0.6 + var(--o) * 0.2);        /* emblem scale */
  --tracking: calc(0.04em + var(--o) * 0.03em);
}

* { box-sizing: border-box; }
html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--stock);
  color: var(--ink);
  font-family: var(--body);
  font-size: 17px;
  line-height: 1.62;
  overflow-x: clip;
}

img, svg { max-width: 100%; height: auto; display: block; }

h1, h2, h3 {
  font-family: var(--display); font-weight: 400;
  color: var(--ink); line-height: 1.1; margin: 0 0 .5em;
  letter-spacing: -0.01em;
}
h1 { font-size: clamp(2.2rem, 6vw, 3.9rem); max-width: var(--measure-display); }
h2 { font-size: clamp(1.7rem, 3.6vw, 2.6rem); max-width: var(--measure-heading); }
h3 { font-size: 1.16rem; }
p { margin: 0 0 1rem; max-width: var(--measure-body); }
a { color: var(--mark); }

/* The sign-painter register: letterspaced small caps on everything that
   labels rather than speaks. This is the design's voice. */
.stamp {
  font-family: var(--body); font-size: .72rem; font-weight: 700;
  letter-spacing: var(--tracking); text-transform: uppercase;
  color: var(--mark);
}

.wrap { max-width: var(--wrap); margin: 0 auto; padding: 0 var(--gutter); }
.lead { font-size: 1.1rem; color: var(--ink-2); }
.center { text-align: center; }
.center h1, .center h2, .center p, .center .lead { margin-inline: auto; }
.tight { margin-bottom: .3em; }

:focus-visible { outline: 2px solid var(--mark); outline-offset: 3px; }
.skip { position: absolute; left: -9999px; top: 0; z-index: 99; background: var(--ink); color: var(--stock); padding: 12px 18px; font-weight: 700; }
.skip:focus { left: 0; }

/* ---------- THE DOUBLE RULE ----------
   The signature. Period printing separated things with a thick rule and a
   thin one, and that pairing is what reads as "old shop" without a single
   texture, gradient or drop shadow. Everything here is drawn with borders. */

.rule2 {
  border-top: calc(var(--rule-w) * 3) solid var(--ink);
  box-shadow: 0 var(--rule-gap) 0 0 var(--rule);   /* the thin companion rule */
  height: 0;
  margin-bottom: calc(var(--rule-gap) + 10px);
}

.section { padding: clamp(3rem, 6vw, 5rem) 0; }
.section--tint { background: var(--stock-2); }

/* ---------- HEADER ---------- */

.masthead { background: var(--ink); color: var(--stock); }
.masthead .wrap { display: flex; align-items: center; gap: 22px; padding-block: .55rem; }
.masthead .stamp { color: var(--stock); }
.masthead__phone { margin-left: auto; color: var(--stock); text-decoration: none; font-weight: 700; letter-spacing: .02em; }
.masthead__phone:hover { color: #fff; }

.bar { border-bottom: calc(var(--rule-w) * 3) solid var(--ink); background: var(--stock); }
.bar .wrap { display: flex; align-items: center; gap: 26px; padding-block: 14px; }
.brand { font-family: var(--display); font-size: 1.5rem; color: var(--ink); text-decoration: none; margin-right: auto; }
.nav { display: flex; gap: 24px; }
.nav a { color: var(--ink-2); text-decoration: none; font-size: .78rem; font-weight: 700; letter-spacing: var(--tracking); text-transform: uppercase; }
.nav a:hover { color: var(--mark); }
@media (max-width: 900px) { .nav { display: none; } }

/* ---------- HERO: the emblem ----------
   Artsy shouts a watermark word behind the headline. Foundry sets an
   emblem: arched name, rule, established year. It is the one ornamental
   object on the page and everything else stays plain because of it. */

.hero { padding: clamp(2.5rem, 5vw, 4rem) 0 clamp(3rem, 6vw, 5rem); text-align: center; }
.emblem { width: min(320px, 74vw); margin: 0 auto clamp(1.25rem, 3vw, 2rem); transform: scale(var(--emblem)); transform-origin: top center; }
.emblem text { font-family: var(--display); fill: var(--ink); }
.emblem .arch { font-size: 26px; }
.emblem .est { font-family: var(--body); font-size: 15px; font-weight: 700; letter-spacing: 4px; fill: var(--mark); }
.emblem .frame { fill: none; stroke: var(--ink); stroke-width: 3; }
.emblem .frame-in { fill: none; stroke: var(--rule); stroke-width: 1.5; }

.hero .lead { margin-top: 1rem; }

/* ---------- BUTTONS: ruled, never filled unless it is THE action ---------- */

.btn {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 52px; padding: 0 1.75rem; font: inherit;
  font-size: .8rem; font-weight: 700; letter-spacing: var(--tracking); text-transform: uppercase;
  border: 2px solid var(--ink); background: transparent; color: var(--ink);
  text-decoration: none; cursor: pointer;
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.btn:hover { background: var(--ink); color: var(--stock); }
.btn--mark { background: var(--mark); border-color: var(--mark); color: #fff; }
.btn--mark:hover { background: var(--mark-2); border-color: var(--mark-2); color: #fff; }
.btn-row { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 1.75rem; }
.btn-row--center { justify-content: center; }

/* ---------- LEDGER STRIP: the facts, set like a receipt ---------- */

.strip { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); border-top: 2px solid var(--ink); border-bottom: 2px solid var(--ink); }
.strip > div { padding: 1.1rem .75rem; text-align: center; border-right: 1px solid var(--rule); }
.strip > div:last-child { border-right: 0; }
.strip b { display: block; font-family: var(--display); font-size: clamp(1.6rem, 3.2vw, 2.2rem); line-height: 1; }
.strip span { display: block; margin-top: .4rem; font-size: .68rem; font-weight: 700; letter-spacing: var(--tracking); text-transform: uppercase; color: var(--ink-2); }

/* ---------- SERVICES: framed plates, not cards ---------- */

.plates { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: clamp(1rem, 2vw, 1.5rem); margin-top: 2rem; }
.plate {
  border: 2px solid var(--ink); padding: 1.4rem 1.25rem;
  display: flex; flex-direction: column; background: var(--stock);
  position: relative;
}
/* the inner hairline: pure period printing, and the whole reason this
   reads as heritage without a texture anywhere */
.plate::after {
  content: ''; position: absolute; inset: calc(3px + var(--o) * 1px);
  border: 1px solid var(--rule); pointer-events: none;
}
.plate h3 { margin-bottom: .35em; }
.plate p { font-size: .95rem; color: var(--ink-2); margin-bottom: 1.1rem; }
.plate__more { margin-top: auto; align-self: flex-start; font-size: .72rem; font-weight: 700; letter-spacing: var(--tracking); text-transform: uppercase; color: var(--mark); text-decoration: none; border-bottom: 2px solid var(--mark); padding-bottom: 2px; }
.plate--mark { background: var(--mark); border-color: var(--ink); }
.plate--mark h3, .plate--mark p { color: #fff; }
.plate--mark .plate__more { color: #fff; border-bottom-color: #fff; }
.plate--mark::after { border-color: rgba(255,255,255,.4); }

/* ---------- SPLIT ---------- */

.split { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(2rem, 4vw, 3.5rem); align-items: center; }
.split--flip > :first-child { order: 2; }
@media (max-width: 900px) { .split { grid-template-columns: 1fr; } .split--flip > :first-child { order: 0; } }
.framed { border: 2px solid var(--ink); padding: 8px; background: var(--stock); }
.framed > * { border: 1px solid var(--rule); }

.marks { list-style: none; padding: 0; margin: 1.25rem 0 0; }
.marks li { position: relative; padding: .7rem 0 .7rem 1.9rem; border-top: 1px solid var(--rule); font-size: .98rem; }
.marks li:last-child { border-bottom: 1px solid var(--rule); }
.marks li::before { content: '\2726'; position: absolute; left: 0; top: .7rem; color: var(--mark); font-size: .9rem; }

/* ---------- TESTIMONY: set as a ruled column ---------- */

.says { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: clamp(1.5rem, 3vw, 2.5rem); margin-top: 2rem; }
.say { border-top: 2px solid var(--ink); padding-top: 1.1rem; }
.say blockquote { margin: 0 0 1rem; font-family: var(--display); font-size: 1.08rem; line-height: 1.5; }
.say figcaption { font-size: .7rem; font-weight: 700; letter-spacing: var(--tracking); text-transform: uppercase; color: var(--ink-2); }
.say figcaption b { display: block; color: var(--ink); }

/* ---------- SERVICE AREA: a ruled roll ---------- */

.roll { list-style: none; padding: 0; margin: 1.5rem 0 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); }
.roll li { border-top: 1px solid var(--rule); }
.roll a { display: block; padding: .7rem .25rem; color: var(--ink); text-decoration: none; font-size: .95rem; }
.roll a:hover { color: var(--mark); }

/* ---------- CLOSING PANEL ---------- */

.panel { border: calc(var(--rule-w) * 3) solid var(--ink); padding: clamp(2rem, 5vw, 3.5rem) clamp(1.25rem, 4vw, 3rem); text-align: center; background: var(--stock-2); position: relative; }
.panel::after { content: ''; position: absolute; inset: calc(5px + var(--o) * 2px); border: 1px solid var(--rule); pointer-events: none; }

/* ---------- FOOTER ---------- */

.foot { border-top: calc(var(--rule-w) * 3) solid var(--ink); padding: 2.5rem 0 1.75rem; }
.foot-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: 2rem; }
.foot-grid ul { list-style: none; padding: 0; margin: 0; }
.foot-grid li { margin-bottom: .5rem; }
.foot a { color: var(--ink-2); text-decoration: none; font-size: .93rem; }
.foot a:hover { color: var(--mark); }
.foot .foot-h { margin: 0 0 .8rem; font-size: .7rem; font-weight: 700; letter-spacing: var(--tracking); text-transform: uppercase; color: var(--ink); }
.colophon { margin-top: 2.5rem; padding-top: 1.1rem; border-top: 1px solid var(--rule); display: flex; flex-wrap: wrap; gap: 1rem; justify-content: space-between; font-size: .82rem; color: var(--ink-2); }

/* ---------- DESIGN-REVIEW CONTROLS (demo builds only) ---------- */

.controls { padding: 3rem 0; border-top: 1px solid var(--rule); }
.swatches { display: flex; gap: .7rem; flex-wrap: wrap; }
.center .swatches { justify-content: center; }
.swatch { width: 32px; height: 32px; border: 2px solid var(--ink); cursor: pointer; padding: 0; }
.swatch[aria-pressed="true"] { outline: 2px solid var(--mark); outline-offset: 3px; }
.note { font-size: .88rem; color: var(--ink-2); margin-bottom: 1rem; }
.note--spaced { margin-top: 1.5rem; margin-bottom: 0; }
.slider { vertical-align: middle; width: 190px; margin-left: 10px; accent-color: var(--mark); }

/* ================= PHOTO HERO =================
   The emblem becomes the logo slot. Its circles were already fill:none, so it was
   transparent to begin with; the work here is putting a photograph behind it and
   lifting the line art so it survives over an image.
   Nothing else about the hero moves: heading, lead and buttons stay where Foundry
   puts them.                                                                      */
.hero--photo { position: relative; isolation: isolate; overflow: hidden; }
.hero--photo .wrap { position: relative; z-index: 2; }

.hero__photo { position: absolute; inset: 0; z-index: 0;
  background: #1b1712 url("/assets/photos/hero.jpg") center 62% / cover no-repeat; }

/* Scrim. Line art and serif type over a photograph need a ground or they break up
   against door panels and ceiling joists. Heaviest through the middle where the
   emblem and headline sit, lighter at the edges so the photograph still reads. */
.hero__photo::after { content: ""; position: absolute; inset: 0;
  background:
    radial-gradient(70% 62% at 50% 46%, rgba(20,16,12,.80) 0%, rgba(20,16,12,.55) 52%, rgba(20,16,12,.30) 100%),
    linear-gradient(180deg, rgba(20,16,12,.62) 0%, rgba(20,16,12,.34) 42%, rgba(20,16,12,.70) 100%); }

/* Emblem: line art to paper. */
.hero--photo .emblem .frame    { stroke: rgba(255,255,255,.94); stroke-width: 3; }
.hero--photo .emblem .frame-in { stroke: rgba(255,255,255,.42); }
.hero--photo .emblem text      { fill: #fff; }
.hero--photo .emblem .est      { fill: rgba(255,255,255,.85); }

/* Type and controls over the photograph. */
.hero--photo .stamp { color: #fff; border-color: rgba(255,255,255,.5); }
.hero--photo h1     { color: #fff; }
.hero--photo .lead  { color: rgba(255,255,255,.90); }
.hero--photo .btn   { border-color: rgba(255,255,255,.72); color: #fff; background: transparent; }
.hero--photo .btn:hover { background: rgba(255,255,255,.14); }
.hero--photo .btn--mark { background: #fff; border-color: #fff; color: var(--ink); }
.hero--photo .btn--mark:hover { background: rgba(255,255,255,.88); color: var(--ink); }

/* The real badge, when one is supplied. Sized by width so the 2:1 artwork is never
   squeezed into the circle the drawn emblem occupied. */
/* Sized by HEIGHT, not width. The supplied PNG is a wide badge; constraining the
   width let a square canvas with transparent margins occupy a 560px tall block and
   push the whole hero off screen. Height keeps it to a predictable band whatever
   aspect the artwork turns out to be. */
.brandmark { display: block; height: clamp(96px, 13vw, 170px); width: auto; max-width: 86vw;
  margin: 0 auto clamp(1.1rem, 2.6vw, 1.8rem); }
/* Over the photo hero the badge carries its own navy, so it needs no recolouring,
   only a little separation from the scrim. */
.hero--photo .brandmark { filter: drop-shadow(0 2px 14px rgba(0,0,0,.45)); }

/* ================= TYPE MATCHED TO THE LOGO =================
   The badge sets the brand's voice in lettering: heavy, condensed, uppercase, tight.
   Foundry's own display face is a SERIF (Iowan Old Style), which is its signature and
   the opposite of that, so the two were arguing with each other on the same page.

   This overrides the display face only. Body copy stays in the system sans, because
   condensed caps are a headline material and unreadable in paragraphs.

   No webfont is loaded, matching every other design in the vault: Oswald if the
   visitor has it, Arial Narrow otherwise, then the condensed system faces, then
   plain sans. A missing Oswald degrades to a narrower sans, never to a serif. */
:root {
  --display: 'Oswald', 'Arial Narrow', 'Helvetica Neue Condensed', 'Roboto Condensed',
             'Liberation Sans Narrow', ui-sans-serif, system-ui, sans-serif;
}
h1, h2, h3, .brand, .strip b {
  font-family: var(--display);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: -0.005em;   /* the badge sets its caps tight, not tracked out */
}
/* Condensed caps read considerably larger than a serif at the same size, and they
   set much tighter. Pull the scale and the leading back or the hero overruns. */
h1 { font-size: clamp(2.1rem, 5.6vw, 4.1rem); line-height: .96; }
h2 { font-size: clamp(1.55rem, 3.3vw, 2.4rem); line-height: 1.02; }
h3 { font-size: clamp(1.05rem, 1.7vw, 1.25rem); line-height: 1.08; }
/* The wordmark in the masthead is the one place the logo lettering is literally
   quoted, so it carries the same treatment at a smaller size. */
.brand { font-size: 1.25rem; letter-spacing: .01em; }
/* The emblem SVG fallback keeps the serif deliberately: if the real badge is ever
   missing, the drawn mark should not pretend to be the logo. */
.emblem text { font-family: 'Iowan Old Style', Palatino, Georgia, serif; text-transform: none; }

/* ================= PALETTE TAKEN FROM THE LOGO =================
   The badge is the brand: navy #002E84, black, white. Sampled from the artwork
   itself rather than approximated. The page now carries the logo's colours instead
   of the logo being bent to the page.

   Foundry ships a WARM press stock (#F7F1E4 cream, #241F1A brown ink). That warmth
   fights a cold navy badge, so the neutrals here are derived from the logo's own
   black and are cool. Specificity: [data-theme="navy"] sets --mark, so these have
   to beat an attribute selector; body[data-theme] does.

   Measured, all AA:
     ink #0B0C0E on stock ............. 18.76:1
     ink on stock-2 ................... 17.33:1
     secondary #53565C on stock ........ 7.06:1
     secondary on stock-2 .............. 6.52:1
     logo blue as link on stock ....... 11.68:1
     logo blue as link on stock-2 ..... 10.79:1
     white on logo blue (button) ...... 12.18:1
     white on hover fill .............. 15.35:1
     stock on ink (dark plate) ........ 18.76:1
     mark-on-dark #7FA3E8 on ink ....... 7.74:1   <- the logo blue itself is 1.61:1
                                                     there, which is why this exists */
body[data-theme] {
  --stock:   #FAFAFB;
  --stock-2: #F0F1F4;
  --ink:     #0B0C0E;
  --ink-2:   #53565C;
  --rule:    #D3D6DC;
  --mark:    #002E84;
  --mark-2:  #00205E;
  --mark-on-dark: #7FA3E8;
}
/* Anywhere the mark has to sit on the dark plate, use the lightened variant. The
   logo blue is nearly black-on-black there. */
.masthead .stamp, .plate--dark .stamp { color: var(--mark-on-dark); }

/* ================= HERO CALL TO ACTION =================
   The number is the control, not a label on a button. Set at display size in the
   same condensed face as the logo lettering, so the first fold reads: badge,
   headline, phone number.

   Deliberately NOT a bordered button. A 3rem phone number inside a button reads as
   a button that is too big; as a plain tappable number with a small label above it,
   it reads as a phone number that is meant to be called. */
.hero__act { display: flex; flex-direction: column; align-items: center;
  gap: clamp(.9rem, 2vw, 1.3rem); margin-top: clamp(1.4rem, 3vw, 2rem); }

.callcta { display: inline-flex; flex-direction: column; align-items: center;
  text-decoration: none; color: #fff; padding: .4rem 1rem;
  min-height: 48px;                      /* thumb target, not just visually large */
  transition: transform .18s ease; }
.callcta__label {
  font-family: var(--body); font-size: .72rem; font-weight: 700;
  letter-spacing: .18em; text-transform: uppercase;
  color: rgba(255,255,255,.82); margin-bottom: .15em; }
.callcta__num {
  font-family: var(--display); font-weight: 700;
  font-size: clamp(2.15rem, 6.2vw, 3.6rem); line-height: 1;
  letter-spacing: -0.01em;
  text-shadow: 0 2px 18px rgba(0,0,0,.55);   /* holds over a busy photograph */ }
.callcta:hover { transform: translateY(-2px); }
.callcta:hover .callcta__num { text-decoration: underline; text-underline-offset: .12em; }
.callcta:focus-visible { outline: 3px solid #fff; outline-offset: 6px; }

/* The form drops to a quiet ghost control. It is still a real action, it is just
   no longer competing with the number. */
.hero--photo .hero__act .btn--ghost {
  background: transparent; border-color: rgba(255,255,255,.6); color: #fff;
  font-size: .74rem; padding: .78rem 1.5rem; }
.hero--photo .hero__act .btn--ghost:hover { background: rgba(255,255,255,.15); }

@media (max-width: 520px) { .callcta__num { font-size: clamp(2rem, 11vw, 2.9rem); } }

/* ================= FIRST FOLD BUDGET =================
   Measured at 1633x726: the header ends at 131px, leaving 595px. The hero was
   spending 788px, which put the PHONE NUMBER below the fold, i.e. the one element
   this hero exists to show. Everything below is a cut against that budget.

   Sized in vh as well as px so it holds on a short laptop, not just this window. */
.hero--photo { padding-block: clamp(1.6rem, 3.4vh, 2.6rem); }

/* Logo: 170px was a third of the budget on its own. */
.brandmark { height: clamp(74px, 11vh, 116px); margin-bottom: clamp(.7rem, 1.6vh, 1.1rem); }

/* Headline: keep it commanding, stop it taking three lines of 58px. */
.hero--photo h1 { font-size: clamp(1.9rem, 4.4vw, 3.15rem); line-height: .98;
  margin: 0 0 .38em; }

/* The lead ran nearly the full 1076px before wrapping. Constrained, it breaks into
   two tight lines under the headline instead of one long ribbon. */
.hero--photo .lead { max-width: 58ch; margin-left: auto; margin-right: auto;
  font-size: clamp(.95rem, 1.15vw, 1.05rem); margin-bottom: 0; }

/* Tighten the vertical rhythm between the remaining blocks. */
.hero--photo .stamp { margin-bottom: clamp(.5rem, 1.2vh, .8rem); }
.hero__act { margin-top: clamp(.9rem, 2.2vh, 1.5rem); gap: clamp(.6rem, 1.4vh, 1rem); }

/* The number stays the biggest thing in the fold; it just stops being 58px on a
   726px-tall screen. */
.callcta__num { font-size: clamp(2rem, 4.6vh, 3.1rem); }
.callcta { padding: .25rem 1rem; }

/* ================= DESKTOP =================
   The fold-budget block above was tuned against a 726px-tall window and left 188px
   of slack on a desktop screen: the logo fell to 110px and the phone number to 33px,
   which is the opposite of the brief. Desktop has the room, so take it back.
   The compact sizes stay for short windows; this only raises the ceiling. */
@media (min-width: 1000px) and (min-height: 640px) {
  .hero--photo { padding-block: clamp(2rem, 4.4vh, 3.4rem); }
  .brandmark   { height: clamp(112px, 15vh, 158px); margin-bottom: clamp(1rem, 2.2vh, 1.5rem); }
  .hero--photo h1   { font-size: clamp(2.6rem, 3.9vw, 4rem); }
  .hero--photo .lead{ font-size: clamp(1.02rem, 1.05vw, 1.15rem); max-width: 60ch; }
  .hero__act   { margin-top: clamp(1.2rem, 2.8vh, 1.9rem); gap: clamp(.8rem, 1.7vh, 1.15rem); }
  /* The number is the point of this fold. On desktop it gets to be the biggest
     thing on the screen after the headline. */
  .callcta__num { font-size: clamp(3rem, 6.2vh, 4.4rem); }
  .callcta__label { font-size: .78rem; letter-spacing: .2em; }
  .hero--photo .hero__act .btn--ghost { font-size: .78rem; padding: .9rem 1.8rem; }
}

/* ================= LOGO OVERLAPS THE HEADER =================
   Bigger, and lifted so it breaks the line between the header and the hero.

   Two things had to move for this to work:

   1. `.hero--photo` carried `overflow: hidden` to keep the photograph inside the
      section, which also clipped anything lifted above the section's top edge. The
      clip now lives on the photo LAYER, so the picture still cannot escape but the
      content can.
   2. Measured: nav links end at y=95 and the header ends at y=131, while the logo's
      centre (817px) sits inside the nav span (754-1085). So the lift stops short of
      the nav text rather than crossing it. It overlaps the header's lower band and
      the boundary line, not the links.

   The badge carries its own dark ground (navy border, black field), so its white
   lettering still reads where it crosses the light header. */
.hero--photo { overflow: visible; }
.hero__photo { overflow: hidden; }

.brandmark { position: relative; z-index: 4; }

@media (min-width: 1000px) and (min-height: 640px) {
  .brandmark {
    height: clamp(132px, 18.5vh, 190px);
    /* Lift, but not past the nav baseline. */
    margin-top: clamp(-3.6rem, -5vh, -2.4rem);
    margin-bottom: clamp(.9rem, 2vh, 1.4rem);
    filter: drop-shadow(0 3px 16px rgba(0,0,0,.38)); }
}
/* Narrow or short screens: the header is tighter and the nav may wrap, so no lift. */
@media (max-width: 999px), (max-height: 639px) {
  .brandmark { margin-top: 0; }
}

/* Stronger lift. The previous clamp resolved to about -38px, which overlapped the
   header by only 7px and read as a near miss rather than a decision.

   Measured budget: the logo's natural top is 162, the header ends at 131 and the nav
   links end at 95. Lifting 58px puts the top at ~104: a 27px overlap of the header,
   still 9px clear of the nav text. Growing the height at the same time keeps the
   bottom where it was, so the fold budget is unchanged. */
@media (min-width: 1000px) and (min-height: 640px) {
  .brandmark { height: clamp(146px, 21vh, 178px); margin-top: -58px; }
}

/* Hero lead removed. The renderer requires the key, so it emits an empty <p>; this
   collapses it rather than leaving a phantom gap between headline and phone. */
.hero--photo .lead:empty { display: none; }

/* ================= HEADER SPACING =================
   Measured at 1633px: the nav sat 114px from the brand and 26px from the CTA. That
   is because `.brand` carries `margin-right:auto`, which pushes the nav AND the
   button to the right edge together, leaving them to share only the flex gap. The
   result reads as a void after the brand and a collision before the button.

   Fix: the nav takes the auto margins instead, so it centres in the space between
   the brand and the button, and a real minimum gap protects the button. */
.bar .wrap { gap: clamp(24px, 3vw, 48px); padding-block: 16px; }
.brand { margin-right: 0; }
.nav { margin-inline: auto; gap: clamp(22px, 2.2vw, 34px); }
/* Tracked uppercase at .78rem needs more air between items than a lowercase nav,
   and it needs a guaranteed gap before the button whatever the viewport. */
.bar .wrap > .btn { margin-left: clamp(20px, 2.4vw, 44px); flex: 0 0 auto; }

/* ---- eyebrow bar ----
   The label rendered 19px tall and the phone 28px, so the two sides of a 45px bar
   were at different type scales and the row looked lopsided. Same size, same
   weight, same tracking; the phone keeps its emphasis through colour, not bulk. */
.masthead .wrap { padding-block: .5rem; gap: clamp(16px, 2vw, 32px); }
.masthead .stamp,
.masthead__phone {
  font-family: var(--body);
  font-size: .74rem; font-weight: 700;
  letter-spacing: .14em; text-transform: uppercase;
  line-height: 1.6; }
.masthead__phone { color: #fff; }
.masthead__phone:hover { color: var(--mark-on-dark); }

/* Removing the brand's auto margin let flex shrink it, and "Reliable Garage Door
   Repair Service" broke onto two lines, which made the header taller than the row
   it sits in. The wordmark is a single object: it does not wrap. */
.brand { white-space: nowrap; flex: 0 0 auto; }
/* At 1100-1320 the full legal name, four nav items and the button stop fitting on
   one row. The nav is the part that can go: the same links are in the footer, and
   the button and the phone are the actions that matter. */
@media (max-width: 1320px) and (min-width: 1000px) {
  .bar .nav { display: none; }
}

/* ================= SERVICE TILE PHOTO BACKDROP =================
   Ported from the Herriman Handyman build. Hovering or keyboard-focusing a service
   plate fades that job's photo in behind the whole section; the other plates go
   translucent so the photo reads through them.

   Opt in per section with .has-photo-backdrop plus a .svc-bg layer, and it only
   runs on a real hover pointer with motion allowed. Touch and reduced-motion
   visitors keep the plain section, and the guard fails CLOSED.

   Foundry adaptation: the scrim is mixed from THIS build's --stock (#FAFAFB, cool)
   rather than Herriman's bone, and the active plate borders in --mark rather than
   their gold. The plates carry a 2px ink border and an inner hairline, which is a
   heavier frame than Herriman's tiles, so the thinned state keeps the border
   visible instead of washing it out completely. */
.has-photo-backdrop { position: relative; overflow: hidden; isolation: isolate; }
.has-photo-backdrop > .wrap { position: relative; z-index: 1; }
.svc-bg { position: absolute; inset: 0; z-index: 0; pointer-events: none;
  opacity: 0; transition: opacity .22s ease; }
.svc-bg img { position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; opacity: 0; transition: opacity .28s ease; }
.svc-bg img.is-shown { opacity: 1; }
/* Weighted scrim, heaviest across the top where the heading sits directly on the
   photo, opening up over the grid where the plates supply their own ground. */
.svc-bg::after { content: ""; position: absolute; inset: 0;
  /* Retuned for THESE photographs. Herriman's curve sat at .30 to .34 across the
     grid and erased Clark's images almost entirely, because his are tight interior
     shots with far less tonal range than their job photos.

     The curve now does one job: hold the section heading and lead, which sit
     DIRECTLY on the photo, and then get out of the way over the grid, where every
     plate carries its own ground. Measured against a fully black photo pixel:
       .62 at the heading band -> ink 6.35:1
       .22 over the grid, under a plate's .70 white -> ink 11.10:1 */
  background: linear-gradient(180deg,
    rgba(250,250,251,.93) 0%,
    rgba(250,250,251,.86) 11%,
    rgba(250,250,251,.62) 20%,
    rgba(250,250,251,.26) 31%,
    rgba(250,250,251,.22) 80%,
    rgba(250,250,251,.44) 100%); }
.has-photo-backdrop.is-showing .svc-bg { opacity: 1; }

@media (hover: hover) and (pointer: fine) {
  .plates.backdrop-ready .plate {
    transition: background-color .3s ease, border-color .2s ease,
                transform .12s ease, box-shadow .2s ease; }
  /* While a photo is up, the plates not being hovered thin out. The ink border
     stays: it is the thing that makes this design read as printed. */
  .has-photo-backdrop.is-showing .plates .plate {
    background: rgba(255,255,255,.70); box-shadow: none; }
  /* Darker body copy while a photo is up: --ink-2 fails AA on a thinned plate
     over a dark photo, --ink clears it with room to spare. */
  .has-photo-backdrop.is-showing .plates .plate p { color: var(--ink); }
  .has-photo-backdrop.is-showing .plates .plate.is-active {
    background: #fff; border-color: var(--mark);
    box-shadow: 0 10px 26px rgba(11,12,14,.22); transform: translateY(-3px); }
  .has-photo-backdrop.is-showing .plates .plate.is-active::after { border-color: var(--mark); }
  /* The one accent plate already sits on --mark; keep it legible while thinned. */
  .has-photo-backdrop.is-showing .plates .plate--mark { background: var(--mark); }
}
@media (prefers-reduced-motion: reduce) { .svc-bg, .svc-bg img { transition: none; } }

/* ============================================================
   CLIENT COMPONENT LAYER  ::  the parts of this site that are not
   in the vault design at all.

   Foundry is a ONE PAGE specimen. A 45 page site needs six things the
   specimen never had to draw: a lead capture form, a breadcrumb, a
   numbered service index, a dark section, a real photo figure and the
   fine print under a form. Those are the only rules in this file.

   Written against Foundry's tokens (--ink, --stock, --rule, --mark,
   --tracking, --rule-w, --display), never Press's. Press's component
   layer used --mono, --label, --edge, --lift, --shade and --tilt, none
   of which exist here, so these are rewrites and not a copy. Porting
   them across verbatim would have produced rules that silently resolve
   to nothing.
   ============================================================ */

/* ---- A dark section. Foundry ships --tint but no dark, and the process
        band and the "before you call" band on every service page both
        need one. It inverts the palette by REDEFINING the tokens rather
        than recolouring rule by rule, so every component inside it
        (.marks, .roll, .say, .plate, .strip) inverts with it and nothing
        has to be special cased.

        Measured on the client palette (--ink #0B0C0E as the ground):
          body     #FAFAFB ....... 18.76:1
          seconday #C3C6CC ....... 11.43:1
          stamp / link #7FA3E8 .... 7.74:1   the logo blue itself is 1.61:1
                                             here, which is why --mark-on-dark
                                             exists at all.                 ---- */
.section--dark {
  background: #0B0C0E;
  --ink:     #FAFAFB;
  --ink-2:   #C3C6CC;
  --rule:    rgba(250,250,251,.26);
  --stock:   #0B0C0E;
  --stock-2: rgba(250,250,251,.06);
  color: var(--ink);
}
.section--dark .stamp,
.section--dark a { color: var(--mark-on-dark); }
.section--dark .btn { border-color: #FAFAFB; color: #FAFAFB; }
.section--dark .btn:hover { background: #FAFAFB; color: #0B0C0E; }
.section--dark .btn--mark { background: var(--mark); border-color: var(--mark); color: #fff; }
.section--dark .btn--mark:hover { background: var(--mark-2); border-color: var(--mark-2); color: #fff; }
.section--dark .marks li::before { color: var(--mark-on-dark); }

/* ---- Breadcrumb. The outer element must stay EXACTLY `<nav class="crumb"`:
        build.py's derived_schema() reads the trail back out of the finished
        page to emit BreadcrumbList, and the crumb gate in check() looks for
        that literal string. This rule set is presentation only. ---- */
.crumb {
  font-size: .72rem; font-weight: 700;
  letter-spacing: var(--tracking); text-transform: uppercase;
  color: var(--ink-2);
  margin-bottom: 1.2rem;
}
.crumb p { margin: 0; max-width: none; }
.crumb a { color: var(--ink-2); text-decoration: none; }
.crumb a:hover { color: var(--mark); }
/* The hero is centred, but a breadcrumb reads as a trail and has to start at the
   left edge of the content column. */
.hero .crumb { text-align: left; }
.hero .crumb, .hero .crumb a { color: rgba(255,255,255,.82); }
.hero .crumb a:hover { color: #fff; }

/* ---- The numbered index chip on a service plate. Foundry's .plate has
        no corner mark; this is the one addition, and it sits in the
        double-rule inset rather than on top of it. ---- */
.plate .chip {
  position: absolute; right: 14px; top: 14px;
  width: 30px; height: 30px;
  display: grid; place-items: center;
  border: var(--rule-w) solid var(--ink);
  background: var(--stock-2); color: var(--ink);
  font-size: .66rem; font-weight: 700; letter-spacing: .04em;
}
.plate h3 { padding-right: 42px; }
.plate--mark .chip { border-color: rgba(255,255,255,.55); background: transparent; color: #fff; }

/* ---- A real photograph in a section. Foundry frames things with
        .framed; this is .framed carrying a photo instead of an SVG,
        with the caption in the sign-painter register. ---- */
.shot { margin: 0; }
.shot .plate-img { display: block; overflow: hidden; aspect-ratio: 4 / 3; }
.shot .plate-img > img, .shot .plate-img > svg { width: 100%; height: 100%; object-fit: cover; }
.shot--tall .plate-img { aspect-ratio: 4 / 4.6; }
.shot figcaption {
  margin-top: .7rem;
  font-size: .68rem; font-weight: 700;
  letter-spacing: var(--tracking); text-transform: uppercase; color: var(--ink-2);
  display: flex; justify-content: space-between; gap: 12px; flex-wrap: wrap;
}

/* ---- Lead capture. The class names, ids and field names in
        partials/form.html are a contract with assets/js/lead.js, the
        check() gate and the CRM callable. Nothing here may rename them. ---- */
.quote-card {
  border: calc(var(--rule-w) * 2) solid var(--ink);
  background: var(--stock);
  padding: clamp(1.2rem, 2.6vw, 1.7rem);
  position: relative;
  text-align: left;
}
.quote-card::after {
  content: ''; position: absolute; inset: 5px;
  border: 1px solid var(--rule); pointer-events: none;
}
.quote-card > * { position: relative; z-index: 1; }
.quote-card h2 { font-size: clamp(1.3rem, 2.4vw, 1.7rem); margin: 0 0 .35em; }
.quote-card__lead { color: var(--ink-2); font-size: .95rem; margin: 0 0 1.1rem; max-width: none; }

.qform .field { margin: 0 0 .85rem; max-width: none; }
.qform label {
  display: block; margin: 0 0 .35rem;
  color: var(--ink);
  font-size: .68rem; font-weight: 700;
  letter-spacing: var(--tracking); text-transform: uppercase;
}
.qform input, .qform select {
  width: 100%; min-height: 46px;
  padding: 0 .7rem;
  border: 1px solid var(--ink);
  background: var(--stock-2);
  color: var(--ink);
  font: 400 1rem/1.4 var(--body);
  border-radius: 0;
  appearance: none;
}
/* The native select arrow is drawn away by appearance:none, so it is redrawn
   here. Two gradients, no image request. */
.qform select {
  background-image:
    linear-gradient(45deg, transparent 50%, var(--ink) 50%),
    linear-gradient(135deg, var(--ink) 50%, transparent 50%);
  background-position: calc(100% - 18px) calc(50% + 2px), calc(100% - 13px) calc(50% + 2px);
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}
.qform input::placeholder { color: var(--ink-2); opacity: 1; }
.qform input:focus-visible, .qform select:focus-visible { outline: 2px solid var(--mark); outline-offset: 2px; }
.btn--block { display: flex; width: 100%; justify-content: center; margin-top: .3rem; }

.fineprint { margin: .7rem 0 0; color: var(--ink-2); font-size: .78rem; line-height: 1.45; max-width: none; }
.qform__status:empty { display: none; }
.qform__status[data-state="error"] { color: var(--mark); font-weight: 700; }

/* The honeypot. No visible label and no tab stop: see partials/form.html. */
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* ---- Hero with a form in it. The one page specimen centres its hero
        because it has nothing but the emblem and the number in it. A
        hero carrying a form is a two column layout, and the text column
        has to go back to ragged-right or it reads as a poster. ---- */
.hero__grid { display: grid; grid-template-columns: 1.05fr .95fr; gap: clamp(2rem, 4vw, 3.5rem); align-items: start; text-align: left; }
.hero__grid h1, .hero__grid .lead, .hero__grid p { margin-inline: 0; }
.hero--photo .hero__grid .stamp,
.hero--photo .hero__grid h1 { color: #fff; }
.hero--photo .hero__grid .lead { color: rgba(255,255,255,.88); }
.hero--photo .hero__grid .btn { border-color: #fff; color: #fff; }
.hero--photo .hero__grid .btn:hover { background: #fff; color: var(--ink); }
.hero--photo .hero__grid .btn--mark { background: var(--mark); border-color: var(--mark); color: #fff; }
.hero--photo .hero__grid .quote-card { color: var(--ink); }
@media (max-width: 860px) {
  .hero__grid { grid-template-columns: 1fr; }
}

/* The full-bleed hero photo runs the height of the plate. On an interior
   page the plate is shorter than the home page hero, so the focal point
   has to move up or the door is cropped out of frame entirely. */
.hero--page .hero__photo { background-position: center 48%; }

/* ---- The hero headline measure.

   The comparison build Porter approved the Foundry hero in was rendering
   WITHOUT css/base.css: the vault's builder wrote the page and provisioned no
   assets, so `../css/base.css` 404d from every page under clients/build/compare/.
   `h1 { max-width: var(--measure-display) }` then resolved against an undefined
   custom property, which is invalid at computed value time, so every headline
   fell back to max-width: none and ran the full 1076px of the wrap.

   The vault builder was fixed on 2026-09-12 (web-templates PR #2) and the
   comparison pages now render the way a shipped site does. This rule stays,
   because the approval does.

   This site has always loaded base.css properly, which is correct: --measure-body
   62ch is real typography on a 45 page content site and a 1076px line length is
   not. But it made the approved hero headline break to four lines at 364px
   instead of the two it was approved at.

   So the measures stay everywhere except here. A display headline over a
   photograph is a poster, not a paragraph, and it gets the full wrap. The
   body and heading measures are untouched.                              ---- */
.hero h1 { max-width: none; margin-inline: auto; }
/* In a two column hero the column IS the measure: 16ch inside a 540px column
   broke a long service page headline to five lines. */
.hero__grid h1 { max-width: none; margin-inline: 0; }

/* ---- A plate that is itself a link. Foundry's specimen .plate is an <article>
        with a separate .plate__more anchor inside it, so the design never had to
        reset link styling on the plate itself. On this site the service index
        plates ARE anchors, which made the whole tile inherit `a { color: var(--mark) }`
        and the UA underline: every card body rendered as underlined blue text. ---- */
a.plate { text-decoration: none; color: inherit; }
a.plate h3 { color: var(--ink); }
a.plate:hover .plate__more { color: var(--mark-2); border-bottom-color: var(--mark-2); }
.has-photo-backdrop.is-showing a.plate h3 { color: var(--ink); }

/* ---- The header bar on a phone.

   The bar is brand + nav + CTA. .nav goes display:none at 900px (foundry.css),
   which leaves the brand and the button, and at 375px "RELIABLE GARAGE DOOR" plus
   "GET A FREE INSPECTION" needs about 470px. The button ran off the right edge and
   was clipped by `body { overflow-x: clip }`, so it never showed as a scrollbar and
   looked deliberate.

   The button is dropped rather than shrunk. The phone number is directly above it
   in the masthead at every width, the hero carries the number again at display
   size, and a third control in a 375px bar is not worth a cramped brand. ---- */
@media (max-width: 620px) {
  .bar .btn { display: none; }
  .bar .brand { font-size: 1.28rem; }
}

/* ---- A figure whose own shape is the point. .shot and .shot--tall both pin an
        aspect ratio and crop to it with object-fit, which is right for a job photo
        going into a grid and wrong for the one portrait on the site: the owner
        photo is 0.538, and forcing it to 4:3 throws away either his face or the
        logo on his shirt. Here the image sets the height. ---- */
.shot--free .plate-img { aspect-ratio: auto; }
.shot--free .plate-img > img { height: auto; }

/* In the hero, a free-ratio portrait is capped by HEIGHT and scaled down rather
   than cropped. The About crop is 0.538, so filling a 510px hero column made it
   864px tall and the headline beside it floated in the middle of nowhere. Capping
   the height keeps the whole frame, face and logo included, and lets the figure
   sit as a portrait beside the text instead of towering over it. */
.hero__grid .shot--free { max-width: 300px; margin-inline: auto; }

/* ================= CTA MODES =================
   Owner decision 2026-09-12: an emergency page asks for a phone call, an
   installation page asks for an application, everything else asks for the short
   request form. build.py's cta_mode() decides; these are the two looks it needs.

   ---- "call": the number alone in the hero ---- */

/* With no form beside it the hero grid has ONE child, which would otherwise sit
   in the first of two columns and wrap the headline down the left half of the
   page for no reason. Spanning both is the whole fix, and `grid-column` works
   without :has() so it needs no support caveat. */
.hero__grid > :only-child { grid-column: 1 / -1; }

/* .hero__act centres itself for the home page, where the hero is centred. In a
   service hero the text is ragged-left, so the controls start at the left edge
   with it. */
/* .hero__act is flex-direction: column, so the horizontal axis is the CROSS axis
   and align-items is what moves it. justify-content does nothing here. */
.hero__act--left { align-items: flex-start; text-align: left; }
.hero__act--left .callcta { align-items: flex-start; padding-left: 0; }
.hero--photo .hero__grid .callcta__label { color: rgba(255,255,255,.86); }
.hero--photo .hero__grid .callcta__num { color: #fff; }

/* ---- "application": the long form ----
   The card opens at three fields. The other six live behind a <details>, so the
   installation hero is not a wall of selects before anyone has decided to engage.
   The form itself stays one column; only the folded block is two, which keeps the
   two REQUIRED fields full width where they read as the ask. */
.quote-card--application .more {
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  margin: .2rem 0 1rem;
}
.quote-card--application .more > summary {
  cursor: pointer; list-style: none;
  padding: .75rem 1.6rem .75rem 0;
  position: relative;
  font-size: .72rem; font-weight: 700;
  letter-spacing: var(--tracking); text-transform: uppercase;
  color: var(--mark);
}
/* Safari still draws its own triangle without this. */
.quote-card--application .more > summary::-webkit-details-marker { display: none; }
/* A plus that becomes a minus. Rotating a single glyph keeps the two states the
   same width, so the label never shifts when it opens. */
.quote-card--application .more > summary::after {
  content: "+"; position: absolute; right: 2px; top: 50%;
  translate: 0 -50%;
  font-size: 1.1rem; font-weight: 400; line-height: 1;
  transition: rotate var(--dur, .2s) var(--ease, ease);
}
/* U+2212 MINUS SIGN, the typographic partner of "+", and deliberately NOT the
   en dash U+2013 that was here first. No en or em dashes in visible copy, and
   a generated ::after marker is visible copy. */
.quote-card--application .more[open] > summary::after { content: "\2212"; }
.quote-card--application .more > summary:hover { color: var(--mark-2); }
.quote-card--application .more > summary:focus-visible {
  outline: 2px solid var(--mark); outline-offset: 2px;
}
.more__grid { padding-bottom: .35rem; }
@media (min-width: 560px) {
  .more__grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0 1rem; }
}

/* ---- The fact strip's last item, when it is alone on its row.

   .strip is repeat(auto-fit, minmax(150px, 1fr)), and auto-fit collapses empty
   tracks, so a strip whose items all fit on one row is never affected. The
   orphan appears only when the item count exceeds the column count unevenly:
   three chips in two columns leaves the third alone with a 166px hole beside it,
   which is what the emergency, city and service pages do on a phone.

   Column counts here are MEASURED, not assumed: this strip resolves to 2 columns
   below 496px, 3 from 496px and 4 from 644px, walked in 4px steps at the wrap's
   real width. If the wrap or the 150px track ever changes, re-measure these two
   breakpoints rather than nudging them.

   Spanning is safe because the strip separates cells with borders and not gaps. */
@media (max-width: 495px) {
  .strip > div:last-child:nth-child(odd) { grid-column: 1 / -1; }
}
@media (min-width: 496px) and (max-width: 643px) {
  .strip > div:last-child:nth-child(3n + 1) { grid-column: 1 / -1; }
}

/* ================= WHAT CUSTOMERS SAY =================
   Owner decision 2026-09-13. Fifty reviews say three things over and over, so the
   home section makes those three the argument instead of stacking praise: our
   heading states the claim, the customer's own words back it, and the reader can
   check one against the other in the same column.

   Foundry already has .say for a single quote. This is the themed variant and it
   is a different object: .say is a quotation, .theme is an argument with a
   quotation under it. The mark-coloured top rule is what ties the three together
   as a set of claims rather than three unrelated cards. */
.themes {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: clamp(1.5rem, 3vw, 2.6rem);
  margin-top: 2rem;
}
.theme { border-top: calc(var(--rule-w) * 3) solid var(--mark); padding-top: 1rem; }
.theme h3 { margin-bottom: .55em; }
.theme blockquote {
  margin: 0;
  font-size: .95rem; line-height: 1.6; color: var(--ink-2);
  /* The quote is the evidence, so it is set as readable prose rather than in the
     display face. Foundry's .say uses --display because there it IS the object. */
}
.theme figcaption {
  margin-top: .85rem;
  font-size: .66rem; font-weight: 700;
  letter-spacing: var(--tracking); text-transform: uppercase; color: var(--ink);
}
.theme figcaption span { color: var(--ink-2); font-weight: 400; }

/* A single quote on a service page. Wider measure than the three-up, because it is
   alone on its row and a 250px column would leave it stranded in the left third. */
.say--solo { max-width: 62ch; margin: 1.4rem 0 0; }
.say--solo blockquote { font-size: clamp(1.05rem, 2vw, 1.28rem); }

/* ---- His social profiles in the footer. Same list as the schema sameAs. ---- */
.social {
  display: flex; flex-wrap: wrap; gap: .5rem 1.4rem;
  margin: 1.6rem 0 0; max-width: none;
  font-size: .7rem; font-weight: 700;
  letter-spacing: var(--tracking); text-transform: uppercase;
}
.social a { color: var(--ink-2); text-decoration: none; }
.social a:hover { color: var(--mark); }

/* FAQ question. An <h3> inside <summary> so the question joins the heading
   outline, styled back to the <b> it replaced: inline, inherited size, bold.
   Without this an h3's UA margins and size would break the accordion rhythm. */
.faq-q { display: inline; margin: 0; font: inherit; font-weight: 700; }
