/* Sociogram — single shared stylesheet.
   Design tokens ported from the original Tailwind/shadcn theme. */

:root {
  --background: 210 20% 98%;
  --foreground: 222 47% 11%;
  --card: 0 0% 100%;
  --card-foreground: 222 47% 11%;
  --primary: 199 89% 38%;
  --primary-foreground: 0 0% 100%;
  --secondary: 210 20% 96%;
  --secondary-foreground: 222 47% 11%;
  --muted: 210 20% 96%;
  --muted-foreground: 215 16% 47%;
  --accent: 199 89% 92%;
  --accent-foreground: 199 89% 28%;
  --destructive: 0 84% 60%;
  --destructive-foreground: 0 0% 100%;
  --success: 142 71% 45%;
  --warning: 38 92% 50%;
  --border: 214 20% 90%;
  --input: 214 20% 90%;
  --ring: 199 89% 38%;
  /* landing: recessed bands, the light "export sheet" that holds the app's
     light-only SVG art, and semantic edge colours for network/matrix marks */
  --surface-2: 210 20% 96%;
  --sheet: 0 0% 100%;
  --sheet-ink: 222 47% 11%;
  --sheet-border: 214 20% 88%;
  --edge-pos: 142 71% 45%;
  --edge-neg: 0 84% 60%;
  --shadow-lift: 0 1px 2px rgb(0 0 0 / 0.04), 0 8px 24px -12px rgb(15 23 42 / 0.18);

  /* ── typografie ──
     Twee families met een strikte taakverdeling. --font-display is voor stem:
     paginatitels, sectiekoppen, de prijs en het woordmerk. --font-sans is voor alles
     wat je leest om te wérken: tabellen, formulieren, knoppen, labels, sociogramnamen.
     Zet de serif nooit onder 17px en nooit op data; daar leest hij slechter, niet beter. */
  --font-sans: 'Inter', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --font-display: 'Source Serif 4', Georgia, 'Times New Roman', serif;

  /* Eén schaal, hele pixels. De oude halve maten (12.5, 13.5, 14.5) kwamen uit de
     Tailwind-port en scheelden op het scherm niets behalve inconsistentie. */
  --text-xs: 11px;
  --text-sm: 12px;
  --text-md: 13px;
  --text-base: 14px;
  --text-lg: 15px;
  --text-xl: 17px;
  --text-2xl: 20px;
  --text-3xl: 26px;
  --text-4xl: 34px;

  /* Ruimte in veelvouden van 4. Wat er stond (18, 22, 26, 36, 44, 72) was niet fout,
     maar ook geen systeem: ongelijke witruimte valt per stuk niemand op en telt over een
     hele pagina op tot iets dat rommelig oogt zonder dat je kunt aanwijzen waarom. */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 24px;
  --space-6: 32px;
  --space-7: 48px;
  --space-8: 64px;
  --space-9: 96px;

  /* Vier radii met elk een taak, in plaats van negen door elkaar. xs is er omdat een
     hoek die binnen een andere hoek zit kleiner moet zijn, anders lijkt hij scheef. */
  --radius-xs: 6px;
  --radius-sm: 8px;
  --radius: 12px;
  --radius-pill: 999px;
  --sidebar: 222 47% 11%;
  --sidebar-foreground: 210 20% 96%;
  --sidebar-accent: 222 47% 18%;
  --sidebar-accent-foreground: 210 20% 96%;
}

.dark {
  --background: 222 47% 6%;
  --foreground: 210 20% 96%;
  --card: 222 47% 9%;
  --card-foreground: 210 20% 96%;
  --primary: 199 89% 48%;
  --primary-foreground: 222 47% 6%;
  --secondary: 222 47% 14%;
  --secondary-foreground: 210 20% 96%;
  --muted: 222 47% 14%;
  --muted-foreground: 215 16% 57%;
  --accent: 199 89% 18%;
  --accent-foreground: 199 89% 68%;
  --destructive: 0 62% 50%;
  --success: 142 71% 40%;
  --warning: 38 92% 45%;
  --border: 222 47% 18%;
  --input: 222 47% 18%;
  --ring: 199 89% 48%;
  --sidebar: 222 47% 6%;
  --sidebar-accent: 222 47% 14%;
  --surface-2: 222 47% 11%;
  /* the sheet stays paper-light on purpose: the app's SVG exports are light-only,
     so a light sheet reads as an intentional export, not a force-whitened box */
  --sheet: 210 20% 97%;
  --sheet-ink: 222 47% 11%;
  --sheet-border: 214 20% 82%;
  --shadow-lift: 0 1px 2px rgb(0 0 0 / 0.3), 0 10px 30px -12px rgb(0 0 0 / 0.6);
}

/* ── base ── */
*, *::before, *::after { box-sizing: border-box; border-color: hsl(var(--border)); }
html, body { margin: 0; padding: 0; height: 100%; }
body {
  font-family: var(--font-sans);
  background: hsl(var(--background));
  color: hsl(var(--foreground));
  font-size: var(--text-base);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}
h1, h2, h3, h4, p { margin: 0; }
/* De displayface, op precies twee plekken: h1 (elke paginatitel) en h2 (sectiekoppen op
   de landing en de juridische pagina's). h3 en lager blijven Inter, want dat zijn
   kaartkoppen en dialoogtitels: functioneel, geen stem. */
h1, h2 {
  font-family: var(--font-display);
  font-weight: 600;
  letter-spacing: -0.01em;
  font-optical-sizing: auto;
}
/* Uitzondering: de vraag in de leerlingvragenlijst is geen kop maar een formulierlabel
   waar een kind op moet antwoorden. Die blijft in de schermfont staan. */
#q-text { font-family: var(--font-sans); }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; }
input, select, textarea { font: inherit; color: inherit; }
[hidden] { display: none !important; }

.icon { width: 1.1em; height: 1.1em; flex-shrink: 0; vertical-align: -0.18em; }

/* ── utilities ── */
.muted { color: hsl(var(--muted-foreground)); }
.small { font-size: var(--text-sm); }
.xs { font-size: var(--text-xs); }
.bold { font-weight: 700; }
.semibold { font-weight: 600; }
/* Zichtbaar voor een schermlezer, niet voor het oog. Voor koppen die de
   koppenstructuur van een pagina compleet maken zonder dat er tekst bij komt. */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.center { text-align: center; }
.mono { font-family: ui-monospace, 'Cascadia Mono', Consolas, monospace; }
.flex { display: flex; align-items: center; gap: 8px; }
.flex-wrap { flex-wrap: wrap; }
.between { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.grow { flex: 1; min-width: 0; }
.stack { display: flex; flex-direction: column; gap: 16px; }
.stack-sm { display: flex; flex-direction: column; gap: 8px; }
.grid-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 16px; }
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.mt-1 { margin-top: 4px; } .mt-2 { margin-top: 8px; } .mt-4 { margin-top: 16px; } .mt-6 { margin-top: 24px; }
.mb-2 { margin-bottom: 8px; } .mb-4 { margin-bottom: 16px; } .mb-6 { margin-bottom: 24px; }
.text-success { color: hsl(var(--success)); }
.text-destructive { color: hsl(var(--destructive)); }
.text-warning { color: hsl(38 92% 40%); }
.dark .text-warning { color: hsl(38 92% 60%); }
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
@media (max-width: 768px) { .grid-2 { grid-template-columns: 1fr; } }

