/* ==========================================================================
   Balter Security — B2B resource site
   Tokens follow design-system/MASTER.md. Motion follows emil-design-eng.
   ========================================================================== */

/* Sizes on fixed-height chrome are chosen, not inherited
 * ---------------------------------------------------------------------------
 * The browser rounds a font's em box to half a pixel, so a size whose 1.2em is
 * fractional cannot sit exactly in the middle of a fixed-height box: 12px is
 * out by 0.30, 11px by 0.15, while 10px (em 12.0) and 13px (em 15.6 → 15.5)
 * measure 0.00 and 0.05. Badges and codes are therefore 10px and the small
 * chips 13px — measured on a bench, not chosen by eye. */

/* Vertical rhythm, decided once
 * ---------------------------------------------------------------------------
 * Text in Canaro sits high in its own line box: the font reserves 834 units
 * above the baseline and 366 below, while the ink of a capital only reaches
 * 680. Anything centred honestly — a flex row, a button with equal padding, a
 * badge — therefore looked a little low, and every place that mattered grew its
 * own nudge. Dozens of them, each right on the day it was written and wrong the
 * next time a sibling changed.
 *
 * The correction belongs to the font, not to the components. These overrides
 * re-cut the line box so the capital band sits in the middle of it:
 *
 *     box centre = (ascent - descent) / 2   must equal   cap / 2
 *     so ascent - descent = cap, with ascent + descent kept at the
 *     font's own 1.2 em.
 *
 * Canaro    cap 680 → 94.00% / 26.00%
 * Zen Dots  cap 715 → 95.75% / 24.25%
 *
 * From here on, centring is centring: `align-items: center` and equal padding
 * are correct on their own, an icon beside a label needs nothing but
 * `align-items: center`, and no rule in this sheet may add a vertical nudge to
 * make text look centred.
 * They also make the result identical on every platform, since a browser
 * otherwise picks hhea or OS/2 metrics depending on the operating system. */
@font-face {
  font-family: 'Zen Dots';
  src: url('assets/fonts/ZenDots-Regular.woff2') format('woff2');
  font-weight: 400;
  font-display: swap;
  ascent-override: 95.75%;
  descent-override: 24.25%;
  line-gap-override: 0%;
}

/* `hidden` must win.
 *
 * The attribute only carries `display: none` from the user-agent stylesheet,
 * which any component rule with its own `display` silently outranks. This has
 * cost time four times in this repository — the mega-menu sheet, the "show
 * more" button, the prototype tag and the sign-in divider — each time as an
 * element that was hidden in JavaScript and stayed on screen anyway.
 *
 * One rule ends the whole class of it. Use `hidden` to hide things. */
[hidden] { display: none !important; }
@font-face {
  font-family: 'Canaro';
  src: url('assets/fonts/Canaro-Book.woff2') format('woff2');
  font-weight: 400;
  font-display: swap;
  ascent-override: 94%;
  descent-override: 26%;
  line-gap-override: 0%;
}
@font-face {
  font-family: 'Canaro';
  src: url('assets/fonts/Canaro-Medium.woff2') format('woff2');
  font-weight: 500;
  font-display: swap;
  ascent-override: 94%;
  descent-override: 26%;
  line-gap-override: 0%;
}
@font-face {
  font-family: 'Canaro';
  src: url('assets/fonts/Canaro-SemiBold.woff2') format('woff2');
  font-weight: 600;
  font-display: swap;
  ascent-override: 94%;
  descent-override: 26%;
  line-gap-override: 0%;
}
@font-face {
  font-family: 'Canaro';
  src: url('assets/fonts/Canaro-Bold.woff2') format('woff2');
  font-weight: 700;
  font-display: swap;
  ascent-override: 94%;
  descent-override: 26%;
  line-gap-override: 0%;
}

:root {
  --accent: #fb824b;
  /* The brand orange as *ink*. On the dark ground it is the brand colour itself
     at 7.7:1. On white it falls to 2.5:1, so the light theme restates it darker
     — the fill stays #fb824b either way, only text moves. */
  --accent-text: #fb824b;
  --accent-2: #6699cc;
  --bg: #0f0f0f;
  --surface: #151515;
  --surface-2: #1c1c1c;
  --line: rgba(255, 255, 255, 0.1);
  --line-strong: rgba(255, 255, 255, 0.2);
  --fg: #ffffff;
  --fg-muted: #959595;
  --fg-dim: rgba(255, 255, 255, 0.45);
  --plate-light: #f4f4f4;
  --plate-dark: #0b0b0b;

  /* Neutrals that used to be written out wherever they were needed, each one
     assuming a dark ground. Named here so a light theme can restate them. */
  --veil: rgba(15, 15, 15, 0.72);          /* header over content */
  --veil-strong: rgba(15, 15, 15, 0.92);   /* header once scrolled, menus */
  --veil-solid: rgba(15, 15, 15, 0.98);    /* the phone menu, which must not read through */
  --scrim: rgba(5, 5, 5, 0.72);            /* behind a modal */
  --tint: rgba(255, 255, 255, 0.06);       /* hover fill on a bare control */
  --hatch: rgba(255, 255, 255, 0.024);     /* the diagonal weave on empty plates */
  --shadow: rgba(0, 0, 0, 0.45);
  --shadow-strong: rgba(0, 0, 0, 0.64);
  --fade-0: rgba(15, 15, 15, 0.1);         /* caption gradient over a cover */
  --fade-1: rgba(15, 15, 15, 0.55);
  --stage: #000;                           /* the video stage, black on any theme */

  --r-sm: 2px;
  --r: 4px;
  --r-lg: 8px;

  --ease-out: cubic-bezier(0.23, 1, 0.32, 1);
  --ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);
  --t-fast: 140ms;
  --t: 200ms;
  --t-slow: 280ms;

  /* Vertical centring aligns the line box, not the letters. Canaro's metrics
     are asymmetric (ascent 83 / descent 37 per 1000) and Zen Dots' differ again,
     so every centred label reads as sitting high.

     Applied as padding-top on a flex-centred box, which shifts the content
     centre by only HALF the padding — the padding also shrinks the content box.
     So the value is twice the 0.11em metric offset.

     (text-box-trim was tried first and removed: it did not actually shrink the
     line box here, and left as a no-op it silently cancelled this correction.) */

  /* Every control in the header stands on this line. They arrived at four
     different heights — 40 for the icon buttons, 32 for the language one left
     over from when it was a row of codes, 38 for the shop button — which reads
     as a wobble rather than a row. */
  --control-h: 40px;

  --gutter: clamp(20px, 5vw, 64px);
  --maxw: 1440px;
  --header-h: 68px;
}

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



html {
  scrollbar-gutter: stable;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--header-h) + 16px);
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: 'Canaro', system-ui, -apple-system, sans-serif;
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

body.is-locked {
  overflow: hidden;
}

h1,
h2,
h3 {
  font-family: 'Zen Dots', 'Canaro', sans-serif;
  font-weight: 400;
  line-height: 1.1;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  margin: 0;
}

p {
  margin: 0;
}

a {
  color: inherit;
  text-decoration: none;
}

img {
  display: block;
  max-width: 100%;
}

button {
  font: inherit;
  color: inherit;
  background: none;
  border: none;
  cursor: pointer;
}

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

.wrap {
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.skip {
  position: absolute;
  left: -9999px;
  top: 8px;
  z-index: 200;
  background: var(--accent);
  color: #0f0f0f;
  padding: 10px 16px;
  font-weight: 600;
}
.skip:focus {
  left: 16px;
}

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

.header {
  position: fixed;
  inset: 0 0 auto;
  height: var(--header-h);
  z-index: 100;
  display: flex;
  align-items: center;
  background: var(--veil);
  backdrop-filter: blur(14px);
  border-bottom: 1px solid transparent;
  transition: border-color var(--t) var(--ease-out), background var(--t) var(--ease-out);
}
.header[data-scrolled='true'] {
  border-bottom-color: var(--line);
  background: var(--veil-strong);
}

.header__inner {
  display: flex;
  align-items: center;
  gap: 32px;
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.logo {
  flex-shrink: 0;
  display: flex;
  align-items: center;
}
.logo img {
  height: 26px;
  width: auto;
}

.nav {
  display: flex;
  gap: 28px;
  margin-inline: auto;
}
.nav a {
  font-size: 14px;
  color: var(--fg-muted);
  padding: 6px 0;
  position: relative;
  transition: color var(--t) var(--ease-out);
}
.nav a::after {
  content: '';
  position: absolute;
  left: 0;
  bottom: 0;
  height: 1px;
  width: 100%;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--t) var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .nav a:hover {
    color: var(--fg);
  }
  .nav a:hover::after {
    transform: scaleX(1);
  }
}

.header__right {
  display: flex;
  align-items: center;
  gap: 16px;
  flex-shrink: 0;
}

/* language switcher */
.lang {
  display: flex;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  overflow: hidden;
}
.lang button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.06em;
  padding: 0 10px;
  line-height: 1;
  min-width: 40px;
  min-height: var(--control-h);
  color: var(--fg-dim);
  transition: color var(--t) var(--ease-out), background var(--t) var(--ease-out);
}
.lang button[aria-pressed='true'] {
  color: #0f0f0f;
  background: var(--fg);
}
@media (hover: hover) and (pointer: fine) {
  .lang button:not([aria-pressed='true']):hover {
    color: var(--fg);
    background: var(--tint);
  }
}

/* ---------------------------------------------------------------- button -- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  min-height: 44px;
  padding: 0 22px;
  line-height: 1;
  border-radius: var(--r-sm);
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0.01em;
  white-space: nowrap;
  transition: transform var(--t-fast) var(--ease-out), background var(--t) var(--ease-out),
    border-color var(--t) var(--ease-out), color var(--t) var(--ease-out);
}
.btn:active {
  transform: scale(0.97);
}

.btn--primary {
  background: var(--accent);
  color: #0f0f0f;
}
.btn--ghost {
  border: 1px solid var(--line-strong);
  color: var(--fg);
}
.btn--sm {
  min-height: 38px;
  padding: 0 16px;
  font-size: 13px;
}

@media (hover: hover) and (pointer: fine) {
  .btn--primary:hover {
    background: #ff9564;
  }
  .btn--ghost:hover {
    border-color: var(--fg);
    background: var(--tint);
  }
}

.btn__arrow {
  transition: transform var(--t) var(--ease-out);
}

@media (hover: hover) and (pointer: fine) {
  .btn:hover .btn__arrow {
    transform: translateX(3px);
  }
}

/* ------------------------------------------------------------------ hero -- */

.hero {
  --lift: 20px;
  position: relative;
  display: flex;
  align-items: center;
  padding-block: calc(var(--header-h) + clamp(26px, 4vw, 50px))
    calc(var(--lift, 20px) + clamp(28px, 3.4vw, 46px));
  overflow: hidden;
}

.hero__grid {
  position: absolute;
  inset: 0;
  background-image: linear-gradient(var(--line) 1px, transparent 1px),
    linear-gradient(90deg, var(--line) 1px, transparent 1px);
  background-size: 64px 64px;
  mask-image: radial-gradient(ellipse 76% 58% at 50% 34%, #000 5%, transparent 74%);
  opacity: 0.5;
  pointer-events: none;
}

.hero__inner {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-size: 12px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--fg-dim);
  margin-bottom: 22px;
}
.eyebrow::before {
  content: '';
  width: 22px;
  height: 1px;
  background: var(--accent);
}

.hero__title {
  font-size: clamp(46px, 7.4vw, 96px);
  line-height: 0.92;
  letter-spacing: 0.01em;
  white-space: pre-line;
  margin-bottom: 14px;
}
.hero__title span {
  color: var(--accent-text);
}

.hero__tagline {
  font-size: clamp(16px, 1.9vw, 23px);
  font-weight: 600;
  letter-spacing: 0.01em;
  margin-bottom: 12px;
}
.hero__lead {
  max-width: 52ch;
  color: var(--fg-muted);
  font-size: clamp(14px, 1.4vw, 17px);
  margin-bottom: 24px;
}

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  justify-content: center;
}
/* The line page reuses this block beside left-aligned copy, so it must not
   inherit the landing hero's centring. */
.masthead .hero__actions {
  justify-content: flex-start;
}

.hero__panel {
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: linear-gradient(160deg, var(--surface), rgba(21, 21, 21, 0.35));
  padding: 8px;
}
.hero__panel img {
  width: 100%;
  border-radius: var(--r);
  aspect-ratio: 4 / 3;
  object-fit: contain;
  padding: 18px;
  background: var(--plate-dark);
}
.hero__panelbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 12px 10px 6px;
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--fg-dim);
}

/* ------------------------------------------------------------ stats bar -- */

.stats {
  border-block: 1px solid var(--line);
  background: var(--surface);
}
.stats__inner {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
}
.stat {
  padding: 26px var(--gutter);
  border-left: 1px solid var(--line);
}
.stat:first-child {
  border-left: 0;
}
.stat__value {
  font-family: 'Zen Dots', sans-serif;
  font-size: clamp(20px, 2.4vw, 30px);
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
.stat__label {
  margin-top: 8px;
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-dim);
}

/* --------------------------------------------------------------- section -- */

.section {
  padding-block: clamp(64px, 9vw, 112px);
}
.section--tight {
  padding-block: clamp(48px, 6vw, 76px);
}
.section__head {
  margin-bottom: clamp(32px, 4vw, 56px);
  max-width: 60ch;
}
.section__title {
  font-size: clamp(26px, 3.6vw, 44px);
}

/* --------------------------------------------------------------- systems -- */

.system {
  border-top: 1px solid var(--line);
  padding-block: clamp(48px, 6vw, 84px);
  scroll-margin-top: calc(var(--header-h) + 20px);
}

.system__head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(24px, 4vw, 64px);
  align-items: end;
  margin-bottom: 36px;
}

.system__badges {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 16px;
}

.badge {
  display: inline-flex;
  align-items: center;
  height: 24px;
  padding: 0 10px;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--fg-muted);
}
.badge--accent {
  border-color: var(--accent-text);
  color: var(--accent-text);
}

.system__name {
  font-size: clamp(32px, 5vw, 62px);
  margin-bottom: 14px;
}
.system__lead {
  font-size: clamp(16px, 1.7vw, 20px);
  font-weight: 500;
  max-width: 34ch;
}
.system__body {
  color: var(--fg-muted);
  max-width: 58ch;
  margin-bottom: 20px;
}

/* academy strip */
.academy {
  display: grid;
  grid-template-columns: minmax(0, 260px) minmax(0, 1fr);
  gap: 20px;
  align-items: center;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
  padding: 14px;
  margin-bottom: 32px;
}

.academy__video {
  position: relative;
  display: block;
  border-radius: var(--r);
  overflow: hidden;
  aspect-ratio: 16 / 9;
  background: var(--plate-dark);
  border: 1px solid var(--line);
}
.academy__video img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0.55;
  transition: opacity var(--t) var(--ease-out), transform var(--t-slow) var(--ease-out);
}
.academy__play {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
}
.academy__play span {
  width: 46px;
  height: 46px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--accent);
  color: #0f0f0f;
  transition: transform var(--t) var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .academy__video:hover img {
    opacity: 0.75;
    transform: scale(1.03);
  }
  .academy__video:hover .academy__play span {
    transform: scale(1.08);
  }
}

.academy__label {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent-text);
  margin-bottom: 12px;
}

.docs {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.doc {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  min-height: 38px;
  padding: 0 12px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  font-size: 13px;
  color: var(--fg-muted);
  transition: border-color var(--t) var(--ease-out), color var(--t) var(--ease-out),
    transform var(--t-fast) var(--ease-out);
}
.doc:active {
  transform: scale(0.97);
}
@media (hover: hover) and (pointer: fine) {
  .doc:hover {
    border-color: var(--line-strong);
    color: var(--fg);
  }
}

/* ----------------------------------------------------------- product grid -- */

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
  gap: 14px;
}

.card {
  display: flex;
  flex-direction: column;
  text-align: left;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
  overflow: hidden;
  transition: border-color var(--t) var(--ease-out), background var(--t) var(--ease-out),
    transform var(--t-fast) var(--ease-out);
}
.card:active {
  transform: scale(0.985);
}

/* Absolute inset rather than max-height: a percentage max-height does not
   resolve against an auto-sized grid row, so tall shots blow the plate open. */
.card__plate {
  position: relative;
  aspect-ratio: 1;
  overflow: hidden;
}
/* The badge on an article that leads its line. Small and quiet: it explains
   why the card is at the top, it does not shout about the product. */
/* The heading over a group of plans that need an account.
   The list is a bordered card whose rows are separated by a top border, so a
   group heading is a row too — set apart by its background and its type rather
   than by floating outside the frame, which is what it did on the first
   attempt: a stray label above the box with the section title pushed away. */
