/* Recon Brief design system (phase 7c station 1).
   Source of truth: docs/planning/phase7c-polish/style-guide.md.
   Tokens only — components never use raw hex below the token block.
   Dark is the default theme; [data-theme="light"] overrides. */

/* ---- Fonts (self-hosted, OFL; latin subsets) --------------------- */

@font-face {
    font-family: "Nunito";
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url("/static/fonts/nunito-400.woff2") format("woff2");
}

@font-face {
    font-family: "Nunito";
    font-style: normal;
    font-weight: 600;
    font-display: swap;
    src: url("/static/fonts/nunito-600.woff2") format("woff2");
}

@font-face {
    font-family: "Nunito";
    font-style: normal;
    font-weight: 700;
    font-display: swap;
    src: url("/static/fonts/nunito-700.woff2") format("woff2");
}

@font-face {
    font-family: "Nunito";
    font-style: normal;
    font-weight: 800;
    font-display: swap;
    src: url("/static/fonts/nunito-800.woff2") format("woff2");
}

@font-face {
    font-family: "IBM Plex Mono";
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url("/static/fonts/ibm-plex-mono-400.woff2") format("woff2");
}

/* ---- Tokens ------------------------------------------------------ */

:root {
    /* surfaces */
    --ground: #16283f;
    --chrome: #0f1b2e;
    --surface: #1e3046;
    --surface-2: #263445;
    --inset: #162539;
    --border: #313e4f;
    --border-strong: #313e4f;
    --track: #404a59;

    /* text */
    --ink: #f5efe3;
    --ink-2: #9fb0c9;
    --ink-3: #929fb4;
    --placeholder: #838e9e;

    /* accent + status */
    --accent: #e89f3d;
    --accent-strong: #cf8a2c;
    --on-accent: #16283f;
    --ok: #49cf8a;
    --ok-strong: #27ae60;
    --warn: #e8c23d;
    --danger: #ec9b9b;
    --watermark-opacity: 0.05;

    /* tint strengths for status pills and alert/notice boxes */
    --pill-mix: 12%;
    --tint-bg: 12%;
    --tint-border: 35%;

    /* type */
    --font-display: "Nunito", system-ui, "Segoe UI", sans-serif;
    --font-body: "Nunito", system-ui, "Segoe UI", sans-serif;
    --font-mono: "IBM Plex Mono", ui-monospace, Consolas, monospace;

    --shadow: 0 1px 3px rgb(0 0 0 / 0.25);
    --scroll-shadow: rgb(0 0 0 / 0.38);
    color-scheme: dark;
}

:root[data-theme="light"] {
    --ground: #f4f1ea;
    --chrome: #1d2a3e;
    --surface: #ffffff;
    --surface-2: #e6dfd0;
    --inset: #faf8f4;
    --border: #cdc5b3;
    --border-strong: #8b7c57;
    --track: #d0cabb;

    --ink: #1d2a3e;
    --ink-2: #43536b;
    --ink-3: #586378;
    --placeholder: #a0a8b6;

    --accent: #724809;
    --accent-strong: #573708;
    --on-accent: #ffffff;
    --ok: #145c33;
    --ok-strong: #0f4b28;
    --warn: #644f08;
    --danger: #962424;
    --watermark-opacity: 0.025;

    --pill-mix: 18%;
    --tint-bg: 22%;
    --tint-border: 55%;

    --shadow: 0 1px 3px rgb(29 42 62 / 0.1);
    --scroll-shadow: rgb(29 42 62 / 0.22);
    color-scheme: light;
}

/* ---- Base -------------------------------------------------------- */

* { box-sizing: border-box; }

body {
    margin: 0;
    font-family: var(--font-body);
    background: var(--ground);
    color: var(--ink);
    line-height: 1.5;
}

h1, h2, h3 {
    font-family: var(--font-display);
    font-weight: 700;
    line-height: 1.2;
}

h1 { font-size: 1.5rem; }

a { color: var(--accent); }

/* :where() keeps the original a:hover specificity, so anchors styled as buttons
   (.btn and its variants) keep their own text colour on hover. */
a:hover:where(:not(.btn)) { color: var(--accent-strong); }

:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

.muted { color: var(--ink-3); }

.mono {
    font-family: var(--font-mono);
    font-size: 0.8125rem;
}

/* ---- Top bar + navigation ---------------------------------------- */

.topbar {
    display: flex;
    align-items: center;
    gap: 2rem;
    padding: 0.55rem 1.5rem;
    background: var(--chrome);
}

.brand {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    color: #f5efe3;
    text-decoration: none;
    flex: 0 0 auto;
}

.brand__logo {
    width: 34px;
    height: 34px;
    flex: 0 0 auto;
    display: block;
}

.brand__name {
    font-family: var(--font-display);
    font-size: 1.15rem;
    font-weight: 800;
    letter-spacing: 0.02em;
}

.nav {
    display: flex;
    align-items: center;
    gap: 1.5rem;
}

.nav-link {
    color: #9fb0c9;
    text-decoration: none;
    font-size: 0.95rem;
    font-weight: 600;
    padding: 0.35rem 0;
    border-bottom: 2px solid transparent;
}

