@font-face{font-family:'PriimoBody';src:url(fonts/body.woff2) format('woff2');font-weight:100 900;font-style:normal;font-display:swap;}
@font-face{font-family:'PriimoDisplay';src:url(fonts/heading.woff2) format('woff2');font-weight:400;font-style:normal;font-display:swap;}
:root {
  /* The chrome above the hero: nav alone, or utility bar plus nav. The hero fills the rest. */
  --chrome: 6rem;
  --primary: #5aa99c;
  --accent: #447f75;
  /* Foreground-only derivatives of accent/primary - see onAccentFor/brandTextFor
     above for the stale-manifest fallback and palette.ts's onAccentText/
     brandTextOnPaper for how each is chosen. Neither --primary nor --accent above
     is touched by this: those still drive every background, rule and button. */
  --on-accent: #ffffff;
  --brand-text: #407970;
  /* Text set directly on the brand colour (a pitch card, a chat bubble, a launcher), and
     the accent as small text on paper: both chosen by contrast the way --on-accent and
     --brand-text are, so a pale yellow brand gets dark type and a deep blue gets light. */
  --on-primary: #10130f;
  --accent-text: #447f75;
  --cta-button: #447f75;
  --on-cta-button: #ffffff;
  --ink: #10130f;
  --paper: #ffffff;
  --paper-2: #f3f3f3;
  --muted: #616260;
  --line: #d9d9d9;
  --radius: 6px;
  --rhythm: 1.2;
  --display: 'PriimoDisplay', "Arial Narrow", "Helvetica Neue Condensed", sans-serif;
  --display-em: 'PriimoDisplayItalic', 'PriimoDisplay', "Arial Narrow", "Helvetica Neue Condensed", sans-serif;
  /* The hero ground: the family's fixed field colour, not a wash of the extracted
     brand darkened toward black - every extracted colour taken down 42% toward black
     used to land in the same place, which is one of the reasons a florist and a
     restaurant rendered as the same page. See GROUNDS in families.ts. */
  --field: #16202b;
  --body: 'PriimoBody', system-ui, sans-serif;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  --wrap: 1200px;
  --gutter: clamp(1.25rem, 4vw, 3rem);
  /* WHICH LEAD this is: duration, travel, easing and stagger for every [data-reveal]
     rule below. See signatureFor() above for how a stale manifest missing its motion
     field entirely still gets a real value here rather than the literal string
     "undefined". */
  --rv-duration: .78s;
  --rv-travel: 14px;
  --rv-ease: cubic-bezier(.25,.6,.3,1);
  --rv-stagger: .09s;
}
*, *::before, *::after { box-sizing: border-box; }
html { scroll-behavior: smooth; }
/* The photo band waits below the fold at scale(1.04) until its reveal fires, 4% wider
   than the viewport, which gave every demo a horizontal scrollbar in a real browser
   (2026-09-04). clip, never hidden: hidden makes body a scroll container and unpins
   the sticky nav. */
html, body { overflow-x: clip; }
/* A column at least one viewport tall, with the footer pushed to the bottom: the contact
   page is short by nature and its footer floated mid-screen (team feedback 2026-09-04). */
body {
  min-height: 100svh; display: flex; flex-direction: column;
  margin: 0; background: var(--paper); color: var(--ink);
  font-family: var(--body); font-size: 17px; line-height: 1.6;
  -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility;
}
img { max-width: 100%; display: block; }
a { color: inherit; }

/* --- type scale -------------------------------------------------------- */
/* Finnish compounds are longer than English columns: a heading hyphenates when a word
   will not fit, and never runs into what sits beside it. Browsers hyphenate only when
   they must, so a heading that fits is untouched. */
h1, h2, h3 { font-family: var(--display); margin: 0; text-wrap: balance; hyphens: auto; overflow-wrap: anywhere; }
h1 {
  font-size: clamp(3rem, 7.6vw, 6rem); line-height: .92;
  font-weight: 400; letter-spacing: -0.01em;
  text-transform: uppercase;
}
h2 {
  font-size: clamp(2.1rem, 4.4vw, 3.4rem); line-height: .98;
  font-weight: 400; letter-spacing: -0.005em;
  text-transform: uppercase;
}
h3 { font-size: clamp(1.6rem, 2.7vw, 2.2rem); line-height: 1.12; font-weight: 400; letter-spacing: -0.01em; }
p { margin: 0 0 1em; }
.lede { font-size: clamp(1.05rem, 1.5vw, 1.24rem); color: var(--muted); max-width: 54ch; }

.eyebrow {
  font-family: var(--mono); font-size: .72rem; text-transform: uppercase;
  /* --brand-text, not --primary: this is foreground text on --paper, and --primary
     is not guaranteed to clear 4.5:1 there (some extracted brand colours are pale
     enough to fail badly). ::before below stays on --primary - it is a 2px
     decorative dash, not text, so WCAG's text-contrast minimum does not apply to it
     and it should keep tracking the real brand colour exactly. */
  letter-spacing: .16em; color: var(--brand-text); margin: 0 0 1rem;
  display: flex; align-items: center; gap: .6rem;
}
.eyebrow::before { content: ""; width: 1.4rem; height: 2px; background: var(--primary); flex: none; }

.wrap { width: min(var(--wrap), 100% - (var(--gutter) * 2)); margin-inline: auto; }
section { padding: calc(4.75rem * var(--rhythm)) 0; }
/* margin-inline stays 0: .wrap centres its child, so a section head with a narrower
   max-width would drift to the middle of the page while the hero stayed flush left. */
.section-head { margin: 0 0 calc(3rem * var(--rhythm)); max-width: 62ch; }
/* Headings carry no bottom margin so they can sit tight to a rule; a lede underneath
   one needs the space putting back, or it reads as a dropped second line. */
.section-head .lede { margin-top: .9rem; }

/* --- utility strip ------------------------------------------------------ */
.utility { background: var(--ink); color: color-mix(in srgb, var(--paper) 72%, var(--ink)); font-family: var(--mono); font-size: .72rem; letter-spacing: .04em; }
.utility-inner { display: flex; gap: 1.5rem; justify-content: space-between; padding: .55rem 0; flex-wrap: wrap; }
.utility i { font-style: normal; opacity: .45; margin: 0 .55rem; }
.utility a { color: inherit; text-decoration: none; }
.utility a:hover { color: var(--paper); }

/* --- nav ---------------------------------------------------------------- */
/* Links left, wordmark centred, number right. The wordmark is the optical centre of
   the page, not a logo parked in a corner. */
.nav { position: sticky; top: 0; z-index: 50; background: color-mix(in srgb, var(--paper) 90%, transparent); backdrop-filter: blur(12px); border-bottom: 1px solid var(--line); }
.nav-inner { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 1.5rem; padding: .95rem 0; }

.brand {
  grid-column: 2; justify-self: center; font-family: var(--display); font-weight: 400;
  font-size: 1.1rem; letter-spacing: .12em; text-transform: uppercase; text-decoration: none;
  white-space: nowrap;
}
.nav-links ul { display: flex; flex-wrap: wrap; gap: .4rem 1.6rem; list-style: none; margin: 0; padding: 0; }
.nav-links a { font-family: var(--mono); font-size: .7rem; text-transform: uppercase; letter-spacing: .14em; text-decoration: none; color: var(--muted); transition: color .2s; white-space: nowrap; }
.nav-links a:hover { color: var(--ink); }
/* A page the full site would have and the demo did not build: named, not linked. */
.nav-planned { font-family: var(--mono); font-size: .7rem; text-transform: uppercase; letter-spacing: .14em; color: color-mix(in srgb, var(--muted) 55%, var(--paper)); cursor: default; white-space: nowrap; }
.nav-cta {
  grid-column: 3; justify-self: end; font-family: var(--mono); font-size: .8rem; font-weight: 600;
  letter-spacing: .04em; text-decoration: none; white-space: nowrap; color: var(--ink);
  padding-bottom: .2rem; box-shadow: inset 0 -2px 0 var(--primary); transition: box-shadow .25s;
}
.nav-cta:hover { box-shadow: inset 0 -1.1em 0 color-mix(in srgb, var(--primary) 16%, transparent); }
.nav-toggle { display: none; grid-column: 1; justify-self: start; background: none; border: 0; padding: .5rem; cursor: pointer; }
.nav-toggle span { display: block; width: 20px; height: 2px; background: var(--ink); margin: 4px 0; transition: transform .3s, opacity .3s; }
.nav-toggle[aria-expanded="true"] span:first-child { transform: translateY(6px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] span:last-child { transform: translateY(-6px) rotate(-45deg); }
/* Family chrome: wordmark left, links pushed right, the number as a filled button. */
/* The stacked bar: the name centred on its own line, the links centred beneath, the phone at the right of the first line. */
.nav--stack .nav-inner { grid-template-columns: 1fr auto 1fr; grid-template-rows: auto auto; row-gap: .35rem; padding-bottom: .7rem; }
.nav--stack .brand { grid-column: 2; grid-row: 1; justify-self: center; }
.nav--stack .nav-cta { grid-column: 3; grid-row: 1; justify-self: end; }
.nav--stack .nav-toggle { grid-column: 1; grid-row: 1; justify-self: start; }
.nav--stack .nav-links { grid-column: 1 / -1; grid-row: 2; justify-self: center; }
.nav--stack .nav-links ul { justify-content: center; }
.nav--left .nav-inner { grid-template-columns: auto 1fr auto; }
/* Rows declared as well as columns: the links precede the wordmark in the DOM, and
   sparse auto-placement would otherwise drop the wordmark onto a second row. */
.nav--left .brand { grid-column: 1; grid-row: 1; justify-self: start; }
.nav--left .nav-links { grid-column: 2; grid-row: 1; justify-self: end; }
.nav--left .nav-cta { grid-column: 3; grid-row: 1; }
.nav-cta--button {
  background: var(--primary); color: var(--on-primary); box-shadow: none;
  padding: .6rem 1rem; border-radius: var(--radius); transition: filter .2s;
}
.nav-cta--button:hover { box-shadow: none; filter: brightness(1.08); }

/* --- buttons ------------------------------------------------------------ */
.btn {
  display: inline-flex; align-items: center; gap: .7rem; padding: 1rem 1.6rem;
  font-family: var(--body); font-size: .82rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: .11em; text-decoration: none; border: 0; cursor: pointer; border-radius: var(--radius);
  transition: transform .24s cubic-bezier(.2,.7,.3,1), box-shadow .24s, background .24s;
}
.btn .arw { width: 22px; height: 11px; transition: transform .28s cubic-bezier(.2,.7,.3,1); }
.btn:hover .arw { transform: translateX(5px); }
.btn--primary { background: var(--primary); color: var(--on-primary); }
.btn--primary:hover { transform: translateY(-2px); box-shadow: 0 14px 30px -14px var(--primary); }
.btn--invert { background: var(--paper); color: var(--brand-text); }
.btn--ghost { background: none; color: inherit; box-shadow: inset 0 0 0 1px color-mix(in srgb, currentColor 38%, transparent); }
.btn--ghost:hover { background: color-mix(in srgb, currentColor 12%, transparent); }
.btn--invert:hover { transform: translateY(-2px); }
.btn--text { background: none; color: var(--ink); padding-left: 0; padding-right: 0; }
.btn--text .btn-label { box-shadow: inset 0 -2px 0 var(--primary); }

/* --- hero --------------------------------------------------------------- */
/* A colour field and a photograph side by side, with the type laid across the join.
   The field is not a fallback for a missing photograph — it is half the composition,
   and when there is no photograph it simply runs full width. */
.hero {
  position: relative; display: grid; align-items: center;
  min-height: calc(100svh - var(--chrome)); padding: calc(5rem * var(--rhythm)) 0;
  color: var(--paper); overflow: hidden; isolation: isolate;
}
.hero-ground { position: absolute; inset: 0; z-index: -2; display: grid; grid-template-columns: 47% 53%; }
.hero--field .hero-ground { grid-template-columns: 1fr; }

/* Contour lines, at the threshold of visible. A flat colour field reads as a CSS
   background; a textured one reads as a surface, which is the whole difference. */
.hero-field {
  background-color: var(--field);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='560' height='560' viewBox='0 0 560 560'%3E%3Cg fill='none' stroke='%23ffffff' stroke-opacity='0.055' stroke-width='1.1'%3E%3Cellipse cx='150' cy='250' rx='90' ry='130' transform='rotate(-24 150 250)'/%3E%3Cellipse cx='150' cy='250' rx='150' ry='205' transform='rotate(-24 150 250)'/%3E%3Cellipse cx='150' cy='250' rx='215' ry='285' transform='rotate(-24 150 250)'/%3E%3Cellipse cx='150' cy='250' rx='290' ry='370' transform='rotate(-24 150 250)'/%3E%3Cellipse cx='470' cy='90' rx='70' ry='105' transform='rotate(18 470 90)'/%3E%3Cellipse cx='470' cy='90' rx='135' ry='180' transform='rotate(18 470 90)'/%3E%3Cellipse cx='470' cy='90' rx='205' ry='260' transform='rotate(18 470 90)'/%3E%3C/g%3E%3C/svg%3E");
  background-size: 560px 560px;
}
.hero-photo { position: relative; overflow: hidden; }
.hero-photo img { width: 100%; height: 100%; object-fit: cover; filter: saturate(1.04) contrast(1.04); }
/* The headline crosses onto the photograph, so the photograph has to give way to it.
   A scrim from the join outward, not a flat wash over the whole image. */
.hero-photo::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(90deg,
    var(--field) 0%,
    color-mix(in srgb, var(--field) 72%, transparent) 24%,
    color-mix(in srgb, var(--field) 22%, transparent) 62%,
    transparent 100%);
}