.plans__group {
  padding: 11px 18px 9px;
  border-top: 1px solid var(--line);
  background: var(--surface-2, var(--tint));
  color: var(--fg-muted);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.plans__group[data-level='abo'] { color: var(--accent-text); }
.plans__group:first-child { border-top: 0; }

.card__new {
  position: absolute;
  top: 10px;
  left: 10px;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  height: 20px;
  padding: 2px 8px 2px;
  border-radius: 3px;
  background: var(--accent);
  color: #0f0f0f;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
}

.card__plate[data-tone='light'] {
  background: var(--plate-light);
}
.card__plate[data-tone='dark'] {
  background: var(--plate-dark);
}
.card__plate img {
  position: absolute;
  inset: 12px;
  width: calc(100% - 24px);
  height: calc(100% - 24px);
  object-fit: contain;
  transition: transform var(--t-slow) var(--ease-out);
}

/*
 * Fixed rows, not flow.
 *
 * Names run from 11 to 78 characters and article numbers from 6 to 27, so a card
 * that lets its parts find their own place puts the tag at a different height on
 * every card and the grid reads as broken. Each part gets one reserved row at a
 * height that does not depend on what is in it: name, article number, tag. Short
 * names leave the row part-empty rather than pulling everything up.
 */
.card__body {
  --card-name-lines: 3;
  padding: 12px 14px 14px;
  border-top: 1px solid var(--line);
  display: grid;
  grid-template-rows: calc(var(--card-name-lines) * 1.35em) auto auto;
  gap: 7px;
  align-content: start;
  flex: 1;
}
.card__name {
  font-size: 13px;
  line-height: 1.35;
  font-weight: 600;
  letter-spacing: 0.01em;
  font-variant-numeric: tabular-nums;
  /* Clamped rather than wrapped: a fourth line would push the row it shares a
     grid with, and the article number below already says exactly which part
     this is. */
  display: -webkit-box;
  -webkit-line-clamp: var(--card-name-lines);
  -webkit-box-orient: vertical;
  overflow: hidden;
  overflow-wrap: anywhere;
}
.card__tag {
  align-self: start;
  justify-self: start;
  /* flex rather than padding-centred, so trimming lands the label dead centre */
  display: inline-flex;
  align-items: center;
  min-height: 20px;
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--fg-dim);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  padding: 0 7px;
}

@media (hover: hover) and (pointer: fine) {
  .card:hover {
    border-color: var(--line-strong);
    background: var(--surface-2);
  }
  .card:hover .card__plate img {
    transform: scale(1.05) translateY(-2px);
  }
}

.grid__more {
  display: flex;
  justify-content: center;
  padding-top: 26px;
}
/* display beats hidden, so say so explicitly — this is why the button used to
   stay behind after the whole list was shown */
.grid__more[hidden] {
  display: none;
}

/* --------------------------------------------------------------- clients -- */

.clients-marquee .marquee__track {
  gap: 12px;
}
.client {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  /* The tile follows the mark, not the other way round. Leo de Groot's is four
     and a half times as wide as it is tall and carries two lines of small type;
     forced into the same box as a square mark it became unreadable. */
  min-width: 172px;
  max-width: 320px;
  min-height: 116px;
  padding: 20px 24px;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  /* Dark in both themes on purpose. Each of these marks had to be chosen for one
     ground or the other: Zander and EGU publish white negatives, Gautzsch and
     Schutzer are flat black and get inverted. Letting the tile follow the theme
     would mean every one of those choices is wrong half the time. */
  background: var(--plate-dark);
  transition: border-color var(--t) var(--ease-out);
}
/* Shown as their owners drew them — no greyscale, no recolouring. */
.client img {
  /* height comes from the balanced block at the end of this file */
  width: auto;
  max-width: 264px;
  object-fit: contain;
  transition: transform var(--t) var(--ease-out);
}
/* Two of these marks are drawn in flat black for white paper and vanish on our
   ground. Both are monochrome, so inverting yields exactly the white version
   their own brand guides call for — not an approximation of one.
   Nothing with colour in it is touched: inverting Bürkle's red would make it
   cyan. Bürkle and Zander already ship white artwork, and EGU publish a white
   version which is the file used here. */
.client img[data-invert] {
  filter: invert(1);
}
/* The Google rating ---------------------------------------------------------
   Five stars from a few dozen tradespeople is the most persuasive line on this
   page. Drawn from our own copy of the number — never an embedded widget. */
.rating {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  margin-top: 14px;
  padding: 0 14px;
  min-height: 38px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--surface);
  color: var(--fg-muted);
  font-size: 13px;
  transition: border-color var(--t) var(--ease-out), background var(--t) var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  a.rating:hover {
    border-color: var(--line-strong);
    background: var(--surface-2);
  }
}
.rating__stars {
  color: var(--accent);
  letter-spacing: 0.08em;
}
.rating__score {
  color: var(--fg);
  font-weight: 600;
}

.client__word {
  font-size: clamp(15px, 1.5vw, 18px);
  font-weight: 600;
  letter-spacing: 0.04em;
  color: #9a9a9a;
  text-align: center;
  transition: transform var(--t) var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .client:hover {
    border-color: var(--line-strong);
  }
  /* The mark grows inside its own box; the box itself never moves, so the row
     stays put and only the one under the cursor answers. */
  .client:hover img,
  .client:hover .client__word {
    transform: scale(1.09);
  }
  .client:hover img[src*='gautzsch'],
  .client:hover img[src*='schutzer'] {
    filter: invert(1);
  }
}

@media (max-width: 720px) {
  /* two per row on phones, so a longer partner list stays compact */
  .client {
    min-height: 96px;
    padding: 16px;
  }
  .client:nth-child(2n) {
    border-right: 0;
  }
  .client:not(:nth-last-child(-n + 2)) {
    border-bottom: 1px solid var(--line);
  }
}

/* ---------------------------------------------------------------- voices -- */

.voices {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 14px;
}
.voice {
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
  padding: 22px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.voice__stars {
  color: var(--accent-text);
  letter-spacing: 3px;
  font-size: 13px;
}
.voice__text {
  color: var(--fg-muted);
  font-size: 15px;
  flex: 1;
}
/* The role stands alone where the name used to be: it carries the attribution
   now, so it gets the rule above it that the name block had. */
.voice__role {
  margin-top: auto;
  padding-top: 14px;
  border-top: 1px solid var(--line);
  font-size: 12px;
  color: var(--fg-dim);
}

.tag-demo {
  display: inline-flex;
  align-items: center;
  height: 20px;
  padding: 0 8px;
  border: 1px dashed var(--line-strong);
  border-radius: var(--r-sm);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--fg-dim);
}

/* ------------------------------------------------------------------ news -- */

.news {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 14px;
}
.post {
  text-align: left;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
  padding: 22px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  min-height: 190px;
  transition: border-color var(--t) var(--ease-out), background var(--t) var(--ease-out),
    transform var(--t-fast) var(--ease-out);
}
.post:active {
  transform: scale(0.985);
}
.post__meta {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--fg-dim);
}
.post__title {
  font-size: 17px;
  font-weight: 600;
  line-height: 1.35;
}
.post__teaser {
  font-size: 14px;
  color: var(--fg-muted);
  flex: 1;
}
.post__more {
  font-size: 13px;
  color: var(--accent-text);
  display: inline-flex;
  gap: 8px;
  align-items: center;
}
@media (hover: hover) and (pointer: fine) {
  .post:hover {
    border-color: var(--line-strong);
    background: var(--surface-2);
  }
}

/* --------------------------------------------------------------- contact -- */

.contact {
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr);
  gap: clamp(24px, 4vw, 56px);
  align-items: start;
}

.switch {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
  margin-bottom: 22px;
}
.switch button {
  text-align: left;
  border: 1px solid var(--line);
  border-radius: var(--r);
  padding: 14px 16px;
  transition: border-color var(--t) var(--ease-out), background var(--t) var(--ease-out),
    transform var(--t-fast) var(--ease-out);
}
.switch button:active {
  transform: scale(0.98);
}
.switch button[aria-pressed='true'] {
  border-color: var(--accent-text);
  background: rgba(251, 130, 75, 0.08);
}
.switch__name {
  font-size: 14px;
  font-weight: 600;
  display: flex;
  align-items: center;
  gap: 8px;
}
/* Three lines, so three blocks. As inline spans they ran together — the
   address ended glued to the hint — and their margins were ignored, because an
   inline box has no vertical margin to give. */
.switch__mail {
  display: block;
  font-size: 12px;
  color: var(--accent-text);
  margin-top: 2px;
}
.switch__hint {
  display: block;
  font-size: 12px;
  color: var(--fg-dim);
  margin-top: 6px;
}

.field {
  margin-bottom: 14px;
}
.field label {
  display: block;
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-dim);
  margin-bottom: 6px;
}
.field input,
.field textarea {
  width: 100%;
  font: inherit;
  font-size: 15px;
  color: var(--fg);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r);
  padding: 12px 14px;
  min-height: 46px;
  transition: border-color var(--t) var(--ease-out), background var(--t) var(--ease-out);
}
.field textarea {
  min-height: 120px;
  resize: vertical;
}
.field input:focus,
.field textarea:focus {
  outline: none;
  border-color: var(--accent-text);
  background: var(--surface-2);
}

.form__row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px;
}
.form__note {
  font-size: 12px;
  line-height: 1.6;
  color: var(--fg-dim);
  margin-top: 12px;
}
/* The note answers the submit, so it has to read as a response rather than as
   the same small print it was a moment ago — the dashed rule is what the
   sign-in page marks its own "nothing happened" with. */
.form__note[data-alert='true'] {
  color: var(--fg-muted);
  padding: 12px 14px;
  border: 1px dashed var(--accent-text);
  border-radius: var(--r-sm);
}
.form__note a {
  color: var(--accent-text);
}

.office {
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
  padding: 24px;
}
.office__title {
  font-family: 'Zen Dots', sans-serif;
  font-size: 18px;
  text-transform: uppercase;
  margin-bottom: 18px;
}
.office__label {
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--fg-dim);
  margin-bottom: 8px;
}
.office__addr {
  color: var(--fg-muted);
  font-size: 15px;
  margin-bottom: 22px;
  font-style: normal;
}
.hours {
  width: 100%;
  border-collapse: collapse;
  font-size: 14px;
}
.hours td {
  padding: 9px 0;
  border-top: 1px solid var(--line);
  font-variant-numeric: tabular-nums;
}
.hours td:last-child {
  text-align: right;
  color: var(--fg-muted);
}

/* One label, one value — the numbers line up rather than running as prose. */
.office__line {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  padding: 6px 0;
  border-bottom: 1px solid var(--line);
}
.office__line:first-of-type {
  border-top: 1px solid var(--line);
}
.office__line > span {
  color: var(--fg-dim);
  font-size: 13px;
}
.office__line a {
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* ---------------------------------------------------------------- social -- */

.social {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.social__link {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 40px;
  padding: 0 14px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  font-size: 13px;
  color: var(--fg-muted);
  transition: border-color var(--t) var(--ease-out), color var(--t) var(--ease-out),
    transform var(--t-fast) var(--ease-out);
}
.social__link:active {
  transform: scale(0.97);
}
.social__link svg {
  flex: 0 0 auto;
  opacity: 0.75;
  transition: opacity var(--t) var(--ease-out);
}
.social__legacy {
  opacity: 0.4;
  cursor: not-allowed;
}
@media (hover: hover) and (pointer: fine) {
  .social__link:hover {
    border-color: var(--accent-text);
    color: var(--fg);
  }
  .social__link:hover svg {
    opacity: 1;
  }
}

/* -------------------------------------------------------------- channels -- */

/*
 * Where the shop call to action used to close the page. Full tiles rather than a
 * row of glyphs: this is the one block asking for something other than a click
 * through to the shop, so it gets the room to say which platform and under what
 * name.
 */
/* Fixed columns rather than auto-fit: with exactly six channels, auto-fit gave
   four and then a ragged two, which reads as a block that ran out rather than
   one that was laid out. */
.channels {
  display: grid;
  grid-template-columns: 1fr;
  gap: 12px;
}
@media (min-width: 620px) {
  .channels {
    grid-template-columns: repeat(2, 1fr);
  }
  /* An odd count leaves the last tile alone on its row. It spans instead, so the
     block ends on a straight edge however many channels there turn out to be. */
  .channel:last-child:nth-child(2n + 1) {
    grid-column: span 2;
  }
}
@media (min-width: 1000px) {
  .channels {
    grid-template-columns: repeat(3, 1fr);
  }
  .channel:last-child:nth-child(2n + 1) {
    grid-column: auto;
  }
  .channel:last-child:nth-child(3n + 1) {
    grid-column: span 3;
  }
  .channel:last-child:nth-child(3n + 2) {
    grid-column: span 2;
  }
}

.channel {
  display: grid;
  grid-template-columns: auto 1fr auto;
  grid-template-rows: auto auto;
  align-items: center;
  gap: 2px 16px;
  padding: 22px;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
  transition: border-color var(--t) var(--ease-out), background var(--t) var(--ease-out),
    transform var(--t-fast) var(--ease-out);
}
.channel:active {
  transform: scale(0.99);
}

/* Every cell is placed explicitly. Grid places items with a definite row before
   the auto ones, so leaving the name and handle to find their own column put the
   arrow second and the name last. */
.channel__mark {
  grid-column: 1;
  grid-row: 1 / 3;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  /* Falls back to the foreground for TikTok and X, whose own colour is black. */
  color: var(--brand, var(--fg));
  border: 1px solid var(--line);
  border-radius: var(--r);
  transition: border-color var(--t) var(--ease-out), background var(--t) var(--ease-out);
}
.channel__mark svg {
  width: 22px;
  height: 22px;
}

.channel__name {
  grid-column: 2;
  grid-row: 1;
  font-size: 15px;
  font-weight: 600;
}
/* The handle is shown because the naming is inconsistent across platforms, and a
   reader looking for us should see which account they are about to open. */
.channel__handle {
  grid-column: 2;
  grid-row: 2;
  font-size: 12px;
  color: var(--fg-dim);
}
.channel__go {
  grid-column: 3;
  grid-row: 1 / 3;
  color: var(--fg-dim);
  transition: color var(--t) var(--ease-out), transform var(--t) var(--ease-out);
}

@media (hover: hover) and (pointer: fine) {
  .channel:hover {
    border-color: var(--line-strong);
    background: var(--surface-2);
  }
  .channel:hover .channel__mark {
    border-color: var(--accent-text);
    color: var(--accent-text);
  }
  .channel:hover .channel__go {
    color: var(--accent-text);
    transform: translateX(3px);
  }
}

/* ------------------------------------------------------------------- cta -- */

.cta {
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: linear-gradient(150deg, var(--surface-2), var(--surface));
  padding: clamp(32px, 5vw, 64px);
  text-align: center;
  position: relative;
  overflow: hidden;
}
.cta__title {
  font-size: clamp(24px, 3.4vw, 40px);
  margin-bottom: 14px;
}
.cta__lead {
  color: var(--fg-muted);
  max-width: 52ch;
  margin: 0 auto 26px;
}

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

.footer {
  border-top: 1px solid var(--line);
  padding-block: 46px 34px;
}
.footer__top {
  display: flex;
  flex-wrap: wrap;
  gap: 28px;
  justify-content: space-between;
  align-items: flex-start;
  margin-bottom: 34px;
}
.footer__claim {
  color: var(--fg-dim);
  font-size: 13px;
  max-width: 34ch;
  margin-top: 12px;
}
.footer__bottom {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  justify-content: space-between;
  padding-top: 22px;
  border-top: 1px solid var(--line);
  font-size: 12px;
  color: var(--fg-dim);
}

/* ----------------------------------------------------------------- modal -- */

.modal {
  position: fixed;
  inset: 0;
  z-index: 150;
  display: grid;
  place-items: center;
  padding: clamp(12px, 3vw, 40px);
}
.modal[hidden] {
  display: none;
}

.modal__scrim {
  position: absolute;
  inset: 0;
  background: var(--scrim);
  backdrop-filter: blur(3px);
  opacity: 0;
  transition: opacity var(--t) var(--ease-out);
}
.modal[data-open='true'] .modal__scrim {
  opacity: 1;
}

.modal__panel {
  position: relative;
  width: min(1080px, 100%);
  max-height: min(86vh, 900px);
  overflow: hidden;
  background: var(--bg);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-lg);
  opacity: 0;
  transform: scale(0.96) translateY(8px);
  transition: opacity var(--t) var(--ease-out), transform var(--t) var(--ease-out);
}
.modal[data-open='true'] .modal__panel {
  opacity: 1;
  transform: scale(1) translateY(0);
}
/* exit is faster than enter */
.modal[data-open='false'] .modal__panel,
.modal[data-open='false'] .modal__scrim {
  transition-duration: var(--t-fast);
}

.modal__close {
  position: absolute;
  top: 12px;
  right: 12px;
  z-index: 2;
  width: 40px;
  height: 40px;
  display: grid;
  place-items: center;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--veil-strong);
  transition: border-color var(--t) var(--ease-out), transform var(--t-fast) var(--ease-out);
}
.modal__close:active {
  transform: scale(0.94);
}
@media (hover: hover) and (pointer: fine) {
  .modal__close:hover {
    border-color: var(--fg);
  }
}

