/* ==========================================================================
   components.css — the reusable UI vocabulary
   Buttons · Cards · Stat tiles · Badges · Tables · Forms · Tabs · Charts
   ========================================================================== */

/* ==========================================================================
   Buttons
   ========================================================================== */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  height: 34px;
  padding: 0 var(--space-4);
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  background: transparent;
  font-size: var(--text-md);
  font-weight: 500;
  line-height: 1;
  white-space: nowrap;
  cursor: pointer;
  user-select: none;
  transition: background var(--duration-fast) var(--ease),
              border-color var(--duration-fast) var(--ease),
              color var(--duration-fast) var(--ease),
              box-shadow var(--duration-fast) var(--ease);
}

.btn:disabled { opacity: 0.5; cursor: not-allowed; }
.btn:focus-visible { outline: none; box-shadow: var(--focus-ring); }

.btn__icon {
  width: 15px;
  height: 15px;
  flex-shrink: 0;
  stroke: currentColor;
  fill: none;
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* Primary — one per view, the single most likely action */
.btn--primary {
  background: var(--accent);
  color: var(--accent-ink);
  box-shadow: var(--shadow-xs);
}
.btn--primary:hover:not(:disabled)  { background: var(--accent-hover); }
.btn--primary:active:not(:disabled) { background: var(--accent-active); }

/* Secondary — the default for everything else */
.btn--secondary {
  background: var(--surface-raised);
  border-color: var(--border-strong);
  color: var(--text-primary);
  box-shadow: var(--shadow-xs);
}
.btn--secondary:hover:not(:disabled)  { background: var(--surface-hover); }
.btn--secondary:active:not(:disabled) { background: var(--surface-active); }

/* Ghost — toolbar and icon actions */
.btn--ghost { color: var(--text-secondary); }
.btn--ghost:hover:not(:disabled)  { background: var(--surface-hover); color: var(--text-primary); }
.btn--ghost:active:not(:disabled) { background: var(--surface-active); }

/* Danger */
.btn--danger {
  background: var(--critical-bg);
  border-color: var(--critical-line);
  color: var(--critical-ink);
}
.btn--danger:hover:not(:disabled) { background: color-mix(in srgb, var(--critical-bg) 70%, var(--status-critical) 12%); }

.btn--sm   { height: 28px; padding: 0 var(--space-3); font-size: var(--text-sm); }
.btn--icon { width: 34px; padding: 0; }
.btn--icon.btn--sm { width: 28px; }
.btn--block { width: 100%; }

/* ==========================================================================
   Speech bubble — the mascot's voice, wherever he turns up
   The tail is a rotated square masked to two edges, so it reads as one shape
   with the bubble instead of a triangle stuck to its side.
   ========================================================================== */

.bubble {
  position: relative;
  padding: var(--space-4) var(--space-5);
  border: 2px solid var(--mascot-ink);
  border-radius: var(--radius-lg);
  background: var(--white);
  color: var(--mascot-ink);
  font-size: var(--text-md);
  font-weight: 600;
  letter-spacing: -0.01em;
  line-height: 1.3;
  white-space: nowrap;
  box-shadow: var(--shadow-sm);
}

.bubble::after {
  content: "";
  position: absolute;
  bottom: 14px;
  width: 14px;
  height: 14px;
  background: var(--white);
}

/* Bubble sits to the RIGHT of the mascot — tail points back at him. */
.bubble--tail-left::after {
  left: -9px;
  border-left: 2px solid var(--mascot-ink);
  border-bottom: 2px solid var(--mascot-ink);
  border-bottom-left-radius: 3px;
  transform: rotate(45deg);
}

/* Bubble sits to the LEFT of the mascot. */
.bubble--tail-right::after {
  right: -9px;
  border-right: 2px solid var(--mascot-ink);
  border-bottom: 2px solid var(--mascot-ink);
  border-bottom-right-radius: 3px;
  transform: rotate(-45deg);
}

/* Bubble sits ABOVE the mascot — tail points down at his head. */
.bubble--tail-down::after {
  left: 50%;
  bottom: -9px;
  margin-left: -7px;
  border-left: 2px solid var(--mascot-ink);
  border-bottom: 2px solid var(--mascot-ink);
  border-bottom-left-radius: 3px;
  transform: rotate(-45deg);
}

/* ==========================================================================
   Mascot stage — the bot with something to say
   --------------------------------------------------------------------------
   Used on the sign-in panel and on the employee dashboard. The entrance lives
   on the container and the float on the image, so the two never fight over
   the same transform — that hand-off is what makes an animation snap the
   moment it starts looping.
   ========================================================================== */

.mascot-stage {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-5);
  min-width: 0;
  text-align: center;
  animation: greeting-rise 620ms var(--ease) both;
}

.mascot-stage .bubble {
  font-size: var(--text-xl);
  padding: var(--space-4) var(--space-6);
  animation: greeting-in 420ms var(--ease) 300ms both;
}

