/* ==========================================================================
   Shoreline Skylights Worthing — design system
   Implements STYLE-GUIDE.md. Do not introduce colours or faces not defined here.
   ========================================================================== */

:root {
  /* Colour — STYLE-GUIDE §1 */
  --brand-primary: #17323A;
  --brand-deep: #0C1E24;
  --accent: #1E7F8C;
  --accent-deep: #10525C;
  /* ACCENT ON DARK GROUNDS.
     The lane sets the accent as text on its carbon bands, which is right for
     ITS accent: a bright warm orange clears AA on near-black comfortably. This
     build's verdigris is much darker and does not — 3.64:1 on brand-deep and
     2.88:1 on brand-primary, against a 4.5 requirement, which produced sixteen
     contrast failures on the front page alone. A recolour is not a token swap:
     which foreground passes depends on the accent's luminance, so a dark-ground
     variant has to be derived rather than inherited.
     #3FA3B0 measures 5.77:1 on brand-deep and 4.55:1 on brand-primary. */
  --accent-light: #3FA3B0;

  /* ON-DARK TEXT RAMP.
     The lane used five undeclared greys for text on its carbon bands, spread
     across two stylesheets. They survived a full brand rename because a hex
     carries no brand name, and two of them do not clear AA on this palette's
     lighter dark ground: #7C8891 at 3.72:1 and #6D7A82 at 3.06:1 on
     brand-primary. Declared here, measured, and used by name.
       on-dark        10.94:1 on brand-deep,  8.64:1 on brand-primary
       on-dark-body    8.56:1               , 6.76:1
       on-dark-muted   6.28:1               , 4.96:1  */
  --on-dark:       #C7D0D6;
  --on-dark-body:  #AEB9C0;
  --on-dark-muted: #949EA6;
  --brand-support: #8FB3B8;
  --white: #FFFFFF;
  --surface: #F7F9F8;
  --surface-alt: #EDF1EE;
  --ink: #202A2D;
  --ink-muted: #556065;
  --hairline: #D8DFDC;
  --hairline-strong: #BCC7C3;

  --success: #2F7D53;
  --warning: #A9761A;
  --danger: #B3392C;

  /* Type — STYLE-GUIDE §2 */
  --font-display: 'Archivo', 'Helvetica Neue', Helvetica, Arial, sans-serif;
  --font-body: 'IBM Plex Sans', ui-sans-serif, system-ui, 'Segoe UI', Roboto, sans-serif;
  --font-mono: 'IBM Plex Mono', ui-monospace, Consolas, monospace;

  /* Layout — STYLE-GUIDE §6 */
  --content: 1200px;
  --wide: 1440px;
  --measure: 68ch;
  --radius: 4px;
  --gutter: 24px;
  --section-y: clamp(64px, 8vw, 112px);
}

@media (min-width: 768px) { :root { --gutter: 40px; } }

/* --------------------------------------------------------------- reset -- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  background: var(--surface);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 1.0625rem;
  line-height: 1.7;
  -webkit-font-smoothing: antialiased;
}

img, svg, video { max-width: 100%; height: auto; display: block; }
h1, h2, h3, h4 { margin: 0 0 0.5em; color: var(--brand-primary); font-family: var(--font-display); text-wrap: balance; }
p { margin: 0 0 1.15em; }
p:last-child { margin-bottom: 0; }

h1 { font-size: clamp(2.25rem, 5vw, 3.75rem); font-weight: 700; letter-spacing: -0.028em; line-height: 1.06; }
h2 { font-size: clamp(1.75rem, 3.5vw, 2.5rem); font-weight: 700; letter-spacing: -0.022em; line-height: 1.16; }
h3 { font-size: clamp(1.25rem, 2vw, 1.5rem); font-weight: 600; letter-spacing: -0.012em; line-height: 1.3; }
h4 { font-size: 1.0625rem; font-weight: 700; letter-spacing: -0.01em; }

a { color: var(--brand-primary); text-decoration-color: var(--accent); text-underline-offset: 3px; }
a:hover { color: var(--accent-deep); }
:focus-visible { outline: 3px solid var(--brand-support); outline-offset: 2px; }

/* ---------------------------------------------------------- utilities -- */
.wrap { max-width: var(--content); margin-inline: auto; padding-inline: var(--gutter); }
.wrap--wide { max-width: var(--wide); }
.measure { max-width: var(--measure); }
.section { padding-block: var(--section-y); }
.section--surface-alt { background: var(--surface-alt); }
.section--white { background: var(--white); }
.section--brand-deep { background: var(--brand-deep); color: var(--on-dark-body); }
.section--brand-deep h2, .section--brand-deep h3 { color: var(--white); }

.eyebrow {
  font-size: 0.75rem; font-weight: 600; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--brand-support);
  margin: 0 0 0.75em; display: block;
}
.section--brand-deep .eyebrow { color: var(--accent-light); }

/* USED BY front-page.php AND DEFINED NOWHERE.
 *
 * .section--carbon and .section--band are the lane's own section modifiers and
 * neither had a rule in either stylesheet. A referenced-but-undefined modifier
 * throws no error and leaves no trace in source review: the section inherits
 * the page ground, and every child styled for the intended ground inverts. Here
 * the quote section rendered a white h2 on the light surface at 1.06:1, which
 * is invisible.
 *
 * The reference build shipped exactly this defect under a different name
 * (.section--slate, white on cream at 1.04:1). Two builds, two lanes, one
 * defect — so build/qa-modifiers.mjs now asserts it rather than trusting it. */
.section--carbon { background: var(--brand-deep); color: var(--on-dark-body); }
/* Scoped to the section's OWN headings. Unscoped, this also whitened the h3 of
   every white card sitting inside the carbon band, giving white on white at
   1:1 — the rule intended to fix a dark ground breaking a light one nested in
   it. The lane's .section--brand-deep carries the same unscoped pattern and is
   only safe because nothing light is nested in it today. */
.section--carbon > .wrap > h2,
.section--carbon > .wrap > h3,
.section--carbon > .wrap > h4,
.section--carbon > .wrap > * > h2 { color: var(--white); }
.section--carbon .eyebrow { color: var(--accent-light); }
.section--carbon a:not(.btn) { color: var(--accent-light); }
.section--carbon strong { color: var(--white); }

.section--band { background: var(--surface-alt); }
.lead { font-size: 1.25rem; line-height: 1.6; max-width: var(--measure); }
.caption { font-size: 0.875rem; font-weight: 500; color: var(--ink-muted); line-height: 1.5; }

.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 999;
  background: var(--brand-deep); color: var(--white);
  padding: 12px 20px; font-weight: 600;
}
.skip-link:focus { left: 0; }

.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;
}

/* ----------------------------------------------------------- buttons -- */
.btn {
  display: inline-block;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1rem;
  line-height: 1.2;
  padding: 15px 28px;
  border-radius: var(--radius);
  border: 2px solid transparent;
  text-decoration: none;
  cursor: pointer;
  transition: background-color .15s ease, border-color .15s ease, color .15s ease;
}
/* THE LABEL COLOUR INVERTS WITH THE ACCENT.
   The lane sets a dark label on its accent, which is right for ITS accent: a
   bright warm orange, where carbon reads 7.9:1 and white would fail at 3.5:1.
   This build's accent is a cool verdigris and is much darker, so the pairing
   flips. Measured on this palette:
     brand-deep on accent       3.64:1   FAILS
     white      on accent       4.70:1   passes
     white      on accent-deep  8.81:1   passes comfortably
   A recolour is not only a token swap. Which foreground passes depends on the
   accent's luminance, so the rule has to be re-derived, not inherited. */
.btn--primary { background: var(--accent); color: var(--white); }
.btn--primary:hover { background: var(--accent-deep); color: var(--white); }
.btn--secondary { background: transparent; color: var(--brand-primary); border-color: var(--hairline-strong); }
.btn--secondary:hover { border-color: var(--brand-primary); color: var(--brand-primary); }
.btn--ghost-light { background: transparent; color: var(--white); border-color: rgba(255,255,255,.35); }
.btn--ghost-light:hover { border-color: var(--white); color: var(--white); }
.btn-row { display: flex; flex-wrap: wrap; gap: 14px; align-items: center; }

/* -------------------------------------------------------------- header -- */
.site-header {
  background: var(--white);
  border-bottom: 1px solid var(--hairline);
  position: sticky; top: 0; z-index: 100;
}
.header-bar {
  display: flex; align-items: center; justify-content: space-between;
  gap: 28px; min-height: 62px; padding-block: 8px;
}
.brand { display: flex; align-items: center; gap: 10px; text-decoration: none; flex: none; }
.brand svg { width: 34px; height: 30px; flex: none; }
.brand-name { line-height: 1; }
.brand-name b {
  display: block; font-family: var(--font-display); font-weight: 800;
  font-size: 1.05rem; letter-spacing: -0.02em; color: var(--brand-deep);
  text-transform: uppercase; white-space: nowrap;
}
.brand-name span {
  display: block; font-size: 0.5625rem; font-weight: 600;
  letter-spacing: 0.26em; text-transform: uppercase; color: var(--ink-muted);
  margin-top: 3px;
}

