/* ============================================================================
   SUNSPIRE SKYLIGHTS CHELMSFORD — design system
   DNA 08 "Spire". The light a spire catches first.

   Distinctiveness is carried by FORM, not colour (STYLE-GUIDE §2). Four sister
   builds already run dark-ground-plus-warm-accent. What makes this site itself:
     1. RAYS instead of rules on every divider
     2. A warm stone mid-tone that no sister has
     3. The spire ornament above major headings on Authority pages

   THE CONTRAST RULE THAT BIT THE LAST BUILD FOUR TIMES (§3):
   Sun Amber on First Light is 2.0:1. It is NEVER text on a light ground.
   Every component that sets a colour for light grounds carries a paired rule
   scoped ONE LEVEL DEEPER for dark grounds, so the dark rule OUTRANKS on
   specificity rather than tying. Never a token remap alone.
   ========================================================================= */

/* ---------------------------------------------------------------- TOKENS */
:root {
  /* palette */
  --spire:        #17293F;   /* Spire Navy */
  --spire-deep:   #0D1826;   /* Night */
  --sun:          #F0A202;   /* Sun Amber — decorative on light, text on dark */
  --sun-soft:     rgba(240,162,2,.22);
  --first-light:  #FFF6E8;   /* warm off-white */
  --stone:        #D8D2C7;   /* Ragstone */
  --stone-dim:    #EFEBE4;
  --ink:          #1B222B;
  --ink-dim:      #5A636E;
  --white:        #FFFFFF;

  /* semantic */
  --ground:       var(--first-light);
  --text:         var(--ink);
  --text-dim:     var(--ink-dim);
  --rule:         var(--stone);

  /* spacing scale — 4 8 12 16 24 32 48 64 96 128. No arbitrary px in templates. */
  --s1: 4px;  --s2: 8px;  --s3: 12px; --s4: 16px; --s5: 24px;
  --s6: 32px; --s7: 48px; --s8: 64px; --s9: 96px; --s10: 128px;

  /* containers */
  --w-narrow:   68ch;
  --w-standard: 1140px;
  --w-wide:     1320px;

  /* type */
  --display: "Sentient", ui-serif, Georgia, serif;
  --body:    "Supreme", system-ui, -apple-system, "Segoe UI", sans-serif;
  --mono:    ui-monospace, "SF Mono", "Cascadia Mono", Menlo, monospace;

  --t-display: clamp(2.75rem, 6vw, 4.25rem);
  --t-h1:      clamp(2.25rem, 4.5vw, 3.25rem);
  --t-h2:      clamp(1.6rem, 2.6vw, 2.1rem);
  --t-h3:      1.3rem;
  --t-lead:    clamp(1.1rem, 1.6vw, 1.3rem);
  --t-body:    1.0625rem;
  --t-small:   .875rem;
  --t-eyebrow: .7rem;

  --radius:    12px;
  --radius-sm: 6px;
  --shadow:    0 1px 2px rgba(23,41,63,.04), 0 8px 24px rgba(23,41,63,.06);
  --shadow-lg: 0 2px 4px rgba(23,41,63,.06), 0 18px 48px rgba(23,41,63,.10);
}

/* ------------------------------------------------------------------ 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(--ground);
  color: var(--text);
  font-family: var(--body);
  font-size: var(--t-body);
  line-height: 1.7;
  -webkit-font-smoothing: antialiased;
}

img, svg, video { max-width: 100%; height: auto; display: block; }
a { color: var(--spire); text-decoration-color: var(--sun); text-underline-offset: 3px; }
a:hover { text-decoration-thickness: 2px; }

h1, h2, h3, h4 {
  font-family: var(--display);
  font-weight: 500;
  line-height: 1.15;
  letter-spacing: -.02em;
  text-wrap: balance;
  color: var(--spire);
  margin: 0 0 var(--s4);
}
h1 { font-size: var(--t-h1); line-height: 1.1; }
h2 { font-size: var(--t-h2); line-height: 1.2; }
h3 { font-size: var(--t-h3); line-height: 1.3; letter-spacing: -.01em; }
p  { margin: 0 0 var(--s5); }

/* ---------------------------------------------------------------- FOCUS */
:focus-visible {
  outline: 3px solid var(--spire);
  outline-offset: 2px;
  border-radius: 2px;
}
.band-navy :focus-visible,
.section--slate :focus-visible,
.site-footer :focus-visible,
.hero :focus-visible { outline-color: var(--white); }

.screen-reader-text {
  position: absolute !important; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap;
}
.skip-link {
  position: absolute; left: var(--s4); top: -60px; z-index: 999;
  background: var(--sun); color: var(--spire); font-weight: 600;
  padding: var(--s3) var(--s5); border-radius: var(--radius-sm);
  transition: top .15s;
}
.skip-link:focus { top: var(--s4); }

/* ------------------------------------------------------------ CONTAINERS */
.container, .wrap { width: min(100% - var(--s7), var(--w-standard)); margin-inline: auto; }
.wrap-wide       { width: min(100% - var(--s7), var(--w-wide)); margin-inline: auto; }
.narrow          { width: min(100% - var(--s7), var(--w-narrow)); margin-inline: auto; }
@media (max-width: 640px) {
  .container, .wrap, .wrap-wide, .narrow { width: calc(100% - var(--s5)); }
}

.section { padding-block: clamp(var(--s8), 8vw, var(--s9)); position: relative; }
.section--paper2 { background: var(--stone-dim); }

/* Dark bands. The radial is anchored to ONE upper corner: light has a direction,
   so a centred glow reads as a graphic effect instead of as a window. */
.section--slate, .band-navy {
  background:
    radial-gradient(120% 80% at 88% -10%, rgba(240,162,2,.16) 0%, rgba(240,162,2,0) 55%),
    var(--spire);
  color: var(--first-light);
}
.band-navy--deep { background:
    radial-gradient(120% 80% at 12% -10%, rgba(240,162,2,.13) 0%, rgba(240,162,2,0) 55%),
    var(--spire-deep); color: var(--first-light); }

/* Dark-ground text overrides. Scoped one level deeper so they OUTRANK. */
.section--slate h1, .section--slate h2, .section--slate h3,
.band-navy h1, .band-navy h2, .band-navy h3,
.site-footer h2, .site-footer h3 { color: var(--first-light); }
.section--slate p, .band-navy p { color: #E4DCCF; }
/* :not(.btn) is load-bearing. A button brings its own ground and its own label
   colour; repainting it with the band's link colour put First Light on Sun
   Amber at 1.98:1. Found the moment a primary button first appeared inside a
   dark panel, which is exactly when a latent rule like this surfaces. */
.section--slate a:not(.btn), .band-navy a:not(.btn), .site-footer a:not(.btn) { color: var(--first-light); }

/* ============================================================ THE RAY SYSTEM
   The signature. A ray element appears at most TWICE per viewport (§5).
   Three hairlines fanning ~3deg from a point at the left of the container.
   Not a sunburst, not a chevron. If it reads as a graphic effect, it is wrong. */
.ray {
  --ray-color: var(--sun);
  --ray-alpha: .30;
  position: relative;
  height: 26px;
  margin-block: var(--s6);
  overflow: hidden;
}
.ray::before, .ray::after,
.ray > i {
  content: "";
  position: absolute;
  left: 0; top: 50%;
  width: 100%; height: 1px;
  background: linear-gradient(90deg,
    rgba(0,0,0,0) 0%,
    color-mix(in srgb, var(--ray-color) calc(var(--ray-alpha) * 100%), transparent) 12%,
    rgba(0,0,0,0) 92%);
  transform-origin: 0 50%;
}
.ray::before { transform: rotate(-1.5deg); }
.ray > i    { transform: rotate(0deg); }
.ray::after  { transform: rotate(1.5deg); }

.section--slate .ray, .band-navy .ray, .site-footer .ray { --ray-alpha: .55; }

/* Legacy edge/ridge hooks from the ported templates resolve to rays, never to
   the diagonal ridge device the sister build used. */
.ridge, .ridge-top, .edge-bottom, .edge-down, .has-edge::after { display: none; }

/* The spire ornament. Authority pages only, above major H2s. Not everywhere,
   or it becomes wallpaper. */
.ornament {
  width: 26px; height: 26px; margin-bottom: var(--s3);
  color: var(--sun); opacity: .9;
}

/* ------------------------------------------------------------------- TYPE */
.eyebrow {
  font-family: var(--mono);
  font-size: var(--t-eyebrow);
  letter-spacing: .16em;
  text-transform: uppercase;
  line-height: 1.4;
  color: var(--spire);          /* light ground: navy, because Sun is 2.0:1 here */
  margin: 0 0 var(--s3);
  display: block;
  font-weight: 600;
}
/* Dark grounds, one level deeper, so these WIN. */
.on-dark .eyebrow,
.section--slate .eyebrow,
.band-navy .eyebrow,
.hero .eyebrow,
.page-hero .eyebrow,
.site-footer .eyebrow,
.quote-panel .eyebrow { color: var(--sun); }

.lead {
  font-size: var(--t-lead);
  line-height: 1.6;
  color: var(--text-dim);
  max-width: 62ch;
}
.section--slate .lead, .band-navy .lead,
.hero .lead, .page-hero .lead, .site-footer .lead { color: #D9D2C6; }

.mark { background: linear-gradient(180deg, transparent 62%, var(--sun-soft) 62%); }

/* ---------------------------------------------------------------- BUTTONS */
.btn, .btn-primary {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s2);
  font-family: var(--body); font-weight: 600; font-size: .95rem;
  padding: var(--s3) var(--s5);
  border-radius: var(--radius-sm);
  border: 1.5px solid transparent;
  text-decoration: none;
  cursor: pointer;
  transition: transform .15s, box-shadow .15s, background-color .15s;
}
.btn--primary, .btn-primary {
  background: var(--sun); color: var(--spire); border-color: var(--sun);
}
.btn--primary:hover, .btn-primary:hover { transform: translateY(-2px); box-shadow: var(--shadow); }
.btn--ghost { background: transparent; color: var(--spire); border-color: var(--spire); }
.btn--ghost:hover { background: var(--spire); color: var(--first-light); }
/* Ghost on dark, deeper scope so it outranks. */
/* Every dark surface carries .on-dark. The list below used to be a hardcoded
   enumeration of dark selectors, and each NEW dark panel silently failed until
   somebody remembered to add it: the sizer's output panel shipped with a navy
   ghost button on a navy ground at 1.43:1. A single opt-in class means a new
   dark surface inherits the corrections instead of needing to be remembered. */
.on-dark .btn--ghost,
.section--slate .btn--ghost, .band-navy .btn--ghost,
.hero .btn--ghost, .page-hero .btn--ghost, .site-footer .btn--ghost {
  color: var(--first-light); border-color: rgba(255,246,232,.5);
}
.on-dark .btn--ghost:hover,
.section--slate .btn--ghost:hover, .band-navy .btn--ghost:hover,
.hero .btn--ghost:hover, .page-hero .btn--ghost:hover {
  background: var(--first-light); color: var(--spire); border-color: var(--first-light);
}
.btn--lg { padding: var(--s4) var(--s7); font-size: 1.05rem; }

/* ----------------------------------------------------------------- HEADER */
/* Logged-in previews carry the 32px WordPress admin bar. A sticky header at
   top:0 slides underneath it and clips the brand lockup, which is what the
   sandbox filmstrip showed. Harmless in production, where the bar is absent. */
body.admin-bar .site-header { top: 32px; }
@media screen and (max-width: 782px) { body.admin-bar .site-header { top: 46px; } }

.site-header {
  background: var(--spire);
  color: var(--first-light);
  position: sticky; top: 0; z-index: 100;
  border-bottom: 1px solid rgba(240,162,2,.28);
}
/* flex-wrap is load-bearing: at 200% text zoom an unwrapped header overflows
   the viewport and fails WCAG 1.4.4. The sister build shipped that defect and
   only passed beforehand because the phone slot was empty. */
.site-header .container {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s5); flex-wrap: wrap;
  padding-block: var(--s3);
}
.brand { display: flex; align-items: center; gap: var(--s3); text-decoration: none; color: inherit; }
.brand__mark { width: 34px; height: 34px; flex: 0 0 auto; }
.brand strong {
  font-family: var(--display); font-size: 1.15rem; font-weight: 500;
  letter-spacing: -.01em; line-height: 1.1; display: block; color: var(--first-light);
}
.brand small {
  font-family: var(--mono); font-size: .62rem; letter-spacing: .14em;
  text-transform: uppercase; color: var(--sun); display: block;
}