.nav-link:hover { color: #f5efe3; }

.nav-link.is-active {
    color: #f5efe3;
    border-bottom-color: var(--accent);
}

.topbar__side {
    margin-left: auto;
    display: flex;
    align-items: center;
    gap: 1rem;
}

/* The user menu is a CSS-only <details> dropdown. */
.user-menu { position: relative; }

.user-menu > summary {
    list-style: none;
    cursor: pointer;
    color: #f5efe3;
    font-family: var(--font-mono);
    font-size: 0.85rem;
    padding: 0.3rem 0.6rem;
    border: 1px solid #313e4f;
    border-radius: 999px;
    /* A long display name is ellipsed rather than wrapped: the pill
       stays a one-line pill and never widens the header (#107). */
    max-width: 10rem;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.user-menu > summary::-webkit-details-marker { display: none; }

.user-menu > summary::after { content: " ▾"; color: #6e7e9a; }

/* The floating half of a CSS-only <details> menu — shared by the user
   menu and the usage page's org picker (#74). The opener sets its own
   `position: relative`; each user pins its own edge and width. */
.popover-panel {
    position: absolute;
    top: calc(100% + 0.4rem);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 10px;
    box-shadow: var(--shadow);
    padding: 0.4rem;
    z-index: 20;
    display: flex;
    flex-direction: column;
}

.user-menu__panel {
    right: 0;
    min-width: 11rem;
}

.user-menu__item {
    display: block;
    width: 100%;
    text-align: left;
    padding: 0.45rem 0.7rem;
    border: none;
    background: none;
    border-radius: 7px;
    color: var(--ink);
    text-decoration: none;
    font-size: 0.95rem;
    font-family: var(--font-body);
    /* Buttons don't inherit line-height; without this the Sign out
       button renders shorter than the links beside it (#104). */
    line-height: inherit;
    cursor: pointer;
}

.user-menu__item:hover {
    background: var(--surface-2);
    color: var(--ink);
}

.logout-form { margin: 0; }

.subnav {
    display: flex;
    gap: 0.3rem;
    padding: 0.35rem 1.5rem;
    background: var(--chrome);
    border-top: 1px solid #1c2c44;
}

.subnav__link {
    color: #9fb0c9;
    text-decoration: none;
    font-size: 0.88rem;
    font-weight: 600;
    padding: 0.25rem 0.7rem;
    border-radius: 999px;
}

.subnav__link:hover { color: #f5efe3; }

.subnav__link.is-active {
    color: #f5efe3;
    background: #223349;
}

/* Narrow widths: the header collapses onto two lines inside the same
   dark bar — brand + user menu on the first, the primary nav on the
   second — instead of forcing a ~650px minimum document width (#107).
   CSS only; no hamburger, no JS. */
@media (max-width: 860px) {
    .topbar {
        flex-wrap: wrap;
        gap: 0.5rem 1rem;
        padding: 0.55rem 1rem;
    }

    .nav {
        order: 3;
        flex-basis: 100%;
        gap: 1rem;
        overflow-x: auto;
    }

    .nav-link { flex: 0 0 auto; }

    .subnav {
        flex-wrap: wrap;
        row-gap: 0.2rem;
        padding: 0.35rem 1rem;
    }

    .subnav__link { flex: 0 0 auto; }
}

/* ---- Page container ---------------------------------------------- */

.content {
    max-width: 960px;
    margin: 2rem auto;
    padding: 0 1.5rem;
    position: relative;
}

.content--wide { max-width: 1160px; }

/* Logo watermark behind the page ground (dashboard). */
.watermarked::before {
    content: "";
    position: fixed;
    inset: 0;
    background: url("/static/logo.png") no-repeat right -8rem top 4rem;
    background-size: 42rem;
    opacity: var(--watermark-opacity);
    pointer-events: none;
    z-index: -1;
}

/* ---- Overflow ----------------------------------------------------
   The `min-width: auto` trap: a flex or grid item defaults to
   `min-width: auto`, i.e. it refuses to shrink below its widest
   unbreakable token. One 60-char run name (a manifest-less run falls
   back to its run name) therefore shoves its row, its card, and the
   page past the viewport. The fix is an explicit `min-width: 0` on
   every flex/grid child that holds text, plus a way out for the token
   itself: an ellipsis where the full string is reachable another way
   (an expandable row), wrapping everywhere else (#62). */

.truncate {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.wrap-anywhere {
    min-width: 0;
    overflow-wrap: anywhere;
}

/* Containers never grow past their parent (grid items default to
   `min-width: auto` too). */
.card,
.dash-grid > * { min-width: 0; }

/* Expandable rows (/home, /reports, /jobs): the case label is the long
   one. Collapsed it truncates so the row stays one tidy line; opening
   the row shows the whole label, wrapped. */
.job-row__summary > * { min-width: 0; }

/* The case label is the long one; the timestamp joins it because a run
   whose name is not a parseable timestamp falls back to the raw
   65-char run name (#107). */
.job-row__case,
.job-row__ts {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.job-row[open] > .job-row__summary .job-row__case,
.job-row[open] > .job-row__summary .job-row__ts {
    overflow: visible;
    white-space: normal;
    overflow-wrap: anywhere;
}

/* A <select> sizes itself to its widest <option>, so one long org name
   drags the page sideways. It stops at its container instead (#107). */
select {
    min-width: 0;
    max-width: 100%;
}

/* The dashboard hero greets the user by display name, so its heading
   carries a user-supplied token (#107). */
.hero > * { min-width: 0; }

/* Two more flex rows that render user- or pipeline-supplied strings:
   the queue row's progress note and the org line on a staff user's
   page (a long org name) (#107). */
.queue-row__progress > * { min-width: 0; }

/* Everything below a row wraps — nothing here is worth an ellipsis,
   because there is no second place to read it. */
.job-row__body,
.job-facts dd,
.report-card__files li,
.queue-row__case,
.case-card,
.case-card__title,
.hero__title,
.hero__lede,
.queue-row__progress,
.form-card p,
.page-head h1,
.page-lede { overflow-wrap: anywhere; }

/* Bare <details> summaries that render user-supplied text. Tag-qualified
   so these win over the component rules further down the file, which set
   only cursor/padding but sit later. */
details.invoice-tree__user > summary,
details.invoice-tree__case > summary,
.permissions-summary { overflow-wrap: anywhere; }

.case-card__title { min-width: 0; }

/* Log lists: 32-char job ids and storage-key targets sit in the 1fr
   track and used to cross the row border (#100). */
.log-row__summary > *,
.log-list__head > * { min-width: 0; overflow-wrap: anywhere; }

/* The fixed rem tracks are wider than a narrow viewport, so below
   ~950px the grid stops being a grid: rows wrap and the page stops
   scrolling sideways. The chevron is a flex item there. */
@media (max-width: 960px) {
    .log-list .log-list__head { display: none; }

    .log-list .log-row__summary {
        display: flex;
        flex-wrap: wrap;
        gap: 0.4rem 0.8rem;
    }
}

.stat-card__value { overflow-wrap: anywhere; }

/* A wide table scrolls inside its card instead of escaping it (#62), and
   is allowed to be wider than the card rather than crushing its columns
   into mid-token wraps (#126): max-content grows the table to its content,
   min-width fills the card when there is room to spare. Short tokens --
   dates, amounts, usernames, buttons -- stay on one line; the cells that
   hold prose opt back into wrapping with `cell--wrap`, capped so one long
   note cannot stretch the table without limit. */
/* A wide table scrolls sideways inside its card (#126); this is the hint
   that it does (#129). Two "cover" layers painted in the card surface
   scroll WITH the content (`background-attachment: local`) on top of two
   fixed shadow layers, so an edge shadow shows only while there is content
   past that edge and disappears when the table fits. Table cells carry no
   background of their own, so the covers stay visible through the rows. */
.table-scroll {
    overflow-x: auto;
    max-width: 100%;
    background-image:
        linear-gradient(to right, var(--surface) 60%, transparent),
        linear-gradient(to left, var(--surface) 60%, transparent),
        linear-gradient(to right, var(--scroll-shadow), transparent),
        linear-gradient(to left, var(--scroll-shadow), transparent);
    background-position: left center, right center, left center, right center;
    background-size: 2.5rem 100%, 2.5rem 100%, 0.9rem 100%, 0.9rem 100%;
    background-repeat: no-repeat;
    background-attachment: local, local, scroll, scroll;
    scrollbar-width: thin;
    scrollbar-color: var(--track) transparent;
}

/* Overlay scrollbars (touch, macOS) hide themselves until you drag, so the
   WebKit bar is pinned thin-but-present. */
.table-scroll::-webkit-scrollbar { height: 6px; }
.table-scroll::-webkit-scrollbar-track { background: transparent; }

.table-scroll::-webkit-scrollbar-thumb {
    background: var(--track);
    border-radius: 999px;
}

.table-scroll > .data-table {
    width: max-content;
    min-width: 100%;
}

.data-table th,
.data-table td { white-space: nowrap; }

/* Prose cells, plus the spanning cells that carry an empty-state sentence. */
.data-table td.cell--wrap,
.data-table td[colspan] {
    white-space: normal;
    overflow-wrap: anywhere;
    max-width: 22rem;
}

.data-table td .btn { white-space: nowrap; }

/* Action cells (#82): a bare link plus one <form> per POST action —
   each POST needs its own CSRF input, so the siblings are structural.
   `.inline-form` is a block-level flex container, so they stack unless
   something makes the cell a row. The flex wrapper is a <div> inside
   the cell rather than the <td> itself: a `display: flex` td stops
   being a table-cell, and its bottom border then tracks its own
   content instead of the row. Buttons already opt out of wrapping via
   `.data-table td .btn` above. */
.data-table td .cell--actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    align-items: center;
}

.data-table td .cell--actions > .inline-form { margin: 0; }

/* The invite link sits next to its Copy button (#114); the default input
   width truncated it mid-token. The table already scrolls sideways. */
.invite-link { min-width: 18rem; }

/* ---- Page header (interior pages) -------------------------------- */

.page-head { margin-bottom: 1.5rem; }

.page-head h1 {
    font-size: 1.8rem;
    font-weight: 800;
    margin: 0 0 0.35rem;
}

.page-lede {
    color: var(--ink-2);
    max-width: 40rem;
    margin: 0;
}

/* ---- Cards ------------------------------------------------------- */

.card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 14px;
    padding: 1.5rem;
    box-shadow: var(--shadow);
}

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

.card-label {
    color: var(--ink-2);
    font-size: 0.75rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
}

.card-link {
    font-size: 0.9rem;
    font-weight: 600;
    text-decoration: none;
}

.card-foot {
    margin-top: 0.8rem;
    display: flex;
    justify-content: flex-end;
}

/* Long legal prose inside a card (#147: the ToS gate). The text scrolls
   in its own box so the accept button below it is always on screen;
   both themes inherit the card's surface and border tokens. */
.terms-scroll {
    max-height: 26rem;
    overflow-y: auto;
    padding: 0.2rem 1rem;
    margin: 1rem 0;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--surface-2);
    scrollbar-width: thin;
    scrollbar-color: var(--track) transparent;
}

/* ---- Dashboard --------------------------------------------------- */

.hero {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1.5rem;
    flex-wrap: wrap;
    margin-bottom: 1.5rem;
}

.hero__title {
    font-size: 2.5rem;
    font-weight: 800;
    margin: 0 0 0.4rem;
}

.hero__lede {
    color: var(--ink-2);
    max-width: 34rem;
    margin: 0;
}

.dash-grid {
    display: grid;
    grid-template-columns: 5fr 6fr;
    gap: 1.5rem;
    margin-top: 1.5rem;
    align-items: start;
}

@media (max-width: 860px) {
    .dash-grid { grid-template-columns: 1fr; }
    .hero__title { font-size: 1.8rem; }
}

.queue-row {
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--surface-2);
    padding: 0.7rem 0.9rem;
    margin: 0.5rem 0;
}

.queue-row__main {
    display: flex;
    align-items: center;
    gap: 0.7rem;
    flex-wrap: wrap;
}

.queue-row__case {
    flex: 1 1 8rem;
    min-width: 0;
    font-weight: 700;
}

.queue-row__meta {
    color: var(--ink-3);
    white-space: nowrap;
}

.queue-row__progress {
    display: flex;
    align-items: center;
    gap: 0.7rem;
    margin-top: 0.55rem;
}

/* ---- Stat cards -------------------------------------------------- */

.stat-cards {
    display: flex;
    flex-wrap: wrap;
    gap: 1.5rem;
    margin: 1.5rem 0;
}

/* The scope line sits on its own row above the tiles (#63). */
.stat-cards__scope {
    flex: 1 0 100%;
    margin: 0;
    font-size: 0.9rem;
}

.stat-cards__scope a { margin-left: 0.5rem; }

.stat-card {
    flex: 1 1 10rem;
    min-width: 0;
    padding: 1.1rem 1.3rem;
    text-align: left;
}

.stat-card__value {
    font-family: var(--font-display);
    font-size: 2.25rem;
    font-weight: 800;
    line-height: 1.15;
}

.stat-card__label {
    color: var(--ink-2);
    font-size: 0.75rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    margin-bottom: 0.3rem;
}

.stat-card__note {
    font-size: 0.8rem;
    margin-top: 0.3rem;
}

/* ---- Pills + progress -------------------------------------------- */

.status-pill {
    display: inline-block;
    border-radius: 999px;
    padding: 0.12rem 0.65rem;
    font-size: 0.8rem;
    font-weight: 700;
    white-space: nowrap;
}

.status-pill--pending {
    background: color-mix(in srgb, var(--ink-2) var(--pill-mix), transparent);
    color: var(--ink-2);
}

.status-pill--processing {
    background: color-mix(in srgb, var(--accent) var(--pill-mix), transparent);
    color: var(--accent);
}

.status-pill--completed {
    background: color-mix(in srgb, var(--ok) var(--pill-mix), transparent);
    color: var(--ok);
}

.status-pill--failed {
    background: color-mix(in srgb, var(--danger) var(--pill-mix), transparent);
    color: var(--danger);
}

.status-pill--cancelled {
    background: color-mix(in srgb, var(--warn) var(--pill-mix), transparent);
    color: var(--warn);
}

.status-pill--ok {
    background: color-mix(in srgb, var(--ok) var(--pill-mix), transparent);
    color: var(--ok);
}

.status-pill--calm {
    background: color-mix(in srgb, var(--ink-2) var(--pill-mix), transparent);
    color: var(--ink-2);
}

.status-pill--warn {
    background: color-mix(in srgb, var(--warn) var(--pill-mix), transparent);
    color: var(--warn);
}

.progress {
    flex: 1;
    height: 7px;
    border-radius: 999px;
    background: var(--track);
    overflow: hidden;
    position: relative;
}

.progress--indeterminate::after {
    content: "";
    position: absolute;
    inset: 0;
    width: 35%;
    border-radius: 999px;
    background: var(--accent-strong);
    animation: progress-slide 1.6s ease-in-out infinite;
}

@keyframes progress-slide {
    0% { transform: translateX(-100%); }
    100% { transform: translateX(300%); }
}

/* ---- Buttons ----------------------------------------------------- */

.btn {
    padding: 0.55rem 1.1rem;
    border: 1px solid var(--border-strong);
    border-radius: 10px;
    background: var(--surface-2);
    color: var(--ink);
    font-size: 0.95rem;
    font-family: var(--font-body);
    font-weight: 600;
    cursor: pointer;
    /* Anchors styled as buttons must not keep the UA's link underline. */
    text-decoration: none;
}

.btn:hover { background: color-mix(in srgb, var(--surface-2) 80%, var(--ink) 6%); }

.btn--primary {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--on-accent);
    border-radius: 999px;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-weight: 800;
    font-size: 0.88rem;
    padding: 0.65rem 1.4rem;
}

.btn--primary:hover { background: var(--accent-strong); border-color: var(--accent-strong); }

.btn--danger {
    background: color-mix(in srgb, var(--danger) 14%, transparent);
    border-color: color-mix(in srgb, var(--danger) 40%, transparent);
    color: var(--danger);
}

.btn--danger:hover { background: color-mix(in srgb, var(--danger) 22%, transparent); }

.btn--ghost { background: none; }

.btn--small {
    padding: 0.35rem 0.7rem;
    font-size: 0.88rem;
}

.btn--full { width: 100%; }

/* A disabled button must READ as disabled, not as a faded copy of the
   live one (#70): an inert flat fill, muted label, soft edge, and no
   hover response — opacity alone left an unavailable action looking
   like an offered one. The `--primary` selectors drop the accent fill
   so an unavailable action never wears the call-to-action colour. */
.btn:disabled,
.btn:disabled:hover,
.btn--primary:disabled,
.btn--primary:disabled:hover {
    background: var(--surface-2);
    border-color: var(--border);
    color: var(--ink-3);
    box-shadow: none;
    opacity: 1;
    cursor: not-allowed;
}

.linklike {
    background: none;
    border: none;
    padding: 0;
    cursor: pointer;
    font-family: inherit;
    font-size: inherit;
}

/* ---- Forms ------------------------------------------------------- */

input[type="text"],
input[type="search"],
input[type="password"],
input[type="email"],
input[type="date"],
input[type="month"],
input[type="number"],
select,
textarea {
    background: var(--inset);
    color: var(--ink);
    border: 1px solid var(--border-strong);
    border-radius: 10px;
    padding: 0.5rem 0.7rem;
    font-size: 1rem;
    font-family: var(--font-body);
}

::placeholder { color: var(--placeholder); }

.form__row {
    display: flex;
    gap: 1rem;
    flex-wrap: wrap;
}

.form__field {
    flex: 1;
    min-width: 14rem;
    display: flex;
    flex-direction: column;
    margin-bottom: 0.8rem;
}

.form__field label {
    color: var(--ink-2);
    font-size: 0.9rem;
    margin-bottom: 0.25rem;
}

.case-form__confirm {
    display: flex;
    gap: 0.6rem;
    align-items: flex-start;
    margin: 0.8rem 0;
}

.stacked-form {
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
    max-width: 26rem;
}

.stacked-form label {
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
    font-size: 0.92rem;
    color: var(--ink-2);
}

.checkbox-label {
    flex-direction: row !important;
    align-items: center;
    gap: 0.4rem !important;
}

.inline-form {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    align-items: center;
    margin: 0.4rem 0;
}

/* ---- Login ------------------------------------------------------- */

.login-card {
    max-width: 380px;
    margin: 3rem auto;
}

.login-card h1 {
    margin-top: 0;
    font-size: 1.4rem;
}

.login-card form {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}

.login-card label {
    font-size: 0.9rem;
    color: var(--ink-2);
    margin-top: 0.6rem;
}

.login-card button {
    margin-top: 1.2rem;
    padding: 0.6rem;
    border: none;
    border-radius: 999px;
    background: var(--accent);
    color: var(--on-accent);
    font-size: 0.95rem;
    font-family: var(--font-body);
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    cursor: pointer;
}

.login-card button:hover { background: var(--accent-strong); }

/* ---- Alerts + notices -------------------------------------------- */

.alert,
.notice {
    border-radius: 10px;
    padding: 0.6rem 0.8rem;
    margin: 0.6rem 0;
    font-size: 0.92rem;
}

.notice {
    background: color-mix(in srgb, var(--warn) var(--tint-bg), transparent);
    border: 1px solid color-mix(in srgb, var(--warn) var(--tint-border), transparent);
}

.alert--success {
    background: color-mix(in srgb, var(--ok) var(--tint-bg), transparent);
    border: 1px solid color-mix(in srgb, var(--ok) var(--tint-border), transparent);
}

.alert--info {
    background: color-mix(in srgb, var(--ink-2) var(--tint-bg), transparent);
    border: 1px solid color-mix(in srgb, var(--ink-2) var(--tint-border), transparent);
}

.alert--warning {
    background: color-mix(in srgb, var(--warn) var(--tint-bg), transparent);
    border: 1px solid color-mix(in srgb, var(--warn) var(--tint-border), transparent);
}

.alert--danger {
    background: color-mix(in srgb, var(--danger) var(--tint-bg), transparent);
    border: 1px solid color-mix(in srgb, var(--danger) var(--tint-border), transparent);
}

/* Warnings carry a left accent so the box reads as a warning in both themes
   (light's --warn is deep enough that its tint alone looks like neutral tan). */
.notice,
.alert--warning {
    border-left: 4px solid var(--warn);
}

.form-error {
    background: color-mix(in srgb, var(--danger) var(--tint-bg), transparent);
    border: 1px solid color-mix(in srgb, var(--danger) var(--tint-border), transparent);
    border-radius: 10px;
    padding: 0.3rem 0.5rem;
    font-size: 0.88rem;
    margin-top: 0.4rem;
    max-width: 28rem;
}

/* ---- Tables ------------------------------------------------------ */

.data-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.95rem;
}