@keyframes fadeIn { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
.fade-in { animation: fadeIn 0.4s ease-out; }
@keyframes spin { to { transform: rotate(360deg); } }
.spinner {
  width: 28px; height: 28px; border-radius: 50%;
  border: 3px solid hsl(var(--muted));
  border-top-color: hsl(var(--primary));
  animation: spin 0.8s linear infinite;
  margin: 0 auto;
}
.spinner.sm { width: 16px; height: 16px; border-width: 2px; margin: 0; }

/* ── app shell ── */
/* De dvh-regel staat er niet voor de sier: op iOS is 100vh de *grote* viewport, de
   hoogte zonder browserbalken. Omdat .shell niet scrollt (overflow: hidden) viel de
   onderste ~90px daardoor onder de Safari-balk, en dat is precies waar .side-foot
   met Instellingen en Uitloggen zit. Onbereikbaar, zonder scrollbalk om erbij te
   komen. dvh rekent die balk wel mee; de vh-regel blijft staan als fallback. */
.shell { display: flex; height: 100vh; height: 100dvh; overflow: hidden; }
.sidebar {
  width: 256px; flex-shrink: 0; display: flex; flex-direction: column;
  background: hsl(var(--sidebar)); color: hsl(var(--sidebar-foreground));
  transition: width 0.25s ease, transform 0.25s ease;
}
.shell.collapsed .sidebar { width: 76px; }
.shell.collapsed .sidebar .side-label { display: none; }
.shell.collapsed .side-item { justify-content: center; padding-left: 0; padding-right: 0; }
.side-brand {
  display: flex; align-items: center; gap: 10px; padding: var(--space-4);
  font-family: var(--font-display); font-size: var(--text-xl); font-weight: 700;
}
.brand-mark {
  width: 32px; height: 32px; border-radius: var(--radius-sm); flex-shrink: 0;
  background: hsl(var(--primary)); color: hsl(var(--primary-foreground));
  display: flex; align-items: center; justify-content: center;
}
.brand-mark .icon { width: 18px; height: 18px; }
.side-sep { border: 0; border-top: 1px solid hsl(var(--sidebar-accent)); margin: 0; }
/* De maten hierboven en hier zijn krap gehouden met een doel: op een gewone laptop
   (rond 700px werkhoogte) moet een leerkracht de hele navigatie in één keer zien,
   zonder te scrollen. Met de oude padding scheelde dat 2 pixels.

   min-height:0 is wat het scrollen werkend maakt: een flex-item krimpt niet onder
   zijn eigen inhoud, dus zonder die regel duwde een te lange nav de voet met
   Instellingen en Uitloggen uit beeld, en was er nergens een scrollbalk om er
   alsnog bij te komen. Nu scrollt de nav zelf en blijft de voet staan. */
.side-nav {
  flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain;
  padding: 10px; display: flex; flex-direction: column; gap: 4px;
  scrollbar-width: thin; scrollbar-color: hsl(var(--sidebar-accent)) transparent;
}
.side-nav::-webkit-scrollbar { width: 8px; }
.side-nav::-webkit-scrollbar-thumb { background: hsl(var(--sidebar-accent)); border-radius: var(--radius-pill); }
.side-nav::-webkit-scrollbar-track { background: transparent; }
.side-item {
  display: flex; align-items: center; gap: 12px; padding: 8px 12px;
  border-radius: var(--radius-sm); font-size: var(--text-md); font-weight: 500;
  color: hsl(var(--sidebar-foreground) / 0.7); transition: all 0.15s;
}
.side-item:hover, .side-item.active { background: hsl(var(--sidebar-accent)); color: hsl(var(--sidebar-accent-foreground)); }
.side-item .icon { width: 19px; height: 19px; }
/* Section label; its top border doubles as a group divider that persists when
   the sidebar is collapsed (the label text hides, the rule stays). */
.side-heading {
  padding: 10px 12px 4px; margin-top: 6px; border-top: 1px solid hsl(var(--sidebar-accent));
  font-size: 10.5px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
  color: hsl(var(--sidebar-foreground) / 0.45);
}
.shell.collapsed .side-heading { padding: 8px 0 0; }
.side-foot { padding: 12px 10px; display: flex; flex-direction: column; gap: 6px; }
.avatar {
  width: 32px; height: 32px; border-radius: 50%; flex-shrink: 0;
  background: hsl(var(--primary) / 0.25); color: hsl(var(--sidebar-foreground));
  display: flex; align-items: center; justify-content: center;
  font-size: 11px; font-weight: 700;
}

.main-wrap { flex: 1; display: flex; flex-direction: column; overflow: hidden; }
.topbar {
  height: 56px; flex-shrink: 0; display: flex; align-items: center; gap: 12px;
  border-bottom: 1px solid hsl(var(--border)); padding: 0 20px;
  background: hsl(var(--background));
}
.topbar h1 { font-size: var(--text-xl); }
.content { flex: 1; overflow-y: auto; }
.page { padding: 24px; max-width: 1200px; margin: 0 auto; display: flex; flex-direction: column; gap: 24px; }

#mobile-overlay { position: fixed; inset: 0; background: rgb(0 0 0 / 0.5); z-index: 40; }
@media (max-width: 768px) {
  .sidebar { position: fixed; z-index: 50; height: 100vh; height: 100dvh; transform: translateX(-100%); }
  .shell.mobile-open .sidebar { transform: none; width: 256px; }
  #btn-collapse { display: none; }
  .page { padding: 16px; }
}
@media (min-width: 769px) { #btn-menu { display: none; } }

/* ── buttons ── */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  padding: 8px 15px; border-radius: var(--radius-sm); border: 1px solid transparent;
  font-size: var(--text-md); font-weight: 500; cursor: pointer; white-space: nowrap;
  background: hsl(var(--primary)); color: hsl(var(--primary-foreground));
  transition: opacity 0.15s, background 0.15s;
}
.btn:hover { opacity: 0.9; }
.btn:disabled { opacity: 0.5; cursor: not-allowed; }
.btn-outline { background: transparent; color: hsl(var(--foreground)); border-color: hsl(var(--input)); }
.btn-outline:hover { background: hsl(var(--muted)); opacity: 1; }
.btn-ghost { background: transparent; color: hsl(var(--foreground)); }
.btn-ghost:hover { background: hsl(var(--muted)); opacity: 1; }
.btn-destructive { background: hsl(var(--destructive)); color: hsl(var(--destructive-foreground)); }
.btn-sm { padding: 5px 12px; font-size: var(--text-sm); }
.btn-lg { padding: 12px 20px; font-size: var(--text-base); }
.btn-icon { padding: 7px; }
/* Een rij die als geheel klikbaar is, zonder eruit te zien als een knop. Bewust een
   echte <button>: openen moet ook met het toetsenbord kunnen en een schermlezer
   moet hem als bedienbaar aankondigen. Gebruikt door de sessielijst op
   observations.html. */
.btn-plain {
  display: block; width: 100%; padding: 0; border: 0; border-radius: var(--radius-xs);
  background: none; color: inherit; font: inherit; text-align: left; cursor: pointer;
}
.btn-plain:hover { opacity: 0.75; }
.btn-icon .icon { width: 16px; height: 16px; }
.btn-block { width: 100%; }
.btn-side {
  background: transparent; color: hsl(var(--sidebar-foreground)); border: none; width: 100%;
  justify-content: flex-start;
}
.btn-side:hover { background: hsl(var(--sidebar-accent)); opacity: 1; }

/* ── forms ── */
.label { display: block; font-size: var(--text-md); font-weight: 500; margin-bottom: 6px; }
.input, .select, .textarea {
  width: 100%; padding: 8px 12px; border-radius: var(--radius-sm);
  border: 1px solid hsl(var(--input)); background: hsl(var(--card));
  color: hsl(var(--foreground)); outline: none; transition: border-color 0.15s, box-shadow 0.15s;
}
.input:focus, .select:focus, .textarea:focus {
  border-color: hsl(var(--ring)); box-shadow: 0 0 0 3px hsl(var(--ring) / 0.15);
}

/* Hiervoor waren de twee regels hierboven en die op .code-box de ENIGE focusregels in
   dit bestand. Knoppen, tabs, zijbalkitems en de naamkiezer vielen terug op de
   standaardomlijning van de browser, en die is op een gevulde .btn nauwelijks te zien:
   met het toetsenbord door de app lopen betekende gokken waar je was.
   :focus-visible en niet :focus, zodat een muisklik geen ring achterlaat. Dezelfde
   --ring als de invoervelden hierboven, zodat het één stijl blijft. */
.btn:focus-visible, .tab:focus-visible, .tab-line:focus-visible,
.side-item:focus-visible, .pick-item:focus-visible, .pick-crown:focus-visible,
.chip:focus-visible, .help-btn:focus-visible,
.socio-controls button:focus-visible, dialog:focus-visible {
  outline: 2px solid hsl(var(--ring));
  outline-offset: 2px;
}
/* De zijbalk heeft zijn eigen donkere achtergrond; daar valt --ring weg. */
.side-item:focus-visible {
  outline-color: hsl(var(--sidebar-foreground) / 0.7);
}
.textarea { min-height: 80px; resize: vertical; }
.select { appearance: none; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23888' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 10px center; padding-right: 34px; }
.field { margin-bottom: 14px; }
.field-error { color: hsl(var(--destructive)); font-size: 12px; margin-top: 4px; }
.checkbox { width: 16px; height: 16px; accent-color: hsl(var(--primary)); cursor: pointer; }
.input-search { padding-left: 34px; }
.search-wrap { position: relative; }
.search-wrap > .icon {
  position: absolute; left: 10px; top: 50%; transform: translateY(-50%);
  width: 15px; height: 15px; color: hsl(var(--muted-foreground)); pointer-events: none;
}

/* Het oogje in een wachtwoordveld. Tegenhanger van .search-wrap hierboven, maar
   rechts en wel klikbaar: dit is het eerste bedienbare element binnen een invoerveld
   in deze stylesheet. Vandaar de eigen focusregel: de gedeelde .btn:focus-visible-groep
   pakt deze knop niet (hij is geen .btn) en haar outline-offset van 2px zou de ring
   buiten de rand van het veld tekenen. */
.pw-wrap { position: relative; }
.input-pw { padding-right: 38px; }
.pw-toggle {
  position: absolute; right: 1px; top: 1px; bottom: 1px; width: 36px;
  display: flex; align-items: center; justify-content: center;
  border: 0; background: none; padding: 0; cursor: pointer;
  color: hsl(var(--muted-foreground)); border-radius: var(--radius-sm);
}
.pw-toggle:hover { color: hsl(var(--foreground)); }
.pw-toggle .icon { width: 16px; height: 16px; }
.pw-toggle:focus-visible { outline: 2px solid hsl(var(--ring)); outline-offset: -2px; }

/* ── (i)-knopje met uitleg (js/help-toggle.js) ── */
/* De uitleg onder een label of kop staat standaard dicht; het rondje ernaast klapt hem
   open. Alleen uitleg gaat hierachter: een statusregel ("24 leerlingen → 6 groepen",
   "vereist een observatiesessie") of een waarschuwing blijft gewoon zichtbaar. */
.help-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px; margin-left: 4px; vertical-align: middle;
  border: 0; background: none; padding: 0; border-radius: 999px; cursor: pointer;
  color: hsl(var(--muted-foreground));
}
.help-btn:hover, .help-btn[aria-expanded="true"] { color: hsl(var(--foreground)); }
.help-btn .icon { width: 15px; height: 15px; }
.help-btn-text { width: auto; height: auto; margin: 0; gap: 6px; font-size: var(--text-xs); }
.help-text { display: block; }

/* ── card ── */
/* Geen schaduw: een kaart ligt op de pagina, hij zweeft er niet boven. Schaduw is in
   deze stylesheet voorbehouden aan dingen die echt los staan (dialoog, toast, tooltip,
   het hero-vel). Scheiding doet de hairline. */
.card {
  background: hsl(var(--card)); color: hsl(var(--card-foreground));
  border: 1px solid hsl(var(--border)); border-radius: var(--radius);
}
.card-header { padding: 20px 20px 8px; }
.card-title { font-size: var(--text-lg); font-weight: 600; }
.card-desc { font-size: var(--text-sm); color: hsl(var(--muted-foreground)); margin-top: 4px; }
.card-content { padding: 16px 20px 20px; }
.card-dashed { border-style: dashed; }
.card:has(> .card-header:last-child) .card-header { padding-bottom: 20px; }
.metric-card { padding: var(--space-5); transition: border-color 0.2s, background 0.2s; }
.metric-card:hover { border-color: hsl(var(--primary) / 0.45); background: hsl(var(--primary) / 0.02); }
.metric-icon { width: 36px; height: 36px; border-radius: var(--radius-sm); display: flex; align-items: center; justify-content: center; }
.metric-icon .icon { width: 18px; height: 18px; }
.tint-primary { background: hsl(var(--primary) / 0.1); color: hsl(var(--primary)); }
.tint-green { background: hsl(142 71% 45% / 0.12); color: hsl(142 71% 35%); }
.tint-blue { background: hsl(217 91% 60% / 0.12); color: hsl(217 91% 50%); }
.tint-amber { background: hsl(38 92% 50% / 0.14); color: hsl(32 95% 40%); }

/* ── grafieken en de trechter (beheerpagina) ──
   De SVG rekt in de breedte mee met zijn kaart en houdt zijn hoogte uit de
   style-regel die chart.js meegeeft; daarom hier alleen de breedte en de
   uitlijning. De trechter is bewust geen SVG: zes balken met een label ernaast
   is een lijstje, en een lijstje hoort geen tekenprogramma nodig te hebben. */
.chart { display: block; width: 100%; overflow: visible; }

/* De as staat bewust BUITEN de SVG. Die rekt met preserveAspectRatio="none", en
   dat rekt letters mee: een <text> hierin wordt breder naarmate de kaart breder
   is. Alleen vormen die mogen vervormen zitten in het tekenvlak.
   minmax(0, 1fr) op de tweede kolom, anders duwt de SVG het raster breder dan de
   kaart. De labels zijn absoluut gepositioneerd op dezelfde breuk als hun
   rasterlijn, want met space-between staan ze er net naast. */
.chart-wrap {
  display: grid; margin: 0;
  grid-template-columns: auto minmax(0, 1fr);
  column-gap: 8px;
}
.chart-cap {
  grid-column: 1 / -1;
  font-size: 11px; color: hsl(var(--muted-foreground));
  margin-bottom: 6px;
}
.chart-y {
  grid-column: 1; position: relative;
  min-width: 14px; font-size: 10px; line-height: 1;
  color: hsl(var(--muted-foreground));
}
.chart-y > span {
  position: absolute; right: 0; transform: translateY(-50%);
  font-variant-numeric: tabular-nums;
}
.chart-plot { grid-column: 2; min-width: 0; }
.chart-x {
  grid-column: 2; position: relative; height: 14px; margin-top: 4px;
  font-size: 10px; line-height: 1; color: hsl(var(--muted-foreground));
}
.chart-x > span { position: absolute; top: 0; white-space: nowrap; }

.funnel-row + .funnel-row { margin-top: 12px; }
.funnel-bar {
  margin-top: 5px; height: 8px; border-radius: var(--radius-pill);
  background: hsl(var(--muted)); overflow: hidden;
}
.funnel-bar > span {
  display: block; height: 100%; border-radius: var(--radius-pill);
  background: hsl(var(--primary));
}

/* ── badges ── */
.badge {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 2px 10px; border-radius: var(--radius-pill);
  font-size: var(--text-xs); font-weight: 600; white-space: nowrap;
  background: hsl(var(--primary)); color: hsl(var(--primary-foreground));
}
.badge-secondary { background: hsl(var(--secondary)); color: hsl(var(--secondary-foreground)); }
.badge-outline { background: transparent; color: hsl(var(--foreground)); border: 1px solid hsl(var(--border)); }
.badge-destructive { background: hsl(var(--destructive)); color: hsl(var(--destructive-foreground)); }
.badge-green { background: hsl(142 71% 45% / 0.15); color: hsl(142 71% 28%); }
.badge-blue { background: hsl(217 91% 60% / 0.15); color: hsl(217 91% 40%); }
.badge-gray { background: hsl(var(--muted)); color: hsl(var(--muted-foreground)); }
.badge-amber { background: hsl(38 92% 50% / 0.15); color: hsl(32 95% 35%); }
.dark .badge-green { color: hsl(142 71% 65%); }
.dark .badge-blue { color: hsl(217 91% 70%); }
.dark .badge-amber { color: hsl(38 92% 65%); }

/* ── tabs ── */
.tabs { display: inline-flex; gap: 2px; background: hsl(var(--muted)); border-radius: var(--radius-sm); padding: 3px; }
.tab {
  padding: 6px 14px; border: none; border-radius: var(--radius-xs); cursor: pointer;
  background: transparent; color: hsl(var(--muted-foreground));
  font-size: var(--text-md); font-weight: 500;
}
.tab.active { background: hsl(var(--card)); color: hsl(var(--foreground)); box-shadow: 0 1px 2px rgb(0 0 0 / 0.08); }
/* Horizontaal scrollen, niet afbreken: de onderrand is één doorlopende lijn waar de
   actieve tab op staat, en die valt uiteen zodra de tabs over twee regels gaan. Op een
   telefoon van 390px passen de vijf tabs van sociograms.html er niet naast elkaar. */
.tabs-line {
  display: flex; gap: 2px; border-bottom: 1px solid hsl(var(--border));
  overflow-x: auto; scrollbar-width: none;
}
.tabs-line::-webkit-scrollbar { display: none; }
.tab-line {
  padding: 9px 16px; border: none; border-bottom: 2px solid transparent; cursor: pointer;
  background: none; color: hsl(var(--muted-foreground)); font-size: var(--text-md); font-weight: 500;
  margin-bottom: -1px; flex: 0 0 auto;
}
.tab-line.active { color: hsl(var(--primary)); border-bottom-color: hsl(var(--primary)); }

/* ── tables ── */
.table-wrap { overflow-x: auto; border: 1px solid hsl(var(--border)); border-radius: var(--radius); }
table.tbl { width: 100%; border-collapse: collapse; font-size: 13px; }
.tbl th {
  text-align: left; padding: 10px 14px; font-weight: 600; font-size: 12px;
  color: hsl(var(--muted-foreground)); border-bottom: 1px solid hsl(var(--border));
  background: hsl(var(--muted) / 0.5);
}
.tbl td { padding: 9px 14px; border-bottom: 1px solid hsl(var(--border)); }
.tbl tr:last-child td { border-bottom: none; }
.tbl tbody tr:hover { background: hsl(var(--muted) / 0.3); }
/* De uitgelichte leerling op sociograms.html. Een streep links en niet alleen een
   kleurvlak, zodat de markering ook zichtbaar is voor wie kleur slecht onderscheidt. */
.tbl tbody tr.row-focus { background: hsl(var(--primary) / 0.1); }
.tbl tbody tr.row-focus td:first-child { box-shadow: inset 3px 0 0 hsl(var(--primary)); }

/* matrix tables (reports) */
.matrix-wrap { overflow-x: auto; }
table.matrix { border-collapse: collapse; font-size: 11.5px; }
.matrix th, .matrix td { border: 1px solid hsl(var(--border)); padding: 5px 9px; text-align: center; white-space: nowrap; }
.matrix th { background: hsl(var(--muted)); font-weight: 600; font-size: 11px; }
/* Deze twee minimumbreedtes stonden inline op de th. Ze staan hier omdat het printblok
   ze moet kunnen uitzetten: op papier is de inhoud smaller dan het minimum, en dan is
   het minimum wat de tabel over de bladrand duwt. */
.matrix .rowhead { position: sticky; left: 0; z-index: 5; background: hsl(var(--card)); text-align: left; font-weight: 500; min-width: 130px; }
/* Een kolomkop is een naam, dus hij bepaalt zijn eigen breedte. De 34px die hier
   stond hoorde bij een genummerde kolom en zou een korte voornaam nu juist te breed
   maken. Op het scherm vangt .matrix-wrap de rest op met overflow-x. */
.matrix th.namehead { min-width: 0; }
.matrix th.rowhead { background: hsl(var(--muted)); z-index: 6; }
.matrix .diag { background: hsl(var(--muted) / 0.6); color: hsl(var(--muted-foreground) / 0.5); }
/* De gesneden matrices bestaan alleen op papier (zie het printblok onderaan). */
#surveys-matrix-print, #obs-matrix-print { display: none; }
.matrix .cell-pos, .pairs-table .cell-pos { color: hsl(142 71% 30%); font-weight: 700; }
.matrix .cell-neg, .pairs-table .cell-neg { color: hsl(0 72% 45%); font-weight: 700; }
/* Totalen dragen geen tint maar een streep. Een groen of rood vlak zonder schaal
   eronder suggereert een betekenis die er niet is; de gekleurde getallen zeggen het
   zelf. (De .tot-pos/.tot-neg-tinten en .cell-both zijn in oktober 2026 opgeruimd:
   niets zette ze nog, ook de voorbeeldmatrix op index.html niet.) */
.matrix tr.totals td { border-top: 2px solid hsl(var(--muted-foreground) / 0.55); }
/* De kolom Sessies van de co-occurrentiematrix: een totaal, dus losgemaakt van de
   cellen met een streep in plaats van met een kleur. */
.matrix .sessions { border-left: 2px solid hsl(var(--muted-foreground) / 0.55); }
.dark .matrix .cell-pos, .dark .pairs-table .cell-pos { color: hsl(142 71% 60%); }
.dark .matrix .cell-neg, .dark .pairs-table .cell-neg { color: hsl(0 84% 65%); }

/* "Wie kiest wie?" op reports.html. Leunt op .tbl voor de opmaak en op de twee
   kleuren hierboven, zodat + en − er hetzelfde uitzien als in de matrix erboven.
   Eigen zijn alleen het uitlijnen van de twee tekenkolommen en de botsingsrij:
   amber met een streep links, want alleen kleur is geen markering (zie .row-focus). */
.pairs-table th + th, .pairs-table td + td { text-align: center; width: 25%; }
.pairs-table tbody tr.clash { background: rgb(245 158 11 / 0.12); }
.pairs-table tbody tr.clash td:first-child { box-shadow: inset 3px 0 0 #F59E0B; }

/* "Een vast maatje, of steeds iemand anders?" onder de co-occurrentiematrix. Alleen
   het aantal wordt gecentreerd; de naam van het vaakste maatje blijft links, want
   een kolom met namen is een kolom tekst en die leest links uitgelijnd. */
/* "Groepjes per leerling": klasgenoot links, de twee getallen gecentreerd, net als de
   twee tekenkolommen van .pairs-table hierboven. */
.copairs-table th + th, .copairs-table td + td { text-align: center; width: 22%; }

/* "Zonder maatje": het balkje maakt 3 van 8 in één oogopslag anders dan 1 van 8.
   Amber en niet rood: dit is een teller die aandacht vraagt, geen diagnose. */
.alone-bar { display: inline-block; width: 120px; height: 6px; border-radius: 3px;
  background: hsl(var(--muted)); overflow: hidden; flex-shrink: 0; }
.alone-bar > span { display: block; height: 100%; background: hsl(38 92% 50%); }

/* De twee knoppen van de dyadelog. Dezelfde twee kleuren als een positieve en een
   negatieve nominatie, zodat de leerkracht niet twee kleurtalen hoeft te leren. */
.pair-good  { border-color: hsl(142 71% 40%); color: hsl(142 71% 30%); }
.pair-clash { border-color: hsl(0 72% 55%);  color: hsl(0 72% 45%); }
.dark .pair-good  { color: hsl(142 71% 60%); }
.dark .pair-clash { color: hsl(0 84% 65%); }

/* ── dialogs ── */
dialog {
  border: 1px solid hsl(var(--border)); border-radius: var(--radius); padding: 0;
  background: hsl(var(--card)); color: hsl(var(--card-foreground));
  width: min(480px, calc(100vw - 32px)); max-height: 90vh; max-height: 90dvh;
  box-shadow: 0 20px 50px rgb(0 0 0 / 0.25);
}
dialog.dlg-wide { width: min(860px, calc(100vw - 32px)); }
dialog::backdrop { background: rgb(0 0 0 / 0.55); }
.dlg-body { padding: 22px; overflow-y: auto; max-height: calc(90vh - 4px); max-height: calc(90dvh - 4px); }
.dlg-title { font-size: var(--text-xl); font-weight: 600; }
.dlg-desc { font-size: var(--text-sm); color: hsl(var(--muted-foreground)); margin: 4px 0 var(--space-4); }
.dlg-foot { display: flex; justify-content: flex-end; gap: 10px; margin-top: 20px; }

/* Het koppelscherm van de leerlingimport (groups.html, student-import.js). Boven elke
   kolom van het bestand een keuzemenu; de tabel scrolt opzij, de dialoog niet. */
/* Stap 1, het keuzevenster: vier tegels, zelfde vorm als .choice-card op observeren. */
#dlg-import-pick { width: min(600px, calc(100vw - 32px)); }
.imp-tiles { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
@media (max-width: 520px) { .imp-tiles { grid-template-columns: 1fr; } }
.imp-tile {
  display: flex; gap: 12px; align-items: flex-start; text-align: left; padding: 14px;
  border: 1px solid hsl(var(--border)); border-radius: var(--radius); background: hsl(var(--card));
  color: inherit; font: inherit; cursor: pointer; transition: border-color 0.15s, background 0.15s;
}
.imp-tile:hover { border-color: hsl(var(--primary) / 0.55); background: hsl(var(--primary) / 0.04); }
.imp-tile:focus-visible { outline: 2px solid hsl(var(--ring)); outline-offset: 2px; }
.imp-tile-icon .icon { width: 22px; height: 22px; color: hsl(var(--primary)); margin-top: 1px; }
.imp-tile-title { display: block; font-weight: 600; }
.imp-tile-ext { display: block; font-size: 12px; font-family: ui-monospace, 'Cascadia Mono', Consolas, monospace; color: hsl(var(--primary)); margin-top: 1px; }
.imp-tile-desc { display: block; font-size: var(--text-sm); color: hsl(var(--muted-foreground)); margin-top: 4px; line-height: 1.4; }
/* Op een smal scherm past de downloadlink niet naast Annuleren: dan krijgt hij een eigen regel. */
#dlg-import-pick .dlg-foot { flex-wrap: wrap; }
#btn-imp-template { min-width: max-content; }
/* Een bestand boven het venster: gestippelde rand, zodat je ziet dat loslaten werkt. */
.imp-drop { outline: 2px dashed transparent; outline-offset: -8px; border-radius: var(--radius); transition: outline-color 0.15s; }
.imp-drop.is-over { outline-color: hsl(var(--primary)); background: hsl(var(--primary) / 0.04); }

.imp-bar { display: flex; flex-wrap: wrap; gap: 12px; margin-bottom: 12px; }
.imp-bar .label { font-size: var(--text-sm); margin-bottom: 4px; }
.imp-bar .select { width: auto; min-width: 160px; }
.imp-map table.tbl { width: auto; min-width: 100%; }
.imp-map th { vertical-align: bottom; min-width: 150px; }
.imp-map .select { padding: 5px 30px 5px 8px; font-size: 13px; font-weight: 500; }
.imp-map .select:not([data-field="skip"]) { border-color: hsl(var(--primary)); }
.imp-map td { white-space: nowrap; max-width: 220px; overflow: hidden; text-overflow: ellipsis; }
.imp-result .input { padding: 4px 8px; font-size: 13px; min-width: 110px; }
.imp-result td { padding-top: 5px; padding-bottom: 5px; }
/* Een rij die niet meegaat wordt lichter, maar de naamvelden blijven leesbaar: juist
   daar herstel je een "Bestaat al" die eigenlijk een andere leerling is. */
.imp-result tr.imp-off td:nth-child(4), .imp-result tr.imp-off td:nth-child(5) { opacity: 0.5; }
.imp-result tr.imp-off .input { color: hsl(var(--muted-foreground)); }

/* Klassen op groups.html: een kaart is een link naar het klasscherm (?class=<id>). Geen
   knoppen op de kaart; de hele kaart is het klikvlak, met dezelfde hover als .metric-card. */
.class-card { display: block; color: inherit; text-decoration: none; transition: border-color 0.2s, background 0.2s; }
.class-card:hover { border-color: hsl(var(--primary) / 0.45); background: hsl(var(--primary) / 0.02); }
.class-card:focus-visible { outline: 2px solid hsl(var(--ring)); outline-offset: 2px; }
.class-card-go { color: hsl(var(--muted-foreground)); display: flex; padding-top: 2px; }
.class-card:hover .class-card-go { color: hsl(var(--primary)); }
.class-back { margin-left: -12px; }
.class-head { display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: space-between; gap: 12px 16px; }
.class-title { font-size: 26px; font-weight: 700; overflow-wrap: anywhere; }
.class-title:focus { outline: none; }
/* Het klasscherm: een klik op de rij opent de leerling. De naam is een knop zonder
   knoplook, zodat de rij ook met het toetsenbord te openen is. */
.student-tbl tbody tr[data-st] { cursor: pointer; }
.student-tbl tbody tr.is-chosen { background: hsl(var(--primary) / 0.07); }
.student-tbl td.pick-cell { width: 44px; }
/* De verborgen kop "Acties" is position:absolute; zonder positioned voorouder binnen de
   scrollende .table-wrap ontsnapt hij aan die scroll en maakt hij de hele pagina op een
   telefoon breder dan het scherm. */
.student-tbl th { position: relative; }
.row-open { all: unset; cursor: pointer; font-weight: 600; white-space: nowrap; }
.row-open:focus-visible { outline: 2px solid hsl(var(--ring)); outline-offset: 2px; border-radius: 2px; }
.student-tbl tbody tr[data-st]:hover .row-open { color: hsl(var(--primary)); }
/* De selectiebalk staat er alleen zolang er een vinkje staat. */
.sel-bar {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px 10px;
  padding: 8px 12px; border: 1px solid hsl(var(--primary) / 0.35); border-radius: var(--radius);
  background: hsl(var(--primary) / 0.05);
}
.sel-bar[hidden] { display: none; }
.sel-bar .select { width: auto; min-width: 180px; padding-top: 5px; padding-bottom: 5px; font-size: var(--text-sm); }
.sel-bar .btn { min-width: max-content; }
#sel-count { margin-right: auto; }
@media (max-width: 520px) {
  #sel-count { flex-basis: 100%; }
  .sel-bar .select { flex: 1 1 100%; }
}

/* Een mededeling van de platformbeheerder (announcements.js, migratie 055), en
   hetzelfde blok als voorbeeld op admin.html. */
.ann-tag { font-size: 12px; font-weight: 600; color: hsl(var(--primary)); margin-bottom: 4px; }
.ann-body { font-size: var(--text-sm); line-height: 1.55; margin-top: 10px; overflow-wrap: anywhere; }
.ann-body p, .ann-body ul { margin: 0 0 10px; }
.ann-body ul { padding-left: 20px; }
.ann-body .msg-head { font-size: var(--text-base); font-weight: 600; margin: 14px 0 6px; }
.ann-body a { color: hsl(var(--primary)); text-decoration: underline; }
.msg-preview-app {
  max-width: 480px; padding: 22px; border: 1px solid hsl(var(--border));
  border-radius: var(--radius); background: hsl(var(--card)); box-shadow: 0 6px 20px rgb(0 0 0 / 0.08);
}
.msg-preview-mail {
  width: 100%; max-width: 640px; height: 520px; border: 1px solid hsl(var(--border));
  border-radius: var(--radius); background: #F9FAFB;
}

/* Het toevoegmenu in de vragenlijstbouwer. Elke keuze toont zijn eigen vraagzinnen,
   want de melding was juist dat "Kort" ongezien het werkpaar neerzette. */
.add-menu-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 8px; }
.add-option {
  display: flex; flex-direction: column; gap: 4px; text-align: left;
  padding: 12px 14px; border: 1px solid hsl(var(--border)); border-radius: var(--radius-sm);
  background: hsl(var(--card)); color: inherit; font: inherit; cursor: pointer;
}
.add-option:hover { border-color: hsl(var(--primary) / 0.5); background: hsl(var(--primary) / 0.04); }

/* De vragenlijstbouwer in drie stappen (surveys.js, buildEditor). De voet plakt onderin
   het venster, zodat Volgende en Opslaan altijd in beeld zijn, ook bij tien vragen. */
.sv-ed { padding-bottom: 0; }
.steps { display: flex; gap: 6px; list-style: none; margin: 12px 0 20px; padding: 0; counter-reset: none; }
.steps li { flex: 1; display: flex; }
.step {
  flex: 1; display: flex; align-items: center; gap: 8px; padding: 8px 10px;
  border: 0; border-bottom: 3px solid hsl(var(--border)); background: none; color: hsl(var(--muted-foreground));
  font: inherit; font-size: var(--text-sm); font-weight: 500; cursor: pointer; text-align: left;
}
.step:hover { color: hsl(var(--foreground)); }
.step-n {
  width: 22px; height: 22px; border-radius: 999px; flex-shrink: 0;
  display: inline-flex; align-items: center; justify-content: center;
  background: hsl(var(--muted)); font-size: 12px; font-weight: 700;
}
.step.done { border-bottom-color: hsl(var(--primary) / 0.45); }
.step.done .step-n { background: hsl(var(--primary) / 0.15); color: hsl(var(--primary)); }
.step[aria-current="step"] { border-bottom-color: hsl(var(--primary)); color: hsl(var(--foreground)); font-weight: 600; }
.step[aria-current="step"] .step-n { background: hsl(var(--primary)); color: hsl(var(--primary-foreground)); }
/* Op een telefoon past alleen de naam van de huidige stap; de andere houden hun nummer. */
@media (max-width: 520px) {
  .step { justify-content: center; padding: 8px 4px; }
  .step:not([aria-current="step"]) .step-name { display: none; }
}
.sv-foot {
  position: sticky; bottom: 0; z-index: 1; display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
  margin: 20px -22px 0; padding: 14px 22px 22px;
  background: hsl(var(--card)); border-top: 1px solid hsl(var(--border));
}
.sv-words { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; }
.sv-words .help-text { flex-basis: 100%; }
.sv-words .tab:disabled { cursor: not-allowed; opacity: 0.6; }
/* "Zonder standaardvragen beginnen": bewust een link en geen tegel naast de aanbevolen sets. */
.link-btn {
  display: inline-flex; align-items: center; gap: 4px; padding: 0; border: 0; background: none;
  color: hsl(var(--primary)); font: inherit; font-weight: 500; cursor: pointer;
}
.link-btn:hover { text-decoration: underline; }
.link-btn .icon { width: 14px; height: 14px; }
.more-opts { border: 1px solid hsl(var(--border)); border-radius: var(--radius-sm); }
.more-opts > summary { padding: 10px 14px; cursor: pointer; }
.more-opts[open] > summary { border-bottom: 1px solid hsl(var(--border)); }
.more-opts .more-body { padding: 14px 14px 0; }
.q-rows { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.q-row { border: 1px solid hsl(var(--border)); border-radius: var(--radius-sm); background: hsl(var(--card)); }
.q-row.is-mand { border-color: hsl(217 91% 60% / 0.4); }
.q-row.open { background: hsl(var(--muted) / 0.45); }
.q-row.is-mand.open { background: hsl(217 91% 60% / 0.04); }
.q-head {
  width: 100%; display: grid; grid-template-columns: auto minmax(0, 1fr) auto auto; align-items: center; gap: 10px;
  padding: 10px 12px; border: 0; background: none; color: inherit; font: inherit; text-align: left; cursor: pointer;
  border-radius: var(--radius-sm);
}
.q-head:hover { background: hsl(var(--muted) / 0.6); }
.q-num {
  width: 22px; height: 22px; border-radius: 999px; display: inline-flex; align-items: center; justify-content: center;
  background: hsl(var(--muted)); font-size: 12px; font-weight: 700;
}
.q-text { font-size: var(--text-md); font-weight: 500; }
.q-meta { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; justify-content: flex-end; }
.q-chev { display: inline-flex; color: hsl(var(--muted-foreground)); }
.q-body { padding: 4px 14px 14px 44px; }
.q-grid { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 0 20px; }
.q-grid .select { min-width: 170px; }
.q-grid .tab { display: inline-flex; align-items: center; gap: 6px; }
#editor-title:focus { outline: none; }
@media (max-width: 520px) { .sv-foot > .btn { flex: 1 1 auto; justify-content: center; } .sv-foot > .grow { display: none; } }
@media (max-width: 520px) {
  .q-head { grid-template-columns: auto minmax(0, 1fr) auto; }
  .q-meta { grid-column: 2 / 3; grid-row: 2; justify-content: flex-start; }
  .q-chev { grid-column: 3; grid-row: 1; }
  .q-body { padding-left: 14px; }
}
.sv-step3 { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 24px; }
@media (max-width: 768px) { .sv-step3 { grid-template-columns: 1fr; } }
.sv-sum-block {
  display: flex; align-items: flex-start; gap: 10px; padding: 12px 14px;
  border: 1px solid hsl(var(--border)); border-radius: var(--radius-sm);
}
.sv-summary { flex: 1; min-width: 0; display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 6px 14px; margin: 0; font-size: var(--text-sm); }
.sv-summary dt { color: hsl(var(--muted-foreground)); }
.sv-summary dd { margin: 0; font-weight: 500; overflow-wrap: anywhere; }
.sv-sum-qs { margin: 6px 0 0; padding-left: 18px; display: flex; flex-direction: column; gap: 2px; }
.sv-preview {
  padding: 16px; border: 1px solid hsl(var(--border)); border-radius: var(--radius);
  background: hsl(var(--background)); box-shadow: 0 6px 20px rgb(0 0 0 / 0.06);
}
.sv-preview .pick-item { cursor: default; }
.sv-preview .pick-item:hover { border-color: hsl(var(--border)); background: hsl(var(--card)); }

/* Een verlopen sessie. Bovenaan en over de volle breedte, want alles eronder is
   vanaf dat moment verouderd. Blijft staan tot je opnieuw inlogt. */
#session-bar {
  position: fixed; top: 0; left: 0; right: 0; z-index: 200;
  display: flex; align-items: center; justify-content: center; gap: 12px;
  padding: 10px 16px; font-size: 13px; font-weight: 500;
  background: hsl(var(--destructive)); color: #fff;
  box-shadow: 0 2px 10px rgb(0 0 0 / 0.2);
}
#session-bar .btn { background: #fff; color: hsl(var(--destructive)); border: 0; }

/* Foutregel onder een formulierveld. Een toast vertelt je niet wélk veld het is,
   en in een lange dialoog staat het veld vaak niet eens in beeld. */
.field-error { display: block; margin-top: 5px; font-size: 12px; color: hsl(var(--destructive)); }
.input[aria-invalid="true"], .select[aria-invalid="true"], .textarea[aria-invalid="true"] {
  border-color: hsl(var(--destructive));
}

/* ── toasts ── */
#toasts { position: fixed; bottom: 20px; right: 20px; z-index: 100; display: flex; flex-direction: column; gap: 8px; }
.toast {
  background: hsl(var(--card)); color: hsl(var(--card-foreground));
  border: 1px solid hsl(var(--border)); border-radius: var(--radius-sm);
  padding: 12px 16px; font-size: var(--text-md); font-weight: 500;
  box-shadow: 0 8px 24px rgb(0 0 0 / 0.15);
  display: flex; align-items: center; gap: 8px; max-width: 340px;
  animation: fadeIn 0.25s ease-out;
}
.toast.err { border-color: hsl(var(--destructive) / 0.5); }
.toast .icon { width: 16px; height: 16px; }
.toast.ok .icon { color: hsl(var(--success)); }
.toast.err .icon { color: hsl(var(--destructive)); }
.toast.info .icon { color: hsl(var(--primary)); }

/* ── misc components ── */
.skeleton { background: hsl(var(--muted)); border-radius: var(--radius-sm); animation: pulse 1.5s ease-in-out infinite; }
@keyframes pulse { 50% { opacity: 0.5; } }
/* De knoppenrij onder een vragenlijstkaart. Zonder dit krimpt een knop onder zijn eigen
   label in plaats van naar de volgende regel te gaan: .grow zet min-width: 0 en .btn zet
   white-space: nowrap, dus flex-wrap kwam nooit aan de beurt en de labels liepen door
   elkaar heen. Dat was al zo bij een actieve vragenlijst (Bewerken, Sluiten, Nieuwe code)
   en Kopieren naar klas maakte het zichtbaar op elke kaart. max-content houdt elke knop op
   zijn eigen breedte, waarna de rij netjes afbreekt. */
.card-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.card-actions .btn { min-width: max-content; }

.progress { background: hsl(var(--muted)); border-radius: var(--radius-pill); height: 6px; overflow: hidden; flex: 1; }
.progress > div { background: hsl(var(--primary)); height: 100%; border-radius: var(--radius-pill); transition: width 0.3s; }
.empty {
  text-align: center; padding: 56px 20px; color: hsl(var(--muted-foreground));
  border: 1px dashed hsl(var(--border)); border-radius: var(--radius);
}
.empty .icon { width: 44px; height: 44px; opacity: 0.4; margin-bottom: 12px; }
.empty .empty-title { font-size: var(--text-lg); font-weight: 600; color: hsl(var(--foreground)); }
.banner {
  display: flex; align-items: flex-start; gap: 10px; padding: 12px 16px;
  border-radius: var(--radius-sm); border: 1px solid; font-size: var(--text-md);
}
.banner-blue { border-color: hsl(217 91% 60% / 0.35); background: hsl(217 91% 60% / 0.08); }
.banner-green { border-color: hsl(142 71% 45% / 0.35); background: hsl(142 71% 45% / 0.08); }
.banner-amber { border-color: hsl(38 92% 50% / 0.4); background: hsl(38 92% 50% / 0.09); color: hsl(32 80% 30%); }
.dark .banner-amber { color: hsl(38 92% 70%); }
.banner-red { border-color: hsl(var(--destructive) / 0.4); background: hsl(var(--destructive) / 0.07); }
.access-code-box {
  background: hsl(var(--primary) / 0.05); border: 1px solid hsl(var(--primary) / 0.2);
  border-radius: var(--radius-sm); padding: 12px;
}
.access-code { font-family: ui-monospace, monospace; font-size: 24px; font-weight: 700; letter-spacing: 0.3em; text-align: center; }
.status-pill { display: inline-block; font-size: var(--text-xs); padding: 2px 9px; border-radius: var(--radius-pill); font-weight: 600; white-space: nowrap; }

/* ── landing ──
   "De klas als netwerk": the sociogram is the page's visual system. Node/ring
   motifs recur (eyebrow bullet, step markers, auth artwork); everything else
   stays flat and calm, exactly like the authenticated app. */
.land-inner { max-width: 1120px; margin: 0 auto; padding: 0 24px; }

.land-nav {
  position: sticky; top: 0; z-index: 40;
  background: hsl(var(--background) / 0.88); backdrop-filter: saturate(180%) blur(10px);
  border-bottom: 1px solid hsl(var(--border));
}
.land-nav-inner {
  max-width: 1120px; margin: 0 auto; padding: 0 24px;
  height: 60px; display: flex; align-items: center; gap: 16px;
}
.land-brand {
  display: flex; align-items: center; gap: 10px;
  font-family: var(--font-display); font-weight: 700; font-size: var(--text-xl); letter-spacing: -0.01em;
}
.land-nav-links { display: flex; align-items: center; gap: 2px; margin-left: var(--space-3); font-size: var(--text-md); }
.land-nav-links a { padding: 7px 12px; border-radius: var(--radius-sm); color: hsl(var(--muted-foreground)); font-weight: 500; }
.land-nav-links a:hover { color: hsl(var(--foreground)); background: hsl(var(--muted)); }
.land-nav-actions { display: flex; align-items: center; gap: 8px; margin-left: auto; }

.btn-menu {
  display: none; align-items: center; justify-content: center;
  width: 38px; height: 38px; border-radius: var(--radius-sm);
  background: transparent; border: 1px solid hsl(var(--border));
  color: hsl(var(--foreground)); cursor: pointer;
}
.mobile-nav { display: none; }
@media (max-width: 860px) {
  .land-nav-links { display: none; }
  .btn-menu { display: inline-flex; }
  .mobile-nav {
    display: block; background: hsl(var(--card));
    border-top: 1px solid hsl(var(--border));
    max-height: 0; overflow: hidden; transition: max-height 0.22s ease;
  }
  .mobile-nav[data-open="true"] { max-height: 340px; }
  .mobile-nav a {
    display: block; padding: 13px 24px; font-weight: 600; font-size: 15px;
    border-bottom: 1px solid hsl(var(--border));
  }
  .mobile-nav a:last-child { border-bottom: 0; }
}
/* On a narrow phone the brand, language toggle, login button and hamburger no longer
   fit on one row. Drop the login button: the mobile menu carries it. */
@media (max-width: 400px) { .land-login { display: none; } }

.land-hero {
  max-width: 1120px; margin: 0 auto; padding: var(--space-8) var(--space-5) var(--space-9);
  display: grid; grid-template-columns: 1.05fr 0.95fr; gap: var(--space-7); align-items: center;
}
/* De serif heeft op deze maat geen extra gewicht nodig; 600 met een lichte negatieve
   spatiëring is waar hij voor getekend is. 800 maakte hem log. */
.land-hero-copy h1 {
  font-size: clamp(32px, 4.6vw, 46px); font-weight: 600;
  letter-spacing: -0.015em; line-height: 1.08; margin: var(--space-4) 0 var(--space-3);
}
/* .badge is nowrap by default; this one is a full sentence and must be free to wrap. */
.land-hero-copy .badge { white-space: normal; text-align: left; }
.land-lead { font-size: var(--text-xl); line-height: 1.6; max-width: 54ch; }
.land-hero-art { position: relative; min-width: 0; }
/* The thesis of the page: one student sits outside the rings. The callout is HTML,
   not baked into the SVG, so it translates with the rest of the page. */
.isolate-tag {
  position: absolute; left: 10px; bottom: 74px;
  display: inline-flex; align-items: center; gap: 7px;
  padding: 6px 10px; border-radius: var(--radius-sm);
  background: hsl(var(--card)); color: hsl(var(--card-foreground));
  border: 1px solid hsl(var(--border)); border-left: 3px solid hsl(var(--warning));
  box-shadow: var(--shadow-lift);
  font-size: 12px; font-weight: 600; white-space: nowrap;
}
.isolate-tag .icon { width: 14px; height: 14px; color: hsl(var(--warning)); }
.node-halo { animation: haloPulse 3.6s ease-in-out infinite; transform-origin: center; }
@keyframes haloPulse { 0%, 100% { opacity: 0.75; } 50% { opacity: 0.2; } }
@media (prefers-reduced-motion: reduce) { .node-halo { animation: none; } }
@media (max-width: 900px) { .land-hero { grid-template-columns: minmax(0, 1fr); gap: 36px; padding: 40px 24px 52px; } }
@media (max-width: 520px) { .isolate-tag { left: 6px; bottom: 66px; font-size: 11px; padding: 5px 8px; } }

/* Section rhythm: alternating tinted bands, no extra chrome. */
.land-section { padding: var(--space-9) 0; border-top: 1px solid hsl(var(--border)); }
.land-section-tint { background: hsl(var(--surface-2)); }
.land-head { max-width: 640px; margin-bottom: var(--space-6); }
.land-h2 { font-size: var(--text-3xl); font-weight: 600; margin: var(--space-3) 0 var(--space-2); }
.eyebrow {
  display: flex; align-items: center; gap: 8px;
  font-size: var(--text-xs); font-weight: 600; letter-spacing: 0.14em; text-transform: uppercase;
  color: hsl(var(--muted-foreground));
}
.eyebrow::before {
  content: ""; flex: none; width: 6px; height: 6px; border-radius: 50%;
  background: hsl(var(--primary)); box-shadow: 0 0 0 4px hsl(var(--primary) / 0.14);
}
@media (max-width: 760px) { .land-section { padding: var(--space-8) 0; } }

/* The "export sheet": the app's SVG exports are light-only art, so they sit on an
   explicit paper sheet that stays light in dark mode instead of being force-whitened. */
.sheet {
  border: 1px solid hsl(var(--sheet-border)); border-radius: var(--radius);
  background: hsl(var(--sheet)); color: hsl(var(--sheet-ink));
  overflow: hidden;
}
.sheet-lift { box-shadow: var(--shadow-lift); }
.sheet-bar {
  display: flex; align-items: center; gap: 8px; padding: 8px 10px;
  border-bottom: 1px solid hsl(var(--sheet-border)); background: hsl(210 20% 98%);
  font-size: 11px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase;
  color: hsl(215 16% 47%);
}
.sheet-dot { flex: none; width: 7px; height: 7px; border-radius: 50%; background: hsl(var(--primary)); }
.sheet-body { padding: 10px; }
.sheet-body img, .sheet-body svg { display: block; width: 100%; max-width: 100%; height: auto; }
.sheet-legend {
  display: flex; flex-wrap: wrap; gap: 12px; padding: 8px 10px;
  border-top: 1px solid hsl(var(--sheet-border));
  font-size: 11px; color: hsl(215 16% 47%);
}
/* Not .chip — that name is taken by the removable chip in the survey picker. */
.sheet-key { display: inline-flex; align-items: center; gap: 6px; }
.sheet-key i { display: block; width: 14px; height: 2px; border-radius: 2px; }
.key-pos i { background: hsl(var(--edge-pos)); }
.key-neg i { background: hsl(var(--edge-neg)); }
.key-mut i { background: hsl(var(--primary)); }

/* Showcase: het vel zit niet meer in een kaart (dat was doos-in-doos, met twee
   randen en twee schaduwen om hetzelfde plaatje). Ook geen raster van zes gelijke
   tegels. De sectie valt uiteen in drie banden, want zo zit het product in elkaar:
   twee bronnen (vragenlijst, observatie) en wat je er daarna mee doet. Binnen een
   band krijgt het beeld dat het meest zegt de volle breedte.
   minmax(0, 1fr), niet 1fr: een kale 1fr-track zakt niet onder de eigen breedte van
   de afbeelding en duwt de pagina op een telefoon zijwaarts. */
.band + .band { margin-top: var(--space-8); padding-top: var(--space-7); border-top: 1px solid hsl(var(--border)); }
.band-head { max-width: 680px; margin-bottom: var(--space-5); }
.band-title {
  font-family: var(--font-display); font-size: var(--text-2xl); font-weight: 600;
  letter-spacing: -0.01em; margin-bottom: var(--space-1);
}
.band-sub { font-size: var(--text-base); }
.showcase-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-7) var(--space-5); }
.show { display: flex; flex-direction: column; gap: var(--space-3); min-width: 0; }
.show-copy { max-width: 60ch; }
.show-title { font-size: var(--text-xl); margin-bottom: var(--space-1); display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.show-lead, .show-wide { grid-column: 1 / -1; }
/* De klasopstelling en de groepen krijgen wel een eigen rij, maar niet de volle
   breedte: hun tekening is ongeveer 800 eenheden breed en de naamlabels zijn 10, dus
   op 1050px worden die 13px en schreeuwt het blok de rest van de pagina omver. Op
   680px komen de naamlabels op ongeveer 8px uit, precies zo groot als in het
   observatienetwerk ernaast. De matrix houdt wel de volle breedte: dat is een tabel,
   die wint bij ruimte. */
.show-plan .sheet { max-width: 680px; }
.show-lead { display: grid; grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr); gap: var(--space-7); align-items: center; }
/* Twee lead-beelden onder elkaar zetten de tekst anders twee keer links; deze
   spiegelt de tweede, zodat het oog niet in één kolom vast blijft zitten. */
