/* ============================================================================
   Batch Web Portal — design system

   One stylesheet, no framework. Bootstrap is still linked for the scaffolded
   Identity pages, but nothing in the portal shell depends on it.

   Palette is a cool neutral grey with a single restrained accent, chosen to sit
   behind dense operational data (job tables, terminal output, status pills)
   without competing with it. Light and dark are both first-class: the operator
   watching a run at night gets dark, the person reading a report gets light.
   ============================================================================ */

:root {
    /* Surfaces, back to front */
    --surface-sunken: #eef0f4;
    --surface-base: #f6f7f9;
    --surface-raised: #ffffff;
    --surface-overlay: #ffffff;
    --surface-inset: #f0f2f6;

    /* Ink */
    --text-primary: #14171f;
    --text-secondary: #4a5163;
    --text-muted: #767e93;
    --text-inverted: #ffffff;

    /* Lines */
    --border: #dde1e9;
    --border-strong: #c6ccd9;

    /* Accent — one hue, used sparingly so it still means something */
    --accent: #2f5fd8;
    --accent-hover: #2850bb;
    --accent-soft: #e8eefc;
    --accent-border: #bdd0f7;

    /* Status */
    --ok: #17794a;
    --ok-soft: #e2f4ea;
    --ok-border: #a9dcc2;
    --warn: #9a6206;
    --warn-soft: #fdf1dc;
    --warn-border: #f0d19a;
    --danger: #bb2b2b;
    --danger-soft: #fdeaea;
    --danger-border: #f3bcbc;
    --info: #1f6a86;
    --info-soft: #e2f1f6;
    --info-border: #a9d4e2;
    --idle: #6b7285;
    --idle-soft: #eef0f4;
    --idle-border: #d3d8e2;

    /* Terminal — always dark, in both themes. A console that turns white
       reads as a document; this one must read as a machine. */
    --term-bg: #14161c;
    --term-fg: #d7dbe4;
    --term-muted: #767e93;
    --term-err: #f08a8a;
    --term-accent: #7fa9f5;

    --radius-sm: 5px;
    --radius: 8px;
    --radius-lg: 12px;

    --shadow-sm: 0 1px 2px rgba(16, 20, 30, .06);
    --shadow: 0 2px 8px rgba(16, 20, 30, .08);
    --shadow-lg: 0 12px 32px rgba(16, 20, 30, .16);

    --sidebar-width: 232px;
    --header-height: 56px;

    --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, Roboto,
                 "Helvetica Neue", Arial, sans-serif;
    --font-mono: "Cascadia Mono", "JetBrains Mono", Consolas, "SF Mono",
                 "Liberation Mono", Menlo, monospace;
}

@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
        --surface-sunken: #0d0f14;
        --surface-base: #14171d;
        --surface-raised: #1a1e26;
        --surface-overlay: #21262f;
        --surface-inset: #10131a;

        --text-primary: #e8eaf0;
        --text-secondary: #a8b0c0;
        --text-muted: #767e93;
        --text-inverted: #14171f;

        --border: #2a303b;
        --border-strong: #3a4250;

        --accent: #6d99f5;
        --accent-hover: #86abf8;
        --accent-soft: #1b2438;
        --accent-border: #2f4270;

        --ok: #5cc48d;
        --ok-soft: #12241b;
        --ok-border: #245039;
        --warn: #e0a94a;
        --warn-soft: #251d0e;
        --warn-border: #4d3c17;
        --danger: #f0817f;
        --danger-soft: #2a1517;
        --danger-border: #56272a;
        --info: #67b6d4;
        --info-soft: #10222a;
        --info-border: #234a58;
        --idle: #8b93a6;
        --idle-soft: #1d222b;
        --idle-border: #333b48;

        --shadow-sm: 0 1px 2px rgba(0, 0, 0, .4);
        --shadow: 0 2px 10px rgba(0, 0, 0, .45);
        --shadow-lg: 0 14px 40px rgba(0, 0, 0, .6);
    }
}

