/* ===========================================================================
   Ōmata — de laptop-mockup direct onder de hero, met "Hoe het werkt" erin.

   NÁ pagina.css LADEN. Een paar regels hieronder overschrijven .steps en
   .body uit pagina.css op gelijke specificiteit, en dan beslist de volgorde.
   Alle kleuren komen uit de bestaande tokens; de hoofdpagina draagt
   data-hero="donker", dus --hero-bg is hier #182019.

   DE OPBOUW IS OMGEKEERD TEN OPZICHTE VAN DE OVERDRACHT, en dat is met opzet.
   Wat hier als basis staat is de leesbare versie: de foto met de pagina erin
   als beeld op volle breedte, en daaronder de vijf stappen als gewone
   blokken. Die versie geldt zonder javascript, op een telefoon en bij
   prefers-reduced-motion — dezelfde drie gevallen waarin het scrollverhaal
   niet werkt of niet gewenst is.

   Het scrollverhaal staat onder .mockup--scene, en die klasse zet
   hero-mockup.js er alleen op als het kan lopen. Stond het omgekeerd, dan
   zou de bezoeker zonder javascript een kleefsectie van 640vh krijgen waarin
   niets beweegt, en zou de tekst van het gesprek wel in de bron staan maar
   voor niemand zichtbaar zijn. Dat laatste is precies het verschil tussen
   bron en beeld dat controle.py elders tegenhoudt.
   =========================================================================== */

/* --mockup-bg en niet --hero-bg: de sectie staat sinds 17 september 2026 op de
   kleur van de uitdoving boven stap 1 en onder stap 5 (was #182019, de kleur
   van de hero erboven). Die uitdoving stond als een zwarte schijn in het vlak;
   nu is ze onzichtbaar omdat het vlak eronder dezelfde kleur heeft. Eén token
   voor beide, want een tweede hex hier is precies de hex die weer uiteen gaat
   lopen. De rand waar deze sectie over de hero schuift, is dus een kleurstap. */
