/* Platz fuer die Bildlaufleiste steht immer, auch wo die Seite kurz genug ist, um ohne
   auszukommen. Sonst ist eine kurze Seite rund 15 px breiter als eine lange, und beim Wechseln
   springt der Inhalt -- was sich wie unterschiedlich breite Seiten liest, obwohl alle dieselbe
   Breitenregel haben. */
html {
    scrollbar-gutter: stable;
}

/* =============================================================================
   Backoffice — light theme and shell
   =============================================================================
   Loaded AFTER the portal's stylesheets, and it overrides ten values.

   The portal's css/styles.css is built in two layers: a small set of source tokens (--bg, --ink,
   --line, --accent) and a long list of aliases mapped onto them (--text-primary,
   --dark-bg-secondary, --card-background, …). Every rule in every portal stylesheet reads an
   alias. So turning the whole thing light means redefining the ten sources, not touching the
   hundreds of rules — and it means a new portal component arrives here already themed.

   The alias NAMES stay dark-sounding (--dark-bg-secondary now holds white). Renaming them would
   mean editing two applications to fix a word, and the word is not what anybody reads.

   Why light at all: this is a tool somebody sits in front of for a working day, usually in an
   office, often beside a spreadsheet. The portal is a product surface and can be dramatic; this
   one should be quiet.
   ============================================================================= */

:root {
    /* ── Die Hoehen der Huelle ────────────────── */
    /* Header und Footer sind beide aus dem Fluss genommen, also muss jedes Element, das sich
       dazwischen aufspannt, ihre Hoehe kennen. Als Variable, weil sie sonst an drei Stellen
       stuende und beim naechsten Umbau an zweien geaendert wuerde — die Schiene hatte genau
       deshalb `top: 0` und lag unter dem Header. */
    --bo-header-height: 58px;
    --bo-footer-height: 46px;

    /* ── The ten sources ─────────────────────── */
    --bg:         #F4F5F7;
    --bg-2:       #FFFFFF;
    --ink:        #16181D;
    --ink-2:      #4E5560;
    --ink-3:      #7A828E;
    --line:       rgba(16, 24, 40, 0.10);
    --line-2:     rgba(16, 24, 40, 0.18);

    /* The portal's cyan is tuned for a near-black ground; on white it fails contrast for text
       and for a focus ring. Same hue, taken down until it carries. */
    --accent:     #0B7C8E;
    --accent-2:   #0997AE;
    --accent-dim: #D6EEF2;

    /* ── The two aliases that are literals, not var() references ──────────────
       Everything else in styles.css maps onto the sources above and follows automatically. These
       two carry their own colour, so they have to be named here or they stay dark. */
    --dark-bg-tertiary: #EDEFF3;
    --hover-color:      rgba(11, 124, 142, 0.08);

    /* Status colours keep their meaning but need more weight on white than on black. */
    --success-color: #1B7F3B;
    --danger-color:  #C0392F;
    --warning-color: #9A6A00;
    --info-color:    #10707F;
}

body {
    background: var(--bg);
    color: var(--ink);

    /* Column, so the footer can be pushed to the bottom of a short page. The start page is the
       short one — nothing but a card of links — and a footer floating halfway up it reads as
       the end of the content rather than the end of the page. */
    min-height: 100vh;
    display: flex;
    flex-direction: column;
}

.bo-page {
    flex: 1 0 auto;
}

/* =============================================================================
   Shell
   ============================================================================= */

.bo-header {
    position: sticky;
    top: 0;
    z-index: 50;
    display: flex;
    align-items: center;
    gap: 20px;
    padding: 0 24px;
    height: var(--bo-header-height);
    border-bottom: 1px solid var(--line);
    background: var(--bg-2);
    box-shadow: 0 1px 3px rgba(16, 24, 40, 0.06);
}

/* The footer. Deliberately quiet — one line of facts, no navigation of its own beyond two links
   that leave the tool. It carries no border-radius and no card: it is the edge of the page.

   Auf Desktop und Tablet unten angedockt: die Zeile nennt Umgebung und angemeldete Person, und
   das sind zwei Angaben, die man beim Arbeiten sehen koennen will, ohne bis ans Ende einer langen
   Liste zu scrollen. Auf dem Telefon nicht — dort ist der Bildschirm zu niedrig, um 46 Pixel
   dauerhaft an etwas zu vergeben, das man einmal am Tag liest. Siehe die Media Query unten. */
.bo-footer {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
    padding: 14px 24px;
    border-top: 1px solid var(--line);
    background: var(--bg-2);
    color: var(--ink-3);
    font-size: 0.78rem;
}

.bo-foot-brand {
    font-family: var(--font-display);
    letter-spacing: 0.04em;
    color: var(--ink-2);
}

.bo-footer strong {
    color: var(--ink-2);
    font-weight: 600;
}

.bo-foot-sep {
    color: var(--line-2);
}

.bo-foot-api {
    font-family: var(--font-mono);
}

/* Pushes the two links to the far edge without a second flex container. */
.bo-foot-spacer {
    flex: 1 1 auto;
}

.bo-footer a {
    color: var(--ink-2);
    text-decoration: none;
    border-bottom: 1px solid var(--line-2);
}

.bo-footer a:hover {
    color: var(--accent);
    border-bottom-color: var(--accent);
}

.bo-foot-warn {
    color: var(--warning-color);
    font-weight: 600;
}

.bo-brand {
    display: inline-flex;
    align-items: baseline;
    gap: 8px;
    font-family: var(--font-display);
    font-size: 1.3rem;
    letter-spacing: 0.04em;
    color: var(--ink);
    text-decoration: none;
}

.bo-brand span {
    font-family: var(--font-body);
    font-size: 0.68rem;
    font-weight: 700;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--accent);
}

.bo-nav {
    display: flex;
    align-items: center;
    gap: 2px;
    flex: 1;
}

.bo-nav a {
    padding: 7px 12px;
    border-radius: 6px;
    color: var(--ink-2);
    font-size: 0.85rem;
    font-weight: 500;
    text-decoration: none;
}

