/* ── Spikey ────────────────────────────────────────────────── */
.spikey {
  --msiks:  #1CA3DC;
  --seam:   #c3d6e2;
  --rim:    #b3c9d8;
  --ink:    #16394f;
  --blush:  #ffb3a0;

  position: fixed; right: 24px; bottom: 24px;
  width: 92px; height: 99px;
  z-index: 9000;
  cursor: pointer;
  /* Drie snelle tikken op een bal met ogen mag geen tekstselectie of een zoombeurt
     opleveren. `manipulation` haalt bovendien de dubbeltik-vertraging van 300ms weg, en die
     zit precies in de weg bij het eitje. */
  user-select: none; -webkit-user-select: none;
  touch-action: manipulation;
  /* HIJ ROLT VAN RECHTS BINNEN. De draaiing hoort bij de afgelegde afstand, anders
     glijdt de bal in plaats van te rollen: bij straal 47px is 190px precies 230 graden
     omtrek. Op een telefoon is de bal kleiner, dus staat daar een grotere hoek. */
  --rol-afstand: 170px;
  --rol-hoek: 240deg;

  /* HIJ WACHT VOOR HIJ BINNENROLT. Meteen bij het laden verschijnen is opdringerig: de
     bezoeker is dan nog aan het kijken waar hij terecht is gekomen. Pas als hij de pagina
     gezien heeft is een bal die binnenrolt een aanbod in plaats van een onderbreking.

     Tien seconden lijkt lang, en dat is het punt. Vijf was net te kort: dan komt hij
     binnen terwijl iemand nog aan het lezen is aan wat hij op de pagina gevonden heeft, en
     dan trekt hij precies weg van waar de aandacht hoort te zitten. Wie hem nodig heeft,
     kijkt vanzelf rond en vindt hem dan alsnog.

     Alle drie de animaties delen deze wachttijd, anders begint het gezicht te verschijnen
     terwijl de bal nog buiten beeld staat. */
  --spikey-wacht: 10s;

  animation: spikey-rol 1s cubic-bezier(.18,.7,.28,1) both;
  animation-delay: var(--spikey-wacht);
}
/* DE SLAGSCHADUW IN TWEE LAGEN. Eén vlakke ellips leest als een grijze vlek; een echte
   schaduw is hard waar de bal de grond raakt en waaiert daarbuiten snel uit. De donkere
   kern is daarom klein en de zachte rand breed. */
.spikey__shadow {
  position: absolute; left: 50%; bottom: 2px;
  width: 48px; height: 8px; margin-left: -24px;
  background:
    radial-gradient(closest-side, rgba(18,58,82,.34) 0%,
                                  rgba(18,58,82,.15) 46%,
                                  rgba(18,58,82,0)  100%);
  border-radius: 50%;
  transform-origin: center;
  filter: blur(1px);
}
.spikey__hop    { position: relative; width: 100%; height: 88px; }

/* EEN RONDE FOCUSRING OM EEN RONDE KNOP. De browser tekent standaard een rechthoek om het
   hele element, en dat element is breder dan hoog én bevat de schaduw: je krijgt dus een
   kadertje dat een halve centimeter naast de bal ligt. Zichtbaar zodra iemand het venster
   met de toets sluit, en ronduit storend zodra de bal door het scherm stuitert.

   De maat komt uit de hoogte van .spikey__hop en niet uit een getal per breekpunt: de bal
   vult die hoogte precies (de SVG past zich in het kleinste van beide maten in), dus
   `aspect-ratio: 1` op die hoogte ís de omtrek van de bal. */
.spikey:focus-visible { outline: none; }
.spikey:focus-visible .spikey__hop::after {
  content: '';
  position: absolute;
  left: 50%; top: 50%;
  height: calc(100% + 9px); aspect-ratio: 1;
  transform: translate(-50%, -50%);
  border: 2px solid var(--msiks);
  border-radius: 50%;
}
.spikey__squash { width: 100%; height: 100%; transform-origin: 50% 100%; }
.spikey svg     { display: block; width: 100%; height: 100%; overflow: visible; }

/* DE DRAAIING ZIT OP DE SVG, NIET OP .spikey. In .spikey zit ook de schaduw, en die hoort
   op de grond te blijven liggen in plaats van mee rond de bal te cirkelen. Het midden van
   de svg is exact het midden van de bal, dus de standaard transform-origin klopt hier. */
.spikey svg {
  animation: spikey-rol-draai 1s cubic-bezier(.18,.7,.28,1) both;
  animation-delay: var(--spikey-wacht);
}
.spikey .face {
  animation: spikey-rol-gezicht 1s ease-out;
  animation-delay: var(--spikey-wacht);
}

