/* ============================================================================
   Tagtool — componentlaag op de s.host-tokens.

   Geen letterlijke kleur, maat, radius of schaduw in dit bestand: alles is een
   var() uit tokens.css. Dat is niet netheid om de netheid — het maakt een
   volgende ronde uit Claude Design een diff op één bestand.
   ============================================================================ */

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

html, body { margin: 0; height: 100%; }

body {
  background: var(--surface-canvas);
  color: var(--text-strong);
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  line-height: var(--lh-normal);
  -webkit-font-smoothing: antialiased;
}

a { color: var(--green-800); }
a:hover { color: var(--green-900); }

code, kbd, .mono { font-family: var(--font-mono); }

:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
  border-radius: var(--radius-xs);
}
/* --focus-ring (--green-800) is 1.67:1 op de donkere rail (--ink-900) —
   onzichtbaar voor wie met het toetsenbord tabt. Lime op ink is 15.64:1. */
.side :focus-visible { outline-color: var(--color-accent); }

.icon { width: var(--space-5); height: var(--space-5); flex: none; }

/* Standaard sr-only-recept: cliprect + 1px in plaats van display:none, zodat
   een schermlezer de tekst wel voorleest terwijl geen enkele ziende layout
   er ruimte voor reserveert. De maten hier zíjn de techniek — er bestaat geen
   token voor "onzichtbaar maar aanwezig", dus geen var() te vinden. */
.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ---------- shell ---------- */

.shell { min-height: 100vh; display: flex; }

.side {
  /* 288px is een kit-maat; er staat geen token op deze waarde. */
  width: 288px;
  flex-shrink: 0;
  background: var(--surface-sidebar);
  display: flex;
  flex-direction: column;
  padding: var(--space-6) var(--space-4) var(--space-5);
}

.brand {
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-1) var(--space-3) var(--space-7);
}
.brand-sq {
  width: var(--space-8); height: var(--space-8); flex: none;
  border-radius: var(--radius-sm);
  background: var(--color-accent);
  color: var(--color-accent-ink);
  display: flex; align-items: center; justify-content: center;
  font-family: var(--font-display);
  font-weight: var(--fw-bold);
  font-size: var(--fs-xl);
  line-height: 1;
}
.brand-name {
  font-family: var(--font-display);
  font-size: var(--fs-xl);
  font-weight: var(--fw-bold);
  letter-spacing: var(--tracking-tight);
  color: var(--text-inverse);
}

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

.nav { display: flex; flex-direction: column; gap: var(--space-1); flex: 1; }

.nav-item {
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--pad-nav-item);
  border-radius: var(--radius-xs);
  font-size: var(--fs-md);
  font-weight: var(--fw-medium);
  color: var(--text-inverse);
  text-decoration: none;
  transition: background 120ms ease, color 120ms ease;
}
.nav-item .icon { opacity: .85; }
/* Overlay op de donkere rail: het design system schrijft deze hover- en
   scheidingslagen zelf ook als rgba(255,255,255,…), niet als token. */
.nav-item:hover { background: rgba(255, 255, 255, .07); color: var(--text-inverse); }
.nav-label { flex: 1; }

.nav-item[aria-current="page"] {
  background: var(--color-accent);
  color: var(--color-accent-ink);
  font-weight: var(--fw-semibold);
}
.nav-item[aria-current="page"]:hover { background: var(--color-accent-hover); color: var(--color-accent-ink); }
.nav-item[aria-current="page"] .icon { opacity: 1; }

.nav-badge {
  display: inline-flex; align-items: center; justify-content: center;
  /* 22px komt uit de kit als vaste badge-maat; er is geen spacing-token op
     deze waarde. */
  min-width: 22px; height: 22px; padding: 0 var(--space-2);
  border-radius: var(--radius-pill);
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  line-height: 1;
  background: var(--color-accent);
  color: var(--color-accent-ink);
}
/* Lime op lime zou verdwijnen op de actieve rij: daar geldt het omgekeerde. */
.nav-item[aria-current="page"] .nav-badge {
  background: var(--ink-900);
  color: var(--color-accent);
}

/* ---------- rail-voet ---------- */

.side-foot {
  /* Scheidingslijn op de donkere rail: rgba-overlay, zoals het design
     system deze zelf ook schrijft. */
  border-top: 1px solid rgba(255, 255, 255, .12);
  padding: var(--space-5) var(--space-4) 0;
}

/* ---------- profielblok ---------- */

.profile { display: flex; align-items: center; gap: var(--space-3); }
.avatar {
  /* --space-8 (40px) is toevallig ook de kit-maat voor deze avatar. */
  width: var(--space-8); height: var(--space-8); flex: none;
  border-radius: var(--radius-pill);
  background: var(--green-700);
  color: var(--white);
  display: flex; align-items: center; justify-content: center;
  font-family: var(--font-display);
  font-weight: var(--fw-semibold);
  /* --fs-body (16px) is precies de maat die de kit hier gebruikt (40 * 0.4). */
  font-size: var(--fs-body);
  line-height: 1;
}
/* min-width: 0 laat de flexkind krimpen zodat de naam kan afkappen in plaats
   van het bolletje en de pijl van de rail te duwen. */
.profile-text { flex: 1; min-width: 0; }
.profile-name {
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
  color: var(--text-inverse);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* .who stond hier al (het hostname-regeltje); nu de tweede regel onder de
   naam in plaats van de enige regel in de voet. */
.side-foot .who {
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
  /* Gedempte tekst op de donkere rail: rgba-overlay, zoals het design
     system deze zelf ook schrijft. */
  color: rgba(255, 255, 255, .55);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

.profile-menu { position: relative; flex: none; margin-top: 0; }
/* Deze summary is zelf de ▾, dus de generieke driehoek van de
   details-regel verderop in dit bestand moet hier uit. */
.profile-toggle::before { content: none; }
.profile-toggle::-webkit-details-marker { display: none; }
.profile-toggle {
  display: flex; align-items: center; justify-content: center;
  list-style: none; cursor: pointer;
  /* 28px: net iets groter dan de tekst zelf, een duimbaar doelgebied — geen
     spacing-token raakt deze maat. */
  width: 28px; height: 28px;
  border-radius: var(--radius-xs);
  font-size: var(--fs-base);
  color: rgba(255, 255, 255, .6);
  transition: color 120ms ease, background 120ms ease;
}
.profile-toggle:hover { color: var(--text-inverse); background: rgba(255, 255, 255, .07); }
.profile-menu[open] .profile-toggle { color: var(--text-inverse); }
.profile-dropdown {
  position: absolute; right: 0; bottom: calc(100% + var(--space-2));
  /* Kaart in de donkere rail: een verhoogd donker vlak, zoals het
     designsysteem dat token zelf omschrijft, in plaats van een wit paneel
     dat op de rail zou schreeuwen. */
  background: var(--ink-700);
  border-radius: var(--radius-sm);
  padding: var(--space-2);
  box-shadow: var(--shadow-lg);
  /* 160px: genoeg voor "Uitloggen" plus wat lucht, geen echte lay-outmaat. */
  min-width: 160px;
  z-index: 20;
}
/* Uitloggen verandert iets aan de server, dus het is een knop in een form en
   geen link. Dat het eruitziet als een link is opmaak, geen semantiek. */
.side-out {
  display: block; width: 100%; text-align: left;
  background: none; border: none;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-xs);
  font-family: inherit; font-size: var(--fs-base); font-weight: var(--fw-semibold);
  color: var(--text-inverse); cursor: pointer;
  transition: background 120ms ease;
}
.side-out:hover { background: rgba(255, 255, 255, .08); }
/* De link naar /account draagt dezelfde klasse als de uitlogknop, en een <a>
   heeft van zichzelf een onderstreping en een linkkleur die de knop ernaast
   niet heeft. Twee regels die er precies hetzelfde uitzien in één menu. */
a.side-out { text-decoration: none; }

/* ---------- canvas ---------- */

/* --space-10 is 64px; één token in plaats van een token en een gelijke
   literal naast elkaar. De kit wil 56px verticaal, maar daar staat geen
   token op — 64 rondom dus. */
.main { flex: 1; min-width: 0; padding: var(--space-10); }

.head-row {
  display: flex; flex-wrap: wrap; justify-content: space-between; align-items: flex-start;
  gap: var(--space-6);
}

.eyebrow {
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--text-body);
  margin: 0 0 var(--space-2);
}
.h1 {
  font-family: var(--font-display);
  font-size: var(--fs-3xl);
  font-weight: var(--fw-bold);
  letter-spacing: var(--tracking-tight);
  color: var(--text-strong);
  margin: 0 0 var(--space-4);
  text-wrap: balance;
}
/* Specificiteitsgelijkspel met `code, kbd, .mono` (beide (0,1,0)): zonder dit
   wint .h1 gewoon omdat hij later in het bestand staat, en class="h1 mono"
   op domain.html rendert dan niet in mono — precies de reden dat de klasse
   daar staat. */
.h1.mono { font-family: var(--font-mono); }
.lede {
  font-size: var(--fs-lg);
  line-height: var(--lh-snug);
  color: var(--text-body);
  /* Leesbreedte als maat, geen echte lay-outafmeting — geen token voor. */
  max-width: 740px;
  margin: 0 0 var(--space-7);
  text-wrap: pretty;
}
/* ---------- oordeel (verdict card) ---------- */

/* Het oordeel was een gekleurde zin tussen de kop en de tabs (.verdict, hier
   weggehaald). Gekleurde tekst op een witte pagina leest als opmaak — iets wat
   een ontwerper heeft gedaan — en niet als het antwoord dat het scherm geeft.
   In een eigen vlak, met een teken ernaast en de keten eronder, is het het
   eerste blok op de pagina en draagt kleur niet langer in zijn eentje de
   betekenis: het glyph in de cirkel doet dat ook, dus in grijswaarden of voor
   een kleurenblinde lezer blijft het overeind.

   Eén kaart voor het oordeel én de keten, geen twee. De keten zegt waar het
   oordeel vandaan komt; een rand ertussen zou er twee losse mededelingen van
   maken. */
.vcard {
  border: var(--border-hairline);
  border-radius: var(--radius-card);
  background: var(--surface-canvas);
  overflow: hidden;
  margin: var(--space-6) 0 var(--space-7);
}
.vcard-head {
  display: flex; align-items: center; gap: var(--space-4);
  padding: var(--space-6) var(--space-7);
}
/* 40px: de cirkel moet naast twee tekstregels staan zonder een van beide te
   overstemmen. Kleiner dan elke spacing-token die hier past, dus als maat
   uitgeschreven. */
.vcard-mark {
  display: inline-flex; align-items: center; justify-content: center;
  flex: none; width: 40px; height: 40px;
  border-radius: var(--radius-pill);
}
/* 18px in een cirkel van 40: de vijf glyphs zijn op 13px getekend voor de
   ketenregels, waar ze naast tekst van dezelfde maat staan. In de cirkel staat
   er niets naast, en dan is 13px een stipje. Geen spacing-token op die maat. */
.vcard-mark svg { width: 18px; height: 18px; }
.vcard-words { min-width: 0; }
.vcard-title {
  margin: 0;
  font-size: var(--fs-lg); font-weight: var(--fw-semibold);
  line-height: var(--lh-snug); color: var(--text-strong);
  text-wrap: pretty;
}
/* Mono, want de zin bij een werkende meetserver eindigt op een hostname
   ("Alles komt binnen op t.klant.nl") en dat is de waarde waar de lezer voor
   kwam. Leesbreedte als maat, zelfde als .lede — geen token voor. */
.vcard-detail {
  margin: var(--space-1) 0 0;
  font-family: var(--font-mono); font-size: var(--fs-base);
  line-height: var(--lh-snug); color: var(--text-body);
  max-width: 740px; text-wrap: pretty;
}

/* Alle vijf Level-waarden staan er expliciet, ook de twee die dezelfde toon
   delen: pending en unknown zijn allebei "nog niet" in plaats van een fout, dus
   ze blijven neutraal — dezelfde toon die de stip in de kop (container-status)
   voor diezelfde twee gebruikt. De cirkel neemt de voorgrondkleur van de toon
   als vulling en zet er witte inkt op; die combinatie is dezelfde die .badge al
   op contrast heeft laten toetsen. */