.show-lead-flip { grid-template-columns: minmax(0, 1.2fr) minmax(0, 0.8fr); }
.show-lead-flip .show-copy { order: 2; }
@media (max-width: 880px) {
  .showcase-grid { grid-template-columns: minmax(0, 1fr); }
  .show-lead, .show-lead-flip { grid-template-columns: minmax(0, 1fr); gap: var(--space-3); align-items: stretch; }
  .show-lead-flip .show-copy { order: 0; }
  .band + .band { margin-top: var(--space-7); padding-top: var(--space-6); }
}
/* De matrix is breed. Op een telefoon geeft meeschalen een onleesbare vlek, dus
   schuift hij binnen het vel opzij, net als de echte tabel in de app. */
@media (max-width: 700px) {
  .show-wide .sheet-body { overflow-x: auto; }
  .show-wide .sheet-body img { width: auto; max-width: none; height: 230px; }
}

/* Verantwoording: tekst links, controleerbare feiten rechts. Geen kaders en geen
   iconen; de ring voor elk item is de vorm van een knoop uit het sociogram. */
.basis-grid { display: grid; grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr); gap: var(--space-7); align-items: start; }
.basis-grid .land-head { margin-bottom: 0; }
/* Staat in de linkerkolom onder de intro, waar toch ruimte over was. De methode en de
   maker horen bij elkaar: allebei antwoord op "waarom zou ik dit geloven". */
