/* ZaehlWeb — appearance following the Ceyeborg CI.
 *
 * Colors and fonts come from ceyeb.org: Exo as the house typeface, Source Code
 * Pro for numbers, midnight blue #000A32 as the base tone, magenta #E40EAB as
 * the only accent. The status colors (good/warning/bad) also come from the
 * house palette, but are deliberately kept apart from the accent: the accent
 * leads, the status color judges.
 *
 * Both themes hang on attributes, never on fixed colors inside a block —
 * :root carries light completely, the two dark blocks only override tokens.
 * Whoever adds a color has to declare it as a token, otherwise it is missing
 * in the other theme.
 */

:root{
  --nacht:#000A32; --magenta:#E40EAB; --violett:#8539F1;

  --grund:#ffffff; --feld:#F5F5F5; --feld2:#ebebef;
  --schrift:#000A32; --schrift2:#444444; --schrift3:#888888;
  --linie:#e0e0e0; --linie2:#c9c9d2;
  --akzent:#E40EAB; --akzent-weich:#fdeaf7;
  --gut:#067a4e; --gut-weich:#e6f6ee;
  --warn:#8a5a10; --warn-weich:#fdf3e0;
  --schlecht:#cf2e2e; --schlecht-weich:#fdeaea;
  --schatten:0 1px 2px rgba(0,10,50,.06), 0 4px 14px rgba(0,10,50,.05);
  --rund:10px;

  /* Chart series. Computed, not guessed: checked for color vision deficiency
     against the respective background (worst pair dE 8.5 against a target of
     8, all pairs, not just adjacent ones). The order is fixed — the color
     belongs to the series, not to its rank. */
  --reihe-app:#E40EAB;
  --reihe-bild:#0693e3;
  --reihe-video:#00d084;
  --reihe-kunde:#8539F1;
  --gitterlinie:#ececf0;
  --bandfarbe:#f0eff4;
}
@media (prefers-color-scheme:dark){
  :root:not([data-thema="hell"]){
    --grund:#000A32; --feld:#081541; --feld2:#0d1c50;
    --schrift:#ffffff; --schrift2:#c2cbe6; --schrift3:#8892b4;
    --linie:#17255c; --linie2:#22316e;
    --akzent:#FF3FC0; --akzent-weich:#2a0b34;
    --gut-weich:#062a20;
    --warn:#fcb900; --warn-weich:#2e2308;
    --schlecht:#ff6b6b; --schlecht-weich:#340f12;
    --schatten:0 1px 2px rgba(0,0,0,.5), 0 4px 16px rgba(0,0,0,.35);
    /* Own steps, not an inversion of the light ones: on midnight blue the
       permissible lightness band is L 0.48-0.67, and the light tones fell
       outside it. Same hues, re-stepped, checked again. */
    --reihe-app:#ca0096;
    --reihe-bild:#097abe;
    --reihe-video:#0a8a57;
    --reihe-kunde:#8930fd;
    --gitterlinie:#152352;
    --bandfarbe:#0c1a4a;
  }
}
:root[data-thema="dunkel"]{
  --grund:#000A32; --feld:#081541; --feld2:#0d1c50;
  --schrift:#ffffff; --schrift2:#c2cbe6; --schrift3:#8892b4;
  --linie:#17255c; --linie2:#22316e;
  --akzent:#FF3FC0; --akzent-weich:#2a0b34;
  --gut:#00d084; --gut-weich:#062a20;
  --warn:#fcb900; --warn-weich:#2e2308;
  --schlecht:#ff6b6b; --schlecht-weich:#340f12;
  --schatten:0 1px 2px rgba(0,0,0,.5), 0 4px 16px rgba(0,0,0,.35);
  --reihe-app:#ca0096;
  --reihe-bild:#097abe;
  --reihe-video:#0a8a57;
  --reihe-kunde:#8930fd;
  --gitterlinie:#152352;
  --bandfarbe:#0c1a4a;
}

