/* somiska.fi - the Claude Design export, built
   -------------------------------------------------------------------------
   This stylesheet reproduces the composition export archived in the hub at
   clients/somiska/experiment/claude-design-export-2026-09-12/. Sizes,
   weights, colours, spacing and rules are the export's own values; the
   desktop composition is 1200px wide with 40px gutters, and every fluid
   value reaches the export's number at a 1200px viewport.

   Three deliberate departures from the export are marked DEVIATION below and
   recorded in DESIGN.md: the gold used for small text on paper, the active
   nav item's underline, and the footer's address line. Each exists because
   the export's own value fails an accessibility contract. DEVIATION 2, the
   closing band's scrim, went with the closing band itself on 2026-09-12; its
   number is kept in DESIGN.md in case the band ever comes back.

   Every colour is a token declared in the single :root block below;
   check-design.py rejects a hex value anywhere else in this file. */

/* ----------------------------------------------------------------- fonts
   Self-hosted, same-origin, SIL Open Font Licence. No CDN and no
   third-party request: _headers ships default-src 'self'. The export loaded
   these three families from Google Fonts; the files come from the hub
   typography kit instead. Only the styles used are shipped.

   The export asks for Fraunces 500 and Alegreya Sans 600, neither of which
   the kit has. Alegreya Sans 600 resolves to 700 under CSS font matching,
   which is what the export renders too, so those rules declare 700 outright.
   Fraunces 500 is resolved per element to 400 or 600; see DESIGN.md. */

@font-face {
  font-family: "Fraunces";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/fonts/fraunces-v38-latin_latin-ext-regular.woff2") format("woff2");
}

@font-face {
  font-family: "Fraunces";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/fonts/fraunces-v38-latin_latin-ext-600.woff2") format("woff2");
}

@font-face {
  font-family: "Fraunces";
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url("/fonts/fraunces-v38-latin_latin-ext-italic.woff2") format("woff2");
}

@font-face {
  font-family: "Literata";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/fonts/literata-v40-latin_latin-ext-regular.woff2") format("woff2");
}

@font-face {
  font-family: "Literata";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/fonts/literata-v40-latin_latin-ext-600.woff2") format("woff2");
}

@font-face {
  font-family: "Alegreya Sans";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/fonts/alegreya-sans-v28-latin_latin-ext-regular.woff2") format("woff2");
}

@font-face {
  font-family: "Alegreya Sans";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/fonts/alegreya-sans-v28-latin_latin-ext-500.woff2") format("woff2");
}

@font-face {
  font-family: "Alegreya Sans";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("/fonts/alegreya-sans-v28-latin_latin-ext-700.woff2") format("woff2");
}