.mascot-stage__bot {
  max-width: 100%;
  width: auto;
  will-change: transform;
  /* Slow and small on purpose — alive, not attention-seeking. Two keyframes
     and `alternate`, so the loop has no seam. */
  animation: mascot-float 2.6s ease-in-out infinite alternate;
}

@keyframes greeting-rise {
  from { opacity: 0; transform: translate3d(0, 20px, 0); }
  to   { opacity: 1; transform: none; }
}

@keyframes greeting-in {
  from { opacity: 0; transform: translateY(8px) scale(0.94); }
  to   { opacity: 1; transform: none; }
}

@keyframes mascot-float {
  from { transform: translate3d(0, 5px, 0) rotate(0.5deg); }
  to   { transform: translate3d(0, -6px, 0) rotate(-0.5deg); }
}

/* ==========================================================================
   Splash — the mascot greeting shown once per session, on either page
   --------------------------------------------------------------------------
   Whether it shows at all is decided by a one-line script in <head> that
   stamps data-splash on <html>, so a returning visitor never sees a flash of
   it. splash.js only owns the timing and the exit.
   ========================================================================== */

.splash {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: grid;
  place-items: center;
  padding: var(--space-6);
  background: var(--surface-page);
  opacity: 1;
  transition: opacity 280ms var(--ease);
}

/* A very soft wash of the mascot's green so the screen isn't a flat field. */
.splash::before {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(48% 42% at 50% 56%, rgba(122, 196, 118, 0.20), transparent 70%);
  pointer-events: none;
}

:root[data-splash="off"] .splash { display: none; }
:root[data-splash="on"] { overflow: hidden; }

.splash[data-state="leaving"] { opacity: 0; }

.splash__stage {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: var(--space-5);
  cursor: default;
}

.splash__bot {
  height: clamp(180px, 36vh, 300px);
  width: auto;
  flex-shrink: 0;
  animation: splash-bot 520ms var(--ease) both;
}

.splash__bubble {
  margin-top: 8%;
  font-size: var(--text-lg);
  transform-origin: left bottom;
  animation: splash-bubble 340ms var(--ease) 260ms both;
}

@keyframes splash-bot {
  from { opacity: 0; transform: translateY(14px) scale(0.97); }
  to   { opacity: 1; transform: none; }
}

@keyframes splash-bubble {
  from { opacity: 0; transform: scale(0.82) translate(-6px, 6px); }
  to   { opacity: 1; transform: none; }
}


@media (max-width: 620px) {
  .splash__stage { flex-direction: column-reverse; align-items: center; gap: var(--space-4); }
  .splash__bubble { margin-top: 0; transform-origin: bottom center; white-space: normal; text-align: center; }
  /* The bubble moves above the mascot here, so its tail has to turn to face
     him. Overriding the modifier keeps the markup identical at every width. */
  .splash__bubble.bubble--tail-left::after {
    left: 50%; right: auto; bottom: -9px; margin-left: -7px;
    border: 0;
    border-left: 2px solid var(--mascot-ink);
    border-bottom: 2px solid var(--mascot-ink);
    transform: rotate(-45deg);
  }
}

@media (prefers-reduced-motion: reduce) {
  .splash__bot,
  .splash__bubble { animation: none; }
}

/* ==========================================================================
   Drive-away — the hand-off from signing in to the dashboard
   --------------------------------------------------------------------------
   Two elements, two jobs. The track owns the horizontal travel and does it
   LINEARLY between exactly two keyframes; the car owns the bob and alternates
   on its own clock. Doing both on one element is what makes a drive judder:
   a timing function applies between *every pair* of keyframes, so each extra
   waypoint adds an ease-in and an ease-out — five little accelerations
   instead of one steady crossing.
   ========================================================================== */

.drive {
  position: fixed;
  inset: 0;
  z-index: 300;
  overflow: hidden;
  background: var(--surface-page);
  opacity: 0;
  transition: opacity 200ms var(--ease);
}

.drive[hidden] { display: none; }
.drive[data-state="on"] { opacity: 1; }

.drive::before {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(46% 40% at 50% 52%, rgba(122, 196, 118, 0.20), transparent 70%);
}

.drive__track {
  position: absolute;
  top: 50%;
  left: 0;
  will-change: transform;
  transform: translate3d(-110%, -50%, 0);
}

.drive[data-state="on"] .drive__track {
  animation: drive-across 1250ms linear forwards;
}

.drive__car {
  display: block;
  height: clamp(150px, 26vh, 250px);
  width: auto;
  will-change: transform;
  animation: car-bob 560ms ease-in-out infinite alternate;
}

@keyframes drive-across {
  from { transform: translate3d(-110%, -50%, 0); }
  to   { transform: translate3d(105vw, -50%, 0); }
}

/* Alternate + ease-in-out gives a continuous sine-like bob with no seam at
   the loop point. */
@keyframes car-bob {
  from { transform: translate3d(0, 3px, 0) rotate(-0.4deg); }
  to   { transform: translate3d(0, -5px, 0) rotate(0.4deg); }
}