.hero-inner { position: relative; z-index: 1; max-width: 62rem; }
/* Everything on the field is a tone of the paper, not the brand accent. The accent is
   whatever colour happened to be most common in their CSS, and against a deep red
   field an extracted blue reads as a mistake rather than a highlight. */
.hero .eyebrow { color: color-mix(in srgb, var(--paper) 74%, var(--field)); }
.hero .eyebrow::before { background: currentColor; }
/* A Finnish compound longer than the column ("saneerauskohteisiin" at 96px is
   wider than 17ch of most faces) hyphenates rather than running into the photograph. */
.hero h1 { margin-bottom: 1.4rem; max-width: 17ch; hyphens: auto; overflow-wrap: anywhere; }
/* Emphasis on the word that is specific to this business. Anton has no italic and a
   synthetic oblique on a condensed grotesque reads as a rendering fault, so a
   condensed display marks its emphasis tonally instead: the headline sits back and
   the marked word comes forward. Both moves are in the reference set — Stewart
   alternate roman and italic, AlterG split theirs light against dark. */
.hero h1 { color: color-mix(in srgb, var(--paper) 76%, var(--field)); }
.hero h1 em { font-style: normal; color: var(--paper); }
.hero .lede { color: color-mix(in srgb, var(--paper) 82%, var(--field)); max-width: 46ch; margin-bottom: 0; }
.hero .actions { display: flex; align-items: center; gap: 1.5rem; flex-wrap: wrap; margin: 2.25rem 0 0; }

/* The operating facts, as a strip under the hero rather than a column beside it.
   Every reference site closes its hero with a band of small, flat, factual links. */
.ledger-strip { background: var(--paper); border-bottom: 1px solid var(--line); }
/* margin-block rather than a bare margin shorthand: setting margin to 0 here
   cancelled the margin-inline auto that .wrap relies on, and the whole strip slid to
   the viewport edge. */
.ledger { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr)); margin-block: 0; }
.ledger-cell { display: grid; gap: .25rem; padding: 1.15rem clamp(1rem, 2vw, 1.6rem) 1.15rem 0; border-left: 1px solid var(--line); padding-left: clamp(1rem, 2vw, 1.6rem); }
.ledger-cell:first-child { border-left: 0; padding-left: 0; }
.ledger dt { font-family: var(--mono); font-size: .66rem; text-transform: uppercase; letter-spacing: .16em; color: var(--brand-text); }
.ledger dd { margin: 0; font-family: var(--display); font-size: clamp(1rem, 1.5vw, 1.3rem); line-height: 1.15; letter-spacing: -0.005em; overflow-wrap: anywhere; }
.ledger-cell:last-child dd { font-family: var(--body); font-size: .86rem; line-height: 1.45; color: var(--muted); }

/* --- hero compositions ---------------------------------------------------- */
/* Six arrangements of the same elements. The headline is identical in all of them —
   the evidence decides what sits around it, never what it says. split is the
   original and is deliberately absent below: it is the base rule set. */

/* TYPE FIRST. The headline owns the fold at a scale nothing else on the page reaches,
   and the photograph drops to a band beneath it. Works with no photograph at all: the
   ground simply runs as a full-width field, which is what hero--field already does. */
.hero--type .hero-ground { grid-template-columns: 1fr; }
.hero--type { align-items: end; }
.hero--type .hero-inner { max-width: 74rem; }
.hero--type h1 { max-width: 22ch; font-size: clamp(2.6rem, 7vw, 6rem); }
.hero--type .lede { max-width: 52ch; }

/* STACKED, CENTRED. Calmer and editorial; suits the appointment trades that already
   take the serif display face. The photograph runs as a wide shallow band under the
   type rather than beside it. */
.hero--stacked .hero-ground { grid-template-columns: 1fr; }
.hero--stacked { text-align: center; }
.hero--stacked .hero-inner { max-width: 52rem; margin-inline: auto; }
.hero--stacked h1 { max-width: 20ch; margin-inline: auto; }
.hero--stacked .lede { margin-inline: auto; }
.hero--stacked .eyebrow { justify-content: center; }
.hero--stacked .actions { justify-content: center; }
/* HERO CONTRAST ON A BRIGHT PHOTOGRAPH - known, unfixed, and deliberately left alone.
   voi Kukka's headline is rgb(207,204,201) over a lit bouquet: legible, but only just.
   Two scrims were tried and both were worse. Drawn in var(--field) it whited the
   photograph out and took the pale type with it; drawn in var(--ink) at the weight
   needed to lift the type, it erased the photograph. The manifest now carries
   heroPhotoIsBright so the signal is there when someone works out the right treatment,
   which is most likely choosing the TYPE colour from the measured luminance rather
   than veiling the picture underneath it. */

/* A photograph the luminance gate measured as too bright for pale type (heroPhotoIsBright,
   stamped as hero--photo-bright by the hero renderer). The two earlier scrims were reverted
   because var(--field) was a paper tone on light palettes and veiled the picture into a
   cream rectangle; every family now owns a dark field, so the veil is always dark and the
   headline always lifts. Heavier than the default left-to-right scrim, still a gradient so
   the photograph survives on the right; the centred stacked hero has no join to cross, so
   its veil rises from below instead. */
.hero--photo-bright .hero-photo::after {
  background: linear-gradient(90deg,
    color-mix(in srgb, var(--field) 94%, transparent) 0%,
    color-mix(in srgb, var(--field) 82%, transparent) 30%,
    color-mix(in srgb, var(--field) 56%, transparent) 64%,
    color-mix(in srgb, var(--field) 34%, transparent) 100%);
}
.hero--bleed.hero--photo-bright .hero-photo::after {
  background: linear-gradient(0deg,
    color-mix(in srgb, var(--field) 96%, transparent) 0%,
    color-mix(in srgb, var(--field) 74%, transparent) 45%,
    color-mix(in srgb, var(--field) 36%, transparent) 100%);
}
.hero--stacked.hero--photo-bright .hero-photo::after {
  background: linear-gradient(0deg,
    color-mix(in srgb, var(--field) 90%, transparent) 0%,
    color-mix(in srgb, var(--field) 62%, transparent) 55%,
    color-mix(in srgb, var(--field) 48%, transparent) 100%);
}

/* No extra scrim on the stacked hero by default, and that is a decision rather than an omission.
   A centred veil was tried and reverted: it is drawn in var(--field), which on a light
   palette is a paper tone, so it washed the photograph out AND took the pale headline
   with it - the hero rendered as an empty cream rectangle. The base left-to-right
   scrim above is what the layout was designed around and reads correctly on both
   palettes. Fixing the marginal case would need to key off the hero's own text colour,
   which is a bigger change than the problem justifies. */


/* FULL BLEED. The photograph is the whole fold and the headline sits low over it. The
   scrim runs bottom-upward rather than left-to-right, because there is no join for the
   headline to cross - it is over the picture everywhere.

   The riskiest of the six, and knowingly so: the bank is licensed stock that does not
   show this business's own work, and a generic photograph at full-fold scale can read
   MORE templated, not less. If the contact sheet says so, drop it from SEEDED in
   hero_composition.ts - no other change is needed. */
.hero--bleed .hero-ground { grid-template-columns: 1fr; }
/* Hide the field only when a photograph is actually there to replace it. hero--field is
   applied exactly when the bank gave this demo no hero photograph, so negating it is the
   signal we already have. Hiding unconditionally would leave hero-ground empty and put a
   void behind the headline, which is the one thing these pages must never do. */
.hero--bleed:not(.hero--field) .hero-field { display: none; }
.hero--bleed { align-items: end; }
.hero--bleed .hero-photo::after {
  background: linear-gradient(0deg,
    color-mix(in srgb, var(--field) 92%, transparent) 0%,
    color-mix(in srgb, var(--field) 68%, transparent) 38%,
    color-mix(in srgb, var(--field) 18%, transparent) 72%,
    transparent 100%);
}
.hero--bleed .hero-inner { max-width: 56rem; }
.hero--bleed h1 { max-width: 18ch; }

/* THE WORK STRIP. Named jobs under the headline. Set small and flat against the
   headline's scale - the point is that these are unfakeable, not that they are loud.
   Nobody claims the Docklands Light Railway who has not wired it. */
.hero--work .hero-inner { max-width: 66rem; }
.hero-work { list-style: none; margin: 1.6rem 0 0; padding: 1.2rem 0 0; display: grid;
  gap: .55rem; border-top: 1px solid color-mix(in srgb, var(--paper) 34%, transparent); max-width: 44ch; }
.hero-work-item { display: grid; grid-template-columns: 2rem 1fr; gap: .7rem; align-items: baseline;
  font-size: .92rem; line-height: 1.35; color: color-mix(in srgb, var(--paper) 88%, var(--field)); }
.hero-work-n { font-family: var(--mono); font-size: .66rem; letter-spacing: .14em;
  color: color-mix(in srgb, var(--paper) 62%, var(--field)); }

/* THE FIGURE PANEL. One number in its own column, at a size nothing else on the page
   reaches. Only the digits get that size — "Established in 1976" set whole at this
   scale spends it on the word "Established". aria-hidden, because the figure is
   already in the page's text and a screen reader should not hear it twice. */
.hero--figure .hero-inner { max-width: 46rem; }
.hero-figure { position: absolute; right: 0; top: 50%; transform: translateY(-50%); z-index: 1;
  display: grid; justify-items: center; gap: .3rem; padding: 2rem clamp(1.5rem, 4vw, 3.5rem);
  border-left: 1px solid color-mix(in srgb, var(--paper) 26%, transparent); }
.hero-figure-n { font-family: var(--display); font-size: clamp(3rem, 8vw, 7rem); line-height: .9;
  letter-spacing: -0.02em; color: var(--paper); }
.hero-figure-w { font-family: var(--mono); font-size: .66rem; text-transform: uppercase;
  letter-spacing: .16em; color: color-mix(in srgb, var(--paper) 68%, var(--field)); text-align: center; }
/* On a phone there is no room beside the headline, so it becomes a band beneath it. */
@media (max-width: 60rem) {
  .hero-figure { position: static; transform: none; justify-items: start; padding: 1.4rem 0 0;
    border-left: 0; border-top: 1px solid color-mix(in srgb, var(--paper) 26%, transparent);
    margin-top: 1.6rem; }
  .hero-figure-w { text-align: left; }
}

/* --- photograph drift ---------------------------------------------------- */
/* A few percent of travel reads as depth; a full-strength parallax reads as 2015 and
   janks on iOS Safari. Nothing here is position:fixed and no text moves - the whole
   difference between this and the stock effect. The container clips and the image
   overscans it by more than the maximum travel, so no edge can ever expose a gap.

   .drift needs its own definite height for that percentage to mean anything (a
   percentage height only resolves against a containing block that has one) - inside
   .hero-photo that comes for free via the grid it sits in (height: 100% below simply
   inherits it), inside a .band figure there is nothing else to derive it from, which
   is why the photography-bands aspect-ratio rules below target .drift, not img.

   .drift img itself is declared further down, after .hero-photo img and .band img -
   all three set width/height/object-fit/display at equal specificity, and the later
   declaration wins ties, so .drift img has to come last in the file or its 116%
   overscan loses the cascade to the 100% it is meant to replace. */