/* DE NADEN DRAAIEN OM HET MIDDELPUNT VAN DE BAL, en dat moet met de hand gezegd worden.
   Met `fill-box` draait een element om het midden van zijn eigen omhullende rechthoek. Voor
   een oog of een mond is dat precies goed, voor de naden niet: hun patroon is wel
   driemaal symmetrisch rond (50,50), maar de omhullende daarvan ligt op (43, 52). Zeven
   eenheden ernaast, op een bal met straal 44. Het patroon zwalkte daardoor bij het denken
   naar rechtsonder weg, en omdat de klipcirkel meedraait staken er naadstukjes buiten de
   rand. `view-box` rekent 50% van de viewBox, en dat is het middelpunt van de bal zelf. */
.ball__seams { transform-box: view-box; transform-origin: 50% 50%; }

/* HET OPPERVLAK DRAAIT, HET LICHT NIET. Alles wat op de bal geschilderd staat zit in deze
   groep; de belichting eronder en erboven blijft liggen. Verschuift de groep, dan draait
   de bal onder een vaste lamp door. Dat is het hele truukje achter het 2.5D-gevoel.

   Het draaipunt moet met de hand gezegd worden, om precies dezelfde reden als bij de
   naden: standaard draait een SVG-element om het midden van zijn eigen omhullende
   rechthoek, en die ligt hier scheef omdat het gezicht meetelt. `view-box` rekent vanaf de
   viewBox, en 50% daarvan IS het midden van de bal. */
.spikey__oppervlak {
  transform-box: view-box;
  transform-origin: 50% 50%;
  transition: transform .18s ease-out;
}

.eye, .lid, .pupils, .mouth, .brow, .face {
  transform-box: fill-box;
  transform-origin: center;
}
.lid    { transform-origin: center top; transform: scaleY(0); }
.face   { transition: opacity .3s ease; }
.pupils { transition: transform .18s ease-out; }
/* Brauwen en ogen veren na. Bij schrikken springen ze omhoog en zakken ze een haartje
   terug; met een gewone ease komt de schrik traag op gang en leest hij als fronsen. */
.brow   { transition: transform .3s cubic-bezier(.34,1.5,.64,1), opacity .3s ease; }
.eye    { transition: transform .22s cubic-bezier(.34,1.5,.64,1); }
.blush  { opacity: 0; transition: opacity .3s ease; }
.spikey:hover .blush { opacity: 1; }


.mouth-smile, .eyes-open  { opacity: 1; }
.mouth-talk, .mouth-oh, .eyes-happy { opacity: 0; }

/* ── keyframes: bal ────────────────────────────────────────── */
@keyframes spikey-rol {
  0%   { transform: translateX(var(--rol-afstand)); opacity: 0; }
  12%  { opacity: 1; }
  82%  { transform: translateX(-7px); }
  100% { transform: translateX(0); opacity: 1; }
}
/* GEEN GEZICHT ZOLANG HIJ ROLT. Dezelfde afspraak als bij het nadenken, waar de bal ook
   draait: een gezicht dat ondersteboven voorbijkomt leest als een fout in plaats van als
   een grap. Het verschijnt in de laatste vierhonderd milliseconden, wanneer de bal
   praktisch stilligt. Bewust ZONDER fill-mode: met `both` zou de eindstand van deze
   animatie de opacity blijven vasthouden, en dan krijgt `is-think` zijn gezicht nooit
   meer weg. */
@keyframes spikey-rol-gezicht {
  0%, 62% { opacity: 0; }
  100%    { opacity: 1; }
}
/* De laatste zeven pixels rolt hij terug: een bal die pal stilvalt ziet er dood uit.
   Zeven pixels is bij deze straal ruim acht graden, vandaar dat de hoeken meelopen. */