.vcard-ok      .vcard-head { background: var(--status-ok-bg); }
.vcard-ok      .vcard-mark { background: var(--status-ok-fg);   color: var(--text-inverse); }
.vcard-warn    .vcard-head { background: var(--status-warn-bg); }
.vcard-warn    .vcard-mark { background: var(--status-warn-fg); color: var(--text-inverse); }
.vcard-broken  .vcard-head { background: var(--status-err-bg); }
.vcard-broken  .vcard-mark { background: var(--status-err-fg);  color: var(--text-inverse); }
.vcard-pending .vcard-head,
.vcard-unknown .vcard-head { background: var(--status-idle-bg); }
.vcard-pending .vcard-mark,
.vcard-unknown .vcard-mark { background: var(--status-idle-fg); color: var(--text-inverse); }

.vcard-chain { padding: var(--space-6) var(--space-7); }

/* ---------- keten (chain) ---------- */

/* Zes koppelingen onder het oordeel: waar de meting breekt, niet waarom — de
   toelichting staat al in de zin ernaast en in de panelen eronder. Eén regel
   per koppeling, geen paneel met een eigen kop: dit is een lijst, geen kaart. */
/* Drie kolommen van twee in plaats van zes regels onder elkaar. De volgorde
   waarin een storing doorwerkt is nog steeds de volgorde waarop de evaluatie
   is gebouwd, maar als leesvolgorde vroeg het van de lezer om zes losse
   zelfstandige naamwoorden tegelijk vast te houden. Server, Domein en Metingen
   geven elk paar het woord dat zegt waar het over gaat. Indeling in chain.go
   (chainColumns), niet hier. */
.chain {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-6);
}
.chain-group { min-width: 0; }
/* Een haarlijn tussen de kolommen en niet eromheen: de kaart heeft zijn rand
   al, en drie omkaderde vakjes in een omkaderd vak is één kader te veel. */
.chain-group + .chain-group {
  border-left: var(--border-hairline);
  padding-left: var(--space-6);
}
.chain-group-title {
  margin: 0 0 var(--space-4);
  font-size: var(--fs-xs); font-weight: var(--fw-semibold);
  letter-spacing: .08em; text-transform: uppercase;
  color: var(--text-muted);
}
.chain-links { list-style: none; margin: 0; padding: 0; }
.chain-item {
  /* flex-start, niet center: een schakel die niet in orde is heeft een tweede
     regel met de reden eronder, en de markering hoort dan naast de eerste regel
     te staan en niet halverwege de twee. De marge op .chain-mark hieronder zet
     hem optisch op de x-hoogte van die eerste regel. */
  display: flex; align-items: flex-start; gap: var(--space-3);
  padding: var(--space-2) 0;
  /* Leesmaat, niet fijndruk. Dit stond op --fs-sm toen de keten een dichte
     lijst van zes regels onder de kop was; in een eigen kaart met drie kolommen
     eromheen leest datzelfde formaat als een voetnoot naast een pagina die
     verder op 16px staat. */
  font-size: var(--fs-body);
}
.chain-text { display: flex; flex-direction: column; gap: 2px; } /* 2px: optische naadbreedte, geen maat */
/* De reden onder een schakel: neutraal van kleur, want het icoon ernaast draagt
   de toestand al. Zes rode zinnen onder elkaar maken van een pagina met één
   probleem een pagina die schreeuwt. Leesbreedte in ch, zoals .lede. */
.chain-detail { color: var(--text-body); font-size: var(--fs-base); max-width: 68ch; }
/* De weg naar de oplossing, op zijn eigen regel onder de zin. Geen knop: een
   knop belooft dat er iets gebeurt als je hem indrukt, en dit brengt je alleen
   ergens heen. Alleen zichtbaar bij een schakel die niet in orde is, dus twee
   ervan naast elkaar komt niet voor. */
.chain-action { display: inline-block; margin-top: var(--space-1); font-size: var(--fs-base); }
/* Eigen markeerklasse, geen hergebruik van .hdot: die stip is een kale kleur
   in vijf varianten en hoort bij de hostname-stippen elders (status.html),
   die wél een tooltip en tabindex hebben. Deze keten-marker is nooit
   interactief en tekent een eigen icoon per toestand (icon-check /
   icon-chain-* in icons.html) in plaats van alleen een kleur, want kleur
   alleen liet "stuk" en "in orde" zonder tekst niet uit elkaar houden. */
/* margin-top zet het teken optisch op de x-hoogte van de eerste regel; 4px is
   de helft van (regelhoogte 23 − glyph 16), naar beneden afgerond. */
.chain-mark { display: inline-flex; align-items: center; justify-content: center; flex: none; margin-top: 4px; }
/* De vijf glyphs zijn op 13px getekend, voor een lijst die zelf 13px was.
   Naast een label van 16px is dat een stipje, dus in de keten schalen ze mee. */
.chain-mark svg { width: 16px; height: 16px; }
.chain-mark.ok        { color: var(--status-ok-fg); }
.chain-mark.attention { color: var(--status-warn-fg); }
.chain-mark.broken    { color: var(--status-err-fg); }
/* unknown en checking delen dezelfde neutrale toon — geen van beide is een
   fout, en geen van beide is groen, dezelfde reden .verdict-pending en
   .verdict-unknown hierboven ook allebei --status-idle-fg nemen. Het
   onderscheid tussen de twee zit in het icoon (vraagteken tegenover drie
   puntjes), niet in de kleur. */
.chain-mark.unknown,
.chain-mark.checking  { color: var(--status-idle-fg); }
.chain-label { color: var(--text-strong); font-weight: var(--fw-medium); }
/* .chain-state has no visual rule of its own: it takes .visually-hidden
   (app.css, near .icon) to stay out of sight while remaining in the
   accessibility tree — StateLabel()'s word is now for a screen reader only,
   the icon above carries the meaning for a sighted reader. */

/* ---------- kruimelpad ---------- */

/* Hier stond één kruimel in hoofdletters, met letterspatiëring, vetgedrukt en
   vast onderstreept: "MEETSERVERS". Dat is de vorm van een eyebrow — een label
   bóven een kop — en daarom voelde het niet als een pad maar als een opschrift.
   Wat een kruimelpad tot kruimelpad maakt is niet de stijl van één woord maar de
   reeks: meerdere stappen, een scheidingsteken ertussen, en de laatste stap die
   de pagina zelf is en dus geen link.

   Dus: gewone kleine tekst in de gedempte toon, geen kapitalen, geen vaste
   onderstreping. Onderstreping komt bij hover en bij focus, zoals een
   kruimelpad dat hoort te doen — de vorm van het pad zegt al dat het navigatie
   is, en een rij vaste streepjes maakt van drie stappen drie knoppen. */
.crumbs ol {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--space-2);
  list-style: none; margin: 0; padding: 0;
}
.crumbs li { display: flex; align-items: center; gap: var(--space-2); }
.crumb {
  font-size: var(--fs-sm);
  color: var(--text-muted);
  text-decoration: none;
}
.crumb:hover, .crumb:focus-visible {
  color: var(--text-strong);
  text-decoration: underline;
  text-underline-offset: 3px;
}
/* De pagina zelf: donkerder dan de stappen erboven, want dit is waar je bent,
   en geen link — vandaar ook geen hover-regel. */
.crumb-here { color: var(--text-body); }
.crumb-sep { color: var(--text-faint); }

/* Elke tabel zit hierin, en sinds de containerpagina's Hostnames-lijst ook
   een onbegrensde klanthostname kan tonen: die ook. De pagina zelf schuift
   nooit opzij. */
/* overflow-x: auto dwingt de browser overflow-y ook tot "auto" (de
   overflow-spec laat "visible" op één as niet naast iets anders dan
   "visible" op de andere as toe) — dus alles wat een hstatus-tip boven de
   allereerste tabelrij laat uitsteken, wordt zonder ruimte erboven gewoon
   afgesneden in plaats van gewoon zichtbaar. padding-top plus een even
   grote negatieve margin-top geven dat scrollgebied die ruimte: de box
   wordt hoger, maar schuift evenveel omhoog, dus de tabel en de
   scrollbalk eronder blijven exact staan waar ze al stonden. Alleen de
   bovenkant heeft dit nodig — elke tip in dit bestand opent omhoog, dus
   de onderkant van de box heeft nooit iets te verbergen. */
.scroll-x { overflow-x: auto; padding-top: 170px; margin-top: -170px; }

/* ---------- KPI-strip ---------- */

.kpis {
  background: var(--surface-canvas);
  border: var(--border-hairline);
  border-radius: var(--radius-card);
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  margin-bottom: var(--space-6);
}
.kpis.k3 { grid-template-columns: repeat(3, 1fr); }
.kpi { padding: var(--space-6) var(--space-7); border-right: var(--border-hairline); }
.kpi:last-child { border-right: none; }
.kpi-k {
  font-size: var(--fs-sm); font-weight: var(--fw-semibold);
  letter-spacing: var(--tracking-caps); text-transform: uppercase;
  color: var(--text-body);
  margin-bottom: var(--space-4);
}
.kpi-v {
  font-family: var(--font-mono);
  font-size: var(--fs-2xl); font-weight: var(--fw-medium);
  font-variant-numeric: tabular-nums;
  color: var(--text-strong);
  letter-spacing: var(--tracking-tight);
  line-height: 1;
}
.kpi-v .u { font-size: var(--fs-base); color: var(--text-body); margin-left: var(--space-1); }
/* Twee van de vier tegels hebben een uitleg, twee niet, en een grid maakt de
   hele rij even hoog. Zonder deze regel plakt de uitleg onder het cijfer en
   houden de tegels zonder uitleg onderaan lucht over. De uitleg zakt daarom
   naar de onderkant, zodat alle vier de tegels dezelfde onderlijn hebben; de
   padding houdt er ruimte tussen als een tegel maar net hoog genoeg is. In deze
   smalle kolom heeft de leesbreedte van .help geen betekenis meer. */
.kpi { display: flex; flex-direction: column; }
.kpi .help { margin-top: auto; padding-top: var(--space-4); max-width: none; }
/* Een fout hoort in het cijfer te zitten, niet alleen in een pil ernaast. */
.kpi-v.err { color: var(--status-err-fg); }
.kpi-v.warn { color: var(--status-warn-fg); }
/* Same treatment as the KPI figure above: a warning is a colour on existing
   text, not a new component. Used for the preview-hostname line on the
   Containers overview — visible without hovering, unlike the dot tooltips
   beside it. */
.help.warn { color: var(--status-warn-fg); }

/* ---------- paneel ---------- */

.pnl {
  background: var(--surface-canvas);
  border: var(--border-hairline);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-none);
  margin-bottom: var(--space-6);
}
.pnl-h {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-4);
  padding: var(--space-5) var(--space-7);
  border-bottom: var(--border-hairline);
  font-size: var(--fs-lg); font-weight: var(--fw-bold);
  letter-spacing: var(--tracking-tight);
  color: var(--text-strong);
}
.pnl-b { padding: var(--space-7); }
/* Een paneel dat alleen een tabel bevat, wil geen dubbele lucht. */
.pnl-b.flush { padding: var(--space-5) var(--space-7); }
.pnl-intro {
  font-size: var(--fs-body); line-height: var(--lh-normal);
  color: var(--text-body);
  margin: 0 0 var(--space-6);
  /* Leesbreedte als maat, geen echte lay-outafmeting — geen token voor. */
  max-width: 900px;
  text-wrap: pretty;
}
.pnl-intro:last-child { margin-bottom: 0; }

/* ---------- tabel ---------- */

.tt { width: 100%; border-collapse: collapse; }
.tt thead th {
  font-size: var(--fs-xs); font-weight: var(--fw-semibold);
  letter-spacing: var(--tracking-caps); text-transform: uppercase;
  color: var(--text-body);
  padding: 0 var(--space-4) var(--space-3) 0;
  border-bottom: var(--border-hairline);
  white-space: nowrap;
}
.tt tbody td {
  padding: var(--space-5) var(--space-4) var(--space-5) 0;
  border-bottom: 1px solid var(--border-subtle);
  vertical-align: middle;
}
.tt td.top { vertical-align: top; }
.tt thead th:last-child, .tt tbody td:last-child { padding-right: 0; }
.tt tbody tr:last-child td { border-bottom: none; }
.tt .l { text-align: left; }
.tt .c { text-align: center; }
.tt .r, .tt th.r { text-align: right; }
/* Hover zegt "deze rij hoort bij elkaar". De kit hangt er ook een onClick op
   de hele <tr> aan; dat nemen we niet over, want een rijklik is niet met het
   toetsenbord te bereiken. De link blijft de naam. */
