:root {
    --void: #001a2d;
    --ink: #f7f9fb;
    --blood: #8b0000;
    --steel: #5f6e7c;
    --gold: #c9a634;
    --slate: #1a1a1a;
    --panel: #0a1d2c;
    --panel-2: #0e2435;
    --glow-blood: rgba(139,0,0,.55);
    --glow-gold: rgba(201,166,52,.55);
    --line: rgba(255,255,255,.06);
}

html, body {
    height: 100%;
    margin: 0;
}

body {
    /* min-height: 100vh makes body grow to cover the full
       scrollable area on mobile, where background-attachment:
       fixed is widely ignored and a height: 100% body box
       would otherwise run out before the document does,
       exposing the browser-default white past the body's
       gradient. With min-height: 100vh the body's box covers
       every scroll position so the gradient paints
       everywhere. html stays without a background so root-
       background-propagation still hands the body gradient
       up to the canvas, which is what kept the
       .blueprint-grid (z-index: -1) visible above the
       propagated background. */
    display: flex;
    flex-direction: column;
    min-height: 100vh;
    font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
    background: linear-gradient(180deg, #002a45 0%, var(--void) 40%, var(--void) 70%, #000d16 100%);
    background-attachment: fixed;
    color: var(--ink);
}

.blueprint-grid {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-image:
        linear-gradient(to right, rgba(95,110,124,0.08) 1px, transparent 1px),
        linear-gradient(to bottom, rgba(95,110,124,0.08) 1px, transparent 1px);
    background-size: 20px 20px;
    pointer-events: none;
    z-index: -1;
}

/* Navbar — imperial bridge: gold inset rail + crimson bloodline */
.navbar {
    background: linear-gradient(180deg, var(--panel-2), var(--panel));
    border-bottom: none;
    padding: 0.7rem 1.5rem;
    display: flex;
    align-items: center;
    justify-content: space-between;
    position: relative;
    box-shadow:
        inset 0 1px 0 rgba(201, 166, 52, 0.18),
        inset 0 -1px 0 rgba(201, 166, 52, 0.32),
        0 6px 18px rgba(0, 0, 0, 0.55);
}

.navbar::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: -2px;
    height: 2px;
    background: linear-gradient(90deg,
        transparent 0%,
        rgba(139, 0, 0, 0.55) 20%,
        rgba(255, 90, 90, 0.85) 50%,
        rgba(139, 0, 0, 0.55) 80%,
        transparent 100%);
    box-shadow: 0 0 14px var(--glow-blood), 0 0 28px rgba(139, 0, 0, 0.35);
    pointer-events: none;
}

.navbar-brand {
    display: flex;
    align-items: center;
    text-decoration: none;
    color: var(--ink);
}

.navbar-brand:hover {
    color: var(--gold);
    text-decoration: none;
}

.navbar-brand-logo {
    width: 48px;
    height: 48px;
    margin-right: 1rem;
    display: block;
    filter: drop-shadow(0 0 6px var(--glow-gold));
}

.navbar-brand-text {
    display: flex;
    flex-direction: column;
    padding-left: 1rem;
    border-left: 1px solid rgba(201, 166, 52, 0.28);
    box-shadow: -1px 0 0 rgba(0, 0, 0, 0.65);
}

.navbar-brand-title {
    color: var(--gold);
    font-weight: 900;
    letter-spacing: 0.22em;
    font-size: 1rem;
    text-transform: uppercase;
    text-shadow: 0 0 8px var(--glow-gold), 0 0 18px rgba(201, 166, 52, 0.32);
}

.navbar-brand-tagline {
    color: var(--steel);
    opacity: 0.85;
    letter-spacing: 0.18em;
    font-size: 0.6rem;
    font-weight: 600;
    text-transform: uppercase;
    margin-top: 2px;
}

.navbar .nav-link {
    color: var(--ink) !important;
    font-size: 0.9rem;
}

.navbar .nav-link:hover {
    color: var(--gold) !important;
}

.nav-user-btn {
    background: linear-gradient(180deg, rgba(201, 166, 52, 0.14), rgba(201, 166, 52, 0.04));
    border: none;
    color: var(--ink);
    padding: 0.45rem 1.1rem;
    font-weight: 700;
    font-size: 0.72rem;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    cursor: pointer;
    font-family: inherit;
    text-decoration: none;
    display: inline-flex;
    align-items: center;
    line-height: 1.5;
    clip-path: polygon(8px 0, 100% 0, 100% calc(100% - 8px), calc(100% - 8px) 100%, 0 100%, 0 8px);
    box-shadow: inset 0 0 0 2px rgba(201, 166, 52, 0.55);
    transition: box-shadow 0.12s ease, color 0.12s ease, background 0.12s ease;
}

.nav-user-btn:hover {
    background: linear-gradient(180deg, rgba(201, 166, 52, 0.28), rgba(201, 166, 52, 0.08));
    box-shadow: inset 0 0 0 2px var(--gold), 0 0 16px var(--glow-gold);
    color: var(--gold);
    text-decoration: none;
}

/* App navbar — the row of cross-page navigation buttons (Status +
   per-component links) that sits beneath the page header. */
.app-navbar {
    background: linear-gradient(180deg, rgba(0, 13, 22, 0.95), rgba(0, 0, 0, 0.65));
    border-bottom: none;
    padding: 0.7rem 1.5rem;
    display: flex;
    align-items: center;
    gap: 0.6rem;
    flex-wrap: wrap;
    position: relative;
    box-shadow:
        inset 0 1px 0 rgba(201, 166, 52, 0.18),
        0 1px 0 rgba(0, 0, 0, 0.85),
        0 4px 14px rgba(0, 0, 0, 0.4);
}

.app-navbar-label {
    color: var(--gold);
    font-weight: 700;
    font-size: 0.65rem;
    text-transform: uppercase;
    letter-spacing: 0.22em;
    margin-right: 0.5rem;
    text-shadow: 0 0 6px rgba(201, 166, 52, 0.35);
}

.app-navbar-link {
    position: relative;
    color: var(--ink);
    font-size: 0.66rem;
    font-weight: 700;
    letter-spacing: 0.22em;
    text-transform: uppercase;
    padding: 0.45rem 1rem 0.45rem 1.15rem;
    background: linear-gradient(180deg, var(--panel-2), var(--panel));
    border: none;
    text-decoration: none;
    clip-path: polygon(7px 0, 100% 0, 100% calc(100% - 7px), calc(100% - 7px) 100%, 0 100%, 0 7px);
    box-shadow: inset 0 0 0 1px rgba(201, 166, 52, 0.22);
    transition: box-shadow 0.15s ease, color 0.15s ease, background 0.15s ease, transform 0.15s ease;
}

.app-navbar-link::before {
    content: "";
    position: absolute;
    left: 0;
    top: 7px;
    bottom: 7px;
    width: 3px;
    background: linear-gradient(180deg, rgba(139, 0, 0, 0.95), rgba(139, 0, 0, 0.55));
    box-shadow: 0 0 8px var(--glow-blood), 0 0 14px rgba(139, 0, 0, 0.35);
}

.app-navbar-link:hover {
    color: var(--gold);
    background: linear-gradient(180deg, var(--panel), var(--void));
    box-shadow:
        inset 0 0 0 1px rgba(201, 166, 52, 0.65),
        0 0 14px rgba(201, 166, 52, 0.28);
    transform: translateY(-1px);
    text-decoration: none;
}

.app-navbar-link:hover::before {
    background: linear-gradient(180deg, rgba(255, 90, 90, 0.95), rgba(139, 0, 0, 0.7));
    box-shadow: 0 0 12px var(--glow-blood), 0 0 22px rgba(255, 90, 90, 0.45);
}

.app-navbar-link.active {
    color: var(--gold);
    background: linear-gradient(180deg, var(--panel), var(--void));
    box-shadow:
        inset 0 0 0 1px rgba(201, 166, 52, 0.65),
        0 0 14px rgba(201, 166, 52, 0.28);
}

.app-navbar-link.active::before {
    background: linear-gradient(180deg, rgba(255, 90, 90, 0.95), rgba(139, 0, 0, 0.7));
    box-shadow: 0 0 12px var(--glow-blood), 0 0 22px rgba(255, 90, 90, 0.45);
}

/* Component tabs — per-page navigation between sub-views of a
   single component (Overview / Logs / Configuration). Lives
   inside a panel-card, between the header strip and the body. */
.component-tabs {
    display: flex;
    flex-wrap: wrap;
    gap: 0.25rem;
    padding: 0.5rem 1.5rem;
    background: rgba(0, 13, 22, 0.5);
    border-bottom: 1px solid var(--line);
}

.component-tabs-link {
    color: var(--steel);
    text-transform: uppercase;
    letter-spacing: 0.12em;
    font-size: 0.7rem;
    font-weight: 700;
    padding: 0.4rem 0.9rem;
    text-decoration: none;
    border-radius: 3px;
    border: 1px solid transparent;
    transition: color 0.12s ease, border-color 0.12s ease, background 0.12s ease;
}

.component-tabs-link:hover {
    color: var(--gold);
    border-color: rgba(201, 166, 52, 0.4);
    background: rgba(201, 166, 52, 0.08);
    text-decoration: none;
}

.component-tabs-link.active {
    color: var(--gold);
    border-color: rgba(201, 166, 52, 0.6);
    background: rgba(201, 166, 52, 0.14);
    text-shadow: 0 0 6px rgba(201, 166, 52, 0.4);
}

/* Overview sub-page card — fitted, centred presentation of the
   component's long-form overview text. Sits inside the panel-card
   body and reads as a quote-block-like fitted slab. */
.overview-card {
    max-width: 70ch;
    margin: 1.5rem auto;
    padding: 1.75rem 2rem;
}

/* Wrapper around the rendered markdown body of a component
   overview. The Python markdown library produces a free
   vocabulary of children (headings, paragraphs, lists, code,
   tables, blockquotes, links); the rules below scope each to
   the dashboard's existing visual vocabulary so an operator-
   authored .md slots in without looking like an unstyled
   default-Bootstrap fragment. */
.component-overview {
    color: var(--ink);
    font-size: 0.95rem;
    line-height: 1.7;
    text-align: left;
    hyphens: auto;
}

.component-overview > :first-child {
    margin-top: 0;
}

.component-overview > :last-child {
    margin-bottom: 0;
}

.component-overview h1,
.component-overview h2,
.component-overview h3,
.component-overview h4,
.component-overview h5,
.component-overview h6 {
    color: var(--gold);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    font-weight: 700;
    margin: 1.5rem 0 0.5rem 0;
}

.component-overview h1 { font-size: 1.05rem; }
.component-overview h2 { font-size: 0.95rem; }
.component-overview h3 { font-size: 0.85rem; }
.component-overview h4,
.component-overview h5,
.component-overview h6 { font-size: 0.8rem; }

.component-overview p {
    margin: 0 0 0.9rem 0;
}

.component-overview ul,
.component-overview ol {
    margin: 0 0 0.9rem 1.5rem;
    padding: 0;
}

.component-overview li {
    margin-bottom: 0.3rem;
}

.component-overview code {
    background: rgba(0, 26, 45, 0.7);
    color: var(--gold);
    padding: 0.1rem 0.35rem;
    border-radius: 3px;
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    font-size: 0.85rem;
}

.component-overview pre {
    background: var(--void);
    color: var(--ink);
    padding: 0.75rem 1rem;
    border: 1px solid var(--line);
    border-radius: 3px;
    overflow: auto;
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    font-size: 0.8rem;
    line-height: 1.45;
    margin: 0 0 0.9rem 0;
}

.component-overview pre code {
    background: transparent;
    color: inherit;
    padding: 0;
    border-radius: 0;
}

.component-overview blockquote {
    border-left: 3px solid var(--gold);
    padding-left: 1rem;
    margin: 0 0 0.9rem 0;
    color: var(--steel);
    font-style: italic;
}

.component-overview a {
    color: var(--gold);
    text-decoration: underline dotted;
    text-underline-offset: 3px;
}

.component-overview a:hover {
    color: var(--ink);
}

.component-overview table {
    width: 100%;
    border-collapse: collapse;
    margin: 0 0 0.9rem 0;
    font-size: 0.85rem;
}

.component-overview th,
.component-overview td {
    text-align: left;
    padding: 0.4rem 0.6rem;
    border-bottom: 1px solid var(--line);
}

.component-overview th {
    color: var(--gold);
    text-transform: uppercase;
    letter-spacing: 0.05em;
    font-size: 0.7rem;
    font-weight: 700;
}