.nav { display: flex; align-items: center; gap: var(--s5); }
.menu { display: flex; align-items: center; gap: var(--s5); list-style: none; margin: 0; padding: 0; }
.menu a { color: var(--first-light); text-decoration: none; font-size: .93rem; font-weight: 500; }
.menu a:hover { color: var(--sun); }
.menu-item { position: relative; }

/* THE DROPDOWN, AND WHY IT WAS UNUSABLE.
 *
 * It opened on hover and closed the instant hover was lost, with a 12px gap
 * between the trigger and the panel. Moving the pointer down from "Services"
 * crossed that gap, hover was lost mid-journey, and the menu vanished. It read
 * as hypersensitive; it was actually a dead zone plus a zero-tolerance close.
 *
 * Three fixes, all needed:
 *   1. A transparent BRIDGE across the gap, so the pointer never leaves the
 *      hoverable area. The visual gap stays.
 *   2. Open the hit area upward past the trigger, so a diagonal approach from
 *      the left or right does not fall off the item.
 *   3. Close on a DELAY. Opening is instant; closing waits 220ms, so a brief
 *      wobble or an overshoot does not dismiss it. Opacity and visibility are
 *      used rather than display, because display cannot be transitioned and so
 *      cannot be delayed.
 */
.submenu {
  position: absolute; top: calc(100% + var(--s3)); left: 0; z-index: 120;
  background: var(--first-light); color: var(--ink);
  border: 1px solid var(--stone); border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
  min-width: 300px; padding: var(--s3);
  list-style: none; margin: 0;

  opacity: 0;
  visibility: hidden;
  transform: translateY(-4px);
  transition: opacity .14s ease, transform .14s ease, visibility 0s linear .22s;
}
/* The bridge. Sits in the gap, invisible, and keeps the pointer inside. */
.submenu::before {
  content: "";
  position: absolute;
  left: 0; right: 0;
  top: calc(var(--s3) * -1 - 2px);
  height: calc(var(--s3) + 4px);
}
.menu-item.has-sub { padding-block: var(--s2); }

.menu-item:hover > .submenu,
.menu-item:focus-within > .submenu,
.menu-item.sub-open > .submenu {
  opacity: 1;
  visibility: visible;
  transform: none;
  transition: opacity .14s ease, transform .14s ease, visibility 0s;
}
@media (prefers-reduced-motion: reduce) {
  .submenu { transform: none; transition: visibility 0s linear .22s; }
  .menu-item:hover > .submenu,
  .menu-item:focus-within > .submenu,
  .menu-item.sub-open > .submenu { transition: visibility 0s; }
}
.submenu a { color: var(--spire); display: block; padding: var(--s2) var(--s3); border-radius: var(--radius-sm); }
.submenu a:hover { background: var(--stone-dim); color: var(--spire); }
.submenu-all { border-top: 1px solid var(--stone); margin-top: var(--s2); padding-top: var(--s2); }
.sub-toggle {
  background: none; border: 0; color: inherit; font: inherit; cursor: pointer;
  display: inline-flex; align-items: center; gap: var(--s1); padding: 0;
}

.header-actions { display: flex; align-items: center; gap: var(--s4); flex-wrap: wrap; }
.header-tel { font-family: var(--mono); font-weight: 600; color: var(--sun); text-decoration: none; font-size: .95rem; }
.menu-toggle { display: none; background: none; border: 1px solid rgba(255,246,232,.4); border-radius: var(--radius-sm); color: var(--first-light); padding: var(--s2) var(--s3); cursor: pointer; }

@media (max-width: 900px) {
  /* THE MOBILE HEADER IS ONE ROW.
   *
   * The nav sits between the brand and the actions in the DOM, so with plain
   * flex-wrap the open menu pushed itself BETWEEN them: brand on row one, menu
   * on row two, phone and burger stranded on row three. Explicit ordering keeps
   * brand and actions together on the first row and drops the panel underneath
   * where it belongs. */
  .site-header .container { flex-wrap: wrap; row-gap: 0; }
  .brand { order: 1; }
  .header-actions { order: 2; margin-left: auto; }
  .nav { order: 3; }

  .menu-toggle { display: inline-flex; }
  .nav { display: none; width: 100%; border-top: 1px solid rgba(240,162,2,.22); margin-top: var(--s3); padding-top: var(--s2); }
  .nav.open { display: block; }
  .brand__mark { width: 30px; height: 30px; }
  .brand strong { font-size: 1.02rem; }

  /* The header phone is REDUNDANT on mobile and it was the thing forcing the
   * header onto two rows: brand plus a full number plus a burger does not fit
   * inside 390px. The sticky bar at the bottom carries "Call us" on every page
   * and is now visible from first paint, so the number is one tap away either
   * way. Removing it here buys a single clean header row. */
  .header-tel { display: none; }
  .menu { flex-direction: column; align-items: stretch; gap: 0; }
  .menu > li { border-top: 1px solid rgba(255,246,232,.14); }
  .menu > li > a, .sub-toggle { display: block; padding: var(--s3) 0; width: 100%; text-align: left; }
  /* ===================== THE MOBILE MENU, PROPERLY =====================
   *
   * Three separate faults, all from desktop rules leaking down:
   *
   * 1. The panel was 950px tall inside an 844px viewport with `overflow:
   *    visible`, so the menu simply ran off the screen and there was nothing to
   *    scroll. It now has its own bounded, scrollable height.
   * 2. Scrolling inside it chained to the document behind, because the body was
   *    never locked and overscroll-behavior was `auto`.
   * 3. The submenu kept the DESKTOP dropdown skin: a First Light panel with a
   *    300px min-width, a border and a shadow, dropped inside a navy nav. That
   *    is what looked wrong on Services and Guides specifically, because they
   *    are the only two items that have one.
   */

  /* Bounded and scrollable, and the scroll stops here rather than moving the
     page underneath. */
  .nav {
    max-height: calc(100vh - 132px);
    max-height: calc(100dvh - 132px);
    overflow-y: auto;
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
    padding-bottom: var(--s5);
  }
  .nav::-webkit-scrollbar { width: 4px; }
  .nav::-webkit-scrollbar-thumb { background: rgba(255,246,232,.28); border-radius: 2px; }

  /* A nested list on the navy ground, not a floating white card. */
  .submenu {
    position: static;
    display: none;
    background: transparent;
    border: 0;
    border-left: 2px solid rgba(240,162,2,.35);
    border-radius: 0;
    box-shadow: none;
    min-width: 0;
    margin: 0 0 var(--s3) var(--s3);
    padding: 0 0 0 var(--s4);
    opacity: 1; visibility: visible; transform: none; transition: none;
  }
  .submenu::before { content: none; }
  .menu-item.sub-open > .submenu { display: block; }
  .menu-item:hover > .submenu { display: none; }
  .menu-item.sub-open:hover > .submenu { display: block; }

  .submenu a {
    color: #D9D2C6;
    padding: var(--s3) 0;
    font-size: .92rem;
    border-radius: 0;
  }
  .submenu a:hover, .submenu a:focus-visible { background: transparent; color: var(--sun); }
  .submenu-all { border-top: 1px solid rgba(240,162,2,.22); margin-top: var(--s2); color: var(--sun); }

  /* The row: link on the left, a 44px caret on the right. Both are real targets
     rather than a 12px glyph. */
  .menu > li.has-sub { display: grid; grid-template-columns: 1fr auto; align-items: center; }
  .menu > li.has-sub > a { grid-column: 1; }
  .menu > li.has-sub > .sub-toggle {
    grid-column: 2;
    width: 44px; height: 44px;
    display: grid; place-items: center;
    color: var(--sun);
    transition: transform .18s ease;
  }
  .menu > li.has-sub > .submenu { grid-column: 1 / -1; }
  .menu > li.sub-open > .sub-toggle { transform: rotate(180deg); }
  @media (prefers-reduced-motion: reduce) {
    .menu > li.has-sub > .sub-toggle { transition: none; }
  }
  .header-cta { display: none; }
}

/* ------------------------------------------------------------------ HEROES
   The homepage hero carries ONE broad low-opacity beam at roof pitch (~28deg),
   reading as light falling into a room. That plus the corner glow is the two
   ray elements this viewport is allowed. */
.hero {
  background: var(--spire-deep);
  color: var(--first-light);
  position: relative; overflow: hidden;
  padding-block: clamp(var(--s8), 9vw, var(--s10));
}
.hero::before {
  content: ""; position: absolute; inset: -20% -10% auto -10%; height: 150%;
  background: linear-gradient(118deg, rgba(240,162,2,0) 34%, rgba(240,162,2,.10) 46%, rgba(240,162,2,0) 58%);
  pointer-events: none;
}
.hero::after {
  content: ""; position: absolute; inset: 0;
  background: radial-gradient(90% 70% at 82% -8%, rgba(240,162,2,.20) 0%, rgba(240,162,2,0) 60%);
  pointer-events: none;
}
.hero__grid { position: relative; z-index: 1; display: grid; gap: var(--s8); align-items: center; grid-template-columns: 1.05fr .95fr; }
.hero h1 { font-size: var(--t-display); line-height: 1.05; color: var(--first-light); }
.hero__badge {
  display: inline-flex; align-items: center; gap: var(--s2);
  font-family: var(--mono); font-size: var(--t-eyebrow); letter-spacing: .14em; text-transform: uppercase;
  color: var(--sun); border: 1px solid var(--sun-soft); border-radius: 999px;
  padding: var(--s2) var(--s4); margin-bottom: var(--s5);
}
.hero__cta { display: flex; gap: var(--s4); flex-wrap: wrap; margin-top: var(--s6); }
.hero__trust { display: flex; gap: var(--s6); flex-wrap: wrap; margin-top: var(--s7); padding-top: var(--s6); border-top: 1px solid rgba(240,162,2,.2); }
.hero__media img { border-radius: var(--radius); box-shadow: var(--shadow-lg); aspect-ratio: 4/3; object-fit: cover; width: 100%; }