.maker { margin-top: var(--space-6); padding-top: var(--space-5); border-top: 1px solid hsl(var(--border)); }
.maker-title { font-size: var(--text-lg); margin-bottom: var(--space-2); }
.maker p { max-width: 46ch; line-height: 1.65; }
.basis-list { display: grid; gap: var(--space-5); margin: 0; }
.basis-list dt { position: relative; padding-left: 22px; font-weight: 600; margin-bottom: var(--space-1); }
.basis-list dd { margin: 0 0 0 22px; font-size: var(--text-md); color: hsl(var(--muted-foreground)); }
@media (max-width: 880px) { .basis-grid { grid-template-columns: minmax(0, 1fr); gap: var(--space-6); } }

/* Prijs: één band met een hairline ertussen, in plaats van zwevende kaarten met
   een uitgelichte "aanbevolen" kolom. Sinds september 2026 drie kolommen (gratis,
   Pro, School); Pro en School zijn op aanvraag, vanuit een account. */
.price-band {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  max-width: 840px; overflow: hidden;
  background: hsl(var(--card)); border: 1px solid hsl(var(--border)); border-radius: var(--radius);
}
.price-band-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); max-width: 1040px; }
.price-vat { margin: 0 0 var(--space-2); }
.price-how { max-width: 1040px; margin-top: var(--space-4); }
.price-col { padding: var(--space-6); display: flex; flex-direction: column; }
.price-col + .price-col { border-left: 1px solid hsl(var(--border)); background: hsl(var(--muted) / 0.4); }
.price-top { display: flex; align-items: center; gap: var(--space-2); }
.price {
  font-family: var(--font-display); font-size: var(--text-4xl); font-weight: 600;
  letter-spacing: -0.01em; font-variant-numeric: tabular-nums; margin: var(--space-3) 0 var(--space-1);
}
.price span { font-family: var(--font-sans); font-size: var(--text-md); font-weight: 500; margin-left: 3px; }
/* Houdt de knop en het wachtlijstformulier op dezelfde hoogte, ongeacht het
   verschil in aantal regels erboven. */
