@charset "utf-8";
/* ==========================================================================
   URANGAN CARAVAN STORAGE — site.css
   Shared design system. One file, no framework, no resets from CDN.

   DESIGN STORY
   The yard is pale concrete with painted bay lines, eucalypt-green Colorbond
   sheds and a very blue Queensland sky. The repeating structural device in
   this system is the "bay line": a thick sun-yellow rule on the leading edge
   of a block, echoing the painted markings on a 9m x 3m site. Use it to mark
   something real (a storage option, a key fact) — never as decoration.

   ---------------------------------------------------------------------------
   COLOUR TOKENS (:root, auto-flip in dark mode)
     --blue        #0855A6  brand blue, in use on signage. Links, headings accent.
     --blue-deep   #063C77  pressed/hover state for blue
     --blue-wash   #DCEAF8  pale sky tint surface
     --ink         #0D2742  primary text + dark bands (navy, not grey-black)
     --ink-soft    #4A5F74  secondary/muted text (AA on white and concrete)
     --sun         #F6B52C  warm accent + primary CTA fill. Always takes --ink text.
     --sun-deep    #D4940F  hover state for sun
     --eucalypt    #7E8C5F  Colorbond green. BORDERS/RULES ONLY, never text.
     --euc-deep    #3E4A2E  dark green band background, takes --paper text
     --euc-tint    #E6EADC  pale green surface
     --concrete    #E9E7E0  pale warm-grey page/section surface
     --concrete-2  #D9D5CA  concrete borders + dividers
     --paper       #FFFFFF  card surfaces
   Semantic aliases used by components (these are what flip in dark mode):
     --bg --surface --surface-2 --text --text-soft --line --link --link-hover

   SPACING / SIZING
     --space-section  vertical padding on .section (override per modifier)
     --gutter         page side gutter (16px floor, grows on wide screens)
     --wrap           max content width (1200px) / --wrap-narrow (760px)
     --radius / --radius-sm / --radius-lg
     --bay            thickness of the bay-line rule (6px)

   TYPE
     --font-display  "Big Shoulders Display" — condensed wayfinding/depot face.
                     Headings, the phone number, and big stat numerals only.
                     It is narrow: safe at very large sizes on a 360px phone.
     --font-text     "Hanken Grotesk" — round, open, high-legibility workhorse.
                     Everything else. Never set display face below ~1.2rem.
     Fluid steps: --fs-xs --fs-sm --fs-base --fs-lede --fs-h3 --fs-h2 --fs-h1

   ---------------------------------------------------------------------------
   COMPONENT CLASSES (build sibling pages against these)

   LAYOUT
     .wrap                  centred container, max --wrap, side gutters
     .wrap--narrow          max --wrap-narrow, for prose pages
     .section               a page band. Vertical rhythm via --space-section.
                            Default background is transparent (page concrete).
       .section--tight        reduced vertical padding
       .section--paper        white/raised surface
       .section--concrete     pale concrete surface
       .section--ink          navy band, light text
       .section--euc          dark eucalypt band, light text
       .section--sun          sun-yellow band, ink text (use once per page)
     .section-head          heading cluster at the top of a section
     .grid                  base grid, gap --space-5
       .grid--2 .grid--3 .grid--4   column counts (collapse to 1 on mobile)
       .grid--split          60/40 two-column (collapses to 1)
     .stack                 vertical flow; children get --flow margin-top
     .full-bleed            escapes .wrap to full viewport width

   TYPE
     .eyebrow      short descriptive line above a heading, with leading rule.
                   Sentence case — do not set it in tracked-out capitals.
     .display      display-face heading treatment (applied to h1/h2 by default)
     .lede         larger intro paragraph
     .measure      caps line length to ~62ch   /   .measure--wide ~72ch
     .note         small print / qualifier text
     .stat         wrapper   .stat__num (display face, huge)   .stat__label
     .badge        small solid label chip (.badge--sun .badge--euc .badge--blue)

   ACTIONS  (every CTA on this site is a tel: or mailto: link — no checkout)
     .btn                   base button, 48px min touch target
       .btn--primary          sun fill, ink text — the main call-to-action
       .btn--ink              navy fill, white text
       .btn--ghost            transparent, current-colour border
       .btn--lg               larger padding + display face
       .btn--block            full width
     .btn-row               wraps a group of buttons with consistent gaps
     .link-plain            underlined inline link, inherits colour

   CONTENT BLOCKS
     .card                  surface block, bordered
       .card--bay             adds the sun bay-line on the leading edge
       .card--flat            no border, surface tint only
     .card__media / .card__body / .card__title / .card__foot
     .media                 image wrapper, 16:9, rounded, covers
       .media--wide           21:9   .media--tall  4:3
       .media--fill           no fixed ratio; fills the height of its grid cell
                              (use beside a column of text so no tint shows)
     .specs                 definition list of hard facts (dt/dd, ruled rows)
     .ticks                 list with drawn check marks (no icon font)
     .rate                  price block  .rate__amount .rate__unit .rate__note
     .strip                 horizontal run of facts  .strip__item
     .factlist              ruled list of label/value pairs (used for NAP)
     .rule                  horizontal divider in --line

   CHROME  (copy the <header> and <footer> markup from index.html verbatim —
            every page shares them, only aria-current moves)
     .skip-link
     .site-header .brand
     .nav .nav__list .nav__link
     .nav-toggle .nav-toggle__label      menu trigger; label hides under 26em
     .header-cta .header-cta__num        persistent phone CTA; number hides
                                         under 26em, icon + "Call" remain
     .nav__cta                           CTA shown only inside the open mobile
                                         menu; hidden on desktop
     .hero .hero__media .hero__inner .hero__meta
     .site-footer .footer-grid .nap .footer-base
     .visually-hidden

   PAGE PATTERNS (homepage, reusable)
     .why-grid / .why-item      three ruled columns carrying a .stat + prose
     .teaser-grid / .teaser-item  bay-lined tiles, 1/2/4 up
     .phone-xl                  oversized phone number link for a closing CTA

   RULES OF THE HOUSE
   - No !important anywhere. Keep specificity flat (single class selectors).
   - Section padding comes from --space-section only; modifiers change colour,
     never padding, so backgrounds and rhythm never fight each other.
   - The coloured bands (--ink / --euc / --sun) re-point --text, --text-soft
     and --line to values that pass 4.5:1 on that background. Build components
     against those tokens, never a hardcoded grey, and they will stay legible
     wherever they are placed and in dark mode.
   - Bookings are by phone and email only. The only conversion paths on this
     site are tel: and mailto: links. No cart, checkout, payment or login
     patterns, and no payment-method logos.
   - Motion only on interactive state change, and it is disabled under
     prefers-reduced-motion.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. TOKENS
   -------------------------------------------------------------------------- */