.bo-nav a:hover {
    background: var(--hover-color);
    color: var(--accent);
}

.bo-nav a.active {
    background: var(--accent-dim);
    color: var(--accent);
    font-weight: 600;
}

/* An environment that is not production has to be unmistakable: this tool edits customer records,
   and "which system am I in" must never be a guess. */
.bo-env {
    padding: 3px 9px;
    border-radius: 4px;
    background: var(--warning-color);
    color: #fff;
    font-family: var(--font-mono);
    font-size: 0.66rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

.bo-env--prod {
    background: var(--danger-color);
}

.bo-user {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    font-size: 0.8rem;
    color: var(--ink-2);
    white-space: nowrap;
}

/* =============================================================================
   The global filter — the thing that makes this a support tool
   =============================================================================
   Its whole job is to be impossible to forget: while it is set, every list on every page is
   narrowed, and somebody who has forgotten will read a short list as the truth. So it is in the
   header, it is coloured, and clearing it is one click away and always visible.
   ============================================================================= */

.bo-filter {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 5px 8px 5px 12px;
    border: 1px solid var(--line-2);
    border-radius: 20px;
    background: var(--bg);
    font-size: 0.8rem;
    color: var(--ink-2);
    cursor: pointer;
}

.bo-filter:hover {
    border-color: var(--accent);
    color: var(--accent);
}

.bo-filter--active {
    border-color: var(--accent);
    background: var(--accent-dim);
    color: var(--accent);
    font-weight: 600;
}

.bo-filter-clear {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 18px;
    height: 18px;
    padding: 0;
    border: none;
    border-radius: 50%;
    background: rgba(11, 124, 142, 0.18);
    color: inherit;
    font-size: 0.7rem;
    line-height: 1;
    cursor: pointer;
}

.bo-filter-clear:hover {
    background: var(--danger-color);
    color: #fff;
}

/* =============================================================================
   Page
   ============================================================================= */

.bo-page {
    max-width: 1500px;
    margin: 0 auto;
    padding: 26px 24px 60px;

    /* body ist ein Flex-Container (Spalte, damit die Fusszeile unten bleibt), und ein Flex-Kind
       hat `min-width: auto` -- es schrumpft also NICHT unter die Inhaltsbreite. Eine Seite mit
       einer breiten Tabelle wird dadurch ueber die 1500 px hinausgedrueckt, eine Seite ohne
       bleibt bei 1500. Das liest sich als "diese Unterseite ist schmaler als die anderen",
       obwohl die schmale die einzige ist, die sich an die Regel haelt.
       Mit min-width: 0 gilt die Breite fuer alle, und breite Inhalte scrollen in ihrem
       .table-wrapper, wo sie hingehoeren. */
    width: 100%;
    min-width: 0;
}

.bo-page-head {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 16px;
    margin-bottom: 20px;
}

.bo-page-head h1 {
    margin: 0;
    font-size: 1.5rem;
    color: var(--ink);
}

.bo-page-head p {
    margin: 4px 0 0;
    color: var(--ink-2);
    font-size: 0.85rem;
}

.bo-card {
    padding: 18px 20px;
    margin-bottom: 18px;
    border: 1px solid var(--line);
    border-radius: 10px;
    background: var(--bg-2);
}

.bo-empty {
    padding: 28px 20px;
    border: 1px dashed var(--line-2);
    border-radius: 10px;
    color: var(--ink-2);
    font-size: 0.86rem;
    text-align: center;
}

/* =============================================================================
   Sign-in
   ============================================================================= */

.bo-login {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 100vh;
    padding: 24px;
}

.bo-login-box {
    width: 100%;
    max-width: 390px;
    padding: 30px 30px 26px;
    border: 1px solid var(--line);
    border-radius: 12px;
    background: var(--bg-2);
    box-shadow: 0 8px 30px rgba(16, 24, 40, 0.08);
}

.bo-login-box h1 {
    margin: 0 0 4px;
    font-family: var(--font-display);
    font-size: 1.6rem;
    letter-spacing: 0.04em;
}

.bo-login-box .bo-sub {
    margin: 0 0 22px;
    color: var(--ink-2);
    font-size: 0.83rem;
}

/* The code field is read off a phone one digit at a time; proportional text makes that harder
   than it needs to be. */
.bo-code-input {
    font-family: var(--font-mono) !important;
    font-size: 1.4rem !important;
    letter-spacing: 0.3em;
    text-align: center;
}

.bo-login-actions {
    display: flex;
    gap: 10px;
    margin-top: 18px;
}

.bo-login-actions .btn {
    flex: 1;
}

.bo-login-note {
    margin-top: 18px;
    padding-top: 14px;
    border-top: 1px solid var(--line);
    color: var(--ink-3);
    font-size: 0.74rem;
    line-height: 1.6;
}

/* =============================================================================
   Corrections to portal rules that assumed a dark ground
   ============================================================================= */

/* Both are declared with a literal white in the portal, which on white is invisible. */
.data-table thead th {
    color: var(--ink-2);
}

/* =============================================================================
   Form fields
   =============================================================================
   The one place where inheriting the portal's rules was not enough, and where it failed
   silently. `.form-group input` in the portal sets the whole shorthand:

       border: 1px solid rgba(255, 255, 255, 0.1);
       background: var(--dark-bg-secondary);

   The colour is a LITERAL white, not a token, so redefining the ten sources cannot reach it —
   and on a white card a 10%-white edge is nothing at all. `--dark-bg-secondary` now holds white,
   so the field's inside matched the card too. The result was a form of labels with invisible
   fields under them, and the only feedback that a field existed was the focus ring.

   The generic `input, select, textarea` rule below could not fix that either: `.form-group input`
   is more specific, and it sets `border`, not `border-color`.

   ── What it looks like now ───────────────────────────────────────────────────
   At rest the field is a shade DARKER than the card, with a real edge. That is the reverse of
   the dark theme and the right way round here: on white, a recess is what says "you can type in
   this". Focus lifts the field to white and colours the edge, so the field being worked on is
   the brightest thing on the form rather than the only visible one. */

:root {
    /* Own tokens rather than reusing --bg: the page ground and a field on a card are different
       jobs, and tying them together would mean a field stops being visible the day somebody
       changes the page colour. */
    --field-bg:         #F2F4F7;
    --field-bg-focus:   #FFFFFF;
    --field-line:       rgba(16, 24, 40, 0.22);
    --field-line-hover: rgba(16, 24, 40, 0.38);

    /* Geometry as tokens, because it is the same geometry in three places — a field inside a
       .form-group, a control somebody dropped straight into a card, and the table search box.
       Named here so density is one edit rather than a hunt through selectors. */
    --field-pad-y:      10px;
    --field-pad-x:      12px;
    --field-radius:     8px;
}

/* Checkboxes and radios are excluded throughout: they paint themselves, and a background on
   them would put a grey square behind the tick. */
input:not([type="checkbox"]):not([type="radio"]):not([type="range"]),
select,
textarea,
.form-group input:not([type="checkbox"]):not([type="radio"]),
.form-group select,
.form-group textarea,
.table-search-input,
.mp-input,
.bo-code-input {
    background: var(--field-bg);
    color: var(--ink);
    border: 1px solid var(--field-line);
}

/* ── The same control wherever it was written ─────────────────────────────────
   forms.css keys its whole appearance on `.form-group input/select/textarea`. That is fine for
   a page built out of form specs and useless for a control rendered on its own — the role
   dropdown on a person's page was a native select sitting next to styled fields, because
   nobody had wrapped it in a div with the right class. Nothing about a control's LOOK should
   depend on its wrapper, so the geometry is stated on the elements. `.form-group` keeps its own
   copy below only to beat the portal's more specific rule. */
input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="file"]),
select,
textarea,
.form-group input:not([type="checkbox"]):not([type="radio"]),
.form-group select,
.form-group textarea,
.table-search-input,
.mp-input {
    padding: var(--field-pad-y) var(--field-pad-x);
    border-radius: var(--field-radius);
    font-family: var(--font-family, var(--font-body));
    font-size: 0.9rem;
    line-height: 1.35;
}