.price-cta { margin-top: auto; padding-top: var(--space-1); }
.plan-list { display: grid; gap: var(--space-2); list-style: none; padding: 0; margin: var(--space-4) 0; }
.plan-list li { position: relative; padding-left: 22px; }
/* Dezelfde knoopring als in de verantwoording, in plaats van een generiek vinkje. */
.plan-list li::before,
.basis-list dt::before,
.flow-items li::before {
  content: ""; position: absolute; left: 0; top: 5px;
  width: 9px; height: 9px; border-radius: 50%;
  border: 2px solid hsl(var(--primary));
}
.plan-list li::before { top: 4px; width: 8px; height: 8px; }
.tick { flex: none; display: inline-flex; color: hsl(var(--success)); }
.tick .icon { width: 16px; height: 16px; }
@media (max-width: 760px) {
  .price-band, .price-band-3 { grid-template-columns: minmax(0, 1fr); }
  .price-col + .price-col { border-left: 0; border-top: 1px solid hsl(var(--border)); }
}

/* De vergelijkingstabel onder de band. Eén rij per echte poort in de code; de
   eerste kolom is de functie, de andere drie een vinkje, een getal of een punt.
   Breed genoeg voor drie kolommen naast de functie, dus op een telefoon scrolt hij
   in zijn eigen kader en nooit de pagina (CLAUDE.md, responsive). */
