/* ============================================================
   base.css - ERS design system (dark-first, mobile-first)
   Tokens: #0a0a0a bg · #F59E0B amber CTA (white text) · 12px radius
   Inter (self-hosted) body · Paramount Sans display headings.
   No framework. Plain custom properties.
   ============================================================ */

/* ---------------- self-hosted fonts ---------------- */
@font-face {
  font-family: "Inter"; font-style: normal; font-weight: 400; font-display: swap;
  src: url("/fonts/inter-400.woff2") format("woff2");
}
@font-face {
  font-family: "Inter"; font-style: normal; font-weight: 600; font-display: swap;
  src: url("/fonts/inter-600.woff2") format("woff2");
}
@font-face {
  font-family: "Inter"; font-style: normal; font-weight: 800; font-display: swap;
  src: url("/fonts/inter-800.woff2") format("woff2");
}
/* ---------------------------------------------------------------------------
   DISPLAY TYPEFACE - LICENCE GATE                             (2026-08-26)

   "Paramount Sans" (designer Roman Jokiranta, sold on Creative Market) is a
   PAID typeface. No webfont licence for it exists in this repo or any other
   ERS repo, and a Creative Market *desktop* licence does NOT cover @font-face
   web embedding - that is a separate, separately-priced licence tier.

   The display face therefore defaults to Oswald, which is licensed under the
   SIL Open Font License 1.1 (see fonts/OFL.txt) and explicitly permits
   webfont embedding. Oswald is a close match for Paramount's condensed
   proportions in UPPERCASE; in mixed case it renders true lowercase where
   Paramount rendered small capitals, so mixed-case headings look slightly
   taller and less uniformly blocky than before.

   *** DO NOT enable the Paramount path without a purchased Creative Market
   *** webfont licence covering these domains and their pageview volume.
   To re-enable it after purchase:
     1. restore fonts/paramount.woff2 (quarantined 2026-08-26, see
        docs/licenses/fonts.md for its location),
     2. uncomment the @font-face below,
     3. change --font-display to var(--font-display-licensed).
   --------------------------------------------------------------------------- */
@font-face {
  font-family: "Oswald";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("/fonts/oswald-700.woff2") format("woff2");
}
/* LICENCE-GATED - requires a Creative Market webfont licence before enabling.
@font-face {
  font-family: "Paramount";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("/fonts/paramount.woff2") format("woff2");
}
*/

:root {
  --bg: #0a0a0a;
  --surface: #141414;
  --surface-2: #1c1c1c;
  --border: #2a2a2a;
  --text: #fafafa;
  --muted: #a3a3a3;
  --amber: #F59E0B;
  --amber-hover: #d97f06;
  /* Dark text on amber: white-on-amber measured 2.15:1 (WCAG fail); #2a1a00 on
     amber is 7.85:1 and 5.61:1 on the hover shade (both pass AA). */
  --on-amber: #2a1a00;
  --radius: 12px;
  --radius-lg: 18px;
  --wrap: 1120px;
  --gap: clamp(1rem, 3vw, 2rem);
  --font-body: "Inter", system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
  --font-display-safe: "Oswald", "Inter", system-ui, sans-serif;
  --font-display-licensed: "Paramount", "Oswald", "Inter", system-ui, sans-serif;
  /* SAFE by default. Do not point this at --font-display-licensed
     until a Paramount Sans webfont licence is purchased. */
  --font-display: var(--font-display-safe);
}

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
body {
  margin: 0; background: var(--bg); color: var(--muted);
  font-family: var(--font-body); font-size: 17px; line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}
img { max-width: 100%; height: auto; display: block; }
a { color: var(--amber); text-decoration: none; }
a:hover { text-decoration: underline; }
h1, h2, h3 { color: var(--text); font-family: var(--font-display); line-height: 1.1; letter-spacing: -0.02em; margin: 0 0 .5em; }
h1 { font-size: clamp(2.1rem, 6vw, 3.6rem); font-weight: 700; }
h2 { font-size: clamp(1.6rem, 4vw, 2.4rem); font-weight: 700; }
h3 { font-size: 1.25rem; font-weight: 800; font-family: var(--font-body); }
p { margin: 0 0 1rem; }

.wrap { width: 100%; max-width: var(--wrap); margin: 0 auto; padding-inline: clamp(1rem, 4vw, 2rem); }
.center { text-align: center; }
.eyebrow { text-transform: uppercase; letter-spacing: .14em; font-size: .8rem; font-weight: 600; color: var(--amber); margin: 0 0 .6rem; }
.lede { font-size: clamp(1.05rem, 2.2vw, 1.3rem); color: var(--muted); max-width: 60ch; }

/* ---------------- skip link + a11y ---------------- */
.skip-link { position: absolute; left: -9999px; top: 0; background: var(--amber); color: var(--on-amber); padding: .6rem 1rem; border-radius: var(--radius); z-index: 100; }
.skip-link:focus { left: 1rem; top: 1rem; }
/* Present to screen readers and to the document outline, invisible on screen. */
.visually-hidden { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0; }
:focus-visible { outline: 2px solid var(--amber); outline-offset: 2px; }