@keyframes spikey-rol-draai {
  0%   { transform: rotate(var(--rol-hoek)); }
  82%  { transform: rotate(-9deg); }
  100% { transform: rotate(0deg); }
}
@keyframes bob {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-4px); }
}
@keyframes breathe {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.015, .985); }
}
@keyframes hop {
  0%   { transform: translateY(0); }
  15%  { transform: translateY(2px); }
  42%  { transform: translateY(-30px); }
  68%  { transform: translateY(0); }
  82%  { transform: translateY(-7px); }
  100% { transform: translateY(0); }
}
@keyframes squash {
  0%   { transform: scale(1) rotate(0); }
  15%  { transform: scale(1.14, .86) rotate(0); }
  30%  { transform: scale(.9, 1.12) rotate(-2.5deg); }   /* lichte draai in de lucht */
  68%  { transform: scale(1.18, .82) rotate(1.5deg); }
  84%  { transform: scale(.97, 1.03) rotate(0); }
  100% { transform: scale(1) rotate(0); }
}
@keyframes squint {
  0%, 55%, 90%, 100% { transform: scaleY(1); }
  62%, 80%           { transform: scaleY(.15); }
}
@keyframes chatter {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.03, .97); }
}
@keyframes mouth-talk {
  0%, 100% { transform: scaleY(.35); }
  50%      { transform: scaleY(1); }
}
/* HIJ SCHRIKT, HIJ SCHUDT GEEN NEE. Een korte opwaartse ruk die uitdempt. Het verschil met
   het heen-en-weer dat hier eerst stond is de richting: omhoog leest als verbazing, heen
   en weer leest als afkeuring. En afkeuring is precies wat een storing niet is. */
@keyframes schrik {
  0%   { transform: translateY(0); }
  16%  { transform: translateY(-10px); }
  44%  { transform: translateY(0); }
  62%  { transform: translateY(-3px); }
  100% { transform: translateY(0); }
}
@keyframes schrik-rek {
  0%   { transform: scale(1); }
  12%  { transform: scale(.92, 1.1); }
  34%  { transform: scale(1.06, .95); }
  62%  { transform: scale(.99, 1.01); }
  100% { transform: scale(1); }
}
@keyframes spin { to { transform: rotate(360deg); } }
@keyframes blink-lid {
  0%, 100% { transform: scaleY(0); }
  45%, 60% { transform: scaleY(1); }
}


/* ── keyframes: schaduw ────────────────────────────────────── */
@keyframes shadow-bob {
  0%, 100% { transform: scaleX(1); opacity: 1; }
  50%      { transform: scaleX(.93); opacity: .8; }
}
@keyframes shadow-hop {
  0%   { transform: scaleX(1); opacity: 1; }
  15%  { transform: scaleX(1.06); opacity: 1; }
  42%  { transform: scaleX(.55); opacity: .45; }
  68%  { transform: scaleX(1.15); opacity: 1; }
  82%  { transform: scaleX(.85); opacity: .75; }
  100% { transform: scaleX(1); opacity: 1; }
}
@keyframes schrik-schaduw {
  0%   { transform: scaleX(1);   opacity: 1; }
  16%  { transform: scaleX(.68); opacity: .5; }
  44%  { transform: scaleX(1.1); opacity: 1; }
  62%  { transform: scaleX(.9);  opacity: .8; }
  100% { transform: scaleX(1);   opacity: 1; }
}

/* ── states ────────────────────────────────────────────────── */
.spikey.is-idle .spikey__hop    { animation: bob 3.8s ease-in-out infinite; }
.spikey.is-idle .spikey__squash { animation: breathe 3.8s ease-in-out infinite; }
.spikey.is-idle .spikey__shadow { animation: shadow-bob 3.8s ease-in-out infinite; }

.spikey.do-blink .lid { animation: blink-lid .22s ease-in-out; }

.spikey.is-hop .spikey__hop    { animation: hop .8s cubic-bezier(.3,0,.5,1); }
.spikey.is-hop .spikey__squash { animation: squash .8s ease-out; }
.spikey.is-hop .spikey__shadow { animation: shadow-hop .8s cubic-bezier(.3,0,.5,1); }
.spikey.is-hop .eye            { animation: squint .8s linear; }

.spikey.is-talk .spikey__hop    { animation: bob 1.2s ease-in-out infinite; }
.spikey.is-talk .spikey__shadow { animation: shadow-bob 1.2s ease-in-out infinite; }
.spikey.is-talk .spikey__squash { animation: chatter .34s ease-in-out infinite; }
.spikey.is-talk .mouth-smile    { opacity: 0; }
.spikey.is-talk .mouth-talk     { opacity: 1; animation: mouth-talk .28s ease-in-out infinite; }

.spikey.is-think .face        { opacity: 0; }
.spikey.is-think .ball__seams { animation: spin 1.6s linear infinite; }
.spikey.is-think .spikey__hop    { animation: bob 3.8s ease-in-out infinite; }
.spikey.is-think .spikey__shadow { animation: shadow-bob 3.8s ease-in-out infinite; }

.spikey.is-happy .brow       { opacity: 0; }
.spikey.is-happy .eyes-open  { opacity: 0; }
.spikey.is-happy .eyes-happy { opacity: 1; }
.spikey.is-happy .blush      { opacity: 1; }
.spikey.is-happy .spikey__hop    { animation: bob 1.6s ease-in-out infinite; }
.spikey.is-happy .spikey__shadow { animation: shadow-bob 1.6s ease-in-out infinite; }
.spikey.is-happy .spikey__squash { animation: breathe 1.6s ease-in-out infinite; }