.cmp-wrap { max-width: 1040px; margin-top: var(--space-6); overflow-x: auto; border: 1px solid hsl(var(--border)); border-radius: var(--radius); background: hsl(var(--card)); }
.cmp { width: 100%; min-width: 640px; border-collapse: collapse; font-size: var(--text-sm); }
.cmp-caption { text-align: left; padding: var(--space-4) var(--space-5) var(--space-2); font-weight: 600; font-size: var(--text-md); }
.cmp th, .cmp td { padding: 10px var(--space-5); border-top: 1px solid hsl(var(--border)); text-align: left; vertical-align: top; }
.cmp thead th { border-top: 0; font-weight: 600; color: hsl(var(--muted-foreground)); font-size: var(--text-xs); text-transform: uppercase; letter-spacing: 0.04em; }
.cmp tbody th { font-weight: 500; width: 46%; }
.cmp-cell { width: 18%; }
.cmp-cell .tick .icon { width: 18px; height: 18px; }
.cmp-no { display: inline-block; width: 18px; text-align: center; color: hsl(var(--muted-foreground)); font-weight: 700; }

/* Het schema onder de hero: verzamelen -> analyseren -> indelen. Geen kaders om de
   blokjes; de pagina scheidt met witruimte en hairlines, en de pijl in de tussenruimte
   is hier de scheiding. De knoopring komt uit de regel hierboven, zodat dit dezelfde
   stip is als in de prijsband en de verantwoording. */
.flow {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-7); list-style: none; padding: 0; margin: 0;
}
.flow-step { position: relative; min-width: 0; }
.flow-label {
  font-size: var(--text-xs); font-weight: 600; letter-spacing: 0.08em;
  text-transform: uppercase; color: hsl(var(--muted-foreground));
  margin-bottom: var(--space-4);
}
.flow-items { display: grid; gap: var(--space-4); list-style: none; padding: 0; margin: 0; }
.flow-items li { position: relative; padding-left: 22px; display: grid; gap: 2px; }
.flow-title { font-size: var(--text-base); }
/* Halverwege de tussenruimte, dus op de helft van de gap naar links. */
.flow-arrow {
  position: absolute; left: calc(var(--space-7) / -2); top: 0;
  transform: translateX(-50%); color: hsl(var(--muted-foreground));
}
.flow-arrow .icon { width: 20px; height: 20px; display: block; }

@media (max-width: 760px) {
  .flow { grid-template-columns: minmax(0, 1fr); gap: var(--space-6); }
  /* Gestapeld wijst de pijl omlaag en gaat hij boven de stap staan in plaats van ernaast. */
  .flow-arrow {
    position: static; transform: none; display: block; margin-bottom: var(--space-3);
  }
  .flow-arrow .icon { transform: rotate(90deg); }
}

/* Auth: a split panel in the app's own language. */
.auth-split {
  display: grid; grid-template-columns: 1fr 1fr; max-width: 900px;
  background: hsl(var(--card)); border: 1px solid hsl(var(--border));
  border-radius: var(--radius); overflow: hidden;
}
.auth-brand {
  position: relative; overflow: hidden; padding: var(--space-6); text-align: left;
  background: hsl(var(--accent)); color: hsl(var(--accent-foreground));
  border-right: 1px solid hsl(var(--border));
  display: flex; flex-direction: column; justify-content: space-between; gap: 28px;
}
.auth-brand-title {
  font-family: var(--font-display); font-size: var(--text-3xl); font-weight: 600;
  letter-spacing: -0.01em; margin-top: var(--space-3);
}
.auth-brand .brand-mark { width: 40px; height: 40px; }
.auth-points { display: grid; gap: 9px; position: relative; list-style: none; padding: 0; margin: 0; }
.auth-points li { display: flex; gap: 8px; align-items: center; font-weight: 500; }
.auth-points .tick { color: hsl(var(--primary)); }
.auth-art { position: absolute; right: -36px; bottom: -36px; opacity: 0.35; color: hsl(var(--primary)); pointer-events: none; }
.auth-art svg { width: 210px; height: 210px; display: block; }
.auth-panel { padding: var(--space-6); text-align: left; }
.auth-panel .tabs { margin-bottom: 22px; }
@media (max-width: 900px) {
  .auth-split { grid-template-columns: 1fr; }
  .auth-brand { border-right: 0; border-bottom: 1px solid hsl(var(--border)); padding: 28px; }
}
@media (max-width: 420px) { .auth-panel { padding: 24px 20px; } }