:root {
  /* --- grounds --- */
  --paper: #F5F0E6;          /* page ground, the export's body background */
  --card: #FBF8F1;           /* lifted card and photo mat */
  --night: #122232;          /* dark band */
  --night-deep: #0E1B29;     /* footer only */

  /* --- ink on paper --- */
  --ink: #16222E;            /* headings, button label */
  --ink-mark: #1A2633;       /* wordmark and the current nav item */
  --ink-body: #3A4856;       /* running text */
  --ink-muted: #4A5866;      /* secondary text, resting nav items */

  /* --- light on night --- */
  --cream: #F0EBE1;          /* headings and strong text */
  --cream-soft: #E8E4DC;     /* the dark bands' inherited body colour */
  --cream-dim: #D8D2C6;      /* ruled lists and step copy */
  --cream-mute: #C9C2B4;     /* running text */

  /* --- gold --- */
  --gold: #C9963F;           /* button fill; accent text on night (6.09:1) */
  --gold-fill-hover: #B8862F;
  --gold-lit: #D4A85A;       /* accent on the scrimmed closing band */
  --gold-deep: #A9782B;      /* the export's paper accent: LARGE text only */
  /* DEVIATION 1. #A9782B reads 3.42:1 on --paper: it clears the 3:1 floor
     for large text and fails the 4.5:1 floor for everything smaller. Small
     text on paper uses this value instead, the same hue and saturation
     darkened by the least that reaches 4.5:1 (HSL lightness 0.416 -> 0.353).
     4.52:1 on --paper, 4.84:1 on --card. The export's own hover, #8A5F1D,
     already passes at 4.95:1 and is used unchanged. */
  --gold-text: #8F6624;
  --gold-text-hover: #8A5F1D;

  /* --- footer --- */
  --slate: #8A94A0;          /* footer copy (5.65:1 on --night-deep) */
  /* DEVIATION 4. The export sets the footer's address line one step quieter
     than the legal line, at #6E7A88, which reads 3.98:1 on --night-deep and
     fails the 4.5:1 floor for 15px text. This is --slate's own hue and
     saturation darkened by the least that still reaches 4.5:1 (HSL lightness
     0.584 -> 0.517), the same method DEVIATION 1 uses on paper. 4.51:1. */
  --slate-dim: #778391;      /* the footer's address line only */
  --linen: #C9BFAE;          /* footer links (9.56:1) */

  /* --- channels: hairlines, scrims and mats only, never text --- */
  --gold-deep-rgb: 169 120 43;
  --gold-rgb: 201 150 63;
  --ink-mark-rgb: 26 38 51;
  --night-rgb: 18 34 50;
  --paper-rgb: 245 240 230;
  --white-rgb: 255 255 255;

  /* --- ground context. Paper is the default; .band, .band--scrim, .card
         and footer re-point these and every component reads them. --- */
  --c-head: var(--ink);
  --c-fg: var(--ink-body);
  --c-mute: var(--ink-muted);
  --c-accent: var(--gold-text);
  --c-accent-hover: var(--gold-text-hover);
  --c-accent-large: var(--gold-deep);
  --c-rule: rgb(var(--gold-deep-rgb) / 0.4);
  --c-rule-soft: rgb(var(--ink-mark-rgb) / 0.15);
  --c-leader: rgb(var(--ink-mark-rgb) / 0.4);
  --c-btn-fill: var(--gold);
  --c-btn-fill-hover: var(--gold-fill-hover);
  --c-btn-text: var(--ink);
  /* The ring sits 3px outside its element, so it lands on the ground around
     it and is measured against that ground, never against the fill. */
  --c-ring: var(--gold-text);

  /* --- type --- */
  --font-display: "Fraunces", Georgia, "Times New Roman", "Liberation Serif", serif;
  --font-body: "Literata", Georgia, "Times New Roman", "Liberation Serif", serif;
  --font-ui: "Alegreya Sans", "Segoe UI", system-ui, -apple-system, "Helvetica Neue", Arial, sans-serif;

  /* Fixed small sizes, exactly as the export sets them. */
  --t-12: 0.75rem;
  --t-13: 0.8125rem;
  --t-14: 0.875rem;
  --t-15: 0.9375rem;
  --t-16: 1rem;

  /* Fluid sizes. Each reaches the export's px value at a 1200px viewport
     (Npx / 1200 * 100 = the vw term) and shrinks below it. */
  --t-17: clamp(1rem, 1.42vw, 1.0625rem);
  --t-18: clamp(1.0625rem, 1.5vw, 1.125rem);
  --t-19: clamp(1.0625rem, 1.58vw, 1.1875rem);
  --t-26: clamp(1.25rem, 2.17vw, 1.625rem);
  --t-27: clamp(1.25rem, 2.25vw, 1.6875rem);
  --t-38: clamp(1.5rem, 3.17vw, 2.375rem);
  --t-42: clamp(1.625rem, 3.5vw, 2.625rem);
  --t-44: clamp(1.625rem, 3.67vw, 2.75rem);
  --t-50: clamp(1.875rem, 4.17vw, 3.125rem);
  --t-54: clamp(1.875rem, 4.5vw, 3.375rem);
  --t-64: clamp(2.125rem, 5.33vw, 4rem);

  /* --- spacing. The export's numbers; the fluid ones land on the export
         value at 1200px. --- */
  --s-2: 0.125rem;
  --s-6: 0.375rem;
  --s-8: 0.5rem;
  --s-10: 0.625rem;
  --s-11: 0.6875rem;
  --s-12: 0.75rem;
  --s-14: 0.875rem;
  --s-16: 1rem;
  --s-18: 1.125rem;
  --s-20: 1.25rem;
  --s-22: 1.375rem;
  --s-24: 1.5rem;
  --s-28: 1.75rem;
  --s-32: 2rem;
  --s-36: 2.25rem;
  --s-40: 2.5rem;
  --s-48: 3rem;
  --s-64: 4rem;

  --gut: clamp(1.25rem, 3.33vw, 2.5rem);      /* 40px */
  --gut-wide: clamp(1.25rem, 4.67vw, 3.5rem); /* 56px, header and footer */
  --gap: clamp(2rem, 6vw, 4.5rem);            /* 72px, the standard column gap */
  --gap-wide: clamp(2rem, 6.67vw, 5rem);      /* 80px */
  --pad-band: clamp(3.5rem, 7.33vw, 5.5rem);  /* 88px, band padding */
  --pad-band-lg: clamp(4rem, 8vw, 6rem);      /* 96px, the front closing band */
  --pad-tail: clamp(3rem, 6vw, 4.5rem);       /* 72px, a band's shorter side */

  --wrap: 1200px;
  --col-side: 440px;   /* the label/heading column in a night section */
  --col-portrait: 420px;
  --col-portrait-tight: 340px; /* minusta's shorter hero frame, see .frame--portrait-tight */
  --col-room: 380px;
  --col-card: 480px;

  --motion: 150ms ease;
}

*,
*::before,
*::after { box-sizing: border-box; }

/* Belt and braces against a sideways scroll: every grid track below is
   minmax(0, ...) so nothing can push the page wider than the viewport, and
   these two lines catch anything that slips through. */
html { -webkit-text-size-adjust: 100%; overflow-x: hidden; }

/* In-page anchor jumps (e.g. minusta.html's #koulutus link) get a smooth
   scroll only when the reader hasn't asked to avoid motion; the reduced-
   motion rule below already strips all transitions regardless. */
@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}

/* The column runs header / main / footer at a minimum of the viewport's own
   height, so a page with little content (yhteystiedot, which is now one hero
   and nothing else) does not leave the footer floating mid-screen with paper
   below it. On a page taller than the viewport nothing changes: main's basis
   is its content and it only ever grows. No vertical margin between these
   three is relied on anywhere, so nothing depended on the margin collapsing
   that a flex container now prevents. */