.data-table th,
.data-table td {
    text-align: left;
    padding: 0.5rem 0.75rem;
    border-bottom: 1px solid var(--border);
}

.data-table th {
    color: var(--ink-2);
    font-size: 0.78rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
}

.data-table .num { text-align: right; }

.row--flagged { background: color-mix(in srgb, var(--warn) 10%, transparent); }

/* ---- Job rows + report cards ------------------------------------- */

.job-row {
    border: 1px solid var(--border);
    border-radius: 10px;
    margin: 0.5rem 0;
    background: var(--surface-2);
}

.job-row__summary {
    display: flex;
    align-items: center;
    gap: 0.8rem;
    padding: 0.6rem 0.8rem;
    cursor: pointer;
    flex-wrap: wrap;
    list-style: none;
}

.job-row__summary::-webkit-details-marker { display: none; }

/* Any `display` other than list-item kills the native triangle, so the
   row draws its own (#67) — and rotates it to announce open/closed. */
.job-row__summary::before {
    content: "▸";
    color: var(--ink-3);
    display: inline-block;
    width: 1em;
    transition: transform 120ms;
}

.job-row[open] > .job-row__summary::before { transform: rotate(90deg); }

.job-row__ts {
    color: var(--ink-3);
    font-family: var(--font-mono);
    font-size: 0.8125rem;
    white-space: nowrap;
}