.land-foot { border-top: 1px solid hsl(var(--border)); background: hsl(var(--surface-2)); padding: 28px 0; }
.land-foot-in { display: flex; flex-wrap: wrap; align-items: center; gap: 14px 20px; }
.land-foot a { color: hsl(var(--muted-foreground)); }
.land-foot a:hover { color: hsl(var(--foreground)); }
.land-foot-copy { margin-left: auto; }
@media (max-width: 760px) { .land-foot-copy { margin-left: 0; width: 100%; } }

/* ── standalone auth card (reset.html) ──
   Same language as the split panel on the landing: accent-tinted head with the node
   artwork, plain card below. */
.auth-solo {
  position: relative; width: 100%; max-width: 420px;
  background: hsl(var(--card)); border: 1px solid hsl(var(--border));
  border-radius: var(--radius); overflow: hidden;
}
.auth-solo-head {
  position: relative; overflow: hidden;
  display: flex; flex-direction: column; align-items: center; gap: 10px; text-align: center;
  padding: 30px 24px 22px; border-bottom: 1px solid hsl(var(--border));
  background: hsl(var(--accent)); color: hsl(var(--accent-foreground));
}
/* Lift the content above the artwork — but not the artwork itself, which must stay
   absolutely positioned or it takes up 210px of flow. */
.auth-solo-head > :not(.auth-art) { position: relative; }
.auth-solo-head .brand-mark { width: 44px; height: 44px; }
.auth-solo-head .brand-mark .icon { width: 24px; height: 24px; }
.auth-solo-head h1 { font-size: var(--text-3xl); }
.auth-solo-head .auth-art { right: -50px; bottom: -60px; opacity: 0.3; }
.auth-solo-body { padding: var(--space-5); }

/* ── public pages (join / survey) ── */
/* Vlak, geen verloop. Het driekleurige verloop dat hier stond was de enige plek in de
   app waar kleur decoratie was in plaats van betekenis, en juist leerlingen zagen het. */
.public-bg {
  min-height: 100vh; min-height: 100dvh; display: flex; flex-direction: column;
  background: hsl(var(--background));
}
.public-center { flex: 1; display: flex; align-items: center; justify-content: center; padding: 16px; }
.code-boxes { display: flex; gap: 8px; justify-content: center; }
.code-box {
  width: 40px; height: 48px; text-align: center;
  font-family: ui-monospace, monospace; font-size: 18px; font-weight: 700;
  border: 2px solid hsl(var(--border)); border-radius: var(--radius-sm);
  background: hsl(var(--card)); color: hsl(var(--foreground)); outline: none;
  text-transform: uppercase; transition: border-color 0.15s;
}
.code-box.filled { border-color: hsl(var(--primary)); background: hsl(var(--primary) / 0.05); }
.code-box:focus { border-color: hsl(var(--primary)); box-shadow: 0 0 0 3px hsl(var(--ring) / 0.18); }
.code-box.error { border-color: hsl(var(--destructive)); }

.pick-item {
  width: 100%; display: flex; align-items: center; gap: 12px; padding: 10px 12px;
  border: 1px solid hsl(var(--border)); border-radius: var(--radius-sm); cursor: pointer;
  background: hsl(var(--card)); text-align: left; transition: all 0.15s; font-size: var(--text-md); font-weight: 500;
}
.pick-item:hover { border-color: hsl(var(--primary) / 0.55); background: hsl(var(--primary) / 0.05); }
.pick-item.picked { border-color: hsl(var(--primary)); background: hsl(var(--primary) / 0.1); }
.pick-item:disabled { opacity: 0.4; cursor: not-allowed; }
.pick-item .avatar { background: hsl(var(--primary) / 0.12); color: hsl(var(--primary)); }
/* De kroon op een leerlingtegel in de observatie-editor. Een zusknop van de tegel en
   geen kind ervan (een knop in een knop is ongeldige HTML), maar absoluut over de
   rechterkant gelegd zodat de tegel er hetzelfde uitziet als toen de kroon er nog in
   zat. Het icoon was 1.1em in een span zonder padding: met een vinger nauwelijks te
   raken, en een misser haalde de leerling uit het groepje. */