/* ── verrast: het gezicht bij een storing ──
   Wenkbrauwen omhoog EN naar buiten gekanteld. Bij het boze gezicht dat hier eerst stond
   gingen juist de binnenste uiteinden omlaag; dat ene verschil bepaalt alles. Verder: ogen
   wijder open en een ronde mond.

   Hij schrikt en gaat daarna gewoon door met ademen. De tweede animatie start na de
   schrik, want anders vechten `schrik` en `bob` om dezelfde translateY. */
.spikey.is-verrast .brow--l { transform: translateY(-7px) rotate(-3deg); }
.spikey.is-verrast .brow--r { transform: translateY(-7px) rotate(3deg); }
.spikey.is-verrast .eye     { transform: scale(1.16); }
.spikey.is-verrast .mouth-smile { opacity: 0; }
.spikey.is-verrast .mouth-oh    { opacity: 1; }
.spikey.is-verrast .spikey__hop {
  animation: schrik .55s cubic-bezier(.3,0,.4,1), bob 3.8s ease-in-out .55s infinite;
}
.spikey.is-verrast .spikey__squash { animation: schrik-rek .55s ease-out; }
.spikey.is-verrast .spikey__shadow {
  animation: schrik-schaduw .55s cubic-bezier(.3,0,.4,1), shadow-bob 3.8s ease-in-out .55s infinite;
}

/* WEGROLLEN IS DE BINNENROL ACHTERUIT. Deze drie regels stonden hier al voor er iets was dat
   ze aanriep; sinds "ga weg" een commando is in het chatvenster, gebeurt dat. */
.spikey.is-away     { animation: spikey-rol .55s cubic-bezier(.45,0,.85,.5) reverse both; }
.spikey.is-away svg { animation: spikey-rol-draai .55s cubic-bezier(.45,0,.85,.5) reverse both; }
.spikey.is-away .face { animation: spikey-rol-gezicht .55s ease-in reverse; }

/* En dan is hij weg. Niet alleen onzichtbaar: `visibility: hidden` zou een onklikbare bal
   laten staan die nog altijd over de tekst heen ligt op een telefoon. */
.spikey.is-weg, .spikey-venster.is-weg, .spikey-scrim.is-weg { display: none !important; }

/* ── de sluier achter het open venster ─────────────────────────
   Het venster is wit en de pagina onder de kopfoto ook, dus zonder dit hield de ballon
   onderaan gewoon op. Een rand en een diepere schaduw hielpen, maar niet genoeg: wat het
   venster nodig had was niet meer omtrek maar minder concurrentie.

   DE Z-INDEX LIGT TUSSEN DE BAL EN DE PAGINA. De bal staat op 9000 en het venster op 9998,
   dus die twee blijven licht terwijl al de rest dooft. Dat is precies het beeld dat je wil:
   niet "er ligt een venster over de site" maar "de site stapt even opzij voor Spikey".

   Hij vangt ook de klikken, en dat hoort bij het dimmen: eruitzien alsof je afgeschermd
   bent en dan tóch de link eronder openen is de vervelendste van de twee. Eén tik erop
   sluit het venster, zoals overal. */
.spikey-scrim {
  position: fixed;
  inset: 0;
  z-index: 8999;
  background: rgba(12, 40, 56, .42);
  opacity: 0;
  visibility: hidden;
  transition: opacity .22s ease, visibility .22s;
}
.spikey-scrim.is-open { opacity: 1; visibility: visible; }

/* ── het eitje: drie snelle klikken en hij stuitert door het scherm ──
   De baan zelf staat NIET hier. Die komt per beeld uit een simulatie in spikey.js en wordt
   als inline stijl weggeschreven; wat hieronder staat is alleen het gezicht dat erbij hoort
   en het opruimen van wat de simulatie voor de voeten loopt.

   GEEN `animation: none` OP `.spikey` ZELF, hoe voor de hand liggend dat ook lijkt. De
   binnenrol staat daar met `fill-mode: both` en een wachttijd van tien seconden. Zet je die
   animatie uit en na afloop weer aan, dan begint ze opnieuw: de bal verdwijnt tien seconden
   en komt daarna nog eens binnenrollen. Het script schrijft zijn transform daarom met
   `!important` weg. In de cascade wint een belangrijke auteursregel van een animatie, dus de
   simulatie stuurt en de binnenrol blijft ongemoeid staan waar hij stond.

   Deze toestand staat met opzet NIET in `spikey.html`. Dat bestand is de speeltuin voor
   keyframes, en hier valt niets te tunen: elke pixel komt uit de rekenlus. Wie hieraan wil
   sleutelen doet dat aan de constanten bovenaan het eitje in spikey.js, en kijkt met
   `node assets/voorbeeld.mjs`. */