/* ==========================================================================
   Brand lockup — shared by the sidebar and the signed-out screens.
   Colours are inherited, so the same markup works on navy and on paper.
   ========================================================================== */

.brand {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-width: 0;
}

.brand__mark {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  flex-shrink: 0;
  border-radius: var(--radius-md);
  background: var(--brand-mark-bg);
  color: var(--brand-mark-ink);
  font-size: var(--text-sm);
  font-weight: 700;
  letter-spacing: -0.02em;
}

.brand__mark img { width: 21px; height: 21px; }

.brand__text { display: flex; flex-direction: column; min-width: 0; }

.brand__name {
  font-size: var(--text-md);
  font-weight: 600;
  letter-spacing: -0.01em;
  white-space: nowrap;
  color: inherit;
}

.brand__tag {
  font-size: var(--text-2xs);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  white-space: nowrap;
  color: currentColor;
  opacity: 0.6;
}

/* ==========================================================================
   Cards & panels
   ========================================================================== */

.card {
  display: flex;
  flex-direction: column;
  background: var(--surface-raised);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-xs);
  overflow: hidden;
}

.card__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--border-subtle);
  /* A title and a "bijgewerkt door … op …" beside it do not both fit on a
     phone; the second one drops to its own line rather than running off the
     edge of the card. */
  flex-wrap: wrap;
}

.card__heading { min-width: 0; }
.card__title { font-size: var(--text-lg); font-weight: 600; }
.card__subtitle { margin-top: 2px; font-size: var(--text-sm); color: var(--text-muted); }
.card__tools { display: flex; align-items: center; gap: var(--space-2); flex-shrink: 0; }
.card__body { padding: var(--space-5); }
.card__body--flush { padding: 0; }

.card__footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-5);
  border-top: 1px solid var(--border-subtle);
  background: var(--surface-page);
  font-size: var(--text-sm);
  color: var(--text-muted);
}

/* ==========================================================================
   Hero figure — the one number a view leads with. Exactly one per view.
   ========================================================================== */

.hero-figure { display: grid; gap: var(--space-2); }

.hero-figure__value {
  font-size: var(--text-3xl);
  font-weight: 600;
  line-height: 1;
  letter-spacing: -0.03em;
  /* Proportional figures on purpose — tabular-nums reads loose at this size */
}

.hero-figure__meta {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
  font-size: var(--text-sm);
  color: var(--text-muted);
}

/* ==========================================================================
   Stat tile — label · value · delta · sparkline
   ========================================================================== */

.stat {
  display: grid;
  gap: var(--space-2);
  transition: opacity var(--duration-base) var(--ease);
  padding: var(--space-4) var(--space-5);
  background: var(--surface-raised);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-xs);
}

.stat[data-loading="true"] { opacity: 0.45; }

.stat__label {
  font-size: var(--text-sm);
  color: var(--text-muted);
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.stat__value {
  font-size: var(--text-2xl);
  font-weight: 600;
  line-height: 1.1;
  letter-spacing: -0.02em;
}

.stat__row {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--space-3);
}

.stat__spark { flex-shrink: 0; }

.delta {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  font-size: var(--text-sm);
  font-weight: 500;
  font-variant-numeric: tabular-nums;
}

.delta--up   { color: var(--text-success); }
.delta--down { color: var(--text-danger); }
.delta--flat { color: var(--text-muted); }

.delta__icon { width: 13px; height: 13px; fill: currentColor; }
.delta__note { color: var(--text-muted); font-weight: 400; }

/* ==========================================================================
   Badges & pills — status always ships with an icon + a text label
   ========================================================================== */

.badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px var(--space-2);
  border: 1px solid var(--neutral-line);
  border-radius: var(--radius-sm);
  background: var(--neutral-bg);
  color: var(--neutral-ink);
  font-size: var(--text-xs);
  font-weight: 500;
  line-height: 1.5;
  white-space: nowrap;
}

.badge__dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
  flex-shrink: 0;
}

.badge--good     { background: var(--good-bg);     border-color: var(--good-line);     color: var(--good-ink); }
.badge--warning  { background: var(--warning-bg);  border-color: var(--warning-line);  color: var(--warning-ink); }
.badge--serious  { background: var(--serious-bg);  border-color: var(--serious-line);  color: var(--serious-ink); }
.badge--critical { background: var(--critical-bg); border-color: var(--critical-line); color: var(--critical-ink); }
.badge--accent   { background: var(--accent-soft); border-color: transparent;          color: var(--accent-soft-ink); }

/* ==========================================================================
   Avatar
   ========================================================================== */

.avatar {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  flex-shrink: 0;
  border-radius: var(--radius-pill);
  background: var(--surface-sunken);
  color: var(--text-secondary);
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: 0.01em;
  user-select: none;
}