.btn--sm { padding: 10px 18px; font-size: 0.9rem; }

.header-actions { display: flex; align-items: center; gap: 14px; flex: none; }
.header-phone {
  font-family: var(--font-mono); font-size: 0.9375rem; font-weight: 600;
  color: var(--brand-primary); text-decoration: none; white-space: nowrap;
}
.header-phone:hover { color: var(--accent-deep); }

.nav-toggle {
  display: none; align-items: center; justify-content: center;
  width: 42px; height: 40px; background: none;
  border: 1px solid var(--hairline-strong); border-radius: var(--radius);
  cursor: pointer; padding: 0;
}
.nav-toggle-bars, .nav-toggle-bars::before, .nav-toggle-bars::after {
  display: block; width: 18px; height: 2px; background: var(--brand-primary);
  position: relative; transition: transform .2s ease, opacity .2s ease;
}
.nav-toggle-bars::before { content: ""; position: absolute; top: -6px; left: 0; }
.nav-toggle-bars::after  { content: ""; position: absolute; top: 6px; left: 0; }
.nav-toggle[aria-expanded="true"] .nav-toggle-bars { background: transparent; }
.nav-toggle[aria-expanded="true"] .nav-toggle-bars::before { transform: translateY(6px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] .nav-toggle-bars::after  { transform: translateY(-6px) rotate(-45deg); }

.primary-nav { margin-left: auto; }
.primary-nav ul {
  list-style: none; margin: 0; padding: 0;
  display: flex; gap: clamp(14px, 1.6vw, 26px); align-items: center;
}
.primary-nav a {
  font-family: var(--font-display); font-size: 0.9375rem; font-weight: 600;
  color: var(--brand-primary); text-decoration: none; white-space: nowrap;
  padding: 4px 0; border-bottom: 2px solid transparent; letter-spacing: -0.01em;
}
.primary-nav a:hover, .primary-nav .current-menu-item > a {
  border-bottom-color: var(--accent); color: var(--brand-primary);
}
/* Page behind the open mobile menu does not scroll. Paired with the class the
   nav script sets on <html>; overflow on the element rather than a style
   attribute so it can be overridden in one place. */
html.nav-locked, html.nav-locked body { overflow: hidden; }

/* Below 1080px the six-item nav gets tight, so collapse to the toggle. */
@media (max-width: 1080px) {
  .nav-toggle { display: inline-flex; }
  .primary-nav {
    display: none; position: absolute; left: 0; right: 0; top: 100%;
    background: var(--white); border-bottom: 1px solid var(--hairline);
    box-shadow: 0 8px 20px rgba(15,21,25,.08); margin: 0;
  }
  .site-header { position: sticky; }
  .header-bar { position: relative; }
  /* Open menu is capped to the screen and scrolls, so every item is reachable
     even with the Services list expanded (was: overflowed off-screen, no scroll). */
  .primary-nav.is-open {
    display: block;
    max-height: calc(100vh - 112px);
    max-height: calc(100dvh - 112px);
    overflow-y: auto;
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
  }
  .primary-nav ul { flex-direction: column; align-items: stretch; gap: 0; padding: 6px var(--gutter) 14px; }
  .primary-nav li { border-top: 1px solid var(--hairline); }
  .primary-nav li:first-child { border-top: 0; }
  .primary-nav a { display: block; padding: 14px 0; font-size: 1rem; }
}
@media (max-width: 560px) {
  .brand-name span { display: none; }
  .header-phone { display: none; }
  /* On phones the brand + this CTA + hamburger overflow the row. The fixed
     bottom bar (.sticky-cta) already carries "Get a fixed quote" here, so drop
     the redundant header button and keep the row within the viewport. */
  .header-actions > a.btn { display: none; }
}

/* --------------------------------------------------------------- hero -- */
.hero { background: var(--white); border-bottom: 1px solid var(--hairline); }
.hero-grid { display: grid; gap: 40px; padding-block: clamp(40px, 6vw, 76px); }
@media (min-width: 900px) { .hero-grid { grid-template-columns: 1.05fr 1fr; gap: 56px; align-items: center; } }
.hero h1 { margin-bottom: 0.35em; }
.hero .lead { margin-bottom: 28px; }
.hero-points { list-style: none; margin: 0 0 30px; padding: 0; display: grid; gap: 10px; }
.hero-points li { display: flex; gap: 11px; align-items: baseline; font-size: 1rem; }
.hero-points li::before {
  content: ""; flex: none; width: 9px; height: 9px;
  background: var(--accent); transform: translateY(1px);
}

/* --------------------------------------------------------------- media -- */
.media { position: relative; background: var(--surface-alt); border: 1px solid var(--hairline); border-radius: var(--radius); overflow: hidden; }
.media img { width: 100%; }
.media figcaption { padding: 10px 14px; font-size: 0.8125rem; color: var(--ink-muted); border-top: 1px solid var(--hairline); background: var(--white); }

/* Placeholder shown until real imagery is generated. Preserves aspect + CLS. */
.img-placeholder {
  position: relative; width: 100%; background: var(--surface-alt);
  border: 1px dashed var(--hairline-strong); border-radius: var(--radius);
  display: flex; align-items: center; justify-content: center;
  color: var(--ink-muted); font-family: var(--font-mono); font-size: 0.75rem;
  text-align: center; padding: 16px; line-height: 1.5;
}
.img-placeholder[data-ratio="16:9"] { aspect-ratio: 16 / 9; }
.img-placeholder[data-ratio="4:3"]  { aspect-ratio: 4 / 3; }
.img-placeholder[data-ratio="1:1"]  { aspect-ratio: 1 / 1; }

/* --------------------------------------------------------------- cards -- */
.grid { display: grid; gap: 20px; }
.grid--2 { grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
.grid--3 { grid-template-columns: repeat(auto-fit, minmax(268px, 1fr)); }
.grid--4 { grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }

.card {
  background: var(--white); border: 1px solid var(--hairline);
  border-radius: var(--radius); padding: 26px;
  transition: border-color .15s ease;
}
.card:hover { border-color: var(--hairline-strong); }
.card h3 { margin-bottom: 0.4em; }
.card p { font-size: 0.9375rem; color: var(--ink-muted); margin-bottom: 0; }
.card .icon { margin-bottom: 16px; }
a.card { text-decoration: none; display: block; }
a.card:hover { border-color: var(--brand-primary); }
.card-link {
  display: inline-block; margin-top: 14px; font-weight: 600;
  font-size: 0.9375rem; color: var(--brand-primary);
  border-bottom: 2px solid var(--accent); text-decoration: none;
}

/* --------------------------------------------------------------- steps -- */
.steps { list-style: none; margin: 0; padding: 0; display: grid; gap: 1px; background: var(--hairline); border: 1px solid var(--hairline); }
@media (min-width: 760px) { .steps { grid-template-columns: repeat(4, 1fr); } }
.steps li { background: var(--white); padding: 24px; }
.step-n {
  font-family: var(--font-mono); font-size: 0.75rem; font-weight: 600;
  color: var(--accent); display: block; margin-bottom: 10px;
  font-variant-numeric: tabular-nums;
}
.steps h3 { font-size: 1.0625rem; margin-bottom: 0.4em; }
.steps p { font-size: 0.9rem; color: var(--ink-muted); margin: 0; }

/* -------------------------------------------------------------- tables -- */
.tablewrap { overflow-x: auto; border: 1px solid var(--hairline); background: var(--white); border-radius: var(--radius); }
table { border-collapse: collapse; width: 100%; min-width: 520px; }
th, td { text-align: left; padding: 12px 16px; border-bottom: 1px solid var(--hairline); font-size: 0.9375rem; }
th {
  font-size: 0.6875rem; font-weight: 600; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--ink-muted);
  background: var(--surface); white-space: nowrap;
}
td.num, .num { font-family: var(--font-mono); font-variant-numeric: tabular-nums; color: var(--brand-primary); }

/* Table titles. The lane styles .tablewrap and th but never had a caption rule,
   because its source build shipped no specification matrices. Set in the
   section-head idiom rather than inventing a second one. */
.tablewrap caption {
  caption-side: top; text-align: left;
  font-family: var(--font-display); font-weight: 700;
  font-size: 1.0625rem; letter-spacing: -0.012em; line-height: 1.3;
  color: var(--brand-primary);
  padding: 16px 16px 13px;
  border-bottom: 1px solid var(--hairline);
}

/* `table { min-width: 520px }` above is right for a five-column comparison and
   wrong for a two-column town matrix, which fits a 390px phone and should not
   scroll sideways to read it. Width follows column count. */
.tablewrap[data-cols="2"] table { min-width: 0; }
.tablewrap[data-cols="3"] table { min-width: 460px; }
.tablewrap[data-cols="2"] th[scope="row"] { width: 38%; }
tbody tr:last-child td { border-bottom: 0; }

/* ----------------------------------------------------------------- faq -- */
.faq { border: 1px solid var(--hairline); border-radius: var(--radius); background: var(--white); }
.faq details { border-bottom: 1px solid var(--hairline); }
.faq details:last-child { border-bottom: 0; }
.faq summary {
  cursor: pointer; padding: 18px 22px; font-family: var(--font-display);
  font-weight: 600; font-size: 1.0625rem; color: var(--brand-primary);
  list-style: none; display: flex; justify-content: space-between; gap: 16px; align-items: center;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after { content: "+"; font-family: var(--font-mono); color: var(--accent); font-size: 1.25rem; flex: none; }
.faq details[open] summary::after { content: "\2212"; }
.faq .faq-body { padding: 0 22px 20px; max-width: var(--measure); }

/* ---------------------------------------------------------- trust bar -- */
.trustbar { background: var(--white); border-block: 1px solid var(--hairline); }
.trustbar ul {
  list-style: none; margin: 0; padding: 18px 0; display: flex;
  flex-wrap: wrap; gap: 14px 34px; justify-content: center;
}
.trustbar li {
  display: flex; align-items: center; gap: 9px;
  font-size: 0.875rem; font-weight: 500; color: var(--brand-primary);
}
.trustbar svg { width: 19px; height: 19px; flex: none; }

/* Empty-state trust slots — styled, positioned, awaiting real credentials. */
.trust-slot {
  border: 1px dashed var(--hairline-strong); border-radius: var(--radius);
  padding: 22px; text-align: center; color: var(--ink-muted);
  font-family: var(--font-mono); font-size: 0.75rem; line-height: 1.6;
}

/* ---------------------------------------------------------- quote form -- */
.quote-panel {
  background: var(--white); border: 1px solid var(--hairline);
  border-radius: var(--radius); padding: clamp(22px, 3vw, 32px);
}
.section--brand-deep .quote-panel { border-color: var(--brand-primary); background: var(--brand-deep); }
.crm-embed {
  min-height: 320px; border: 1px dashed var(--hairline-strong);
  border-radius: var(--radius); background: var(--surface);
  display: flex; align-items: center; justify-content: center;
  text-align: center; padding: 26px; color: var(--ink-muted);
  font-family: var(--font-mono); font-size: 0.8125rem; line-height: 1.7;
}

/* --------------------------------------------------------- sticky CTA -- */
.sticky-cta {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 90;
  background: var(--brand-deep); border-top: 2px solid var(--accent);
  padding: 10px 14px; display: flex; gap: 10px;
  box-shadow: 0 -2px 12px rgba(15,21,25,.16);
}
.sticky-cta .btn { flex: 1; text-align: center; padding: 13px 14px; font-size: 0.9375rem; }
@media (min-width: 1000px) { .sticky-cta { display: none; } }
@media (max-width: 999px) { body { padding-bottom: 68px; } }

/* -------------------------------------------------------------- prose -- */
.prose { max-width: var(--measure); }
.prose h2 { margin-top: 1.9em; }
.prose h3 { margin-top: 1.6em; }
.prose ul, .prose ol { padding-left: 1.3em; margin: 0 0 1.15em; }
.prose li { margin-bottom: 0.5em; }
.prose > .tablewrap, .prose > .callout, .prose > .media { margin-block: 1.8em; max-width: none; }

/* ---------------------------------------------- content image layouts -- */
/* Turns a wall of text into a designed page. Markers in post_content are
   rendered by shoreline_render_content_images() and tagged wide / left / right. */
.content-media { margin-block: 2em; border: 1px solid var(--hairline); border-radius: var(--radius); overflow: hidden; background: var(--surface-alt); }
.content-media img { width: 100%; display: block; }
.content-media figcaption {
  padding: 11px 16px; font-size: 0.8125rem; color: var(--ink-muted);
  border-top: 1px solid var(--hairline); background: var(--white);
}
.content-media.media--wide { max-width: none; }

/* The banded variant. Same block width, different ground and a caption set
   beside the image at desktop, so two figures on one page do not read as two
   copies of the same box. */
.content-media.media--band { max-width: none; background: var(--brand-primary); border-color: var(--brand-primary); }
.content-media.media--band figcaption {
  background: var(--brand-primary); color: var(--on-dark-body); border-top-color: rgba(255,255,255,0.16);
  font-family: var(--font-mono); font-size: 0.75rem; letter-spacing: 0.06em; text-transform: uppercase;
}
@media (min-width: 900px) {
  .content-media.media--right { float: right; width: 46%; margin: 0.4em 0 1.4em 2em; }
  .content-media.media--left  { float: left;  width: 46%; margin: 0.4em 2em 1.4em 0; }
}
/* Clear floats at each H2 so sections never collide with a floated image. */
.prose h2 { clear: both; }

/* Hero image band on authority/area pages */
.page-hero {
  display: grid; gap: 0; align-items: stretch;
  border-bottom: 1px solid var(--hairline); background: var(--white);
}
@media (min-width: 900px) { .page-hero { grid-template-columns: 1.05fr 0.95fr; } }
.page-hero-copy {
  padding: clamp(30px, 4vw, 56px) clamp(20px, 4vw, 56px);
  display: flex; flex-direction: column; justify-content: center;
}
.page-hero-copy .wrap-narrow { max-width: 36rem; width: 100%; }
@media (min-width: 1440px) { .page-hero-copy { padding-left: max(56px, calc((100vw - var(--wide)) / 2 + 40px)); } }
.page-hero-copy .eyebrow { margin-bottom: 0.6em; }
.page-hero-copy h1 { margin-bottom: 0.4em; }
.page-hero-copy .lead { margin-bottom: 20px; font-size: 1.15rem; }
.page-hero-copy .hero-points { margin-bottom: 26px; }
.page-hero-media { position: relative; min-height: 340px; background: var(--surface-alt); }
.page-hero-media img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
@media (max-width: 899px) { .page-hero-media { min-height: 240px; order: -1; } }

/* Key-fact strip under the hero */
.factstrip { background: var(--brand-deep); }
.factstrip ul { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); }
.factstrip li { padding: 18px 24px; border-right: 1px solid var(--brand-primary); color: var(--on-dark-body); font-size: 0.9rem; line-height: 1.4; }
.factstrip li:last-child { border-right: 0; }
.factstrip strong { display: block; color: var(--white); font-family: var(--font-display); font-size: 1.35rem; font-weight: 700; letter-spacing: -0.02em; margin-bottom: 2px; }
.factstrip .accent { color: var(--accent); }
@media (max-width: 620px) { .factstrip li { border-right: 0; border-bottom: 1px solid var(--brand-primary); } }

/* Standard hero tightened */
.hero--standard .wrap { padding-block: clamp(30px, 4vw, 52px); }

/* Pull-stat inside prose */
.prose .pullstat {
  border-left: 4px solid var(--accent); padding: 4px 0 4px 22px; margin: 1.6em 0;
  font-family: var(--font-display); font-weight: 700; font-size: clamp(1.4rem, 3vw, 2rem);
  letter-spacing: -0.02em; color: var(--brand-primary); line-height: 1.15;
}
.prose .pullstat span { display: block; font-family: var(--font-body); font-weight: 500; font-size: 0.9375rem; color: var(--ink-muted); margin-top: 6px; letter-spacing: 0; }

.callout {
  border-left: 4px solid var(--accent);
  background: var(--white); border: 1px solid var(--hairline);
  border-left: 4px solid var(--accent);
  padding: 20px 24px; border-radius: var(--radius);
}
.callout p:last-child { margin-bottom: 0; }
.callout strong { color: var(--brand-primary); }

/* ------------------------------------------------------------ sidebar -- */
/* Mobile-first single fluid column. minmax(0,1fr) (not the implicit auto track)
   lets the column shrink below wide children — e.g. min-width tables, which then
   scroll inside their .tablewrap instead of blowing out the page width. */
.page-layout { display: grid; grid-template-columns: minmax(0, 1fr); gap: 44px; }
.page-layout > * { min-width: 0; }
@media (min-width: 1000px) { .page-layout { grid-template-columns: minmax(0, 1fr) 320px; gap: 56px; align-items: start; } }
.sidebar { display: grid; gap: 20px; }
@media (min-width: 1000px) { .sidebar { position: sticky; top: 96px; } }
.sidebar-box { background: var(--white); border: 1px solid var(--hairline); border-radius: var(--radius); padding: 22px; }
.sidebar-box h3 { font-size: 1.0625rem; margin-bottom: 0.5em; }
.sidebar-box > p { font-size: 0.9375rem; color: var(--ink-muted); margin-bottom: 16px; }
.sidebar-box .btn { width: 100%; text-align: center; }
.sidebar-box--cta { background: var(--brand-deep); border-color: var(--brand-deep); }
.sidebar-box--cta h3 { color: var(--white); }
.sidebar-box--cta > p { color: var(--on-dark-body); }
.sidebar-links, .sidebar-facts { list-style: none; margin: 0; padding: 0; }
.sidebar-links li { border-top: 1px solid var(--hairline); }
.sidebar-links li:first-child { border-top: 0; }
.sidebar-links a { display: block; padding: 10px 0; font-size: 0.9375rem; text-decoration: none; color: var(--brand-primary); }
.sidebar-links a:hover { color: var(--accent-deep); }
.sidebar-facts li { padding: 9px 0 9px 22px; font-size: 0.9375rem; color: var(--ink); border-top: 1px solid var(--hairline); position: relative; }
.sidebar-facts li:first-child { border-top: 0; }
.sidebar-facts li::before { content: ""; position: absolute; left: 0; top: 15px; width: 9px; height: 9px; background: var(--accent); }

/* ----------------------------------------------------------- breadcrumb -- */
.breadcrumb { padding-block: 14px; border-bottom: 1px solid var(--hairline); background: var(--white); }
.breadcrumb ol { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 8px; font-size: 0.8125rem; color: var(--ink-muted); }
.breadcrumb li::after { content: "/"; margin-left: 8px; color: var(--hairline-strong); }
.breadcrumb li:last-child::after { content: ""; }
.breadcrumb a { color: var(--ink-muted); text-decoration: none; }
.breadcrumb a:hover { color: var(--accent-deep); }

/* -------------------------------------------------------------- footer -- */
.site-footer { background: var(--brand-deep); color: var(--on-dark-muted); padding-block: clamp(44px, 6vw, 68px) 0; }
.footer-grid { display: grid; gap: 34px; }
@media (min-width: 780px) { .footer-grid { grid-template-columns: 1.4fr 1fr 1fr 1fr; } }
/* Footer column headings are H2, not H4. They sit after the page body, whose
   deepest heading is H2, and an H4 there is a two-level jump that every page
   on the site inherited. The visual weight is set here rather than by picking
   a smaller tag, which is what the levels are for. */
.site-footer h4,
.site-footer .foot-h {
  color: var(--white); font-size: 0.75rem; font-weight: 600;
  letter-spacing: 0.12em; text-transform: uppercase; margin-bottom: 14px;
}
.site-footer ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
.site-footer a { color: var(--on-dark-muted); text-decoration: none; font-size: 0.9375rem; }
.site-footer a:hover { color: var(--white); }
.footer-brand svg { width: 44px; height: 39px; margin-bottom: 14px; }
.footer-brand p { font-size: 0.9375rem; max-width: 34ch; }
/* Legal links: their own row rather than buried in a column, because these are
   the pages a reader looks for deliberately and the ones that were orphaned. */
.footer-legal {
  margin-top: 34px; display: flex; flex-wrap: wrap; gap: 8px 22px;
  font-size: 0.8125rem;
}

.footer-bottom {
  margin-top: 40px; border-top: 1px solid var(--brand-primary);
  padding-block: 20px 28px; display: flex; flex-wrap: wrap;
  gap: 12px 26px; justify-content: space-between;
  font-size: 0.8125rem;
}
/* --ink-muted is a LIGHT-ground token and the footer is brand-deep: this read
   2.65:1. The build credit is the one link on the site that must stay legible
   regardless of brand palette, since it is not the client's to maintain. */
.footer-credit a { color: var(--on-dark-body); border-bottom: 1px solid var(--brand-primary); }
.footer-credit a:hover { color: var(--white); border-bottom-color: var(--accent); }

/* -------------------------------------------------------------- motion -- */
/* CSS default IS the final state, so a JS failure degrades to a visible page. */
[data-reveal] { opacity: 1; transform: none; }
.js-motion [data-reveal]:not(.is-revealed) { opacity: 0; transform: translateY(12px); }
.js-motion [data-reveal] { transition: opacity .28s cubic-bezier(.22,.61,.36,1), transform .28s cubic-bezier(.22,.61,.36,1); }

@media (prefers-reduced-motion: reduce) {
  .js-motion [data-reveal]:not(.is-revealed) { opacity: 1; transform: none; }
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

/* --------------------------------------------------------------- print -- */
@media print {
  .site-header, .site-footer, .sticky-cta, .sidebar { display: none; }
  body { background: #fff; }
}

/* ==========================================================================
   EDITORIAL SYSTEM — the reading treatment
   --------------------------------------------------------------------------
   Written because the authored body arrived as headings and paragraphs and
   nothing else. A stylesheet cannot address "this h2 and the prose belonging
   to it": sibling combinators reach the next element, never the group. So
   build/enrich-content.mjs wraps every h2 section in .ed-sec and groups
   parallel h3 runs into .ed-panels, and everything below is only reachable
   because those wrappers exist.

   The devices are capped in the enricher, not here: one lede per page, four
   statements, two notes. A device applied to every section is a second body
   style, not a device.

   Shoreline's register is the engineered one — flint and chalk, mono labels,
   a spec-sheet index down the gutter — deliberately not the sister builds.
   ========================================================================== */

/* The measure moves onto the text blocks so that panels, tables and figures
   can break out of it. A single constrained column is the wall of text. */
.prose { max-width: 80ch; }
.ed-sec > p, .ed-sec > ul, .ed-sec > ol,
.ed-sec > h2, .ed-sec > h3, .ed-sec > h4 { max-width: var(--measure); }
.ed-panels, .ed-sec > .tablewrap, .ed-figure, .ed-sec > .content-media { max-width: none; }

.ed-sec { position: relative; }
.ed-sec + .ed-sec { margin-top: 3.4rem; }
.ed-sec > h2:first-child { margin-top: 0; }
.ed-sec > *:last-child { margin-bottom: 0; }

/* The lede. One per page, on the opening paragraph. */
.ed-lede { font-size: 1.1875rem; line-height: 1.6; color: var(--brand-primary); }

/* The statement. A short declarative that already closed its section, set
   apart in place — never copied out of the flow and repeated. */
.ed-sec > p.ed-statement, .ed-statement {
  font-family: var(--font-display);
  font-size: clamp(1.2rem, 2.3vw, 1.5rem);
  line-height: 1.3;
  color: var(--brand-primary);
  max-width: 46ch;
  margin-block: 2.3rem;
  padding-top: 1rem;
  border-top: 2px solid var(--accent);
}

/* The numbers paragraph, lifted out of the argument around it. */
.ed-note {
  background: var(--surface-alt);
  border: 1px solid var(--hairline);
  border-left: 3px solid var(--brand-support);
  border-radius: 0 var(--radius) var(--radius) 0;
  padding: 1.15rem 1.35rem;
  margin-block: 2.1rem;
  font-size: 0.98rem;
  max-width: none;
}
.ed-note::before {
  content: "Specification";
  display: block;
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent-deep);
  margin-bottom: 0.5rem;
}
.ed-note p:last-child { margin-bottom: 0; }

/* Parallel h3 subsections as panels rather than more prose. */
.ed-panels { display: grid; gap: 16px; margin-block: 2.3rem; }
@media (min-width: 720px) {
  .ed-panels { grid-template-columns: repeat(auto-fit, minmax(min(270px, 100%), 1fr)); }
}
.ed-panel {
  background: var(--white);
  border: 1px solid var(--hairline);
  border-radius: var(--radius);
  padding: 20px 22px 16px;
}
.ed-panel > h3:first-child { margin-top: 0; margin-bottom: 0.5em; font-size: 1.0625rem; max-width: none; }
.ed-panel > *:last-child { margin-bottom: 0; }
.ed-panel p, .ed-panel ul, .ed-panel ol { max-width: none; }
.ed-panel .ed-statement { margin-block: 1.4rem; font-size: 1.05rem; max-width: none; }

/* Body figures placed at section boundaries. Block level and centred, never
   floated at column width. */
.ed-figure { margin-block: 2.6rem; }
.ed-figure img { display: block; width: 100%; height: auto; border-radius: var(--radius); }
.ed-figure figcaption {
  font-size: 0.875rem; color: var(--ink-muted); padding-top: 10px;
  border-top: 1px solid var(--hairline); margin-top: 12px;
}

/* ---------------------------------------------------------------------------
   PER-TEMPLATE TREATMENTS
   Every template type gets its own reading treatment. They share the devices
   above and differ in how the section itself is set, so a service hub does not
   read like a town page and neither reads like an answer.
   ------------------------------------------------------------------------- */

/* SERVICE HUBS — the spec sheet. A mono section index down the gutter, which
   is the engineered register and reads as documentation rather than a blog. */
.prose--spec { counter-reset: edsec; }
.prose--spec .ed-sec { counter-increment: edsec; padding-top: 0.4rem; }
.prose--spec .ed-sec + .ed-sec { border-top: 1px solid var(--hairline); padding-top: 2.6rem; }
.prose--spec .ed-sec > h2::before {
  content: counter(edsec, decimal-leading-zero);
  display: block;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: 0.16em;
  color: var(--accent-deep);
  margin-bottom: 0.55rem;
}
@media (min-width: 1180px) {
  .prose--spec .ed-sec { display: grid; grid-template-columns: 4.5rem minmax(0, 1fr); column-gap: 1.6rem; }
  .prose--spec .ed-sec > * { grid-column: 2; }
  .prose--spec .ed-sec > h2 { grid-column: 2; }
  .prose--spec .ed-sec > h2::before { position: absolute; left: 0; margin-top: 0.55rem; }
}

/* TOWN PAGES — the field notebook. Alternate sections sit on chalk, which
   gives the page a horizon line every other section instead of one column. */
.prose--field .ed-sec { padding: 0.2rem 0; }
.prose--field .ed-sec:nth-of-type(even) {
  background: var(--surface-alt);
  padding: 2.2rem clamp(18px, 3vw, 34px);
  border-radius: var(--radius);
  margin-inline: clamp(-34px, -3vw, -18px);
}
@media (max-width: 719px) {
  .prose--field .ed-sec:nth-of-type(even) { margin-inline: -18px; padding-inline: 18px; }
}
.prose--field .ed-sec > h2 {
  padding-left: 0.9rem;
  border-left: 3px solid var(--accent);
}

/* GUIDES — the long read. A hairline above each section and a wider two-up
   panel grid; no numbering, so it does not read as a hub. */
.prose--guide .ed-sec + .ed-sec {
  border-top: 1px solid var(--hairline);
  padding-top: 2.8rem;
}
.prose--guide .ed-sec > h2 { font-size: clamp(1.35rem, 2.6vw, 1.75rem); }
@media (min-width: 720px) {
  .prose--guide .ed-panels { grid-template-columns: 1fr 1fr; }
}

/* ANSWERS — the answer first. The opening section is the answer, set on chalk
   with an accent rule, and the rest of the page is the working. */
.prose--answer > .ed-sec:first-child {
  background: var(--surface-alt);
  border-left: 3px solid var(--accent);
  border-radius: 0 var(--radius) var(--radius) 0;
  padding: 1.6rem clamp(18px, 2.4vw, 26px);
}
.prose--answer > .ed-sec:first-child > h2 { font-size: 1.25rem; }
.prose--answer .ed-sec + .ed-sec { margin-top: 2.6rem; }

/* CORE PAGES — the reference shelf. Services, areas, costs, the guarantee and
   the legal pages. These are read to find one fact rather than end to end, so
   the section heads are the navigation: heavier than a guide's, spaced wider,
   and with a rule that makes a page of twenty of them scannable at a glance.
   The class was being emitted by page.php and page-contact.php and defined
   nowhere, so fourteen pages silently fell back to the bare .prose ground. */
.prose--core .ed-sec + .ed-sec {
  border-top: 1px solid var(--hairline);
  padding-top: 2.4rem;
}
.prose--core h2 {
  font-size: clamp(1.15rem, 2vw, 1.4rem);
  letter-spacing: -0.01em;
}
.prose--core h2 + p {
  color: var(--ink-muted);
  font-weight: 500;
}
.prose--core > p:first-child {
  font-size: 1.0625rem;
  line-height: 1.65;
}

/* ------------------------------------------------- contact page body -- */
/* The authored body sits between the coverage strip and the map. It needs
   breathing room above and below because it is bounded by two full-bleed
   elements rather than by page whitespace. */
.contact-body { padding-block: clamp(28px, 4vw, 48px); }

/* ------------------------------------------------ library index -- */
/* The full guide and answer listings on /guides/ and /answers/. A hundred and
   twenty titles is a lot to put on a page: grouped by hub, set as plain lists,
   and columned so the eye scans down a short column rather than a long page. */
.library-index h2 { margin-bottom: 6px; }
.library-groups {
  margin-top: 30px; display: grid; gap: 30px 40px;
}
@media (min-width: 720px)  { .library-groups { grid-template-columns: 1fr 1fr; } }
@media (min-width: 1100px) { .library-groups { grid-template-columns: 1fr 1fr 1fr; } }
.library-group { break-inside: avoid; }
.library-group h3 {
  font-size: 0.8125rem; font-weight: 600; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--ink-muted);
  padding-bottom: 8px; margin-bottom: 10px;
  border-bottom: 1px solid var(--hairline);
  display: flex; justify-content: space-between; align-items: baseline; gap: 10px;
}
.library-group h3 a { color: var(--brand-primary); text-decoration: none; }
.library-group h3 a:hover { color: var(--accent); }
.library-count {
  font-family: var(--font-mono); font-size: 0.7rem; color: var(--hairline-strong);
  letter-spacing: 0;
}
.library-group ul { list-style: none; margin: 0; padding: 0; }
.library-group li { margin-bottom: 7px; }
.library-group a {
  color: var(--ink); text-decoration: none; font-size: 0.9375rem; line-height: 1.4;
  border-bottom: 1px solid transparent;
}
.library-group li a:hover { color: var(--accent); border-bottom-color: var(--accent); }

/* The guides hub body sits under the cluster cards, so it needs a rule above it
   to separate the two rather than reading as a caption to the last card. */
/* No top offset and no divider. This block sits alone inside a tinted band, so
   the change of background already separates it, and the 40px margin collapsed
   out through .wrap and made the band 152px deep at the top against 112 at the
   bottom. The rule was left over from when this prose followed the topic list
   inside the same white section. */
.guides-intro { margin-top: 0; }

/* ------------------------------------------------------- local FAQs -- */
/* Town pages only. <details> rather than a JS accordion: it works with no
   script, it is keyboard operable for free, and a browser's find-in-page can
   open one. */
.faq-section { padding-block: clamp(34px, 5vw, 60px); }
.faq-list { margin-top: 22px; max-width: 74ch; }
.faq-item { border-top: 1px solid var(--hairline); }
.faq-item:last-child { border-bottom: 1px solid var(--hairline); }
.faq-item summary {
  list-style: none; cursor: pointer; padding: 18px 34px 18px 0;
  font-weight: 600; font-size: 1.0625rem; position: relative;
}
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary::after {
  content: ""; position: absolute; right: 6px; top: 50%;
  width: 9px; height: 9px; margin-top: -6px;
  border-right: 2px solid var(--accent); border-bottom: 2px solid var(--accent);
  transform: rotate(45deg); transition: transform .15s ease;
}
.faq-item[open] summary::after { transform: rotate(-135deg); margin-top: -2px; }
.faq-item summary:hover { color: var(--accent); }
.faq-item summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.faq-item p { margin: 0 0 20px; color: var(--ink-muted); max-width: 68ch; }

/* ===================================================== areas index == */
/* Place names, grouped. This page used to be sixty seven cards each carrying
   the phrase "Rooflight and Skylight Installation in", which is a keyword list
   wearing a layout. A reader here is looking for their town, so the town is the
   only thing the row says. */
.areas-hero { padding-block: clamp(38px, 5vw, 64px); }
.areas-index { padding-block: clamp(34px, 5vw, 56px); }

.area-group + .area-group { margin-top: 46px; padding-top: 40px; border-top: 1px solid var(--hairline); }
.area-group-head { display: flex; align-items: center; gap: 20px; margin-bottom: 20px; }
.area-group-img {
  width: 92px; height: 92px; flex: 0 0 92px; border-radius: var(--radius);
  object-fit: cover; display: block;
}
.area-group-head h2 { margin: 0; font-size: clamp(1.2rem, 2.2vw, 1.5rem); }
.area-group-note {
  margin: 5px 0 0; font-size: 0.8125rem; color: var(--ink-muted);
  letter-spacing: 0.02em;
}

.area-list {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: 2px 26px;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
}
.area-list a {
  display: block; padding: 9px 2px; color: var(--ink); text-decoration: none;
  font-size: 1rem; border-bottom: 1px solid var(--hairline);
  transition: color .12s ease, padding-left .12s ease;
}
.area-list a:hover, .area-list a:focus-visible {
  color: var(--accent); padding-left: 6px;
}

.areas-outside {
  margin-top: 44px; padding-top: 26px; border-top: 1px solid var(--hairline);
  color: var(--ink-muted); max-width: var(--measure);
}

/* The coverage map. A ring on a map answers "do you come to me" faster than a
   list of names can, so it sits under the index rather than replacing it. */
.areas-map-section { padding-block: clamp(34px, 5vw, 56px); }
.areas-map-copy { margin-bottom: 22px; }
.areas-map-copy h2 { margin: 0 0 8px; }
.areas-map {
  height: clamp(320px, 46vh, 520px); border-radius: var(--radius);
  border: 1px solid var(--hairline); overflow: hidden;
}

/* ==================================================== guides topics == */
/* Alternating rows: image one side, three real guide titles the other. Breadth
   is shown by showing some of it, not by printing every link on the site. */
.guides-hero { padding-block: clamp(38px, 5vw, 64px); }
.guides-stat {
  display: flex; flex-wrap: wrap; gap: 14px 44px; margin-top: 30px;
  padding-top: 24px; border-top: 1px solid var(--hairline);
}
.guides-stat div { display: flex; align-items: baseline; gap: 9px; }
.guides-stat b {
  font-family: var(--font-display); font-size: clamp(1.6rem, 3vw, 2.1rem);
  color: var(--accent); line-height: 1;
}
/* The guides hero is a dark band, so the light-background muted ink measured
   2.65:1 against it. */
.guides-stat span { font-size: 0.8125rem; color: var(--on-dark-muted); letter-spacing: 0.04em; }

.guides-topics { padding-block: clamp(34px, 5vw, 56px); }
.guide-topic { display: grid; gap: 26px; align-items: start; }
.guide-topic + .guide-topic { margin-top: 44px; padding-top: 44px; border-top: 1px solid var(--hairline); }
@media (min-width: 860px) {
  .guide-topic { grid-template-columns: 340px minmax(0, 1fr); gap: 40px; }
  /* Alternate the side so ten rows do not read as ten of the same row. */
  .guide-topic:nth-child(even) { grid-template-columns: minmax(0, 1fr) 340px; }
  .guide-topic:nth-child(even) .guide-topic-media { order: 2; }
}

.guide-topic-media { position: relative; }
.guide-topic-media img {
  width: 100%; height: 240px; object-fit: cover; display: block;
  border-radius: var(--radius);
}
.guide-topic-n {
  position: absolute; left: 14px; top: 12px;
  font-family: var(--font-mono); font-size: 0.75rem; font-weight: 600;
  color: var(--white); background: rgba(23,50,58,.78);
  padding: 4px 9px; border-radius: 4px; letter-spacing: 0.06em;
}

.guide-topic-body h2 { margin: 0 0 8px; font-size: clamp(1.3rem, 2.4vw, 1.7rem); }
.guide-topic-body h2 a { color: var(--brand-primary); text-decoration: none; }
.guide-topic-body h2 a:hover { color: var(--accent); }
.guide-topic-blurb { margin: 0 0 18px; color: var(--ink-muted); max-width: 60ch; }

.guide-topic-samples { list-style: none; margin: 0 0 18px; padding: 0; }
.guide-topic-samples li { border-top: 1px solid var(--hairline); }
.guide-topic-samples a {
  display: block; padding: 11px 0 11px 20px; position: relative;
  color: var(--ink); text-decoration: none; font-size: 0.9375rem; line-height: 1.45;
  transition: color .12s ease, padding-left .12s ease;
}
.guide-topic-samples a::before {
  content: ""; position: absolute; left: 2px; top: 50%; margin-top: -3px;
  width: 6px; height: 6px; border-right: 2px solid var(--accent);
  border-bottom: 2px solid var(--accent); transform: rotate(-45deg);
}
.guide-topic-samples a:hover { color: var(--accent); padding-left: 26px; }

.guide-topic-more {
  display: inline-block; font-weight: 600; font-size: 0.9375rem;
  color: var(--accent); text-decoration: none;
  border-bottom: 2px solid var(--accent); padding-bottom: 2px;
}
.guide-topic-more:hover { color: var(--brand-primary); border-bottom-color: var(--brand-primary); }

/* =================================================== answers index == */
.answers-hero { padding-block: clamp(38px, 5vw, 60px); }

/* The search. A person arriving here has a question, so the first thing on the
   page is somewhere to type it. Filters data already in the document, so there
   is no request and it works before anything else has loaded. */
.answer-search { margin-top: 28px; max-width: 640px; position: relative; }
.answer-search input {
  width: 100%; font: inherit; font-size: 1.0625rem;
  padding: 15px 18px; border: 1px solid var(--hairline-strong);
  border-radius: var(--radius); background: var(--white); color: var(--ink);
}
.answer-search input:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-color: var(--accent); }
.answer-search-count { margin: 10px 2px 0; font-size: 0.875rem; color: var(--ink-muted); min-height: 1.2em; }
.answer-results {
  list-style: none; margin: 8px 0 0; padding: 0;
  border: 1px solid var(--hairline); border-radius: var(--radius);
  background: var(--white); overflow: hidden;
}
.answer-results li + li { border-top: 1px solid var(--hairline); }
.answer-results a {
  display: flex; justify-content: space-between; align-items: baseline; gap: 16px;
  padding: 13px 16px; color: var(--ink); text-decoration: none; font-size: 0.9375rem;
}
.answer-results a span { color: var(--ink-muted); font-size: 0.75rem; white-space: nowrap; }
.answer-results a:hover { background: var(--surface-alt); color: var(--accent); }