/* Overgangen uit: de simulatie schrijft elk beeld een nieuwe waarde, en een overgang die
   telkens opnieuw begint maakt daar pap van. Bij de ogen komt er nog iets bij: in de
   cascade wint een overgang van een animatie, dus een lopende overgang zou de klapknijp
   van een botsing gewoon overstemmen. */
.spikey.is-vlucht .spikey__oppervlak,
.spikey.is-vlucht .pupils,
.spikey.is-vlucht .eye { transition: none; }

/* Het gezicht van iemand die net een mep gekregen heeft. Dezelfde onderdelen als bij
   `is-verrast`, want het is dezelfde schrik: brauwen omhoog, ogen wijd, mond rond. */
.spikey.is-vlucht .brow--l { transform: translateY(-6px) rotate(-4deg); }
.spikey.is-vlucht .brow--r { transform: translateY(-6px) rotate(4deg); }
.spikey.is-vlucht .eye     { transform: scale(1.13); }
.spikey.is-vlucht .mouth-smile { opacity: 0; }
.spikey.is-vlucht .mouth-oh    { opacity: 1; }
.spikey.is-vlucht .blush       { opacity: 1; }

/* Uitgestuiterd, en op weg terug naar zijn hoek. Hij heeft het overleefd, dus hij mag
   weer blij kijken terwijl hij naar huis rolt. */
.spikey.is-thuis .brow       { opacity: 0; }
.spikey.is-thuis .eyes-open  { opacity: 0; }
.spikey.is-thuis .eyes-happy { opacity: 1; }
.spikey.is-thuis .blush      { opacity: 1; }

@keyframes spikey-opdoemen { from { opacity: 0; } to { opacity: 1; } }

@media (prefers-reduced-motion: reduce) {
  .spikey, .spikey *, .spikey.is-away { animation: none !important; }
  /* Wachten is geen bewegen. Wie animaties uitzet wil niet dat er iets voorbijrolt, maar
     heeft evengoed baat bij een assistent die niet meteen over de pagina heen valt. Hij
     doemt dus gewoon op, na dezelfde wachttijd. */
  .spikey {
    transform: none;
    animation: spikey-opdoemen .25s ease var(--spikey-wacht) both !important;
  }
  .pupils, .spikey__oppervlak { transition: none; }
  .spikey.is-think .face { opacity: 1; }  /* geen spin, dus gezicht blijft */
}
/* OP EEN TELEFOON STOND HIJ OP 68px EN DAT WAS TE KLEIN: op een scherm van 390px breed las
   hij als een icoontje in plaats van als een figuur, en de ogen waren nauwelijks te zien.
   Nu 84px, nog altijd kleiner dan de 92 op een groot scherm.

   DE ROLHOEK HANGT AAN DE MAAT, en die moet je dus meeveranderen. De bal legt bij het
   binnenrollen `--rol-afstand` af en draait daarbij `--rol-hoek`; klopt de verhouding niet,
   dan glijdt hij in plaats van te rollen. Wordt hij een factor k groter, dan is dezelfde
   afstand k keer minder omtrek: 270deg ÷ (84/68) = 219deg. */
@media (max-width: 640px) { .spikey { width: 84px; height: 91px; right: 20px; bottom: 26px;
    --rol-afstand: 140px; --rol-hoek: 219deg; }
  .spikey__hop { height: 81px; } }

/* ── het gespreksvenster: een tekstballon ──────────────────────
   Het venster hoort bij Spikey, dus het ziet eruit als iets wat hij zegt: een ballon met
   een staart die op zijn kruin wijst, die uit hem opengroeit en bij het sluiten weer in
   hem terugvalt (transform-origin rechtsonder).

   DE SCHADUW ZIT OP EEN FILTER EN NIET OP EEN BOX-SHADOW. Een box-shadow volgt de
   rechthoek; de staart zou dan zijn eigen schaduwtje krijgen met een zichtbare naad op de
   grens. `drop-shadow` volgt de omtrek van wat er werkelijk staat, staart inbegrepen.

   Hij erft Poppins van de site in plaats van system-ui aan te roepen. Dat lettertype
   staat op elke pagina al klaar, dus het kost geen enkele extra aanvraag, en het scheelt
   het verschil tussen "hoort bij deze club" en "hier is een widget geplakt".
   ────────────────────────────────────────────────────────────── */