body {
  margin: 0;
  background: var(--paper);
  color: var(--ink-mark);
  font-family: var(--font-body);
  font-size: var(--t-17);
  line-height: 1.68;
  overflow-x: hidden;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

main {
  flex: 1 0 auto;
  display: flex;
  flex-direction: column;
}

img { max-width: 100%; height: auto; }

a {
  color: var(--c-accent);
  text-decoration: none;
  transition: color var(--motion), background-color var(--motion),
              border-color var(--motion);
}

a:hover { color: var(--c-accent-hover); }

a:focus-visible {
  outline: 2px solid var(--c-ring);
  outline-offset: 3px;
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 20;
  background: var(--ink);
  color: var(--cream);
  padding: var(--s-12) var(--s-16);
  font-family: var(--font-ui);
  font-weight: 700;
  font-size: var(--t-13);
  letter-spacing: 0.08em;
}

.skip-link:focus { left: var(--s-12); top: var(--s-12); }
.skip-link:focus-visible { outline: 2px solid var(--gold-text); outline-offset: 3px; }

.wrap {
  max-width: var(--wrap);
  margin: 0 auto;
  padding-left: var(--gut);
  padding-right: var(--gut);
}

/* ------------------------------------------------------- ground contexts
   One block per ground. Components never name a colour; they read --c-*. */

.band {
  background: var(--night);
  color: var(--cream-soft);
}

.band,
footer {
  --c-head: var(--cream);
  --c-fg: var(--cream-mute);
  --c-mute: var(--cream-mute);
  --c-accent: var(--gold);
  --c-accent-hover: var(--gold-lit);
  --c-accent-large: var(--gold);
  --c-rule: rgb(var(--gold-rgb) / 0.25);
  --c-rule-soft: rgb(var(--gold-rgb) / 0.3);
  --c-leader: rgb(var(--gold-rgb) / 0.3);
  --c-btn-fill: var(--gold);
  --c-btn-fill-hover: var(--gold-fill-hover);
  --c-btn-text: var(--ink);
  /* On any dark ground the ring is cream: 13.59:1 on --night, 14.64:1 on the
     footer. A gold ring here would be 6.09:1 and still pass, but cream is the
     loudest thing available and a focus ring should be the loudest thing on
     the page. */
  --c-ring: var(--cream);
}

/* The card is paper wherever it lands, so money is always read in the light. */
.card {
  background: var(--card);
  border: 1px solid rgb(var(--gold-deep-rgb) / 0.4);
  padding: var(--s-40);
  color: var(--ink-body);
  --c-head: var(--ink);
  --c-fg: var(--ink-body);
  --c-mute: var(--ink-muted);
  --c-accent: var(--gold-text);
  --c-accent-hover: var(--gold-text-hover);
  --c-accent-large: var(--gold-deep);
  --c-rule: rgb(var(--gold-deep-rgb) / 0.4);
  --c-leader: rgb(var(--ink-mark-rgb) / 0.4);
  --c-ring: var(--gold-text);
}

/* ------------------------------------------------------------- masthead */

header {
  background: var(--paper);
  border-bottom: 1px solid rgb(var(--ink-mark-rgb) / 0.15);
}

.masthead {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--s-20) var(--s-32);
  flex-wrap: wrap;
  padding: var(--s-24) var(--gut-wide);
}

.brand { display: block; }

.brand-role {
  display: block;
  font-family: var(--font-ui);
  font-weight: 700;
  font-size: var(--t-12);
  letter-spacing: 0.32em;
  color: var(--gold-text);
}

.brand-name {
  display: block;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.5rem;
  line-height: 1.2;
  color: var(--ink-mark);
  margin-top: var(--s-2);
}

.brand:hover .brand-name { color: var(--ink); }
.brand:hover .brand-role { color: var(--gold-text-hover); }

.masthead nav ul {
  display: flex;
  flex-wrap: wrap;
  list-style: none;
  margin: 0;
  padding: 0;
  gap: 0 var(--s-32);
}

.masthead nav a {
  display: inline-block;
  font-family: var(--font-ui);
  font-weight: 400;
  font-size: var(--t-14);
  line-height: 1.7;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-muted);
  /* Padding, not font-size, brings the tap area to 47.8px: a 23.8px line box
     plus 12px above and below. */
  padding: var(--s-12) 0;
  border-bottom: 1px solid transparent;
}

.masthead nav a:hover { color: var(--ink); }

/* The contact item is gold at rest in the export: on any page it reads as
   the way out. Small text, so it takes the darkened gold. */
.masthead nav .nav-cta { color: var(--gold-text); }
.masthead nav .nav-cta:hover { color: var(--gold-text-hover); }

/* DEVIATION 3. The export marks the current page by colour alone: --ink-mark
   against the --ink-muted of every other item. That is information carried by
   colour and nothing else, so the current item also takes a gold rule under
   it, and aria-current="page" in the markup. */
.masthead nav a[aria-current="page"] {
  color: var(--ink-mark);
  border-bottom-color: var(--gold-text);
}

/* The phone menu toggle is a checkbox, not <details>/<summary>: a closed
   <details> element does not render its non-summary content regardless of
   what CSS says about it, so the nav list disappeared at every width when
   the disclosure was closed by default. A checkbox has no such native
   hiding behaviour — visibility is entirely ours to declare, so plain CSS
   can show the nav at wide widths and gate it on :checked at narrow ones.
   The input sits right before .nav-button and nav in the markup so both
   can be reached with `~`. Visually hidden with the clip technique (not
   display:none) so it stays focusable and operable by keyboard; the label
   right after it is the visible button. */
.nav-toggle {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.nav-button {
  display: none;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  cursor: pointer;
}

.nav-toggle:focus-visible ~ .nav-button {
  outline: 2px solid var(--c-ring);
  outline-offset: 2px;
}

.nav-toggle-icon,
.nav-toggle-icon::before,
.nav-toggle-icon::after {
  width: 22px;
  height: 2px;
  background: var(--ink-mark);
  transition: transform var(--motion), background-color var(--motion);
}

.nav-toggle-icon {
  position: relative;
  display: block;
}

.nav-toggle-icon::before,
.nav-toggle-icon::after {
  content: "";
  position: absolute;
  left: 0;
}

.nav-toggle-icon::before { top: -7px; }
.nav-toggle-icon::after { top: 7px; }

/* Checked state turns the three lines into a cross rather than adding a
   second icon. */
.nav-toggle:checked ~ .nav-button .nav-toggle-icon { background: transparent; }
.nav-toggle:checked ~ .nav-button .nav-toggle-icon::before { transform: translateY(7px) rotate(45deg); }
.nav-toggle:checked ~ .nav-button .nav-toggle-icon::after { transform: translateY(-7px) rotate(-45deg); }

/* Below 56rem the brand plus five nav items no longer fit one row. The
   masthead becomes two blocks: brand, then nav under a hairline. Untouched
   by the checkbox menu below: it only starts acting at 44rem. */
@media (max-width: 55.999rem) {
  .masthead {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--s-6);
  }

  .masthead nav {
    width: 100%;
    border-top: 1px solid rgb(var(--ink-mark-rgb) / 0.15);
    padding-top: var(--s-2);
  }

  .masthead nav ul { gap: 0 var(--s-24); }
}