:root[data-theme="dark"] {
    --surface-sunken: #0d0f14;
    --surface-base: #14171d;
    --surface-raised: #1a1e26;
    --surface-overlay: #21262f;
    --surface-inset: #10131a;

    --text-primary: #e8eaf0;
    --text-secondary: #a8b0c0;
    --text-muted: #767e93;
    --text-inverted: #14171f;

    --border: #2a303b;
    --border-strong: #3a4250;

    --accent: #6d99f5;
    --accent-hover: #86abf8;
    --accent-soft: #1b2438;
    --accent-border: #2f4270;

    --ok: #5cc48d;
    --ok-soft: #12241b;
    --ok-border: #245039;
    --warn: #e0a94a;
    --warn-soft: #251d0e;
    --warn-border: #4d3c17;
    --danger: #f0817f;
    --danger-soft: #2a1517;
    --danger-border: #56272a;
    --info: #67b6d4;
    --info-soft: #10222a;
    --info-border: #234a58;
    --idle: #8b93a6;
    --idle-soft: #1d222b;
    --idle-border: #333b48;

    --shadow-sm: 0 1px 2px rgba(0, 0, 0, .4);
    --shadow: 0 2px 10px rgba(0, 0, 0, .45);
    --shadow-lg: 0 14px 40px rgba(0, 0, 0, .6);
}

/* ---------------------------------------------------------------- base ---- */

*, *::before, *::after { box-sizing: border-box; }

html, body {
    margin: 0;
    padding: 0;
    height: 100%;
}