.answers-topics { padding-block: clamp(34px, 5vw, 56px); }
.answers-grid { display: grid; gap: 30px 36px; }
@media (min-width: 700px)  { .answers-grid { grid-template-columns: 1fr 1fr; } }
@media (min-width: 1100px) { .answers-grid { grid-template-columns: 1fr 1fr 1fr; } }

.answer-topic h2 {
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
  margin: 0 0 12px; padding-bottom: 10px; border-bottom: 2px solid var(--brand-primary);
  font-size: 1.0625rem;
}
.answer-topic h2 a { color: var(--brand-primary); text-decoration: none; }
.answer-topic h2 a:hover { color: var(--accent); }
.answer-topic-n {
  font-family: var(--font-mono); font-size: 0.7rem; color: var(--accent);
  font-weight: 600; letter-spacing: 0;
}
.answer-topic ul { list-style: none; margin: 0 0 12px; padding: 0; }
.answer-topic li { margin-bottom: 8px; }
.answer-topic li a {
  color: var(--ink); text-decoration: none; font-size: 0.9375rem; line-height: 1.4;
  border-bottom: 1px solid transparent;
}
.answer-topic li a:hover { color: var(--accent); border-bottom-color: var(--accent); }
.answer-topic-more { font-size: 0.8125rem; font-weight: 600; color: var(--accent); text-decoration: none; }
.answer-topic-more:hover { color: var(--brand-primary); }