/* ---------------- buttons ---------------- */
.btn {
  display: inline-block; font-weight: 600; font-size: 1rem; line-height: 1;
  padding: .95rem 1.5rem; border-radius: var(--radius); border: 1px solid transparent;
  cursor: pointer; transition: background .15s, border-color .15s, transform .05s; text-decoration: none;
}
.btn:hover { text-decoration: none; }
.btn:active { transform: translateY(1px); }
.btn-primary { background: var(--amber); color: var(--on-amber); }
.btn-primary:hover { background: var(--amber-hover); color: var(--on-amber); }
.btn-ghost { background: transparent; color: var(--text); border-color: var(--border); }
.btn-ghost:hover { border-color: var(--amber); color: var(--text); }
.cta-row { display: flex; flex-wrap: wrap; gap: .8rem; margin-top: 1.4rem; }

/* ---------------- header / nav ---------------- */
.site-header { position: sticky; top: 0; z-index: 40; background: rgba(10,10,10,.82); backdrop-filter: blur(10px); border-bottom: 1px solid var(--border); }
.header-inner { display: flex; align-items: center; justify-content: space-between; min-height: 66px; }
.brand { flex: none; display: flex; align-items: center; gap: .65rem; font-family: var(--font-display); font-weight: 700; font-size: 1.25rem; color: var(--text); letter-spacing: -.02em; }
/* Optional header lockup image: <img class="brand-logo" src="/img/ers-lockup.png"> */
.brand-logo { height: 46px; width: auto; display: block; flex: none; }
.brand:hover { text-decoration: none; color: var(--amber); }
.site-nav { display: flex; align-items: center; gap: 1.4rem; }
.site-nav a { color: var(--muted); font-weight: 600; font-size: .98rem; }
.site-nav a:hover { color: var(--text); text-decoration: none; }
.nav-cta { color: var(--on-amber) !important; }
.nav-burger { display: none; }

/* ---------------- grouped nav (optional) ----------------
   Only renders when site.config.js defines `ia`. A flat `nav` alone keeps the
   original single-row menu, so existing sites are unaffected.
   Markup is <details class="nav-group"><summary class="nav-top">, which means
   the whole menu expands, collapses and announces its state with JavaScript
   off. layout.js only layers hover-open and one-at-a-time on top.
   Reach for this once a site passes ~7 top-level links: past that the row
   either wraps or gets squeezed until the wordmark has to be hidden. */
.nav-groups { display: flex; align-items: center; gap: 1.4rem; }
.nav-top {
  display: inline-flex; align-items: center; gap: .34rem;
  color: var(--muted); font-weight: 600; font-size: .98rem; white-space: nowrap;
  cursor: pointer; list-style: none;
}
.nav-top::-webkit-details-marker { display: none; }
.nav-top:hover, .nav-group[open] > .nav-top { color: var(--text); }
/* caret: a rotated corner, so it needs no icon font and inherits colour */
.nav-caret {
  width: .4em; height: .4em; flex: none; margin-top: -.18em;
  border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
  transform: rotate(45deg); transition: transform .2s var(--ease);
}
.nav-group[open] .nav-caret { transform: rotate(-135deg); margin-top: .1em; }
.nav-panel-list { list-style: none; margin: 0; padding: 0; }

@media (min-width: 821px) {
  .nav-group { position: relative; }
  .nav-panel {
    position: absolute; top: calc(100% + 15px); left: -1.15rem;
    min-width: 264px; padding: 1.15rem 1.25rem 1rem;
    background: var(--surface); border: 1px solid var(--border);
    border-radius: var(--radius-lg); box-shadow: var(--shadow-lg);
    z-index: 60;
  }
  /* invisible bridge across the gap so the panel does not snap shut while the
     cursor travels from the label down into it */
  .nav-group[open]::after { content: ""; position: absolute; top: 100%; left: -1.15rem; width: 264px; height: 17px; }
  .nav-panel-blurb { margin: 0 0 .85rem; padding-bottom: .8rem; border-bottom: 1px solid var(--border); color: var(--muted); font-size: .84rem; line-height: 1.45; white-space: normal; }
  .nav-panel-list a { display: block; padding: .52rem .6rem; margin-inline: -.6rem; border-radius: 10px; color: var(--text); font-size: .95rem; font-weight: 600; }
  .nav-panel-list a:hover { background: var(--surface-2); color: var(--amber-text, var(--amber)); text-decoration: none; }
}