.job-row__case {
    flex: 1 1 10rem;
    min-width: 0;
    font-weight: 700;
}

.job-row__pages {
    color: var(--ink-3);
    font-family: var(--font-mono);
    font-size: 0.8125rem;
    white-space: nowrap;
}

.job-row__kind {
    color: var(--ink-3);
    font-family: var(--font-mono);
    font-size: 0.8125rem;
    white-space: nowrap;
    border: 1px solid var(--border);
    border-radius: 999px;
    padding: 0 0.5rem;
}

.job-row__user {
    color: var(--ink-3);
    font-size: 0.88rem;
}

.job-row__body {
    border-top: 1px solid var(--border);
    padding: 0.6rem 0.8rem;
}

.job-row__body dl {
    display: grid;
    grid-template-columns: max-content 1fr;
    gap: 0.2rem 1rem;
    margin: 0 0 0.5rem;
}

.job-row__body dt { color: var(--ink-2); }

.job-row__body dd { margin: 0; }

.job-row__staff-meta {
    margin: 0.4rem 0;
    font-size: 0.9rem;
}

/* ---- Log lists (events / audit / job timeline) -------------------
   Same expand-below-the-row shape as the report card: the whole row is
   the <details>, the detail JSON opens underneath at full width (#100).
   Columns line up across rows because every row shares the page's
   --log-cols track list; the leading track is the chevron gutter. */