.tt tbody tr:hover td { background: var(--surface-panel); }

.num {
  font-family: var(--font-mono); font-size: var(--fs-body);
  font-variant-numeric: tabular-nums;
  text-align: right; color: var(--text-strong);
  white-space: nowrap;
}
/* Een nul is ruis naast een getal dat er wel toe doet. */
.num.zero { color: var(--text-faint); }

.host, .hn { font-family: var(--font-mono); font-size: var(--fs-body); color: var(--text-strong); }
/* text-underline-offset: 3px is een optische correctie onder de 12px-grens;
   ze komt hierna nog een paar keer terug op dezelfde waarde. */
.host a, .hn a { color: inherit; text-decoration: underline; text-underline-offset: 3px; text-decoration-color: var(--border-strong); }
.host a:hover, .hn a:hover { text-decoration-color: currentColor; }
.host .tag { display: block; font-family: var(--font-sans); font-size: var(--fs-sm); color: var(--text-body); margin-top: var(--space-1); }
.hn-row { display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-2); }
.hn-row + .hn-row { margin-top: var(--space-3); }
.hn-label { font-size: var(--fs-xs); letter-spacing: var(--tracking-caps); text-transform: uppercase; color: var(--text-body); }

.cname { font-size: var(--fs-md); font-weight: var(--fw-bold); color: var(--text-strong); }
.cname a { color: inherit; text-decoration: underline; text-underline-offset: 3px; text-decoration-color: var(--border-strong); }
.cname a:hover { text-decoration-color: currentColor; }
.imgcell { font-family: var(--font-mono); font-size: var(--fs-sm); color: var(--text-body); }

.empty {
  padding: var(--space-8) var(--space-4);
  text-align: center;
  color: var(--text-body);
  font-size: var(--fs-base);
}

/* De dichte variant: alleen voor de twee logtabellen. Daar scan je, en dan
   telt hoeveel regels er passen zwaarder dan hoeveel lucht er omheen staat. */
.tt.dense tbody td { padding: var(--space-2) var(--space-4) var(--space-2) 0; font-family: var(--font-mono); font-size: var(--fs-sm); }
.tt.dense .num { font-size: var(--fs-sm); }
.logstatus { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
.logstatus.s2 { color: var(--text-body); }
.logstatus.s4 { color: var(--status-warn-fg); }
.logstatus.s5 { color: var(--status-err-fg); font-weight: var(--fw-bold); }

/* ---------- statuspil ---------- */

.badge {
  display: inline-flex; align-items: center; gap: var(--space-2);
  /* --pad-chip is 4px 10px; deze pil is bewust 1px breder (11px) dan die
     token — een optische correctie, geen ontbrekend token. */
  padding: var(--space-1) 11px;
  border-radius: var(--radius-pill);
  font-size: var(--fs-sm); font-weight: var(--fw-semibold);
  white-space: nowrap;
}
/* 7px stip: kleiner dan elke spacing-token, puur optisch. */
.badge::before { content: ""; width: 7px; height: 7px; border-radius: var(--radius-pill); flex: none; background: currentColor; }
.badge.ok   { background: var(--status-ok-bg);   color: var(--status-ok-fg); }
.badge.warn { background: var(--status-warn-bg); color: var(--status-warn-fg); }
.badge.err  { background: var(--status-err-bg);  color: var(--status-err-fg); }
.badge.idle { background: var(--status-idle-bg); color: var(--status-idle-fg); }
.badge.ok::before   { background: var(--status-ok-dot); }
.badge.warn::before { background: var(--status-warn-dot); }
.badge.err::before  { background: var(--status-err-dot); }
.badge.idle::before { background: var(--status-idle-dot); }
/* Melding boven een formulier: dezelfde tonen, maar als blok. */
.notice { display: block; margin-bottom: var(--space-4); }

/* ---------- balk voor een onbevestigd adres ---------- */

/* Eén regel bovenaan elke pagina achter een sessie, met de knop ernaast in
   plaats van eronder: hij hoort te lezen als een mededeling met een uitweg,
   niet als een blok dat de pagina opendrukt. Op smal wordt hij vanzelf twee
   regels — flex-wrap, geen tweede regel opmaak. */
.verify-bar {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-4);
  margin-bottom: var(--space-6);
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md);
  background: var(--status-warn-bg); color: var(--status-warn-fg);
  font-size: var(--fs-sm);
}
.verify-bar p { margin: 0; flex: 1 1 320px; }

/* ---------- statusstip (hstatus) ---------- */

/* Vorm van de HostStatus-component uit de s.host-UI-kit: een stip die zijn
   toestand toont in een tooltip erboven, met een pijltje terug naar de stip.
   Hier gedeeld tussen de containerstatus en de hostname-checks — allebei
   waren tekstbadges, allebei zijn nu deze stip.

   Met het toetsenbord bereikbaar (tabindex) en de tooltip verschijnt ook op
   focus, niet alleen op hover — anders is hij voor wie niet met de muis werkt
   gewoon niet te openen. De betekenis staat daarnaast als tekst op het
   element zelf (aria-label), voor wie de tooltip niet kan lezen; de
   zichtbare tooltip is aria-hidden zodat een schermlezer niet twee keer
   dezelfde tekst krijgt. */
.hstatus {
  position: relative;
  display: inline-flex; align-items: center;
  cursor: default;
  /* 2px: iets groter duidelijk-focusbaar gebied dan de kale 10px-stip. */
  padding: 2px;
  border-radius: var(--radius-xs);
}
/* 10px stip: kit-maat, kleiner dan elke spacing-token, puur optisch. */
/* Geen randje om de groene stip. Dat stond er even om het lime op wit zijn vorm te
   geven, maar het maakte er een knopje van in plaats van een lampje — en de puls
   hieronder doet dat werk beter: iets dat beweegt zie je ook zonder contrastrand. */
.hdot { width: 10px; height: 10px; border-radius: var(--radius-pill); flex: none; background: var(--status-ok-dot); }
.hdot.warn { background: var(--status-warn-dot); }
.hdot.err  { background: var(--status-err-dot); }
.hdot.idle { background: var(--status-idle-dot); }

.htip {
  position: absolute; left: 50%; bottom: calc(100% + var(--space-3));
  transform: translateX(-50%);
  background: var(--ink-900); color: var(--text-inverse);
  /* 13px pad-links/rechts: kit-maat voor deze bel, geen token. */
  padding: var(--space-3) 13px;
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-md);
  white-space: nowrap;
  opacity: 0; visibility: hidden;
  transition: opacity 120ms ease;
  z-index: 30;
  display: flex; flex-direction: column;
  gap: 7px; /* tussen de regels in de bel, onder de 12px-grens */
  font-size: var(--fs-sm); font-weight: var(--fw-medium);
}
/* De uitleg bij "niet gezond" is een hele zin, geen checklist-regel — die mag
   wél wrappen, anders wordt de bel breder dan een telefoonscherm. Een echte
   width in plaats van max-width: een kolom-flexcontainer zonder eigen
   breedte valt bij shrink-to-fit terug op het langste losse woord van zijn
   regels, niet op de hele regel, en wordt dan een lint van één woord breed. */
.htip.wrap { white-space: normal; width: 260px; }
.hstatus:hover .htip,
.hstatus:focus-visible .htip { opacity: 1; visibility: visible; }
/* Pijltje als border-trick, dezelfde truc als de driehoek bij <details>
   verderop: 6px tekent alleen dit ene pijltje en staat verder nergens. */
.htip::after {
  content: ""; position: absolute; top: 100%; left: 50%;
  transform: translateX(-50%);
  border: 6px solid transparent; border-top-color: var(--ink-900);
}
.htip-row { display: flex; align-items: center; gap: 9px; } /* 9px: zelfde optische afstand als tussen driehoek en label bij <details> */
.htip-row .v { color: var(--color-accent); display: inline-flex; flex: none; }
/* Bewust geen lime hier: een streepje bij "Adres nog niet actief" in dezelfde
   kleur als een geslaagde controle zou lezen alsof het ook geslaagd is. */
.htip-row .x { color: var(--status-warn-dot); display: inline-flex; flex: none; }

/* Elke .hn-row die in .scroll-x zit, kan door een lange hostname helemaal
   naar de rechterrand van wat er te scrollen valt geduwd worden — die rand
   is ook de rand van .scroll-x zelf, dus de helft van een gecentreerde bel
   zou zonder extra scrollruimte onbereikbaar blijven — precies de
   valse-pas situatie die dit bestand moet vermijden. Rechts verankeren in
   plaats van centreren houdt de bel binnen wat al zichtbaar is. Dit geldt
   voor de Hostnames-kolom in de containertabel (containers.html) en voor de
   Hostnames-lijst op de containerpagina (container.html), die om dezelfde
   reden in .scroll-x zit. De containerstatus-stip (in de Status-kolom, los
   van .scroll-x) blijft gecentreerd: rechts verankeren zou die bel juist
   voorbij de linkerrand duwen. */
.scroll-x .hn-row .htip { left: auto; right: 0; transform: none; }
.scroll-x .hn-row .htip::after { left: auto; right: 7px; transform: none; } /* 7px: onder het bolletje, geen token */

/* A long hostname must never push its own status dot onto a second line —
   a dot that has drifted away from its hostname reads as attached to
   whatever happens to sit above it instead. Both the Containers overview
   (inside .scroll-x, a narrow column) and the container page's Hostnames
   panel (wider, but a customer's own domain is not bounded in length
   either) need this, so the rule is unscoped and applies to every .hn-row.

   Flex-wrap decides line breaks from each item's own size, before any
   shrinking is applied — so with the dot as a separate flex item after the
   role label, the browser wraps the dot onto its own line the moment the
   hostname alone is long enough to fill the row, and there is nothing to
   stop it. .hn-main groups the hostname with its dot into one flex item
   instead of two, so there is no line break left to put between them: the
   pair either fits on the row with the label, or the label alone (the
   least important of the three) drops to its own line below — checked up
   to a 141-character hostname, on both pages, at which point the row
   itself runs out of room and .scroll-x's horizontal scroll takes over
   instead, which keeps the page itself from ever overflowing (confirmed
   with scrollWidth === innerWidth on both). The container page's Hostnames
   panel is not a table, but it wraps its rows in .scroll-x for exactly
   this reason — without it, an unbounded customer hostname widens the
   panel instead of scrolling inside it, and the *page* gains the
   horizontal scrollbar this whole mechanism exists to avoid. Either way
   the dot stays exactly where it belongs, immediately after the hostname
   it describes.

   `.hn` also gets white-space: nowrap here: a hostname is mostly hyphens
   (this file's own naming convention prefixes one with "t-preview-"), and
   the UA's line-breaking rules treat a hyphen as a break opportunity —
   without this, a long-enough hostname wraps *inside itself* before the
   row-level rule above ever gets a say, taking the dot down with it. */
.hn-row .hn-main { display: flex; align-items: center; gap: var(--space-2); }
.hn-row .hn { white-space: nowrap; }
.hn-row .hn-label,
.hn-row .hstatus { flex: none; }

/* Dezelfde reden, andere plek: de containerstatus-stip bovenaan container.html
   staat door justify-content: space-between aan de rechterkant van
   .head-row. Een gecentreerde tip overschrijdt daar de paginarand zodra het
   scherm smal genoeg is (gevonden door scrollWidth tegen innerWidth te
   leggen op 480px, niet door naar de opmaak te kijken) — ook al is hij op
   dat moment onzichtbaar, hij telt nog mee voor de breedte van de pagina. */
.head-row .htip { left: auto; right: 0; transform: none; }
.head-row .htip::after { left: auto; right: 7px; transform: none; }

/* Een pulserende stip betekent "dit draait" — dus alleen op de actieve
   containerstatus, nooit op een hostname-stip en nooit op een storing of een
   "wordt opgezet". Rustig en traag: dit staat in een tabel die iemand de hele
   dag leest. Er stond hier ooit een knipperende-stip-regel met dezelfde
   reduced-motion-guard; die is verwijderd en dit is een nieuwe, geen
   teruggevonden oude. */
