/* SYNCED COPY — DO NOT EDIT — source: LicensingManagement/LDataBrain/wwwroot/css/ldata-components.css (sha256:a606ab996572c2b7) — run scripts/sync-design-tokens.sh */
/* ============================================================================
   ldata-components.css — v1 — shared LDataBrain component classes (.ld-*)
   CANONICAL COPY: LicensingManagement/LDataBrain/wwwroot/css/ldata-components.css
   Distributed by scripts/sync-design-tokens.sh — edit ONLY the canonical copy.

   Every class consumes ldata-tokens.css (--ld-*) exclusively: no literal
   colors, no physical direction properties. Specs from DESIGN.md /
   docs/05-frontend-ui.md / docs/06-crud-form-page-template.md.
   ========================================================================== */

/* ── Section card (outer wrapper) ────────────────────────────────────────── */
.ld-card {
    border-radius: var(--ld-radius-xl);
    border: 1px solid var(--ld-card-border);
    background: var(--ld-card-bg);
    box-shadow: var(--ld-card-shadow);
    padding: var(--ld-card-pad);
    color: var(--ld-text);
}

.ld-card--prominent {
    box-shadow: var(--ld-section-shadow);
}

@media (max-width: 767px) {
    .ld-card {
        padding: var(--ld-space-3);
    }
}

/* ── Section title ───────────────────────────────────────────────────────── */
/* html-qualified on purpose. Six of the eleven modules carry a blanket
   .app-body h1,h2,h3 { letter-spacing: -0.02em } from their own site.css, which
   is (0,1,1) and beat this rule at (0,1,0) — measured, the section title came
   out at -0.26px tracking in those six and 1.04px in the other five, for the
   same class, in all three themes. The signature IS the 0.08em; DESIGN.md calls
   this repetition the brand across 400+ pages. One qualifier makes it hold
   everywhere without an !important. */
html .ld-section-title {
    display: flex;
    align-items: center;
    gap: var(--ld-space-2);
    font-size: .8125rem;
    line-height: 1.25rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .08em;
    color: var(--ld-text);
}

.ld-section-title > i,
.ld-section-title > svg {
    /* --ld-brand is the BUTTON fill: designed to sit under white text, not to BE
       the mark on a surface. As a glyph it measured 2.82:1 on the gray card and
       2.09 on the gray tile, under the 3:1 a graphical element needs.
       --ld-stat-label is the same accent with a lightness clamp for exactly this
       job: 6.21 and 4.62, and better on light and black too. */
    color: var(--ld-stat-label);
    font-size: .875rem;
}

.ld-section-subtitle {
    unicode-bidi: plaintext;   /* see .ld-tile__meta */
    margin-block-start: var(--ld-space-1);
    font-size: .875rem;
    color: var(--ld-text-secondary);
}

/* ── Stat / KPI tile ─────────────────────────────────────────────────────── */
.ld-stat-card {
    border-radius: var(--ld-radius-lg);
    padding-block: var(--ld-space-3);
    padding-inline: var(--ld-space-4);
    background: var(--ld-stat-bg);
    border: 1px solid var(--ld-stat-border);
    box-shadow: var(--ld-stat-shadow);
}

.ld-stat-label {
    color: var(--ld-stat-label);
    font-weight: 600;
    font-size: .75rem;
    letter-spacing: .04em;
}

.ld-stat-value {
    color: var(--ld-text-value);
    font-weight: 700;
    font-size: var(--ld-fs-stat);
    margin-block-start: .125rem;
}

.ld-stat-helper {
    color: var(--ld-helper);
    font-size: .75rem;
    margin-block-start: .125rem;
}

@media (max-width: 767px) {
    .ld-stat-value {
        font-size: var(--ld-fs-stat-mobile);
    }
}


/* ── THE STANDARD TILE ─────────────────────────────────────────────────────
   One tile for the whole platform. It exists because 77 pages had rolled
   their own, and the failures were always the same four:

     · the value sat at a different height in every tile, because a two-word
       label pushed it down and a one-word label did not;
     · rows squeezed a fixed column count into any width, so labels broke to
       three lines;
     · long notes were cut mid-sentence with no way to read the rest;
     · the status pill was drawn differently on every page.

   The fix is structural, not decorative: the tile is a three-row grid —
   header / value / footer — the label reserves its two lines whether it needs
   them or not, and the footer is pinned to the bottom. Every tile in a row
   therefore aligns on the same two baselines no matter what it contains.

   Markup:
     <div class="ld-tile-grid">
       <article class="ld-tile">
         <header class="ld-tile__head">
           <h3 class="ld-tile__label">AWS current month</h3>
           <span class="ld-status" data-status="ok">…</span>
         </header>
         <p class="ld-tile__value">USD 208.90</p>
         <p class="ld-tile__meta">Forecast: USD 1,026.89</p>
       </article>
     </div>
   ───────────────────────────────────────────────────────────────────────── */
.ld-tile-grid {
    display: grid;
    /* auto-fit, not a fixed column count: a hard repeat(8,…) is what forced
       three-line labels on narrow screens. Tiles reflow instead of squeezing. */
    grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
    gap: var(--ld-tile-gap);
    align-items: stretch;
}
.ld-tile-grid--wide  { grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr)); }
.ld-tile-grid--dense { grid-template-columns: repeat(auto-fit, minmax(10.5rem, 1fr)); }

.ld-tile {
    display: grid;
    grid-template-rows: auto auto 1fr;   /* head · value · footer takes the rest */
    gap: var(--ld-space-1);
    min-inline-size: 0;                  /* lets the children truncate */
    padding-block: var(--ld-space-3);
    padding-inline: var(--ld-space-4);
    border-radius: var(--ld-radius-lg);
    background: var(--ld-stat-bg);
    border: 1px solid var(--ld-stat-border);
    /* Light theme lifts the raised surface off the quiet card; the dark themes
       signal the same elevation by brightness alone, so there it is none. */
    box-shadow: var(--ld-tile-shadow, none);
    color: var(--ld-text);
}
/* The documented dual shadow, for tiles that lead a page. */
.ld-tile--prominent { box-shadow: var(--ld-stat-shadow); }
/* Denser rows: same structure, less air. */
.ld-tile--compact { padding-block: var(--ld-space-2); padding-inline: var(--ld-space-3); }

.ld-tile__head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--ld-space-2);
    min-inline-size: 0;
}

.ld-tile__label {
    margin: 0;
    min-inline-size: 0;
    font-size: .6875rem;
    font-weight: 600;
    line-height: 1.25;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--ld-stat-label);
    /* Two lines are RESERVED, not just permitted: a one-line label keeps the
       same box as a two-line one, so the value below starts at the same height
       in every tile of the row. A third line is clamped with an ellipsis and
       the full text stays available through the element's title. */
    /* Exactly two line boxes: line-height 1.25 x 2 = 2.5em. Reserving less
       than a real two-line box (2.1em was the first attempt) lets a two-word
       label grow past the reservation and push its value below its
       neighbours' — the misalignment this whole rule exists to prevent. */
    min-block-size: 2.5em;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    overflow-wrap: anywhere;
}

.ld-tile__value {
    margin: 0;
    font-size: var(--ld-fs-stat);
    font-weight: 700;
    line-height: 1.15;
    color: var(--ld-text-value);
    /* Digits line up column-to-column; a long value truncates rather than
       wrapping and shoving the footer out of alignment. */
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.ld-tile__value--sm { font-size: var(--ld-fs-stat-sm); }
/* The currency or unit rides with the number but never competes with it. */
.ld-tile__unit { font-size: .625em; font-weight: 600; color: var(--ld-text-muted); margin-inline-end: .25em; }
/* A value the tile has no data for: an em dash, not an empty box. */
.ld-tile__value:empty::before { content: "—"; color: var(--ld-text-dim); }

.ld-tile__meta {
    margin: 0;
    align-self: end;                     /* pinned to the bottom of the tile */
    /* Meta lines are technical and usually English even on a Hebrew page. In an
       RTL container a mixed run has its terminal period thrown to the front and
       its leading number dragged to the end - "1,204 requests - … completion"
       became "requests - … completion 1,204", putting a count beside the wrong
       word on a money page. plaintext is the CSS form of dir="auto": each line
       takes its direction from its first strong character, which also moves the
       truncation to the END of the sentence instead of eating the label. */
    unicode-bidi: plaintext;
    font-size: .6875rem;
    line-height: 1.35;
    color: var(--ld-text-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* A warning that belongs to this tile. Clamped to two lines, and because it
   is clamped it always carries the full text in its title — the old version
   cut sentences off with no way to read them. */
.ld-tile__note {
    unicode-bidi: plaintext;   /* see .ld-tile__meta */
    align-self: end;
    margin-block-start: var(--ld-space-2);
    padding-block: .35rem;
    padding-inline: var(--ld-space-2);
    border-radius: var(--ld-radius-sm);
    font-size: .6875rem;
    line-height: 1.35;
    background: var(--ld-warn-bg);
    border: 1px solid var(--ld-warn-border);
    color: var(--ld-warn-text);
    cursor: help;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    overflow-wrap: anywhere;
}
.ld-tile__note[data-status="error"] {
    background: var(--ld-error-bg);
    border-color: var(--ld-error-border);
    color: var(--ld-error-text);
}

@media (max-width: 767px) {
    .ld-tile__value { font-size: var(--ld-fs-stat-mobile); }
}

/* ── STATUS PILL ───────────────────────────────────────────────────────────
   One pill, three states. Every page had been drawing its own. Colour is
   carried by the status tokens, so it means the same thing everywhere and
   follows all three themes.
   ───────────────────────────────────────────────────────────────────────── */
.ld-status {
    display: inline-flex;
    align-items: center;
    gap: .3125rem;
    flex: 0 0 auto;
    padding-block: .125rem;
    padding-inline: var(--ld-space-2);
    border-radius: var(--ld-radius-full);
    border: 1px solid;
    font-size: .65rem;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
    white-space: nowrap;
}
.ld-status[data-status="ok"]    { background: var(--ld-success-bg); border-color: var(--ld-success-border); color: var(--ld-success-text); }
.ld-status[data-status="warn"]  { background: var(--ld-warn-bg);    border-color: var(--ld-warn-border);    color: var(--ld-warn-text); }
.ld-status[data-status="error"] { background: var(--ld-error-bg);   border-color: var(--ld-error-border);   color: var(--ld-error-text); }
.ld-status[data-status="idle"]  { background: var(--ld-badge-bg);   border-color: var(--ld-badge-border);   color: var(--ld-text-muted); }
.ld-status__dot { inline-size: .4rem; block-size: .4rem; flex: none; border-radius: var(--ld-radius-full); background: currentColor; }

/* ── Sub-card (Open/Done counters, progress tiles) ───────────────────────── */
.ld-subcard {
    border-radius: var(--ld-radius-lg);
    padding-block: var(--ld-space-2);
    padding-inline: var(--ld-space-3);
    background: var(--ld-subcard-bg);
    border: 1px solid var(--ld-subcard-border);
    box-shadow: var(--ld-tile-shadow, none);
}

/* ── Tabs ────────────────────────────────────────────────────────────────── */
.ld-tab-frame {
    border-radius: var(--ld-radius-lg);
    padding: var(--ld-space-1);
    background: var(--ld-tab-frame-bg);
    border: 1px solid var(--ld-tab-frame-border);
}

.ld-tab-nav {
    display: inline-flex;
    align-items: center;
    gap: .125rem;
    border-radius: var(--ld-radius-md);
    padding-block: .125rem;
    padding-inline: var(--ld-space-1);
    background: var(--ld-tab-nav-bg);
    border: 1px solid var(--ld-tab-nav-border);
    box-shadow: var(--ld-tab-nav-shadow);
}

.ld-tab {
    display: inline-flex;
    align-items: center;
    border: 1px solid transparent;
    border-radius: var(--ld-radius-sm);
    padding-block: var(--ld-space-1);
    padding-inline: .625rem;
    font-size: .6875rem;
    font-weight: 500;
    color: var(--ld-text-secondary);
    background: transparent;
    cursor: pointer;
    transition: color .15s ease, background-color .15s ease;
}

/* The tab never had a focus rule at the source, so seven pages each wrote
   their own — the same disease .ld-btn-danger had. Flat ring: the tab is a
   light fill, measured 4.02-8.14 against it in round 5. The anchor reset
   rides along for the same reason: four pages re-declared it. */
.ld-tab:focus-visible { outline: none; box-shadow: var(--ld-focus-ring-flat); }
a.ld-btn, a.ld-btn-ghost, a.ld-tab, a.ld-action-pill { text-decoration: none; }

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

.ld-tab > i,
.ld-tab > svg {
    margin-inline-end: .35rem;
    font-size: var(--ld-fs-icon-sm);
}

.ld-tab.ld-tab-active,
.ld-tab-active {
    background: var(--ld-tab-active-bg);
    border-color: var(--ld-tab-active-border);
    color: var(--ld-tab-active-text);
    border-block-end: 2.5px solid var(--ld-tab-underline);
    font-weight: 500;
}

/* ── Chips / badges ──────────────────────────────────────────────────────── */
.ld-chip {
    display: inline-flex;
    align-items: center;
    gap: .375rem;
    border-radius: var(--ld-radius-full);
    padding-block: var(--ld-space-1);
    padding-inline: var(--ld-space-3);
    font-size: .6875rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .06em;
    background: var(--ld-chip-bg);
    border: 1px solid var(--ld-chip-border);
    color: var(--ld-chip-text);
}

.ld-chip--brand {
    background: var(--ld-chip-brand-bg);
    border-color: var(--ld-chip-brand-border);
    color: var(--ld-chip-brand-text);
}

.ld-chip--brand > i,
.ld-chip--brand > svg {
    color: var(--ld-chip-brand-icon);
}

.ld-badge {
    display: inline-flex;
    align-items: center;
    gap: var(--ld-space-1);
    border-radius: var(--ld-radius-full);
    padding-block: .125rem;
    padding-inline: var(--ld-space-2);
    font-size: .6875rem;
    font-weight: 600;
    background: var(--ld-badge-bg);
    border: 1px solid var(--ld-badge-border);
    color: var(--ld-text-secondary);
}

/* ── Buttons ─────────────────────────────────────────────────────────────── */
.ld-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--ld-radius-md);
    border: 1px solid var(--ld-btn-border);
    padding-block: var(--ld-space-2);
    padding-inline: var(--ld-space-4);
    font-size: .75rem;
    font-weight: 600;
    background: var(--ld-btn-bg);
    color: var(--ld-btn-text);
    box-shadow: var(--ld-btn-shadow);
    transition: background-color .15s ease, border-color .15s ease;
    cursor: pointer;
}

.ld-btn:hover {
    background: var(--ld-btn-hover-bg);
    border-color: var(--ld-btn-hover-border);
}

/* Disabled. The component had no disabled state at all: a disabled button
   rendered identically to an enabled one and still showed a pointer, so the
   only way to discover it was dead was to click it. Applies to every button
   variant, since they share the same failure. */
.ld-btn:disabled,
.ld-btn[aria-disabled="true"],
.ld-btn-ghost:disabled,
.ld-btn-ghost[aria-disabled="true"],
.ld-btn-danger:disabled,
.ld-btn-danger[aria-disabled="true"] {
    opacity: .5;
    cursor: not-allowed;
    box-shadow: none;
}
/* A disabled control must not answer hover either. */
.ld-btn:disabled:hover,
.ld-btn-ghost:disabled:hover,
.ld-btn-danger:disabled:hover {
    background: var(--ld-btn-bg);
    border-color: var(--ld-btn-border);
}
.ld-btn-ghost:disabled:hover { background: transparent; border-color: var(--ld-btn-ghost-border); }
.ld-btn-danger:disabled:hover { background: var(--ld-danger-btn-bg, var(--ld-error-bg)); }

.ld-btn:focus-visible {
    outline: none;
    box-shadow: var(--ld-focus-ring);
}

.ld-btn > i,
.ld-btn > svg {
    margin-inline-end: .4rem;
    font-size: var(--ld-fs-icon-sm);
}

.ld-btn--sm {
    padding-block: .375rem;
    padding-inline: var(--ld-space-3);
}

.ld-btn-ghost {
    display: inline-flex;
    align-items: center;
    border-radius: var(--ld-radius-full);
    border: 1px solid var(--ld-btn-ghost-border);
    padding-block: .375rem;
    padding-inline: var(--ld-space-3);
    font-size: .875rem;
    background: transparent;
    color: var(--ld-btn-ghost-text);
    cursor: pointer;
    transition: background-color .15s ease;
}

.ld-btn-ghost:hover {
    background: var(--ld-btn-ghost-hover-bg);
}

.ld-btn-ghost:focus-visible {
    outline: none;
    box-shadow: var(--ld-focus-ring-flat);
}

.ld-btn-danger {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--ld-radius-full);
    border: 1px solid transparent;
    padding-block: .375rem;
    padding-inline: .875rem;
    font-size: .8125rem;
    font-weight: 600;
    background: var(--ld-btn-danger-bg);
    color: #ffffff;
    cursor: pointer;
    transition: background-color .15s ease;
}

.ld-btn-danger:hover {
    background: var(--ld-btn-danger-hover-bg);
}

/* The danger button never had a focus rule here, so four pages wrote their own
   and a fifth left it on the browser's. It is filled, so it takes the offset
   ring like the other filled buttons. */