.spikey-venster {
  --msiks:   #1CA3DC;
  --msiks-d: #1590c4;
  --ink:     #16394f;
  --papier:  #fff;
  --lijn:    #e6eff5;

  /* Waar de staart hangt, gemeten vanaf de rechterrand van de ballon. De punt moet
     samenvallen met het midden van de bal, en loopt dus mee als de bal van maat verandert. */
  --staart-rechts: 34px;

  position: fixed;
  right: 24px;
  bottom: 132px;
  z-index: 9998;

  display: flex;
  flex-direction: column;
  width: min(348px, calc(100vw - 48px));
  max-height: min(500px, calc(100vh - 210px));

  background: var(--papier);
  color: var(--ink);
  border-radius: 22px;
  /* SCHADUW EN GEEN RAND, en dat is geen stijlkeuze maar een noodzaak: het staartje onderaan
     is een eigen element met een `clip-path`, en een `border` op de ballon stopt bij de
     ballon. Dan hangt er een randloos driehoekje onder een omrande doos. `drop-shadow` volgt
     de vórm, staartje inbegrepen, en dus doet alles hier dat.

     Er heeft hier even een haarlijn bij gestaan (een derde `drop-shadow` met nul vervaging),
     toen de ballon nog los over de pagina lag en onderaan in het wit verdween. Sinds de
     sluier erachter ligt, is die lijn overbodig én lelijk: twee dingen die hetzelfde
     oplossen, waarvan de ene het randje van een uitgeknipte sticker geeft. De schaduw is wel
     dieper gebleven dan ze was. */
  filter: drop-shadow(0 18px 38px rgba(22, 57, 79, .26))
          drop-shadow(0 4px 10px rgba(22, 57, 79, .12));
  font: 15px/1.55 'Poppins', system-ui, -apple-system, 'Segoe UI', sans-serif;

  /* Dicht is de standaard: hij mag niemand overvallen. */
  opacity: 0;
  visibility: hidden;
  transform: translateY(10px) scale(.94);
  transform-origin: bottom right;
  transition: opacity .2s ease,
              transform .26s cubic-bezier(.16, .84, .44, 1),
              visibility .26s;
}
.spikey-venster.is-open { opacity: 1; visibility: visible; transform: none; }

.spikey-venster::after {
  content: '';
  position: absolute;
  right: var(--staart-rechts);
  bottom: -14px;
  width: 24px;
  height: 15px;
  background: var(--papier);
  clip-path: polygon(0 0, 100% 0, 58% 100%);
}

.spikey-venster__kop {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .5rem;
  padding: .85rem 1rem .35rem;
}
/* Klein en in kapitalen: een naambordje, geen titelbalk van een programma. Het bolletje
   ervoor zegt dat hij er is, zonder dat er "online" bij hoeft. */