@keyframes hdot-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: .45; }
}
/* Dezelfde puls op twee plekken: de stip in de kolom Status van de lijst, en de
   stip in de pil rechtsboven op de pagina van één meetserver. Alleen bij "in orde"
   — een lampje dat knippert bij een fout zou aandacht trekken naar iets dat al
   rood en al benoemd is. */
.hdot.pulse, .badge.ok::before { animation: hdot-pulse 2.4s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce) {
  .hdot.pulse, .badge.ok::before { animation: none; }
}

/* De drie bolletjes van "wordt opgezet" lopen achter elkaar aan en uit. Het is
   het enige pictogram dat beweegt, en het is er een waar bewegen niet decoratie
   is maar de mededeling zelf: hier wordt op dit moment aan gewerkt, en er komt
   vanzelf iets anders te staan. Een statische stip zegt dat ook, maar iemand
   die een minuut naar een opzetkaart kijkt, weet dan niet of hij naar een
   proces kijkt of naar een bevroren scherm.

   Opacity en geen schaal of verspringing: het icoon staat op 13 px in een
   rondje van 36, en alles wat beweegt binnen die maat leest als trillen. De
   drie delen dezelfde keyframe met een verschoven start, zodat het van links
   naar rechts loopt -- de leesrichting, dus het loopt vooruit en niet heen en
   weer.

   1,2 s en een derde ertussen: langzaam genoeg om rustig te blijven op een
   kaart die minuten kan blijven staan, snel genoeg om binnen één blik als
   beweging herkend te worden. */
@keyframes working-dot {
  0%, 100% { opacity: 1; }
  50% { opacity: .3; }
}
.icon-working circle { animation: working-dot 1.2s ease-in-out infinite; }
.icon-working circle:nth-child(2) { animation-delay: .16s; }
.icon-working circle:nth-child(3) { animation-delay: .32s; }
/* Dezelfde afspraak als bij .hdot.pulse hierboven: wie beweging heeft
   uitgezet, krijgt het icoon zoals het was. De bolletjes blijven staan, en er
   verandert verder niets aan de kaart -- de mededeling stond er al in woorden. */
@media (prefers-reduced-motion: reduce) {
  .icon-working circle { animation: none; }
}

/* ---------- oordeel (cverdict) ---------- */

/* De stip eronder (hstatus/hdot) draagt de kleur al voor deze rij; deze zin
   is een tweede, onafhankelijk berekend signaal (verdict.For, niet
   .Unhealthy/.Status) en zou een eigen statuskleur naast de stip kunnen
   tegenspreken in plaats van bevestigen. Neutraal houden — --status-idle-fg
   hoort al bij de bestaande statuslaag in plaats van los gekozen te zijn, en
   --line-100 is dezelfde vlakke ondergrond als overal elders, hier hergebruikt
   als neutrale grondtoon in plaats van een eigen kleur voor dit label. */
.cverdict {
  display: inline-block;
  font-size: var(--fs-sm);
  color: var(--status-idle-fg);
  background: var(--line-100);
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-xs);
  margin-bottom: var(--space-2);
}

/* ---------- tabs ---------- */

/* Ruimte erboven, niet alleen eronder. De strip stond met space-6 onder de
   knoppenrij en space-6 boven het eerste paneel, en dat las als één blok: de tabs
   horen bij wat eronder komt en niet bij de knoppen erboven, dus de afstand naar
   boven moet gróter zijn dan die naar beneden. Vandaar space-9 tegen space-6 —
   asymmetrie die de groepering doet, niet lucht om de lucht.

   De knoppenrij houdt zijn eigen marge, dus dit komt erbovenop; daarom staat het
   hier en niet als een grotere margin-bottom op .btn-row, die ook op schermen
   zonder tabs staat. */
.tabs {
  display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-2);
  border-bottom: var(--border-hairline);
  margin-top: var(--space-9);
  margin-bottom: var(--space-6);
}
.tab {
  display: inline-flex; align-items: center; gap: var(--space-2);
  padding: var(--space-3) var(--space-5);
  border-radius: var(--radius-xs);
  font-size: var(--fs-md); font-weight: var(--fw-semibold);
  color: var(--text-body); text-decoration: none;
  transition: background 120ms ease, color 120ms ease;
}
.tab:hover { background: var(--surface-chip); color: var(--text-strong); }
.tab .count { font-weight: var(--fw-medium); color: var(--text-body); }
.tab.on { background: var(--green-900); color: var(--text-inverse); }
.tab.on:hover { background: var(--green-900); color: var(--text-inverse); }
.tab.on .count { color: var(--color-accent); }

/* ---------- knoppen ---------- */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2);
  font-family: var(--font-sans); font-size: var(--fs-sm); font-weight: var(--fw-semibold);
  line-height: 1;
  padding: var(--pad-button);
  border-radius: var(--radius-button);
  border: 1px solid transparent;
  cursor: pointer; white-space: nowrap; text-decoration: none;
  transition: background 120ms ease, opacity 120ms ease;
}
.btn.primary { background: var(--color-accent); color: var(--color-accent-ink); }
.btn.primary:hover { background: var(--color-accent-hover); color: var(--color-accent-ink); }
.btn.secondary { background: var(--surface-chip); color: var(--text-strong); }
.btn.secondary:hover { background: var(--border-default); color: var(--text-strong); }
.btn.ghost { background: transparent; color: var(--text-body); border-color: var(--border-default); }
.btn.ghost:hover { background: var(--surface-panel); color: var(--text-strong); }
/* --danger-500 op wit is ~3.4:1 — te licht voor tekst op deze maat. Rust op
   --danger-700 (6.96:1). Hover mag niet lichter worden — dat zou de tekst
   juist onder de 4.5:1-eis duwen zodra de cursor erop staat — dus een eigen,
   donkerder --danger-800 (10.06:1) in plaats van terug naar --danger-500. */
.btn.danger { background: var(--danger-700); color: var(--text-inverse); }
.btn.danger:hover { background: var(--danger-800); color: var(--text-inverse); }
.btn[disabled] { opacity: .45; cursor: not-allowed; }
.btn-row { display: flex; flex-wrap: wrap; gap: var(--space-3); margin-bottom: var(--space-6); }

.link-btn {
  background: none; border: none; padding: 0;
  font-family: inherit; font-size: var(--fs-body); font-weight: var(--fw-semibold);
  color: var(--text-body); cursor: pointer;
  text-decoration: underline; text-underline-offset: 3px;
}
.link-btn:hover { color: var(--text-strong); }

/* ---------- formulier ---------- */

/* Breedte van het formulier zelf (niet leesbreedte, dat is .lede/.pnl-intro);
   geen token op deze maat. */
.form-narrow { max-width: 1120px; }
.field { margin-bottom: var(--space-6); }
.flabel {
  display: block;
  font-size: var(--fs-base); font-weight: var(--fw-bold);
  color: var(--text-strong);
  /* 9px: optische correctie onder de 12px-grens. */
  margin-bottom: 9px;
}
.control {
  display: block; width: 100%;
  font-family: var(--font-sans); font-size: var(--fs-body);
  color: var(--text-strong);
  background: var(--surface-canvas);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-xs);
  padding: var(--space-3) var(--space-3);
}
/* Bewuste uitzondering op de --text-faint-opruiming hierboven: een
   placeholder op volle tekstcontrast leest als een ingevulde waarde, en die
   verwarring is een groter probleem dan het contrast. Geldt alleen zolang
   wat de placeholder laat zien ook in de .help onder het veld staat. */
.control::placeholder { color: var(--text-faint); }
.control:focus {
  outline: none;
  border-color: var(--green-800);
  /* Focusring-dikte: geen token voor de breedte van een box-shadow-ring. */
  box-shadow: 0 0 0 3px var(--green-100);
}
.control.mono { font-family: var(--font-mono); }
/* 150px: startmaat voor een tekstvak, geen token — de gebruiker sleept toch. */
textarea.control { min-height: 150px; resize: vertical; }
select.control {
  appearance: none; -webkit-appearance: none;
  padding-right: var(--space-8);
}

/* ---------- het pijltje van een uitklapveld ---------- */

/* Een <svg> over de select heen, geen background-image met een data-URI.

   De data-URI werkte niet: de Content-Security-Policy van deze app is
   default-src 'self' zonder eigen img-src, en die dekt ook afbeeldingen — dus
   data: is verboden en de browser tekende niets. Het viel niet op omdat de
   gedumpte schermen in dist/ geen CSP-header hebben en het pijltje daar wél
   verscheen. Zie de test die verbiedt dat er ooit weer een data:-URI in dit
   bestand komt.

   pointer-events: none, zodat een klik gewoon de select opent — het pijltje
   ligt eroverheen en mag niets van die klik afvangen. */
.selwrap { position: relative; display: block; }
.chev {
  position: absolute; right: var(--space-4); top: 50%;
  transform: translateY(-50%);
  display: inline-flex; pointer-events: none;
  color: var(--text-body);
}
.selwrap .chev { right: var(--space-5); color: var(--text-muted); }
.help {
  font-size: var(--fs-sm); line-height: var(--lh-normal);
  color: var(--text-body);
  /* 9px: dezelfde optische correctie als .flabel. */
  margin: 9px 0 0;
  /* Leesbreedte als maat, geen token voor. */
  max-width: 640px;
  text-wrap: pretty;
}
/* Een .help die op zichzelf staat in plaats van onder een veld te hangen.

   .help heeft met opzet geen ondermarge: hij is een bijschrift en hoort tegen
   het ding aan te liggen waar hij bij hoort. Als losse mededeling tussen twee
   blokken — de regel over hoe ver de geschiedenis terugreikt, boven de tegels
   op Analytics — plakte hij daardoor tegen de kaart eronder. */
.help.note { margin: 0 0 var(--space-6); }

.grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 0 var(--space-6); }
.actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-5); margin-top: var(--space-1); }

/* ---------- stappenrail ---------- */

/* Lijn per stap i.p.v. één ::before op de container: zo hangt het eindpunt
   af van het laatste bolletje, niet van de totale hoogte van de container.
   Die hoogte klopt alleen als de tekst van de laatste stap niet hoger is
   dan zijn bolletje — zodra .step-desc wrapt, liep de oude lijn door tot
   onder de tekst. Nu tekent elke stap zijn eigen segment, van net onder
   zijn bolletje tot de onderkant van zijn eigen box (waar het bolletje van
   de volgende stap begint); de laatste stap krijgt geen segment. */
.steps { position: relative; }
.step { display: flex; gap: var(--space-5); padding-bottom: var(--space-6); position: relative; }
.step:last-child { padding-bottom: 0; }
.step:not(:last-child)::before {
  content: ""; position: absolute;
  /* 11px centreert de 2px-lijn onder het 24px-bolletje (var(--space-6)),
     zoals voorheen — geen token, geometrie afgeleid van --space-6. */
  left: 11px;
  /* Startpunt net onder het bolletje: de 1px margin-top van .step-dot plus
     de bolletjeshoogte zelf, geen van beide een token. */
  top: calc(var(--space-6) + 1px);
  bottom: 0;
  width: 2px; background: var(--border-default);
}
.step-dot {
  width: var(--space-6); height: var(--space-6);
  flex: none;
  /* 1px: optische correctie zodat het bolletje uitlijnt met de eerste
     tekstregel van .step-title. */
  margin-top: 1px;
  border: 2px solid var(--border-strong);
  border-radius: var(--radius-pill);
  background: var(--surface-canvas);
  display: flex; align-items: center; justify-content: center;
  color: var(--white);
  z-index: 1;
}
.step.now .step-dot { border-color: var(--green-800); background: var(--green-100); }
.step.done .step-dot { border-color: var(--green-800); background: var(--green-800); }
/* Vinkje als border-trick: geen icon-font, geen los SVG-bestand. De maten
   (11px/6px/2px/1px) tekenen alleen dit ene vinkje en komen nergens anders
   terug, dus geen token. */
.step.done .step-dot::after {
  content: ""; width: 11px; height: 6px;
  border: 2px solid currentColor; border-top: 0; border-right: 0;
  transform: rotate(-45deg) translate(1px, -1px);
}
.step-title { font-size: var(--fs-body); font-weight: var(--fw-bold); color: var(--text-strong); }
.step-desc {
  font-size: var(--fs-base); line-height: var(--lh-normal);
  color: var(--text-body); margin-top: var(--space-1);
  /* Leesbreedte als maat, geen token voor. */
  max-width: 660px; text-wrap: pretty;
}