.ld-btn-danger:focus-visible {
    outline: none;
    box-shadow: var(--ld-focus-ring);
}

/* Table export/print actions are desktop utilities. On a phone they consume a
   full toolbar row for output flows that are not useful in the mobile work
   context. The semantic hook is the forward convention; the ID/DataTables
   selectors keep existing pages aligned from this one central rule. */
html.ld-phone .ld-export-actions,
html.ld-phone :is(div, span):has(> :is(button, a)[id*="Excel" i]):has(> :is(button, a)[id*="Print" i]):not(:has(> :is(button, a):not([id*="Excel" i]):not([id*="Pdf" i]):not([id*="Print" i]))),
html.ld-phone :is(button, a)[id$="BtnExcel" i],
html.ld-phone :is(button, a)[id$="BtnPdf" i],
html.ld-phone :is(button, a)[id$="BtnPrint" i],
html.ld-phone :is(button, a)[id*="btnExportExcel" i],
html.ld-phone :is(button, a)[id*="btnExportPdf" i],
html.ld-phone :is(button, a)[id*="ExportPdf" i],
html.ld-phone :is(button, a)[id$="Print" i],
html.ld-phone .dt-button.buttons-excel,
html.ld-phone .dt-button.buttons-pdf,
html.ld-phone .dt-button.buttons-print {
    display: none !important;
}

/* ── Tables ──────────────────────────────────────────────────────────────── */
/* Every conventional table inside the main application inherits this baseline.
   A specialized visualisation may opt out with data-ld-table-layout="custom". */
.ld-app-surface table:has(> thead):not([data-ld-table-layout="custom"]) {
    inline-size: 100%;
    border-collapse: separate;
    border-spacing: 0;
    font-size: .8125rem;
    color: var(--ld-text);
}
.ld-app-surface table:has(> thead):not([data-ld-table-layout="custom"]) > thead {
    position: sticky;
    inset-block-start: 0;
    z-index: 25;
    background: var(--ld-card-bg);
    box-shadow: 0 1px 0 var(--ld-divider);
}
.ld-app-surface table:has(> thead):not([data-ld-table-layout="custom"]) > thead th {
    padding-block: var(--ld-space-3);
    padding-inline: var(--ld-space-3);
    border-block-end: 1px solid var(--ld-table-head-border);
    background: var(--ld-table-head-bg);
    color: var(--ld-text-secondary);
    font-size: .6875rem;
    font-weight: 600;
    letter-spacing: .04em;
    text-align: start;
    text-transform: uppercase;
}
.ld-app-surface table:has(> thead):not([data-ld-table-layout="custom"]) > tbody > tr > td {
    padding-block: var(--ld-space-2);
    padding-inline: var(--ld-space-3);
    border-block-end: 1px solid var(--ld-table-row-border);
}
.ld-app-surface table:has(> thead):not([data-ld-table-layout="custom"]) > tbody > tr:hover > td {
    background: var(--ld-table-row-hover);
}

.ld-table-wrap {
    overflow: auto;
    max-block-size: max(10rem, calc(100dvh - 9rem));
    overscroll-behavior: contain;
    scrollbar-gutter: stable;
    border-radius: var(--ld-radius-lg);
    background: var(--ld-table-bg);
    border: 1px solid var(--ld-card-border);
}

/* Standard long-table viewport. The table, rather than the whole page, owns
   vertical scrolling so column context never disappears above the screen. */
.ld-table-viewport {
    overflow: auto;
    max-block-size: max(10rem, calc(100dvh - 9rem));
    overscroll-behavior: contain;
    scrollbar-gutter: stable;
}

/* Progressive AJAX regions keep their geometry while fresh server-rendered
   records are loaded. The busy cue is intentionally quiet and theme-neutral. */
.ld-ajax-region {
    transition: opacity var(--ld-motion-fast, 150ms) ease;
}

.ld-ajax-region[aria-busy="true"] {
    cursor: progress;
    opacity: .62;
    pointer-events: none;
}

@media (prefers-reduced-motion: reduce) {
    .ld-ajax-region {
        transition: none;
    }
}
.ld-table-wrap > table > thead,
.ld-table-viewport > table > thead {
    position: sticky;
    inset-block-start: 0;
    z-index: 25;
    background: var(--ld-card-bg);
    box-shadow: 0 1px 0 var(--ld-divider);
}

/* User-pinned columns stay at the logical leading edge during horizontal
   scrolling. JavaScript supplies the accumulated inline offset so any number
   of columns can be pinned, in either LTR or RTL. */
.ld-app-surface table .ld-table-pinned-column {
    position: sticky;
    inset-inline-start: var(--ld-pinned-inline-start, 0);
    z-index: 15;
    background: var(--ld-card-bg);
    background-clip: padding-box;
}

.ld-app-surface table thead .ld-table-pinned-column {
    z-index: 35;
    background: var(--ld-table-head-bg);
}

.ld-app-surface table tbody tr:hover .ld-table-pinned-column {
    background: var(--ld-table-row-hover);
}

.ld-app-surface table .ld-table-pinned-column--edge {
    box-shadow: 1px 0 0 var(--ld-divider), 8px 0 12px -12px var(--ld-text);
}

[dir="rtl"] .ld-app-surface table .ld-table-pinned-column--edge {
    box-shadow: -1px 0 0 var(--ld-divider), -8px 0 12px -12px var(--ld-text);
}

/* One shared chooser serves every conventional main-app table. Existing page
   choosers are upgraded in place; tables without one receive this compact
   control immediately before their scroll viewport. */
.ld-table-column-tools {
    position: relative;
    display: flex;
    justify-content: flex-end;
    padding-block: var(--ld-space-2);
    padding-inline: var(--ld-space-3);
    border-block-end: 1px solid var(--ld-divider);
    background: var(--ld-card-bg);
}

.ld-table-column-tools__toggle {
    display: inline-flex;
    align-items: center;
    gap: var(--ld-space-2);
    min-block-size: 2rem;
    padding-block: var(--ld-space-1);
    padding-inline: var(--ld-space-3);
    border: 1px solid var(--ld-input-border);
    border-radius: var(--ld-radius-full);
    color: var(--ld-text);
    background: transparent;
    font-size: .75rem;
    font-weight: 600;
    transition: background-color var(--ld-motion-fast, 150ms) ease;
}

.ld-table-column-tools__toggle:hover,
.ld-table-column-tools__toggle[aria-expanded="true"] {
    background: var(--ld-surface-accent);
}

.ld-table-column-tools__toggle:focus-visible,
.ld-table-column-menu__pin:focus-visible,
.ld-table-column-menu input:focus-visible {
    outline: none;
    box-shadow: var(--ld-focus-ring-flat);
}

.ld-table-column-tools__chevron {
    font-size: .625rem;
    transition: transform var(--ld-motion-fast, 150ms) ease;
}

.ld-table-column-tools__toggle[aria-expanded="true"] .ld-table-column-tools__chevron {
    transform: rotate(180deg);
}

.ld-table-column-menu {
    position: absolute;
    inset-block-start: calc(100% - var(--ld-space-1));
    inset-inline-end: var(--ld-space-3);
    z-index: 60;
    inline-size: min(15rem, calc(100vw - 2rem));
    max-block-size: min(24rem, calc(100dvh - 8rem));
    overflow: auto;
    padding: var(--ld-space-2);
    border: 1px solid var(--ld-glass-border, var(--ld-border));
    border-radius: var(--ld-radius-lg);
    color: var(--ld-text);
    /* The menu owns its type. It inherited, which meant the documented body
       step wherever an app stylesheet had set one — and the UA's 16px in the
       modules that ship no preflight, where the same menu rendered a size
       larger than every table it configures. */
    font-size: .8125rem;
    line-height: 1.25;
    background: var(--ld-glass-bg, var(--ld-surface-secondary));
    box-shadow: var(--ld-glass-shadow, var(--ld-card-shadow));
    backdrop-filter: blur(24px) saturate(1.4);
}

.ld-table-column-menu.hidden {
    display: none;
}

[dir="ltr"] .ld-table-column-menu {
    left: auto;
}

[dir="rtl"] .ld-table-column-menu {
    right: auto;
}

.ld-table-column-menu__head,
.ld-table-column-menu__row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--ld-space-2);
}

.ld-table-column-menu__head {
    padding-block: var(--ld-space-1) var(--ld-space-2);
    padding-inline: var(--ld-space-2);
    color: var(--ld-text-muted);
    font-size: .625rem;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
}

.ld-table-column-menu__row {
    padding: var(--ld-space-1);
    border-radius: var(--ld-radius-sm);
}

.ld-table-column-menu__row:hover {
    background: var(--ld-surface-accent);
}

.ld-table-column-menu__row > label {
    display: flex;
    align-items: center;
    flex: 1;
    gap: var(--ld-space-2);
    min-inline-size: 0;
    padding-block: var(--ld-space-1);
    padding-inline: var(--ld-space-2);
    cursor: pointer;
}

.ld-table-column-menu__row > label > span {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ld-table-column-menu input[type="checkbox"] {
    accent-color: var(--ld-brand);
}

/* The pin is a full-round ghost action pill — the table-action vocabulary
   DESIGN.md already documents — not a filled block. It paints its own ground:
   the AI modules ship no preflight, so an unstyled <button> wears the UA's
   gray ButtonFace, which is exactly the heavy gray blob this replaced. Same
   lesson as border-box: a component never relies on a reset it cannot see.
   Unpinned, the tack lies tilted and muted; hovering straightens it — the
   control previews its action instead of just brightening; pinned, it stands
   upright on the brand chip. */
.ld-table-column-menu__pin {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    inline-size: 1.75rem;
    block-size: 1.75rem;
    padding: 0;
    appearance: none;
    -webkit-appearance: none;
    background: transparent;
    border: 1px solid transparent;
    border-radius: var(--ld-radius-full);
    color: var(--ld-text-dim);
    cursor: pointer;
    transition: background-color .15s ease, color .15s ease, border-color .15s ease;
}

.ld-table-column-menu__pin > i {
    font-size: .6875rem;
    transform: rotate(45deg);
    transition: transform .15s ease;
}

.ld-table-column-menu__pin:hover {
    background: color-mix(in srgb, currentColor 10%, transparent);
    color: var(--ld-text);
}

.ld-table-column-menu__pin:hover > i {
    transform: rotate(0deg);
}

.ld-table-column-menu__pin[aria-pressed="true"] {
    border-color: var(--ld-chip-brand-border);
    color: var(--ld-chip-brand-text);
    background: var(--ld-chip-brand-bg);
}

.ld-table-column-menu__pin[aria-pressed="true"] > i {
    transform: rotate(0deg);
}

@media (prefers-reduced-motion: reduce) {
    .ld-table-column-menu__pin,
    .ld-table-column-menu__pin > i { transition: none; }
}

html.ld-phone .ld-table-column-tools {
    padding-inline: var(--ld-space-2);
}

html.ld-phone .ld-table-column-tools__toggle {
    min-block-size: 2.75rem;
}

html.ld-phone .ld-table-column-menu {
    inset-inline: var(--ld-space-2);
    inline-size: auto;
}

/* Existing pages commonly use Tailwind's horizontal-scroll utility around a
   table. Promote those wrappers to the same bounded data viewport centrally. */
.ld-app-surface :where(.overflow-x-auto, .table-responsive-wrapper):has(> table > thead) {
    overflow: auto;
    max-block-size: max(10rem, calc(100dvh - 9rem));
    overscroll-behavior: contain;
    scrollbar-gutter: stable;
}

.ld-table {
    width: 100%;
    border-collapse: collapse;
    font-size: .8125rem;
    color: var(--ld-text);
}

.ld-table thead th {
    background: var(--ld-table-head-bg);
    text-transform: uppercase;
    font-size: .6875rem;
    letter-spacing: .04em;
    text-align: start;
    padding-block: var(--ld-space-3);
    padding-inline: var(--ld-space-3);
    border-block-end: 1px solid var(--ld-table-head-border);
    color: var(--ld-text-secondary);
}

.ld-table td {
    padding-block: var(--ld-space-2);
    padding-inline: var(--ld-space-3);
    border-block-end: 1px solid var(--ld-table-row-border);
}

.ld-table tbody tr:hover td {
    background: var(--ld-table-row-hover);
}

/* ── Compact settings surface ──────────────────────────────────────────────
   Settings pages are high-frequency operational forms, not reading pages.
   The root hook makes the compact rhythm reusable across modules while the
   physical-phone branch retains the normal component touch floor. */
.ld-settings-page {
    inline-size: 100%;
}

.ld-settings-stack > * + * {
    margin-block-start: var(--ld-space-2);
}

.ld-settings-nav {
    min-inline-size: 0;
}

html:not(.ld-phone) .ld-settings-nav {
    display: flex;
    align-items: center;
    gap: var(--ld-space-3);
    padding-block: .625rem;
    padding-inline: var(--ld-space-3);
}

html:not(.ld-phone) .ld-settings-nav > .ld-section-title {
    flex: 0 0 auto;
}

html:not(.ld-phone) .ld-settings-nav .ld-settings-tabs {
    flex: 1 1 auto;
    min-inline-size: 0;
    margin-block-start: 0;
}

html:not(.ld-phone) .ld-settings-page :where(.ld-card):not(.ld-settings-card--flush) {
    padding-block: .625rem;
    padding-inline: var(--ld-space-3);
}

.ld-settings-card--flush {
    padding: 0;
    overflow: hidden;
}

html:not(.ld-phone) .ld-settings-page :where(.ld-table-column-tools) {
    padding-block: var(--ld-space-1);
    padding-inline: var(--ld-space-3);
}

html:not(.ld-phone) .ld-settings-page :where(.ld-table-column-tools__toggle) {
    min-block-size: 1.75rem;
    padding-block: .125rem;
}

html:not(.ld-phone) .ld-settings-page :where(.ld-table) thead th {
    padding-block: .4375rem;
}

html:not(.ld-phone) .ld-settings-page :where(.ld-table) td {
    padding-block: var(--ld-space-1);
    line-height: 1.2rem;
}

html:not(.ld-phone) .ld-settings-page :where(.ld-table) :where(.ld-input, .ld-select) {
    min-block-size: 1.75rem;
    padding-block: var(--ld-space-1);
}

html:not(.ld-phone) .ld-settings-page :where(.ld-table) label:has(input[type="checkbox"]) {
    min-block-size: 1.75rem;
}

html.ld-phone .ld-settings-page :where(.ld-tab, .ld-btn, .ld-btn-ghost, .ld-btn-danger, .ld-input, .ld-select),
html.ld-phone .ld-settings-page label:has(input[type="checkbox"]) {
    min-block-size: 2.75rem;
}

/* A per-column search row sits under the header. It is written as `td` in a
   `tbody`-style table and as `th` inside `<thead>` when the row must stay with
   the sticky header — both are the same component and take the same quieter
   ground, one step off the header so the two rows never read as one block.
   Scoped to .ld-app-surface as well as .ld-table because the conventional
   tables the shared sheet paints do not all carry the class. */
.ld-table .ld-filter-row :is(td, th),
.ld-app-surface table > thead > tr.ld-filter-row > th {
    background: var(--ld-table-filter-bg);
    /* The cell pads by one step, not two. A filter cell is not a header cell:
       it already contains a bordered input carrying its own padding, so the
       header's py-2 stacked on top of that box made the search strip the
       TALLEST row in the table — measured 41px against a 40px header and a
       35px body row, which reads as a second header rather than as a control
       belonging to the one above it. At one step the order comes back:
       header 40 > body 35 > filter 32. */
    padding-block: var(--ld-space-1);
}

.ld-col-search {
    width: 100%;
    border-radius: var(--ld-radius-sm);
    padding-block: var(--ld-space-1);
    padding-inline: var(--ld-space-2);
    font-size: .75rem;
    background: var(--ld-table-input-bg);
    border: 1px solid var(--ld-table-input-border);
    color: var(--ld-input-text);
}

/* The control shipped without its two states, so every page that used it had
   to supply them — and the ones that hand-rolled the input instead reached for
   Tailwind's blue-500/blue-600 on focus, which is not the ring DESIGN.md
   measures. A component owns its states. */
.ld-col-search::placeholder {
    color: var(--ld-input-placeholder);
}

.ld-col-search:focus {
    outline: none;
    border-color: var(--ld-focus-border);
    box-shadow: var(--ld-focus-ring);
}

.ld-action-pill {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--ld-radius-full);
    padding-block: var(--ld-space-1);
    padding-inline: var(--ld-space-2);
    background: var(--ld-action-pill-bg);
    border: 1px solid var(--ld-action-pill-border);
    color: var(--ld-brand);
    font-size: .75rem;
    cursor: pointer;
    transition: background-color .15s ease;
}

.ld-action-pill:hover {
    background: var(--ld-surface-accent);
}

/* ── Data-management page composition ─────────────────────────────────────
   Shared structure for Companies, Customers, Projects, Roles, Users and the
   other management lists. Page prefixes remain valid JavaScript hooks; paint
   and responsive composition belong here. */