/* A select is a control, not a text box: it says so with the pointer and a chevron rather than
   with the browser's own arrow, which is the one part of a native select that does not follow
   any of the colours above. */
select {
    cursor: pointer;
    appearance: none;
    -webkit-appearance: none;
    background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='none' stroke='%234E5560' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round' d='M1 1.5 6 6.5 11 1.5'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right var(--field-pad-x) center;
    padding-right: calc(var(--field-pad-x) * 2 + 12px);
}

/* A control declared without a wrapper still has to sit on its own line rather than shrink to
   its content, but only where it is alone — inside a .form-group the width rule above wins. */
.bo-card > select,
.bo-card > input:not([type="checkbox"]):not([type="radio"]),
.bo-card > textarea {
    width: 100%;
}

/* Hover before focus: the edge firms up under the pointer, so a field answers before it is
   clicked. Cheap, and it is what makes a dense form feel like controls rather than text. */
input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):hover,
select:hover,
textarea:hover,
.form-group input:not([type="checkbox"]):not([type="radio"]):hover,
.form-group select:hover,
.form-group textarea:hover,
.table-search-input:hover,
.mp-input:hover {
    border-color: var(--field-line-hover);
}

input:not([type="checkbox"]):not([type="radio"]):focus,
select:focus,
textarea:focus,
.form-group input:focus,
.form-group select:focus,
.form-group textarea:focus,
.table-search-input:focus,
.mp-input:focus,
.bo-code-input:focus {
    background: var(--field-bg-focus);
    border-color: var(--accent);
    /* The portal's ring is rgba(34, 199, 224, 0.1) — a literal cyan at 10%, which on white is
       almost nothing. Same idea, enough of it to see. */
    box-shadow: 0 0 0 3px rgba(11, 124, 142, 0.16);
}

/* A field that cannot be edited must not look like one that can. Flat, no edge to speak of:
   the absence of the recess is the message. */
input:disabled, select:disabled, textarea:disabled,
input[readonly], textarea[readonly] {
    background: var(--dark-bg-tertiary);
    border-color: var(--line);
    color: var(--ink-2);
    cursor: not-allowed;
}

/* The portal asks the browser for dark form controls, which here paints a black date picker and
   black dropdown scrollbars on a white page. */
.form-group input[type="date"],
.form-group input[type="datetime-local"],
.form-group input[type="time"],
input[type="date"], input[type="datetime-local"], input[type="time"] {
    color-scheme: light;
}

/* Declared with a literal white in the portal, so the option list stayed dark. */
.form-group select option, select option {
    background: var(--bg-2);
    color: var(--ink);
}

input::placeholder, textarea::placeholder {
    color: var(--ink-3);
}

/* A ROLE, not a status. Deliberately a different visual family from badge-positive and its
   siblings: a row carries both kinds at once, and if they looked alike a reader would take
   "Hersteller" for a verdict on the account rather than a description of it. Outlined instead of
   filled, in ink rather than in a signal colour. */
.badge.bo-role {
    border: 1px solid var(--line-2);
    background: transparent;
    color: var(--ink-2);
    font-weight: 600;
}

/* The revoke control sits INSIDE the badge it removes, so there is no doubt which role a click
   takes away. A separate button per row with the role named beside it would be the same thing said
   twice, and the second saying is the one that goes out of step. */
.bo-role-revoke {
    margin-left: 6px;
    padding: 0 2px;
    border: 0;
    background: transparent;
    color: var(--ink-3);
    font-size: 0.95rem;
    line-height: 1;
    cursor: pointer;
}

.bo-role-revoke:hover {
    color: var(--danger-color);
}

/* Granting: a picker and a button, on the row of the person it applies to. Kept visually quieter
   than the row's own content — it is an action available on this person, not a property of them. */