/* ---------- record ---------- */

.record {
  font-family: var(--font-mono); font-size: var(--fs-sm);
  background: var(--surface-chip);
  border-radius: var(--radius-xs);
  padding: var(--space-3) var(--space-4);
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-5);
  color: var(--text-strong);
  overflow-x: auto;
}
.record .k { color: var(--text-body); }
/* Leesbreedte als maat, geen token voor. */
.record.block { display: block; white-space: pre-wrap; overflow-wrap: anywhere; max-width: 640px; }

/* ---------- sparkline ---------- */

/* Eén reeks, dus geen legenda: de hostname in de rij is het label. De schaal
   begint bij nul — een afgesneden basislijn maakt van een rimpel een klif. */
.spark { display: block; overflow: visible; }
.spark-area { fill: var(--green-800); fill-opacity: .10; stroke: none; }
.spark-line {
  fill: none; stroke: var(--green-800);
  /* stroke-width is geen CSS-maat met een token — SVG-lijndikte. */
  stroke-width: 2.2;
  stroke-linejoin: round; stroke-linecap: round;
}
.spark-end { fill: var(--green-800); }
.spark-hit { fill: transparent; }
.spark-hit:hover { fill: var(--green-800); fill-opacity: .07; }

/* ---------- twee panelen naast elkaar ---------- */

/* "Metingen per dag" en "Metingen per event" zijn twee antwoorden op dezelfde
   vraag, dus horen ze op één regel te staan zolang dat kan.
   
   1.15fr tegen 1fr: de grafiek heeft de breedte nodig (veertien staven met een
   label eronder), de tabel heeft er vier kolommen die zich vlot laten smaller
   maken. Precies gelijk verdelen zou de grafiek als eerste onleesbaar maken.

   De omslag staat op 1500px en niet lager, en die grens komt uit meten en niet uit
   een ronde waarde: de rail is 288px, dus bij 1500px houdt een helft ongeveer 500px
   over, en daaronder wordt de viewBox van 820 zo klein geschaald dat de dagnamen
   onder de staven verdwijnen. Zie .bars in dit bestand voor diezelfde afweging aan
   de andere kant.

   De grafiek zelf hoeft hier niets bijzonders: sinds de dagnamen HTML zijn en
   buiten de SVG staan, krimpen de staven zonder dat er een label verdwijnt.

   Geen align-items: start. De twee panelen worden even hoog, en de lege ruimte
   onder de kortste komt binnen zijn eigen rand te liggen in plaats van ernaast —
   twee gelijke kaarten in plaats van één kaart met een gat eronder. */
@media (min-width: 1500px) {
  .duo {
    display: grid;
    grid-template-columns: 1.15fr 1fr;
    gap: var(--space-6);
    /* De rechterkolom is één paneel dat zich over de hoogte van de hele
       linkerkolom uitstrekt. start, want de tabel mag niet meerekken met een
       linkerkolom die door de tegels langer is geworden — dan zou hij eindigen
       op een gat onder zijn laatste rij. */
    align-items: start;
  }
  /* De panelen binnen het raster dragen hun eigen ondermarge niet meer — de
     gap doet dat werk — maar het raster zelf moet die marge wél hebben. Zonder
     deze regel plakt het paneel eronder (Browserverdeling) tegen de onderkant
     van de tabel, terwijl elk ander paneel op deze pagina er --space-6 vanaf
     staat. */
  .duo { margin-bottom: var(--space-6); }
  .duo > .pnl { margin-bottom: 0; }
  /* Alleen binnen het raster: hierbuiten staan de grafiek en de tabel onder
     elkaar, en dan is de ondermarge van de grafiek de enige ruimte tussen die
     twee. */
  .duo-col > .pnl:last-child { margin-bottom: 0; }
}

/* De linkerkolom: de tegels, en daaronder de grafiek. Een eigen wrapper en niet
   twee grid-items, zodat de tabel rechts één cel blijft die naast allebei staat
   in plaats van naast een van de twee. */
.duo-col { display: flex; flex-direction: column; }

/* ---------- statistiektegel ---------- */

/* Twee getallen naast elkaar boven de grafiek. Eigen component en geen .pnl:
   een paneel heeft een kop met een rand eronder, en dat is drie regels chroom
   om één getal heen. Deze heeft alleen een rand.

   auto-fit met een minimum, zodat ze onder elkaar vallen zodra ze naast elkaar
   te smal worden om een getal in de display-letter te dragen. */
.tiles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--space-5);
  margin-bottom: var(--space-6);
}
.tile {
  border: var(--border-hairline);
  border-radius: var(--radius-card);
  background: var(--surface-canvas);
  padding: var(--space-6);
}
.tile-h { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); }
.tile-label { color: var(--text-body); font-size: var(--fs-base); }
.tile-info { display: inline-flex; color: var(--text-faint); }
.hstatus:hover .tile-info,
.hstatus:focus-visible .tile-info { color: var(--text-body); }

/* De display-letter en tabulaire cijfers, dezelfde combinatie als de
   herokop op het dashboard: dit is één getal dat gelezen moet worden, niet een
   zin. */
.tile-v {
  margin: var(--space-3) 0 0;
  font-family: var(--text-metric-family);
  font-size: var(--fs-2xl); font-weight: var(--fw-bold);
  font-variant-numeric: tabular-nums;
  line-height: var(--lh-tight); color: var(--text-strong);
}
.tile-trend {
  display: flex; align-items: center; gap: var(--space-1);
  margin: var(--space-3) 0 0;
  font-size: var(--fs-base); font-variant-numeric: tabular-nums;
  color: var(--text-strong);
}
/* Alleen omlaag krijgt een kleur. Minder metingen en minder consent zijn
   allebei slechter, dus één regel dekt beide tegels — en een groen cijfer bij
   elke stijging maakt van een tegel een scorebord. */
.tile-trend.down { color: var(--status-err-fg); }
.tile-arrow { display: inline-flex; }
.tile-note {
  margin: var(--space-1) 0 0;
  font-size: var(--fs-sm); color: var(--text-muted); text-wrap: pretty;
}

/* ---------- aandeelstaaf in een tabelrij ---------- */

/* Eén staaf per rij in één tint, en geen cirkel: zie het commentaar bij de tabel
   in daychart.html. Twaalf pixels hoog (onder de 24px die de mark-spec als
   maximum geeft), vierkant aan de basislijn en 4px rond aan het data-einde —
   precies de vorm die de spec voorschrijft, zodat het einde van de staaf de
   waarde markeert en de basis de nul.

   De baan eronder is de groene wash op 10%, dezelfde als de sparkline gebruikt:
   dat is de honderd procent waar de staaf een deel van is, en hij mag niet mee
   gaan concurreren met de staaf zelf. */
/* nowrap op de cel en een krimpbare baan: in een halfbreed paneel is deze kolom
   de smalste van vier, en daar viel het percentage onder de staaf. Liever een
   kortere staaf dan een getal op een tweede regel — de staaf is de illustratie, het
   getal is het antwoord. Onder de minimumbreedte neemt .scroll-x het over, zoals
   bij elke tabel hier. */
.tt td.share-cell { white-space: nowrap; }
.share {
  display: inline-block; vertical-align: middle;
  width: clamp(48px, 8vw, 120px); height: 12px;
  background: var(--green-800); background: color-mix(in srgb, var(--green-800) 10%, transparent);
  border-radius: 0 var(--radius-xs) var(--radius-xs) 0;
  overflow: hidden;
}
.share-fill {
  display: block; height: 100%;
  background: var(--green-800);
  border-radius: 0 var(--radius-xs) var(--radius-xs) 0;
}
/* Het getal draagt een teksttoken, nooit de kleur van de staaf. */
.share-num {
  margin-left: var(--space-3);
  font-family: var(--font-mono); font-size: var(--fs-sm);
  color: var(--text-muted);
}

/* ---------- gegevenskaart (tab Overzicht) ---------- */

/* Label links, waarde rechts, één per regel met een haarlijn ertussen. Een <dl>
   en geen tabel: dit zijn paren, geen rijen met kolommen die je vergelijkt.

   220px voor de labelkolom: "Configuratie uit Tag Manager" is het langste label
   en past daarin op één regel, zodat de waardes allemaal op dezelfde x beginnen.
   Onder 700px klapt het naar twee regels per paar — een label van 220px naast een
   waarde van 100px is op een telefoon meer marge dan inhoud. */
.facts { display: grid; margin: 0; }
.fact {
  display: grid; grid-template-columns: 220px 1fr; gap: var(--space-4);
  align-items: baseline;
  padding: var(--space-4) 0;
  border-bottom: var(--border-hairline);
}
.fact:first-child { padding-top: 0; }
.fact:last-child { border-bottom: none; padding-bottom: 0; }
.fact dt { color: var(--text-body); }
/* De waardecel is een rij, want naast de waarde staat soms een knop — kopiëren
   bij de drie waarden die ergens anders ingevuld moeten worden, Upgrade bij het
   verbruik. De knop hangt rechts via .fact-act's margin-left:auto, zodat alle
   knoppen in de kolom op één lijn staan ongeacht hoe lang de waarde is.

   min-width:0 zodat .clip daadwerkelijk kan afkappen: een grid- of flexcel
   krimpt anders niet onder de breedte van haar inhoud. Het staat nu op de
   waarde zelf (.fact-v) en niet meer op de dd, want de dd is de rij. */
.fact dd { margin: 0; color: var(--text-strong); min-width: 0;
  display: flex; align-items: center; gap: var(--space-4); }