.ld-data-card {
    inline-size: 100%;
    border-radius: var(--ld-radius-xl);
    border: 1px solid var(--ld-card-border);
    background: var(--ld-card-bg);
    box-shadow: var(--ld-section-shadow);
    color: var(--ld-text);
    overflow: visible;
}
.ld-data-toolbar {
    display: flex;
    align-items: center;
    gap: var(--ld-space-2);
    padding-block: var(--ld-space-3);
    padding-inline: var(--ld-space-4);
    border-block-end: 1px solid var(--ld-divider);
}
.ld-data-toolbar__icon { flex: none; color: var(--ld-stat-label); font-size: .875rem; }
.ld-data-toolbar__identity { min-inline-size: 0; }
.ld-data-description {
    margin-block-start: var(--ld-space-1);
    color: var(--ld-text-muted);
    font-size: .75rem;
    line-height: 1.45;
}
.ld-data-toolbar__actions {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: var(--ld-space-2);
    flex-wrap: nowrap;
    margin-inline-start: auto;
}
.ld-data-note {
    display: flex;
    align-items: flex-start;
    gap: var(--ld-space-2);
    padding-block: var(--ld-space-2);
    padding-inline: var(--ld-space-4);
    border-block-end: 1px solid var(--ld-divider);
    background: var(--ld-surface-subtle);
    color: var(--ld-text-muted);
    font-size: .6875rem;
    line-height: 1.5;
}
.ld-data-note > i { flex: none; margin-block-start: .2em; color: var(--ld-stat-label); }
.ld-data-note > p { margin: 0; max-inline-size: 90ch; }
.ld-data-note strong { color: var(--ld-text-value); font-weight: 700; }
.ld-toolbar-control {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: .4rem;
    min-block-size: 2rem;
    padding-block: .375rem;
    padding-inline: var(--ld-space-3);
    border: 1px solid var(--ld-btn-ghost-border);
    border-radius: var(--ld-radius-full);
    background: transparent;
    color: var(--ld-btn-ghost-text);
    font-size: .875rem;
    white-space: nowrap;
    cursor: pointer;
    transition: background-color .15s ease, border-color .15s ease;
}
.ld-toolbar-control:hover { background: var(--ld-btn-ghost-hover-bg); }
.ld-toolbar-control:focus-visible { outline: none; box-shadow: var(--ld-focus-ring-flat); }
.ld-toolbar-control > i,
.ld-toolbar-control > svg { color: var(--ld-stat-label); font-size: var(--ld-fs-icon-sm); }
.ld-toolbar-group {
    display: inline-flex;
    align-items: center;
    overflow: hidden;
    border: 1px solid var(--ld-btn-ghost-border);
    border-radius: var(--ld-radius-full);
    color: var(--ld-btn-ghost-text);
}
.ld-toolbar-group > :where(button, a) {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--ld-space-1);
    min-block-size: 2rem;
    padding-block: .375rem;
    padding-inline: var(--ld-space-3);
    border: 0;
    background: transparent;
    color: inherit;
    font-size: .875rem;
    cursor: pointer;
}
.ld-toolbar-group > :where(button, a):not(:last-child) { border-inline-end: 1px solid var(--ld-divider); }
.ld-toolbar-group > :where(button, a):hover { background: var(--ld-btn-ghost-hover-bg); }
.ld-toolbar-group > :where(button, a):focus-visible { outline: none; box-shadow: var(--ld-focus-ring-flat); }

/* Dense operational-table heading: identity remains a stable visual anchor
   while controls form deliberate, separately bounded groups. */
.ld-ops-toolbar {
    min-block-size: 3.75rem;
    padding-block: var(--ld-space-3);
    padding-inline: var(--ld-space-4);
    background: var(--ld-surface-subtle);
}
.ld-ops-toolbar__identity {
    display: flex;
    align-items: center;
    gap: var(--ld-space-2);
    min-inline-size: 12rem;
}
.ld-ops-toolbar__identity .ld-section-title { margin: 0; }
.ld-ops-toolbar__icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: none;
    inline-size: 2rem;
    block-size: 2rem;
    border: 1px solid color-mix(in srgb, var(--ld-stat-label) 24%, transparent);
    border-radius: var(--ld-radius-md);
    background: color-mix(in srgb, var(--ld-stat-label) 10%, transparent);
    color: var(--ld-stat-label);
    box-shadow: var(--ld-tile-shadow);
}
.ld-ops-toolbar__actions { gap: var(--ld-space-2); }
.ld-ops-toolbar .ld-toolbar-control,
.ld-ops-toolbar .ld-toolbar-group > :where(button, a) { min-block-size: 2.25rem; }
.ld-ops-toolbar .ld-toolbar-control--icon {
    inline-size: 2.25rem;
    padding-inline: 0;
}
.ld-export-icon--excel { color: var(--ld-success-text); }
.ld-export-icon--pdf { color: var(--ld-error-text); }
.ld-export-icon--print { color: var(--ld-stat-label); }
.ld-count-chip {
    display: inline-flex;
    align-items: center;
    gap: var(--ld-space-1);
    padding-block: .125rem;
    padding-inline: var(--ld-space-3);
    border: 1px solid var(--ld-chip-brand-border);
    border-radius: var(--ld-radius-full);
    background: var(--ld-chip-brand-bg);
    color: var(--ld-chip-brand-text);
    font-size: .75rem;
    font-weight: 700;
    white-space: nowrap;
}
.ld-filter-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--ld-space-4);
    padding-block: var(--ld-space-2);
    padding-inline: var(--ld-space-4);
    border-block-end: 1px solid var(--ld-divider);
    background: var(--ld-table-filter-bg);
    color: var(--ld-text-secondary);
}
.ld-context-menu {
    position: fixed;
    z-index: 9999;
    display: none;
    min-inline-size: 13rem;
    padding-block: .35rem;
    border: 1px solid var(--ld-glass-border);
    border-radius: var(--ld-radius-lg);
    background: var(--ld-glass-bg);
    color: var(--ld-text);
    box-shadow: var(--ld-glass-shadow);
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    font-size: .8125rem;
    line-height: 1;
}
.ld-context-menu.open { display: block; }
.ld-context-menu__item {
    display: flex;
    align-items: center;
    gap: .6rem;
    inline-size: 100%;
    padding-block: .5rem;
    padding-inline: .85rem;
    border: 0;
    background: transparent;
    color: inherit;
    text-align: start;
    white-space: nowrap;
    cursor: pointer;
}
.ld-context-menu__item:hover { background: var(--ld-multi-hover); }
.ld-context-menu__item:focus-visible { outline: none; box-shadow: var(--ld-focus-ring-flat); }
.ld-context-menu__item > i { inline-size: 1rem; text-align: center; font-size: .75rem; }
.ld-context-menu__item--danger { color: var(--ld-error-text); }
.ld-context-menu__item--danger:hover { background: var(--ld-error-bg); }
.ld-context-menu__separator {
    block-size: 1px;
    margin-block: .3rem;
    margin-inline: .6rem;
    background: var(--ld-divider);
}
.ld-mobile-record-list { display: none; }
.ld-mobile-record {
    min-inline-size: 0;
    padding: var(--ld-space-3);
    border: 1px solid var(--ld-stat-border);
    border-radius: var(--ld-radius-lg);
    background: var(--ld-stat-bg);
    color: var(--ld-text);
    box-shadow: var(--ld-tile-shadow);
}
.ld-mobile-record__head { display: flex; align-items: center; gap: var(--ld-space-2); min-inline-size: 0; }
.ld-mobile-record__grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: .5rem var(--ld-space-3);
    margin-block-start: var(--ld-space-2);
}
.ld-mobile-record__label {
    color: var(--ld-text-muted);
    font-size: .625rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .05em;
}
.ld-mobile-record__value { min-inline-size: 0; color: var(--ld-text); font-size: .8125rem; overflow-wrap: anywhere; }
.ld-mobile-record__actions {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--ld-space-2);
    margin-block-start: var(--ld-space-2);
    padding-block-start: var(--ld-space-2);
    border-block-start: 1px solid var(--ld-divider);
}
.ld-mobile-record__actions > :where(a, button, form),
.ld-mobile-record__actions > form > button { inline-size: 100%; justify-content: center; }
.ld-mobile-record__actions > :only-child { grid-column: 1 / -1; }
html.ld-phone .ld-data-card { border-radius: var(--ld-radius-xl); overflow: hidden; }
html.ld-phone .ld-data-toolbar {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    align-items: center;
    gap: var(--ld-space-2);
    padding: var(--ld-space-3);
}
html.ld-phone .ld-data-toolbar__actions {
    grid-column: 1 / -1;
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    inline-size: 100%;
    margin-inline-start: 0;
    gap: var(--ld-space-2);
}
html.ld-phone .ld-data-toolbar__actions > :where(a, button, div) { min-inline-size: 0; }
html.ld-phone .ld-data-toolbar__actions .ld-toolbar-control { inline-size: 100%; min-block-size: 2.75rem; padding-inline: var(--ld-space-2); }
html.ld-phone .ld-data-toolbar__actions .ld-count-chip { min-block-size: 2.75rem; justify-content: center; }
html.ld-phone .ld-data-description { font-size: .6875rem; }
html.ld-phone .ld-data-note { padding: var(--ld-space-3); font-size: .75rem; }
html.ld-phone .ld-filter-bar { flex-direction: column; align-items: stretch; gap: var(--ld-space-2); padding: var(--ld-space-3); }
html.ld-phone .ld-filter-bar > * { inline-size: 100%; }
html.ld-phone .ld-filter-bar :where(input, select) { min-inline-size: 0; inline-size: 100%; min-block-size: 2.75rem; }
html.ld-phone .ld-desktop-table { display: none; }
html.ld-phone .ld-mobile-record-list { display: grid; gap: var(--ld-space-3); padding: var(--ld-space-3); }
html.ld-phone .ld-mobile-record__actions :where(a, button) { min-block-size: 2.75rem; }
@media (prefers-reduced-motion: reduce) {
    .ld-toolbar-control,
    .ld-context-menu__item { transition: none; }
}

/* ── Forms ───────────────────────────────────────────────────────────────── */
.ld-form-card {
    border-radius: var(--ld-radius-xl);
    border: 1px solid var(--ld-form-card-border);
    background: var(--ld-form-card-bg);
    box-shadow: var(--ld-form-card-shadow);
    padding: var(--ld-card-pad);
}
.ld-form-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--ld-space-4);
    padding-block-end: var(--ld-space-3);
}
.ld-form-actions { display: flex; align-items: center; gap: var(--ld-space-2); }
.ld-form-divider { border-block-start: 1px solid var(--ld-divider); }
.ld-form-body { padding-block-start: var(--ld-space-3); border-block-start: 1px solid var(--ld-divider); }
.ld-form-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--ld-space-3); }
.ld-form-grid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.ld-form-span-all { grid-column: 1 / -1; }
.ld-form-section {
    padding: var(--ld-space-3);
    border: 1px solid var(--ld-stat-border);
    border-radius: var(--ld-radius-lg);
    background: var(--ld-stat-bg);
}
.ld-detail-list {
    display: grid;
    gap: var(--ld-space-2);
    margin-block: var(--ld-space-4);
}
.ld-detail-row {
    display: grid;
    grid-template-columns: minmax(8rem, .35fr) minmax(0, 1fr);
    align-items: baseline;
    gap: var(--ld-space-3);
    padding-block: var(--ld-space-2);
    border-block-end: 1px solid var(--ld-divider);
}
.ld-detail-row__label { color: var(--ld-text-secondary); font-size: .8125rem; font-weight: 500; }
.ld-detail-row__value { min-inline-size: 0; color: var(--ld-text); overflow-wrap: anywhere; }
.role-tab-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
    gap: var(--ld-space-1);
}
.role-tab-grid .page-tab {
    min-inline-size: 0;
    justify-content: center;
    border-radius: var(--ld-radius-sm);
    padding-block: var(--ld-space-2);
    padding-inline: var(--ld-space-2);
    overflow-wrap: anywhere;
}

/* Migration aliases. These selectors deliberately outrank old page-scoped
   single-class theme ladders while every Razor form moves to the semantic
   classes above. This is one source of truth, not another local skin. */
html[data-theme] .ld-app-surface :is(.usr-form-card, .role-form-card) {
    border-color: var(--ld-form-card-border);
    border-radius: var(--ld-radius-xl);
    background: var(--ld-form-card-bg);
    box-shadow: var(--ld-form-card-shadow);
    color: var(--ld-text);
}
html[data-theme] .ld-app-surface :is(.usr-form-label, .role-form-label) {
    color: var(--ld-form-label);
}
html[data-theme] .ld-app-surface :is(.usr-form-input, .usr-form-input-readonly, .role-form-input) {
    min-inline-size: 0;
    min-block-size: 2.25rem;
    border-color: var(--ld-input-border);
    border-radius: var(--ld-radius-md);
    background: var(--ld-input-bg);
    color: var(--ld-input-text);
}
html[data-theme] .ld-app-surface :is(.usr-form-input, .role-form-input):focus {
    outline: none;
    border-color: var(--ld-focus-border);
    box-shadow: var(--ld-focus-ring);
}
html[data-theme] .ld-app-surface :is(.usr-form-input-readonly, .role-form-input[readonly]) {
    background: var(--ld-surface-secondary);
    color: var(--ld-text-muted);
}
html[data-theme] .ld-app-surface .role-section-card {
    border-color: var(--ld-stat-border);
    border-radius: var(--ld-radius-lg);
    background: var(--ld-stat-bg);
    color: var(--ld-text);
    box-shadow: var(--ld-tile-shadow);
}
html.ld-phone .ld-form-card { padding: var(--ld-space-3); }
html.ld-phone .ld-form-head { flex-direction: column; align-items: stretch; gap: var(--ld-space-3); }
html.ld-phone .ld-form-actions { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
html.ld-phone .ld-form-actions > :where(a, button) { min-block-size: 2.75rem; inline-size: 100%; justify-content: center; }
html.ld-phone .ld-form-grid,
html.ld-phone .ld-form-grid--3 { grid-template-columns: minmax(0, 1fr); }
html.ld-phone .ld-form-span-all { grid-column: auto; }
html.ld-phone .ld-detail-row { grid-template-columns: minmax(0, 1fr); gap: var(--ld-space-1); }
html.ld-phone .ld-app-surface :is(.usr-header-row, .role-header-row) {
    flex-direction: column;
    align-items: stretch;
}
html.ld-phone .ld-app-surface :is(.usr-header-actions, .role-header-actions) {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    inline-size: 100%;
}
html.ld-phone .ld-app-surface :is(.usr-form-grid, .role-form-grid) {
    grid-template-columns: minmax(0, 1fr) !important;
}
html.ld-phone .role-tab-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
html.ld-phone .role-tab-grid .page-tab {
    min-block-size: 2.75rem;
    font-size: .6875rem;
    line-height: 1.2;
    white-space: normal;
}
html.ld-phone .role-tab-grid .page-tab > .opacity-60 { display: none; }

.ld-label {
    display: block;
    color: var(--ld-form-label);
    font-size: .875rem;
    font-weight: 500;
    margin-block-end: .375rem;
}

.ld-input {
    width: 100%;
    /* Every input type lands on one height. A date input carries a built-in
       picker button and renders ~2px taller than a number input, which is
       enough to break the baseline of a form row. */
    min-block-size: 2.25rem;
    /* Lets an input shrink inside a grid cell: date inputs have an intrinsic
       min-content width that otherwise pushes the row into overflow. */
    min-inline-size: 0;
    border-radius: var(--ld-radius-md);
    padding-block: var(--ld-space-2);
    padding-inline: var(--ld-space-3);
    font-size: .875rem;
    background: var(--ld-input-bg);
    border: 1px solid var(--ld-input-border);
    color: var(--ld-input-text);
}

.ld-input::placeholder {
    color: var(--ld-input-placeholder);
}

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

.ld-input:disabled {
    opacity: .5;
}

.ld-select {
    width: 100%;
    border-radius: var(--ld-radius-lg);
    padding-block: var(--ld-space-2);
    padding-inline: var(--ld-space-3);
    font-size: .875rem;
    background: var(--ld-select-bg);
    border: 1px solid var(--ld-select-border);
    color: var(--ld-select-text);
}

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

/* ── Status banners ──────────────────────────────────────────────────────── */
.ld-alert {
    border-radius: var(--ld-radius-lg);
    border: 1px solid;
    padding-block: .625rem;
    padding-inline: .875rem;
    font-size: .875rem;
}

.ld-alert--success {
    background: var(--ld-success-bg);
    border-color: var(--ld-success-border);
    color: var(--ld-success-text);
}

.ld-alert--error {
    background: var(--ld-error-bg);
    border-color: var(--ld-error-border);
    color: var(--ld-error-text);
}

.ld-alert--warn {
    background: var(--ld-warn-bg);
    border-color: var(--ld-warn-border);
    color: var(--ld-warn-text);
}

/* An alert states its kind in FORM as well as colour: the icon carries it for
   anyone who does not read the tint, and it is the part that survives a
   colour-blind reader, a greyscale print and a screenshot. */
.ld-alert__title {
    display: flex;
    align-items: center;
    gap: var(--ld-space-2);
    font-weight: 600;
    font-size: .875rem;
}
.ld-alert__title > i { flex: 0 0 auto; font-size: .875rem; }
.ld-alert__body {
    margin: .375rem 0 0;
    padding-inline-start: var(--ld-space-5);
    font-size: .8125rem;
    line-height: 1.45;
    /* Inherits the alert's colour at a lower weight rather than reverting to
       body text, so the whole block still reads as one warning. */
    opacity: .92;
}
.ld-alert__body > li + li { margin-block-start: .125rem; }

/* ── Star / favorite ─────────────────────────────────────────────────────── */
.ld-star-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--ld-radius-md);
    padding: .375rem;
    /* 26x30 measured — under even the 24px WCAG 2.5.8 floor once borders are
       counted. The glyph keeps its size; the target grows. */
    min-inline-size: 2rem;
    min-block-size: 2rem;
    background: var(--ld-star-btn-bg);
    border: 1px solid var(--ld-star-btn-border);
    color: var(--ld-star-muted);
    cursor: pointer;
    transition: color .15s ease, background-color .15s ease;
}