:root {
  /* Read by site.js so the script and the stylesheet cannot disagree
     about where the burger menu gives way to the horizontal nav. */
  --nav-breakpoint: 76.0625em;

  --blue: #0855a6;
  --blue-deep: #063c77;
  --blue-wash: #dceaf8;
  --ink: #0d2742;
  --ink-soft: #4a5f74;
  --sun: #f6b52c;
  --sun-deep: #d4940f;
  --eucalypt: #7e8c5f;
  --euc-deep: #3e4a2e;
  --euc-tint: #e6eadc;
  --concrete: #e9e7e0;
  --concrete-2: #d9d5ca;
  --paper: #fff;

  /* semantic aliases — these are what flip for dark mode */
  --bg: var(--concrete);
  --surface: var(--paper);
  --surface-2: var(--blue-wash);
  --text: var(--ink);
  --text-soft: var(--ink-soft);
  --line: var(--concrete-2);
  --link: var(--blue);
  --link-hover: var(--blue-deep);
  --band-ink-bg: var(--ink);
  --band-ink-text: #f2f6fa;

  /* Condensed fallbacks, so a failed webfont barely shifts headline metrics. */
  --font-display: "Big Shoulders Display", "Arial Narrow",
    "Liberation Sans Narrow", "Helvetica Neue Condensed", "Haettenschweiler",
    system-ui, sans-serif;
  --font-text: "Hanken Grotesk", system-ui, -apple-system, "Segoe UI", Roboto,
    "Helvetica Neue", Arial, sans-serif;

  --fs-xs: 0.8125rem;
  --fs-sm: 0.9375rem;
  --fs-base: clamp(1rem, 0.97rem + 0.16vw, 1.0625rem);
  --fs-lede: clamp(1.125rem, 1.05rem + 0.42vw, 1.3125rem);
  --fs-h3: clamp(1.4rem, 1.26rem + 0.7vw, 1.8rem);
  --fs-h2: clamp(2.25rem, 1.6rem + 2.9vw, 3.6rem);
  --fs-h1: clamp(3rem, 2.05rem + 4.8vw, 5.75rem);

  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.5rem;
  --space-6: 2rem;
  --space-7: 3rem;
  --space-8: 4rem;
  --space-section: clamp(3.25rem, 2rem + 5vw, 6rem);
  --gutter: clamp(1rem, 0.4rem + 2.6vw, 2.5rem);
  --wrap: 1200px;
  --wrap-narrow: 760px;
  --radius-sm: 3px;
  --radius: 6px;
  --radius-lg: 10px;
  --bay: 6px;
  --flow: 1.1em;
  --shadow: 0 1px 2px rgb(13 39 66 / 0.07), 0 10px 26px -14px rgb(13 39 66 / 0.3);
  --focus: 3px solid var(--blue);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #081726;
    --surface: #0f2639;
    --surface-2: #14304a;
    --text: #e7eef5;
    --text-soft: #a8bacb;
    --line: #27425c;
    --link: #6fb2f0;
    --link-hover: #9ccbf7;
    --concrete: #0f2639;
    --concrete-2: #27425c;
    --paper: #0f2639;
    --euc-tint: #1c2a1a;
    --blue-wash: #14304a;
    --band-ink-bg: #050f1a;
    --focus: 3px solid var(--sun);
  }
}

:root[data-theme="dark"] {
  --bg: #081726;
  --surface: #0f2639;
  --surface-2: #14304a;
  --text: #e7eef5;
  --text-soft: #a8bacb;
  --line: #27425c;
  --link: #6fb2f0;
  --link-hover: #9ccbf7;
  --concrete: #0f2639;
  --concrete-2: #27425c;
  --paper: #0f2639;
  --euc-tint: #1c2a1a;
  --blue-wash: #14304a;
  --band-ink-bg: #050f1a;
  --focus: 3px solid var(--sun);
}

/* --------------------------------------------------------------------------
   2. BASE
   -------------------------------------------------------------------------- */
*,
*::before,
*::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  overflow-x: hidden;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-text);
  font-size: var(--fs-base);
  line-height: 1.6;
  font-weight: 400;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

h1, h2, h3, h4, p, ul, ol, dl, dd, figure, blockquote { margin: 0; }
ul, ol { padding: 0; list-style: none; }

img, svg, video { max-width: 100%; height: auto; display: block; }
/* <picture> (WebP + JPEG) is inline by default, which breaks object-fit. */
picture { display: block; }

a { color: var(--link); text-underline-offset: 0.18em; }
a:hover { color: var(--link-hover); }