body {
    font-family: var(--font-sans);
    font-size: 14px;
    line-height: 1.55;
    color: var(--text-primary);
    background: var(--surface-base);
    -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 {
    margin: 0 0 .5rem;
    font-weight: 620;
    letter-spacing: -.011em;
    line-height: 1.25;
}

h1 { font-size: 1.5rem; }
h2 { font-size: 1.15rem; }
h3 { font-size: 1rem; }

p { margin: 0 0 .75rem; }

a {
    color: var(--accent);
    text-decoration: none;
}

a:hover { text-decoration: underline; }

code, pre, .mono { font-family: var(--font-mono); font-size: .86em; }

hr {
    border: 0;
    border-top: 1px solid var(--border);
    margin: 1.25rem 0;
}

::selection { background: var(--accent-soft); }

/* -------------------------------------------------------------- layout ---- */

.portal {
    display: grid;
    grid-template-columns: var(--sidebar-width) 1fr;
    min-height: 100vh;
}

.sidebar {
    position: sticky;
    top: 0;
    height: 100vh;
    display: flex;
    flex-direction: column;
    background: var(--surface-sunken);
    border-right: 1px solid var(--border);
    overflow-y: auto;
}

.sidebar-brand {
    display: flex;
    align-items: center;
    gap: .625rem;
    height: var(--header-height);
    padding: 0 1rem;
    flex-shrink: 0;
    border-bottom: 1px solid var(--border);
}

.brand-mark {
    width: 26px;
    height: 26px;
    border-radius: 6px;
    background: linear-gradient(135deg, var(--accent), #7b4fd6);
    color: #fff;
    display: grid;
    place-items: center;
    font-weight: 700;
    font-size: 13px;
    flex-shrink: 0;
}

.brand-text {
    font-weight: 640;
    letter-spacing: -.015em;
    font-size: .95rem;
}

.brand-sub {
    display: block;
    font-size: .68rem;
    font-weight: 500;
    color: var(--text-muted);
    letter-spacing: .02em;
    text-transform: uppercase;
    line-height: 1;
    margin-top: 1px;
}

.nav-section {
    padding: .875rem .625rem .25rem;
}

.nav-section-title {
    font-size: .68rem;
    font-weight: 620;
    text-transform: uppercase;
    letter-spacing: .06em;
    color: var(--text-muted);
    padding: 0 .5rem .3rem;
}

.nav-link {
    display: flex;
    align-items: center;
    gap: .55rem;
    padding: .44rem .55rem;
    margin-bottom: 1px;
    border-radius: var(--radius-sm);
    color: var(--text-secondary);
    font-weight: 500;
    font-size: .875rem;
    text-decoration: none;
    transition: background .12s ease, color .12s ease;
}

.nav-link:hover {
    background: var(--surface-inset);
    color: var(--text-primary);
    text-decoration: none;
}

.nav-link.active {
    background: var(--accent-soft);
    color: var(--accent);
    font-weight: 580;
}

.nav-link .ico {
    width: 16px;
    height: 16px;
    flex-shrink: 0;
    opacity: .85;
}

.nav-badge {
    margin-left: auto;
    font-size: .7rem;
    font-weight: 620;
    padding: .05rem .34rem;
    border-radius: 999px;
    background: var(--accent);
    color: #fff;
    min-width: 18px;
    text-align: center;
}

.sidebar-footer {
    margin-top: auto;
    padding: .625rem;
    border-top: 1px solid var(--border);
}

.main {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.topbar {
    height: var(--header-height);
    flex-shrink: 0;
    display: flex;
    align-items: center;
    gap: 1rem;
    padding: 0 1.5rem;
    background: var(--surface-raised);
    border-bottom: 1px solid var(--border);
    position: sticky;
    top: 0;
    z-index: 20;
}

.topbar-title {
    font-weight: 600;
    font-size: .95rem;
    letter-spacing: -.01em;
}

.topbar-actions {
    margin-left: auto;
    display: flex;
    align-items: center;
    gap: .5rem;
}

.content {
    flex: 1;
    padding: 1.5rem;
    max-width: 1500px;
    width: 100%;
}

.content.flush {
    padding: 0;
    max-width: none;
    display: flex;
    min-height: 0;
}

.page-head {
    display: flex;
    align-items: flex-start;
    gap: 1rem;
    margin-bottom: 1.25rem;
}

.page-head p {
    color: var(--text-secondary);
    margin: .15rem 0 0;
    max-width: 68ch;
}

.page-head-actions { margin-left: auto; display: flex; gap: .5rem; flex-shrink: 0; }

/* --------------------------------------------------------------- cards ---- */

.card {
    background: var(--surface-raised);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-sm);
}

.card-head {
    display: flex;
    align-items: center;
    gap: .625rem;
    padding: .75rem 1rem;
    border-bottom: 1px solid var(--border);
}

.card-head h2, .card-head h3 { margin: 0; font-size: .9rem; font-weight: 620; }
.card-head .spacer { margin-left: auto; }
/* Explanatory line under a card heading, for text that describes the whole table below it. */
.card-note { padding: .625rem 1rem 0; }
.card-body { padding: 1rem; }
.card-body.tight { padding: .625rem; }

.grid {
    display: grid;
    gap: 1rem;
}

.grid.cols-2 { grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); }
.grid.cols-3 { grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); }
.grid.cols-4 { grid-template-columns: repeat(auto-fit, minmax(185px, 1fr)); }

/* ----------------------------------------------------------- stat tiles ---- */

.stat {
    background: var(--surface-raised);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: .875rem 1rem;
    box-shadow: var(--shadow-sm);
}

.stat-label {
    font-size: .72rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: var(--text-muted);
}

.stat-value {
    font-size: 1.75rem;
    font-weight: 640;
    letter-spacing: -.02em;
    line-height: 1.15;
    margin-top: .15rem;
    font-variant-numeric: tabular-nums;
}

.stat-note {
    font-size: .78rem;
    color: var(--text-secondary);
    margin-top: .1rem;
}

.stat.accent .stat-value { color: var(--accent); }
.stat.ok .stat-value { color: var(--ok); }
.stat.warn .stat-value { color: var(--warn); }
.stat.danger .stat-value { color: var(--danger); }

/* --------------------------------------------------------------- pills ---- */

.pill {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    padding: .1rem .45rem;
    border-radius: 999px;
    font-size: .74rem;
    font-weight: 600;
    line-height: 1.5;
    white-space: nowrap;
    border: 1px solid transparent;
}

.pill::before {
    content: "";
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: currentColor;
    flex-shrink: 0;
}