/* ================================================== tables, revisited == */
/* TWO FAULTS, ONE CAUSE: the table was being asked to live inside the reading
   measure.
   
   On desktop, `.authority-prose` is capped at 74ch and a four or five column
   comparison was squeezed into it, so a table with real content in it rendered
   as a narrow box of wrapped fragments — unreadable, and the widest thing on
   the page reduced to the narrowest.
   
   On a phone the same table, carrying `min-width: 520px`, pushed straight
   through the 390px viewport: `.tablewrap` had `overflow-x: auto` but no
   definite width to scroll within, so it grew instead of scrolling.
   
   A table is data, not prose. It gets the full column, and it scrolls inside
   itself when the column is narrower than the data needs. */

/* Ancestors must be allowed to be narrower than their content, or the scroll
   container can never take effect. This is the default that grid and flex
   items do not have. */
.prose, .authority-prose, .ed-sec, .page-layout > *, .authority-grid > * { min-width: 0; }

/* Break out of the reading measure, up to the width of the column the prose
   sits in. Capped so it can never exceed the viewport. */
/* The cap is the VIEWPORT, not the containing block. `min(100%, ...)` resolves
   100% against the text column, so it cancelled the breakout below and the
   table stayed at the reading measure with a negative margin doing nothing. */
.prose > .tablewrap,
.prose .ed-sec > .tablewrap,
.authority-prose > .tablewrap,
.authority-prose .ed-sec > .tablewrap {
  max-width: calc(100vw - 2 * var(--gutter));
  width: 100%;
}
@media (min-width: 1080px) {
  /* Reclaim the slack INSIDE the prose column, and only on the side where
     there is slack. Both long-form layouts put a sidebar on one side and the
     text measure leaves the rest of the column empty on the other:

       authority-read   244px contents | prose      -> slack is to the RIGHT
       page-layout      prose | 320px sidebar       -> slack is to the RIGHT

     A symmetric negative margin pushed the table left, straight through the
     contents list, and squashed the quote button in it to four lines. */
  .authority-prose > .tablewrap,
  .authority-prose .ed-sec > .tablewrap {
    width: calc(100% + 8.5rem); margin-left: 0; margin-right: -8.5rem;
  }
  .page-layout .prose > .tablewrap,
  .page-layout .prose .ed-sec > .tablewrap {
    width: calc(100% + 7rem); margin-left: 0; margin-right: -7rem;
  }
}