.tile-wrap { position: relative; display: flex; }
.tile-wrap.has-crown .pick-item { padding-right: 44px; }
.pick-crown {
  position: absolute; right: 2px; top: 2px; bottom: 2px; width: 40px;
  display: flex; align-items: center; justify-content: center;
  border: 0; background: none; border-radius: var(--radius-sm);
  cursor: pointer; color: hsl(var(--muted-foreground));
}
.pick-crown.on { color: #B45309; }
.pick-crown:hover { background: hsl(var(--muted)); }
.pick-crown .icon { width: 18px; height: 18px; }
/* Met een vinger is 40px de ondergrens; op een muis is het ruim genoeg. */
@media (pointer: coarse) {
  .pick-crown { width: 48px; }
  .tile-wrap.has-crown .pick-item { padding-right: 52px; }
}
.chip {
  display: inline-flex; align-items: center; gap: 5px; padding: 4px 11px;
  border-radius: var(--radius-pill); border: none; cursor: pointer;
  background: hsl(var(--primary) / 0.1); color: hsl(var(--primary));
  font-size: 12px; font-weight: 600;
}
.chip:hover { background: hsl(var(--destructive) / 0.12); color: hsl(var(--destructive)); }
/* Op de observatiepagina wordt staand in de zaal getikt. De audit van september 2026
   mat daar 32px voor de namen en 24px voor de chips; 40px is de ondergrens die de
   kroon hierboven al had. Alleen daar, zodat de lijsten op andere pagina's niet
   ineens hoger worden. */
.obs-touch .pick-item, .obs-touch .chip { min-height: 40px; }
.obs-touch .choice-card { display: flex; gap: 14px; align-items: flex-start; text-align: left; width: 100%; padding: 16px; cursor: pointer; font: inherit; color: inherit; }
.obs-touch .choice-card:hover { border-color: hsl(var(--primary) / 0.55); }
.obs-touch .choice-card .icon { width: 22px; height: 22px; color: hsl(var(--primary)); flex-shrink: 0; margin-top: 2px; }

/* De twee waarschuwingen boven een sociogram zijn meestal leeg; leeg kosten ze in de
   .stack anders elk een tussenruimte. */
#plan-view > #merge-warning:empty, #plan-view > #students-warning:empty { display: none; }

/* ── sociogram canvas ── */
/* touch-action bepaalt hier het hele aanraakgedrag, ook van seating en teams: elk
   canvas draagt deze klasse. Met `none` claimden de pan-handlers iedere veeg binnen
   een vak van 560px, dus op een telefoon kon je de pagina niet langs een sociogram
   scrollen — en de browser mocht ook niet knijpen, terwijl geen enkele renderer
   zelf multi-touch kende. `pan-y` geeft de verticale veeg terug aan de browser
   (inclusief de natuurlijke uitloop) en laat twee vingers over aan attachGestures. */
.socio-canvas {
  border: 1px solid hsl(var(--border)); border-radius: var(--radius); overflow: hidden;
  background: white; position: relative; height: 560px; touch-action: pan-y;
}
/* Een sleep die op een knooppunt begint is bedoeld en mag beide richtingen opeisen.
   Stoelen, plaatsen en groepskaartjes zijn tikdoelen, geen sleepdoelen: die houden
   pan-y, zodat een veeg die daar begint gewoon de pagina scrollt. */
.socio-node, .cluster-node { touch-action: none; }
/* Gezet door setPanLock() in svg-view.js zodra de tekening groter is dan het vak: dan is
   er iets om naartoe te pannen en pakt het canvas het gebaar. Dit MOET tussen gebaren
   omgaan, niet halverwege: zie de uitleg bij setPanLock. */
.socio-canvas.pan-lock { touch-action: none; }
.dark .socio-canvas { background: hsl(222 47% 7%); }
.socio-canvas > svg { display: block; width: 100%; height: 100%; cursor: grab; }
.socio-canvas > svg.panning { cursor: grabbing; }
.socio-node { cursor: pointer; }
.socio-controls {
  position: absolute; bottom: 12px; left: 12px; display: flex; flex-direction: column; gap: 4px;
  background: hsl(var(--card)); border: 1px solid hsl(var(--border));
  border-radius: var(--radius-sm); padding: 3px; box-shadow: 0 2px 8px rgb(0 0 0 / 0.1);
}
.socio-controls button {
  border: none; background: none; cursor: pointer; padding: 6px;
  border-radius: var(--radius-xs); display: flex; color: hsl(var(--foreground));
}
.socio-controls button:hover { background: hsl(var(--muted)); }
/* 560px is bijna een heel telefoonscherm: er stond niets van de pagina omheen, dus je
   kon niet zien dat er nog iets ná de tekening kwam. */
@media (max-width: 768px) { .socio-canvas { height: 380px; } }
/* Met een vinger zijn de zoomknoppen van ~30px te klein; op een muis is 30px prima. */
@media (pointer: coarse) { .socio-controls button { padding: 10px; } }
/* Het schuifje "Zichtbaarheid eenrichtingslijnen" naast de filtervinkjes. */
.oneway-opacity { width: 120px; accent-color: hsl(var(--primary)); cursor: pointer; }
/* Legenda boven een sociogram: kolommen (lijnen, kleuren) met de items onder elkaar.
   Uit de evaluatie met collega's (oktober 2026): de lange rij liep door elkaar. */
.socio-legend { display: flex; flex-wrap: wrap; gap: 10px 40px; }
.socio-legend .leg-col { display: flex; flex-direction: column; gap: 5px; }
.socio-legend .leg-head { font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; }
.socio-legend .leg-item { display: flex; align-items: center; gap: 6px; }
.socio-legend .leg-line { display: inline-block; width: 26px; height: 0; border-top: 3px solid; flex: none; }
.socio-legend .leg-tri { width: 26px; height: 8px; fill: #64748B; flex: none; }
.socio-legend .leg-ring { display: inline-block; width: 12px; height: 12px; border-radius: 50%; border: 2px dashed #F59E0B; flex: none; }
.socio-legend .leg-dot { display: inline-block; width: 12px; height: 12px; border-radius: 50%; border: 2px solid; flex: none; }
/* De legenda als paneel rechtsboven IN de tegel (oktober 2026). Broertje van de svg,
   net als .socio-controls: alle gebaren hangen aan de svg, dus slepen en zoomen
   merken het paneel niet. Inklapbaar via .leg-toggle (sociograms.js). */
/* Links en zo smal mogelijk (keuze van de eigenaar): één kolom, tekst die afbreekt.
   fit() laat zijn breedte vrij (fitInset), dus de tekening begint rechts ervan. Onder
   het paneel zitten de zoomknoppen linksonder; bij 560px hoogte raken ze elkaar niet. */
.socio-legend-panel {
  position: absolute; top: 12px; left: 12px; z-index: 1;
  width: max-content; max-width: min(190px, calc(100% - 24px));
  max-height: calc(100% - 24px); overflow: auto;
  background: hsl(var(--card)); border: 1px solid hsl(var(--border));
  border-radius: var(--radius-sm); box-shadow: 0 2px 8px rgb(0 0 0 / 0.1);
  padding: 4px 10px 8px;
}
.socio-legend-panel .leg-toggle {
  display: flex; align-items: center; gap: 6px;
  border: none; background: none; cursor: pointer; padding: 4px 0;
  font-size: 12px; font-weight: 600; color: hsl(var(--foreground));
}
.socio-legend-panel .leg-body { margin-top: 4px; flex-direction: column; gap: 10px; }
.socio-legend-panel .leg-body[hidden] { display: none; }
.socio-legend-panel .leg-item { align-items: flex-start; line-height: 1.35; }
/* Bij afbrekende tekst staat het voorbeeld naast de eerste regel, niet in het midden. */
.socio-legend-panel .leg-item > .leg-line { margin-top: 7px; }
.socio-legend-panel .leg-item > .leg-tri { margin-top: 5px; }
.socio-legend-panel .leg-item > .leg-dot,
.socio-legend-panel .leg-item > .leg-ring { margin-top: 2px; }
.leg-expl { display: grid; gap: 6px; max-width: 760px; margin-top: 10px; }
.leg-expl > div { display: flex; gap: 8px; align-items: baseline; }
.leg-expl .leg-dot { display: inline-block; width: 12px; height: 12px; border-radius: 50%; border: 2px solid; flex: none; position: relative; top: 2px; }

/* ── clustering (force-directed) view ── */
.cluster-node { cursor: pointer; }

/* ── seating plan + work groups ── */
.seat-cell { cursor: pointer; }
.seat-student { cursor: pointer; }
.team-card { cursor: pointer; }
.team-student { cursor: pointer; }

/* Printing a plan (.plan-print on the body: seating, teams, sociograms, reports).
   Deliberately NOT a visibility-only trick like the base print block below: that leaves
   the sidebar's and topbar's layout boxes in place, so an absolutely-positioned subtree
   lands next to the paper edge instead of on it. Here the chrome is taken out of the
   flow entirely.
   The drawing itself only fits because setPrintMode() gives the SVG a viewBox around
   the whole thing and drops the pan/zoom transform — CSS alone cannot rescale it. */
@media print {
  @page { size: A4 landscape; margin: 10mm; }

  /* The base print block below blanks every page with `body * { visibility: hidden }`,
     so these pages must claim their own visibility back. */
  .plan-print #plan-view,
  .plan-print #plan-view * { visibility: visible !important; }

  /* `html, body { height: 100% }` pins the body to a full sheet, so the plan spilled
     onto a second page even though it fits. On paper the body must be as tall as its
     content, no taller. */
  html:has(body.plan-print), body.plan-print {
    height: auto !important; min-height: 0 !important; overflow: visible !important;
  }

  .plan-print .sidebar,
  .plan-print .topbar,
  .plan-print .no-print { display: none !important; }

  .plan-print .shell,
  .plan-print .main-wrap,
  .plan-print .content,
  .plan-print .page {
    display: block; height: auto; min-height: 0; overflow: visible;
    padding: 0; margin: 0; gap: 0;
  }
  /* Alles binnen .page verbergen behalve #plan-view zelf, zijn voorouders (anders
     verdwijnt de plattegrond mét de doos waar hij in zit) en zijn inhoud. Dit stond
     eerst als twee regels op vaste id's (.page > *:not(#main-view) en
     #main-view > *:not(#plan-view)), wat alleen klopte voor de nesting van seating en
     teams. Sociograms nest een niveau dieper en reports heeft helemaal geen #main-view.
     Eén regel op de plek van het plan in plaats van op de weg ernaartoe werkt voor alle
     vier. :has() is hier geen nieuwe afhankelijkheid: de regel hierboven gebruikt hem al. */
  .plan-print .page *:not(:has(#plan-view), #plan-view, #plan-view *) { display: none !important; }
  .plan-print .print-only { display: block; }

  /* A4 landscape minus the 10mm margins leaves ~190mm of height. The header and the
     legend take ~20mm, so the drawing gets 150mm — anything taller spills onto a second
     sheet, which is exactly the bug this replaces. */
  .plan-print #plan-view { display: block; break-inside: avoid; }
  .plan-print #plan-view > * { margin: 0 0 4mm 0; }
  .plan-print #plan-view .socio-canvas {
    height: auto; border: none; overflow: visible; background: white;
  }
  /* height:auto works because the SVG carries a viewBox in print mode. */
  .plan-print #plan-view .socio-canvas > svg {
    width: 100%; height: auto; max-height: 150mm;
  }
  /* Sociograms heeft naast de tekening nog een kop, een uitlegregel en twee legenda's,
     en dan is 150mm net te veel: nagemeten liep het blad over op 194 van de 190mm die
     A4-liggend biedt. Seating en teams hebben die randen niet, dus die houden 150mm. */
  .socio-print #plan-view .socio-canvas > svg { max-height: 120mm; }
  /* Op papier staat de legenda zoals op het scherm: een smalle kolom LINKS naast de
     tekening (keuze van de eigenaar, oktober 2026), en altijd open, ook als het paneel
     op het scherm dichtgeklapt was. Niet zwevend: de tegel wordt een rij met eerst het
     paneel (order) en dan de svg, die de rest van de breedte krijgt. */
  .socio-print #plan-view .socio-canvas { display: flex; align-items: flex-start; gap: 6mm; }
  .socio-print #plan-view .socio-legend-panel {
    order: -1; flex: none; position: static; width: 48mm; max-width: none; max-height: none;
    overflow: visible; border: none; box-shadow: none; padding: 0; background: none;
  }
  .socio-print #plan-view .socio-canvas > svg { flex: 1 1 0; min-width: 0; width: auto; }
  .socio-print #plan-view .socio-legend-panel .leg-body { display: flex !important; flex-direction: column; gap: 3mm; margin: 0; }
  .socio-print #plan-view .socio-legend-panel .leg-col { gap: 1.5mm; }

  /* Een matrix is geen tekening maar een tabel (reports.html), dus die heeft het
     omgekeerde nodig: geen viewBox maar de dingen die een lange tabel op papier
     leesbaar houden. De sticky rowhead is op het scherm precies goed en op papier
     precies fout: hij schuift over de kolommen heen. */
  .plan-print #plan-view .matrix-wrap { overflow: visible; }
  .plan-print #plan-view .matrix .rowhead { position: static; }
  .plan-print #plan-view table.matrix { width: 100%; font-size: 7.5pt; }
  /* Nagemeten: met genummerde kolommen past een klas van 25 op 952px binnen de 1047px
     van A4-liggend, maar een klas van 30 komt op 1102px uit. Het krappere padding haalt
     die terug naar 978px. Op het scherm blijft de ruimere versie staan. */
  .plan-print #plan-view .matrix th,
  .plan-print #plan-view .matrix td { padding: 1px 3px; }
  .plan-print #plan-view .matrix .rowhead { min-width: 0; }
  /* De schermtabel is één brede tabel; op papier komt hij uit renderPrintSheets()
     in kolomgroepen die elk op een blad passen, met de namenkolom herhaald. Elke
     groep na de eerste begint op een nieuw blad. */
  .plan-print #plan-view #surveys-matrix,
  .plan-print #plan-view #obs-matrix { display: none; }
  .plan-print #plan-view #surveys-matrix-print,
  .plan-print #plan-view #obs-matrix-print { display: block; }
  .plan-print #plan-view .print-sheet { width: 100%; }
  .plan-print #plan-view .print-sheet-wrap + .print-sheet-wrap { break-before: page; }
  /* Loopt de tabel over een tweede blad, dan hoort de kop mee. */
  .plan-print #plan-view .matrix thead { display: table-header-group; }
  .plan-print #plan-view .matrix tr { break-inside: avoid; }
  /* break-inside: avoid op #plan-view klopt voor één tekening, maar een matrix van
     dertig leerlingen mág breken. */
  .plan-print #plan-view:has(.matrix) { break-inside: auto; }
}

/* ── printen: de basis ──
   Alles op papier is onzichtbaar tenzij een pagina het zelf terugzet (.plan-print
   hierboven, .qr-printing hieronder). Een pagina zonder printfunctie levert zo een leeg
   blad op in plaats van een half afgesneden scherm, en de sleuteldialoog komt nooit op
   papier. Hier hoorde vroeger een .print-report-subboom bij die zichzelf terugzette;
   die werd sinds juli 2026 door geen pagina meer gebruikt en is in oktober opgeruimd. */
.print-only { display: none; }
.no-print { }            /* visible on screen; hidden in @media print (.plan-print) */
@media print {
  @page { margin: 12mm; }
  body { background: white; }
  body * { visibility: hidden; }
  .socio-controls { display: none !important; }
  .help-btn { display: none !important; }   /* een (i) zegt op papier niets */
}

/* ── de sleutel hoort nooit op papier (reports.html) ─────────────────────────
   #dlg-key zet de weergavenaam naast de echte naam. Een uitgeprinte sleutel naast
   een uitgeprinte matrix maakt de anonimisering ongedaan zonder dat je het
   doorhebt, en dat is precies het papier dat op een verkeerde stapel belandt.
   Daarom staat er in de dialoog geen printknop, maar dat sluit Ctrl+P niet af.

   .no-print helpt hier niet: die regel werkt alleen op .plan-print-pagina's, binnen
   de pagina, en de dialoog staat daarbuiten. De `visibility: hidden` uit `body *` zou hem al
   moeten verbergen, maar een modale <dialog> staat in de top layer en gedraagt zich
   daar bij het afdrukken niet betrouwbaar — zie het QR-blok hieronder, dat om
   dezelfde reden buiten de dialoog om werkt. display:none laat geen ruimte voor
   twijfel. */
@media print { #dlg-key { display: none !important; } }

/* ── QR-code afdrukken (surveys.html) ────────────────────────────────────────
   Op het scherm bestaat dit blok niet; het is er alleen voor de printer. De
   dialoog zelf afdrukken werkt niet betrouwbaar, want een modale <dialog> staat
   in de top layer. Zie showQr() in js/pages/surveys.js.

   Dezelfde aanpak als het basisblok hierboven: alles onzichtbaar, daarna één
   subboom terugzetten. De body-klasse is wat het afbakent, zodat een gewone
   print op deze pagina niets van dit blok te zien krijgt. */
.qr-print { display: none; }
@media print {
  body.qr-printing * { visibility: hidden; }
  body.qr-printing .qr-print, body.qr-printing .qr-print * { visibility: visible; }
  body.qr-printing .qr-print {
    display: block; position: absolute; left: 0; top: 0; width: 100%;
    text-align: center;
  }
  body.qr-printing .qr-print p { margin-top: 8mm; font-size: 12pt; }
}

/* ── foutenlijst op logs.html ────────────────────────────────────────────────
   Gestapeld in plaats van een tabel. Vijf kolommen passen niet op een telefoon,
   en juist de melding viel dan buiten beeld. Zie js/pages/logs.js. */
.err-row { padding: 10px 0; border-bottom: 1px solid hsl(var(--border)); }
.err-row:last-child { border-bottom: 0; }
.err-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.err-count { font-weight: 700; font-variant-numeric: tabular-nums; }
.err-when { margin-left: auto; }
.err-msg { font-size: 13px; margin-top: 3px; overflow-wrap: anywhere; }
.err-stack { white-space: pre-wrap; overflow-wrap: anywhere; margin: 4px 0 0; }