/* 44rem is the site's existing phone line (.photo-row switches at the same
   point). Below 32rem the five items measure about 374px of text and need
   470px with the gaps, against 350px of content on a 390px phone — no
   wrapped grid fits without a lopsided last row, and the 32-44rem range is
   just as tight. Below this point the masthead reverts to one row (brand,
   then the toggle at the right edge) and the nav list becomes a checkbox
   disclosure instead: closed by default, no JavaScript, the browser's own
   keyboard and click handling. 44rem and up: unchanged from before this
   menu existed — the checkbox and button are hidden and the nav list is
   the plain, always-visible flex row from the base rules above. */
@media (max-width: 43.999rem) {
  /* Anchors the opened dropdown to the full header width, not to the
     masthead's own padding box. */
  header { position: relative; }

  .masthead {
    flex-direction: row;
    flex-wrap: nowrap;
    align-items: center;
    /* nav (below) stays a real, static flex child — position:absolute would
       make it the ul's containing block instead of header, and the dropdown
       needs to stay anchored to the full header width. That leaves it as a
       third, empty flex item while the ul beneath it is closed, so the base
       column-gap is zeroed here and .nav-button's own auto margin takes the
       freed space instead; that is what puts the button flush at the right
       edge rather than splitting the space with the empty nav item under
       the base space-between rule. */
    column-gap: 0;
  }

  .masthead nav {
    width: auto;
    border-top: none;
    padding-top: 0;
  }

  .nav-button {
    display: flex;
    margin-left: auto;
  }

  .masthead nav ul { display: none; }

  .nav-toggle:checked ~ nav ul {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    background: var(--paper);
    border-top: 1px solid rgb(var(--ink-mark-rgb) / 0.15);
    padding: var(--s-12) var(--gut);
    z-index: 20;
  }

  .nav-toggle:checked ~ nav ul a { display: block; }
}

@media (min-width: 44rem) {
  .nav-toggle,
  .nav-button {
    display: none;
  }
}

/* ------------------------------------------------------------ typography */

h1, h2, h3 {
  font-family: var(--font-display);
  font-weight: 400;
  color: var(--c-head);
  margin: 0;
}

/* The italic in a heading is the display face's own drawn italic in the
   accent, on place names and the one word the line turns on. At 27px and up
   it is large text, so it keeps the export's gold. */
h1 em, h2 em {
  font-style: italic;
  font-weight: 400;
  color: var(--c-accent-large);
}

.eyebrow {
  font-family: var(--font-ui);
  font-weight: 700;
  font-size: var(--t-14);
  line-height: 1.4;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--c-accent);
  margin: 0 0 var(--s-24);
}

.band .eyebrow { margin-bottom: var(--s-18); }

.crumb {
  font-family: var(--font-ui);
  font-weight: 700;
  font-size: var(--t-13);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-muted);
  margin: 0;
}

.crumb a { color: var(--c-accent); }

p { margin: 0 0 var(--s-20); }
p:last-child { margin-bottom: 0; }

/* ---------------------------------------------------------------- header
   The light top of every page. The front page and Minusta run it as a grid
   with the portrait beside; Palvelut, Hinnat and Yhteystiedot run it as a
   single column or with a card. */

.page-top {
  padding-top: var(--pad-band);
  padding-bottom: var(--pad-band);
}

.page-top--tail { padding-bottom: var(--pad-tail); }

/* A page that is only its hero. It takes whatever height main has left over
   and centres the column in it, so the footer sits at the bottom of the
   screen rather than halfway up it. One page uses this: yhteystiedot. */
.page-top--fill {
  flex: 1 0 auto;
  display: flex;
  align-items: center;
  padding-top: var(--pad-band-lg);
  padding-bottom: var(--pad-band-lg);
}

.page-top--fill > .wrap { width: 100%; }

.top-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--gap);
  align-items: center;
}

@media (min-width: 56rem) {
  .top-grid { grid-template-columns: minmax(0, 1fr) minmax(0, var(--col-portrait)); }
  .top-grid--room { grid-template-columns: minmax(0, 1fr) minmax(0, var(--col-room)); }
  .top-grid--card {
    grid-template-columns: minmax(0, 1fr) minmax(0, var(--col-card));
    align-items: start;
  }
}

/* The reader's own thought, and the largest type on the site. Fraunces 400
   at 50px: the export asks for 500, and at this size the lighter cut keeps
   the line a thought rather than a claim. */
.voice {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: var(--t-50);
  line-height: 1.15;
  color: var(--ink);
  max-width: 640px;
  margin: 0 0 var(--s-28);
  text-wrap: pretty;
}

/* The front page h1 answers the voice underneath at about half its size and
   in the body ink, not the heading ink. */
.hero-h1 {
  font-size: var(--t-27);
  line-height: 1.35;
  color: var(--ink-body);
  margin: 0 0 var(--s-22);
}

.page-h1 {
  font-size: var(--t-54);
  line-height: 1.1;
  color: var(--ink);
  max-width: 760px;
  margin: 0 0 var(--s-28);
}

.page-h1--tall {
  font-size: var(--t-64);
  line-height: 1.05;
  margin-bottom: var(--s-28);
}

.crumb + .page-h1 { margin-top: var(--s-24); }

.lead {
  font-size: var(--t-19);
  line-height: 1.68;
  color: var(--ink-body);
  max-width: 580px;
  margin: 0;
  text-wrap: pretty;
}

.lead--wide { max-width: 640px; }
.lead--narrow { max-width: 520px; }