.drift { overflow: hidden; position: relative; height: 100%; }

/* --- photography bands ---------------------------------------------------- */
/* Full-bleed, fixed aspect, and entirely absent when the bank had nothing. Colour,
   not greyscale: desaturating every photograph was what made varied stock cohere,
   and it also drained the life out of trades whose work is the colour of the work. */
.band { display: grid; gap: 3px; background: var(--line); }
.band figure { margin: 0; overflow: hidden; position: relative; }
/* The caption hook every photo container emits (band() in sections.ts). Off by
   default - most families' photography carries no caption at all - and turned on by
   the one family whose coherence comes from captions rather than from a filter. */
.band figcaption { display: none; }
.band img {
  width: 100%; height: 100%; object-fit: cover; display: block;
  /* A single light grade, the same on every photograph, is what makes six sources
     read as one commission. It is a nudge, not a filter. */
  filter: saturate(1.04) contrast(1.04);
}
.band--hero { grid-template-columns: 1fr; }
/* aspect-ratio lives on .drift, not img: img is now the overscanning drift layer
   (above), and its height is a percentage of .drift's box. A percentage height only
   resolves against a containing block with a definite height, so .drift needs to be
   the one carrying the aspect-ratio - if it stayed on img, .drift's own height would
   have nothing to derive from (it has no other explicit height in a band, unlike
   .hero-photo which gets one from the grid it sits in) and would collapse to zero. */
.band--hero .drift { aspect-ratio: 21 / 8; }
.band--work { grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr)); margin-top: calc(3.5rem * var(--rhythm)); }
/* A definite height, not an aspect ratio under a height cap: given both, the browser
   keeps the ratio and shrinks the WIDTH, so a 2026px frame held a 710px picture with
   paper beside it (parlour, plate, yard all did this). */
.band--work .drift { height: clamp(12rem, 28vw, 24rem); }
.band--work.band--1 .drift { height: clamp(14rem, 34vw, 26rem); }

/* Declared here, after .hero-photo img and .band img, so it wins the cascade for the
   width/height/object-fit/display all three set (see the comment on .drift, above) -
   .drift img is what actually draws the overscanning, shiftable layer. */
.drift img { height: 116%; width: 100%; object-fit: cover; display: block; position: relative; top: -8%; }

/* The overscan above exists so the image can be shifted without ever exposing an
   edge; this is what shifts it. A few percent of travel reads as depth. Scroll-driven
   and zero JS - Firefox and reduced-motion simply keep the static overscan. All five
   reference sites do exactly this; none of them loads an animation library. */
@media (prefers-reduced-motion: no-preference) {
@supports (animation-timeline: view()) {
.drift img { animation: band-drift linear both; animation-timeline: view(); }
@keyframes band-drift {
/* The percentage is relative to the image's own height (116%), not the container.
±4% of 116% = ±4.64% of the container, which is safely inside the ±8% overscan
provided by height: 116% with top: -8%. The value mirrors TRAVEL = 0.08 declared in
the JavaScript drift below (grep for TRAVEL), whose clamp of p to ±0.5 produces
this same ±4% figure; keep the two in step if either is retuned. */
from { transform: translateY(4%); }
to { transform: translateY(-4%); }
}
}
}

/* --- trust bar ---------------------------------------------------------- */
.trust-bar { background: var(--ink); color: var(--paper); padding: 1.6rem 0; }
.trust-items { display: flex; flex-wrap: wrap; gap: 1rem 2.5rem; }
.trust-item {
  font-family: var(--mono); font-size: .78rem; letter-spacing: .06em; text-transform: uppercase;
  padding-left: .9rem; border-left: 3px solid var(--primary);
}

/* --- partners ----------------------------------------------------------- */
/* Third-party marks, held back: greyed, each in the same fixed box so a wide mark
   and a square one sit at one visual weight; colour returns on hover. */
.partners { padding: 3.2rem 0 3.6rem; border-top: 1px solid var(--line); }
.partner-row { list-style: none; margin: 1.6rem 0 0; padding: 0; display: flex; flex-wrap: wrap; align-items: center; gap: 1.6rem 3.2rem; }
.partner img { display: block; width: 9.5rem; height: 2.4rem; object-fit: contain; filter: grayscale(1); opacity: .62; transition: opacity .3s, filter .3s; }
.partner:hover img { filter: none; opacity: 1; }
.partner--text span { font-family: var(--mono); font-size: .78rem; letter-spacing: .1em; text-transform: uppercase; color: color-mix(in srgb, var(--ink) 62%, var(--paper)); }
@media (max-width: 40rem) { .partner-row { gap: 1.2rem 2rem; } .partner img { width: 7rem; height: 1.9rem; } }

/* --- services ----------------------------------------------------------- */
/* A ruled list. Each entry is as tall as its own words, so a short service and a
   long one cannot knock the section out of rhythm the way paired columns did. */
.svc-list { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 22rem), 1fr)); gap: calc(1rem * var(--rhythm)) clamp(2rem, 5vw, 4rem); border-top: 2px solid var(--ink); }
/* Three columns close only at 3 and 6; each .svc carries its own rule, so 2, 4 and 5
   draw a line under some columns and not others. The services section carries 2 to 5
   items (clamped in the pipeline - see the comment on generate.ts's services map), so
   the render ceiling is 5 and this enumeration is complete; raise that ceiling and 6
   needs adding here. 4 takes two columns because 2+2 closes; 2 and 5 take one, because
   two points in two columns read as a grid that lost its other half (the same call
   why-list makes for its own count of 2, below). */
/* minmax(min(100%, 24rem), 1fr), not a bare repeat(2, ...): the same 24rem track-min
   why-list already uses to close its own count of 4 (below) forces exactly two columns
   at the 1200px wrap while staying responsive - min(100%, 24rem) resolves to 100% once
   the viewport is narrower than 24rem, so this still collapses to one column on a phone
   instead of squeezing two fixed columns into it. */
.svc-list[data-count="4"] { grid-template-columns: repeat(auto-fit, minmax(min(100%, 24rem), 1fr)); }
.svc-list[data-count="2"], .svc-list[data-count="5"] { grid-template-columns: minmax(0, 1fr); }
/* Count 3 is NOT "closes naturally at the 1200px wrap" the way it first looked -
   that claim was only ever checked at one width. auto-fit's column count is a
   continuous function of container width, and between 22rem*2 and 22rem*3 it gives
   TWO columns for three items, leaving the third alone in row two beside an empty
   cell: measured ragged from 810px to 1278px, clean only at 1280px and above where
   three 22rem tracks first fit. Fixed the same way proof-columns is fixed below -
   forced explicit at a width where three columns already fit, one column below it -
   rather than left to auto-fit, so no two-column state can occur at all. Swept
   320px-1600px after the fix; clean throughout. */
.svc-list[data-count="3"] { grid-template-columns: minmax(0, 1fr); }
@media (min-width: 1280px) {
  .svc-list[data-count="3"] { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
.svc { position: relative; padding: 1.9rem 0; border-bottom: 1px solid var(--line); }
.svc h3 { margin-bottom: .55rem; }
.svc p { color: var(--muted); margin-bottom: 0; max-width: 46ch; }
.svc-covers { margin-top: .7rem !important; font-size: .9rem; line-height: 1.5; color: var(--muted); }
/* Planned services are greyed and chipped; built ones link on. */
.chip { position: absolute; top: 1.9rem; right: 0; font-family: var(--mono); font-size: .62rem; letter-spacing: .14em; text-transform: uppercase; color: var(--muted); border: 1px solid var(--line); padding: .3rem .55rem; }
.svc--planned h3 { color: var(--muted); }
.svc--planned p { color: color-mix(in srgb, var(--muted) 80%, var(--paper)); }
.svc-more { display: inline-flex; align-items: center; gap: .4rem; margin-top: .9rem; font-family: var(--mono); font-size: .72rem; text-transform: uppercase; letter-spacing: .12em; color: var(--brand-text); text-decoration: none; }
.svc-more svg { width: .9rem; height: .9rem; }
.svc-ledger .chip { position: static; display: inline-block; margin-bottom: .6rem; }
/* A built service page: compact hero, then what it covers beside its photograph. */
.page-hero { padding: clamp(3.5rem, 8vw, 6rem) 0 clamp(2rem, 5vw, 3.5rem); border-bottom: 1px solid var(--line); }
.page-hero h1 { font-size: clamp(2.4rem, 5.2vw, 4.4rem); line-height: .96; margin: .6rem 0 1rem; max-width: 18ch; }
.page-hero .lede { max-width: 56ch; font-size: 1.08rem; line-height: 1.6; color: var(--muted); }
.service-detail { padding: clamp(2.5rem, 6vw, 4.5rem) 0; }
.detail-intro p { font-size: clamp(1.02rem, 1.4vw, 1.15rem); line-height: 1.6; max-width: 58ch; }
.detail-intro + .detail-heading { margin-top: 2rem; font-size: clamp(1.4rem, 2.4vw, 1.9rem); }
/* --- audiences ------------------------------------------------------------ */
.aud-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr)); gap: 1rem; }
.aud-tile { padding: 1.5rem 1.5rem 1.4rem; border: 1px solid var(--line); border-radius: var(--radius); background: var(--paper-2); display: grid; gap: .6rem; align-content: start; }
.aud-ico { display: grid; place-items: center; width: 2.75rem; height: 2.75rem; border-radius: calc(var(--radius) + 4px); background: color-mix(in srgb, var(--primary) 12%, var(--paper)); color: var(--brand-text); }
.aud-ico .qz-ico { width: 1.4rem; height: 1.4rem; }
.aud-tile h3 { font-size: 1.25rem; margin: 0; }
.aud-tile p { margin: 0; color: var(--muted); font-size: .95rem; }
/* --- faq ------------------------------------------------------------------ */
.faq-wrap { max-width: 54rem; }
.faq-list { display: grid; gap: 0; border-top: 1px solid var(--line); }
.faq-item { border-bottom: 1px solid var(--line); }
.faq-item summary { list-style: none; cursor: pointer; display: flex; justify-content: space-between; align-items: center; gap: 1rem; padding: 1.1rem 0; font-family: var(--display); font-size: clamp(1.05rem, 1.6vw, 1.3rem); }
.faq-item summary::-webkit-details-marker { display: none; }
.faq-mark { position: relative; flex: none; width: 1.4rem; height: 1.4rem; border-radius: 50%; border: 1px solid var(--line); }
.faq-mark::before, .faq-mark::after { content: ""; position: absolute; left: 50%; top: 50%; width: .6rem; height: 2px; background: var(--primary); transform: translate(-50%, -50%); }
.faq-mark::after { transform: translate(-50%, -50%) rotate(90deg); transition: transform .25s; }
.faq-item[open] .faq-mark::after { transform: translate(-50%, -50%) rotate(0deg); }
.faq-item p { margin: 0 0 1.2rem; color: var(--muted); max-width: 60ch; line-height: 1.6; }
.detail-grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr); gap: clamp(2rem, 5vw, 4rem); align-items: start; }
.detail-grid--text { grid-template-columns: minmax(0, 40rem); }
.detail-list { list-style: none; margin: 1.2rem 0 0; padding: 0; }
.detail-list li { padding: .85rem 0; border-top: 1px solid var(--line); font-size: 1.02rem; }
.detail-list li:last-child { border-bottom: 1px solid var(--line); }
.detail-photo { margin: 0; aspect-ratio: 4 / 3; overflow: hidden; border-radius: var(--radius); }
.detail-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
@media (max-width: 56rem) { .detail-grid { grid-template-columns: 1fr; } }
.worklist { font-family: var(--mono); font-size: .74rem; letter-spacing: .04em; color: var(--ink); margin-top: 1rem !important; line-height: 1.6; }
.worklist span { color: var(--brand-text); text-transform: uppercase; letter-spacing: .14em; margin-right: .6rem; }

/* --- services: ledger ----------------------------------------------------- */
/* One column of numbered rows, echoing the ledger strip the hero already closes on
   rather than importing a new idiom. It cannot ragged-row the way .svc-list can: there
   is exactly one column at every count the section receives, so unlike .svc-list above
   it needs no data-count collapse rule. */