.avatar--sm { width: 24px; height: 24px; font-size: var(--text-2xs); }
.avatar--nav { background: rgba(255, 255, 255, 0.14); color: var(--nav-ink-strong); }

/* Een kleur per persoon, gekozen door `Data.who(naam)`. De grijze cirkel
   hierboven blijft de basis: hij is wat je krijgt als er geen naam is (een
   groep, een "+3", een onbekende), en dat hoort er anders uit te zien dan
   iemand. */
.avatar--who1 { background: var(--who-1); color: var(--who-ink); }
.avatar--who2 { background: var(--who-2); color: var(--who-ink); }
.avatar--who3 { background: var(--who-3); color: var(--who-ink); }
.avatar--who4 { background: var(--who-4); color: var(--who-ink); }
.avatar--who5 { background: var(--who-5); color: var(--who-ink); }
.avatar--who6 { background: var(--who-6); color: var(--who-ink); }

/* In de zijbalk staat je eigen gezicht op een donkergroen vlak; een gekleurde
   cirkel daarin is een tweede accent naast het accent. */
.avatar--nav[class*="avatar--who"] { background: rgba(255, 255, 255, 0.14); color: var(--nav-ink-strong); }

/* ==========================================================================
   Forms
   ========================================================================== */

.field { display: grid; gap: var(--space-2); }

.field__label {
  font-size: var(--text-sm);
  font-weight: 500;
  color: var(--text-primary);
}

.field__hint { font-size: var(--text-xs); color: var(--text-muted); }

.field__error {
  font-size: var(--text-xs);
  color: var(--critical-ink);
  display: flex;
  align-items: center;
  gap: 4px;
}

.input,
.select,
.textarea {
  width: 100%;
  min-height: 34px;
  padding: 0 var(--space-3);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  background: var(--surface-raised);
  color: var(--text-primary);
  font-size: var(--text-md);
  transition: border-color var(--duration-fast) var(--ease),
              box-shadow var(--duration-fast) var(--ease);
}

.textarea { padding: var(--space-2) var(--space-3); min-height: 84px; resize: vertical; line-height: var(--leading-snug); }

.input::placeholder,
.textarea::placeholder { color: var(--text-faint); }

.input:hover,
.select:hover,
.textarea:hover { border-color: var(--text-faint); }

.input:focus,
.select:focus,
.textarea:focus {
  outline: none;
  border-color: var(--border-focus);
  box-shadow: var(--focus-ring);
}

.input:disabled,
.select:disabled,
.textarea:disabled { background: var(--surface-sunken); color: var(--text-faint); cursor: not-allowed; }

.input[aria-invalid="true"] { border-color: var(--status-critical); }

.select {
  appearance: none;
  padding-right: var(--space-8);
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16' fill='none' stroke='%235f6c69' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 6.5 8 10.5 12 6.5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--space-3) center;
  cursor: pointer;
}

:root[data-theme="dark"] .select,
:root:where(:not([data-theme="light"])) .select {
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16' fill='none' stroke='%2387928f' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 6.5 8 10.5 12 6.5'/%3E%3C/svg%3E");
}
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .select {
    background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16' fill='none' stroke='%235f6c69' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 6.5 8 10.5 12 6.5'/%3E%3C/svg%3E");
  }
}

/* Search input with a leading icon */
.search {
  position: relative;
  display: flex;
  align-items: center;
  min-width: 0;
}

.search__icon {
  position: absolute;
  left: var(--space-3);
  width: 15px;
  height: 15px;
  pointer-events: none;
  stroke: var(--text-faint);
  fill: none;
  stroke-width: 1.7;
  stroke-linecap: round;
}

.search .input { padding-left: var(--space-8); }
.search--topbar { width: min(340px, 40vw); }

/* Switch */
.switch {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  cursor: pointer;
  user-select: none;
}

.switch__input { position: absolute; opacity: 0; width: 0; height: 0; }

.switch__track {
  position: relative;
  width: 36px;
  height: 20px;
  flex-shrink: 0;
  border-radius: var(--radius-pill);
  background: var(--border-strong);
  transition: background var(--duration-fast) var(--ease);
}

.switch__thumb {
  position: absolute;
  top: 2px;
  left: 2px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--white);
  box-shadow: var(--shadow-xs);
  transition: transform var(--duration-fast) var(--ease);
}

.switch__input:checked + .switch__track { background: var(--accent); }
.switch__input:checked + .switch__track .switch__thumb { transform: translateX(16px); }
.switch__input:focus-visible + .switch__track { box-shadow: var(--focus-ring); }

.switch__text { display: grid; gap: 1px; }
.switch__label { font-size: var(--text-md); font-weight: 500; }
.switch__hint { font-size: var(--text-xs); color: var(--text-muted); }

/* Checkbox */
.checkbox {
  width: 15px;
  height: 15px;
  accent-color: var(--accent);
  cursor: pointer;
  flex-shrink: 0;
}

/* ==========================================================================
   Segmented control — mutually exclusive views (e.g. Chart / Table)
   ========================================================================== */