/* Scroll, do not grow. */
.tablewrap { overflow-x: auto; max-width: 100%; -webkit-overflow-scrolling: touch; }

/* Give the columns room to breathe once they have it, and tighten the type a
   little so five columns fit without wrapping every cell to three lines. */
.tablewrap table { min-width: 560px; }
.tablewrap[data-cols="4"] table { min-width: 680px; }
.tablewrap[data-cols="5"] table { min-width: 780px; }
.tablewrap[data-cols="4"] th, .tablewrap[data-cols="4"] td,
.tablewrap[data-cols="5"] th, .tablewrap[data-cols="5"] td { padding: 11px 13px; font-size: 0.875rem; }

/* A table that scrolls needs to say so, or a reader assumes the visible columns
   are all of them. */
.tablewrap { position: relative; }
.tablewrap::after {
  content: ""; position: absolute; top: 0; right: 0; bottom: 0; width: 34px;
  pointer-events: none; opacity: 0; transition: opacity .15s ease;
  background: linear-gradient(to right, rgba(255,255,255,0), var(--white));
}
.tablewrap.is-scrollable::after { opacity: 1; }

/* ------------------------------------------- ed-panels, contained --- */
/* THE REAL HORIZONTAL OVERFLOW. The homepage scrolled sideways on a phone —
   1002px of document in a 390px viewport — and the reported culprit was a
   table, which was a red herring: the table was inside its scroll container
   behaving perfectly.
   
   It was .ed-panels. `max-width: none` was set on it to let panels break the
   reading measure on desktop, and with no explicit single-column track on
   mobile the grid sized itself to its content instead of to its parent. Each
   panel then rendered 977px wide inside a 342px column.
   
   An explicit single column below the breakpoint, a cap at the parent width,
   and min-width: 0 on the items so they are allowed to be narrow. */