.svc-ledger { list-style: none; margin: 0; padding: 0; display: grid; gap: 0; border-top: 2px solid var(--ink); }
.svc-ledger li { display: grid; grid-template-columns: 3.5rem minmax(0, 1fr); gap: 1.5rem; padding: 1.9rem 0; border-bottom: 1px solid var(--line); align-items: baseline; }
/* --brand-text, not --primary: foreground text on --paper - see the comment on
   .eyebrow above for why the two tokens differ. */
.svc-ledger-n { font-family: var(--mono); font-size: 1.1rem; font-weight: 600; color: var(--brand-text); }
.svc-ledger-body h3 { margin-bottom: .55rem; }
.svc-ledger-body p { color: var(--muted); margin-bottom: 0; max-width: 46ch; }

/* --- services: aside ------------------------------------------------------- */
/* A sticky left column for the section head, the services stacked to its right on
   --paper-2. One column at every count - the split itself is what distinguishes this
   variant, not the item list, so like the ledger above it needs no data-count rule. */
.svc-aside-grid { display: grid; grid-template-columns: minmax(0, 26rem) minmax(0, 1fr); gap: clamp(2rem, 5vw, 4rem); align-items: start; }
.svc-aside-head { position: sticky; top: 6rem; margin: 0; min-width: 0; }
/* The head column is at most 26rem; a family's uppercase display h2 at the shared
   size put "Sähköurakoinnin" 189px into the list beside it. Sized for the column,
   with hyphenation as the last resort, never overflow. */
.svc-aside-head h2 { font-size: clamp(1.9rem, 3.1vw, 2.9rem); overflow-wrap: anywhere; hyphens: auto; }
.svc-aside-list { display: grid; background: var(--paper-2); padding: clamp(1.5rem, 3vw, 2.5rem); border-radius: var(--radius); }
/* .svc carries its own border-bottom for the ruled-list variant above, where the
   list has no surrounding card. Here it sits inside a padded card with no rule of
   its own, so the last .svc's border-bottom hangs as a floating rule with 26px of
   padding beneath it and nothing beneath that. */
.svc-aside-list .svc:last-child { border-bottom: 0; }

/* --- credentials --------------------------------------------------------- */
/* A row of figures, each on its own hairline column. What replaced a strip of bare
   words in a dark bar: an accreditation gets the space it earns. */
.creds { padding: calc(2.75rem * var(--rhythm)) 0; border-bottom: 1px solid var(--line); }
.creds-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 10rem), 1fr)); gap: 1.75rem clamp(1.5rem, 3vw, 3rem); }
.cred { display: grid; gap: .2rem; padding-left: 1rem; border-left: 2px solid var(--primary); }
.cred-label { font-family: var(--display); font-size: clamp(1.15rem, 1.9vw, 1.6rem); line-height: 1.1; letter-spacing: -0.01em; }
.cred-detail { font-family: var(--mono); font-size: .68rem; text-transform: uppercase; letter-spacing: .16em; color: var(--muted); }

/* --- why us -------------------------------------------------------------- */
/* A tally, not a sequence: these are discrete claims and the reader is counting
   reasons, so the figures sit beside the text rather than above it as steps. */
.why-list { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 24rem), 1fr)); gap: 0 clamp(2rem, 5vw, 4rem); border-top: 2px solid var(--ink); }
.why-list li { display: grid; grid-template-columns: 2.75rem minmax(0, 1fr); gap: 1rem; align-items: baseline; padding: 1.5rem 0; border-bottom: 1px solid var(--line); }
/* --brand-text, not --primary: foreground text on --paper - see the comment on
   .eyebrow above for why the two tokens differ. */
.why-n { font-family: var(--mono); font-size: .78rem; font-weight: 600; letter-spacing: .1em; color: var(--brand-text); }
.why-text { font-size: clamp(1rem, 1.4vw, 1.16rem); line-height: 1.45; }
/* Two columns only close cleanly on an even count: each li carries its own rule, so an
   odd count leaves the last row's border drawn under one column and not the other. At
   the 1200px wrap a 24rem minimum track fits exactly two columns, so 4 and 6 close and
   1, 3 and 5 do not. Those collapse to one column, and 2 joins them by choice — two
   points in two columns reads as a grid that lost its other half, whereas one column
   reads as a short list somebody meant to write. The render ceiling is 6, so this
   enumeration is complete; raise that ceiling and 7 needs adding here. */
.why-list[data-count="1"], .why-list[data-count="2"], .why-list[data-count="3"], .why-list[data-count="5"] { grid-template-columns: minmax(0, 1fr); }
/* Four is the one count auto-fit gets wrong at desktop width: three 24rem tracks fit
   the wrap, so four items become a row of three and an orphan. Two by two instead. */
.why-list[data-count="4"] { grid-template-columns: repeat(2, minmax(0, 1fr)); }
/* A single point needs no tally, and gets the text column the number was holding. */
.why-list[data-count="1"] li { grid-template-columns: minmax(0, 1fr); }
/* Single column has the full measure, so use it. At the shared 1.16rem ceiling these
   sat as a checklist of five small lines with a lot of air between them - the least
   persuasive treatment of the most persuasive content on the page, which is the
   business's own best sentences about itself. Larger type, tighter rows. */
.why-list[data-count="1"] .why-text,
.why-list[data-count="2"] .why-text,
.why-list[data-count="3"] .why-text,
.why-list[data-count="5"] .why-text { font-size: clamp(1.15rem, 2vw, 1.5rem); line-height: 1.35; max-width: 44ch; }
.why-list[data-count="1"] li,
.why-list[data-count="2"] li,
.why-list[data-count="3"] li,
.why-list[data-count="5"] li { padding: 1.15rem 0; align-items: start; }
.why-list[data-count="1"] .why-n,
.why-list[data-count="2"] .why-n,
.why-list[data-count="3"] .why-n,
.why-list[data-count="5"] .why-n { padding-top: .38rem; }

/* --- why us: cards --------------------------------------------------------- */
/* Bordered cards, two to a row. minmax(min(100%, 24rem), 1fr), the same 24rem track
   minimum .why-list uses above (not .svc-list's 22rem, which closes three columns at
   this wrap's 1200px width, not two) - that is what keeps this a two-column grid
   responsive down to a phone instead of a fixed repeat(2, ...) that would force two
   columns onto a 390px viewport. */
.why-cards { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 24rem), 1fr)); gap: 1.5rem clamp(2rem, 5vw, 4rem); }
/* Each card carries its own border, so two columns only close cleanly on an even
   count: an odd count leaves one lone card in the final row instead of a matched
   pair. The render ceiling is 6, so - exactly as .why-list above - 1, 3 and 5 collapse
   to one column and 2 joins them (a lone pair of cards reads as a grid that lost its
   other half); this enumeration is complete, and raising the ceiling means adding 7. */
.why-cards[data-count="1"], .why-cards[data-count="2"], .why-cards[data-count="3"], .why-cards[data-count="5"] { grid-template-columns: minmax(0, 1fr); }
.why-cards[data-count="4"] { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.why-card { border: 1px solid var(--line); border-radius: var(--radius); padding: clamp(1.5rem, 3vw, 2rem); }
.why-card .why-text { font-size: clamp(1rem, 1.4vw, 1.16rem); line-height: 1.45; }

/* --- why us: statements ----------------------------------------------------- */
/* One large claim per row, unnumbered - these are quoted from the business's own
   site, not steps to count, so the section's job is to let each one read at size
   rather than tally them. One column at every count the section can receive, so
   unlike .why-cards above it needs no data-count collapse rule. */
.why-stmts { list-style: none; margin: 0; padding: 0; display: grid; border-top: 2px solid var(--ink); }
.why-stmts li { padding: clamp(2rem, 4vw, 3rem) clamp(1.5rem, 3vw, 2.5rem); }
.why-stmts li:nth-child(even) { background: var(--paper-2); }
.why-stmt-text { display: block; font-family: var(--display); font-size: clamp(1.5rem, 3.2vw, 2.4rem); line-height: 1.18; letter-spacing: -0.01em; font-weight: 400; max-width: 34ch; }

/* --- team ---------------------------------------------------------------- */
.team { background: var(--paper-2); }
.team-list { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 14rem), 1fr)); gap: 1px; background: var(--line); border: 1px solid var(--line); }
/* This grid shows --line through its gap exactly the way .proof-columns and this
   file's other hairline grids do (see the comment there), which the final review
   for demo-variety-layer4 initially read as "no filled background, so a ragged row
   here is mild whitespace" - wrong: .team-list DOES paint background: var(--line)
   on the container, so an empty cell here is the same solid grey rectangle
   .proof-columns had, not whitespace. Measured ragged (four items, three columns)
   from 736px to 978px, clean at 980px and above where four 14rem-minimum tracks
   first fit. Fixed the same way: forced explicit above that width, one column
   below it, so no three-column intermediate state can occur. Swept 320px-1600px
   after the fix; clean throughout. */
.team-list[data-count="4"] { grid-template-columns: minmax(0, 1fr); }
@media (min-width: 980px) {
  .team-list[data-count="4"] { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
/* The fix wave that closed count 4 above left counts 1-3 on the bare auto-fit
   template, and 3 is the one that ragged-rows: three items and a 2-column fit
   leaves item 3 alone in row two beside an empty, --line-painted cell. Measured
   ragged from 492px to 733px, clean at 734px and above where three 14rem-minimum
   tracks first fit - reproduced live against lead 07717112 (ENLITE FITNESS, a
   3-person team), the one live lead this reached. Counts 1 and 2 were swept too
   and never rag (2 fits its own two columns exactly; auto-fit collapses any track
   that ends up with no item placed in it at all, which is what keeps a 2-item row
   whole rather than leaving a phantom third cell). Fixed the same way as count 4:
   forced explicit above the width three columns fit, one column below it. Swept
   320px-1600px after the fix; clean throughout at all of counts 1, 2, 3 and 4. */
.team-list[data-count="3"] { grid-template-columns: minmax(0, 1fr); }
@media (min-width: 734px) {
  .team-list[data-count="3"] { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
.team-list li { background: var(--paper-2); display: grid; gap: .3rem; padding: 1.5rem 1.4rem; align-content: start; }
.team-name { font-family: var(--display); font-size: clamp(1.2rem, 2vw, 1.6rem); line-height: 1.1; letter-spacing: -0.01em; }
.team-role { color: var(--muted); font-size: .88rem; line-height: 1.45; }

/* --- team: monograms ------------------------------------------------------- */
/* Portrait-sized cards, two to a row. 24rem is not a fresh guess: it is the same
   track minimum .why-cards and .svc-list's four-item collapse already use above,
   confirmed there to resolve to exactly two columns at this file's real 1200px
   wrap - reused rather than picked anew, because an unmeasured track-min is exactly
   how two earlier tasks in this plan got their column count wrong. */
.team-monograms { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 24rem), 1fr)); gap: 1.5rem clamp(2rem, 5vw, 4rem); }
/* Team tops out at four items. Two needs no help - a pair of cards exactly fills one
   row of a two-column grid - and four is a genuine 2x2, so both are left to the
   auto-fit template above. Three is the ragged case: two cards close row one and the
   third sits alone in row two beside an empty cell, a portrait-shaped hole where a
   card should be, so it collapses to one column instead. One card is not ragged -
   auto-fit already collapses the second track when nothing sits in it - but left
   alone it stretches to the full 1200px wrap, a badge and two lines of text lost in
   a card sized for two columns; capping its track at one card's own width keeps it
   reading as a considered size rather than an accident of the grid maths. */
/* One person is a byline, not a grid: a single horizontal card, centred under the
   heading, monogram beside the name. A lone 24rem card left-aligned in a two-column
   grid read as a section that had lost its other half. */
.team-monograms[data-count="1"] { grid-template-columns: minmax(0, 40rem); justify-content: center; }
.team-monograms[data-count="1"] li { grid-template-columns: auto minmax(0, 1fr); grid-template-areas: "mono name" "mono role"; column-gap: 1.4rem; align-items: center; }
.team-monograms[data-count="1"] .team-monogram { grid-area: mono; }
.team-monograms[data-count="1"] .team-name { grid-area: name; align-self: end; }
.team-monograms[data-count="1"] .team-role { grid-area: role; align-self: start; }
.team-monograms[data-count="3"] { grid-template-columns: minmax(0, 1fr); }
.team-monograms li { display: grid; gap: .9rem; align-content: start; justify-items: start; border: 1px solid var(--line); border-radius: var(--radius); padding: clamp(1.75rem, 3.5vw, 2.5rem); }
/* The monogram stands where a portrait would. The image bank holds no people, so
   this is the honest treatment - and it is drawn from the name in sections.ts'
   monogram(), never hardcoded. aria-hidden in the markup: the name that follows it
   already says the same thing to assistive tech. */
.team-monogram { display: grid; place-items: center; width: 4.25rem; height: 4.25rem; border-radius: 50%; background: var(--ink); color: var(--paper); font-family: var(--mono); font-size: 1.2rem; font-weight: 600; letter-spacing: .02em; }

/* --- team: inline ------------------------------------------------------- */
/* One generous line per person - name set large in the display face, role set small
   in mono, a hairline between rows. One column at every count the section can
   receive, so unlike .team-monograms above it needs no data-count collapse rule.
   The name and role spans keep the shared .team-name/.team-role classes .team-list
   and .team-monograms already use above, rather than their own team-inline-*
   classes, and are restyled here by descendant selector - the same element, a
   different line height and typeface for this variant's "generous leading". */
.team-inline-list { list-style: none; margin: 0; padding: 0; display: grid; border-top: 2px solid var(--ink); }
.team-inline-list li { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: .4rem 2rem; padding: clamp(1.6rem, 3.2vw, 2.4rem) 0; border-bottom: 1px solid var(--line); }
.team-inline-list .team-name { font-family: var(--display); font-size: clamp(1.3rem, 2.6vw, 2rem); line-height: 1.5; letter-spacing: -0.01em; }
.team-inline-list .team-role { font-family: var(--mono); font-size: .76rem; text-transform: uppercase; letter-spacing: .09em; color: var(--muted); }

/* --- recent work --------------------------------------------------------- */
.work-list { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr)); gap: 0 clamp(2rem, 5vw, 4rem); border-top: 2px solid var(--ink); }
.work-list li { padding: 1.15rem 0 1.15rem 1.6rem; border-bottom: 1px solid var(--line); font-family: var(--display); font-size: clamp(1.05rem, 1.6vw, 1.35rem); letter-spacing: -0.005em; position: relative; }
.work-list li::before { content: ""; position: absolute; left: 0; top: calc(1.15rem + .62em); width: .6rem; height: 2px; background: var(--primary); }