/* .lead carries margin: 0, so a hero that runs to two lead paragraphs
   (minusta, since its work history moved up into the hero) needs the gap
   declared here rather than left to the base p rule the class overrides. */
.lead + .lead { margin-top: var(--s-18); }

.hero-lead {
  font-size: var(--t-18);
  line-height: 1.68;
  color: var(--ink-body);
  max-width: 580px;
  margin: 0 0 var(--s-20);
  text-wrap: pretty;
}

.hero-body {
  font-size: var(--t-17);
  line-height: 1.65;
  color: var(--ink-muted);
  max-width: 580px;
  margin: 0 0 var(--s-36);
  text-wrap: pretty;
}

/* --------------------------------------------------------------- actions */

/* On the contact page the button row follows a lead paragraph directly, so
   the gap above it belongs to the row rather than to the paragraph. */
.actions--spaced { margin-top: var(--s-36); }

.actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-16) var(--s-20);
}

.btn {
  display: inline-block;
  background: var(--c-btn-fill);
  color: var(--c-btn-text);
  font-family: var(--font-ui);
  font-weight: 700;
  font-size: var(--t-15);
  line-height: 1.2;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: var(--s-16) var(--s-28);
}

.btn:hover { background: var(--c-btn-fill-hover); color: var(--c-btn-text); }

.aside {
  font-family: var(--font-ui);
  font-weight: 400;
  font-size: var(--t-17);
  color: var(--c-mute);
  margin: 0;
  max-width: 34rem;
}

/* ----------------------------------------------------------------- frame
   Documentary photographs (rooms, the method image) are matted the way a
   picture in a room is: a hairline of gold and a margin of paper, or on
   night a wash of translucent white. The export's mat is 12px on paper,
   10px on night.

   The portrait is the one photograph of Sami himself rather than a room, so
   it carries the site's one bolder frame instead: the mat itself is solid
   gold. No hairline on that variant — with mat and border the same colour
   it drew nothing, so it's dropped rather than shipped as dead paint. See
   DESIGN.md's "Frame" section for both variants and their contrast. */

.frame {
  border: 1px solid var(--gold);
  background: var(--card);
  padding: var(--s-12);
  margin: 0;
}

.frame img { display: block; width: 100%; }

.band .frame {
  background: rgb(var(--white-rgb) / 0.04);
  padding: var(--s-10);
}

/* The portrait slot: 394px of image in a 420px frame, the size the export
   asks for. The file ships at 788x880, twice the slot. */
.frame--portrait {
  width: min(100%, var(--col-portrait));
  background: var(--gold);
  border: none;
}
.frame--portrait img { aspect-ratio: 394 / 440; object-fit: cover; }

/* Minusta's hero portrait is a little narrower than the front page's, so
   the frame is shorter and the first dark heading clears the fold sooner.
   340px is narrower than the grid's own 420px column (--col-portrait), and
   a block box with an explicit width start-aligns in its grid area by
   default, so without help the frame would sit flush left with 80px of
   empty column to its right rather than reaching the wrap's true right
   edge. justify-self: end closes that gap so the frame's right edge lands
   where the full-width portrait's does elsewhere — the edge the photo row
   below it is measured against. */
.frame--portrait-tight {
  width: min(100%, var(--col-portrait-tight));
  justify-self: end;
}

.frame--room img { aspect-ratio: 356 / 440; object-fit: cover; }

/* A documentary photograph filling the right-hand column of a .split: the two
   reception rooms on vastaanotot. The 3:2 box is a CSS-only centre crop of
   the 1200x900 files already in the repo, the same guard .photo-row uses, so
   no new image file was cut for this slot. max-width matches .split p so the
   photograph and the paragraph under it share one right edge. */
.frame--wide {
  max-width: 620px;
  margin-bottom: var(--s-20);
}

.frame--wide img { aspect-ratio: 3 / 2; object-fit: cover; }

/* A reception's street address, under the heading in a .split's label column.
   --gold-lit on --night is 7.34:1, the same pairing the old contact page's
   room addresses used. */
.split-address {
  font-family: var(--font-ui);
  font-weight: 500;
  font-style: normal;
  font-size: var(--t-15);
  color: var(--gold-lit);
  margin-top: var(--s-12);
}

/* Two photographs of the same room, now equal 3:2 frames rather than the
   earlier 1.7fr/1fr split — object-fit: cover crops each source to the box,
   so neither file needs a physical recrop for this. The row is exactly as
   wide as the hero content above (.wrap, plus the tight portrait's own
   right-aligned fix above), so its left edge meets the intro text and its
   right edge meets the portrait frame. The row sits below the fold at
   1440x900 on purpose (2026-09-03: the in-page link that points at it moved
   into the hero, so the row no longer has to fit above the fold — it reads
   as an invitation to keep scrolling, not a thing that must be seen first). */
.photo-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--s-20);
  padding-bottom: var(--pad-band);
}

/* Stacked, each photograph keeps its own shape. Side by side, both are cut
   to the same 3:2 box so the row reads as one system. */
.photo-row img { object-fit: cover; aspect-ratio: 3 / 2; }