.detail {
  display: grid;
  grid-template-columns: minmax(0, 0.95fr) minmax(0, 1.05fr);
  /* definite, not max-height: a capped auto row still measures its content, so
     the columns would size to the specifications and overflow the panel */
  height: min(86vh, 900px);
}
/* min-height:0 is what lets a grid child scroll instead of stretching the row */
.detail__media {
  display: grid;
  grid-template-rows: minmax(0, 1fr);
  min-height: 420px;
  border-right: 1px solid var(--line);
}
.detail__info {
  padding: 28px 26px;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* ------------------------------------------------------------------ gallery */

.gallery {
  display: grid;
  grid-template-rows: minmax(0, 1fr) auto;
  min-height: 0;
}
.gallery__stage {
  position: relative;
  min-height: 0;
  overflow: hidden;
  transition: background var(--t) var(--ease-out);
}
.gallery__stage[data-tone='light'] {
  background: var(--plate-light);
}
.gallery__stage[data-tone='dark'] {
  background: var(--plate-dark);
}
.gallery__shot {
  position: absolute;
  inset: 24px;
  width: calc(100% - 48px);
  height: calc(100% - 48px);
  object-fit: contain;
  opacity: 0;
  transition: opacity var(--t) var(--ease-out);
}
.gallery__shot[data-on] {
  opacity: 1;
}

.gallery__step {
  position: absolute;
  top: 50%;
  translate: 0 -50%;
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border: 1px solid var(--line-strong);
  border-radius: 50%;
  background: var(--bg);
  color: var(--fg);
  cursor: pointer;
  opacity: 0;
  transition: opacity var(--t) var(--ease-out), border-color var(--t) var(--ease-out);
}
.gallery__step[data-step='-1'] {
  left: 14px;
  rotate: 180deg;
}
.gallery__step[data-step='1'] {
  right: 14px;
}
/* the arrows are a shortcut, not the only way through — the strip always shows */
.gallery:hover .gallery__step,
.gallery__step:focus-visible {
  opacity: 1;
}
@media (hover: none) {
  .gallery__step {
    opacity: 1;
  }
}
@media (hover: hover) and (pointer: fine) {
  .gallery__step:hover {
    border-color: var(--fg);
  }
}

.gallery__count {
  position: absolute;
  right: 14px;
  bottom: 12px;
  /* the plate is light under some shots and dark under others, so the counter
     carries its own ground rather than depending on either */
  padding: 4px 10px;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  background: var(--bg);
  font-size: 11px;
  letter-spacing: 0.08em;
  font-variant-numeric: tabular-nums;
  color: var(--fg-muted);
}

.gallery__strip {
  display: flex;
  gap: 8px;
  padding: 12px 14px;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  border-top: 1px solid var(--line);
}

/* --------------------------------------------------- article videos (popup) */

.clips {
  display: grid;
  gap: 8px;
}
.clip {
  display: grid;
  grid-template-columns: 96px minmax(0, 1fr);
  gap: 12px;
  align-items: center;
  padding: 8px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: none;
  text-align: start;
  cursor: pointer;
  transition: border-color var(--t) var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .clip:hover {
    border-color: var(--line-strong);
  }
}
.clip__thumb {
  display: grid;
  place-items: center;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  border-radius: var(--r-sm);
  background: var(--plate-dark);
}
.clip__thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.clip__icon {
  color: var(--fg-dim);
}
.clip__text {
  display: grid;
  gap: 3px;
}
.clip__title {
  font-size: 13px;
  color: var(--fg);
}
.clip__meta {
  font-size: 11px;
  letter-spacing: 0.06em;
  color: var(--fg-dim);
}
.clip[data-state='planned'] .clip__title {
  color: var(--fg-muted);
}

/* ---------------------------------------------------------------- scrollbars

   The platform default is a white trough with a grey thumb — bright enough to
   read as part of the page on a near-black panel. These match the surface they
   sit on: no trough, a thumb the weight of a hairline rule, firmer on hover. */

.detail__info,
.gallery__strip {
  scrollbar-width: thin;
  scrollbar-color: var(--line-strong) transparent;
}
.detail__info::-webkit-scrollbar,
.gallery__strip::-webkit-scrollbar {
  width: 10px;
  height: 10px;
}
.detail__info::-webkit-scrollbar-track,
.gallery__strip::-webkit-scrollbar-track {
  background: transparent;
}
.detail__info::-webkit-scrollbar-thumb,
.gallery__strip::-webkit-scrollbar-thumb {
  border: 3px solid transparent;      /* inset the thumb without a second element */
  background-clip: content-box;
  background-color: var(--line-strong);
  border-radius: 999px;
}
@media (hover: hover) and (pointer: fine) {
  .detail__info::-webkit-scrollbar-thumb:hover,
  .gallery__strip::-webkit-scrollbar-thumb:hover {
    background-color: var(--fg-dim);
  }
}
.gallery__thumb {
  flex: 0 0 auto;
  width: 60px;
  height: 60px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  overflow: hidden;
  cursor: pointer;
  opacity: 0.55;
  transition: opacity var(--t) var(--ease-out), border-color var(--t) var(--ease-out);
}
.gallery__thumb[data-tone='light'] {
  background: var(--plate-light);
}
.gallery__thumb[data-tone='dark'] {
  background: var(--plate-dark);
}
.gallery__thumb img {
  width: 100%;
  height: 100%;
  object-fit: contain;
}
.gallery__thumb[aria-selected='true'] {
  opacity: 1;
  border-color: var(--accent-text);
}
@media (hover: hover) and (pointer: fine) {
  .gallery__thumb:hover {
    opacity: 1;
  }
}
.detail__name {
  font-size: clamp(20px, 2.6vw, 28px);
  margin-block: 10px 6px;
  font-variant-numeric: tabular-nums;
}
.detail__system {
  font-size: 12px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--accent-text);
}

.subhead {
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--fg-dim);
  margin-block: 24px 10px;
}

.specs {
  width: 100%;
  border-collapse: collapse;
  font-size: 14px;
}
.specs th,
.specs td {
  text-align: left;
  padding: 10px 0;
  border-top: 1px solid var(--line);
  vertical-align: top;
}
.specs th {
  font-weight: 400;
  color: var(--fg-dim);
  width: 45%;
}
.specs td {
  font-variant-numeric: tabular-nums;
}

.files {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.file {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 42px;
  padding: 0 12px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  font-size: 13px;
  color: var(--fg-muted);
  transition: border-color var(--t) var(--ease-out), color var(--t) var(--ease-out);
}
.file__ext {
  font-size: 10px;
  letter-spacing: 0.08em;
  color: var(--fg-dim);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  padding: 2px 6px;
}
.file__size {
  margin-left: auto;
  font-size: 12px;
  color: var(--fg-dim);
  font-variant-numeric: tabular-nums;
}
@media (hover: hover) and (pointer: fine) {
  .file:hover {
    border-color: var(--line-strong);
    color: var(--fg);
  }
}

/* news modal */
/* The panel stopped scrolling when the product gallery got a fixed column, so a
   post has to carry its own scroll — otherwise a long article is simply cut off
   at the bottom of the panel with no way to reach the rest. */
.article {
  display: grid;
  grid-template-rows: auto minmax(0, 1fr);
  max-height: min(86vh, 900px);
}
.article__inner {
  min-height: 0;
  padding: 30px 28px;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  scrollbar-color: var(--line-strong) transparent;
}
.article__inner::-webkit-scrollbar {
  width: 10px;
}
.article__inner::-webkit-scrollbar-track {
  background: transparent;
}
.article__inner::-webkit-scrollbar-thumb {
  border: 3px solid transparent;
  background-clip: content-box;
  background-color: var(--line-strong);
  border-radius: 999px;
}
.article__body {
  color: var(--fg-muted);
  margin-top: 16px;
}
.article__nav {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  margin-top: 30px;
  padding-top: 20px;
  border-top: 1px solid var(--line);
}
.article__nav button:disabled {
  opacity: 0.3;
  cursor: not-allowed;
}

/* --------------------------------------------------------------- reveal -- */

[data-reveal] {
  opacity: 0;
  transform: translateY(14px);
}
[data-reveal].is-in {
  opacity: 1;
  transform: none;
  transition: opacity 520ms var(--ease-out), transform 520ms var(--ease-out);
}

/* ------------------------------------------------------------ responsive -- */

@media (max-width: 1080px) {
  .hero__inner,
  .system__head,
  .contact,
  .detail {
    grid-template-columns: 1fr;
  }
  .hero {
    min-height: 0;
  }
  .hero__inner {
    display: flex;
    flex-direction: column;
  }
  .detail {
    grid-template-columns: 1fr;
    grid-template-rows: auto minmax(0, 1fr);
  }
  .detail__media {
    border-right: 0;
    border-bottom: 1px solid var(--line);
    min-height: 0;
    height: 46vh;
  }
  .detail__info {
    min-height: 0;
  }
  .gallery__thumb {
    width: 48px;
    height: 48px;
  }
  .stats__inner {
    grid-template-columns: repeat(2, 1fr);
  }
  .stat:nth-child(3) {
    border-left: 0;
  }
  .stat:nth-child(n + 3) {
    border-top: 1px solid var(--line);
  }
  .academy {
    grid-template-columns: 1fr;
  }
  .hero__panel {
    display: none;
  }
}

/* ------------------------------------------------------------ mobile nav -- */

.burger {
  display: none;
  width: 44px;
  height: 44px;
  place-items: center;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  transition: border-color var(--t) var(--ease-out), transform var(--t-fast) var(--ease-out);
}
.burger:active {
  transform: scale(0.94);
}
.burger span,
.burger span::before,
.burger span::after {
  display: block;
  width: 16px;
  height: 1.5px;
  background: currentColor;
  transition: transform var(--t) var(--ease-out), opacity var(--t) var(--ease-out);
}
.burger span::before,
.burger span::after {
  content: '';
  position: absolute;
}
.burger span::before {
  transform: translateY(-5px);
}
.burger span::after {
  transform: translateY(5px);
}
.burger[aria-expanded='true'] span {
  background: transparent;
}
.burger[aria-expanded='true'] span::before {
  transform: rotate(45deg);
}
.burger[aria-expanded='true'] span::after {
  transform: rotate(-45deg);
}

/* Touch pointers get the full 44px target; precise pointers keep the tighter
   rhythm the technical look depends on. Raising the token rather than listing
   the header controls again is what keeps the row level — the theme button was
   added later and missed the list, so it stood 4px short of its neighbours. */
@media (pointer: coarse) {
  :root {
    --control-h: 44px;
  }
  .btn--sm,
  .doc,
  .file,
  .social a,
  .social span {
    min-height: 44px;
  }
}

@media (max-width: 720px) {
  .burger {
    display: grid;
    position: relative;
  }
  .nav {
    position: fixed;
    inset: var(--header-h) 0 auto;
    flex-direction: column;
    gap: 0;
    margin: 0;
    padding: 8px var(--gutter) 20px;
    background: var(--veil-solid);
    backdrop-filter: blur(14px);
    border-bottom: 1px solid var(--line);
    transform: translateY(-8px);
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--t) var(--ease-out), transform var(--t) var(--ease-out);
  }
  .nav[data-open='true'] {
    transform: none;
    opacity: 1;
    pointer-events: auto;
  }
  .nav a {
    /* same correction, added to the phone menu's own padding */
    padding: 15px 0 15px;
    font-size: 16px;
    color: var(--fg);
    border-bottom: 1px solid var(--line);
  }
  .nav a::after {
    display: none;
  }
  .header__inner {
    justify-content: space-between;
    gap: 12px;
  }
  #header-cta {
    display: none;
  }
  .grid {
    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  }
  .form__row,
  .switch {
    grid-template-columns: 1fr;
  }
  .hero__actions .btn {
    width: 100%;
  }
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
  [data-reveal] {
    opacity: 1;
    transform: none;
  }
  .modal__panel {
    transform: none;
  }
}

/* ==========================================================================
   Additions: assistant, line cards, promo gallery, news covers, marquee,
   line masthead and filters.
   ========================================================================== */

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

.section--flush {
  padding-top: clamp(30px, 3.6vw, 52px);
  padding-bottom: clamp(48px, 6vw, 76px);
}
.section__lead {
  margin-top: 14px;
  color: var(--fg-muted);
  max-width: 62ch;
}

/* ------------------------------------------------------- technical assistant */

.chat {
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: linear-gradient(160deg, var(--surface-2), var(--surface));
  padding: clamp(20px, 3vw, 30px);
  position: relative;
  overflow: hidden;
}
.chat::before {
  content: '';
  position: absolute;
  inset: 0 auto auto 0;
  width: 100%;
  height: 1px;
  background: linear-gradient(90deg, var(--accent), transparent 42%);
}

.chat__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 12px;
}
.chat__badge {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent-text);
}
.chat__status {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 12px;
  color: var(--fg-dim);
}
.chat__status::before {
  content: '';
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--fg-dim);
}
.chat__status[data-online='true']::before {
  background: #5fd08a;
}

.chat__lead {
  color: var(--fg-muted);
  max-width: 68ch;
  margin-bottom: 18px;
}

.chat__panel {
  display: none;
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--bg);
  margin-bottom: 12px;
}
.chat__panel[data-active='true'] {
  display: block;
}
.chat__log {
  max-height: 340px;
  overflow-y: auto;
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 14px;
  scroll-behavior: smooth;
}

.msg {
  display: flex;
  flex-direction: column;
  gap: 6px;
  max-width: 74ch;
}
.msg--user {
  align-self: flex-end;
  align-items: flex-end;
}
.msg--user .msg__body {
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r) var(--r) var(--r-sm) var(--r);
  padding: 10px 14px;
  font-size: 15px;
}
.msg__who {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--accent-text);
}
.msg--assistant .msg__body {
  font-size: 15px;
  color: var(--fg);
  white-space: pre-wrap;
  line-height: 1.62;
}
.msg[data-error='true'] .msg__body {
  color: var(--fg-muted);
}

/* three-dot wait state, only while nothing has streamed yet */
.msg[data-pending='true'] .msg__body::after {
  content: '';
  display: inline-block;
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: var(--fg-dim);
  box-shadow: 8px 0 var(--fg-dim), 16px 0 var(--fg-dim);
  animation: pulse 1.1s var(--ease-in-out) infinite;
}
@keyframes pulse {
  0%, 100% { opacity: 0.35; }
  50% { opacity: 1; }
}

.chat__form {
  display: flex;
  gap: 8px;
}
.chat__form input {
  flex: 1;
  min-width: 0;
  font: inherit;
  font-size: 15px;
  color: var(--fg);
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--r);
  padding: 0 16px;
  min-height: 50px;
  transition: border-color var(--t) var(--ease-out);
}
.chat__form input:focus {
  outline: none;
  border-color: var(--accent-text);
}
.chat__form input:disabled {
  opacity: 0.5;
}
.chat__form .btn {
  min-height: 50px;
}

.chat__suggestions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 12px;
}
.chat__note {
  margin-top: 14px;
  font-size: 12px;
  color: var(--fg-dim);
  max-width: 74ch;
}

.chip {
  display: inline-flex;
  align-items: center;
  justify-content: flex-start;
  text-align: left;
  gap: 8px;
  min-height: 34px;
  padding: 0 12px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  font-size: 13px;
  color: var(--fg-muted);
  transition: border-color var(--t) var(--ease-out), color var(--t) var(--ease-out),
    background var(--t) var(--ease-out), transform var(--t-fast) var(--ease-out);
}
.chip:active {
  transform: scale(0.97);
}
.chip[aria-pressed='true'] {
  border-color: var(--accent-text);
  color: var(--fg);
  background: rgba(251, 130, 75, 0.1);
}
.chip:disabled {
  opacity: 0.34;
  cursor: default;
}

.chip__n {
  font-size: 11px;
  line-height: 1;
  color: var(--fg-dim);
  font-variant-numeric: tabular-nums;
}
@media (hover: hover) and (pointer: fine) {
  .chip:hover {
    border-color: var(--line-strong);
    color: var(--fg);
  }
}

/* --------------------------------------------------------------- line cards */

.lines {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 14px;
}
.lines--compact {
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
}

.line {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
  overflow: hidden;
  transition: border-color var(--t) var(--ease-out), background var(--t) var(--ease-out),
    transform var(--t-fast) var(--ease-out);
}
.line:active {
  transform: scale(0.99);
}
.line__media {
  position: relative;
  aspect-ratio: 16 / 11;
  overflow: hidden;
}
.line--compact .line__media {
  aspect-ratio: 16 / 9;
}
.line__media[data-tone='light'] {
  background: var(--plate-light);
}
.line__media[data-tone='dark'] {
  background: var(--plate-dark);
}
.line__media img {
  position: absolute;
  inset: 14px;
  width: calc(100% - 28px);
  height: calc(100% - 28px);
  object-fit: contain;
  transition: transform var(--t-slow) var(--ease-out);
}
.line__body {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 18px;
  border-top: 1px solid var(--line);
  flex: 1;
}
.line__badges {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.line__name {
  font-family: 'Zen Dots', sans-serif;
  font-size: clamp(20px, 2.4vw, 27px);
  text-transform: uppercase;
  letter-spacing: 0.02em;
}
.line__lead {
  font-size: 14px;
  color: var(--fg-muted);
  flex: 1;
}
.line__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding-top: 12px;
  border-top: 1px solid var(--line);
  font-size: 12px;
  color: var(--fg-dim);
}
.line__go {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: var(--accent-text);
  font-weight: 600;
}
.line__go svg {
  transition: transform var(--t) var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .line:hover {
    border-color: var(--line-strong);
    background: var(--surface-2);
  }
  .line:hover .line__media img {
    transform: scale(1.05);
  }
  .line:hover .line__go svg {
    transform: translateX(3px);
  }
}

/* ----------------------------------------------------------- promo gallery */

.promo {
  display: grid;
  /* auto-fill, not auto-fit: a single document must stay a card, not stretch
     across the whole band. Empty tracks simply collapse. */
  grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
  gap: 14px;
}
/*
 * The card is the way in: a stretched link covers it so the target is the whole
 * card rather than a 90px "Ansehen" next to it. The download stays a separate
 * control and has to sit above the link, or it would be swallowed by it.
 */
.promo__item {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 14px;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
  padding: 18px;
  transition: border-color var(--t) var(--ease-out), background var(--t) var(--ease-out);
}
.promo__sheet {
  position: relative;
  display: grid;
  place-items: center;
  aspect-ratio: 3 / 4;
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: linear-gradient(155deg, var(--surface-2), var(--bg));
  overflow: hidden;
}
.promo__sheet img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top center;
  transition: transform var(--t-slow) var(--ease-out);
}
/* Landscape documents (the A3 flyer spreads) get a wider tile and are shown
   whole — cropping a spread to a portrait frame hides most of the artwork. */