.ld-star-btn.is-on {
    background: var(--ld-star-on-bg);
    border-color: var(--ld-star-on-border);
    color: var(--ld-star-on-text);
}

/* ── Modal layer ───────────────────────────────────────────────────────────
   The frozen Tailwind bundle never generated the arbitrary z utilities the
   modals use (z-[9999] on the Headscale phone-setup / Projects dialogs and
   the global nav loader, z-[80] on move-node), so those overlays had NO
   z-index and the sticky table header (z 25) painted straight across the
   dialog (2026-08-19). Declared here once; the shell's top bar stays above
   the page at its own level, which is intended. */
.z-\[9999\] { z-index: 9999; }
.z-\[80\]   { z-index: 80; }
.ld-modal-layer { position: fixed; inset: 0; z-index: 9999; }

/* ── Glass dropdown / popover ────────────────────────────────────────────── */
.ld-glass {
    border-radius: var(--ld-radius-xl);
    background: var(--ld-glass-bg);
    border: 1px solid var(--ld-glass-border);
    box-shadow: var(--ld-glass-shadow);
    backdrop-filter: blur(24px) saturate(1.4);
    -webkit-backdrop-filter: blur(24px) saturate(1.4);
}

/* ── Motion hygiene ──────────────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
    .ld-btn,
    .ld-btn-ghost,
    .ld-btn-danger,
    .ld-tab,
    .ld-action-pill,
    .ld-star-btn {
        transition: none;
    }
}

/* ── Date field + calendar (ldata.date.js) ───────────────────────────────── */
/* The field wrapper is display:block so a stretching input keeps stretching;
   the trigger is pinned to the inline-end edge, which mirrors for RTL. */
.ld-date-field {
    position: relative;
    display: block;
    min-inline-size: 0;
}
.ld-date-field > .ld-input {
    inline-size: 100%;
    padding-inline-end: 2.25rem;         /* room for the trigger */
}
.ld-date-field__btn {
    position: absolute;
    inset-block-start: 50%;
    inset-inline-end: .375rem;
    transform: translateY(-50%);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    inline-size: 1.75rem;
    block-size: 1.75rem;
    padding: 0;
    border: 0;
    border-radius: var(--ld-radius-sm, .375rem);
    background: transparent;
    color: var(--ld-text-muted);
    cursor: pointer;
}
.ld-date-field__btn:hover { color: var(--ld-brand); background: var(--ld-surface-secondary); }
.ld-date-field__btn:focus-visible {
    outline: none;
    box-shadow: var(--ld-focus-ring-flat);
    color: var(--ld-brand);
}

.ld-datepicker,
.ld-datepicker *,
.ld-date-field,
.ld-date-field__btn {
    /* Declared explicitly: the shell ships no Tailwind reset, so without this
       the panel is 302px wide there and 280px inside a module that has one. */
    box-sizing: border-box;
}
.ld-datepicker {
    z-index: 1200;
    inline-size: 17.5rem;
    padding: .625rem;
    border-radius: var(--ld-radius-lg);
    background: var(--ld-glass-bg);
    border: 1px solid var(--ld-glass-border);
    box-shadow: var(--ld-glass-shadow);
    backdrop-filter: blur(24px) saturate(1.4);
    color: var(--ld-text);
    font-size: .8125rem;
}
.ld-dp__head {
    display: grid;
    grid-template-columns: auto 1fr auto;
    align-items: center;
    gap: var(--ld-space-1);
    margin-block-end: var(--ld-space-2);
}
.ld-dp__title {
    text-align: center;
    font-weight: 600;
    color: var(--ld-text);
}
.ld-dp__nav {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    inline-size: 1.75rem;
    block-size: 1.75rem;
    padding: 0;
    border: 1px solid transparent;
    border-radius: var(--ld-radius-sm, .375rem);
    background: transparent;
    color: var(--ld-text-secondary);
    cursor: pointer;
}
.ld-dp__nav:hover { background: var(--ld-surface-secondary); color: var(--ld-brand); }
.ld-dp__nav:focus-visible { outline: none; box-shadow: var(--ld-focus-ring-flat); }
/* "Previous" points at the start of the line in either direction. */
.ld-dp__nav[data-nav="1"] svg { transform: scaleX(-1); }
[dir="rtl"] .ld-dp__nav[data-nav="-1"] svg { transform: scaleX(-1); }
[dir="rtl"] .ld-dp__nav[data-nav="1"] svg { transform: scaleX(1); }

.ld-dp__grid {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    /* 4px, not 2: a focused day rings 2px outward and the arrow keys move the
       ring across this grid on every keystroke, so the gutter has to hold it
       without touching the neighbouring date. */
    gap: 4px;
}
.ld-dp__dow {
    text-align: center;
    font-size: .6875rem;
    font-weight: 600;
    color: var(--ld-text-muted);
    padding-block: var(--ld-space-1);
}
.ld-dp__day {
    aspect-ratio: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    border: 1px solid transparent;
    border-radius: var(--ld-radius-sm, .375rem);
    background: transparent;
    color: var(--ld-text);
    font-variant-numeric: tabular-nums;
    font-size: .8125rem;
    cursor: pointer;
}
.ld-dp__day:hover:not([disabled]) { background: var(--ld-surface-secondary); }
.ld-dp__day:focus-visible { outline: none; box-shadow: var(--ld-focus-ring-flat); }
/* The selected day is the one the picker puts tabindex="0" on, so it is exactly
   where the keyboard lands - and it is the one cell with a brand fill. On light
   the ring colour IS the seed brand, so ring and fill measured 1.00: focused and
   unfocused were the same picture. A white hairline inside the fill carries the
   state there; the outer ring still does the work against the popover. */
.ld-dp__day[aria-selected="true"]:focus-visible {
    box-shadow: var(--ld-focus-ring-flat), inset 0 0 0 2px #ffffff;
}
.ld-dp__day[data-today="1"] { border-color: var(--ld-brand); font-weight: 600; }
.ld-dp__day[aria-selected="true"] {
    background: var(--ld-brand);
    border-color: var(--ld-brand);
    color: #ffffff;
    font-weight: 600;
}

html.ld-phone .ld-ops-toolbar {
    align-items: flex-start;
    gap: var(--ld-space-3);
    padding: var(--ld-space-3);
}
html.ld-phone .ld-ops-toolbar__identity { min-inline-size: 0; }
html.ld-phone .ld-ops-toolbar__actions {
    inline-size: 100%;
    justify-content: flex-start;
    flex-wrap: wrap;
    margin-inline-start: 0;
}
.ld-dp__day[disabled] { color: var(--ld-text-dim); opacity: .45; cursor: default; }

.ld-dp__foot {
    display: flex;
    gap: .375rem;
    margin-block-start: var(--ld-space-2);
    padding-block-start: var(--ld-space-2);
    border-block-start: 1px solid var(--ld-divider);
}
.ld-dp__act {
    flex: 1;
    padding-block: .3125rem;
    border: 1px solid var(--ld-btn-ghost-border);
    border-radius: var(--ld-radius-sm, .375rem);
    background: transparent;
    color: var(--ld-text-secondary);
    font-size: .75rem;
    cursor: pointer;
}
.ld-dp__act:hover { background: var(--ld-btn-ghost-hover-bg); color: var(--ld-text); }
.ld-dp__act:focus-visible { outline: none; box-shadow: var(--ld-focus-ring-flat); }

/* ── Navigation rail ─────────────────────────────────────────────────────────
   ONE rail, eleven modules. Every module already agreed on the structure —
   aside[data-sidebar="main"] > nav > details.sidebar-group > summary + .submenu
   — and then each painted it privately: ten pinned the surface to bg-slate-700
   and the selected item to bg-slate-600, while the shell carried a further
   fourteen html[data-theme="light"] !important patches to undo that in light.

   The paint now lives here and nowhere else. The structural hooks stay exactly
   as they were (.submenu, .sb-text, .caret, .sidebar-group) because eleven
   layouts' collapse logic keys off them; only the colour left the modules.

   Two class hooks are new: .ld-rail-item on every summary and link, and
   .is-active on the one the operator is looking at.
   ------------------------------------------------------------------------- */
aside[data-sidebar="main"] {
    background: var(--ld-rail-bg);
    border-inline-end: 1px solid var(--ld-rail-border);
    color: var(--ld-rail-text);
}
aside[data-sidebar="main"] a { color: inherit; text-decoration: none; }

/* Items. The hover veil is built from currentColor, so it lifts on a light
   rail and on a black one without a theme block of its own. */
.ld-rail-item {
    border-radius: var(--ld-radius-lg);
    transition: background-color .15s ease, color .15s ease;
}
.ld-rail-item:hover { background-color: var(--ld-rail-hover-bg); }
.ld-rail-item:focus-visible { outline: none; box-shadow: var(--ld-focus-ring-flat); }

/* "You are here." The tint, the hairline and the clamped accent are the
   selected-tab family — the rail is a tab strip stood on its end. */
.ld-rail-item.is-active {
    background: var(--ld-rail-active-bg);
    box-shadow: inset 0 0 0 1px var(--ld-rail-active-border);
    color: var(--ld-rail-active-text);
}
.ld-rail-item.is-active i { color: inherit; }

/* The open group's children, with a neutral tree guide. Logical properties
   throughout: the RTL mirror is the browser's job, not a second rule block. */
aside[data-sidebar="main"] .submenu {
    position: static;
    border-inline-start: 2px solid var(--ld-rail-guide);
    margin-inline-start: var(--ld-space-4);
    padding-block: var(--ld-space-1);
    padding-inline: var(--ld-space-2) 0;
    background: var(--ld-rail-sub-bg);
    border-radius: var(--ld-radius-md);
}
aside[data-sidebar="main"] .submenu .ld-rail-item { padding-inline-start: var(--ld-space-5); }

/* Group separators, when a rail uses them. */
aside[data-sidebar="main"] .ld-rail-sep {
    border-block-start: 1px solid var(--ld-rail-guide);
    margin-block: var(--ld-space-2);
    margin-inline: var(--ld-space-3);
}

/* Caret. Rotation is the open state, so it belongs to the component. */
aside[data-sidebar="main"] summary { list-style: none; cursor: pointer; }
aside[data-sidebar="main"] summary::-webkit-details-marker { display: none; }
aside[data-sidebar="main"] .caret { transition: transform .25s ease; }
aside[data-sidebar="main"] details[open] > summary .caret { transform: rotate(180deg); }
@media (prefers-reduced-motion: reduce) {
    aside[data-sidebar="main"] .caret,
    .ld-rail-item { transition: none; }
}

/* Collapsed to icons, the group icon is the only thing left to read, so it
   takes the same accent that marks the selected item. Three layouts hard-coded
   #38bdf8 here and a fourth used --ld-sky; the rail owns it now. */
body.sidebar-collapsed aside[data-sidebar="main"] nav summary i {
    color: var(--ld-rail-active-border);
}

/* Collapsed to icons. Twelve layouts carried this block; five were byte-identical
   and the rest were that same core plus a module delta. The core lives here, so a
   module now writes only what is genuinely its own — Video's 4rem, Cyber's row
   geometry, IMS's customer card. Module <style> renders after this sheet, so any
   delta still wins. */
body.sidebar-collapsed [data-sidebar="main"] {
    /* !important: the rail's own width rule renders later, from the module partial. */
    width: var(--ld-rail-collapsed-w) !important;
    min-width: var(--ld-rail-collapsed-w) !important;
    overflow: visible;
}
body.sidebar-collapsed [data-sidebar="main"] .sb-text,
body.sidebar-collapsed [data-sidebar="main"] .caret-col { display: none !important; }
body.sidebar-collapsed [data-sidebar="main"] summary,
body.sidebar-collapsed [data-sidebar="main"] summary > div { justify-content: center; }
body.sidebar-collapsed [data-sidebar="main"] summary i { font-size: 1.15rem; }
body.sidebar-collapsed [data-sidebar="main"] nav i { text-align: center; }

/* The tree guide has nothing left to indent; it becomes a rule between groups. */
body.sidebar-collapsed [data-sidebar="main"] .submenu {
    border-inline-start: none;
    margin-inline-start: 0;
    padding-inline-start: 0;
    margin-block-start: .35rem;
    padding-block: .35rem .15rem;
    border-block-start: 1px solid var(--ld-rail-guide);
    background: transparent;
    border-radius: 0;
}
body.sidebar-collapsed [data-sidebar="main"] .submenu .ld-rail-item,
body.sidebar-collapsed [data-sidebar="main"] .submenu a,
body.sidebar-collapsed [data-sidebar="main"] .submenu button {
    justify-content: center;
    padding-inline: 0;
}

/* Rail icons. The markup asked for sizes that mostly do not exist: w-3 is absent
   from six of the eight frozen bundles and text-[10px] from Monitoring's, so the
   glyph fell back to 16px while the group icon above it rendered at 11 — the
   child read larger than its parent, and without the fixed box the labels never
   formed a column. Sized here, in one place, for all eleven rails. */
/* Descendant, not child. Enumerating the wrappers missed the AI-agent shells,
   which nest the glyph in <span class="summary-main">: their group icon rendered
   12px in a 14px box while the CHILD beneath it rendered 13 in 16 — the same
   inverted hierarchy this rule exists to prevent, surviving inside the rule. */
aside[data-sidebar="main"] .ld-rail-item i {
    inline-size: 1rem;
    flex: none;
    font-size: var(--ld-rail-icon);
    text-align: center;
}
aside[data-sidebar="main"] .submenu .ld-rail-item i {
    font-size: var(--ld-rail-icon-sub);
}
/* The caret is chrome, not an item icon: it keeps its own smaller size and is
   excluded from the box above by living in .caret-col. */
aside[data-sidebar="main"] .caret-col > i { font-size: .75rem; line-height: 1; }

/* Group separation. This was markup: whoever wrote a rail hand-placed
   <div class="border-t border-slate-600/40"> between some groups. Monitoring
   got three for its four groups; the shell has thirteen groups and got none,
   which is why the same tree read as a structured list in one module and a flat
   one in another. The rule is generated now, so a rail cannot forget it and
   cannot double it.

   It sits on the group that FOLLOWS something, so the first item in the rail
   never opens with a stray line. */
/* The sibling pairs are enumerated rather than written as `* + details`:
   Tailwind's space-y-0.5 on the nav is `.space-y-0\.5 > :not([hidden]) ~
   :not([hidden])`, three classes, and it was beating the shorter selector — the
   rule landed 2px under the previous group instead of 8. */
aside[data-sidebar="main"] nav > a + details.sidebar-group,
aside[data-sidebar="main"] nav > button + details.sidebar-group,
aside[data-sidebar="main"] nav > div + details.sidebar-group,
aside[data-sidebar="main"] nav > details.sidebar-group + details.sidebar-group,
aside[data-sidebar="main"] nav > details.sidebar-group + a,
aside[data-sidebar="main"] nav > details.sidebar-group + button {
    border-block-start: 1px solid var(--ld-rail-guide);
    margin-block-start: var(--ld-space-2);
    padding-block-start: var(--ld-space-2);
}

/* The rail's footer: the one row that is identity rather than navigation — the
   active company, the active customer. It is the LAST flex child of the rail, so
   it needs no absolute positioning and cannot overlap the nav above it; WM had
   it at position:absolute with a matching padding-bottom reserved in the nav,
   and the two drifted. */
aside[data-sidebar="main"] .ld-rail-foot {
    flex: none;
    border-block-start: 1px solid var(--ld-rail-guide);
    padding: var(--ld-space-2) var(--ld-space-2) calc(var(--ld-space-3) + env(safe-area-inset-bottom));
}
/* The accents a rail is allowed: the selected-state colour for an identity
   value, the warning colour for its absence. */
aside[data-sidebar="main"] .ld-rail-accent { color: var(--ld-rail-active-text); }
aside[data-sidebar="main"] .ld-rail-warn   { color: var(--ld-warn-text); }

/* ── The unstyled form control ───────────────────────────────────────────────
   @tailwindcss/forms paints every text-entry control in its base layer: an
   opaque white fill and a mid-gray border, neither of which is ours. A control that nobody classed therefore
   renders as a WHITE box with a mid-gray border on both dark themes — 1,608 of
   them across ten modules, because the base is invisible until you look at a
   rendered page in a dark theme.

   The design system owns what an unstyled control looks like. `html[data-theme]`
   carries one more attribute than the base's bare `[type=…]`, so this wins the
   cascade without !important, and `.ld-input` — declared later in this same
   sheet — still wins over it.

   Checkboxes, radios and buttons are excluded: the base treats them separately
   and they are not text-entry surfaces. */