.segmented {
  display: inline-flex;
  padding: 2px;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  background: var(--surface-sunken);
}

.segmented__btn {
  padding: 0 var(--space-3);
  height: 26px;
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-muted);
  font-size: var(--text-sm);
  font-weight: 500;
  cursor: pointer;
  transition: background var(--duration-fast) var(--ease), color var(--duration-fast) var(--ease);
}

.segmented__btn:hover { color: var(--text-primary); }

.segmented__btn[aria-pressed="true"],
.segmented__btn[aria-selected="true"] {
  background: var(--surface-raised);
  color: var(--text-primary);
  box-shadow: var(--shadow-xs);
}

/* ==========================================================================
   Filter bar — ONE row, above everything it scopes. Never inside a card.
   ========================================================================== */

.filter-bar {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
  margin-bottom: var(--space-4);
}

.filter-bar__group { display: flex; align-items: center; gap: var(--space-2); }
.filter-bar__spacer { flex: 1; min-width: var(--space-2); }

.filter-bar .select,
.filter-bar .input { width: auto; min-width: 150px; }

/* ==========================================================================
   Tabs
   ========================================================================== */

.tabs {
  display: flex;
  gap: var(--space-1);
  border-bottom: 1px solid var(--border-subtle);
  margin-bottom: var(--space-5);
  overflow-x: auto;
}

.tab {
  position: relative;
  padding: var(--space-3) var(--space-3);
  border: 0;
  background: transparent;
  color: var(--text-muted);
  font-size: var(--text-md);
  font-weight: 500;
  white-space: nowrap;
  cursor: pointer;
  transition: color var(--duration-fast) var(--ease);
}

.tab:hover { color: var(--text-primary); }

.tab[aria-selected="true"] { color: var(--text-primary); }
.tab[aria-selected="true"]::after {
  content: "";
  position: absolute;
  inset: auto 0 -1px;
  height: 2px;
  border-radius: var(--radius-pill) var(--radius-pill) 0 0;
  background: var(--accent);
}

/* ==========================================================================
   Table
   ========================================================================== */

.table-wrap { width: 100%; overflow-x: auto; }

.table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  font-size: var(--text-sm);
}

.table th,
.table td {
  padding: var(--space-3) var(--space-4);
  text-align: left;
  border-bottom: 1px solid var(--border-subtle);
  vertical-align: middle;
  white-space: nowrap;
}

.table thead th {
  position: sticky;
  top: 0;
  z-index: 1;
  background: var(--surface-page);
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--text-muted);
}

.table tbody tr { transition: background var(--duration-fast) var(--ease); }
.table tbody tr:hover { background: var(--surface-hover); }
.table tbody tr:last-child td { border-bottom: 0; }

.table .num { text-align: right; font-variant-numeric: tabular-nums; }
.table .col-shrink { width: 1%; }

.table__sort {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  border: 0;
  padding: 0;
  background: transparent;
  color: inherit;
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  cursor: pointer;
}

.table__sort:hover { color: var(--text-primary); }

.table__sort-icon {
  width: 11px;
  height: 11px;
  stroke: currentColor;
  fill: none;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  opacity: 0;
  transition: opacity var(--duration-fast) var(--ease), transform var(--duration-fast) var(--ease);
}

.table__sort:hover .table__sort-icon { opacity: 0.45; }
[aria-sort="ascending"] .table__sort-icon,
[aria-sort="descending"] .table__sort-icon { opacity: 1; }
[aria-sort="descending"] .table__sort-icon { transform: rotate(180deg); }

.cell-primary { display: flex; align-items: center; gap: var(--space-3); }
.cell-primary__text { display: grid; gap: 1px; min-width: 0; }
.cell-primary__name { font-weight: 500; color: var(--text-primary); }
.cell-primary__meta { font-size: var(--text-xs); color: var(--text-muted); }

/* ==========================================================================
   Pagination
   ========================================================================== */

.pagination { display: flex; align-items: center; gap: var(--space-2); }

.pagination__pages {
  font-size: var(--text-sm);
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
  padding-inline: var(--space-2);
}

/* ==========================================================================
   List — activity feed / side panels
   ========================================================================== */

.list { display: grid; }

.list__item {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-5);
  border-bottom: 1px solid var(--border-subtle);
}

.list__item:last-child { border-bottom: 0; }
.list__body { min-width: 0; flex: 1; }
.list__title { font-size: var(--text-sm); font-weight: 500; }
.list__meta { font-size: var(--text-xs); color: var(--text-muted); margin-top: 1px; }
.list__side { flex-shrink: 0; font-size: var(--text-xs); color: var(--text-faint); white-space: nowrap; }

/* Progress rows (a meter: fill carries the value, track is a lighter step) */
.meter-row { display: grid; gap: var(--space-2); padding: var(--space-3) var(--space-5); }
.meter-row + .meter-row { border-top: 1px solid var(--border-subtle); }

.meter-row__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  font-size: var(--text-sm);
}