.pill.no-dot::before { display: none; }

.pill-ok      { color: var(--ok);     background: var(--ok-soft);     border-color: var(--ok-border); }
.pill-warn    { color: var(--warn);   background: var(--warn-soft);   border-color: var(--warn-border); }
.pill-danger  { color: var(--danger); background: var(--danger-soft); border-color: var(--danger-border); }
.pill-info    { color: var(--info);   background: var(--info-soft);   border-color: var(--info-border); }
.pill-accent  { color: var(--accent); background: var(--accent-soft); border-color: var(--accent-border); }
.pill-idle    { color: var(--idle);   background: var(--idle-soft);   border-color: var(--idle-border); }

.pill.running::before { animation: pulse 1.4s ease-in-out infinite; }

@keyframes pulse {
    0%, 100% { opacity: 1; }
    50% { opacity: .25; }
}

@media (prefers-reduced-motion: reduce) {
    .pill.running::before { animation: none; }
}

/* ------------------------------------------------------------- buttons ---- */

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: .4rem;
    padding: .4rem .75rem;
    border-radius: var(--radius-sm);
    border: 1px solid var(--border-strong);
    background: var(--surface-raised);
    color: var(--text-primary);
    font-family: inherit;
    font-size: .85rem;
    font-weight: 550;
    cursor: pointer;
    transition: background .12s ease, border-color .12s ease, opacity .12s ease;
    text-decoration: none;
    white-space: nowrap;
}

.btn:hover { background: var(--surface-inset); text-decoration: none; }

.btn:disabled, .btn[disabled] {
    opacity: .5;
    cursor: not-allowed;
}

.btn-primary {
    background: var(--accent);
    border-color: var(--accent);
    color: #fff;
}

.btn-primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); }

.btn-danger {
    background: var(--danger);
    border-color: var(--danger);
    color: #fff;
}

.btn-ghost {
    background: transparent;
    border-color: transparent;
    color: var(--text-secondary);
}

.btn-ghost:hover { background: var(--surface-inset); color: var(--text-primary); }

.btn-sm { padding: .22rem .5rem; font-size: .78rem; }
.btn-lg { padding: .55rem 1.1rem; font-size: .9rem; }
.btn-block { width: 100%; }

/* --------------------------------------------------------------- forms ---- */

.field { margin-bottom: .875rem; }

.field label, .form-label {
    display: block;
    font-size: .8rem;
    font-weight: 600;
    color: var(--text-secondary);
    margin-bottom: .25rem;
}

.field-hint {
    font-size: .78rem;
    color: var(--text-muted);
    margin-top: .25rem;
}

input[type="text"], input[type="email"], input[type="password"], input[type="number"],
input[type="search"], select, textarea, .form-control, .form-select {
    width: 100%;
    padding: .42rem .6rem;
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    background: var(--surface-raised);
    color: var(--text-primary);
    font-family: inherit;
    font-size: .875rem;
    transition: border-color .12s ease, box-shadow .12s ease;
}

textarea { resize: vertical; min-height: 72px; line-height: 1.5; }

input:focus, select:focus, textarea:focus,
.form-control:focus, .form-select:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
}

input::placeholder, textarea::placeholder { color: var(--text-muted); }

.row-2 {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: .75rem;
}

/* A run of related actions inside one table cell — wrapped so a narrow window grows the
   row instead of pushing the buttons off the edge of the table. */
.btn-row {
    display: flex;
    flex-wrap: wrap;
    gap: .35rem;
    justify-content: flex-end;
}

.checkline {
    display: flex;
    align-items: center;
    gap: .45rem;
    font-size: .85rem;
    color: var(--text-secondary);
}

.checkline input { width: auto; }

/* -------------------------------------------------------------- tables ---- */

.table-wrap {
    overflow-x: auto;
    border-radius: var(--radius);
}

table.data {
    width: 100%;
    border-collapse: collapse;
    font-size: .855rem;
}