html[data-theme] [type="text"],
html[data-theme] [type="email"],
html[data-theme] [type="url"],
html[data-theme] [type="password"],
html[data-theme] [type="number"],
html[data-theme] [type="date"],
html[data-theme] [type="datetime-local"],
html[data-theme] [type="month"],
html[data-theme] [type="week"],
html[data-theme] [type="time"],
html[data-theme] [type="search"],
html[data-theme] [type="tel"],
html[data-theme] [multiple],
html[data-theme] input:where(:not([type])),
html[data-theme] select,
html[data-theme] textarea {
    background-color: var(--ld-input-bg);
    border-color: var(--ld-input-border);
    color: var(--ld-input-text, var(--ld-text));
}
html[data-theme] [type="text"]::placeholder,
html[data-theme] [type="email"]::placeholder,
html[data-theme] [type="search"]::placeholder,
html[data-theme] [type="number"]::placeholder,
html[data-theme] input:where(:not([type]))::placeholder,
html[data-theme] textarea::placeholder {
    color: var(--ld-input-placeholder);
}
/* A select narrow enough to clip its own value is a control that lies about
   its state: "MAIN-SERVER — latest Brainium candidate (qwen3.8…" reads as a
   different answer than the one selected. Browsers size a select to its widest
   option only while nothing squeezes it; inside a flex row, or under a
   max-width someone typed to keep a form tidy, the chosen text is the first
   thing cut.

   min() is the whole rule: at least as wide as the widest option, never wider
   than what the container can give. Element-level specificity on purpose — any
   page that deliberately sets a width, and every full-width select in a filter
   bar or table, still wins. */
select:not([multiple]):not([size]) {
    min-inline-size: min(100%, max-content);
}
/* Except in a table cell, where the select is one compact control repeated down
   a column: growing it to its widest option widens the column for every row and
   pushes the table into a horizontal scroll. A row control keeps its own width
   and its title/option list carries the rest. */
:is(td, th) select {
    min-inline-size: 0;
}

/* The base's focus ring is a Tailwind blue that belongs to no theme. */
html[data-theme] [type="text"]:focus,
html[data-theme] [type="email"]:focus,
html[data-theme] [type="url"]:focus,
html[data-theme] [type="password"]:focus,
html[data-theme] [type="number"]:focus,
html[data-theme] [type="date"]:focus,
html[data-theme] [type="search"]:focus,
html[data-theme] [type="tel"]:focus,
html[data-theme] input:where(:not([type])):focus,
html[data-theme] select:focus,
html[data-theme] textarea:focus {
    outline: none;
    border-color: var(--ld-focus-border);
    box-shadow: var(--ld-focus-ring-flat);
}

/* The page header. Ten Work pages spelled the same h1 inline and nine the same
   lead paragraph under it; both were already tokenised, so the only thing
   missing was a name. */
.ld-page-title {
    font-size: 1.5rem;
    font-weight: 600;
    color: var(--ld-text-value);
    text-wrap: balance;
}
.ld-page-lead {
    margin-block-start: var(--ld-space-1);
    font-size: .875rem;
    color: var(--ld-text-secondary);
    max-inline-size: 75ch;
}

/* ── The page shell ──────────────────────────────────────────────────────────
   A page is full-bleed inside the content area, and its content sits in ONE
   outer card that opens with an icon, a title and a line saying what the page
   is for. Work carried three different answers to this — the full-bleed card,
   a `w-full ps-1 md:ps-2` variant, and eighteen pages pinned to
   `max-width:800…1200px; margin:0 auto`, which left a bare heading floating in
   a column with the screen empty on both sides.

   The structure was only ever copy-paste, which is why a page written any other
   way stayed that way. It is a component now. */
/* ── The Arrival ───────────────────────────────────────────────────────────
   The platform had no page entrance, and that was not an oversight: every
   animation in it means WORK IS HAPPENING — the shell's navigation spinner, a
   training run's sliding bar, a flowing wire, a pulsing container dot. Nothing
   moved merely because it appeared.

   So an entrance here is not decoration added to a still page; it is the
   completion of a gesture the shell already started. The spinner says the page
   is coming, and the content settles out of it — once, quickly, and never
   again while the operator works.

   Three rules keep it honest on an Operate surface:
     * the FINAL state is the default. The animation runs `from` a displaced
       position, so a page whose CSS or JS failed still shows its content
       rather than a stack of invisible cards.
     * it is short, and it is SEEN. 320ms over 14px: the first pass ran 260ms
       over 8px, which was inside the threshold of perception on a dense
       screen — an entrance nobody notices is not a quiet entrance, it is a
       broken one, and it still costs the frame budget. Past ~400ms an expert
       reading this dashboard a hundred times a day starts waiting for it.
     * it settles, it does not cascade. A small stagger orders the sections by
       hierarchy and stops at the fourth — a reveal running down a long page is
       a different, slower promise than this one.
   Honors prefers-reduced-motion by not running at all.

   WHY A CLASS AND NOT PLAIN CSS: these pages are embedded, and the shell puts
   a full-screen loader over the frame while it navigates. A CSS animation that
   starts at parse time runs its whole 260ms UNDER that overlay and is over
   before the operator sees anything — which is exactly what "I see no
   difference" looked like. The class is added when the document is actually
   shown, so the entrance plays in view or not at all.                      */
.ld-enter.ld-enter--go > * {
    /* backwards, NOT both: `backwards` applies the displaced start during the
       delay and then hands the element back to its own style. `both` adds a
       forwards fill that pins the last keyframe — with no `to` declared that
       is meant to be the element's style, and it is one spec reading away
       from holding an element at opacity 0 forever. The safer word says
       exactly what is wanted. */
    animation: ld-arrive var(--ld-enter-dur, 320ms) cubic-bezier(.16, 1, .3, 1) backwards;
}
.ld-enter--go > *:nth-child(2) { animation-delay: 40ms; }
.ld-enter--go > *:nth-child(3) { animation-delay: 80ms; }
.ld-enter--go > *:nth-child(n+4) { animation-delay: 120ms; }

@keyframes ld-arrive {
    from { opacity: 0; transform: translateY(var(--ld-enter-rise, 14px)); }
}

@media (prefers-reduced-motion: reduce) {
    .ld-enter--go > * { animation: none; }
}

.ld-page {
    inline-size: 100%;
    max-inline-size: 100%;
    /* clip, not hidden: `overflow-x: hidden` forces overflow-y from visible
       to auto, which quietly turns every page into a scroll container — and a
       dropdown reaching past a SHORT page's bottom edge (the Columns menu on
       a two-row table) was clipped at that boundary. `clip` bounds the axis
       it names and leaves the other truly visible; it is the same technique
       .ld-app-surface already uses for the same reason. */
    overflow-x: clip;
    box-sizing: border-box;
    margin-block-start: var(--ld-space-3);
    padding-inline: var(--ld-space-2);
    padding-block-end: calc(2rem + env(safe-area-inset-bottom, 0px));
}
.ld-page-card {
    border-radius: var(--ld-radius-xl);
    background: var(--ld-card-bg);
    border: 1px solid var(--ld-card-border);
    box-shadow: var(--ld-card-shadow);
    color: var(--ld-text);
    overflow: hidden;
}
/* The opening block: what this page is, and what it is for. */
.ld-page-head {
    padding-inline: var(--ld-space-4);
    padding-block: var(--ld-space-3) var(--ld-space-2);
}
.ld-page-head .ld-page-lead { margin-block-start: var(--ld-space-1); }

/* The body of the card, and the rule between it and the header. */
.ld-page-body {
    padding-inline: var(--ld-space-4);
    padding-block: var(--ld-space-3) var(--ld-space-4);
    border-block-start: 1px solid var(--ld-divider);
}

/* A page whose body is a vertical stack of panels rather than one card. Ten Edge
   pages each declared it privately — .ea-root, .cc-root, .disc-root, .esc-root,
   .evp-root and .pda-shell — as the same flex column with the same gap under six
   different names. */
.ld-page--stack {
    display: flex;
    flex-direction: column;
    gap: var(--ld-space-4);
}

/* Dense operational dashboards may align every major panel interval with the
   shell gutter: 8px between cards, below the top bar, and beside the rail.
   Self-sufficient on purpose: it was written as a modifier of .ld-page--stack
   and overrode only `gap`, but all 177 pages apply it ALONE. Without a flex
   context the gap did nothing while `> * { margin-block: 0 }` still stripped
   the margins the cards had — so a class named "stack" removed the spacing and
   never replaced it, and every one of those pages had its panels touching. */
.ld-page--stack-uniform {
    display: flex;
    flex-direction: column;
    gap: var(--ld-space-2);
}

.ld-page--stack-uniform > * {
    margin-block: 0;
}

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

/* Roles permission workbench -------------------------------------------------
   Role, sub-role and sub-sub-role editors share one compact operating model:
   a stable section rail at the logical start and one large assignment canvas.
   Existing data-page hooks keep the Razor handlers and tab scripts unchanged. */
:where(
    [data-page="management-role-create"],
    [data-page="management-role-edit"],
    [data-page="management-sub-role-create"],
    [data-page="management-sub-role-edit"],
    [data-page="management-sub-sub-role-create"],
    [data-page="management-sub-sub-role-edit"]
) :where(.ld-form-body, .role-workbench-body) {
    display: grid;
    grid-template-columns: minmax(11.5rem, 13.5rem) minmax(0, 1fr);
    gap: var(--ld-space-3);
    align-items: start;
}

:where(
    [data-page="management-role-create"],
    [data-page="management-role-edit"],
    [data-page="management-sub-role-create"],
    [data-page="management-sub-role-edit"],
    [data-page="management-sub-sub-role-create"],
    [data-page="management-sub-sub-role-edit"]
) :where(.ld-form-body, .role-workbench-body) > :not([data-page-tabs]):not(.page-panel) {
    grid-column: 1 / -1;
}

:where(
    [data-page="management-role-create"],
    [data-page="management-role-edit"],
    [data-page="management-sub-role-create"],
    [data-page="management-sub-role-edit"],
    [data-page="management-sub-sub-role-create"],
    [data-page="management-sub-sub-role-edit"]
) [data-page-tabs] {
    position: sticky;
    inset-block-start: var(--ld-space-2);
    z-index: 3;
    display: flex;
    flex-direction: column;
    gap: var(--ld-space-1);
    grid-column: 1;
    margin: 0;
    padding: var(--ld-space-2);
    border: 1px solid var(--ld-border);
    border-radius: var(--ld-radius-lg);
    background: var(--ld-surface-subtle);
    box-shadow: var(--ld-tile-shadow);
}

:where(
    [data-page="management-role-create"],
    [data-page="management-role-edit"],
    [data-page="management-sub-role-create"],
    [data-page="management-sub-role-edit"],
    [data-page="management-sub-sub-role-create"],
    [data-page="management-sub-sub-role-edit"]
) [data-page-tabs] .page-tab {
    inline-size: 100%;
    min-block-size: 2.5rem;
    justify-content: flex-start;
    gap: var(--ld-space-2);
    padding: var(--ld-space-2) var(--ld-space-3);
    border: 1px solid transparent;
    border-radius: var(--ld-radius-md);
    background: transparent;
    color: var(--ld-text-secondary);
    font-size: .875rem;
    text-align: start;
}

:where(
    [data-page="management-role-create"],
    [data-page="management-role-edit"],
    [data-page="management-sub-role-create"],
    [data-page="management-sub-role-edit"],
    [data-page="management-sub-sub-role-create"],
    [data-page="management-sub-sub-role-edit"]
) [data-page-tabs] .page-tab:hover:not(:disabled) {
    border-color: var(--ld-border);
    background: var(--ld-surface-accent);
    color: var(--ld-text);
}

:where(
    [data-page="management-role-create"],
    [data-page="management-role-edit"],
    [data-page="management-sub-role-create"],
    [data-page="management-sub-role-edit"],
    [data-page="management-sub-sub-role-create"],
    [data-page="management-sub-sub-role-edit"]
) [data-page-tabs] .role-page-tab-active {
    border-color: var(--ld-tab-active-border) !important;
    background: var(--ld-tab-active-bg) !important;
    color: var(--ld-tab-active-text) !important;
    box-shadow: inset 2px 0 0 var(--ld-brand);
}

[dir="rtl"] :where(
    [data-page="management-role-create"],
    [data-page="management-role-edit"],
    [data-page="management-sub-role-create"],
    [data-page="management-sub-role-edit"],
    [data-page="management-sub-sub-role-create"],
    [data-page="management-sub-sub-role-edit"]
) [data-page-tabs] .role-page-tab-active {
    box-shadow: inset -2px 0 0 var(--ld-brand);
}

:where(
    [data-page="management-role-create"],
    [data-page="management-role-edit"],
    [data-page="management-sub-role-create"],
    [data-page="management-sub-role-edit"],
    [data-page="management-sub-sub-role-create"],
    [data-page="management-sub-sub-role-edit"]
) .page-panel {
    grid-column: 2;
    min-inline-size: 0;
}

:where(
    [data-page="management-role-create"],
    [data-page="management-role-edit"],
    [data-page="management-sub-role-create"],
    [data-page="management-sub-role-edit"],
    [data-page="management-sub-sub-role-create"],
    [data-page="management-sub-sub-role-edit"]
) :where(.role-section-card, .ld-form-section) {
    border-radius: var(--ld-radius-lg);
    padding: var(--ld-space-3);
}

.role-overview-strip {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 1px;
    border-block-end: 1px solid var(--ld-border);
    background: var(--ld-border);
}

.role-overview-stat {
    display: flex;
    align-items: center;
    gap: var(--ld-space-2);
    min-inline-size: 0;
    padding: var(--ld-space-2) var(--ld-space-3);
    background: var(--ld-surface-subtle);
}

.role-overview-stat__icon {
    display: inline-flex;
    inline-size: 1.75rem;
    block-size: 1.75rem;
    flex: 0 0 1.75rem;
    align-items: center;
    justify-content: center;
    border-radius: var(--ld-radius-md);
    background: var(--ld-chip-brand-bg);
    color: var(--ld-brand);
}

.role-overview-stat__value {
    display: block;
    color: var(--ld-text-value);
    font-size: 1rem;
    font-weight: 700;
    line-height: 1.1;
}

.role-overview-stat__label {
    display: block;
    margin-block-start: 2px;
    color: var(--ld-text-muted);
    font-size: .75rem;
}

.role-export-split:not(:last-child) {
    border-inline-end: 1px solid var(--ld-border);
}

.role-auto-inline {
    margin-inline-start: auto;
}

html[data-theme="black"] :where(
    [data-page="management-role-create"],
    [data-page="management-role-edit"],
    [data-page="management-sub-role-create"],
    [data-page="management-sub-role-edit"],
    [data-page="management-sub-sub-role-create"],
    [data-page="management-sub-sub-role-edit"],
    [data-page="management-sub-role-delete"],
    [data-page="management-sub-sub-role-delete"]
) .role-form-card {
    background: var(--ld-form-card-bg) !important;
}

.role-local-workbench {
    display: grid;
    grid-template-columns: minmax(11.5rem, 13.5rem) minmax(0, 1fr);
    gap: var(--ld-space-3);
    align-items: start;
}

.role-local-workbench__nav {
    position: sticky;
    inset-block-start: var(--ld-space-2);
    display: flex;
    flex-direction: column;
    gap: var(--ld-space-1);
    padding: var(--ld-space-2);
    border: 1px solid var(--ld-border);
    border-radius: var(--ld-radius-lg);
    background: var(--ld-surface-subtle);
    box-shadow: var(--ld-tile-shadow);
}

.role-local-workbench__nav a {
    display: flex;
    min-block-size: 2.5rem;
    align-items: center;
    gap: var(--ld-space-2);
    padding: var(--ld-space-2) var(--ld-space-3);
    border: 1px solid transparent;
    border-radius: var(--ld-radius-md);
    color: var(--ld-text-secondary);
    font-size: .8125rem;
    font-weight: 600;
}

.role-local-workbench__nav a:hover,
.role-local-workbench__nav a:focus-visible {
    border-color: var(--ld-tab-active-border);
    background: var(--ld-tab-active-bg);
    color: var(--ld-tab-active-text);
}

.role-local-workbench__content {
    display: flex;
    min-inline-size: 0;
    flex-direction: column;
    gap: var(--ld-space-2);
}

:where(
    [data-page="management-role-delete"],
    [data-page="management-sub-role-delete"],
    [data-page="management-sub-sub-role-delete"]
) :where(.ld-card, .role-form-card) {
    border: 1px solid var(--ld-form-card-border);
    border-radius: var(--ld-radius-xl);
    background: var(--ld-form-card-bg);
    box-shadow: var(--ld-form-card-shadow);
}

:where(
    [data-page="management-sub-role-delete"],
    [data-page="management-sub-sub-role-delete"]
) .role-form-card {
    padding: var(--ld-space-4);
}