/* Execution-page action card — single operator-triggered action
   per block (button + spinner + status), ready for the page to
   stack additional actions vertically as more operations land. */
.exec-section {
    display: flex;
    flex-direction: column;
    gap: 1.5rem;
    padding: 0.5rem 0;
}

.exec-action {
    padding: 1.25rem 1.5rem;
}

.exec-action-title {
    color: var(--gold);
    text-transform: uppercase;
    letter-spacing: 0.1em;
    font-size: 0.85rem;
    font-weight: 700;
    margin: 0 0 0.5rem 0;
}

.exec-action-description {
    color: var(--ink);
    font-size: 0.85rem;
    line-height: 1.55;
    margin: 0 0 1rem 0;
}

.exec-action-controls {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.85rem;
}

.exec-action-status {
    color: var(--steel);
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    font-size: 0.75rem;
    letter-spacing: 0.05em;
}

.exec-action-status.error {
    color: #ff9b9b;
}

/* Collapse toggle inside an exec-action card. The whole header
   row is the click target: an .exec-action-title sits inside
   the button so the gold-uppercase weight matches every other
   section header on the page, with a chevron pushed to the
   right edge that rotates 180deg when aria-expanded flips to
   true. Bootstrap's collapse plugin owns the show/hide of the
   targeted body via data-toggle="collapse" data-target="#id";
   this rule only owns the visual chrome. */
.exec-action-collapse-toggle {
    display: flex;
    align-items: center;
    width: 100%;
    background: transparent;
    border: none;
    padding: 0;
    cursor: pointer;
    text-align: left;
    color: inherit;
}

.exec-action-collapse-toggle:focus {
    outline: none;
}

.exec-action-collapse-toggle .exec-action-title {
    margin-bottom: 0;
}

.exec-action-collapse-chevron {
    margin-left: auto;
    color: var(--gold);
    transition: transform 0.2s ease;
}

.exec-action-collapse-toggle[aria-expanded="true"] .exec-action-collapse-chevron {
    transform: rotate(180deg);
}

/* Schedule form — vertical stack of one toggle per row, then a
   labelled time input, then a Save button. Mirrors the
   exec-action card chrome used by the Execution page. */
.schedule-form {
    display: flex;
    flex-direction: column;
    gap: 0.65rem;
    align-items: flex-start;
    margin-top: 0.5rem;
}

.schedule-row {
    display: flex;
    align-items: center;
    gap: 0.85rem;
    flex-wrap: wrap;
}

.schedule-time-row {
    margin-top: 0.35rem;
}

.schedule-time-label {
    color: var(--steel);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    font-size: 0.62rem;
    font-weight: 700;
    margin: 0;
}

.schedule-time-input {
    background: var(--void);
    color: var(--ink);
    border: 1px solid var(--steel);
    border-radius: 3px;
    padding: 0.4rem 0.6rem;
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    font-size: 0.85rem;
    letter-spacing: 0.05em;
    color-scheme: dark;
}

.schedule-time-input:focus {
    outline: none;
    border-color: var(--gold);
    box-shadow: 0 0 0 2px var(--glow-gold);
}

.schedule-time-input:disabled {
    background: rgba(0, 26, 45, 0.5);
    color: var(--steel);
    border-color: rgba(95, 110, 124, 0.3);
}

.schedule-time-hint {
    color: var(--steel);
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    font-size: 0.7rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

/* Blacklist page — bulk-add textarea on top, scrollable list
   of current entries below with a per-row remove button. */
.bl-section {
    display: flex;
    flex-direction: column;
    gap: 1.25rem;
}

.bl-add,
.bl-list {
    padding: 1rem 1.25rem;
}

.bl-add-header,
.bl-list-header {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    margin-bottom: 0.6rem;
}

.bl-add-title,
.bl-list-title {
    color: var(--gold);
    text-transform: uppercase;
    letter-spacing: 0.1em;
    font-size: 0.8rem;
    font-weight: 700;
    margin: 0;
}

.bl-add-hint {
    color: var(--steel);
    font-size: 0.72rem;
    letter-spacing: 0.04em;
}

.bl-add-textarea {
    width: 100%;
    background: var(--void);
    color: var(--ink);
    border: 1px solid var(--steel);
    border-radius: 3px;
    padding: 0.5rem 0.7rem;
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    font-size: 0.85rem;
    line-height: 1.4;
    resize: vertical;
    color-scheme: dark;
    box-sizing: border-box;
}

.bl-add-textarea:focus {
    outline: none;
    border-color: var(--gold);
    box-shadow: 0 0 0 2px var(--glow-gold);
}

.bl-add-controls {
    display: flex;
    align-items: center;
    gap: 0.85rem;
    margin-top: 0.6rem;
    flex-wrap: wrap;
}

.bl-add-status {
    color: var(--steel);
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    font-size: 0.72rem;
    letter-spacing: 0.04em;
}

.bl-add-status.error {
    color: #ff9b9b;
}

.bl-add-result {
    margin-top: 0.7rem;
    padding: 0.6rem 0.75rem;
    background: var(--void);
    border: 1px solid var(--line);
    border-radius: 3px;
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    font-size: 0.74rem;
}

.bl-add-result-row {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    line-height: 1.4;
}

.bl-add-result-label {
    color: var(--steel);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-weight: 700;
    font-size: 0.66rem;
    min-width: 12em;
}

.bl-add-result-row.bl-add-result-added .bl-add-result-label { color: var(--gold); }
.bl-add-result-row.bl-add-result-dup .bl-add-result-label { color: var(--ink); }
.bl-add-result-row.bl-add-result-invalid .bl-add-result-label { color: #ff9b9b; }

.bl-add-result-value {
    color: var(--ink);
    word-break: break-word;
}

.bl-list-count {
    color: var(--gold);
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    font-weight: 700;
    font-size: 0.85rem;
}

.bl-list-status {
    color: var(--steel);
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    font-size: 0.72rem;
    margin-bottom: 0.5rem;
    min-height: 1em;
}

.bl-list-status.error {
    color: #ff9b9b;
}

.bl-list-items {
    max-height: 50vh;
    overflow: auto;
    background: var(--void);
    border: 1px solid var(--line);
    border-radius: 3px;
    padding: 0.4rem;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
    gap: 0.35rem;
}

.bl-list-empty {
    grid-column: 1 / -1;
    text-align: center;
    color: var(--steel);
    font-style: italic;
    padding: 1rem;
}

.bl-list-item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.4rem;
    background: linear-gradient(180deg, var(--panel-2), var(--panel));
    border-radius: 3px;
    padding: 0.3rem 0.5rem;
    box-shadow: inset 0 0 0 1px rgba(201, 166, 52, 0.18);
}

.bl-list-item-symbol {
    color: var(--ink);
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    font-weight: 700;
    font-size: 0.78rem;
    letter-spacing: 0.05em;
}

.bl-list-item-remove {
    padding: 0.15rem 0.45rem !important;
    font-size: 0.7rem !important;
}

/* Width-constrains the blacklist TTL number input on the
   Configuration page — type="number" otherwise inherits the
   wide form-control width. */
.bl-ttl-input {
    width: 6rem;
}

/* ORK component Configuration page form. One outer grid binds
   labels and inputs across every section so all rows align to
   the widest label per edict 33 — `display: contents` on each
   .ork-config-row makes its children (label, input, unit) land
   directly in the parent grid's columns. Section blocks span
   all columns to act as full-width visual dividers between
   groups of related rows. */
.ork-config-form {
    display: grid;
    grid-template-columns: max-content max-content 1fr;
    align-items: center;
    column-gap: 0.85rem;
    row-gap: 0.55rem;
}

.ork-config-section {
    grid-column: 1 / -1;
    margin-top: 1.5rem;
    padding-top: 1.25rem;
    border-top: 1px solid var(--line);
}

.ork-config-section:first-child {
    margin-top: 0;
    padding-top: 0;
    border-top: none;
}

.ork-config-section-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    margin-bottom: 0.4rem;
}

.ork-config-section-title {
    color: var(--gold);
    text-transform: uppercase;
    letter-spacing: 0.1em;
    font-size: 0.85rem;
    font-weight: 700;
    margin: 0;
}

.ork-config-section-status {
    color: var(--steel);
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    font-size: 0.72rem;
    letter-spacing: 0.05em;
}

.ork-config-section-status.error {
    color: #ff9b9b;
}

.ork-config-section-desc {
    color: var(--ink);
    font-size: 0.85rem;
    line-height: 1.55;
    margin: 0 0 0.6rem 0;
}

/* Each row is layout-transparent — its three children become
   grid items in the parent's columns, so labels (column 1) and
   inputs (column 2) automatically align across every row in
   every section. */
.ork-config-row {
    display: contents;
}

.ork-config-label {
    color: var(--steel);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    font-size: 0.62rem;
    font-weight: 700;
    margin: 0;
}

.ork-config-input {
    width: 6rem;
    background: var(--void);
    color: var(--ink);
    border: 1px solid var(--steel);
    border-radius: 3px;
    padding: 0.4rem 0.6rem;
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    font-size: 0.85rem;
    letter-spacing: 0.05em;
    color-scheme: dark;
}

.ork-config-input:focus {
    outline: none;
    border-color: var(--gold);
    box-shadow: 0 0 0 2px var(--glow-gold);
}

.ork-config-input:disabled {
    background: rgba(0, 26, 45, 0.5);
    color: var(--steel);
    border-color: rgba(95, 110, 124, 0.3);
}

.ork-config-unit {
    color: var(--steel);
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    font-size: 0.7rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

/* Inventory view — read-only display of the current universe
   inventory (built_at + symbol grid). */
.inv-view {
    display: flex;
    flex-direction: column;
    gap: 1rem;
}

.inv-view-header {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 1.5rem;
    padding: 0.75rem 1rem;
}

.inv-view-meta {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
}

.inv-view-label {
    color: var(--steel);
    text-transform: uppercase;
    letter-spacing: 0.18em;
    font-size: 0.6rem;
    font-weight: 700;
}

.inv-view-value {
    color: var(--ink);
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    font-size: 0.95rem;
    font-weight: 600;
    letter-spacing: 0.05em;
}

.inv-view-status {
    margin-left: auto;
    color: var(--steel);
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    font-size: 0.75rem;
}

.inv-view-status.error {
    color: #ff9b9b;
}

.inv-view-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
    gap: 0.4rem;
    max-height: 60vh;
    overflow: auto;
    padding: 0.75rem;
    background: var(--void);
    border: 1px solid var(--line);
    border-radius: 3px;
}

.inv-view-symbol {
    background: linear-gradient(180deg, var(--panel-2), var(--panel));
    color: var(--ink);
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    font-size: 0.78rem;
    font-weight: 700;
    text-align: center;
    padding: 0.4rem 0.5rem;
    border-radius: 3px;
    box-shadow: inset 0 0 0 1px rgba(201, 166, 52, 0.18);
    letter-spacing: 0.05em;
    text-decoration: none;
    transition: color 0.12s ease, box-shadow 0.12s ease;
}

.inv-view-symbol:hover,
.inv-view-symbol:focus {
    color: var(--gold);
    box-shadow: inset 0 0 0 1px rgba(201, 166, 52, 0.55);
    text-decoration: none;
}

.inv-view-empty {
    grid-column: 1 / -1;
    text-align: center;
    color: var(--steel);
    font-style: italic;
    padding: 1.5rem;
}

/* Dropdown — match the panel-card surface */
.dropdown-menu {
    background: linear-gradient(180deg, var(--panel-2), var(--panel)) !important;
    border: none !important;
    box-shadow:
        inset 0 0 0 1px rgba(201, 166, 52, 0.32),
        0 8px 22px rgba(0, 0, 0, 0.6),
        0 0 18px rgba(201, 166, 52, 0.12) !important;
    border-radius: 4px !important;
    padding: 0.35rem 0 !important;
}

.dropdown-item {
    color: var(--ink) !important;
    font-size: 0.78rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    font-weight: 600;
    padding: 0.5rem 1rem !important;
}

.dropdown-item:hover,
.dropdown-item:focus {
    background: linear-gradient(90deg, rgba(139, 0, 0, 0.35), transparent 65%) !important;
    color: var(--gold) !important;
    box-shadow: inset 2px 0 0 var(--blood);
}

.dropdown-divider {
    border-color: rgba(201, 166, 52, 0.22) !important;
    margin: 0.25rem 0 !important;
}

/* Main content */
.main-content {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 2rem 1rem;
}

.main-content.dashboard-content {
    align-items: flex-start;
    padding-top: 2rem;
}