*{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
body{
  margin:0; background:var(--grund); color:var(--schrift);
  font:400 15px/1.6 Exo,"Segoe UI",-apple-system,BlinkMacSystemFont,Helvetica,Arial,sans-serif;
  -webkit-font-smoothing:antialiased;
}
.zahl,.z{font-family:"Source Code Pro",ui-monospace,SFMono-Regular,Menlo,monospace;
  font-variant-numeric:tabular-nums}
a{color:var(--akzent); text-decoration:none}
a:hover{text-decoration:underline}
:focus-visible{outline:2px solid var(--akzent); outline-offset:2px; border-radius:4px}

/* ---------- Header ---------- */
.kopf{position:sticky; top:0; z-index:20; background:var(--grund);
  border-bottom:1px solid var(--linie); padding:10px 16px;
  display:flex; align-items:center; gap:14px; flex-wrap:wrap}
.kopf .marke{display:flex; align-items:center; gap:10px; min-width:0}
.kopf .marke .marke-logo{height:26px}
.kopf .wo{font-weight:600; letter-spacing:-.01em; white-space:nowrap}
.kopf .wo span{color:var(--schrift3); font-weight:400}
.kopf .luft{flex:1 1 auto}
.kopf .wer{color:var(--schrift3); font-size:13px; white-space:nowrap}

/* ---------- Building blocks ---------- */
.huelle{padding:16px; max-width:1400px; margin:0 auto}
.knopf{display:inline-block; background:var(--feld); color:var(--schrift);
  border:1px solid var(--linie2); border-radius:99px; padding:5px 13px;
  font-size:13.5px; cursor:pointer; white-space:nowrap}
.knopf:hover{border-color:var(--schrift3); text-decoration:none}
.knopf[aria-pressed="true"]{background:var(--akzent); border-color:var(--akzent);
  color:#fff; font-weight:500}
.knopf.stark{background:var(--akzent); border-color:var(--akzent); color:#fff;
  font-weight:600; padding:9px 18px; font-size:15px}

.kacheln{display:grid; gap:10px; margin:0 0 16px;
  grid-template-columns:repeat(auto-fit,minmax(150px,1fr))}
.kachel{background:var(--feld); border:1px solid var(--linie); border-radius:var(--rund);
  padding:11px 13px}
.kachel b{display:block; font:600 26px/1.15 "Source Code Pro",monospace;
  font-variant-numeric:tabular-nums; letter-spacing:-.02em}
.kachel span{display:block; color:var(--schrift3); font-size:11.5px;
  text-transform:uppercase; letter-spacing:.06em; margin-top:3px}
.kachel em{font-style:normal; color:var(--schrift3); font-size:11.5px}

.werkzeug{display:flex; gap:8px; flex-wrap:wrap; align-items:center; margin:0 0 14px}
.werkzeug input[type=search]{flex:1 1 190px; min-width:0; background:var(--feld);
  color:var(--schrift); border:1px solid var(--linie2); border-radius:99px;
  padding:7px 15px; font:inherit; font-size:14px}
.werkzeug .gruppe{display:flex; gap:6px; flex-wrap:wrap}

.perle{display:inline-block; border-radius:99px; padding:2px 9px; font-size:12px;
  font-weight:500; border:1px solid transparent; white-space:nowrap}
.perle.gut{background:var(--gut-weich); color:var(--gut); border-color:var(--gut)}
.perle.schlecht{background:var(--schlecht-weich); color:var(--schlecht); border-color:var(--schlecht)}
.perle.warn{background:var(--warn-weich); color:var(--warn); border-color:var(--warn)}
.perle.ruhig{background:var(--feld2); color:var(--schrift2); border-color:var(--linie2)}
.abw.gut{color:var(--gut)} .abw.schlecht{color:var(--schlecht)}

/* ---------- List: cards (phone) ---------- */
.karten{display:grid; gap:10px}
.karte{display:block; background:var(--grund); border:1px solid var(--linie);
  border-left:3px solid var(--linie2); border-radius:var(--rund);
  padding:12px 14px; color:inherit; box-shadow:var(--schatten)}
.karte:hover{border-color:var(--schrift3); text-decoration:none}
.karte.raus{border-left-color:var(--schlecht)}
.karte.drin{border-left-color:var(--gut)}
.karte .oben{display:flex; justify-content:space-between; align-items:baseline; gap:10px}
.karte .titel{font-weight:600; font-size:14.5px; word-break:break-all}
.karte .unten{display:flex; gap:14px; flex-wrap:wrap; margin-top:9px;
  color:var(--schrift2); font-size:13px}
.karte .unten b{font-family:"Source Code Pro",monospace; font-variant-numeric:tabular-nums;
  color:var(--schrift); font-weight:600}
.karte .unten i{font-style:normal; color:var(--schrift3); font-size:11px;
  text-transform:uppercase; letter-spacing:.05em; display:block}

/* ---------- List: table (desktop) ---------- */
.rahmen{display:none}
table{border-collapse:collapse; width:100%; font-size:13.5px}
th,td{padding:7px 10px; text-align:left; border-bottom:1px solid var(--linie);
  white-space:nowrap}
thead th{position:sticky; top:0; background:var(--grund); z-index:5;
  font-size:11px; text-transform:uppercase; letter-spacing:.06em;
  color:var(--schrift3); font-weight:600}
td.n{text-align:right; font-family:"Source Code Pro",monospace;
  font-variant-numeric:tabular-nums}
tbody tr:hover{background:var(--feld)}

@media (min-width:860px){
  .karten{display:none}
  .rahmen{display:block; overflow-x:auto; border:1px solid var(--linie);
    border-radius:var(--rund); background:var(--grund)}
  .huelle{padding:20px 24px}
}

/* ---------- Capture ---------- */
.zurueck{display:inline-block; margin:0 0 12px; color:var(--schrift3); font-size:13.5px}
h1{font-size:21px; margin:0 0 4px; letter-spacing:-.02em; text-wrap:balance;
  word-break:break-all}
h2{font-size:12px; text-transform:uppercase; letter-spacing:.07em;
  color:var(--schrift3); margin:24px 0 9px; font-weight:600}
.unterzeile{color:var(--schrift3); font-size:13.5px; margin:0 0 6px}
.paare{display:grid; gap:1px; background:var(--linie); border:1px solid var(--linie);
  border-radius:var(--rund); overflow:hidden}
.paar{display:flex; justify-content:space-between; gap:16px; align-items:baseline;
  background:var(--grund); padding:9px 13px}
.paar dt{color:var(--schrift2); font-size:13.5px}
.paar dd{margin:0; font-family:"Source Code Pro",monospace;
  font-variant-numeric:tabular-nums; font-weight:600; text-align:right}
.spalten{display:grid; gap:18px}
@media (min-width:860px){.spalten{grid-template-columns:1fr 1fr}}

/* ---------- Login ---------- */
.tor{min-height:100dvh; display:grid; place-items:center; padding:24px}
.tor form{width:100%; max-width:340px; display:grid; gap:13px}
.tor .marke-logo{height:34px}
.tor h1{font-size:17px; text-align:center; margin:0 0 2px}
.tor p.sub{text-align:center; color:var(--schrift3); font-size:13.5px; margin:0 0 8px}
.tor label{display:grid; gap:5px; font-size:13px; color:var(--schrift2)}
.tor input{background:var(--feld); color:var(--schrift); border:1px solid var(--linie2);
  border-radius:8px; padding:10px 13px; font:inherit; width:100%}
.tor .fehler{background:var(--schlecht-weich); color:var(--schlecht);
  border:1px solid var(--schlecht); border-radius:8px; padding:9px 12px; font-size:13.5px}
.leer{color:var(--schrift3); padding:36px 12px; text-align:center}

@media (prefers-reduced-motion:reduce){*{animation:none!important; transition:none!important}}

/* Language switcher: two compact buttons that do not bulk up the header. */
/* Flex on its own, not inherited: .gruppe carries display:flex only below
   .werkzeug — which does not exist in the header or on the login page, so the
   switcher's two forms used to sit one below the other. */
.sprache{display:flex; gap:4px; flex-wrap:nowrap; align-items:center}
.knopf.mini{padding:3px 9px; font-size:12px; letter-spacing:.04em; font-weight:600}


/* ---------- Overview chart ---------- */
.uebersicht-rahmen{margin:0 0 16px; padding:12px 14px 6px; background:var(--grund);
  border:1px solid var(--linie); border-radius:var(--rund); box-shadow:var(--schatten)}
.uebersicht-rahmen figcaption{display:flex; flex-wrap:wrap; gap:4px 12px;
  align-items:baseline; margin:0 0 8px}
.uebersicht-rahmen figcaption b{font-size:14px; font-weight:600}
.uebersicht-rahmen figcaption span{color:var(--schrift3); font-size:12.5px}

.legende{display:flex; flex-wrap:wrap; gap:6px 16px; margin:0 0 8px; padding:0;
  list-style:none; font-size:12.5px; color:var(--schrift2)}
.legende li{display:flex; align-items:center; gap:6px}
.legende i{width:11px; height:11px; border-radius:3px; display:block; flex:none}
.legende i.trendmarke{height:0; border-top:2px dashed var(--schrift3); border-radius:0}

/* The chart is wider than a phone. It scrolls inside its own box so that the
   page itself never scrolls sideways. */
.rollfeld{overflow-x:auto; overflow-y:hidden; -webkit-overflow-scrolling:touch;
  padding-bottom:4px}
.rollfeld svg{display:block}

svg.uebersicht .gitter line{stroke:var(--gitterlinie); stroke-width:1}
svg.uebersicht .band{fill:var(--bandfarbe)}
svg.uebersicht .null{stroke:var(--linie2); stroke-width:1}
svg.uebersicht .verlaufslinie{fill:none; stroke-width:1.5; stroke-opacity:.75;
  stroke-linejoin:round; stroke-linecap:round}
svg.uebersicht .trend{stroke:var(--schrift3); stroke-width:2; stroke-dasharray:5 4}
svg.uebersicht .punkt{stroke:var(--grund); stroke-width:2}   /* 2px ring so that
   overlapping points stay apart */
svg.uebersicht .ueber{stroke:var(--grund); stroke-width:1.5}
svg.uebersicht .saeule:hover{filter:brightness(1.12)}
svg.uebersicht text.achse{fill:var(--schrift3); font-size:10.5px;
  font-family:"Source Code Pro",ui-monospace,monospace; font-variant-numeric:tabular-nums}
svg.uebersicht text.achse.schwach{opacity:.72; font-size:9.5px}
svg.uebersicht text.feldname{fill:var(--schrift2); font-size:11px; font-weight:500}

/* Hover tracks: invisible until the pointer rests on them. */
svg.uebersicht .spur{fill:transparent; cursor:crosshair}
svg.uebersicht .spur:hover,
svg.uebersicht .spur.aktiv{fill:color-mix(in srgb, var(--akzent) 8%, transparent)}
svg.uebersicht .spur:focus-visible{outline:2px solid var(--akzent); outline-offset:-2px}

/* Tooltip box — follows the pointer, stays inside the frame. */
.uebersicht-rahmen{position:relative}
.hinweis{position:absolute; z-index:30; pointer-events:none;
  background:var(--grund); color:var(--schrift);
  border:1px solid var(--linie2); border-radius:9px; padding:9px 11px;
  box-shadow:0 4px 18px rgba(0,10,50,.18); font-size:12.5px; min-width:196px;
  max-width:280px}
.hinweis[hidden]{display:none}
.hinweis .ht{font-weight:600; font-size:13px; margin:0 0 1px; word-break:break-all}
.hinweis .hu{color:var(--schrift3); font-size:11.5px; margin:0 0 7px}
.hinweis .hs{color:var(--schrift3); font-size:10.5px; text-transform:uppercase;
  letter-spacing:.06em; margin:7px 0 3px; border-top:1px solid var(--linie);
  padding-top:5px}
.hinweis .hz{display:flex; align-items:center; gap:7px; padding:1.5px 0}
.hinweis .hz i{width:9px; height:9px; border-radius:2px; flex:none}
.hinweis .hz i.ohne{background:transparent}
.hinweis .hz span{color:var(--schrift2); flex:1 1 auto}
.hinweis .hz b{font-family:"Source Code Pro",ui-monospace,monospace;
  font-variant-numeric:tabular-nums; font-weight:600; white-space:nowrap}

/* Brief flash when new captures have arrived in the background. */
.uebersicht-rahmen.frisch{animation:frisch 1.2s ease-out}
@keyframes frisch{from{border-color:var(--akzent); box-shadow:0 0 0 3px var(--akzent-weich)}
  to{border-color:var(--linie); box-shadow:var(--schatten)}}
@media (prefers-reduced-motion:reduce){.uebersicht-rahmen.frisch{animation:none}}

/* Sequential number — the same one as on the chart's x axis. It does not
   refer to the capture itself but to its position in the current selection:
   filtering restarts the count, and the chart counts along. */
.nr{font-family:"Source Code Pro",ui-monospace,monospace; font-variant-numeric:tabular-nums;
  color:var(--schrift3); font-weight:600}
b.nr{display:inline-block; min-width:1.6em; padding:0 5px; margin-right:4px;
  background:var(--feld2); border-radius:5px; text-align:center; font-size:11.5px}
b.nr.gross{font-size:15px; padding:1px 8px; vertical-align:middle}
td.nr{color:var(--schrift3)}

/* ---------- Stage: panorama, overlay, video ---------- */
.buehne{margin:0 0 20px}
.reiter{display:flex; gap:6px; align-items:center; flex-wrap:wrap; margin:0 0 10px}
.reiter .luft{flex:1 1 auto}
.schalter{display:flex; align-items:center; gap:6px; font-size:13px;
  color:var(--schrift2); cursor:pointer}

/* The panorama is up to 31426 px tall — it scrolls inside its own box so
   that the page itself does not become endless. */
.tafel{max-height:70vh; overflow:auto; background:var(--feld);
  border:1px solid var(--linie); border-radius:var(--rund);
  -webkit-overflow-scrolling:touch}
/* The markers are positioned against the image, not the scrolling box. */
.tafel-innen{position:relative}
.tafel img{display:block; width:100%; height:auto}
.tafel .marker{position:absolute; inset:0; pointer-events:none}
.tafel .marker i{position:absolute; width:100%; left:0!important;
  border-top:1px solid var(--akzent); opacity:.75}
.tafel .marker i::after{content:""; position:absolute; right:4px; top:-3px;
  width:6px; height:6px; border-radius:50%; background:var(--akzent)}
.buehne .fach[hidden]{display:none}
.buehne video{width:100%; max-height:70vh; background:#000;
  border:1px solid var(--linie); border-radius:var(--rund); display:block}

/* ---------- Narrow form pages ---------- */
.huelle.schmal{max-width:440px}
.formular{display:grid; gap:13px; margin:14px 0 0}
.formular label{display:grid; gap:5px; font-size:13px; color:var(--schrift2)}
.formular input{background:var(--feld); color:var(--schrift);
  border:1px solid var(--linie2); border-radius:8px; padding:10px 13px;
  font:inherit; width:100%}
.formular .fehler{background:var(--schlecht-weich); color:var(--schlecht);
  border:1px solid var(--schlecht); border-radius:8px; padding:9px 12px; font-size:13.5px}
.hinweistext{color:var(--schrift3); font-size:12.5px; margin:0}
a.wer{color:var(--schrift3); text-decoration:none; border-bottom:1px dotted var(--linie2)}
a.wer:hover{color:var(--akzent); text-decoration:none}

/* Band per capture: just visible enough to hold the group together, and faint
   enough not to distort the series colors. */
svg.uebersicht .streifen rect{fill:var(--schrift); opacity:.035}

/* Language switcher on the login page: top right, as in the header of the
   signed-in pages — the same corner, so nobody has to look for it twice. It
   is layered over the login box rather than placed before it: the input is
   meant to stay centered. */
.tor{position:relative}
.tor-sprache{position:absolute; top:14px; right:16px; z-index:5}

/* ---------- Loading state of the stage ----------
   As long as image or video are not there, the box is kept empty and a word
   is shown. The markers stay invisible: over an empty box they would all
   stack on top of each other at the upper edge and look like a bug. */
/* While loading, the box stays flat. Otherwise the image reserves its full
   height — up to 31426 px — via width/height, and the centered loading word
   would sit 15000 px below the visible window. All one would see is an empty
   gray area, and the page would be taken for broken. */
.tafel-innen.laedt{min-height:260px; max-height:260px; overflow:hidden}
.videofeld.laedt{min-height:260px}
.tafel-innen.laedt img,
.videofeld.laedt video{opacity:0}
.tafel-innen.laedt .marker{visibility:hidden}
.tafel-innen .ladewort,
.videofeld .ladewort,
.tafel-innen .ladefehler,
.videofeld .ladefehler{position:absolute; inset:0; margin:0;
  display:flex; align-items:center; justify-content:center; gap:10px;
  color:var(--schrift3); font-size:13.5px; text-align:center; padding:20px}
.tafel-innen:not(.laedt) .ladewort,
.videofeld:not(.laedt) .ladewort{display:none}
.ladefehler[hidden]{display:none!important}
.ladefehler{color:var(--schlecht)}
.videofeld{position:relative; min-height:220px}

/* The dot left of the word spins so that it stays recognizable that
   something is happening — with a 25 MB panorama it takes a while. */
.ladewort::before{content:""; width:14px; height:14px; flex:none;
  border:2px solid var(--linie2); border-top-color:var(--akzent);
  border-radius:50%; animation:dreht .8s linear infinite}
@keyframes dreht{to{transform:rotate(360deg)}}
@media (prefers-reduced-motion:reduce){
  .ladewort::before{animation:none; border-top-color:var(--linie2)}
}
/* Image and video fade in softly as soon as they are there. */
.tafel-innen img,.videofeld video{transition:opacity .25s ease}
@media (prefers-reduced-motion:reduce){
  .tafel-innen img,.videofeld video{transition:none}
}

/* Which collection is being looked at. It stands above everything it
   changes, and reads as a statement of place rather than as one more filter:
   the count belongs to the name, and a collection that cannot be written to
   says so once, here, instead of on every control it hides. */
.satzwahl{margin:0 0 14px}
.satzwahl .knopf b{margin-left:7px; color:var(--schrift3); font-weight:600}
.satzwahl .knopf[aria-pressed="true"] b{color:inherit; opacity:.75}
.nurlesend{display:inline-flex; align-items:center; gap:6px; margin:0;
  font-size:12px; color:var(--schrift3)}
.nurlesend::before{content:""; width:6px; height:6px; border-radius:50%;
  background:var(--schrift3); flex:none}

/* Taking a recording out of the view. Deliberately plain and set apart from
   the numbers above it: it is an administrative act, not part of reading the
   measurement. */
.verstecken{display:flex; flex-wrap:wrap; align-items:center; gap:8px;
  margin:18px 0 0; padding:12px 14px; border:1px dashed var(--linie);
  border-radius:var(--rund); background:var(--feld2)}
.verstecken label{font-size:12px; font-weight:600; letter-spacing:.04em;
  text-transform:uppercase; color:var(--schrift2)}
.verstecken input{flex:1 1 180px; min-width:0}
.verstecken .hinweis{flex:1 1 100%; font-size:12px; color:var(--schrift3)}

/* The direction pair. The arrow carries the meaning, the word explains it —
   and the word steps aside on a narrow screen, where the toolbar has to fit
   a filter row, a view switch and the sorting keys as well. */
.richtungswahl .knopf{gap:5px}
@media (max-width:640px){
  .richtungswahl .wort{position:absolute; width:1px; height:1px; padding:0;
    margin:-1px; overflow:hidden; clip-path:inset(50%); white-space:nowrap}
}

/* ---------- Tile view ---------- */
.kachelraster{display:grid; gap:14px; margin:0 0 18px;
  grid-template-columns:repeat(auto-fill,minmax(210px,1fr))}
.bildkachel{display:block; color:inherit; background:var(--grund);
  border:1px solid var(--linie); border-left:3px solid var(--linie2);
  border-radius:var(--rund); overflow:hidden; box-shadow:var(--schatten)}
.bildkachel:hover{border-color:var(--schrift3); text-decoration:none}
.bildkachel.raus{border-left-color:var(--schlecht)}
.bildkachel.drin{border-left-color:var(--gut)}
.bildfeld{position:relative; background:var(--feld2); aspect-ratio:3/2}
.bildfeld img{display:block; width:100%; height:100%; object-fit:cover}
/* A recording without a panorama keeps the shape of a tile — an empty
   frame reads as "nothing was stitched here", a broken image reads as a
   fault in the application. */
.ohnebild{position:absolute; inset:0; display:flex; align-items:center;
  justify-content:center; font-size:12px; color:var(--schrift3);
  background:repeating-linear-gradient(135deg,transparent,transparent 7px,
    var(--linie) 7px,var(--linie) 8px)}
/* Number and deviation sit on the image — they belong to the capture, not
   to the caption, and are readable without a change of gaze. */
.bildfeld .nr{position:absolute; top:7px; left:7px; margin:0;
  background:var(--grund); box-shadow:var(--schatten)}
.bildfeld .perle{position:absolute; top:7px; right:7px;
  background:var(--grund); box-shadow:var(--schatten)}
.untertitel{padding:9px 11px 11px; display:grid; gap:3px}
.untertitel .titel{font-weight:600; font-size:13px; word-break:break-all}
/* Up to four numbers on a tile that is 150px wide on a phone. Flex with a
   wrap keeps them readable instead of running off the edge, and the row gap
   is tighter than the column gap so a wrapped line still reads as one
   group. */
.untertitel .zahlen{display:flex; flex-wrap:wrap; gap:1px 9px;
  color:var(--schrift3); font-size:12px; line-height:1.5}
.untertitel .zahlen b{font-family:"Source Code Pro",ui-monospace,monospace;
  font-variant-numeric:tabular-nums; color:var(--schrift); font-weight:600}
.werkzeug .luft{flex:1 1 auto}
@media (max-width:859px){ .kachelraster{grid-template-columns:repeat(auto-fill,minmax(150px,1fr))} }

/* Tiles grouped by recording day. A day is a shift at the machine: what was
   recorded on one day shares its conditions, and in one uninterrupted grid
   the step from one day to the next passed unnoticed. The heading is a
   divider, not a decoration — it carries the date and how many recordings
   belong to it. */
.tage{display:grid; gap:24px}
.tagblock{display:grid; gap:10px}
.tagblock .kachelraster{margin:0}
.tagkopf{display:flex; align-items:baseline; gap:12px; margin:0;
  font-size:12px; font-weight:600; letter-spacing:.06em; text-transform:uppercase;
  color:var(--schrift2)}
/* The rule fills whatever the date and the count leave over. */
.tagkopf::after{content:""; flex:1 1 auto; height:1px; background:var(--linie)}
.tagkopf .datum{font-variant-numeric:tabular-nums}
.tagkopf .anzahl{order:3; font-weight:500; text-transform:none; letter-spacing:0;
  color:var(--schrift3); white-space:nowrap}

/* ---------- Tiles with a detail panel beside them ----------
   The panel only appears once a recording is picked, so the tiles get the
   full width until then. Below 1100 px there is no room for two columns:
   the panel then sits above the tiles as a block. */
.werkfeld{display:grid; gap:16px; align-items:start}
@media (min-width:1100px){
  .werkfeld:has(.seitenblick:not([hidden])){grid-template-columns:1fr 340px}
  .werkfeld .seitenblick{position:sticky; top:60px; max-height:calc(100vh - 76px);
    overflow:auto}
}
.seitenblick{position:relative; background:var(--grund); border:1px solid var(--linie);
  border-radius:var(--rund); padding:14px; box-shadow:var(--schatten)}
.seitenblick[hidden]{display:none}
.seitenblick .zu{position:absolute; top:8px; right:8px; z-index:2;
  width:26px; height:26px; padding:0; line-height:1; font-size:15px}
.blick-leer{color:var(--schrift3); text-align:center; padding:28px 8px; font-size:13.5px}
.blick-vorschau{display:block; position:relative; border-radius:8px; overflow:hidden; margin:0 0 10px;
  background:var(--feld2); aspect-ratio:3/2}
.blick-vorschau img{display:block; width:100%; height:100%; object-fit:cover}
.blick-kopf{display:flex; align-items:center; gap:2px; margin:0 0 2px}
.blick-name{font-weight:600; font-size:13.5px; word-break:break-all}
.blick-perlen{display:flex; gap:6px; flex-wrap:wrap; margin:8px 0 0}
.seitenblick h2{margin:16px 0 6px}
.seitenblick .paar{padding:6px 10px}
.seitenblick .knopf.stark{width:100%; text-align:center; margin-top:14px}
/* The second button in the panel — into the counting surface — sits
   under the first and is the same width, so the two read as one pair. */
.seitenblick .knopf{width:100%; text-align:center}
/* The chosen tile keeps a visible ring, so it is clear what the panel shows. */
.bildkachel.gewaehlt{outline:2px solid var(--akzent); outline-offset:-1px}

/* Theme switch: one button, three states. The glyph is written by thema.js. */
.knopf.thema{width:30px; padding:3px 0; text-align:center; font-size:14px; line-height:1.3}
.knopf.thema[data-stand="hell"]{background:var(--warn-weich); border-color:var(--warn); color:var(--warn)}
.knopf.thema[data-stand="dunkel"]{background:var(--akzent-weich); border-color:var(--akzent); color:var(--akzent)}
.tor-sprache{gap:6px; align-items:center}

/* ---------- Counting surface ----------
 * The counting tool (Tool/zaehltool.py) is served in a frame under the
 * header. So the page must not scroll itself: the frame gets whatever height
 * is left over, and the scrolling happens inside it, where the image stage
 * expects it. A fixed calc(100vh - 47px) would be wrong the moment the header
 * wraps onto a second line, so this is a flex column instead — the header
 * keeps its natural height, the stage takes the rest.
 *
 * The header is static here rather than sticky: in a column that does not
 * scroll there is nothing for it to stick to, and position:sticky would only
 * cost it a stacking context.
 */
body.zaehlseite{display:flex; flex-direction:column; height:100dvh; overflow:hidden}
body.zaehlseite .kopf{position:static; flex:0 0 auto}
.zaehlleiste{flex:0 0 auto; display:flex; align-items:baseline; gap:12px;
  padding:6px 16px; border-bottom:1px solid var(--linie); min-width:0}
.zaehlleiste .zurueck{margin:0}
.zaehlname{color:var(--schrift3); font-size:13.5px; overflow:hidden;
  text-overflow:ellipsis; white-space:nowrap}
.zaehlbuehne{flex:1 1 auto; min-height:0}
.zaehlbuehne iframe{display:block; width:100%; height:100%; border:0}
.fehlerfeld{background:var(--schlecht-weich); color:var(--schlecht);
  border:1px solid var(--schlecht); border-radius:var(--rund);
  padding:10px 14px; margin:0 0 10px; max-width:60em}

/* ---------- Logo ----------
   Three states, the same pattern as every colour token: the bare rule is
   light, the media query covers "system dark" unless light was chosen
   explicitly, and the explicit dark choice wins in the other direction.

   This has to follow data-thema, not just prefers-color-scheme. The negative
   version fills "CEYE" with #fff — on a white ground it simply disappears,
   which is what happened when the theme switch was set to light on a dark
   system. */
.marke-logo{display:block; width:105px; height:26px; flex:none;
  background:url("logo.6bf5f903bea4.svg") no-repeat left center; background-size:contain}
.marke-logo.gross{width:137px; height:34px; margin:0 auto 6px}
@media (prefers-color-scheme:dark){
  :root:not([data-thema="hell"]) .marke-logo{background-image:url("logo-negativ.915b47bc1a0b.svg")}
}
:root[data-thema="dunkel"] .marke-logo{background-image:url("logo-negativ.915b47bc1a0b.svg")}

/* ---------- Editable customer count ---------- */
.kundenzeile dd{display:flex; align-items:center; gap:6px}
.kundenform{display:flex; align-items:center; gap:5px; margin:0}
.kundenform input{width:5.5em; background:var(--feld); color:var(--schrift);
  border:1px solid var(--linie2); border-radius:6px; padding:3px 7px;
  font:inherit; font-family:"Source Code Pro",ui-monospace,monospace;
  font-variant-numeric:tabular-nums; text-align:right}
.kundenform input:focus{border-color:var(--akzent); outline:none}
/* The tick says the number was entered here rather than read from the file —
   otherwise there is no way to tell the two apart. */
.quelle{color:var(--gut); font-weight:700; cursor:help}
.kundenmeldung{margin:8px 0 0; padding:6px 9px; border-radius:6px; font-size:12.5px}
.kundenmeldung[hidden]{display:none}
.kundenmeldung.gut{background:var(--gut-weich); color:var(--gut)}
.kundenmeldung.schlecht{background:var(--schlecht-weich); color:var(--schlecht)}
/* Counts lead to the recording: a number that looks wrong is a reason to
   watch it, and that should be one click. */
a.zumvideo{color:inherit; text-decoration:none; border-bottom:1px dotted var(--linie2)}
a.zumvideo:hover{color:var(--akzent); border-bottom-color:var(--akzent); text-decoration:none}

/* The chart's tracks are links to the recording. */
svg.uebersicht a{cursor:pointer}
svg.uebersicht a:focus-visible .spur{fill:color-mix(in srgb, var(--akzent) 14%, transparent);
  outline:2px solid var(--akzent); outline-offset:-2px}
.hinweis .hklick{margin:7px 0 0; padding-top:6px; border-top:1px solid var(--linie);
  color:var(--akzent); font-size:11.5px}

/* ---------- Modelldiagnose ---------- */
/* Woran sich eine Aufnahme messen laesst: von Hand gezaehlt (kraeftiges
   Gruen), aus Labels abgeleitet (Gruen), gar nicht (Rot). Die Farbe sitzt am
   linken Rand, damit sie beim Ueberfliegen der Liste auffaellt, ohne die
   Zahlen einzufaerben. */
tr.art-test td:first-child{box-shadow:inset 3px 0 0 var(--gut); font-weight:600}
tr.art-gelabelt td:first-child{box-shadow:inset 3px 0 0 var(--gut); opacity:.85}
tr.art-ohne td:first-child{box-shadow:inset 3px 0 0 var(--schlecht)}
.bildkachel.art-test{border-left-color:var(--gut); border-left-width:4px}
.bildkachel.art-gelabelt{border-left-color:var(--gut)}
.bildkachel.art-ohne{border-left-color:var(--schlecht)}
.artlegende i{width:11px; height:11px; border-radius:3px; display:block}
.artlegende i.art-test{background:var(--gut); box-shadow:0 0 0 2px var(--gut-weich)}
.artlegende i.art-gelabelt{background:var(--gut); opacity:.6}
.artlegende i.art-ohne{background:var(--schlecht)}
.reihenschalter{display:flex; align-items:center; gap:7px; text-decoration:none;
  color:inherit; padding:2px 7px; margin:-2px -7px; border-radius:99px}
.reihenschalter:hover{background:var(--feld2); text-decoration:none}
.reihenschalter.aus{opacity:.4}
.reihenschalter.aus i{background:var(--linie2) !important}
.blick-modelle{margin:10px 0 0; padding-top:8px; border-top:1px solid var(--linie)}
.blick-modell{display:flex; align-items:center; gap:7px; font-size:13px;
  padding:2px 0}
.blick-modell .titel{flex:1 1 auto; color:var(--schrift2); font-weight:400}
.blick-modell .abw{font-size:12px}
.zaehlstand{display:inline-block; font-size:11.5px; padding:2px 8px;
  border-radius:99px; background:var(--feld2); color:var(--schrift3)}
.zaehlstand.fertig{background:var(--gut-weich); color:var(--gut)}
.zaehlstand.laeuft{background:var(--warn-weich); color:var(--warn)}
.knopf.mini{padding:3px 10px; font-size:12px}
table.zeilen td a{color:inherit; text-decoration:none}
table.zeilen td a:hover{text-decoration:underline}
.diagrammfeld{margin:0 0 26px}
.diagrammfeld > h2{display:flex; align-items:baseline; gap:12px; flex-wrap:wrap}
.diagrammfeld .wieviele{font-size:12.5px; font-weight:400; color:var(--schrift3)}

/* Grundform des Farbpunkts. Er steht immer NEBEN einer Beschriftung, nie
   allein — Farbe ist hier die zweite Auskunft, nicht die einzige. */
.punkt{display:inline-block; width:9px; height:9px; border-radius:2px;
  margin-right:7px; vertical-align:baseline; flex:none}
/* Reihenfarben werden wiederverwendet, statt eine zweite Palette einzufuehren:
   sie sind bereits gegen beide Flaechen und gegen Farbfehlsichtigkeit
   geprueft, und die Farbe gehoert damit weiterhin zur Sache, nicht zum Rang. */
.punkt.panopticdl{background:var(--reihe-bild)}
.punkt.mask2former{background:var(--reihe-app)}
.punkt.solov2{background:var(--reihe-video)}
.punkt.detrseg{background:var(--reihe-kunde)}
.punkt.zuwenig{background:var(--warn)}
.punkt.zuviel{background:var(--reihe-kunde)}
td.zuwenig{color:var(--warn)}
td.zuviel{color:var(--reihe-kunde)}

/* Wege im Kopf. Erst ab zwei Seiten sinnvoll — vorher stand dort nichts. */
.wege{display:flex; gap:2px; margin-left:14px}
.wege a{padding:5px 11px; border-radius:99px; font-size:13.5px; font-weight:500;
  color:var(--schrift2); text-decoration:none; white-space:nowrap}
.wege a:hover{background:var(--feld2); color:var(--schrift)}
.wege a[aria-current="page"]{background:var(--akzent); color:#fff}
@media (max-width:640px){ .wege{margin-left:6px} .wege a{padding:5px 8px; font-size:12.5px} }

.diagnosekopf{margin:0 0 22px}
.diagnosekopf h1{font-size:clamp(23px,3.4vw,31px); margin:0 0 6px; font-weight:600;
  letter-spacing:-.01em; text-wrap:balance}
.diagnosekopf p{margin:0; color:var(--schrift2); max-width:62ch}
.diagnosekopf .wann{display:inline-block; margin-left:8px; font-size:12.5px;
  color:var(--schrift3); white-space:nowrap}

/* Der Befund steht ueber der Tabelle und ist deshalb auch optisch schwerer. */
.befund{background:var(--warn-weich); border:1px solid var(--linie);
  border-left:3px solid var(--warn); border-radius:var(--rund);
  padding:16px 18px; margin:0 0 28px}
.befund h2{font-size:17px; margin:0 0 7px; font-weight:600}
.befund p{margin:0 0 9px; max-width:70ch}
.befund p:last-child{margin-bottom:0}

.feldgruppe{margin:0 0 34px}
.feldgruppe > h2{font-size:19px; font-weight:600; margin:0 0 4px;
  padding-bottom:7px; border-bottom:1px solid var(--linie)}
.unterzeile{color:var(--schrift3); font-size:14px; margin:8px 0 14px; max-width:70ch}
.fussnote{color:var(--schrift3); font-size:13px; margin:10px 0 0; max-width:70ch}

.rolle{overflow-x:auto; -webkit-overflow-scrolling:touch}
table.diagnose{border-collapse:collapse; width:100%; min-width:600px; font-size:14px}
table.diagnose th,table.diagnose td{text-align:right; padding:7px 10px;
  border-bottom:1px solid var(--linie); white-space:nowrap}
table.diagnose th:first-child,table.diagnose td:first-child{text-align:left;
  white-space:normal; min-width:200px}
table.diagnose thead th{font-size:11.5px; letter-spacing:.04em; text-transform:uppercase;
  color:var(--schrift3); font-weight:600; border-bottom:1px solid var(--linie2)}
table.diagnose tbody tr:hover{background:var(--feld)}
table.diagnose td.stark{font-weight:600}
table.diagnose tr.beste td{background:var(--gut-weich)}
table.diagnose tr.beste td:first-child{box-shadow:inset 3px 0 0 var(--gut)}
table.diagnose tr.geraet td{color:var(--schrift2); font-style:italic}
table.diagnose td i{font-size:12px; color:var(--schrift3); font-style:normal;
  margin-left:6px}

.vorbehalt{background:var(--feld); border:1px solid var(--linie);
  border-radius:var(--rund); padding:14px 17px; margin:16px 0 0}
.vorbehalt h3{font-size:14px; margin:0 0 8px; font-weight:600}
.vorbehalt ul{margin:0; padding-left:19px; font-size:14px; color:var(--schrift2)}
.vorbehalt li{margin:0 0 6px}
.vorbehalt li:last-child{margin-bottom:0}

.staende{display:grid; gap:10px; grid-template-columns:repeat(auto-fit,minmax(190px,1fr));
  margin:0 0 18px}
.stand{background:var(--feld); border:1px solid var(--linie);
  border-radius:var(--rund); padding:12px 14px}
.stand b{font-size:12px; color:var(--schrift3); letter-spacing:.06em}
.stand span{display:inline-block; font-size:24px; font-weight:600; margin:2px 6px 0 0;
  font-variant-numeric:tabular-nums}
.stand em{font-size:13px; color:var(--schrift2); font-style:normal}
.stand i{display:block; font-size:12px; color:var(--schrift3); font-style:normal;
  margin-top:5px; padding-top:5px; border-top:1px solid var(--linie)}

.hebel{display:grid; gap:12px; grid-template-columns:repeat(auto-fit,minmax(230px,1fr));
  margin:0 0 14px}
.hebel > div{background:var(--gut-weich); border:1px solid var(--linie);
  border-left:3px solid var(--gut); border-radius:var(--rund); padding:14px 16px}
.hebel b{display:block; font-size:24px; font-weight:600; letter-spacing:-.01em}
.hebel span{display:block; font-size:13px; color:var(--schrift2); margin-top:4px}

/* Die Zeilentabelle: die drei von Hand gemachten Spalten stehen optisch
   zusammen, weil sie der Massstab sind und nicht eine Messung unter vielen. */
table.zeilen td.hand{background:var(--feld)}
table.zeilen th .wann{display:block; font-weight:400; font-size:10.5px;
  color:var(--schrift3); letter-spacing:0; text-transform:none}
table.zeilen td:first-child{min-width:170px; font-size:12.5px;
  color:var(--schrift2)}

ol.schritte{counter-reset:s; list-style:none; padding:0; margin:0; display:grid; gap:11px}
ol.schritte li{counter-increment:s; position:relative; background:var(--feld);
  border:1px solid var(--linie); border-radius:var(--rund);
  padding:13px 16px 13px 44px}
ol.schritte li::before{content:counter(s); position:absolute; left:15px; top:13px;
  font-family:"Source Code Pro",ui-monospace,monospace; font-size:13px;
  font-weight:600; color:var(--schrift3)}
ol.schritte b{display:block; margin-bottom:3px; font-size:14.5px}
ol.schritte p{margin:0; font-size:14px; color:var(--schrift2)}

/* A labelled sequence that never reached the recordings collection. It has a
   count and a reference, so it belongs in the list — but no images behind it,
   so nothing here is a link and the tile says why instead of showing a gap. */
.bildkachel.ohnebild{cursor:default}
.bildkachel.ohnebild .bildfeld{display:flex; align-items:center;
  justify-content:center; background:var(--flaeche2); min-height:150px}
.kein_bild{color:var(--schrift3); font-size:.8rem; letter-spacing:.04em}

/* Marks a row the model was fitted to. It has to be quiet enough not to
   compete with the numbers and loud enough that nobody reads a training image
   as evidence of accuracy. */
.trainmarke{display:inline-block; margin-left:.45rem; padding:0 .32rem;
  border:1px solid var(--linie2); border-radius:3px; font-size:.66rem;
  letter-spacing:.03em; color:var(--schrift3); white-space:nowrap;
  vertical-align:middle}
.bildkachel .trainmarke{margin-left:.3rem; font-size:.6rem}

/* A derived count says where it comes from. The number stays the number; the
   source is a quiet tag beside it that leads to the polygons themselves. */
.herkunft{text-decoration:none; color:inherit; white-space:nowrap}
.herkunft:hover .quelle,.herkunft:focus-visible .quelle{
  border-color:var(--akzent); color:var(--akzent)}
.quelle{display:inline-block; margin-left:.3rem; padding:0 .25rem;
  border:1px solid var(--linie2); border-radius:3px; font-size:.6rem;
  letter-spacing:.05em; color:var(--schrift3); vertical-align:middle}
.quellezeile{color:var(--schrift3); font-size:.7rem}

/* The CVAT button sits in the action column, where the actions are. It is an
   outbound link, so it stays quieter than the counting button beside it. */
.knopf.cvatknopf{border-style:dashed}
/* A count that came from polygons, not from someone counting. The underline is
   the invitation to hover; the tooltip says how much was drawn by hand. */
.ausLabels{border-bottom:1px dotted var(--linie2); cursor:help}

/* A count whose labelling was never accepted. The number stays — hiding it
   would be its own distortion — but it must not read as a reference. */
.ausLabels.unsicher{border-bottom-style:none; color:var(--warn);
  text-decoration:underline; text-decoration-style:wavy;
  text-decoration-color:var(--warn); text-underline-offset:3px}
.labelmarke{display:inline-block; margin-left:.35rem; padding:0 .3rem;
  border:1px solid color-mix(in srgb, var(--warn) 45%, transparent);
  background:var(--warn-weich); border-radius:3px; font-size:.62rem;
  letter-spacing:.03em; color:var(--warn); white-space:nowrap;
  vertical-align:middle}

/* The hand-counted recordings. In the merged tab they are the only rows whose
   reference nobody derived from anything, so they get a name and not just an
   edge colour. */
.testmarke{display:inline-block; margin-left:.45rem; padding:0 .32rem;
  border:1px solid color-mix(in srgb, var(--gut) 45%, transparent);
  background:var(--gut-weich); border-radius:3px; font-size:.66rem;
  letter-spacing:.03em; color:var(--gut); white-space:nowrap;
  vertical-align:middle; font-weight:600}
.bildkachel .testmarke{margin-left:.3rem; font-size:.6rem}