.log-row {
    border: 1px solid var(--border);
    border-radius: 10px;
    margin: 0.5rem 0;
    background: var(--surface-2);
}

.log-row__summary,
.log-list__head {
    display: grid;
    grid-template-columns: 1.2rem var(--log-cols);
    gap: 0.8rem;
    align-items: baseline;
}

.log-list__head {
    padding: 0.6rem 0.8rem 0.2rem;
    border: 1px solid transparent;
    color: var(--ink-2);
    font-size: 0.78rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
}

.log-list__head::before { content: ""; }

/* No detail ⇒ no <details>, so nothing to promise: no pointer, but the
   chevron gutter stays reserved so the columns still line up. */
.log-row--flat .log-row__summary { cursor: default; }

.log-row--flat .log-row__summary::before { content: ""; }

/* The Event cell (#102/#101): the human label leads, the slug stays
   beside it (the filter box matches on the slug), and the row's own
   summary of its detail drops onto a second line beneath. Label and
   slug wrap inside the cell when they don't fit on one line. */
.log-row__event { font-weight: 600; }

.log-row__slug { font-size: 0.85rem; }

.log-row__summary-line {
    display: block;
    margin-top: 0.15rem;
    font-size: 0.85rem;
    overflow-wrap: anywhere;
}

/* `main.content` caps this list at ~860px however wide the viewport is,
   so the tracks share ~54rem, not a screenful: 1.2 chevron + 14
   timestamp + 13.5 event + 8 username + 3.2 of gaps leaves ~12rem for
   the Job column. Anything wider starves that column and the 32-char
   job id wraps one character per line (`overflow-wrap: anywhere`),
   which is what a fat Event track cost on the first pass. */