@media (min-width: 44rem) {
  .photo-row { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
}

/* Phones only: the row is decorative filler ahead of the page's real
   content (the work-history sections), so it is dropped rather than
   stacked. `loading="lazy"` on both images means a hidden row never
   downloads them. */
@media (max-width: 43.999rem) {
  .photo-row { display: none; }
}

/* ------------------------------------------------------------ photo bands
   Decorative, no text over either. The divider is the transition from the
   lamplit top of the front page into the evening room below it. Neither
   image carries information, so neither takes alt text: they are CSS
   backgrounds. See DESIGN.md > Images. */

.band-divider {
  height: clamp(7rem, 15vw, 11.25rem);
  background-color: var(--night);
  background-image:
    linear-gradient(180deg, rgb(var(--paper-rgb) / 0.2) 0%, rgb(var(--night-rgb) / 0.4) 100%),
    url("/images/meri-tyyni-2400.webp");
  background-size: cover;
  background-position: 50% 50%;
  background-repeat: no-repeat;
}

/* The same divider, on the ten subpages that were cutting straight from
   cream to navy with no transition, so the dark half read as a footer and
   people stopped scrolling. Sized to about a third of the front page's own
   divider: enough to register as a deliberate hinge between the two halves
   without competing with the front page's, which sits at a real narrative
   pivot mid-scroll. Same file, same fade, no new asset. */
.band-divider--sub { height: clamp(2.33rem, 5vw, 3.75rem); }

/* A hairline only where the export draws one: between two dark bands. The
   first dark band on a page opens without one. */
.band + .band { border-top: 1px solid rgb(var(--gold-rgb) / 0.25); }

.band-inner {
  padding-top: var(--pad-band);
  padding-bottom: var(--pad-band);
}

/* ----------------------------------------------------------------- facts
   The front page's eligibility strip: one highlighted cream band between
   the hero and the seascape divider, not four separate tab-like columns.
   It carries no bottom margin: the divider begins where the strip's own
   bottom hairline ends, as the export draws it. Each item is a
   short phrase after a CSS-drawn tick. At 44rem+ the group spans the wrap's
   full content column (space-between, first/last item flush to the hero's
   own left/right edges) rather than sitting as a tight centred cluster -
   changed 2026-09-04 so the strip reads as belonging to the column above
   it, not as a floating badge. Below 44rem it's a two-by-two grid,
   unaffected by the space-between rule. */

.fact-strip {
  background: var(--card);
  border-top: 1px solid rgb(var(--gold-deep-rgb) / 0.4);
  border-bottom: 1px solid rgb(var(--gold-deep-rgb) / 0.4);
}

/* margin: 0 auto (not just 0) - the .facts list carries .wrap's own class
   for its max-width and gutter, but .wrap's `margin: 0 auto` and this rule
   have equal specificity, so whichever comes later in the cascade wins.
   Plain `margin: 0` here used to win and silently drop the auto-centring,
   leaving the group hugging the gutter on wide screens instead of sitting
   under the hero column above it - fixed 2026-09-03. */
.facts {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: var(--s-24) var(--s-48);
  list-style: none;
  margin: 0 auto;
  padding: var(--s-24) 0;
  font-family: var(--font-ui);
  font-weight: 700;
  font-size: var(--t-16);
  color: var(--ink);
}

.facts li {
  display: flex;
  align-items: center;
  gap: var(--s-8);
}

/* Decorative, not informational: the tick repeats what the text already
   says rather than adding meaning of its own, so its cream-on-gold
   contrast (2.23:1, below the 3:1 non-text floor) is accepted the same way
   the frame's gold mat is - see DESIGN.md > Frame.

   font-size lives on ::before, not here. `em` units on a property other
   than font-size resolve against the font-size the SAME rule sets, not the
   inherited one - so a `font-size: 0.7em` alongside `width/height: 1.1em`
   on one selector made the circle 1.1em of its own shrunk 0.7em (~12px),
   not 1.1em of the 16px item text (~17.6px) as intended. Fixed 2026-09-03
   by leaving this element's font-size at the inherited 16px and scaling
   only the glyph inside it. */
.fact-tick {
  display: inline-flex;
  flex: none;
  align-items: center;
  justify-content: center;
  width: 1.1em;
  height: 1.1em;
  border-radius: 50%;
  background: var(--gold);
  color: var(--cream);
  line-height: 1;
}

.fact-tick::before { content: "\2713"; font-size: 0.7em; }

/* Desktop and tablet: spread the group across the full content column
   instead of clustering it in the middle. No horizontal padding/margin on
   .facts and no change to `gap` - space-between places the first item's
   left edge and the last item's right edge exactly at the wrap's own
   edges, with `gap` only acting as a minimum should the column ever get
   narrow enough to need it (it doesn't in practice - see DESIGN.md). */
@media (min-width: 44rem) {
  .facts {
    justify-content: space-between;
    /* The column-gap floor drops to 24px here, where space-between decides
       the real spacing anyway (at 1120px it puts ~48px between the items
       either way, so nothing moves). It only matters as the wrap threshold:
       the four items now hold one row until their own content passes
       1048px rather than 976px. With "Helsingin kaupungin palveluseteli" in
       the strip from 2026-09-12 the estimated content is ~975px, which
       cleared the old floor by about 1px - inside the error of a
       per-character width estimate, and a wrap would drop one item onto a
       lopsided second row. */
    column-gap: var(--s-24);
  }
}

/* Phones: a controlled two-by-two grid rather than the auto-wrap the
   flex row would otherwise produce. */
@media (max-width: 43.999rem) {
  .facts {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    justify-items: start;
    gap: var(--s-20) var(--s-24);
  }
}

/* --------------------------------------------------------------- feature
   Heading left, prose right. Used once, on the front page, where the
   heading is the whole left column rather than a label above one. */

.feature {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--s-24);
  padding-bottom: var(--pad-band);
  align-items: start;
}

@media (min-width: 56rem) {
  .feature {
    grid-template-columns: var(--col-side) minmax(0, 1fr);
    gap: var(--gap);
  }
}

.feature h2 {
  font-size: var(--t-38);
  line-height: 1.14;
}

.feature p {
  max-width: 620px;
  text-wrap: pretty;
}

/* ----------------------------------------------------------------- split
   The grammar of every dark section: a label and a heading on the left, the
   content on the right. Consecutive splits on one ground are separated by a
   hairline, not by a second helping of band padding, so a long band reads as
   one room with subjects in it. */

.split {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--s-24);
}