:focus-visible {
  outline: var(--focus);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

::selection { background: var(--sun); color: var(--ink); }

h1, h2 {
  font-family: var(--font-display);
  font-weight: 800;
  line-height: 0.92;
  letter-spacing: -0.005em;
  text-wrap: balance;
}
h1 { font-size: var(--fs-h1); }
h2 { font-size: var(--fs-h2); }

h3 {
  font-family: var(--font-text);
  font-size: var(--fs-h3);
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: -0.012em;
}

h4 {
  font-family: var(--font-text);
  font-size: 1.0625rem;
  font-weight: 700;
  line-height: 1.3;
}

.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.skip-link {
  position: absolute;
  top: 0; left: var(--space-4);
  transform: translateY(-120%);
  z-index: 200;
  background: var(--sun);
  color: var(--ink);
  font-weight: 700;
  padding: var(--space-3) var(--space-5);
  border-radius: 0 0 var(--radius) var(--radius);
  text-decoration: none;
  transition: transform 0.15s ease;
}
.skip-link:focus { transform: translateY(0); color: var(--ink); }

/* --------------------------------------------------------------------------
   3. LAYOUT
   -------------------------------------------------------------------------- */
.wrap {
  width: 100%;
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.wrap--narrow { max-width: var(--wrap-narrow); }

.section {
  padding-block: var(--space-section);
  background: transparent;
  color: var(--text);
}
.section--tight { --space-section: clamp(2.25rem, 1.5rem + 3vw, 3.75rem); }
.section--concrete { background: var(--concrete); }
.section--paper { background: var(--paper); }

/* Bands re-point the semantic tokens, not just `color`, so any component
   dropped in — including one authored elsewhere — inherits readable text,
   muted text and rules instead of the light-mode values. All clear 4.5:1. */
.section--ink {
  background: var(--band-ink-bg);
  color: var(--band-ink-text);
  --text: #f2f6fa;
  --text-soft: #b9cada;
  --line: rgb(255 255 255 / 0.18);
}
.section--euc {
  background: var(--euc-deep);
  color: #f3f6ec;
  --text: #f3f6ec;
  --text-soft: #c9d4b4;
  --line: rgb(255 255 255 / 0.2);
}
.section--sun {
  background: var(--sun);
  color: var(--ink);
  --text: var(--ink);
  --text-soft: #4a380a;
  --line: rgb(13 39 66 / 0.25);
}

/* Brand blue flips to sun on dark bands: both pairings are under 1.5:1. */
.section--ink .wx-icon,
.section--euc .wx-icon,
.section--ink .feed__where,
.section--euc .feed__where { color: var(--sun); }

.section--ink a:not(.btn) { color: #a9d2f7; }
.section--ink a:not(.btn):hover { color: #d6ebff; }
.section--euc a:not(.btn) { color: #e3edcb; }
.section--sun a:not(.btn) { color: var(--ink); }

.section-head { margin-bottom: clamp(1.75rem, 1rem + 2.4vw, 3rem); }
.section-head p { margin-top: var(--space-4); }
/* Display headings sit on 0.92 line-height: following text needs clearance. */
h2 + p,
h2 + .lede,
h1 + p,
h1 + .lede { margin-top: var(--space-5); }

.stack > * + * { margin-top: var(--flow); }

.grid { display: grid; gap: var(--space-5); }
@media (min-width: 46em) {
  .grid--2 { grid-template-columns: repeat(2, 1fr); }
  .grid--3 { grid-template-columns: repeat(2, 1fr); }
  .grid--4 { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 64em) {
  .grid--3 { grid-template-columns: repeat(3, 1fr); }
  .grid--4 { grid-template-columns: repeat(4, 1fr); }
  .grid--split { grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr); gap: var(--space-7); }
}

.full-bleed {
  width: 100vw;
  margin-left: 50%;
  transform: translateX(-50%);
}

.rule { height: 1px; border: 0; background: var(--line); margin: var(--space-6) 0; }

/* --------------------------------------------------------------------------
   4. TYPE COMPONENTS
   -------------------------------------------------------------------------- */
.eyebrow {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  font-size: var(--fs-sm);
  font-weight: 600;
  line-height: 1.35;
  color: var(--link);
  margin-bottom: var(--space-4);
}
.eyebrow::before {
  content: "";
  flex: none;
  width: 28px;
  height: var(--bay);
  border-radius: 1px;
  background: var(--sun);
}
.section--ink .eyebrow,
.section--euc .eyebrow { color: var(--sun); }
.section--sun .eyebrow { color: var(--ink); }
.section--sun .eyebrow::before { background: var(--ink); }

.display { font-family: var(--font-display); font-weight: 800; line-height: 0.92; }

.lede {
  font-size: var(--fs-lede);
  line-height: 1.5;
  color: var(--text-soft);
  max-width: 54ch;
}
.section--ink .lede,
.section--euc .lede { color: #c6d6e4; }
.section--euc .lede { color: #d6e0c4; }
.section--sun .lede { color: #3b2c06; }

.measure { max-width: 62ch; }
.measure--wide { max-width: 72ch; }

.note {
  font-size: var(--fs-sm);
  line-height: 1.5;
  color: var(--text-soft);
}
.section--sun .note { color: #4a380a; }

.link-plain { color: inherit; text-decoration: underline; text-decoration-thickness: 2px; }
.link-plain:hover { color: var(--link-hover); }
.section--sun .link-plain:hover { color: var(--ink); }

.stat { display: block; }
.stat__num {
  display: block;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(3rem, 2rem + 4vw, 4.75rem);
  line-height: 0.82;
  letter-spacing: -0.01em;
  color: var(--link);
}
.section--ink .stat__num,
.section--euc .stat__num { color: var(--sun); }
.stat__label {
  display: block;
  margin-top: var(--space-2);
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--text-soft);
}

.badge {
  display: inline-block;
  padding: 0.3em 0.75em;
  border-radius: var(--radius-sm);
  font-size: var(--fs-xs);
  font-weight: 700;
  line-height: 1.4;
  letter-spacing: 0.01em;
  background: var(--surface-2);
  color: var(--text);
}
.badge--sun { background: var(--sun); color: var(--ink); }
.badge--euc { background: var(--euc-deep); color: #f3f6ec; }
.badge--blue { background: var(--blue); color: #fff; }

/* --------------------------------------------------------------------------
   5. ACTIONS
   -------------------------------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  min-height: 48px;
  padding: 0.7rem 1.35rem;
  border: 2px solid transparent;
  border-radius: var(--radius);
  background: var(--surface-2);
  color: var(--text);
  font-family: var(--font-text);
  font-size: 1rem;
  font-weight: 700;
  line-height: 1.2;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  transition: background-color 0.15s ease, color 0.15s ease,
    border-color 0.15s ease, transform 0.08s ease;
}
.btn:hover { transform: translateY(-1px); }
.btn:active { transform: translateY(0); }

.btn--primary { background: var(--sun); color: var(--ink); border-color: var(--sun); }
.btn--primary:hover { background: var(--sun-deep); border-color: var(--sun-deep); color: var(--ink); }

.btn--ink { background: var(--ink); color: #fff; border-color: var(--ink); }
.btn--ink:hover { background: #061b31; color: #fff; }

.btn--ghost { background: transparent; color: inherit; border-color: currentColor; }

/* Ghost buttons invert on hover: the outline fills in and the label flips to
   the band's own background colour.
 *
 * This was previously `background: currentColor; color: var(--bg)` in one
 * rule. currentColor resolves against the colour set in that same block, so
 * the background landed on the identical value as the text and the label
 * disappeared — 1:1 contrast on every ghost button on the site. There is no
 * way to express "the colour I had a moment ago" in CSS, so each band states
 * its pair explicitly. Verified at >= 4.5:1 in both themes. */
.btn--ghost:hover,
.btn--ghost:focus-visible {
  background: var(--text);
  color: var(--bg);
  border-color: var(--text);
}

.section--ink .btn--ghost:hover,
.section--ink .btn--ghost:focus-visible,
.hero .btn--ghost:hover,
.hero .btn--ghost:focus-visible,
.site-header .btn--ghost:hover,
.site-header .btn--ghost:focus-visible,
.nav .btn--ghost:hover,
.nav .btn--ghost:focus-visible {
  background: #fff;
  color: var(--ink);
  border-color: #fff;
}

.section--euc .btn--ghost:hover,
.section--euc .btn--ghost:focus-visible {
  background: #fff;
  color: var(--euc-deep);
  border-color: #fff;
}

.section--sun .btn--ghost:hover,
.section--sun .btn--ghost:focus-visible {
  background: var(--ink);
  color: var(--sun);
  border-color: var(--ink);
}

.btn--lg {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(1.4rem, 1.15rem + 1.1vw, 1.9rem);
  letter-spacing: 0.005em;
  min-height: 60px;
  padding: 0.6rem 1.6rem;
}
.btn--block { display: flex; width: 100%; }

.btn-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  align-items: center;
}
.btn .ico { flex: none; width: 1.15em; height: 1.15em; fill: currentColor; }

/* --------------------------------------------------------------------------
   6. CONTENT BLOCKS
   -------------------------------------------------------------------------- */
.card {
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  overflow: hidden;
}
.card--flat { border-color: transparent; background: var(--surface-2); }

/* Bay line: a painted-marking rule on the block's leading edge. */
.card--bay { border-left: var(--bay) solid var(--sun); border-radius: 0 var(--radius-lg) var(--radius-lg) 0; }
.card--bay-euc { border-left: var(--bay) solid var(--eucalypt); }

.card__media { position: relative; }
.card__media img { width: 100%; aspect-ratio: 16 / 9; object-fit: cover; }
.card__media .badge { position: absolute; top: var(--space-3); left: var(--space-3); }

.card__body {
  padding: clamp(1.25rem, 0.9rem + 1.4vw, 1.9rem);
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  flex: 1;
}
.card__title { font-size: var(--fs-h3); font-weight: 700; line-height: 1.15; letter-spacing: -0.012em; }
.card__foot {
  margin-top: auto;
  padding-top: var(--space-4);
  border-top: 1px solid var(--line);
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

/* The ratio below is a default, not a cap. When a .media sits in a grid cell
   that is stretched taller than its ratio (a photo beside a column of text,
   say), height:100% resolves against that definite height and the image
   covers the whole cell. Where the cell height is auto, height:100% resolves
   to auto and the aspect-ratio takes over. Without this the figure stretches
   but the image does not, and the wrapper's background shows as a gap. */
.media { border-radius: var(--radius-lg); overflow: hidden; background: var(--surface-2); display: grid; }
.media picture { display: block; height: 100%; }
.media img { width: 100%; height: 100%; aspect-ratio: 16 / 9; object-fit: cover; display: block; }
.media--wide img { aspect-ratio: 21 / 9; }
.media--tall img { aspect-ratio: 4 / 3; }
/* No fixed ratio: fills whatever height its grid cell is stretched to. */
.media--fill,
.media--fill picture { height: 100%; }
.media--fill img { height: 100%; aspect-ratio: auto; min-height: 18rem; }

/* Spec-plate list of hard facts */
.specs { display: grid; grid-template-columns: auto 1fr; gap: 0; font-size: var(--fs-sm); }
.specs dt {
  padding: 0.55rem var(--space-4) 0.55rem 0;
  border-top: 1px solid var(--line);
  color: var(--text-soft);
  font-weight: 500;
}
.specs dd {
  padding: 0.55rem 0;
  border-top: 1px solid var(--line);
  margin: 0;
  font-weight: 600;
  text-align: right;
}

.ticks { display: grid; gap: var(--space-3); }
.ticks li { position: relative; padding-left: 1.9rem; }
.ticks li::before {
  content: "";
  position: absolute;
  left: 0; top: 0.42em;
  width: 1.1rem; height: 0.6rem;
  border-left: 3px solid var(--sun);
  border-bottom: 3px solid var(--sun);
  transform: rotate(-45deg);
  border-radius: 1px;
}

.rate { display: flex; align-items: baseline; flex-wrap: wrap; gap: var(--space-2); }
.rate__amount {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(2.4rem, 1.9rem + 2vw, 3.2rem);
  line-height: 0.85;
  color: var(--text);
}
.rate__unit { font-size: var(--fs-sm); font-weight: 600; color: var(--text-soft); }
.rate__note { flex-basis: 100%; font-size: var(--fs-xs); color: var(--text-soft); }

.strip { display: flex; flex-wrap: wrap; gap: 0; }
.strip__item {
  flex: 1 1 14rem;
  padding: var(--space-4) 0;
  border-top: var(--bay) solid var(--sun);
  margin-right: var(--space-6);
}
.strip__item:last-child { margin-right: 0; }
.strip__item b { display: block; font-size: 1.0625rem; }
.strip__item span { display: block; font-size: var(--fs-sm); opacity: 0.82; }

.factlist { display: grid; gap: 0; }
.factlist > div {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-3) 0;
  border-bottom: 1px solid var(--line);
}
.factlist dt { color: var(--text-soft); font-size: var(--fs-sm); font-weight: 500; }
.factlist dd { margin: 0; font-weight: 600; text-align: right; }
.section--ink .factlist > div { border-color: rgb(255 255 255 / 0.15); }
.section--ink .factlist dt { color: #a9bccd; }

/* --------------------------------------------------------------------------
   7. HEADER + NAV
   -------------------------------------------------------------------------- */
.site-header {
  position: sticky;
  top: 0;
  z-index: 100;
  background: var(--ink);
  color: #fff;
  border-bottom: var(--bay) solid var(--sun);
}
.site-header .wrap {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  min-height: 68px;
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  margin-right: auto;
  flex: none;
  min-width: 0;
  min-height: 44px;
  color: #fff;
  text-decoration: none;
  line-height: 1;
}
.brand svg { flex: none; width: 38px; height: 27px; color: #fff; }
.brand .roof { stroke: var(--sun); }
/* Stacked wordmark, set the way the shed sign reads: name over locality.
   Two short lines leave room for the phone CTA and menu trigger at 320px. */
.brand b {
  display: block;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(1.2rem, 1.05rem + 0.6vw, 1.5rem);
  line-height: 0.9;
  letter-spacing: 0.01em;
  white-space: nowrap;
}
.brand i {
  display: block;
  margin-top: 0.12em;
  font-style: normal;
  font-size: clamp(0.75rem, 0.7rem + 0.2vw, 0.875rem);
  font-weight: 600;
  color: #bcd4ea;
  letter-spacing: 0.015em;
  white-space: nowrap;
}
@media (max-width: 26em) {
  .brand svg { width: 30px; height: 21px; }
  .nav-toggle__label { display: none; }
}

.nav__list { display: flex; align-items: center; gap: var(--space-2); }
.nav__link {
  display: flex;
  align-items: center;
  min-height: 44px;
  padding: 0 0.7rem;
  color: #dce8f3;
  font-size: var(--fs-sm);
  font-weight: 600;
  white-space: nowrap;
  text-decoration: none;
  border-radius: var(--radius-sm);
  border-bottom: 3px solid transparent;
  transition: color 0.15s ease, border-color 0.15s ease;
}
.nav__link:hover { color: #fff; border-bottom-color: var(--sun); }
.nav__link[aria-current="page"] { color: #fff; border-bottom-color: var(--sun); }

/* Persistent phone CTA. Visible at every width; under 26em it drops the
   number and shows the handset plus "Call" so the three controls still fit. */
.header-cta {
  order: 2;
  min-height: 44px;
  padding: 0.45rem 1rem;
  font-size: var(--fs-sm);
  white-space: nowrap;
}
.nav__cta { display: none; }

.nav-toggle {
  order: 3;
  display: none;
  align-items: center;
  gap: 0.5rem;
  min-height: 44px;
  min-width: 44px;
  padding: 0 0.75rem;
  background: transparent;
  border: 2px solid rgb(255 255 255 / 0.4);
  border-radius: var(--radius);
  color: #fff;
  font-family: var(--font-text);
  font-size: var(--fs-sm);
  font-weight: 700;
  cursor: pointer;
}
.nav-toggle svg { width: 18px; height: 14px; stroke: currentColor; stroke-width: 2.4; }

@media (max-width: 76em) {
  .nav-toggle { display: inline-flex; }
  .nav {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    background: var(--ink);
    border-bottom: var(--bay) solid var(--sun);
    padding: var(--space-3) 0 var(--space-5);
    display: none;
  }
  .nav[data-open="true"] { display: block; }
  .nav__list { flex-direction: column; align-items: stretch; gap: 0; padding-inline: var(--gutter); }
  .nav__link {
    min-height: 52px;
    padding: 0 var(--space-2);
    font-size: 1.0625rem;
    border-bottom: 1px solid rgb(255 255 255 / 0.14);
    border-radius: 0;
  }
  .nav__link:hover,
  .nav__link[aria-current="page"] { border-bottom-color: var(--sun); }
  .nav { order: 4; }
  .nav__cta {
    display: flex;
    width: calc(100% - (var(--gutter) * 2));
    margin: var(--space-4) var(--gutter) 0;
  }
  .nav__cta + .nav__cta { margin-top: var(--space-3); }
}
@media (max-width: 26em) {
  .header-cta { padding: 0.45rem 0.8rem; gap: 0.4rem; }
  .header-cta__num { display: none; }
  .nav-toggle { padding: 0 0.6rem; }
}
@media (min-width: 76.0625em) {
  /* brand (0) | nav (1) | phone CTA (2) */
  .nav { display: block; order: 1; }
}

/* --------------------------------------------------------------------------
   8. HERO
   -------------------------------------------------------------------------- */
.hero { position: relative; isolation: isolate; background: var(--ink); color: #fff; }
.hero__media { position: absolute; inset: 0; z-index: -2; }
.hero__media picture { height: 100%; }
.hero__media img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 58%; }
.hero::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  /* Soft vertical scrim keeps the sky's colour; a stronger left-to-right one
     covers only where the text sits. Worst case behind the lede is ~8:1. */
  background:
    linear-gradient(185deg, rgb(13 39 66 / 0.2) 0%, rgb(13 39 66 / 0.08) 40%, rgb(13 39 66 / 0.88) 100%),
    linear-gradient(95deg, rgb(13 39 66 / 0.85) 0%, rgb(13 39 66 / 0.45) 55%, rgb(13 39 66 / 0.12) 100%);
}
.hero__inner {
  padding-block: clamp(3.5rem, 2rem + 11vw, 8.5rem) clamp(2rem, 1.2rem + 3vw, 3.5rem);
  max-width: 54rem;
}
.hero h1 { margin-bottom: var(--space-5); text-shadow: 0 2px 18px rgb(6 20 36 / 0.45); }
.hero .eyebrow { color: var(--sun); }
.hero .lede { color: #dde9f4; max-width: 44ch; }
.hero .btn-row { margin-top: clamp(1.5rem, 1rem + 2vw, 2.25rem); }

.hero__meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-6);
  padding-block: var(--space-4) clamp(1.5rem, 1rem + 2vw, 2.25rem);
  font-size: var(--fs-sm);
  font-weight: 600;
  color: #cfe0ef;
}
.hero__meta span { display: inline-flex; align-items: center; gap: 0.5rem; }
.hero__meta span::before {
  content: "";
  width: 10px; height: 10px;
  border-radius: 1px;
  background: var(--sun);
  flex: none;
}

/* --------------------------------------------------------------------------
   9. PAGE-LEVEL PATTERNS
   -------------------------------------------------------------------------- */
.why-grid { display: grid; gap: var(--space-6); }
.why-item { padding-top: var(--space-5); border-top: 2px solid var(--eucalypt); }
.why-item h3 { margin: var(--space-3) 0 var(--space-3); }
@media (min-width: 52em) {
  .why-grid { grid-template-columns: repeat(3, 1fr); gap: var(--space-7); }
}

.teaser-grid { display: grid; gap: var(--space-4); }
.teaser-item {
  padding: var(--space-5);
  background: rgb(255 255 255 / 0.07);
  border-radius: var(--radius);
  border-left: var(--bay) solid var(--sun);
}
.teaser-item h3 { font-size: 1.0625rem; margin-bottom: var(--space-2); }
.teaser-item p { font-size: var(--fs-sm); line-height: 1.55; color: #d6e0c4; }
@media (min-width: 46em) { .teaser-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 72em) { .teaser-grid { grid-template-columns: repeat(4, 1fr); } }

.phone-xl {
  display: inline-block;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(2.9rem, 1.6rem + 6vw, 5.5rem);
  line-height: 0.88;
  letter-spacing: -0.01em;
  color: var(--ink);
  text-decoration: none;
  border-bottom: var(--bay) solid var(--ink);
}
.phone-xl:hover { color: var(--ink); border-bottom-color: rgb(13 39 66 / 0.4); }

/* --------------------------------------------------------------------------
   10. FOOTER
   -------------------------------------------------------------------------- */
.site-footer {
  background: var(--band-ink-bg);
  color: #c6d6e4;
  padding-block: var(--space-7) var(--space-6);
  border-top: var(--bay) solid var(--sun);
  font-size: var(--fs-sm);
}
.site-footer a { color: #dce8f3; text-decoration: none; }
.site-footer a:hover { color: #fff; text-decoration: underline; }
.site-footer .footer-head { color: #fff; margin-bottom: var(--space-3); font-size: 0.9375rem; }

.footer-grid { display: grid; gap: var(--space-6); }
@media (min-width: 46em) { .footer-grid { grid-template-columns: 1.4fr 1fr 1fr; gap: var(--space-7); } }

.footer-grid li a { display: inline-flex; align-items: center; min-height: 44px; }

.nap { font-style: normal; line-height: 1.7; }
.nap a { display: inline-block; padding-block: 0.4rem; font-weight: 600; }

.footer-base {
  margin-top: var(--space-6);
  padding-top: var(--space-4);
  border-top: 1px solid rgb(255 255 255 / 0.15);
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-5);
  justify-content: space-between;
  font-size: var(--fs-xs);
  color: #9ab0c4;
}

/* --------------------------------------------------------------------------
   11. MOTION PREFERENCES
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms;
    animation-iteration-count: 1;
    transition-duration: 0.01ms;
    scroll-behavior: auto;
  }
  .btn:hover { transform: none; }
}

@media print {
  .site-header, .nav, .hero__media, .btn { display: none; }
  body { background: #fff; color: #000; }
}

/* Live conditions strip on the homepage teaser (sits on the --euc band). */
.teaser-live {
  margin: 0 0 var(--space-5);
  padding: var(--space-4) var(--space-5);
  border-left: var(--bay) solid var(--sun);
  border-radius: var(--radius-sm);
  background: rgba(255, 255, 255, 0.07);
  font-size: var(--fs-lede);
}

/* --------------------------------------------------------------------------
   14. LIVE MODULES  (Local News page + homepage teaser)

   Every [data-module] block ships with static fallback markup in the HTML.
   assets/js/news.js replaces that content only once a fetch succeeds, so
   these styles must look correct in all three states:
     (no attribute)        JS never ran — fallback prose, styled as normal text
     [data-state=loading]  fallback still showing, faintly dimmed
     [data-state=ready]    live content
     [data-state=error]    fallback restored, quiet notice appended by CSS
   -------------------------------------------------------------------------- */

.module {
  display: block;
}

.module[data-state="loading"] {
  opacity: 0.72;
  transition: opacity 0.25s ease;
}

.module[data-state="ready"] {
  opacity: 1;
}

/* A short, honest line when a feed could not be reached. The fallback content
   above it is still perfectly readable, so this is a footnote, not an alarm. */
.module[data-state="error"]::after {
  content: "Live data could not be loaded just now — showing our standing information.";
  display: block;
  margin-top: var(--space-3);
  padding-top: var(--space-3);
  border-top: 1px solid var(--line);
  font-size: var(--fs-xs);
  color: var(--text-soft);
}

.module__note {
  margin-top: var(--space-4);
}

.module__empty {
  margin: 0;
}

.module__heading {
  margin: 0 0 var(--space-2);
}

.module__src {
  color: inherit;
}

/* ---------------------------------------------------------------- weather */

.wx-icon {
  width: 2.25rem;
  height: 2.25rem;
  flex: none;
  color: var(--blue);
}

.wx-icon--lg {
  width: 4.5rem;
  height: 4.5rem;
  color: var(--sun-deep);
}

.wx-icon--sm {
  width: 1.5rem;
  height: 1.5rem;
}

.wx-now {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  flex-wrap: wrap;
}

.wx-now__read {
  min-width: 0;
}

.wx-temp {
  display: flex;
  align-items: baseline;
  gap: 0.1em;
  margin: 0;
  line-height: 1;
}

.wx-temp__val {
  font-family: var(--font-display);
  font-size: clamp(3rem, 9vw, 4.25rem);
  font-weight: 700;
  color: var(--text);
}

.wx-temp__unit {
  font-family: var(--font-display);
  font-size: clamp(1.25rem, 3vw, 1.6rem);
  color: var(--text-soft);
}

.wx-cond {
  margin: 0.15em 0 0;
  font-weight: 600;
}

.wx-meta {
  margin: 0.25em 0 0;
  font-size: var(--fs-sm);
  color: var(--text-soft);
}

/* Six-day strip. Scrolls horizontally on a narrow phone rather than
   squeezing six columns into 328px. */
.wx-days {
  display: grid;
  grid-template-columns: repeat(6, minmax(3.5rem, 1fr));
  gap: var(--space-2);
  margin: var(--space-5) 0 0;
  padding: 0;
  list-style: none;
  overflow-x: auto;
  scrollbar-width: thin;
  overscroll-behavior-x: contain;
}

.wx-day {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.35rem;
  padding: var(--space-3) 0.25rem;
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  text-align: center;
}

.wx-day__name {
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: 0.02em;
  color: var(--text-soft);
}

.wx-day__range {
  display: flex;
  align-items: baseline;
  gap: 0.35em;
}

.wx-day__max {
  font-family: var(--font-display);
  font-size: 1.3rem;
  font-weight: 700;
}

.wx-day__min {
  font-size: var(--fs-xs);
  color: var(--text-soft);
}

.wx-day__pop {
  font-size: var(--fs-xs);
  color: var(--blue);
  min-height: 1em;
}

/* --------------------------------------------------------- weather teaser */

.teaser-wx {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
  margin: 0;
}

.teaser-wx__temp {
  font-family: var(--font-display);
  font-size: 1.9rem;
  font-weight: 700;
  line-height: 1;
}

.teaser-wx__cond {
  font-weight: 600;
}

.teaser-wx__range {
  font-size: var(--fs-sm);
  color: var(--text-soft);
}

/* ------------------------------------------------------------- stat grid */

.stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(7rem, 1fr));
  gap: var(--space-4);
  margin: 0;
}

/* ------------------------------------------------------------------ feeds */

.feed {
  margin: 0;
  padding: 0;
  list-style: none;
}

.feed__item {
  padding: var(--space-4) 0;
  border-top: 1px solid var(--line);
}

.feed__item:first-child {
  border-top: 0;
  padding-top: 0;
}

.feed__link {
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  font-weight: 600;
  line-height: 1.15;
  color: var(--text);
  text-decoration: none;
  text-decoration-color: var(--line);
  text-underline-offset: 3px;
}

.feed__link:hover,
.feed__link:focus-visible {
  color: var(--link-hover);
  text-decoration: underline;
}

.feed__meta {
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
  flex-wrap: wrap;
  margin: 0.4em 0 0;
  font-size: var(--fs-xs);
  color: var(--text-soft);
}

.feed__source {
  font-weight: 700;
  letter-spacing: 0.01em;
  color: var(--text);
}

.feed__desc {
  margin: 0.5em 0 0;
  max-width: 62ch;
  font-size: var(--fs-sm);
  color: var(--text-soft);
}

/* Traffic items are statements, not links out. */
.feed__head {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
  margin: 0;
}

.feed__title {
  font-family: var(--font-display);
  font-size: 1.3rem;
  font-weight: 600;
  line-height: 1.2;
}

.feed__where {
  margin: 0.3em 0 0;
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--blue);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .wx-icon {
    color: var(--sun);
  }
  :root:not([data-theme="light"]) .feed__where {
    color: var(--sun);
  }
}

:root[data-theme="dark"] .wx-icon {
  color: var(--sun);
}

:root[data-theme="dark"] .feed__where {
  color: var(--sun);
}

/* --------------------------------------------------------------------------
   15. BREADCRUMB
   A real component, so pages stop borrowing .btn-row for a list of links.
   -------------------------------------------------------------------------- */
.breadcrumb {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.25rem 0.5rem;
  margin: 0 0 var(--space-5);
  padding: 0;
  list-style: none;
  font-size: var(--fs-xs);
  color: var(--text-soft);
}

.breadcrumb li {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
}

.breadcrumb a {
  color: inherit;
  text-underline-offset: 2px;
}

.breadcrumb a:hover,
.breadcrumb a:focus-visible {
  color: var(--link-hover);
}

.breadcrumb [aria-current="page"] {
  color: var(--text);
  font-weight: 600;
}

/* --------------------------------------------------------------------------
   16. SPACING UTILITIES
   A deliberately tiny set, so pages never need an inline style attribute for
   vertical rhythm. Values come from the same scale as everything else.
   -------------------------------------------------------------------------- */
.push-sm { margin-top: var(--space-4); }
.push    { margin-top: var(--space-5); }
.push-lg { margin-top: var(--space-6); }

/* Full-bleed media sits edge to edge, so it loses its corner radius. */
.media--flush { border-radius: 0; }

/* Narrow desktops: the five nav labels are long, so trim the rhythm rather
   than let the phone CTA run into the edge. */
@media (min-width: 76.0625em) and (max-width: 88em) {
  .site-header .wrap { gap: var(--space-3); }
  .nav__list { gap: 0.25rem; }
  .nav__link { padding-inline: 0.5rem; font-size: 0.9375rem; }
}

/* --------------------------------------------------------------------------
   17. HEADER ROBUSTNESS AT NARROW WIDTHS

   The wordmark is set in a condensed display face and fits on one line at
   phone widths — but only once that webfont has arrived. Before it does, and
   on any connection where Google Fonts is blocked, the fallback is wider and
   `white-space: nowrap` pushed the Call button and the Menu trigger clean off
   the right edge, leaving the site unnavigable on a 360px phone. The menu
   button has to be reachable whatever font is in use, so below the desktop
   nav breakpoint the wordmark is allowed to shrink and wrap, and the two
   controls to its right are never allowed to shrink.
   -------------------------------------------------------------------------- */
@media (max-width: 76em) {
  .brand { flex: 0 1 auto; }
  .brand b { white-space: normal; }
  .header-cta,
  .nav-toggle { flex: none; }
}

@media (max-width: 30em) {
  /* On the smallest phones the locality line is the first thing to go — the
     page says where we are in the hero anyway. */
  .brand i { display: none; }
}

/* --------------------------------------------------------------------------
   18. PAGE HERO
   The homepage hero is a tall, full-bleed statement. Interior pages want the
   same photographic treatment at a calmer height, and their intro markup is
   already a breadcrumb plus a .section-head rather than a .hero__inner — so
   this modifier carries the vertical rhythm on .wrap instead, and nothing in
   those pages has to be restructured.

   The scrim and text colours come from .hero itself, so contrast behaves
   exactly as it does on the homepage.
   -------------------------------------------------------------------------- */
.hero--page .wrap {
  padding-block: clamp(2.25rem, 1.4rem + 4.5vw, 4.5rem)
                 clamp(2rem, 1.2rem + 3vw, 3.5rem);
}

.hero--page h1 { margin-bottom: var(--space-4); }

/* The breadcrumb sits on photography now, so it needs the hero's palette
   rather than the light-surface one. */
.hero--page .breadcrumb { color: #cfe0ef; margin-bottom: var(--space-5); }
.hero--page .breadcrumb a { color: #cfe0ef; }
.hero--page .breadcrumb a:hover,
.hero--page .breadcrumb a:focus-visible { color: var(--sun); }
.hero--page .breadcrumb [aria-current="page"] { color: #fff; }

/* Supporting paragraphs that are not .lede — the standing explanation on the
   Local News page, for one. */
.hero--page .section-head > p:not(.eyebrow):not(.lede),
.hero--page .stack > p:not(.eyebrow):not(.lede) { color: #cfe0ef; }

.hero--page .note { color: #b9cde0; }

/* --------------------------------------------------------------------------
   19. FEED THUMBNAILS
   Pictures come from whichever publisher wrote the story, so they arrive at
   unpredictable sizes and sometimes do not arrive at all. The row is a flex
   pair: a fixed-width picture and a text column that fills what is left. An
   item with no picture is simply the text column at full width — there is no
   placeholder and no reserved gap.
   -------------------------------------------------------------------------- */
.feed__item--has-thumb {
  display: flex;
  align-items: flex-start;
  gap: var(--space-4);
}

.feed__body { min-width: 0; flex: 1; }

/* The tile is always a monogram of the masthead; a photograph, when the
   publisher provides one, covers it. So a story with no picture keeps the
   row's shape, and a picture that fails to load uncovers the monogram
   instead of collapsing the row. The hue is derived from the publisher's
   name, which keeps one masthead consistent and a column of them varied. */
.feed__thumb {
  position: relative;
  flex: none;
  width: 7.5rem;
  aspect-ratio: 3 / 2;
  overflow: hidden;
  border-radius: var(--radius-sm);
  display: grid;
  place-items: center;
  background:
    linear-gradient(140deg,
      hsl(var(--mono-hue, 210) 32% 26%),
      hsl(var(--mono-hue, 210) 28% 17%));
  box-shadow: inset 0 0 0 1px hsl(var(--mono-hue, 210) 30% 60% / 0.18);
}

.feed__mono {
  font-family: var(--font-display, inherit);
  font-size: 1.5rem;
  line-height: 1;
  letter-spacing: 0.02em;
  color: hsl(var(--mono-hue, 210) 45% 82%);
  user-select: none;
}

.feed__thumb img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

@media (max-width: 30em) {
  /* On a narrow phone a 120px picture leaves too little for the headline. */
  .feed__thumb { width: 5rem; }
  .feed__item--has-thumb { gap: var(--space-3); }
  .feed__mono { font-size: 1.1rem; }
}

/* --------------------------------------------------------------------------
   20. SEASONAL TIPS
   The tips are our own writing, not a news item, so the mark beside them is
   a drawn icon rather than a photograph — no network, no licensing, and it
   recolours itself in dark mode. An entry can opt into a real photo instead
   by naming one in seasonal-tips.json.
   -------------------------------------------------------------------------- */
.seasonal__head {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  margin-bottom: var(--space-4);
}

.seasonal__head > div { min-width: 0; }
.seasonal__head .eyebrow { margin-bottom: 0.35rem; }
.seasonal__head .module__heading { margin: 0; }

.seasonal__icon {
  flex: none;
  color: var(--sun-deep);
}

.seasonal__photo {
  flex: none;
  width: 6rem;
  aspect-ratio: 1;
  overflow: hidden;
  border-radius: var(--radius-sm);
  background: var(--surface-2);
}

.seasonal__photo img { width: 100%; height: 100%; object-fit: cover; display: block; }

@media (max-width: 30em) {
  .seasonal__head { gap: var(--space-3); }
  .seasonal__photo { width: 4.5rem; }
}

/* Provider credit and sponsored placement in the footer base. Set quieter
   than the copyright line — it is attribution, not a message to the
   visitor. */
.footer-credit { color: #93aec7; }

.footer-credit a {
  color: #cfe0ef;
  text-decoration: underline;
  text-underline-offset: 2px;
}

.footer-credit a:hover,
.footer-credit a:focus-visible { color: var(--sun); }

/* --------------------------------------------------------------------------
   21. LOCATION MAP

   Full-bleed OpenStreetMap with a card carrying the address and the two
   actions that matter. The card is real markup, present before any script
   runs and still there if none ever does — the map is the enhancement, not
   the other way round.
   -------------------------------------------------------------------------- */
.map {
  position: relative;
  background: var(--surface-2);
  border-top: var(--bay) solid var(--sun);
  border-bottom: 1px solid var(--line);
}

.map__canvas {
  /* Tall enough that the overlay card clears the bottom edge at every
     viewport height — checked, not guessed. */
  height: clamp(26rem, 58vh, 34rem);
  width: 100%;
  background: var(--euc-tint);
  z-index: 0;            /* keeps Leaflet's panes under the card */
}

/* OSM's default tiles are bright and busy next to this palette. A light
   touch takes the glare off and lets the navy card sit on top without
   fighting, while leaving the map legible — which is the whole point of
   it. */
.map__canvas .leaflet-tile-pane {
  filter: saturate(0.72) contrast(0.96) brightness(1.03);
}

/* The pin is an inline SVG in a divIcon, so Leaflet's default marker
   background and border have to be cleared. */
.map__pin {
  background: none;
  border: 0;
  filter: drop-shadow(0 3px 6px rgb(6 20 36 / 0.45));
}

.map__pin svg { display: block; }

/* Leaflet's own chrome, brought into the design system. */
.map .leaflet-control-zoom a {
  color: var(--ink);
  background: #fff;
  border-color: rgb(13 39 66 / 0.15);
  font-weight: 700;
}

.map .leaflet-control-zoom a:hover { background: var(--concrete); }

.map .leaflet-control-attribution {
  background: rgb(255 255 255 / 0.88);
  color: #3c4c5c;
  font-size: 0.6875rem;
}

.map .leaflet-control-attribution a { color: var(--blue); }

/* ---- the card ---------------------------------------------------------- */

.map__card {
  background: var(--ink);
  color: #fff;
  padding: var(--space-5);
  border-left: var(--bay) solid var(--sun);
}

.map__card .eyebrow { color: var(--sun); }

.map__card h3 {
  margin: 0 0 var(--space-3);
  color: #fff;
  /* Held at the h3 step: the display face at a larger size wraps "Urangan
     Caravan Storage" onto three lines in the card's width and pushes the
     buttons off the bottom of the map. */
  font-size: var(--fs-h3);
  line-height: 1.05;
}
.map__card .map__addr { margin: 0 0 var(--space-2); font-weight: 600; }
.map__card .note { color: #b9cde0; }
.map__card .btn-row { margin-top: var(--space-4); }

/* On a phone the card sits under the map, full width — a floating panel
   over a small map would cover the thing it is describing. */
@media (min-width: 48em) {
  .map__card {
    position: absolute;
    z-index: 500;              /* above Leaflet's panes and controls */
    top: var(--space-5);
    left: max(var(--gutter), calc((100% - var(--wrap)) / 2));
    width: min(24rem, calc(100% - (var(--gutter) * 2)));
    border-radius: var(--radius);
    box-shadow: 0 12px 32px rgb(6 20 36 / 0.3);
  }
}

/* While the tiles are still coming, and if they never do. */
.map[data-map-state="failed"] .map__canvas { display: none; }

/* Distance badge on a traffic item. Quieter than the road name it follows —
   it qualifies the location rather than competing with it. */
.feed__dist {
  margin-left: 0.6em;
  padding-left: 0.6em;
  border-left: 1px solid var(--line);
  font-weight: 500;
  color: var(--text-soft);
}

/* Two-finger hint, shown only when a single finger lands on the map. */
.map__hint {
  position: absolute;
  inset: 0;
  z-index: 450;              /* over the tiles, under the details card */
  display: grid;
  place-items: center;
  margin: 0;
  padding: var(--space-5);
  background: rgb(13 39 66 / 0.62);
  color: #fff;
  font-weight: 700;
  text-align: center;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;      /* never intercepts the gesture it describes */
  transition: opacity 0.18s ease, visibility 0.18s ease;
}

.map__hint[data-show] { opacity: 1; visibility: visible; }

@media (prefers-reduced-motion: reduce) {
  .map__hint { transition: none; }
}

/* --------------------------------------------------------------------------
   22. MARINE — the water band

   Full-bleed section with a live wave along the bottom. The two custom
   properties below are written by news.js from the actual reading:
     --wave-amp     vertical scale, from the measured wave height
     --wave-pace    seconds per cycle, from the measured wave period
   Before data arrives they hold neutral defaults, so the band looks
   deliberate whether or not the fetch ever succeeds.
   -------------------------------------------------------------------------- */
.marine {
  --wave-amp: 1;
  --wave-pace: 11s;

  position: relative;
  overflow: hidden;
  /* Extra bottom padding equal to the sea's height, so no text ever sits on
     top of the moving water. */
  padding-block: var(--space-section)
                 calc(var(--space-section) + clamp(5rem, 11vw, 8.5rem));
  /* Deeper than the ink band: this is water, and it wants to read as its own
     place in the page rather than another navy section. */
  background: linear-gradient(177deg, #0b2742 0%, #0a3457 58%, #0d4a74 100%);
  color: #fff;
  border-top: var(--bay) solid var(--sun);
}

.marine .eyebrow { color: var(--sun); }
.marine h2 { color: #fff; }
.marine .lede { color: #cfe3f4; }
.marine p { color: #bdd6ea; }
.marine .note { color: #9bb9d2; }

.marine__body { position: relative; z-index: 1; }

/* The stat row news.js renders into this section. */
.marine .stat-grid {
  margin-bottom: var(--space-6);
  grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr));
  gap: var(--space-5);
}

.marine .stat__label { color: #9bb9d2; }

.marine .stat__num {
  color: #fff;
  font-family: var(--font-display);
  font-size: clamp(2.6rem, 1.6rem + 4vw, 4.25rem);
  line-height: 0.95;
}

/* ---- the sea ----------------------------------------------------------- */

.marine__sea {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: clamp(5rem, 11vw, 8.5rem);
  z-index: 0;
  pointer-events: none;
}

.marine__sea svg { display: block; width: 100%; height: 100%; }

.marine__wave {
  transform: scaleY(var(--wave-amp));
  transform-origin: 50% 100%;
  transform-box: fill-box;
  animation: marine-drift var(--wave-pace) linear infinite;
}

.marine__wave path { fill: currentColor; }

.marine__wave--back  { color: rgb(255 255 255 / 0.07); animation-duration: calc(var(--wave-pace) * 1.9); }
.marine__wave--mid   { color: rgb(255 255 255 / 0.10); animation-duration: calc(var(--wave-pace) * 1.35); animation-direction: reverse; }
.marine__wave--front { color: rgb(255 255 255 / 0.16); }

@keyframes marine-drift {
  from { translate: 0 0; }
  to   { translate: -1200px 0; }
}

/* Motion is the point of this element, so reduced-motion stops it rather
   than speeding it up — the wave shape still reads as water standing still. */
@media (prefers-reduced-motion: reduce) {
  .marine__wave { animation: none; }
}

/* --------------------------------------------------------------------------
   23. ALL-CLEAR BANNER
   Shown above the standing route guidance when no traffic event is reported
   — which is most days. It replaces nothing: the prose underneath is what a
   reader actually wants when there is no incident to read about.
   -------------------------------------------------------------------------- */
.allclear {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  flex-wrap: wrap;
  margin-bottom: var(--space-5);
  padding: var(--space-4) var(--space-5);
  border-radius: var(--radius);
  border-left: var(--bay) solid var(--eucalypt);
  background: var(--euc-tint);
  color: var(--euc-deep);
}

.allclear__icon { width: 2rem; height: 2rem; flex: none; }
.allclear__text { flex: 1 1 14rem; min-width: 0; }

.allclear__head {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  line-height: 1.1;
}

.allclear__sub { margin: 0.25em 0 0; font-size: var(--fs-sm); color: var(--euc-deep); }
.allclear__cta { flex: none; }

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .allclear {
    background: rgb(126 140 95 / 0.16);
    color: #d8e3c4;
    border-left-color: var(--eucalypt);
  }
  :root:not([data-theme="light"]) .allclear__sub { color: #c3d2ab; }
}

:root[data-theme="dark"] .allclear {
  background: rgb(126 140 95 / 0.16);
  color: #d8e3c4;
}

:root[data-theme="dark"] .allclear__sub { color: #c3d2ab; }

/* Same banner, different meaning: we could not reach the feed, so this is
   explicitly not an all-clear. Sun rather than eucalypt, because the reader
   needs to notice the difference before they tow. */
.allclear--unknown {
  border-left-color: var(--sun-deep);
  background: rgb(246 181 44 / 0.14);
  color: var(--ink);
}

.allclear--unknown .allclear__sub { color: var(--ink-soft); }

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .allclear--unknown {
    background: rgb(246 181 44 / 0.14);
    color: #f3e4c4;
  }
  :root:not([data-theme="light"]) .allclear--unknown .allclear__sub { color: #e0cda6; }
}

:root[data-theme="dark"] .allclear--unknown { background: rgb(246 181 44 / 0.14); color: #f3e4c4; }
:root[data-theme="dark"] .allclear--unknown .allclear__sub { color: #e0cda6; }