/* Login card */
.login-card {
    background: var(--slate);
    border: 1px solid var(--line);
    border-radius: 4px;
    width: 100%;
    max-width: 420px;
    box-shadow: 0 8px 30px rgba(0,0,0,0.4);
    color: var(--ink);
}

.login-header {
    text-align: center;
    padding: 2rem 2rem 1rem;
    border-bottom: 1px solid var(--line);
}

.login-header h1 {
    font-size: 1.1rem;
    font-weight: 600;
    color: var(--ink);
    letter-spacing: 0.1em;
    margin: 0.5rem 0 0;
}

.login-header .subtitle {
    color: var(--steel);
    font-size: 0.8rem;
    text-transform: uppercase;
    letter-spacing: 0.05em;
}

.login-failed {
    max-width: 420px;
    background: var(--blood);
    border-left: 3px solid #ff4444;
    color: var(--ink);
}

.login-actions {
    display: flex;
    gap: 0.5rem;
}

/* Forms */
.form-group {
    margin-bottom: 1rem;
}

label, .form-label {
    display: block;
    font-size: 0.8rem;
    color: var(--steel);
    margin-bottom: 0.35rem;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    font-weight: 600;
}

.form-control {
    display: block;
    width: 100%;
    padding: 0.6rem 0.75rem;
    font-size: 0.95rem;
    font-family: inherit;
    color: var(--ink) !important;
    background: var(--void) !important;
    border: 1px solid var(--steel) !important;
    border-radius: 3px;
    outline: none;
    box-sizing: border-box;
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

.form-control:focus {
    border-color: var(--gold) !important;
    box-shadow: 0 0 0 2px var(--glow-gold) !important;
}

.form-control::placeholder {
    color: var(--steel) !important;
    opacity: 0.6;
}

.form-control:disabled,
.form-control[readonly] {
    background: rgba(0,26,45,0.5) !important;
    color: var(--steel) !important;
    border-color: rgba(95,110,124,0.3) !important;
}

select.form-control {
    appearance: auto;
}

/* Buttons */
.btn-primary {
    display: block;
    width: 100%;
    padding: 0.7rem 1.5rem;
    font-family: inherit;
    font-size: 0.95rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: var(--void) !important;
    background: var(--gold) !important;
    border: none !important;
    border-radius: 3px;
    cursor: pointer;
    transition: background 0.15s ease;
}

.btn-primary:hover,
.btn-primary:focus {
    background: #b8952e !important;
    color: var(--void) !important;
}

.btn-gold {
    background: var(--gold) !important;
    color: var(--void) !important;
    border: none !important;
    padding: 0.5rem 1.5rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    font-size: 0.9rem;
    border-radius: 3px;
    transition: background 0.15s ease;
}

.btn-gold:hover {
    background: #b8952e !important;
    color: var(--void) !important;
}

.btn-void {
    background: var(--void) !important;
    color: var(--ink) !important;
    border: 1px solid var(--steel) !important;
    padding: 0.5rem 1.5rem;
    font-weight: 600;
    font-size: 0.85rem;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    border-radius: 3px;
    transition: border-color 0.15s ease;
}

.btn-void:hover {
    border-color: var(--gold) !important;
    color: var(--gold) !important;
}

.btn-blood {
    background: var(--blood) !important;
    color: var(--ink) !important;
    border: none !important;
    padding: 0.5rem 1rem;
    font-weight: 600;
    border-radius: 3px;
}

.btn-blood:hover {
    background: #a00000 !important;
}

.btn-outline-secondary {
    color: var(--steel) !important;
    border-color: var(--steel) !important;
    background: transparent !important;
    font-size: 0.85rem;
    text-transform: uppercase;
    letter-spacing: 0.05em;
}

.btn-outline-secondary:hover {
    color: var(--ink) !important;
    border-color: var(--ink) !important;
    background: rgba(255,255,255,0.05) !important;
}

/* Panel cards */
/* Imperial frame. The parent panel-card wraps each component's
   sub-page surface. Where every .tetsuo-card inside owns the
   blood-spine identity, the frame instead reads as gilded navy
   lacquer — heavy, architectural, designed to hold the contents
   without competing for attention. Geometry is opposite the
   children's: their clip-path cuts top-left + bottom-right, ours
   cuts top-right + bottom-left, so the two forms interlock
   instead of echoing each other. The header carries an
   iconostasis-band gilt divider; the un-bevelled corners get
   small L-bracket details so the frame reads as an instrument
   case, not a flat panel. */
.panel-card {
    position: relative;
    background: linear-gradient(165deg,
        var(--panel-2) 0%,
        var(--panel) 45%,
        #000d16 100%);
    color: var(--ink);
    margin-bottom: 1.5rem;
    border-radius: 18px;
    clip-path: polygon(
        0 0,
        calc(100% - 16px) 0,
        100% 16px,
        100% 100%,
        16px 100%,
        0 calc(100% - 16px));
    box-shadow:
        0 24px 48px -12px rgba(0, 0, 0, 0.7),
        0 0 0 1px rgba(201, 166, 52, 0.25),
        inset 0 0 0 1px rgba(0, 0, 0, 0.35),
        inset 0 1px 0 rgba(201, 166, 52, 0.18),
        inset 0 -20px 40px -20px rgba(139, 0, 0, 0.18);
    overflow: hidden;
}

/* Gilt banner across the head of the frame — a thin gold rule
   bright in the middle, fading to nothing at the bevel corners
   so it visually terminates at the cut rather than crashing
   into it. */
.panel-card::before {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    top: 0;
    height: 2px;
    background: linear-gradient(90deg,
        transparent 0%,
        rgba(201, 166, 52, 0.35) 8%,
        var(--gold) 50%,
        rgba(201, 166, 52, 0.35) 92%,
        transparent 100%);
    box-shadow: 0 0 12px rgba(201, 166, 52, 0.45);
    z-index: 2;
    pointer-events: none;
}

/* Bottom-edge counterpart — same gilt, dimmer; anchors the
   frame visually so it doesn't dissolve into the page. */
.panel-card::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 1px;
    background: linear-gradient(90deg,
        transparent 0%,
        rgba(201, 166, 52, 0.2) 12%,
        rgba(201, 166, 52, 0.55) 50%,
        rgba(201, 166, 52, 0.2) 88%,
        transparent 100%);
    z-index: 2;
    pointer-events: none;
}

.panel-card-header {
    position: relative;
    padding: 1.65rem 1.85rem 1.25rem;
    background: linear-gradient(180deg,
        rgba(201, 166, 52, 0.05) 0%,
        transparent 100%);
}

/* Iconostasis-band divider under the header — heavier gold
   above, a thinner steel shadow below — like the gilded
   banding in a Russian icon frame. */
.panel-card-header::after {
    content: "";
    position: absolute;
    left: 1.85rem;
    right: 1.85rem;
    bottom: 0;
    height: 1px;
    background: linear-gradient(90deg,
        rgba(201, 166, 52, 0.55) 0%,
        rgba(201, 166, 52, 0.25) 70%,
        transparent 100%);
    box-shadow: 0 -3px 0 rgba(201, 166, 52, 0.12);
}

.panel-card-header h2 {
    font-size: 1.05rem;
    font-weight: 700;
    color: var(--gold);
    letter-spacing: 0.32em;
    text-transform: uppercase;
    margin: 0;
    text-shadow:
        0 0 18px rgba(201, 166, 52, 0.35),
        0 1px 0 rgba(0, 0, 0, 0.4);
    display: flex;
    align-items: center;
    gap: 0.7rem;
}

/* Lozenge ornament before the title — small gold diamond,
   45° rotated square with a faint glow. Reads as the seal
   on an imperial document; pure CSS, no icon library. */
.panel-card-header h2::before {
    content: "";
    width: 6px;
    height: 6px;
    background: var(--gold);
    transform: rotate(45deg);
    box-shadow: 0 0 8px rgba(201, 166, 52, 0.6);
    flex-shrink: 0;
}

.panel-card-header .subtitle {
    display: block;
    margin-top: 0.45rem;
    color: var(--steel);
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.22em;
    padding-left: calc(6px + 0.7rem);
}

.panel-card-body {
    position: relative;
    padding: 1.75rem;
}

/* L-shaped corner brackets on the un-bevelled corners
   (TL + BR). The bevel-cut corners (TR + BL) don't need
   brackets — the cut itself is the corner detail. Pinned
   to the body so the brackets sit inside the gilt rule
   and don't fight the header text. */
.panel-card-body::before,
.panel-card-body::after {
    content: "";
    position: absolute;
    width: 14px;
    height: 14px;
    border: 1px solid rgba(201, 166, 52, 0.55);
    pointer-events: none;
}

.panel-card-body::before {
    top: 10px;
    left: 10px;
    border-right: none;
    border-bottom: none;
}

.panel-card-body::after {
    bottom: 10px;
    right: 10px;
    border-left: none;
    border-top: none;
}

/* Tables */
.table {
    color: var(--ink);
}

.table thead th {
    background: var(--void);
    color: var(--ink);
    font-weight: 600;
    text-transform: uppercase;
    font-size: 0.75rem;
    letter-spacing: 0.05em;
    border: none;
}

.table td {
    vertical-align: middle;
    color: var(--ink);
    border-color: var(--line);
}

.table-striped tbody tr:nth-of-type(odd) {
    background: rgba(0,26,45,0.3);
}

/* Links */
a {
    color: var(--gold);
    transition: color 0.1s ease;
}

a:hover {
    color: #b8952e;
}

/* Alerts */
.alert-danger,
.alert-danger.alert-dismissible {
    background: var(--blood) !important;
    border-color: var(--blood) !important;
    color: var(--ink) !important;
    border-radius: 3px;
}

.alert-danger .close,
.alert-danger .close:hover {
    color: var(--ink) !important;
    opacity: 0.8;
    text-shadow: none;
}

.alert-error {
    background: var(--blood);
    border-left: 3px solid #ff4444;
    color: var(--ink);
    padding: 0.75rem 1rem;
    margin-bottom: 1rem;
    border-radius: 3px;
}

.alert-success,
.alert-success.alert-dismissible {
    background: rgba(26,122,26,0.4) !important;
    border-color: #1a7a1a !important;
    color: var(--ink) !important;
    border-radius: 3px;
}

.alert-success .close,
.alert-success .close:hover {
    color: var(--ink) !important;
    opacity: 0.8;
    text-shadow: none;
}

/* Badges */
.badge {
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    font-size: 0.7rem;
}

/* Action groups */
.action-group {
    display: flex;
    gap: 0.35rem;
    justify-content: flex-end;
}

/* Headings */
h1, h2, h3, h4, h5, h6 {
    color: var(--ink);
}

/* Utility */
.color-gold { color: var(--gold); }
.color-steel { color: var(--steel); }
.color-blood { color: var(--blood); }
.color-ink { color: var(--ink); }
.code-steel { color: var(--steel); font-size: 0.75rem; }

/* Component-page tabs (used by the per-component dashboards) */
.tab-content-pad {
    padding-top: 1rem;
}

.nav-tabs {
    border-bottom-color: var(--line);
}

.nav-tabs .nav-link {
    color: var(--steel);
    background: transparent;
    border: none;
    border-bottom: 2px solid transparent;
    padding: 0.5rem 1rem;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    font-size: 0.75rem;
    font-weight: 600;
}

.nav-tabs .nav-link:hover {
    color: var(--ink);
    border-bottom-color: rgba(201, 166, 52, 0.3);
}

.nav-tabs .nav-link.active {
    color: var(--gold);
    background: transparent;
    border-bottom-color: var(--gold);
}

/* Log viewer (used by component pages that proxy a peer's logs) */
.logs-toolbar {
    display: flex;
    align-items: center;
    gap: 1rem;
    margin-bottom: 0.75rem;
    flex-wrap: wrap;
}

.logs-meta {
    color: var(--steel);
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    font-size: 0.75rem;
    letter-spacing: 0.05em;
}

.logs-meta-clickable {
    cursor: pointer;
    text-decoration: underline dotted;
    text-underline-offset: 3px;
}

.logs-meta-clickable:hover {
    color: var(--gold);
}

.logs-spinner {
    color: var(--gold);
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.08em;
}

/* Tail toggle — binary cap on the log render. Off: only the most
   recent TAIL_LIMIT filtered rows hit the DOM. On: render all
   filtered rows. Reuses .slider-toggle for the visual; this rule
   just pushes it (and the row-count label next to it) to the
   right of the toolbar. */
.logs-tail-toggle {
    margin-left: auto;
}

.logs-tail-of {
    color: var(--steel);
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    font-size: 0.68rem;
    letter-spacing: 0.05em;
}

.logs-pre {
    max-height: 60vh;
    overflow: auto;
    background: var(--void);
    color: var(--ink);
    border: 1px solid var(--line);
    border-radius: 3px;
    padding: 0.75rem 1rem;
    margin: 0;
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    font-size: 0.78rem;
    line-height: 1.45;
    white-space: pre;
    word-break: normal;
}

/* Structured-log table view — used by component pages that proxy a
   peer's parsed log JSON. Scrollable wrap with sticky header so
   column labels stay visible while paging through many rows. */
.logs-table-wrap {
    max-height: 60vh;
    overflow: auto;
    background: var(--void);
    border: 1px solid var(--line);
    border-radius: 3px;
}

.logs-table {
    width: 100%;
    border-collapse: collapse;
    color: var(--ink);
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    font-size: 0.75rem;
    line-height: 1.4;
    margin: 0;
}

.logs-table thead th {
    position: sticky;
    top: 0;
    z-index: 1;
    background: var(--panel-2);
    color: var(--gold);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    font-size: 0.62rem;
    font-weight: 700;
    text-align: left;
    padding: 0.5rem 0.75rem;
    border-bottom: 1px solid rgba(201, 166, 52, 0.3);
}

.logs-table tbody tr:nth-child(odd) {
    background: rgba(0, 26, 45, 0.35);
}

.logs-table tbody tr:hover {
    background: rgba(201, 166, 52, 0.08);
}

.logs-table td {
    padding: 0.3rem 0.75rem;
    border: none;
    vertical-align: top;
}

.logs-col-time {
    width: 215px;
    white-space: nowrap;
    color: var(--steel);
}

.logs-col-level {
    white-space: nowrap;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.05em;
}

.logs-col-mask {
    white-space: nowrap;
    color: var(--gold);
    font-weight: 600;
}

.logs-col-msg {
    color: var(--ink);
    word-break: break-word;
    white-space: pre-wrap;
}

/* Collapsible filters panel that sits above the log table */
.logs-filters {
    margin-bottom: 0.75rem;
    background: rgba(0, 26, 45, 0.4);
    border: 1px solid var(--line);
    border-radius: 3px;
    overflow: hidden;
}

.logs-filters-toggle {
    display: flex;
    align-items: center;
    width: 100%;
    background: transparent;
    border: none;
    color: var(--gold);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    font-size: 0.7rem;
    font-weight: 700;
    padding: 0.5rem 0.75rem;
    cursor: pointer;
}

.logs-filters-toggle:hover,
.logs-filters-toggle:focus {
    background: rgba(201, 166, 52, 0.08);
    color: var(--gold);
    outline: none;
}

.logs-filters-chevron {
    margin-left: auto;
    transition: transform 0.2s ease;
}

.logs-filters-toggle[aria-expanded="true"] .logs-filters-chevron {
    transform: rotate(180deg);
}

.logs-filters-body {
    padding: 0.75rem;
    border-top: 1px solid var(--line);
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
}

.logs-filters-group {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.5rem;
}

.logs-filters-label {
    color: var(--steel);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    font-size: 0.62rem;
    font-weight: 700;
    min-width: 60px;
}

.logs-filters-pills {
    display: flex;
    flex-wrap: wrap;
    gap: 0.35rem;
}

.logs-pill {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.2rem 0.6rem;
    background: var(--void);
    border: 1px solid var(--line);
    border-radius: 999px;
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    font-size: 0.68rem;
    color: var(--steel);
    cursor: pointer;
    margin: 0;
    user-select: none;
    transition: background 0.12s ease, border-color 0.12s ease, color 0.12s ease;
}

.logs-pill input {
    margin: 0;
    cursor: pointer;
}

.logs-pill:has(input:checked) {
    background: rgba(201, 166, 52, 0.12);
    border-color: rgba(201, 166, 52, 0.5);
    color: var(--ink);
}

/* Per-level pill tints when checked — match the row colors so the
   filter UI reads as the same vocabulary as the table. */
.logs-pill.logs-level-FATAL:has(input:checked) {
    color: #ff5a5a;
    border-color: rgba(255, 90, 90, 0.6);
    background: rgba(255, 90, 90, 0.1);
}

.logs-pill.logs-level-WARN:has(input:checked) {
    color: #ff9b9b;
    border-color: rgba(255, 155, 155, 0.55);
    background: rgba(255, 155, 155, 0.1);
}

.logs-pill.logs-level-DEBUG:has(input:checked) {
    color: #9bbfd9;
    border-color: rgba(155, 191, 217, 0.55);
    background: rgba(155, 191, 217, 0.1);
}

.logs-pill.logs-level-RAMBL:has(input:checked) {
    color: #9bd9a4;
    border-color: rgba(155, 217, 164, 0.55);
    background: rgba(155, 217, 164, 0.1);
}

.logs-level-FATAL { color: #ff5a5a; }
.logs-level-WARN  { color: #ff9b9b; }
.logs-level-INFO  { color: var(--ink); }
.logs-level-DEBUG { color: #9bbfd9; }
.logs-level-RAMBL { color: #9bd9a4; }

.logs-empty {
    text-align: center;
    color: var(--steel);
    padding: 1.5rem !important;
    font-style: italic;
}

/* Inventory tab — toggle pills + Submit, themed to match the
   logs-pill family so the visual vocabulary stays consistent. */
.inv-section {
    display: flex;
    flex-direction: column;
    gap: 1rem;
}

/* Visual separation between stacked sections on the same page —
   adjacent-sibling so the first section gets no top spacing.
   The hairline reads as a divider between the two header strips
   rather than letting them blur together. */
.inv-section + .inv-section {
    margin-top: 2rem;
    padding-top: 1.5rem;
    border-top: 1px solid var(--line);
}

.inv-section-header {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    border-bottom: 1px solid var(--line);
    padding-bottom: 0.5rem;
}

.inv-section-title {
    color: var(--gold);
    text-transform: uppercase;
    letter-spacing: 0.1em;
    font-size: 0.85rem;
    font-weight: 700;
    margin: 0;
}

.inv-section-status {
    color: var(--steel);
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    font-size: 0.72rem;
    letter-spacing: 0.05em;
}

.inv-pills {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.5rem;
}

/* Sliding toggle — iOS-style switch the dashboard uses for binary
   configuration flags. The button is the click target; the inner
   .slider-toggle-switch is the track, with a pseudo-element thumb
   that translates between the off and on positions. State lives on
   the button's aria-pressed attribute so the JS reads/writes one
   accessor and screen readers announce the state correctly. */
.slider-toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.7rem;
    padding: 0.35rem 0.5rem;
    background: transparent;
    border: none;
    color: var(--ink);
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    font-size: 0.78rem;
    font-weight: 600;
    cursor: pointer;
    user-select: none;
}

.slider-toggle:focus {
    outline: none;
}

.slider-toggle:focus .slider-toggle-switch {
    box-shadow: 0 0 0 3px rgba(201, 166, 52, 0.25);
}

.slider-toggle-switch {
    position: relative;
    flex: 0 0 auto;
    display: inline-block;
    width: 38px;
    height: 20px;
    background: var(--steel);
    border-radius: 999px;
    transition: background 0.18s ease;
}

.slider-toggle-switch::after {
    content: "";
    position: absolute;
    top: 2px;
    left: 2px;
    width: 16px;
    height: 16px;
    background: var(--ink);
    border-radius: 50%;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
    transition: transform 0.18s ease, background 0.18s ease;
}

.slider-toggle[aria-pressed="true"] .slider-toggle-switch {
    background: var(--gold);
}

.slider-toggle[aria-pressed="true"] .slider-toggle-switch::after {
    transform: translateX(18px);
    background: var(--void);
}

.slider-toggle-label {
    color: inherit;
}

.slider-toggle:hover .slider-toggle-label {
    color: var(--gold);
}

.inv-actions {
    display: flex;
    gap: 0.5rem;
}

/* Footer */
.footer {
    background: var(--void);
    border-top: 2px solid var(--slate);
    padding: 0.75rem 1.5rem;
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    align-items: center;
    gap: 0.25rem 1rem;
    font-size: 0.75rem;
    flex-shrink: 0;
}

.footer a {
    color: var(--ink);
    text-decoration: none;
    font-weight: 600;
}

.footer a:hover {
    color: var(--gold);
}

.footer .copyright {
    color: var(--steel);
}

.footer-meta {
    display: flex;
    align-items: baseline;
}

.footer-meta-item {
    display: flex;
    align-items: baseline;
    margin-left: 1.5rem;
}

.footer-meta-label {
    color: var(--steel);
    text-transform: uppercase;
    font-size: 0.65rem;
    letter-spacing: 0.08em;
    font-weight: 600;
    margin-right: 0.4rem;
}

@media (max-width: 576px) {
    .login-card { margin: 0 0.5rem; }
    .navbar-brand-tagline { display: none; }
    .footer {
        flex-direction: column;
        text-align: center;
        padding: 0.5rem 1rem;
    }
    .login-actions {
        flex-direction: column;
    }
    .login-actions .btn { width: 100%; }
}

.empty-state {
    color: var(--steel);
    text-align: center;
}

/* Admin-required banner — horizontal slab between the
   component-tabs row and the panel-card-body on pages whose
   mutating actions require admin credentials. Visible only to
   logged-out and logged-in-non-admin viewers (the partial owns
   the visibility check). Same blood-red vocabulary as the
   destructive-action button family — a warning that the form
   controls below are read-only for this viewer. No border-
   radius so it sits flush inside the panel-card's clip path
   like a banner band rather than a floating chip. The dismiss
   button hides it for the rest of the page lifecycle via the
   handler in site.js (toggling the .d-none utility class) —
   not persisted across reloads or navigations, by design. */
.admin-required-banner {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    background: linear-gradient(180deg, rgba(139, 0, 0, 0.92), rgba(139, 0, 0, 0.72));
    color: var(--ink);
    padding: 0.65rem 1.5rem;
    border-top: 1px solid rgba(255, 90, 90, 0.45);
    border-bottom: 1px solid rgba(255, 90, 90, 0.45);
    box-shadow:
        inset 0 0 14px rgba(139, 0, 0, 0.45),
        0 0 14px var(--glow-blood);
    font-size: 0.8rem;
    letter-spacing: 0.04em;
}

.admin-required-banner-icon {
    color: var(--ink);
    flex-shrink: 0;
}

.admin-required-banner-message {
    flex: 1;
    color: var(--ink);
    line-height: 1.4;
}

.admin-required-banner-close {
    background: transparent;
    border: none;
    color: var(--ink);
    font-size: 1.4rem;
    line-height: 1;
    cursor: pointer;
    padding: 0 0.35rem;
    opacity: 0.85;
    flex-shrink: 0;
}

.admin-required-banner-close:hover {
    opacity: 1;
}

/* Confirm modal — themed Bootstrap modal used for destructive-action prompts */
.modal-content {
    background: var(--slate);
    border: 1px solid var(--line);
    color: var(--ink);
}

.modal-header {
    border-bottom: 1px solid var(--line);
}

.modal-footer {
    border-top: 1px solid var(--line);
}

.modal-title {
    color: var(--gold);
    font-size: 0.9rem;
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
}

/* Detail table: label/value cells used on entity detail pages */
.detail-label {
    color: var(--steel);
    text-transform: uppercase;
    font-size: 0.75rem;
    font-weight: 600;
    border-color: var(--line);
}

.detail-label-first {
    width: 160px;
}

.detail-label-multiline {
    vertical-align: top;
}

.detail-label-last {
    color: var(--steel);
    text-transform: uppercase;
    font-size: 0.75rem;
    font-weight: 600;
    border: none;
}

.detail-value {
    border-color: var(--line);
}

.detail-value-prewrap {
    border-color: var(--line);
    white-space: pre-wrap;
}

.detail-value-last {
    border: none;
}

/* API error box — fixed, discardable, shown on HTMX non-2xx responses */
.api-error-box {
    position: fixed;
    top: 1rem;
    right: 1rem;
    max-width: 420px;
    background: var(--blood);
    color: var(--ink);
    border: 1px solid #ff4444;
    border-radius: 4px;
    padding: 0.75rem 1rem;
    box-shadow: 0 4px 12px rgba(0,0,0,0.4);
    z-index: 1080;
    display: flex;
    align-items: center;
    gap: 0.75rem;
}

.api-error-box-body {
    flex: 1;
}

.api-error-box-close {
    background: transparent;
    border: none;
    color: var(--ink);
    font-size: 1.25rem;
    line-height: 1;
    cursor: pointer;
    padding: 0;
    opacity: 0.8;
}

.api-error-box-close:hover {
    opacity: 1;
}
/* ── Tetsuo card (gen-7 status-row style) ───────────────────────────────────── */
/* Beveled clip-path polygon with a blood-red spine on the left edge and an
   inner perimeter glow. Ported from tetsuo.silogroup.org's per-component
   status row. Renamed from .row to avoid colliding with Bootstrap's grid. */
.tetsuo-card {
    position: relative;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    background: linear-gradient(180deg, var(--panel-2), var(--panel));
    padding: 16px 18px;
    border-radius: 14px;
    clip-path: polygon(12px 0, 100% 0, 100% calc(100% - 12px), calc(100% - 12px) 100%, 0 100%, 0 12px);
    transition: transform 0.12s ease, box-shadow 0.12s ease, filter 0.12s ease;
    color: var(--ink);
}

.tetsuo-card::before {
    content: "";
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    width: 8px;
    background: linear-gradient(180deg, rgba(139,0,0,.9), rgba(139,0,0,.6));
    box-shadow: 0 0 18px var(--glow-blood);
    border-top-left-radius: 14px;
    border-bottom-left-radius: 14px;
}

.tetsuo-card::after {
    content: "";
    position: absolute;
    inset: 0;
    pointer-events: none;
    border-radius: 14px;
    box-shadow: inset 0 0 0 2px var(--line);
    clip-path: inherit;
    transition: box-shadow 0.12s ease;
}

.tetsuo-card:hover {
    transform: translateY(-2px);
    box-shadow: 0 6px 18px rgba(0,0,0,0.35);
    filter: saturate(1.08);
}

.tetsuo-card:hover::after {
    box-shadow: inset 0 0 0 2px rgba(201,166,52,.35), 0 0 12px rgba(201,166,52,.22);
}

/* Shared card chrome — every panel surface on every component
   sub-page (Overview slab, Execution action, Blacklist add/list,
   Inventory header band) uses the same blood-spine + polygon
   bevel + 14px radius vocabulary the .tetsuo-card status row
   uses. Pulled out into one rule so the chrome lives in a
   single place; each per-class rule above keeps its own layout
   (padding, max-width, flex direction) — only the chrome is
   shared. */
.overview-card,
.exec-action,
.bl-add,
.bl-list,
.inv-view-header {
    position: relative;
    background: linear-gradient(180deg, var(--panel-2), var(--panel));
    border-radius: 14px;
    clip-path: polygon(12px 0, 100% 0, 100% calc(100% - 12px), calc(100% - 12px) 100%, 0 100%, 0 12px);
    color: var(--ink);
}

.overview-card::before,
.exec-action::before,
.bl-add::before,
.bl-list::before,
.inv-view-header::before {
    content: "";
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    width: 8px;
    background: linear-gradient(180deg, rgba(139,0,0,.9), rgba(139,0,0,.6));
    box-shadow: 0 0 18px var(--glow-blood);
    border-top-left-radius: 14px;
    border-bottom-left-radius: 14px;
}

.overview-card::after,
.exec-action::after,
.bl-add::after,
.bl-list::after,
.inv-view-header::after {
    content: "";
    position: absolute;
    inset: 0;
    pointer-events: none;
    border-radius: 14px;
    box-shadow: inset 0 0 0 2px var(--line);
    clip-path: inherit;
}

.tetsuo-card-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: 14px;
}

.tetsuo-card-info {
    display: flex;
    flex-direction: column;
    gap: 6px;
    min-width: 0;
}

.tetsuo-card-name {
    font-weight: 900;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    font-size: 1rem;
    color: var(--ink);
    text-decoration: none;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    transition: color 0.12s ease;
}

.tetsuo-card-name:hover {
    color: var(--gold);
    text-decoration: none;
}

.tetsuo-card-purpose {
    font-size: 0.75rem;
    color: #cbd5df;
    opacity: 0.88;
    line-height: 1.45;
    max-width: 80ch;
}

.tetsuo-card-info {
    flex: 1 1 auto;
}

.tetsuo-card-check {
    flex: 0 0 auto;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    justify-content: center;
    gap: 4px;
    padding: 8px 12px;
    min-width: 150px;
    background: linear-gradient(180deg, rgba(0, 13, 22, 0.9), rgba(0, 0, 0, 0.55));
    box-shadow:
        inset 0 0 0 1px rgba(201, 166, 52, 0.4),
        inset 0 0 12px rgba(201, 166, 52, 0.08);
    border-radius: 3px;
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    line-height: 1;
}

.tetsuo-card-check-label {
    color: var(--gold);
    font-weight: 800;
    letter-spacing: 0.22em;
    font-size: 0.55rem;
    text-transform: uppercase;
    text-shadow: 0 0 4px rgba(201, 166, 52, 0.4);
}

.tetsuo-card-check-value {
    color: var(--ink);
    opacity: 0.92;
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.05em;
    font-size: 0.72rem;
    text-shadow: 0 0 4px rgba(201, 166, 52, 0.18);
    white-space: nowrap;
}

.tetsuo-card-error {
    position: relative;
    flex: 0 0 100%;
    width: 100%;
    padding: 6px 10px 6px 14px;
    background: linear-gradient(180deg, rgba(139, 0, 0, 0.22), rgba(139, 0, 0, 0.06));
    border-radius: 3px;
    box-shadow:
        inset 2px 0 0 var(--blood),
        inset 0 0 14px rgba(139, 0, 0, 0.35),
        0 0 12px rgba(139, 0, 0, 0.18);
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    font-size: 0.68rem;
    line-height: 1.4;
    color: #ff9b9b;
    letter-spacing: 0.05em;
    text-shadow: 0 0 8px rgba(255, 90, 90, 0.55);
    word-break: break-word;
}

.tetsuo-card-error::before {
    content: "▲ ERR ";
    color: var(--blood);
    font-weight: 900;
    margin-right: 6px;
    letter-spacing: 0.12em;
    text-shadow: 0 0 8px var(--glow-blood), 0 0 16px rgba(139, 0, 0, 0.55);
}

.tetsuo-status-badge {
    min-width: 156px;
    text-align: center;
    font-weight: 900;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    font-size: 0.75rem;
    padding: 10px 14px;
    color: var(--ink);
    border-radius: 4px;
    transition: transform 0.12s ease, box-shadow 0.12s ease, filter 0.12s ease;
}

.tetsuo-card:hover .tetsuo-status-badge {
    transform: scale(1.03);
}

.state-NOT_RUNNING {
    background: var(--ink);
    color: #0b0f14;
    box-shadow: inset 0 0 0 2px var(--ink), 0 0 14px rgba(255,255,255,0.35);
}

.state-RUNNING_BUSY {
    box-shadow: inset 0 0 0 2px var(--steel), 0 0 14px rgba(95,110,124,0.25);
    color: var(--gold);
    background: linear-gradient(180deg, rgba(112,128,144,0.85), rgba(112,128,144,0.65));
}

.state-RUNNING_READY {
    box-shadow: inset 0 0 0 2px var(--gold), 0 0 18px var(--glow-gold);
    color: var(--gold);
    background: linear-gradient(180deg, rgba(201,166,52,0.18), rgba(201,166,52,0.06));
}

.state-RUNNING_ERROR {
    box-shadow: inset 0 0 0 2px var(--blood), 0 0 18px var(--glow-blood);
    background: linear-gradient(180deg, rgba(139,0,0,0.9), rgba(139,0,0,0.6));
    color: var(--ink);
}

/* Peer is otherwise idle but a fired job is blocked on an
   upstream dependency that hasn't yet completed since the
   last NYSE market close — the peer polls ORK's
   /api/jobs/recent and self-enters READY_WAITING until the
   dependency clears. Muted blue-grey reads as "paused,
   waiting" — distinct from READY (gold, nothing pending) and
   BUSY (steel, doing work). */
.state-READY_WAITING {
    box-shadow: inset 0 0 0 2px #6b8fa3, 0 0 14px rgba(107, 143, 163, 0.3);
    color: #b8d4e8;
    background: linear-gradient(180deg, rgba(107,143,163,0.22), rgba(107,143,163,0.08));
}

.state-INTERNAL {
    box-shadow: inset 0 0 0 2px rgba(255,255,255,0.10);
    background: linear-gradient(180deg, #0b1219, #0e151d);
    color: #e7e9ec;
}

/* Status ping. Brief two-element pulse fired on every poll
   tick of status.js: the card's existing blood spine on the
   ::before pseudo flares from its resting dark crimson to a
   brighter blood glow for one beat, and the steel inset
   frame on the ::after pseudo tints from its resting line
   colour to a thin gold hairline for the same duration. Both
   run on identical easing so they read as one acknowledgement
   rather than two stacked effects. 240ms total, snap then
   settle. No screen blend, no full-card paint shift, no
   left-to-right sweep. Imperial discipline rather than
   carnival flash. The .tetsuo-card-sweep element stays in
   the DOM (status.html line 22) but renders nothing now;
   left in place so a future cleanup pass can remove the
   markup as a focused change. */
.tetsuo-card-sweep {
    display: none;
}

.tetsuo-card.is-polling::before {
    animation: tetsuo-spine-pulse 240ms cubic-bezier(0.18, 0.74, 0.32, 1);
}

.tetsuo-card.is-polling::after {
    animation: tetsuo-frame-pulse 240ms cubic-bezier(0.18, 0.74, 0.32, 1);
}

@keyframes tetsuo-spine-pulse {
    0%, 100% {
        box-shadow: 0 0 18px var(--glow-blood);
    }
    35% {
        box-shadow:
            0 0 22px rgba(255, 70, 70, 0.7),
            0 0 42px rgba(139, 0, 0, 0.55);
    }
}

@keyframes tetsuo-frame-pulse {
    0%, 100% {
        box-shadow: inset 0 0 0 2px var(--line);
    }
    35% {
        box-shadow:
            inset 0 0 0 1px rgba(201, 166, 52, 0.4),
            inset 0 0 10px rgba(201, 166, 52, 0.12);
    }
}

/* Jobs page — table view of every recorded Job (manual +
   scheduled), with filters and pagination. Mirrors the logs-
   table chrome so the visual vocabulary stays consistent. */
.jobs-section {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}

/* Drop Bootstrap's container max-width on every per-component
   sub-page and on the About page. The component-tabs row only
   renders inside component/_layout.html, so :has(.component-
   tabs) is the precise scope for "every component page" without
   leaking onto the public status page (no _layout.html), the
   login page, or the user profile / list pages. The About page
   has no component-tabs but opts in via its own .about-page
   marker. Scoped via :has() so anything outside those two
   classes keeps Bootstrap's default container width. */
.main-content > .container:has(.component-tabs),
.main-content > .container:has(.about-page) {
    max-width: none;
}

/* The shared .overview-card chrome caps text at 70ch for
   readable prose on the per-component Overview tabs. The About
   page opts out of that cap (the panel-card itself is already
   widened above) so the rendered tetsuo.md fills the wider
   chrome instead of sitting in a narrow column inside an
   otherwise-wide card. */
.about-page .overview-card {
    max-width: none;
}

.jobs-toolbar {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.85rem;
}

.jobs-filter-group {
    display: flex;
    align-items: center;
    gap: 0.4rem;
}

.jobs-filter-label {
    color: var(--steel);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    font-size: 0.62rem;
    font-weight: 700;
    margin: 0;
}

.jobs-filter-select {
    background: var(--void);
    color: var(--ink);
    border: 1px solid var(--steel);
    border-radius: 3px;
    padding: 0.3rem 0.5rem;
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    font-size: 0.78rem;
    color-scheme: dark;
}

.jobs-filter-select:focus {
    outline: none;
    border-color: var(--gold);
    box-shadow: 0 0 0 2px var(--glow-gold);
}

.jobs-status {
    margin-left: auto;
    color: var(--steel);
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    font-size: 0.72rem;
}

.jobs-status.error {
    color: #ff9b9b;
}

.jobs-table-wrap {
    max-height: 60vh;
    overflow: auto;
    background: var(--void);
    border: 1px solid var(--line);
    border-radius: 3px;
}

.jobs-table {
    /* width: auto + min-width: 100% lets the table fill the
       wrap by default but grow beyond it when columns demand
       more space — combined with .jobs-table-wrap's
       overflow: auto this triggers horizontal scrolling
       instead of crushing one column to a few characters
       wide. table-layout: auto keeps columns sized to their
       content (clamped by per-column min/max-width). */
    width: auto;
    min-width: 100%;
    table-layout: auto;
    border-collapse: collapse;
    color: var(--ink);
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    font-size: 0.74rem;
    line-height: 1.4;
    margin: 0;
}

.jobs-table thead th {
    position: sticky;
    top: 0;
    z-index: 1;
    background: var(--panel-2);
    color: var(--gold);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    font-size: 0.62rem;
    font-weight: 700;
    text-align: left;
    padding: 0.5rem 0.75rem;
    border-bottom: 1px solid rgba(201, 166, 52, 0.3);
}

.jobs-table tbody tr:nth-child(odd) {
    background: rgba(0, 26, 45, 0.35);
}

.jobs-table tbody tr:hover {
    background: rgba(201, 166, 52, 0.08);
}

.jobs-table td {
    padding: 0.35rem 0.75rem;
    border: none;
    vertical-align: top;
}

.jobs-col-time, .jobs-col-completed {
    white-space: nowrap;
    color: var(--steel);
}

.jobs-col-component, .jobs-col-job, .jobs-col-source, .jobs-col-state {
    white-space: nowrap;
    font-weight: 600;
}

.jobs-col-component { color: var(--gold); }
.jobs-col-job { color: var(--ink); }
.jobs-col-source { color: var(--steel); text-transform: lowercase; }

.jobs-col-duration {
    white-space: nowrap;
    color: var(--ink);
    font-variant-numeric: tabular-nums;
}

.jobs-col-error {
    /* Wraps normally at word boundaries. The min-width
       floor keeps the column wide enough that a short error
       sits on one line and a longer error wraps as English
       reads — not the per-character mid-word break the old
       squeezed column produced. Combined with the table's
       width: auto + min-width: 100% and the wrap's
       overflow: auto, a really long error pushes the table
       wider and the wrap scrolls horizontally; nothing is
       truncated or hidden. */
    color: #ff9b9b;
    min-width: 40ch;
    word-break: break-word;
}

.jobs-state-SCHEDULED      { color: #9bbfd9; }
.jobs-state-READY_WAITING  { color: #b8d4e8; }
.jobs-state-PENDING        { color: var(--steel); }
.jobs-state-RUNNING        { color: var(--gold); text-shadow: 0 0 6px var(--glow-gold); }
.jobs-state-COMPLETED_OK   { color: #9bd9a4; }
.jobs-state-COMPLETED_ERROR { color: #ff5a5a; }
.jobs-state-INTERRUPTED    { color: #ff9b9b; }
.jobs-state-UNREACHABLE    { color: #ff9b9b; }
.jobs-state-CANCELLED      { color: #d9b89b; }

/* Sitemap auth-level tints — same visual vocabulary as the
   jobs-state family above. Public reads steel (no gate,
   neutral); login reads muted blue-grey reusing the
   READY_WAITING badge tone (light auth — same colour the
   status grid uses for "paused, waiting"); self-or-admin
   reads gold (escalated to ownership); admin reads blood
   (heavy auth — same colour the destructive-action buttons
   use). */
.sitemap-auth-none          { color: var(--steel); }
.sitemap-auth-login         { color: #b8d4e8; }
.sitemap-auth-self_or_admin { color: var(--gold); text-shadow: 0 0 6px var(--glow-gold); }
.sitemap-auth-admin         { color: var(--blood); text-shadow: 0 0 6px var(--glow-blood); }

/* Actions column — per-row buttons (cancel, etc.). Right-
   aligned so the verb sits at the visual edge. */
.jobs-col-actions {
    white-space: nowrap;
    text-align: right;
}

.jobs-cancel-btn {
    padding: 0.2rem 0.6rem !important;
    font-size: 0.68rem !important;
    letter-spacing: 0.04em;
}

.jobs-cancel-btn:disabled {
    opacity: 0.55;
    cursor: not-allowed;
}

.jobs-empty {
    text-align: center;
    color: var(--steel);
    padding: 1.5rem !important;
    font-style: italic;
}

.jobs-pagination {
    display: flex;
    align-items: center;
    gap: 0.6rem;
}

.jobs-page-label {
    color: var(--ink);
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    font-size: 0.78rem;
}

.jobs-total-label {
    color: var(--steel);
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    font-size: 0.72rem;
    margin-left: auto;
}

/* Inline link beside execution-action buttons — used to point
   the operator at the Jobs page when a build is in flight. */
.exec-action-link {
    color: var(--gold);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    font-size: 0.7rem;
    font-weight: 700;
    text-decoration: none;
    padding: 0.4rem 0.7rem;
    border: 1px solid rgba(201, 166, 52, 0.45);
    border-radius: 3px;
    transition: background 0.12s ease, border-color 0.12s ease;
}

.exec-action-link:hover, .exec-action-link:focus {
    background: rgba(201, 166, 52, 0.12);
    border-color: var(--gold);
    text-decoration: none;
}

/* Full Forecasts page — date-picked, filterable, sortable view
   of the consolidated daily forecast report SIG produces at
   forecasts_dir/full/<entry_date>.txt. Reuses .logs-toolbar /
   .logs-meta / .logs-table-wrap / .logs-table chrome for
   visual consistency; the rules below add the Direction filter
   pill row, the sortable-header arrow indicator, per-column
   widths + alignment, and the UP / DOWN row colouring. */
.full-forecasts {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

/* Direction filter — three pills (ALL / UP / DOWN) sitting in
   the toolbar next to the date label. Pure-button shape (no
   input/label wrapper) so the active state lives on a CSS
   class rather than the :has(input:checked) selector the
   logs-pill family uses. */
.ff-direction-filter {
    display: flex;
    gap: 0.35rem;
}

.logs-filter-pill {
    display: inline-flex;
    align-items: center;
    padding: 0.22rem 0.7rem;
    background: var(--void);
    border: 1px solid var(--line);
    border-radius: 999px;
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    font-size: 0.68rem;
    color: var(--steel);
    text-transform: uppercase;
    letter-spacing: 0.05em;
    cursor: pointer;
    transition: background 0.12s ease, border-color 0.12s ease, color 0.12s ease;
}

.logs-filter-pill:hover {
    color: var(--ink);
    border-color: rgba(201, 166, 52, 0.45);
}

.logs-filter-pill:focus {
    outline: none;
    box-shadow: 0 0 0 2px var(--glow-gold);
}

.logs-filter-pill.active {
    background: rgba(201, 166, 52, 0.14);
    border-color: rgba(201, 166, 52, 0.6);
    color: var(--gold);
}

/* Sortable column headers. Default state shows a dimmed
   "▲▼" hint glyph so the operator sees the column is
   sortable; the active sort key swaps in a single solid
   arrow tinted gold matching the column-active vocabulary
   used elsewhere on the dashboard. */
.ff-sortable {
    cursor: pointer;
    user-select: none;
}

.ff-sortable::after {
    content: " ▲▼";
    opacity: 0.25;
    font-size: 0.6rem;
    letter-spacing: 0;
}

.ff-sortable.ff-sort-asc::after {
    content: " ▲";
    opacity: 1;
    color: var(--gold);
}

.ff-sortable.ff-sort-desc::after {
    content: " ▼";
    opacity: 1;
    color: var(--gold);
}

.ff-sortable:hover {
    color: var(--ink);
}

/* Per-column widths + alignment for the Full Forecasts table.
   .ff-table extends .logs-table so all the table chrome
   (sticky header, alternating row tint, hover) carries
   through; these rules only set widths and right-aligned
   tabular numerics for the two numeric columns. Selector is
   .ff-table thead th.ff-col-* / .ff-table tbody td.ff-col-*
   for specificity high enough to override .logs-table thead
   th's text-align: left without resorting to !important. */
.ff-table thead th.ff-col-symbol,
.ff-table tbody td.ff-col-symbol {
    width: 110px;
    color: var(--ink);
    font-weight: 700;
    letter-spacing: 0.05em;
}

/* Forecast date column — repeats the report's entry_date on every
   row as a rhetorical anchor so an analyst reading the table never
   loses sight of which date a row's UP/DOWN call applies to. Steel
   tone keeps it visually quieter than Symbol / Direction since the
   value is constant across rows from the same report. */
.ff-table thead th.ff-col-forecast-date,
.ff-table tbody td.ff-col-forecast-date {
    width: 120px;
    color: var(--steel);
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.04em;
    white-space: nowrap;
}

.ff-table thead th.ff-col-direction,
.ff-table tbody td.ff-col-direction {
    width: 110px;
    font-weight: 700;
    letter-spacing: 0.08em;
}

.ff-table thead th.ff-col-confidence,
.ff-table tbody td.ff-col-confidence,
.ff-table thead th.ff-col-probability,
.ff-table tbody td.ff-col-probability {
    width: 140px;
    text-align: right;
    font-variant-numeric: tabular-nums;
}

/* Direction colouring on data rows. UP reads as the same
   family as the logs-level RAMBL pill (greens), DOWN as the
   same family as logs-level WARN (pinks), so the operator's
   eye carries the same visual vocabulary across the
   dashboard's tables. */
.ff-up {
    color: #9bd9a4;
}

.ff-down {
    color: #ff9b9b;
}

/* Upper-limit input groups in the toolbar — one for Confidence,
   one for Probability. Both work on the 0–100 percent scale the
   table renders; blank means "no cap". Width-constrained so a
   3-digit value comfortably fits; matches the schedule-time-
   input / jobs-filter-select chrome elsewhere on the dashboard
   so the toolbar reads as one consistent strip rather than a
   collage of input styles. */
.ff-limit-group {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
}

.ff-limit-label {
    color: var(--steel);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    font-size: 0.62rem;
    font-weight: 700;
    margin: 0;
}

.ff-limit-input {
    width: 4.5rem;
    background: var(--void);
    color: var(--ink);
    border: 1px solid var(--steel);
    border-radius: 3px;
    padding: 0.25rem 0.4rem;
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    font-size: 0.78rem;
    letter-spacing: 0.04em;
    color-scheme: dark;
}

.ff-limit-input:focus {
    outline: none;
    border-color: var(--gold);
    box-shadow: 0 0 0 2px var(--glow-gold);
}

.ff-limit-input::placeholder {
    color: var(--steel);
    opacity: 0.55;
}

.ff-limit-unit {
    color: var(--steel);
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    font-size: 0.7rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

/* Currency badge — top-right of the MAG Full Forecasts card.
   Asserts whether the loaded report matches the next NYSE
   trading day after the most recent market close (the date the
   daily forecast cycle is expected to produce). Two state
   modifier classes paint the pill in the project's
   positive / negative vocabulary — sage green reuses the
   .ff-up tone (logs-level RAMBL family), blood pink reuses the
   .ff-down tone (logs-level WARN family) — so the operator's
   eye picks up the verdict at a glance without reading the
   label. ml-auto on the element pushes it to the rightmost
   flex slot in the toolbar regardless of how many filters sit
   ahead of it. */
.ff-currency-badge {
    display: inline-flex;
    align-items: center;
    padding: 0.22rem 0.7rem;
    background: var(--void);
    border: 1px solid var(--line);
    border-radius: 999px;
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    font-size: 0.68rem;
    color: var(--steel);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    font-weight: 700;
}

.ff-currency-badge.ff-currency-current {
    color: #9bd9a4;
    border-color: rgba(155, 217, 164, 0.55);
    background: rgba(155, 217, 164, 0.10);
}

.ff-currency-badge.ff-currency-stale {
    color: var(--blood);
    border-color: var(--glow-blood);
    background: rgba(139, 0, 0, 0.18);
}

/* DSH financial dashboard — three stacked widgets (current
   balance, balance-over-time graph, current holdings). The
   cards apply the canonical .tetsuo-card chrome (blood-red
   spine + polygon bevel + panel gradient) the status grid uses,
   so the dashboard reads as part of the same visual vocabulary
   rather than a separate gold-spine variant. .dsh-* rules below
   only override what differs from the status-card defaults:
   the graph + holdings cards need column-flex (the default
   tetsuo-card is row), and the inner widgets carry their own
   typography and colour treatment. */
.dsh-graph-card,
.dsh-holdings-card {
    flex-direction: column;
    align-items: stretch;
}

.dsh-card-label {
    color: var(--gold);
    text-transform: uppercase;
    letter-spacing: 0.1em;
    font-size: 0.85rem;
    font-weight: 700;
}

.dsh-card-meta {
    color: var(--steel);
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    font-size: 0.72rem;
    letter-spacing: 0.05em;
}

.dsh-card-meta.error {
    color: #ff9b9b;
}

.dsh-balance-value {
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    font-weight: 800;
    letter-spacing: 0.06em;
    font-size: 2rem;
    color: var(--gold);
    text-shadow: 0 0 18px var(--glow-gold);
}

.dsh-balance-value.dsh-positive {
    color: var(--gold);
    text-shadow: 0 0 18px var(--glow-gold);
}

.dsh-balance-value.dsh-negative {
    color: var(--blood);
    text-shadow: 0 0 18px var(--glow-blood);
}

.dsh-graph {
    width: 100%;
    height: 600px;
    margin-left: 8px;
}

.dsh-holdings-list {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    width: 100%;
}

.dsh-holding-item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0.5rem 0.85rem;
    background: linear-gradient(180deg, rgba(14, 36, 53, 0.6), rgba(10, 29, 44, 0.4));
    border-radius: 4px;
    box-shadow: inset 0 0 0 1px var(--line);
    transition: box-shadow 0.12s ease;
}

.dsh-holding-item:hover {
    box-shadow: inset 0 0 0 1px rgba(201, 166, 52, 0.35);
}

.dsh-holding-empty {
    color: var(--steel);
    font-style: italic;
    justify-content: center;
}

.dsh-holding-symbol {
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    font-weight: 800;
    letter-spacing: 0.06em;
    font-size: 0.9rem;
    color: var(--ink);
}

.dsh-holding-change {
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    font-weight: 700;
    font-size: 0.95rem;
    letter-spacing: 0.04em;
    color: var(--steel);
}

.dsh-holding-change.dsh-positive {
    color: var(--gold);
    text-shadow: 0 0 10px var(--glow-gold);
}

.dsh-holding-change.dsh-negative {
    color: var(--blood);
    text-shadow: 0 0 10px var(--glow-blood);
}

/* ============================================================
   About / commissioning page (.codex-page).
   Treated separately from the panel-card frame the component
   sub-pages use — the About page is a narrative manifesto, not
   a control surface, so the imperial frame would smother the
   text instead of present it. The codex layout is editorial:
   oversized hero, ornamental rule, drop-cap, Roman-numeral
   chapter marks on h2, lozenge bullet list, and a stamped
   seal at the foot of the page so it bookends like a decree.
   ============================================================ */

.codex-page {
    position: relative;
    max-width: 960px;
    margin: 1.5rem auto;
    padding: 3.5rem 3rem;
    color: var(--ink);
    background:
        radial-gradient(ellipse at 50% 0%,
            rgba(201, 166, 52, 0.06) 0%,
            transparent 55%),
        linear-gradient(165deg,
            var(--panel-2) 0%,
            var(--panel) 50%,
            #000d16 100%);
    border-radius: 18px;
    clip-path: polygon(
        0 0,
        calc(100% - 16px) 0,
        100% 16px,
        100% 100%,
        16px 100%,
        0 calc(100% - 16px));
    box-shadow:
        0 24px 48px -12px rgba(0, 0, 0, 0.7),
        0 0 0 1px rgba(201, 166, 52, 0.25),
        inset 0 0 0 1px rgba(0, 0, 0, 0.35),
        inset 0 1px 0 rgba(201, 166, 52, 0.18),
        inset 0 -20px 40px -20px rgba(139, 0, 0, 0.18);
    overflow: hidden;
}

/* Watermark seal — a giant lozenge of diagonal hatching pinned
   to the right side at very low opacity. Acts as decoration
   behind the column without disrupting the reading width. */
.codex-page::before {
    content: "";
    position: absolute;
    right: -120px;
    top: 50%;
    transform: translateY(-50%) rotate(45deg);
    width: 380px;
    height: 380px;
    border: 1px solid rgba(201, 166, 52, 0.08);
    background: repeating-linear-gradient(45deg,
        transparent 0 14px,
        rgba(201, 166, 52, 0.025) 14px 15px);
    pointer-events: none;
    z-index: 0;
}

/* Top gilt rule — same vocabulary as .panel-card so the
   imperial-frame language is consistent across page types. */
.codex-page::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    top: 0;
    height: 2px;
    background: linear-gradient(90deg,
        transparent 0%,
        rgba(201, 166, 52, 0.35) 8%,
        var(--gold) 50%,
        rgba(201, 166, 52, 0.35) 92%,
        transparent 100%);
    box-shadow: 0 0 12px rgba(201, 166, 52, 0.45);
    z-index: 2;
    pointer-events: none;
}

.codex-page > * {
    position: relative;
    z-index: 1;
}

/* Hero block — opens the document. Eyebrow tagline above,
   oversized gold display title, centred ornamental rule with
   a stylised mark, subtitle below. */
.codex-hero {
    text-align: center;
    margin-bottom: 3rem;
}

.codex-hero-eyebrow {
    color: var(--steel);
    font-size: 0.7rem;
    letter-spacing: 0.45em;
    text-transform: uppercase;
    margin-bottom: 1.5rem;
}

.codex-hero-title {
    color: var(--gold);
    font-size: 2.6rem;
    font-weight: 800;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    margin: 0;
    text-shadow:
        0 0 32px rgba(201, 166, 52, 0.35),
        0 1px 0 rgba(0, 0, 0, 0.5);
}

.codex-hero-ornament {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 1rem;
    margin: 1.5rem auto 1.25rem;
    max-width: 380px;
}

.codex-hero-rule {
    flex: 1;
    height: 1px;
    background: linear-gradient(90deg,
        transparent 0%,
        rgba(201, 166, 52, 0.55) 50%,
        transparent 100%);
}

.codex-hero-mark {
    color: var(--gold);
    font-size: 1.25rem;
    text-shadow: 0 0 12px rgba(201, 166, 52, 0.6);
    line-height: 1;
}

.codex-hero-tagline {
    color: var(--steel);
    font-size: 0.85rem;
    letter-spacing: 0.32em;
    text-transform: uppercase;
    margin: 0;
}

/* Body — rendered markdown. Editorial column: generous line
   height, Roman-numeral chapter prefixes on h2, lozenge
   bullets, drop-cap on the very first paragraph. */
.codex-body {
    counter-reset: codex-chapter;
    color: var(--ink);
    font-size: 1.02rem;
    line-height: 1.85;
    max-width: 70ch;
    margin: 0 auto;
}

.codex-body > :first-child {
    margin-top: 0;
}

.codex-body > :last-child {
    margin-bottom: 0;
}

.codex-body h1,
.codex-body h2,
.codex-body h3,
.codex-body h4 {
    color: var(--gold);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.22em;
    margin: 3rem 0 1rem;
    padding-bottom: 0.6rem;
    border-bottom: 1px solid rgba(201, 166, 52, 0.22);
}

.codex-body h2 {
    counter-increment: codex-chapter;
    font-size: 1.15rem;
    display: flex;
    align-items: baseline;
    gap: 1rem;
}

.codex-body h2::before {
    content: counter(codex-chapter, upper-roman);
    color: var(--blood);
    font-size: 0.85rem;
    letter-spacing: 0.1em;
    min-width: 2.5em;
    text-shadow: 0 0 12px rgba(139, 0, 0, 0.55);
    flex-shrink: 0;
}

.codex-body h1 { font-size: 1.4rem; }
.codex-body h3 { font-size: 1rem; }
.codex-body h4 { font-size: 0.9rem; }

.codex-body p {
    margin: 0 0 1.2rem;
}

/* Drop-cap on the very first paragraph of the manifesto.
   Sized at four-and-some lines, gold with a faint glow. */
.codex-body > p:first-of-type::first-letter {
    float: left;
    font-size: 4.2rem;
    line-height: 0.85;
    color: var(--gold);
    padding: 0.25rem 0.7rem 0.1rem 0;
    margin-right: 0.15rem;
    font-weight: 800;
    text-shadow:
        0 0 22px rgba(201, 166, 52, 0.45),
        0 1px 0 rgba(0, 0, 0, 0.5);
}

.codex-body ul,
.codex-body ol {
    list-style: none;
    padding: 0;
    margin: 0 0 1.2rem;
}

.codex-body ul li,
.codex-body ol li {
    position: relative;
    padding-left: 1.8rem;
    margin-bottom: 0.65rem;
}

/* Lozenge bullets — same 45° rotated gold square the
   .panel-card-header h2 ornament uses, scaled smaller. */
.codex-body ul li::before {
    content: "";
    position: absolute;
    left: 0.4rem;
    top: 0.85em;
    width: 6px;
    height: 6px;
    background: var(--gold);
    transform: rotate(45deg);
    box-shadow: 0 0 6px rgba(201, 166, 52, 0.55);
}

.codex-body ol {
    counter-reset: codex-list;
}

.codex-body ol li {
    counter-increment: codex-list;
}

.codex-body ol li::before {
    content: counter(codex-list, decimal-leading-zero);
    position: absolute;
    left: 0;
    color: var(--gold);
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    font-size: 0.85rem;
    letter-spacing: 0.05em;
}

.codex-body strong {
    color: var(--gold);
    font-weight: 700;
}

.codex-body em {
    color: var(--steel);
    font-style: italic;
}

.codex-body code {
    background: rgba(0, 0, 0, 0.45);
    color: var(--gold);
    padding: 0.12rem 0.4rem;
    border-radius: 3px;
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    font-size: 0.85rem;
    border: 1px solid rgba(201, 166, 52, 0.18);
}

.codex-body pre {
    position: relative;
    background: rgba(0, 0, 0, 0.55);
    color: var(--ink);
    padding: 1rem 1.25rem 1rem 1.5rem;
    border-radius: 3px;
    overflow: auto;
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    font-size: 0.85rem;
    line-height: 1.55;
    margin: 0 0 1.2rem;
    box-shadow:
        0 0 18px rgba(139, 0, 0, 0.18),
        inset 0 0 0 1px rgba(255, 255, 255, 0.04);
}

/* Blood spine on pre blocks — uses the same vocabulary as
   .tetsuo-card via a ::before pseudo so the spine pattern
   is consistent (never border-left for spine treatments). */
.codex-body pre::before {
    content: "";
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    width: 3px;
    background: var(--blood);
    box-shadow: 0 0 8px var(--glow-blood);
    border-top-left-radius: 3px;
    border-bottom-left-radius: 3px;
}

.codex-body pre code {
    background: transparent;
    border: none;
    padding: 0;
}

.codex-body blockquote {
    position: relative;
    margin: 1.2rem 0;
    padding: 0.6rem 1.4rem;
    color: var(--steel);
    font-style: italic;
}

.codex-body blockquote::before {
    content: "";
    position: absolute;
    left: 0;
    top: 0.4em;
    bottom: 0.4em;
    width: 3px;
    background: var(--blood);
    box-shadow: 0 0 8px var(--glow-blood);
}

.codex-body a {
    color: var(--gold);
    text-decoration: underline dotted;
    text-underline-offset: 4px;
    transition: color 0.12s ease, text-shadow 0.12s ease;
}

.codex-body a:hover {
    color: var(--ink);
    text-shadow: 0 0 8px rgba(201, 166, 52, 0.4);
}

.codex-body table {
    width: 100%;
    border-collapse: collapse;
    margin: 0 0 1.2rem;
    font-size: 0.92rem;
}

.codex-body th,
.codex-body td {
    padding: 0.55rem 0.8rem;
    text-align: left;
    border-bottom: 1px solid rgba(201, 166, 52, 0.15);
}

.codex-body th {
    color: var(--gold);
    text-transform: uppercase;
    letter-spacing: 0.12em;
    font-size: 0.78rem;
    font-weight: 700;
    border-bottom-color: rgba(201, 166, 52, 0.45);
}

/* Footer seal — mirrors the hero ornament so the page reads
   as bookended by two ornamental gates. Treated like the
   stamp at the foot of an imperial decree. */
.codex-seal {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 1rem;
    margin: 3.5rem auto 0;
    max-width: 380px;
}

.codex-seal-rule {
    flex: 1;
    height: 1px;
    background: linear-gradient(90deg,
        transparent 0%,
        rgba(201, 166, 52, 0.4) 50%,
        transparent 100%);
}

.codex-seal-mark {
    color: var(--steel);
    font-size: 0.72rem;
    letter-spacing: 0.32em;
    text-transform: uppercase;
    white-space: nowrap;
}

@media (max-width: 720px) {
    .codex-page {
        padding: 2rem 1.25rem;
        margin: 0.5rem;
    }
    .codex-page::before {
        display: none;
    }
    .codex-hero-title {
        font-size: 1.7rem;
        letter-spacing: 0.12em;
    }
    .codex-hero-eyebrow {
        letter-spacing: 0.32em;
        font-size: 0.62rem;
    }
    .codex-body {
        font-size: 0.96rem;
    }
    .codex-body > p:first-of-type::first-letter {
        font-size: 3rem;
    }
    .codex-body h2 {
        flex-direction: column;
        align-items: flex-start;
        gap: 0.2rem;
    }
    /* Codex seal — on desktop the three children (rule, mark,
       rule) flow horizontally inside a 380px-capped flex row
       with the mark forced to white-space: nowrap. On mobile
       that string overflows the 380px cap and the codex-page's
       overflow: hidden clips it on the right. At narrow widths
       the seal stacks vertically instead: top rule, centred
       mark that wraps on word boundaries, bottom rule. The
       rule flex: 1 default is overridden to 0 0 auto so the
       rules do not grow into the column's main axis, and a
       fixed width keeps them visible as ornamental hairlines
       above and below the wrapped text. */
    .codex-seal {
        flex-direction: column;
        align-items: center;
        max-width: 100%;
        padding: 0 1rem;
        gap: 0.65rem;
    }
    .codex-seal-rule {
        flex: 0 0 auto;
        width: 60%;
        height: 1px;
    }
    .codex-seal-mark {
        white-space: normal;
        text-align: center;
        letter-spacing: 0.2em;
        line-height: 1.5;
    }
}

/* ── Mobile rescue ──────────────────────────────────────────
   Additive-only — every rule below is scoped inside the media
   queries (the two .nav-toggle / .nav-toggle-items defaults
   immediately below excepted; those are dormant-on-desktop
   sentinels for markup the rescue introduces). Desktop CSS
   above this line is bit-identical to before the rescue. The
   imperial vocabulary (gold + blood + void, clip-path bevels,
   blood spine, gilt rules) is preserved at every breakpoint;
   only sizes, padding, and layout direction reduce at narrow
   widths. */

/* Hamburger toggle defaults — display: none on desktop hides
   the button; display: contents on the items wrapper lets the
   wrapped link list flow transparently into the .app-navbar /
   .component-tabs flex parent so the desktop layout is
   identical to the pre-wrapping markup. The mobile breakpoint
   overrides both. */
.nav-toggle {
    display: none;
}

.nav-toggle-items {
    display: contents;
}

@media (max-width: 768px) {
    /* Top brand row (.navbar in partials/header.html).
       flex-wrap lets the user-action wrapper (the .d-flex
       around the login button or user dropdown) drop to its
       own full-width row aligned to the right edge when the
       brand block already owns row 1's width budget. On
       wider tablets where both fit, the wrap is a no-op and
       brand + action sit on the same row via the inherited
       space-between alignment, identical to desktop. */
    .navbar {
        padding: 0.5rem 0.75rem;
        flex-wrap: wrap;
        gap: 0.4rem 0;
    }
    .navbar-brand {
        flex: 1 1 auto;
        min-width: 0;
    }
    .navbar > .d-flex {
        flex: 0 0 100%;
        width: 100%;
        justify-content: flex-end;
    }
    .navbar-brand-logo {
        width: 36px;
        height: 36px;
        margin-right: 0.5rem;
    }
    .navbar-brand-text {
        padding-left: 0.5rem;
        min-width: 0;
        flex: 1 1 auto;
    }
    /* Tagline stays visible across the full mobile range.
       The existing 576-block earlier in this file hides it
       via display: none; this rule wins by being later in
       the cascade. Sized smaller + lower opacity than
       desktop. Line-clamped to 2 lines with ellipsis so a
       long tagline (the default "Distributed System for
       Whole Market Analysis and Automated Trading" runs
       ~65 characters) wraps inside the brand-text column
       rather than running past the viewport edge. The
       min-width: 0 added to .navbar-brand-text + .navbar-
       brand above is what lets the tagline shrink below
       single-line content width so the wrap actually
       engages instead of overflowing the row. */
    .navbar-brand-tagline {
        display: -webkit-box;
        -webkit-line-clamp: 2;
        -webkit-box-orient: vertical;
        overflow: hidden;
        white-space: normal;
        font-size: 0.55rem;
        letter-spacing: 0.1em;
        margin-top: 1px;
        opacity: 0.7;
        line-height: 1.25;
    }
    .navbar-brand-title {
        font-size: 0.85rem;
        letter-spacing: 0.16em;
    }
    .nav-user-btn {
        padding: 0.35rem 0.75rem;
        font-size: 0.62rem;
        letter-spacing: 0.12em;
    }

    /* Cross-component navbar + per-component component-tabs:
       both collapse into a hamburger drawer. The outer
       .app-navbar / .component-tabs keep their existing chrome
       (gradient background + gold inset rule + bottom shadow
       or hairline) but now lay out as a column: the toggle
       button sits at top, the wrapped link panel collapses
       and reveals via the aria-expanded sibling selector. */
    .app-navbar,
    .component-tabs {
        flex-direction: column;
        align-items: stretch;
        padding: 0;
        gap: 0;
    }

    .nav-toggle {
        display: flex;
        align-items: center;
        gap: 0.6rem;
        width: 100%;
        background: transparent;
        border: none;
        color: var(--gold);
        padding: 0.65rem 1rem;
        font-size: 0.68rem;
        font-weight: 700;
        letter-spacing: 0.22em;
        text-transform: uppercase;
        text-align: left;
        cursor: pointer;
        font-family: inherit;
        text-shadow: 0 0 6px rgba(201, 166, 52, 0.35);
    }

    .nav-toggle:focus {
        outline: none;
        background: rgba(201, 166, 52, 0.08);
    }

    .nav-toggle-label {
        flex: 1;
    }

    .nav-toggle-chevron {
        margin-left: auto;
        color: var(--gold);
        transition: transform 0.2s ease;
    }

    .nav-toggle[aria-expanded="true"] .nav-toggle-chevron {
        transform: rotate(180deg);
    }

    /* Collapsed/expanded items panel. max-height transition
       gives a smooth reveal; padding transitions alongside so
       the drawer collapses fully flat when closed. Items keep
       their full clip-path + blood-spine chrome but lay out
       vertically at full width so the drawer reads as a stack
       of imperial command blocks rather than a flat link list. */
    .nav-toggle-items {
        display: flex;
        flex-direction: column;
        gap: 0.4rem;
        max-height: 0;
        overflow: hidden;
        padding: 0 0.85rem;
        transition: max-height 0.28s ease, padding 0.28s ease;
    }

    .nav-toggle[aria-expanded="true"] + .nav-toggle-items {
        max-height: 70vh;
        padding: 0.45rem 0.85rem 0.85rem;
        overflow-y: auto;
    }

    .app-navbar-link,
    .component-tabs-link {
        width: 100%;
        text-align: left;
    }

    /* Main content padding — the outer 2rem 1rem of negative
       space around the panel-card eats viewport width on a
       phone; tighten to 0.75rem 0.5rem so the card has room. */
    .main-content {
        padding: 0.75rem 0.5rem;
    }

    /* Panel-card chrome — preserve the polygon bevel + gilt
       rule + L-bracket identity, just shrink the bevel from
       16px to 10px and tighten interior padding so the body
       has working room at narrow widths. */
    .panel-card {
        margin-bottom: 1rem;
        clip-path: polygon(
            0 0,
            calc(100% - 10px) 0,
            100% 10px,
            100% 100%,
            10px 100%,
            0 calc(100% - 10px));
    }
    .panel-card-header {
        padding: 1rem 1rem 0.75rem;
    }
    .panel-card-header h2 {
        font-size: 0.85rem;
        letter-spacing: 0.18em;
    }
    .panel-card-header::after {
        left: 1rem;
        right: 1rem;
    }
    .panel-card-header .subtitle {
        letter-spacing: 0.16em;
    }
    .panel-card-body {
        padding: 1rem 0.75rem;
    }

    /* ORK Configuration form — desktop is a three-column
       grid (label / input / unit) bound by display: contents
       on .ork-config-row. Mobile drops both: flex column on
       the form, flex column on each row, so label → input →
       unit stack vertically. */
    .ork-config-form {
        display: flex;
        flex-direction: column;
        gap: 0.85rem;
    }
    .ork-config-row {
        display: flex;
        flex-direction: column;
        align-items: flex-start;
        gap: 0.35rem;
    }
    .ork-config-input {
        width: 100%;
        box-sizing: border-box;
    }
    .ork-config-section {
        padding-top: 1rem;
        margin-top: 1rem;
    }
    .ork-config-section-head {
        flex-direction: column;
        align-items: flex-start;
        gap: 0.25rem;
    }

    /* Status grid cards — drop the rigid min-widths on the
       check block and badge so they can shrink to viewport
       width without forcing a horizontal scrollbar. The
       card already wraps via flex-wrap: wrap; freeing the
       min-widths lets it stack cleanly into Info / Check /
       Badge bands. */
    .tetsuo-card-check {
        min-width: 0;
        width: 100%;
        align-items: flex-start;
        padding: 6px 10px;
    }
    .tetsuo-status-badge {
        min-width: 0;
        width: 100%;
        font-size: 0.7rem;
        padding: 8px 10px;
    }

    /* Status-grid scroll stability. Each poll, status.js
       rewrites the state class + text on .tetsuo-status-badge,
       rewrites .tetsuo-card-check-value, and toggles
       .tetsuo-card-error and .ff-currency-badge in or out of
       layout. Three problems compound on a phone scrolled to
       the bottom: (a) document height shrinks when an error
       block disappears, the browser clamps scroll position to
       the new max and the operator gets snapped upward and
       cannot reach the footer between polls; (b) even with
       stable height, the browser's scroll-anchor algorithm
       picks an element near the viewport top to keep stable,
       and if it picks one of these rewriting nodes the anchor
       itself moves under the viewport; (c) the cumulative
       micro-shifts on a tight cadence make scrolling the
       grid feel actively hostile. Three-part fix below: */

    /* (1) Reserve the layout slot for the two show-on-demand
       chips so their visibility toggle does not change card
       height or document height. Bootstrap's .d-none and the
       hidden attribute both evaluate to display: none; the
       !important here beats Bootstrap's !important via more
       specific selectors, and visibility: hidden keeps the
       slot in the flex layout without painting the chip. */
    .tetsuo-card-error.d-none {
        display: block !important;
        visibility: hidden;
    }
    .ff-currency-badge[hidden] {
        display: inline-flex !important;
        visibility: hidden;
    }

    /* (2) Disable scroll anchoring at the document root so
       any residual layout shift (a long error wrapping to a
       second line, the first paint that promotes a card from
       INTERNAL to its real state, etc.) does not pull scroll
       position along with it. The operator's scroll stays at
       fixed pixel offset; the document may shift around it,
       but it never shifts the operator. Scoped to mobile so
       desktop's anchoring (useful on htmx-driven log / job
       tables there) is unchanged. */
    html {
        overflow-anchor: none;
    }

    /* (3) Belt-and-suspenders: even with anchoring off at
       the root, opt the rewriting nodes out of being anchor
       candidates. If a future change re-enables anchoring at
       a lower level, these stay safe. */
    .tetsuo-card,
    .tetsuo-card-check-value,
    .tetsuo-status-badge,
    .tetsuo-card-error,
    .ff-currency-badge {
        overflow-anchor: none;
    }

    /* DSH balance graph — responsive height. aspect-ratio
       fills width and grows tall enough to read without
       eating the whole viewport. */
    .dsh-graph {
        height: auto;
        aspect-ratio: 4 / 5;
        min-height: 280px;
        margin-left: 0;
    }

    /* Data tables (.logs-table, .jobs-table, .ff-table) —
       on desktop the time column is a fixed 215px and the
       message column wraps via word-break: break-word. On a
       375px phone, the time column claims most of the wrap
       width and the message column collapses to a 1-2 char
       stack per row. The wraps (.logs-table-wrap,
       .jobs-table-wrap) already have overflow: auto, so the
       fix is just forcing the table itself wider than the
       wrap: horizontal scroll engages, every column gets
       its natural desktop width, the operator swipes
       sideways to read the message column at full width. */
    .logs-table,
    .jobs-table,
    .ff-table {
        width: auto;
        min-width: 720px;
    }

    /* Footer — extend the existing 576-stack treatment up to
       768 so the IP/Time meta and copyright never fight for
       the same row at mid-narrow widths. */
    .footer {
        flex-direction: column;
        text-align: center;
        padding: 0.5rem 0.75rem;
        gap: 0.35rem 0;
    }
    .footer-meta-item {
        margin-left: 0;
    }
}

/* Extra tightening for phone-portrait widths — only the
   biggest offenders that still feel cramped at ≤480. */
@media (max-width: 480px) {
    .panel-card-header h2 {
        font-size: 0.75rem;
        letter-spacing: 0.14em;
    }
    .panel-card-body {
        padding: 0.85rem 0.6rem;
    }
    .tetsuo-card {
        padding: 10px 12px 10px 16px;
    }
}