@media (max-width: 820px) {
  /* Hamburger is a real <button> - 44x44 tap target (WCAG 2.5.5), keyboard
     focusable, gets the global :focus-visible ring. */
  .nav-burger {
    display: flex; flex: none; flex-direction: column; justify-content: center; align-items: center;
    gap: 5px; width: 44px; height: 44px; padding: 0; cursor: pointer;
    background: none; border: 0; color: inherit;
  }
  .nav-burger span { display: block; width: 24px; height: 2px; background: var(--text); border-radius: 2px; transition: .2s; }
  .site-nav {
    position: fixed; inset: 66px 0 auto 0; flex-direction: column; align-items: stretch; gap: 0;
    background: var(--surface); border-bottom: 1px solid var(--border); padding: .5rem 0;
    transition: transform .22s ease; max-height: calc(100vh - 66px); overflow: auto;
  }
  .site-nav a { padding: .9rem 1.5rem; border-top: 1px solid var(--border); }
  .nav-cta { margin: .6rem 1.5rem; text-align: center; border-radius: var(--radius); }
  /* With JS the menu is a disclosure: collapsed AND removed from the tab order
     (visibility:hidden) until the button opens it, so keyboard users don't tab
     onto invisible off-screen links. Without JS (.js absent) the nav stays
     visible/stacked so every link remains reachable. */
  .js .site-nav { transform: translateY(-120%); visibility: hidden; }
  .js .site-header.nav-open .site-nav { transform: translateY(0); visibility: visible; }

  /* In the drawer the same <details> groups become accordions. */
  .nav-groups { display: flex; flex-direction: column; align-items: stretch; gap: 0; }
  .nav-group { border-top: 1px solid var(--border); }
  .nav-top { justify-content: space-between; min-height: 44px; padding: .9rem 1.5rem; }
  a.nav-top.nav-flat { border-top: 1px solid var(--border); }
  .nav-panel { background: var(--surface-2); padding: .35rem 0 .6rem; }
  .nav-panel-blurb { display: none; }
  /* deeper indent + lighter weight so children read as children */
  .nav-panel-list a { display: block; padding: .62rem 1.5rem .62rem 2.4rem; font-weight: 500; border-top: 0; }
}

/* ---------------- hero + sections ---------------- */
.hero { padding: clamp(3rem, 9vw, 6rem) 0 clamp(2rem, 6vw, 4rem); background:
  radial-gradient(1200px 500px at 80% -10%, rgba(245,158,11,.12), transparent 60%), var(--bg); }
.section { padding: clamp(2.5rem, 7vw, 5rem) 0; }
.section-title { max-width: 24ch; }

/* ---- rotating hero video (decorative, full-bleed behind the hero copy) ---- */
.hero-has-video { position: relative; overflow: hidden; isolation: isolate; }
.hero-has-video .wrap { position: relative; }
.hero-video { position: absolute; inset: 0; overflow: hidden; pointer-events: none; z-index: -1; }
.hero-video video { position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; opacity: 0; transition: opacity .6s ease; }
.hero-video video.hv-on { opacity: 1; }
/* Scrim keeps hero copy readable (WCAG AA) over any footage. */
.hero-has-video::after { content: ""; position: absolute; inset: 0; z-index: -1;
  background: linear-gradient(180deg, rgba(9,12,16,.72), rgba(9,12,16,.88)); }
.hero-has-video { --text: #f5f7fa; --muted: #c7d0da; color: var(--muted); }
@media (prefers-reduced-motion: reduce) {
  .hero-video video { display: none; transition: none; }
}
.tone-raise { background: var(--surface); border-block: 1px solid var(--border); }
.tone-cta { background: linear-gradient(180deg, var(--surface), var(--bg)); }
.cta-band { text-align: center; padding: clamp(1.5rem, 5vw, 3rem) 0; }
.cta-band .cta-row { justify-content: center; }

/* ---------------- cards ---------------- */
.card-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: var(--gap); }
.card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 1.6rem; }

/* ---- card hover POP (lift + scale + shadow elevation) ----
   Applied to every card-like component. Hover-capable pointers get the full
   pop; touch devices get a subtle always-on elevation plus a press effect so
   the sense of depth is not hover-only. Transform animation is disabled under
   prefers-reduced-motion (border/shadow color feedback is kept). */
.card, .post-card, .gallery-item {
  transition: border-color .18s ease, transform .18s ease, box-shadow .18s ease;
}
@media (hover: hover) and (pointer: fine) {
  .card:hover, .post-card:hover, .gallery-item:hover {
    border-color: var(--amber);
    transform: translateY(-5px) scale(1.015);
    box-shadow: 0 18px 38px -14px rgba(0, 0, 0, .65);
  }
}
@media (hover: none) {
  .card, .post-card, .gallery-item {
    box-shadow: 0 10px 26px -16px rgba(0, 0, 0, .6);
  }
  .card:active, .post-card:active, .gallery-item:active {
    border-color: var(--amber);
    transform: scale(.985);
  }
}
@media (prefers-reduced-motion: reduce) {
  .card:hover, .post-card:hover, .gallery-item:hover,
  .card:active, .post-card:active, .gallery-item:active {
    transform: none;
  }
}
.card-icon { font-size: 1.8rem; margin-bottom: .6rem; }
.card h3, .card .card-title { color: var(--text); font-family: var(--font-body); font-weight: 800; font-size: 1.25rem; line-height: 1.1; letter-spacing: -0.02em; margin: 0 0 .4rem; }
.card p { color: var(--muted); margin-bottom: .6rem; }
.card-link { font-weight: 600; }
.card-img { border-radius: var(--radius); margin-bottom: 1rem; aspect-ratio: 16/10; object-fit: cover; width: 100%; }
.tag { display: inline-block; font-size: .72rem; text-transform: uppercase; letter-spacing: .08em; color: var(--amber); border: 1px solid var(--border); border-radius: 999px; padding: .2rem .7rem; margin-bottom: .6rem; }
.price-note { color: var(--muted); font-size: .92rem; }
.empty { color: var(--muted); text-align: center; padding: 2rem 0; }