@media (max-width: 1024px) {
    .role-local-workbench {
        grid-template-columns: 1fr;
    }

    .role-local-workbench__nav {
        position: static;
        flex-direction: row;
        overflow-x: auto;
    }

    .role-local-workbench__nav a {
        flex: 0 0 auto;
        white-space: nowrap;
    }

    :where(
        [data-page="management-role-create"],
        [data-page="management-role-edit"],
        [data-page="management-sub-role-create"],
        [data-page="management-sub-role-edit"],
        [data-page="management-sub-sub-role-create"],
        [data-page="management-sub-sub-role-edit"]
    ) :where(.ld-form-body, .role-workbench-body) {
        grid-template-columns: 1fr;
    }

    :where(
        [data-page="management-role-create"],
        [data-page="management-role-edit"],
        [data-page="management-sub-role-create"],
        [data-page="management-sub-role-edit"],
        [data-page="management-sub-sub-role-create"],
        [data-page="management-sub-sub-role-edit"]
    ) [data-page-tabs] {
        position: static;
        grid-column: 1;
        flex-direction: row;
        overflow-x: auto;
        scrollbar-width: thin;
    }

    :where(
        [data-page="management-role-create"],
        [data-page="management-role-edit"],
        [data-page="management-sub-role-create"],
        [data-page="management-sub-role-edit"],
        [data-page="management-sub-sub-role-create"],
        [data-page="management-sub-sub-role-edit"]
    ) [data-page-tabs] .page-tab {
        inline-size: auto;
        flex: 0 0 auto;
        white-space: nowrap;
    }

    :where(
        [data-page="management-role-create"],
        [data-page="management-role-edit"],
        [data-page="management-sub-role-create"],
        [data-page="management-sub-role-edit"],
        [data-page="management-sub-sub-role-create"],
        [data-page="management-sub-sub-role-edit"]
    ) .page-panel {
        grid-column: 1;
    }
}

html.ld-phone .role-overview-strip {
    grid-template-columns: repeat(2, minmax(0, 1fr));
}

html.ld-phone .role-local-workbench,
html.ld-phone :where(
    [data-page="management-role-create"],
    [data-page="management-role-edit"],
    [data-page="management-sub-role-create"],
    [data-page="management-sub-role-edit"],
    [data-page="management-sub-sub-role-create"],
    [data-page="management-sub-sub-role-edit"]
) :where(.ld-form-body, .role-workbench-body) {
    grid-template-columns: minmax(0, 1fr);
}

html.ld-phone .role-local-workbench__nav,
html.ld-phone :where(
    [data-page="management-role-create"],
    [data-page="management-role-edit"],
    [data-page="management-sub-role-create"],
    [data-page="management-sub-role-edit"],
    [data-page="management-sub-sub-role-create"],
    [data-page="management-sub-sub-role-edit"]
) [data-page-tabs] {
    position: static;
    grid-column: 1;
    flex-direction: row;
    overflow-x: auto;
    scrollbar-width: thin;
}

html.ld-phone .role-local-workbench__nav a,
html.ld-phone :where(
    [data-page="management-role-create"],
    [data-page="management-role-edit"],
    [data-page="management-sub-role-create"],
    [data-page="management-sub-role-edit"],
    [data-page="management-sub-sub-role-create"],
    [data-page="management-sub-sub-role-edit"]
) [data-page-tabs] .page-tab {
    inline-size: auto;
    flex: 0 0 auto;
    white-space: nowrap;
}

html.ld-phone :where(
    [data-page="management-role-create"],
    [data-page="management-role-edit"],
    [data-page="management-sub-role-create"],
    [data-page="management-sub-role-edit"],
    [data-page="management-sub-sub-role-create"],
    [data-page="management-sub-sub-role-edit"]
) .page-panel {
    grid-column: 1;
}

html.ld-phone :where(
    [data-page="management-role-create"],
    [data-page="management-role-edit"],
    [data-page="management-sub-role-create"],
    [data-page="management-sub-role-edit"],
    [data-page="management-sub-sub-role-create"],
    [data-page="management-sub-sub-role-edit"]
) [data-page-tabs] .page-tab {
    min-block-size: 2.75rem;
}

/* Every routable shell page receives this class from _Layout. It establishes
   one application-wide floor without imposing CRUD structure on editors,
   chats, iframe hosts, logs, authentication or other specialized surfaces. */
.ld-app-surface {
    color: var(--ld-text);
    overflow-x: clip;
    scrollbar-gutter: stable;
}
.ld-app-surface--standard {
    /* The inline gutter is one step wider than the block one, deliberately.
       Above the first panel there is a top bar with its own border and its
       own dark ground — 8px is enough separation because the boundary is
       already drawn. Beside it there is the navigation rail, a full-height
       surface the eye reads as a neighbouring PANEL rather than as chrome,
       and at 8px the two read as one block that happens to have a line
       through it. 12px is the smallest step on the scale that separates
       them without opening a corridor. 16px was tried and read as loose once
       the pages actually obeyed it — for a while they did not; 84 of them
       pinned this value themselves — so it settles at 12px. It is set here,
       on the shared surface, so all eleven modules move together. */
    padding-block-start: var(--ld-space-2);
    padding-inline: var(--ld-space-3);
    padding-block-end: var(--ld-space-5);
}
.ld-app-surface--flush {
    padding: 0;
}
.ld-app-surface > :where(section, .ld-page, [data-page]) { min-inline-size: 0; }
/* Standard shell pages use one 8px gap from the top bar and navigation rail.
   The shell owns that gap. This boundary reset deliberately outranks legacy
   inline/page rules; opt out only for a documented standalone reading surface. */
@media (min-width: 768px) {
    .ld-app-surface--standard > :where(section, main, article, div, form, .ld-page, [data-page]):not([data-ld-shell-spacing="preserve"]) {
        margin-block-start: 0 !important;
        margin-inline: 0 !important;
        padding-block-start: 0 !important;
        padding-inline: 0 !important;
    }

    /* Management-list roots continue the shell's 8px rhythm automatically.
       Reset legacy space-y/alert margins so invisible script siblings cannot
       add another interval before the first visible data card. */
    .ld-app-surface--standard > .ld-page:has(> .ld-data-card) {
        display: flex;
        flex-direction: column;
        gap: var(--ld-space-2);
    }
    .ld-app-surface--standard > .ld-page:has(> .ld-data-card) > * {
        margin-block: 0 !important;
    }

    /* Viewport-fitted operational tables have one scroll owner: the bounded
       table viewport. The shell page itself must never add a second scrollbar. */
    .ld-app-surface:has(> .ld-page--table-fit) {
        overflow-y: hidden !important;
    }
    .ld-app-surface > .ld-page--table-fit {
        display: flex;
        flex-direction: column;
        block-size: 100%;
        min-block-size: 0;
        margin-block-start: 0;
        padding-block-start: 0;
        padding-block-end: 0;
    }
    .ld-page--table-fit > :where(.ld-data-card, .dock-table-card, .vol-table-card, .img-table-card) {
        display: flex;
        flex: 1 1 auto;
        flex-direction: column;
        min-block-size: 0;
    }
    .ld-page--table-fit .ld-table-viewport {
        flex: 1 1 auto;
        min-block-size: 0;
        max-block-size: none;
    }
}
html.ld-phone .ld-app-surface:not(.ld-app-surface--viewport) {
    /* The phone gutter is the spacing token, not .375rem. Six pixels is not a
       step on the scale, and three module layouts had already overridden this
       line back to --ld-space-2 with !important — which is the tell that the
       value here was the one out of step, not them. */
    padding-inline: var(--ld-space-2);
    padding-block-start: var(--ld-space-3);
    padding-block-end: calc(2rem + env(safe-area-inset-bottom, 0px));
}
html.ld-phone .ld-app-surface > :where(section, .ld-page, [data-page]) {
    inline-size: 100%;
    max-inline-size: 42rem;
    margin-inline: auto;
}

/* ── .ld-rail-fab — the embedded module's own rail control ────────────────────
   Mounted by ldata.embed.js only when a module is framed on a phone, where its
   own top bar is hidden and the shell's hamburger has to cross three frames to
   reach the rail. Sits at the top start corner, clear of the shell's top bar
   (which is outside this document), and above page content. */
.ld-rail-fab {
    position: fixed;
    inset-block-start: .5rem;
    inset-inline-start: .5rem;
    z-index: 210;                      /* the drawer itself is 200 */
    inline-size: 2.5rem;
    block-size: 2.5rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--ld-border);
    border-radius: var(--ld-radius-md);
    background: var(--ld-card-bg);
    color: var(--ld-text);
    box-shadow: var(--ld-shadow-sm);
    cursor: pointer;
}

.ld-rail-fab:hover { background: var(--ld-rail-hover-bg); }
.ld-rail-fab:focus-visible { outline: 2px solid var(--ld-tab-underline); outline-offset: 2px; }

/* Once the drawer is open it covers this corner — the backdrop and the links
   take over, and a control sitting on top of the rail would only be in the way. */
body.mobile-sb-open .ld-rail-fab,
body.sidebar-mobile-open .ld-rail-fab { display: none; }

/* ── Log viewer (`.ld-log-*`) ───────────────────────────────────────────────
   Shared operational surface for container, service, agent and device logs.
   Modules provide data and behavior; this component owns the complete visual
   anatomy, theme adaptation, RTL geometry and physical-phone composition. */
.ld-log-page { display: grid; gap: var(--ld-space-3); inline-size: 100%; padding-inline: var(--ld-space-2); }
.ld-log-card { overflow: hidden; border: 1px solid var(--ld-card-border); border-radius: var(--ld-radius-xl); background: var(--ld-card-bg); box-shadow: var(--ld-card-shadow); color: var(--ld-text); }
.ld-log-identity { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; gap: var(--ld-space-3); padding: var(--ld-space-3) var(--ld-space-4); }
.ld-log-back,
.ld-log-action { display: inline-flex; align-items: center; justify-content: center; gap: var(--ld-space-2); min-block-size: 2.25rem; padding-inline: var(--ld-space-3); border: 1px solid var(--ld-input-border); border-radius: var(--ld-radius-full); color: var(--ld-text); background: transparent; font-size: .75rem; font-weight: 600; transition: background-color var(--ld-motion-fast) ease; }
.ld-log-back:hover,
.ld-log-action:hover { background: var(--ld-surface-accent); }
[dir="rtl"] .ld-log-back i { transform: scaleX(-1); }
.ld-log-back:focus-visible,
.ld-log-action:focus-visible,
.ld-log-toggle:focus-within { outline: none; box-shadow: var(--ld-focus-ring-flat); }
.ld-log-identity__main { display: flex; align-items: center; gap: var(--ld-space-3); min-inline-size: 0; }
.ld-log-identity__icon { display: grid; place-items: center; flex: 0 0 auto; inline-size: 2.5rem; block-size: 2.5rem; border: 1px solid var(--ld-chip-border); border-radius: var(--ld-radius-lg); color: var(--ld-brand); background: var(--ld-tab-active-bg); }
.ld-log-identity__copy { min-inline-size: 0; }
.ld-log-kicker { display: flex; align-items: center; gap: var(--ld-space-2); color: var(--ld-text-muted); font-size: .625rem; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; }
.ld-log-title { margin: 0; overflow: hidden; color: var(--ld-text-value); font-size: 1rem; font-weight: 700; line-height: 1.35; text-overflow: ellipsis; white-space: nowrap; }
.ld-log-image { margin-block-start: var(--ld-space-1); overflow: hidden; color: var(--ld-text-muted); font-size: .6875rem; text-overflow: ellipsis; white-space: nowrap; }
.ld-log-identity__meta { display: flex; align-items: center; justify-content: flex-end; gap: var(--ld-space-2); flex-wrap: wrap; }
.ld-log-status,
.ld-log-meta { display: inline-flex; align-items: center; gap: var(--ld-space-2); min-block-size: 1.75rem; padding-inline: var(--ld-space-3); border: 1px solid var(--ld-border); border-radius: var(--ld-radius-full); color: var(--ld-text-muted); background: var(--ld-surface-subtle); font-size: .6875rem; }
.ld-log-status { font-weight: 700; letter-spacing: .05em; text-transform: uppercase; }
.ld-log-status__dot { inline-size: .5rem; block-size: .5rem; border-radius: var(--ld-radius-full); background: currentColor; }
.ld-log-status.is-running { border-color: var(--ld-success-border); color: var(--ld-success-text); background: var(--ld-success-bg); }
.ld-log-status.is-error { border-color: var(--ld-error-border); color: var(--ld-error-text); background: var(--ld-error-bg); }
.ld-log-status.is-warning { border-color: var(--ld-warn-border); color: var(--ld-warn-text); background: var(--ld-warn-bg); }
.ld-log-status.is-info { border-color: var(--ld-chip-border); color: var(--ld-brand); background: var(--ld-tab-active-bg); }
.ld-log-meta code { color: var(--ld-text-value); font-family: SFMono-Regular, Menlo, Consolas, "Liberation Mono", "Courier New", monospace; font-size: inherit; }
.ld-log-stream { display: grid; grid-template-rows: auto auto minmax(20rem, 1fr); min-block-size: calc(100dvh - 10.5rem); }
.ld-log-stream__head { display: flex; align-items: center; gap: var(--ld-space-3); padding: var(--ld-space-3) var(--ld-space-4); border-block-end: 1px solid var(--ld-divider); }
.ld-log-stream__heading { display: flex; align-items: center; gap: var(--ld-space-2); min-inline-size: 0; }
.ld-log-stream__heading i { color: var(--ld-brand); }
.ld-log-stream__heading h2 { margin: 0; color: var(--ld-text-secondary); font-size: .8125rem; font-weight: 600; letter-spacing: .08em; text-transform: uppercase; }
.ld-log-live { display: inline-flex; align-items: center; gap: var(--ld-space-2); color: var(--ld-success-text); font-size: .6875rem; font-weight: 600; }
.ld-log-live__pulse { inline-size: .45rem; block-size: .45rem; border-radius: var(--ld-radius-full); background: currentColor; box-shadow: 0 0 0 .2rem color-mix(in srgb, currentColor 16%, transparent); }
.ld-log-live.is-paused { color: var(--ld-text-muted); }
.ld-log-live.is-paused .ld-log-live__pulse { box-shadow: none; }
.ld-log-actions { display: flex; align-items: center; justify-content: flex-end; gap: var(--ld-space-2); margin-inline-start: auto; }
.ld-log-toolbar { display: grid; grid-template-columns: minmax(14rem, 1fr) auto; align-items: center; gap: var(--ld-space-3); padding: var(--ld-space-3) var(--ld-space-4); border-block-end: 1px solid var(--ld-divider); background: var(--ld-surface-tertiary); }
.ld-log-search { position: relative; display: block; min-inline-size: 0; }
.ld-log-search > i { position: absolute; inset-inline-start: var(--ld-space-3); inset-block-start: 50%; translate: 0 -50%; color: var(--ld-input-placeholder); font-size: .75rem; pointer-events: none; }
.ld-log-search input { inline-size: 100%; min-block-size: 2.5rem; padding-block: var(--ld-space-2); padding-inline: 2.35rem var(--ld-space-3); border: 1px solid var(--ld-input-border); border-radius: var(--ld-radius-md); color: var(--ld-input-text); background: var(--ld-input-bg); font-size: .8125rem; }
.ld-log-search input::placeholder { color: var(--ld-input-placeholder); }
.ld-log-search input:focus { outline: none; border-color: var(--ld-brand); box-shadow: var(--ld-focus-ring); }
.ld-log-filters { display: flex; align-items: center; justify-content: flex-end; gap: var(--ld-space-2); flex-wrap: wrap; }
.ld-log-toggle { display: inline-flex; align-items: center; gap: var(--ld-space-2); min-block-size: 2.5rem; padding-inline: var(--ld-space-3); border: 1px solid var(--ld-border); border-radius: var(--ld-radius-full); color: var(--ld-text-secondary); background: var(--ld-card-bg); font-size: .6875rem; font-weight: 600; cursor: pointer; }
.ld-log-toggle input { inline-size: 1rem; block-size: 1rem; margin: 0; accent-color: var(--ld-brand); }
.ld-log-console-shell { display: grid; grid-template-rows: auto minmax(0, 1fr); min-block-size: 0; padding: var(--ld-space-3); background: var(--ld-surface-subtle); }
.ld-log-console-meta { display: flex; align-items: center; gap: var(--ld-space-2); padding-block-end: var(--ld-space-2); color: var(--ld-text-muted); font-size: .6875rem; }
.ld-log-console-meta__item { display: inline-flex; align-items: center; gap: var(--ld-space-2); }
.ld-log-console-meta__item + .ld-log-console-meta__item::before { content: ""; inline-size: 1px; block-size: .85rem; margin-inline-end: var(--ld-space-2); background: var(--ld-divider); }
.ld-log-console { overflow: auto; min-block-size: 20rem; max-block-size: calc(100dvh - 16rem); padding: var(--ld-space-3); border: 1px solid var(--ld-border); border-radius: var(--ld-radius-lg); color: var(--ld-text-value); background: color-mix(in srgb, var(--ld-page-bg) 88%, black); font-family: SFMono-Regular, Menlo, Consolas, "Liberation Mono", "Courier New", monospace; font-size: .75rem; line-height: 1.65; white-space: pre-wrap; overflow-wrap: anywhere; tab-size: 4; scrollbar-gutter: stable; box-shadow: inset 0 1px 3px color-mix(in srgb, black 25%, transparent); }
.ld-log-console.is-nowrap { white-space: pre; overflow-wrap: normal; }
.ld-log-line { display: block; min-inline-size: max-content; padding-inline: var(--ld-space-2); border-radius: var(--ld-radius-xs); white-space: inherit; }
.ld-log-line--error { color: var(--ld-error-text); background: var(--ld-error-bg); }
.ld-log-line--warning { color: var(--ld-warn-text); background: var(--ld-warn-bg); }
.ld-log-line--info { color: var(--ld-text-value); background: var(--ld-tab-active-bg); }
.ld-log-mark { border-radius: var(--ld-radius-xs); color: var(--ld-text-value); background: var(--ld-tab-active-bg); box-shadow: 0 0 0 1px var(--ld-tab-active-border); }
.ld-log-empty { display: grid; place-items: center; min-block-size: 16rem; color: var(--ld-text-muted); font-family: system-ui, -apple-system, "Segoe UI", sans-serif; text-align: center; }