.promo__sheet[data-format='landscape'] {
  aspect-ratio: 4 / 3;
}
.promo__sheet[data-format='landscape'] img {
  object-fit: contain;
  object-position: center;
  padding: 10px;
}
@media (hover: hover) and (pointer: fine) {
  .promo__item:hover .promo__sheet img {
    transform: scale(1.03);
  }
}
/* folded corner for the fallback tile only */
.promo__sheet:not(:has(img))::after {
  content: '';
  position: absolute;
  top: 0;
  right: 0;
  border-width: 0 20px 20px 0;
  border-style: solid;
  border-color: var(--line) var(--bg);
}
.promo__ext {
  font-family: 'Zen Dots', sans-serif;
  font-size: 15px;
  letter-spacing: 0.1em;
  color: var(--fg-dim);
}

/*
 * On a phone the cover fills most of the screen and pushes the four cards it
 * belongs to a scroll apart. The card is still the link, so nothing is lost by
 * dropping the picture and keeping what the document actually is.
 */
@media (max-width: 700px) {
  .promo__sheet {
    display: none;
  }
  .promo__item {
    gap: 10px;
  }
}
.promo__info {
  display: flex;
  flex-direction: column;
  gap: 6px;
  flex: 1;
}
.promo__kind {
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--accent-text);
}
.promo__title {
  font-size: 15px;
  font-weight: 600;
}
.promo__meta {
  font-size: 12px;
  color: var(--fg-dim);
  font-variant-numeric: tabular-nums;
}
.promo__link {
  position: absolute;
  inset: 0;
  z-index: 1;
  border-radius: inherit;
}

.promo__actions {
  position: relative;
  z-index: 2;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: auto;
}

/* The whole card reacts, not just the link, so it reads as one target. */
@media (hover: hover) and (pointer: fine) {
  .promo__item:has(.promo__link):hover {
    border-color: var(--line-strong);
    background: var(--surface-2);
  }
}
.promo__item:has(.promo__link:focus-visible) {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.promo__link:focus-visible {
  outline: none;
}
.doc[aria-disabled='true'] {
  opacity: 0.4;
  cursor: not-allowed;
}
@media (hover: hover) and (pointer: fine) {
  .promo__item:hover {
    border-color: var(--line-strong);
    background: var(--surface-2);
  }
}

/* ------------------------------------------------------------- news covers */

.post {
  padding: 0;
  overflow: hidden;
  gap: 0;
}
.post__cover {
  position: relative;
  aspect-ratio: 16 / 10;
  overflow: hidden;
  display: block;
}
.post__cover[data-tone='light'] {
  background: var(--plate-light);
}
.post__cover[data-tone='dark'] {
  background: var(--plate-dark);
}
.post__cover img {
  position: absolute;
  inset: 10px;
  width: calc(100% - 20px);
  height: calc(100% - 20px);
  object-fit: contain;
  transition: transform var(--t-slow) var(--ease-out);
}
.post__body {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 18px;
  border-top: 1px solid var(--line);
  flex: 1;
}
@media (hover: hover) and (pointer: fine) {
  .post:hover .post__cover img {
    transform: scale(1.04);
  }
}

.article__cover {
  position: relative;
  aspect-ratio: 21 / 9;
  border-bottom: 1px solid var(--line);
}
.article__cover[data-tone='light'] {
  background: var(--plate-light);
}
.article__cover[data-tone='dark'] {
  background: var(--plate-dark);
}
.article__cover img {
  position: absolute;
  inset: 16px;
  width: calc(100% - 32px);
  height: calc(100% - 32px);
  object-fit: contain;
}
/* ------------------------------------------------------------ voice marquee */

.voices-band {
  padding-bottom: clamp(56px, 7vw, 92px);
}
.marquee {
  /* A touch wider than the grid, not edge to edge. Running the full width of a
     large monitor made both bands read as a separate site behind the page; a
     small overhang keeps them related to the columns without lining up with them.
     Below that width they simply fill what there is. */
  max-width: calc(var(--maxw) + 260px);
  margin-inline: auto;
  overflow: hidden;
  padding-block: 4px;
  mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
}
.marquee__track {
  display: flex;
  gap: 14px;
  width: max-content;
  padding-inline: 7px;
  animation: marquee var(--marquee-duration, 44s) linear infinite;
}
@keyframes marquee {
  to {
    transform: translateX(-50%);
  }
}
.marquee .voice {
  width: min(380px, 82vw);
  flex-shrink: 0;
}
.voice__top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}
/* pause on hover and while a card inside has keyboard focus */
@media (hover: hover) and (pointer: fine) {
  .marquee:hover .marquee__track {
    animation-play-state: paused;
  }
}
.marquee__track:focus-within {
  animation-play-state: paused;
}

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

.masthead {
  position: relative;
  overflow: hidden;
  padding-block: calc(var(--header-h) + clamp(40px, 6vw, 76px)) clamp(40px, 5vw, 64px);
  border-bottom: 1px solid var(--line);
}
.masthead__inner {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr);
  gap: clamp(28px, 4vw, 64px);
  align-items: center;
}
.masthead__name {
  font-size: clamp(38px, 6vw, 76px);
  margin-block: 14px 12px;
}
.masthead__lead {
  font-size: clamp(16px, 1.8vw, 21px);
  font-weight: 500;
  max-width: 40ch;
  margin-bottom: 14px;
}

.crumb {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  color: var(--fg-dim);
  margin-bottom: 18px;
  transition: color var(--t) var(--ease-out);
}
.crumb svg {
  transform: rotate(180deg);
  transition: transform var(--t) var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .crumb:hover {
    color: var(--fg);
  }
  .crumb:hover svg {
    transform: rotate(180deg) translateX(3px);
  }
}

.filters {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 10px;
}

@media (max-width: 1080px) {
  .masthead__inner {
    grid-template-columns: 1fr;
  }
  .masthead .hero__panel {
    display: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .marquee {
    overflow-x: auto;
    mask-image: none;
    -webkit-mask-image: none;
  }
  .marquee__track {
    animation: none;
  }
  .msg[data-pending='true'] .msg__body::after {
    animation: none;
  }
}

/* assistant message formatting */
.msg--assistant .msg__body {
  white-space: normal;
}
.msg__body p + p {
  margin-top: 10px;
}
.msg__body ul {
  margin: 10px 0 0;
  padding-left: 18px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.msg__body li {
  padding-left: 2px;
}
.msg__body li::marker {
  color: var(--accent-text);
}
.msg__body strong {
  font-weight: 600;
  color: var(--fg);
}

/* Touch sizing for components defined after the earlier coarse-pointer block —
   kept at the end of the file so it wins the cascade. */
@media (pointer: coarse) {
  .chip {
    min-height: 44px;
  }
}

/* Promo actions sit side by side in a narrow card — the default doc padding
   overflows the tile width by a few pixels and wraps them into a column. */
.promo__actions .doc {
  padding: 0 10px;
  gap: 7px;
}

/* ==========================================================================
   Hero — Balter Academy reels
   ========================================================================== */

/* Warm light rising from behind the cards. Kept to a single soft ellipse: the
   brand uses orange as an accent, so it lifts the composition without becoming
   a background wash. */
.hero__glow {
  position: absolute;
  left: 50%;
  /* Sized to spill past the card fan on all sides — behind opaque cards a
     tighter glow is simply invisible. */
  bottom: -12%;
  width: min(1560px, 140vw);
  height: min(680px, 74vh);
  transform: translateX(-50%);
  background: radial-gradient(
    ellipse at 50% 92%,
    rgba(251, 130, 75, 0.42) 0%,
    rgba(251, 130, 75, 0.22) 26%,
    rgba(251, 130, 75, 0.08) 46%,
    transparent 70%
  );
  pointer-events: none;
  filter: blur(14px);
}

/* A hairline of accent under the fan, so the light reads as coming from a real
   edge rather than a floating blob. */
.hero::after {
  content: '';
  position: absolute;
  left: 50%;
  bottom: 0;
  width: min(1120px, 86vw);
  height: 1px;
  transform: translateX(-50%);
  background: linear-gradient(
    90deg,
    transparent,
    rgba(251, 130, 75, 0.55) 22%,
    rgba(251, 130, 75, 0.75) 50%,
    rgba(251, 130, 75, 0.55) 78%,
    transparent
  );
  pointer-events: none;
}

.reels {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(12px, 1.6vw, 20px);
  width: 100%;
  max-width: 1120px;
  margin-top: clamp(22px, 2.8vw, 34px);
  perspective: 1400px;
}
/* One or two featured lessons sit in the middle, not against the left edge.
   The track keeps the width a card has in the full row of three — 1120px less
   two gaps, divided by three — so a lone video is a card, not a billboard. */
.reels:has(.reel:first-child:last-child) {
  grid-template-columns: minmax(0, 360px);
  justify-content: center;
}
.reels:has(.reel:nth-child(2):last-child) {
  grid-template-columns: repeat(2, minmax(0, 360px));
  justify-content: center;
}

.reel {
  position: relative;
  width: 100%;
  display: flex;
  flex-direction: column;
  text-align: left;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
  overflow: hidden;
  transform-origin: 50% 100%;
  transition: transform var(--t-slow) var(--ease-out),
    border-color var(--t) var(--ease-out), background var(--t) var(--ease-out);
}
/* Outer cards sit slightly lower and tilt away, so the trio reads as a fan
   rather than three equal tiles. Only ever a trio: tilting the first of two
   cards leans the pair sideways, and tilting a single card leaves it crooked
   under a centred headline with nothing to lean against. */
.reels:has(.reel:nth-child(3)) .reel:nth-child(1) {
  transform: translateY(var(--lift)) rotate(-1.4deg);
}
.reels:has(.reel:nth-child(3)) .reel:nth-child(3) {
  transform: translateY(var(--lift)) rotate(1.4deg);
}
.reel:nth-child(2) {
  z-index: 2;
}

.reel__shot {
  position: relative;
  display: block;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  border-bottom: 1px solid var(--line);
}
.reel__shot[data-tone='light'] {
  background: var(--plate-light);
}
.reel__shot[data-tone='dark'] {
  background: var(--plate-dark);
}
/* A lesson whose still has not been cached yet keeps the plate — the card is
   the same size either way, so nothing moves when the picture arrives. */
.reel__shot[data-state='empty'] {
  background: var(--plate-dark);
}
.reel__shot img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--t-slow) var(--ease-out);
}
/* Darken the still so the play button and the card text stay legible. */
.reel__shot::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, var(--fade-0), var(--fade-1));
  pointer-events: none;
}

.reel__play {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: 46px;
  height: 46px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--accent);
  color: #0f0f0f;
  box-shadow: 0 8px 26px var(--shadow);
  transition: transform var(--t) var(--ease-out);
}

.reel__soon {
  position: absolute;
  top: 10px;
  right: 10px;
  height: 22px;
  display: inline-flex;
  align-items: center;
  padding: 0 8px;
  border: 1px dashed var(--line-strong);
  border-radius: var(--r-sm);
  background: var(--veil-strong);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--fg-dim);
}

.reel__body {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 13px 15px 14px;
  flex: 1;
}
.reel__system {
  font-family: 'Zen Dots', sans-serif;
  font-size: 12px;
  letter-spacing: 0.06em;
  color: var(--accent-text);
}
.reel__title {
  font-size: 15px;
  font-weight: 600;
  line-height: 1.35;
  flex: 1;
}
.reel__meta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--line);
  font-size: 12px;
  color: var(--fg-dim);
}
.reel__go {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  color: var(--fg-muted);
  white-space: nowrap;
}
.reel__go svg {
  transition: transform var(--t) var(--ease-out);
}

@media (hover: hover) and (pointer: fine) {
  .reel:hover {
    border-color: var(--line-strong);
    background: var(--surface-2);
  }
  .reel:hover {
    transform: translateY(-8px);
  }
  .reels:has(.reel:nth-child(3)) .reel:nth-child(1):hover {
    transform: translateY(calc(var(--lift) - 8px)) rotate(-1.4deg);
  }
  .reels:has(.reel:nth-child(3)) .reel:nth-child(3):hover {
    transform: translateY(calc(var(--lift) - 8px)) rotate(1.4deg);
  }
  .reel:hover .reel__shot img {
    transform: scale(1.04);
  }
  .reel:hover .reel__play {
    transform: translate(-50%, -50%) scale(1.08);
  }
  .reel:hover .reel__go svg {
    transform: translateX(3px);
  }
}
.reel:active {
  filter: brightness(0.97);
}

/* entrance: the fan swings up from below the fold line */
.reels[data-state='out'] .reel {
  opacity: 0;
  transform: translateY(64px) scale(0.96);
}
.reels[data-state='in'] .reel {
  opacity: 1;
  transition: opacity 620ms var(--ease-out), transform 720ms var(--ease-out),
    border-color var(--t) var(--ease-out), background var(--t) var(--ease-out);
  /* --i is the stagger index, set from JS; without the fallback the whole
     calc() is invalid and the delay is dropped. */
  transition-delay: calc(var(--i, 0) * 110ms);
}
.reels[data-state='in']:has(.reel:nth-child(3)) .reel:nth-child(1) {
  transform: translateY(var(--lift)) rotate(-1.4deg);
}
.reels[data-state='in']:has(.reel:nth-child(3)) .reel:nth-child(3) {
  transform: translateY(var(--lift)) rotate(1.4deg);
}
.reels[data-state='in'] .reel:nth-child(2) {
  transform: translateY(0);
}

@media (max-width: 900px) {
  .reels {
    --lift: 0px;
  }
  /* The fan is a desktop composition; on a phone the cards are a carousel and
     a tilted card in a scroller reads as a rendering fault. The selectors carry
     the same :has() as the rules they undo — without it they are less specific
     and the tilt survives the media query. */
  .reels:has(.reel:nth-child(3)) .reel:nth-child(1),
  .reels:has(.reel:nth-child(3)) .reel:nth-child(3),
  .reels[data-state='in']:has(.reel:nth-child(3)) .reel:nth-child(1),
  .reels[data-state='in']:has(.reel:nth-child(3)) .reel:nth-child(3) {
    transform: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .reels[data-state] .reel {
    opacity: 1;
    transition: none;
  }
}

/* Swipeable reels on phones ------------------------------------------------ */

/*
 * The visible bar is 2px; the button around it is 32px so a thumb can hit it.
 * That extra 30px is target, not layout, so it is pulled back out — otherwise
 * making the control usable silently opens a gap under the carousel.
 */
.reels__dots {
  --dot-pad: 15px;
  display: none;
  gap: 6px;
  justify-content: center;
  margin-top: calc(14px - var(--dot-pad));
  margin-bottom: calc(var(--dot-pad) * -1);
}
/*
 * The dot is a 30×32 button with the visible bar drawn inside it, rather than a
 * 18×2 target: it is a real control now — it jumps to a card and stops the
 * rotation — and two pixels of height is not something a thumb can hit.
 */
.reels__dot {
  position: relative;
  width: 30px;
  height: 32px;
  padding: 0;
}
.reels__dot::after {
  content: '';
  position: absolute;
  top: 50%;
  inset-inline: 6px;
  height: 2px;
  background: var(--line-strong);
  border-radius: 1px;
  transform: translateY(-50%);
  transition: background var(--t) var(--ease-out), inset-inline var(--t) var(--ease-out);
}
.reels__dot.is-on::after {
  inset-inline: 2px;
  background: var(--accent);
}

@media (max-width: 900px) {
  /* On a phone the fan is stacked under the buttons rather than beside them, so
     the glow has to climb high enough to reach the CTA — otherwise it dies
     behind the cards and the composition splits in two. */
  .hero__glow {
    /* Bottom aligned with the accent line, so the light appears to rise from
       that edge instead of running past it and being clipped by the section. */
    bottom: clamp(20px, 5vw, 30px);
    width: 170vw;
    height: min(880px, 92vh);
    background: radial-gradient(
      ellipse at 50% 100%,
      rgba(251, 130, 75, 0.52) 0%,
      rgba(251, 130, 75, 0.32) 22%,
      rgba(251, 130, 75, 0.15) 42%,
      rgba(251, 130, 75, 0.05) 60%,
      transparent 76%
    );
    /* No blur: it bleeds ~18px past the element and the section clips it, which
       is what made the light stop below the accent line instead of on it. The
       radial stops are already smooth on their own. */
    filter: none;
  }

  /* The accent line reads as the base the glow rises from, so on phones it sits
     just under the position dots rather than at the very bottom of the hero —
     otherwise it floats free of the carousel it belongs to. */
  .hero::after {
    bottom: clamp(20px, 5vw, 30px);
    width: min(420px, 74vw);
  }

  /* Horizontal snap carousel. The negative margin lets cards bleed to the
     screen edge while scroll-padding keeps the first one aligned with the
     page gutter. */
  .reels {
    /* more air between the actions and the carousel than on desktop */
    margin-top: clamp(38px, 9vw, 58px);
    display: flex;
    grid-template-columns: none;
    max-width: none;
    margin-inline: calc(var(--gutter) * -1);
    padding-inline: var(--gutter);
    scroll-padding-inline: var(--gutter);
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .reels::-webkit-scrollbar {
    display: none;
  }
  .reel {
    flex: 0 0 clamp(240px, 78vw, 340px);
    scroll-snap-align: center;
  }
  .reels__dots {
    display: flex;
  }
}

/* Assistant on phones ------------------------------------------------------ */

@media (max-width: 720px) {
  /* A side-by-side field and button leaves the input too narrow to read back
     what you typed. The button becomes an icon inside the field instead, which
     is the pattern people already know from messaging. */
  .chat__form {
    position: relative;
  }
  .chat__form input {
    width: 100%;
    padding-right: 62px;
  }
  .chat__form .btn {
    padding: 0;
    position: absolute;
    top: 50%;
    right: 4px;
    transform: translateY(-50%);
    width: 44px;
    min-width: 44px;
    height: 44px;
    min-height: 44px;
    padding: 0;
    border-radius: var(--r-sm);
  }
  .chat__form .btn:active {
    transform: translateY(-50%) scale(0.94);
  }
  /* keep the label for screen readers, drop it visually */
  .chat__form .btn span {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }
  .chat__form .btn svg {
    width: 17px;
    height: 17px;
  }

  /* All four suggestions stay visible — scrolling them sideways hid three of
     them behind an edge with nothing to say they were there. */
  .chat__suggestions {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 6px;
  }
  .chat__suggestions .chip {
    width: 100%;
    justify-content: flex-start;
    text-align: left;
    padding: 10px 14px;
    line-height: 1.35;
  }
}


/* ==========================================================================

/* These centre by flex, so the padding must not also push the box taller than
   its intended height. */
.badge,
.tag-demo,
.reel__soon,
.file,
.social a,
.social span,
.card__tag {
  box-sizing: border-box;
}

/* Icons are already centred geometrically — undo the shift for them. */

/* ==========================================================================
   Line page — learning path, wiring diagrams
   ========================================================================== */

.path {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  counter-reset: step;
}

.step {
  position: relative;
  padding-left: 46px;
}
/* The spine that turns separate lessons into one chain. */
.step::before {
  content: '';
  position: absolute;
  left: 15px;
  top: 0;
  bottom: 0;
  width: 1px;
  background: var(--line);
}
/* The spine begins and ends at the node, never above the first or below the
   last — so a single-lesson path draws a dot and no stray line. */
.step:first-child::before {
  top: 50%;
}
.step:last-child::before {
  bottom: 50%;
}

/* The node on the spine.
   Centred on the row rather than set at a fixed offset from its top: the fixed
   offset was measured against a text-only row, and the day the first lesson got
   a thumbnail the row grew taller than the guess and the dot slid up out of
   line with its own title. Centring holds whatever the row ends up being. */
.step::after {
  content: '';
  position: absolute;
  left: 11px;
  top: 50%;
  translate: 0 -50%;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--bg);
  border: 1px solid var(--line-strong);
  transition: border-color var(--t) var(--ease-out), background var(--t) var(--ease-out);
}
.step[data-state='live']::after {
  background: var(--accent);
  border-color: var(--accent-text);
}

.step__body {
  /* A button shrinks to its content and centres text by default; these replaced
     block-level elements and must still fill the row and read left-aligned. */
  width: 100%;
  text-align: left;
  display: grid;
  grid-template-columns: auto auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 16px;
  padding: 14px 16px;
  margin-block: 3px;
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--surface);
  transition: border-color var(--t) var(--ease-out), background var(--t) var(--ease-out),
    transform var(--t-fast) var(--ease-out);
}
a.step__body:active {
  transform: scale(0.995);
}