.meter-row__value { font-variant-numeric: tabular-nums; color: var(--text-secondary); }

.meter {
  height: 6px;
  border-radius: var(--radius-pill);
  background: var(--surface-sunken);
  overflow: hidden;
}

.meter__fill {
  height: 100%;
  border-radius: var(--radius-pill);
  background: var(--chart-1);
}

/* ==========================================================================
   Empty state
   ========================================================================== */

.empty {
  display: grid;
  justify-items: center;
  gap: var(--space-3);
  padding: var(--space-9) var(--space-5);
  text-align: center;
}

.empty__icon {
  width: 34px;
  height: 34px;
  stroke: var(--text-faint);
  fill: none;
  stroke-width: 1.4;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.empty__title { font-size: var(--text-md); font-weight: 600; }
/* Twee namen voor dezelfde regel, met opzet: de CSS heette hier altijd
   `__text` en elk scherm in de app schrijft `__body` — tien plekken, van de
   projectlijst tot de bestandspagina. Die tekst stond dus overal ongestyled,
   in de gewone tekstgrootte en -kleur, en dat viel niet op omdat een lege
   staat er toch al kaal uitziet. Beide namen doen het nu; welke van de twee
   sterft, is een opruiming voor één keer en niet voor onderweg. */
.empty__text,
.empty__body { font-size: var(--text-sm); color: var(--text-muted); max-width: 40ch; }

/* ==========================================================================
   Charts
   The container is sized to include the x-axis band, so axis labels are
   never cut off and the card never grows a nested scrollbar.
   ========================================================================== */

.chart {
  position: relative;
  width: 100%;
  transition: opacity var(--duration-base) var(--ease);
}

.chart[data-loading="true"] { opacity: 0.45; }  /* refetch holds the frame */

.chart__svg { display: block; width: 100%; overflow: visible; }

.chart__grid-line { stroke: var(--chart-grid); stroke-width: 1; shape-rendering: crispEdges; }
.chart__axis-line { stroke: var(--chart-axis); stroke-width: 1; shape-rendering: crispEdges; }

.chart__tick {
  fill: var(--chart-label);
  font-size: 11px;
  font-variant-numeric: tabular-nums;
}

.chart__label-value {
  fill: var(--text-primary);
  font-size: 11px;
  font-weight: 600;
}

.chart__line { fill: none; stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
.chart__area { stroke: none; opacity: 0.10; }

.chart__dot { stroke: var(--chart-surface); stroke-width: 2; }  /* 2px surface ring */

.chart__crosshair { stroke: var(--chart-axis); stroke-width: 1; pointer-events: none; }

.chart__bar { transition: opacity var(--duration-fast) var(--ease); pointer-events: none; }
.chart__hit { fill: transparent; cursor: pointer; }
.chart__hit:focus-visible { outline: 2px solid var(--border-focus); outline-offset: -2px; }
.chart[data-hovering="true"] .chart__bar:not([data-active="true"]) { opacity: 0.55; }

/* Legend — always present for 2+ series. Mirrors the mark: rect for bars
   and areas, a line-key for lines. */
.legend {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  flex-wrap: wrap;
  padding-top: var(--space-3);
}

.legend__item {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-sm);
  color: var(--text-secondary);  /* text tokens, never the series colour */
}

.legend__swatch { width: 10px; height: 10px; border-radius: 2px; flex-shrink: 0; }
.legend__key    { width: 14px; height: 2px; border-radius: 1px; flex-shrink: 0; }

/* Tooltip — value leads, series name follows */
.chart-tooltip {
  position: absolute;
  z-index: 10;
  width: max-content;
  max-width: 100%;
  min-width: 148px;
  padding: var(--space-3);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  background: var(--surface-raised);
  box-shadow: var(--shadow-md);
  pointer-events: none;
  opacity: 0;
  transform: translate(-50%, -100%);
  transition: opacity var(--duration-fast) var(--ease);
}

.chart-tooltip[data-visible="true"] { opacity: 1; }

.chart-tooltip__title {
  font-size: var(--text-xs);
  color: var(--text-muted);
  margin-bottom: var(--space-2);
}

.chart-tooltip__row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-sm);
}

.chart-tooltip__row + .chart-tooltip__row { margin-top: var(--space-1); }
.chart-tooltip__key { width: 12px; height: 2px; border-radius: 1px; flex-shrink: 0; }
.chart-tooltip__value { font-weight: 600; font-variant-numeric: tabular-nums; }
.chart-tooltip__name { color: var(--text-muted); margin-left: auto; white-space: nowrap; }

/* Chart's table twin — the WCAG-clean equivalent, so no value is
   reachable only by hovering. */
.chart-table { display: none; }
.chart-view[data-view="table"] .chart-table { display: block; }
.chart-view[data-view="table"] .chart,
.chart-view[data-view="table"] .legend { display: none; }

/* ==========================================================================
   Dropdown menu
   ========================================================================== */

