/* GearMotive — hand-written replacement for ~30 Kadence/plugin stylesheets.
 *
 * Structure mirrors the original's rendered anatomy (tools/measure.mjs), not
 * Kadence's class names. Anything that differs from the measured original is
 * marked FIX with the defect it repairs — see PROJECT.md's defect table.
 */

@import url("/assets/fonts/montserrat.css");
@import url("tokens.css");

/* ---- Design system tokens (E-SQ5 prototype, 2026-10-10) ----------------------
 * The values shared by the site-wide button, the focus ring, the header link
 * and the E-SQ5 product page. Values used in one place stay literal. */
:root {
  /* Buttons: one red for the whole site. #c60000 is 6.17:1 under white. */
  --btn-bg: #c60000;
  --btn-bg-hover: #a90000;
  --btn-radius: 2px;
  --btn-fs: 12px;
  --btn-pad: 13px 22px;
  --btn-minh: 46px;
  --btn-gap: 24px;

  /* Focus ring: see the global :focus-visible rule. */
  --focus-ring: #e69a00;
  --focus-ring-edge: #101113;

  /* Product page (pp-*). */
  --pp-ink: #202124;
  --pp-surface: #f3f3f3;
  --pp-surface-alt: #e4e4e4;
  --pp-card: #e5e5e5;
  --pp-frame: #ededed;
  --pp-dark: #101113;
  --pp-dark-line: #343638;
  --pp-table-line: #3b3d3f;
  --pp-ratio-line: #494b4d;
  --pp-red: #c60000;
  --pp-red-deep: #b50000;
  --pp-red-on-dark: #ff535b;
  --pp-icon-on-dark: #ff3845;
  --pp-use-on-dark: #ff6970;
  --pp-wrap: 1280px;
  --pp-gutter: 64px;
  --pp-gutter-sm: 36px;
  --pp-space-section: 48px;
  --pp-space-section-sm: 34px;
  --pp-radius: 2px;
  --pp-fs-body: 15px;
  --pp-lh-body: 1.55;
  --pp-fs-h1: clamp(30px, 3.3vw, 48px);
  --pp-fs-h2: clamp(26px, 2.8vw, 38px);
  --pp-fs-h2-sm: 22px;
  --pp-fs-h3: 18px;
  --pp-fs-small: 13px;
  --pp-fs-xs: 12px;
}

/* ---- Reset ------------------------------------------------------------- */

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

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--page);
  color: var(--ink);
  font-family: var(--font);
  font-size: var(--fs-body);
  font-weight: var(--fw-body);
  line-height: var(--lh-body);
  overflow-x: hidden;
}

img, svg, video, iframe { max-width: 100%; height: auto; display: block; }
img { border-radius: inherit; }

h1, h2, h3, h4 { margin: 0 0 var(--space-tight); font-weight: var(--fw-bold); }
h1 { font-size: var(--fs-h1); line-height: var(--lh-h1); }
h2 { font-size: var(--fs-h2); line-height: var(--lh-h2); }
h3 { font-size: var(--fs-h3); line-height: var(--lh-h3); }

p { margin: 0 0 var(--space-tight); }
p:last-child { margin-bottom: 0; }

a { color: var(--link-text); }
a:hover, a:focus-visible { text-decoration-thickness: 2px; }

/* Global focus ring, two tones, so it shows on every surface the site uses.
 * The amber band (3px, hugging the element) is for dark surfaces; the
 * near-black edge (2px, outside the band) is for light ones. Contrast (WCAG
 * relative luminance): the edge against #fff 18.89, #f3f3f3 17.03, #e4e4e4
 * 14.86; the amber against #101113 8.08, #191919 7.52, #2f2f2f 5.73. Amber
 * alone on light surfaces is 2.34 / 2.11 / 1.84, which was the old failure.
 * Every surface reaches at least 3:1. */
:focus-visible {
  outline: 2px solid var(--focus-ring-edge);
  outline-offset: 3px;
  box-shadow: 0 0 0 3px var(--focus-ring);
}

/* The original's rule under every heading: a short red line, 280×1px, as
 * measured (`kt-divider`, rgb(255,0,0)). Left-aligned in content, centred in
 * the title band. It was a faint full-width grey line until 2026-08-26. */
hr {
  border: 0;
  border-top: 1px solid #ff0000;
  width: 280px;
  max-width: 100%;
  margin: var(--space-tight) 0 var(--space-block);
}
.hero hr { margin-inline: auto; }

/* FIX: the original's skip link rendered at 2.91:1 and was positioned where it
 * could be seen. Standard visually-hidden-until-focus instead. */
.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  background: var(--footer);
  color: #fff;
  padding: 12px 20px;
  border-radius: 0 0 var(--r) 0;
}
.skip-link:focus { left: 0; }

/* ---- Layout ------------------------------------------------------------ */

.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.container--narrow { max-width: calc(var(--narrow) + var(--gutter) * 2); }

/* ONE band treatment for the whole site: the shared photograph under a grey
 * overlay, white text.
 *
 * The original had five — 31 bands on #555555@0.90, 14 on #ffffff@0.90, and 13
 * flat surface/mid/dark — which is why the blog and the archives read as a
 * different site. Collapsing them removed four modifier classes, the per-row
 * inline backgrounds, and the tone plumbing that produced them.
 *
 * The overlay was #555555 at 0.90 — the site's own Kadence palette6, which is
 * what 31 of the original's bands already used — and is now #000000 at 0.60.
 * ⚠ That is not a taste change, it is what the new photographs require: they
 * are near-black (median luminance 9-11 against the race car's 80), so 90% of
 * an #555555 overlay composited them to a flat 77-90 grey and NOTHING of the
 * picture reached the screen. Measured, not eyeballed.
 *
 * The worst-case guarantee is unchanged, and that is the point of picking these
 * two numbers: white body text is **5.74:1 even if the photo underneath were
 * pure white** — 0.4 x 255 = 102 — which is the only honest way to judge text
 * over an image. It is the same pair the hero band already carries.
 * ⚠ Keep any replacement pair at or under that composite. #555555 at 0.90 also
 * satisfies it (76.5 + 25.5 = 102); most looser pairs do not. */
/* THREE band photographs, one per page family. Chosen by the client 2026-08-25
 * after seeing all three live; the trial is over and this is the design.
 *
 * The "main background" is the photograph under every dark content band. It was
 * `car-racing-4394450_1920.jpg`, a stock race car, deliberately different from
 * the hero's gearbox so a page had variety without per-row backgrounds. It is
 * now three frames from the 2026-08 product shoot:
 *
 *   -106  the two front pages        /  and  /fi/
 *   -110  everything else            /faq/, /references/, the blog, archives
 *
 * Each page names its photograph in its <body data-band="…">, so nothing
 * depends on which rule comes last.
 */
:root {
  --band-photo: url("/assets/wp-content/uploads/2026/08/Gearmotive_tuotekuvat-110.jpg");
  /* ⚠ These two travel WITH the photographs. Going back to the race car means
   * going back to #555555 / 0.9 in the same edit, or the band turns into a
   * black slab — and the three frames here are near-black, so raising the
   * opacity back to 0.9 makes all three vanish into flat grey. */
  --band-overlay: #000000;
  --band-overlay-opacity: 0.6;
}
body[data-band="106"] { --band-photo: url("/assets/wp-content/uploads/2026/08/Gearmotive_tuotekuvat-106.jpg"); }
body[data-band="110"] { --band-photo: url("/assets/wp-content/uploads/2026/08/Gearmotive_tuotekuvat-110.jpg"); }

.section {
  position: relative;
  padding-block: var(--space-section);
  background-image: var(--band-photo);
  background-size: cover;
  background-position: center;
  background-attachment: fixed;
  color: #fff;
}

.section::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--band-overlay);
  opacity: var(--band-overlay-opacity);
}
.section > * { position: relative; }

.section h1, .section h2, .section h3, .section h4 { color: #fff; }
.section a { color: var(--link-text); }

/* The alternate band: a DIFFERENT photograph under a near-white overlay, dark
 * text. Alternating is what stops a long page reading as one slab — and it is
 * the pairing the original used on the front page and /references/.
 *
 * Worst case (photo underneath pure black → surface #e6e6e6): --ink 8.02:1,
 * --link 7.88:1. --muted was 4.38:1 and is now #676767, 4.53:1. */
.section--light {
  background-image: url("/assets/wp-content/uploads/2020/11/koppalogolla-1536x864.jpg");
  color: var(--ink);
}
/* The light band keeps its own pair: a near-white overlay at 0.9 over a
 * different, much brighter photograph. It is not on trial and does not follow
 * --band-overlay. */
.section--light::before { background: #ffffff; opacity: 0.9; }
.section--light h1,
.section--light h2,
.section--light h3,
.section--light h4 { color: var(--ink); }
.section--light a { color: var(--link-text-on-light); }

/* ⚠ A component that paints its own light surface must restate BOTH its ink and
 * its link colour, in every band. Inheriting the band's is how 49 gallery
 * captions once shipped as white-on-white, and how they came back as pale
 * --link-on-dark on a white plate the moment the dark band grew links.
 *
 * One list, deliberately: every light-surface component belongs here, so adding
 * a new one is a single edit rather than a new rule. Must stay AFTER both band
 * rules — same specificity (0,1,1), so source order decides. */
.card, .gallery figcaption, .post-card, .form-field,
.leaflet-container, .reseller-directory, .story-light {
  color: var(--ink);
}
/* Headings need saying separately: .section h2 sets #fff explicitly, so a
 * heading inside a card never inherits the card's colour. 19 "Features" h2s
 * shipped white-on-white on the product pages before this line existed. */
.card :is(h1, h2, h3, h4),
.post-card :is(h1, h2, h3, h4), .reseller-directory :is(h1, h2, h3, h4),
.story-light :is(h1, h2, h3, h4) {
  color: var(--ink);
}
/* .reseller-directory is the reseller list under the map (reseller-map.js,
 * tools/sync-reseller-map.mjs); its links are descendants, hence "a". */
.card a, .gallery figcaption a, .post-card a,
.form-field a, .leaflet-container a, .reseller-directory a, .story-light a {
  color: var(--link-text-on-light);
}

/* .testimonial paints a DARK surface, so it is NOT in that list. Its only link
 * is the name chip, which states #fff on the red chip further down. */
.testimonial { color: var(--on-dark); }

/* .btn paints the accent-red surface (the form submits, the CTAs), so it states
 * its own text colour — but NOT here. See "the last word on link colour", after
 * the hero rules at the bottom of this file. */

/* ⚠ iOS Safari cannot render a fixed background. */
@media (max-width: 1024px), (hover: none) {
  .section { background-attachment: scroll; }
}

/* A section whose background is a photograph. The scrim is the FIX for the
 * 128 undecidable-contrast occurrences: white text on an arbitrary image. */
.section--photo {
  position: relative;
  background-repeat: no-repeat;
  color: #fff;
  /* The design: content scrolls over a photograph held still behind it. Which
   * bands do this is measured per row, not assumed — see build.mjs. */
  background-attachment: var(--bg-attachment, scroll);
}

.hero { background-attachment: var(--bg-attachment, scroll); }

/* ⚠ iOS Safari cannot render a fixed background — it either janks badly or
 * scales the image to the document height. The original disables it with
 * exactly this query (and so must we, or phones get a broken hero). */
@media (max-width: 1024px), (hover: none) {
  .section--photo,
  .hero { background-attachment: scroll !important; }
}
/* The overlay is measured per row (colour + opacity) and passed in as custom
 * properties, because every band uses different values. --scrim-solid is only
 * the fallback for a band that had no overlay at all. */
.section--photo::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--overlay, var(--scrim-solid));
  opacity: var(--overlay-opacity, 1);
}
.section--photo > * { position: relative; }