/* ---------------- quotes ---------------- */
.quote-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: var(--gap); }
.quote { margin: 0; background: var(--surface); border-left: 3px solid var(--amber); border-radius: var(--radius); padding: 1.4rem; }
.quote p { color: var(--text); font-size: 1.05rem; }
.quote cite { color: var(--muted); font-style: normal; font-weight: 600; font-size: .9rem; }

/* ---------------- product detail ---------------- */
.product-detail { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--gap); }
@media (min-width: 780px) { .product-detail { grid-template-columns: 1fr 1.2fr; align-items: start; } }
.product-hero-img { border-radius: var(--radius-lg); border: 1px solid var(--border); }
.product-body :is(h2,h3) { color: var(--text); margin-top: 1.5rem; }
/* Wide tables scroll inside their own container instead of the page body. */
.table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.spec-table { width: 100%; border-collapse: collapse; margin: 1rem 0; }
.spec-table th, .spec-table td { text-align: left; padding: .7rem .8rem; border-bottom: 1px solid var(--border); }
.spec-table th { color: var(--muted); font-weight: 600; width: 45%; }
.spec-table td { color: var(--text); }

/* ---------------- gallery ---------------- */
.gallery-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: var(--gap); }
.gallery-item { margin: 0; border-radius: var(--radius-lg); overflow: hidden; border: 1px solid var(--border); background: var(--surface); }
.gallery-item img { aspect-ratio: 4/3; object-fit: cover; width: 100%; }
.gallery-item figcaption { padding: .8rem 1rem; color: var(--muted); font-size: .9rem; }