table.data th {
    text-align: left;
    font-size: .72rem;
    font-weight: 620;
    text-transform: uppercase;
    letter-spacing: .045em;
    color: var(--text-muted);
    padding: .5rem .75rem;
    border-bottom: 1px solid var(--border);
    white-space: nowrap;
    background: var(--surface-raised);
    position: sticky;
    top: 0;
}

table.data td {
    padding: .5rem .75rem;
    border-bottom: 1px solid var(--border);
    vertical-align: middle;
}

table.data tbody tr:last-child td { border-bottom: 0; }
table.data tbody tr:hover { background: var(--surface-inset); }
table.data td.num { text-align: right; font-variant-numeric: tabular-nums; }
table.data td.shrink { width: 1%; white-space: nowrap; }

.empty {
    padding: 2.5rem 1rem;
    text-align: center;
    color: var(--text-muted);
}

.empty-title {
    font-weight: 600;
    color: var(--text-secondary);
    margin-bottom: .2rem;
}

/* ------------------------------------------------------------ terminal ---- */

.terminal {
    background: var(--term-bg);
    color: var(--term-fg);
    border-radius: var(--radius);
    border: 1px solid #2a303b;
    font-family: var(--font-mono);
    font-size: 12.5px;
    line-height: 1.55;
    padding: .75rem .875rem;
    overflow: auto;
    white-space: pre-wrap;
    word-break: break-word;
    min-height: 240px;
    max-height: 60vh;
}

.terminal .t-time { color: var(--term-muted); }
.terminal .t-err { color: var(--term-err); }
.terminal .t-portal { color: var(--term-accent); }

.terminal-head {
    display: flex;
    align-items: center;
    gap: .5rem;
    padding: .4rem .75rem;
    background: #1d222b;
    border: 1px solid #2a303b;
    border-bottom: 0;
    border-radius: var(--radius) var(--radius) 0 0;
    font-family: var(--font-mono);
    font-size: .75rem;
    color: var(--term-muted);
}

.terminal-head + .terminal { border-radius: 0 0 var(--radius) var(--radius); }

/* ---------------------------------------------------------------- chat ---- */

.chat {
    display: grid;
    grid-template-columns: 250px 1fr;
    flex: 1;
    min-height: 0;
    width: 100%;
}

.chat-list {
    border-right: 1px solid var(--border);
    background: var(--surface-sunken);
    display: flex;
    flex-direction: column;
    min-height: 0;
}

.chat-list-head { padding: .625rem; border-bottom: 1px solid var(--border); }
.chat-list-items { overflow-y: auto; padding: .375rem; flex: 1; }

.chat-item {
    display: block;
    padding: .45rem .55rem;
    border-radius: var(--radius-sm);
    color: var(--text-secondary);
    font-size: .84rem;
    cursor: pointer;
    text-decoration: none;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    border: 0;
    background: transparent;
    width: 100%;
    text-align: left;
    font-family: inherit;
}

/* The row owns hover and selection so the delete control can sit inside it without the
   title button and the delete button highlighting separately. */
.chat-row {
    display: flex;
    align-items: center;
    border-radius: var(--radius-sm);
}

.chat-row:hover { background: var(--surface-inset); }
.chat-row:hover .chat-item { color: var(--text-primary); }
.chat-row.active { background: var(--accent-soft); }
.chat-row.active .chat-item { color: var(--accent); font-weight: 560; }

.chat-item:hover { color: var(--text-primary); text-decoration: none; }

/* Hidden until the row is hovered or focused: a delete affordance on every row at all times
   invites the mis-click it is guarding against. Focus-within keeps it reachable by keyboard. */
.chat-delete {
    flex-shrink: 0;
    width: 22px;
    height: 22px;
    margin-right: .3rem;
    padding: 0;
    display: grid;
    place-items: center;
    border: 0;
    border-radius: 4px;
    background: transparent;
    color: var(--text-muted);
    cursor: pointer;
    opacity: 0;
    transition: opacity .12s ease, color .12s ease, background .12s ease;
}

.chat-row:hover .chat-delete,
.chat-row:focus-within .chat-delete { opacity: 1; }