/* ⚠ A component that paints its OWN light surface must state its own colour.
 *
 * `.section--photo` sets `color: #fff` for the text that sits directly on the
 * photograph — but an accordion panel or a card inside that band paints itself
 * white and the white text kept inheriting. That is 413 invisible words on the
 * product pages, and it looks fine in a screenshot of the band because the
 * band itself reads correctly. Anything with a light background belongs here.
 */
.card,
.gallery figure,
.gallery figcaption,
.logos figcaption,
.post-card,
.post-card-body,
.form,
.form-status {
  color: var(--ink);
}
.section--photo .card a,
.section--photo .post-card a { color: var(--link-text-on-light); }

/* A band whose overlay is LIGHT keeps the normal dark ink — white text on a
 * white overlay is the exact defect this replaces. */
.section--photo-light { color: var(--ink); }
.section--photo-light h1,
.section--photo-light h2,
.section--photo-light h3 { color: var(--ink); }
.section--photo-light a { color: var(--link-text-on-light); }

.hero--light, .hero--light h1 { color: var(--ink); }

.grid { display: grid; gap: var(--space-block); }
.grid--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.grid--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }

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

/* ---- Two-column text-and-picture rows ---------------------------------- */

/* Order is normalised to text-left / picture-right in build.mjs; this only
 * lays them out. `align-items: center` because the picture and the paragraph
 * are rarely the same height and the original centres them. */
.cols {
  display: grid;
  gap: var(--space-block);
  align-items: center;
}
/* Two of these stacked run together otherwise — the second block's heading
 * sits hard against the last paragraph of the first and reads as part of it. */
.cols + .cols,
.cols + .gallery,
.gallery + .cols { margin-top: var(--space-section); }

.cols--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.cols--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.cols--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.cols--6 { grid-template-columns: repeat(6, minmax(0, 1fr)); }
/* Data columns (the BMW guide's gear ratios and chassis lists) top-align;
 * centring is for a picture beside a paragraph. */
.cols--4, .cols--6, .cols--text { align-items: start; }
/* Gear ratios: label above value in every cell, as the original wrapped them. */
.cols--6 strong { display: block; }
/* Air around a column row and after a list — the original's row padding,
 * which the flattened version had lost. */
p + .cols, .cols + p, ul + p { margin-top: var(--space-block); }
.col > * + * { margin-top: var(--space-tight); }
.col .figure { margin: 0; }
.col img { width: 100%; }

/* Stacking early, and always text-then-picture: at this width a picture above
 * its own heading reads as belonging to the section before it. */
@media (max-width: 860px) {
  .cols--2, .cols--3 { grid-template-columns: 1fr; }
  .cols--4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .cols--6 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (max-width: 480px) {
  .cols--4 { grid-template-columns: 1fr; }
  .cols--6 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* ---- Header ------------------------------------------------------------ */

/* The original header is 90px, fully transparent, floating over the hero and
 * scrolling away with the page; when you scrolled it came back as a solid grey
 * bar. Two client changes to that (2026-08-25): it is 72px, and it now STAYS —
 * `position: fixed` — without ever turning grey. It stays transparent, over
 * whatever is behind it, at every scroll position.
 *
 * ⚠ Transparent-and-fixed is exactly the combination that breaks contrast, and
 * no gate can see it: every gate measures a page at rest, at scroll 0, where
 * the bar sits over the dark hero and reads 12:1. Scroll it 800px onto a
 * `.section--light` and the same white links are on #e6e6e6. That is what
 * `--header-scrim` is for — read its note in tokens.css before touching it.
 *
 * ⚠ Fixed, not sticky. `position: sticky` needs the header to be in flow, and
 * it is not: it is pulled out so the hero can start at y=0 and run underneath
 * it, which is the whole transparent-over-the-photograph design. */
.site-header {
  position: fixed;
  inset: 0 0 auto;
  height: var(--header-h);
  z-index: 20;
  color: #fff;
}
/* Solid header: used wherever nothing dark sits behind it — posts, archives,
 * the privacy pages. The original does exactly this; a transparent header over
 * a white section is white-on-white. */
/* Every page renders the same dark title band, so the header is transparent
 * everywhere — there is no solid variant any more. It existed for the three
 * articles, which used to have no band; giving them one made the whole
 * modifier dead code, and dead code in a header is exactly where per-page
 * drift creeps back in. */
.site-header::before {
  content: "";
  position: absolute;
  inset: 0;
  height: calc(var(--header-h) * 1.6);
  background: var(--header-scrim);
  pointer-events: none;
}
.site-header .container {
  position: relative;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-block);
}

.site-brand { display: flex; align-items: center; flex: 0 0 auto; }
.site-brand img { width: 176px; max-height: 52px; object-fit: contain; }

/* ⚠ The menu's own emphasis, and the ONLY action in the bar. A call-to-action
 * button lived here for a few hours on 2026-08-25 and was removed: on a desktop
 * it duplicated a menu that already offers Contact, and on a phone it took the
 * logo's corner. Emphasising the existing item says the same thing with one
 * element instead of two.
 *
 * ⚠ Marked by `highlight` in site.json, not by matching the label — the menu is
 * a CMS-managed region the client will edit, and a match on "Contact" would stop
 * working silently the day it is renamed.
 *
 * Contrast is unaffected: the text stays #fff on the header scrim, which is
 * guaranteed 4.74:1 at worst. The border is currentColor, so it cannot be a
 * colour-only signal that fails against a background nobody measured. */
.nav-highlight > a {
  border: 2px solid currentColor;
  border-radius: var(--btn-radius);
  padding-inline: 16px;
}
.nav-highlight > a:hover,
.nav-highlight > a:focus-visible {
  /* --btn-bg is the site button red, #c60000 (6.17:1 under white),
   * so white-on-it is safe. Do not "brighten" it to --accent #ff0000: that is
   * 4.00:1 and was the original's own defect. */
  background: var(--btn-bg);
  border-color: var(--btn-bg);
}

.nav { display: flex; }
.nav-lang-list { list-style: none; margin: 0; padding: 0; display: flex; }

.nav ul {
  display: flex;
  align-items: center;
  gap: 0;
  list-style: none;
  margin: 0;
  padding: 0;
}
.nav li { position: relative; }
.nav a {
  display: flex;
  align-items: center;
  height: 48px;
  padding-inline: 12px;
  color: #fff;
  text-decoration: none;
  white-space: nowrap;
}
.nav a:hover, .nav a:focus-visible { text-decoration: underline; }
.nav [aria-current="page"] { text-decoration: underline; text-underline-offset: 6px; }

/* Products submenu */
.nav .has-sub > button {
  display: flex;
  align-items: center;
  gap: 6px;
  height: 48px;
  padding-inline: 12px;
  background: none;
  border: 0;
  color: inherit;
  font: inherit;
  cursor: pointer;
}
.nav .has-sub > button::after {
  content: "";
  width: 7px; height: 7px;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(45deg) translateY(-2px);
}
.nav .sub {
  display: none;
  position: absolute;
  z-index: 30;              /* above the band below the header */
  top: 100%;
  left: 0;
  min-width: 260px;
  background: var(--footer);
  border-radius: 0 0 var(--r) var(--r);
  box-shadow: var(--shadow-card);
  padding: 6px 0;
}
.nav .has-sub[aria-expanded="true"] .sub,
.nav .has-sub:hover .sub,
.nav .has-sub:focus-within .sub { display: block; }
.nav .sub a { height: auto; padding: 10px 18px; }

.nav-lang a { gap: 8px; }
.nav-lang img { width: 18px; height: auto; display: inline-block; }

/* Mobile menu. The original's was a Kadence off-canvas drawer; this is the
 * same behaviour with markup the responsive gate recognises. */
.nav-toggle {
  display: none;
  align-items: center;
  justify-content: center;
  gap: 8px;
  background: none;
  border: 0;
  color: inherit;
  font: inherit;
  padding: 10px;
  /* ⚠ The tap target must stay ≥48×48. Without the minima it measured 44×22 —
   * below even WCAG 2.5.8's 24px floor — and the dead strips above and below
   * the icon meant most thumb taps on a phone missed: reported as "hamburger
   * not clickable" on iOS (DuckDuckGo, 2026-08-24). An automated tap at the
   * exact centre still worked, so the responsive gate stayed green. The button
   * paints nothing, so the larger box is invisible. */
  min-width: 48px;
  min-height: 48px;
  cursor: pointer;
}
.nav-toggle span {
  display: block;
  width: 24px; height: 2px;
  background: currentColor;
  box-shadow: 0 7px currentColor, 0 -7px currentColor;
}

@media (max-width: 1100px) {
  .nav-toggle { display: flex; }
  .nav {
    display: none;
    position: absolute;
    top: var(--header-h);
    left: 0;
    right: 0;
    background: var(--footer);
    padding: var(--space-tight) var(--gutter) var(--space-block);
    box-shadow: var(--shadow-card);
    /* ⚠ The header is `position: fixed` now, so the dropdown is fixed with it
     * and cannot be scrolled to. Ten menu items plus the language switcher
     * overflow a short phone in landscape, and the items past the fold were
     * simply unreachable. */
    max-height: calc(100dvh - var(--header-h));
    overflow-y: auto;
    overscroll-behavior: contain;
  }
  .nav[data-open="true"] { display: block; }
  .nav ul { flex-direction: column; align-items: stretch; }
  .nav a, .nav .has-sub > button { height: auto; padding: 14px 0; width: 100%; }
  .nav .sub { position: static; display: block; background: none; box-shadow: none; padding: 0 0 0 18px; }

  /* In the dropdown the items are full-width rows, so the pill would stretch
   * across the whole panel and stop reading as a pill. */
  .nav-highlight > a { display: inline-flex; width: auto; padding-block: 10px; }
}

/* ---- Hero -------------------------------------------------------------- */

/* ONE hero background for the whole site.
 *
 * Defined here rather than inline per page so the band, the header over it and
 * the Products dropdown behave identically everywhere — which is the point:
 * the dropdown always has the same dark photograph behind it, so its links are
 * always legible. Pages used to inherit whatever photo their first row happened
 * to carry, or none at all, and the header changed character page to page.
 */
.hero {
  background-image: url("/assets/wp-content/uploads/2022/11/Gearmotive_tuotekuvat-132_2-jpg.webp");
  background-size: cover;
  background-position: center;
  --overlay: #000000;
  --overlay-opacity: 0.6;
  position: relative;
  min-height: 639px;
  display: grid;
  place-items: center;
  padding: calc(var(--header-h) + var(--space-block)) var(--gutter) var(--space-block);
  /* ⚠ background-COLOR, not the `background` shorthand. The shorthand resets
   * background-image to none, and sitting below the longhands above it silently
   * wiped the shared hero photograph — the band rendered flat #111 and looked
   * like the image had simply not been set. */
  background-color: #111;
  background-repeat: no-repeat;
  color: #fff;
  text-align: center;
  border-bottom: 6px solid var(--accent);
}
.hero::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--overlay, var(--scrim-solid));
  opacity: var(--overlay-opacity, 1);
}
.hero > * { position: relative; max-width: var(--content); }
.hero img { margin-inline: auto; }

/* A page that is not the front page has a short hero. */
.hero--page { min-height: 340px; }

/* The call to action under the page title. `.hero` is `text-align: center` and
 * the button is inline-flex, so it centres itself. */
.hero-cta { margin-top: var(--space-tight); }

/* ---- The front page's floating CTA (phones only) ----------------------- */