.step__num {
  /* Fixed width: Zen Dots has no tabular figures, so 01 and 06 differ in width
     and the titles beside them would not line up. */
  width: 26px;
  flex-shrink: 0;
  font-family: 'Zen Dots', sans-serif;
  font-size: 13px;
  color: var(--fg-dim);
  transition: color var(--t) var(--ease-out);
}
.step[data-state='live'] .step__num {
  color: var(--accent-text);
}

.step__text {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
}
.step__title {
  font-size: 15px;
  font-weight: 600;
  line-height: 1.35;
}
.step__goal {
  font-size: 12px;
  color: var(--fg-dim);
}

.step__meta {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-shrink: 0;
}
.step__min {
  font-size: 12px;
  color: var(--fg-dim);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.step__go {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: var(--accent);
  color: #0f0f0f;
  transition: transform var(--t) var(--ease-out);
}
.step__pending {
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--fg-dim);
  border: 1px dashed var(--line-strong);
  border-radius: var(--r-sm);
  padding: 0 8px;
  min-height: 22px;
  display: inline-flex;
  align-items: center;
  white-space: nowrap;
}

@media (hover: hover) and (pointer: fine) {
  a.step__body:hover {
    border-color: var(--line-strong);
    background: var(--surface-2);
  }
  a.step__body:hover .step__go {
    transform: scale(1.08);
  }
}

.path__foot {
  margin-top: 22px;
}

/* wiring diagrams ------------------------------------------------------- */

.plans {
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  overflow: hidden;
}
.plan {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 18px;
  padding: 14px 18px;
  border-top: 1px solid var(--line);
  background: var(--surface);
  transition: background var(--t) var(--ease-out);
}
.plan:first-child {
  border-top: 0;
}
.plan__code {
  font-size: 12px;
  letter-spacing: 0.06em;
  color: var(--accent-text);
  font-variant-numeric: tabular-nums;
}
.plan__name {
  font-size: 14px;
  color: var(--fg);
}
.plan__dl {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 36px;
  min-width: 96px;
  padding: 0 12px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  font-size: 13px;
  color: var(--fg-muted);
  white-space: nowrap;
  transition: border-color var(--t) var(--ease-out), color var(--t) var(--ease-out),
    background var(--t) var(--ease-out), transform var(--t-fast) var(--ease-out);
}
a.plan__dl:active {
  transform: scale(0.96);
}
.plan__dl.is-disabled {
  opacity: 0.45;
  cursor: not-allowed;
  border-style: dashed;
}
@media (hover: hover) and (pointer: fine) {
  a.plan__dl:hover {
    border-color: var(--accent-text);
    color: var(--fg);
    background: rgba(251, 130, 75, 0.08);
  }
}
.plan[data-state='pending'] {
  background: transparent;
}
.plan[data-state='pending'] .plan__code,
.plan[data-state='pending'] .plan__name {
  color: var(--fg-dim);
}
.plan__pending {
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--fg-dim);
  border: 1px dashed var(--line-strong);
  border-radius: var(--r-sm);
  padding: 0 8px;
  min-height: 22px;
  display: inline-flex;
  align-items: center;
}
@media (hover: hover) and (pointer: fine) {
  a.plan:hover {
    background: var(--surface-2);
  }
}

@media (max-width: 720px) {
  .step {
    padding-left: 34px;
  }
  .step::before {
    left: 11px;
  }
  .step::after {
    left: 7px;
  }
  .step__body {
    grid-template-columns: auto minmax(0, 1fr);
    gap: 12px;
    padding: 12px 14px;
  }
  .step__meta {
    grid-column: 2;
    justify-content: space-between;
  }
  .plan {
    gap: 12px;
  }
  .plan__dl {
    min-width: 0;
    padding-inline: 10px;
  }
}

/* Lesson thumbnail ---------------------------------------------------------
   Reserved at its final 16:9 size while empty, so nothing shifts when the real
   YouTube still is dropped in. */

.step__thumb {
  position: relative;
  display: grid;
  place-items: center;
  width: 104px;
  aspect-ratio: 16 / 9;
  flex-shrink: 0;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--plate-dark);
  overflow: hidden;
}
.step__thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--t-slow) var(--ease-out);
}
/* Faint play mark reads as "a video belongs here", not as a broken image. */
.step__thumbicon {
  display: grid;
  place-items: center;
  color: var(--fg-dim);
  opacity: 0.45;
}
.step__thumb[data-state='empty'] {
  background: repeating-linear-gradient(
    -45deg,
    var(--plate-dark),
    var(--plate-dark) 6px,
    var(--hatch) 6px,
    var(--hatch) 12px
  );
}
.step[data-state='live'] .step__thumb {
  border-color: var(--line-strong);
}

@media (hover: hover) and (pointer: fine) {
  a.step__body:hover .step__thumb img {
    transform: scale(1.06);
  }
}

@media (max-width: 720px) {
  .step__body {
    grid-template-columns: auto minmax(0, 1fr);
    row-gap: 10px;
  }
  .step__thumb {
    width: 72px;
    grid-row: 1;
    grid-column: 1;
  }
  /* Number sits inside the thumbnail corner to keep the row compact. Offsets
     account for the body padding so it lands within the plate, not beside it. */
  .step__num {
    position: absolute;
    left: 18px;
    top: 16px;
    z-index: 2;
    padding: 2px 5px;
    border-radius: var(--r-sm);
    background: var(--veil-strong);
    font-size: 9px;
    line-height: 1.2;
  }
  .step__body {
    position: relative;
  }
  .step__text {
    grid-row: 1;
    grid-column: 2;
  }
  .step__meta {
    grid-row: 2;
    grid-column: 1 / -1;
  }
}

/* Shareable product page --------------------------------------------------- */

.share {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 18px;
  padding-top: 18px;
  border-top: 1px solid var(--line);
}

.share__link,
.share__copy {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  min-height: 40px;
  padding: 0 14px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  font-size: 13px;
  color: var(--fg-muted);
  transition: border-color var(--t) var(--ease-out), color var(--t) var(--ease-out),
    background var(--t) var(--ease-out), transform var(--t-fast) var(--ease-out);
}
.share__link:active,
.share__copy:active {
  transform: scale(0.97);
}
.share__copy[data-done='true'] {
  border-color: var(--accent-text);
  color: var(--accent-text);
}
.share__hint {
  font-size: 12px;
  color: var(--fg-dim);
}
.share .tag-demo {
  margin-left: 2px;
}

@media (hover: hover) and (pointer: fine) {
  .share__link:hover,
  .share__copy:hover {
    border-color: var(--line-strong);
    color: var(--fg);
  }
}

/* ZIP bundle under the diagram list ---------------------------------------- */

.plans__foot {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-top: 16px;
}
.plans__count {
  font-size: 12px;
  color: var(--fg-dim);
  font-variant-numeric: tabular-nums;
}
.btn.is-disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

@media (max-width: 720px) {
  .share {
    gap: 6px;
  }
  .share__hint {
    width: 100%;
  }
}

/* Hero film on a line page ------------------------------------------------- */

.herofilm {
  position: relative;
  display: flex;
  justify-content: center;
  perspective: 1400px;
}

/* Light cast by the card, anchored to it at any viewport width. Sits behind the
   card because ::before paints before the child content. */
.herofilm::before {
  content: '';
  position: absolute;
  left: 50%;
  bottom: -46%;
  width: 190%;
  height: 150%;
  transform: translateX(-50%);
  background: radial-gradient(
    ellipse at 50% 68%,
    rgba(251, 130, 75, 0.34) 0%,
    rgba(251, 130, 75, 0.17) 30%,
    rgba(251, 130, 75, 0.05) 52%,
    transparent 72%
  );
  pointer-events: none;
}

/* Tilted like the landing fan, so the page opens with the same energy. The tilt
   is decorative only — it straightens on hover so nothing reads as skewed while
   being looked at. */
.herofilm .herofilm__card {
  width: min(520px, 100%);
  /* Outranks .reel:nth-child(1) from the landing fan, which would otherwise
     apply its own tilt to this single card. */
  transform: rotate(-1.6deg);
  box-shadow: 0 26px 60px var(--shadow);
}
.herofilm__card .reel__shot {
  aspect-ratio: 16 / 9;
}
.herofilm__card .reel__title {
  font-size: 16px;
}

@media (hover: hover) and (pointer: fine) {
  .herofilm .herofilm__card:hover {
    transform: rotate(0deg) translateY(-4px);
  }
}

@media (max-width: 1080px) {
  /* Stacked, the card only repeats lesson 01, which the learning path shows a
     screen later — so the masthead goes straight from the copy to the course.
     The glow goes with it: without the film there is nothing for it to light. */
  .herofilm {
    display: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .herofilm .herofilm__card {
    transform: none;
  }
}

/* The masthead's own glow element is unused: a percentage of the section drifts
   away from the card as the viewport widens, because the card is bounded by the
   content width. The light is emitted by the card itself instead — see
   .herofilm::before — so it can never separate from it. */
.masthead .hero__glow {
  display: none;
}

/* The accent line the glow rises from — the masthead already has a bottom
   border, so this sits on top of it rather than adding a second edge. */
.masthead::after {
  content: '';
  position: absolute;
  left: 50%;
  bottom: 0;
  width: min(1240px, 92vw);
  height: 1px;
  transform: translateX(-50%);
  background: linear-gradient(
    90deg,
    transparent,
    rgba(251, 130, 75, 0.18) 40%,
    rgba(251, 130, 75, 0.55) var(--line-peak, 70%),
    rgba(251, 130, 75, 0.18) 96%,
    transparent
  );
  pointer-events: none;
}

@media (max-width: 1080px) {
  .masthead .hero__glow,
  .masthead::after {
    display: none;
  }
}

/* ==========================================================================
   In-page viewers — Academy video and wiring diagrams
   ========================================================================== */

.viewer {
  display: flex;
  flex-direction: column;
}

.viewer__stage {
  position: relative;
  aspect-ratio: 16 / 9;
  background: var(--stage);
  border-bottom: 1px solid var(--line);
}
.viewer__stage iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}
/* A document viewer fills the panel instead of claiming a fixed height: a fixed
   one plus the bar exceeded the panel's own max-height and the bar was clipped
   off the bottom. The stage flexes, the bar never does. */
.modal__panel:has(.viewer__stage--doc) {
  height: min(86vh, 900px);
  overflow: hidden;
}
.modal__panel:has(.viewer__stage--doc) #modal-content,
.modal__panel:has(.viewer__stage--doc) .viewer {
  /* the content wrapper needs a height too, or the viewer's 100% resolves
     against nothing and the stage collapses */
  height: 100%;
}
.viewer__stage--doc {
  aspect-ratio: auto;
  flex: 1;
  min-height: 0;
  background: var(--plate-light);
}
.viewer__bar {
  flex-shrink: 0;
}
.viewer__stage--empty {
  display: grid;
  place-items: center;
  background: repeating-linear-gradient(
    -45deg,
    var(--plate-dark),
    var(--plate-dark) 8px,
    var(--hatch) 8px,
    var(--hatch) 16px
  );
}
.viewer__stage--doc.viewer__stage--empty {
  flex: 0 0 auto;
  height: min(42vh, 420px);
}
/* A pending plan has no pages to scroll, so the panel should hug its content. */
.modal__panel:has(.viewer__stage--empty) {
  height: auto;
}
.viewer__mark {
  display: grid;
  place-items: center;
  width: 62px;
  height: 62px;
  border-radius: 50%;
  border: 1px solid var(--line-strong);
  color: var(--fg-dim);
}

.viewer__bar {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 20px;
  padding: 20px 24px 22px;
  /* room for the close button in the corner */
  padding-right: 64px;
}
.viewer__text {
  display: flex;
  flex-direction: column;
  gap: 5px;
  min-width: 0;
}
.viewer__sub {
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--accent-text);
}
.viewer__title {
  font-family: 'Canaro', sans-serif;
  font-size: clamp(16px, 2vw, 20px);
  font-weight: 600;
  text-transform: none;
  letter-spacing: 0;
  line-height: 1.35;
}
.viewer__note {
  margin-top: 6px;
  font-size: 13px;
  color: var(--fg-muted);
  max-width: 62ch;
}
.viewer__actions {
  display: inline-flex;
  gap: 8px;
  flex-shrink: 0;
}
.viewer__bar .doc {
  flex-shrink: 0;
}

/* The diagram row: name opens the viewer, button downloads. */
.plan__open {
  width: 100%;
  display: grid;
  grid-template-columns: minmax(120px, 190px) minmax(0, 1fr);
  align-items: center;
  gap: 18px;
  text-align: left;
  padding: 0;
  min-width: 0;
  transition: color var(--t) var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .plan__open:hover .plan__name {
    color: var(--accent-text);
  }
}

@media (max-width: 720px) {
  .viewer__bar {
    flex-direction: column;
    gap: 14px;
    padding: 16px 18px 18px;
    padding-right: 56px;
  }
  .modal__panel:has(.viewer__stage--doc) {
    height: min(88vh, 760px);
  }
  .plan__open {
    grid-template-columns: minmax(0, 1fr);
    gap: 4px;
  }
}

/* Rendered diagram pages. */
.viewer__pages {
  position: absolute;
  inset: 0;
  overflow: auto;
  -webkit-overflow-scrolling: touch;
  background: var(--plate-light);
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 10px;
}
.viewer__pages img {
  width: 100%;
  height: auto;
  border-radius: var(--r-sm);
}

@media (max-width: 900px) {
  .viewer__pages {
    padding: 8px;
    /* A single page fits the frame whole — the preview should show the shape of
       the plan at a glance. Multi-page sets still scroll. */
    justify-content: center;
    align-items: center;
  }
  .viewer__pages img:only-child {
    width: auto;
    max-width: 100%;
    max-height: 100%;
    object-fit: contain;
  }
}

/* ==========================================================================
   Header: systems dropdown and account
   ========================================================================== */

.nav__group {
  position: relative;
}
.nav .nav__trigger {
  position: relative;
}
.nav .nav__trigger {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  /* the same sum the sibling links use, so the whole row shares one baseline */
  padding: 6px 0 6px;
  font-size: 14px;
  color: var(--fg-muted);
  cursor: pointer;
  transition: color var(--t) var(--ease-out);
}
/* keep the underline behaviour of the other nav links */
.nav .nav__trigger::after {
  content: '';
  position: absolute;
  left: 0;
  bottom: 0;
  height: 1px;
  width: 100%;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--t) var(--ease-out);
}
.nav__group[data-open='true'] .nav__trigger::after {
  transform: scaleX(1);
}
.nav__trigger svg {
  transition: transform var(--t) var(--ease-out);
}
.nav__group[data-open='true'] .nav__trigger {
  color: var(--fg);
}
.nav__group[data-open='true'] .nav__trigger svg {
  transform: rotate(180deg);
}

/* Mega menu: the five lines as equally weighted tiles, laid out in a row.
   Modelled on the Figma products grid — these are peer systems, not a
   hierarchy, so a grid states that better than a list does. */