.ed-panels { grid-template-columns: minmax(0, 1fr); max-width: 100%; }
.ed-panel { min-width: 0; }
@media (min-width: 720px) {
  .ed-panels { grid-template-columns: repeat(auto-fit, minmax(min(270px, 100%), 1fr)); }
}

/* ===================================================== tables: fit, never scroll ==
 *
 * SUPERSEDES the scroll-container rule above, on the operator's instruction:
 * "each table needs to fit the content so the reader doesn't have to scroll."
 *
 * The earlier rule was "a table is data, not prose: it gets the full column and
 * scrolls inside itself when the column is narrower than the data needs." That
 * holds for a table of numbers. It does not hold here: these cells carry prose
 * ("Mechanical lift and an access route for it") and 169 of the 202 tables are
 * three columns of it. On a 390px phone that is ninety pixels a column —
 * squeezed to read AND dragged sideways to finish a row.
 *
 * Two mechanisms replace the scroll, because one is not enough:
 *
 *   WIDE   table-layout: fixed. With auto layout a cell of prose reports a
 *          min-content width and the table grows past its column however small
 *          the min-width is set — which is why removing min-width alone left
 *          a 5-column table 655px wider than the 756px column it sits in, and
 *          pushed the whole page sideways. Fixed layout makes the columns share
 *          the width they have and the prose wrap inside them.
 *
 *   NARROW the table stacks: each row becomes a block titled by its row
 *          heading, each cell a labelled line under it. The label comes from
 *          data-label, written by build/fix-table-labels.mjs from the column
 *          heading. Nothing hidden, nothing truncated, nothing scrolled.
 *
 * Keyed on :not([data-cols="2"]) rather than on each count, because the
 * homepage builds its own .tablewrap in front-page.php with no data-cols at
 * all — keyed on the counts, every rule here missed it and it was the worst
 * offender on the site.
 */