@media (min-width: 56rem) {
  .split {
    grid-template-columns: minmax(0, var(--col-side)) minmax(0, 1fr);
    gap: var(--gap);
  }
}

.split + .split {
  margin-top: var(--pad-tail);
  padding-top: var(--pad-tail);
  border-top: 1px solid rgb(var(--gold-rgb) / 0.25);
}

/* In-page anchor targets: every hero's down-arrow "keep reading" link, plus
   `#palvelut`, which the four method-page crumbs and minusta's method link
   reach from another page. Reserve the band's own top padding plus one
   spacing step above the landing point, so a jump here shows the dark band
   from its top edge with its padding intact, not flush-cropped. Some ids sit
   on the band itself (`#palvelut`, `#toimipisteet`), some on the first split
   inside it; the reserve is the same either way. */
#palvelut,
#koulutus,
#toimipisteet,
#lahtokohta,
#menetelma,
#laskutus {
  scroll-margin-top: calc(var(--pad-band) + var(--s-32));
}

.split h2 {
  font-size: var(--t-38);
  line-height: 1.14;
}

.split p {
  max-width: 620px;
  text-wrap: pretty;
  color: var(--c-fg);
}

/* The lead paragraph of a band, above the splits or the cards. */
.band-lead {
  font-size: var(--t-17);
  line-height: 1.68;
  color: var(--c-fg);
  max-width: 640px;
  margin: 0 0 var(--s-28);
  text-wrap: pretty;
}

.band-h2 {
  font-size: var(--t-44);
  line-height: 1.1;
  max-width: 640px;
  margin: 0 0 var(--s-24);
}

/* ------------------------------------------------------------------- duo
   The mirror of .split: content on the wide left, a lit card on the narrow
   right. Used once, where the money sits beside the eligibility list rather
   than under it. */

.duo {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--s-32);
  align-items: start;
}

@media (min-width: 56rem) {
  .duo {
    grid-template-columns: minmax(0, 1fr) minmax(0, var(--col-side));
    gap: var(--gap-wide);
  }
}

.duo > div > p { max-width: 560px; text-wrap: pretty; }
.duo .ruled { max-width: 560px; }

/* ----------------------------------------------------------------- cards
   The four methods on the front page: two columns, each opening on a
   hairline. Fraunces 600 at 26px, because at 400 a heading this size blurs
   into the 15px paragraph under it on a dark ground. */

.cards {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--s-48) var(--gap);
  list-style: none;
  margin: var(--s-32) 0 0;
  padding: 0;
}

@media (min-width: 44rem) {
  .cards { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
}

.cards > li {
  border-top: 1px solid rgb(var(--gold-rgb) / 0.35);
  padding-top: var(--s-22);
}

.cards h3 {
  font-weight: 600;
  font-size: var(--t-26);
  line-height: 1.2;
  margin: 0 0 var(--s-12);
}

.cards p {
  font-size: var(--t-17);
  line-height: 1.65;
  color: var(--c-fg);
  margin: 0 0 var(--s-14);
  text-wrap: pretty;
}

/* A card's destinations, stacked. Two of the four method blocks carry a
   second, outward link under the first; align-items keeps each link's own
   rule as wide as its text rather than as wide as the column. */
.card-links {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--s-8);
}

/* The one outward link treatment on the site: Sami's earlier pages for
   integratiivinen psykoterapia and EMDR. It stays in the ground's own accent
   (--gold on night, 6.09:1) rather than the export's slate, which would
   put a second link colour on the dark ground; it recedes by weight and size
   instead, one step under the .more-rule above it. No arrow: the arrow marks
   a destination inside the site. */
.link-out {
  font-family: var(--font-ui);
  font-weight: 400;
  font-size: var(--t-14);
  color: var(--c-accent);
}

/* ----------------------------------------------------------------- ruled
   A stack of statements, each opening on a hairline. The export's list
   everywhere: eligibility, qualifications, payment methods. */

.ruled {
  display: flex;
  flex-direction: column;
  gap: var(--s-12);
  list-style: none;
  margin: 0;
  padding: 0;
  font-size: var(--t-17);
  line-height: 1.6;
  color: var(--cream-dim);
  max-width: 620px;
}

.ruled li {
  border-top: 1px solid rgb(var(--gold-rgb) / 0.3);
  padding-top: var(--s-12);
}

.ruled--tight { font-size: var(--t-17); line-height: 1.55; }
.ruled--tight li { padding-top: var(--s-11); }

/* ------------------------------------------------------------- fact list */

.fact-list {
  display: flex;
  flex-direction: column;
  gap: var(--s-28);
  margin: 0;
  font-size: var(--t-15);
  line-height: 1.7;
  color: var(--c-fg);
  max-width: 620px;
}

.fact-list dt {
  font-family: var(--font-body);
  font-weight: 600;
  color: var(--c-head);
  margin-bottom: var(--s-6);
}

.fact-list dd { margin: 0; }

/* --------------------------------------------------------------- prices
   Term, dotted leader, value. The leader is generated on the row, so it
   grows to whatever gap the two ends leave. */

.prices {
  display: flex;
  flex-direction: column;
  gap: var(--s-16);
  margin: 0;
  font-family: var(--font-ui);
  font-weight: 400;
  font-size: var(--t-16);
  color: var(--c-fg);
}

.prices > div {
  display: flex;
  align-items: baseline;
  gap: var(--s-6);
}

.prices dt { flex: none; }

.prices > div::after {
  content: "";
  flex: 1 1 auto;
  order: 2;
  border-bottom: 1px dotted var(--c-leader);
  transform: translateY(-4px);
}

.prices dd {
  order: 3;
  flex: none;
  margin: 0;
  font-weight: 700;
  color: var(--c-head);
}

.card h2,
.card h3 {
  font-weight: 600;
  font-size: var(--t-26);
  line-height: 1.2;
  margin: 0 0 var(--s-28);
}