/* ⚠ Hidden by default and shown only in burger mode — `.btn.cta-float` is
 * (0,2,0) so it out-weighs `.btn`'s own `display: inline-flex` 400 lines below,
 * which a bare `.cta-float` would lose to on source order. That exact mistake
 * put the old header CTA on every phone page.
 *
 * The front page has no title band to hang a CTA under (it has a logo, not a
 * heading), and putting one in the bar took the logo's corner and read as a
 * misplaced button. Fixed to the bottom of the viewport instead: always
 * reachable, never in the brand's way. Client, 2026-08-25. */
.btn.cta-float { display: none; }

@media (max-width: 1100px) {
  .btn.cta-float {
    display: inline-flex;
    position: fixed;
    left: var(--gutter);
    right: var(--gutter);
    /* ⚠ env() for the iPhone home indicator. Without it the button sits under
     * the system gesture bar, where roughly the bottom third of it cannot be
     * tapped — and it looks perfectly fine in every screenshot. */
    bottom: calc(12px + env(safe-area-inset-bottom, 0px));
    z-index: 30;          /* over the header's 20 */
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.45);
  }

  /* ⚠ The button floats over the page, so the last thing on it would otherwise
   * sit permanently underneath. The footer's bottom row is short, so this is
   * the button's height plus its offset plus a line of air. */
  body.is-front { padding-bottom: 92px; }

  /* ⚠ One CTA per screen — and this one is on screen at ALL times, so the
   * front page's pre-footer band is a guaranteed duplicate rather than a
   * possible one. It stays in the markup (it is the `cms:cta` managed region,
   * identical across all 50 pages) and is hidden here. */
  .is-front .cta-band { display: none; }
}

@media (max-width: 800px) {
  .hero { min-height: 420px; }
  .hero--page { min-height: 240px; }
}

/* ---- Cards, media ------------------------------------------------------ */

.card {
  background: var(--surface);
  border-radius: var(--r);
  box-shadow: var(--shadow-soft);
  padding: var(--space-block);
}
.figure { border-radius: var(--r); overflow: hidden; box-shadow: var(--shadow-card); }
.figure img { width: 100%; }

/* ---- Gallery ----------------------------------------------------------- */

.gallery { display: grid; gap: var(--space-tight); grid-template-columns: repeat(4, minmax(0, 1fr)); }
.gallery figure { margin: 0; border-radius: var(--r); overflow: hidden; background: var(--surface); box-shadow: var(--shadow-soft); }
.gallery img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; }

/* FIX: gallery captions were #fff on #fff — 1.00:1, 49 occurrences, completely
 * invisible on /references/ and /fi/referenssit/. */
.gallery figcaption {
  padding: 10px 12px;
  font-size: var(--fs-xs);
  color: var(--ink);
  background: var(--surface);
}