.bo-role-grant {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: 8px;
    padding-top: 8px;
    border-top: 1px dashed var(--line-2);
}

/* Only how much room it takes. Its colours, padding and chevron come from the shared control
   rule — a local font-size and padding here is exactly what made this one dropdown look like a
   different application's. */
.bo-role-grant select {
    flex: 1 1 auto;
    max-width: 26rem;
}

/* =============================================================================
   Detail pages
   ============================================================================= */

.bo-detail-title {
    margin: 0 0 12px;
    font-size: 1rem;
    color: var(--ink);
}

.bo-detail-note {
    margin: 0 0 14px;
    padding-bottom: 12px;
    border-bottom: 1px solid var(--line);
    color: var(--ink-2);
    font-size: 0.82rem;
    line-height: 1.65;
}

.bo-rows {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
    gap: 2px 24px;
}

.bo-row {
    display: flex;
    gap: 10px;
    padding: 5px 0;
    font-size: 0.84rem;
    border-bottom: 1px solid var(--line);
}

.bo-row .label {
    min-width: 118px;
    color: var(--ink-3);
}

.bo-row .value {
    color: var(--ink);
    word-break: break-word;
}

/* Free text written by a customer: it arrives with its own line breaks, and collapsing them turns
   a structured complaint into one paragraph nobody reads. */
.bo-longtext {
    white-space: pre-wrap;
    line-height: 1.6;
}

.bo-mono {
    font-family: var(--font-mono);
    font-size: 0.74rem;
    word-break: break-all;
}

/* =============================================================================
   Timeline — status history and comments
   ============================================================================= */

.bo-timeline {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.bo-timeline-entry {
    padding: 10px 12px;
    border: 1px solid var(--line);
    border-left: 3px solid var(--line-2);
    border-radius: 6px;
    background: var(--bg);
}

/* An internal note has to be distinguishable from one the customer can read at a glance, not only
   by its badge: somebody scanning the thread is looking for what was said OUTWARDS. */
.bo-timeline-entry--internal {
    border-left-color: var(--ink-3);
    background: var(--dark-bg-tertiary);
}

.bo-timeline-head {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
    margin-bottom: 4px;
    font-size: 0.82rem;
    color: var(--ink);
}

.bo-timeline-head .text-muted {
    margin-left: auto;
    font-size: 0.74rem;
}

.bo-timeline-body {
    color: var(--ink-2);
    font-size: 0.83rem;
}

/* =============================================================================
   Status pickers
   =============================================================================
   The explanation of a status lives NEXT TO the select, never inside an <option>. A browser
   renders option text flat — no weight, no colour, no second line, and some truncate it — so
   "active — freigegeben, wartende Geräte gehen live" came out as one grey run in a narrow box.

   Outside it, the sentence can carry a tone. That is doing work: releasing an account puts every
   waiting device live, and a sentence saying so should not look like one saying "still in review".
   ============================================================================= */

.bo-status-explain {
    display: block;
    margin-top: 6px;
    padding-left: 9px;
    border-left: 2px solid var(--line-2);
    font-size: 0.78rem;
    line-height: 1.55;
    color: var(--ink-2);
}

.bo-status-explain--good {
    border-left-color: var(--success-color);
}

.bo-status-explain--warn {
    border-left-color: var(--warning-color);
    color: var(--ink);
}

.bo-status-explain--neutral {
    border-left-color: var(--line-2);
}

/* =============================================================================
   Search results
   =============================================================================
   A list of hits is read by scanning, and scanning needs two things this did not have: a boundary
   between one hit and the next, and a clear first line. So each hit is a card with its own edge,
   and inside it the weight steps down — name, then details, then the matched fields, then the
   access verdict. Everything after the name is deliberately quieter than it.
   ============================================================================= */

.bo-hit {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 18px;
    padding: 14px 18px;
    margin-bottom: 10px;
    border: 1px solid var(--line);
    border-left: 3px solid var(--line-2);
    border-radius: 10px;
    background: var(--bg-2);
}

.bo-hit:hover {
    border-left-color: var(--accent);
    box-shadow: 0 2px 10px rgba(16, 24, 40, 0.06);
}

.bo-hit-main {
    min-width: 0;   /* lets long values wrap instead of pushing the buttons off the card */
}

/* The one line somebody is looking for. Everything else on the card is support. */
.bo-hit-label {
    font-size: 1rem;
    font-weight: 700;
    color: var(--ink);
    line-height: 1.35;
}

.bo-hit-sub {
    margin-top: 2px;
    font-size: 0.82rem;
    font-weight: 400;
    color: var(--ink-2);
}

/* Why this row is in the list. Monospaced because these are field values, and set apart with a
   rule so it does not read as more of the subtitle. */
.bo-hit-matched {
    display: flex;
    flex-wrap: wrap;
    gap: 4px 16px;
    margin-top: 8px;
    padding-top: 8px;
    border-top: 1px dashed var(--line);
    font-family: var(--font-mono);
    font-size: 0.72rem;
    color: var(--ink-3);
}

.bo-hit-matched em {
    font-style: normal;
    font-weight: 600;
    color: var(--ink-2);
}

.bo-hit-access {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: 8px;
    font-size: 0.76rem;
}

.bo-hit-actions {
    display: flex;
    flex-direction: column;
    gap: 6px;
    flex-shrink: 0;
}

/* ── The field narrowing row ─────────────────────────── */

.bo-fields {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 6px 10px;
    margin-top: 12px;
    padding-top: 12px;
    border-top: 1px solid var(--line);
}

.bo-field-label {
    color: var(--ink-3);
    font-size: 0.76rem;
    text-transform: uppercase;
    letter-spacing: 0.06em;
}

.bo-field-toggle {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 3px 9px;
    border: 1px solid var(--line-2);
    border-radius: 20px;
    font-family: var(--font-mono);
    font-size: 0.72rem;
    color: var(--ink-2);
    cursor: pointer;
}

.bo-field-toggle:hover {
    border-color: var(--accent);
    color: var(--accent);
}

.bo-field-toggle input {
    margin: 0;
    width: auto;
}

@media (max-width: 720px) {
    .bo-hit {
        flex-direction: column;
    }

    .bo-hit-actions {
        flex-direction: row;
        width: 100%;
    }
}

/* A staff row that needs attention. Reuses the hit card because it is the same kind of object — a
   person with supporting detail — and a second card style for the same shape would drift. */
.bo-hit--attention {
    border-left-color: var(--danger-color);
}

/* =============================================================================
   Editing — the button, and the dialog it opens
   =============================================================================
   The button is not on a card. It used to be, together with a heading, and the card then held
   nothing else once the form moved into a dialog — a full-width frame around one control. */

.bo-edit-bar {
    display: flex;
    justify-content: flex-end;
    margin-bottom: 18px;
}

/* A card heading with one action beside it — signing authorities, billing recipients. Not the
   edit button any more; that one no longer has a card to sit in. */
.bo-edit-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
}