.mockup { --mockup-bg: #0b100d; position: relative; background: var(--mockup-bg); color: var(--hero-text); }

/* --- de basis: beeld boven, stappen eronder ----------------------------- */

.mockup__kader { position: relative; }

/* Het beeldvlak van de foto. container-type maakt 100cqw hieronder de
   breedte van dít vlak, en daarmee schaalt de pagina in het scherm zonder
   javascript exact mee. */
.mockup__beeld {
  position: relative; width: 100%; aspect-ratio: 16 / 9;
  container-type: inline-size; overflow: hidden;
}

/* De foto is een uitsnede: de laptop met een doorzichtige achtergrond. Wat
   erachter ligt is de egale kleur van de sectie zelf (--hero-bg) en niets
   anders — de studio-achtergrond van de foto, met haar kringen en haar
   vignet, is er sinds 15 september 2026 uit. Ze was onscherp en donkerder dan
   de sectie, en daarmee viel de foto als een vlek in de pagina. */
.mockup__foto { position: absolute; inset: 0; }
.mockup__foto img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* Het scherm rekent in een kader van 1920x1080 — zo staan alle maten in de
   javascript en in de meetkunde hieronder — en wordt als geheel naar de
   breedte van het beeldvlak geschaald.

   Die schaal komt van twee kanten, en dat is met opzet: hero-mockup.js zet
   --mockup-vlak zodra er javascript is, en de terugval erachter rekent hem
   uit de containerbreedte (100cqw / 1920px — een lengte door een lengte,
   dus een getal; dat mag sinds Values 4 en Safari kent het vanaf 16.4).
   Zonder de eerste blijft het beeld goed in elke browser die de tweede
   kent; zonder beide zou het scherm buiten de foto vallen.

   De matrix is de stand uit hero-mockup.js bij voortgang 0: de pagina
   projectief in het glas van de foto. Hij staat hier hard in de css zodat de
   mockup ook zonder javascript in het scherm ligt en niet over de hele foto.
   Wijzigen de vier hoeken in hero-mockup.js, dan hoort deze waarde mee te
   wijzigen (de log van `homografie(1920, 1080, VLAK)`). */
.mockup__scherm {
  position: absolute; left: 0; top: 0; width: 1920px; height: 1080px;
  transform-origin: 0 0; overflow: hidden;
  scale: var(--mockup-vlak, calc(100cqw / 1920px));
  transform: matrix3d(0.510417, 0, 0, 0, 0.017099, 0.499392, 0, 0.00002, 0, 0, 1, 0, 470, 130, 0, 1);
}
.mockup__pagina { position: absolute; inset: 0; background: #0d120f; }
.mockup__ringen {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; object-position: 50% 42%;
}

/* --- de hero in het scherm ---------------------------------------------- */

.mockup__hero {
  position: absolute; inset: 0; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 34px; padding: 0 120px;
}
.mockup__kop {
  margin: 0; max-width: 17ch; text-align: center; font-size: 92px; line-height: 1.06;
  font-weight: 600; letter-spacing: -0.03em; color: var(--washi);
}
.mockup__lead { margin: 0; font-size: 28px; white-space: nowrap; color: color-mix(in srgb, var(--washi) 80%, transparent); }

.mockup__balk {
  margin-top: 14px; width: min(920px, 100%); display: flex; align-items: center; gap: 12px;
  padding: 12px; border-radius: 26px;
  background: color-mix(in srgb, var(--hero-bg) 50%, transparent);
  border: 1px solid color-mix(in srgb, var(--washi) 20%, transparent);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.20), 0 18px 40px rgba(0, 0, 0, 0.28);
}
/* GEEN backdrop-filter, en dat is met opzet — de echte balk in het hero heeft
   er wel een. Deze balk zit in een laag die per frame projectief herrekend
   wordt, en een blur-achter-het-vlak dwingt de browser dan elk frame de
   ondergrond opnieuw te vervagen. Dat is de duurste regel die je in een
   scrollverhaal kan zetten; op 1920x1080 zakt het beeld er zichtbaar door.
   In een scherm van een foto ziet niemand het verschil met een vlak. */
.mockup__klem, .mockup__stuur {
  flex: none; display: flex; align-items: center; justify-content: center;
  width: 56px; height: 56px; border-radius: 17px;
  background: color-mix(in srgb, var(--washi) 10%, transparent); color: var(--washi);
}
.mockup__stuur { border-radius: 999px; background: color-mix(in srgb, var(--washi) 72%, transparent); color: var(--sumi-deep); }
.mockup__veld {
  flex: 1; min-width: 0; padding: 17px 20px; border-radius: 17px;
  background: var(--sumi-deep); font-size: 26px;
  color: color-mix(in srgb, var(--washi) 52%, transparent);
}

/* De sluier onder het gesprek. Buiten het scrollverhaal staat ze op nul: dan
   is het scherm de hero en niets anders. */
.mockup__sluier {
  position: absolute; inset: 0; opacity: 0;
  background: linear-gradient(270deg, rgba(10, 15, 12, 0.82) 0%, rgba(10, 15, 12, 0.42) 46%, rgba(10, 15, 12, 0.20) 100%);
}

/* --- het gesprek --------------------------------------------------------
   Buiten het scrollverhaal is dit een gewoon leesbaar blok onder de stappen:
   op een telefoon en zonder javascript is een gesprek in het scherm van de
   foto een paar pixels hoog, en dan is het geen illustratie meer maar ruis.
   ----------------------------------------------------------------------- */
.mockup__chat {
  margin: 30px var(--gutter) 0; padding: 16px;
  display: flex; flex-direction: column;
  border-radius: 18px; background: #171514;
  border: 1px solid color-mix(in srgb, var(--washi) 14%, transparent);
}
.mockup__chatkop { padding: 0 0 12px; border-bottom: 1px solid color-mix(in srgb, var(--washi) 14%, transparent); }
.mockup__chattitel { margin: 0; font-size: 17px; font-weight: 600; letter-spacing: -0.01em; }
.mockup__chatstand { display: flex; align-items: center; gap: 8px; margin: 5px 0 0; font-size: 13px; color: #9d968e; }
.mockup__chatstand span { width: 8px; height: 8px; border-radius: 50%; background: #6e9179; flex: none; }

.mockup__berichten {
  flex: 1; min-height: 0; list-style: none; margin: 0; padding: 14px 0;
  display: flex; flex-direction: column; justify-content: flex-end; gap: 9px; overflow: hidden;
}
.mockup__bericht {
  flex: none; max-width: 88%; padding: 11px 15px; border-radius: 16px;
  font-size: 16px; line-height: 1.5;
}
.mockup__bericht--klant { align-self: flex-end; background: var(--washi); color: var(--sumi-deep); }
.mockup__bericht--omata {
  align-self: flex-start; background: color-mix(in srgb, var(--washi) 10%, transparent);
  color: var(--washi); border: 1px solid color-mix(in srgb, var(--washi) 16%, transparent);
}
.mockup__bericht--stand {
  align-self: center; max-width: none; display: flex; align-items: center; gap: 9px;
  padding: 9px 14px; border-radius: 999px; font-size: 14px;
  background: color-mix(in srgb, var(--washi) 8%, transparent);
  border: 1px solid color-mix(in srgb, var(--washi) 20%, transparent);
  color: color-mix(in srgb, var(--washi) 86%, transparent);
}
.mockup__bericht--stand::before { content: ""; width: 8px; height: 8px; border-radius: 999px; background: var(--accent2); flex: none; }

.mockup__invoer { display: flex; align-items: center; gap: 10px; padding: 12px 0 0; border-top: 1px solid color-mix(in srgb, var(--washi) 14%, transparent); }
.mockup__invoerveld {
  flex: 1; padding: 11px 14px; border-radius: 13px; background: #221f1e;
  border: 1px solid color-mix(in srgb, var(--washi) 14%, transparent);
  font-size: 15px; color: color-mix(in srgb, var(--washi) 46%, transparent);
}
/* De verstuurknop. Licht vlak, donkere pijl — en dat is geen smaakkeuze maar
   wat de knop waar dit een afbeelding van is, werkelijk doet.

   STOND OP var(--shu) MET EEN LICHTE PIJL, tot 15 september 2026. Dat was de
   knop van toen: een groene schijf. De echte balk op de homepage is daar
   inmiddels van weg — .heroballk__send in hoofdpagina-glas.css staat op
   --washi met --sumi-deep — en deze mockup was dus een plaatje van een knop
   die niet meer bestond. Op de donkere band eronder las dat groen bovendien
   dof: --shu is de groene van het lichte papier, niet die van een donker
   vlak.

   Let op de buur hierboven: .mockup__stuur stond al op washi met een donkere
   pijl. Binnen dit ene bestand stonden de twee verstuurknoppen dus al niet
   meer in dezelfde kleur. */
.mockup__invoerstuur { flex: none; display: flex; align-items: center; justify-content: center; width: 38px; height: 38px; border-radius: 999px; background: var(--washi); color: var(--sumi-deep); }
.mockup__invoerstuur svg { width: 18px; height: 18px; }

/* --- de vijf stappen ----------------------------------------------------
   Dezelfde <ol class="steps"> die tot 15 september 2026 in de sectie "Hoe
   het werkt" stond, met dezelfde tekst: het HowTo-blok in de bron spiegelt
   ze woord voor woord en controle_howto() in controle.py houdt dat vast.
   Alleen de opmaak is die van de mockup — cijfer in een rondje met het
   label ernaast — in plaats van het raster uit pagina.css. */
.mockup__stappen { padding: 34px var(--gutter) 0; }
/* De kleur hoort hier, en dat is geen smaakkeuze: h2 in pagina.css staat op
   var(--text-primary) — de inkt van het lichte papier — en deze sectie is
   donker. Tot 16 september 2026 stond de kop op een telefoon dus zwart op
   bijna zwart. Op een breed scherm viel het niemand op, want daar is hij
   alleen voor een schermlezer (zie .mockup--scene hieronder). */
.mockup__kopje { margin: 0 0 6px; color: var(--hero-text); }

/* De twee knoppen van de speler: alleen zichtbaar onder .mockup--gsm, dus
   alleen wanneer de speler echt loopt. Zie de toelichting in index.html.

   DE RUIT KOMT UIT glas.css, via .glas en .glas--donker op de knop zelf. Hier
   staat alleen wat afwijkt: de maat, de ronding (999 in plaats van de 22 van
   .glas) en de kleur van de pijl. Vijf eigenschappen hier nog eens zetten zou
   een tweede glas maken dat niet meebeweegt met de rest van de pagina — en de
   terugval in glas.css voor een browser zonder backdrop-filter zou er niet
   voor gelden. hero-mockup.css staat ná glas.css, dus deze ronding wint. */
.mockup__bediening { display: none; }
.mockup__stapknop {
  flex: none; display: flex; align-items: center; justify-content: center;
  width: 46px; height: 46px; padding: 0; border-radius: 999px;
  font: inherit; cursor: pointer; -webkit-tap-highlight-color: transparent;
  color: var(--hero-text);
}
.mockup__stapknop svg { width: 21px; height: 21px; }
/* Het lichte ringetje en niet --shu-deep zoals elders in pagina.css: dat is
   de donkergroene van het lichte papier en die is op dit vlak niet te zien.
   Zelfde afweging als .gesprekken__spoor:focus-visible. */
.mockup__stapknop:focus-visible { outline: 3px solid var(--washi); outline-offset: 2px; }
.mockup__stapknop:disabled { opacity: 0.32; cursor: default; }

.mockup .steps { margin: 0; padding: 0; list-style: none; display: block; }
.mockup .steps > li {
  display: block; padding: 26px 0;
  border-top: 1px solid color-mix(in srgb, var(--washi) 14%, transparent);
  border-bottom: 0;
}
.mockup .steps > li:first-of-type { border-top: 0; }
/* De teller boven de kop: 01 / 05. Stond hier tot 15 september 2026 als een
   cijfer in een rondje met "Stap 1 van 5" ernaast. Dat waren drie dingen die
   hetzelfde zeiden, en het rondje trok het oog naar het cijfer in plaats van
   naar de kop. Deze vorm zegt het in één regel — en ze hoeft niet vertaald te
   worden, want cijfers zijn in de drie talen dezelfde. */
.mockup__stapteller {
  margin: 0 0 10px; font-size: 13px; font-weight: 600; letter-spacing: 0.18em;
  font-variant-numeric: tabular-nums; color: var(--hero-text);
}
.mockup__stapvan { color: color-mix(in srgb, var(--hero-text) 45%, transparent); }
.mockup .steps h3 { margin: 0 0 8px; font-size: 24px; line-height: 1.12; letter-spacing: -0.02em; color: var(--hero-text); }
.mockup .steps .body { margin: 0; font-size: 16px; line-height: 1.6; max-width: var(--max-read); color: color-mix(in srgb, var(--hero-text) 82%, transparent); }

@media (max-width: 480px) {
  .mockup .steps h3 { font-size: 21px; }
  .mockup .steps .body { font-size: 15px; }
}

/* ===========================================================================
   HET SCROLLVERHAAL. Alleen met .mockup--scene, en die zet hero-mockup.js
   erop bij meer dan 900px en zonder prefers-reduced-motion.

   De sectie plakt en de voortgang komt uit de scrollpositie: geen autoplay,
   geen knoppen die om aandacht vragen, en niets dat doorloopt terwijl de
   bezoeker leest. Het spoor bepaalt hoe lang ze plakt — ongeveer één
   schermhoogte per stap plus de inrit. Korter en de stappen vliegen voorbij.
   =========================================================================== */

/* 900vh en niet 640: het bereik van de sectie gaat in vijf banden van één
   stap (zie stapBij() in hero-mockup.js), en een band hoort minstens zo veel
   scroll te zijn als één trackpadveeg. Bij 640vh was een band 108vh en zette
   een gewone veeg anderhalve stap; bij 900vh is ze 160vh en zet een gewone
   veeg één stap. Nagemeten met bouwscripts/meet_toneel.mjs. */
.mockup--scene .mockup__spoor { height: 900vh; }
.mockup--scene .mockup__kleefvlak { position: sticky; top: 0; height: 100vh; overflow: hidden; }

/* Het kader is altijd 16:9 en staat gecentreerd; alle maten in de javascript
   gaan uit van 1920x1080, dus hier schalen we dat kader één keer. */
.mockup--scene .mockup__kader {
  position: absolute; left: 50%; top: 50%;
  width: 1920px; height: 1080px; margin: -540px 0 0 -960px;
  transform: scale(var(--mockup-fit, 1)); transform-origin: 50% 50%;
}
.mockup--scene .mockup__beeld { position: absolute; inset: 0; aspect-ratio: auto; overflow: visible; }
/* ===========================================================================
   DE TWEE STANDEN VAN HET TONEEL, en waarom de beweging hier staat en niet
   in de javascript.

   Tot 16 september 2026 schreef hero-mockup.js elk frame de matrix, de
   dekkingen en de kaartpositie als inline stijl, met een eigen easing en een
   eigen gespeelde scroll. Dat is weg. De javascript zet twee dingen — de
   klasse is-vol (de laptop is paneel geworden) en de positie van de kaart —
   en de overgangen hieronder doen de beweging. Transform en opacity zijn de
   twee eigenschappen die de browser buiten de hoofddraad kan animeren.

   De duren staan in hero-mockup.js (MORF en STAPDUUR) en komen hier binnen
   als --mockup-morf en --mockup-stap: de poort die bepaalt wanneer de
   volgende stap mag, rekent met dezelfde getallen. De waarden achter de
   komma zijn de terugval als het script niet aan bod komt.

   De vertragingen zetten de choreografie: eerst staat de laptop er gewoon,
   dan rijdt de camera in en dooft de foto, en pas als het scherm bijna
   paneel is komen de sluier, het gesprek en de kaart op.
   =========================================================================== */
.mockup--scene { --mockup-bocht: cubic-bezier(0.22, 0.61, 0.36, 1); }

/* Het kader komt op als de sectie ruim binnen is; de javascript zet is-in. */
.mockup--scene .mockup__kader { opacity: 0; transition: opacity 260ms linear; }
.mockup--scene.is-in .mockup__kader { opacity: 1; }

.mockup--scene .mockup__scherm {
  scale: none; box-shadow: 0 24px 60px rgba(0, 0, 0, 0.5);
  /* De drie standen komen uit hero-mockup.js (VLAK, PANEEL en CAM_DIEP, via
     homografie()); dat is de enige plek waar ze gerekend worden. De waarden
     achter de komma zijn diezelfde getallen, hier uitgeschreven zodat in de
     css te lezen staat wat er gebeurt. */
  transform: var(--mockup-laptop,
    matrix3d(0.510417, 0, 0, 0, 0.017099, 0.499392, 0, 0.00002, 0, 0, 1, 0, 470, 130, 0, 1));
  border-radius: 0; will-change: transform;
  /* ALLEEN transform IN DE OVERGANG, en border-radius er met opzet uit. Een
     hoekradius is niet samen te stellen op de compositor: animeer je hem, dan
     moet de browser dit vlak élk frame opnieuw verven — 1920 bij 1080, met de
     hele pagina erin en een schaduw van 60px eromheen. En een laag die elk
     frame verft, kan haar transform niet meer op de compositor houden. */
  transition: transform var(--mockup-morf, 1500ms) var(--mockup-bocht);
}
.mockup--scene.is-vol .mockup__scherm {
  transform: var(--mockup-paneel,
    matrix3d(0.578125, 0, 0, 0, 0, 0.577778, 0, 0, 0, 0, 1, 0, 780, 228, 0, 1));
  border-radius: 20px;
}

.mockup--scene .mockup__foto {
  will-change: transform, opacity;
  transition: transform var(--mockup-morf, 1500ms) var(--mockup-bocht),
              opacity calc(var(--mockup-morf, 1500ms) * 0.5) linear
                      calc(var(--mockup-morf, 1500ms) * 0.18);
}
.mockup--scene.is-vol .mockup__foto {
  opacity: 0; transform: var(--mockup-cam, translate(40px, 160px) scale(1.7));
}

/* Wordt de pagina een paneel, dan gaat de hero eronder bijna uit: het gesprek
   staat er dan over en een halve kop van 92px die achter het chatvenster
   uitkomt, leest als een fout. */
.mockup--scene .mockup__hero {
  transition: opacity calc(var(--mockup-morf, 1500ms) * 0.34) linear
                      calc(var(--mockup-morf, 1500ms) * 0.24);
}
.mockup--scene.is-vol .mockup__hero { opacity: 0.08; }

.mockup--scene .mockup__sluier {
  will-change: opacity;
  transition: opacity calc(var(--mockup-morf, 1500ms) * 0.4) linear
                      calc(var(--mockup-morf, 1500ms) * 0.4);
}
.mockup--scene.is-vol .mockup__sluier { opacity: 1; }

/* GEEN OVERGANG, VOOR ÉÉN FRAME. Wie de laptop al gezien heeft of van onderen
   terugkomt, krijgt het paneel meteen; zonder dit zou de morf alsnog spelen,
   en van onderen zelfs achterstevoren. De javascript zet deze klasse erop,
   legt de stand vast en haalt hem er in hetzelfde blok weer af. */
.mockup--scene.mockup--kaal, .mockup--scene.mockup--kaal * { transition: none !important; }

.mockup--scene.is-vol .mockup__stappen, .mockup--scene.is-vol .mockup__chat { opacity: 1; }
.mockup--scene.is-vol .mockup__chat { transform: none; }

/* De kop hoort bij de sectie, niet bij het toneel: in het scrollverhaal
   staat hij alleen voor een schermlezer, net als .visually-hidden. */
.mockup--scene .mockup__kopje {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

/* Het gesprek in het scrollverhaal: over het paneel, in de maten van het
   kader van 1920x1080. Het paneel loopt van 820,248 tot 1860,833 (zie PANEEL
   in hero-mockup.js); dit vak zit daar met 44 marge in. Wijzigt PANEEL, dan
   wijzigen deze vier waarden mee. */
.mockup--scene .mockup__chat {
  position: absolute; left: 864px; top: 292px; width: 952px; height: 497px;
  margin: 0; padding: 0; border-radius: 24px; overflow: hidden;
  box-shadow: 0 30px 70px rgba(0, 0, 0, 0.5);
  opacity: 0; transform: translateY(40px); will-change: opacity, transform;
  transition: opacity calc(var(--mockup-morf, 1500ms) * 0.42) linear
                      calc(var(--mockup-morf, 1500ms) * 0.42),
              transform calc(var(--mockup-morf, 1500ms) * 0.5) var(--mockup-bocht)
                        calc(var(--mockup-morf, 1500ms) * 0.42);
}
.mockup--scene .mockup__chatkop { padding: 22px 26px 20px; }
.mockup--scene .mockup__chattitel { font-size: 25px; }
.mockup--scene .mockup__chatstand { gap: 9px; margin-top: 8px; font-size: 20px; }
.mockup--scene .mockup__chatstand span { width: 10px; height: 10px; }
/* Het gesprek staat onderaan in het vak, zoals een chat waarin net iets
   nieuws is gekomen. Wat er bovenuit loopt, doven we uit in plaats van het
   hard af te snijden: een bericht dat halverwege tegen de kop aan botst,
   leest als een fout in de opmaak. */
.mockup--scene .mockup__berichten {
  padding: 22px 26px; gap: 14px;
}
/* Het bericht dat bovenuit loopt, dooft uit in plaats van hard afgesneden te
   worden. GEEN mask-image: alles wat binnen een masker beweegt moet door die
   maskerlaag heen samengesteld worden, en dan kan de browser de beweging niet
   zelfstandig afhandelen. Een gradiënt in de kleur van het chatvenster
   (#171514) is visueel hetzelfde en wordt één keer geverfd. De lijst begint
   op 85 en is 431 hoog (opgemeten); het masker doofde de eerste 11% uit. */
.mockup--scene .mockup__chat::after {
  content: ""; position: absolute; left: 0; right: 0; top: 85px; height: 47px;
  pointer-events: none;
  background: linear-gradient(180deg, #171514 0%, rgba(23, 21, 20, 0) 100%);
}
/* WAT NOG NIET GEZEGD IS, STAAT OOK NIET IN DE OPMAAK, en dat is het verschil
   tussen een gesprek dat opkomt en een leeg venster. De berichten staan
   onderaan in het vak; een bericht dat alleen doorzichtig is, houdt zijn plek
   en duwt de berichten die er wél al zijn boven de rand van het vak. Bij de
   eerste stappen stond het venster daarom leeg, en pas bij stap 4 kwam er
   tekst in beeld — met genoeg berichten viel er weer iets binnen het vak.

   Daarom display: none en niet opacity: 0. Het opkomen doet een keyframe en
   geen transitie: een transitie loopt niet op een element dat net van
   display verandert. */
/* Het opkomen zelf geldt in beide standen: het scrollverhaal op een breed
   scherm en de speler op een telefoon laten dezelfde berichten op dezelfde
   manier komen, alleen in andere maten. Vandaar :is() en geen tweede kopie —
   een kopie van deze drie regels is precies de kopie die uiteen gaat lopen
   zodra er aan één kant iets wijzigt. */
.mockup--scene .mockup__bericht {
  max-width: 84%; padding: 16px 22px; border-radius: 22px; font-size: 25px; line-height: 1.45;
}
:is(.mockup--scene, .mockup--gsm) .mockup__bericht { display: none; }
:is(.mockup--scene, .mockup--gsm) .mockup__bericht.is-op { display: block; }
:is(.mockup--scene, .mockup--gsm) .mockup__bericht--stand.is-op { display: flex; }
/* EN VERDER GEBEURT ER HIER NIETS. Tot 16 september 2026 kwamen de berichten
   van een stap een voor een op, met een keyframe per bel. Dat voelde laggy en
   daar is een reden voor: deze berichten hangen in het kader van 1920 bij
   1080. Elke bel die erbij kwam, was een nieuwe indeling én een nieuwe verf
   van die hele laag, en dat midden in een scroll. Nu is elke stap één
   stilstaand beeld: het gesprek tot en met die stap staat er in één keer. */
.mockup--scene .mockup__bericht--stand { padding: 12px 20px; font-size: 22px; gap: 12px; }
.mockup--scene .mockup__bericht--stand::before { width: 9px; height: 9px; }
.mockup--scene .mockup__invoer { padding: 20px; gap: 12px; }
.mockup--scene .mockup__invoerveld { padding: 16px 20px; border-radius: 16px; font-size: 23px; }
.mockup--scene .mockup__invoerstuur { width: 54px; height: 54px; }
.mockup--scene .mockup__invoerstuur svg { width: 24px; height: 24px; }

/* De stapkaart: kolom van 492 links, de actieve stap in het midden, per stap
   330px omhoog, de buren weggedoofd en boven en onder uitgemaskeerd. */
.mockup--scene .mockup__stappen {
  position: absolute; left: 72px; top: 0; bottom: 0; width: 660px;
  padding: 0; overflow: hidden; opacity: 0; will-change: opacity;
  transition: opacity calc(var(--mockup-morf, 1500ms) * 0.42) linear
                      calc(var(--mockup-morf, 1500ms) * 0.42);
  /* Het venster laat de stap ervoor en erna voor een stuk zien: zij staan
     vervaagd (de dekking komt uit hero-mockup.js), en het masker dooft ze aan
     de rand uit. Stond tot 16 september 2026 op 22%/78%: dat venster was
     krap anderhalf slot hoog en dan is er van de buren niets te zien. */
}
/* Om dezelfde reden als in het gesprek: een gradiënt in de sectiekleur en
   geen masker, want in dit vak beweegt de kolom en doven vijf kaarten. */
.mockup--scene .mockup__stappen::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  /* Dezelfde kleur als de sectie, uit hetzelfde token — zie de kop bij .mockup.
     De stop op alpha 0 is transparent: een verloop mengt vermenigvuldigd met
     de dekking, dus daar komt geen grijs tussen. */
  background: linear-gradient(180deg, var(--mockup-bg) 0%, transparent 10%,
                              transparent 90%, var(--mockup-bg) 100%);
}
.mockup--scene .steps {
  position: absolute; left: 0; top: 50%; width: 100%; will-change: transform;
  /* De stand van stap 1; de javascript schrijft hem bij elke wissel. */
  transform: translateY(-240px);
  transition: transform var(--mockup-stap, 560ms) var(--mockup-bocht);
}
/* 480 is de hoogte van één slot en staat ook als SLOT in hero-mockup.js. */
.mockup--scene .steps > li {
  height: 480px; padding: 0; border: 0;
  display: flex; flex-direction: column; justify-content: center;
  /* De dekking komt van de javascript: de stap zelf op 1, de buren op
     BUURDEKKING, de rest op 0. Nul als beginstand, zodat er niets staat
     voordat de eerste stap gezet is. */
  opacity: 0;
  transition: opacity var(--mockup-stap, 560ms) var(--mockup-bocht);
}
.mockup--scene .mockup__stapteller { margin-bottom: 22px; font-size: 20px; }
.mockup--scene .steps h3 { margin: 0 0 20px; font-size: 46px; }
/* De uitleg op leesmaat: 28 in de ruimte van het kader is op een venster van
   1440 ongeveer 21px op het scherm. Op 20 was het 15px, en dat is klein voor
   de enige echte tekst van deze sectie. */
.mockup--scene .steps .body { font-size: 28px; line-height: 1.55; max-width: none; }

/* ===========================================================================
   DE SPELER OP EEN TELEFOON. Alleen met .mockup--gsm, en die zet
   hero-mockup.js erop tot en met 900px, buiten prefers-reduced-motion.

   WAT HIER ANDERS IS DAN HET SCROLLVERHAAL, en waarom.

   De laptop gaat eruit. Op 375px is het beeldvlak in de foto nog 190px breed:
   een pagina van 1920 bij 1080 wordt daarin 5,5 keer verkleind, en dan is de
   kop van 92px zeventien pixels hoog en het gesprek onleesbaar. Wat er stond
   was dus een foto van een laptop met een vlek erin, en daarboven nog eens
   dezelfde hero die de bezoeker net in het echt gezien heeft. Eén beeld te
   veel, en het beeld dat het minst zegt.

   De stappen komen één voor één, met het gesprek eronder mee. Dat is dezelfde
   film als op een breed scherm — dezelfde tijden uit hero-mockup.js, dezelfde
   berichten op dezelfde plek in het gesprek — maar zonder kleefsectie en
   zonder dat er iets aan de scroll hangt. **Op een telefoon is de scroll van
   de bezoeker en van niemand anders**: een sectie die het wiel afpakt, of die
   de vinger volgt om een camera te bewegen, is op een aanraakscherm het
   verschil tussen een pagina die werkt en een pagina die vastzit.

   De vijf stappen liggen daarom in één rastercel op elkaar. Zo is de hoogte
   van het vak die van de langste stap en verspringt de pagina niet bij elke
   wissel — een blok dat onder je vinger van hoogte verandert, leest als een
   fout, en op een telefoon scrol je precies daar.
   =========================================================================== */

/* DE LAPTOP GAAT OP EEN TELEFOON ALTIJD WEG, ook wanneer de speler niet
   loopt: zonder javascript en bij prefers-reduced-motion evengoed. Wat er dan
   staat zijn de vijf stappen met het gesprek eronder, en die zijn te lezen —
   de foto was dat niet. Daarom een mediaquery en geen regel onder
   .mockup--gsm. De 900 is dezelfde grens als in hero-mockup.js; wijzigt de
   ene, dan hoort de andere mee. */
@media (max-width: 900px) {
  .mockup__beeld { display: none; }
}

/* Zo hoog als het kan: de lucht boven de kop en tussen de stap en het gesprek
   is tot het bot. Wat eronder overblijft is inhoud — de kop van de sectie, de
   langste van de vijf stappen (het raster houdt die hoogte vrij, zie
   hieronder) en de 32 waarin de pijlen zweven. Van de 303 pixels die hier tot
   16 september 2026 boven het gesprek stonden, blijven er 281 over. */
.mockup--gsm .mockup__stappen { padding-top: 10px; }

.mockup--gsm .steps { display: grid; }
/* Geen visibility: hidden en geen display: none, met opzet. De vier stappen
   die niet in beeld staan blijven in de toegankelijkheidsboom, precies zoals
   op een breed scherm, waar ze alleen vervaagd naast de actieve staan: een
   schermlezer hoort hier dus alle vijf en hangt niet af van twee knoppen om
   bij de rest te komen. pointer-events houdt tegen dat een tik onzichtbare
   tekst van de stap eronder selecteert. */
.mockup--gsm .steps > li {
  grid-area: 1 / 1; padding: 0; border: 0;
  opacity: 0; pointer-events: none;
}
/* DE OVERGANG STAAT OP DE STAP DIE KOMT EN NIET OP DE VIJF SAMEN, en dat is
   geen detail: de klasse .mockup--gsm komt van de javascript, dus ze landt ná
   de eerste opmaak. Stond de transitie op de regel hierboven, dan zouden de
   vier stappen die niet aan de beurt zijn op dat moment van 1 naar 0 gaan in
   240 ms — een kwart seconde met vijf teksten over elkaar, precies bij het in
   beeld komen. Nu verdwijnt de vorige meteen en komt de volgende op. */
.mockup--gsm .steps > li.is-op {
  opacity: 1; pointer-events: auto;
  transition: opacity 240ms var(--ease);
}

/* DE VOLGORDE OP EEN TELEFOON: de stap boven, het gesprek eronder, en de twee
   pijlen ertussen — zwevend op de bovenrand van het gesprek, rechts, en niet
   als derde blok in de kolom. Zo staan ze bij wat ze verderzetten en kost het
   geen extra hoogte op een scherm dat er weinig van heeft.

   DE PLAATS HANGT AAN ÉÉN GETAL, en dat is geen sierlijkheid: --gsm-luchtje
   zet zowel de afstand tussen de stap en het gesprek als hoe ver de pijlen
   naar beneden zakken. Zet je er één van de twee met de hand, dan zweven ze
   de volgende keer een halve knop naast de rand en is er niets dat het zegt.
   De 50% is de halve knophoogte: daarmee ligt de rij precies op de rand. */
.mockup--gsm { --gsm-luchtje: 32px; }
.mockup--gsm .mockup__stappen { position: relative; }
.mockup--gsm .mockup__chat { margin-top: var(--gsm-luchtje); }
.mockup--gsm .mockup__bediening {
  display: flex; gap: 10px; padding: 0; z-index: 2;
  position: absolute; right: var(--gutter); top: 100%;
  transform: translateY(calc(var(--gsm-luchtje) - 50%));
}

/* HET MOET OP ÉÉN SCHERM PASSEN: de stap boven en het gesprek eronder, samen
   binnen wat er zichtbaar is, zonder te scrollen binnen de sectie.

   Daarom een kolom met een vaste hoogte — één klein schermbeeld min de balk
   bovenaan — waarin de stap zijn eigen hoogte houdt en het gesprek de rest
   opeet. Niet met een vaste hoogte voor dat gesprek: de stap is zo hoog als
   de langste van de vijf, en dat verschilt per taal, per lettergrootte en per
   schermbreedte. Op een venster van 844 komt het gesprek zo op 479 pixels
   uit, op 667 op 302, en onder de 200 gaat het niet.

   100svh en niet 100vh: vh is op een telefoon de hoogte mét de adresbalk
   weggedacht, en dan valt de onderkant van het gesprek erachter zodra die
   balk in beeld staat. --header-h zet pagina.js alleen wanneer de taalmelding
   erbij staat; 68 is de balk zonder die melding.

   Het gesprek houdt dus altijd een vaste hoogte, en dat is ook nodig om een
   andere reden: zonder die hoogte groeit het venster bij elk bericht dat
   erbij komt en schuift alles eronder mee. De berichten staan onderaan
   (justify-content: flex-end in de basis), dus wat nieuw is staat in beeld en
   wat ouder is schuift er bovenuit — met hetzelfde masker als in het
   scrollverhaal, zodat een bericht aan de rand uitdooft in plaats van
   afgesneden te worden. */
.mockup--gsm .mockup__kader {
  display: flex; flex-direction: column;
  height: calc(100svh - var(--header-h, 68px));
  padding-bottom: 16px;
}
.mockup--gsm .mockup__stappen { flex: none; }
.mockup--gsm .mockup__chat { flex: 1 1 auto; height: auto; min-height: 200px; }
/* DE CHATBALK EN DE VERSTUURKNOP ONDERAAN HET GESPREK GAAN OP EEN TELEFOON
   UIT. Ze zijn een afbeelding van een balk waarin je niet kan typen, en op een
   scherm waar dit paneel de volle breedte heeft, leest die afbeelding als de
   echte chat: wie erop tikt krijgt niets, terwijl de echte balk een paar
   honderd pixels hoger op dezelfde pagina staat. Die vergissing is hier
   gratis te vermijden.

   De ruimte die vrijkomt gaat naar het gesprek zelf — de berichten staan
   onderaan in het vak, dus er komt een bericht meer in beeld in plaats van
   lucht onderaan.

   Op een breed scherm blijft de balk staan: daar zit het paneel in een toneel
   en niet onder je duim, en daar hoort het beeld van de chat compleet te zijn.
   Zie de toelichting bij .mockup__invoerstuur hierboven, die de kleur van die
   knop aan de echte knop vastlegt. */
.mockup--gsm .mockup__invoer { display: none; }
/* De kop van het gesprek houdt rechts plaats vrij voor de twee pijlen die er
   boven zweven: 102 pixels knoppen plus de lucht ernaast. Zonder deze regel
   loopt een langere titel ("Démarrez votre demande") eronder door. */
.mockup--gsm .mockup__chatkop { padding-right: 116px; }
.mockup--gsm .mockup__berichten {
  -webkit-mask-image: linear-gradient(180deg, transparent 0%, #000 12%);
  mask-image: linear-gradient(180deg, transparent 0%, #000 12%);
}