.menu-anchor { position: relative; }

.menu {
  position: absolute;
  top: calc(100% + var(--space-2));
  right: 0;
  z-index: 40;
  min-width: 210px;
  padding: var(--space-2);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  background: var(--surface-raised);
  box-shadow: var(--shadow-lg);
}

.menu[hidden] { display: none; }

/* Een menu hangt aan zijn knop en die knop kan overal staan; op een smal scherm
   steekt het er dan uit. Deze twee worden gezet door de code die het menu opent
   (`houdBinnen()` in drive-ui.js) op grond van een METING, niet van een
   mediaquery: waar een knop terechtkomt hangt af van hoe breed de woorden
   ervoor zijn, en die verschillen per taal.

   `pinL` staat ná `pinR` zodat links wint als ze allebei aanslaan — een menu dat
   breder is dan het venster kun je maar beter vanaf de linkerrand laten lopen,
   want daar begint het lezen. Het maximum is het vangnet daaronder.

   Ze staan dubbel geschreven (`.menu.menu--pinR`) en dat is geen slordigheid:
   ze moeten `.menu--filter` uit drive.css overrulen, en dat bestand laadt ná
   dit. Volgorde helpt dan niet, specificiteit wel. */
.menu.menu--pinR { left: auto; right: 0; }
.menu.menu--pinL { left: 0; right: auto; }
.menu { max-width: calc(100vw - 16px); }

.menu__label {
  padding: var(--space-2) var(--space-3) var(--space-1);
  font-size: var(--text-2xs);
  font-weight: 600;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--text-faint);
}

.menu__item {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  padding: var(--space-2) var(--space-3);
  border: 0;
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--text-primary);
  font-size: var(--text-md);
  text-align: left;
  cursor: pointer;
}

.menu__item:hover { background: var(--surface-hover); }

.menu__check {
  margin-left: auto;
  font-size: 16px;
  font-weight: 700;
  color: var(--accent);
  opacity: 0;
}

.menu__item[aria-checked="true"] .menu__check { opacity: 1; }
.menu__sep { height: 1px; margin: var(--space-2) 0; background: var(--border-subtle); }

/* ==========================================================================
   Alert — form-level feedback, announced to screen readers
   ========================================================================== */

.alert {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-3);
  border: 1px solid var(--critical-line);
  border-radius: var(--radius-md);
  background: var(--critical-bg);
  color: var(--critical-ink);
  font-size: var(--text-sm);
  line-height: var(--leading-snug);
}

.alert[hidden] { display: none; }

.alert__icon {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  margin-top: 1px;
  stroke: currentColor;
  fill: none;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.alert--info {
  border-color: var(--border-subtle);
  background: var(--surface-sunken);
  color: var(--text-secondary);
}

/* ==========================================================================
   Busy state — a button that is waiting on the network
   ========================================================================== */

.btn[data-busy="true"] { pointer-events: none; opacity: 0.85; }

.spinner {
  width: 14px;
  height: 14px;
  flex-shrink: 0;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  animation: spin 0.6s linear infinite;
}

@keyframes spin { to { transform: rotate(360deg); } }

/* ==========================================================================
   Language switch
   ========================================================================== */

.lang-switch { display: inline-flex; }
.lang-switch .segmented__btn { min-width: 34px; font-variant-numeric: normal; }

/* ==========================================================================
   Style-guide page helpers
   ========================================================================== */

.swatch-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(132px, 1fr)); gap: var(--space-3); }

.swatch { display: grid; gap: var(--space-2); }
.swatch__chip { height: 52px; border-radius: var(--radius-md); border: 1px solid var(--border-subtle); }
.swatch__name { font-size: var(--text-xs); font-weight: 500; }
.swatch__hex { font-family: var(--font-mono); font-size: var(--text-2xs); color: var(--text-muted); }

.specimen { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }
.specimen + .specimen { margin-top: var(--space-4); }

.type-row { display: flex; align-items: baseline; gap: var(--space-4); flex-wrap: wrap; }
.type-row + .type-row { margin-top: var(--space-4); padding-top: var(--space-4); border-top: 1px solid var(--border-subtle); }
.type-row__meta { font-family: var(--font-mono); font-size: var(--text-2xs); color: var(--text-muted); width: 130px; flex-shrink: 0; }

/* ==========================================================================
   Back link — one step up, above a page title
   ========================================================================== */

.back-link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: var(--space-3);
  font-size: var(--text-sm);
  color: var(--text-secondary);
  text-decoration: none;
}

.back-link:hover { color: var(--text-primary); }
.back-link svg { width: 14px; height: 14px; stroke: currentColor; fill: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; flex-shrink: 0; }

/* ==========================================================================
   Toasts — something happened that the page itself cannot show
   Bottom-centre rather than a corner: it is a message, not a notification
   badge, and it should be read before it leaves.
   ========================================================================== */