.bo-modal {
    width: min(760px, calc(100vw - 32px));
    max-height: calc(100vh - 64px);
    padding: 0;
    border: 1px solid var(--line-2);
    border-radius: 12px;
    background: var(--bg-2);
    color: var(--ink);
    box-shadow: 0 24px 64px rgba(16, 24, 40, 0.22);

    /* The dialog is its own column so the head and foot stay put and only the fields scroll. A
       long form otherwise pushes the save button off the bottom of the screen, and the way out of
       that is scrolling to somewhere you cannot see. */
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

/* :not([open]) rather than a display rule: `display` on a <dialog> is what the element itself
   toggles, and setting it here stops showModal() from ever showing anything. */
.bo-modal:not([open]) {
    display: none;
}

.bo-modal::backdrop {
    background: rgba(16, 24, 40, 0.45);
}

.bo-modal-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    padding: 16px 20px;
    border-bottom: 1px solid var(--line);
}

.bo-modal-head h2 {
    margin: 0;
    font-size: 1.05rem;
    color: var(--ink);
}

.bo-modal-close {
    padding: 0 6px;
    border: 0;
    background: transparent;
    color: var(--ink-3);
    font-size: 1.5rem;
    line-height: 1;
    cursor: pointer;
}

.bo-modal-close:hover {
    color: var(--ink);
}

.bo-modal-body {
    flex: 1 1 auto;
    overflow-y: auto;
    padding: 18px 20px;
}

.bo-modal-foot {
    display: flex;
    justify-content: flex-end;
    gap: 10px;
    padding: 14px 20px;
    border-top: 1px solid var(--line);
    background: var(--bg);
}

/* Inside the dialog the portal's form sections already sit on a surface, so their own frame is
   one border too many. */
.bo-modal-body .form-section {
    padding: 0;
    margin-bottom: 18px;
    border: 0;
    background: transparent;
}

.bo-modal-body details.form-section {
    padding: 10px 12px;
    border: 1px solid var(--line);
    border-radius: 8px;
    background: var(--bg);
}

/* =============================================================================
   Change history
   =============================================================================
   A list of changes, not a table. The fields that moved in one save belong together under the
   date and the person, and a table would put each field on its own row with the date repeated —
   which is exactly the unreadable form the history table already has.

   Three columns per field line: what changed, what it was, what it became. Aligned down the
   panel so a reader scanning for one field finds it in the same place in every entry. */

.bo-history-summary {
    cursor: pointer;
    list-style: revert;
}

.bo-history-body {
    margin-top: 14px;
}

.bo-history-entry {
    padding: 10px 0 12px;
    border-top: 1px solid var(--line);
}

.bo-history-entry:first-child {
    border-top: 0;
    padding-top: 0;
}

/* The creation has no fields to show and should not look like an entry that lost them. */
.bo-history-entry--created {
    opacity: 0.75;
}

.bo-history-when {
    font-family: var(--font-mono);
    font-size: 0.78rem;
    color: var(--ink-2);
}

.bo-history-who {
    margin-top: 2px;
    font-size: 0.86rem;
    color: var(--ink);
}

.bo-history-fields {
    margin-top: 8px;
    display: grid;
    /* The label column sizes to the longest label and stops there, so the values line up without
       a fixed width that a longer label would break out of. */
    grid-template-columns: max-content minmax(0, 1fr) auto minmax(0, 1fr);
    gap: 4px 10px;
    align-items: baseline;
    font-size: 0.84rem;
}

.bo-history-field {
    display: contents;
}

.bo-history-label {
    color: var(--ink-2);
    font-weight: 600;
}

.bo-history-before {
    color: var(--ink-3);
    text-decoration: line-through;
    overflow-wrap: anywhere;
}

.bo-history-arrow {
    color: var(--ink-3);
}

.bo-history-after {
    color: var(--ink);
    font-weight: 600;
    overflow-wrap: anywhere;
}

.bo-history-staff {
    border-color: var(--accent);
    color: var(--accent);
}

.bo-history-empty,
.bo-history-more {
    padding: 8px 0;
    font-size: 0.84rem;
}

/* On a narrow screen the four-column grid collapses to label over values rather than wrapping
   mid-value, which is where a long address becomes unreadable. */
@media (max-width: 720px) {
    .bo-history-fields {
        grid-template-columns: 1fr;
        gap: 2px;
    }

    .bo-history-field {
        display: block;
        padding: 4px 0;
    }

    .bo-history-arrow {
        padding: 0 4px;
    }
}

/* A history panel nested inside a hit card, rather than standing as its own card. */
.bo-billing-history {
    margin-top: 8px;
    padding-top: 8px;
    border-top: 1px dashed var(--line);
    font-size: 0.82rem;
}

/* =============================================================================
   Persons — the two views of one list
   =============================================================================
   Links rather than a control with hidden state: which view somebody is looking at is in the
   address, so it can be sent to a colleague and survives a reload. */

.bo-modes {
    display: flex;
    gap: 4px;
    margin-bottom: 16px;
}