.page-hero {
  background:
    radial-gradient(100% 80% at 86% -12%, rgba(240,162,2,.17) 0%, rgba(240,162,2,0) 58%),
    var(--spire);
  color: var(--first-light);
  padding-block: clamp(var(--s7), 6vw, var(--s9));
}
.page-hero h1 { color: var(--first-light); }
.page-hero--tight { padding-block: var(--s7); }
.page-hero__copy { max-width: 68ch; }

/* The banner sits FLUSH to the bottom edge of the hero and is height-capped.
   Full-bleed at 16:9 it stood 768px tall on a 1366px viewport, pushing the
   first line of copy off the fold, and the hero's own bottom padding then left
   a dead navy strip underneath it. Negative margin closes the strip; the cap
   keeps the banner a band rather than a page. */
.page-hero { padding-bottom: 0; }
.page-hero__media {
  margin-top: clamp(var(--s6), 5vw, var(--s8));
  margin-bottom: 0;
  line-height: 0;
}
.page-hero__media img {
  width: 100%;
  height: clamp(220px, 26vw, 380px);
  object-fit: cover;
  display: block;
}

@media (max-width: 900px) {
  .hero__grid { grid-template-columns: 1fr; gap: var(--s6); }
}

/* -------------------------------------------------------------- BREADCRUMB */
.breadcrumbs { font-size: var(--t-small); margin-bottom: var(--s4); color: #C9C1B4; }
.breadcrumbs a { color: var(--first-light); text-decoration-color: var(--sun-soft); }
.breadcrumbs span[aria-current] { color: var(--sun); }

/* ------------------------------------------------------------------ GRIDS */
/* Tracks are auto-fit with an explicit minimum, NOT `repeat(N, 1fr)`.
 *
 * `1fr` is `minmax(auto, 1fr)`, and the `auto` minimum is min-content, so a
 * track REFUSES to go narrower than its contents. At 200% text zoom the
 * viewport is still 1366px but the cards need more, so four tracks expanded to
 * 345px each and pushed the document to 1828px wide. That is a WCAG 1.4.4
 * failure, and it was invisible at 100% zoom.
 *
 * An explicit minimum lets the track cap itself, and auto-fit then drops the
 * column count when the minimum no longer fits. The `min(Npx, 100%)` guard
 * stops the minimum itself overflowing a viewport narrower than N. */
.grid { display: grid; gap: var(--s5); }
/* The minimum has to be chosen against the container, or auto-fit quietly
 * changes the design: at a 1140px container with a 24px gap, a 300px minimum
 * fits THREE tracks, so `.grid-2` rendered as three columns and one orphan.
 *
 * For n tracks to be the maximum that fit:  n*min + (n-1)*gap <= 1140
 *                                     and  (n+1)*min + n*gap  >  1140
 *   2 columns -> min in (364, 558]   picked 420
 *   3 columns -> min in (267, 364]   picked 300
 *   4 columns -> min in (209, 267]   picked 240
 * Below those widths auto-fit drops a column, which is the responsive and the
 * text-zoom behaviour both. */
.grid-2 { grid-template-columns: repeat(auto-fit, minmax(min(420px, 100%), 1fr)); }
.grid-3 { grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr)); }
.grid-4 { grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr)); }

/* Long words in a narrow track must break rather than widen it. */
.hub-card, .svc, .pillar, .step, .stat, .pain, .cbody { overflow-wrap: break-word; }
.step { flex-wrap: wrap; }

.section-head { max-width: 70ch; margin-bottom: var(--s7); }

/* ------------------------------------------------------------------ CARDS */
.hub-card, .svc, .pillar, .step, .stat, .aside-card, .pain {
  background: var(--white);
  border: 1px solid var(--stone);
  border-radius: var(--radius);
  padding: var(--s5);
  box-shadow: var(--shadow);
  transition: transform .15s, box-shadow .15s;
}
.hub-card:hover, .svc:hover, .pillar:hover { transform: translateY(-2px); box-shadow: var(--shadow-lg); }
.section--paper2 .hub-card, .section--paper2 .svc, .section--paper2 .pillar { background: var(--first-light); }
/* Cards on dark bands, deeper scope. */
.section--slate .hub-card, .section--slate .svc, .section--slate .pillar,
.section--slate .stat, .section--slate .step,
.band-navy .hub-card, .band-navy .svc, .band-navy .pillar, .band-navy .stat {
  background: var(--spire-deep); border-color: var(--sun-soft); box-shadow: none; color: var(--first-light);
}
.section--slate .hub-card h3, .section--slate .svc h3, .section--slate .pillar h3,
.band-navy .hub-card h3, .band-navy .svc h3 { color: var(--first-light); }
.section--slate .hub-card p, .section--slate .svc p, .band-navy .hub-card p { color: #D6CEC2; }

.hub-cards { display: grid; gap: var(--s5); grid-template-columns: repeat(auto-fill, minmax(290px, 1fr)); }
.hub-card { display: flex; flex-direction: column; padding: 0; overflow: hidden; }
.hub-card img { aspect-ratio: 16/9; object-fit: cover; width: 100%; }
.cbody { padding: var(--s5); display: flex; flex-direction: column; gap: var(--s2); flex: 1; }
.cbody h3 { margin: 0; font-size: 1.15rem; }
.cbody p { margin: 0; color: var(--text-dim); font-size: .95rem; }
.cbody a { margin-top: auto; padding-top: var(--s3); font-weight: 600; font-size: .9rem; text-decoration: none; }
.cbody a::after { content: " \2192"; }

.svc__media img { aspect-ratio: 16/9; object-fit: cover; width: 100%; border-radius: var(--radius) var(--radius) 0 0; }
.svc__body { padding: var(--s5); display: flex; flex-direction: column; gap: var(--s2); flex: 1; }
.svc__body p { flex: 1; margin: 0; color: var(--text-dim); font-size: .95rem; }
/* Cards without a photograph stretch to the tallest sibling and leave a pool of
   empty space under the link. Pushing the link to the bottom turns that gap
   into deliberate spacing instead of a hole. */
.svc { display: flex; flex-direction: column; padding: 0; }
.svc__link { margin-top: auto; padding-top: var(--s3); }
.svc__link { font-weight: 600; text-decoration: none; }

.pillar__icon { width: 30px; height: 30px; color: var(--spire); margin-bottom: var(--s3); }
.section--slate .pillar__icon, .band-navy .pillar__icon { color: var(--sun); }

.step { display: flex; gap: var(--s4); align-items: flex-start; }
.step__n {
  flex: 0 0 auto; width: 34px; height: 34px; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--sun); color: var(--spire);
  font-family: var(--mono); font-weight: 700; font-size: .85rem;
}
.process-name { font-family: var(--display); font-size: 1.1rem; }