.log-list--events { --log-cols: 14rem 13.5rem 8rem 1fr; }

.log-list--timeline { --log-cols: 14rem 5rem 1fr; }

.log-list--audit { --log-cols: 14rem 13rem 9rem 1fr; }

.report-card__owner {
    color: var(--ink-3);
    font-size: 0.88rem;
    white-space: nowrap;
}

.report-card__files {
    margin: 0.3rem 0 0;
    padding-left: 1.2rem;
}

.report-card__files li { margin: 0.25rem 0; }

.job-tag {
    display: inline-block;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: var(--surface);
    color: var(--ink-2);
    padding: 0.05rem 0.55rem;
    font-size: 0.8rem;
    margin-right: 0.35rem;
    white-space: nowrap;
}

.job-facts {
    display: grid;
    grid-template-columns: max-content 1fr;
    gap: 0.2rem 1rem;
    margin: 0.8rem 0;
}

.job-facts dt { color: var(--ink-2); }
.job-facts dd { margin: 0; }

/* ---- Upload flow ------------------------------------------------- */

.dropzone {
    border: 2px dashed color-mix(in srgb, var(--accent) 55%, transparent);
    border-radius: 12px;
    padding: 2rem;
    text-align: center;
    background: var(--surface-2);
    cursor: pointer;
}

.dropzone--dragover {
    border-color: var(--accent);
    background: color-mix(in srgb, var(--accent) 8%, var(--surface-2));
}

.dropzone__label { cursor: pointer; display: block; }

.dropzone__title {
    font-family: var(--font-display);
    font-weight: 700;
    font-size: 1.05rem;
}

.dropzone__hint {
    color: var(--ink-3);
    font-size: 0.9rem;
    margin-top: 0.4rem;
}

.dropzone input[type="file"] { display: none; }

.upload-indicator { margin-top: 0.8rem; }

.price-box {
    border: 1px solid var(--border);
    border-radius: 12px;
    padding: 0.8rem 1rem;
    margin: 0.8rem 0;
    background: var(--surface-2);
    display: inline-block;
    min-width: 14rem;
}

.price-box__label {
    color: var(--ink-2);
    font-size: 0.75rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
}

.price-box__value {
    font-family: var(--font-display);
    font-size: 1.6rem;
    font-weight: 800;
}

.price-box__detail {
    color: var(--ink-3);
    font-size: 0.9rem;
}

/* ---- Filter bar + worker banner ---------------------------------- */

.filter-bar {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    align-items: center;
    margin: 0.8rem 0;
}

.filter-bar__search {
    flex: 1 1 14rem;
    min-width: 10rem;
}

.filter-bar__date {
    color: var(--ink-2);
    font-size: 0.9rem;
    display: flex;
    gap: 0.3rem;
    align-items: center;
}

.filter-bar__clear { font-size: 0.9rem; }

/* Organization multi-select (#74): a <details> checkbox list over the
   shared popover panel. Long org names wrap instead of widening the
   panel past a phone's viewport, and a long list scrolls. */
.filter-bar__picker { position: relative; }

.filter-bar__picker > summary { list-style: none; }

.filter-bar__picker > summary::-webkit-details-marker { display: none; }

.filter-bar__panel {
    left: 0;
    width: max-content;
    max-width: min(20rem, 90vw);
    max-height: 15rem;
    overflow-y: auto;
}

.filter-bar__option {
    display: flex;
    gap: 0.45rem;
    align-items: flex-start;
    padding: 0.3rem 0.5rem;
    font-size: 0.9rem;
    line-height: 1.35;
    color: var(--ink);
    overflow-wrap: anywhere;
    cursor: pointer;
}

.filter-bar__option input { margin-top: 0.2rem; }

/* On a phone the picker takes its own row, so the panel opens flush
   with the form and can never hang off the side of the viewport. */
@media (max-width: 480px) {
    .filter-bar__picker { flex: 1 1 100%; }

    .filter-bar__panel {
        width: 100%;
        max-width: 100%;
    }
}

.jobs-count { margin: 0.3rem 0; }

.worker-banner {
    border-radius: 10px;
    padding: 0.5rem 0.8rem;
    margin: 0.6rem 0;
    font-size: 0.92rem;
}

.worker-banner--calm {
    background: var(--surface-2);
    border: 1px solid var(--border);
    color: var(--ink-2);
}

.worker-banner--ok {
    background: color-mix(in srgb, var(--ok) var(--tint-bg), transparent);
    border: 1px solid color-mix(in srgb, var(--ok) var(--tint-border), transparent);
}