.bo-mode {
    padding: 6px 14px;
    border: 1px solid var(--line-2);
    border-radius: 20px;
    background: var(--bg-2);
    color: var(--ink-2);
    font-size: 0.82rem;
    text-decoration: none;
}

.bo-mode:hover {
    border-color: var(--accent);
    color: var(--accent);
}

.bo-mode--active {
    border-color: var(--accent);
    background: var(--accent-dim);
    color: var(--accent);
    font-weight: 600;
}

/* The one mark the merged list exists to carry. Filled rather than outlined, unlike the plain
   role badges beside it: "works here" is a different KIND of statement from "holds this role",
   and a reader scanning the column should not have to read the words to tell them apart. */
.badge.bo-role.bo-person-staff {
    border-color: var(--accent);
    background: var(--accent-dim);
    color: var(--accent);
}

/* ── KI-Credits ──────────────────────────────────────────────────────────────
   Saldo, Buch und die beiden Regeln. Die Zahl ist gross, weil sie die Antwort auf die Frage ist,
   mit der jemand diese Karte oeffnet; alles andere erklaert sie. */

.bo-credit-balance {
    display: flex;
    align-items: baseline;
    gap: 12px;
    margin-bottom: 14px;
}

.bo-credit-number {
    font-size: 2rem;
    font-weight: 700;
    line-height: 1;
    color: var(--accent);
}

/* Ein Fehlbetrag ist ein Zustand, kein Fehler — aber einer, den man sofort sehen muss. */
.bo-credit-number--negative {
    color: var(--danger-color);
}

.bo-credit-caption {
    color: var(--ink-2);
    font-size: 0.85rem;
}

.bo-credit-form {
    display: flex;
    flex-wrap: wrap;
    gap: 12px 16px;
    align-items: flex-start;
    padding: 12px 0;
    border-top: 1px solid var(--line);
    border-bottom: 1px solid var(--line);
    margin-bottom: 14px;
}

.bo-credit-form-row {
    display: flex;
    flex-direction: column;
    gap: 4px;
    min-width: 160px;
}

.bo-credit-form-row--wide {
    flex: 1 1 280px;
}

.bo-credit-form-row label {
    font-size: 0.78rem;
    text-transform: uppercase;
    letter-spacing: 0.4px;
    color: var(--ink-2);
}

/* forms.css haengt sein Feld-Aussehen an .form-group input/select/textarea, und diese Felder
   stehen direkt in einer Karte — also sagen sie es selbst. */
.bo-credit-form input,
.bo-credit-form select {
    padding: 8px 10px;
    background: var(--dark-bg-tertiary);
    border: 1px solid var(--line);
    border-radius: 8px;
    color: var(--ink);
    font-family: inherit;
    font-size: 0.9rem;
}

.bo-credit-form input:focus,
.bo-credit-form select:focus {
    outline: none;
    border-color: var(--accent);
}

.bo-credit-hint {
    font-size: 0.72rem;
    color: var(--ink-3);
    max-width: 260px;
    line-height: 1.4;
}

.bo-credit-delta {
    font-variant-numeric: tabular-nums;
    font-weight: 600;
    color: var(--success-color);
    white-space: nowrap;
}

.bo-credit-delta--negative {
    color: var(--danger-color);
}

.bo-credit-actor {
    font-family: var(--font-mono);
    font-size: 0.78rem;
    color: var(--ink-2);
    word-break: break-all;
}

.bo-credit-settings {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 6px 16px;
    margin: 0 0 8px;
}

.bo-credit-settings dt {
    color: var(--ink-2);
    font-size: 0.85rem;
}

.bo-credit-settings dd {
    margin: 0;
    font-weight: 600;
}

/* ==========================================================================
   Kennungen, denen man folgen kann
   ==========================================================================
   Beide Aktionen sichtbar statt einer hinter einem Doppelklick: ein Doppelklick verraet sich
   nirgends, ist auf einem Touchgeraet nicht ausloesbar und ueber die Tastatur gar nicht.
   Ausschliesslich Tokens — dieses Theme definiert genau die um. */

.bo-id-chip {
    display: inline-flex;
    gap: 4px;
    align-items: center;
}

.bo-id-open {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    border: 1px solid var(--line-2);
    border-radius: 5px;
    color: var(--text-secondary);
    text-decoration: none;
    flex: 0 0 auto;
}

.bo-id-open:hover,
.bo-id-open:focus-visible {
    border-color: var(--primary-color);
    color: var(--primary-color);
}

.bo-id-open svg {
    width: 13px;
    height: 13px;
}

/* Spruenge auf gefilterte Listen: vom Account auf SEINE Standorte, von dort auf SEINE Geraete. */
.bo-related-link {
    display: inline-block;
    margin-right: 6px;
    padding: 2px 8px;
    border: 1px solid var(--line-2);
    border-radius: 999px;
    color: var(--text-secondary);
    font-size: 0.8rem;
    text-decoration: none;
}

.bo-related-link:hover,
.bo-related-link:focus-visible {
    border-color: var(--primary-color);
    color: var(--primary-color);
}

/* ==========================================================================
   Kennzahlen auf der Startseite
   ==========================================================================
   Jede Kachel ist ein Link auf genau die gefilterte Liste, die die Zahl gezaehlt hat. Sie sieht
   deshalb anklickbar aus statt wie eine Anzeige — eine Zahl, die man nur ansehen kann, erzeugt
   die Frage "und wo sehe ich die jetzt". */

.bo-kpis {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    margin-top: 18px;
}

.bo-kpi {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 170px;
    padding: 14px 16px;
    border: 1px solid var(--line);
    border-radius: 10px;
    background: var(--card-background);
    color: inherit;
    text-decoration: none;
}

.bo-kpi:hover,
.bo-kpi:focus-visible {
    border-color: var(--primary-color);
}

.bo-kpi-value {
    font-size: 1.7rem;
    font-weight: 600;
    line-height: 1.1;
}

.bo-kpi-label {
    font-size: 0.9rem;
}