.fact-v { min-width: 0; }
.fact-v.mono { font-family: var(--font-mono); font-size: var(--fs-sm); }
.fact-v.clip { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fact-act { margin-left: auto; flex: none; }
/* De Server container URL is de enige waarde hier die ook ergens naartoe gaat.
   Onderstreept in plaats van gekleurd: op een regel met mono-tekst leest een
   afwijkende kleur als een statuskleur. */
.fact-link { color: var(--text-strong); text-decoration: underline; text-underline-offset: 3px; }
.fact-link:hover { color: var(--text-strong); }

@media (max-width: 700px) {
  /* De knop onder de waarde in plaats van ernaast: op één kolom zou
     margin-left:auto hem tegen de rechterrand duwen met de waarde eronder
     weggedrukt. */
  .fact dd { flex-wrap: wrap; }
  .fact-act { margin-left: 0; }
}

.facts-more { margin-top: var(--space-5); }
.facts-more summary {
  cursor: pointer; color: var(--text-body); font-size: var(--fs-sm);
}
.facts-more .record.block { margin-top: var(--space-3); }

@media (max-width: 700px) {
  .fact { grid-template-columns: 1fr; gap: var(--space-1); }
}

/* ---------- staafdiagram: metingen per dag ---------- */

/* Eén reeks van veertien dagen, dezelfde inkt als de sparkline hierboven: dit
   en die tekenen hetzelfde soort ding op twee tijdschalen, en twee kleuren
   zouden suggereren dat het twee grootheden zijn.

   Geen breedte of hoogte op het element, alleen een viewBox (monitor.Bars kiest
   880×150): de grafiek schaalt daardoor met het paneel en heeft geen eigen
   media query nodig. Dat schaalt de labels mee — op een telefoon wordt de tekst
   dus kleiner in plaats van dat er labels wegvallen. Bekende grens, geen
   ongeluk: dit is een scherm dat op een bureau wordt gelezen, en de alternatieven
   (elke tweede tick verbergen, of de grafiek zijdelings laten scrollen zoals de
   tabellen) kosten meer dan ze op die breedte opleveren. */
/* Zijdelings schuiven in plaats van labels van vier pixels: onder ongeveer 900px
   is het paneel smaller dan de grafiek, en dan schuift hij mee zoals de tabellen
   op deze schermen dat ook doen. Eigen wikkel en niet .scroll-x, want die heeft
   170px verticale ruimte voor tooltip-bellen die hier niet zijn. */
.bars-wrap { min-width: 0; }
.bars-plot { position: relative; }
/* Geen min-width en geen zijdelings schuiven meer: de staven mogen krimpen, want
   de labels doen dat niet meer mee — die staan als HTML buiten de SVG. Daardoor
   werkt dezelfde grafiek in een paneel van een halve schermbreedte én op een
   telefoon, en is de wikkel er alleen nog om de labels eronder te houden.
   preserveAspectRatio="none" hoort erbij: de hoogte is een vaste 150 en de breedte
   volgt het paneel, dus de staven rekken in de breedte in plaats van dat de hele
   tekening kleiner wordt. */
.bars { display: block; width: 100%; height: 150px; overflow: visible; }

/* Het getal van de hoogste dag, op de hulplijn. translateY(-100%) zet de
   onderkant van de tekst op de lijn, zoals de SVG-variant met dy="-4" deed. */
.bars-peak {
  position: absolute; left: 0;
  transform: translateY(-100%);
  font-family: var(--font-mono); font-size: var(--fs-2xs);
  color: var(--text-faint);
}

/* De dagnamen: één vak per dag, even breed en gecentreerd, dus recht onder de
   staven. Onder de 640px vallen de even dagen weg in plaats van dat alles
   onleesbaar klein wordt — dat kan nu, omdat het HTML-elementen zijn en geen
   tekst in een tekening. */
.bars-ticks { display: flex; margin-top: var(--space-2); }
.bars-ticks span {
  flex: 1 1 0; min-width: 0;
  text-align: center;
  font-size: var(--fs-2xs); color: var(--text-muted);
  white-space: nowrap; overflow: hidden;
}
/* Vandaag: dezelfde lichtere toon als zijn staaf, zodat de halve dag ook in het
   label als een halve dag leest. */
.bars-ticks span.now { color: var(--text-faint); }
/* Te veel staven voor even veel labels. visibility en geen display: het vak
   blijft staan, dus de labels die er wél zijn blijven boven hun eigen staaf
   hangen in plaats van naar links te schuiven.

   thin3 laat de eerste van elke drie staan en niet de tweede, zodat het eerste
   label onder de eerste staaf blijft — een reeks die met een gat begint leest als
   een grafiek die te laat begint. */
.bars-ticks.thin2 span:nth-child(even) { visibility: hidden; }
.bars-ticks.thin3 span:not(:nth-child(3n+1)) { visibility: hidden; }
@media (max-width: 640px) {
  .bars-ticks span:nth-child(even) { visibility: hidden; }
}

.bars-bar { fill: var(--green-800); }
/* Vandaag is nog niet af. Lichter, zodat een halve dag niet als een daling
   leest — de hover zegt het ook, maar de vorm mag er niet om liegen. */
.bars-bar.partial { fill: var(--green-500); }

/* Eén hulplijn op de hoogste dag, met zijn getal, in plaats van een as met
   streepjes: daarmee is elke staaf af te lezen zonder raster. */
.bars-rule { stroke: var(--border-default); stroke-width: 1; stroke-dasharray: 2 3; }
/* De stip boven een dag waarop niet alles aankwam. Alleen aanwezig waar er iets
   kwijt is, en uitgelegd door de regel onder de grafiek — die er om dezelfde
   reden alleen staat als er stippen zijn. */
.bars-lost { fill: var(--status-warn-fg); }
/* De as waar de staven op staan. Doorgetrokken tegenover de stippellijn van
   .bars-rule, en een tint donkerder: de grond van een grafiek is geen hulplijn.
   vector-effect, want preserveAspectRatio="none" rekt de tekening in de breedte
   en zou een lijn van 1 mee laten rekken tot hij dikker oogt dan hij is. */
.bars-axis {
  stroke: var(--border-strong); stroke-width: 1;
  vector-effect: non-scaling-stroke;
}

/* Het hover-vlak in de SVG draagt alleen nog zijn <title>. Het oplichten zit op
   .bars-slot hieronder: die laag ligt erbovenop en vangt de muis, dus een
   :hover-regel hier zou nooit meer afgaan. */
.bars-hit { fill: transparent; pointer-events: none; }

/* ---------- de bubbel boven een staaf ---------- */

/* Eén laag over de tekening, even hoog als de tekening zelf (.bars is 150px, en
   .bars-plot is er de relatieve ouder van). inset:0 in plaats van een eigen
   hoogte, zodat de laag meeverandert als die 150 ooit iets anders wordt. */
.bars-tips { position: absolute; inset: 0; }
.bars-slot {
  position: absolute; top: 0; bottom: 0;
  /* Geen achtergrond in rust: een raster van grijze vakken over een grafiek is
     precies het chroom dat deze grafiek niet heeft. */
  background: transparent;
  transition: background 120ms ease;
}
/* Geen kleurfallback vóór de color-mix, anders dan bij de aandeelstaaf
   hierboven: die valt terug op een volle kleur en dat is daar de bedoeling. Een
   volle groene rechthoek over de staven heen zou hier de grafiek onleesbaar
   maken, dus een browser zonder color-mix krijgt liever geen tint — de bubbel
   verschijnt dan nog steeds. */
.bars-slot:hover { background: color-mix(in srgb, var(--green-800) 6%, transparent); }

/* De bubbel zelf: donker, mono, met een puntje naar de staaf eronder. Verborgen
   met visibility en niet met display, zodat de overgang iets heeft om op te
   animeren en de breedte al vaststaat voordat hij verschijnt.

   translate(-50%, -8px): horizontaal gecentreerd op de sleuf, en 8px boven de
   bovenkant van de staaf — genoeg lucht voor het puntje eronder. Geen
   spacing-token, want het is de hoogte van dat puntje plus een haar. */
.bars-tip {
  position: absolute; left: 50%;
  transform: translate(-50%, -8px);
  visibility: hidden; opacity: 0;
  transition: opacity 120ms ease, visibility 120ms ease;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  background: var(--ink-900); color: var(--text-inverse);
  font-family: var(--font-mono); font-size: var(--fs-sm);
  line-height: var(--lh-tight);
  white-space: nowrap; pointer-events: none;
  z-index: 2;
}
/* Het puntje, met dezelfde border-truc als .htip hierboven: één manier om in
   dit bestand een tooltip-pijltje te tekenen, niet twee. 6px is de maat die
   .htip er ook voor gebruikt. */
.bars-tip::after {
  content: ""; position: absolute; top: 100%; left: 50%;
  transform: translateX(-50%);
  border: 6px solid transparent; border-top-color: var(--ink-900);
}
.bars-slot:hover .bars-tip { visibility: visible; opacity: 1; }

/* De eerste en de laatste bubbel zouden buiten het paneel steken. Ze schuiven
   naar binnen in plaats van te worden afgekapt; het puntje blijft op de staaf
   staan, want dat is waar het naar wijst. */
.bars-slot:first-child .bars-tip { left: 0; transform: translate(0, -8px); }
.bars-slot:first-child .bars-tip::after { left: 12px; transform: translateX(-50%); }
.bars-slot:last-child .bars-tip { left: 100%; transform: translate(-100%, -8px); }
.bars-slot:last-child .bars-tip::after { left: auto; right: 12px; transform: translateX(50%); }

/* ---------- power-ups ---------- */

/* De kop van een kaart die aanstaat krijgt dezelfde tint als de oordeelkaart
   bovenaan de pagina: op een tab met vier kaarten is de vorm van de kop het
   eerste wat je ziet, en dan hoef je de pillen niet één voor één te lezen om te
   weten welke functies aanstaan. Een kaart die uitstaat blijft wit — de
   afwezigheid van tint is hier de betekenis. */
.pu.on > .pnl-h { background: var(--status-ok-bg); }

.pnl-h .pu-title { display: flex; align-items: center; gap: var(--space-3); }
/* De rechterkant van de kop: eerst het detail, dan de toestand. In die volgorde
   omdat het detail bij de toestand hoort — "/av7t4fg4bx.js" en "aan" lezen als
   één mededeling, en de pil sluit hem af. */
.pu-right { display: flex; align-items: center; gap: var(--space-4); min-width: 0; }
.pu-meta { font-size: var(--fs-sm); font-weight: var(--fw-regular); color: var(--text-body); white-space: nowrap; }

/* De aan/uit-pil. Kleiner dan .badge en in kapitalen, want hij zegt één woord
   en moet naast een kaarttitel staan zonder ermee te concurreren. Geen
   hergebruik van .badge: die tekent zijn stip via ::before in currentColor, en
   deze pil heeft juist een stip in een ándere kleur dan zijn tekst — lime op
   donkergroen. */
.pu-state {
  display: inline-flex; align-items: center; gap: var(--space-2);
  padding: var(--space-1) 10px; /* 10px: optische maat voor een pil van één woord. */
  border-radius: var(--radius-pill);
  font-size: var(--fs-2xs); font-weight: var(--fw-semibold);
  letter-spacing: .08em; text-transform: uppercase;
  white-space: nowrap; flex: none;
  background: var(--surface-chip); color: var(--text-muted);
}
.pu-state.on { background: var(--green-800); color: var(--text-inverse); }
/* 7px: dezelfde optische maat als de stip in .badge. */
.pu-dot {
  width: 7px; height: 7px; border-radius: var(--radius-pill); flex: none;
  background: var(--gray-400);
}
.pu-state.on .pu-dot { background: var(--color-accent); }
.pu-meta.mono { font-family: var(--font-mono); }
.pu-desc {
  font-size: var(--fs-body); line-height: var(--lh-normal);
  color: var(--text-body); margin: 0 0 var(--space-5);
  /* Leesbreedte als maat, geen token voor. */
  max-width: 920px; text-wrap: pretty;
}

/* ---------- instellingen: rijen met een schuifje ---------- */

/* Een groep schakelaars in één paneel: titel en uitleg links, het schuifje
   rechts, en een scheidslijn tussen de rijen in plaats van een rand eromheen —
   de rand van het paneel doet dat werk al. */
.set-list { list-style: none; margin: 0; padding: 0; }
.set-row {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: var(--space-5); padding: var(--space-5) 0;
  border-top: 1px solid var(--border-subtle);
}
.set-row:first-child { border-top: 0; padding-top: 0; }
.set-row:last-child { padding-bottom: 0; }
.set-title {
  display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap;
  font-weight: var(--fw-medium); color: var(--text-strong);
}
/* De uitleg staat hier onder de titel en niet boven een knop, dus de marge
   eronder is niet die van een paneel. */
.set-row .pu-desc { margin: var(--space-2) 0 0; }
.set-row .btn { flex: none; }

/* Het schuifje. Een button en geen checkbox — zie de reden in het sjabloon —
   dus alle maten staan hier en niks komt van de browser. */
.switch {
  flex: none; appearance: none; cursor: pointer;
  /* 40×22 met een knop van 16: de twee overgebleven pixels aan elke kant zijn
     wat een schuifje als een schuifje laat lezen. Vaste maten, geen tokens:
     dit is één component en deze getallen staan verder nergens. */
  width: 40px; height: 22px; padding: 0;
  border: 0; border-radius: var(--radius-pill);
  background: var(--gray-300);
  transition: background 120ms ease;
}
.switch:focus-visible { outline: 2px solid var(--green-800); outline-offset: 2px; }
.switch.on { background: var(--green-800); }
.switch-knob {
  display: block; width: 16px; height: 16px; margin-left: 3px;
  border-radius: var(--radius-pill); background: #fff;
  transition: transform 120ms ease;
}
.switch.on .switch-knob { transform: translateX(18px); }

/* ---------- dialoog ---------- */

/* Alleen voor wat niet terug te draaien is. De browser doet de overlay, het
   afvangen van Escape en de focus; hier staat alleen hoe hij eruitziet. */
.dlg {
  max-width: 520px; padding: var(--space-6);
  border: 1px solid var(--border-default); border-radius: var(--radius-card);
  background: var(--surface-canvas); color: var(--text-body);
}
.dlg::backdrop { background: rgb(0 0 0 / .45); }
/* Eén regel van een keuzelijst: het rondje links, de tekst ernaast. Niet
   .set-row, want die duwt zijn twee helften juist uit elkaar — daar staat een
   schuifje aan de andere kant van het paneel, hier hoort de tekst tegen zijn
   eigen rondje aan. Het hele ding is een <label>, zodat de tekst net zo goed
   aanklikbaar is als het rondje zelf. */
.choice {
  display: flex; align-items: flex-start; gap: var(--space-4);
  padding: var(--space-4) 0; border-top: 1px solid var(--border-subtle);
  cursor: pointer;
}
.choice:first-child { border-top: 0; padding-top: 0; }
.choice input { flex: none; margin-top: .25em; }
.choice .pu-desc { display: block; margin: var(--space-2) 0 0; }
.dlg-title { margin: 0 0 var(--space-4); font-size: var(--fs-lg); color: var(--text-strong); }
.dlg-body { margin: 0 0 var(--space-6); line-height: var(--lh-normal); text-wrap: pretty; }
.dlg-row { display: flex; justify-content: flex-end; gap: var(--space-3); }

/* <details> blijft <details>. Alleen de driehoek is van ons. */

/* <details> blijft <details>. Alleen de driehoek is van ons. */
details { margin-top: var(--space-4); }
details > summary {
  list-style: none; cursor: pointer;
  /* 9px: optische correctie tussen driehoek en label, onder de 12px-grens. */
  display: inline-flex; align-items: center; gap: 9px;
  font-size: var(--fs-base); color: var(--text-body);
}
details > summary::-webkit-details-marker { display: none; }
/* Driehoek als border-trick, geen los icon: 5px is de vaste maat van deze
   ene driehoek en staat verder nergens. */
details > summary::before {
  content: ""; flex: none;
  border: 5px solid transparent; border-left-color: var(--text-faint);
  border-right-width: 0;
  transition: transform 120ms ease;
}
/* -2px: optische herpositionering zodat de driehoek na de rotatie weer op
   dezelfde plek staat. */
details[open] > summary::before { transform: rotate(90deg) translateX(-2px); }
details > summary:hover { color: var(--text-strong); }

.file-links { display: flex; flex-wrap: wrap; gap: var(--space-7); margin-top: var(--space-2); }
.file-links a { font-family: var(--font-mono); font-size: var(--fs-base); text-decoration: underline; text-underline-offset: 3px; }

/* ---------- login ---------- */

/* De enige plek waar de sage backdrop van het design system te zien is. In de
   app zelf staat de rail al tegen wit; hier is er geen rail. */
.login-wrap {
  min-height: 100vh;
  display: grid; place-items: center;
  padding: var(--space-6);
  background: var(--surface-app);
}
.login { width: 100%; max-width: 400px; }
.login .brand { padding: 0 0 var(--space-5); }
/* Op sage, niet op zwart: de wordmark is hier ink. */
.login .brand-name { color: var(--text-strong); }
.login .pnl { margin-bottom: 0; }
.login h1 { font-size: var(--fs-lg); font-weight: var(--fw-bold); margin: 0; }
.login .who {
  font-family: var(--font-mono); font-size: var(--fs-sm);
  color: var(--text-body); margin: var(--space-1) 0 var(--space-6);
}

/* ---------- filterbalk ---------- */

/* Eén rij keuzes boven de panelen die ze besturen, die op een smal scherm
   omklapt in plaats van de tabel opzij te duwen. */
.filters {
  display: flex; flex-wrap: wrap; gap: var(--space-3);
  margin-bottom: var(--space-6);
}

/* Een echte <select>, met appearance:none zodat hij op alle drie de platforms
   dezelfde hoogte en hetzelfde randje heeft — de systeemvariant is op macOS een
   halve rem hoger dan op Windows en dan staat de rij niet meer op één lijn.
   Het pijltje staat als <svg> in de label ernaast (zie .chev): een
   background-image met een data-URI wordt door de CSP van deze app geweigerd.

   De select is het label: de zichtbare tekst is altijd de gekozen waarde ("Alle
   browsers" of "Safari"), dus een label ernaast zou hetzelfde twee keer zeggen.
   Voor wie het scherm niet ziet staat het er wél, als .visually-hidden. */
.sel { position: relative; display: inline-flex; }
.sel select {
  appearance: none; -webkit-appearance: none;
  /* Leesmaat, gelijk aan de bodytekst. Dit stond op --fs-sm, en dat is de maat
     van een bijschrift: de filterbalk is het eerste wat een lezer op dit scherm
     aanraakt, en hij stond in kleinere letter dan alles wat eronder staat. */
  font-family: var(--font-sans); font-size: var(--fs-body); font-weight: var(--fw-medium);
  color: var(--text-strong);
  background: var(--surface-canvas);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-xs);
  /* Rechts ruimte voor het pijltje: 36px is de breedte van de afbeelding plus
     de optische marge eromheen, geen spacing-maat. */
  padding: var(--space-3) 36px var(--space-3) var(--space-4);
  cursor: pointer;
}
.sel select:hover { border-color: var(--border-strong); }
.sel select:focus-visible {
  outline: none;
  border-color: var(--green-800);
  box-shadow: 0 0 0 3px var(--green-100);
}

