/* base.css — "fine" template: editorial.
 *
 * A complete, quality-gated design system (hand-rolled BEM, framework-free).
 * The build agent copies this file verbatim into a site's `style.css` and
 * overwrites ONLY the delimited brand-parameter block below — every other rule
 * ships byte-identical, which is how the fixture's Lighthouse/axe pass transfers
 * to every real build. See TEMPLATE.md for the character spec and the
 * brand-parameter contract (which properties to fill, how tints/shades derive).
 *
 * Character: refined, elegant-professional — high-contrast display serif over a
 * clean sans, small crisp radii (2-4px), near-monochrome ink-on-paper, hairline
 * rules and generous whitespace instead of shadows (one float shadow is reserved
 * for fixed overlays), slow deliberate deceleration motion. Shares the BEM
 * vocabulary of the "soft" and "sharp" templates so a site's HTML renders under
 * any of the three stylesheets unchanged. Fonts are self-hosted from the site's
 * own `fonts/` folder — see the generated web-font section below.
 */

/* ── Web fonts (self-hosted, generated) ──────────────────────────────────
 * Regenerate with `node scripts/fetch-template-fonts.mjs`. The files live in
 * the site's own `fonts/` folder, so a page load reaches no third party.
 * Rules for every approved pair are listed; only the pair named in the brand
 * block is ever requested, and a build copies only that pair's files. */