.bo-kpi-hint {
    color: var(--text-muted);
    font-size: 0.78rem;
}

/* ==========================================================================
   Service-Request-Schiene
   ==========================================================================
   Schiebt den Inhalt zur Seite statt ihn zu ueberdecken: wer einen Fall liest, liest ihn NEBEN
   dem Datensatz, um den es geht — ein Overlay verdeckt genau die Angaben, auf die sich die
   Antwort bezieht. Ausschliesslich Tokens, wie alles hier. */

.bo-rail {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    z-index: 40;
    display: flex;
    width: 340px;
    transform: translateX(100%);
    transition: transform 0.18s ease;
    background: var(--card-background);
    border-left: 1px solid var(--line);
}

.bo-rail-is-open .bo-rail {
    transform: translateX(0);
}

/* Der Griff bleibt sichtbar, wenn die Schiene zu ist — sonst gaebe es nichts zu klicken. */
.bo-rail-toggle {
    position: absolute;
    top: 96px;
    left: -40px;
    display: flex;
    flex-direction: column;
    gap: 4px;
    align-items: center;
    width: 40px;
    padding: 12px 0;
    border: 1px solid var(--line);
    border-right: 0;
    border-radius: 10px 0 0 10px;
    background: var(--card-background);
    color: var(--text-secondary);
    cursor: pointer;
}

.bo-rail-toggle-label {
    writing-mode: vertical-rl;
    transform: rotate(180deg);
    font-size: 0.78rem;
    letter-spacing: 0.04em;
}

.bo-rail-count:not(:empty) {
    min-width: 20px;
    padding: 1px 5px;
    border-radius: 999px;
    background: var(--primary-color);
    color: var(--bg);
    font-size: 0.72rem;
    font-weight: 600;
    text-align: center;
}

.bo-rail-body {
    flex: 1;
    overflow-y: auto;
    padding: 18px 16px;
}

.bo-rail-scope,
.bo-rail-empty {
    margin: 0 0 12px;
    color: var(--text-muted);
    font-size: 0.82rem;
}

.bo-rail-card {
    margin-bottom: 10px;
    border: 1px solid var(--line);
    border-radius: 8px;
    overflow: hidden;
}

.bo-rail-card--open {
    border-color: var(--primary-color);
}

.bo-rail-card-head {
    display: flex;
    flex-direction: column;
    gap: 6px;
    width: 100%;
    padding: 10px 12px;
    border: 0;
    background: none;
    color: inherit;
    text-align: left;
    cursor: pointer;
}

.bo-rail-card-subject {
    font-size: 0.88rem;
    font-weight: 600;
}

.bo-rail-card-meta {
    display: flex;
    gap: 6px;
    align-items: center;
    flex-wrap: wrap;
}

.bo-rail-thread:not(:empty) {
    padding: 0 12px 12px;
}

.bo-rail-messages {
    display: flex;
    flex-direction: column;
    gap: 8px;
    max-height: 320px;
    overflow-y: auto;
    margin-bottom: 10px;
}

.bo-rail-message {
    padding: 8px 10px;
    border-radius: 8px;
    background: var(--dark-bg-tertiary, var(--bg-2));
    font-size: 0.84rem;
}

/* Die eigene Seite abgesetzt, damit ein Verlauf beim Ueberfliegen zwei Sprecher hat. */
.bo-rail-message--support {
    background: color-mix(in srgb, var(--primary-color) 12%, transparent);
}

.bo-rail-message-head {
    display: flex;
    justify-content: space-between;
    gap: 8px;
    margin-bottom: 4px;
    /* Die Leiste ist schmal, und eine Absenderadresse ist laenger als sie. Ohne das Umbrechen
       schiebt jede Zeile die Leiste auf und es entstehen waagerechte Balken. */
    flex-wrap: wrap;
    min-width: 0;
}

.bo-rail-message-head strong {
    min-width: 0;
    overflow-wrap: anywhere;
}

/* Ueber dem Namen statt daneben: als drittes Element in derselben Zeile war die Markierung
   genau das, was die Leiste zu breit machte -- und sie gehoert ohnehin nicht neben den
   Absender, sondern ueber die Nachricht, auf die sie sich bezieht. */
.bo-rail-internal-flag {
    display: inline-block;
    margin-bottom: 4px;
}

/* Worum es in dem Fall geht, vor dem Verlauf. */
.bo-rail-opening {
    margin-bottom: 10px;
    padding-bottom: 10px;
    border-bottom: 1px solid var(--line);
}

.bo-rail-opening p {
    margin: 4px 0 0;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
    color: var(--ink-2);
}

.bo-rail-message p {
    margin: 0;
}

.bo-rail-reply textarea {
    width: 100%;
    margin-bottom: 8px;
}

.bo-rail-reply-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}

.bo-rail-error:not([hidden]) {
    margin: 8px 0 0;
    color: var(--danger-color);
    font-size: 0.8rem;
}

/* Der Inhalt weicht aus, statt ueberdeckt zu werden. */
.bo-rail-is-open .bo-page {
    padding-right: 356px;
}

@media (max-width: 900px) {
    .bo-rail {
        width: 100%;
    }

    /* Auf einem schmalen Bildschirm gibt es keinen Platz zum Ausweichen, also liegt sie darueber
       — dort ist ein Overlay die ehrliche Loesung und kein Kompromiss. */
    .bo-rail-is-open .bo-page {
        padding-right: 0;
    }
}


/* ==========================================================================
   Angedockter Footer und die Hoehen, mit denen sich die Schiene dazwischen spannt
   ==========================================================================
   Ab Tablet-Breite. Darunter bleibt der Footer im Fluss: auf einem Telefon ist der Bildschirm zu
   niedrig, um dauerhaft eine Zeile an etwas zu vergeben, das man einmal am Tag liest. */