.nav__menu {
  position: fixed;
  top: var(--header-h);
  left: 50%;
  width: min(var(--maxw), calc(100vw - var(--gutter) * 2));
  padding: 18px;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-lg);
  background: rgba(17, 17, 17, 0.98);
  backdrop-filter: blur(20px);
  box-shadow: 0 30px 70px var(--shadow-strong);
  opacity: 0;
  visibility: hidden;
  transform: translateX(-50%) translateY(-10px);
  transition: opacity var(--t) var(--ease-out), transform var(--t) var(--ease-out),
    visibility 0s linear var(--t);
}
.nav__menu::after {
  content: '';
  position: absolute;
  top: -1px;
  left: 24px;
  right: 24px;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--accent), transparent);
  opacity: 0.75;
}
.nav__group[data-open='true'] .nav__menu {
  opacity: 1;
  visibility: visible;
  transform: translateX(-50%) translateY(6px);
  transition-delay: 0s;
}
/* Invisible bridge over the gap between the header trigger and the panel, so a
   pointer travelling down stays inside the menu's own hit area. Sized to the
   header height, which is what actually separates them. */
.nav__menu::before {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: 100%;
  height: 40px;
}

.nav__grid {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 10px;
}

.nav .nav__tile {
  display: flex;
  flex-direction: column;
  gap: 4px;
  /* Needs .nav to outrank `.nav a`, which sets its own padding for the plain
     nav links and would otherwise flatten these to zero. */
  padding: 12px;
  border: 1px solid transparent;
  border-radius: var(--r);
  background: var(--surface);
  transition: border-color var(--t) var(--ease-out), background var(--t) var(--ease-out),
    transform var(--t-fast) var(--ease-out);
}
.nav__tile::after {
  display: none;
}
.nav__tile:active {
  transform: scale(0.985);
}

.nav__thumb {
  display: block;
  aspect-ratio: 16 / 9;
  border-radius: var(--r-sm);
  overflow: hidden;
  background: var(--plate-dark);
  border: 1px solid var(--line);
  margin-bottom: 8px;
}
/* the family shots are on white, so the plate has to match or they float */
.nav__thumb[data-tone='light'] {
  background: var(--plate-light);
}
.nav__thumb img {
  width: 100%;
  height: 100%;
  /* contain, not cover: these are whole product families, and cropping one to a
     16:9 window cuts the outer stations off the ends */
  object-fit: contain;
  opacity: 0.9;
  transition: opacity var(--t) var(--ease-out), transform var(--t-slow) var(--ease-out);
}
.nav__thumb[data-tone='light'] img {
  opacity: 1;
}

.nav__name {
  font-family: 'Zen Dots', sans-serif;
  font-size: 13px;
  letter-spacing: 0.02em;
  color: var(--fg);
  transition: color var(--t) var(--ease-out);
}
.nav__meta {
  font-size: 11px;
  line-height: 1.35;
  color: var(--fg-dim);
  min-height: 2.7em;
}
.nav__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-top: auto;
  padding-top: 8px;
  border-top: 1px solid var(--line);
}
.nav__bus {
  font-size: 10px;
  letter-spacing: 0.04em;
  color: var(--fg-dim);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.nav__n {
  font-size: 10px;
  color: var(--fg-dim);
  font-variant-numeric: tabular-nums;
  padding: 0 6px;
  min-height: 18px;
  display: inline-flex;
  align-items: center;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  flex-shrink: 0;
}
.nav__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin-top: 14px;
  padding-top: 14px;
  border-top: 1px solid var(--line);
}
.nav__hint {
  font-size: 12px;
  color: var(--fg-dim);
}
.nav__all {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  font-size: 13px;
  color: var(--accent-text);
  white-space: nowrap;
  transition: color var(--t) var(--ease-out);
}
.nav__all::after {
  display: none;
}
.nav__all svg {
  transition: transform var(--t) var(--ease-out);
}

@media (hover: hover) and (pointer: fine) {
  .nav__trigger:hover {
    color: var(--fg);
  }
  .nav__tile:hover {
    border-color: var(--line-strong);
    background: var(--surface-2);
  }
  .nav__tile:hover .nav__name {
    color: var(--accent-text);
  }
  .nav__tile:hover .nav__thumb img {
    opacity: 1;
    transform: scale(1.05);
  }
  .nav__all:hover svg {
    transform: translateX(3px);
  }
}

.account {
  display: inline-grid;
  place-items: center;
  width: var(--control-h);
  height: var(--control-h);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  color: var(--fg-muted);
  flex-shrink: 0;
  transition: border-color var(--t) var(--ease-out), color var(--t) var(--ease-out),
    transform var(--t-fast) var(--ease-out);
}
.account:active {
  transform: scale(0.95);
}
@media (hover: hover) and (pointer: fine) {
  .account:hover {
    border-color: var(--line-strong);
    color: var(--fg);
  }
}

@media (max-width: 720px) {
  /* Inside the burger panel the trigger stays: Systeme is a row like the others
     and expands in place, so the panel opens short and the visitor chooses
     whether to see the systems. */
  .nav__group {
    position: static;
    border-bottom: 1px solid var(--line);
  }
  .nav .nav__trigger {
    display: flex;
    justify-content: space-between;
    width: 100%;
    padding: 15px 0 15px;
    font-size: 16px;
    color: var(--fg);
  }
  .nav .nav__trigger::after {
    display: none;
  }

  .nav__menu {
    position: static;
    width: auto;
    padding: 0;
    border: 0;
    border-radius: 0;
    background: none;
    backdrop-filter: none;
    box-shadow: none;
    transform: none;
    opacity: 1;
    visibility: visible;
    /* collapsed by default; grid-template-rows animates cleanly to auto */
    display: grid;
    grid-template-rows: 0fr;
    transition: grid-template-rows var(--t-slow) var(--ease-out);
  }
  .nav__menu::after,
  .nav__menu::before {
    display: none;
  }
  .nav__group[data-open='true'] .nav__menu {
    grid-template-rows: 1fr;
    transform: none;
  }
  .nav__inner {
    min-height: 0;
    overflow: hidden;
  }

  .nav__grid {
    grid-template-columns: 1fr 1fr;
    gap: 8px;
    padding-bottom: 14px;
  }
  .nav .nav__tile {
    /* Type is set larger here, so the desktop padding leaves the name and the
       range label sitting on the card edge. */
    padding: 14px;
    gap: 5px;
    border-color: var(--line);
  }
  .nav__thumb {
    margin-bottom: 10px;
  }
  .nav__meta {
    min-height: 0;
    line-height: 1.4;
  }
  .nav__foot {
    padding-top: 10px;
    margin-top: 4px;
  }
  .nav__bar {
    margin-top: 0;
    padding-bottom: 14px;
    border-top: 0;
  }
  .nav__hint {
    display: none;
  }
}

/* ==========================================================================
   Sign-in page
   ========================================================================== */

body.auth {
  min-height: 100vh;
  position: relative;
  overflow-x: hidden;
}

/* Split screen: what this is on one side, the form on the other — the same
   composition as the panel at admin.academy.balter.de. A bare form cannot say
   which door it is, and someone arriving with a balter.de password should be
   told before they type it that this is the technical portal and that the
   account is the one they already have. */
.auth__split {
  min-height: 100vh;
  display: grid;
  grid-template-columns: 1.05fr 1fr;
}
.auth__brand {
  position: relative;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(28px, 5vw, 64px);
  border-right: 1px solid var(--line);
}
.auth__brandinner {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: clamp(20px, 3vw, 34px);
  max-width: 440px;
}
.auth__eyebrow {
  margin: 0 0 10px;
  font-family: 'Zen Dots', sans-serif;
  font-size: 9px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--accent-text);
}
.auth__h1 {
  margin: 0 0 14px;
  font-size: clamp(26px, 3.4vw, 40px);
  line-height: 1.12;
}
.auth__what {
  margin: 0;
  color: var(--fg-muted);
  font-size: 14px;
  line-height: 1.65;
}
.auth__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.auth__list li {
  position: relative;
  padding-left: 18px;
  font-size: 13px;
  color: var(--fg-muted);
}
.auth__list li::before {
  content: '';
  position: absolute;
  left: 0;
  top: 8px;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--accent);
  opacity: 0.8;
}
/* The line that stops a password going into the wrong door. */
.auth__not {
  margin: 0;
  padding: 12px 14px;
  border: 1px dashed var(--line-strong);
  border-radius: var(--r);
  font-size: 12px;
  line-height: 1.6;
  color: var(--fg-dim);
}
.auth__not b {
  display: block;
  color: var(--fg-muted);
}
.auth__panel {
  display: grid;
  place-items: center;
  gap: 22px;
  align-content: center;
  padding: clamp(28px, 5vw, 64px);
  background: var(--surface);
}

@media (max-width: 900px) {
  .auth__split {
    grid-template-columns: 1fr;
  }
  /* The form first: the brand side is context, not the reason for the visit. */
  .auth__panel {
    order: -1;
    min-height: 100vh;
  }
  .auth__brand {
    border-right: 0;
    border-top: 1px solid var(--line);
  }
  .auth__h1 {
    font-size: 28px;
  }
}

.auth__grid {
  position: absolute;
  inset: 0;
  background-image: linear-gradient(var(--line) 1px, transparent 1px),
    linear-gradient(90deg, var(--line) 1px, transparent 1px);
  background-size: 64px 64px;
  mask-image: radial-gradient(ellipse 70% 60% at 50% 40%, #000 5%, transparent 74%);
  opacity: 0.45;
  pointer-events: none;
}
/* Round, and behind the words rather than under the page: width and height set
   separately give a flattened ellipse that stops at the edges of whatever it
   sits behind. */
.auth__glow {
  position: absolute;
  left: 50%;
  top: 50%;
  width: min(900px, 120%);
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  background: radial-gradient(
    ellipse at 50% 100%,
    rgba(251, 130, 75, 0.26) 0%,
    rgba(251, 130, 75, 0.1) 34%,
    transparent 70%
  );
  pointer-events: none;
}

.auth__card {
  width: min(400px, 100%);
}
.auth__logo {
  display: inline-block;
}
.auth__logo img {
  display: block;
  height: 26px;
  width: auto;
  opacity: 0.9;
}


.auth__head {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 10px;
}
.auth__title {
  font-size: clamp(22px, 3vw, 30px);
}
.auth__lead {
  color: var(--fg-muted);
  font-size: 14px;
  margin-bottom: 24px;
}
.auth__submit {
  width: 100%;
  margin-top: 4px;
}
.auth__forgot {
  display: inline-block;
  margin-top: 12px;
  font-size: 13px;
  color: var(--fg-dim);
  transition: color var(--t) var(--ease-out);
}

.auth__or {
  display: flex;
  align-items: center;
  gap: 14px;
  margin: 22px 0 18px;
  font-size: 12px;
  color: var(--fg-dim);
}
.auth__or::before,
.auth__or::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--line);
}

.auth__providers {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.provider {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 11px;
  width: 100%;
  min-height: 46px;
  padding: 0 16px;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  font-size: 14px;
  font-weight: 600;
  color: var(--fg);
  transition: background var(--t) var(--ease-out), border-color var(--t) var(--ease-out),
    transform var(--t-fast) var(--ease-out);
}
.provider:active {
  transform: scale(0.98);
}
.provider svg {
  flex-shrink: 0;
}

.auth__note {
  margin-top: 20px;
  font-size: 12px;
  line-height: 1.6;
  color: var(--fg-dim);
  padding: 12px 14px;
  border: 1px dashed var(--line-strong);
  border-radius: var(--r-sm);
  transition: border-color var(--t) var(--ease-out), color var(--t) var(--ease-out);
}
.auth__note[data-alert='true'] {
  border-color: var(--accent-text);
  color: var(--fg-muted);
}

.auth__foot {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 22px;
  padding-top: 18px;
  border-top: 1px solid var(--line);
  font-size: 13px;
  color: var(--fg-dim);
}
.auth__foot a {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  color: var(--accent-text);
}
.auth__back {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  color: var(--fg-dim);
  transition: color var(--t) var(--ease-out);
}
.auth__back svg {
  transform: rotate(180deg);
}

@media (hover: hover) and (pointer: fine) {
  .provider:hover {
    background: var(--surface-2);
    border-color: var(--fg);
  }
  .auth__forgot:hover,
  .auth__back:hover {
    color: var(--fg);
  }
}

/* Touch sizing for the header controls added after the earlier coarse block. */
@media (pointer: coarse) {
  .account {
    width: 44px;
    height: 44px;
  }
  .nav__item,
  .provider {
    min-height: 46px;
  }
}

/* Header logo: wordmark on desktop, mark alone on phones ------------------- */

.logo .logo__mark {
  display: none;
  /* Taller than the wordmark: a square mark reads smaller than a wide one at
     the same height, and it sits beside 44px controls. */
  height: 32px;
  width: auto;
}

@media (max-width: 720px) {
  /* The wordmark competes with the language switch, account and burger in a
     375px header. The mark alone still identifies the brand. */
  .logo__full {
    display: none;
  }
  .logo .logo__mark {
    display: block;
  }
}

/* ==========================================================================
   Language: globe trigger and picker
   ========================================================================== */

.globe {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  min-height: var(--control-h);
  padding: 0 12px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  color: var(--fg-muted);
  transition: border-color var(--t) var(--ease-out), color var(--t) var(--ease-out),
    transform var(--t-fast) var(--ease-out);
}
.globe:active {
  transform: scale(0.96);
}
.globe__code {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.06em;
}
@media (hover: hover) and (pointer: fine) {
  .globe:hover {
    border-color: var(--line-strong);
    color: var(--fg);
  }
}

.langpick {
  padding: clamp(22px, 4vw, 30px);
}
.langpick__title {
  font-size: clamp(18px, 2.4vw, 24px);
  margin-bottom: 18px;
}
.langpick__list {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 8px;
}
.langpick__item {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas: 'native code' 'name code';
  align-items: center;
  gap: 0 12px;
  text-align: left;
  padding: 12px 14px;
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--surface);
  transition: border-color var(--t) var(--ease-out), background var(--t) var(--ease-out),
    transform var(--t-fast) var(--ease-out);
}
.langpick__item:active {
  transform: scale(0.985);
}
.langpick__item[aria-current='true'] {
  border-color: var(--accent-text);
  background: rgba(251, 130, 75, 0.08);
}
/* The native name leads: someone looking for their own language scans for the
   word they actually recognise, not its English label. */
.langpick__native {
  grid-area: native;
  font-size: 15px;
  font-weight: 600;
  color: var(--fg);
}
.langpick__name {
  grid-area: name;
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--fg-dim);
}
.langpick__code {
  grid-area: code;
  font-size: 10px;
  color: var(--fg-dim);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  padding: 0 7px;
  min-height: 22px;
  display: inline-flex;
  align-items: center;
}
@media (hover: hover) and (pointer: fine) {
  .langpick__item:hover {
    border-color: var(--line-strong);
    background: var(--surface-2);
  }
}

/* ==========================================================================
   Right-to-left (Arabic)

   Mirrors direction-dependent layout. Anything that is a real direction — an
   arrow pointing "forward", a timeline spine, a slide-in — flips. Anything
   physical does not: product photography, the logo, and Latin article numbers.
   ========================================================================== */

[dir='rtl'] .eyebrow::before {
  order: 1;
}
[dir='rtl'] .btn__arrow,
[dir='rtl'] .line__go svg,
[dir='rtl'] .reel__go svg,
[dir='rtl'] .post__more svg,
[dir='rtl'] .nav__all svg,
[dir='rtl'] .auth__foot a svg {
  transform: scaleX(-1);
}
[dir='rtl'] .crumb svg {
  transform: none;
}
[dir='rtl'] .auth__back svg {
  transform: none;
}

/* the learning-path spine moves to the other side */
[dir='rtl'] .step {
  padding-left: 0;
  padding-right: 46px;
}
[dir='rtl'] .step::before {
  left: auto;
  right: 15px;
}
[dir='rtl'] .step::after {
  left: auto;
  right: 11px;
}

[dir='rtl'] .chat__form input {
  padding-right: 16px;
  padding-left: 62px;
}
[dir='rtl'] .chat__form .btn {
  right: auto;
  left: 4px;
}
[dir='rtl'] .msg--user {
  align-self: flex-start;
  align-items: flex-start;
}

[dir='rtl'] .modal__close {
  right: auto;
  left: 12px;
}
[dir='rtl'] .viewer__bar {
  padding-right: 24px;
  padding-left: 64px;
}
[dir='rtl'] .reel__soon,
[dir='rtl'] .step__num {
  right: auto;
  left: 10px;
}

/* Article numbers, bus names and product codes stay left-to-right inside RTL
   text — they are Latin identifiers, not words. */
[dir='rtl'] .card__name,
[dir='rtl'] .plan__code,
[dir='rtl'] .nav__bus,
[dir='rtl'] .detail__name,
[dir='rtl'] .specs td,
[dir='rtl'] .hours td,
[dir='rtl'] .globe__code,
[dir='rtl'] .langpick__code {
  direction: ltr;
  text-align: right;
}
[dir='rtl'] .hero__title,
[dir='rtl'] .line__name,
[dir='rtl'] .nav__name,
[dir='rtl'] .stat__value {
  direction: ltr;
}


/* ------------------------------------------------- article grid, shop data */

/*
 * The filter row carries two kinds of chip. Type answers "what is this" and is
 * a single choice; the properties after the divider stack. Without the divider
 * the row reads as one list and nobody can tell whether two chips mean AND or
 * OR — so the rule is doing real work, not decoration.
 */
/* Axis headings are German like the tags they group, so they keep their own
   direction in a mirrored layout. */
[dir='rtl'] .axis__label {
  direction: ltr;
  text-align: right;
}

/* ------------------------------------------------------- local navigation */