/* ---------- ring ---------- */

/* De ring en zijn legenda naast elkaar, en onder elkaar zodra de kolom te smal
   wordt. De legenda mag de ruimte hebben: daar staan de namen en de
   percentages, en dat is waar een lezer naar kijkt. */
.ringrow { display: flex; align-items: center; gap: var(--space-8); flex-wrap: wrap; }
.ring { flex: none; }
.ring-track { stroke: var(--line-100); }
/* Vijf tinten, donker naar licht, allemaal uit de bestaande groenreeks. Geen
   nieuwe kleuren: de donkerste is dezelfde --color-data als de staven en de
   sparkline, zodat het grootste segment dezelfde kleur heeft als de rest van de
   data op dit scherm. */
.ring-slice.tone-1, .legend-dot.tone-1 { stroke: var(--color-data); background: var(--color-data); }
.ring-slice.tone-2, .legend-dot.tone-2 { stroke: var(--green-500); background: var(--green-500); }
.ring-slice.tone-3, .legend-dot.tone-3 { stroke: var(--green-200); background: var(--green-200); }
.ring-slice.tone-4, .legend-dot.tone-4 { stroke: var(--green-100); background: var(--green-100); }
.ring-slice.tone-5, .legend-dot.tone-5 { stroke: var(--line-200);  background: var(--line-200); }

.legend { list-style: none; margin: 0; padding: 0; flex: 1; min-width: 200px; }
.legend-row {
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-2) 0;
}
/* 10px stip: dezelfde maat als de statusstip, en om dezelfde reden geen token. */
.legend-dot { width: 10px; height: 10px; border-radius: var(--radius-pill); flex: none; }
.legend-name { flex: 1; font-size: var(--fs-base); color: var(--text-strong); }
.legend-num {
  font-family: var(--font-mono); font-size: var(--fs-sm);
  font-variant-numeric: tabular-nums; color: var(--text-body);
}

/* ---------- sectiekop ---------- */

/* Boven een groep panelen, niet boven de pagina. --fs-xl staat in tokens.css
   al gereserveerd voor precies dit ("section heading") en werd nog nergens
   gebruikt: kleiner dan .h1 (--fs-3xl) en groter dan een paneelkop
   (--fs-lg), zodat pagina → sectie → paneel drie maten zijn in plaats van
   twee. De marge boven is ruim, want dit is waar het dashboard van de cijfers
   naar iets anders overstapt; zonder die lucht leest de sectie als een
   voetnoot bij de tabel erboven. */
.h2 {
  font-family: var(--font-display);
  font-size: var(--fs-xl);
  font-weight: var(--fw-bold);
  letter-spacing: var(--tracking-tight);
  color: var(--text-strong);
  margin: var(--space-9) 0 var(--space-5);
  text-wrap: balance;
}

/* De rechterkant van een paneelkop wanneer daar geen statuspil hoort maar
   tekst. Rustiger dan de kop, anders leest het als een tweede kop op dezelfde
   regel. */
.pnl-aside { font-size: var(--fs-sm); font-weight: var(--fw-medium); color: var(--text-muted); }

/* Eén paneel per pagina mag om aandacht vragen zonder een melding te zijn: de
   hulp-uitnodiging onderaan het dashboard. Een grijstint en geen kleur — elk
   gekleurd vlak op dit scherm betekent een toestand van iemands meting, en een
   uitnodiging is geen toestand. */
.pnl.tint { background: var(--surface-panel); }

/* ---------- balkje ---------- */

/* Een getal met een balkje eronder, in een tabelcel. Het cijfer staat er zelf
   ook: een balk alleen is af te lezen als "ongeveer een kwart" en niet als
   12%, en de kolom is te smal voor een as. role="img" met een aria-label
   staat in de template, want los van dat label is dit voor een schermlezer
   twee lege elementen. */
.meter { display: flex; flex-direction: column; gap: var(--space-2); min-width: 96px; }
.meter-v {
  font-family: var(--font-mono); font-size: var(--fs-sm);
  font-variant-numeric: tabular-nums; color: var(--text-body);
}
/* 4px hoog: dunner dan elke spacing-token, puur optisch — een dikkere balk
   gaat in een rij met een sparkline ernaast met die sparkline concurreren. */
.meter-track {
  display: block; height: 4px; border-radius: var(--radius-pill);
  background: var(--border-default); overflow: hidden;
}
.meter-fill { display: block; height: 100%; border-radius: var(--radius-pill); background: var(--text-strong); }

/* ---------- artikelenlijst ---------- */

.arts { list-style: none; margin: 0; padding: 0; }
.art {
  display: flex; gap: var(--space-4);
  padding: var(--space-5) 0;
  border-bottom: var(--border-hairline);
}
.art:last-child { border-bottom: none; }
/* Twee schuine streepjes in de merkkleuren, als aanloop naar de titel. Puur
   decoratief — het element is aria-hidden — dus het mag een gradient zijn in
   plaats van een icoon: dit is de enige plek die het gebruikt, en een SVG
   erbij zou een bestand zijn voor twee streepjes. De maten zijn optisch en
   staan daarom niet in tokens. */
.art-mark {
  flex: none; align-self: flex-start; margin-top: 3px;
  width: 11px; height: 15px;
  background: linear-gradient(105deg,
    transparent 0 14%, var(--green-700) 14% 40%,
    transparent 40% 60%, var(--color-accent) 60% 86%, transparent 86%);
}
.art-text { display: flex; flex-direction: column; gap: var(--space-1); min-width: 0; }
.art-title { font-size: var(--fs-base); font-weight: var(--fw-medium); color: var(--text-strong); }
.art-date { font-family: var(--font-mono); font-size: var(--fs-xs); color: var(--text-muted); }

/* ---------- smal ---------- */

/* .side blijft 288px tot de 900px-omslag hieronder, terwijl .kpis tot dan
   vier (of drie) gelijke kolommen wil. Tussen 901 en 1000px past dat niet
   meer naast elkaar en schuift de pagina zelf opzij — precies wat "de pagina
   zelf schuift nooit opzij" verbiedt. Twee kolommen lossen dat op zonder op
   de 900px-regel hieronder te wachten. Moet boven die regel staan, anders
   wint hij nooit: gelijke specificiteit, latere regel. */
@media (max-width: 1040px) {
  .kpis, .kpis.k3 { grid-template-columns: 1fr 1fr; }
}

@media (max-width: 900px) {
  .shell { flex-direction: column; }
  .side {
    width: auto;
    flex-direction: row; align-items: center;
    gap: var(--space-1);
    padding: var(--space-3);
    overflow-x: auto;
  }
  .brand { padding: 0 var(--space-2) 0 0; }
  /* Alleen op de rail: daar verdringen drie nav-items en een wordmark elkaar
     op een telefoon. Het inlogscherm heeft geen rail en wél de ruimte. */
  .side .brand-name { display: none; }
  .nav { flex-direction: row; gap: var(--space-0); }
  /* Kleinere padding en een kortere afstand tot het label dan de rechtopstaande
     rail: drie items en een logo moeten samen in de breedte van een telefoon
     passen, zonder dat de rail zelf gaat scrollen. */
  .nav-item { padding: var(--space-2); gap: var(--space-2); white-space: nowrap; }
  .side-foot { display: none; }
  .main { padding: var(--space-6) var(--space-5); }
  .h1 { font-size: var(--fs-2xl); }
  .lede { font-size: var(--fs-md); }

  /* Vier kolommen van 150px passen niet op een telefoon, en de kit tekent
     geen smalle variant. Eén kolom leest prima bij drie of vier waarden, en
     de scheidingslijn kan mee kantelen zonder dat de laatste cel een rand
     tegen de kaartrand aan krijgt. */
  .kpis, .kpis.k3 { grid-template-columns: 1fr; }
  .kpi { border-right: none; border-bottom: var(--border-hairline); }
  .kpi:last-child { border-bottom: none; }

  /* Twee selects naast elkaar op een telefoon worden allebei te smal voor
     hun eigen optietekst. */
  .grid2 { grid-template-columns: 1fr; }
}