@media (min-width: 768px) {
    .bo-footer {
        position: fixed;
        right: 0;
        bottom: 0;
        left: 0;
        z-index: 45;
        min-height: var(--bo-footer-height);
    }

    /* Der Platz, den der angedockte Footer der Seite wegnimmt, wird ihr unten zurueckgegeben —
       sonst endet jede Seite unter ihm, und zwar genau um die letzte Zeile. */
    .bo-page {
        padding-bottom: calc(var(--bo-footer-height) + 20px);
    }
}

/* Die Schiene spannt sich ZWISCHEN Header und Footer, nicht ueber sie.
   Vorher stand sie auf `top: 0` und lag damit unter dem Header — die Ueberschrift der Schiene und
   die erste Karte waren verdeckt. */
.bo-rail {
    top: var(--bo-header-height);
}

@media (min-width: 768px) {
    .bo-rail {
        bottom: var(--bo-footer-height);
    }
}

/* Der Griff sitzt jetzt relativ zur Schiene, die selbst schon unter dem Header beginnt. */
.bo-rail-toggle {
    top: 24px;
}

/* Eine interne Notiz ist keine Nachricht an den Kunden. Sie steht abgesetzt, weil sonst jemand
   darauf antwortet in dem Glauben, der Kunde habe sie gelesen. */
.bo-rail-message--internal {
    border-left: 3px solid var(--line-2);
    background: transparent;
}

.bo-rail-message-head .badge {
    font-size: 0.68rem;
}

/* ==========================================================================
   KI-Studio auf der Personenseite
   Die Sperre steht über der Liste, weil sie die Entscheidung ist und die Liste
   die Grundlage dafür. Eine aktive Sperre ist abgesetzt, aber nicht rot: sie
   ist eine getroffene Entscheidung, keine Störung.
   ========================================================================== */
.bo-studio-block {
    margin-bottom: 16px;
}

/* ==========================================================================
   Startseite: Kennzahlen und die Signaturstellen darunter
   ========================================================================== */
.bo-kpis {
    margin-bottom: 18px;
}

/* Zugeklappt der Normalfall. Die Zusammenfassung ist die ganze Zeile klickbar,
   nicht nur das Dreieck -- ein Ziel von 12 px Breite trifft niemand zweimal. */
.bo-authorities > summary {
    cursor: pointer;
    list-style: none;
    display: flex;
    align-items: center;
    gap: 10px;
}

.bo-authorities > summary::-webkit-details-marker {
    display: none;
}

.bo-authorities > summary::before {
    content: 'B8';
    color: var(--ink-3);
    transition: transform 0.15s ease;
}

.bo-authorities[open] > summary::before {
    transform: rotate(90deg);
}

.bo-authorities > summary .bo-detail-title {
    margin: 0;
}

/* Aufgeklappt braucht der Text Abstand zur Zeile, zugeklappt waere er ein Loch. */
.bo-authorities[open] > summary {
    margin-bottom: 12px;
}

.bo-studio-block--active {
    padding: 12px 14px;
    border: 1px solid var(--border-color);
    border-left: 3px solid var(--text-secondary);
    border-radius: 6px;
    background: var(--bg-tertiary);
}

.bo-studio-block--active p {
    margin: 6px 0 0;
}

.bo-studio-block-form {
    display: flex;
    gap: 10px;
    align-items: center;
    margin-top: 10px;
}

/* forms.css haengt sein Feld-Aussehen an .form-group input, und dieses Feld steht direkt im
   Formular -- ohne die Zeilen hier hatte es weder Polsterung noch Rahmen und sah daneben aus
   wie ein Rest. Dasselbe, was .bo-credit-form eine Karte weiter oben fuer sich sagt. */
.bo-studio-block-form input {
    flex: 1 1 auto;
    min-width: 0;
    padding: 9px 12px;
    background: var(--dark-bg-tertiary);
    border: 1px solid var(--line);
    border-radius: 8px;
    color: var(--ink);
    font-family: inherit;
    font-size: 0.9rem;
}

.bo-studio-block-form input:focus {
    outline: none;
    border-color: var(--accent);
}

/* ==========================================================================
   Knoepfe, die in einer Zeile stehen statt gestapelt
   ==========================================================================
   forms.css setzt `form .btn-primary, form .btn-secondary { width: 100% }`.
   Das ist fuer gestapelte Formulare richtig -- Anmeldung, Bearbeiten-Dialog --
   und dort soll es bleiben. In einer Flex-ZEILE macht es jeden Knopf so breit
   wie den Container: drei Antwortknoepfe werden zu drei vollbreiten Zeilen, und
   neben einem Eingabefeld bleibt fuer das Feld nichts uebrig.

   Deshalb benannt je Stelle und nicht pauschal zurueckgesetzt: wer die Regel
   global aushebelt, zerlegt genau die Formulare, fuer die sie gemacht wurde. */
.bo-rail-reply-actions .btn,
.bo-credit-form .btn,
.bo-credit-form button[type="submit"] {
    width: auto;
}

/* Der Knopf nimmt, was er braucht. Das Feld ist die Eingabe, er ist die Bestaetigung.

   `width: auto` ist hier die eigentliche Zeile: forms.css setzt `form .btn-primary,
   form .btn-secondary { width: 100% }` -- gedacht fuer gestapelte Formulare, in denen der
   Absendeknopf die volle Zeile einnimmt. In einer Flex-Zeile wird daraus die Basisbreite des
   Knopfes, er nimmt die ganze Karte ein und das Eingabefeld bleibt als Rest uebrig. Genau das
   war hier zu sehen, und `flex: 0 0 auto` allein hat es nicht geheilt, sondern festgeschrieben. */
.bo-studio-block-form .btn {
    flex: 0 0 auto;
    width: auto;
    white-space: nowrap;
}

.bo-studio-summary {
    margin: 0 0 10px;
}

/* Die Beschreibung ist der Grund, warum es diese Tabelle gibt — sie bekommt die
   Breite, und der Rest richtet sich danach. */
.bo-studio-table .bo-studio-prompt {
    max-width: 380px;
}

.bo-studio-table td {
    vertical-align: top;
}

.bo-studio-table .bo-num {
    text-align: right;
    white-space: nowrap;
}