/*
 * Sits in the flow under the masthead and sticks when it reaches the header, so
 * it costs nothing at the top of the page and is there for the rest of it.
 *
 * Together the two bars are 112px — about 12% of a laptop viewport, a little over
 * the tenth NN/g recommends, which is why this one is as short as a row of links
 * can be and why it does not appear until the masthead is behind you.
 */
.subnav {
  position: sticky;
  top: var(--header-h);
  z-index: 40;
  height: 44px;
  background: var(--veil-strong);
  backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--line);
}

/* The bar bleeds to the edges like the header, but its contents sit on the same
   grid as everything else — a row of links starting 40px from a 2000px screen
   while the page below starts at the container edge reads as a different site. */
.subnav__inner {
  display: flex;
  align-items: center;
  gap: 4px;
  width: 100%;
  max-width: var(--maxw);
  height: 100%;
  margin-inline: auto;
  padding-inline: var(--gutter);
  overflow-x: auto;
  scrollbar-width: none;
}
.subnav__inner::-webkit-scrollbar {
  display: none;
}

/* The system's name anchors the bar: without it a row of section links could
   belong to any page on the site. */
.subnav__here {
  flex: 0 0 auto;
  margin-inline-end: 14px;
  padding-inline-end: 14px;
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg);
  border-inline-end: 1px solid var(--line);
}

.subnav__link {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 30px;
  padding: 0 10px;
  font-size: 13px;
  color: var(--fg-muted);
  border-radius: var(--r);
  white-space: nowrap;
  transition: color var(--t) var(--ease-out), background var(--t) var(--ease-out);
}
.subnav__link[aria-current='true'] {
  color: var(--fg);
  background: var(--surface-2);
}
.subnav__n {
  font-size: 10px;
  color: var(--fg-dim);
  font-variant-numeric: tabular-nums;
}

@media (hover: hover) and (pointer: fine) {
  .subnav__link:hover {
    color: var(--fg);
  }
}

/*
 * Anchors must clear both bars, or the heading a reader jumped to lands behind
 * them and the page looks like it went to the wrong place. This is the single
 * most common way this pattern is got wrong.
 */
/* Only the pages that carry the local bar need room for it; `scroll-padding-top`
   on the document already accounts for the header, and the two add up rather than
   overriding, which put the target a second bar's height too low. */
main > section[id] {
  scroll-margin-top: 44px;
}

/* ------------------------------------------------------------- refine bar */

/*
 * Below the type row: the Filter button, two properties left in the open, what
 * is currently applied, and the count. Separated from the type row by a rule so
 * that a reader can see at a glance that the two lines answer different
 * questions — "what is it" above, "which one" below.
 */
.refine {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  padding-top: 12px;
  margin-bottom: 22px;
  border-top: 1px solid var(--line);
}

.refine__open {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 32px;
  padding: 0 12px;
  font-size: 13px;
  color: var(--fg);
  background: var(--surface-2);
  border: 1px solid var(--line-strong);
  border-radius: var(--r);
  transition: border-color var(--t) var(--ease-out), background var(--t) var(--ease-out),
    transform var(--t-fast) var(--ease-out);
}
.refine__open:active {
  transform: scale(0.97);
}
.refine__open svg {
  flex: 0 0 auto;
}

/* The number of active filters, on the button itself — the one place a reader
   looks to find out whether a short list is short because of them. */
.refine__n {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 17px;
  height: 17px;
  padding: 0 4px;
  font-size: 10px;
  color: var(--bg);
  background: var(--accent);
  border-radius: 9px;
}

/* Applied filters read as removable, not as another thing to switch on: accent
   border, and the × is part of the control rather than a second button. */
.refine__applied {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  min-height: 32px;
  padding: 0 10px;
  font-size: 13px;
  color: var(--accent-text);
  border: 1px solid var(--accent);
  border-radius: var(--r);
  transition: background var(--t) var(--ease-out), transform var(--t-fast) var(--ease-out);
}
.refine__applied:active {
  transform: scale(0.97);
}

.refine__clear {
  min-height: 32px;
  padding: 0 4px;
  font-size: 13px;
  color: var(--fg-dim);
  text-decoration: underline;
  text-underline-offset: 3px;
}

.refine__count {
  margin-inline-start: auto;
  font-size: 12px;
  color: var(--fg-dim);
  font-variant-numeric: tabular-nums;
}

/* The two quick picks are properties, not types, and are quieter than the row
   above so the eye is not asked to choose between two equal-looking rows. */
.chip--quiet {
  color: var(--fg-dim);
  border-style: dashed;
}

@media (hover: hover) and (pointer: fine) {
  .refine__open:hover {
    border-color: var(--fg-dim);
    background: var(--surface);
  }
  .refine__applied:hover {
    background: rgba(251, 130, 75, 0.12);
  }
  .refine__clear:hover {
    color: var(--fg);
  }
}

/* ---------------------------------------------------------------- the sheet */

/*
 * A popover anchored to the content on desktop, a bottom sheet on a phone. Both
 * are the same markup: the panel is placed by the breakpoint, not rebuilt.
 */
/*
 * `display: flex` on a class beats the `hidden` attribute's user-agent rule, so
 * the closed sheet stayed in the layout at zero opacity and swallowed every click
 * on the page behind it. Any full-screen overlay styled with display needs its
 * hidden state restated.
 */
.sheet[hidden] {
  display: none;
}

.sheet {
  position: fixed;
  inset: 0;
  z-index: 90;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  background: rgba(0, 0, 0, 0.5);
  opacity: 0;
  transition: opacity var(--t) var(--ease-out);
}
.sheet[data-open='true'] {
  opacity: 1;
}

.sheet__panel {
  width: min(560px, 100%);
  max-height: 86vh;
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-lg) var(--r-lg) 0 0;
  transform: translateY(12px);
  transition: transform var(--t) var(--ease-out);
}
.sheet[data-open='true'] .sheet__panel {
  transform: none;
}

.sheet__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 18px 20px 14px;
  border-bottom: 1px solid var(--line);
}
.sheet__title {
  font-size: 13px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--fg-muted);
}
.sheet__close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  color: var(--fg-muted);
  border: 1px solid var(--line);
  border-radius: var(--r);
}

.sheet__body {
  padding: 6px 20px 18px;
  overflow-y: auto;
}

.axis {
  padding-top: 16px;
}
/* The axis label is what turns eight loose chips into four questions. */
.axis__label {
  margin-bottom: 8px;
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-dim);
}
.axis__row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.sheet__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 14px 20px;
  border-top: 1px solid var(--line);
}
.sheet__foot .btn--primary {
  flex: 1;
  justify-content: center;
}

/* On a pointer device the sheet stops being a sheet: it sits centred as a small
   panel, because a phone-height drawer on a 27-inch screen is a long throw of
   the eye for four rows of chips. */
@media (min-width: 760px) {
  .sheet {
    align-items: center;
  }
  .sheet__panel {
    border-radius: var(--r-lg);
  }
  .sheet__foot .btn--primary {
    flex: 0 0 auto;
  }
}

@media (max-width: 700px) {
  .refine__count {
    margin-inline-start: 0;
    width: 100%;
  }

  /*
   * Eleven types wrap onto six rows of a 375px screen — the wall of chips this
   * redesign exists to remove, reappearing on the device most people read on. One
   * swipeable row costs one row, and the type stays in the open where it belongs
   * rather than being buried in the sheet with the properties.
   *
   * The trade is that some types start off-screen, so the edge is faded to say so
   * — a hard cut at the viewport edge reads as the end of the list.
   */
  .filters {
    flex-wrap: nowrap;
    overflow-x: auto;
    scrollbar-width: none;
    scroll-snap-type: x proximity;
    padding-bottom: 2px;
  }
  .filters::-webkit-scrollbar {
    display: none;
  }
  .filters > .chip {
    flex: 0 0 auto;
    scroll-snap-align: start;
  }
  .filters[data-more='end'],
  .filters[data-more='both'] {
    mask-image: linear-gradient(to right, #000 calc(100% - 32px), transparent);
  }
  .filters[data-more='start'] {
    mask-image: linear-gradient(to left, #000 calc(100% - 32px), transparent);
  }
  .filters[data-more='both'] {
    mask-image: linear-gradient(to right, transparent, #000 32px,
      #000 calc(100% - 32px), transparent);
  }
}

/* The article number is what an installer actually orders by, so it stays
   visible on the card rather than hiding in the detail panel. */
/* The article number is what an installer orders by, so it gets its own row and
   is never truncated. The longest in the catalogue is 27 characters, which fits
   the narrowest card the grid produces. */
.card__ref {
  font-size: 11px;
  line-height: 1.4;
  letter-spacing: 0.04em;
  font-variant-numeric: tabular-nums;
  color: var(--fg-dim);
  white-space: nowrap;
}

.detail__ref {
  margin-top: 2px;
  font-size: 13px;
  letter-spacing: 0.03em;
  color: var(--fg-dim);
}

/* The shop's own wording, kept where there is room for it. Card names are cut to
   what separates one article from its siblings; this is the rest. */
.detail__full {
  margin-top: 10px;
  font-size: 13px;
  line-height: 1.5;
  color: var(--fg-muted);
}

.detail__tags {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 12px;
}

.detail__summary {
  margin-top: 16px;
  padding: 0;
  list-style: none;
  color: var(--fg-muted);
  display: grid;
  gap: 5px;
}

.detail__summary li {
  position: relative;
  padding-inline-start: 14px;
}

.detail__summary li::before {
  content: '';
  position: absolute;
  inset-inline-start: 0;
  top: 0.62em;
  width: 4px;
  height: 1px;
  background: var(--accent);
}

.grid__empty {
  padding: 40px 0;
  color: var(--fg-dim);
}


/* ==========================================================================
   Legal pages — Impressum and Datenschutz.

   Deliberately plain: one column, generous measure, no reveal animation and no
   chrome to scroll past. These are read when someone is looking for one fact.
   ========================================================================== */

/* -------------------------------------------------------------- failure -- */

/* The banner sits above the header (z-index 100) and below the skip link (200):
   it has to be visible over fixed chrome, and it must never cover the one
   control a keyboard visitor reaches first. */
.failure {
  position: fixed;
  inset: 0 0 auto;
  z-index: 150;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 12px;
  padding: 10px 16px;
  background: var(--veil-solid);
  border-bottom: 1px solid var(--accent-text);
  color: var(--fg-muted);
  font-size: 13px;
  line-height: 1.5;
  text-align: center;
}
.failure__retry {
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  background: none;
  color: var(--accent-text);
  font: inherit;
  padding: 4px 12px 4px;
  cursor: pointer;
  transition: border-color var(--t) var(--ease-out);
}
.failure__retry:active {
  transform: scale(0.97);
}
@media (hover: hover) and (pointer: fine) {
  .failure__retry:hover {
    border-color: var(--accent-text);
  }
}

.legal-page {
  background: var(--bg);
}
.legal {
  max-width: 68ch;
  margin-inline: auto;
  padding: clamp(32px, 6vw, 72px) var(--gutter) 96px;
}
.legal__logo {
  display: inline-block;
  margin-bottom: clamp(28px, 5vw, 48px);
}
.legal h1 {
  font-size: clamp(26px, 4vw, 38px);
  margin-bottom: 10px;
}
.legal__lead {
  color: var(--fg-muted);
  margin-bottom: clamp(24px, 4vw, 40px);
}
.legal h2 {
  font-size: 13px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--accent-text);
  margin-block: clamp(28px, 4vw, 40px) 10px;
}
.legal p {
  color: var(--fg-muted);
  line-height: 1.7;
  margin-bottom: 12px;
}
.legal a {
  color: var(--fg);
  text-decoration: underline;
  text-underline-offset: 3px;
}
@media (hover: hover) and (pointer: fine) {
  .legal a:hover {
    color: var(--accent-text);
  }
}
.legal code {
  font-size: 0.92em;
  color: var(--fg);
}
.legal__back {
  margin-top: clamp(40px, 6vw, 64px);
  padding-top: 20px;
  border-top: 1px solid var(--line);
}

/* The footer's legal row: required links, set apart from the marketing ones. */
.footer__legal {
  display: flex;
  flex-wrap: wrap;
  gap: 18px;
  margin-top: 18px;
}
.footer__legal a {
  font-size: 12px;
  color: var(--fg-dim);
}
@media (hover: hover) and (pointer: fine) {
  .footer__legal a:hover {
    color: var(--fg);
  }
}


/* The theme control sits next to the language one and borrows its shape. */
.theme {
  display: flex;
}

/* btn--sm is 38px wherever else it is used and stays that way; inside the
   header it joins the row. Alignment is set here rather than on each control so
   there is one place to change the height of the whole row. */
.header__right .btn--sm {
  min-height: var(--control-h);
}
.header__right {
  align-items: center;
}

/* ==========================================================================
   Light theme.

   Three states, not two: an explicit choice writes data-theme on the root, and
   the default writes nothing at all — then the system preference decides. So
   every value is stated twice, once under the media query guarded against an
   explicit light choice, once under the attribute so the toggle wins both ways.

   Only the neutrals move. The orange fill is the brand and stays exactly as it
   is; only the orange *ink* darkens, because #fb824b reads at 2.5:1 on white.
   ========================================================================== */

@media (prefers-color-scheme: light) {
  :root:not([data-theme='dark']) {
    --accent-text: #b5491a;
    --bg: #ffffff;
    --surface: #f7f7f7;
    --surface-2: #f0f0f0;
    --line: rgba(0, 0, 0, 0.12);
    --line-strong: rgba(0, 0, 0, 0.26);
    --fg: #0f0f0f;
    --fg-muted: #5a5a5a;
    --fg-dim: rgba(0, 0, 0, 0.5);
    --plate-light: #f2f2f2;
    --plate-dark: #1a1a1a;
    --veil: rgba(255, 255, 255, 0.78);
    --veil-strong: rgba(255, 255, 255, 0.94);
    --veil-solid: rgba(255, 255, 255, 0.99);
    --scrim: rgba(20, 20, 20, 0.5);
    --tint: rgba(0, 0, 0, 0.05);
    --hatch: rgba(0, 0, 0, 0.035);
    --shadow: rgba(0, 0, 0, 0.14);
    --shadow-strong: rgba(0, 0, 0, 0.2);
  }
}

:root[data-theme='light'] {
  --accent-text: #b5491a;
  --bg: #ffffff;
  --surface: #f7f7f7;
  --surface-2: #f0f0f0;
  --line: rgba(0, 0, 0, 0.12);
  --line-strong: rgba(0, 0, 0, 0.26);
  --fg: #0f0f0f;
  --fg-muted: #5a5a5a;
  --fg-dim: rgba(0, 0, 0, 0.5);
  --plate-light: #f2f2f2;
  --plate-dark: #1a1a1a;
  --veil: rgba(255, 255, 255, 0.78);
  --veil-strong: rgba(255, 255, 255, 0.94);
  --veil-solid: rgba(255, 255, 255, 0.99);
  --scrim: rgba(20, 20, 20, 0.5);
  --tint: rgba(0, 0, 0, 0.05);
  --hatch: rgba(0, 0, 0, 0.035);
  --shadow: rgba(0, 0, 0, 0.14);
  --shadow-strong: rgba(0, 0, 0, 0.2);
}

/* The wordmark and the eagle exist only in white. The brandbook keeps the logo
   mono, so inverting a pure-white mark yields exactly the black one — no second
   asset, and no risk of the two drifting apart. */
@media (prefers-color-scheme: light) {
  :root:not([data-theme='dark']) img[src*='-white.svg'],
  :root:not([data-theme='dark']) .brand svg {
    filter: invert(1);
  }
}
:root[data-theme='light'] img[src*='-white.svg'],
:root[data-theme='light'] .brand svg {
  filter: invert(1);
}
/* The favicon plate is already dark by design and must not inherit that. */
:root[data-theme='light'] .legal__logo img {
  filter: invert(1);
}


/* Logo sizes and treatment live on the partner now, not here: a mark added in
   the panel used to get neither, because both were keyed on the file name. */


/* ==========================================================================
   Blog: post bodies and the archive.
   ========================================================================== */

.article__body > * + * {
  margin-top: 14px;
}
.article__h {
  font-size: clamp(16px, 2vw, 20px);
  margin-top: 26px;
}
.article__list {
  display: grid;
  gap: 6px;
  padding-left: 18px;
  list-style: disc;
  color: var(--fg-muted);
}
.article__quote {
  padding-left: 16px;
  border-left: 2px solid var(--accent);
  font-size: clamp(15px, 1.8vw, 18px);
  color: var(--fg);
}
.article__figure {
  margin-block: 20px;
  border: 1px solid var(--line);
  border-radius: var(--r);
  overflow: hidden;
}
.article__figure img {
  display: block;
  width: 100%;
}

/* A video is a still until it is asked for: nothing reaches YouTube before the
   click, which is what the privacy policy says happens. */
.article__video {
  position: relative;
  display: block;
  width: 100%;
  margin-block: 20px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--r);
  overflow: hidden;
  background: var(--plate-dark);
  cursor: pointer;
}
.article__video img {
  display: block;
  width: 100%;
  opacity: 0.86;
  transition: opacity var(--t) var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .article__video:hover img {
    opacity: 1;
  }
}
.article__play {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  font-size: 34px;
  color: var(--accent);
}
.article__frame {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  margin-block: 20px;
  border: 0;
  border-radius: var(--r);
}

/* Two labelled groups, not one row: rubrik and system answer different
   questions, and a reader should not have to work out which is which. */
.blogfilter {
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding-bottom: clamp(20px, 3vw, 30px);
  margin-bottom: clamp(24px, 4vw, 40px);
  border-bottom: 1px solid var(--line);
}
.blogfilter__group {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}
.blogfilter__label {
  min-width: 84px;
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--fg-dim);
}
.chip--clear {
  align-self: flex-start;
  border-style: dashed;
}
.blogyear__label span {
  color: var(--fg-dim);
  letter-spacing: 0.06em;
}
.blogyear + .blogyear {
  margin-top: clamp(36px, 6vw, 64px);
}
.blogyear__label {
  font-size: 12px;
  letter-spacing: 0.14em;
  color: var(--fg-dim);
  padding-bottom: 12px;
  margin-bottom: 20px;
  border-bottom: 1px solid var(--line);
  font-variant-numeric: tabular-nums;
}