/* 1. Nothing scrolls. The min-widths existed only to force a scroll container. */
.tablewrap { overflow-x: visible; }
table,
.tablewrap table,
.authority-prose table,
.tablewrap[data-cols="3"] table,
.tablewrap[data-cols="4"] table,
.tablewrap[data-cols="5"] table { min-width: 0; }

/* The shadow is the affordance for a scroll that no longer happens. */
.tablewrap::after { content: none; }

/* 2. Side by side: share the width, wrap the prose. */
.tablewrap table { table-layout: fixed; width: 100%; }
th, td { overflow-wrap: break-word; word-break: normal; hyphens: auto; }
/* `th { white-space: nowrap }` above holds a 2-column table open past a 390px
   phone — 13px of scroll for the sake of not wrapping one heading. */
thead th, tbody th[scope="row"] { white-space: normal; }
/* The row-header column names the thing; give it more than an equal share so
   the value columns are not starved by a long label. */
.tablewrap table th[scope="row"] { width: 26%; }
.tablewrap[data-cols="2"] table th[scope="row"] { width: 38%; }

/* 3. Narrow: stack. A 5-column table runs out of readable width first, so it
      stacks sooner — the breakpoint follows the column count, because what a
      table needs is set by how much prose it carries, not by the device. */
@media (max-width: 1024px) {
  .tablewrap[data-cols="5"] table { display: block; }
}
@media (max-width: 820px) {
  .tablewrap:not([data-cols="2"]) table { display: block; }
}

@media (max-width: 1024px) {
  .tablewrap[data-cols="5"] thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
  .tablewrap[data-cols="5"] tbody { display: block; }
  .tablewrap[data-cols="5"] tbody tr { display: block; padding: 4px 0 8px; border-bottom: 1px solid var(--hairline); }
  .tablewrap[data-cols="5"] tbody tr:last-child { border-bottom: 0; }
  .tablewrap[data-cols="5"] tbody th[scope="row"],
  .tablewrap[data-cols="5"] tbody td { display: block; width: auto; border-bottom: 0; }
}
@media (max-width: 820px) {
  .tablewrap:not([data-cols="2"]) thead {
    /* Not display:none — that drops it from the accessibility tree as well as
       the layout. Clipped, so a screen reader still announces the headings. */
    position: absolute; width: 1px; height: 1px; overflow: hidden;
    clip-path: inset(50%); white-space: nowrap;
  }
  .tablewrap:not([data-cols="2"]) tbody { display: block; }
  .tablewrap:not([data-cols="2"]) tbody tr {
    display: block; padding: 4px 0 8px; border-bottom: 1px solid var(--hairline);
  }
  .tablewrap:not([data-cols="2"]) tbody tr:last-child { border-bottom: 0; }
  .tablewrap:not([data-cols="2"]) tbody th[scope="row"],
  .tablewrap:not([data-cols="2"]) tbody td { display: block; width: auto; border-bottom: 0; }
}

/* Shared look for the stacked state. Safe to apply at any width: these only
   bite once the cells are display:block, and a table that is still side by
   side is unaffected by block-level padding on its own cells. */
.tablewrap tbody th[scope="row"] {
  background: var(--surface);
  font-family: var(--font-display); font-weight: 700;
  font-size: 0.9375rem; letter-spacing: -0.01em; text-transform: none;
  color: var(--brand-primary);
}
@media (max-width: 1024px) {
  .tablewrap[data-cols="5"] tbody th[scope="row"] { padding: 11px 16px; }
  .tablewrap[data-cols="5"] tbody td { padding: 9px 16px 0; font-size: 0.9375rem; }
  .tablewrap[data-cols="5"] tbody td::before {
    content: attr(data-label); display: block; margin-bottom: 2px;
    font-size: 0.6875rem; font-weight: 600; letter-spacing: 0.12em;
    text-transform: uppercase; color: var(--ink-muted);
  }
}
@media (max-width: 820px) {
  .tablewrap:not([data-cols="2"]) tbody th[scope="row"] { padding: 11px 16px; }
  .tablewrap:not([data-cols="2"]) tbody td { padding: 9px 16px 0; font-size: 0.9375rem; }
  .tablewrap:not([data-cols="2"]) tbody td::before {
    content: attr(data-label); display: block; margin-bottom: 2px;
    font-size: 0.6875rem; font-weight: 600; letter-spacing: 0.12em;
    text-transform: uppercase; color: var(--ink-muted);
  }
  /* The corner column has no heading to announce. */
  .tablewrap tbody td:not([data-label])::before,
  .tablewrap tbody td[data-label=""]::before { content: none; }
  /* The 4/5-column type tightening was for fitting columns side by side. */
  .tablewrap[data-cols="4"] th, .tablewrap[data-cols="4"] td,
  .tablewrap[data-cols="5"] th, .tablewrap[data-cols="5"] td { font-size: 0.9375rem; }
}