@media (max-width: 900px) {
    .ld-log-identity { grid-template-columns: auto minmax(0, 1fr); }
    .ld-log-identity__meta { grid-column: 1 / -1; justify-content: flex-start; }
    .ld-log-toolbar { grid-template-columns: 1fr; }
    .ld-log-filters { justify-content: flex-start; }
}

html.ld-phone .ld-log-page { gap: var(--ld-space-2); padding-inline: 0; }
html.ld-phone .ld-log-card { border-radius: var(--ld-radius-lg); }
html.ld-phone .ld-log-identity { grid-template-columns: auto minmax(0, 1fr); gap: var(--ld-space-2); padding: var(--ld-space-3); }
html.ld-phone .ld-log-back { inline-size: 2.75rem; block-size: 2.75rem; padding: 0; }
html.ld-phone .ld-log-back span,
html.ld-phone .ld-log-identity__icon { display: none; }
html.ld-phone .ld-log-title { font-size: .875rem; }
html.ld-phone .ld-log-image { font-size: .625rem; }
html.ld-phone .ld-log-identity__meta { display: grid; grid-template-columns: 1fr 1fr; grid-column: 1 / -1; inline-size: 100%; }
html.ld-phone .ld-log-meta,
html.ld-phone .ld-log-status { justify-content: center; min-inline-size: 0; padding-inline: var(--ld-space-2); }
html.ld-phone .ld-log-meta--updated { grid-column: 1 / -1; }
html.ld-phone .ld-log-stream { min-block-size: calc(100dvh - 15rem); }
html.ld-phone .ld-log-stream__head { align-items: flex-start; flex-wrap: wrap; padding: var(--ld-space-3); }
html.ld-phone .ld-log-actions { display: grid; grid-template-columns: repeat(3, 1fr); inline-size: 100%; margin-inline-start: 0; }
html.ld-phone .ld-log-action { min-block-size: 2.75rem; padding-inline: var(--ld-space-2); }
html.ld-phone .ld-log-action span { font-size: .6875rem; }
html.ld-phone .ld-log-toolbar { padding: var(--ld-space-3); }
html.ld-phone .ld-log-search input { min-block-size: 2.75rem; font-size: 1rem; }
html.ld-phone .ld-log-filters { display: grid; grid-template-columns: 1fr 1fr; inline-size: 100%; }
html.ld-phone .ld-log-toggle { min-block-size: 2.75rem; padding-inline: var(--ld-space-2); }
html.ld-phone .ld-log-toggle:last-child { grid-column: 1 / -1; }
html.ld-phone .ld-log-console-shell { padding: var(--ld-space-2); }
html.ld-phone .ld-log-console-meta { overflow-x: auto; white-space: nowrap; }
html.ld-phone .ld-log-console { max-block-size: none; min-block-size: 52dvh; padding: var(--ld-space-2); border-radius: var(--ld-radius-md); font-size: .71875rem; line-height: 1.55; }


/* ═══════════════════════════════════════════════════════════════════════════
   THE TOKEN UTILITIES — one declaration, named by the token it carries

   A page that writes style="color:var(--ld-text-muted)" has already done the
   hard part: it named the token. What it lost was the layer. Inline style is
   the one place nothing can reach — no theme block, no phone rule, no
   accessibility switch, no page override — so the value is correct and
   frozen at the same time. Nine hundred of those attributes had collected in
   the FIX module alone, all of them already token-backed.

   These classes are the same values in a place the cascade can see. Nothing
   here is new design: every declaration is one token, and a reader of the
   markup still sees which one is in play.

   THE SCOPE RULE. The colour, surface and border families are scoped under
   html[data-theme] and the type, rhythm and pattern families are not. That
   is not decoration: the modules that carry a frozen Tailwind bundle also
   carry a compatibility map (html[data-theme] .bg-slate-800 { … }) that
   re-points a thousand shade-named utilities onto the token palette. A bare
   class loses to it on specificity, so a migrated element that still carries
   bg-slate-800 would come back wearing the map's colour instead of its own.
   Matching the map's weight, from a stylesheet that loads after it, keeps the
   element exactly where the inline style had put it. The families that never
   collide stay bare, so a page can still override them.

   USE THE COMPONENT FIRST. .ld-chip, .ld-stat-card, .ld-tile, .ld-btn,
   .ld-label and .ld-form-grid already exist; a hand-rolled recipe that
   matches one of them should become that component, not four utilities.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── Ink ─────────────────────────────────────────────────────────────────
   .ld-t-brand resolves to the STAT LABEL, not to the brand. Measured,
   color: var(--ld-brand) fails AA nearly everywhere it lands — 2.77 on the
   light floor, 2.77 on the gray card, 3.44 on the light tile — because
   Console Blue is a FILL colour, judged at the 3:1 a graphical element
   needs. --ld-stat-label is the same blue clamped one step deeper for
   exactly this, and it measures 5.07 to 11.89 on the same surfaces. That is
   The Stat-Label Blue rule; the brand keeps buttons, borders and the ring. */
html[data-theme] .ld-t-text           { color: var(--ld-text); }
html[data-theme] .ld-t-text-secondary { color: var(--ld-text-secondary); }
html[data-theme] .ld-t-text-muted     { color: var(--ld-text-muted); }
html[data-theme] .ld-t-text-dim       { color: var(--ld-text-dim); }
html[data-theme] .ld-t-text-value     { color: var(--ld-text-value); }
html[data-theme] .ld-t-helper         { color: var(--ld-helper); }
html[data-theme] .ld-t-brand          { color: var(--ld-stat-label); }
html[data-theme] .ld-t-error          { color: var(--ld-error-text); }
html[data-theme] .ld-t-warn           { color: var(--ld-warn-text); }
html[data-theme] .ld-t-success        { color: var(--ld-success-text); }
html[data-theme] .ld-t-inherit        { color: inherit; }

/* ── Surfaces ────────────────────────────────────────────────────────────── */
html[data-theme] .ld-bg-card    { background: var(--ld-card-bg); }
html[data-theme] .ld-bg-subtle  { background: var(--ld-surface-subtle); }
html[data-theme] .ld-bg-accent  { background: var(--ld-surface-accent); }
html[data-theme] .ld-bg-stat    { background: var(--ld-stat-bg); }
html[data-theme] .ld-bg-none    { background: none; }

/* ── Boundaries ──────────────────────────────────────────────────────────── */
html[data-theme] .ld-bc-border     { border-color: var(--ld-border); }
html[data-theme] .ld-bc-stat       { border-color: var(--ld-stat-border); }
html[data-theme] .ld-bc-divider    { border-color: var(--ld-divider); }
html[data-theme] .ld-bc-table-row  { border-color: var(--ld-table-row-border); }
html[data-theme] .ld-bc-chip-brand { border-color: var(--ld-chip-brand-border); }
html[data-theme] .ld-bd-1          { border: 1px solid var(--ld-border); }
html[data-theme] .ld-bd-none       { border: none; }

/* ── The ramp ────────────────────────────────────────────────────────────
   Six steps, and only six. See --ld-fs-* in ldata-tokens.css for why. */
.ld-fs-nano  { font-size: var(--ld-fs-nano); }
.ld-fs-micro { font-size: var(--ld-fs-micro); }
.ld-fs-label { font-size: var(--ld-fs-label); }
.ld-fs-sm    { font-size: var(--ld-fs-sm); }
.ld-fs-body  { font-size: var(--ld-fs-body); }
.ld-fs-lg    { font-size: var(--ld-fs-lg); }

/* ── Rhythm ──────────────────────────────────────────────────────────────
   Logical, so RTL needs no second set, and on the spacing scale, so the
   operator's density seed finally reaches these gaps — a hand-written
   margin-top:.6rem answered to nothing. */
.ld-mbs-1 { margin-block-start: var(--ld-space-1); }
.ld-mbs-2 { margin-block-start: var(--ld-space-2); }
.ld-mbs-3 { margin-block-start: var(--ld-space-3); }
.ld-mbs-4 { margin-block-start: var(--ld-space-4); }
.ld-mbs-5 { margin-block-start: var(--ld-space-5); }
.ld-mbs-0 { margin-block-start: 0; }
.ld-mbe-1 { margin-block-end: var(--ld-space-1); }
.ld-mbe-2 { margin-block-end: var(--ld-space-2); }
.ld-mbe-3 { margin-block-end: var(--ld-space-3); }
.ld-mbe-4 { margin-block-end: var(--ld-space-4); }
.ld-mbe-5 { margin-block-end: var(--ld-space-5); }
.ld-mbe-0 { margin-block-end: 0; }
.ld-mie-1 { margin-inline-end: var(--ld-space-1); }
.ld-mis-auto { margin-inline-start: auto; }

/* ── Rows ────────────────────────────────────────────────────────────────
   The most repeated shape in the platform's forms and toolbars: things in a
   line, wrapping, on the gap scale. Alignment is the modifier because that
   is the only part that actually varied. */
.ld-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--ld-space-3); }
.ld-row--tight { gap: var(--ld-space-2); }
.ld-row--snug  { gap: var(--ld-space-1); }
.ld-row--wide  { gap: var(--ld-space-4); }
.ld-row--end   { align-items: flex-end; }
.ld-row--top   { align-items: flex-start; }
.ld-row--base  { align-items: baseline; }
.ld-row--between { justify-content: space-between; }
.ld-row--nowrap  { flex-wrap: nowrap; }

/* ── Small patterns ──────────────────────────────────────────────────────── */

/* The micro field label: an uppercase, tracked caption above a value. It was
   written inline 10+ times with the same five declarations. */
.ld-microlabel {
    display: block;
    font-size: var(--ld-fs-micro);
    text-transform: uppercase;
    letter-spacing: .08em;
    color: var(--ld-text-secondary);
}

/* The same caption without the shout — a note under a field or a value. */
.ld-fieldnote {
    display: block;
    font-size: var(--ld-fs-micro);
    color: var(--ld-text-secondary);
}

/* A button that is a word, not a control: no chrome, the page's own type,
   the cursor doing the affording. The danger variant is the only one that
   takes a colour, and it takes the error ink rather than a red of its own. */
.ld-linkbtn {
    background: none;
    border: none;
    padding: 0;
    font: inherit;
    font-size: var(--ld-fs-sm);
    color: var(--ld-stat-label);
    cursor: pointer;
    text-align: start;
}
.ld-linkbtn:hover { text-decoration: underline; text-underline-offset: .15em; }
.ld-linkbtn:focus-visible { outline: none; box-shadow: var(--ld-focus-ring-flat); border-radius: var(--ld-radius-xs); }
.ld-linkbtn--danger { color: var(--ld-error-text); }
.ld-linkbtn--muted  { color: var(--ld-text-muted); }

/* A <summary> that opens a small inline form. list-style:none plus the
   ::-webkit marker reset is the whole trick; both are needed. */
.ld-disclosure-summary {
    font-size: var(--ld-fs-sm);
    color: var(--ld-stat-label);
    cursor: pointer;
    list-style: none;
}
.ld-disclosure-summary::-webkit-details-marker { display: none; }
.ld-disclosure-summary:hover { text-decoration: underline; text-underline-offset: .15em; }

/* A colour sample beside a label — the one square the label pages draw. */
/* ── The fold ────────────────────────────────────────────────────────────
   A section that answers its own question while closed. The summary is a
   rail, not a second title: it carries the section's name and — on the far
   end — the ONE number that decides whether opening it is worth a click
   ("3,995 banked", "18 runs · rollback"). The card keeps its own frame
   inside and joins the rail when open.

   It exists because the Brainium Model screen had four tabs over twelve
   panels, five of which were already <details> — two layers of concealment
   over the same content, and neither layer told you what was behind it. */
/* ── The hub ─────────────────────────────────────────────────────────────
   One tile holding what would otherwise be a stack of cards. The cards keep
   their markup and lose their chrome — no border, no ground of their own, no
   outer gap — so the group reads as sections of one surface rather than as
   competing frames. A hairline separates them, which is the whole job the
   borders were doing badly.

   Written for the Brainium Model screen, where five cards described one
   decision; used again by Detector Training for the same reason. Anything a
   page nests inside it (.ld-card, .vt-card, .mst-card, .yst-card) flattens. */
.ld-hub { padding: 0; }
.ld-hub > :is(.ld-card, .vt-card, .mst-card, .cm-card, .yst-card),
.ld-hub > * > :is(.ld-card, .vt-card, .mst-card, .cm-card, .yst-card) {
    border: 0; background: none; box-shadow: none;
    border-radius: 0; margin-block: 0;
    padding-inline: var(--ld-space-4); padding-block: var(--ld-space-3);
}
/* Between sections, never above the first or below the last. */
/* Direct children only. A fold takes its separator on the SUMMARY below,
   which is the line the reader actually sees; adding one to the card inside
   it as well drew the boundary twice. */
.ld-hub > :not(:first-child):is(.ld-card, .vt-card, .mst-card, .yst-card) {
    border-block-start: 1px solid var(--ld-border);
}
/* A fold inside a hub is a section of it: full width, no frame of its own. */
.ld-hub > .ld-fold { margin-block-start: 0; }
.ld-hub > .ld-fold > .ld-fold__summary {
    border-inline: 0; border-radius: 0; background: none;
    border-block-start: 1px solid var(--ld-border); border-block-end: 0;
    padding-inline: var(--ld-space-4);
}
.ld-hub > .ld-fold[open] > .ld-fold__summary { border-block-end: 0; }
html.ld-phone .ld-hub > :is(.ld-card, .vt-card, .mst-card, .yst-card),
html.ld-phone .ld-hub > * > :is(.ld-card, .vt-card, .mst-card, .yst-card),
html.ld-phone .ld-hub > .ld-fold > .ld-fold__summary { padding-inline: var(--ld-space-3); }

/* ── The figure ──────────────────────────────────────────────────────────
   A number with a label above it and a note under it, and NO box. It exists
   because a panel that draws a border around each of eleven numbers is a
   wall: the two or three that carry a decision are lost among the ones that
   only carry context. Separation is by space, and weight is by colour —
   .ld-figure-value--quiet for a number that is background, .ok / .warn for
   one that says whether you may proceed.

   Wrap it in an <a> and the note underlines on hover; that is the whole
   affordance, because the number is the link. */
.ld-figures { display: flex; flex-wrap: wrap; gap: var(--ld-space-5); align-items: flex-start; }
.ld-figure { display: flex; flex-direction: column; gap: 2px; min-inline-size: 8.5rem; text-decoration: none; }
a.ld-figure:hover .ld-figure-note { text-decoration: underline; text-underline-offset: .15em; }
a.ld-figure:focus-visible { outline: none; box-shadow: var(--ld-focus-ring-flat); border-radius: var(--ld-radius-sm); }
.ld-figure-value { font-size: var(--ld-fs-stat); font-weight: 700; line-height: 1.1; color: var(--ld-text-value); }
.ld-figure-value.ok     { color: var(--ld-success-text); }
.ld-figure-value.warn   { color: var(--ld-warn-text); }
.ld-figure-value--brand { color: var(--ld-stat-label); }
.ld-figure-value--quiet { color: var(--ld-text-secondary); }
/* A timestamp is a fact, not a magnitude — it reads at label size. */
.ld-figure-value--stamp { font-size: var(--ld-fs-lg); font-weight: 600; }
.ld-figure-note { font-size: var(--ld-fs-micro); color: var(--ld-text-muted); }
/* The same data one weight down: "By teacher Claude 1,922 · OpenAI 38 · …" */
.ld-runs { display: flex; flex-direction: column; gap: 3px; margin-block-start: var(--ld-space-3);
           font-size: var(--ld-fs-label); color: var(--ld-text-muted); }
.ld-runs b { color: var(--ld-text-secondary); font-weight: 600; margin-inline-end: .4rem; }
@media (max-width: 767px) { .ld-figures { gap: var(--ld-space-4); } }

.ld-fold { margin-block-start: var(--ld-space-2); }
.ld-fold > .ld-fold__summary {
    display: flex; align-items: center; gap: var(--ld-space-2); cursor: pointer;
    padding-block: var(--ld-space-2); padding-inline: var(--ld-space-3);
    border: 1px solid var(--ld-border); border-radius: var(--ld-radius-lg);
    background: var(--ld-surface-subtle);
    font-size: var(--ld-fs-sm); font-weight: 600; color: var(--ld-text-secondary);
    list-style: none;
}
.ld-fold > .ld-fold__summary::-webkit-details-marker { display: none; }
.ld-fold > .ld-fold__summary::after {
    content: "\203A";                 /* › — turns down when the fold opens */
    margin-inline-start: var(--ld-space-2);
    font-size: 1.1rem; line-height: 1; color: var(--ld-text-muted);
    transition: transform .15s ease;
}
.ld-fold[open] > .ld-fold__summary::after { transform: rotate(90deg); }
.ld-fold[open] > .ld-fold__summary {
    border-end-start-radius: 0; border-end-end-radius: 0; border-block-end: none;
}
.ld-fold > .ld-fold__summary:hover { background: var(--ld-table-row-hover); }
.ld-fold > .ld-fold__summary:focus-visible { outline: none; box-shadow: var(--ld-focus-ring-flat); }
/* The rail already names the section, so the card inside it does not say the
   name a second time. A title that carries something the summary does NOT
   say — "Newest 100 of 3,995" — opts out with .ld-fold-keep. */