/* --- proof -------------------------------------------------------------- */
.proof { background: var(--paper-2); }
/* Stretch, not auto: quotes of wildly different lengths used to leave a ragged row
   of captions floating at three different heights. */
.quotes { display: grid; gap: 2rem; grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr)); align-items: stretch; }
/* Three testimonials in an 18rem-minimum auto-fit grid leave a ragged two-column row
   from 662px to 1008px, the same shape of bug as .proof-columns and .svc-list above -
   auto-fit's column count is a continuous function of width and gives two columns for
   three items well before three columns fit. .quotes paints no background of its own,
   so unlike those two the vacancy here reads as whitespace rather than a filled void -
   still fixed the same way: forced explicit at the width three columns already fit
   (1009px and up), one column below it. Swept 320px-1600px after the fix; clean
   throughout. */
.quotes[data-count="3"] { grid-template-columns: minmax(0, 1fr); }
@media (min-width: 1009px) {
  .quotes[data-count="3"] { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
.quote { margin: 0; display: flex; flex-direction: column; border-left: 3px solid var(--primary); padding-left: 1.25rem; }
.quote blockquote {
  margin: 0 0 1.25rem; font-family: var(--body); font-size: clamp(1.05rem, 1.5vw, 1.28rem);
  line-height: 1.45; font-weight: 500; letter-spacing: -0.01em;
}
.quote figcaption { margin-top: auto; font-family: var(--mono); font-size: .7rem; text-transform: uppercase; letter-spacing: .1em; color: var(--muted); }

/* --- proof: feature -------------------------------------------------------- */
/* The first testimonial set large on an inverted ground. Unlike hero--bleed's
   dark-on-photograph pairing, this inverts onto a flat token colour rather than an
   image of unknown brightness, so it needs no luminance gate: --ink and --paper come
   from the family's ground table, paired to stay high-contrast regardless of whether
   a demo's family is plate (cream / near-black) or parlour (cream / charcoal) or any
   future family. See the comment on proofFeature in sections.ts for the palette
   range tested. Attribution is a <figcaption> sibling of the <blockquote>, both
   inside one <figure> - the spec's own pattern for a quotation with its source, and
   one that adds no implicit ARIA landmark role (see the comment on proofFeature in
   sections.ts). */
/* The heading lives inside the dark block with the lead quote: on paper above it,
   the two read as separate sections. */
.proof-feature-lead { background: var(--ink); color: var(--paper); padding: clamp(3rem, 7vw, 5.5rem) 0; }
.proof-feature-lead h2 { color: var(--paper); }
.proof-feature-lead .eyebrow { color: color-mix(in srgb, var(--paper) 72%, var(--ink)); }
.proof-feature-quote {
  margin: 0; font-family: var(--display); font-weight: 400;
  font-size: clamp(1.7rem, 3.6vw, 2.7rem); line-height: 1.2; letter-spacing: -0.01em; max-width: 40ch;
}
.proof-feature-figure figcaption {
  display: block; margin-top: 1.5rem; font-family: var(--mono); font-size: .74rem;
  text-transform: uppercase; letter-spacing: .1em; color: color-mix(in srgb, var(--paper) 70%, var(--ink));
}
/* The rest, small and back on the normal ground - complete quotes, not just names:
   dropping the body to leave only an attribution would cut a testimonial down to a
   credit, which this section can no more do than it can drop one outright. */
.proof-feature-rest {
  list-style: none; margin: clamp(2rem, 4vw, 3rem) 0 0; padding: 0; display: grid;
  gap: 0 clamp(2rem, 5vw, 4rem); grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr));
}
.proof-feature-rest li { padding: 1.15rem 0; border-top: 1px solid var(--line); }
.proof-feature-rest blockquote { margin: 0; font-size: .92rem; line-height: 1.42; color: var(--muted); }
.proof-feature-rest figcaption { display: block; margin-top: .4rem; font-family: var(--mono); font-size: .68rem; text-transform: uppercase; letter-spacing: .08em; color: var(--muted); }

/* --- proof: columns -------------------------------------------------------- */
/* Equal columns, rule-separated by a 1px gap over --line showing through - the same
   technique .team-list uses for its own hairline grid, rather than a border-left
   whose direction would need flipping when the grid collapses to one column below.
   Each .proof-column is a <figure> with a <blockquote> and sibling <figcaption>, the
   same pairing .quote above and proofFeature's figures use - see the comment on
   proofFeature in sections.ts for why that beats a <footer> nested in the
   blockquote. */
.proof-columns {
  display: grid; grid-template-columns: minmax(0, 1fr);
  gap: 1px; background: var(--line); border-top: 2px solid var(--ink); border-bottom: 2px solid var(--ink);
}
/* Two items in a three-column track stretch to fill the collapsed third track, each
   nearly twice the width three columns give them - a visibly different grid, not the
   same one missing a column. Collapsing to one column instead is the same call
   .why-list and .why-cards make for their own count of 2. The ceiling is 3, so this
   enumeration is complete; raise it and the counts in between need remeasuring. */
.proof-columns[data-count="2"] { grid-template-columns: minmax(0, 1fr); }
/* THE CRITICAL FIX, final review of demo-variety-layer4. This used to read
   repeat(auto-fit, minmax(min(100%, 22rem), 1fr)), with a comment claiming three
   items "resolve to exactly three columns" - true only at this file's own 1200px
   test wrap. auto-fit's column count is a continuous function of container width:
   below ~1150px it gives TWO columns for three items, leaving the third alone in row
   two beside an empty cell. Because this container paints background: var(--line)
   through its gap: 1px, that empty cell is not whitespace - it is a solid grey
   rectangle (370x160px at 800px viewport width) where a testimonial should be. Live
   on 5 of 33 leads (07754378, 10543710, SC573834, 11684817, 07717112). Measured
   ragged from 768px to 1149px, clean at 1150px and above where three 22rem tracks
   first fit. Fixed by making the three-column case explicit at that width instead of
   leaving it to auto-fit, so no two-column state can exist at any width - swept and
   confirmed clean from 320px to 1600px. See the comment on proofColumns in
   sections.ts for the full account. */
.proof-columns[data-count="3"] { grid-template-columns: minmax(0, 1fr); }
@media (min-width: 1150px) {
  .proof-columns[data-count="3"] { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
.proof-column { margin: 0; background: var(--paper-2); padding: clamp(1.75rem, 3.5vw, 2.5rem) clamp(1.5rem, 3vw, 2rem); }
.proof-column blockquote {
  margin: 0; font-family: var(--body); font-size: clamp(1rem, 1.4vw, 1.15rem);
  line-height: 1.45; font-weight: 500; letter-spacing: -0.01em;
}
.proof-column figcaption { display: block; margin-top: 1.1rem; font-family: var(--mono); font-size: .7rem; text-transform: uppercase; letter-spacing: .1em; color: var(--muted); }

/* --- about / area / process --------------------------------------------- */
.about-copy { max-width: 62ch; }
.about p { color: var(--muted); }

.area { background: var(--paper-2); }
.area-grid { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 2rem clamp(2rem, 5vw, 4rem); align-items: end; }
.area-full { color: var(--muted); margin: .9rem 0 0; max-width: 48ch; }
.area-aside { margin: 0; display: grid; gap: 1.1rem; }
.area-aside div { display: grid; gap: .2rem; }
.area-aside dt { font-family: var(--mono); font-size: .68rem; text-transform: uppercase; letter-spacing: .16em; color: var(--brand-text); }
.area-aside dd { margin: 0; font-family: var(--display); font-size: clamp(1.05rem, 1.5vw, 1.35rem); letter-spacing: -0.005em; }

.steps { list-style: none; margin: 0; padding: 0; display: grid; gap: 0; }
.steps li { display: grid; grid-template-columns: 5rem minmax(0, 1fr); gap: 1.5rem; padding: 1.6rem 0; border-top: 1px solid var(--line); align-items: baseline; }
.steps li:last-child { border-bottom: 1px solid var(--line); }
.step-n { font-family: var(--mono); font-size: 1.5rem; font-weight: 600; color: var(--brand-text); }
.step-body strong { display: block; font-family: var(--display); font-size: 1.35rem; font-weight: 400; margin-bottom: .3rem; }
.step-body span { color: var(--muted); }

/* --- cta band ----------------------------------------------------------- */
.cta-band { background: var(--field); color: var(--paper); }
/* --on-accent, not a hardcoded #10130f: that near-black only reads against a light
   accent, and this is the page's primary call to action - see onAccentText in
   palette.ts, which picks near-black or near-white by the accent's own luminance. */
.cta-band .btn--invert { background: var(--cta-button); color: var(--on-cta-button); }
/* Left-aligned and capped, rather than pushed apart. space-between put the headline
   at one edge and the button at the other with half a screen of brand colour in
   between, and on narrower viewports the actions wrapped and left the void anyway. */
.cta-grid { display: grid; gap: 2rem clamp(2rem, 5vw, 4rem); grid-template-columns: minmax(0, 1fr) auto; align-items: end; }
.cta-band h2 { margin-bottom: .6rem; max-width: 20ch; }
.cta-band p { color: color-mix(in srgb, var(--paper) 78%, var(--field)); max-width: 46ch; margin: 0; }
.cta-actions { display: flex; align-items: center; gap: 1.5rem; flex-wrap: wrap; }
/* Family chrome: the closing band in four shapes. */
.cta-band--line .cta-grid { align-items: center; }
.cta-band--line h2 { margin-bottom: 0; max-width: 28ch; }
.cta-band--quote .cta-grid { grid-template-columns: minmax(0, 1fr); gap: 2rem; }
.cta-quote { margin: 0 0 2.2rem; max-width: 34ch; }
.cta-quote p { margin: 0; font-family: var(--display); font-size: clamp(1.5rem, 3vw, 2.4rem); line-height: 1.15; color: var(--paper); }
.cta-quote footer { margin-top: 1rem; font-family: var(--mono); font-size: .68rem; text-transform: uppercase; letter-spacing: .14em; color: color-mix(in srgb, var(--paper) 62%, var(--field)); }
.cta-band--quote h2 { font-size: clamp(1.1rem, 1.8vw, 1.4rem); max-width: 40ch; }
.cta-band--split { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); align-items: stretch; }
.cta-band--split .cta-photo { position: relative; min-height: 22rem; }
.cta-band--split .cta-photo img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.cta-band--split .cta-grid { grid-template-columns: minmax(0, 1fr); align-items: start; align-content: center; padding-left: clamp(2rem, 4vw, 4rem); }
@media (max-width: 60rem) {
  .cta-band--split { grid-template-columns: minmax(0, 1fr); }
  .cta-band--split .cta-photo { min-height: 14rem; }
  .cta-band--split .cta-grid { padding-left: 0; }
}
.cta-number { font-family: var(--mono); font-size: 1.25rem; font-weight: 600; white-space: nowrap; }

/* --- contact ------------------------------------------------------------ */
.contact-grid { display: grid; grid-template-columns: .85fr 1.15fr; gap: clamp(2rem, 5vw, 4rem); align-items: start; }
.contact-intro p { color: var(--muted); }
.details { margin: 2rem 0 0; display: grid; gap: 1.1rem; }
.details div { display: grid; gap: .2rem; }
.details dt { font-family: var(--mono); font-size: .7rem; text-transform: uppercase; letter-spacing: .13em; color: var(--brand-text); }
.details dd { margin: 0; font-size: 1.02rem; }
.details a { text-decoration: none; }
.details a:hover { color: var(--brand-text); }
.form { background: var(--paper-2); border: 1px solid var(--line); border-radius: var(--radius); padding: clamp(1.5rem, 3vw, 2.25rem); display: grid; gap: 1.1rem; }
.field { display: grid; gap: .4rem; }
.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 1.1rem; }
.form label { font-family: var(--mono); font-size: .7rem; text-transform: uppercase; letter-spacing: .13em; color: var(--muted); }
.form input, .form textarea {
  font: inherit; font-size: .98rem; padding: .85rem .9rem; border: 1px solid var(--line);
  border-radius: var(--radius); background: var(--paper); color: var(--ink); width: 100%;
  transition: border-color .2s, box-shadow .2s;
}
.form input:focus, .form textarea:focus { outline: none; border-color: var(--primary); box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 18%, transparent); }
.form button { justify-self: start; }
.form-status { min-height: 1.3em; margin: 0; font-family: var(--mono); font-size: .8rem; color: var(--brand-text); }