/* ---------------------------------------- tables vs the sticky quote sidebar --
 *
 * REPORTED: on the service pages the "Get a fixed quote" sidebar overlaps the
 * tables while scrolling.
 *
 * MEASURED at 1440px, rather than reasoned about:
 *
 *     grid            744px | 56px gutter | 320px sidebar
 *     prose column    [153 .. 897]   744px wide
 *     sidebar         [953 .. 1273]  position: sticky
 *     tablewrap       [250 .. 1009]  margin-right: -7rem  (-112px)
 *
 * The table runs 112px past the end of its own column: the whole 56px gutter,
 * and then 56px INTO the sidebar. The sidebar is sticky, so it stays pinned
 * beside the prose and the table slides under it for the length of the scroll.
 *
 * The breakout rule above says it is "reclaiming the slack INSIDE the prose
 * column". On this layout there is no slack to reclaim — the prose element
 * measures 744px and the column is 744px, so every pixel of the negative margin
 * is overshoot. The rule was written for a case that does not exist here, and
 * the table-layout: fixed change made it obvious by filling the full broken-out
 * width instead of scrolling inside it.
 *
 * A table gets its column and stops there. 744px across four columns is 186px
 * each, which is not tight.
 *
 * Left alone: .authority-prose keeps its breakout. That layout puts its
 * contents rail on the LEFT with nothing to the right, so extending right runs
 * into empty column rather than into a panel — measured, and it overflows
 * nothing. */
@media (min-width: 1080px) {
  .page-layout .prose > .tablewrap,
  .page-layout .prose .ed-sec > .tablewrap,
  .page-layout .authority-prose > .tablewrap,
  .page-layout .authority-prose .ed-sec > .tablewrap {
    width: 100%;
    margin-left: 0;
    margin-right: 0;
  }
}

/* ================================================ reader pages: guides + answers ==
 *
 * 320 pages whose job is to be read, not to sell. The quote sidebar is gone from
 * them (page.php, $is_reader) and the width it held goes to a contents list,
 * because the real obstacle on these pages is not persuasion, it is finding the
 * section you came for: the median guide has 18 h2 sections in 1,894 words and
 * the median answer 19 in 968.
 *
 * Desktop: contents on the left, sticky, with the current section marked. The
 * text keeps a reading measure — a 1,200px line is not an improvement on a
 * cramped one, so removing the sidebar must not widen the prose to the window.
 *
 * Phone: contents collapse into a disclosure above the article, so the first
 * thing on screen is still the first paragraph, not a screenful of navigation.
 */
.reader-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 28px;
}
.reader-layout > .prose { max-width: 70ch; min-width: 0; }

.reader-rail { min-width: 0; }
.reader-toc-label {
  display: block; margin-bottom: 10px;
  font-size: 0.6875rem; font-weight: 600; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--ink-muted);
}
.reader-toc-list { list-style: none; margin: 0; padding: 0; }
.reader-toc-list li { margin: 0; }
.reader-toc-list a {
  display: block; padding: 6px 12px;
  border-left: 2px solid var(--hairline);
  color: var(--ink-muted); text-decoration: none;
  font-size: 0.875rem; line-height: 1.45;
}
.reader-toc-list a:hover { color: var(--brand-primary); background: var(--surface); }
/* The section being read. aria-current is set by the scroll observer, so the
   styling and the accessibility state cannot disagree. */
.reader-toc-list a[aria-current="true"] {
  color: var(--brand-primary); font-weight: 600;
  border-left-color: var(--accent); background: var(--surface);
}

/* Related reading, at the end of the article. */
.reader-related { margin-top: 44px; padding-top: 24px; border-top: 1px solid var(--hairline); }
.reader-related h2 { margin: 0 0 14px; font-size: 1.125rem; }
.reader-related ul {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: minmax(0, 1fr); gap: 2px;
}
.reader-related a {
  display: block; padding: 10px 14px; border-radius: var(--radius);
  color: var(--brand-primary); text-decoration: none;
  font-size: 0.9375rem; background: var(--surface);
}
.reader-related a:hover { background: var(--white); box-shadow: 0 1px 3px rgba(15,21,25,.08); }

/* ---- phone and small tablet: contents as a disclosure above the article ---- */
@media (max-width: 899px) {
  .reader-layout { gap: 18px; }
  /* Source order puts the prose first and the rail second, which is the right
     order for a screen reader and the wrong one visually: the contents belong
     above the text it indexes. One line of order, rather than moving the
     markup and breaking the reading order. */
  .reader-layout > .reader-rail { order: -1; }
  .reader-toc {
    border: 1px solid var(--hairline); border-radius: var(--radius);
    background: var(--white);
  }
  .reader-toc-label {
    margin: 0; padding: 13px 16px; cursor: pointer;
    display: flex; align-items: center; justify-content: space-between;
    color: var(--brand-primary); font-size: 0.75rem;
  }
  .reader-toc-label::after {
    content: ""; width: 8px; height: 8px; margin-left: 12px;
    border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
    transform: rotate(45deg) translateY(-2px); transition: transform .15s ease;
  }
  .reader-toc[data-open="true"] .reader-toc-label::after { transform: rotate(-135deg) translateY(-2px); }
  .reader-toc:not([data-open="true"]) .reader-toc-list { display: none; }
  .reader-toc-list { padding: 4px 0 10px; max-height: 50vh; overflow-y: auto; }
  .reader-toc-list a { padding: 8px 16px; border-left: 0; }
  .reader-toc-list a[aria-current="true"] { border-left: 0; box-shadow: inset 2px 0 0 var(--accent); }
  .reader-related ul { gap: 4px; }
}

/* ---- desktop: contents beside the text, sticky ---- */
@media (min-width: 900px) {
  .reader-layout { grid-template-columns: 236px minmax(0, 1fr); gap: 48px; align-items: start; }
  .reader-layout > .reader-rail { order: -1; position: sticky; top: 92px; }
  .reader-toc-list { max-height: calc(100vh - 140px); overflow-y: auto; }
  /* The label is a heading here, not a control. */
  .reader-toc-label { cursor: default; }
  .reader-related ul { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
}
@media (min-width: 1200px) {
  .reader-layout { grid-template-columns: 252px minmax(0, 1fr); gap: 64px; }
}

/* ===================================================== mobile: measured fixes ==
 *
 * From build/qa-mobile.mjs, one page of every template at 390/430/768. Zero
 * horizontal overflow anywhere, which is the check that matters most and the one
 * this build has failed before. These are the rest.
 */

/* 1. FORM FIELDS MUST BE 16px OR iOS SAFARI ZOOMS THE PAGE.
 *
 * Focus an input under 16px on an iPhone and Safari zooms the whole viewport to
 * meet it. The visitor then fills in the quote form pinched in, and has to zoom
 * back out to read anything. The fields measured 14.4px. This is the single
 * most consequential mobile fix on the site, because it lands on the form that
 * takes the enquiry. */
input, textarea, select, button {
  font-size: 16px;
  font-family: inherit;
}
/* The CRM embed builds its own fields; reach them too. */
#mortaro-form input, #mortaro-form textarea, #mortaro-form select { font-size: 16px; }
@media (min-width: 1000px) {
  /* Above the phone range the constraint does not apply and the design's own
     sizing comes back. */
  input, textarea, select, button { font-size: inherit; }
}

/* 2. TAP TARGETS. WCAG 2.2 AA asks 24x24 CSS px for anything you press. The
 *    footer legal row measured 26px, the build credit 20px and several link
 *    lists 22px. Padding rather than font size, so nothing about the type
 *    changes. */
@media (max-width: 999px) {
  .footer-bottom a,
  .footer-credit a,
  .footer-legal a,
  .sidebar-links a,
  .reader-related a,
  .footer-col a {
    display: inline-block;
    min-height: 24px;
    padding-block: 5px;
  }
  /* A list of links is a menu on a phone: give the rows room to be aimed at. */
  .sidebar-links li,
  .footer-col li { margin-bottom: 2px; }
}

/* 3. THE FIXED BOTTOM BAR IS 70px, THE PAGE RESERVED 68px.
 *
 * Two pixels of every page sat under the bar for ever. The reserve is now the
 * measured height plus the safe-area inset, so a phone with a home indicator
 * does not eat the last line either. */
@media (max-width: 999px) {
  body { padding-bottom: calc(76px + env(safe-area-inset-bottom, 0px)); }
  .sticky-cta { padding-bottom: calc(10px + env(safe-area-inset-bottom, 0px)); }
}

/* 4. READING SIZE ON READER PAGES.
 *
 * Body copy measured 15px. That is legible but it is not comfortable for the
 * 1,900-word guides, and these are the pages the operator asked to be "best for
 * the reader". 16px with a little more line height, on the reader tier only, so
 * the money pages keep the density their layout was designed around. */
@media (max-width: 899px) {
  .reader-layout > .prose { font-size: 1rem; }
  .reader-layout > .prose p,
  .reader-layout > .prose li { font-size: 1rem; line-height: 1.68; }
  .reader-layout > .prose h2 { margin-top: 1.7em; }
}