.stat { text-align: left; }
.stat__num { font-family: var(--display); font-size: clamp(2rem, 4vw, 2.75rem); line-height: 1; display: block; color: var(--spire); }
.section--slate .stat__num, .band-navy .stat__num { color: var(--sun); }
.stat__label { font-family: var(--mono); font-size: var(--t-eyebrow); letter-spacing: .14em; text-transform: uppercase; color: var(--text-dim); }
.section--slate .stat__label, .band-navy .stat__label { color: #C9C1B4; }

.chips { display: flex; flex-wrap: wrap; gap: var(--s2); list-style: none; padding: 0; margin: 0; }
.chip {
  font-size: var(--t-small); padding: var(--s1) var(--s3);
  border: 1px solid var(--stone); border-radius: 999px;
  background: var(--first-light); color: var(--spire); text-decoration: none;
}
.chip:hover { border-color: var(--sun); }
.section--slate .chip, .band-navy .chip { background: var(--spire-deep); border-color: var(--sun-soft); color: var(--first-light); }

.ticks, .pains { list-style: none; padding: 0; margin: 0 0 var(--s5); display: grid; gap: var(--s3); }
.ticks li { position: relative; padding-left: var(--s6); }
.ticks li::before {
  content: ""; position: absolute; left: 0; top: .62em;
  width: 14px; height: 2px; border-radius: 1px;
  background: linear-gradient(90deg, var(--sun), rgba(240,162,2,.35));
}
.ti { font-weight: 600; }

.trust-strip {
  padding-block: var(--s6);
  border-block: 1px solid var(--rule);
}
/* The flex belongs on the CONTAINER, not the section: the section is full-bleed
   and its only child is the container, so flexing the section does nothing and
   the items stack. The homepage hid this with an inline style; every other
   template showed it. */
.trust-strip .container {
  display: flex; flex-wrap: wrap;
  gap: var(--s4) var(--s7);
  align-items: center;
}
.trust-strip svg { display: inline-block; vertical-align: -.2em; margin-right: var(--s2); }
.ht { font-family: var(--mono); font-size: var(--t-small); letter-spacing: .08em; text-transform: uppercase; color: var(--text-dim); }
/* Dark grounds, one level deeper. Ink Dim on Night is 2.5:1 and the hero trust
   line was effectively invisible in the first capture. Same failure mode as the
   eyebrow, caught by the same gate. */
.hero .ht, .section--slate .ht, .band-navy .ht, .site-footer .ht { color: #C9C1B4; }

/* ------------------------------------------------------------ QUOTE PANEL
   Anchored, on-brand, never a floating grey box. Every quote CTA on the site
   anchors to #quote on the SAME page rather than navigating away. */
.quote-panel {
  background:
    radial-gradient(80% 90% at 100% 0%, rgba(240,162,2,.20) 0%, rgba(240,162,2,0) 62%),
    var(--spire);
  color: var(--first-light);
  border-radius: var(--radius);
  padding: clamp(var(--s6), 5vw, var(--s8));
  position: relative; overflow: hidden;
}
.quote-panel h2 { color: var(--first-light); }
.quote-panel label { display: block; font-size: var(--t-small); font-weight: 600; margin-bottom: var(--s1); color: var(--first-light); }
.quote-panel input, .quote-panel select, .quote-panel textarea {
  width: 100%; font: inherit; font-size: .95rem;
  padding: var(--s3) var(--s4);
  border: 1px solid rgba(255,246,232,.32); border-radius: var(--radius-sm);
  background: rgba(255,246,232,.06); color: var(--first-light);
  margin-bottom: var(--s4);
}
.quote-panel input::placeholder, .quote-panel textarea::placeholder { color: rgba(255,246,232,.55); }
.quote-panel input:focus-visible, .quote-panel select:focus-visible, .quote-panel textarea:focus-visible {
  outline: 3px solid var(--sun); outline-offset: 1px;
}

/* ------------------------------------------------------------------ PROSE */
.prose { max-width: var(--w-narrow); }
.prose > * + * { margin-top: var(--s5); }
.prose h2 { margin-top: var(--s8); }
.prose h3 { margin-top: var(--s7); }
.prose ul, .prose ol { padding-left: var(--s5); }
.prose li + li { margin-top: var(--s2); }
.prose blockquote {
  margin: var(--s7) 0; padding: var(--s5) var(--s6);
  border-left: 3px solid var(--sun);
  background: var(--stone-dim); border-radius: 0 var(--radius) var(--radius) 0;
  font-family: var(--display); font-size: 1.15rem; line-height: 1.5;
}
.read-intro { font-size: var(--t-lead); line-height: 1.6; color: var(--text-dim); }
.answer-lead {
  font-size: 1.15rem; line-height: 1.6;
  background: var(--stone-dim); border: 1px solid var(--stone);
  border-left: 4px solid var(--sun);
  border-radius: 0 var(--radius) var(--radius) 0;
  padding: var(--s5) var(--s6); margin-bottom: var(--s7);
  font-weight: 500; color: var(--ink);
}

/* Content images: block-level, centred, 16:9. NEVER floated. A floated image
   landing before a heading has nothing to wrap it and opens a dead gap. */
.content-figure, .sun-fig { margin: var(--s7) auto; max-width: 100%; }
.content-figure img, .sun-fig img {
  width: 100%; aspect-ratio: 16/9; object-fit: cover;
  border-radius: var(--radius); border: 1px solid var(--stone);
}
.content-figure figcaption, .sun-fig figcaption {
  font-size: var(--t-small); color: var(--text-dim); margin-top: var(--s3); text-align: center;
}

/* ----------------------------------------------------------------- TABLES */
.tablewrap { overflow-x: auto; border-radius: var(--radius); border: 1px solid var(--stone); margin: var(--s6) 0; }
.prose table, .tablewrap table { border-collapse: collapse; width: 100%; font-size: .95rem; }
.prose thead th, .tablewrap thead th {
  background: var(--spire); color: var(--first-light);
  font-family: var(--mono); font-size: var(--t-eyebrow); letter-spacing: .12em; text-transform: uppercase;
  text-align: left; padding: var(--s3) var(--s4); font-weight: 600;
}
.prose td, .prose th, .tablewrap td, .tablewrap th { padding: var(--s3) var(--s4); border-bottom: 1px solid var(--stone); }
.prose tbody tr:nth-child(even), .tablewrap tbody tr:nth-child(even) { background: var(--stone-dim); }
.prose tbody td:first-child, .tablewrap tbody td:first-child { font-family: var(--display); font-size: 1.02rem; }
.prose tbody tr:last-child td { border-bottom: 0; }

/* ==================================================== LONG-READ TREATMENTS
   No single reading device appears on more than one tier (§7). Applying one
   pattern site-wide is the template-rhythm defect at site level. */

/* 1. INDEX DOSSIER — homepage + service pages. Sticky contents rail. */
/* Column order follows DOM order. The body comes FIRST in the markup so the
   reading and tab order is content-then-sidebar, which means the WIDE column
   must be declared first. Declaring `250px 1fr` put the article in the 250px
   slot and the sidebar in 826px: the page rendered at 39,480px tall with the
   prose in a gutter. Caught by the height reading on the visual gate, which is
   why the gate reports document height at all. */
.service-layout { display: grid; grid-template-columns: minmax(0, 1fr) 260px; gap: var(--s8); align-items: start; }
/* Explicit placement, because DOM order is now contents-then-body while the
   visual order stays body-then-rail. */
.service-body  { grid-column: 1; grid-row: 1; }
.service-aside { grid-column: 2; grid-row: 1; }
@media (max-width: 980px) {
  /* Stacked, the contents list leading is the correct reading order too. */
  .service-body  { grid-column: 1; grid-row: 2; }
  .service-aside { grid-column: 1; grid-row: 1; }
}
.service-aside { position: sticky; top: 96px; display: grid; gap: var(--s4); }

/* The CONTENTS LIST scrolls, not the whole rail.
   Scrolling the rail meant a 34-section Authority page pushed the quote card
   below the fold of the sticky column, reachable only by scrolling inside the
   sidebar, which nobody does. Bounding the list instead keeps both the
   navigation and the conversion element visible at every scroll position. */
.aside-card ol { max-height: 44vh; overflow-y: auto; padding-right: var(--s2); }
.aside-card ol::-webkit-scrollbar { width: 5px; }
.aside-card ol::-webkit-scrollbar-thumb { background: var(--stone); border-radius: 3px; }
.aside-card { padding: var(--s5); }
.aside-card ol { list-style: none; counter-reset: dossier; padding: 0; margin: 0; font-size: .92rem; }
.aside-card ol li { counter-increment: dossier; margin-bottom: var(--s2); }
.aside-card ol a { display: block; padding-left: var(--s6); position: relative; text-decoration: none; color: var(--text-dim); }
.aside-card ol a::before {
  content: counter(dossier, decimal-leading-zero);
  position: absolute; left: 0; font-family: var(--mono); font-size: .72rem; color: var(--sun);
}
.aside-card ol a:hover, .aside-card ol a.is-active { color: var(--spire); font-weight: 600; }
.aside-tel { font-family: var(--mono); font-weight: 600; }
.aside-trust { font-size: var(--t-small); color: var(--text-dim); }
.service-body { min-width: 0; }
@media (max-width: 980px) {
  .service-layout { grid-template-columns: 1fr; gap: var(--s6); }
  .service-aside { position: static; }
}

/* 2. EDITORIAL CHAPTERS — area pages. No rail. Alternating bands, town kicker. */
/* The GENERIC chapter rhythm, for any editorial-chapters page that is not an
 * area page. Scoped with :not() because `.tr-chapters .chapter:nth-child(even)`
 * is (0,3,0) and silently outranked every rule the area variant defines,
 * including its dark interlude, which then rendered cream text on stone.
 *
 * nth-child was also the wrong selector once photograph plates became siblings
 * of the chapters: the "even" chapters were no longer the even children. */
.tr-chapters:not(.tr-chapters--area) .chapter { padding-block: var(--s8); }
.tr-chapters:not(.tr-chapters--area) .chapter:nth-child(even) { background: var(--stone-dim); }
.tr-chapters h2 { position: relative; }
.tr-chapters h2::before {
  content: attr(data-kicker);
  display: block;
  font-family: var(--mono); font-size: var(--t-eyebrow); letter-spacing: .16em; text-transform: uppercase;
  color: var(--spire); opacity: .62; margin-bottom: var(--s2);
}

/* 3. FEATURE FLOW — guides. Drop cap, centred chapter numerals on a ray. */
.tr-flow { max-width: var(--w-narrow); margin-inline: auto; }
.tr-flow > h2 {
  text-align: center; margin-top: var(--s9);
  padding-top: var(--s7);
  background-image:
    linear-gradient(90deg, transparent, var(--sun-soft) 22%, var(--sun-soft) 78%, transparent);
  background-size: 100% 1px; background-repeat: no-repeat; background-position: 0 var(--s5);
}
.tr-flow > h2::before {
  content: counter(flowchap, decimal-leading-zero);
  counter-increment: flowchap;
  display: block; font-family: var(--mono); font-size: .78rem; letter-spacing: .2em;
  color: var(--sun); margin-bottom: var(--s3);
}
.tr-flow { counter-reset: flowchap; }
.tr-flow > p:first-of-type::first-letter {
  float: left; font-family: var(--display); font-size: 3.4em; line-height: .82;
  padding: .06em .1em 0 0; color: var(--spire);
}

/* 4. DIRECT ANSWER — answer pages. The answer in a callout FIRST. */
.tr-answer { max-width: var(--w-narrow); margin-inline: auto; }
.tr-answer h2 { font-size: 1.35rem; margin-top: var(--s7); }

/* 5. DESIGNED ARTICLE — costs, about, quote, legal. Centred, kicker headings. */
.tr-article { max-width: var(--w-narrow); margin-inline: auto; }
.tr-article h2 {
  font-size: 1.5rem; margin-top: var(--s8); padding-top: var(--s5);
  border-top: 1px solid var(--stone);
}
.tr-article h2::before {
  content: ""; display: block; width: 22px; height: 2px;
  background: var(--sun); margin-bottom: var(--s3);
}

/* ----------------------------------------------------------- MOBILE CTA */
.mobile-cta { display: none; }
@media (max-width: 760px) {
  /* No entrance animation and no transform: the bar is simply there. */
  .mobile-cta {
    display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 200;
    transform: none !important;
    background: var(--spire); border-top: 1px solid var(--sun-soft);
    padding: var(--s2); gap: var(--s2);
  }
  .mc-call, .mc-quote {
    display: flex; align-items: center; justify-content: center; gap: var(--s2);
    padding: var(--s3); border-radius: var(--radius-sm);
    font-weight: 600; font-size: .92rem; text-decoration: none;
  }
  .mc-quote { background: var(--sun); color: var(--spire); }
  .mc-call { color: var(--first-light); border: 1px solid rgba(255,246,232,.4); }

  /* THE PADDING GOES ON THE FOOTER, NOT THE BODY.
   *
   * Clearing the sticky bar with `body { padding-bottom: 68px }` worked
   * mechanically and looked wrong: the body's background is First Light, so
   * those 68px rendered as a pale strip underneath the navy footer on every
   * page. Putting the space inside the footer means the footer's own dark ground
   * extends behind the bar and the seam disappears. */
  .site-footer { padding-bottom: calc(var(--s6) + 68px); }
}

/* ----------------------------------------------------------------- FOOTER */
.site-footer {
  background: var(--spire-deep);
  color: #D9D2C6;
  padding-block: var(--s8) var(--s6);
  /* NO margin-top.
   *
   * It carried 96px, and because the body ground is First Light that rendered as
   * a pale band sitting between the navy quote section and the navy footer, on
   * every page, at every width. Two dark bands separated by a cream stripe.
   *
   * A margin outside a full-bleed band shows the body through it, which is
   * almost never what is wanted. The section above already brings its own bottom
   * padding, so the space was never needed in the first place. */
}
.footer-grid { display: grid; grid-template-columns: 1.4fr 1fr 1fr 1fr; gap: var(--s7); }
@media (max-width: 900px) { .footer-grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 560px) { .footer-grid { grid-template-columns: 1fr; } }
.site-footer h3 { font-size: .95rem; font-family: var(--body); font-weight: 600; letter-spacing: .02em; }
.site-footer ul { list-style: none; padding: 0; margin: 0; display: grid; gap: var(--s2); font-size: .92rem; }
.site-footer a { color: #D9D2C6; text-decoration: none; }
.site-footer a:hover { color: var(--sun); text-decoration: underline; }
.footer-brand p { font-size: .92rem; max-width: 40ch; }
.site-footer .brand small { color: var(--sun); }
.footer-bottom {
  margin-top: var(--s7); padding-top: var(--s5);
  border-top: 1px solid rgba(240,162,2,.2);
  display: flex; flex-wrap: wrap; gap: var(--s4) var(--s6);
  justify-content: space-between; align-items: center;
  font-size: var(--t-small);
}
.footer-legal { display: flex; flex-wrap: wrap; gap: var(--s4); list-style: none; padding: 0; margin: 0; }
.footer-builtby { color: #8E97A3; }

/* --------------------------------------------------------------- MOTION
   THE GATE. Reveal-on-scroll hides an element until an observer un-hides it,
   which means a page whose JS never ran renders large blocks of invisible
   content. The full-page screenshot gate caught exactly that: three sections
   of the homepage captured completely blank, because elements far below the
   viewport never intersected anything.

   So the hidden state is applied ONLY under .js-motion, which a tiny inline
   script in the head adds after confirming motion is wanted and observers
   exist. No JS, no script, an error before that line, or reduced-motion: the
   content is simply visible. Withheld by choice, never by default. */
.js-motion [data-reveal] { opacity: 0; transform: translateY(12px); }
.js-motion [data-reveal].is-in { opacity: 1; transform: none; transition: opacity .4s ease-out, transform .4s ease-out; }

@media (prefers-reduced-motion: reduce) {
  /* Nothing is ever only reachable after an animation. */
  .js-motion [data-reveal], [data-reveal], [data-reveal].is-in { opacity: 1 !important; transform: none !important; transition: none !important; }
  *, *::before, *::after { animation-duration: .001ms !important; transition-duration: .001ms !important; }
}


/* ==================================================== SERVICE PAGE SECTIONS
   The service pages carry the homepage's section architecture. These are the
   few pieces that architecture needed and the homepage did not. */

/* The service hero is the homepage hero at slightly lower volume: the same
   split, the same beam, less vertical air, because a service page has to get
   to its content sooner than a front door does. */
.hero--service { padding-block: clamp(var(--s7), 6vw, var(--s9)); }
.hero--service h1 { font-size: var(--t-h1); }
.hero--service .breadcrumbs { margin-bottom: var(--s5); }

/* Proof list on the dark band. Each item leads with a bolded noun, so the
   marker sits against the first line rather than centred on the block. */
.ticks--proof { gap: var(--s5); }
.ticks--proof li { padding-left: var(--s7); }
.ticks--proof li strong { color: var(--first-light); }
.section--slate .ticks--proof li { color: #D9D2C6; }

/* Numbered option cards. The numeral is a LABEL, not a sequence: these are
   parallel choices rather than steps, so it is set quietly in the corner
   instead of in a filled circle like the process band uses. */
.pillar { position: relative; }
/* Spire Navy, NOT Sun Amber.
 *
 * This shipped as `color: var(--sun)` and measured 1.98:1 on First Light. It is
 * the exact failure STYLE-GUIDE §3 exists to prevent, written into the guide
 * and then walked into within the hour: Sun Amber is text ONLY on dark grounds,
 * and a numeral is text. On light it is navy; the amber returns as a rule
 * beneath it, where it is decoration rather than a glyph. */
.pillar__n {
  font-family: var(--mono);
  font-size: .72rem;
  letter-spacing: .14em;
  font-weight: 600;
  color: var(--spire);
  display: block;
  margin-bottom: var(--s3);
  padding-bottom: var(--s2);
  border-bottom: 2px solid var(--sun);
  width: 1.9em;
}
/* Dark grounds, scoped deeper so it outranks. */
.section--slate .pillar__n,
.band-navy .pillar__n { color: var(--sun); border-bottom-color: var(--sun-soft); }
.pillar h3 { margin-bottom: var(--s2); font-size: 1.08rem; }
.pillar p { color: var(--text-dim); font-size: .95rem; margin: 0; }
.section--paper2 .pillar p { color: var(--text-dim); }

/* An empty anchor target must not open a gap in the flow. */
#detail { display: block; height: 0; overflow: hidden; }


/* ======================================================= AREA PAGE SECTIONS */

.hero--area { padding-block: clamp(var(--s7), 6vw, var(--s9)); }
.hero--area h1 { font-size: var(--t-h1); }
.hero--area .breadcrumbs { margin-bottom: var(--s5); }

/* The coverage facts band. Two of these cells hold WORDS, not numbers, so the
   display size that suits "30" would set a town name at 44px and wrap it. The
   word variant drops to a readable size and keeps the row aligned. */
/* A facts strip, not a full section: four short cells do not need 96px of air
   above and below them. */
.area-facts { padding-block: clamp(var(--s6), 4vw, var(--s7)); }
.area-facts .stat { background: transparent; border: 0; box-shadow: none; padding: 0; }
.area-facts .stat__num { color: var(--sun); }
.area-facts__word {
  font-size: clamp(1.25rem, 2.2vw, 1.7rem);
  line-height: 1.15;
  text-wrap: balance;
}
.section--slate .area-facts .stat__label { color: #C9C1B4; }

/* Chapter rhythm. The bands alternate, so the first chapter must not repeat the
   ground of the section immediately above it. */
/* ---------------------------------------------- the feature opener
   A contents spread. The intro sits left, the numbered index right, so the
   reader gets a map of the page before committing to it. */
.area-opener__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(var(--s6), 5vw, var(--s9));
  align-items: start;
}
@media (max-width: 900px) { .area-opener__grid { grid-template-columns: 1fr; } }
.area-opener__intro .read-intro { margin-top: var(--s5); }

.area-index {
  list-style: none;
  margin: 0; padding: 0;
  counter-reset: none;
  column-count: 1;
  border-top: 1px solid var(--stone);
}
.area-index li { border-bottom: 1px solid var(--stone); }
.area-index a {
  display: grid;
  grid-template-columns: 2.6em 1fr;
  gap: var(--s3);
  align-items: baseline;
  padding: var(--s3) 0;
  text-decoration: none;
  color: var(--spire);
  transition: padding-left .15s, color .15s;
}
.area-index a:hover { padding-left: var(--s3); color: var(--ink); }
.area-index__n {
  font-family: var(--mono);
  font-size: .72rem;
  letter-spacing: .1em;
  color: var(--ink-dim);
}
.area-index a:hover .area-index__n { color: var(--sun-ink, #8A6300); }
.area-index__t { font-family: var(--display); font-size: 1.02rem; line-height: 1.35; }

/* ---------------------------------------------- chapter rhythm
   Three grounds in rotation and a full-bleed plate between groups, so a long
   town page reads as a paced feature rather than one continuous slab. */
.tr-chapters--area .chapter { padding-block: clamp(var(--s7), 6vw, var(--s8)); background: var(--first-light); }
.tr-chapters--area .chapter--stone { background: var(--stone-dim); }
/* THE INTERLUDE MUST SET ITS OWN GROUND.
 *
 * It carries `.section--slate` for the dark styling, but that selector is
 * (0,1,0) and `.tr-chapters--area .chapter` above is (0,2,0), so the light
 * ground won and the interlude rendered CREAM TEXT ON CREAM. The contrast gate
 * caught 17 failures on one page.
 *
 * This is the same invisible-text failure the sister build shipped four times,
 * arriving by a new route: not a token remap this time, but a background
 * override that outranked the band it was meant to sit on. The lesson holds
 * either way, and it is why STYLE-GUIDE §3 says overrides must OUTRANK rather
 * than merely exist. Never rely on a component class to win against a
 * container-scoped rule. */
.tr-chapters--area .chapter--interlude {
  padding-block: clamp(var(--s8), 7vw, var(--s9));
  background:
    radial-gradient(110% 80% at 86% -10%, rgba(240,162,2,.16) 0%, rgba(240,162,2,0) 58%),
    var(--spire);
  color: var(--first-light);
}

/* The chapter mark: numeral, ray, town. A fixed rail on desktop so every
   heading starts on the same vertical line and the numerals stack visibly
   down the page. */
.chapter__inner {
  display: grid;
  grid-template-columns: 132px minmax(0, 1fr);
  gap: clamp(var(--s5), 4vw, var(--s7));
  align-items: start;
}
@media (max-width: 900px) {
  .chapter__inner { grid-template-columns: 1fr; gap: var(--s4); }
}
.chapter__mark { position: sticky; top: 112px; display: grid; gap: var(--s3); justify-items: start; }
@media (max-width: 900px) {
  .chapter__mark { position: static; grid-auto-flow: column; align-items: center; justify-content: start; gap: var(--s3); }
}
.chapter__n {
  font-family: var(--display);
  font-size: clamp(2rem, 3.4vw, 2.9rem);
  line-height: 1;
  color: var(--spire);
  opacity: .28;
}
.chapter__rule { display: block; width: 46px; height: 2px; background: var(--sun); border-radius: 1px; }
@media (max-width: 900px) { .chapter__rule { width: 24px; } }
.chapter__town {
  font-family: var(--mono);
  font-size: .62rem;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--ink-dim);
}
.chapter__body { max-width: var(--w-narrow); }
.chapter__body > h2:first-child { margin-top: 0; }

/* Interlude, on the dark ground. The numeral inverts and gains presence: this
   is the beat in the rhythm, so it is allowed to be louder. */
.chapter--interlude .chapter__n { color: var(--first-light); opacity: .5; }
.chapter--interlude .chapter__town { color: #C9C1B4; }
.chapter--interlude .prose h2,
.chapter--interlude .prose h3 { color: var(--first-light); }
.chapter--interlude .prose p,
.chapter--interlude .prose li { color: #E4DCCF; }
.chapter--interlude .prose strong { color: var(--first-light); }
.chapter--interlude .prose a { color: var(--first-light); text-decoration-color: var(--sun); }
.chapter--interlude .prose blockquote { background: rgba(255,246,232,.06); border-left-color: var(--sun); color: var(--first-light); }
.chapter--interlude .prose thead th { background: var(--spire-deep); }
.chapter--interlude .prose td,
.chapter--interlude .prose th { border-bottom-color: rgba(255,246,232,.18); }
.chapter--interlude .prose tbody tr:nth-child(even) { background: rgba(255,246,232,.04); }
.chapter--interlude .tablewrap { border-color: rgba(255,246,232,.22); }

/* Full-bleed photograph between chapter groups. */
.area-plate { margin: 0; position: relative; }
.area-plate img { width: 100%; height: clamp(240px, 34vw, 460px); object-fit: cover; display: block; }
.area-plate figcaption {
  font-size: var(--t-small);
  color: var(--text-dim);
  padding-block: var(--s3);
  background: var(--first-light);
}

/* The callout the writers were told to use, given real presence on a town page.
   It is the one moment per chapter where a single sentence gets to be loud. */
.tr-chapters--area .callout,
.prose .callout {
  font-family: var(--display);
  font-size: clamp(1.15rem, 1.9vw, 1.45rem);
  line-height: 1.45;
  color: var(--spire);
  border-left: 3px solid var(--sun);
  padding: var(--s4) 0 var(--s4) var(--s5);
  margin: var(--s7) 0;
}
.chapter--interlude .callout { color: var(--first-light); }

#local { display: block; height: 0; overflow: hidden; }


/* ============================================ AREA READ: RAIL AND TREATMENTS */

.area-read { position: relative; }

/* The persistent chapter rail.
   Fixed to the viewport rather than sticky inside a column, because the
   chapters are full-bleed bands and cannot sit inside a constrained grid
   without losing the interlude and the photograph plates.

   It is ANCHOR LINKS. With no JavaScript it still renders and still works;
   the script only adds the active state and the progress fill. */
.area-rail {
  position: fixed;
  left: max(var(--s5), calc((100vw - var(--w-wide)) / 2 - 40px));
  top: 50%;
  transform: translateY(-50%);
  z-index: 40;
  opacity: 0;
  visibility: hidden;
  transition: opacity .25s ease;
  max-height: 70vh;
  display: grid;
  grid-template-columns: 3px 1fr;
  gap: var(--s4);

  /* THE RAIL CARRIES ITS OWN GROUND.
   *
   * It is position:fixed, so the chapter bands scroll behind it: cream, stone,
   * then the navy interlude. Ink Dim numerals sat at roughly 1.6:1 over the
   * interlude and effectively vanished.
   *
   * The automated contrast check CANNOT see this. It resolves an element's
   * background by walking its ancestors, and this element's ancestor is the
   * body, which is cream. It reported a comfortable pass on text that was
   * invisible for a fifth of the page. Fixed elements over scrolling grounds
   * have to be judged by eye, or given a ground of their own. This one is
   * given a ground of its own, which also makes it read as a control rather
   * than as stray numerals. */
  background: var(--first-light);
  border: 1px solid var(--stone);
  border-radius: var(--radius);
  padding: var(--s4) var(--s4) var(--s4) var(--s3);
  box-shadow: var(--shadow);
}
.area-rail.is-visible { opacity: 1; visibility: visible; }

.area-rail__progress {
  display: block;
  width: 3px;
  background: var(--stone);
  border-radius: 2px;
  position: relative;
  overflow: hidden;
}
.area-rail__progress i {
  position: absolute; inset: 0 0 auto 0;
  height: 0;
  background: var(--sun);
  border-radius: 2px;
  transition: height .12s linear;
}

.area-rail ol {
  list-style: none;
  margin: 0; padding: 0;
  display: grid;
  gap: 2px;
  overflow-y: auto;
  scrollbar-width: none;
}
.area-rail ol::-webkit-scrollbar { display: none; }

.area-rail a {
  display: grid;
  grid-template-columns: 2.2em minmax(0, 1fr);
  align-items: baseline;
  gap: var(--s2);
  padding: 5px 0;
  text-decoration: none;
  color: var(--ink-dim);
  font-size: .78rem;
  line-height: 1.3;
}
.area-rail__n { font-family: var(--mono); font-size: .62rem; letter-spacing: .08em; }
/* Titles stay hidden until the rail is hovered or a link is focused, so the
   rail is a quiet marker at rest and a full contents list on demand. */
.area-rail__t {
  max-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  opacity: 0;
  transition: max-width .22s ease, opacity .22s ease;
}
.area-rail:hover .area-rail__t,
.area-rail:focus-within .area-rail__t { max-width: 15rem; opacity: 1; }
.area-rail a:hover, .area-rail a:focus-visible { color: var(--spire); }
.area-rail a.is-current { color: var(--spire); font-weight: 600; }
.area-rail a.is-current .area-rail__n { color: var(--sun-ink, #8A6300); }

/* Below the point where a fixed rail would crowd the measure, it goes. The
   feature opener at the top of the page is the contents list on mobile. */
@media (max-width: 1500px) { .area-rail { display: none; } }
@media (prefers-reduced-motion: reduce) {
  .area-rail, .area-rail__t, .area-rail__progress i { transition: none; }
}

/* --------------------------------------------------- chapter treatments */

/* The opening sentence of every chapter, set to be readable on a skim. */
.chapter__body .standfirst {
  font-size: clamp(1.08rem, 1.5vw, 1.22rem);
  line-height: 1.55;
  color: var(--ink);
}
.chapter--interlude .chapter__body .standfirst { color: var(--first-light); }

/* SPEC PANEL. A chapter carrying a comparison table is reference material, so
   it breaks the measure and takes the full column width. Reading a table at
   68ch means scrolling it sideways for no reason. */
.chapter--panel .chapter__body { max-width: none; }
.chapter--panel .chapter__body > p,
.chapter--panel .chapter__body > h2,
.chapter--panel .chapter__body > h3,
.chapter--panel .chapter__body > ul { max-width: var(--w-narrow); }
.chapter--panel .tablewrap { margin-block: var(--s7); box-shadow: var(--shadow); }
.chapter--panel .tablewrap table { font-size: .98rem; }

/* CARD GRID. A chapter whose substance is a list gets that list as cards
   rather than as bullets, because a list of four coverings with a sentence each
   is comparison material, not prose. */
.chapter--cards .chapter__body { max-width: none; }
.chapter--cards .chapter__body > p,
.chapter--cards .chapter__body > h2,
.chapter--cards .chapter__body > h3 { max-width: var(--w-narrow); }
.chapter--cards .chapter__body > ul {
  list-style: none;
  padding: 0;
  margin: var(--s6) 0;
  display: grid;
  gap: var(--s4);
  grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr));
}
.chapter--cards .chapter__body > ul > li {
  background: var(--white);
  border: 1px solid var(--stone);
  border-radius: var(--radius);
  padding: var(--s5);
  margin: 0;
  font-size: .95rem;
  line-height: 1.6;
  color: var(--text-dim);
}
.chapter--cards .chapter__body > ul > li strong {
  display: block;
  color: var(--spire);
  font-family: var(--display);
  font-weight: 500;
  font-size: 1.05rem;
  margin-bottom: var(--s2);
}
.chapter--stone.chapter--cards .chapter__body > ul > li { background: var(--first-light); }
.chapter--interlude.chapter--cards .chapter__body > ul > li {
  background: var(--spire-deep);
  border-color: var(--sun-soft);
  color: #D6CEC2;
}
.chapter--interlude.chapter--cards .chapter__body > ul > li strong { color: var(--first-light); }


/* ============================================ GUIDE (feature-flow) TREATMENT
   Centred, single column, chapter numerals sitting ON a ray. Distinct from the
   Authority dossier rail and from the Area page's fixed left rail, per §7. */

.hero--guide {
  background:
    radial-gradient(90% 70% at 50% -20%, rgba(240,162,2,.15) 0%, rgba(240,162,2,0) 60%),
    var(--spire);
  padding-block: clamp(var(--s7), 6vw, var(--s9));
  text-align: center;
}
.hero--guide__inner { max-width: 72ch; margin-inline: auto; }
.hero--guide h1 { font-size: var(--t-h1); }
.hero--guide .lead { margin-inline: auto; }
.hero--guide .breadcrumbs { justify-content: center; display: flex; gap: var(--s2); flex-wrap: wrap; }
.hero--guide .eyebrow { color: var(--sun); }
.hero--guide__meta {
  display: flex; gap: var(--s5); justify-content: center; flex-wrap: wrap;
  margin: var(--s6) 0 0;
  padding-top: var(--s5);
  border-top: 1px solid rgba(240,162,2,.22);
}

/* Reading progress, pinned under the sticky header. A guide is read straight
   through, so the useful signal is how much is left, not where to jump. */
.feature { position: relative; }
.feature__progress {
  position: fixed;
  left: 0; right: 0; top: 0;
  height: 3px;
  background: transparent;
  z-index: 90;
  pointer-events: none;
  opacity: 0;
  transition: opacity .2s ease;
}
.feature__progress.is-visible { opacity: 1; }
.feature__progress i {
  display: block; height: 100%; width: 0;
  background: var(--sun);
  transition: width .1s linear;
}
body.admin-bar .feature__progress { top: 32px; }
@media (prefers-reduced-motion: reduce) {
  .feature__progress, .feature__progress i { transition: none; }
}

.feature__open { padding-block: clamp(var(--s7), 5vw, var(--s8)) var(--s6); }
.feature__lede { margin-inline: auto; }
.feature__lede p:first-of-type::first-letter {
  float: left;
  font-family: var(--display);
  font-size: 3.6em;
  line-height: .82;
  padding: .06em .1em 0 0;
  color: var(--spire);
}

.fchapter { padding-block: clamp(var(--s6), 4vw, var(--s7)); }
.fchapter--prose:nth-of-type(even) { background: var(--stone-dim); }

/* The numeral centred ON a ray: two hairlines running out from a mono numeral.
   This is the guide tier's signature and appears nowhere else. */
.fchapter__mark {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: var(--s4);
  max-width: var(--w-narrow);
  margin: 0 auto var(--s6);
}
.fchapter__ray {
  display: block; height: 1px;
  background: linear-gradient(90deg, rgba(240,162,2,0), rgba(240,162,2,.55));
}
.fchapter__mark .fchapter__ray:last-child {
  background: linear-gradient(90deg, rgba(240,162,2,.55), rgba(240,162,2,0));
}
.fchapter__n {
  font-family: var(--mono);
  font-size: .74rem;
  letter-spacing: .2em;
  color: var(--spire);
  font-weight: 600;
}
.fchapter__body { max-width: var(--w-narrow); margin-inline: auto; }
.fchapter__body > h2 { margin-top: 0; text-align: center; text-wrap: balance; }

/* Content-driven width, same rule as the area pages, different surroundings. */
.fchapter--panel .fchapter__body,
.fchapter--cards .fchapter__body { max-width: var(--w-standard); }
.fchapter--panel .fchapter__body > p,
.fchapter--panel .fchapter__body > h3,
.fchapter--cards .fchapter__body > p,
.fchapter--cards .fchapter__body > h3 { max-width: var(--w-narrow); margin-inline: auto; }
.fchapter--cards .fchapter__body > ul {
  list-style: none; padding: 0; margin: var(--s6) 0;
  display: grid; gap: var(--s4);
  grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr));
}
.fchapter--cards .fchapter__body > ul > li {
  background: var(--white); border: 1px solid var(--stone);
  border-radius: var(--radius); padding: var(--s5); margin: 0;
  font-size: .95rem; line-height: 1.6; color: var(--text-dim);
}
.fchapter--cards .fchapter__body > ul > li strong {
  display: block; color: var(--spire); font-family: var(--display);
  font-weight: 500; font-size: 1.05rem; margin-bottom: var(--s2);
}
.fchapter__body .standfirst {
  font-size: clamp(1.06rem, 1.4vw, 1.18rem);
  line-height: 1.6;
  color: var(--ink);
}

/* ================================================== ANSWER (direct-answer)
   Answer first, then short sections. No rail, no numerals, no progress: a
   1,000-word page does not need navigating, and giving it a contents device is
   how one reading treatment ends up on every tier. */

.hero--answer {
  background:
    radial-gradient(90% 80% at 78% -15%, rgba(240,162,2,.16) 0%, rgba(240,162,2,0) 58%),
    var(--spire);
  padding-block: clamp(var(--s6), 5vw, var(--s8));
}
.hero--answer__inner { max-width: 24ch; max-width: 68ch; }
.hero--answer h1 { font-size: clamp(1.9rem, 3.6vw, 2.7rem); }
.hero--answer .eyebrow { color: var(--sun); }

.answer-top { padding-block: clamp(var(--s7), 5vw, var(--s8)); }
.answer-top__inner {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 320px;
  gap: clamp(var(--s6), 4vw, var(--s8));
  align-items: start;
}
@media (max-width: 980px) { .answer-top__inner { grid-template-columns: 1fr; } }

.answer-lead {
  margin: 0;
  font-size: clamp(1.12rem, 1.7vw, 1.3rem);
}
.answer-lead__tag {
  display: block;
  font-family: var(--mono);
  font-size: var(--t-eyebrow);
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--spire);
  margin-bottom: var(--s3);
  font-weight: 600;
}
.answer-lead p { margin: 0; }

.answer-aside {
  background:
    radial-gradient(80% 90% at 100% 0%, rgba(240,162,2,.18) 0%, rgba(240,162,2,0) 62%),
    var(--spire);
  color: var(--first-light);
  border-radius: var(--radius);
  padding: var(--s6);
  position: sticky;
  top: 104px;
}
.answer-aside h2 { color: var(--first-light); font-size: 1.25rem; margin-bottom: var(--s3); }
.answer-aside p { color: #D9D2C6; font-size: .95rem; }
.answer-aside__facts {
  list-style: none; padding: 0; margin: var(--s5) 0 0;
  display: grid; gap: var(--s2);
  font-size: .88rem; color: #D9D2C6;
  border-top: 1px solid rgba(240,162,2,.22);
  padding-top: var(--s4);
}

.asection { padding-block: clamp(var(--s6), 4vw, var(--s7)); }
.asection--tint { background: var(--stone-dim); }
.asection .tr-answer > h2 { margin-top: 0; font-size: 1.35rem; }
.asection--panel .tr-answer,
.asection--cards .tr-answer { max-width: var(--w-standard); }
.asection--cards .tr-answer > ul {
  list-style: none; padding: 0; margin: var(--s5) 0;
  display: grid; gap: var(--s4);
  grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr));
}
.asection--cards .tr-answer > ul > li {
  background: var(--white); border: 1px solid var(--stone);
  border-radius: var(--radius); padding: var(--s4); margin: 0;
  font-size: .93rem; color: var(--text-dim);
}
.asection--tint.asection--cards .tr-answer > ul > li { background: var(--first-light); }

/* Related questions, on the dark band. A two-column list of real questions is
   more useful at the foot of an answer than another CTA. */
.qlist {
  list-style: none; padding: 0; margin: 0;
  display: grid; gap: 0;
  grid-template-columns: repeat(auto-fit, minmax(min(340px, 100%), 1fr));
  column-gap: var(--s7);
}
.qlist li { border-top: 1px solid rgba(240,162,2,.22); }
.qlist a {
  display: block;
  padding: var(--s4) 0;
  color: var(--first-light);
  text-decoration: none;
  font-family: var(--display);
  font-size: 1.05rem;
  line-height: 1.35;
  transition: padding-left .15s ease, color .15s ease;
}
.qlist a:hover { padding-left: var(--s3); color: var(--sun); }
.qlist a::after { content: " 92"; color: var(--sun); }


/* ================================================================ DIAGRAMS
   Inline SVG drawn to the numbers the copy quotes. Colours come from the
   palette tokens, so a diagram cannot drift away from the brand and cannot
   go invisible on a band it was not designed for. */

.diagram { margin: var(--s7) 0; }
.diagram__frame {
  background: var(--white);
  border: 1px solid var(--stone);
  border-radius: var(--radius);
  padding: clamp(var(--s5), 3vw, var(--s7));
  overflow-x: auto;
}
.section--paper2 .diagram__frame { background: var(--first-light); }
.diagram figcaption {
  font-size: var(--t-small);
  color: var(--text-dim);
  margin-top: var(--s4);
  max-width: 76ch;
}
.diagram .dg { width: 100%; height: auto; display: block; min-width: 460px; }

.dg-deck        { fill: var(--stone); }
.dg-struct      { fill: #E3DED5; }
.dg-kerb        { fill: var(--spire); opacity: .9; }
.dg-frame       { fill: var(--spire); }
.dg-frame-line  { stroke: var(--spire); stroke-width: 2.4; fill: none; stroke-linejoin: round; }
.dg-glass       { fill: #BBD6E6; }
.dg-glass-fill  { fill: #BBD6E6; opacity: .55; }
.dg-mem         { stroke: var(--ink); stroke-width: 2.6; fill: none; stroke-linejoin: round; }
.dg-bar         { stroke: var(--spire); stroke-width: 1.6; }
.dg-slope       { stroke: var(--spire); stroke-width: 2.6; fill: none; }
.dg-ground      { stroke: var(--ink-dim); stroke-width: 1.4; stroke-dasharray: 5 4; }
.dg-arc         { fill: rgba(240,162,2,.20); }
.dg-arc-line    { stroke: var(--sun); stroke-width: 1.4; fill: none; stroke-dasharray: 4 5; }
.dg-dim         { stroke: var(--ink-dim); stroke-width: 1.2; }
.dg-tick        { stroke: var(--ink-dim); stroke-width: 1.4; fill: none; }
.dg-flow        { stroke: var(--sun); stroke-width: 2.2; fill: none; stroke-linecap: round; }
.dg-beam-hard   { fill: rgba(240,162,2,.26); }
.dg-beam-soft   { fill: rgba(240,162,2,.18); }
/* Diagram text is real text, so it scales with the page and is selectable. */
.dg-label { font-family: var(--body); font-size: 13px; font-weight: 600; fill: var(--spire); }
.dg-note  { font-family: var(--body); font-size: 11.5px; fill: var(--ink-dim); }

/* ==================================================================== TOOLS */

.tool-band { background: var(--stone-dim); }
.tool {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 380px);
  gap: clamp(var(--s5), 3vw, var(--s7));
  align-items: start;
  background: var(--first-light);
  border: 1px solid var(--stone);
  border-radius: var(--radius);
  padding: clamp(var(--s5), 3vw, var(--s7));
  box-shadow: var(--shadow);
}
@media (max-width: 900px) { .tool { grid-template-columns: 1fr; } }

.tool__controls {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--s4) var(--s5);
}
@media (max-width: 560px) { .tool__controls { grid-template-columns: 1fr; } }
.tool__field--wide { grid-column: 1 / -1; }
.tool__field label {
  display: block;
  font-family: var(--mono);
  font-size: var(--t-eyebrow);
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--spire);
  font-weight: 600;
  margin-bottom: var(--s2);
}
.tool__input { position: relative; display: flex; align-items: center; }
.tool__unit {
  position: absolute; right: var(--s4);
  font-family: var(--mono); font-size: .8rem; color: var(--ink-dim);
  pointer-events: none;
}
.tool input[type="number"], .tool select {
  width: 100%;
  font: inherit;
  font-size: 1rem;
  padding: var(--s3) var(--s4);
  border: 1.5px solid var(--stone);
  border-radius: var(--radius-sm);
  background: var(--white);
  color: var(--ink);
}
.tool select {
  appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, var(--spire) 50%), linear-gradient(135deg, var(--spire) 50%, transparent 50%);
  background-position: calc(100% - 18px) 55%, calc(100% - 13px) 55%;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  padding-right: var(--s7);
}
.tool input:focus-visible, .tool select:focus-visible { outline: 3px solid var(--spire); outline-offset: 1px; }

.tool__out {
  display: block;
  background:
    radial-gradient(80% 90% at 100% 0%, rgba(240,162,2,.20) 0%, rgba(240,162,2,0) 62%),
    var(--spire);
  color: var(--first-light);
  border-radius: var(--radius);
  padding: var(--s6);
}
.tool__label {
  display: block;
  font-family: var(--mono); font-size: var(--t-eyebrow);
  letter-spacing: .16em; text-transform: uppercase;
  color: var(--sun); margin-bottom: var(--s2);
}
.tool__figure {
  display: block;
  font-family: var(--display);
  font-size: clamp(1.7rem, 3vw, 2.3rem);
  line-height: 1.1;
  color: var(--first-light);
}
.tool__sub { display: block; font-size: var(--t-small); color: #C9C1B4; margin-top: var(--s2); }
.tool__rows { margin: var(--s5) 0 0; display: grid; gap: var(--s4); }
.tool__rows > div { display: grid; gap: var(--s1); border-top: 1px solid rgba(240,162,2,.22); padding-top: var(--s3); }
.tool__rows dt {
  font-family: var(--mono); font-size: var(--t-eyebrow);
  letter-spacing: .14em; text-transform: uppercase; color: var(--sun);
}
.tool__rows dd { margin: 0; font-size: .93rem; line-height: 1.55; color: #E4DCCF; }
.tool__cta { display: flex; gap: var(--s3); flex-wrap: wrap; margin: var(--s6) 0 0; }

/* The written rule, always present. The tool upgrades it, never replaces it. */
.tool__fallback { margin-top: var(--s5); }
.tool__fallback summary {
  cursor: pointer;
  font-family: var(--mono); font-size: var(--t-small);
  letter-spacing: .06em; color: var(--spire);
  padding: var(--s3) 0;
}
.tool__fallback summary:focus-visible { outline: 3px solid var(--spire); outline-offset: 2px; }
/* With JS running the tool is the primary path, so the written rule collapses.
   Without it, the details element stays open and carries the guidance. */
.tool.is-live ~ .tool__fallback[open] { }

/* Feature table: reference material given presence. */
.tablewrap--feature { box-shadow: var(--shadow); background: var(--white); }
.tablewrap--feature .t-low  { color: #1F6B45; font-weight: 600; }
.tablewrap--feature .t-mid  { color: #8A6300; font-weight: 600; }
.tablewrap--feature .t-high { color: #A3401B; font-weight: 600; }

.feature__diagram { padding-block: 0 var(--s7); }


/* Dark surfaces opt in, rather than being enumerated. */
.on-dark { color: var(--first-light); }
.on-dark h2, .on-dark h3, .on-dark strong { color: var(--first-light); }
.on-dark p, .on-dark li, .on-dark dd { color: #E4DCCF; }
.on-dark a:not(.btn) { color: var(--first-light); }
.on-dark :focus-visible { outline-color: var(--white); }


/* ==================================================================== CONTACT
   Form first, two columns, direct-contact card beside it. Lean by design: a
   contact page is a utility. */
.contact {
  display: grid;
  grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
  gap: clamp(var(--s6), 4vw, var(--s8));
  align-items: start;
}
@media (max-width: 900px) { .contact { grid-template-columns: 1fr; } }
.contact__form h2 { margin-bottom: var(--s3); }
.contact__panel {
  margin-top: var(--s5);
  background: var(--white);
  border: 1px solid var(--stone);
  border-radius: var(--radius);
  padding: clamp(var(--s4), 2.5vw, var(--s6));
  box-shadow: var(--shadow);
  min-height: 120px;
}
.contact__card {
  background:
    radial-gradient(80% 90% at 100% 0%, rgba(240,162,2,.18) 0%, rgba(240,162,2,0) 62%),
    var(--spire);
  border-radius: var(--radius);
  padding: clamp(var(--s5), 3vw, var(--s6));
  position: sticky;
  top: 104px;
}
.contact__card h2 { font-size: 1.25rem; margin-bottom: var(--s5); }
.contact__line { display: grid; gap: var(--s1); margin: 0 0 var(--s5); }
.contact__line .eyebrow { margin: 0; color: var(--sun); }
.contact__val { font-size: .98rem; line-height: 1.5; }
a.contact__val { color: var(--first-light); text-decoration-color: var(--sun); word-break: break-word; }
.contact__note {
  font-size: var(--t-small);
  color: #C9C1B4;
  border-top: 1px solid rgba(240,162,2,.22);
  padding-top: var(--s4);
  margin: 0;
}

/* ===================================================================== COSTS
   The quote checker. A checklist with a running total, which is what somebody
   holding three quotations actually needs. */
.checker {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 360px);
  gap: clamp(var(--s6), 4vw, var(--s7));
  align-items: start;
}
@media (max-width: 980px) { .checker { grid-template-columns: 1fr; } }

.checker__list { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--stone); }
.checker__list li { border-bottom: 1px solid var(--stone); }
.checker__list label {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--s1);
  padding: var(--s4) var(--s4) var(--s4) var(--s8);
  cursor: pointer;
  position: relative;
}
.checker__t { font-family: var(--display); font-size: 1.06rem; color: var(--spire); }
.checker__d { font-size: .92rem; line-height: 1.55; color: var(--text-dim); }

/* The native box is moved off-screen rather than hidden, so it stays focusable
   and announced; the drawn box is the visible control. */
.checker__box {
  position: absolute;
  opacity: 0;
  width: 22px; height: 22px;
  margin: 0;
}
.checker__list label::before {
  content: "";
  position: absolute;
  left: var(--s4); top: calc(var(--s4) + 2px);
  width: 22px; height: 22px;
  border: 1.5px solid var(--stone);
  border-radius: 5px;
  background: var(--white);
  transition: background-color .12s, border-color .12s;
}
.checker__list label::after {
  content: "";
  position: absolute;
  left: calc(var(--s4) + 6px); top: calc(var(--s4) + 9px);
  width: 10px; height: 5px;
  border-left: 2.5px solid var(--spire);
  border-bottom: 2.5px solid var(--spire);
  transform: rotate(-45deg) scale(0);
  transition: transform .12s;
}
.checker__box:checked + label::before { background: var(--sun); border-color: var(--sun); }
.checker__box:checked + label::after { transform: rotate(-45deg) scale(1); }
.checker__box:focus-visible + label::before { outline: 3px solid var(--spire); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) {
  .checker__list label::before, .checker__list label::after { transition: none; }
}

.checker__out {
  background:
    radial-gradient(80% 90% at 100% 0%, rgba(240,162,2,.20) 0%, rgba(240,162,2,0) 62%),
    var(--spire);
  border-radius: var(--radius);
  padding: var(--s6);
  position: sticky;
  top: 104px;
  display: block;
}
.checker__verdict { font-size: .95rem; line-height: 1.6; margin: var(--s4) 0 0; }


/* ========================================================== WHATSAPP WIDGET
   The house pattern from the delivered sister build: a labelled pill that opens
   a short form and composes a pre-filled message. Ported rather than invented,
   because a plain icon link is what I built first and it did not match the
   other skylight sites.

   WhatsApp green is kept deliberately. It is the recognition cue, and swapping
   it for brand colour would cost the affordance for no gain. */

.wa { position: fixed; right: var(--s4); bottom: var(--s4); z-index: 120; }
.wa__fab {
  display: flex; align-items: center; gap: var(--s2);
  background: #25D366; color: #0B1F14; border: 0;
  border-radius: 999px; padding: 12px 18px;
  font-family: var(--body); font-weight: 700; font-size: 14px;
  cursor: pointer; box-shadow: 0 8px 24px rgba(0,0,0,.28);
}
.wa__fab:hover { background: #1FBE5A; }
.wa__fab:focus-visible { outline: 3px solid var(--spire); outline-offset: 3px; }

.wa__panel {
  position: absolute; right: 0; bottom: 60px;
  width: min(320px, calc(100vw - var(--s6)));
  max-height: min(70vh, 560px); overflow-y: auto;
  background: var(--first-light);
  border: 1px solid var(--stone);
  border-top: 3px solid #25D366;
  border-radius: var(--radius);
  padding: var(--s5);
  box-shadow: 0 18px 44px rgba(23,41,63,.28);
}
.wa__head {
  display: flex; justify-content: space-between; align-items: center;
  margin-bottom: var(--s3); font-family: var(--display); color: var(--spire);
}
.wa__close {
  background: transparent; border: 0; font-size: 22px; line-height: 1;
  cursor: pointer; color: var(--ink-dim); padding: 0 var(--s1);
}
.wa__close:focus-visible { outline: 3px solid var(--spire); outline-offset: 2px; }
.wa__intro { font-size: 13px; color: var(--text-dim); margin-bottom: var(--s4); }
.wa label {
  display: block; font-family: var(--mono);
  font-size: .66rem; letter-spacing: .1em; text-transform: uppercase;
  font-weight: 600; color: var(--spire); margin-bottom: var(--s3);
}
.wa input, .wa textarea, .wa select {
  width: 100%; margin-top: 4px; padding: 9px 10px;
  border: 1px solid var(--stone); border-radius: var(--radius-sm);
  font-family: var(--body); font-size: 14px;
  color: var(--ink); background: var(--white);
  text-transform: none; letter-spacing: normal;
}
.wa input:focus-visible, .wa textarea:focus-visible, .wa select:focus-visible {
  outline: 3px solid var(--spire); outline-offset: 1px;
}
.wa__row { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s3); }
.btn--wa { background: #25D366; color: #0B1F14; width: 100%; justify-content: center; border-color: #25D366; }
.btn--wa:hover { background: #1FBE5A; color: #0B1F14; border-color: #1FBE5A; }

/* Clear of the sticky mobile CTA bar, and the label folds away on the narrowest
   screens so the pill cannot crowd the bar beneath it. */
@media (max-width: 760px) {
  .wa { bottom: calc(68px + var(--s3)); right: var(--s3); }
}
/* THE LABEL STAYS AT EVERY SIZE.
 *
 * It was clipped below 420px, so on a 390px phone the widget rendered as a bare
 * green circle: identical to the plain icon link that was rejected in the first
 * place, and indistinguishable from "no WhatsApp feature". The label IS the
 * feature. The sister build shows it at all widths and so does this one; the pill
 * is tightened instead so it still clears the sticky bar. */
@media (max-width: 420px) {
  .wa__fab { padding: 10px 14px; font-size: 13px; }
  .wa__fab svg { width: 20px; height: 20px; }
}


/* ================================================ MOTION, WITHOUT A LIBRARY
   Both animations previously ran through anime.js, 17KB for a clip-path tween
   and a staggered rise. As keyframes they run on the compositor, cost no parse
   time and no main-thread work, and look the same.

   Every start state is scoped to .js-motion, so a page whose JavaScript never
   runs renders the finished state instead of a blank one. */

@keyframes sun-ray-draw {
  from { clip-path: inset(0 100% 0 0); }
  to   { clip-path: inset(0 0 0 0); }
}
/* THE CLIP GOES ON THE LINES, NOT ON THE HOST.
 *
 * Clipping `.ray` itself to zero width made it unobservable: an element clipped
 * to zero area has zero intersection, so the IntersectionObserver whose job is
 * to reveal it could never see it and the signature animation silently never
 * played. Reveals fired 14/14 on the same page with identical observer options;
 * rays fired 0/1. The element was hidden in a way that hid it from the thing
 * meant to un-hide it.
 *
 * The host now stays unclipped and observable. The three hairlines carry the
 * clip and the animation, which is where the effect actually lives. */
.js-motion .ray::before,
.js-motion .ray > i,
.js-motion .ray::after { clip-path: inset(0 100% 0 0); }

.js-motion .ray.is-drawn::before,
.js-motion .ray.is-drawn > i,
.js-motion .ray.is-drawn::after {
  animation: sun-ray-draw 600ms cubic-bezier(.22, 1, .36, 1) forwards;
}

@keyframes sun-hero-rise {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}
/* The four hero elements, in the order a reader meets them. Delays reproduce
   the 70ms stagger the library was configured with. */
.js-motion .hero__copy.is-entering .hero__badge,
.js-motion .hero__copy.is-entering h1,
.js-motion .hero__copy.is-entering .lead,
.js-motion .hero__copy.is-entering .hero__cta {
  animation: sun-hero-rise 420ms cubic-bezier(.33, 1, .68, 1) both;
}
.js-motion .hero__copy.is-entering .hero__badge { animation-delay: 0ms; }
.js-motion .hero__copy.is-entering h1           { animation-delay: 70ms; }
.js-motion .hero__copy.is-entering .lead        { animation-delay: 140ms; }
.js-motion .hero__copy.is-entering .hero__cta   { animation-delay: 210ms; }

@media (prefers-reduced-motion: reduce) {
  .js-motion .ray::before, .js-motion .ray > i, .js-motion .ray::after,
  .ray::before, .ray > i, .ray::after { clip-path: none !important; animation: none !important; }
  .js-motion .hero__copy.is-entering * { animation: none !important; opacity: 1 !important; transform: none !important; }
}


/* ===================================================== TABLES ON SMALL SCREENS
   `.tablewrap` has always had `overflow-x: auto`, but `table { width: 100% }`
   meant the table compressed to fit rather than scrolling. On a 390px screen a
   three-column comparison squeezed its explanation column to about 14
   characters and wrapped it to twelve lines, turning each row into a wall.

   Giving the table a minimum width lets the wrapper do the job it was already
   set up for. Desktop is untouched: the minimum is below the container width,
   so the table still fills its column exactly as before. */
@media (max-width: 700px) {
  /* A scroll container only scrolls if its ANCESTORS are allowed to be narrower
   * than its content. Grid and flex children default to `min-width: auto`, which
   * is min-content, so the 34rem table propagated straight up the tree and
   * pushed an area page to 558px inside a 390px viewport. The visual gate caught
   * it; it is the same `min-width: auto` trap as the card grids.
   *
   * min-width: 0 on the chain, and the wrapper capped to the viewport, so the
   * overflow terminates at the wrapper where it belongs. */
  .chapter__inner, .chapter__inner > *,
  .prose, .chapter__body, .fchapter__body,
  .tr-answer, .tr-flow, .tr-article, .service-body { min-width: 0; }
  .tablewrap { max-width: 100%; }

  .tablewrap table,
  .prose table { min-width: 34rem; width: auto; }
  .tablewrap { -webkit-overflow-scrolling: touch; }
  /* A visible edge, so it reads as scrollable rather than as clipped. */
  .tablewrap { background:
      linear-gradient(to right, var(--white) 30%, rgba(255,255,255,0)) left center / 28px 100% no-repeat,
      linear-gradient(to left, var(--white) 30%, rgba(255,255,255,0)) right center / 28px 100% no-repeat,
      var(--white); }
  .tablewrap td, .tablewrap th, .prose td, .prose th { white-space: normal; }
}


/* ============================================ MENU OPEN: LOCK THE PAGE
   Without this, scrolling the open mobile menu scrolls the document behind it.
   overscroll-behavior alone is not enough once the panel itself is at its
   scroll limit, so the document is locked outright while the menu is open. */
html.nav-open, html.nav-open body { overflow: hidden; touch-action: none; }