.ld-fold > :where(.ld-card, .vt-card, .mst-card, .cm-card, .yst-card, .yst-serving)
        > :where(.ld-section-title, .cm-title, .vt-section-title, h2, h3):first-child:not(.ld-fold-keep) {
    display: none;
}

.ld-fold__hint {
    margin-inline-start: auto; font-weight: 500;
    color: var(--ld-text-muted); font-size: var(--ld-fs-label);
}
/* Whatever card sits inside joins the rail above it. */
.ld-fold[open] > :where(.ld-card, .vt-card, .mst-card, .cm-card) {
    border-start-start-radius: 0; border-start-end-radius: 0; margin-block-start: 0;
}
@media (prefers-reduced-motion: reduce) {
    .ld-fold > .ld-fold__summary::after { transition: none; }
}

.ld-swatch {
    display: inline-block;
    inline-size: 14px;
    block-size: 14px;
    border-radius: var(--ld-radius-xs);
    border: 1px solid var(--ld-border);
    flex: none;
}

/* Leading icon inside a chip or a meta line: quieter than its text, and
   spaced from it without a wrapper. */
.ld-icon-lead {
    font-size: var(--ld-fs-micro);
    opacity: .7;
    margin-inline-end: 3px;
}

/* ── The rest of the vocabulary ──────────────────────────────────────────── */
html[data-theme] .ld-bg-secondary   { background: var(--ld-surface-secondary); }
html[data-theme] .ld-bg-success     { background: var(--ld-success-bg); }
html[data-theme] .ld-bg-warn        { background: var(--ld-warn-bg); }
html[data-theme] .ld-bg-error       { background: var(--ld-error-bg); }
html[data-theme] .ld-bg-btn         { background: var(--ld-btn-bg); }
html[data-theme] .ld-bg-transparent { background: transparent; }
html[data-theme] .ld-t-btn          { color: var(--ld-btn-text); }
html[data-theme] .ld-bc-input       { border-color: var(--ld-input-border); }
html[data-theme] .ld-bd-chip-brand  { border: 1px solid var(--ld-chip-brand-border); }
html[data-theme] .ld-bbs-row        { border-block-start: 1px solid var(--ld-table-row-border); }
html[data-theme] .ld-bbe-row        { border-block-end: 1px solid var(--ld-table-row-border); }

.ld-shadow-card   { box-shadow: var(--ld-card-shadow); }
.ld-rounded-md    { border-radius: var(--ld-radius-md); }
.ld-rounded-lg    { border-radius: var(--ld-radius-lg); }
.ld-rounded-xl    { border-radius: var(--ld-radius-xl); }
.ld-rounded-full  { border-radius: var(--ld-radius-full); }
.ld-clickable     { cursor: pointer; }
.ld-list-plain    { list-style: none; }
.ld-tracking-wide { letter-spacing: .08em; }
.ld-p-0           { padding: 0; }
.ld-mis-1 { margin-inline-start: var(--ld-space-1); }
.ld-mis-2 { margin-inline-start: var(--ld-space-2); }
.ld-mie-2 { margin-inline-end: var(--ld-space-2); }
.ld-mie-3 { margin-inline-end: var(--ld-space-3); }

/* ── The loud few ────────────────────────────────────────────────────────
   Seventy inline declarations carried !important, and every one of them was
   fighting the same thing: a shade-named Tailwind utility on the same
   element, re-coloured by a compatibility map that also shouts. They are
   here rather than inline so the value is at least reachable, and they are
   the ONLY !important in this layer. An element that needs one usually
   wants a component instead — a tile carrying .ld-bg-stat-i .ld-bc-stat-i
   is a .ld-stat-card that has not been told yet. */
/* ── Geometry the frozen Tailwind bundles do not carry ───────────────────
   Seven utilities, each one asked for four or more times by the pages and
   absent from the compiled build, so the page had no way to say it except
   inline. Everything Tailwind DOES carry (overflow, text-align, position,
   whitespace, truncate) stays Tailwind's job — this is not a second
   framework, it is the gaps in the first one. */
.ld-h-full       { block-size: 100%; }
.ld-maxw-full    { max-inline-size: 100%; }
.ld-minw-0       { min-inline-size: 0; }
.ld-m-0          { margin: 0; }
.ld-leading-none { line-height: 1; }
.ld-opacity-50   { opacity: .5; }
.ld-shrink-0     { flex: none; }

/* Visible to a screen reader, to nothing else. Four call sites were already
   asking for it by name before it existed, which meant four labels that were
   simply absent. Clip-path over the old width/height/overflow trick: it does
   not collapse the box, so a focusable child inside it still lands. */
.ld-sr-only {
    position: absolute;
    inline-size: 1px; block-size: 1px;
    margin: -1px; padding: 0; border: 0;
    clip-path: inset(50%);
    overflow: hidden; white-space: nowrap;
}
.ld-p-1 { padding: var(--ld-space-1); }
.ld-p-2 { padding: var(--ld-space-2); }
.ld-p-3 { padding: var(--ld-space-3); }
.ld-p-4 { padding: var(--ld-space-4); }
.ld-pbs-2 { padding-block-start: var(--ld-space-2); }
.ld-pbs-3 { padding-block-start: var(--ld-space-3); }
.ld-pbe-2 { padding-block-end: var(--ld-space-2); }
.ld-pbe-3 { padding-block-end: var(--ld-space-3); }
.ld-rounded-sm { border-radius: var(--ld-radius-sm); }

/* ── The dead-utility replacements ───────────────────────────────────────
   The modules ship a FROZEN Tailwind bundle: whatever was not compiled the
   day it was built does not exist, and a class the markup names anyway is a
   silent no-op — no warning, no error, just a declaration that never lands.
   Roughly 1,300 of them had collected across FIX, CRM and IMS: text-[10px],
   py-1.5, mt-0.5, px-2.5, ml-auto, transition-colors, hover:opacity-80. The
   dense type simply inherited its size and the auto margins never pushed.

   These are the replacements, on the scale and logical, so the markup can
   say what it meant. ml-auto in particular becomes margin-INLINE-start, so
   it pushes the right way in Hebrew, which the physical class never did. */
.ld-pb-1 { padding-block: var(--ld-space-1); }
.ld-pb-2 { padding-block: var(--ld-space-2); }
.ld-pb-3 { padding-block: var(--ld-space-3); }
.ld-pi-1 { padding-inline: var(--ld-space-1); }
.ld-pi-2 { padding-inline: var(--ld-space-2); }
.ld-pi-3 { padding-inline: var(--ld-space-3); }
.ld-pi-4 { padding-inline: var(--ld-space-4); }

.ld-transition-colors {
    transition: color .15s ease, background-color .15s ease,
                border-color .15s ease, fill .15s ease;
}
/* One hover dim, not two. hover:opacity-80 and hover:opacity-90 were both in
   play and neither existed; the difference between them was never seen. */
.ld-hover-dim { transition: opacity .15s ease; }
.ld-hover-dim:hover { opacity: .85; }

/* The second batch, found the same way: named in the markup, absent from the
   frozen bundle, doing nothing. bg-sky-600 is why a primary button rendered
   as a bare pill with no fill; focus:ring-* is why several controls had no
   visible focus at all, which is an accessibility failure and not a cosmetic
   one; bg-brand/15 is why brand chips had no tint. These restore the intent
   without touching geometry — the fill classes set colour only, so an element
   that already carries its own padding and radius keeps them. */
html[data-theme] .ld-fill-brand { background: var(--ld-btn-bg); color: var(--ld-btn-text); }
html[data-theme] .ld-fill-brand:hover { background: var(--ld-btn-hover-bg); }
.ld-focus-ring:focus-visible { outline: none; box-shadow: var(--ld-focus-ring); }
.ld-opacity-40 { opacity: .4; }
.ld-hover-opaque { transition: opacity .15s ease; }
.ld-hover-opaque:hover { opacity: 1; }
.ld-minw-10 { min-inline-size: 10rem; }
/* Vertical rhythm between siblings, the owl selector rather than a gap, so it
   works in a plain block container as space-y did. */
.ld-stack-1 > * + * { margin-block-start: var(--ld-space-1); }
.ld-stack-2 > * + * { margin-block-start: var(--ld-space-2); }
.ld-stack-3 > * + * { margin-block-start: var(--ld-space-3); }
@media (min-width: 768px) { .ld-md-span-2 { grid-column: span 2 / span 2; } }
@media (prefers-reduced-motion: reduce) { .ld-hover-opaque { transition: none; } }

@media (prefers-reduced-motion: reduce) {
    .ld-transition-colors, .ld-hover-dim { transition: none; }
}
html[data-theme] .ld-bg-chip-brand { background: var(--ld-chip-brand-bg); }
html[data-theme] .ld-bc-error      { border-color: var(--ld-error-border); }
.ld-shadow-tile { box-shadow: var(--ld-tile-shadow); }

/* A chip whose text is a NAME — a project, a person, a label the user typed.
   The base chip shouts (uppercase, tracked, 700) because it labels a state;
   a chip that carries someone's words must not put them in capitals. Same
   shape, same surfaces, ordinary sentence case. */
.ld-chip--plain {
    text-transform: none;
    letter-spacing: 0;
    font-weight: 600;
    font-size: var(--ld-fs-label);
}

html[data-theme] .ld-bg-stat-i   { background-color: var(--ld-stat-bg) !important; }
html[data-theme] .ld-bg-subtle-i { background-color: var(--ld-surface-subtle) !important; }
html[data-theme] .ld-bg-card-i   { background-color: var(--ld-card-bg) !important; }
html[data-theme] .ld-bc-stat-i   { border-color: var(--ld-stat-border) !important; }
html[data-theme] .ld-t-brand-i   { color: var(--ld-stat-label) !important; }
html[data-theme] .ld-t-text-i    { color: var(--ld-text) !important; }
html[data-theme] .ld-t-text-muted-i { color: var(--ld-text-muted) !important; }
html[data-theme] .ld-t-warn-i    { color: var(--ld-warn-text) !important; }
html[data-theme] .ld-opaque-i    { opacity: 1 !important; }

/* ═══════════════════════════════════════════════════════════════════════════
   ACCESSIBILITY LAYER — html.ld-a11y-* switches and the skip link
   Set by ldata.appearance.js from the shell's accessibility panel and carried
   into every embedded module by SET_UI_VARS. Each switch is one thing the
   operator asked for, keyed off <html>, so no page has to know it exists.
   High contrast lives in ldata-tokens.css (html[data-a11y-contrast="high"]),
   text size is the --ui-text seed on the root font-size.
   ═══════════════════════════════════════════════════════════════════════════ */

/* Skip link: the first tab stop on every shell page. Invisible until focused,
   then a solid pill at the top start corner, above every bar. */
.ld-skip-link {
    position: fixed; inset-block-start: .5rem; inset-inline-start: .5rem; z-index: 10000;
    padding: .5rem .875rem; border-radius: var(--ld-radius-md);
    background: var(--ld-btn-bg); color: var(--ld-btn-text); font: 600 .875rem/1.2 system-ui, sans-serif;
    text-decoration: none; box-shadow: var(--ld-focus-ring);
    transform: translateY(-200%); opacity: 0; pointer-events: none;
    transition: transform .15s ease-out, opacity .15s ease-out;
}
.ld-skip-link:focus, .ld-skip-link:focus-visible { transform: none; opacity: 1; pointer-events: auto; outline: none; }

/* Underlined links: every link an operator can click says so, everywhere. */
html.ld-a11y-links a:not(.ld-btn):not(.ld-btn-ghost):not(.ld-skip-link),
html[data-a11y-contrast="high"] a:not(.ld-btn):not(.ld-btn-ghost):not(.ld-skip-link) {
    text-decoration: underline; text-decoration-thickness: from-font; text-underline-offset: .15em;
}

/* Strong focus: a thick, offset, two-colour outline on every focusable thing,
   over whatever ring the component drew, so keyboard position is never lost. */
html.ld-a11y-focus :focus-visible {
    outline: 3px solid var(--ld-focus-ring-color); outline-offset: 3px;
    box-shadow: 0 0 0 2px var(--ld-card-bg), 0 0 0 6px var(--ld-focus-ring-color);
}

/* Reduce motion: the operator's explicit request; the OS-level preference is
   honoured by each component's own media query. Durations collapse to nothing
   rather than being removed, so state changes still land and JS transitionend
   handlers still fire. */
html.ld-a11y-motion *, html.ld-a11y-motion *::before, html.ld-a11y-motion *::after {
    animation-duration: .01ms !important; animation-iteration-count: 1 !important;
    transition-duration: .01ms !important; scroll-behavior: auto !important;
}

/* Readable text: a plain humanist sans, opened tracking and leading, on the
   text-carrying elements only - never on <i>/<svg>, whose "font" is an icon set. */
html.ld-a11y-readable :where(body, p, span, div, li, dt, dd, td, th, label, legend, input, textarea, select, button, a, small, strong, em, b, h1, h2, h3, h4, h5, h6, summary, figcaption, blockquote):not(:where(.fa, .fas, .far, .fab, .fal, .fad, .fat, .fa-solid, .fa-regular, .fa-brands, .fa-light, .fa-thin, .fa-duotone, .material-icons, .material-symbols-outlined, .material-symbols-rounded, .material-symbols-sharp, .glyphicon, .bi)) {
    font-family: Arial, "Noto Sans", "Noto Sans Hebrew", "Segoe UI", Helvetica, sans-serif;
    letter-spacing: .02em; word-spacing: .08em; line-height: 1.6;
}
html.ld-a11y-readable :where(p, li, td, dd, blockquote) { line-height: 1.7; }

/* Big cursor: a 40px arrow, and a 40px hand where the incumbent cursor is a
   pointer. White with a black outline so it reads on light and dark alike. */
html.ld-a11y-cursor, html.ld-a11y-cursor * { cursor: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='40' height='40' viewBox='0 0 40 40'><path d='M6 4 L6 32 L13.5 25 L19 37 L24 35 L18.5 23 L28 23 Z' fill='%23fff' stroke='%23000' stroke-width='2.5' stroke-linejoin='round'/></svg>") 5 3, auto; }
html.ld-a11y-cursor :where(a, button, [role="button"], label, select, summary, .ld-btn, .ld-btn-ghost, .ld-tab, .ld-chip, input[type="checkbox"], input[type="radio"], input[type="range"], [onclick]),
html.ld-a11y-cursor :where(a, button, [role="button"], label, select, summary, [onclick]) * { cursor: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='40' height='40' viewBox='0 0 40 40'><path d='M15 3 a3 3 0 0 1 6 0 v13 h2 v-9 a3 3 0 0 1 6 0 v9 h2 v-6 a3 3 0 0 1 6 0 v16 c0 6-4 11-11 11 h-5 c-4 0-6-2-8-5 l-7-10 a3 3 0 0 1 5-3 l4 5 v-21 a3 3 0 0 1 6 0 z' fill='%23fff' stroke='%23000' stroke-width='2.5' stroke-linejoin='round'/></svg>") 14 3, pointer; }
html.ld-a11y-cursor :where(input[type="text"], input[type="search"], input[type="email"], input[type="password"], input[type="number"], input[type="url"], input[type="tel"], textarea, [contenteditable="true"]) { cursor: text; }

/* BEGIN SHARED INTERFACE TYPOGRAPHY */
/* Navigation-sized text is a platform preference, including page-local and
   framework utilities. The early theme layer also precedes Tailwind utility layers for important
   declarations; icons and authored content opt out. */
@layer theme {
    @media screen {
        :root {
            --ld-interface-font: system-ui, -apple-system, "Segoe UI", Roboto, "Inter", sans-serif;
            --ld-interface-size: calc(12px * var(--ld-seed-text, var(--ui-text, 1)));
        }
        html.ld-a11y-readable {
            --ld-interface-font: Arial, "Noto Sans", "Noto Sans Hebrew", "Segoe UI", Helvetica, sans-serif;
        }
        :where(
            body, h1, h2, h3, h4, h5, h6, p, div, span, a, button, input,
            select, option, optgroup, textarea, label, legend, summary, small,
            strong, b, em, li, dt, dd, table, th, td, caption, time, bdi,
            output, figcaption, blockquote
        ):not(:where(
            .fa, .fas, .far, .fab, .fal, .fad, .fat, .fa-solid, .fa-regular,
            .fa-brands, .fa-light, .fa-thin, .fa-duotone, .material-icons,
            .material-symbols-outlined, .material-symbols-rounded,
            .material-symbols-sharp, .glyphicon, .bi,
            pre, pre *, code, code *, kbd, kbd *, samp, samp *, svg *, math *,
            .font-mono, .font-mono *, .monaco-editor, .monaco-editor *,
            .CodeMirror, .CodeMirror *, .cm-editor, .cm-editor *,
            .ace_editor, .ace_editor *, .ld-log-console, .ld-log-console *,
            .clock-slot, .clock-slot *, [contenteditable="true"],
            [contenteditable="true"] *, [data-ld-typography="preserve"],
            [data-ld-typography="preserve"] *
        )) {
            font-family: var(--ld-interface-font) !important;
            font-size: var(--ld-interface-size) !important;
        }
    }
}