.card-note {
  font-size: var(--t-15);
  line-height: 1.6;
  color: var(--ink-muted);
  margin: var(--s-28) 0 var(--s-20);
  text-wrap: pretty;
}

/* ------------------------------------------------------------------ note
   A qualification attached to the block above it. On paper and on night it
   is the same thing: a hairline and a quiet paragraph, never a second card. */

.note {
  border-top: 1px solid var(--c-rule);
  padding-top: var(--s-12);
  margin: var(--s-32) 0 0;
  font-size: var(--t-16);
  line-height: 1.6;
  color: var(--c-mute);
  max-width: 620px;
}

.note p { margin: 0; }

/* ------------------------------------------------------------------ rooms
   Two receptions side by side on the dark ground, each with its own
   photograph, address and accessibility facts. The facts are per location
   and are never merged into one claim. */

.rooms {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--s-48);
  margin: 0;
}

@media (min-width: 44rem) {
  .rooms { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--s-48); }
}

.rooms h3 {
  font-weight: 600;
  font-size: var(--t-26);
  line-height: 1.2;
  margin: 0 0 var(--s-10);
}

.rooms .frame { margin-bottom: var(--s-18); }
.rooms .frame img { aspect-ratio: 1 / 1; object-fit: cover; }

.rooms address {
  font-family: var(--font-ui);
  font-weight: 500;
  font-style: normal;
  font-size: var(--t-15);
  color: var(--gold-lit);
  margin-bottom: var(--s-14);
}

.rooms p {
  font-size: var(--t-17);
  line-height: 1.65;
  color: var(--cream-mute);
  margin: 0;
  text-wrap: pretty;
}

/* -------------------------------------------------------------- link list
   Used where a page is mostly a set of destinations: the 404 and the
   documents page. */

.link-list {
  display: flex;
  flex-direction: column;
  gap: var(--s-24);
  list-style: none;
  margin: 0;
  padding: 0;
  max-width: 620px;
}

.link-list li {
  border-top: 1px solid rgb(var(--gold-rgb) / 0.3);
  padding-top: var(--s-14);
}

.link-list > li > a {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--t-26);
  line-height: 1.2;
  color: var(--c-head);
  border-bottom: 1px solid var(--c-accent);
  padding-bottom: 2px;
}

.link-list > li > a:hover { color: var(--c-accent); }

.link-list p {
  font-size: var(--t-16);
  line-height: 1.6;
  color: var(--c-fg);
  margin: var(--s-12) 0 0;
}

/* ---------------------------------------------------------------- links
   The export's two link treatments: a small gold link inside a block, and a
   ruled one that closes a section. Both carry the arrow as generated
   content, because it is decoration and not part of the link's name. */

.more,
.more-rule {
  font-family: var(--font-ui);
  font-weight: 700;
  letter-spacing: 0.08em;
  color: var(--c-accent);
}

.more { font-size: var(--t-15); }

.more-rule {
  font-size: var(--t-15);
  letter-spacing: 0.1em;
  border-bottom: 1px solid var(--c-rule);
  padding-bottom: 3px;
}

.more-rule:hover { border-bottom-color: var(--c-accent-hover); }

.more::after,
.more-rule::after { content: "\00a0\2192"; }

/* An in-page link to content further down the same page points down, not
   right: it names a destination on the page, not another page. */
.more--down::after { content: "\00a0\2193"; }

.more-line { margin: var(--s-32) 0 0; }
.more-line--wide { margin-top: var(--s-48); }
.more-line--lead { margin-top: var(--s-36); }

/* On vastaanotot the hero has no lead paragraph, so the arrow link follows
   the h1 directly and takes the heading's own closing gap rather than adding
   a paragraph-sized one on top of it. */
.page-h1 + .more-line--lead { margin-top: 0; }

/* The arrow-link treatment for a hero's own invitation to keep reading, not
   a footnote to the paragraph above it: minusta's hero originated it, and it
   now sits under the intro paragraph on all eight pages whose light top ends
   in a dark band that needs naming (front page's hero flows straight into
   its own fact strip and steps, so it doesn't need one).
   Plain inline padding painted a 44px+ box but didn't reliably reserve a
   44px hit area (padding on a normal inline element doesn't grow the line
   box). inline-flex with a min-height makes the box's real, hit-testable
   height 44px. That taller box would otherwise push the paragraph's own
   flow height out by roughly the difference between 44px and the text's
   own line box (~32px at this size) — the negative vertical margin below
   cancels that so the link's visible position and the rhythm above/below
   it stay where they were. */
.more--lead {
  font-size: var(--t-19);
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  vertical-align: middle;
  margin: calc(var(--s-6) * -1) 0;
}

/* ----------------------------------------------------------------- footer */

footer {
  background: var(--night-deep);
  color: var(--slate);
  padding: var(--s-40) var(--gut-wide);
  font-family: var(--font-ui);
  font-weight: 400;
  font-size: var(--t-15);
  line-height: 1.7;
}

.footer-inner {
  max-width: var(--wrap);
  margin: 0 auto;
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: var(--s-20) var(--s-40);
}

.footer-name { margin: 0; max-width: 640px; }

.footer-links {
  display: flex;
  flex-wrap: wrap;
  list-style: none;
  margin: 0;
  padding: 0;
  gap: var(--s-10) var(--s-28);
}

.footer-links a { color: var(--linen); }
.footer-links a:hover { color: var(--cream); }

/* The two reception addresses, on every page: the only place they appear
   outside vastaanotot.html and the front page's JSON-LD. flex-basis: 100%
   puts it on its own line under the legal line and the link list rather than
   beside them as a third item in the footer's space-between row. */
.footer-where {
  flex-basis: 100%;
  margin: 0;
  color: var(--slate-dim);
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after { transition: none !important; }
}