/* ---------- kopieerknop ---------- */

/* Bij de drie waarden op een meetserverpagina die letterlijk ergens anders
   ingevuld moeten worden. Kleiner dan .btn en met een rand in plaats van een
   vulling: het is een hulpje naast een waarde, geen actie die de pagina
   verandert, en drie gevulde knoppen in één kolom zouden de kaart in drie
   knoppen veranderen met wat tekst ertussen.

   data-state komt van copy.js en zegt wat er werkelijk gebeurde — ook als het
   niet lukte. Zie de tweede tak in die luisteraar. */
.copy-btn {
  display: inline-flex; align-items: center; gap: var(--space-2);
  font-family: var(--font-sans); font-size: var(--fs-xs); font-weight: var(--fw-medium);
  line-height: 1;
  padding: var(--space-2) var(--space-3);
  border: var(--border-hairline); border-radius: var(--radius-button);
  background: var(--surface-canvas); color: var(--text-body);
  cursor: pointer; white-space: nowrap;
  transition: background 120ms ease, color 120ms ease, border-color 120ms ease;
}
.copy-btn:hover { background: var(--surface-panel); color: var(--text-strong); }
/* 14px: het pictogram moet kleiner zijn dan .icon's 20px, anders wordt de knop
   een pictogram met een woord eronder geplakt. Geen spacing-token op die maat. */
.copy-btn .icon { width: 14px; height: 14px; }
.copy-btn[data-state="done"]   { color: var(--status-ok-fg);  border-color: var(--status-ok-fg); }
.copy-btn[data-state="failed"] { color: var(--status-err-fg); border-color: var(--status-err-fg); }

/* ---------- chip: een pil zonder stip ---------- */

/* .badge tekent altijd een stip van 7px, want daar staat hij voor: een
   toestand. Het plan van een abonnement is geen toestand — er valt niets aan
   groen of rood te zijn — dus het krijgt dezelfde vorm zonder dat teken. */
.chip {
  display: inline-flex; align-items: center;
  padding: var(--pad-chip);
  border-radius: var(--radius-pill);
  background: var(--surface-chip); color: var(--text-strong);
  font-size: var(--fs-sm); font-weight: var(--fw-semibold);
  white-space: nowrap;
}

/* Kleine variant van .btn, voor een knop die in een tabelrij staat in plaats
   van in een knoppenrij. */
.btn.sm { font-size: var(--fs-xs); padding: var(--space-2) var(--space-4); }

/* De meter uit de dashboardtabel, maar op een rij: in een kolom van 220px met
   een label ernaast is percentage-boven-balk twee regels waar één volstaat. */
.meter-row { flex-direction: row; align-items: center; gap: var(--space-3); min-width: 0; }
/* 160px: lang genoeg om een verschil van een paar procent te laten zien, kort
   genoeg om de Upgrade-knop niet naar de volgende regel te duwen. Geen token. */
.meter-row .meter-track { width: 160px; flex: none; }

@media (max-width: 900px) {
  /* Onder elkaar in plaats van drie kolommen: de reden onder een schakel die
     niet in orde is, is een hele zin, en die past niet in een derde van een
     smal scherm. De haarlijn tussen de kolommen verdwijnt mee — een verticale
     scheiding tussen blokken die onder elkaar staan, scheidt niets. */
  .chain { grid-template-columns: 1fr; gap: var(--space-5); }
  .chain-group + .chain-group { border-left: none; padding-left: 0; }
}


/* ---------- instellingenformulier van een meetserver ---------- */

/* Een veld met een knop ernaast: de identifier staat readonly op het scherm
   omdat de lezer hem elders nodig heeft, niet omdat hij hier te veranderen is.
   De knop hangt rechts en krimpt niet mee. */
.field-row { display: flex; align-items: stretch; gap: var(--space-3); }
.field-row .control { flex: 1 1 auto; min-width: 0; }
.field-row .copy-btn { flex: none; }
/* Readonly en niet disabled: disabled haalt het veld uit de tabvolgorde en uit
   de selectie, en juist selecteren is wat je met deze waarde wilt doen. De
   grijze vulling zegt dat er niets te typen valt. */
.control[readonly] { background: var(--surface-panel); color: var(--text-body); cursor: default; }
.control[readonly]:focus { border-color: var(--border-default); box-shadow: none; }
/* Breder dan een gewoon formulier: de configuratie is één lange sleutel en de
   naam staat eronder, dus de kolom volgt het paneel in plaats van de leesmaat
   van een zin. */
.setform { max-width: 900px; }
.setform .field + .field { margin-top: var(--space-6); }
.setform textarea.control { min-height: 96px; resize: vertical; }

/* ---------- toegang ---------- */

/* De uitnodigingsregel: rol, adres, en twee knoppen. Het adresveld neemt de
   ruimte, de rest houdt zijn maat. Wrapt op een smal scherm in plaats van de
   knoppen weg te duwen. */
.invite {
  display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap;
  padding: 0 var(--space-7) var(--space-5);
}
.invite .control[type="email"] { flex: 1 1 260px; min-width: 0; }
.invite .btn { flex: none; }

/* De rol als pil. Eigen klasse en geen .badge: die tekent een stip, en een rol
   is geen toestand die aan of uit kan staan. */
.role {
  display: inline-flex; align-items: center;
  padding: var(--pad-chip);
  border-radius: var(--radius-pill);
  background: var(--status-err-bg); color: var(--status-err-fg);
  font-size: var(--fs-xs); font-weight: var(--fw-semibold);
  white-space: nowrap;
}

/* De twee knopjes achteraan een rij. Rechts uitgelijnd, want de kolom is dat
   ook, en met een echte rand: een pictogram zonder omtrek in een tabelrij leest
   als een decoratie in plaats van als iets waar je op kunt klikken. */
.row-acts { display: inline-flex; gap: var(--space-2); justify-content: flex-end; }
.icon-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; /* aanraakmaat voor een knop van één pictogram. */
  border: var(--border-hairline); border-radius: var(--radius-button);
  background: var(--surface-canvas); color: var(--text-body);
  cursor: pointer;
  transition: background 120ms ease, color 120ms ease, border-color 120ms ease;
}
.icon-btn:hover { background: var(--surface-panel); color: var(--text-strong); border-color: var(--border-strong); }

/* ---------------------------------------------------------------------------
   Pakketkaarten op het aanmaakscherm.

   De vorm van de prijzensectie op tagyard.io: naam, dan het bedrag groot met
   een klein valutateken, dan de limiet. Alles uit tokens.css, zoals de rest van
   dit bestand -- het lime is --lime-500, hetzelfde groen dat de site gebruikt.

   Waarom geen uitgelichte kaart zoals op de site: daar is het uitlichten een
   verkoopmiddel. Hier zou het een voorselectie zijn zonder vinkje, en dit
   scherm bestaat juist omdat mensen doorklikken op wat er al staat. Het accent
   hangt daarom aan :has(:checked) -- aan de keuze, niet aan een aanbeveling.
   --------------------------------------------------------------------------- */
.plan-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: var(--space-3);
}

.plan-card {
  position: relative;
  display: block;
  padding: var(--space-5);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-sm);
  background: var(--surface-canvas);
  cursor: pointer;
  transition: border-color 120ms ease, box-shadow 120ms ease;
}
.plan-card:hover { border-color: var(--border-strong); }

/* De radio zelf verdwijnt, maar blijft bereikbaar voor toetsenbord en
   schermlezer: de kaart is zijn label. Niet display:none -- dat haalt hem uit
   de tabvolgorde en dan is het formulier alleen met de muis in te vullen. */
.plan-card input {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
}

/* Gekozen: dezelfde nadruk die de site aan Pro geeft, hier aan wat jij aanwees. */
.plan-card:has(input:checked) {
  border-color: var(--text-strong);
  box-shadow: inset 0 0 0 1px var(--text-strong);
}
.plan-card:has(input:checked) .plan-name { color: var(--text-strong); }
.plan-card:has(input:checked)::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: var(--space-1);
  background: var(--lime-500);
  border-radius: 0 0 var(--radius-sm) var(--radius-sm);
}

/* De focusring hoort om de kaart, niet om de weggewerkte radio. */
.plan-card:has(input:focus-visible) {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}

.plan-card-body { display: block; }

.plan-name {
  display: block;
  font-size: var(--fs-md);
  font-weight: 700;
  color: var(--text-strong);
}

.plan-price {
  display: flex;
  align-items: baseline;
  gap: var(--space-1);
  margin: var(--space-4) 0 var(--space-3);
}
.plan-currency { font-size: var(--fs-lg); color: var(--text-muted); }
.plan-amount {
  font-size: var(--fs-3xl);
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.02em;
  color: var(--text-strong);
}
.plan-per { font-size: var(--fs-sm); color: var(--text-muted); }

.plan-limit { display: block; font-size: var(--fs-base); color: var(--text-muted); }
/* Het getal vet en in de mono, zoals op de site: het is wat de klant vergelijkt. */
.plan-limit b { font-family: var(--font-mono); font-weight: 700; color: var(--text-body); }

.plan-year {
  display: block;
  margin-top: var(--space-2);
  font-size: var(--fs-xs);
  color: var(--text-muted);
}

/* ---------------------------------------------------------------------------
   De termijnpil, en wat hij met de kaarten doet.

   Dezelfde vorm als BillingToggle op de site: een pil met twee helften, de
   actieve donker, een lime badge op het jaar. Daar is het een weergavefilter
   dat met javascript de bedragen wisselt; hier zijn het twee echte radio's die
   verstuurd worden, en de pil is niets dan hun opmaak. Vandaar :has() in plaats
   van een script.

   De maand staat aan bij het openen, net als op de site, en anders dan de
   pakketkaarten ernaast. Die kunnen leeg beginnen; de pil niet, want de
   bedragen op de kaarten volgen hem en er staat er altijd één. Een lege pil
   naast maandbedragen zegt "nog niets gekozen" over iets dat al gekozen te
   zien is.
   --------------------------------------------------------------------------- */
.field-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-3);
  /* Dezelfde optische correctie die .flabel zelf had; die staat hier op nul,
     want in een rij zou een ondermarge de twee helften uit elkaar trekken. */
  margin-bottom: 9px;
}
.field-head .flabel { margin-bottom: 0; }

.term-toggle {
  display: inline-flex;
  align-items: center;
  padding: var(--space-1);
  border: var(--border-hairline);
  border-radius: var(--radius-pill);
  background: var(--surface-list);
  flex-shrink: 0;
}

.term-seg {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: 7px 16px;
  border-radius: var(--radius-pill);
  cursor: pointer;
  font-size: var(--fs-sm);
  font-weight: var(--fw-bold);
  color: var(--text-muted);
  white-space: nowrap;
  transition: background 120ms ease, color 120ms ease;
}

/* Weggewerkt, niet weggehaald: display:none haalt hem uit de tabvolgorde en dan
   is de termijn alleen met de muis te kiezen. Zelfde reden als .plan-card. */
.term-seg input {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
}

.term-seg:has(input:checked) {
  background: var(--ink-900);
  color: var(--white);
}
.term-seg:has(input:focus-visible) {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}

.term-save {
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  font-weight: var(--fw-bold);
  letter-spacing: var(--tracking-wide);
  background: var(--lime-500);
  color: var(--ink-900);
  padding: 2px 7px;
  border-radius: var(--radius-pill);
}
/* Op de donkere helft draait de badge om: lime op donker naast wit zou twee
   accenten naast elkaar zijn. Zelfde omkering als .dark op de site. */
.term-seg:has(input:checked) .term-save {
  background: var(--white);
  color: var(--ink-900);
}

/* Wat de pil op de kaarten doet. Twee bedragen staan in de opmaak, één is
   zichtbaar; het jaar wacht tot de klant het aanwijst. Zonder keuze staat de
   maandprijs groot -- precies wat de kaart deed voordat de pil er was. */
.plan-price.on-year,
.plan-year.on-year { display: none; }
.field-pakket:has([name="interval"][value="jaar"]:checked) .plan-price.on-month,
.field-pakket:has([name="interval"][value="jaar"]:checked) .plan-year.on-month { display: none; }
.field-pakket:has([name="interval"][value="jaar"]:checked) .plan-price.on-year { display: flex; }
.field-pakket:has([name="interval"][value="jaar"]:checked) .plan-year.on-year { display: block; }