/* ---------------- blog ---------------- */
.post-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: var(--gap); }
.post-card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 1.4rem; }
.post-card h2 { font-size: 1.3rem; }
.post-meta { color: var(--muted); font-size: .85rem; }
.article { max-width: 760px; padding-block: clamp(2rem, 6vw, 3.5rem); }
.article-cover, .article img { border-radius: var(--radius-lg); margin: 1.2rem 0; }
.article-body { color: #cfcfcf; font-size: 1.08rem; }
.article-body :is(h2,h3) { color: var(--text); margin-top: 1.8rem; }

/* ---------------- forms ---------------- */
.contact-grid { display: grid; gap: var(--gap); }
@media (min-width: 820px) { .contact-grid { grid-template-columns: 1fr 1.4fr; } }
.contact-info { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 1.6rem; }
.contact-info h2 { font-size: 1.3rem; }
.contact-info strong { color: var(--text); }
.lead-form { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 1.6rem; }
.form-heading { font-size: 1.4rem; }
.form-sub { color: var(--muted); }
.lead-form label { display: block; color: var(--muted); font-size: .9rem; font-weight: 600; margin-bottom: 1rem; }
.field-row { display: grid; gap: 0; }
@media (min-width: 560px) { .field-row { grid-template-columns: 1fr 1fr; gap: 1rem; } }
.lead-form input, .lead-form textarea, .lead-form select {
  width: 100%; margin-top: .35rem; background: var(--bg); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--radius); padding: .8rem .9rem; font: inherit;
  min-height: 44px;
}
/* Focused fields keep a VISIBLE ring (the old outline:none suppressed it). */
.lead-form input:focus, .lead-form textarea:focus, .lead-form select:focus { border-color: var(--amber); }
.lead-form input:focus-visible, .lead-form textarea:focus-visible, .lead-form select:focus-visible { outline: 3px solid var(--amber); outline-offset: 2px; }
.lead-form .hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
.form-status { min-height: 1.2em; margin: .6rem 0 0; font-size: .9rem; }
.form-status.ok { color: #34d399; }
.form-status.err { color: #f87171; }
.notice { padding: 1rem 1.2rem; border-radius: var(--radius); margin-bottom: 1.2rem; border: 1px solid var(--border); }
.notice-ok { border-color: #14532d; background: rgba(34,197,94,.1); color: #86efac; }
.notice-muted { color: var(--muted); background: var(--surface-2); }

/* ---------------- booking ---------------- */
.slot-list { list-style: none; padding: 0; display: flex; flex-wrap: wrap; gap: .6rem; }

/* ---------------- footer ---------------- */
.site-footer { border-top: 1px solid var(--border); background: var(--surface); margin-top: 3rem; padding: clamp(2rem, 5vw, 3.5rem) 0 1.5rem; }
.footer-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: var(--gap); }
/* Sitemap-style footer, used when site.config.js defines `ia`. The sitemap gets
   its own full-width band rather than sharing a row with the brand block: at
   1120px a shared row leaves each column ~100px and wraps most labels onto two
   or three lines. Brand, contact, family and social sit in the band below. */
.footer-sitemap { display: grid; grid-template-columns: 1fr; gap: 1.2rem 1.6rem; }
.footer-base {
  display: grid; grid-template-columns: 1fr; gap: var(--gap);
  margin-top: 2rem; padding-top: 1.6rem; border-top: 1px solid var(--border);
}
/* Wrapping link text must not collide with the 44px tap-target rule, which
   makes every anchor an inline-flex box. */
.footer-links a { line-height: 1.35; }
.footer-inline { display: flex; flex-wrap: wrap; gap: 0 1.1rem; }
.footer-inline li { margin: 0; }
@media (min-width: 480px) {
  .footer-sitemap { grid-template-columns: repeat(2, 1fr); }
  .footer-base { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 700px) { .footer-sitemap { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 1000px) {
  .footer-sitemap { grid-template-columns: repeat(5, 1fr); }
  /* the contact column carries the street address, so it gets the widest share */
  .footer-base { grid-template-columns: 1.15fr 1.5fr 1fr 1fr; }
}
.footer-brand { font-family: var(--font-display); font-weight: 700; font-size: 1.2rem; color: var(--text); }
.footer-tagline { color: var(--muted); font-size: .92rem; max-width: 34ch; }
.footer-legal-name { color: var(--muted); font-size: .85rem; }
.footer-col h3, .footer-col .footer-heading { color: var(--text); font-family: var(--font-body); font-weight: 800; font-size: .95rem; line-height: 1.1; letter-spacing: -0.02em; margin: 0 0 .8rem; }
.footer-col p { margin: .3rem 0; font-size: .92rem; }
.footer-links { list-style: none; padding: 0; margin: 0; }
.footer-links li { margin: 0; }
.footer-links a, .footer-col a { color: var(--muted); }
/* Full 44px tap targets for footer link lists (WCAG 2.5.5).
   min-width catches one-letter labels like the "X" social link. */
.footer-links a, .footer-meta a { display: inline-flex; align-items: center; min-height: 44px; min-width: 44px; }
.footer-links a:hover, .footer-col a:hover { color: var(--amber); }
.footer-bottom { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: .5rem; margin-top: 2rem; padding-top: 1.2rem; border-top: 1px solid var(--border); color: var(--muted); font-size: .84rem; }
/* Gap-separated rather than " · " text: the 44px min-width tap targets made
   the interpuncts sit at visibly uneven distances from their labels. */
.footer-meta { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 0 1.4rem; }

@media (prefers-reduced-motion: reduce) { * { transition: none !important; scroll-behavior: auto !important; } }

/* ============================================================
   Shared system parity - the brand-agnostic half of the ERS
   Metals design pass, expressed only in tokens so the same block
   drops into the engine, ers-hub-site and ers-installations-site
   without touching any site's palette.

   Everything here is either a defect fix or a system rule that
   every ERS site needs. Anything that carries brand identity
   (hero treatment, colour choices, per-site components) stays in
   the site's own section below.
   ============================================================ */

/* ---- vertical rhythm -------------------------------------------------------
   Two tokens, because the two cases are not the same. --section-y is padding
   on a TRANSPARENT section, so two adjacent sections leave 2x of it as
   genuinely empty page. --band-y is padding inside a COLORED band, where the
   space is filled and reads as the band's own breathing room, so it can be
   larger. Both were a single ~5.5rem value, which left ~176px of dead page
   between any two plain sections. */
:root {
  --section-y: clamp(2.5rem, 5vw, 3.5rem);
  --band-y: clamp(3rem, 6vw, 4.5rem);
}
.section { padding: var(--section-y) 0; }
.section:is(.tone-raise, .tone-dark, .tone-cta, .tone-amber) { padding-block: var(--band-y); }
/* A trailing <p>/<ul>/<table> adds its own bottom margin on top of the
   section's padding, which is what pushed the worst gaps past 190px. The
   padding is the spacing contract; nothing inside gets to add to it. The
   second selector reaches one level in for a trailing <table> that sits inside
   .table-scroll and so escapes a direct-child reset. */
.section > .wrap > :last-child,
.section > .wrap > :last-child > :last-child,
.hero > .wrap > :last-child,
.article > :last-child,
.article-body > :last-child { margin-bottom: 0; }
/* A conversion band is rendered as section({tone, body: ctaBand()}), so its own
   padding stacked on the section's. Inside a section the section owns it. */
.section > .wrap > .cta-band { padding-block: 0; }

/* [hidden] must beat the layout display rules below (e.g. the lightbox flex),
   or a JS-toggled element stays on screen. */
[hidden] { display: none !important; }

/* ---- card elevation: two generic hooks -------------------------------------
   The per-component hover list above is a hand-kept allow-list, and plain CSS
   gives us no way to name a selector list once - which is exactly how a card
   family ships dead on hover. For any NEW component do NOT extend that list:
   put `lift` (or `lift-flat` for a wide element or a chip) in the class
   attribute instead. */
.lift, .lift-flat {
  /* colour + background ride along so a component whose hover is purely a
     colour change eases instead of snapping, without a second `transition`
     declaration that would overwrite this one. */
  transition: transform .3s var(--ease), box-shadow .3s var(--ease),
              border-color .3s var(--ease), color .3s var(--ease), background-color .3s var(--ease);
}
@media (hover: hover) {
  .lift:hover { transform: translateY(-6px) scale(1.015); box-shadow: var(--shadow-lg); }
  /* Wide cards and chips lift without scale - scaling a full-width card warps it. */
  .lift-flat:hover { transform: translateY(-3px); box-shadow: var(--shadow-md); }
}
/* Keyboard parity. Deliberately OUTSIDE @media (hover: hover): a keyboard is
   not a mouse, and a tablet with one attached still needs the cue. Two focus
   pseudos, because there are two shapes of card - the ones that ARE the anchor
   take :focus-visible, the ones whose link is a CHILD take :focus-within. */
.lift:focus-visible, .lift:focus-within { transform: translateY(-6px) scale(1.015); box-shadow: var(--shadow-lg); }
.lift-flat:focus-visible, .lift-flat:focus-within { transform: translateY(-3px); box-shadow: var(--shadow-md); }
@media (hover: none) {
  .lift:active, .lift-flat:active { transform: scale(.985); }
}
/* One ELEMENT list rather than a hover/active pseudo pair: matching the element
   means a newly added :focus-visible or :focus-within lift cannot slip through
   un-neutered. `* { transition: none }` further down only kills the tween; an
   untreated hover transform would still be an instant jump, which is precisely
   what this setting exists to prevent. */
@media (prefers-reduced-motion: reduce) {
  .lift, .lift-flat { transform: none !important; }
}

/* ---- forms -----------------------------------------------------------------
   `font: inherit` picked up the label's .9rem, so fields computed to ~15px.
   iOS Safari zooms the page in on any field under 16px and does NOT zoom back
   out afterwards, which strands mobile visitors mid-form on /contact and /book.
   The label stays .9rem; only the control is pinned to 16px. */
.lead-form input, .lead-form textarea, .lead-form select {
  font-size: 16px;
  min-height: 44px;               /* WCAG 2.5.5 tap target */
}
/* A bare <select> renders as a native control that ignores the field styling
   above on macOS/iOS. Strip the appearance and draw our own caret. */
.lead-form select {
  appearance: none; -webkit-appearance: none;
  padding-right: 2.4rem;
  background-image:
    linear-gradient(45deg, transparent 50%, currentColor 50%),
    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: right 1.15rem top 55%, right .85rem top 55%;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}
.lead-form textarea { min-height: 7rem; }

/* ---- tap targets -----------------------------------------------------------
   >=44px on every link list (WCAG 2.5.5; roughly two thirds of this traffic is
   mobile). min-width catches one-letter labels like the "X" social link. */
.footer-links a, .footer-meta a, .footer-contact-line a, .card-link {
  display: inline-flex; align-items: center; min-height: 44px; min-width: 44px;
}
/* Gap-separated rather than " · " text: the 44px min-width made the interpuncts
   sit at visibly uneven distances from their labels. */
.footer-meta { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 0 1.4rem; }

/* ---- small print -----------------------------------------------------------
   Warranty caveats, "confirm at time of order" and the like. Quieter than body
   copy but still meant to be read, so it keeps the muted colour rather than
   dropping below it. */
.note { color: var(--muted); font-size: .92rem; max-width: 68ch; margin-top: 1rem; }

/* ---- chips (filters, categories, tags) ------------------------------------
   Plain links, so filtering keeps working with JavaScript off. */
.chip {
  display: inline-flex; align-items: center; gap: .45rem; min-height: 44px; padding: .6rem 1.1rem;
  border: 1px solid var(--border); border-radius: 999px; background: var(--surface);
  color: var(--muted); font-size: .9rem; font-weight: 600;
}
.chip:hover, .chip:focus-visible { color: var(--text); border-color: var(--amber); text-decoration: none; }
.chip[aria-current], .chip-on { background: var(--amber); border-color: var(--amber); color: var(--on-amber); font-weight: 700; }
.chip-count, .chip-on span { font-size: .78rem; opacity: .8; font-variant-numeric: tabular-nums; }

/* ---- pager -----------------------------------------------------------------
   44x44 minimum like every other control on the site. */
.pager { display: flex; align-items: center; justify-content: center; flex-wrap: wrap; gap: .5rem; margin-top: calc(var(--gap) * 1.5); }
.pager-nums { display: flex; flex-wrap: wrap; gap: .35rem; }
.pager-num, .pager-step {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 44px; min-height: 44px; padding: 0 .8rem;
  border: 1px solid var(--border); border-radius: 999px; color: var(--muted); font-size: .9rem;
}
.pager-num:hover, .pager-step:hover, .pager-num:focus-visible, .pager-step:focus-visible { color: var(--text); border-color: var(--amber); text-decoration: none; }
/* .pager-on is a <span>, not an anchor: the current page is not a target, so it
   gets neither the lift nor this hover. */
.pager-on { background: var(--amber); border-color: var(--amber); color: var(--on-amber); font-weight: 700; }

/* ---- long-form + tables ----------------------------------------------------
   Post bodies are DB-authored, so a bare URL or a long part number can appear
   anywhere. Without this a long unbroken token has no break opportunity and
   scrolls the whole page sideways at 320px. break-word only splits a word that
   cannot fit on its own line, so normal prose is untouched. */
.article-body, .prose, .faq-a { overflow-wrap: break-word; }
.prose { max-width: 68ch; }
.prose p { margin: 0 0 1rem; color: var(--muted); }
.prose p:last-child { margin-bottom: 0; }
/* Let a wide table scroll on its own rather than pushing the page sideways. */
.table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.article-body table { width: 100%; border-collapse: collapse; margin: 1.4rem 0; font-size: .95rem; display: block; overflow-x: auto; }
.article-body :is(th, td) { border: 1px solid var(--border); padding: .6rem .8rem; text-align: left; vertical-align: top; }
.article-body th { background: var(--surface-2); color: var(--text); font-weight: 700; }
.article-body blockquote { margin: 1.4rem 0; padding: .2rem 0 .2rem 1.2rem; border-left: 3px solid var(--amber); color: var(--text); font-style: italic; }
/* Anchor jumps must not land the heading under the sticky header. */
.article-body :is(h2, h3)[id] { scroll-margin-top: 6rem; }
.article-lede { font-size: 1.2rem; line-height: 1.55; color: var(--text); margin: 1rem 0 0; }

/* ---- blog cards with cover art --------------------------------------------
   The image has to run edge to edge inside the rounded corner, so padding moves
   off the card and onto the body. Gated on :has() so a post card that has NOT
   been rebuilt around .post-card-body keeps its own padding rather than
   collapsing to zero; a browser without :has() simply keeps the old layout. */
.post-card:has(.post-card-body) { overflow: hidden; display: flex; flex-direction: column; padding: 0; }
.post-card-body { padding: 1.4rem; display: flex; flex-direction: column; flex: 1; }
.post-card-body > :last-child { margin-top: auto; }
.post-card-media { display: block; aspect-ratio: 16 / 9; background: var(--surface-2); }
.post-card-img { width: 100%; height: 100%; object-fit: cover; display: block; }
.post-card-cat { font-size: .74rem; text-transform: uppercase; letter-spacing: .08em; color: var(--amber); font-weight: 700; margin: 0 0 .35rem; }

/* ---- gallery affordance + lightbox ----------------------------------------
   The whole thumbnail is the target; the badge only advertises that. */
.gallery-link { display: block; position: relative; }
.gallery-link:hover { text-decoration: none; }
.gallery-title { display: block; color: var(--text); font-weight: 700; }
.gallery-caption { display: block; }
.gallery-zoom {
  position: absolute; right: .7rem; bottom: .7rem;
  display: flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; border-radius: 999px; border: 1px solid rgba(255, 255, 255, .3);
  background: rgba(20, 17, 13, .82); color: #f8f5f0; font-size: 1.05rem; line-height: 1;
  opacity: 0; transition: opacity .18s var(--ease);
}
.gallery-item:hover .gallery-zoom, .gallery-link:focus-visible .gallery-zoom { opacity: 1; }
/* Touch has no hover state to reveal it, so show the affordance outright. */
@media (hover: none) { .gallery-zoom { opacity: 1; } }

/* Dark band over the page: re-scope the tokens the way the footer does, so
   text, borders and the :focus-visible ring all stay legible on the scrim. */
.lb-open { overflow: hidden; }
.lightbox {
  position: fixed; inset: 0; z-index: 60; display: flex; align-items: center; justify-content: center;
  padding: clamp(1rem, 4vw, 3rem);
  --text: #f8f5f0; --muted: #c0b6a8; --border: #3d352b; --accent: #f5c563; --surface: #221d16;
  color: var(--muted);
}
.lightbox-scrim { position: absolute; inset: 0; background: rgba(14, 12, 9, .95); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); }
.lightbox-figure { position: relative; margin: 0; display: flex; flex-direction: column; gap: 1rem; width: 100%; max-width: min(1100px, 100%); max-height: 100%; }
/* Fill the stage on the long axis of the viewport, whatever the photo's shape.
   No border or fill: object-fit leaves empty box on odd aspect ratios, and the
   scrim should show through it rather than a visible frame. */
.lightbox-img { height: 62vh; width: auto; max-width: 100%; object-fit: contain; margin: 0 auto; border-radius: var(--radius-lg); }
@media (max-aspect-ratio: 1/1) { .lightbox-img { width: 100%; height: auto; max-height: 62vh; } }
.lightbox-meta { text-align: center; color: var(--muted); }
.lightbox-title { color: var(--text); font-size: clamp(1.1rem, 3vw, 1.5rem); margin: 0 0 .2rem; }
.lightbox-caption { margin: 0; }
.lightbox-caption:empty, .lightbox-title:empty { display: none; }
.lightbox-count { margin: .5rem 0 0; font-size: .78rem; letter-spacing: .12em; text-transform: uppercase; }
.lightbox-btn {
  position: absolute; z-index: 1; display: flex; align-items: center; justify-content: center;
  width: 48px; height: 48px; border: 1px solid var(--border); border-radius: 999px;
  background: var(--surface); color: var(--text); font-size: 1.6rem; line-height: 1;
  /* Colour only, never transform: .lightbox-prev/.lightbox-next centre
     themselves with translateY(-50%) and any transform here would fight it. */
  transition: border-color .2s var(--ease), color .2s var(--ease);
}
.lightbox-btn:hover { border-color: var(--amber); color: var(--amber); text-decoration: none; }
.lightbox-close { top: clamp(.6rem, 2vw, 1.4rem); right: clamp(.6rem, 2vw, 1.4rem); font-size: 1.15rem; }
.lightbox-prev { left: clamp(.4rem, 2vw, 1.4rem); top: 50%; transform: translateY(-50%); }
.lightbox-next { right: clamp(.4rem, 2vw, 1.4rem); top: 50%; transform: translateY(-50%); }

/* Reduced motion: kill every transition AND animation (Ken Burns slow-zoom,
   scroll-cue bob, drawer slide) sitewide; the hero video is hidden separately. */
@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; animation: none !important; scroll-behavior: auto !important; }
}

/* Keyboard parity for this site's card families (the hover list above is
   pointer-only). New components should use `lift` / `lift-flat` instead. */
.card:focus-within, .post-card:focus-within, .gallery-item:focus-within {
  transform: translateY(-5px) scale(1.015);
  box-shadow: 0 18px 38px -14px rgba(0, 0, 0, .65);
  border-color: var(--amber);
}

/* ============================================================
   Consent gate (server/views/consent.js)
   Non-essential tags load ONLY after an affirmative decision, so this
   banner is the first and only thing that decides whether analytics
   ever run. Accept and Reject are deliberately the SAME control: same
   size, same weight, same colours, same one click. Making Accept the
   prominent one is a dark pattern and its own finding.
   The banner ships with the `hidden` attribute and is revealed by the
   runtime only when there is no stored decision, so a returning visitor
   never sees it flash.
   ============================================================ */
.consent[hidden] { display: none; }
.consent {
  position: fixed; inset: auto 0 0 0; z-index: 200;
  background: var(--surface); border-top: 1px solid var(--border);
  box-shadow: 0 -10px 30px -18px rgba(0, 0, 0, .8);
  padding: 1rem 0 max(1rem, env(safe-area-inset-bottom));
}
.consent-inner {
  width: min(var(--wrap, 1120px), 100% - 2.4rem); margin-inline: auto;
  display: flex; flex-wrap: wrap; align-items: center; gap: 1rem 1.6rem;
  justify-content: space-between;
}
.consent-text { margin: 0; flex: 1 1 22rem; color: var(--muted); font-size: .9rem; line-height: 1.55; }
.consent-text strong { color: var(--text); }
.consent-actions { display: flex; flex-wrap: wrap; gap: .7rem; flex: 0 0 auto; }
.consent-btn {
  /* Identical styling for both choices - no visual hierarchy between
     accepting and rejecting. Change one, change the other. */
  font: inherit; font-weight: 600; font-size: .92rem;
  min-height: 44px; padding: .6rem 1.15rem;
  background: var(--surface-2, transparent); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--radius, 12px);
  cursor: pointer; text-align: center; flex: 1 1 auto; min-width: 11rem;
}
.consent-btn:hover { border-color: var(--text); }
.footer-consent {
  font: inherit; font-size: inherit; color: inherit;
  background: none; border: 0; padding: 0; cursor: pointer;
  text-decoration: underline; text-underline-offset: 3px;
  display: inline-flex; align-items: center; min-height: 44px;
}
@media (max-width: 560px) {
  .consent-inner { flex-direction: column; align-items: stretch; gap: .8rem; }
  /* In a column, flex-basis is a HEIGHT: 22rem left a tall empty gap. */
  .consent-text { flex: none; }
  .consent-actions { width: 100%; }
}