.spikey-venster__kop > span {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .09em;
  text-transform: uppercase;
  opacity: .5;
}
.spikey-venster__kop > span::before {
  content: '';
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--msiks);
  box-shadow: 0 0 0 3px rgba(28, 163, 220, .16);
}
.spikey-venster__sluit {
  border: 0;
  background: none;
  font: inherit;
  font-size: 20px;
  line-height: 1;
  color: var(--ink);
  opacity: .4;
  cursor: pointer;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  transition: opacity .15s ease, background .15s ease;
}
.spikey-venster__sluit:hover { opacity: .9; background: #f0f6fa; }
.spikey-venster__sluit:focus-visible { outline: 2px solid var(--msiks); outline-offset: 2px; }

.spikey-venster__log {
  flex: 1;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: .55rem 1rem .45rem;
  display: flex;
  flex-direction: column;
  gap: .45rem;
  scrollbar-width: thin;
  scrollbar-color: #cfe0ea transparent;
}

.spikey-bericht {
  max-width: 86%;
  padding: .55rem .8rem;
  border-radius: 18px;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  animation: spikey-bericht-in .26s cubic-bezier(.16, .84, .44, 1) both;
}
/* Elk bericht komt van onder omhoog binnen, in de richting waarin het gesprek loopt. */
@keyframes spikey-bericht-in {
  from { opacity: 0; transform: translateY(7px); }
}
.spikey-bericht--spikey {
  align-self: flex-start;
  background: #f1f7fb;
  border-bottom-left-radius: 6px;
}
.spikey-bericht--jij {
  align-self: flex-end;
  background: linear-gradient(180deg, #22a9df, var(--msiks-d));
  color: #fff;
  border-bottom-right-radius: 6px;
}
.spikey-bericht a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-thickness: 1px;
}

/* VOORZETJES: drie vragen om mee te beginnen.
   Een leeg invoerveld is de grootste drempel van elke chat. Op een clubsite komt daar
   bovenop dat een bezoeker niet weet wát hij mag vragen, en dus vraagt hij niets. Deze
   drie tonen meteen het bereik: kalender, lidgeld, meetrainen. Ze verdwijnen zodra de
   eerste vraag gesteld is.

   ZE ZIJN ÉÉN KEER WEGGEHAALD EN METEEN WEER TERUGGEZET, dus dit is de moeite van het
   opschrijven waard. Het idee was om hun taak door de begroeting te laten overnemen: dan
   staat er in woorden wat hij allemaal weet. Dat werd op twee manieren slechter. Een
   opsomming van onderwerpen in een zin is trager te lezen dan drie knopjes, en je kunt er
   niet op klikken, dus de drempel die ze wegnamen kwam gewoon terug. Wat tekst wél kan en
   een knopje niet, is zeggen wie hij is; dat doet de begroeting nu, en verder niets. */
/* EEN KOLOM EN GEEN WRAPPENDE RIJ, en dat is een reparatie van twee dingen tegelijk.
   Het stond hier als `flex-wrap: wrap` zonder richting, dus als een rij. Dat werkte zolang
   de voorzetjes kort waren, maar deze vragen zijn hele zinnen: ze passen toch nooit met
   twee naast elkaar, en ze kregen zo elk een andere breedte met een afgebroken regel op een
   willekeurige plek. Bovendien stond het label "Bijvoorbeeld:" als gewoon item in diezelfde
   rij, en of dat op een eigen regel belandde hing af van de breedte van het eerste knopje.
   Bij een korte eerste vraag kwam het ernaast te staan.

   In een kolom is allebei dat weg: het label staat altijd bovenaan, en elk knopje begint op
   dezelfde lijn. `align-items: flex-start` houdt ze wel bij hun eigen breedte, want een rij
   even brede balken leest als een keuzemenu, en dat is precies wat deze knopjes niet zijn. */
.spikey-voorzetjes {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: .35rem;
  margin: .1rem 0 .15rem;
}
.spikey-voorzet {
  font: inherit;
  font-size: 13px;
  padding: .35rem .75rem;
  border: 1px solid #d7e7f1;
  border-radius: 14px;
  background: #fff;
  color: var(--msiks-d);
  cursor: pointer;
  transition: background .15s ease, border-color .15s ease, transform .15s ease;

  /* Een knop centreert zijn tekst vanzelf, en dát was het lelijkste eraan: bij een vraag
     die over twee regels loopt, zweefde de tweede regel in het midden. Links uitlijnen, net
     als alle andere tekst in dit venster. */
  text-align: left;

  /* Breder dan de ballon kan niet, en een vraag van vijf woorden hoeft niet op vijf regels:
     `max-width` begrenst, de kolom hierboven laat hem verder zelf zijn maat kiezen. */
  max-width: 100%;
}
.spikey-voorzet:hover { background: #f2f9fd; border-color: var(--msiks); transform: translateY(-1px); }
.spikey-voorzet:focus-visible { outline: 2px solid var(--msiks); outline-offset: 2px; }

/* "Bijvoorbeeld:" boven de knopjes. Klein en bleek, want het is een aanwijzing en geen
   mededeling: het moet gelezen worden vóór de knopjes, niet in plaats daarvan. Dezelfde
   maat en letterdikte als het naambordje bovenaan, zodat het venster één stem houdt, maar
   zonder de kapitalen en de spatiëring, want dit is een zinnetje en geen etiket. */
.spikey-voorzetjes__label {
  font-size: 12px;
  font-weight: 600;
  opacity: .45;
  margin-bottom: .1rem;
}

/* Drie stipjes terwijl het antwoord op gang komt. */
.spikey-tikt { display: inline-flex; gap: 4px; padding: .2rem 0; }
.spikey-tikt span {
  width: 6px; height: 6px; border-radius: 50%;
  background: currentColor; opacity: .35;
  animation: spikey-tikt 1.2s infinite;
}
.spikey-tikt span:nth-child(2) { animation-delay: .18s; }
.spikey-tikt span:nth-child(3) { animation-delay: .36s; }
@keyframes spikey-tikt {
  0%, 60%, 100% { opacity: .25; transform: translateY(0); }
  30%           { opacity: .8;  transform: translateY(-3px); }
}

/* De voet is één pil waarin het veld en de knop samen zitten, en die pil licht op bij
   focus. Twee losse kaders naast elkaar leest als een formulier, en dit is een gesprek. */
.spikey-venster__voet {
  display: flex;
  /* De knop is rond en zit in een ronde pil: dan hoort de ruimte eromheen aan alle kanten
     gelijk te zijn. Met `flex-end` stond hij tegen de onderrand geplakt (vijf pixels
     onder, tien boven), en dat zag je meteen. De oorzaak was niet de uitlijning maar de
     hoogte: het invoerveld was hoger dan de knop. Nu is het net iets lager, zodat de knop
     de pilhoogte bepaalt en er vanzelf evenveel ruimte boven als onder overblijft. Blijft
     `flex-end`, want zodra de vraag over meer regels loopt hoort de knop mee te zakken in
     plaats van in het midden te blijven zweven. */
  align-items: flex-end;
  gap: .4rem;
  /* Onderaan evenveel lucht als links en rechts. De voetnoot vulde dat gat vroeger op. */
  margin: .2rem .8rem .8rem;
  padding: 5px 5px 5px 12px;
  border: 1px solid var(--lijn);
  border-radius: 22px;
  background: #fff;
  transition: border-color .15s ease, box-shadow .15s ease;
}
.spikey-venster__voet:focus-within {
  border-color: var(--msiks);
  box-shadow: 0 0 0 3px rgba(28, 163, 220, .13);
}
.spikey-venster__invoer {
  flex: 1;
  font: inherit;
  color: inherit;
  padding: 5px 4px;
  border: 0;
  background: none;
  resize: none;
  max-height: 5.5rem;
}
/* HET THEMA GEEFT ELK INVOERVELD EEN MAGENTA FOCUSRING, ook bij een muisklik. Hier zit
   de ring op de voetpil eromheen, dus die van de site moet weg. Twee klassen diep, want
   `textarea:focus:not(:focus-visible)` uit het thema telt een elementnaam mee en wint het
   anders van een enkele klasse. */
.spikey-venster .spikey-venster__invoer:focus,
.spikey-venster .spikey-venster__invoer:focus-visible {
  outline: none;
  border-color: transparent;
  box-shadow: none;
}
.spikey-venster__invoer::placeholder { color: var(--ink); opacity: .38; }

/* Uitgeschakeld ziet er uitgeschakeld uit. Het veld blijft staan (het weghalen laat de
   voet inklappen en dat leest als een fout), maar een cursor die er niet in gaat staan en
   een lichtere aanwijzing maken duidelijk dat hier niets meer te typen valt. */
.spikey-venster__invoer:disabled { cursor: default; }
.spikey-venster__invoer:disabled::placeholder { opacity: .55; }
.spikey-venster__stuur {
  flex: none;
  width: 34px;
  height: 34px;
  border: 0;
  border-radius: 50%;
  background: var(--msiks);
  color: #fff;
  cursor: pointer;
  display: grid;
  place-items: center;
  transition: background .15s ease, transform .15s ease, opacity .15s ease;
}
.spikey-venster__stuur svg { width: 17px; height: 17px; display: block; }
.spikey-venster__stuur:hover:not(:disabled) { background: var(--msiks-d); transform: translateY(-1px); }
.spikey-venster__stuur:disabled { opacity: .35; cursor: default; }
.spikey-venster__stuur:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }

/* DEZE GETALLEN HANGEN AAN DE MAAT EN DE PLAATS VAN DE BAL hierboven. Verzet je daar iets,
   reken ze dan alle drie opnieuw uit, anders wijst het staartje naast de bal.

   De bal staat op 20px van rechts en 26px van onder, en is 84 bij 91.

   `bottom`          acht pixels lucht boven de bal: 26 + 91 + 8 = 125.
   `left`/`right`    twaalf, net iets minder dan de twintig van de bal, zodat de ballon
                     breder oogt dan hij maar toch niet tegen de schermrand plakt.
   `--staart-rechts` de punt hoort op het midden van de bal, en dat ligt op 20 + 42 = 62px
                     van de rechterrand. De ballon begint op 12, en de punt van het staartje
                     zit nog eens tien pixels links van waar de variabele hem neerzet:
                     62 - 12 - 10 = 40. */
@media (max-width: 640px) {
  .spikey-venster {
    right: 12px;
    left: 12px;
    bottom: 125px;
    width: auto;
    max-height: min(70vh, 440px);
    --staart-rechts: 40px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .spikey-venster { transition: opacity .12s ease, visibility .12s; transform: none; }
  .spikey-scrim   { transition: opacity .12s ease, visibility .12s; }
  .spikey-bericht { animation: none; }
  .spikey-tikt span { animation: none; }
  .spikey-voorzet:hover { transform: none; }
}