.chat-delete:hover { background: var(--danger-soft); color: var(--danger); }
.chat-delete svg { width: 13px; height: 13px; }
.chat-item-time { display: block; font-size: .7rem; color: var(--text-muted); font-weight: 400; }

.chat-main {
    display: flex;
    flex-direction: column;
    min-height: 0;
    background: var(--surface-base);
}

.chat-scroll {
    flex: 1;
    overflow-y: auto;
    padding: 1.25rem;
}

.chat-thread { max-width: 820px; margin: 0 auto; }

.msg { display: flex; gap: .75rem; margin-bottom: 1.25rem; }

.msg-avatar {
    width: 26px;
    height: 26px;
    border-radius: 6px;
    flex-shrink: 0;
    display: grid;
    place-items: center;
    font-size: .7rem;
    font-weight: 700;
    color: #fff;
}

.msg-user .msg-avatar { background: var(--idle); }
.msg-assistant .msg-avatar { background: linear-gradient(135deg, var(--accent), #7b4fd6); }

.msg-body { min-width: 0; flex: 1; }

.msg-role {
    font-size: .75rem;
    font-weight: 620;
    color: var(--text-muted);
    margin-bottom: .15rem;
}

.msg-content {
    white-space: pre-wrap;
    word-break: break-word;
    color: var(--text-primary);
}

.msg-error {
    margin-top: .4rem;
    font-size: .8rem;
    color: var(--danger);
}

/* The citation line under an assistant reply or a job detail card, showing which uploaded
   document the answer drew on. Muted, because it is provenance rather than part of the answer. */
.msg-sources {
    margin-top: .45rem;
    padding-top: .4rem;
    border-top: 1px dashed var(--border);
    font-size: .78rem;
    color: var(--text-muted);
    white-space: normal;
}

.chat-composer {
    border-top: 1px solid var(--border);
    background: var(--surface-raised);
    padding: .75rem 1.25rem 1rem;
}

.composer-inner { max-width: 820px; margin: 0 auto; }

/* Uploaded-knowledge attachments — same picker markup on Chat and New job. */

.attach-chips {
    display: flex;
    flex-wrap: wrap;
    gap: .4rem;
    margin-bottom: .5rem;
}

.attach-chip {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    padding: .2rem .3rem .2rem .55rem;
    border: 1px solid var(--border-strong);
    border-radius: 999px;
    background: var(--surface-raised);
    font-size: .78rem;
}

.attach-chip button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.1rem;
    height: 1.1rem;
    padding: 0;
    border: 0;
    border-radius: 999px;
    background: var(--idle-soft);
    color: var(--text-muted);
    cursor: pointer;
    font-size: .7rem;
    line-height: 1;
}

.attach-chip button:hover { background: var(--danger-soft); color: var(--danger); }

.attach-panel {
    margin-bottom: .5rem;
    border: 1px solid var(--border-strong);
    border-radius: var(--radius);
    background: var(--surface-raised);
    padding: .6rem;
}

.attach-tabs {
    display: flex;
    gap: .3rem;
    margin-bottom: .5rem;
    flex-wrap: wrap;
}

.attach-tab {
    padding: .25rem .6rem;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: transparent;
    font-size: .78rem;
    cursor: pointer;
}

.attach-tab.active { background: var(--accent-soft); border-color: var(--accent-border); color: var(--accent); }

.attach-list {
    max-height: 180px;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    gap: .15rem;
    margin-bottom: .5rem;
}

.attach-list-item {
    display: flex;
    align-items: center;
    gap: .4rem;
    padding: .25rem .3rem;
    border-radius: var(--radius-sm);
    font-size: .8rem;
}

.attach-list-item:hover { background: var(--surface); }
.attach-list-item input { width: auto; }

.composer-box {
    display: flex;
    gap: .5rem;
    align-items: flex-end;
    border: 1px solid var(--border-strong);
    border-radius: var(--radius);
    background: var(--surface-raised);
    padding: .375rem .375rem .375rem .625rem;
    transition: border-color .12s ease, box-shadow .12s ease;
}

.composer-box:focus-within {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
}

.composer-box textarea {
    border: 0;
    background: transparent;
    padding: .3rem 0;
    min-height: 24px;
    max-height: 200px;
    resize: none;
}

.composer-box textarea:focus { box-shadow: none; }

.composer-meta {
    display: flex;
    align-items: center;
    gap: .5rem;
    margin-top: .4rem;
    font-size: .76rem;
    color: var(--text-muted);
}

.composer-meta select {
    width: auto;
    padding: .1rem .35rem;
    font-size: .76rem;
    border-radius: var(--radius-sm);
}

.typing-dot {
    display: inline-block;
    width: 7px;
    height: 14px;
    background: var(--text-primary);
    vertical-align: text-bottom;
    animation: blink 1s steps(2, start) infinite;
}

@keyframes blink { to { visibility: hidden; } }

/* -------------------------------------------------------------- alerts ---- */

.alert {
    padding: .625rem .875rem;
    border-radius: var(--radius-sm);
    border: 1px solid;
    font-size: .865rem;
    margin-bottom: 1rem;
}

.alert-info    { background: var(--info-soft);   border-color: var(--info-border);   color: var(--info); }
.alert-warn    { background: var(--warn-soft);   border-color: var(--warn-border);   color: var(--warn); }
.alert-danger  { background: var(--danger-soft); border-color: var(--danger-border); color: var(--danger); }
.alert-ok      { background: var(--ok-soft);     border-color: var(--ok-border);     color: var(--ok); }

.alert strong { font-weight: 650; }

/* ------------------------------------------- one-time enrollment token ---- */

/* A read-only <code> box, not an <input>: the browser must never offer to autofill or "save"
   a one-time secret. */
.token-copy {
    display: flex;
    align-items: flex-start;
    flex-wrap: wrap;
    gap: .5rem;
}

.token-value {
    flex: 1 1 18rem;
    min-width: 0;
    padding: .45rem .6rem;
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    background: var(--surface-raised);
    /* The whole token has to stay visible, so it wraps rather than overflowing. */
    word-break: break-all;
    /* Deliberately NOT user-select:all. It reads as a convenience but makes a partial
       drag-selection impossible and swallows any stray click as a whole-block selection —
       which is exactly the awkward selection the copy button exists to remove. Plain text
       selection, and the button for the one-click case. */
    user-select: text;
    cursor: text;
}

.token-copy-btn { flex: 0 0 auto; }

/* Inline confirmation row shown under a worker while a token regeneration is armed. A row
   rather than a modal dialog: this portal has no JS dialog of its own, and window.confirm is
   a browser-chrome dependency the page does not otherwise take. */
.confirm-row td {
    background: var(--surface-inset);
    border-top: 1px solid var(--border);
}

/* ------------------------------------------------------------- helpers ---- */

.muted { color: var(--text-muted); }
.secondary { color: var(--text-secondary); }
.small { font-size: .8rem; }
.nowrap { white-space: nowrap; }
.tabular { font-variant-numeric: tabular-nums; }
.flex { display: flex; }
.flex-center { display: flex; align-items: center; }
.gap-sm { gap: .375rem; }
.gap { gap: .75rem; }
.mt { margin-top: 1rem; }
.mt-sm { margin-top: .5rem; }
.mb { margin-bottom: 1rem; }
.ml-auto { margin-left: auto; }
.stack > * + * { margin-top: .75rem; }

.user-chip {
    display: flex;
    align-items: center;
    gap: .5rem;
    padding: .4rem .5rem;
    border-radius: var(--radius-sm);
    font-size: .83rem;
    color: var(--text-secondary);
}

.user-avatar {
    width: 24px;
    height: 24px;
    border-radius: 50%;
    background: var(--accent-soft);
    color: var(--accent);
    display: grid;
    place-items: center;
    font-weight: 650;
    font-size: .72rem;
    flex-shrink: 0;
}

/* Identity scaffolding lives outside the portal shell — give it a centred card
   instead of the raw full-width Bootstrap form the template ships. */
.auth-shell {
    min-height: 100vh;
    display: grid;
    place-items: center;
    padding: 2rem 1rem;
    background: var(--surface-sunken);
}

.auth-card {
    width: 100%;
    max-width: 420px;
    background: var(--surface-raised);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    padding: 1.75rem;
}

.auth-brand {
    display: flex;
    align-items: center;
    gap: .625rem;
    margin-bottom: 1.25rem;
}

.auth-card h1 { font-size: 1.2rem; }
.auth-card .btn-primary { width: 100%; }

.validation-message, .text-danger {
    color: var(--danger);
    font-size: .8rem;
}

@media (max-width: 900px) {
    .portal { grid-template-columns: 1fr; }
    .sidebar { position: fixed; z-index: 50; transform: translateX(-100%); }
    .chat { grid-template-columns: 1fr; }
    .chat-list { display: none; }
}

/* ------------------------------------------------------- rendered markdown ---- */
/* Assistant replies are markdown. Scoped under .markdown so the user's own
   plain-text bubbles are untouched, and tuned tight: chat is a conversation,
   not a document, so headings and blocks stay close to body size. */

.msg-content.markdown { white-space: normal; }

.markdown > *:first-child { margin-top: 0; }
.markdown > *:last-child { margin-bottom: 0; }

.markdown p { margin: 0 0 .6rem; }

.markdown h1, .markdown h2, .markdown h3,
.markdown h4, .markdown h5, .markdown h6 {
    margin: 1rem 0 .4rem;
    font-weight: 640;
    line-height: 1.3;
}

.markdown h1 { font-size: 1.15rem; }
.markdown h2 { font-size: 1.05rem; }
.markdown h3 { font-size: .97rem; }
.markdown h4, .markdown h5, .markdown h6 { font-size: .92rem; }

.markdown ul, .markdown ol { margin: 0 0 .6rem; padding-left: 1.35rem; }
.markdown li { margin-bottom: .2rem; }
.markdown li > ul, .markdown li > ol { margin-top: .2rem; margin-bottom: .2rem; }

.markdown a { color: var(--accent); text-decoration: underline; }

.markdown strong { font-weight: 650; }

/* Inline code sits inside running text, so it must not shift the line box. */
.markdown code {
    font-family: var(--font-mono);
    font-size: .86em;
    background: var(--surface-inset);
    border: 1px solid var(--border);
    border-radius: 4px;
    padding: .05rem .28rem;
}

/* Fenced blocks get the terminal treatment: dark in both themes, horizontally
   scrollable so a long EasyLanguage line never widens the chat column. */
.markdown pre {
    margin: 0 0 .6rem;
    padding: .65rem .8rem;
    background: var(--term-bg);
    border: 1px solid #2a303b;
    border-radius: var(--radius);
    overflow-x: auto;
}

.markdown pre code {
    background: none;
    border: 0;
    padding: 0;
    color: var(--term-fg);
    font-size: 12.5px;
    line-height: 1.5;
    white-space: pre;
}

.markdown blockquote {
    margin: 0 0 .6rem;
    padding: .1rem 0 .1rem .75rem;
    border-left: 3px solid var(--border-strong);
    color: var(--text-secondary);
}

.markdown table {
    width: auto;
    max-width: 100%;
    border-collapse: collapse;
    margin: 0 0 .6rem;
    font-size: .85rem;
    display: block;
    overflow-x: auto;
}

.markdown th, .markdown td {
    border: 1px solid var(--border);
    padding: .3rem .5rem;
    text-align: left;
}

.markdown th { background: var(--surface-inset); font-weight: 620; }

.markdown hr {
    border: 0;
    border-top: 1px solid var(--border);
    margin: .8rem 0;
}

.markdown img { max-width: 100%; height: auto; }

/* White pad so a dark theme does not invert the modules a phone camera has to read. */
.authenticator-qr {
    display: inline-block;
    margin: .75rem 0 1rem;
    padding: 12px;
    background: #fff;
    border-radius: var(--radius-sm);
    line-height: 0;
}

.authenticator-qr svg {
    display: block;
    width: 168px;
    height: 168px;
}