.notice-err { border-color: #e0a3a3; background: rgba(220, 38, 38, .1); color: #7f1d1d; }
/* ---- /book appointment picker ----
   Three server-rendered steps: type -> time -> confirm. No client JS, so
   every slot is a link and has to hold up as a tap target. */
.appt-card { display: block; text-decoration: none; color: inherit; }
.appt-card:hover, .appt-card:focus-visible { border-color: var(--accent); }
.appt-card .card-link { display: inline-block; margin-top: .8rem; color: var(--accent); }
.appt-meta { color: var(--accent); font-size: .85rem; font-weight: 600; margin: 0; }
.bk-back { margin: 0 0 1rem; font-size: .9rem; }
.bk-back a { color: var(--muted); text-decoration: none; }
.bk-back a:hover { color: var(--accent); }
.bk-step-title { margin: 0 0 .3rem; }
.bk-when { margin: .2rem 0 .6rem; font-size: 1.05rem; }
.iv-tz { font-size: .85rem; color: var(--muted); margin: .2rem 0 0; }
.bk-days { display: grid; gap: 1.6rem; margin: 1.8rem 0 2.4rem; }
.bk-day-label { font-size: 1.05rem; margin: 0 0 .7rem; }
.bk-slots { display: flex; flex-wrap: wrap; gap: .55rem; }
.bk-slot { min-width: 6.5rem; justify-content: center; }
.iv-done { text-align: center; padding-block: 2rem; }
@media (max-width: 560px) {
  /* Two per row beats a ragged wrap when there are a dozen times in a day,
     and most of this traffic is on a phone. */
  .bk-slots { display: grid; grid-template-columns: 1fr 1fr; }
  .bk-slot { width: 100%; }
}