.toasts {
  position: fixed;
  left: 50%;
  bottom: var(--space-6);
  z-index: 90;
  display: grid;
  gap: var(--space-2);
  justify-items: center;
  transform: translateX(-50%);
  pointer-events: none;
}

/* Op een smal scherm mag hij niet breder zijn dan het scherm. Dit stond als
   min(420px, calc(100vw - 2rem)) geschreven; een browser die min() met een
   calc() erin niet slikt, gooide dan de hele regel weg en werd de melding
   schermvullend. Twee losse regels doen hetzelfde en kunnen niet omvallen. */
@media (max-width: 452px) { .toast { max-width: calc(100vw - 2rem); } }

.toast {
  max-width: 420px;
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  background: var(--surface-inverse);
  color: var(--text-inverse);
  font-size: var(--text-sm);
  box-shadow: var(--shadow-lg);
  animation: toast-in 200ms var(--ease) both;
}

.toast--warning { background: var(--warning-bg); color: var(--warning-ink); border-color: var(--warning-line); }
.toast--danger  { background: var(--critical-bg); color: var(--critical-ink); border-color: var(--critical-line); }
.toast--out { opacity: 0; transform: translateY(6px); transition: opacity 240ms var(--ease), transform 240ms var(--ease); }

@keyframes toast-in {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: translateY(0); }
}

.toast--good { background: var(--good-bg); color: var(--good-ink); border-color: var(--good-line); }

/* ==========================================================================
   Reference card — a thing from elsewhere in the application
   --------------------------------------------------------------------------
   The same card in a message, in the composer and in the share dialog, so a
   shared project looks the same everywhere it is talked about. The whole card
   is the button: one tab stop, one hit area, and no small target inside a
   large one for a thumb to miss.
   ========================================================================== */

.ref-card {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  padding: var(--space-3);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  background: var(--surface-raised);
  color: var(--text-primary);
  font: inherit;
  text-align: left;
}

button.ref-card { cursor: pointer; transition: border-color 140ms var(--ease), background 140ms var(--ease); }
button.ref-card:hover { border-color: var(--border-strong); background: var(--surface-hover); }
button.ref-card:focus-visible { outline: none; border-color: var(--border-focus); box-shadow: var(--focus-ring); }
.ref-card--gone { opacity: 0.75; }

.ref-card__icon {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border-radius: var(--radius-md);
  background: var(--accent-soft);
  flex-shrink: 0;
}

.ref-card__glyph { width: 18px; height: 18px; stroke: var(--accent-soft-ink); fill: none; stroke-width: 1.7; }
.ref-card--person .ref-card__icon { background: var(--neutral-bg); }
.ref-card--person .ref-card__glyph { stroke: var(--neutral-ink); }

.ref-card__text { min-width: 0; display: grid; gap: 2px; }

.ref-card__kind {
  font-size: var(--text-2xs);
  font-weight: 600;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--text-muted);
}

/* Two lines of a title, then an ellipsis: a card that grows without limit
   stops being a card and starts being the message. */
.ref-card__title {
  font-size: var(--text-md);
  font-weight: 600;
  line-height: var(--leading-snug);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.ref-card__sub {
  font-size: var(--text-xs);
  color: var(--text-muted);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.ref-card__facts { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-top: var(--space-1); }

.ref-card__fact {
  padding: 1px var(--space-2);
  border-radius: var(--radius-pill);
  background: var(--neutral-bg);
  color: var(--neutral-ink);
  font-size: var(--text-2xs);
  font-weight: 600;
  white-space: nowrap;
}

.ref-card__fact--good     { background: var(--good-bg);     color: var(--good-ink); }
.ref-card__fact--warning  { background: var(--warning-bg);  color: var(--warning-ink); }
.ref-card__fact--serious  { background: var(--serious-bg);  color: var(--serious-ink); }
.ref-card__fact--critical { background: var(--critical-bg); color: var(--critical-ink); }
.ref-card__fact--info     { background: var(--accent-soft); color: var(--accent-soft-ink); }

.ref-card__go {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--accent-soft-ink);
  white-space: nowrap;
}

.ref-card__go svg { width: 15px; height: 15px; stroke: currentColor; fill: none; stroke-width: 1.9; }

/* In a bubble there is no room for a caption as well — the chevron says it. */
@media (max-width: 620px) {
  .ref-card__go span { display: none; }
}

/* ==========================================================================
   Utilities
   ========================================================================== */

.hidden { display: none !important; }
.mt-4 { margin-top: var(--space-4); }
.mt-5 { margin-top: var(--space-5); }


/* Het sneltoetsenlijstje: toets links, wat hij doet rechts. */
.keys {
  display: grid;
  grid-template-columns: 96px minmax(0, 1fr);
  align-items: center;
  gap: var(--space-2) var(--space-4);
}

.keys__key {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: var(--text-xs);
  color: var(--text-secondary);
  background: var(--surface-sunken);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-sm);
  padding: 2px var(--space-2);
  text-align: center;
}

.keys__what { font-size: var(--text-sm); color: var(--text-primary); }