.worker-banner--warn {
    background: color-mix(in srgb, var(--warn) var(--tint-bg), transparent);
    border: 1px solid color-mix(in srgb, var(--warn) var(--tint-border), transparent);
}

/* ---- Cases ------------------------------------------------------- */

.cases-list {
    display: flex;
    flex-direction: column;
    gap: 1rem;
    margin-top: 1rem;
}

.case-card {
    border: 1px solid var(--border);
    border-radius: 14px;
    background: var(--surface);
    padding: 1rem;
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
    box-shadow: var(--shadow);
}

/* The head row wraps: a long case name takes the first line on its own
   and the meta (owner · Complete · count) drops to the next, instead of
   the meta spans being shrunk to a character per line by the title
   (#127). The title stays the only flexible item — `min-width: 0` plus
   #62's `overflow-wrap: anywhere` let it wrap inside the card; the meta
   spans are short unbreakable tokens that never shrink. */
.case-card__head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.25rem 0.6rem;
}

.case-card__title { margin: 0; font-size: 1.05rem; }
.case-card__owner,
.case-card__count { white-space: nowrap; flex: 0 0 auto; }
.case-card__count { margin-left: auto; font-variant-numeric: tabular-nums; }
.case-card__deponents,
.case-card__window,
.case-card__hint { margin: 0; font-size: 0.95rem; }

.case-card__actions {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    flex-wrap: wrap;
}

/* #65: the card expands to its own report rows. The summary is the
   card's quiet control — muted, and the same 0.95rem as the deponents
   line above it. The rows are .job-row blocks, which already wrap and
   carry a vertical margin; inside a card they only need to stay
   shrinkable, so a 390px viewport keeps them within its padding. */
.case-card__reports > summary {
    cursor: pointer;
    font-size: 0.95rem;
}

.case-card__reports .report-card {
    min-width: 0;
    margin: 0.5rem 0 0;
}

/* ---- Billing / staff surfaces ------------------------------------ */

/* The billing month control (#80). Two bare accent glyphs read as
   decoration, not navigation — and the month, the thing you are
   actually looking at, was the dimmest text in the row. Now: a
   caption saying what the row steps, real buttons with chrome and a
   tappable target, the month prominent in body ink, and a fixed
   shape — on the current month the Next slot holds a disabled button
   (#70's inert style) beside the badge instead of vanishing. */
.month-nav {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.6rem;
    margin: 1rem 0;
}

/* <button> keeps the UA's line-height: normal while the anchors
   inherit the body's 1.5, which left the two controls 2px apart in the
   same row. Pin it so the pair is one control. */
.month-nav .btn { line-height: 1.5; }

.month-nav__caption {
    color: var(--ink-2);
    font-size: 0.75rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
}

.month-nav__label {
    color: var(--ink);
    font-size: 1.15rem;
    font-weight: 700;
}

.month-nav__jump { font-size: 0.85rem; }

/* The /usage window statement (#115): the same label the billing month
   nav shows, on a page with no stepper to hold it — one flex row so the
   org admin's scope toggle sits beside it. */
.usage-window {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.6rem;
    margin: 1rem 0;
}

.month-nav .badge {
    display: inline-block;
    background: var(--inset);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 0.15rem 0.5rem;
    color: var(--ink-2);
    font-size: 0.78rem;
    font-weight: 600;
}

.invoice-tree__user > summary {
    cursor: pointer;
    font-weight: 700;
    padding: 0.4rem 0;
}

.invoice-tree__case { margin: 0.3rem 0 0.6rem 1.2rem; }

.invoice-tree__case > summary {
    cursor: pointer;
    padding: 0.25rem 0;
}

.novo-block {
    background: var(--inset);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 0.6rem;
    font-size: 0.8rem;
    font-family: var(--font-mono);
    overflow-x: auto;
}

.settings-table td { vertical-align: top; }

.setting-description {
    font-size: 0.85rem;
    max-width: 28rem;
}

.setting-edit-form {
    display: flex;
    gap: 0.4rem;
    align-items: flex-start;
}

.setting-edit-form input[type="text"],
.setting-edit-form textarea {
    font-family: var(--font-mono);
    font-size: 0.9rem;
    width: 14rem;
}

.setting-edit-form textarea { width: 20rem; }

.settings-table .btn--small {
    padding: 0.35rem 0.7rem;
    font-size: 0.88rem;
}

.settings-table .btn--ghost { background: none; color: inherit; }

.setting-reset-form { margin-top: 0.4rem; }

/* Grouped sections + on-demand descriptions keep the page scannable (#98). */
.settings-group h2 {
    margin-top: 0;
    font-size: 1.05rem;
}

.setting-help > summary {
    cursor: pointer;
    font-size: 0.82rem;
    color: var(--ink-2);
}

.setting-help .setting-description { margin: 0.3rem 0 0; }

.setting-json {
    font-family: var(--font-mono);
    font-size: 0.8rem;
    margin: 0;
}

/* An overridden row wears a left accent so the eye finds it (#98). */
.settings-table tr.is-overridden > td:first-child {
    border-left: 3px solid var(--warn);
    padding-left: 0.55rem;
}

/* llm_prices edits as rows, not raw JSON (#97). */
.price-table { max-width: 23rem; }

/* Every track collapsible (`minmax(0, ...)`): fixed track minimums summed
   to a hard ~291px floor, so under ~370px of card the grid outgrew its
   `.table-scroll` and clipped the Remove column (#97). The fr ratios are
   unchanged, so wide viewports lay out exactly as before — only the floor
   is gone. `auto` on the last track keeps the Remove label whole. */
.price-row {
    display: grid;
    grid-template-columns: minmax(0, 1.6fr) minmax(0, 0.7fr) minmax(0, 0.7fr) auto;
    gap: 0.35rem;
    align-items: center;
    margin-bottom: 0.3rem;
}