/* playfair-worksans */
@font-face {
  font-family: 'Playfair Display';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('fonts/playfair-display-normal-500.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Playfair Display';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('fonts/playfair-display-normal-600.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Work Sans';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('fonts/work-sans-normal-400.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Work Sans';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('fonts/work-sans-normal-500.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Work Sans';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('fonts/work-sans-normal-600.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* cormorant-inter */
@font-face {
  font-family: 'Cormorant Garamond';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('fonts/cormorant-garamond-normal-600.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Cormorant Garamond';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('fonts/cormorant-garamond-normal-700.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('fonts/inter-normal-400.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('fonts/inter-normal-500.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('fonts/inter-normal-600.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* spectral-sourcesans */
@font-face {
  font-family: 'Source Sans 3';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('fonts/source-sans-3-normal-400.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Source Sans 3';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('fonts/source-sans-3-normal-600.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Spectral';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('fonts/spectral-normal-500.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Spectral';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('fonts/spectral-normal-600.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* newsreader-geist */
@font-face {
  font-family: 'Geist';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('fonts/geist-normal-400.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Geist';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('fonts/geist-normal-500.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Geist';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('fonts/geist-normal-600.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Newsreader';
  font-style: normal;
  font-weight: 500 600;
  font-display: swap;
  src: url('fonts/newsreader-normal-500-600.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ── end web fonts ── */

/* ── Brand parameters (per-site) ─────────────────────────────────────────────
 * The ONLY block the build agent edits. Fill from the extracted brand (logo
 * colours, screenshot palette, tone). Keep every property; adjust its value.
 * Contrast contract: --color-text and --color-muted must hold >= 4.5:1 on
 * --color-surface and --color-surface-alt; --color-on-primary must hold >= 4.5:1
 * on --color-primary; --color-primary used as link text must hold >= 4.5:1 on
 * --color-surface. --color-accent is DECORATIVE ONLY (rules, marks, nav
 * underline) — never load-bearing text unless it independently meets 4.5:1.
 * Because primary sits close to text in a near-monochrome scheme, body links keep
 * their underline (the reset preserves it) so affordance never rests on colour.
 * Fonts must be one of TEMPLATE.md's approved display-serif + clean-sans pairs. */
:root {
  --color-primary: #720030;        /* on-air crimson ink — links, buttons, ink blocks */
  --color-primary-shade: #5e0028;  /* darker primary for hover/active */
  --color-primary-tint: #f4ebee;   /* pale crimson wash for icon plates / hero */
  --color-on-primary: #faf6f2;     /* text/icons on --color-primary blocks */
  --color-accent: #a8874f;         /* muted brass — fine rules and marks, decorative only (~3.2:1) */
  --color-surface: #faf8f5;        /* warm paper white page background */
  --color-surface-alt: #f2eee9;    /* light stone for alternating sections */
  --color-text: #1e1a1c;           /* warm ink body text */
  --color-muted: #5a5357;          /* muted ink-grey secondary text (>= 4.5:1 on both surfaces) */
  --color-border: #ddd5d3;         /* pale stone hairline */

  --font-heading: 'Playfair Display', Georgia, 'Times New Roman', serif;
  --font-body: 'Work Sans', 'Segoe UI', system-ui, -apple-system, sans-serif;
}
/* ── end Brand parameters ──────────────────────────────────────────────────── */

/* ── Fixed tokens (the template character — do not edit per site) ──────────── */
:root {
  /* Type scale 1.414 (augmented fourth), fluid via clamp; body line-height 1.6.
     Top two steps widened for display drama (same convention as sharp). */
  --step--1: clamp(0.8rem, 0.78rem + 0.13vw, 0.875rem);
  --step-0: clamp(1rem, 0.96rem + 0.18vw, 1.125rem);
  --step-1: clamp(1.41rem, 1.36rem + 0.29vw, 1.59rem);
  --step-2: clamp(2rem, 1.92rem + 0.42vw, 2.25rem);
  --step-3: clamp(2.83rem, 2.71rem + 0.59vw, 3.18rem);
  --step-4: clamp(3.36rem, 2.89rem + 2.33vw, 4.76rem);
  --step-5: clamp(3.9rem, 3.08rem + 4.08vw, 6.35rem);
  --leading-body: 1.6;
  --leading-tight: 1.1;

  /* Spacing — the most generous of the three; 1.5rem base rhythm. */
  --space-3xs: 0.375rem;
  --space-2xs: 0.75rem;
  --space-xs: 1rem;
  --space-s: 1.5rem;
  --space-m: 2.5rem;
  --space-l: 4rem;
  --space-xl: 6rem;
  --section-pad: clamp(4rem, 2.8rem + 6vw, 7.5rem);
  --container-max: 68rem; /* narrower, elegant measure */

  /* Small crisp radii — the fine character (between sharp's 0-2 and soft's 8-24). */
  --radius-s: 2px;
  --radius-m: 3px;
  --radius-l: 4px;
  --radius-pill: 4px;

  /* Elevation = whitespace + hairline rules. No card shadows; --shadow-float is
     reserved for fixed overlays (cookie banner, mobile nav sheet) that cannot
     float on whitespace alone. --border-rule is the broadsheet top rule. */
  --border-hair: 1px solid var(--color-border);
  --border-rule: 2px solid var(--color-text);
  --shadow-float: 0 8px 24px rgba(32, 29, 26, 0.08);

  /* Slow deliberate deceleration, 600-700ms — a long ease-out. */
  --ease-fine: cubic-bezier(0.19, 1, 0.22, 1);
  --dur-reveal: 640ms;
  --dur-hover: 300ms;

  --header-h: 4.5rem;
}

/* ── Reset ───────────────────────────────────────────────────────────────── */
*,
*::before,
*::after {
  box-sizing: border-box;
}
* {
  margin: 0;
}
html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* Reserve the scrollbar's width at all times. The lightbox hides the page's
     overflow while it is open, which would otherwise take the bar away and shift
     the whole page sideways behind the dialog. */
  scrollbar-gutter: stable;
}
body {
  font-family: var(--font-body);
  font-size: var(--step-0);
  line-height: var(--leading-body);
  color: var(--color-text);
  background: var(--color-surface);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
img,
picture,
svg,
video {
  display: block;
  max-width: 100%;
  height: auto;
}
input,
button,
textarea,
select {
  font: inherit;
  color: inherit;
}
/* Body-copy links stay underlined: in a near-monochrome scheme, primary sits
   close to text, so the underline (not colour) is the affordance (WCAG 1.4.1). */
a {
  color: var(--color-primary);
  text-underline-offset: 0.18em;
}
a:hover {
  color: var(--color-primary-shade);
}
h1,
h2,
h3,
h4 {
  font-family: var(--font-heading);
  line-height: var(--leading-tight);
  font-weight: 600; /* every approved heading face carries a 600 — renders alike */
  color: var(--color-text);
  letter-spacing: -0.01em;
}
h1 {
  font-size: var(--step-4);
}
h2 {
  font-size: var(--step-3);
}
h3 {
  font-size: var(--step-2);
}
ul,
ol {
  padding-left: 1.25em;
}

/* Visible, high-contrast focus ring for keyboard users on every interactive
   element — primary, never the sub-AA accent; never removed without a
   equal-or-better replacement. */
:focus-visible {
  outline: 3px solid var(--color-primary);
  outline-offset: 2px;
  border-radius: var(--radius-s);
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
}

/* ── Utilities ───────────────────────────────────────────────────────────── */
.container {
  width: 100%;
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--space-s);
}
/* Accessible visually-hidden text (labels for icon-only controls, etc.). */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
/* Skip link: off-screen until focused, then pinned top-left for keyboard users. */
.skip-link {
  position: absolute;
  left: var(--space-s);
  top: -4rem;
  z-index: 100; /* above the sticky header */
  padding: var(--space-2xs) var(--space-xs);
  background: var(--color-primary);
  color: var(--color-on-primary);
  border-radius: var(--radius-s);
  transition: top var(--dur-hover) var(--ease-fine);
}
.skip-link:focus {
  top: var(--space-2xs);
  color: var(--color-on-primary);
}

/* Scroll-reveal: only hidden when JS is active (root .js), so no-JS keeps all
   content visible. main.js adds .is-visible as elements enter the viewport. The
   16px lift is a settle, not a slide — deliberate, in keeping with the character. */
.js [data-reveal] {
  opacity: 0;
  transform: translateY(16px);
  transition: opacity var(--dur-reveal) var(--ease-fine), transform var(--dur-reveal) var(--ease-fine);
}
.js [data-reveal].is-visible {
  opacity: 1;
  transform: none;
}

/* ── Kickers (shared hero/section eyebrow — uppercase, letterspaced, with an
      accent rule that grows in on load) ─────────────────────────────────────── */
.hero__kicker,
.section__kicker {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  margin-bottom: var(--space-s);
  font-family: var(--font-body); /* sans eyebrow, distinct from the serif headings */
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  font-size: var(--step--1);
  color: var(--color-primary); /* text slot — primary carries the contrast; accent stays decorative */
}
/* A short brass rule trailing the eyebrow (decorative — accent is sub-AA, never text). */
.hero__kicker::after,
.section__kicker::after {
  content: '';
  width: 2.5rem;
  height: 2px;
  background: var(--color-accent);
  transform-origin: left;
}
.js .hero__kicker::after,
.js .section__kicker::after {
  animation: rule-grow var(--dur-reveal) var(--ease-fine) both;
}

/* ── Buttons ─────────────────────────────────────────────────────────────── */
.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-3xs);
  min-height: 3rem; /* comfortable tap target */
  padding: 0.85rem 1.75rem;
  border: 1px solid transparent;
  border-radius: var(--radius-s); /* crisp rectangle — the fine character */
  font-weight: 600;
  font-size: var(--step-0);
  letter-spacing: 0.04em;
  text-decoration: none;
  cursor: pointer;
  transition: background-color var(--dur-hover) var(--ease-fine), color var(--dur-hover) var(--ease-fine),
    border-color var(--dur-hover) var(--ease-fine);
}
/* Primary = solid ink block. */
.button--primary {
  background: var(--color-primary);
  color: var(--color-on-primary);
  border-color: var(--color-primary);
}
.button--primary:hover {
  background: var(--color-primary-shade);
  color: var(--color-on-primary);
  border-color: var(--color-primary-shade);
}
/* Ghost = 1px hairline that darkens to ink on hover — no fill, no lift, no shadow. */
.button--ghost {
  background: transparent;
  color: var(--color-primary);
  border-color: var(--color-border);
}
.button--ghost:hover {
  color: var(--color-primary-shade);
  border-color: var(--color-primary);
}

/* ── Header / navigation ─────────────────────────────────────────────────── */
.site-header {
  position: sticky;
  top: 0;
  z-index: 50; /* above page content, below the skip link */
  /* Opaque, not a translucent backdrop-blur: blur() forces a GPU repaint on
     every scroll frame, which spikes rendering cost — an opaque header keeps the
     sticky nav cheap and the paint deterministic. */
  background: var(--color-surface);
  border-bottom: var(--border-hair);
  transition: border-color var(--dur-hover) var(--ease-fine);
}
/* main.js adds --stuck once scrolled off the top; strengthen the hairline to the
   broadsheet rule. */
.site-header--stuck {
  border-bottom: var(--border-rule);
}
.site-header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-s);
  min-height: var(--header-h);
}
.site-header__brand {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  font-family: var(--font-heading);
  font-weight: 600;
  font-size: var(--step-1);
  color: var(--color-text);
  text-decoration: none;
}
.site-header__logo {
  width: 2.25rem;
  height: 2.25rem;
  border-radius: var(--radius-s);
}
.nav__toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 3rem;
  height: 3rem;
  background: transparent;
  border: var(--border-hair);
  border-radius: var(--radius-s);
  cursor: pointer;
}
.nav__toggle-bars {
  position: relative;
  width: 1.25rem;
  height: 1px;
  background: var(--color-text);
}
.nav__toggle-bars::before,
.nav__toggle-bars::after {
  content: '';
  position: absolute;
  left: 0;
  width: 1.25rem;
  height: 1px;
  background: var(--color-text);
}
.nav__toggle-bars::before {
  top: -6px;
}
.nav__toggle-bars::after {
  top: 6px;
}
.nav__menu {
  list-style: none;
  padding: 0;
  margin: 0;
}
.nav__item {
  margin: 0;
}
.nav__link {
  display: inline-flex;
  align-items: center;
  min-height: 3rem;
  padding: 0 var(--space-2xs);
  color: var(--color-text);
  text-decoration: none;
  font-weight: 500;
  letter-spacing: 0.01em;
  border-bottom: 2px solid transparent; /* accent underline target */
  transition: border-color var(--dur-hover) var(--ease-fine), color var(--dur-hover) var(--ease-fine);
}
.nav__link:hover,
.nav__link--active {
  color: var(--color-primary);
  border-bottom-color: var(--color-accent); /* decorative accent underline */
}

/* Mobile-first: nav collapses behind the toggle; expands to a row at >= 48rem. */
@media (max-width: 47.99rem) {
  .nav__menu {
    display: none;
    position: absolute;
    left: 0;
    right: 0;
    top: 100%;
    flex-direction: column;
    gap: 0;
    padding: var(--space-2xs) var(--space-s) var(--space-s);
    background: var(--color-surface);
    border-top: var(--border-hair);
    /* The open sheet floats over content — the one place a shadow is warranted. */
    box-shadow: var(--shadow-float);
  }
  .nav__menu--open {
    display: flex;
  }
  .nav__link {
    width: 100%;
    border-bottom: var(--border-hair);
  }
}
@media (min-width: 48rem) {
  .nav__toggle {
    display: none;
  }
  .nav__menu {
    display: flex;
    align-items: center;
    gap: var(--space-s);
  }
}

/* ── Hero ────────────────────────────────────────────────────────────────── */
.hero {
  padding-block: var(--section-pad);
  /* A near-monochrome scheme: a faint paper wash, no bold block. */
  background:
    radial-gradient(120% 90% at 100% 0%, var(--color-primary-tint), transparent 55%),
    var(--color-surface);
  /* Enhanced wash: a barely-there breath of the accent in the opposite corner —
     a whisper, in keeping with ink-on-paper. Derived from brand tokens via
     color-mix(); browsers without color-mix() keep the declaration above. */
  background:
    radial-gradient(120% 90% at 100% 0%, var(--color-primary-tint), transparent 55%),
    radial-gradient(90% 70% at 0% 100%, color-mix(in srgb, var(--color-accent) 5%, transparent), transparent 55%),
    var(--color-surface);
  border-bottom: var(--border-hair);
  overflow: hidden; /* contains the framed media bleed on small screens */
}
.hero__inner {
  display: grid;
  gap: var(--space-l);
  align-items: center;
}
.hero__body {
  min-width: 0; /* grid child: keep a long word from blowing out the text column */
}
.hero__title {
  /* Below --step-5: at the full display step a 13-character Finnish word ("painopalvelua")
     is wider than the hero column at every tested width, and breaks. This is the largest size
     at which it still fits. --step-5 itself is unchanged, and still sizes the 404 digits. */
  font-size: clamp(3.04rem, 2.40rem + 3.18vw, 4.95rem);
  overflow-wrap: break-word;
  /* Finnish compounds routinely run past 20 characters ("ympäristöystävällistä"). One cannot
     fit unbroken in a hero column at display size — it would take body-copy size to fit. Left
     unhyphenated the browser still breaks the word, it just breaks it silently, which reads
     worse than a hyphen. So hyphenate, but only from 16 characters up, leaving at least 5
     letters before and 4 after the break. The 13-14 character words that a Finnish tagline
     is full of ("painopalvelua") are kept whole by the title size instead, so they never get
     the hyphen that made the first live site look broken. A 16+ character business name will
     be hyphenated; a visible hyphen is still the better of the two possible breaks. Safari
     before 17 ignores the 16-character floor and applies only the 5/4 limits. */
  hyphens: auto;
  -webkit-hyphens: auto;
  hyphenate-limit-chars: 16 5 4;
  -webkit-hyphenate-limit-before: 5;
  -webkit-hyphenate-limit-after: 4;
  max-width: 15ch; /* a ch cap limits swap reflow (CLS) at the display size */
}
.hero__text {
  margin-top: var(--space-s);
  font-size: var(--step-1);
  color: var(--color-muted);
  max-width: 46ch;
}
.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs);
  margin-top: var(--space-m);
}
.hero__media {
  border: var(--border-hair); /* a fine editorial frame, not a shadow */
  border-radius: var(--radius-l);
  overflow: hidden;
  animation: hero-settle var(--dur-reveal) var(--ease-fine) both;
}
.hero__art {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 4 / 3;
  object-fit: cover;
}
@media (min-width: 56rem) {
  .hero__inner {
    grid-template-columns: 1.1fr 0.9fr;
  }
}

/* ── Section scaffolding ─────────────────────────────────────────────────── */
.section {
  padding-block: var(--section-pad);
}
.section--alt {
  background: var(--color-surface-alt);
  /* Paper-tone gradation: the stone tone eases in from the surface above rather
     than switching abruptly. Token-derived; browsers without color-mix() keep
     the solid stone. */
  background: linear-gradient(180deg, color-mix(in srgb, var(--color-surface-alt) 55%, var(--color-surface)), var(--color-surface-alt) 30%);
}
.section__head {
  max-width: 52ch;
  margin-bottom: var(--space-l);
}
.section__title {
  font-size: var(--step-3);
  /* A Finnish compound such as "Arkkitehtisuunnittelu" is wider than a phone's
     text column at this size. Left alone the browser breaks it silently mid-word
     and the page scrolls sideways; the hero title already carries the same
     treatment, with the same 16-character floor so ordinary words stay whole. */
  overflow-wrap: break-word;
  hyphens: auto;
  -webkit-hyphens: auto;
  hyphenate-limit-chars: 16 5 4;
  -webkit-hyphenate-limit-before: 5;
  -webkit-hyphenate-limit-after: 4;
}
.section__lead {
  margin-top: var(--space-s);
  color: var(--color-muted);
  font-size: var(--step-1);
}

/* ── Cards (broadsheet top-rule + whitespace — no shadow, no numbering) ────── */
.card-grid {
  display: grid;
  gap: var(--space-l);
  grid-template-columns: minmax(0, 1fr); /* a card can never be wider than its track, whatever its longest word */
}
@media (min-width: 40rem) {
  .card-grid {
    grid-template-columns: repeat(2, 1fr);
  }
}
@media (min-width: 60rem) {
  .card-grid {
    grid-template-columns: repeat(3, 1fr);
  }
}
.card {
  overflow-wrap: anywhere; /* a compound word longer than a 320px column breaks instead of widening the page */
  hyphens: auto;
  padding-top: var(--space-s);
  background: transparent;
  border-top: var(--border-rule); /* the editorial rule that heads each card */
  transition: border-color var(--dur-hover) var(--ease-fine);
}
.card:hover {
  border-top-color: var(--color-accent); /* a quiet brass shift, no lift */
}
.card__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 3rem;
  height: 3rem;
  margin-bottom: var(--space-s);
  background: var(--color-primary-tint);
  color: var(--color-primary);
  border-radius: var(--radius-s);
}
.card__title {
  font-size: var(--step-2);
  margin-bottom: var(--space-2xs);
}
.card__text {
  color: var(--color-muted);
}
/* The reset at the top of this file removes every margin, so a card holding more
   than a title and one paragraph (a photo, a second paragraph, a line of links)
   would stack its pieces flush against each other. This spaces each piece from the
   one above it. The gap equals the title's own bottom margin, and the two overlap
   rather than add up, so the space under a title stays as it was. */
.card > * + * {
  margin-top: var(--space-2xs);
}
/* The icon is an inline box, so its bottom margin does not overlap the next
   piece's top margin. It already sets the space beneath it, so the piece that
   follows adds none. */
.card__icon + * {
  margin-top: 0;
}

/* ── Gallery ─────────────────────────────────────────────────────────────── */
.gallery {
  display: grid;
  gap: var(--space-s);
  grid-template-columns: 1fr;
}
@media (min-width: 40rem) {
  .gallery {
    grid-template-columns: repeat(2, 1fr);
  }
}
@media (min-width: 60rem) {
  .gallery {
    grid-template-columns: repeat(3, 1fr);
  }
}
.gallery__item {
  overflow: hidden;
  border: var(--border-hair); /* fine frame, not a rounded shadowed tile */
  border-radius: var(--radius-l);
}
/* The link wraps the whole picture, so it has to be a block. An inline link keeps
   the space a text line reserves under its letters, which would make the item
   taller than the photograph inside it. */
.gallery__link {
  display: block;
}
/* Only when the script is running does a click open the picture, so only then
   does the pointer promise it. */
.js .gallery__link {
  cursor: zoom-in;
}
.gallery__caption {
  padding: 0.75rem 1rem;
  font-size: 0.875rem;
  color: var(--color-muted);
}
.gallery__image {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  transition: transform var(--dur-hover) var(--ease-fine);
}
.gallery__item:hover .gallery__image {
  transform: scale(1.03);
}
/* A gallery holding one picture runs the full row instead of sitting in a third
   of it. Written as a class rather than read off `:only-child`, so the markup
   says what it is. At 4:3 a full-width photograph would be nearly a screen tall,
   so the single picture takes the wide-panel ratio. */
.gallery--single .gallery__item {
  grid-column: 1 / -1;
}
.gallery--single .gallery__image {
  aspect-ratio: 12 / 5;
}

/* ── Lightbox ────────────────────────────────────────────────────────────── */
/* main.js builds this dialog the first time a visitor opens a gallery picture.
   The dialog carries no frame of its own: the photograph is the content and the
   darkened page behind it is the frame. Every control is a visible word, so the
   template needs no icon font and the labels follow the site's own language. */
.lightbox {
  width: 100vw;
  max-width: 100vw;
  height: 100vh;
  max-height: 100vh;
  padding: 0;
  border: 0;
  /* The dialog paints the dark ground itself rather than leaving it to the
     backdrop alone: it fills the viewport, and a browser that does not paint
     ::backdrop would otherwise show the light page straight under the caption. */
  background: rgba(20, 18, 16, 0.88);
  color: var(--color-on-primary);
  overflow: hidden;
}
.lightbox::backdrop {
  background: rgba(20, 18, 16, 0.88);
}
.lightbox__figure {
  margin: 0;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-2xs);
  /* The picture keeps clear of the chrome around it: the close button above, and
     the counter with the previous/next pair below. */
  padding: 4rem var(--space-s) 7.5rem;
}
.lightbox__image {
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  min-height: 0; /* lets the picture shrink so the caption always fits under it */
  object-fit: contain;
}
.lightbox__caption {
  flex: 0 0 auto;
  max-width: 60ch;
  text-align: center;
  font-size: var(--step--1);
  color: var(--color-on-primary);
}
.lightbox__close,
.lightbox__prev,
.lightbox__next {
  position: fixed;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 3rem;
  min-height: 3rem; /* the same comfortable tap target the page's buttons keep */
  padding: var(--space-3xs) var(--space-xs);
  font-family: var(--font-body);
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--color-text);
  background: var(--color-surface);
  border: var(--border-hair);
  border-radius: var(--radius-s);
  box-shadow: var(--shadow-float);
  cursor: pointer;
}
.lightbox__close {
  top: var(--space-s);
  right: var(--space-s);
}
.lightbox__prev,
.lightbox__next {
  top: 50%;
  transform: translateY(-50%);
}
.lightbox__prev {
  left: var(--space-s);
}
.lightbox__next {
  right: var(--space-s);
}
.lightbox__count {
  position: fixed;
  left: 50%;
  bottom: var(--space-s);
  transform: translateX(-50%);
  font-size: var(--step--1);
  color: var(--color-on-primary);
}
/* On a phone there is no room beside the photograph, so the pair moves under it,
   side by side, and the counter rises above them. */
@media (max-width: 39.99em) {
  .lightbox__prev,
  .lightbox__next {
    top: auto;
    bottom: var(--space-s);
    transform: none;
  }
  .lightbox__prev {
    left: auto;
    right: 50%;
    margin-right: var(--space-2xs); /* two 48px targets closer than 16px read as one control */
  }
  .lightbox__next {
    right: auto;
    left: 50%;
    margin-left: var(--space-2xs);
  }
  .lightbox__count {
    bottom: calc(var(--space-s) + 3.5rem);
  }
}
@media (min-width: 40em) {
  .lightbox__figure {
    /* Previous and next sit at the sides on a wide screen, so the picture leaves
       them a column of their own. */
    padding: 4rem 9rem 5rem;
  }
}
@media (prefers-reduced-motion: no-preference) {
  .lightbox[open] {
    animation: lightbox-in var(--dur-hover) var(--ease-fine);
  }
  @keyframes lightbox-in {
    from {
      opacity: 0;
    }
    to {
      opacity: 1;
    }
  }
}
/* The only thing holding the page still behind the open dialog — the script
   deliberately scrolls nothing itself. It is paired with the stable scrollbar
   gutter in the reset, so the page does not shift sideways as the bar goes. */
html.lightbox-open {
  overflow: hidden;
}

/* ── Price cards ─────────────────────────────────────────────────────────── */
/* One card per package, for a business that publishes prices; a business that
   does not leaves the section out rather than filling it with vague cards. Each
   card ends with exactly one action pinned to the bottom, so the buttons line up
   across cards whose lists are of different lengths. */
.plan-grid {
  display: grid;
  gap: var(--space-s);
  grid-template-columns: repeat(auto-fit, minmax(min(18rem, 100%), 1fr) /* never wider than the container, so a 320px phone gets one full-width column */);
  margin-top: var(--space-l);
}
.plan {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  padding: var(--space-m);
  background: var(--color-surface);
  border: var(--border-hair);
  border-radius: var(--radius-l);
}
/* Elevation in this template is whitespace and a rule, never a shadow, so the
   highlighted card is marked by a brass frame rather than a coloured fill. The
   badge above its name says the same thing in words. */
.plan--featured {
  border: 2px solid var(--color-accent);
}
.plan__badge {
  align-self: flex-start;
  padding: var(--space-3xs) var(--space-xs);
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-primary);
  background: var(--color-primary-tint);
  border-radius: var(--radius-s);
}
.plan__name {
  font-family: var(--font-heading);
  font-size: var(--step-1);
  line-height: var(--leading-tight);
}
.plan__price {
  font-family: var(--font-heading);
  /* The weight the page's headings already load. At 500 the browser fetches a
     second file of the same family for this one line. */
  font-weight: 600;
  font-size: var(--step-2);
  line-height: var(--leading-tight);
  /* A price is as often a phrase as a figure ("Sopimuksen mukaan"), and at
     display size one long word in a narrow card would widen the whole page
     instead of wrapping inside the card. */
  overflow-wrap: break-word;
  hyphens: auto;
  -webkit-hyphens: auto;
}
/* The prefix and the period read as one phrase with the figure. Kept from
   breaking inside themselves, so a narrow card wraps "/ kk" as a unit instead of
   stranding the slash at the end of the line above. */
.plan__prefix,
.plan__period {
  white-space: nowrap;
  font-family: var(--font-body);
  font-size: var(--step-0);
  font-weight: 400;
  color: var(--color-muted);
}
.plan__list {
  list-style: none;
  padding: 0;
  display: grid;
  gap: var(--space-3xs);
  color: var(--color-muted);
}
.plan__action {
  margin-top: auto;
  padding-top: var(--space-xs);
}

/* ── Testimonial (oversized serif quote between thin rules — no tinted box) ── */
.testimonial {
  max-width: 40ch;
  margin-inline: auto;
  padding-block: var(--space-l);
  text-align: center;
  border-top: var(--border-hair);
  border-bottom: var(--border-hair);
}
.testimonial__quote {
  font-family: var(--font-heading);
  font-weight: 500;
  font-size: var(--step-3);
  line-height: 1.25;
}
.testimonial__author {
  display: block;
  margin-top: var(--space-s);
  font-family: var(--font-body);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  font-size: var(--step--1);
  color: var(--color-muted);
}

/* ── FAQ ─────────────────────────────────────────────────────────────────── */
.faq {
  max-width: 52rem;
  margin-inline: auto;
  border-top: var(--border-hair);
}
.faq__item {
  padding-block: var(--space-s);
  border-bottom: var(--border-hair);
}
.faq__question {
  font-size: var(--step-1);
  margin-bottom: var(--space-2xs);
}
.faq__answer {
  color: var(--color-muted);
  max-width: 60ch;
}
/* An answer longer than one paragraph (two paragraphs, or a paragraph and a list)
   needs the same spacing as a card: with every margin reset, its pieces would run
   together. */
.faq__answer > * + * {
  margin-top: var(--space-2xs);
}

/* ── Contact form (posts via mailto — no runtime dependency) ─────────────── */
.contact-form {
  display: grid;
  gap: var(--space-s);
  max-width: 40rem;
}
.contact-form__field {
  display: grid;
  gap: var(--space-3xs);
}
.contact-form__label {
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-size: var(--step--1);
}
.contact-form__input,
.contact-form__textarea {
  width: 100%;
  padding: 0.8rem 1rem;
  background: var(--color-surface);
  border: var(--border-hair);
  border-radius: var(--radius-s);
}
.contact-form__input:focus,
.contact-form__textarea:focus {
  outline: none;
  border-color: var(--color-primary);
  box-shadow: 0 0 0 2px var(--color-primary-tint);
}
.contact-form__textarea {
  min-height: 8rem;
  resize: vertical;
}

/* ── Footer ──────────────────────────────────────────────────────────────── */
.site-footer {
  padding-block: var(--space-xl) var(--space-l);
  background: var(--color-surface-alt);
  border-top: var(--border-rule); /* broadsheet rule closes the page */
}
.site-footer__inner {
  display: grid;
  gap: var(--space-l);
  grid-template-columns: 1fr;
}
@media (min-width: 48rem) {
  .site-footer__inner {
    grid-template-columns: repeat(3, 1fr);
  }
}
.site-footer__heading {
  font-family: var(--font-heading);
  font-size: var(--step-1);
  margin-bottom: var(--space-s);
}
.site-footer__link {
  display: block;
  padding: var(--space-3xs) 0;
  color: var(--color-text);
  text-decoration: none;
}
.site-footer__link:hover {
  color: var(--color-primary);
}
.site-footer__legal {
  margin-top: var(--space-l);
  padding-top: var(--space-s);
  border-top: var(--border-hair);
  color: var(--color-muted);
  font-size: var(--step--1);
}

/* ── Legal page ──────────────────────────────────────────────────────────── */
.legal {
  max-width: 62ch;
  margin-inline: auto;
  padding-block: var(--section-pad);
}
.legal__title {
  margin-bottom: var(--space-m);
}
.legal__body > * + * {
  margin-top: var(--space-s);
}
.legal__body h2 {
  font-size: var(--step-2);
  margin-top: var(--space-l);
}

/* ── Cookie banner (used only when a site sets cookies; main.js dismisses) ── */
.cookie-banner {
  position: fixed;
  left: var(--space-s);
  right: var(--space-s);
  bottom: var(--space-s);
  z-index: 80; /* above content, below the skip link */
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-s);
  padding: var(--space-s) var(--space-m);
  background: var(--color-surface);
  border: var(--border-hair);
  border-radius: var(--radius-m);
  /* A fixed overlay cannot float on whitespace alone — the one place a shadow is used. */
  box-shadow: var(--shadow-float);
}
.cookie-banner__text {
  flex: 1 1 16rem;
  color: var(--color-muted);
  font-size: var(--step--1);
}
.cookie-banner__actions {
  display: flex;
  gap: var(--space-2xs);
}

/* ── Demo banner (pre-contract preview marker) ───────────────────────────── */
.demo-banner {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 90; /* above the cookie banner */
  padding: var(--space-2xs) var(--space-s);
  text-align: center;
  background: var(--color-primary); /* ink block carries --color-on-primary at >= 4.5:1 */
  color: var(--color-on-primary);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-size: var(--step--1);
}

/* The global `a` rule sets the link colour directly, which beats the colour the
   banner merely passes down by inheritance — so without this the CTA renders in
   --color-primary on a --color-primary background at 1:1 and disappears. */
.demo-banner a {
  color: inherit;
}


/* ── Error (404) page ────────────────────────────────────────────────────── */
.error-page {
  display: grid;
  place-content: center;
  gap: var(--space-s);
  min-height: 60vh;
  padding: var(--section-pad) var(--space-s);
  text-align: center;
}
.error-page__code {
  font-family: var(--font-heading);
  font-size: var(--step-5);
  color: var(--color-primary);
}
.error-page__title {
  font-size: var(--step-3);
}
.error-page__text {
  color: var(--color-muted);
  max-width: 40ch;
  margin-inline: auto;
}

/* ── Motion keyframes ────────────────────────────────────────────────────── */
/* hero-settle: the hero media eases up ~14px on load, slowly. Transform-only —
   no opacity fade — so the (large) hero media paints immediately and never
   delays Largest Contentful Paint. */
@keyframes hero-settle {
  from {
    transform: translateY(14px);
  }
  to {
    transform: none;
  }
}
/* rule-grow: a horizontal accent rule scales in left-to-right for section-head
   and kicker underlines. */
@keyframes rule-grow {
  from {
    transform: scaleX(0);
  }
  to {
    transform: scaleX(1);
  }
}

/* ── Map, click-to-load (only on a page with a confirmed street address) ──── */
/* The block reserves its space up front and holds only plain markup: nothing is
   requested from Google until the visitor presses the button, at which point
   main.js swaps the placeholder for the map frame. */
.map-embed {
  position: relative;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  background: var(--color-surface-alt);
  border: var(--border-hair);
  border-radius: var(--radius-m);
}
@media (min-width: 45rem) {
  .map-embed {
    aspect-ratio: 16 / 9;
  }
}
.map-embed__placeholder {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-xs);
  padding: var(--space-s);
  text-align: center;
}
/* The small print that tells the visitor what pressing the button will load. */
.map-embed__note {
  max-width: 44ch;
  color: var(--color-muted);
  font-size: var(--step--1);
}
/* Hidden without JavaScript, where pressing it could only do nothing; the link
   below it opens the same location in that case. */
.map-embed__load {
  display: none;
}
.js .map-embed__load {
  display: inline-flex;
}
.map-embed__link {
  font-size: var(--step--1);
}
.map-embed__frame {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}

/* Detect disabled scripting before first paint; a delayed script must not collapse a tall header. */
@media (max-width: 47.99rem) and (scripting: none) {
  html:not(.js) .site-header { position: relative; }
  html:not(.js) .site-header__inner { flex-wrap: wrap; }
  html:not(.js) .site-header nav { width: 100%; }
  html:not(.js) .nav__toggle { display: none; }
  html:not(.js) .nav__menu {
    display: flex;
    position: static;
    flex-direction: column;
    align-items: stretch;
    width: 100%;
    min-width: 0;
    box-shadow: none;
  }
  html:not(.js) .nav__link { white-space: normal; overflow-wrap: anywhere; }
}

/* ── Site-specific composition ── */
/* Episode titles carry long Finnish compounds ("ihmisoikeustuomioistuimeen", 26 letters) that are
   wider than a 320px column at the question size; break them like card text instead of widening the page. */
.faq__question,
.faq__answer {
  overflow-wrap: anywhere;
  hyphens: auto;
}
/* The episode archive opens its years with native <details>; the summary is the click target. */
summary.faq__question {
  cursor: pointer;
}
/* Episode lists sit flush with their section heading instead of centring in the measure. */
.section .faq {
  margin-inline: 0;
}
/* The illustration that follows the channel cards keeps one large spacing step from them. */
.card-grid + .gallery {
  margin-top: var(--space-l);
}
/* "Puheenaihe" in Playfair 600 at the title's 320px size (about 49px) is wider than the 257px
   column and broke mid-word; below 360px the title steps down so the name stays whole. */
@media (max-width: 22.5rem) {
  .hero__title {
    font-size: 2.75rem;
  }
}
/* The 404 page centres its text, so its two actions centre with it. */
.error-page .hero__actions {
  justify-content: center;
}

/* The fixed demo banner would otherwise cover the end of the footer: reserve its height
   (measured at 2 lines on phones, 1 line from tablet width up) below the page content. */
body:has(> .demo-banner) {
  padding-bottom: 6rem;
}
@media (min-width: 48em) {
  body:has(> .demo-banner) {
    padding-bottom: 3.5rem;
  }
}