@media (max-width: 1000px) { .gallery { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 700px)  { .gallery { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* ---- Logo grid (resellers) --------------------------------------------- */

.logos { display: grid; gap: var(--space-block); grid-template-columns: repeat(4, minmax(0, 1fr)); }
.logos figure { margin: 0; text-align: center; }
.logos img {
  width: 100%;
  max-width: 200px;
  height: 120px;
  object-fit: contain;
  margin-inline: auto;
  background: var(--surface);
  border-radius: var(--r);
  padding: 12px;
}
.logos figcaption { margin-top: 10px; font-size: var(--fs-small); }

@media (max-width: 1000px) { .logos { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 640px)  { .logos { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* ---- Reseller map (client, 2026-09-01) ---------------------------------- */

/* Leaflet with a MapLibre GL base layer (OpenFreeMap vector, the self-hosted
 * style at /assets/map/style-en.json), on /resellers/ and /fi/jalleenmyyjat/
 * only — see reseller-map.js. The map reads as one of the site's light plates
 * sitting on the dark band, like the gallery cards under it.
 *
 * ⚠ z-index: 0 is load-bearing. Leaflet's panes and controls run their own
 * z-indexes up to 1000; without a stacking context here they compete with the
 * page's — and the fixed header is z-index 20, so the zoom buttons would ride
 * OVER the transparent bar the moment the map scrolls under it. The same shape
 * as the dead-z-index dropdown bug this file already records. */
.reseller-map {
  position: relative;
  z-index: 0;
  margin-bottom: var(--space-block);
  border-radius: var(--r);
  overflow: hidden;
  box-shadow: var(--shadow-soft);
}
.reseller-map-canvas {
  height: 480px;
  /* Positron's water fill (#c2c8ca, the style's "water" layer), not its land
   * background #f2f3f0 — at world zoom the canvas is wider than the world, and
   * the spill-over past the 180th meridian should read as sea, as the old OSM
   * sea colour did. Never transparent, or the band's photograph shows through. */
  background: #c2c8ca;
}
@media (max-width: 700px) { .reseller-map-canvas { height: 360px; } }

/* Shown in place of the map when WebGL or MapLibre is unavailable (see
 * reseller-map.js). It fills the canvas, so the message sits in the middle of
 * the plate. Colours are explicit: the canvas beneath has no map to take them
 * from. #424242 on #f2f3f0 is about 9:1. */
.map-fallback {
  margin: 0;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  text-align: center;
  background: #f2f3f0;
  color: var(--ink);
}

/* The brand-red teardrop pin. Drawn on ::after, NOT on the icon element:
 * Leaflet positions each marker with an inline transform on the element
 * itself, so a rotate() there is silently overwritten.
 *
 * ⚠ No `position` here. The element is already absolute via leaflet.css's
 * `.leaflet-marker-icon` — same (0,1,0) specificity, and styles.css loads
 * later, so a `position: relative` on .map-pin WINS and puts every marker
 * back into document flow: each pin lands at its flow offset plus Leaflet's
 * transform — right longitude, scrambled latitude. It shipped that way for
 * about ten minutes; the ::after anchors to the marker without it. */
.map-pin::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50% 50% 50% 0;
  transform: rotate(-45deg);
  background: #fc0000;
  border: 2px solid #fff;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
}

/* Leaflet paints a zoom button at its range limit #bbb — 1.75:1, and the world
 * view STARTS at min zoom, so "−" ships disabled. Same selector as leaflet.css,
 * later in the cascade. Still reads as dimmed next to the red live buttons, but
 * clears 3:1 on every surface Leaflet puts behind it. */
.leaflet-bar a.leaflet-disabled { color: #6f6f6f; }

.leaflet-popup-content-wrapper { border-radius: var(--r-sm); }
.map-popup-name { margin: 0; font-weight: var(--fw-bold); }
.map-popup-addr { margin: 4px 0 0; font-size: var(--fs-xs); }

/* The map starts inert under this button — a full-width interactive map is a
 * scroll trap on a phone and a wheel trap on a desktop; see reseller-map.js.
 * The label reuses .btn so its contrast never depends on the tiles behind the
 * scrim. Above Leaflet's controls (z-index 1000), safely inside .reseller-map's
 * own stacking context. */
.map-unlock {
  position: absolute;
  inset: 0;
  z-index: 1100;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  border: 0;
  padding: 0;
  font: inherit;
  background: rgba(0, 0, 0, 0.25);
  cursor: pointer;
}
.map-unlock .btn { pointer-events: none; }
.map-unlock:hover .btn { background: var(--btn-bg-hover); }

/* ---- Reseller directory (2026-10-11, #27) ---- */
/* The list under the map. It sits on the light .pp-section below the dark map
 * band (the pp layout), so it is flat: no plate, no padding, no radius, no
 * shadow. Its ink, heading and link colours come from the three shared
 * light-surface lists above, which name this class.
 * Columns: 1 below 360px, 2 from 360px, 3 from 1000px. */
.reseller-directory {
  margin-bottom: var(--space-block);
}
.reseller-directory h3 { margin: 0 0 6px; }
.reseller-directory-lead { margin: 0 0 18px; }
.reseller-directory-groups { columns: 1; column-gap: 28px; }
@media (min-width: 360px) { .reseller-directory-groups { columns: 2; } }
@media (min-width: 1000px) { .reseller-directory-groups { columns: 3; } }
/* A lone group (the Finnish page: one country) takes the full width at every
 * viewport; the column rules above are for many groups. :has() is already used
 * in this stylesheet (the pp-pricing rules). */
.reseller-directory-groups:has(> :only-child) { columns: 1; }
.reseller-directory-group { break-inside: avoid; margin: 0 0 18px; }
.reseller-directory-group h4 { margin: 0 0 6px; font-size: 1rem; font-weight: 700; }
.reseller-directory ul { list-style: none; margin: 0; padding: 0; }
.reseller-directory li { margin: 0 0 10px; overflow-wrap: anywhere; }
.reseller-directory-place { display: block; font-size: var(--pp-fs-small); }

/* ---- Product pages: list bullets (the original's gear glyph) ---------- */
.is-product .section ul:not(.nav-lang-list) { list-style: none; padding-left: 0; }
.is-product .section ul:not(.nav-lang-list) > li {
  position: relative;
  padding-left: 30px;
  margin-block: 10px;
}
.is-product .section ul:not(.nav-lang-list) > li::before {
  content: "";
  position: absolute;
  left: 0; top: 4px;
  width: 20px; height: 20px;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23ff0000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='3'/%3E%3Cpath d='M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z'/%3E%3C/svg%3E") center / contain no-repeat;
}

/* ---- Testimonials ------------------------------------------------------ */

/* Dark cards on a photo band, as measured. ⚠ The original is a CAROUSEL
 * showing 3 of 8; this is a grid showing all 8. Deliberate deviation — it
 * needs no JS, hides nothing from crawlers, and is keyboard-reachable.
 * Noted in PROJECT.md; say so rather than letting it read as fidelity. */
.testimonials { display: grid; gap: var(--space-block); grid-template-columns: repeat(3, minmax(0, 1fr)); }

.testimonial {
  background: var(--band-dark);
  color: var(--on-dark);
  border-radius: var(--r);
  box-shadow: var(--shadow-card);
  padding: var(--space-block);
  text-align: center;
  display: flex;
  flex-direction: column;
  gap: var(--space-tight);
}
.testimonial h3 { color: #fff; margin: 0; font-size: var(--fs-h3); line-height: var(--lh-h3); }

/* Gold on --band-dark is 8.3:1 — the original used icon images at the same
 * hue, so this keeps the look and gains a text alternative. */
.testimonial-stars { color: #ffc400; letter-spacing: 2px; margin: 0; }

.testimonial-body { flex: 1; }

.testimonial-meta {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-tight);
  text-align: left;
}
.testimonial-thumb {
  width: 96px; height: 72px;
  object-fit: cover;
  border-radius: var(--r-xs);
  background: var(--surface);
  flex: 0 0 auto;
}
/* FIX: name band was #fff on #ff0000 = 4.00:1. --accent-deep = 4.53:1. */
.testimonial-name {
  display: inline-block;
  background: var(--accent-deep);
  color: #fff;
  padding: 8px 14px;
  border-radius: var(--r-xs);
  font-weight: var(--fw-bold);
  font-size: var(--fs-small);
  margin: 0;
}
.testimonial-name a { color: #fff; }
/* FIX: occupation was #696969 on #2f2f2f = 2.44:1; --muted-on-dark is 4.53:1. */
.testimonial-role { color: var(--muted-on-dark); font-size: var(--fs-small); margin: 6px 0 0; }

@media (max-width: 1000px) { .testimonials { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 640px)  { .testimonials { grid-template-columns: 1fr; } }

/* ---- Video ------------------------------------------------------------- */

.video-grid { display: grid; gap: var(--space-block); grid-template-columns: repeat(3, minmax(0, 1fr)); }
.video {
  background: var(--footer);
  border-radius: var(--r);
  overflow: hidden;
}
.video-frame { position: relative; aspect-ratio: 16 / 9; }
.video-frame iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }

/* Click-to-play facade — see build.mjs. Nothing loads from Google until this
 * button is pressed. */
.video-facade {
  display: block;
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  padding: 0;
  border: 0;
  background: #000;
  cursor: pointer;
}
.video-facade img { width: 100%; height: 100%; object-fit: cover; }
.video-play {
  position: absolute;
  inset: 50% auto auto 50%;
  transform: translate(-50%, -50%);
  width: 68px; height: 48px;
  border-radius: 12px;
  background: var(--accent-deep);
}
.video-play::after {
  content: "";
  position: absolute;
  inset: 50% auto auto 50%;
  transform: translate(-40%, -50%);
  border-style: solid;
  border-width: 11px 0 11px 19px;
  border-color: transparent transparent transparent #fff;
}
.video-facade:hover .video-play { background: #c40000; }
.video-title {
  padding: 12px var(--space-tight);
  color: #fff;
  font-size: 13.6px;
}

@media (max-width: 1000px) { .video-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 640px)  { .video-grid { grid-template-columns: 1fr; } }

/* ---- Article / blog ---------------------------------------------------- */

.article { max-width: 800px; margin-inline: auto; }
.article img { border-radius: var(--r); }
.article ul, .article ol { padding-left: 1.4em; }
.article li + li { margin-top: 8px; }

.entry-meta { font-size: var(--fs-small); color: var(--muted); margin-bottom: var(--space-block); }

.post-list { display: grid; gap: var(--space-block); grid-template-columns: repeat(3, minmax(0, 1fr)); }
.post-card { background: var(--surface); border-radius: var(--r); box-shadow: var(--shadow-soft); overflow: hidden; }
.post-card img { width: 100%; aspect-ratio: 16 / 9; object-fit: cover; }
.post-card-body { padding: var(--space-block); }
.post-card h2 { font-size: var(--fs-h2-card); line-height: var(--lh-h2-card); }

@media (max-width: 1000px) { .post-list { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 640px)  { .post-list { grid-template-columns: 1fr; } }

/* The hashtag-style tag links under each article. These are deliberate SEO
 * structure — see CLAUDE.md. Do not remove because they look decorative. */
.tag-list { display: flex; flex-wrap: wrap; gap: 8px; margin-top: var(--space-block); }
.tag-list a {
  font-size: var(--fs-tag);
  font-weight: var(--fw-bold);
  padding: 6px 14px;
  border-radius: var(--r-pill);
  background: var(--page);
  border: 1px solid var(--rule);
  text-decoration: none;
}

/* ---- Footer ------------------------------------------------------------ */

.site-footer {
  background: var(--footer);
  color: var(--on-dark);
  padding-block: var(--space-block);
  /* Client, 2026-09-01: smaller footer type. One step down from the 17px body
   * (still ≥ 4.5:1 for every colour in here — the AA threshold is the same). */
  font-size: var(--fs-small);
}
.site-footer a { color: var(--link-on-dark); }
/* Column 1 carries the logo, the address AND the contact details, so it gets
 * the extra width. Columns 2 and 3 are the two funding logos. */
.footer-grid { display: grid; gap: var(--space-block); grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr) minmax(0, 1fr); align-items: start; }

/* One class for BOTH funding logos — they link to the same page and must not
 * drift apart. inline-block so the link's hit area is the image, not the
 * full-width line box a bare inline <a> would give it. */
.footer-logo { display: inline-block; margin-top: var(--space-tight); }
.footer-logo img { display: block; }
.footer-cols { display: grid; gap: var(--space-block); grid-template-columns: repeat(3, minmax(0, 1fr)); }
.footer-title { font-size: var(--fs-eyebrow); line-height: var(--lh-eyebrow); text-transform: uppercase; color: var(--on-dark); }
.site-footer img { max-width: 200px; }

.footer-bottom {
  border-top: 1px solid rgba(255, 255, 255, 0.12);
  margin-top: var(--space-block);
  padding-top: var(--space-block);
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-tight);
  justify-content: space-between;
  align-items: center;
  font-size: var(--fs-xs);
}
/* FIX: "Created with ♥ by" was #696969 on #191919 = 3.20:1. */
.footer-credit { color: var(--muted-on-dark); }

@media (max-width: 1000px) {
  .footer-grid { grid-template-columns: 1fr 1fr; }
  .footer-cols { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 640px) {
  .footer-grid, .footer-cols { grid-template-columns: 1fr; }
}

/* ---- Forms ------------------------------------------------------------- */

.form { display: grid; gap: var(--space-tight); }
.form fieldset { border: 1px solid var(--rule); border-radius: var(--r); padding: var(--space-block); margin: 0; }
.form legend { font-weight: var(--fw-bold); padding-inline: 8px; }
.form-row { display: grid; gap: var(--space-tight); grid-template-columns: repeat(2, minmax(0, 1fr)); }
.form-field { display: grid; gap: 6px; }
.form-field label { font-size: var(--fs-small); font-weight: var(--fw-bold); }
.form-field .req { color: var(--accent-deep); }

.form input[type="text"], .form input[type="email"], .form input[type="tel"],
.form select, .form textarea {
  font: inherit;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid #c9c9c9;
  border-radius: var(--r-sm);
  padding: 12px 14px;
  width: 100%;
}
.form textarea { min-height: 140px; resize: vertical; }
.form input:focus-visible, .form select:focus-visible, .form textarea:focus-visible {
  border-color: var(--accent-deep);
}

.form-options { display: grid; gap: 10px; }
.form-check { display: flex; gap: 10px; align-items: flex-start; }
.form-check input { margin-top: 4px; flex: 0 0 auto; }

/* The honeypot. Must be reachable by nothing a human uses, and invisible
 * without display:none (some bots skip display:none fields). */
.form-hp {
  position: absolute;
  left: -9999px;
  width: 1px; height: 1px;
  overflow: hidden;
}

/* The site-wide button, from the E-SQ5 design (2026-10-10). Uppercase, one red
 * (#c60000, 6.17:1 under white; hover #a90000, 7.82:1). `a.btn` gets an arrow
 * from ::after; a <button> or span.btn does not. The variants are (0,2,0), so
 * they beat `a.btn { color: #fff }` (0,1,1) and the band link rules whatever
 * their source order. The form submits, the CTAs and the product pages all use
 * this one control. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--btn-gap);
  min-height: var(--btn-minh);
  padding: var(--btn-pad);
  border: 1px solid var(--btn-bg);
  border-radius: var(--btn-radius);
  background: var(--btn-bg);
  color: #fff;
  font: inherit;
  font-size: var(--btn-fs);
  font-weight: var(--fw-bold);
  text-transform: uppercase;
  text-decoration: none;
  cursor: pointer;
}
.btn:hover { background: var(--btn-bg-hover); border-color: var(--btn-bg-hover); }
.btn[disabled] { opacity: 0.6; cursor: progress; }

/* The arrow is drawn here, not in the markup. The empty alt keeps it out of the
 * accessible name. */
a.btn::after { content: "\2192" / ""; }

/* Dark surfaces. */
.btn.btn--ghost { background: transparent; border-color: #fff; color: #fff; }
.btn.btn--ghost:hover { background: #fff; border-color: #fff; color: var(--pp-dark); }

/* Light surfaces: #b50000 is 5.57:1 on #e4e4e4. */
.btn.btn--outline { background: transparent; border-color: var(--pp-red-deep); color: var(--pp-red-deep); }
.btn.btn--outline:hover { background: var(--pp-red-deep); border-color: var(--pp-red-deep); color: #fff; }

/* ---- Article CTA band --------------------------------------------------- */

/* Quote-form links on the three articles — client-requested 2026-08-24, the
 * CTA work BRAND.md kept out of the migration. Generated into the `cms:cta`
 * region by build.mjs. The band is a `.section--light`, so ink and link
 * colours come from the band rules; `.btn` paints its own surface. */
.cta-band { text-align: center; }
.cta-lead {
  font-size: var(--fs-h3);
  line-height: var(--lh-h3);
  font-weight: var(--fw-bold);
  margin-bottom: var(--space-tight);
}
.cta-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-tight);
}

/* The contact pages' hero CTA scrolls to the on-page form; without this the
 * fixed header sits on the section heading when the anchor lands. */
#contact-form { scroll-margin-top: calc(var(--header-h) + 12px); }

.form-status { padding: var(--space-tight); border-radius: var(--r-sm); font-weight: var(--fw-bold); }
.form-status[data-type="success"] { background: #e6f4ea; color: #1b5e20; }
.form-status[data-type="error"]   { background: #fdecea; color: #8a1c12; }

@media (max-width: 640px) {
  .form-row { grid-template-columns: 1fr; }
}

/* ---- Long Finnish compounds -------------------------------------------- */

/* "Sekventiaalivaihteisto", "jälleenmyyjät", "tietosuojaseloste" overflow a
 * 320px viewport. Scoped to the breakpoint that actually fails, so wider
 * layouts are provably untouched. */
/* At 320px Finnish compounds ("sekventiaalivaihteisto", "kytkinkoppamuutokset",
 * "suorakytkentäratassarja") push past their container — the responsive gate
 * measured 5–66px of clipped text inside accordion panels and reseller
 * captions. Scoped to the breakpoint that actually fails, so wider layouts are
 * provably untouched. Body copy needs this as much as headings do. */
@media (max-width: 480px) {
  h1, h2, h3, h4,
  p, li, figcaption, dd, dt, td, th,
  .testimonial-name, .footer-title, .logos figcaption, .form-field label {
    hyphens: auto;
    overflow-wrap: anywhere;
    word-break: normal;
  }
  .logos figcaption { overflow-wrap: anywhere; }
}

/* ---- Motion ------------------------------------------------------------ */

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

/* ---- Post navigation --------------------------------------------------- */

.post-nav {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-block);
  justify-content: space-between;
  max-width: 800px;
  margin: var(--space-section) auto 0;
  padding-top: var(--space-block);
  border-top: 1px solid var(--rule);
}
.post-nav-item { display: grid; gap: 4px; max-width: 340px; }
.post-nav-item span { font-size: var(--fs-xs); color: var(--muted); text-transform: uppercase; }
.post-nav-item:last-child:not(:only-child) { text-align: right; }

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

/* ---- Post header ------------------------------------------------------- */

.entry-eyebrow {
  font-size: var(--fs-tag);
  font-weight: var(--fw-bold);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin-bottom: 8px;
}
.article > h1 { margin-bottom: 12px; }

/* A section heading the source content marked as <h1>. Demoted to <h2> for a
 * correct document outline; kept at h1 size so the page looks unchanged. */
.h1-size { font-size: var(--fs-h1); line-height: var(--lh-h1); }

/* ---- Testimonial carousel ---------------------------------------------- */

/* Front pages only. A scroll-snap track rather than a JS-positioned slider:
 * without JS it degrades to a scrollable row with every testimonial reachable,
 * and it needs no width maths that could disagree with the CSS. */
.testimonials--carousel {
  display: flex;
  gap: var(--space-block);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  scrollbar-width: thin;
  padding-bottom: var(--space-tight);
}
.testimonials--carousel > .testimonial {
  flex: 0 0 calc((100% - var(--space-block) * 2) / 3);
  scroll-snap-align: start;
}

.carousel-controls {
  display: flex;
  gap: var(--space-tight);
  justify-content: center;
  margin-top: var(--space-tight);
}
.carousel-btn {
  width: 44px;
  height: 44px;
  border: 0;
  border-radius: 50%;
  background: var(--accent-deep);
  color: #fff;
  font-size: 24px;
  line-height: 1;
  cursor: pointer;
}
.carousel-btn:hover { background: #c40000; }
.carousel-btn[disabled] { opacity: 0.4; cursor: default; }

@media (max-width: 1000px) {
  .testimonials--carousel > .testimonial { flex-basis: calc((100% - var(--space-block)) / 2); }
}
@media (max-width: 640px) {
  .testimonials--carousel > .testimonial { flex-basis: 100%; }
}

@media (prefers-reduced-motion: reduce) {
  .testimonials--carousel { scroll-behavior: auto; }
}

/* ---- Article share buttons --------------------------------------------- */

/* Above and below the article, as the original had them. Real links, not
 * script-driven popups, so they work with JS off and can be opened in a new
 * tab deliberately. */
.share { display: flex; gap: 10px; margin: var(--space-block) 0; }
.share-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  color: #fff;
  text-decoration: none;
}
.share-btn svg { width: 17px; height: 17px; fill: currentColor; }
.share-btn--fb { background: #1877f2; }   /* 3.6:1 vs white — icon, not text */
.share-btn--x  { background: #000000; }
.share-btn:hover { opacity: 0.85; }

/* A one-sentence lead under a page title, inside the band. */
.hero-lead {
  max-width: 62ch;
  margin: var(--space-tight) auto 0;
  font-size: var(--fs-body);
}

/* ---- Product-page design system (pp-*), E-SQ5 prototype, 2026-10-10 ----------
 * Replaces the inline <style> that lived in src/ng-seq-5/index.html.
 * Every rule is scoped under .pp, the page's root wrapper, which replaces the
 * old #main root prefix. Values are the tokens in :root above. The
 * section sits after the base element rules and before the link-colour rule at
 * the end of the file. The responsive rules are grouped at the end, in the
 * original order (1050, 760, 420). */

/* Root and type */
.pp { color: var(--pp-ink); background: var(--pp-surface); font-size: var(--pp-fs-body); line-height: var(--pp-lh-body); }
.pp h1, .pp h2, .pp h3 { color: inherit; font-weight: 700; letter-spacing: -.035em; margin: 0 0 20px; }
.pp h1 { font-size: var(--pp-fs-h1); line-height: 1.04; text-transform: uppercase; }
.pp h2 { font-size: var(--pp-fs-h2); line-height: 1.08; text-transform: uppercase; }
.pp h3 { font-size: var(--pp-fs-h3); line-height: 1.25; letter-spacing: -.02em; }
.pp p { margin: 0 0 16px; }
.pp .pp-wrap { width: min(var(--pp-wrap), calc(100% - var(--pp-gutter))); margin-inline: auto; }
.pp .pp-eyebrow { font-size: var(--pp-fs-small); font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--pp-red); margin-bottom: 10px; }
.pp .pp-lead { font-size: 22px; font-weight: 700; line-height: 1.3; }
.pp .pp-price-lead { font-size: 19px; margin-top: 24px; }
.pp .pp-line { display: block; }

/* Dark band and hero */
.pp .pp-dark { color: #fff; background: var(--pp-dark); }
.pp .pp-dark .pp-eyebrow { color: var(--pp-red-on-dark); }
/* Flat, as at HEAD: the client's radial gradient never rendered there (a higher-specificity flat rule won), and the contrast gate cannot resolve a gradient. */
.pp .pp-hero { padding: 140px 0 48px; background: var(--pp-dark); }
.pp .pp-hero-grid { display: grid; grid-template-columns: 1.05fr 1fr; align-items: center; gap: 36px; }
.pp .pp-hero-image { position: relative; aspect-ratio: 1.5; overflow: hidden; border-radius: var(--pp-radius); background: #252525; -webkit-mask-image: linear-gradient(to right,transparent,#000 9%,#000 91%,transparent),linear-gradient(to bottom,transparent,#000 10%,#000 90%,transparent); -webkit-mask-composite: source-in; mask-image: linear-gradient(to right,transparent,#000 9%,#000 91%,transparent),linear-gradient(to bottom,transparent,#000 10%,#000 90%,transparent); mask-composite: intersect; }
.pp .pp-hero-photo { position: absolute; display: block; width: 135%; max-width: none; height: auto; left: -17.5%; top: -20%; }

/* Benefit tiles. The label is a <p>, styled as the old <h3> was. The description
 * rule excludes it, so the label keeps its own size and margin. */
.pp .pp-benefits { border-top: 1px solid var(--pp-dark-line); display: grid; grid-template-columns: repeat(4, 1fr); }
.pp .pp-benefit { display: grid; grid-template-columns: 38px 1fr; gap: 16px; padding: 30px 20px; border-right: 1px solid var(--pp-dark-line); }
.pp .pp-benefit:last-child { border-right: 0; }
.pp .pp-benefit svg { width: 36px; height: 36px; color: var(--pp-icon-on-dark); }
.pp .pp-benefit .pp-benefit-label { font-size: 13px; line-height: 1.25; font-weight: 700; text-transform: uppercase; margin: 0 0 8px; letter-spacing: 0; }
.pp .pp-benefit p:not(.pp-benefit-label) { font-size: 12px; line-height: 1.45; margin: 0; }

/* Sections, intro and fitment rows, applications photo strip */
.pp .pp-section { padding: var(--pp-space-section) 0; }
.pp .pp-intro, .pp .pp-fitment { display: grid; grid-template-columns: .9fr 1.1fr; gap: 44px; align-items: center; }
.pp .pp-applications { display: grid; grid-template-columns: repeat(4, 1fr); gap: 2px; padding-right: 7%; }
.pp .pp-sport-frame { height: clamp(170px, 20vw, 260px); overflow: hidden; }
.pp .pp-sport-crop { display: block; width: 100%; height: 100%; }
.pp .pp-sport-crop { object-fit: cover; object-position: 50% 50%; max-width: none; }
.pp .pp-crop { display: block; width: 100%; height: 100%; max-width: none; object-fit: contain; }
.pp .pp-application { position: relative; margin: 0; min-width: 0; width: 132%; background: var(--pp-ink); clip-path: polygon(24.4% 0, 100% 0, 75.6% 100%, 0 100%); }
.pp .pp-application figcaption { position: absolute; bottom: 0; left: 0; width: 100%; height: 36px; display: flex; justify-content: center; align-items: center; padding-right: 20%; background: var(--pp-ink); color: #fff; font-size: var(--pp-fs-small); font-weight: 700; text-transform: uppercase; }
.pp .pp-fitment-band { background: var(--pp-surface-alt); }
.pp .pp-checks { list-style: none; padding: 0; margin: 20px 0; font-size: var(--pp-fs-small); }
.pp .pp-checks li { position: relative; padding-left: 25px; margin: 4px 0; }
.pp .pp-checks li::before { content: '\2713'; position: absolute; left: 0; color: var(--pp-red-deep); font-weight: 700; }

/* Exploded figure (the assembly drawing and its three captions) */
.pp .pp-exploded-scaled { display: block; width: 100%; height: auto; }
.pp .pp-exploded-fit { mix-blend-mode: multiply; filter: brightness(1.07); }
.pp .pp-exploded-assembly { margin: 0; min-width: 0; }
.pp .pp-exploded-assembly img { display: block; width: 100%; height: auto; border-radius: 0; }
/* Captions are 12px at every width. They may wrap. */
.pp .pp-exploded-assembly figcaption { display: grid; grid-template-columns: 55fr 25fr 20fr; gap: 8px; margin-top: 14px; text-align: center; font-size: var(--pp-fs-xs); line-height: 1.2; font-weight: 700; text-transform: uppercase; }

/* Spec table and ratio tables */
.pp .pp-specs { display: grid; grid-template-columns: 1.4fr 1fr 1fr; gap: 28px; align-items: start; }
.pp .pp-specs h2 { font-size: 21px; letter-spacing: -.02em; }
.pp table { width: 100%; border-collapse: collapse; font-size: var(--pp-fs-xs); color: inherit; }
.pp th, .pp td { padding: 6px 0; border: 0; border-bottom: 1px solid var(--pp-table-line); background: transparent; text-align: left; vertical-align: top; }
.pp th { font-weight: 500; padding-right: 14px; }
.pp .pp-ratio { border: 1px solid var(--pp-ratio-line); padding: 20px; }
.pp .pp-ratio h3 { margin-bottom: 6px; font-size: 14px; text-transform: uppercase; }
.pp .pp-ratio .pp-use { color: var(--pp-use-on-dark); font-size: var(--pp-fs-xs); margin-bottom: 12px; }
.pp .pp-ratio p:last-child { margin: 12px 0 0; font-size: var(--pp-fs-xs); }

/* Price cards and their photo frames */
.pp .pp-pricing { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 6px; }
.pp .pp-price-card { background: var(--pp-card); padding: 12px; min-width: 0; }
.pp .pp-price-card h3 { font-size: 12px; margin: 14px 0 8px; letter-spacing: 0; }
.pp .pp-price-card .pp-cost { color: var(--pp-red-deep); font-size: 16px; font-weight: 700; margin: 0; }
.pp .pp-shifter-card-frame { height: 140px; overflow: hidden; background: var(--pp-frame); }
.pp .pp-shifter-card { display: block; width: 100%; height: 100%; object-fit: contain; transform: translateY(10px) scale(1.12); }
.pp .pp-shifter-detail-frame { width: 100%; max-width: 320px; height: 400px; overflow: hidden; margin: 8px auto 22px; }
.pp .pp-shifter-detail { display: block; width: 100%; height: 100%; object-fit: contain; transform: scale(1.15); }
.pp .pp-kit-card-frame { height: 140px; overflow: hidden; background: var(--pp-frame); }
.pp .pp-kit-card-frame .pp-kit-image { object-fit: contain; transform: none; }
.pp .pp-kit-image { mix-blend-mode: multiply; filter: brightness(1.055); display: block; width: 100%; height: 100%; object-fit: contain; transform: none; }
.pp .pp-kit-detail-frame { width: 100%; max-width: 420px; aspect-ratio: 1190/510; overflow: hidden; margin: 22px auto 0; }
.pp .pp-adapter-card-frame { height: 140px; overflow: hidden; }
.pp .pp-adapter-image { display: block; width: 100%; height: 100%; object-fit: cover; object-position: center; transform: translate(4%, -10%) scale(1.35); transform-origin: 50% 64%; }
.pp .pp-adapter-card-frame .pp-adapter-image { object-fit: contain; transform: translate(3%, -4%) scale(1.45); }
.pp .pp-adapter-detail-frame { width: 100%; max-width: 420px; aspect-ratio: 4/3; overflow: hidden; margin: 22px auto 0; }
.pp .pp-shaft-card-frame { height: 140px; overflow: hidden; }
.pp .pp-shaft-image { display: block; width: 100%; height: 100%; object-fit: contain; transform: translateY(-7%) scale(1.65); transform-origin: 50% 60%; }
.pp .pp-shifter-layout .pp-shaft-detail-frame { margin: 0; }
.pp .pp-shaft-detail-frame { width: 100%; max-width: 280px; aspect-ratio: 2/3; overflow: hidden; margin: 22px auto 0; }
.pp .pp-gearbox-card-frame { height: 140px; overflow: hidden; display: flex; align-items: center; justify-content: center; }
/* The gearbox, shaft and adapter frames share one tile colour. */
.pp .pp-gearbox-card-frame, .pp .pp-shaft-card-frame, .pp .pp-adapter-card-frame { background: var(--pp-frame); isolation: isolate; }
.pp .pp-gearbox-card-frame svg, .pp .pp-shaft-card-frame .pp-shaft-image, .pp .pp-adapter-card-frame .pp-adapter-image { mix-blend-mode: multiply; filter: brightness(1.055); }
.pp .pp-gearbox-card-frame svg, .pp .pp-bellhousing-card-frame svg, .pp .pp-fitment-card-frame svg, .pp .pp-fitment-detail-frame svg, .pp .pp-housing-detail-frame svg, .pp .pp-bellhousing-detail-frame svg { display: block; width: 100%; height: 100%; mix-blend-mode: multiply; filter: brightness(1.055); }
.pp .pp-gearbox-card-frame .pp-crop, .pp .pp-bellhousing-card-frame .pp-crop, .pp .pp-fitment-card-frame .pp-crop, .pp .pp-fitment-detail-frame .pp-crop, .pp .pp-housing-detail-frame .pp-crop, .pp .pp-bellhousing-detail-frame .pp-crop { mix-blend-mode: multiply; filter: brightness(1.055); }
.pp .pp-bellhousing-card-frame { height: 140px; overflow: hidden; background: var(--pp-frame); isolation: isolate; }
.pp .pp-fitment-card-frame { height: 140px; overflow: hidden; background: var(--pp-frame); isolation: isolate; }
.pp .pp-fitment-detail-frame { width: 100%; max-width: 280px; aspect-ratio: 410/920; overflow: hidden; background: var(--pp-frame); isolation: isolate; }
.pp .pp-housing-detail-frame { width: 100%; aspect-ratio: 1140/740; overflow: hidden; background: var(--pp-frame); isolation: isolate; }
.pp .pp-bellhousing-detail-frame { width: 100%; max-width: 280px; aspect-ratio: 410/720; overflow: hidden; background: var(--pp-frame); isolation: isolate; }
.pp .pp-fitment-detail-frame, .pp .pp-bellhousing-detail-frame { max-width: 100px; justify-self: center; align-self: start; }
.pp .pp-housing-detail-frame { max-width: 280px; margin: 20px auto 0; }
.pp .pp-bellhousing-detail-frame { max-width: 115px; }

/* Component title and the accordion of components */
.pp h2.pp-component-title { margin: var(--pp-space-section) 0 20px; }
.pp .pp-components { display: grid; grid-template-columns: 1fr 1fr; gap: 0 24px; align-items: stretch; }
.pp .pp-components details { margin: 0 0 6px; border: 0; background: var(--pp-card); color: var(--pp-ink); }
.pp .pp-components summary { display: flex; align-items: center; justify-content: space-between; gap: 14px; padding: 17px 20px; cursor: pointer; list-style: none; font-size: var(--pp-fs-small); font-weight: 700; color: var(--pp-ink); background: var(--pp-card); }
.pp .pp-components summary::-webkit-details-marker { display: none; }
.pp .pp-components details summary::after { content: '+'; position: static; transform: none; font-size: 24px; font-weight: 500; width: auto; height: auto; border: 0; background: none; color: var(--pp-ink); }
.pp .pp-components details[open] summary::after { content: '\2212'; }
.pp .pp-components .accordion-body { padding: 10px 20px 22px; color: var(--pp-ink); font-size: var(--pp-fs-small); background: var(--pp-card); }
.pp .pp-components ul { padding-left: 20px; }
/* Each component photo blends into the accordion surface, as in the display kit. */
.pp .pp-components .pp-housing-detail-frame, .pp .pp-components .pp-fitment-detail-frame, .pp .pp-components .pp-bellhousing-detail-frame, .pp .pp-components .pp-shifter-detail-frame, .pp .pp-components .pp-shaft-detail-frame, .pp .pp-components .pp-kit-detail-frame, .pp .pp-components .pp-adapter-detail-frame { background: transparent; isolation: auto; }
.pp .pp-components .accordion-body img, .pp .pp-components .accordion-body svg { mix-blend-mode: multiply; filter: brightness(1.055); }
.pp .pp-components .accordion-body .pp-shifter-detail { mix-blend-mode: multiply; filter: brightness(1.14); mask-image: linear-gradient(to right, transparent, #000 12%, #000 88%, transparent); -webkit-mask-image: linear-gradient(to right, transparent, #000 12%, #000 88%, transparent); }
/* Component photography stays proportional to the copy beside it. */
.pp .pp-components .pp-shifter-detail { object-fit: cover; object-position: center; transform: scale(1.05); }
.pp .pp-components .pp-shifter-layout { display: grid; grid-template-columns: minmax(0, 1fr) minmax(100px, 34%); gap: 20px; align-items: start; }
.pp .pp-shifter-layout .pp-shifter-detail-frame { margin: 0; height: auto; aspect-ratio: 2832/4240; }
.pp .pp-components .pp-shifter-layout .pp-shifter-detail-frame { height: 300px; max-width: 180px; aspect-ratio: auto; align-self: center; justify-self: center; }
.pp .pp-components .pp-fitment-detail-frame { max-width: 140px; align-self: center; }
.pp .pp-components .pp-bellhousing-detail-frame { max-width: 160px; align-self: center; }
.pp .pp-components .pp-housing-detail-frame { max-width: 360px; }
.pp .pp-components .pp-kit-detail-frame { max-width: 360px; margin-top: 20px; }
.pp .pp-components .pp-shaft-detail-frame { max-width: 200px; margin-top: 20px; }
.pp .pp-components .pp-adapter-detail-frame { max-width: 300px; margin-top: 20px; }

/* Context links and the actions row */
.pp .pp-context { border-top: 1px solid #ccc; padding-top: 22px; margin-top: 28px; font-size: var(--pp-fs-small); }
.pp .pp-actions { display: flex; flex-wrap: wrap; gap: 12px; }
/* Links: the light red on light surfaces, the lighter red on dark. Buttons keep
 * their own colour (a.btn at the end of the file). */
.pp a:not(.btn) { color: var(--link-text-on-light); }
.pp .pp-dark a:not(.btn) { color: var(--link-text); }

/* ---- Product pages (step 2): the E-SQ5 components, extras for every product page (2026-10-11) ----
 * Every product page uses the E-SQ5 classes. Only what they need beyond them is here. */

/* Hero photo shot on white: a light frame, the whole product visible, the photo multiplied onto the frame. */
.pp .pp-hero-image--light { aspect-ratio: auto; padding: 24px; display: flex; align-items: center; justify-content: center; background: var(--pp-frame); border-radius: var(--pp-radius); -webkit-mask-image: none; mask-image: none; }
.pp .pp-hero-image--light .pp-hero-photo { position: static; display: block; width: 100%; height: auto; max-width: none; left: auto; top: auto; mix-blend-mode: multiply; filter: brightness(1.055); }
/* Whole portrait/dark photo in the landscape hero frame, letterboxed (not cropped): the E-SQ5 135% crop cut diagrams. */
.pp .pp-hero-image--contain .pp-hero-photo { position: absolute; inset: 0; width: 100%; height: 100%; max-width: none; left: 0; top: 0; object-fit: contain; }

/* Section title where the copy has no heading: the h2 look at the one-line size. */
.pp .pp-title { margin: 0 0 20px; font-size: var(--pp-fs-h2-sm); font-weight: 700; line-height: 1.2; letter-spacing: -.02em; text-transform: uppercase; }

/* Price card name when it is a paragraph: drawn like the card's h3. */
.pp .pp-price-card .pp-price-name { margin: 14px 0 8px; font-size: 12px; line-height: 1.25; font-weight: 700; letter-spacing: 0; }

/* This page has five price cards (E-SQ5 has seven): a five-column grid, every card a column, the cost on the card's bottom edge so the costs share one baseline. */
.pp .pp-pricing:has(> .pp-price-card:nth-child(5):last-child) { grid-template-columns: repeat(5, minmax(0, 1fr)); }
.pp .pp-pricing:has(> .pp-price-card:nth-child(5):last-child) > .pp-price-card { display: flex; flex-direction: column; }
.pp .pp-pricing:has(> .pp-price-card:nth-child(5):last-child) > .pp-price-card .pp-cost { margin-top: auto; }
/* Cost lines that mix words and prices: the words are ink at body-small size, only the <strong> prices are the red 16px. */
.pp .pp-price-card .pp-cost:has(> strong) { color: var(--pp-ink); font-size: 12px; font-weight: 400; line-height: 1.4; }
.pp .pp-price-card .pp-cost strong { color: var(--pp-red-deep); font-size: 16px; font-weight: 700; }
/* Photo cards on this page: the photo fills its frame (cover, no brightness lift, so no lighter strip shows at the edges). */
.pp .pp-kit-card-frame .pp-photo-fill { object-fit: cover; filter: none; }

/* Ratio tables: row 1 is the h3 look (the set name, the <strong> in the on-dark use colour), row 2 the small caption. */
.pp .pp-ratio thead th { border-bottom: 0; padding: 0; text-align: left; }
.pp .pp-ratio thead tr:first-child th { font-size: 14px; line-height: 1.25; font-weight: 700; text-transform: uppercase; letter-spacing: -.02em; padding-bottom: 6px; }
.pp .pp-ratio thead tr:first-child strong { color: var(--pp-use-on-dark); }
.pp .pp-ratio thead tr:last-child th { font-size: var(--pp-fs-xs); font-weight: 400; color: inherit; padding-bottom: 12px; }
/* The parenthetical inside a ratio set name: 12px, plain. */
.pp .pp-ratio-note { font-size: 12px; font-weight: 400; text-transform: none; letter-spacing: 0; }

/* The line under the spec table: small, tight to the table. */
.pp .pp-specs > div:first-child > table + p { margin: 10px 0 0; font-size: var(--pp-fs-xs); line-height: 1.4; }

/* Check marks on a dark band: the lighter red. */
.pp .pp-dark .pp-checks li::before { color: var(--pp-red-on-dark); }

/* Option photographs: the E-SQ5 detail-frame look, photo contained in a light frame. */
.pp .pp-components .pp-gallery { grid-template-columns: repeat(auto-fill, 180px); justify-content: start; margin: 8px 0 18px; }
/* 1-4 price cards (the BMW, FI and ZF pages): the pilot's five-column track, so a card keeps the pilot's width and picture frame; cards sit left-aligned and the spare tracks stay empty. At 760 and below the base two-column rule applies, as on the pilot. */
@media (min-width: 761px) {
  .pp .pp-pricing:has(> .pp-price-card:nth-child(-n+4):last-child) { grid-template-columns: repeat(5, minmax(0, 1fr)); }
  .pp .pp-pricing:has(> .pp-price-card:nth-child(-n+4):last-child) > .pp-price-card { display: flex; flex-direction: column; }
  .pp .pp-pricing:has(> .pp-price-card:nth-child(-n+4):last-child) > .pp-price-card .pp-cost { margin-top: auto; }
}

/* FI hero titles on a phone: the longest Finnish compound ("SUORAKYTKENTÄVAIHTEISTO", 444px at 30px uppercase, 461px for "...VAIHTEISTOT") does not fit a 320-390px column, and the 480px rule's overflow-wrap:anywhere then broke it mid-word. Scale the title to the column: one 16th of the column width per px (column = 100vw minus the 36px gutter). :lang(fi) keeps the EN pages untouched. */
@media (max-width: 520px) {
  .pp h1:lang(fi) { font-size: min(var(--pp-fs-h1), calc((100vw - 40px) / 16)); }
}
/* Phone headings and table cells keep whole words. The <=480px rule above sets hyphens:auto and overflow-wrap:anywhere on h1-h3, th and td, so a word that fitted was still hyphenated ("vaih-teisto", "RAL-LICROSS") and a word that did not fit was broken mid-word. Here they hyphenate nothing and wrap nothing: a word too wide for its column is sized down, never broken. Body copy (p, li) keeps the global rule. */
@media (max-width: 480px) {
  .pp h1, .pp h2, .pp h3, .pp th, .pp td { hyphens: manual; overflow-wrap: normal; }
  /* Uppercase FI h2s: the longest compound ("SUORAKYTKENTÄRATASSARJA", 15.6em wide at the measured size) must fit one column (100vw minus the 36px gutter). 16em = a margin over it. EN h2s and the .pp-h2-sm headings are untouched. */
  .pp h2:lang(fi):not(.pp-h2-sm) { font-size: min(var(--pp-fs-h2), calc((100vw - 36px) / 16)); }
}
/* An h2 inside an option panel (origin "Features"): the one-line h2 size, not the page-level h2. */
.pp .pp-components .accordion-body h2 { font-size: var(--pp-fs-h2-sm); line-height: 1.2; margin: 18px 0 10px; }
.pp .pp-components .pp-gallery img { object-fit: contain; }

/* Option panes at phone width: a pane may shrink below its min-content (a grid item's min-width:auto kept other-racing-gears at 320 at 320px wide). */
@media (max-width: 760px) {
  .pp .pp-components details { min-width: 0; }
}

/* Long-form copy at a readable measure. */
.pp .pp-prose { max-width: 74ch; }
.pp .pp-prose h2 { margin-top: 0; }
.pp .pp-prose + .pp-gallery { margin-top: 36px; }

/* Photo grid: equal 4:3 crops, as many columns as the width allows. */
.pp .pp-gallery { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 10px; margin: 0 0 16px; }
.pp .pp-gallery figure { margin: 0; min-width: 0; }
.pp .pp-gallery a { display: block; aspect-ratio: 4 / 3; overflow: hidden; border-radius: var(--pp-radius); background: var(--pp-frame); }
.pp .pp-gallery img { display: block; width: 100%; height: 100%; object-fit: cover; }

/* Responsive: 1050 */
@media (max-width: 1050px) {
  .pp .pp-benefits { grid-template-columns: repeat(2, 1fr); }
  .pp .pp-benefit:nth-child(2) { border-right: 0; }
  .pp .pp-pricing { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .pp .pp-intro, .pp .pp-fitment { gap: 24px; }
  .pp .pp-pricing:has(> .pp-price-card:nth-child(5):last-child) { grid-template-columns: repeat(5, minmax(0, 1fr)); }
}

/* Advantages band: one column up to 520px. At two columns (421-520px) the ~170px tiles broke long words mid-word (ALKUPERÄISII/N, SIIRTÄJÄMEK/ANISMIT). Same look as the 420px rule below, which is unchanged. */
@media (max-width: 520px) {
  .pp .pp-benefits { grid-template-columns: 1fr; }
  .pp .pp-benefit { border-right: 0; border-bottom: 1px solid var(--pp-dark-line); padding: 22px 0; }
  /* The 480px rule above sets hyphens:auto and overflow-wrap:anywhere on every p; in one column that still auto-hyphenates "robust" as "ro-/bust" at 480. Band text wraps by word here. */
  .pp .pp-benefit p { hyphens: manual; overflow-wrap: normal; }
}

/* Responsive: 760 */
@media (max-width: 760px) {
  .pp .pp-wrap { width: calc(100% - var(--pp-gutter-sm)); }
  .pp .pp-hero { padding: 115px 0 32px; }
  .pp .pp-hero-grid, .pp .pp-intro, .pp .pp-fitment { grid-template-columns: 1fr; gap: 28px; }
  .pp .pp-section { padding: var(--pp-space-section-sm) 0; }
  .pp .pp-applications { grid-template-columns: repeat(2, 1fr); padding-right: 14%; }
  .pp .pp-sport-frame { height: 230px; }
  .pp .pp-specs { grid-template-columns: 1fr; gap: 16px; }
  .pp .pp-specs > div:first-child { grid-column: 1 / -1; }
  .pp .pp-pricing { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .pp .pp-components { grid-template-columns: 1fr; }
  .pp h2.pp-component-title { margin-top: var(--pp-space-section-sm); }
  .pp .pp-gallery { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .pp .pp-pricing:has(> .pp-price-card:nth-child(5):last-child) { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .pp .pp-pricing:has(> .pp-price-card:nth-child(5):last-child) > .pp-price-card:nth-child(5) { grid-column: 1 / -1; }
  .pp .pp-components .pp-gallery { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* Responsive: 420 */
@media (max-width: 420px) {
  /* Price cards one per row below 421px (owner decision, issue #61). The 2-column grid at 760px left a card ~135px wide at 360px, and the FI ZF name broke mid-word. Same selectors and specificity as the 5-card :has rules above, and listed after them, so this wins at every width. */
  .pp .pp-pricing,
  .pp .pp-pricing:has(> .pp-price-card:nth-child(5):last-child) { grid-template-columns: minmax(0, 1fr); }
  .pp .pp-benefits { grid-template-columns: 1fr; }
  .pp .pp-benefit { border-right: 0; border-bottom: 1px solid var(--pp-dark-line); padding: 22px 0; }
  .pp .pp-actions .btn { width: 100%; }
  .pp .pp-ratio { padding: 14px; }
  .pp .pp-exploded-assembly figcaption { gap: 4px; }
  .pp .pp-components .pp-shifter-layout { grid-template-columns: 1fr; gap: 20px; }
  .pp .pp-shifter-layout .pp-shifter-detail-frame { margin: 0 auto; max-width: 220px; }
  .pp .pp-components .pp-shifter-layout .pp-shifter-detail-frame { height: 270px; }
  .pp .pp-components .pp-fitment-detail-frame { max-width: 115px; }
  .pp .pp-components .pp-bellhousing-detail-frame { max-width: 145px; }
  .pp .pp-components .pp-shaft-detail-frame { max-width: 180px; }
  .pp .pp-kit-detail-frame { max-width: 320px; margin: 0 auto; }
}

/* The one-line heading size. Last in the section, so it beats the H2 rules
 * above at equal specificity, as the inline style it replaces did. */
.pp h2.pp-h2-sm { font-size: var(--pp-fs-h2-sm); }

/* ---- Front page story ------------------------------------------------------
 * The English front page as six sections, in the product-page design system's
 * visual language: solid dark (--pp-dark) and light (--pp-surface) bands, red
 * eyebrows, heavy uppercase display headings, a slanted photo strip, cards.
 * Colours, type sizes, radii and the wrap come from the pp-* tokens above. The
 * literals kept are the ones no token covers (noted where they occur). Every
 * selector is scoped under .story-*, so nothing here reaches the product pages.
 *
 * Surfaces are SOLID on purpose: the story bands carry no background photograph,
 * so nothing here depends on the --band-overlay guarantee. .story-light is in the
 * three shared light-surface lists near the top (ink, headings, links). The hero
 * band is untouched: it is the dark, hit-testable element behind the fixed
 * transparent header.
 *
 * ⚠ The hero gains two small children (tagline, scroll link). Both are hidden on
 * short viewports, together with the logo (the rule at the bottom). */
.story-band { padding-block: clamp(40px, 6vw, 80px); scroll-margin-top: var(--header-h); }
.story-dark { background: var(--pp-dark); color: #fff; }
.story-dark :is(h1, h2, h3) { color: #fff; }
.story-dark a:not(.btn) { color: var(--link-text); }   /* #fc0000 on --pp-dark: 4.7:1 */
.story-light { background: var(--pp-surface); }
.story-wrap { width: min(var(--pp-wrap), calc(100% - var(--pp-gutter))); margin-inline: auto; }
@media (max-width: 760px) {
  .story-wrap { width: calc(100% - var(--pp-gutter-sm)); }
}

.story h1, .story h2 {
  text-transform: uppercase;
  letter-spacing: -0.035em;
  line-height: 1.06;
  margin: 0 0 20px;
}
.story h1 { font-size: var(--pp-fs-h1); }
.story h2 { font-size: var(--pp-fs-h2); }
.story h3 { font-size: var(--pp-fs-h3); line-height: 1.25; letter-spacing: -0.02em; margin: 0 0 10px; }
.story-eyebrow {
  font-size: var(--pp-fs-small); font-weight: var(--fw-bold); letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--pp-red); margin: 0 0 12px;
}
.story-dark .story-eyebrow { color: var(--pp-red-on-dark); }   /* 6.4:1 on --pp-dark */
.story-lead { font-size: clamp(17px, 1.5vw, 21px); font-weight: var(--fw-bold); line-height: 1.35; max-width: 60ch; }
.story-head { max-width: 760px; margin-bottom: clamp(24px, 3vw, 40px); }
.story-more { font-weight: var(--fw-bold); margin-top: 18px; }
.story-more a { text-decoration: none; }
.story-more a:hover, .story-more a:focus-visible { text-decoration: underline; }
.story-footnote { font-size: var(--fs-small); color: #c5c9cc; margin-top: clamp(24px, 3vw, 40px); max-width: 70ch; }

/* Opening band: the logo stays; a one-line tagline and a scroll link join it. */
.story-opening { display: grid; justify-items: center; gap: 14px; }
.story-tagline {
  margin: 0; font-size: clamp(15px, 1.4vw, 20px); font-weight: var(--fw-bold);
  letter-spacing: 0.04em; text-transform: uppercase; color: #fff;
}
.hero .story-scroll { color: #fff; font-weight: var(--fw-bold); font-size: 14px; text-decoration: none; border-bottom: 2px solid var(--pp-red-deep); padding-bottom: 2px; }
.hero .story-scroll:hover, .hero .story-scroll:focus-visible { border-bottom-color: #fff; }

/* Text + picture rows. Every picture is 3:2 and cropped, never stretched. */
.story-grid { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(24px, 4vw, 56px); align-items: center; }
.story-figure { margin: 0; min-width: 0; }
.story-figure img {
  display: block; width: 100%; height: auto; aspect-ratio: 3 / 2; object-fit: cover;
  border-radius: var(--pp-radius);
}
.story-figure img[src*="koppalogolla"] { object-position: 50% 50%; }
.story-figure img[src*="tuotekuvat-132"] { object-position: 50% 60%; }

/* Four stat tiles, ruled like the E-SQ5 benefit row. */
.story-stats {
  list-style: none; padding: 0; margin: clamp(32px, 4vw, 56px) 0 0;
  display: grid; grid-template-columns: repeat(4, 1fr); border-top: 1px solid var(--pp-dark-line);
}
.story-stats li { padding: 26px 20px 26px 0; border-right: 1px solid var(--pp-dark-line); margin-right: 20px; }
.story-stats li:last-child { border-right: 0; margin-right: 0; }
.story-stats strong { display: block; font-size: clamp(30px, 3vw, 44px); line-height: 1; letter-spacing: -0.03em; margin-bottom: 8px; }
.story-stats span { font-size: var(--pp-fs-small); line-height: 1.45; color: #c5c9cc; }

/* The slanted photo strip (the E-SQ5 page's four-discipline row). Captions sit
 * on a solid bar, never over the photograph. */
.story-strip { overflow: hidden; }
.story-strip-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 2px; padding-right: 7%; }
.story-tile {
  position: relative; margin: 0; min-width: 0; width: 132%; background: var(--pp-ink);
  clip-path: polygon(24.4% 0, 100% 0, 75.6% 100%, 0 100%);
}
.story-tile-frame { height: clamp(170px, 20vw, 260px); overflow: hidden; }
.story-tile-frame img { display: block; width: 100%; height: 100%; object-fit: cover; object-position: center; border-radius: 0; }
.story-tile figcaption {
  position: absolute; bottom: 0; left: 0; width: 100%; height: 36px;
  display: flex; justify-content: center; align-items: center; padding-right: 20%;
  background: var(--pp-ink); color: #fff; font-size: var(--pp-fs-small); font-weight: var(--fw-bold); text-transform: uppercase;
}
/* Per-photo framing. The car sits above the caption bar; the drag photo has
 * black bars top and bottom, so it is enlarged just enough to crop them out. */
.story-tile--drift .story-tile-frame img { object-position: 45% 60%; }
.story-tile--rally .story-tile-frame img { object-position: 92% 50%; }
.story-tile--circuit .story-tile-frame img { object-position: 50% 55%; }
.story-tile--drag .story-tile-frame img { object-position: 50% 52%; transform: scale(1.02); }

/* Product cards on the light band. Three frames of one size (4:3, the E-SQ5
 * photograph's own #f5f5f5 ground), each picture sized and centred by hand. */
.story-cards { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; }
.story-card { background: var(--pp-card); padding: 14px 16px 18px; min-width: 0; display: flex; flex-direction: column; }
.story-card-frame {
  position: relative; aspect-ratio: 4 / 3; overflow: hidden; background: #f5f5f5;
  margin: -14px -16px 14px;
}
.story-card-frame img { position: absolute; top: 0; left: 0; width: 100%; height: 100%; object-fit: cover; border-radius: 0; }
/* Bellhousing: 800x1200 portrait, the part sits low-centre; scaled to ~80% of the frame. */
.story-card-frame--esq5 img { object-position: 50% 58%; transform: scale(0.8); }
/* ZF cut-outs on white: white becomes the frame ground, ~5% inset. The photo's
 * subject is off-centre, so each is nudged to the frame's middle. */
.story-card-frame--zfsq5 img, .story-card-frame--zfh5 img {
  top: 5%; left: 5%; width: 90%; height: 90%; object-fit: contain; mix-blend-mode: multiply;
}
.story-card-frame--zfsq5 img { transform: scale(1.55); transform-origin: 50% 58%; }
.story-card-frame--zfh5 img { transform: translateX(-7%); }
.story-card p { font-size: 14px; }
.story-card .story-price { color: var(--pp-red-deep); font-weight: var(--fw-bold); margin-top: auto; }   /* 5.9:1 on --pp-card */
.story-card .story-more { margin-top: 8px; }

/* Reviews reuse the site's dark testimonial cards and carousel unchanged. */
.story-reviews .testimonials--carousel { margin-top: 0; }

/* Last section: three short columns. */
.story-next { display: grid; grid-template-columns: repeat(3, 1fr); gap: 2px; border-top: 1px solid var(--pp-dark-line); }
.story-next-item { padding: 26px 24px 26px 0; border-right: 1px solid var(--pp-dark-line); margin-right: 24px; }
.story-next-item:last-child { border-right: 0; margin-right: 0; }
.story-next-item p { font-size: 14px; color: #c5c9cc; }
.story-next-item .story-more { color: #fff; }

@media (max-width: 1050px) {
  .story-stats { grid-template-columns: repeat(2, 1fr); }
  .story-stats li:nth-child(2) { border-right: 0; }
}
@media (max-width: 760px) {
  .story-cards { grid-template-columns: 1fr; }
  .story-grid, .story-next { grid-template-columns: 1fr; }
  .story-grid--flip .story-figure { order: 2; }
  .story-strip-grid { grid-template-columns: repeat(2, 1fr); padding-right: 14%; }
  .story-tile-frame { height: 200px; }
  .story-next-item { border-right: 0; margin-right: 0; padding: 20px 0; border-bottom: 1px solid var(--pp-dark-line); }
  .story-next-item:last-child { border-bottom: 0; }
}
@media (max-width: 420px) {
  .story-stats { grid-template-columns: 1fr; }
  .story-stats li { border-right: 0; margin-right: 0; padding: 18px 0; border-bottom: 1px solid var(--pp-dark-line); }
  .story-stats li:last-child { border-bottom: 0; }
  .story-tile-frame { height: 150px; }
}
/* On a short viewport the band collapses to menu height (see the phone rules
 * above) and the logo goes; the two new children must go with it, or they
 * overflow the band. */
@media (max-height: 620px) and (max-width: 1024px) {
  .hero--front .story-tagline, .hero--front .story-scroll { display: none; }
}

/* ---- The reseller map, restated inside .pp .pp-dark (2026-10-11) ----------
 * The map now sits in the dark pp band. The band's rules above would otherwise
 * reach the map's own elements: `.pp .pp-dark a:not(.btn)` (0,3,1) and
 * `.pp p` (0,1,1) beat Leaflet's and the map's rules. These restate what the
 * map had on origin/main, scoped to the map only. They come after the pp-dark
 * link rule, so a tie goes here. Checked computed-style by Playwright. */
.pp .pp-dark .reseller-map a { color: var(--link-text-on-light); }
.pp .pp-dark .reseller-map a.leaflet-disabled { color: #6f6f6f; }
.pp .pp-dark .reseller-map a.leaflet-popup-close-button { color: #757575; }
.pp .pp-dark .reseller-map a.leaflet-popup-close-button:hover,
.pp .pp-dark .reseller-map a.leaflet-popup-close-button:focus { color: #585858; }
/* Popup paragraphs: on origin/main Leaflet's `.leaflet-popup-content p` (1.3em)
 * beats `.map-popup-addr` (4px), and the global `p:last-child` zeroes the
 * bottom. Both are reproduced here so the computed values match origin/main. */
.pp .pp-dark .reseller-map .leaflet-popup-content p { margin: 1.3em 0; }
.pp .pp-dark .reseller-map .leaflet-popup-content p:last-child { margin-bottom: 0; }
.pp .pp-dark .reseller-map .map-popup-name { margin: 0; }
.pp .pp-dark .reseller-map .map-fallback { margin: 0; }

/* ⚠ Text links are the client's #fc0000 (2026-08-25) — `--link-text` on a dark
 * surface, `--link-text-on-light` on a light one, which is the same red
 * deepened to #c60000 because #fc0000 measures 4.09:1 on white and failed the
 * gate 324 times. See tokens.css for why one value cannot cover both.
 *
 * The menu and the footer are deliberately NOT in this set: the menu keeps
 * white, the footer keeps `--link-on-dark`.
 *
 * The old lesson still applies to whatever replaces it: moving /references/'s
 * lead paragraph into the band once put a #7e2121 link on #111 at 1.91:1, and
 * the contrast gate caught it, not a person looking at the page. */
.hero a { color: var(--link-text); }
.hero--light a { color: var(--link-text-on-light); }

/* ⚠ THE LAST WORD ON LINK COLOUR, and it has to be last.
 *
 * `.btn` paints the accent-red surface, so it states white. Every band's link
 * rule — `.section a`, `.section--light a`, `.hero a` — is `(0,1,1)`, and so is
 * `a.btn`, which means SOURCE ORDER decides and nothing else. This rule used to
 * sit 800 lines earlier, next to the light-surface components, and the moment
 * the title band grew a CTA the button shipped #fc0000 text on an #ee0000
 * button: 1.1:1, invisible, in a screenshot that otherwise looked perfect. Do
 * not move it back up. */
a.btn { color: #fff; }

/* ---- The front page's opening band ------------------------------------- */

/* The original's front page opens on a 639px band carrying the shared
 * photograph with the logo centred on it at 850x373 — measured, not guessed:
 * artifacts/measured/en-home.json. On a desktop that is `.hero` unmodified;
 * `.hero--front` adds nothing there. The modifier exists for one purpose, and
 * only inside a media query: phones and landscape phones drop the band's logo
 * and collapse the band to menu height. See the bottom of this file.
 *
 * ⚠ There is NO intro animation. The logo used to start large in the band and
 * fly into the header's brand slot while the band collapsed to menu height;
 * the client asked for the original's header back on 2026-08-25, so the whole
 * mechanism is deleted — `.hero--intro`, `.hero-band`, `.hero-fly`,
 * `.intro-rise`, `.has-intro`, the derived `--brand-left` landing coordinate,
 * and the reduced-motion and short-viewport opt-outs that existed only to
 * switch it off. Nothing animates on load anywhere on the site now, so there
 * is no CLS to trade against: the front page cannot shift what it never
 * moves. */
.hero-logo {
  /* Natural width 1024; the original rendered it at 850. */
  width: min(100%, 850px);
  height: auto;
}

/* ---- Phones ------------------------------------------------------------ */

/* ⚠ The logo does not scale in the original, and that is a defect the client
 * asked to have fixed rather than preserved (2026-08-25): a fixed 200px logo
 * in a 90px header is over half the width of a 390px screen.
 *
 * ⚠ Shrinking the logo alone changes nothing visible — the 90px bar stays 90px
 * and the logo just floats in it. `--header-h` has to come down with it, and
 * because it is a token the nav dropdown's `top`, the hero's padding and the
 * mobile menu all follow. Do not hard-code 90px anywhere.
 *
 * The BAND's logo is not shrunk here, it is removed outright — see the
 * front-page rule below. Title bands keep the 240px they get at 800px; this
 * block deliberately no longer touches `.hero`'s height, because the only
 * page whose band was taller than that now collapses instead. */
@media (max-width: 600px) {
  :root { --header-h: 64px; }

  .site-brand img { width: 140px; max-height: 44px; }

  /* ⚠ The title has to give up room so the CTA fits directly under it inside
   * the band, rather than being pushed below the fold — client, 2026-08-25.
   * 32px/48px wrapped most page titles to three lines on a 390px screen and
   * left nothing. Scoped to the band: an <h1> in body copy keeps its size. */
  .hero h1 { font-size: 26px; line-height: 32px; }
  .hero-cta { padding: 11px 18px; }

  /* The front page's band shows the logo again, and this is why it can: the bar
   * above it now carries the CTA instead of a second copy of the logo. When
   * both were on screen the pair ate three quarters of the phone; one of them
   * does not. 260px is ~31% of a 390x844 screen. */
  .hero--front { min-height: 260px; }
  .hero-logo { width: min(100%, 280px); }
}

/* ⚠ On a phone the front page's band shows NO logo and is no taller than the
 * menu.
 *
 * The client asked for the original's full-height opening band back
 * (2026-08-25) and got it — on a desktop. On a phone it is the wrong trade
 * twice over: the band plus the header ate roughly three quarters of the screen
 * before a word of content, and the logo it was showing is already in the menu
 * bar directly above it, at the same moment, in the same colours. One logo,
 * once. Measured: 300px of band (36% of a 390x844 phone) down to 64px (8%),
 * with the first content section starting at y=64.
 *
 * ⚠ The band is NOT deleted, and must not be. The header is transparent, so it
 * needs something dark and hit-testable behind it or the contrast gate resolves
 * the nav's backdrop to the page colour and fails every link in it — that has
 * happened three separate ways on this page already. `.hero` itself is that
 * element. It just stops being taller than the menu it sits behind: it is a
 * grid with `place-items: center` whose only child is now empty, so
 * `min-height` plus the 6px red rule is the entire height.
 *
 * ⚠ The second condition is a landscape phone, not a laptop. At 844x390 the
 * viewport is wide enough for the desktop rules, so the band would be 639px —
 * larger than the whole screen. Capped at 1024px wide deliberately: a short
 * desktop window keeps the design the client signed off. */
/* ⚠ A SHORT viewport — a phone held sideways — still gets no band at all. At
 * 844x390 the width is past every phone breakpoint, so the desktop rules would
 * hand it a 639px band: larger than the entire screen. The logo is absent there
 * and that is accepted; the bar carries the CTA and the footer carries the
 * mark.
 *
 * ⚠ Capped at 1024px wide deliberately — a short DESKTOP window must keep the
 * design the client signed off, not silently lose the front page's band.
 *
 * ⚠ Must stay after the 600px block: a 390x600 phone matches both, and the
 * collapse has to win. Same specificity, so source order decides. */
@media (max-height: 620px) and (max-width: 1024px) {
  .hero--front { min-height: var(--header-h); padding-block: 0; }
  .hero--front .hero-logo { display: none; }
}

.story-tile-frame svg { display:block; width:100%; height:100%; }