.price-row--head {
    font-size: 0.75rem;
    color: var(--ink-2);
    text-transform: uppercase;
    letter-spacing: 0.06em;
}

.price-row input[type="text"] {
    font-family: var(--font-mono);
    font-size: 0.85rem;
    width: 100%;
    /* A grid item's automatic minimum is its content size; an input's is
       its `size` attribute. Without this the inputs would put the floor
       back that the collapsible tracks just removed. */
    min-width: 0;
}

.price-remove {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    font-size: 0.78rem;
}

.setting-price-form .btn { margin-top: 0.2rem; }

/* The settings table is exempt from the #126 max-content sizing: its rows
   are prose descriptions and full-width inputs, and below 861px it stops
   being a table at all (see the stacking media query below). */
.table-scroll > .settings-table { width: 100%; }

.settings-table th,
.settings-table td { white-space: normal; }

/* Narrow widths: the settings table stops behaving like a table and
   stacks — every row a labelled block, every control full width, so
   the value inputs and Save buttons stay on screen (#107). At >= 861px
   the table renders unchanged (the E2E spec matches rows by role at
   the default viewport). */
@media (max-width: 860px) {
    .settings-table thead { display: none; }

    .settings-table tr {
        display: block;
        border-bottom: 1px solid var(--border);
        padding: 0.6rem 0;
    }

    .settings-table td {
        display: block;
        border-bottom: none;
        padding: 0.25rem 0;
    }

    .settings-table td::before {
        content: attr(data-label);
        display: block;
        color: var(--ink-2);
        font-size: 0.78rem;
        text-transform: uppercase;
        letter-spacing: 0.06em;
    }

    .settings-table tr.is-overridden {
        border-left: 3px solid var(--warn);
        padding-left: 0.55rem;
    }

    .setting-edit-form { flex-wrap: wrap; }

    .setting-edit-form input[type="text"],
    .setting-edit-form select,
    .setting-edit-form textarea { width: 100%; }
}

.badge--override {
    display: inline-block;
    background: color-mix(in srgb, var(--warn) var(--tint-bg), transparent);
    border: 1px solid color-mix(in srgb, var(--warn) var(--tint-border), transparent);
    border-radius: 6px;
    padding: 0.05rem 0.4rem;
    font-size: 0.78rem;
    margin-left: 0.4rem;
}

.badge--inactive {
    display: inline-block;
    background: color-mix(in srgb, var(--danger) var(--tint-bg), transparent);
    border: 1px solid color-mix(in srgb, var(--danger) var(--tint-border), transparent);
    border-radius: 6px;
    padding: 0.05rem 0.4rem;
    font-size: 0.78rem;
    margin-left: 0.4rem;
}

.form-card { margin: 0.8rem 0; }

.form-card h2 {
    margin-top: 0;
    font-size: 1.1rem;
}

.flag-row {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 0.8rem;
    padding: 0.4rem 0;
    border-bottom: 1px solid var(--border);
}

.flag-row:last-child { border-bottom: none; }

.plain-list {
    list-style: none;
    padding: 0;
    margin: 0.6rem 0 0;
}

.permissions-summary {
    cursor: pointer;
    font-weight: 700;
}

.permissions-legend {
    margin: 0.6rem 0 0.2rem;
    padding-left: 1.1rem;
    font-size: 0.85rem;
    line-height: 1.5;
}

.permissions-table td:first-child {
    max-width: 34rem;
    white-space: normal;
    overflow-wrap: anywhere;
}

.permissions-table td:first-child .muted { font-size: 0.85rem; }

.permissions-table .radio-cell {
    display: flex;
    gap: 0.35rem;
    align-items: center;
    color: var(--ink-2);
    font-size: 0.9rem;
}

.events-table__ts { white-space: nowrap; }

.events-table__detail {
    background: var(--inset);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 0.5rem;
    font-size: 0.82rem;
    font-family: var(--font-mono);
    overflow-x: auto;
    margin: 0.3rem 0 0;
}

.pagination {
    display: flex;
    gap: 1rem;
    align-items: center;
    margin-top: 0.8rem;
}

/* ---- MFA --------------------------------------------------------- */

.mfa-qr {
    margin: 1rem 0;
    max-width: 240px;
    background: #ffffff;
    border-radius: 10px;
    padding: 0.6rem;
}

.mfa-qr svg {
    width: 100%;
    height: auto;
    display: block;
}

/* The code rows are `.inline-form` with a wider gap (#116): the input is
   autofocused, and the 2px focus ring at 2px offset eats 4px of the
   0.4rem default on each side — enough that the ring reads as touching
   the label and the submit button. */
.mfa-code-form { gap: 0.6rem; }

.mfa-recovery-codes {
    font-family: var(--font-mono);
    font-size: 1rem;
    letter-spacing: 0.06em;
    line-height: 1.8;
    background: var(--inset);
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 0.8rem 1rem;
    user-select: all;
}

/* ---- Confirmation dialog ----------------------------------------- */
/* Native <dialog>, centred by the UA; app.js opens it with showModal.
   Card surface + tokens so it reads as one of our surfaces in both
   themes (#76). */

.dialog {
    background: var(--surface);
    color: var(--ink);
    border: 1px solid var(--border);
    border-radius: 14px;
    box-shadow: var(--shadow);
    padding: 1.5rem;
    max-width: 28rem;
    width: calc(100% - 2rem);
}

/* A literal scrim, not a token: ::backdrop only inherits custom
   properties from the dialog in recent engines, so a var() here would
   resolve to nothing (and dim nothing) elsewhere. */
.dialog::backdrop {
    background: rgb(0 0 0 / 0.5);
}

.dialog__title {
    margin: 0 0 0.6rem;
    font-size: 1.15rem;
}

.dialog p {
    color: var(--ink-2);
    margin: 0 0 1rem;
}

.dialog textarea {
    width: 100%;
    font-size: 0.95rem;
}

.dialog__actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: 0.6rem;
    margin-top: 1.2rem;
}