/* ------------------------------------------------------------------ konto */

/* The account page. Cards rather than a dashboard: there is no activity worth
   charting, only a few statements of fact and the things they unlock. */
.konto {
  display: grid;
  gap: 20px;
  max-width: 760px;
}
.konto__card {
  padding: 24px;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
}
.konto__h {
  margin: 0 0 16px;
  font-size: 15px;
  letter-spacing: 0.02em;
}
.konto__row {
  display: flex;
  gap: 16px;
  padding: 9px 0;
  border-top: 1px solid var(--line);
}
.konto__row:first-of-type { border-top: 0; padding-top: 0; }
.konto__k { flex: 0 0 34%; color: var(--fg-muted); font-size: 13px; }
.konto__v { min-width: 0; word-break: break-word; font-size: 13px; }
.konto__p { margin: 0 0 14px; color: var(--fg-dim); font-size: 14px; line-height: 1.55; }
.konto__muted { margin: 0; color: var(--fg-muted); font-size: 13px; }
.konto__card .btn { margin-top: 16px; }

.konto__plans { display: grid; gap: 1px; background: var(--line); border: 1px solid var(--line); border-radius: var(--r-sm); overflow: hidden; }
.konto__plan {
  display: grid;
  grid-template-columns: minmax(0, auto) minmax(0, 1fr) auto;
  align-items: center;
  gap: 14px;
  padding: 12px 14px;
  background: var(--surface);
  text-decoration: none;
  transition: background var(--t) var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .konto__plan:hover { background: var(--tint); }
}
.konto__plancode { color: var(--accent-text); font-size: 12px; letter-spacing: 0.04em; }
.konto__planname { min-width: 0; font-size: 13px; }
.konto__planline { color: var(--fg-muted); font-size: 11px; letter-spacing: 0.05em; }

@media (max-width: 640px) {
  .konto__row { display: grid; gap: 2px; }
  .konto__k { flex: none; }
  .konto__plan { grid-template-columns: minmax(0, 1fr); gap: 4px; }
}

/* A password field that can be read back. The button sits inside the field's
   box rather than beside it, so the row keeps its shape in every language —
   a label next to the input would be the widest thing on the form in German. */
.pw { position: relative; display: block; }
.pw input { width: 100%; padding-right: 48px; }
.pw__eye {
  position: absolute;
  top: 50%;
  inset-inline-end: 6px;
  transform: translateY(-50%);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border: 0;
  border-radius: var(--r-sm);
  background: none;
  color: var(--fg-muted);
  cursor: pointer;
  transition: color var(--t) var(--ease-out), background var(--t) var(--ease-out);
}
.pw__eye:hover { color: var(--fg); background: var(--tint); }
.pw__eye:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* The account page's own form. Fields stack, because a two-column form in a
   card that is already narrow only makes both columns too small. */
.konto__form { display: grid; gap: 16px; max-width: 420px; }
.konto__field { display: grid; gap: 7px; }
.konto__flabel { color: var(--fg-muted); font-size: 12px; letter-spacing: 0.04em; text-transform: uppercase; }
.konto__fhint { color: var(--fg-muted); font-size: 12px; }
.konto__status { margin: 0; min-height: 18px; font-size: 13px; }
.konto__status[data-tone='good'] { color: var(--ok-text, var(--fg)); }
.konto__status[data-tone='bad'] { color: var(--err-text, var(--accent-text)); }

/* The heading over a group of documents. The material list is a grid rather
   than a bordered card, so the heading spans every column and rules across —
   the same statement as on the plan list, in the shape this list has. */
.promo__group {
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 8px 0 2px;
  color: var(--fg-muted);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.promo__group::after { content: ''; flex: 1; height: 1px; background: var(--line); }
.promo__group[data-level='abo'] { color: var(--accent-text); }

/* A document whose file is still being made. Deliberately quiet: it holds the
   place in the grid and says why it is empty, rather than looking like a card
   that failed to load. */
.promo__item[data-pending] { opacity: 0.62; }
.promo__sheet[data-pending='true'] {
  display: grid;
  place-items: center;
  background: var(--surface-2, var(--tint));
  border: 1px dashed var(--line);
}
.promo__sheet[data-pending='true'] .promo__ext {
  color: var(--fg-muted);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

/* ==========================================================================
   Unterlagen in blocks — Kataloge, Präsentationen, Flyer
   ==========================================================================
   One flat run of cards stopped being an index once presentations arrived:
   a catalogue, a two-page flyer and an eighty-slide deck are three different
   errands, and the reader was matching titles one by one. The blocks are the
   index, separated by space — the heading already carries a rule of its own.
*/
.promo-blocks { display: flex; flex-direction: column; gap: 40px; }
.promo-block { display: flex; flex-direction: column; gap: 16px; }
.promo-blocks .promo__group { padding: 0; }

/* The way from the landing selection to the library. Its own line under the
   row rather than a button beside the heading: the row is the thing being
   offered, and the link is what to do when none of the four is it. */
.promo__all {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-top: 18px;
  color: var(--accent);
  font-size: 13px;
  font-weight: 600;
}
.promo__all svg { transition: transform var(--t) var(--ease-out); }
.promo__all:active { transform: scale(0.97); }
@media (hover: hover) and (pointer: fine) {
  .promo__all:hover svg { transform: translateX(4px); }
}
/* A presentation shown in the flat card opens rather than downloads, so its
   control is a destination, not a file action. */
.doc--go { color: var(--accent); border-color: transparent; padding-left: 0; }

/*
 * A presentation card.
 *
 * Portrait, because the thing inside it is: every door station is tall and
 * narrow, and in the flat document card the reader saw a 40px strip of one.
 * Same proportion as the launcher inside the presentation, so opening one
 * continues the page rather than jumping to a different-looking product.
 */
.decks {
  display: grid;
  /* auto-fill for the same reason as .promo: one presentation stays a card. */
  grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
  gap: 16px;
}
.deck {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  aspect-ratio: 3 / 4;
  min-height: 290px;
  padding: 20px;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--tint);
  color: #fff;
  transition: transform var(--t) var(--ease-out), border-color var(--t) var(--ease-out);
}
.deck__bg { position: absolute; inset: 0; z-index: -2; background-size: cover; background-position: center; }
/* Not decoration: the text sits on the photograph and the stations are pale,
   so without this the title fails contrast on half the covers. */
.deck__scrim {
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(
    180deg,
    rgba(15, 15, 15, 0.05) 0%,
    rgba(15, 15, 15, 0.3) 38%,
    rgba(15, 15, 15, 0.8) 68%,
    rgba(15, 15, 15, 0.96) 100%
  );
}
/* The card is the target; the downloads are separate controls above it. */
.deck__link { position: absolute; inset: 0; z-index: 1; border-radius: inherit; }
.deck__tier {
  align-self: flex-start;
  margin-bottom: auto;
  padding: 5px 10px;
  border-radius: 100px;
  background: var(--accent);
  color: #0f0f0f;
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.deck__name {
  margin-top: 14px;
  font-family: 'Zen Dots', sans-serif;
  font-size: 21px;
  line-height: 1.06;
  /* break-word but never hyphens: Zen Dots is a display face and an automatic
     hyphen in the middle of a product name reads as a different product. */
  overflow-wrap: break-word;
  hyphens: manual;
}
.deck__meta { margin-top: 6px; font-size: 12.5px; color: rgba(255, 255, 255, 0.66); }
.deck__go {
  margin-top: 12px;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--accent);
}
.deck__go svg { transition: transform var(--t) var(--ease-out); }
.deck__files { position: relative; z-index: 2; display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px; }
.deck__files .doc { color: rgba(255, 255, 255, 0.72); border-color: rgba(255, 255, 255, 0.22); }
.deck:active { transform: scale(0.97); }
@media (hover: hover) and (pointer: fine) {
  .deck:hover { border-color: var(--accent); }
  .deck:hover .deck__go svg { transform: translateX(4px); }
  .deck__files .doc:hover { color: #fff; border-color: rgba(255, 255, 255, 0.5); }
}
.deck:focus-within { outline: 2px solid var(--accent); outline-offset: 2px; }
/* On a phone a full-width 3:4 card is most of the screen, and five of them are
   five screens of scrolling to see what exists. Two up keeps the set readable
   as a set — which is the only reason they are cards and not a list. */
@media (max-width: 560px) {
  .decks { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
  .deck { min-height: 0; padding: 14px; }
  .deck__name { font-size: 16px; margin-top: 10px; }
  .deck__meta { font-size: 11.5px; }
  /* The label wraps at this width and the arrow was left stranded at the far
     right of the second line. The whole card is the link, so the arrow is the
     part that can go. */
  .deck__go { font-size: 11.5px; margin-top: 8px; }
  .deck__go svg { display: none; }
  /* The downloads become two more tap targets in a card the size of a thumb.
     They are on the Unterlagen page and in the presentation itself; here the
     card's one job is to open the deck. */
  .deck__files { display: none; }
}

/* ==========================================================================
   Reading a post
   --------------------------------------------------------------------------
   This was a dialogue box: the picture on top, the text in a small scrolling
   window underneath, and the whole thing capped at 86vh. Every guide on modals
   says the same thing about that arrangement — if the content has to be
   scrolled, it is a page and not a modal. So it reads like a page now, inside
   the shell the site already has.

   The column is 68 characters wide. Four decades of typography research keep
   landing between fifty and seventy-five, with sixty-six as the middle, and
   the difference between that and a full-width paragraph is the difference
   between reading and scanning.
   ========================================================================== */

.modal--wide .modal__panel {
  width: min(1180px, 100%);
  max-height: 94vh;
}

/* The reader carries its own way out, in the bar and labelled. The modal's
   round × is then a second close button for the same window: it sits behind
   the bar's right end and shows as a pale corner nobody can name. One exit. */
.modal__panel:has(.reader) .modal__close {
  display: none;
}

.reader {
  max-height: 94vh;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  scrollbar-color: var(--line-strong) transparent;
}

.reader__bar {
  position: sticky;
  top: 0;
  z-index: 3;
  display: flex;
  align-items: center;
  padding: 10px 16px;
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--line);
}
.reader__back {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 0 8px;
  min-height: 30px;
  border: 0;
  background: transparent;
  color: var(--fg-dim);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
  border-radius: var(--r-sm);
}
.reader__back svg {
  transform: rotate(180deg);
}
@media (hover: hover) and (pointer: fine) {
  .reader__back:hover {
    color: var(--fg);
  }
}
/* How far through the piece the reader is. */
.reader__progress {
  position: absolute;
  left: 0;
  bottom: -1px;
  width: 100%;
  height: 2px;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform 90ms linear;
}

/* The cover introduces the piece; it does not fill the screen. Contained, not
   cropped: half the shop's photographs are portrait. */
.reader__cover {
  position: relative;
  height: min(320px, 34vh);
  border-bottom: 1px solid var(--line);
}
.reader__cover[data-tone='light'] { background: var(--plate-light); }
.reader__cover[data-tone='dark'] { background: var(--plate-dark); }
.reader__cover img {
  position: absolute;
  inset: 18px;
  width: calc(100% - 36px);
  height: calc(100% - 36px);
  object-fit: contain;
}

.reader__col {
  /* Measured, not assumed. `68ch` sounds like sixty-eight characters and is
     not: `ch` is the width of a zero, and in Canaro the average letter is
     narrower than that — 68ch came out at ninety-three characters a line,
     half again the width the research recommends. This is the width that
     actually fits about sixty-eight of them, padding included. */
  width: min(620px, 100%);
  margin: 0 auto;
  padding: clamp(24px, 4vw, 44px) clamp(18px, 4vw, 28px) clamp(40px, 6vw, 72px);
}
.reader__title {
  margin: 10px 0 14px;
  font-size: clamp(23px, 3.4vw, 34px);
  line-height: 1.12;
}
.reader__lead {
  margin: 0 0 26px;
  color: var(--fg-muted);
  font-size: clamp(15px, 1.9vw, 18px);
  line-height: 1.65;
}
/* Longer measure, more air between the lines: the two go together. */
.reader__body {
  font-size: clamp(15px, 1.8vw, 17px);
  line-height: 1.75;
}
.reader__body > * + * {
  margin-top: 18px;
}

/* Comments under the post ----------------------------------------------------
   Open, the way comments under a video are open. The form starts as one line:
   a form that opens with four fields is a form that gets read rather than
   filled in, and the remark it loses — "the terminal is labelled differently on
   mine" — is the reason to have comments at all. */
.talkback {
  margin-top: clamp(36px, 6vw, 64px);
  padding-top: clamp(24px, 4vw, 36px);
  border-top: 1px solid var(--line);
}
.talkback__title {
  margin: 0 0 18px;
  font-size: clamp(16px, 2vw, 20px);
}
.talkback__empty {
  margin: 20px 0 0;
  color: var(--fg-dim);
  font-size: 14px;
}

.talkback__box {
  margin-bottom: 26px;
}
/* One line until it is touched: no border, no box, just the invitation. */
.talkback__line {
  width: 100%;
  padding: 8px 0;
  border: 0;
  border-bottom: 1px solid var(--line);
  background: transparent;
  color: var(--fg);
  font: inherit;
  font-size: 15px;
  line-height: 1.6;
  resize: none;
  overflow: hidden;
  transition: border-color var(--t) var(--ease-out);
}
.talkback__line:focus {
  outline: none;
  border-bottom-color: var(--accent);
}
.talkback__line::placeholder {
  color: var(--fg-dim);
}

.talkback__more {
  margin-top: 12px;
}
.talkback__who {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 8px;
  margin-bottom: 12px;
}
.talkback__who input {
  width: 100%;
  min-width: 0;
  padding: 9px 11px;
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--surface);
  color: var(--fg);
  font: inherit;
  font-size: 14px;
  transition: border-color var(--t) var(--ease-out), background var(--t) var(--ease-out);
}
.talkback__who input:focus {
  outline: none;
  border-color: var(--accent-text);
  background: var(--surface-2);
}
.talkback__actions {
  display: flex;
  align-items: center;
  gap: 8px;
}
.talkback__hint {
  flex: 1;
  font-size: 12px;
  color: var(--fg-dim);
}

/* Answering somebody: the form moves into the thread and says whose reply it
   is, with the way out on the same line. */
.talkback__box--reply {
  margin: 10px 0 22px 26px;
  padding-left: 16px;
  border-left: 2px solid var(--accent);
}
.talkback__replying {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 6px;
  font-size: 12px;
  color: var(--fg-dim);
}
/* The label shares its line with a round button, so it is nudged rather than
.talkback__cancelreply {
  display: grid;
  place-items: center;
  width: 20px;
  height: 20px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--surface-2);
  color: var(--fg-dim);
  font-size: 14px;
  line-height: 1;
  cursor: pointer;
}
@media (hover: hover) and (pointer: fine) {
  .talkback__cancelreply:hover {
    background: var(--surface);
    color: var(--fg);
  }
}

/* A field no human sees and no human fills in — the cheapest spam filter there
   is, and it costs the person writing nothing. */
.talkback__trap {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}
.talkback__note {
  margin: 10px 0 0;
  padding: 9px 11px;
  border: 1px solid var(--line-strong);
  border-radius: var(--r);
  font-size: 13px;
  color: var(--fg-muted);
}
.talkback__note {
  padding-top: 9px;
  padding-bottom: 9px;
}

.cmt {
  padding: 14px 0;
  border-top: 1px solid var(--line);
}
.cmt:first-child {
  border-top: 0;
}
/* A reply is indented once and never twice: past one level a thread becomes a
   staircase nobody can read on a phone. */
.cmt--reply {
  margin: 12px 0 0 26px;
  padding: 12px 0 0 16px;
  border-top: 0;
  border-left: 2px solid var(--line);
}
.cmt__head {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 4px;
}
.cmt__who {
  font-size: 13px;
  font-weight: 600;
}
.cmt__firm,
.cmt__when {
  font-size: 11px;
  color: var(--fg-dim);
}
/* Which desk answered, beside the mark. Technik and Vertrieb answer different
   questions, and the reader can tell whose answer they are reading. */
.cmt__desk {
  font-size: 13px;
  font-weight: 600;
  color: var(--accent-text);
}
.cmt__body {
  margin: 0;
  font-size: 15px;
  line-height: 1.65;
  color: var(--fg-muted);
  white-space: pre-wrap;
}
/* Ours is marked, not framed: the name carries it, and the seal says why the
   name can be believed. Anybody can type "Balter" into a name field; only the
   panel can write a comment the server files as ours. */
.cmt--ours {
  border-left-color: var(--accent);
}
.cmt--ours .cmt__who {
  color: var(--accent-text);
}
.cmt__seal {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  height: 18px;
  padding: 0 7px;
  border: 1px solid color-mix(in srgb, var(--accent) 45%, transparent);
  border-radius: 999px;
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  color: var(--accent-text);
  font-size: 10px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.cmt__reply {
  margin-top: 8px;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--fg-dim);
  font: inherit;
  font-size: 12px;
  cursor: pointer;
}
@media (hover: hover) and (pointer: fine) {
  .cmt__reply:hover {
    color: var(--fg);
  }
}

@media (max-width: 640px) {
  .talkback__who {
    grid-template-columns: 1fr;
  }
  .talkback__box--reply {
    margin-left: 14px;
    padding-left: 12px;
  }
}