/* --- footer ------------------------------------------------------------- */
.footer { margin-top: auto; background: var(--ink); color: color-mix(in srgb, var(--paper) 68%, var(--ink)); padding: 3.5rem 0 2.5rem; }
.footer-grid { display: grid; grid-template-columns: .8fr 1.2fr; gap: 2.5rem; }
.footer-brand { font-family: var(--display); font-weight: 400; text-transform: uppercase; color: var(--paper); font-size: 1.1rem; letter-spacing: -0.01em; }
.footer a { color: var(--paper); }
.footer-note { font-size: .82rem; line-height: 1.6; }
/* Our mark on their draft: a dark mono pill naming where it goes. None of the demo's
   type or colour on purpose (see apps.ts priimoTag). */
.priimo-tag { display: inline-flex; align-items: center; gap: .6rem; padding: .38rem .7rem .38rem .75rem; border-radius: 999px; background: #10130f; color: #f2efe9; font-family: var(--mono); font-size: .64rem; letter-spacing: .1em; text-transform: uppercase; text-decoration: none; line-height: 1; white-space: nowrap; box-shadow: 0 1px 2px rgba(0,0,0,.25); }
/* The spans carry their own colours: a family's footer or eyebrow link rule reaches the anchor, never these. */
a.priimo-tag .priimo-tag-label { color: #f2efe9; }
a.priimo-tag .priimo-tag-host { color: #ffd166; letter-spacing: .04em; text-transform: none; font-size: .68rem; }
.priimo-tag:hover { background: #1e2320; }
.priimo-tag:hover .priimo-tag-host { text-decoration: underline; }
.footer .priimo-tag { margin-top: .6rem; }
/* The whole card is the link when the service has a page. */
.svc--link { cursor: pointer; }
.svc--link .svc-more::after { content: ""; position: absolute; inset: 0; }
.svc--link:hover .svc-more { color: var(--brand-text); }
.footer-note p { margin-bottom: .7rem; }
.footer-note a { overflow-wrap: anywhere; }
/* Family chrome: a dense four-column footer, and a one-row minimal one. */
.footer-grid--dense { grid-template-columns: 1.1fr .7fr 1fr 1.4fr; gap: 2rem; }
.footer-links { list-style: none; margin: 0; padding: 0; }
.footer-links a { display: block; padding: .15rem 0; font-family: var(--mono); font-size: .74rem; text-transform: uppercase; letter-spacing: .12em; color: inherit; text-decoration: none; }
.footer-links a:hover { color: var(--paper); }
.footer-details { margin: 0; }
.footer-details dt { font-family: var(--mono); font-size: .66rem; text-transform: uppercase; letter-spacing: .14em; margin-bottom: .2rem; }
.footer-details dd { margin: 0 0 .9rem; font-size: .9rem; color: color-mix(in srgb, var(--paper) 82%, var(--ink)); }
/* The business's own profiles, in the page links' mono caps. */
.footer-socials { list-style: none; margin: 1.1rem 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: .3rem 1.4rem; }
.footer-socials a { font-family: var(--mono); font-size: .72rem; text-transform: uppercase; letter-spacing: .12em; color: inherit; text-decoration: none; border-bottom: 1px solid color-mix(in srgb, var(--paper) 30%, transparent); }
.footer-socials a:hover { color: var(--paper); border-bottom-color: var(--paper); }
.footer--minimal { padding: 2.5rem 0 2rem; }
.footer-row { display: flex; flex-wrap: wrap; align-items: baseline; gap: .6rem 2.2rem; margin-bottom: 1.4rem; }
.footer-row p { margin: 0; }
.footer--minimal .footer-note { font-size: .76rem; max-width: 70ch; }
@media (max-width: 60rem) {
  .footer-grid--dense { grid-template-columns: 1fr 1fr; }
}

/* --- plate hero ------------------------------------------------------------ */
/* sttsaneeraus's fold: the headline huge and uppercase, an accent bar that draws
   itself in from the page's left edge under it, and the photograph mounted on a dark
   plate in the right field with one corner cut. The inner spans the whole wrap (the
   62rem cap is for copy that sits alone) so the headline column has room to set
   Finnish compounds at size without breaking them. */
.hero--plate .hero-inner { max-width: none; display: grid; grid-template-columns: minmax(0, 1fr) minmax(16rem, 40%); column-gap: clamp(2rem, 5vw, 5rem); align-items: center; }
.hero--plate .hero-inner > :not(.hero-plate) { grid-column: 1; }
.hero--plate h1 { max-width: none; font-size: clamp(2.6rem, 5.2vw, 5.5rem); line-height: .95; text-transform: uppercase; letter-spacing: -.01em; margin-bottom: .5rem; animation: h1-wipe .56s cubic-bezier(.16,1,.3,1) .08s both; }
/* The bar is in flow (it reserves its height under the headline) and paints through a
   pseudo-element that runs from its own right edge to beyond the viewport's left edge,
   so it reaches the page edge whatever the wrap's margin is. Negative overflow to the
   left never scrolls. */
.hero-flank { display: block; position: relative; height: clamp(8px, 1vw, 12px); width: 88%; margin: .5rem 0 1.8rem; }
.hero-flank::before { content: ''; position: absolute; top: 0; bottom: 0; right: 0; width: 100vw; background: var(--accent); clip-path: inset(0 0 0 0); animation: flank-draw .8s cubic-bezier(.16,1,.3,1) .3s both; }
.hero-plate { --cut: clamp(40px, 5vw, 72px); grid-column: 2; grid-row: 1 / span 6; position: relative; margin: 0; aspect-ratio: 4 / 5; align-self: center; }
.hero-plate-back { position: absolute; inset: 1.4rem -1.4rem -1.4rem 1.4rem; background: color-mix(in srgb, var(--field) 55%, #000); clip-path: polygon(0 0, 100% 0, 100% 100%, calc(0.306 * var(--cut)) 100%, 0 calc(100% - var(--cut))); }
.hero-plate .drift { position: relative; height: 100%; clip-path: polygon(0 0, 100% 0, 100% 100%, calc(0.306 * var(--cut)) 100%, 0 calc(100% - var(--cut))); }
@keyframes h1-wipe { from { clip-path: inset(-5% 100% -5% -5%); } to { clip-path: inset(-5% -5% -5% -5%); } }
@keyframes flank-draw { from { clip-path: inset(0 100% 0 0); } to { clip-path: inset(0 0 0 0); } }
@media (max-width: 56rem) {
  .hero--plate .hero-inner { grid-template-columns: 1fr; }
  .hero-plate { grid-column: 1; grid-row: auto; aspect-ratio: 16 / 10; margin-top: 1.5rem; }
}

/* --- marquee --------------------------------------------------------------- */
/* Names in display type on an endless line (sttsaneeraus). Two copies of the list;
   the track slides by half its width, so the loop has no seam. */
.marquee { overflow: hidden; background: var(--paper-2); padding: clamp(1rem, 2vw, 1.5rem) 0; border-block: 1px solid var(--line); }
.marquee-track { display: flex; width: max-content; animation: marquee 40s linear infinite; }
.marquee:hover .marquee-track { animation-play-state: paused; }
.marquee-list { list-style: none; margin: 0; padding: 0 0 0 clamp(1.75rem, 4vw, 4rem); display: flex; gap: clamp(1.75rem, 4vw, 4rem); flex: none; font-family: var(--display); font-size: clamp(1.6rem, 3.2vw, 2.8rem); line-height: 1; letter-spacing: -.01em; white-space: nowrap; color: var(--ink); }
.marquee-list li { display: flex; align-items: center; gap: clamp(1.75rem, 4vw, 4rem); }
.marquee-list li::after { content: ""; display: block; width: 10px; height: 14px; background: var(--accent); clip-path: polygon(4px 0, 100% 0, 6px 100%, 0 100%); }
@keyframes marquee { to { transform: translateX(-50%); } }

/* --- figures --------------------------------------------------------------- */
/* The facts as numerals on a dark band (sttsaneeraus's luvut): the number in the
   display face and the family accent, its unit beside it, the caption in mono. */
.figures { background: var(--ink); color: var(--paper); padding: clamp(1.75rem, 4vw, 3rem) 0; }
.figures-row { list-style: none; margin-block: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 11rem), 1fr)); gap: 1.5rem clamp(1.5rem, 4vw, 3rem); }
.figures-row li { display: grid; gap: .45rem; align-content: end; }
.fig-n { display: flex; align-items: baseline; gap: .35rem; font-family: var(--display); font-weight: 400; font-size: clamp(2.4rem, 5vw, 4.2rem); line-height: .95; letter-spacing: -.01em; color: var(--accent); }
.fig-n--word { font-size: clamp(1.6rem, 3vw, 2.6rem); }
.fig-u { font-size: .45em; letter-spacing: 0; }
.fig-c { font-family: var(--mono); font-size: .72rem; letter-spacing: .12em; text-transform: uppercase; color: color-mix(in srgb, var(--paper) 72%, var(--ink)); }

/* --- services as bands --------------------------------------------------- */
/* Full-bleed rows, photograph inside the row (sttsaneeraus). Alternating surface
   tones; the hovered row takes a third tone that touches neither neighbour. */
.services--bands .section-head { margin-bottom: 0; }
.svc-bands { margin-top: calc(2.4rem * var(--rhythm)); display: grid; }
.svc-band { position: relative; display: grid; grid-template-columns: minmax(0, 1fr) min(38%, 34rem); align-items: stretch; min-height: clamp(11rem, 18vw, 15rem); text-decoration: none; color: inherit; transition: background-color .25s ease; }
.svc-band--text { grid-template-columns: 1fr; }
.svc-band:nth-child(odd) { background: var(--paper-2); }
.svc-band:first-child { min-height: clamp(16rem, 28vw, 24rem); }
.svc-band-body { position: relative; padding: clamp(1.5rem, 3vw, 2.6rem) var(--gutter); display: grid; align-content: center; gap: .55rem; max-width: 62ch; }
.svc-band-body .chip { position: static; justify-self: start; }
.svc-band h3 { font-family: var(--display); font-size: clamp(1.7rem, 3.4vw, 3rem); line-height: 1; letter-spacing: -.01em; transition: transform .25s cubic-bezier(.33,1,.68,1), color .25s ease; }
.svc-band:first-child h3 { font-size: clamp(2.3rem, 4.8vw, 4.2rem); }
.svc-band p { margin: 0; }
.svc-band .svc-more { margin-top: .5rem; }
.svc-band-photo { position: relative; overflow: hidden; margin: 0; }
.svc-band-photo figcaption { display: none; }
.svc-band-photo .drift { height: 100%; transition: transform .8s cubic-bezier(.2,.7,.3,1); }
.svc-band:not(.svc-band--planned):hover { background: color-mix(in srgb, var(--paper-2) 82%, var(--ink)); }
.svc-band:not(.svc-band--planned):hover h3 { transform: translateX(8px); }
.svc-band:not(.svc-band--planned):hover .drift { transform: scale(1.03); }
.svc-band--planned { cursor: default; }
.svc-band--planned h3 { color: var(--muted); }
@media (max-width: 46rem) {
  .svc-band { grid-template-columns: 1fr; min-height: 0; }
  .svc-band-photo { aspect-ratio: 16 / 9; order: -1; }
}

/* --- hover ----------------------------------------------------------------- */
/* A page that answers the pointer reads as made, not generated. One vocabulary:
   rules and numerals turn brand-coloured, cards lift a hair, photographs settle in,
   arrows travel. Nothing on a planned card, which is not there to be clicked. */
.svc, .why-card, .team-monograms li, .proof-columns li, .cred, .work-list li, .svc-ledger li, .detail-list li, .team-inline-list li { transition: border-color .25s ease, color .25s ease, transform .3s cubic-bezier(.2,.7,.3,1), box-shadow .3s ease; }
.svc h3, .svc-ledger-n, .work-list li::before, .team-monogram, .detail-list li { transition: color .25s ease, background-color .25s ease, transform .3s cubic-bezier(.2,.7,.3,1), width .3s ease; }
.svc:not(.svc--planned):hover { border-bottom-color: var(--primary); }
.svc:not(.svc--planned):hover h3, .svc-ledger li:not(.svc--planned):hover .svc-ledger-n, .team-inline-list li:hover .team-name { color: var(--brand-text); }
.svc-more { transition: gap .25s ease; } .svc-more:hover { gap: .75rem; }
.why-card:hover, .proof-columns li:hover { border-color: var(--primary); transform: translateY(-2px); box-shadow: 0 18px 34px -26px color-mix(in srgb, var(--ink) 55%, transparent); }
.cred:hover { border-left-width: 4px; }
.work-list li:hover { border-bottom-color: var(--primary); } .work-list li:hover::before { width: 1.2rem; }
.team-monograms li:hover .team-monogram { background: var(--primary); color: var(--on-primary); }
.detail-list li:hover { border-top-color: var(--brand-text); padding-left: .4rem; color: var(--primary); }
.brand { transition: opacity .2s ease; } .brand:hover { opacity: .7; }
.band figure .drift, .hero-photo .drift { transition: transform .8s cubic-bezier(.2,.7,.3,1); }
.band figure:hover .drift { transform: scale(1.025); }
.qz-tile, .form-field input, .form-field textarea, .form-field select { transition: border-color .25s ease, box-shadow .25s ease; }
.form-field input:focus, .form-field textarea:focus, .form-field select:focus { border-color: var(--primary); box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 18%, transparent); outline: none; }

/* --- reveal -------------------------------------------------------------- */
/* Motion differs by what the element IS - a rule is drawn, a photograph settles, type
   rises, prose only fades. Uniform motion reads as applied. The duration, travel and
   easing come from the lead's motion signature (--rv-*, set in :root above), so two
   demos with the same structure still do not move the same way.
   A bare [data-reveal] keeps the original behaviour: 25 call sites use it and none of
   them may silently lose its animation. */
.js [data-reveal] {
  opacity: 0; transform: translateY(var(--rv-travel));
  transition: opacity var(--rv-duration) var(--rv-ease), transform var(--rv-duration) var(--rv-ease);
}
.js [data-reveal].in { opacity: 1; transform: none; }

.js [data-reveal="rule"] { transform: scaleX(0); transform-origin: left; }
.js [data-reveal="rule"].in { transform: scaleX(1); }

/* The band wrapper gets this, never the <img> inside it: the <img> already carries the
   drift transform (.drift img, above), written straight to .style.transform by the
   scroll-driven script. Both on one element would fight over the same CSS property and
   one would silently lose - Task 6 put drift on the image specifically so the reveal
   could keep the wrapper without colliding. */
.js [data-reveal="photo"] { transform: scale(1.04); }
.js [data-reveal="photo"].in { transform: scale(1); }

.js [data-reveal="type"] { transform: translateY(calc(var(--rv-travel) * 0.7)); }

/* Prose that slides is prose that is hard to read. */
.js [data-reveal="text"] { transform: none; }

.js [data-reveal-delay="1"] { transition-delay: var(--rv-stagger); }
.js [data-reveal-delay="2"] { transition-delay: calc(var(--rv-stagger) * 2); }
.js [data-reveal-delay="3"] { transition-delay: calc(var(--rv-stagger) * 3); }
.js [data-reveal-delay="4"] { transition-delay: calc(var(--rv-stagger) * 4); }

a:focus-visible, button:focus-visible { outline: 3px solid var(--primary); outline-offset: 3px; }

@media (max-width: 900px) {
  .why-list[data-count="4"], .why-cards[data-count="4"] { grid-template-columns: minmax(0, 1fr); }
  body { font-size: 16px; }
  .hero-grid, .contact-grid, .area-grid, .cta-grid, .svc-aside-grid { grid-template-columns: 1fr; }
  .area-grid, .cta-grid { align-items: start; }
  /* Unstuck at the same breakpoint the grid itself collapses at: a single column has
     nothing beside the head to stay pinned against, so a lingering sticky position
     would just pin it mid-scroll for no reason. */
  .svc-aside-head { position: static; top: auto; }
  /* A Finnish compound at 7.6vw overflows a 390px phone ("SANEERAUSKOHTEISIIN"
     at 48px is wider than the column). Cap the size by the viewport, and let the
     browser hyphenate as a last resort rather than clip. */
  .hero h1 { max-width: none; font-size: min(clamp(3rem, 7.6vw, 6rem), 10.4vw); hyphens: auto; overflow-wrap: anywhere; }
  /* On a phone every photographed hero becomes bleed: the split's two columns made
     the photograph a strip beside the copy, and type's band beneath the headline
     began part-way down with the copy across its edge. One column, the field gone,
     the photograph behind the copy under a vertical scrim. The plate keeps its own
     figure. */
  .hero:not(.hero--field):not(.hero--plate) .hero-ground { grid-template-columns: 1fr; grid-template-rows: 1fr; }
  .hero:not(.hero--field):not(.hero--plate) .hero-field { display: none; }
  .hero:not(.hero--field):not(.hero--plate) .hero-photo::after {
    background: linear-gradient(180deg,
      color-mix(in srgb, var(--field) 88%, transparent) 0%,
      color-mix(in srgb, var(--field) 70%, transparent) 55%,
      color-mix(in srgb, var(--field) 45%, transparent) 100%);
  }
  .band--hero .drift { aspect-ratio: 3 / 2; }
  .band--work.band--1 .drift { height: auto; aspect-ratio: 3 / 2; }
  .field-row { grid-template-columns: 1fr; }
  .footer-grid { grid-template-columns: 1fr; }
  .nav-toggle { display: block; }
  .nav-inner { grid-template-columns: auto 1fr; }
  .brand { grid-column: 2; justify-self: start; }
  .nav-cta { grid-column: 1 / -1; justify-self: start; }
  .nav-links { position: absolute; left: 0; right: 0; top: 100%; background: var(--paper); border-bottom: 1px solid var(--line); display: none; }
  .nav-links.open { display: block; }
  .nav-links ul { flex-direction: column; gap: 0; padding: .5rem var(--gutter) 1rem; }
  .nav-links li { border-top: 1px solid var(--line); }
  .nav-links a { display: block; padding: .9rem 0; }
  .nav-inner { position: relative; flex-wrap: wrap; }
  .nav-cta { order: 4; width: 100%; justify-content: center; }
  .steps li { grid-template-columns: 3rem minmax(0, 1fr); gap: 1rem; }
}

@media (prefers-reduced-motion: reduce) {
  /* The apps' drawer, window, scrim and launcher stop moving too (renderer/apps.ts). */
  .ptab-panel, .pchat-win, .scrim, .pchat-bubble { transition: none; }
  .why-card:hover, .proof-columns li:hover, .band figure:hover .drift { transform: none; }
  .marquee-track { animation: none; flex-wrap: wrap; width: auto; }
  .hero--plate h1, .hero-flank::before { animation: none; }
  .marquee-list[aria-hidden] { display: none; }
  .marquee-list { flex-wrap: wrap; white-space: normal; row-gap: .5rem; }
  html { scroll-behavior: auto; }
  *, *::before, *::after { transition: none !important; animation: none !important; }
  [data-reveal] { opacity: 1; transform: none; }
  /* The blanket transition/animation kill above does not stop a transform set from
     JavaScript - script() must check the media query itself, which it does, but this
     rule is the defensive backstop in case a drift transform is ever written before
     that check runs. */
  .drift img { transform: none !important; top: -8%; }
}

[data-family="ledger"] { --radius: 0; --rhythm: .82; counter-reset: ledger-sec; }
/* The signature: a numbered eyebrow on the heading's own baseline. */
[data-family="ledger"] .section-head {
  max-width: none; counter-increment: ledger-sec;
  border-top: 1px solid var(--brand-text); padding-top: 1rem;
  margin-bottom: calc(2.4rem * var(--rhythm));
}
[data-family="ledger"] .section-head .eyebrow {
  display: inline-block; margin: 0 .8rem 0 0; font-size: .7rem; letter-spacing: .14em;
}
[data-family="ledger"] .section-head .eyebrow::before {
  content: counter(ledger-sec, decimal-leading-zero) "\00a0\2014\00a0";
  width: auto; height: auto; background: none; margin: 0; color: var(--brand-text);
}
[data-family="ledger"] .section-head h2 {
  display: inline; font-size: clamp(1.35rem, 2vw, 1.85rem); letter-spacing: .005em;
}
/* Loud exactly once a page, indexed by data-hpos (headed sections only). */
[data-family="ledger"] [data-hpos="0"] .section-head h2 {
  font-size: clamp(2.2rem, 4.6vw, 3.4rem); line-height: .96; letter-spacing: -.01em;
}
/* A plain field behind the hero: the contour lines read as a pattern on this family's quiet page. */
[data-family="ledger"] .hero-field { background-image: none; }
/* Services as numbered rows, descriptions right-aligned against the titles. */
[data-family="ledger"] .svc-list[data-count] { grid-template-columns: minmax(0, 1fr); gap: 0; }
[data-family="ledger"] .svc-list,
[data-family="ledger"] .svc-aside-list { counter-reset: ledger-svc; }
[data-family="ledger"] .svc {
  display: grid; grid-template-columns: 3.25rem minmax(0, 26ch) minmax(0, 1fr);
  gap: 0 clamp(1rem, 3vw, 2.5rem); align-items: baseline; padding: 1.15rem 0;
}
[data-family="ledger"] .svc-list .svc::before,
[data-family="ledger"] .svc-aside-list .svc::before {
  counter-increment: ledger-svc; content: counter(ledger-svc, decimal-leading-zero);
  font-family: var(--mono); font-size: .78rem; font-weight: 600; color: var(--brand-text);
}
[data-family="ledger"] .svc h3 { margin-bottom: 0; font-size: clamp(1.05rem, 1.7vw, 1.35rem); }
[data-family="ledger"] .svc p { text-align: right; max-width: none; font-size: .93rem; }
[data-family="ledger"] .svc .worklist, [data-family="ledger"] .svc .svc-covers { grid-column: 2 / -1; text-align: right; }
/* The planned chip and the read-more link take a row of their own under the text: the chip sat
   absolute over the description's right edge, and the link wrapped inside the number column. */
[data-family="ledger"] .svc .chip { order: 10; position: static; grid-column: 2 / -1; justify-self: end; margin-top: .5rem; }
[data-family="ledger"] .svc .svc-more { order: 11; grid-column: 2 / -1; justify-self: end; white-space: nowrap; margin-top: .6rem; }
/* The sticky-aside variant's column is too narrow for three tracks. */
[data-family="ledger"] .svc-aside-list {
  background: var(--paper); border-top: 2px solid var(--ink); padding: 0;
}
[data-family="ledger"] .svc-aside-list .svc { grid-template-columns: 2.5rem minmax(0, 1fr); }
[data-family="ledger"] .svc-aside-list .svc h3,
[data-family="ledger"] .svc-aside-list .svc p,
[data-family="ledger"] .svc-aside-list .svc .worklist, [data-family="ledger"] .svc-aside-list .svc .svc-covers { grid-column: 2; text-align: left; }
[data-family="ledger"] .svc-ledger li { grid-template-columns: 3.25rem minmax(0, 1fr); padding: 1.15rem 0; }
[data-family="ledger"] .svc-ledger-n { font-size: .78rem; }
[data-family="ledger"] .svc-ledger-body {
  display: grid; grid-template-columns: minmax(0, 26ch) minmax(0, 1fr);
  gap: 0 clamp(1rem, 3vw, 2.5rem); align-items: baseline;
}
[data-family="ledger"] .svc-ledger-body h3 { margin-bottom: 0; font-size: clamp(1.05rem, 1.7vw, 1.35rem); }
[data-family="ledger"] .svc-ledger-body p { text-align: right; max-width: none; font-size: .93rem; }
[data-family="ledger"] .svc-ledger-body .worklist, [data-family="ledger"] .svc-ledger-body .svc-covers { grid-column: 1 / -1; text-align: right; }
[data-family="ledger"] .svc-ledger-body .chip { position: static; grid-column: 1 / -1; justify-self: end; margin-top: .5rem; }
[data-family="ledger"] .svc-ledger-body .svc-more { grid-column: 1 / -1; justify-self: end; white-space: nowrap; margin-top: .6rem; }
/* Trust badges as one hairline-separated row, not a grid of blocks. */
[data-family="ledger"] .creds { padding: calc(1.6rem * var(--rhythm)) 0; }
[data-family="ledger"] .creds-row { display: flex; flex-wrap: wrap; gap: .5rem 0; align-items: baseline; }
[data-family="ledger"] .cred {
  display: flex; align-items: baseline; gap: .6rem;
  border-left: 1px solid var(--line); padding: 0 clamp(1rem, 2.4vw, 2rem);
}
[data-family="ledger"] .cred:first-child { border-left: 0; padding-left: 0; }
[data-family="ledger"] .cred-label { font-family: var(--body); font-weight: 600; font-size: .95rem; letter-spacing: 0; }
[data-family="ledger"] .cred-detail { font-size: .62rem; }
/* An uneven mosaic, captions burned in, full colour, no overscan. */
[data-family="ledger"] .band { gap: 1px; }
[data-family="ledger"] .band--work.band--2,
[data-family="ledger"] .band--work.band--3 { grid-auto-rows: clamp(7rem, 13vw, 13rem); }
[data-family="ledger"] .band--work.band--3 { grid-template-columns: 1.7fr 1fr; }
[data-family="ledger"] .band--work.band--3 figure:first-child { grid-row: span 2; }
[data-family="ledger"] .band--work.band--2 { grid-template-columns: 1.55fr 1fr; }
[data-family="ledger"] .band--work.band--2 .drift,
[data-family="ledger"] .band--work.band--3 .drift { aspect-ratio: auto; height: 100%; }
[data-family="ledger"] .band figcaption {
  display: block; position: absolute; left: 0; bottom: 0; max-width: 92%;
  font-family: var(--mono); font-size: .6rem; text-transform: uppercase; letter-spacing: .1em;
  color: var(--paper); background: color-mix(in srgb, var(--ink) 82%, transparent);
  padding: .38rem .7rem;
}
[data-family="ledger"] .band img,
[data-family="ledger"] .hero-photo img { filter: none; }
[data-family="ledger"] .drift img { height: 100%; top: 0; animation: none; }
/* Motion: the sticky header, and nothing else. Content is visible either way. */
[data-family="ledger"].js [data-reveal] { opacity: 1; transform: none; transition: none; }
[data-family="ledger"] .nav { background: var(--paper); backdrop-filter: none; border-bottom: 1px solid var(--ink); }
[data-family="ledger"] .nav-inner { padding: .7rem 0; }
[data-family="ledger"] .nav-cta .arw { color: var(--brand-text); }
[data-family="ledger"] .ledger-strip { border-top: 1px solid var(--line); }
/* A dense multi-column masthead of a footer. */
[data-family="ledger"] .footer { padding: 2.4rem 0 2rem; }
[data-family="ledger"] .footer--plain .footer-grid {
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
  gap: 1.4rem clamp(1.5rem, 3vw, 2.5rem);
}
[data-family="ledger"] .footer-brand { font-size: .95rem; letter-spacing: .04em; }
[data-family="ledger"] .footer p,
[data-family="ledger"] .footer a { font-family: var(--mono); font-size: .72rem; letter-spacing: .02em; }
[data-family="ledger"] .footer--plain .footer-note { columns: 2; column-gap: clamp(1.5rem, 3vw, 2.5rem); }
[data-family="ledger"] .footer--plain .footer-note p { break-inside: avoid; }
/* A right-aligned description column cannot survive a phone. */
@media (max-width: 46rem) {
  [data-family="ledger"] .svc { grid-template-columns: 2.5rem minmax(0, 1fr); }
  [data-family="ledger"] .svc h3,
  [data-family="ledger"] .svc p,
  [data-family="ledger"] .svc .worklist, [data-family="ledger"] .svc .svc-covers { grid-column: 2; text-align: left; }
  [data-family="ledger"] .svc-ledger-body { grid-template-columns: minmax(0, 1fr); }
  [data-family="ledger"] .svc-ledger-body p,
  [data-family="ledger"] .svc-ledger-body .worklist, [data-family="ledger"] .svc-ledger-body .svc-covers { text-align: left; }
  [data-family="ledger"] .band--work.band--2,
  [data-family="ledger"] .band--work.band--3 { grid-template-columns: minmax(0, 1fr); grid-auto-rows: auto; }
  [data-family="ledger"] .band--work.band--3 figure:first-child { grid-row: auto; }
  [data-family="ledger"] .band--work.band--2 .drift,
  [data-family="ledger"] .band--work.band--3 .drift { height: auto; aspect-ratio: 4 / 3; }
  [data-family="ledger"] .footer--plain .footer-note { columns: 1; }
}

[data-family="ledger"] .figures { background: var(--paper-2); color: var(--ink); border-top: 2px solid var(--ink); border-bottom: 1px solid var(--line); }
[data-family="ledger"] .fig-n { color: var(--brand-text); }
[data-family="ledger"] .fig-c { color: var(--muted); }
[data-family="ledger"] .figures-row li { border-left: 1px solid var(--line); padding-left: 1.2rem; }
[data-family="ledger"] .figures-row li:first-child { border-left: 0; padding-left: 0; }



/* --- Asennus-Santeri ------------------------------------------------------ */
.skip { position: absolute; left: -999px; top: 0; z-index: 100; background: var(--ink); color: var(--paper); padding: .7rem 1rem; }
.skip:focus { left: 1rem; top: 1rem; }
main { display: block; }
.nav-links a[aria-current="page"] { text-decoration: underline; text-underline-offset: .35em; }
.aud-ico::before {
  content: ""; display: block; width: 1.4rem; height: 1.4rem; background: currentColor;
  -webkit-mask: var(--ico) center / contain no-repeat; mask: var(--ico) center / contain no-repeat;
}
.aud-tile:nth-child(4n+1) .aud-ico { --ico: url(img/ico-home.svg); }
.aud-tile:nth-child(4n+2) .aud-ico { --ico: url(img/ico-building.svg); }
.aud-tile:nth-child(4n+3) .aud-ico { --ico: url(img/ico-cabin.svg); }
.aud-tile:nth-child(4n+4) .aud-ico { --ico: url(img/ico-shop.svg); }
.steps { counter-reset: step; }
.steps li { counter-increment: step; }
.step-n::before { content: counter(step, decimal-leading-zero); }
.cta-number { text-decoration: none; }
.contact h1 { font-family: var(--display); font-weight: 400; font-size: clamp(2.2rem, 4.6vw, 3.4rem); line-height: 1; margin: .4rem 0 1rem; }
.form-panel { background: var(--paper-2); padding: clamp(1.5rem, 3vw, 2.5rem); border-top: 2px solid var(--ink); }
.form-title { font-family: var(--display); font-weight: 400; font-size: clamp(1.35rem, 2vw, 1.85rem); margin: 0 0 .4rem; }
.form-lead { color: var(--muted); margin: 0 0 1.4rem; }
.form-slot { min-height: 12rem; }
.form-fallback p { margin: 0 0 1rem; }
.footer-note .credit a { text-decoration: underline; }
.footer-links a, .footer-socials a, .footer p a { display: inline-block; min-height: 24px; padding: .3rem 0; }
.nav-links a { display: inline-block; min-height: 24px; padding: .3rem 0; }
/* The photograph is a light, white-walled scene: the copy needs a deeper scrim than the draft's darker stock image did. */
.hero--type .hero-photo::after {
  background:
    linear-gradient(0deg, color-mix(in srgb, var(--field) 70%, transparent) 0%, transparent 70%),
    linear-gradient(90deg, color-mix(in srgb, var(--field) 92%, transparent) 0%, color-mix(in srgb, var(--field) 78%, transparent) 38%, color-mix(in srgb, var(--field) 30%, transparent) 72%, transparent 100%);
}
.brand img { display: block; height: 26px; width: auto; }
.partners-inline { margin-top: calc(2.4rem * var(--rhythm)); border-top: 1px solid var(--line); padding-top: 1.4rem; }
.partners-inline .partner-row { margin-top: .8rem; }
.partners-inline .partner img { filter: none; opacity: 1; }
/* The strip under the hero holds facts now, not a closing note: every cell gets the value type. */
.ledger-cell:last-child dd { font-family: var(--display); font-size: clamp(1rem, 1.5vw, 1.3rem); line-height: 1.15; color: var(--ink); }
/* Two links need no menu button: on a phone the header is one row of logo and call button,
   one row of both links, and the utility bar's email is already in the strip, footer and contact page. */
@media (max-width: 900px) {
  .utility { display: none; }
  .nav-toggle { display: none; }
  .nav-inner { grid-template-columns: auto 1fr; }
  .brand { grid-column: 1; }
  .nav-cta { grid-column: 2; justify-self: end; order: 0; width: auto; }
  .nav-links { position: static; display: block; grid-column: 1 / -1; background: none; border: 0; }
  .nav-links ul { flex-direction: row; gap: 1.4rem; padding: 0; }
  .nav-links li { border-top: 0; }
  .nav-links a { display: inline-block; padding: .3rem 0; }
}
@media (max-width: 900px) {
  .nav--left .nav-inner { grid-template-columns: auto 1fr; row-gap: .6rem; }
  .nav--left .brand { grid-column: 1; grid-row: 1; }
  .nav--left .nav-cta { grid-column: 2; grid-row: 1; justify-self: end; }
  .nav--left .nav-links { grid-column: 1 / -1; grid-row: 2; }
}
/* Three short statements read as a row, not as three full-width boxes with one line each. */
@media (min-width: 901px) { .why-cards[data-count="3"] { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 1.5rem; } }
/* Stacked on a phone, the facts are rows divided by rules, not columns with a left border. */
@media (max-width: 40rem) {
  .ledger-cell, .ledger-cell:first-child { border-left: 0; padding: .8rem 0; border-top: 1px solid var(--line); grid-template-columns: 9rem 1fr; align-items: baseline; }
  .ledger-cell:first-child { border-top: 0; }
}
/* Privacy page and the note under the form */
.policy-wrap { max-width: 46rem; }
.policy h1 { font-family: var(--display); font-weight: 400; font-size: clamp(2.2rem, 4.6vw, 3.4rem); line-height: 1; margin: .4rem 0 .6rem; }
.policy-date { font-family: var(--mono); font-size: .75rem; color: var(--muted); margin: 0 0 1.2rem; }
.policy-controller { margin: 1.6rem 0 2.4rem; }
.policy-sec { padding: 1.4rem 0 0; border-top: 1px solid var(--line); margin-top: 1.4rem; }
.policy-sec h2 { font-family: var(--display); font-weight: 400; font-size: clamp(1.35rem, 2vw, 1.75rem); margin: 0 0 .6rem; }
.policy-text p, .policy-text li { color: var(--ink); line-height: 1.6; }
.policy-text ul { padding-left: 1.2rem; }
.form-privacy { font-size: .85rem; color: var(--muted); margin: 1rem 0 0; }
.form-privacy a, .footer-note a { text-decoration: underline; }
