﻿
a, .btn-link {
    color: var(--rl-accent);
}

.content {
    padding-top: 0;
}

h1:focus {
    outline: none;
}

.valid.modified:not([type=checkbox]) {
    outline: 1px solid var(--rl-ok);
}

.invalid {
    outline: 1px solid var(--rl-danger);
}

.validation-message {
    color: var(--rl-danger);
}

.blazor-error-boundary {
    background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
    padding: 1rem 1rem 1rem 3.7rem;
    color: white;
}

    .blazor-error-boundary::after {
        content: "An error has occurred."
    }

.darker-border-checkbox.form-check-input {
    border-color: #929292;
}

/* ---- Ripllm design tokens -------------------------------------------------
   Defined on bare :root for light, then redefined for dark. Every colour has a
   light definition here so nothing depends on a media query resolving.
--------------------------------------------------------------------------- */
:root {
    /* Let the browser paint its own widgets - scrollbars, select popups, number
       spinners - in the same scheme as the tokens below. */
    color-scheme: light dark;

    --rl-bg: #ffffff;
    --rl-bg-rgb: 255, 255, 255;
    --rl-surface: #ffffff;
    --rl-surface-raised: #f5f6f8;
    --rl-surface-sunken: #eceef2;
    --rl-text: #16181d;
    --rl-text-rgb: 22, 24, 29;
    --rl-text-muted: #5b6472;
    --rl-border: #dfe2e8;
    --rl-accent: #2f6df6;
    --rl-accent-contrast: #ffffff;
    --rl-bubble-user: #2f6df6;
    --rl-bubble-user-text: #ffffff;
    --rl-danger: #b3261e;
    --rl-ok: #1a7f4b;
    /* Chart series, in the order a legend hands them out. Picked to stay apart from each
       other for the common kinds of colour blindness, and to sit on either surface. */
    --rl-chart-1: #2f6df6;
    --rl-chart-2: #12a594;
    --rl-chart-3: #c2410c;
    --rl-chart-4: #8b5cf6;
    --rl-chart-5: #b45309;
    --rl-chart-grid: #e3e6ec;
    --rl-radius: 12px;

    /* One speed and one curve for every hover, press, open and close, so the app moves as one thing. The
       reduced-motion block at the end of this file turns all of it off. */
    --rl-dur: 0.16s;
    --rl-ease: cubic-bezier(0.2, 0.8, 0.2, 1);

    /* The face warnings keep, and the face everything else starts from until a person picks one. */
    --rl-font-fallback: 'Helvetica Neue', Helvetica, Arial, sans-serif;
    --rl-font: var(--rl-font-fallback);
    --rl-font-size: 16px;
    --rl-bg-image: none;

    /* Bootstrap ships a light theme and only switches on [data-bs-theme].
       Its components set their own colour from these - .card and .table both do -
       so without this bridge a dark page gets near-black text inside every card.
       Written once: the vars resolve through the tokens above, which the dark
       block below redefines. */
    --bs-body-color: var(--rl-text);
    --bs-body-color-rgb: var(--rl-text-rgb);
    --bs-emphasis-color: var(--rl-text);
    --bs-emphasis-color-rgb: var(--rl-text-rgb);
    --bs-secondary-color: var(--rl-text-muted);
    --bs-body-bg: var(--rl-bg);
    --bs-body-bg-rgb: var(--rl-bg-rgb);
    --bs-border-color: var(--rl-border);
    --bs-link-color: var(--rl-accent);
    --bs-link-hover-color: var(--rl-accent);
    --bs-heading-color: inherit;
}

@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
        --rl-bg: #14161a;
        --rl-bg-rgb: 20, 22, 26;
        --rl-surface: #1a1d23;
        --rl-surface-raised: #1f232a;
        --rl-surface-sunken: #24282f;
        --rl-text: #e8eaee;
        --rl-text-rgb: 232, 234, 238;
        --rl-text-muted: #9aa3b2;
        --rl-border: #2c313a;
        --rl-accent: #6f9bff;
        --rl-accent-contrast: #0d1017;
        --rl-bubble-user: #2f5bb7;
        --rl-bubble-user-text: #ffffff;
        --rl-danger: #ff8f87;
        --rl-ok: #5bd08c;
        --rl-chart-1: #6f9bff;
        --rl-chart-2: #35d3bd;
        --rl-chart-3: #ff9f5e;
        --rl-chart-4: #b794ff;
        --rl-chart-5: #f0c05a;
        --rl-chart-grid: #2c313a;
    }
}

/* ---- branding ------------------------------------------------------------
   The Studio Umbra files live in wwwroot/brand (sources in src/Ripllm.Res/Logos).
   mark.svg is the eclipse alone and works on either ink; the wordmark comes in
   two inks, so a page that shows it puts both <img>s in and these rules pick
   the one that reads on the current theme — same rule as the tokens above. */
.brand-wordmark--onDark { display: none; }
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .brand-wordmark--onLight { display: none; }
    :root:not([data-theme="light"]) .brand-wordmark--onDark { display: inline-block; }
}
:root[data-theme="dark"] .brand-wordmark--onLight { display: none; }
:root[data-theme="dark"] .brand-wordmark--onDark { display: inline-block; }

html, body {
    background-color: var(--rl-bg);
    background-image: var(--rl-bg-image);
    background-attachment: fixed;
    color: var(--rl-text);
    font-family: var(--rl-font);
    font-size: var(--rl-font-size);
}

/* Error and warning copy stays on the default face, whatever face the rest of the app is
   using. A missing or decorative face must not be what a failure is written in. */
.pe-toast--warn,
.pe-toast--error,
.admin__status--bad,
.validation-message,
.blazor-error-boundary,
#blazor-error-ui,
.cs-error,
.chat__hint--warn,
.push__error,
.pill--warn,
.wf-vitem--warning {
    font-family: var(--rl-font-fallback);
}

/* ---- app shell ----------------------------------------------------------
   One sidebar for the whole app beside a main column that fills the viewport
   exactly. The collapse is a data attribute on <html>, written by js/ui.js: the
   sidebar is an interactive island inside a statically rendered layout, so
   component state would reset on every navigation but this does not.
-------------------------------------------------------------------------- */
.app {
    display: flex;
    height: 100dvh;
    overflow: hidden;
}

.app__sidebar {
    width: 17rem;
    flex-shrink: 0;
    display: flex;
    flex-direction: column;
    min-height: 0;
    background: var(--rl-surface-raised);
    border-right: 1px solid var(--rl-border);
    overflow: hidden;
    transition: width 0.16s ease;
}

/* Collapsed is a rail of icons, not nothing: the icons still lead everywhere, names come as a tooltip, and a fold's
   links open as a flyout. Only a phone hides the sidebar outright, in the media block further down. */
:root[data-sidebar="collapsed"] .app__sidebar { width: 3.4rem; }

.app__main {
    flex: 1;
    min-width: 0;
    min-height: 0;
    display: flex;
    flex-direction: column;
    position: relative;
}

.app__content {
    flex: 1;
    min-height: 0;
    display: flex;
    flex-direction: column;
    overflow-y: auto;
}

/* The chat and the admin pages lay out their own margins; the Identity forms and the
   error pages bring nothing, so the shell supplies it for them. */
.app__content:not(:has(> .chat, > .chatshell, > .admin, > .report, > .wbp, > .pe-host, > .cs, > .arcade, > .reader)) { padding: 1.5rem 2rem 3rem; }

/* Umbra Arcade is another site shown in a frame that fills the page edge to edge. The frame's own background is the page's,
   so the moment before Umbra paints does not flash white in a dark theme. */
.arcade { flex: 1; min-height: 0; display: flex; }
.arcade__frame { flex: 1; width: 100%; min-height: 0; border: 0; background: var(--rl-surface); color-scheme: normal; }
/* The Web reader: an address bar over a frame that shows the page being read, filling the rest of the screen. */
.reader { flex: 1; min-height: 0; display: flex; flex-direction: column; }
.reader__bar { display: flex; align-items: center; gap: .5rem; padding: .6rem .9rem; border-bottom: 1px solid var(--rl-border); }
.reader__bar input { flex: 1; min-width: 0; }
.reader__status { margin: 0; padding: .3rem .9rem; font-size: .85rem; color: var(--rl-muted, inherit); min-height: 1.6rem; }
.reader__status--bad { color: var(--rl-danger, #cf222e); }
.reader__frame { flex: 1; width: 100%; min-height: 0; border: 0; background: #fff; color-scheme: normal; }

/* The only way back to a hidden sidebar, so it floats over whatever page is showing. */
.app__reveal {
    display: none;
    position: absolute;
    top: 0.55rem;
    left: 0.55rem;
    z-index: 5;
    align-items: center;
    justify-content: center;
    width: 2rem;
    height: 2rem;
    padding: 0;
    border: 1px solid var(--rl-border);
    border-radius: 8px;
    background: var(--rl-surface-raised);
    color: var(--rl-text-muted);
    cursor: pointer;
}

.app__reveal svg { width: 1.15rem; height: 1.15rem; }
.app__reveal:hover { color: var(--rl-text); }

/* The floating reveal button exists for phones only (see the media block below); a desktop keeps the rail. */

.app__scrim { display: none; }

.icon-sprite { display: none; }

/* ---- sidebar ------------------------------------------------------------ */
.side {
    display: flex;
    flex-direction: column;
    min-height: 0;
    height: 100%;
    /* Fixed, so the panel keeps its shape while the shell animates the width down to zero. */
    width: 17rem;
    flex-shrink: 0;
}

.side__top {
    display: flex;
    align-items: center;
    gap: 0.1rem;
    padding: 0.6rem 0.65rem 0.6rem 0.9rem;
}

.side__brand {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    font-weight: 600;
    font-size: 0.95rem;
    letter-spacing: -0.01em;
    color: var(--rl-text);
    text-decoration: none;
    margin-right: 0.4rem;
    min-width: 0;
}

/* The mark's glow is drawn out to the edge of its square, so it is set a little larger than the text and pulled
   in with negative margin to sit on the same line without pushing the header taller. */
.side__brandMark {
    width: 1.75rem;
    height: 1.75rem;
    margin: -0.3rem -0.15rem -0.3rem -0.25rem;
    flex-shrink: 0;
}

.side__brandName { white-space: nowrap; }

/* Shade is the second product, pinned under the Studio Umbra name so it is not a row in the menu. */
.side__product {
    display: flex;
    align-items: center;
    gap: 0.45rem;
    margin: -0.2rem 0.45rem 0.35rem 0.65rem;
    padding: 0.05rem 0.4rem 0.05rem 0.25rem;
    border-radius: 8px;
    font-weight: 600;
    font-size: 0.95rem;
    letter-spacing: -0.01em;
    color: var(--rl-text);
    text-decoration: none;
}

.side__product:hover,
.side__product.active { background: var(--rl-surface-sunken); }

/* The pages under the Studio Umbra name. Shade and Games sit outside this, on the same line as that name. */
.side__main {
    display: flex;
    flex-direction: column;
    gap: 0.05rem;
    margin: 0 0 0.45rem 1.35rem;
}

.side__nav > .side__product,
.side__productFold > summary.side__product {
    margin: 0.1rem 0;
    padding-left: 0.25rem;
}

.side__productFold > summary {
    list-style: none;
    cursor: pointer;
    text-transform: none;
    letter-spacing: -0.01em;
    font-size: 0.95rem;
    font-weight: 600;
    color: var(--rl-text);
}

.side__productFold > summary::-webkit-details-marker { display: none; }
.side__product .side__caret { width: 0.85rem; height: 0.85rem; margin-left: auto; transition: transform 0.12s ease; }
.side__productFold[open] > summary .side__caret { transform: rotate(90deg); }
.side__link .side__brandMark { width: 1.05rem; height: 1.05rem; margin: 0; }

.convert__choices { display: flex; flex-wrap: wrap; gap: 0.45rem; }
.convert__choice {
    display: flex; flex-direction: column; align-items: flex-start; gap: 0.1rem;
    padding: 0.45rem 0.75rem; border: 1px solid var(--rl-border); border-radius: 8px;
    background: var(--rl-surface-raised); color: var(--rl-text); font: inherit; cursor: pointer;
}
.convert__choice.is-on { border-color: var(--rl-accent); box-shadow: var(--rl-ring); }
.convert__choice:disabled { opacity: 0.55; cursor: not-allowed; }
.convert__preview { display: block; max-width: min(100%, 28rem); margin-top: 0.75rem; border-radius: 8px; background: var(--rl-surface); }
.vault { display: grid; gap: 0.75rem; }
.vault__row { display: flex; align-items: center; gap: 0.85rem; }
.vault__mark { width: 2.75rem; height: 2.75rem; flex-shrink: 0; border-radius: 8px; }
.vault__name { font-weight: 600; }
.vault__meta { color: var(--rl-text-muted); font-size: 0.85rem; }

.side__toggle { margin-left: auto; }
.side__hdrLink.active { background: var(--rl-surface-sunken); color: var(--rl-text); }
.side__hdrLink:hover { color: var(--rl-text); }

/* The one scroller between the fixed top area and the pinned footer: nav + conversation list
   share it, so a tall nav scrolls instead of clipping the list or pushing the footer away. */
.side__scroll { flex: 1; min-height: 0; overflow-y: auto; }

.side__nav { padding: 0 0.65rem; display: flex; flex-direction: column; gap: 0.05rem; }

.side__find {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    margin: 0 0 0.45rem;
}

.side__find input {
    flex: 1;
    min-width: 0;
    border: 1px solid var(--rl-border);
    border-radius: 8px;
    background: var(--rl-surface-sunken);
    color: var(--rl-text);
    padding: 0.35rem 0.6rem;
    font: inherit;
    font-size: 0.86rem;
}

.side__nav.is-filtering > :not(.side__find):not(.is-nav-match),
.side__nav.is-filtering .side__main > :not(.is-nav-match),
.side__nav.is-filtering details > :not(.is-nav-match):not(summary),
.side__nav.is-filtering .side__tool:not(.is-nav-match) { display: none; }

.side__nav.is-filtering details.is-nav-match { display: block; }

.palette {
    position: fixed;
    inset: 0;
    z-index: 80;
    display: grid;
    place-items: start center;
    padding: 12vh 1rem 1rem;
}

.palette[hidden] { display: none; }

.palette__scrim {
    position: absolute;
    inset: 0;
    border: 0;
    background: rgba(8, 6, 16, 0.55);
}

.palette__panel {
    position: relative;
    width: min(36rem, 100%);
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    padding: 0.7rem;
    border: 1px solid var(--rl-border);
    border-radius: 14px;
    background: var(--rl-surface-raised);
    box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.35);
}

.palette__panel input {
    width: 100%;
    border: 0;
    background: transparent;
    color: var(--rl-text);
    font: inherit;
    font-size: 1rem;
    padding: 0.35rem 0.4rem;
}

.palette__panel input:focus { outline: none; }

.palette__list { display: flex; flex-direction: column; max-height: 50vh; overflow: auto; }

.palette__item {
    display: flex;
    align-items: baseline;
    gap: 0.75rem;
    width: 100%;
    border: 0;
    border-radius: 8px;
    background: transparent;
    color: var(--rl-text);
    text-align: left;
    font: inherit;
    padding: 0.45rem 0.6rem;
    cursor: pointer;
}

.palette__item small { margin-left: auto; color: var(--rl-text-muted); font-size: 0.75rem; }
.palette__item.is-on,
.palette__item:hover { background: var(--rl-surface-sunken); }

.palette__empty { margin: 0.4rem 0.6rem; color: var(--rl-text-muted); font-size: 0.86rem; }

.palette__hint { margin: 0; color: var(--rl-text-muted); font-size: 0.75rem; display: flex; gap: 0.35rem; align-items: center; }
.palette__hint kbd { border: 1px solid var(--rl-border); border-radius: 4px; padding: 0 0.25rem; }

.side__link {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.42rem 0.6rem;
    border-radius: 8px;
    font-size: 0.86rem;
    color: var(--rl-text-muted);
    text-decoration: none;
    white-space: nowrap;
}

.side__link svg { width: 1.05rem; height: 1.05rem; flex-shrink: 0; }
.side__link { transition: background var(--rl-dur), color var(--rl-dur); }
.side__link:hover { background: var(--rl-surface-sunken); color: var(--rl-text); }
.side__link.active { background: var(--rl-surface-sunken); color: var(--rl-text); font-weight: 600; }
.side__link--sub { padding-left: 1.5rem; }

.side__more > summary {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.42rem 0.6rem;
    border-radius: 8px;
    font-size: 0.78rem;
    font-weight: 600;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    color: var(--rl-text-muted);
    cursor: pointer;
    list-style: none;
}

.side__more > summary::-webkit-details-marker { display: none; }
.side__more > summary:hover { background: var(--rl-surface-sunken); }
.side__caret { width: 0.85rem; height: 0.85rem; transition: transform 0.12s ease; }
.side__more[open] > summary .side__caret { transform: rotate(90deg); }

/* An image tool and the fold that opens its past runs. The link keeps the whole row's width; the chevron sits
   over its right edge rather than taking space from a label that is already close to wrapping. */
.side__tool { position: relative; display: flex; align-items: center; }

.side__tool .side__link { flex: 1; min-width: 0; overflow: hidden; padding-right: 1.7rem; }
.side__tool .side__link span { overflow: hidden; text-overflow: ellipsis; }

.side__toolToggle {
    position: absolute;
    right: 0.25rem;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 1.3rem;
    height: 1.3rem;
    padding: 0;
    border: none;
    border-radius: 6px;
    background: none;
    color: var(--rl-text-muted);
    cursor: pointer;
}

.side__toolToggle:hover { background: var(--rl-surface); color: var(--rl-text); }

.side__toolRuns {
    display: flex;
    flex-direction: column;
    gap: 0.02rem;
    /* Indented past the tool's own icon, and given a rule, so a run reads as belonging to the tool above it
       rather than as another tool in the list. */
    margin: 0.1rem 0 0.25rem 1.95rem;
    padding-left: 0.45rem;
    border-left: 1px solid var(--rl-border);
}

.side__toolRun {
    display: flex;
    align-items: baseline;
    gap: 0.4rem;
    padding: 0.26rem 0.4rem;
    border-radius: 6px;
    color: var(--rl-text-muted);
    text-decoration: none;
    font-size: 0.8rem;
}

.side__toolRun:hover { background: var(--rl-surface-sunken); color: var(--rl-text); }
.side__toolRun--active { background: var(--rl-surface-sunken); color: var(--rl-text); font-weight: 600; }

.side__toolRunName { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.side__toolRunWhen {
    flex-shrink: 0;
    color: var(--rl-text-muted);
    font-size: 0.7rem;
    font-variant-numeric: tabular-nums;
}

.side__toolEmpty {
    margin: 0;
    padding: 0.26rem 0.4rem;
    color: var(--rl-text-muted);
    font-size: 0.78rem;
}

/* ---- sidebar search and conversation list ------------------------------- */
.side__search {
    position: relative;
    display: flex;
    align-items: center;
    margin: 0.7rem 0.65rem 0.35rem;
}

.side__search > svg {
    position: absolute;
    left: 0.55rem;
    width: 1rem;
    height: 1rem;
    color: var(--rl-text-muted);
    pointer-events: none;
}

.side__search input {
    font: inherit;
    font-size: 0.84rem;
    width: 100%;
    padding: 0.4rem 1.6rem 0.4rem 1.9rem;
    border: 1px solid var(--rl-border);
    border-radius: 8px;
    background: var(--rl-surface);
    color: var(--rl-text);
}

.side__searchClear {
    position: absolute;
    right: 0.35rem;
    background: none;
    border: none;
    color: var(--rl-text-muted);
    cursor: pointer;
    font-size: 0.75rem;
    padding: 0.2rem 0.3rem;
}

/* Inside .side__scroll now, so it flows at its natural height and the shared scroller handles
   overflow — no nested scrollbar of its own. */
.side__threads {
    padding: 0.25rem 0.65rem 0.5rem;
}

.side__group {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.25rem;
    margin: 0.8rem 0 0.2rem;
    padding: 0 0.2rem;
    color: var(--rl-text-muted);
}

/* The search-results heading, which has a label rather than a fold. */
.side__group > span {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 0.7rem;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
}

.side__groupToggle {
    flex: 1;
    min-width: 0;
    display: flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.22rem 0.2rem;
    border: none;
    border-radius: 8px;
    background: none;
    color: inherit;
    font: inherit;
    text-align: left;
    cursor: pointer;
}

.side__groupToggle:hover { background: var(--rl-surface-sunken); }

.side__groupChevron {
    width: 0.8rem;
    height: 0.8rem;
    flex-shrink: 0;
    color: var(--rl-text-muted);
    transition: transform 120ms ease;
}

.side__groupChevron.is-open { transform: rotate(90deg); }

.side__groupName { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.side__groupCount {
    flex-shrink: 0;
    padding: 0.02rem 0.38rem;
    border-radius: 999px;
    background: var(--rl-surface-sunken);
    color: var(--rl-text-muted);
    font-size: 0.68rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

/* A project names a place, a date only names a moment - so the project heading is the one
   that gets the weight. Sizing the two the same is what made projects hard to pick out. */
.side__group--project { margin-top: 1.05rem; }

.side__group--project .side__groupName {
    font-size: 0.95rem;
    font-weight: 700;
    letter-spacing: -0.01em;
    color: var(--rl-text);
}

.side__groupIcon {
    width: 0.95rem;
    height: 0.95rem;
    flex-shrink: 0;
    color: var(--rl-accent);
}

.side__group--date .side__groupName {
    font-size: 0.7rem;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
}

.side__group--date .side__groupCount { display: none; }

.side__groupAdd {
    display: flex;
    color: var(--rl-text-muted);
    opacity: 0;
}

.side__group:hover .side__groupAdd { opacity: 1; }
.side__groupAdd svg { width: 0.95rem; height: 0.95rem; }
.side__groupAdd:hover { color: var(--rl-text); }

.side__empty { color: var(--rl-text-muted); font-size: 0.82rem; padding: 0.6rem 0.35rem; }

.side__archiveToggle {
    width: 100%;
    margin-top: 0.75rem;
    padding: 0.35rem;
    background: none;
    border: none;
    color: var(--rl-text-muted);
    font: inherit;
    font-size: 0.78rem;
    cursor: pointer;
}

.side__archiveToggle:hover { color: var(--rl-text); }

.side__scopeToggle {
    background: none;
    border: none;
    padding: 0;
    color: var(--rl-text-muted);
    font: inherit;
    font-size: 0.7rem;
    font-weight: 600;
    cursor: pointer;
    white-space: nowrap;
}

.side__scopeToggle:hover { color: var(--rl-text); }

.thread {
    display: flex;
    align-items: center;
    gap: 0.25rem;
    border-radius: 8px;
    padding: 0.02rem 0.2rem 0.02rem 0.35rem;
}

.thread:hover { background: var(--rl-surface-sunken); }
.thread--active { background: var(--rl-surface-sunken); }
.thread--active .thread__title { color: var(--rl-text); font-weight: 600; }

.thread__title {
    flex: 1;
    min-width: 0;
    display: flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.36rem 0;
    color: var(--rl-text-muted);
    text-decoration: none;
    font-size: 0.85rem;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.thread:hover .thread__title { color: var(--rl-text); }

.thread__busy {
    width: 0.45rem;
    height: 0.45rem;
    border-radius: 50%;
    flex-shrink: 0;
    background: var(--rl-accent);
    animation: rl-blink 1.4s ease-in-out infinite;
}

@keyframes rl-blink {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.25; }
}

.thread__actions { display: flex; gap: 0.1rem; opacity: 0; }
/* Reason: a hold on a touch screen has no hover to keep the buttons on screen, and a ring
   that vanishes under the finger drawing it is worse than no ring. */
.thread:hover .thread__actions,
.thread:focus-within .thread__actions,
.thread:has([data-holding]) .thread__actions,
.thread--menu .thread__actions { opacity: 1; }
.thread--menu { background: var(--rl-surface-sunken); }

.thread__rename {
    flex: 1;
    min-width: 0;
    margin: 0.2rem 0;
    padding: 0.2rem 0.4rem;
    font: inherit;
    font-size: 0.85rem;
    color: var(--rl-text);
    background: var(--rl-surface);
    border: 1px solid var(--rl-accent);
    border-radius: 6px;
    outline: none;
}

/* ---- row menu ------------------------------------------------------------
   Fixed rather than absolute: the thread list scrolls and would clip it. The
   flyout for "Move to project" opens from :hover / :focus-within, so it needs
   no component state and no round trip per row the pointer crosses.
-------------------------------------------------------------------------- */
.tmenu__scrim { position: fixed; inset: 0; z-index: 58; }

.tmenu {
    position: fixed;
    z-index: 59;
    left: min(var(--mx, 0px), calc(100vw - 15rem));
    top: min(var(--my, 0px), calc(100dvh - 15rem));
    min-width: 13rem;
    padding: 0.3rem;
    display: flex;
    flex-direction: column;
    background: var(--rl-surface-raised);
    border: 1px solid var(--rl-border);
    border-radius: 10px;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.25);
}

.tmenu__item {
    position: relative;
    display: flex;
    align-items: center;
    gap: 0.55rem;
    width: 100%;
    padding: 0.4rem 0.55rem;
    border: 0;
    border-radius: 6px;
    background: transparent;
    color: var(--rl-text);
    font: inherit;
    font-size: 0.85rem;
    text-align: left;
    white-space: nowrap;
    cursor: pointer;
}

.tmenu__item svg { width: 0.95rem; height: 0.95rem; flex-shrink: 0; color: var(--rl-text-muted); }
.tmenu__item span { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.tmenu__item:hover:not(:disabled), .tmenu__item:focus-visible { background: var(--rl-surface-sunken); outline: none; }
.tmenu__item:disabled { opacity: 0.55; cursor: default; }
.tmenu__item--current { font-weight: 600; }

.tmenu__item--danger, .tmenu__item--danger svg { color: var(--rl-danger); }

.tmenu__more { flex: 0 0 auto; }
.tmenu__has > .tmenu__item .tmenu__more { margin-left: auto; }

.tmenu__rule { height: 1px; margin: 0.25rem 0.2rem; background: var(--rl-border); }

.tmenu__empty { padding: 0.4rem 0.55rem; color: var(--rl-text-muted); font-size: 0.8rem; white-space: nowrap; }

.tmenu__has { position: relative; }

.tmenu__sub {
    display: none;
    position: absolute;
    left: calc(100% + 0.2rem);
    top: -0.3rem;
    min-width: 12rem;
    max-width: 18rem;
    max-height: min(22rem, 60dvh);
    overflow-y: auto;
    padding: 0.3rem;
    flex-direction: column;
    background: var(--rl-surface-raised);
    border: 1px solid var(--rl-border);
    border-radius: 10px;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.25);
}

/* Reason: the 0.2rem gap between item and flyout would drop :hover on the way across, so the flyout
   reaches back over it with a transparent strip. */
.tmenu__sub::before { content: ""; position: absolute; left: -0.4rem; top: 0; bottom: 0; width: 0.4rem; }
.tmenu__has:hover > .tmenu__sub, .tmenu__has:focus-within > .tmenu__sub { display: flex; }

/* A hold on Archive or Delete drains the countdown ring beside its label: see ConversationList.razor.css. */

/* ---- press and hold ------------------------------------------------------
   One button, three answers: a full 1.5s hold acts outright, a quick click asks
   first, and a press abandoned in between does nothing. The ring is the only
   thing that makes the difference between the last two visible, so it is drawn
   from a custom property js/hold.js writes every frame - see js/hold.js.
-------------------------------------------------------------------------- */
.hold-btn { position: relative; }
.hold-btn__glyph { width: 0.95rem; height: 0.95rem; }

.hold-btn__ring {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    opacity: 0;
    transform: rotate(-90deg);
    pointer-events: none;
    transition: opacity 100ms ease;
}

.hold-btn[data-holding] .hold-btn__ring { opacity: 1; }
.hold-btn[data-holding] { background: var(--rl-surface-sunken); }

.hold-btn__track { fill: none; stroke: var(--rl-border); stroke-width: 1.75; }

.hold-btn__fill {
    fill: none;
    stroke: var(--rl-accent);
    stroke-width: 1.75;
    stroke-linecap: round;
    /* 2 * pi * r, for the r the markup draws. */
    stroke-dasharray: 64.4;
    stroke-dashoffset: calc(64.4 * (1 - var(--rl-hold, 0)));
}

.hold-btn--danger .hold-btn__fill { stroke: var(--rl-danger); }

@media (prefers-reduced-motion: reduce) {
    .hold-btn__ring, .side__groupChevron { transition: none; }
}

/* ---- confirmation -------------------------------------------------------
   What a plain click gets. Deliberately a real dialog rather than a browser
   confirm(): the hint about holding is the part that stops it being needed.
-------------------------------------------------------------------------- */
.confirm {
    position: fixed;
    inset: 0;
    z-index: 60;
    display: grid;
    place-items: center;
    padding: 1rem;
}

.confirm__scrim { position: absolute; inset: 0; background: rgba(0, 0, 0, 0.45); animation: rl-fade 0.15s ease-out both; }

.confirm__box {
    position: relative;
    width: min(26rem, 100%);
    padding: 1.1rem 1.2rem;
    border: 1px solid var(--rl-border);
    border-radius: var(--rl-radius);
    background: var(--rl-surface-raised);
    box-shadow: 0 18px 48px rgba(0, 0, 0, 0.35);
    animation: rl-rise 0.2s var(--rl-ease) both;
}

.confirm__title { margin: 0 0 0.45rem; font-size: 1.05rem; }
.confirm__body { margin: 0 0 0.45rem; font-size: 0.9rem; color: var(--rl-text); }
.confirm__hint { margin: 0 0 1rem; font-size: 0.78rem; color: var(--rl-text-muted); }
.confirm__actions { display: flex; justify-content: flex-end; gap: 0.5rem; }

.thread--hit {
    flex-direction: column;
    align-items: flex-start;
    gap: 0.1rem;
    padding: 0.4rem 0.35rem;
    text-decoration: none;
}

.thread--hit .thread__title { padding: 0; }

.thread__snippet {
    font-size: 0.76rem;
    color: var(--rl-text-muted);
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.side__foot {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 0.35rem;
    padding: 0.5rem 0.65rem;
    border-top: 1px solid var(--rl-border);
}

.side__footRow {
    display: flex;
    align-items: center;
    gap: 0.35rem;
    min-width: 0;
}

.side__footRow > :first-child { flex: 1; min-width: 0; }

.side__user {
    flex: 1;
    min-width: 0;
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.3rem 0.35rem;
    border-radius: 8px;
    color: var(--rl-text);
    text-decoration: none;
    font-size: 0.85rem;
}

.side__user:hover { background: var(--rl-surface-sunken); color: var(--rl-text); }

.side__avatar {
    display: grid;
    place-items: center;
    width: 1.6rem;
    height: 1.6rem;
    flex-shrink: 0;
    border-radius: 50%;
    background: var(--rl-accent);
    color: var(--rl-accent-contrast);
    font-size: 0.75rem;
    font-weight: 700;
}

.side__userName { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* The account label is a button now (it opens the popout), so it drops the button chrome and keeps the link look. */
button.side__user { border: 0; background: none; font: inherit; cursor: pointer; text-align: left; transition: background var(--rl-dur); }
.side__userChevron { width: 0.9rem; height: 0.9rem; margin-left: auto; flex: none; color: var(--rl-text-muted); transform: rotate(-90deg); transition: transform var(--rl-dur) var(--rl-ease); }
.side__user:hover .side__userChevron { transform: rotate(-90deg) translateX(1px); }
.side__user { anchor-name: --side-user; }

/* ---- the account popout -------------------------------------------------------
   A browser popover: top layer, light-dismissed, no circuit needed to open it. It
   sits just above the label that opened it, anchored where the browser can and
   pinned above the footer where it cannot.
-------------------------------------------------------------------------------- */
.side__account {
    position: fixed; inset: auto auto 5.2rem 0.65rem; margin: 0;
    width: min(24rem, calc(100vw - 1.3rem)); max-height: min(78vh, 44rem); overflow: auto;
    padding: 0; border: 1px solid var(--rl-border); border-radius: 14px;
    background: var(--rl-surface); color: var(--rl-text);
    box-shadow: 0 18px 48px rgba(0, 0, 0, 0.32), 0 2px 8px rgba(0, 0, 0, 0.12);
    opacity: 0; transform: translateY(8px) scale(0.98);
    transition: opacity var(--rl-dur) ease-out, transform var(--rl-dur) var(--rl-ease), overlay var(--rl-dur) allow-discrete, display var(--rl-dur) allow-discrete;
}
.side__account:popover-open { opacity: 1; transform: none; }
@starting-style { .side__account:popover-open { opacity: 0; transform: translateY(8px) scale(0.98); } }
.side__account::backdrop { background: transparent; }
@supports (position-anchor: --x) {
    .side__account { position-anchor: --side-user; inset: auto; left: anchor(left); bottom: anchor(top); margin-bottom: 0.5rem; }
}
.side__accountHead { display: flex; align-items: center; gap: 0.75rem; padding: 0.9rem 0.9rem 0.8rem; border-bottom: 1px solid var(--rl-border); background: var(--rl-surface-raised); }
.side__avatar--lg { width: 2.6rem; height: 2.6rem; font-size: 1.1rem; }
.side__accountWho { flex: 1; min-width: 0; display: flex; flex-direction: column; line-height: 1.25; }
.side__accountWho strong { font-size: 0.95rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.side__accountWho small { color: var(--rl-text-muted); font-size: 0.78rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.side__accountClose { flex: none; }
.side__accountMenu { display: flex; flex-direction: column; padding: 0.4rem; }
.side__accountItem {
    display: flex; align-items: center; gap: 0.6rem; width: 100%; padding: 0.5rem 0.6rem;
    border: 0; border-radius: 8px; background: none; color: var(--rl-text); font: inherit; font-size: 0.88rem;
    text-decoration: none; text-align: left; cursor: pointer; transition: background var(--rl-dur), color var(--rl-dur);
}
.side__accountItem svg { width: 1rem; height: 1rem; flex: none; color: var(--rl-text-muted); }
.side__accountItem:hover { background: var(--rl-surface-sunken); color: var(--rl-text); }
.side__accountItem:hover svg { color: inherit; }
.side__accountItem--danger:hover { background: color-mix(in srgb, var(--rl-danger, #e11d48) 14%, transparent); color: var(--rl-danger, #e11d48); }
.side__accountBody { border-top: 1px solid var(--rl-border); padding: 0.4rem 0.9rem 0.2rem; }
.side__accountBody .card { margin: 0; padding: 0.6rem 0 0.4rem; border: 0; border-radius: 0; background: none; box-shadow: none; animation: none; }
.side__accountBody .card h2 { font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.04em; color: var(--rl-text-muted); margin: 0 0 0.35rem; }
.side__accountBody .card > .card__meta:first-of-type { display: none; }
.side__accountBody .appearance__grid { grid-template-columns: repeat(auto-fill, 7rem); }
.side__accountNote { margin: 0.5rem 0; }
.side__accountFoot { padding: 0.4rem; border-top: 1px solid var(--rl-border); }

/* ---- busy: an animated stand-in for "Loading…" ------------------------------------ */
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
.busy { display: inline-flex; align-items: center; justify-content: center; padding: 0.6rem; color: var(--rl-text-muted); }
.busy--fill { display: flex; width: 100%; min-height: 6rem; flex: 1; }
.busy__dots { display: inline-flex; gap: 0.3rem; }
.busy__dots i { width: 0.5rem; height: 0.5rem; border-radius: 50%; background: currentColor; opacity: 0.35; animation: rl-busy 1.1s ease-in-out infinite; }
.busy__dots i:nth-child(2) { animation-delay: 0.15s; }
.busy__dots i:nth-child(3) { animation-delay: 0.3s; }
@keyframes rl-busy { 0%, 80%, 100% { opacity: 0.3; transform: translateY(0) scale(0.85); } 40% { opacity: 1; transform: translateY(-3px) scale(1); } }

/* ---- page loading: the logo's eclipse, turning ------------------------------------------------------------------
   A dark moon over a pale sun set a little to one side, so only a crescent of the sun shows; the sun turns, and the lit
   rim runs round the moon's edge. The corona breathes behind both. Colours are the mark's own (brand/mark.svg). */
.eclipse { position: relative; display: inline-block; flex: none; width: var(--eclipse-size, 4.5rem); height: var(--eclipse-size, 4.5rem); }
.eclipse__corona, .eclipse__sun, .eclipse__sun::before, .eclipse__moon { position: absolute; inset: 0; border-radius: 50%; }
.eclipse__corona {
    inset: -60%;
    background: radial-gradient(closest-side, rgba(196, 181, 253, 0) 42%, rgba(168, 85, 247, 0.55) 58%, rgba(124, 58, 237, 0.22) 78%, rgba(124, 58, 237, 0));
    animation: rl-eclipse-breathe 2.4s ease-in-out infinite;
}
.eclipse__sun { animation: rl-eclipse-turn 1.5s linear infinite; }
.eclipse__sun::before {
    content: "";
    background: #ede9fe;
    transform: translateX(7%);
    box-shadow: 0 0 0.5rem 0.1rem rgba(168, 85, 247, 0.9), 0 0 1.4rem 0.3rem rgba(124, 58, 237, 0.45);
}
.eclipse__moon { background: radial-gradient(circle at 42% 38%, #3b2168, #1b0f38 70%, #0c0620); box-shadow: inset 0 0 0 1px rgba(196, 181, 253, 0.3); }
@keyframes rl-eclipse-turn { to { transform: rotate(360deg); } }
@keyframes rl-eclipse-breathe { 0%, 100% { opacity: 0.7; transform: scale(0.94); } 50% { opacity: 1; transform: scale(1.05); } }
@media (prefers-reduced-motion: reduce) {
    .eclipse__sun { animation-duration: 6s; }
    .eclipse__corona { animation: none; }
}

/* A page's own wait (PageLoading): fills the space the page will take, unseen for the first half second. */
.page-loading {
    flex: 1; display: flex; align-items: center; justify-content: center; width: 100%; min-height: 50vh; padding: 2rem;
    animation: rl-page-loading-in 0.2s ease-out 0.5s both;
}
@keyframes rl-page-loading-in { from { opacity: 0; } to { opacity: 1; } }

/* The wait for the server on a navigation (js/page-loader.js): covers the page column, never the sidebar, once a page
   has kept the reader waiting half a second. Under the menus, the tool runs and the pet. */
.page-loader {
    position: fixed; top: 0; left: 0; width: 100%; height: 100%; z-index: 850;
    display: flex; align-items: center; justify-content: center;
    background: rgba(var(--rl-bg-rgb), 0.8); backdrop-filter: blur(3px);
    opacity: 0; visibility: hidden; pointer-events: none;
    transition: opacity 0.18s ease, visibility 0s linear 0.18s;
}
.page-loader.is-on { opacity: 1; visibility: visible; pointer-events: auto; transition: opacity 0.18s ease; }
.page-loader__status { visibility: visible; }
/* The page that arrived under the overlay shows its own wait at once: the reader has already waited the half second. */
.page-loader.is-handoff { transition: none; }
.page-loader.is-handoff ~ * .page-loading { animation: none; }

/* On a phone the sidebar covers the page instead of squeezing it. */
@media (max-width: 780px) {
    .app__sidebar {
        position: fixed;
        inset: 0 auto 0 0;
        z-index: 30;
        box-shadow: 0 0 2rem rgba(0, 0, 0, 0.35);
    }

    :root:not([data-sidebar="collapsed"]) .app__scrim {
        display: block;
        position: fixed;
        inset: 0;
        z-index: 20;
        background: rgba(0, 0, 0, 0.45);
        animation: rl-fade var(--rl-dur) ease-out both;
    }

    .app__reveal { display: flex; }

    :root[data-sidebar="collapsed"] .app__sidebar {
        width: 0;
        border-right-width: 0;
    }

    /* Nothing may sit under the floating reveal button, so each page style keeps clear of it. */
    :root[data-sidebar="collapsed"] .chat__bar { padding-left: 3.2rem; }
    :root[data-sidebar="collapsed"] .admin { padding-left: 3.4rem; }
    :root[data-sidebar="collapsed"] .report__head { padding-left: 3.2rem; }
    :root[data-sidebar="collapsed"] .chatshell__main > .talk { padding-left: 3.4rem; }
    :root[data-sidebar="collapsed"] .app__content:not(:has(> .chat, > .chatshell, > .admin, > .report, > .wbp, > .pe-host, > .cs, > .arcade, > .reader)) { padding-left: 3.4rem; }
}

/* ---- small shared controls ---------------------------------------------- */
.icon-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0.32rem;
    border: none;
    border-radius: 8px;
    background: none;
    color: var(--rl-text-muted);
    cursor: pointer;
}

.icon-btn svg { width: 1.1rem; height: 1.1rem; }
.icon-btn { transition: background var(--rl-dur), color var(--rl-dur), transform var(--rl-dur) var(--rl-ease); }
.icon-btn:hover { background: var(--rl-surface-sunken); color: var(--rl-text); }
.icon-btn:active { transform: scale(0.9); }
.icon-btn--tiny { padding: 0.22rem; }
.icon-btn--tiny svg { width: 0.95rem; height: 0.95rem; }
.icon-btn--danger:hover { color: var(--rl-danger); }
.icon-btn input[type=file] { display: none; }

.ghost-btn {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    font: inherit;
    font-size: 0.82rem;
    padding: 0.32rem 0.6rem;
    border: 1px solid var(--rl-border);
    border-radius: 8px;
    background: var(--rl-surface);
    color: var(--rl-text-muted);
    cursor: pointer;
}

.ghost-btn svg { width: 1rem; height: 1rem; }
.ghost-btn:hover { color: var(--rl-text); background: var(--rl-surface-sunken); }
.ghost-btn--on { color: var(--rl-text); border-color: var(--rl-accent); }
.ghost-btn:disabled { opacity: 0.5; cursor: not-allowed; }

/* ---- shared controls ---------------------------------------------------- */
.btn {
    font: inherit;
    padding: 0.45rem 0.9rem;
    border-radius: 8px;
    border: 1px solid var(--rl-border);
    background: var(--rl-surface-raised);
    color: var(--rl-text);
    cursor: pointer;
    text-decoration: none;
    display: inline-block;
    transition: background var(--rl-dur), border-color var(--rl-dur), color var(--rl-dur), transform var(--rl-dur) var(--rl-ease), box-shadow var(--rl-dur), filter var(--rl-dur);
}
.btn:hover { background: var(--rl-surface-sunken); }
.btn:active:not(:disabled) { transform: scale(0.97); }
.btn:focus-visible { outline: 2px solid var(--rl-accent); outline-offset: 2px; }
.btn--primary { background: var(--rl-accent); color: var(--rl-accent-contrast); border-color: transparent; font-weight: 600; }
/* .btn:hover outranks .btn--primary, so without this a filled button went grey under the pointer. */
.btn--primary:hover { background: var(--rl-accent); filter: brightness(1.1); }
.btn--danger { color: var(--rl-danger); }
/* Filled, not just tinted: this is the one that goes through with a delete, and it has to
   read differently from the Cancel sitting next to it. Placed after .btn so the base rule's
   own background does not win on source order. */
.btn--destructive { background: var(--rl-danger); border-color: transparent; color: #fff; font-weight: 600; }
.btn--destructive:hover { background: var(--rl-danger); filter: brightness(1.1); }
.btn--small { padding: 0.25rem 0.55rem; font-size: 0.85rem; }
.btn--block { display: block; text-align: center; }
.btn--link { background: none; border: none; color: var(--rl-accent); padding: 0; cursor: pointer; }
.btn--icon { padding: 0.45rem 0.6rem; }
.btn--icon input[type=file] { display: none; }
.btn:disabled { opacity: 0.55; cursor: not-allowed; }

/* ---- admin pages -------------------------------------------------------- */
.admin { padding: 1rem 1.5rem 2rem; overflow-y: auto; animation: rl-fade 0.18s ease-out both; }
.admin__head { display: flex; align-items: center; justify-content: space-between; gap: 1rem; margin-bottom: 0.75rem; }
.admin__headActions { display: flex; align-items: center; gap: 0.5rem; }
.admin__head h1 { margin: 0; font-size: 1.35rem; }
.admin__status { padding: 0.6rem 0.9rem; border-radius: 8px; border: 1px solid var(--rl-border); }
.admin__status--ok { border-color: var(--rl-accent); }
.admin__status--bad { border-color: var(--rl-danger); color: var(--rl-danger); }
.unsafe-people { list-style: none; margin: 0.5rem 0 0; padding: 0; display: grid; gap: 0.4rem; }
.unsafe-people li { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; }
.unsafe-people .btn { margin-left: auto; }

.card {
    background: var(--rl-surface);
    border: 1px solid var(--rl-border);
    border-radius: var(--rl-radius);
    padding: 0.85rem 1rem;
    margin-bottom: 0.85rem;
    animation: rl-rise 0.22s var(--rl-ease) both;
}
/* The first few cards on a page arrive one after another rather than as a block. */
.card:nth-child(2) { animation-delay: 0.03s; }
.card:nth-child(3) { animation-delay: 0.06s; }
.card:nth-child(4) { animation-delay: 0.09s; }
.card:nth-child(n + 5) { animation-delay: 0.12s; }
.card h2 { margin: 0 0 0.6rem; font-size: 1.05rem; }
.card__head { display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem; flex-wrap: wrap; }
.card__actions { display: flex; gap: 0.5rem; flex-wrap: wrap; }
.card__meta { color: var(--rl-text-muted); font-size: 0.85rem; }
.card__empty { color: var(--rl-text-muted); }

/* ---- role library ------------------------------------------------------- */
.card__hint { color: var(--rl-text-muted); font-size: 0.85rem; margin: 0 0 0.9rem; max-width: 46rem; }
.roles { list-style: none; margin: 0 0 1rem; padding: 0; display: flex; flex-direction: column; gap: 0.5rem; }
.roles__item {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1rem;
    padding: 0.6rem 0.75rem;
    border: 1px solid var(--rl-border);
    border-radius: 8px;
    background: var(--rl-surface-raised);
}
/* The bare pill carries no background of its own; here it is a label rather than a status. */
.roles__item .pill { background: var(--rl-surface-sunken); color: var(--rl-text-muted); }
.roles__desc { color: var(--rl-text-muted); font-size: 0.85rem; margin-top: 0.25rem; }

.field { margin-bottom: 0.7rem; display: flex; flex-direction: column; gap: 0.3rem; max-width: 46rem; }
.file-or-library { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; }
.lib-pic__err { flex-basis: 100%; color: var(--rl-danger); }
.field--row { flex-direction: row; gap: 1.5rem; align-items: center; }
.field label { font-weight: 600; font-size: 0.9rem; }
.field small { color: var(--rl-text-muted); }
.field input, .field select, .field textarea {
    font: inherit;
    box-sizing: border-box;
    padding: 0.45rem 0.7rem;
    border: 1px solid var(--rl-border);
    border-radius: 8px;
    background: var(--rl-surface-raised);
    color: var(--rl-text);
    transition: border-color var(--rl-dur), box-shadow var(--rl-dur), background-color var(--rl-dur);
}
.field input:not([type=checkbox], [type=radio], [type=range], [type=color], [type=file]),
.field select { min-height: var(--rl-control-h); }
.field textarea { min-height: 4.5rem; }
.field input:focus-visible, .field select:focus-visible, .field textarea:focus-visible {
    outline: none;
    border-color: var(--rl-accent);
    background: var(--rl-surface);
    box-shadow: var(--rl-ring);
}

/* Selects and search boxes that sit in a tool row, not inside .field. The class kept them out of the
   bare-control rules, so they were the browser's own widget beside every other field. */
.composer__select {
    box-sizing: border-box;
    min-height: var(--rl-control-h);
    max-width: 100%;
    padding: 0.45rem 0.7rem;
    border: 1px solid var(--rl-border);
    border-radius: 8px;
    background: var(--rl-surface-raised);
    color: var(--rl-text);
    font: inherit;
    line-height: 1.4;
    transition: border-color var(--rl-dur), box-shadow var(--rl-dur), background-color var(--rl-dur);
}
select.composer__select { padding-right: 2rem; cursor: pointer; }
.composer__select:hover:not(:disabled, :focus) { border-color: var(--rl-border-strong); }
.composer__select:focus-visible {
    outline: none;
    border-color: var(--rl-accent);
    background: var(--rl-surface);
    box-shadow: var(--rl-ring);
}
.composer__select:disabled { opacity: .6; cursor: not-allowed; }

/* A label and its controls on one line: voice pickers, My machine URLs, the mic check. */
.studio__row { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem 0.65rem; }
.studio__row > input:not([type=checkbox], [type=radio]),
.studio__row > select { flex: 1 1 14rem; min-width: 0; }
.studio__lede { margin: 0 0 0.85rem; max-width: 46rem; color: var(--rl-text-muted); font-size: 0.92rem; line-height: 1.55; }
.studio__lede em, .studio__lede strong { color: var(--rl-text); font-style: normal; font-weight: 600; }
/* Older page sheets still say words__lede / paste__lede / mbpage__lede / admin__lede — same lede. */
.words__lede, .paste__lede, .mbpage__lede, .admin__lede { margin: 0 0 0.85rem; max-width: 46rem; color: var(--rl-text-muted); font-size: 0.92rem; line-height: 1.55; }
.words__lede em, .paste__lede em, .admin__lede strong { color: var(--rl-text); font-style: normal; font-weight: 600; }
.studio__state { color: var(--rl-text-muted); font-size: 0.88rem; }
.studio__warn { color: var(--rl-danger); }
.studio__scroll { max-height: 28rem; overflow: auto; margin-top: 0.75rem; border: 1px solid var(--rl-border); border-radius: var(--rl-radius); }
/* A placeholder must never read as a saved value: an admin who mistakes the
   example URL for a stored one thinks the server is configured when it is not. */
.field input::placeholder, .field textarea::placeholder,
.agent__composer input::placeholder, .table input::placeholder {
    color: var(--rl-text-muted);
    opacity: 0.75;
    font-style: italic;
}
.input--tiny { width: 6rem; }

/* ---- file pickers ------------------------------------------------------- */
/* Every visible <input type=file> reads as a .btn followed by the chosen name, instead of the
   browser's grey "Choose File" chip inside a text box. The input itself gives up the field frame
   (border, fill, focus ring) and hands all of that to the ::file-selector-button, which copies
   .btn's padding, radius, hover fill, press scale and focus outline. Pickers that are tucked into
   an icon or label button (.icon-btn, .btn--icon, .cs-pick__file, .cs-composer__file) stay hidden. */
input[type=file] {
    font: inherit;
    color: var(--rl-text-muted);
    font-size: 0.9rem;
    max-width: 100%;
    cursor: pointer;
}
.field input[type=file] {
    padding: 0;
    border: 0;
    border-radius: 0;
    background: none;
    box-shadow: none;
}
.field input[type=file]:focus-visible { border-color: transparent; box-shadow: none; }
input[type=file]::file-selector-button {
    font: inherit;
    font-size: 0.95rem;
    line-height: 1.35;
    padding: 0.45rem 0.9rem;
    margin: 0 0.75rem 0 0;
    border-radius: 8px;
    border: 1px solid var(--rl-border);
    background: var(--rl-surface-raised);
    color: var(--rl-text);
    cursor: pointer;
    transition: background var(--rl-dur), border-color var(--rl-dur), color var(--rl-dur), transform var(--rl-dur) var(--rl-ease), box-shadow var(--rl-dur);
}
input[type=file]:hover:not(:disabled)::file-selector-button { background: var(--rl-surface-sunken); }
input[type=file]:active:not(:disabled)::file-selector-button { transform: scale(0.97); }
input[type=file]:focus-visible { outline: none; }
input[type=file]:focus-visible::file-selector-button { outline: 2px solid var(--rl-accent); outline-offset: 2px; }
input[type=file]:disabled { cursor: not-allowed; }
input[type=file]:disabled::file-selector-button { opacity: 0.55; cursor: not-allowed; }
@media (pointer: coarse) {
    input[type=file]::file-selector-button { min-height: 2.5rem; }
}

/* A "?" badge beside a field label that reveals a short explanation on hover or keyboard focus. Kept
   theme-aware through the same tokens as everything else, so the bubble reads in light and dark alike. */
.infotip {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.05rem;
    height: 1.05rem;
    margin-left: 0.35rem;
    border: 1px solid var(--rl-border);
    border-radius: 50%;
    background: var(--rl-surface-sunken);
    color: var(--rl-text-muted);
    font-size: 0.68rem;
    font-weight: 700;
    line-height: 1;
    cursor: help;
    vertical-align: middle;
    outline-offset: 2px;
}
.infotip__mark { pointer-events: none; }
.infotip__bubble {
    position: absolute;
    bottom: calc(100% + 0.5rem);
    left: 50%;
    transform: translateX(-50%);
    z-index: 60;
    width: max-content;
    max-width: 20rem;
    padding: 0.5rem 0.65rem;
    border: 1px solid var(--rl-border);
    border-radius: 0.45rem;
    background: var(--rl-surface-raised);
    color: var(--rl-text);
    font-size: 0.8rem;
    font-weight: 400;
    line-height: 1.4;
    text-align: left;
    white-space: normal;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.25);
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.12s ease;
    pointer-events: none;
}
.infotip:hover .infotip__bubble,
.infotip:focus .infotip__bubble,
.infotip:focus-visible .infotip__bubble {
    opacity: 1;
    visibility: visible;
}
/* Near the right edge of a card the centered bubble would overflow the viewport; this keeps it in view. */
.infotip__bubble { max-width: min(20rem, 80vw); }

.table { width: 100%; border-collapse: collapse; font-size: 0.9rem; }
.table th, .table td { text-align: left; padding: 0.45rem 0.5rem; border-bottom: 1px solid var(--rl-border); }
.table th { color: var(--rl-text-muted); font-weight: 600; }
.table input[type=text], .table input:not([type]) {
    font: inherit; width: 100%; padding: 0.3rem 0.45rem;
    background: var(--rl-surface-raised); color: var(--rl-text);
    border: 1px solid var(--rl-border); border-radius: 6px;
}
.td--center { text-align: center; }
.td--num { text-align: right; font-variant-numeric: tabular-nums; }
.td--actions { white-space: nowrap; display: flex; gap: 0.35rem; }

/* ---- dashboard ----------------------------------------------------------
   Tiles, charts and bars, shared by the admin usage screen and the per-thread
   metrics pane. Shared deliberately: the same quantity styled two ways in two
   places is how a figure stops being comparable to itself.

   Charts are inline SVG in a fixed coordinate space scaled to the card's width.
   No chart library: three shapes do not justify one, and a build step that pulls
   a bundle in would be the largest thing on the page.
-------------------------------------------------------------------------- */
.tiles {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
    gap: 0.75rem;
    margin-bottom: 1rem;
}

.tiles--tight { grid-template-columns: repeat(auto-fit, minmax(8.5rem, 1fr)); gap: 0.5rem; margin: 0; }

.tile {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    padding: 0.75rem 0.85rem;
    border: 1px solid var(--rl-border);
    border-radius: var(--rl-radius);
    background: var(--rl-surface-raised);
}

.tile__label {
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--rl-text-muted);
}

.tile__value { font-size: 1.55rem; line-height: 1.15; font-variant-numeric: tabular-nums; }
.tiles--tight .tile__value { font-size: 1.2rem; }
.tile__note { font-size: 0.75rem; color: var(--rl-text-muted); }

.dash__row { display: grid; grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr)); gap: 1rem; }
.dash__row .card { margin-bottom: 0; }

.chart { display: block; width: 100%; height: auto; }
.chart__grid { stroke: var(--rl-chart-grid); stroke-width: 1; }
.chart__tick { fill: var(--rl-text-muted); font-size: 11px; font-variant-numeric: tabular-nums; }
.chart__bar--1 { fill: var(--rl-chart-1); }
.chart__bar--2 { fill: var(--rl-chart-2); }
.chart g:hover .chart__bar { fill-opacity: 0.72; }

.legend {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    margin: 0.6rem 0 0;
    padding: 0;
    list-style: none;
    font-size: 0.78rem;
    color: var(--rl-text-muted);
}

.card__head .legend { margin: 0; }
.legend li { display: flex; align-items: center; gap: 0.35rem; }
.legend__swatch { width: 0.7rem; height: 0.7rem; border-radius: 3px; flex-shrink: 0; }
.legend__swatch--1 { background: var(--rl-chart-1); }
.legend__swatch--2 { background: var(--rl-chart-2); }
.legend__aside { font-style: italic; }

.meter {
    display: flex;
    height: 0.7rem;
    border-radius: 999px;
    overflow: hidden;
    background: var(--rl-surface-sunken);
}

.meter__part { display: block; }
.meter__part--1 { background: var(--rl-chart-1); }
.meter__part--2 { background: var(--rl-chart-2); }

.bar { margin-bottom: 0.7rem; }
.bar:last-child { margin-bottom: 0; }
.bar__head { display: flex; align-items: baseline; justify-content: space-between; gap: 0.5rem; }

.bar__name {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 0.82rem;
}

.bar__value { flex-shrink: 0; font-size: 0.82rem; font-variant-numeric: tabular-nums; }

.bar__track {
    height: 0.5rem;
    margin: 0.28rem 0 0.2rem;
    border-radius: 999px;
    background: var(--rl-surface-sunken);
    overflow: hidden;
}

.bar__fill { display: block; height: 100%; border-radius: 999px; background: var(--rl-chart-1); }
.bar__note { font-size: 0.74rem; color: var(--rl-text-muted); }

/* A wide table is scrolled inside its card rather than widening the page. */
.table-scroll { overflow-x: auto; }

.facts { display: grid; grid-template-columns: auto 1fr; gap: 0.2rem 0.75rem; margin: 0; font-size: 0.8rem; }
.facts dt { color: var(--rl-text-muted); }
.facts dd { margin: 0; font-variant-numeric: tabular-nums; }

.metrics { display: flex; flex-direction: column; gap: 1rem; }
.metrics__section { display: flex; flex-direction: column; }

.metrics__heading {
    margin: 0 0 0.45rem;
    font-size: 0.78rem;
    font-weight: 600;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--rl-text-muted);
}
.pill { font-size: 0.7rem; padding: 0.1rem 0.45rem; border-radius: 99px; margin-left: 0.5rem; vertical-align: middle; }
.pill--off { background: var(--rl-surface-sunken); color: var(--rl-text-muted); }
.pill--online { background: var(--rl-ok); color: #fff; }
.pill--offline { background: var(--rl-danger); color: #fff; }
.pill--checking { background: var(--rl-surface-sunken); color: var(--rl-text-muted); }

/* ---- agents grid -------------------------------------------------------- */
.agents { display: grid; grid-template-columns: repeat(auto-fill, minmax(22rem, 1fr)); gap: 1rem; }
.agent { margin-bottom: 0; display: flex; flex-direction: column; gap: 0.6rem; }
.agent h2 { font-size: 1rem; margin: 0; }
.agent h2 a { color: inherit; text-decoration: none; }
.agent__preview { min-height: 4.5rem; }
.agent__stream { margin: 0; font-size: 0.85rem; line-height: 1.5; }
.agent__stream--muted { color: var(--rl-text-muted); }
.agent__error { margin: 0; font-size: 0.85rem; color: var(--rl-danger); }
.agent__composer { display: flex; gap: 0.5rem; }
.agent__composer input {
    flex: 1; font: inherit; padding: 0.4rem 0.6rem;
    border: 1px solid var(--rl-border); border-radius: 8px;
    background: var(--rl-surface-raised); color: var(--rl-text);
}
.pill--busy { background: var(--rl-accent); color: var(--rl-accent-contrast); }
.pill--bad { background: var(--rl-danger); color: #fff; }
.pill--warn { background: var(--rl-warning, #e3b341); color: #241a02; }
.field small.warn { color: var(--rl-danger); }
/* Under a voice picker: the next line waits while the speech server loads that voice's model. On its own line even
   in a wrapping row of controls (Talk). */
.voice-load, .field small.voice-load {
    display: block; flex-basis: 100%; margin-top: 0.35rem; font-size: 0.8rem;
    color: var(--rl-warning, #e3b341);
}

/* ---- site status table -------------------------------------------------- */
.status__stamp { color: var(--rl-text-muted); font-size: 0.8rem; align-self: center; }
.status__scroll { overflow-x: auto; }
.status { width: 100%; border-collapse: collapse; font-size: 0.88rem; }
.status th, .status td {
    text-align: left; padding: 0.5rem 0.7rem; border-bottom: 1px solid var(--rl-border);
    vertical-align: top;
}
.status thead th {
    font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.04em;
    color: var(--rl-text-muted); font-weight: 600; border-bottom: 1px solid var(--rl-border);
}
.status tbody tr:last-child td { border-bottom: none; }
.status__num { width: 2rem; color: var(--rl-text-muted); font-variant-numeric: tabular-nums; }
.status__name { font-weight: 600; }
.status .pill { margin-left: 0; }

/* ---- resources (Admin → Resources, and the summary on Site status) --------- */
.res__grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 26rem), 1fr)); gap: 1.25rem; }
.res__grid .card { margin-bottom: 0; }
.res__head { display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem; margin-bottom: 0.75rem; }
.res__title { margin: 0 0 0.15rem; font-size: 1.05rem; }
.res__row {
    display: grid; grid-template-columns: 7.5rem minmax(0, 1fr); column-gap: 0.75rem; row-gap: 0.2rem;
    align-items: center; margin: 0.55rem 0;
}
.res__label { font-size: 0.8rem; color: var(--rl-text-muted); grid-row: span 2; align-self: start; padding-top: 0.05rem; }
.res__value { font-size: 0.85rem; font-variant-numeric: tabular-nums; }
.res__sub { display: block; color: var(--rl-text-muted); font-size: 0.78rem; }
.res__error { color: var(--rl-danger); font-size: 0.85rem; margin: 0.5rem 0; }
.res__procs { width: 100%; border-collapse: collapse; font-size: 0.82rem; margin-top: 0.75rem; }
.res__procs th {
    text-align: left; font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.04em;
    color: var(--rl-text-muted); font-weight: 600; padding: 0.3rem 0; border-bottom: 1px solid var(--rl-border);
}
.res__procs td { padding: 0.3rem 0; border-bottom: 1px solid var(--rl-border); }
.res__procs tr:last-child td { border-bottom: none; }
.res__num { text-align: right !important; font-variant-numeric: tabular-nums; white-space: nowrap; }
.res__more { margin-top: 0.5rem; font-size: 0.82rem; }
.res__more summary { cursor: pointer; color: var(--rl-text-muted); }
.res__summary td { white-space: nowrap; }

.meter {
    height: 0.55rem; border-radius: 999px; overflow: hidden; display: flex;
    background: var(--rl-surface-sunken); border: 1px solid var(--rl-border);
}
.meter--mini { width: 6rem; height: 0.4rem; margin-bottom: 0.2rem; }
.meter__fill { height: 100%; transition: width 0.4s ease; }
.meter__fill--ok { background: var(--rl-ok); }
.meter__fill--warn { background: var(--rl-warning, #e3b341); }
.meter__fill--bad { background: var(--rl-danger); }
.meter__fill--other { background: var(--rl-text-muted); opacity: 0.45; }
/* The mic check draws one fill and a threshold tick. The chart meter above is a flex row of parts;
   this one has to be a track the tick can sit on. */
.meter:has(.meter__mark) { position: relative; display: block; overflow: visible; margin: 0.7rem 0; }
.meter:has(.meter__mark) .meter__fill { display: block; background: var(--rl-text-muted); border-radius: inherit; }
.meter__fill--over { background: var(--rl-ok); }
.meter__mark { position: absolute; top: -3px; bottom: -3px; width: 2px; margin-left: -1px; background: var(--rl-text); border-radius: 1px; }
.probe { display: grid; grid-template-columns: 8rem 1fr; gap: 0.35rem 0.75rem; margin: 0.85rem 0 0; max-width: 36rem; }
.probe dt { margin: 0; color: var(--rl-text-muted); font-size: 0.82rem; }
.probe dd { margin: 0; font-variant-numeric: tabular-nums; }

/* ---- workloads (Admin → Workloads) ------------------------------------------ */
.wl__tiles { display: grid; grid-template-columns: repeat(auto-fill, minmax(9.5rem, 1fr)); gap: 0.75rem; }
.wl__tile {
    display: flex; flex-direction: column; gap: 0.15rem; padding: 0.6rem 0.75rem;
    border: 1px solid var(--rl-border); border-radius: var(--rl-radius); background: var(--rl-surface-raised);
}
.wl__big { font-size: 1.25rem; font-weight: 600; font-variant-numeric: tabular-nums; }
.wl__cap { font-size: 0.75rem; color: var(--rl-text-muted); }
.wl__num { white-space: nowrap; font-variant-numeric: tabular-nums; }

@media (max-width: 780px) {
    .res__row { grid-template-columns: minmax(0, 1fr); }
    .res__label { grid-row: auto; }
}
.status__kind {
    font-size: 0.72rem; padding: 0.1rem 0.4rem; border-radius: 999px;
    background: var(--rl-surface-sunken); color: var(--rl-text-muted);
}
.status__host { color: var(--rl-accent); text-decoration: none; }
.status__host:hover { text-decoration: underline; }
.status__ip, .status__gpu { color: var(--rl-text-muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
.status__notes { color: var(--rl-text-muted); }
.status__row--off { opacity: 0.55; }
.status__row--expandable { cursor: pointer; }
.status__row--expandable:hover td { background: var(--rl-surface-sunken); }
.status__caret {
    display: inline-block; color: var(--rl-text-muted); font-size: 0.75rem;
    transition: transform 0.12s ease; transform-origin: 45% 55%;
}
.status__caret--open { transform: rotate(90deg); }
.status__childcount {
    margin-left: 0.5rem; font-size: 0.72rem; font-weight: 600; padding: 0.05rem 0.4rem;
    border-radius: 999px; background: var(--rl-surface-sunken); color: var(--rl-text-muted);
    font-variant-numeric: tabular-nums;
}
.status__num .status__caret { margin-right: 0.35rem; }
.status__row--service td { background: color-mix(in srgb, var(--rl-surface-sunken) 55%, transparent); font-size: 0.84rem; }
.status td.status__svc { padding-left: 1.6rem; }
.status__svc .status__caret { margin-right: 0.45rem; }
.status__caret--none { visibility: hidden; }
.status td .status__kind + .status__kind { margin-left: 0.25rem; }
.status td.status__detail--service { padding-left: 2.8rem; }
.status__detailrow td { background: var(--rl-surface-sunken); padding-top: 0.6rem; padding-bottom: 0.8rem; }
.status__detail { display: flex; flex-wrap: wrap; gap: 1.2rem 2rem; }
.status__group { min-width: 12rem; }
.status__group-title {
    font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.04em;
    color: var(--rl-text-muted); font-weight: 600; margin-bottom: 0.35rem;
}
.status__group-count {
    margin-left: 0.3rem; padding: 0.02rem 0.35rem; border-radius: 999px;
    background: var(--rl-surface); color: var(--rl-text-muted); font-variant-numeric: tabular-nums;
}
.status__group-list {
    list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.15rem;
}
.status__group-list li {
    font-size: 0.82rem; font-variant-numeric: tabular-nums; word-break: break-all;
}

/* ---- tool calls, approvals, notes --------------------------------------- */
.approval__args, .approval__result {
    background: var(--rl-surface-sunken); padding: 0.7rem 0.9rem; border-radius: 8px;
    overflow-x: auto; font-size: 0.85rem; white-space: pre-wrap;
}
/* A parked call decided inside the conversation that is waiting on it. */
.approve {
    margin-top: 0.5rem; padding: 0.75rem 0.9rem; border-radius: 10px;
    border: 1px solid var(--rl-border); border-left: 3px solid var(--rl-accent, #4f7cff);
    background: var(--rl-surface); display: flex; flex-direction: column; gap: 0.55rem;
}
.approve__head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.4rem; font-size: 0.9rem; }
.approve__head strong { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
.approve__note { color: var(--rl-text-muted); }
.approve .approval__args { margin: 0; max-height: 14rem; overflow-y: auto; }
.approve__error { margin: 0; color: var(--rl-danger, #e5484d); font-size: 0.85rem; }
.notes__preview {
    border-top: 1px solid var(--rl-border); margin-top: 0.75rem; padding-top: 0.75rem; font-size: 0.9rem;
}
.notes__preview :first-child { margin-top: 0; }

/* ---- markdown in a reply ------------------------------------------------
   Global, not scoped to Chat.razor: markdown arrives as a MarkupString, so its
   elements never carry the component's scope attribute and a scoped rule cannot
   reach them. */
.msg__body :first-child { margin-top: 0; }
.msg__body :last-child { margin-bottom: 0; }
.msg__body code { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 0.9em; }
.msg__body :not(pre) > code {
    background: var(--rl-surface-sunken); padding: 0.1rem 0.3rem; border-radius: 4px;
}
.msg__body pre {
    margin: 0; background: var(--rl-surface-sunken);
    padding: 0.7rem 0.9rem; border-radius: 8px; overflow-x: auto;
}
.msg__body table { border-collapse: collapse; }
.msg__body th, .msg__body td { border: 1px solid var(--rl-border); padding: 0.3rem 0.5rem; }

/* ---- code blocks --------------------------------------------------------
   Chrome added by js/code-blocks.js around every fenced block in a reply. */
.code {
    border: 1px solid var(--rl-border); border-radius: 8px;
    background: var(--rl-surface-sunken); margin: 0.75rem 0; overflow: hidden;
}
.code__bar {
    display: flex; align-items: center; gap: 0.5rem;
    padding: 0.3rem 0.5rem 0.3rem 0.7rem;
    border-bottom: 1px solid var(--rl-border);
    background: var(--rl-surface-raised);
    font-size: 0.75rem; color: var(--rl-text-muted);
}
.code__lang { font-weight: 600; text-transform: uppercase; letter-spacing: 0.03em; }
.code__count { margin-right: auto; }
.code__btn {
    font: inherit; cursor: pointer; padding: 0.15rem 0.5rem;
    border: 1px solid var(--rl-border); border-radius: 6px;
    background: var(--rl-surface); color: var(--rl-text);
}
.code__btn:hover { border-color: var(--rl-accent); color: var(--rl-accent); }
.code__btn[data-done] { border-color: var(--rl-accent); color: var(--rl-accent); }
.code pre { border-radius: 0; }

/* Collapsed is the default for anything long: a 400-line file should not bury
   the rest of the reply. The fade says there is more without adding a control. */
.code--collapsed .code__body { max-height: 20rem; overflow: hidden; position: relative; }
.code--collapsed .code__body::after {
    content: ""; position: absolute; inset: auto 0 0 0; height: 3.5rem; pointer-events: none;
    background: linear-gradient(to bottom, transparent, var(--rl-surface-sunken));
}

/* ---- live tool call ------------------------------------------------------
   The window onto a file while it is still being written. Global rather than
   scoped: the card is its own component, so a rule in Chat.razor.css would not
   reach it.
-------------------------------------------------------------------------- */
.live {
    border: 1px solid var(--rl-border);
    border-left: 3px solid var(--rl-accent);
    border-radius: 10px;
    background: var(--rl-surface-raised);
    overflow: hidden;
}

.live__bar {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.4rem 0.7rem;
    border-bottom: 1px solid var(--rl-border);
    font-size: 0.78rem;
    color: var(--rl-text-muted);
}

.live__verb { color: var(--rl-text); font-weight: 500; }

.live__path {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    direction: rtl;
    text-align: left;
}

.live__count { font-variant-numeric: tabular-nums; white-space: nowrap; }

.live__spinner {
    width: 0.55rem;
    height: 0.55rem;
    flex-shrink: 0;
    border-radius: 50%;
    background: var(--rl-accent);
    animation: rl-blink 1.2s ease-in-out infinite;
}

.live--quiet { background: transparent; }
.live--quiet .live__bar { border-bottom: 0; }
.live--quiet + .live--quiet { margin-top: -1px; border-top-left-radius: 0; border-top-right-radius: 0; }
.live--quiet:has(+ .live--quiet) { border-bottom-left-radius: 0; border-bottom-right-radius: 0; }

/* Fixed height so a file being written does not jump about as lines arrive; the newest line
   sits at the bottom, which is where the model is actually writing. */
.live--file .live__window {
    height: 11.5rem;
    overflow: auto;
    padding: 0.4rem 0;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.78rem;
    line-height: 1.45;
}

.live__row { display: flex; gap: 0.6rem; padding: 0 0.7rem; }

.live__no {
    min-width: 2.6rem;
    text-align: right;
    color: var(--rl-text-muted);
    opacity: 0.6;
    user-select: none;
    font-variant-numeric: tabular-nums;
}

.live__text { white-space: pre; }

/* The last line is the one being typed, so it gets the cursor. */
.live__row:last-child .live__text::after {
    content: "";
    display: inline-block;
    width: 0.45rem;
    height: 0.9em;
    margin-left: 1px;
    vertical-align: text-bottom;
    background: var(--rl-accent);
    animation: rl-caret 1s steps(2, start) infinite;
}

@keyframes rl-caret {
    0%, 50% { opacity: 1; }
    50.01%, 100% { opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
    .live__spinner, .live__row:last-child .live__text::after { animation: none; }
}

/* ---- tool activity -------------------------------------------------------
   What the agent did, one line each, in English. Global rather than scoped:
   the rows are their own component, so a rule in Chat.razor.css cannot reach
   them.
-------------------------------------------------------------------------- */
.acts { display: flex; flex-direction: column; gap: 0.25rem; }

.act {
    border: 1px solid transparent;
    border-radius: 8px;
    font-size: 0.82rem;
    color: var(--rl-text-muted);
}

.act:hover, .act[open] { border-color: var(--rl-border); background: var(--rl-surface-raised); }
.act--plain > summary { cursor: default; pointer-events: none; }
.act--plain:hover { border-color: transparent; background: none; }

.act > summary {
    display: flex;
    align-items: baseline;
    gap: 0.45rem;
    flex-wrap: wrap;
    padding: 0.25rem 0.5rem;
    cursor: pointer;
    list-style: none;
}

.act > summary::-webkit-details-marker { display: none; }

/* The dot carries the kind of work, so a column of rows reads as a shape
   before it reads as words: read, written, searched. */
.act__mark {
    width: 0.5rem;
    height: 0.5rem;
    flex-shrink: 0;
    border-radius: 2px;
    align-self: center;
    background: var(--rl-text-muted);
    opacity: 0.5;
}

.act__mark--write { background: var(--rl-accent); opacity: 1; }
.act__mark--read, .act__mark--list { border-radius: 50%; }
.act__mark--search { transform: rotate(45deg); }

.act__verb { color: var(--rl-text); font-weight: 500; }

.act__subject {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.78rem;
    color: var(--rl-text);
    overflow-wrap: anywhere;
}

.act__where { font-style: italic; }
.act__outcome::before { content: "— "; }
.act__time { margin-left: auto; font-variant-numeric: tabular-nums; opacity: 0.75; }

.act--failed .act__verb, .act--failed .act__outcome { color: var(--rl-danger); }

.act__body {
    margin: 0 0.5rem 0.5rem;
    padding: 0.5rem 0.6rem;
    max-height: 16rem;
    overflow: auto;
    border-radius: 6px;
    background: var(--rl-surface-sunken);
    font-size: 0.75rem;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

.act__body--args { color: var(--rl-text-muted); }

/* ---- command log ---------------------------------------------------------
   Commands collected out of the run and into one place. A turn that ran twenty
   of them used to put twenty lines of truncated stdout between the reader and
   the reply.
-------------------------------------------------------------------------- */
.clog {
    border: 1px solid var(--rl-border);
    border-radius: 8px;
    background: var(--rl-surface-raised);
}

.clog > summary {
    display: flex;
    align-items: center;
    gap: 0.45rem;
    padding: 0.35rem 0.55rem;
    cursor: pointer;
    font-size: 0.82rem;
    list-style: none;
}

.clog > summary::-webkit-details-marker { display: none; }
.clog > summary svg { width: 0.95rem; height: 0.95rem; color: var(--rl-text-muted); flex-shrink: 0; }
.clog > summary strong { font-weight: 500; }
.clog__count { color: var(--rl-text-muted); font-size: 0.78rem; }

.clog > summary::after {
    content: "▸";
    margin-left: auto;
    color: var(--rl-text-muted);
    font-size: 0.7rem;
}

.clog[open] > summary::after { content: "▾"; }
.clog[open] > summary { border-bottom: 1px solid var(--rl-border); }

.clog__body {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    padding: 0.5rem;
}

/* ---- cards inside a side panel -------------------------------------------
   A panel's scroller is a column flex container with a definite height, and
   `overflow: hidden` on a card makes it a scroll container, which drops its
   automatic minimum size to zero. Without this a log of twenty commands
   squashes every block into a sliver instead of scrolling.
-------------------------------------------------------------------------- */
.canvas__scroll > * { flex-shrink: 0; }

/* ---- one command ---------------------------------------------------------
   The prompt line is the point: a summary of a command is not something anyone
   can check, re-run, or paste into a shell.
-------------------------------------------------------------------------- */
.cmd {
    border: 1px solid var(--rl-border);
    border-left: 3px solid var(--rl-accent);
    border-radius: 8px;
    background: var(--rl-surface);
    overflow: hidden;
}

.cmd--failed { border-left-color: var(--rl-danger); }
.cmd--running { border-left-color: #d08700; }

.cmd__line {
    display: flex;
    align-items: baseline;
    gap: 0.4rem;
    padding: 0.4rem 0.55rem 0.2rem;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.78rem;
}

.cmd__prompt { color: var(--rl-accent); user-select: none; }
.cmd__text { flex: 1; min-width: 0; color: var(--rl-text); overflow-wrap: anywhere; }

.cmd__badge {
    flex-shrink: 0;
    padding: 0.05rem 0.4rem;
    border-radius: 99px;
    font-size: 0.68rem;
    font-family: inherit;
    white-space: nowrap;
}

.cmd__badge--ok { background: rgba(47, 109, 246, 0.14); color: var(--rl-accent); }
.cmd__badge--bad { background: rgba(179, 38, 30, 0.15); color: var(--rl-danger); }
.cmd__badge--waiting, .cmd__badge--running { background: rgba(208, 135, 0, 0.18); color: #d08700; }
.cmd__badge--running { animation: rl-blink 1.4s ease-in-out infinite; }

.cmd__meta {
    display: flex;
    gap: 0.5rem;
    padding: 0 0.55rem 0.35rem;
    font-size: 0.7rem;
    color: var(--rl-text-muted);
    font-variant-numeric: tabular-nums;
}

.cmd__meta span + span::before { content: "· "; }

.cmd__note {
    margin: 0;
    padding: 0 0.55rem 0.45rem;
    font-size: 0.75rem;
    font-style: italic;
    color: var(--rl-text-muted);
}

/* Output keeps its own lines: wrapping a table of build output destroys the
   only structure it has. It scrolls sideways instead. */
.cmd__out {
    margin: 0;
    padding: 0.45rem 0.55rem 0.55rem;
    border-top: 1px solid var(--rl-border);
    background: var(--rl-surface-sunken);
    font-size: 0.75rem;
    line-height: 1.45;
    white-space: pre;
    overflow: auto;
}

.cmd__out--clamped { max-height: 18rem; }

/* Wide output is readable two ways and the reader picks: scrolled, where a table of
   build output keeps its columns, or wrapped, where nothing hides off the right edge.
   The switch lives on <html> so it survives the panel being re-rendered. */
html[data-term-wrap] .cmd__out {
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

/* ---- file change card --------------------------------------------------- */
.fc {
    border: 1px solid var(--rl-border);
    border-radius: 10px;
    background: var(--rl-surface-raised);
    overflow: hidden;
}

.fc__bar {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.3rem 0.5rem 0.3rem 0.35rem;
    font-size: 0.8rem;
}

.fc__toggle {
    flex: 1;
    min-width: 0;
    display: flex;
    align-items: center;
    gap: 0.4rem;
    font: inherit;
    padding: 0.25rem 0.3rem;
    border: none;
    border-radius: 8px;
    background: none;
    color: var(--rl-text);
    cursor: pointer;
    text-align: left;
}

.fc__toggle:hover { background: var(--rl-surface-sunken); }

.fc__caret { width: 0.85rem; height: 0.85rem; flex-shrink: 0; transition: transform 0.12s ease; }
.fc--open .fc__caret { transform: rotate(90deg); }
.fc__icon { width: 1rem; height: 1rem; flex-shrink: 0; color: var(--rl-text-muted); }

.fc__path {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.fc__stat { display: flex; gap: 0.4rem; font-variant-numeric: tabular-nums; white-space: nowrap; }
.fc__plus { color: #2e7d32; }
.fc__minus { color: var(--rl-danger); }

@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .fc__plus { color: #7bc47f; }
}
:root[data-theme="dark"] .fc__plus { color: #7bc47f; }

.fc__new,
.fc__badge {
    font-size: 0.68rem;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    padding: 0.08rem 0.4rem;
    border-radius: 99px;
    background: var(--rl-surface-sunken);
    color: var(--rl-text-muted);
}

.fc__badge--reverted { color: var(--rl-danger); }

.fc__actions { display: flex; align-items: center; gap: 0.2rem; }

.fc__body {
    border-top: 1px solid var(--rl-border);
    background: var(--rl-surface);
}

.fc__note {
    margin: 0;
    padding: 0.5rem 0.75rem;
    font-size: 0.78rem;
    color: var(--rl-text-muted);
}

.fc__note--bad { color: var(--rl-danger); }

.fc__decide {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-wrap: wrap;
    padding: 0.5rem 0.75rem;
    border-top: 1px solid var(--rl-border);
    background: var(--rl-surface-raised);
    font-size: 0.78rem;
    color: var(--rl-text-muted);
}

.fc__decide > span { flex: 1; min-width: 10rem; }

/* ---- code with line numbers, and diffs ---------------------------------- */
.code-lines,
.diff {
    max-height: 30rem;
    overflow: auto;
    padding: 0.5rem 0;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.78rem;
    line-height: 1.5;
}

.code-lines__row { display: flex; gap: 0.7rem; padding: 0 0.75rem; }

.code-lines__no,
.diff__no {
    min-width: 2.8rem;
    text-align: right;
    flex-shrink: 0;
    color: var(--rl-text-muted);
    opacity: 0.55;
    user-select: none;
    font-variant-numeric: tabular-nums;
}

.code-lines__text,
.diff__text { white-space: pre; }

.diff__hunk {
    padding: 0.35rem 0.75rem;
    margin-top: 0.35rem;
    background: var(--rl-surface-sunken);
    color: var(--rl-text-muted);
    font-size: 0.74rem;
}

.diff__hunk:first-child { margin-top: 0; }

.diff__line { display: flex; gap: 0.5rem; padding: 0 0.75rem; }

.diff__sign { width: 0.7rem; flex-shrink: 0; user-select: none; }

/* Tinted rows plus a sign column: the colour alone would not survive a
   greyscale screenshot, and would say nothing to a reader who cannot see it. */
.diff__line--added { background: rgba(46, 125, 50, 0.13); }
.diff__line--removed { background: rgba(179, 38, 30, 0.13); }
.diff__line--added .diff__sign { color: #2e7d32; }
.diff__line--removed .diff__sign { color: var(--rl-danger); }

@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .diff__line--added { background: rgba(123, 196, 127, 0.14); }
    :root:not([data-theme="light"]) .diff__line--removed { background: rgba(255, 143, 135, 0.14); }
    :root:not([data-theme="light"]) .diff__line--added .diff__sign { color: #7bc47f; }
}
:root[data-theme="dark"] .diff__line--added { background: rgba(123, 196, 127, 0.14); }
:root[data-theme="dark"] .diff__line--removed { background: rgba(255, 143, 135, 0.14); }
:root[data-theme="dark"] .diff__line--added .diff__sign { color: #7bc47f; }

.msg__files--written { flex-direction: column; align-items: stretch; }

.ghost-btn--tight { padding: 0.2rem 0.45rem; font-size: 0.76rem; }

/* Pressed state for the wrap switch. It is driven from <html> rather than from
   component state, so it cannot be a class the server puts on the button. */
html[data-term-wrap] [data-wrap-toggle] { color: var(--rl-text); border-color: var(--rl-accent); }

.ghost-btn__count {
    display: inline-grid;
    place-items: center;
    min-width: 1.15rem;
    height: 1.15rem;
    padding: 0 0.25rem;
    border-radius: 99px;
    background: var(--rl-accent);
    color: var(--rl-accent-contrast);
    font-size: 0.68rem;
    font-weight: 700;
}

/* ---- Comfy ------------------------------------------------------------- */
/* Results are a checkerboard-backed grid so transparent textures read correctly
   against the surface rather than disappearing into it. */
.comfy-results {
    display: grid; gap: 1rem; margin-top: 0.25rem;
    grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
}
.comfy-result { margin: 0; display: flex; flex-direction: column; gap: 0.4rem; }
.comfy-result img {
    width: 100%; height: auto; display: block; border-radius: 8px;
    border: 1px solid var(--rl-border);
    background-color: var(--rl-surface-sunken);
    background-image:
        linear-gradient(45deg, rgba(128, 128, 128, 0.18) 25%, transparent 25%),
        linear-gradient(-45deg, rgba(128, 128, 128, 0.18) 25%, transparent 25%),
        linear-gradient(45deg, transparent 75%, rgba(128, 128, 128, 0.18) 75%),
        linear-gradient(-45deg, transparent 75%, rgba(128, 128, 128, 0.18) 75%);
    background-size: 16px 16px;
    background-position: 0 0, 0 8px, 8px -8px, -8px 0;
}
.comfy-result figcaption { font-size: 0.8rem; color: var(--rl-text-muted); word-break: break-all; }

/* A mesh has no thumbnail to show, so its results are a plain list of files rather than a picture grid. */
.comfy-meshes { list-style: none; margin: 0.25rem 0 0.75rem; padding: 0; display: grid; gap: 0.4rem; }
.comfy-meshes li { word-break: break-all; }

/* Job progress. A determinate bar only appears once the sampler reports steps; before that the stage is
   genuinely unknown in percentage terms, so the bar sweeps instead of sitting at zero looking hung. */
.comfy-progress {
    height: 0.5rem; border-radius: 999px; overflow: hidden; margin: 0.5rem 0 0.4rem;
    background: var(--rl-surface-sunken); border: 1px solid var(--rl-border);
}
.comfy-progress__bar {
    height: 100%; background: var(--rl-accent); border-radius: 999px;
    transition: width 0.25s ease;
}
.comfy-progress--waiting { position: relative; }
.comfy-progress__bar--pulse { width: 35%; animation: comfy-sweep 1.4s ease-in-out infinite; }

/* A run the server stopped. It keeps the width it reached, because where it got to is the useful part of a
   failure, but it must not keep the accent colour of a run that is still going. */
.comfy-progress__bar--failed { background: var(--rl-danger); animation: none; }
.comfy-job__head--failed { color: var(--rl-danger); }

@keyframes comfy-sweep {
    0%   { margin-left: -35%; }
    100% { margin-left: 100%; }
}

@media (prefers-reduced-motion: reduce) {
    .comfy-progress__bar--pulse { animation: none; width: 100%; opacity: 0.45; }
}

/* The mask painter. The canvas is sized by its backing store and scaled down by CSS, so the cursor is a plain
   crosshair rather than a brush-sized ring: at the sizes textures are edited, a ring drawn in CSS pixels lies
   about where the stroke actually lands. */
.mask-painter { display: flex; flex-direction: column; gap: 0.6rem; max-width: 46rem; }
.mask-painter__stage {
    border: 1px solid var(--rl-border); border-radius: 8px; overflow: hidden;
    background-color: var(--rl-surface-sunken);
    background-image:
        linear-gradient(45deg, rgba(128, 128, 128, 0.18) 25%, transparent 25%),
        linear-gradient(-45deg, rgba(128, 128, 128, 0.18) 25%, transparent 25%),
        linear-gradient(45deg, transparent 75%, rgba(128, 128, 128, 0.18) 75%),
        linear-gradient(-45deg, transparent 75%, rgba(128, 128, 128, 0.18) 75%);
    background-size: 16px 16px;
    background-position: 0 0, 0 8px, 8px -8px, -8px 0;
}
.mask-painter__canvas {
    display: block; width: 100%; height: auto; cursor: crosshair;
    /* Reason: without this a drag on the canvas scrolls the page on a touch screen instead of painting. */
    touch-action: none;
}
.mask-painter__tools { display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap; }
.mask-painter__tools input[type=range] { width: 9rem; }
.mask-painter__tools .card__meta { margin-left: auto; }

/* ---- side-by-side diff ---------------------------------------------------
   Four columns in one grid — number, old text, number, new text — so both
   halves share a single horizontal scroll and a line never drifts out of
   step with the line it became. min-width: max-content lets the grid grow
   to the widest line instead of wrapping code.
-------------------------------------------------------------------------- */
.sxs {
    border: 1px solid var(--rl-border);
    border-radius: 8px;
    background: var(--rl-surface);
    overflow: hidden;
}

.sxs__pane { overflow-x: auto; }

.sxs__grid {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto minmax(0, 1fr);
    min-width: max-content;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.78rem;
    line-height: 1.55;
}

.sxs__head {
    grid-column: span 2;
    padding: 0.3rem 0.75rem;
    font-family: inherit;
    font-size: 0.72rem;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--rl-text-muted);
    background: var(--rl-surface-raised);
    border-bottom: 1px solid var(--rl-border);
}

.sxs__head--new { border-left: 1px solid var(--rl-border); }

.sxs__hunk {
    grid-column: span 4;
    padding: 0.25rem 0.75rem;
    color: var(--rl-text-muted);
    background: var(--rl-surface-sunken);
    border-top: 1px solid var(--rl-border);
    border-bottom: 1px solid var(--rl-border);
}

.sxs__no {
    padding: 0 0.5rem;
    text-align: right;
    color: var(--rl-text-muted);
    user-select: none;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.sxs__no--right { border-left: 1px solid var(--rl-border); }

.sxs__cell { padding: 0 0.6rem; white-space: pre; }

.sxs__cell--del { background: rgba(179, 38, 30, 0.13); }
.sxs__cell--ins { background: rgba(46, 125, 50, 0.13); }

/* An absent line is a gap, not a blank one: shaded so the eye reads it as
   "nothing here" rather than as a line that was emptied. */
.sxs__cell--gap {
    background: repeating-linear-gradient(
        45deg,
        rgba(128, 128, 128, 0.06) 0 6px,
        transparent 6px 12px);
}

@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .sxs__cell--del { background: rgba(255, 143, 135, 0.14); }
    :root:not([data-theme="light"]) .sxs__cell--ins { background: rgba(123, 196, 127, 0.14); }
}
:root[data-theme="dark"] .sxs__cell--del { background: rgba(255, 143, 135, 0.14); }
:root[data-theme="dark"] .sxs__cell--ins { background: rgba(123, 196, 127, 0.14); }

/* ---- question card -------------------------------------------------------
   A reply that ends in questions, rendered as rows to click rather than as
   prose to answer. Each row carries its own number so the whole card can be
   answered from the keyboard, and the free-text row is always last so there
   is never a question with no way out.
-------------------------------------------------------------------------- */
.ask {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    border: 1px solid var(--rl-border);
    border-radius: 12px;
    background: var(--rl-surface);
    padding: 0.7rem 0.8rem;
}

.ask--past { opacity: 0.72; }

.ask__head { display: flex; align-items: center; gap: 0.6rem; }

.ask__toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    border: none;
    background: none;
    padding: 0;
    font: inherit;
    font-size: 0.92rem;
    color: var(--rl-text);
    cursor: pointer;
}

.ask__chev {
    width: 0.9rem;
    height: 0.9rem;
    flex-shrink: 0;
    opacity: 0.6;
    transform: rotate(-90deg);
    transition: transform 120ms ease;
}

.ask__chev--open { transform: none; }

.ask__progress {
    margin-left: auto;
    font-size: 0.75rem;
    color: var(--rl-text-muted);
    font-variant-numeric: tabular-nums;
}

.ask__q { display: flex; flex-direction: column; gap: 0.4rem; border-radius: 10px; }

/* A rule between questions, because two stacks of rows with nothing between them read
   as one long list of options belonging to the first question. */
.ask__q + .ask__q { border-top: 1px solid var(--rl-border); padding-top: 0.75rem; }

/* The focus ring sits on the question, not on a row inside it: the number keys
   act on whichever question has focus, so that is the thing to show. */
.ask__q:focus-visible {
    outline: 2px solid var(--rl-accent);
    outline-offset: 4px;
}

.ask__qhead { display: flex; align-items: baseline; gap: 0.45rem; flex-wrap: wrap; }

.ask__chip {
    font-size: 0.68rem;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--rl-text-muted);
    background: var(--rl-surface-sunken);
    border-radius: 999px;
    padding: 0.1rem 0.45rem;
}

.ask__prompt { font-size: 0.9rem; font-weight: 600; }

.ask__multi { font-size: 0.72rem; color: var(--rl-text-muted); }

.ask__opts { display: flex; flex-direction: column; gap: 0.3rem; }

.ask__opt {
    display: flex;
    align-items: flex-start;
    gap: 0.6rem;
    width: 100%;
    text-align: left;
    font: inherit;
    border: 1px solid transparent;
    border-radius: 9px;
    background: var(--rl-surface-raised);
    padding: 0.5rem 0.6rem;
    color: var(--rl-text);
    cursor: pointer;
}

.ask__opt:hover { background: var(--rl-surface-sunken); }

.ask__opt--on {
    border-color: var(--rl-accent);
    background: rgba(47, 109, 246, 0.09);
}

.ask__opt:disabled { cursor: default; }
.ask__opt:disabled:hover { background: var(--rl-surface-raised); }

.ask__opt--other { cursor: text; align-items: center; }
.ask__opt--other:hover { background: var(--rl-surface-raised); }

.ask__optText { display: flex; flex-direction: column; gap: 0.15rem; min-width: 0; flex: 1; }

.ask__optLabel { font-size: 0.86rem; font-weight: 600; }

.ask__rec {
    font-size: 0.7rem;
    font-style: normal;
    font-weight: 500;
    color: var(--rl-accent);
    margin-left: 0.35rem;
}

.ask__optDesc { font-size: 0.78rem; color: var(--rl-text-muted); line-height: 1.45; }

.ask__input {
    width: 100%;
    font: inherit;
    font-size: 0.82rem;
    border: 1px solid var(--rl-border);
    border-radius: 8px;
    background: var(--rl-surface-raised);
    color: var(--rl-text);
    padding: 0.3rem 0.45rem;
}

.ask__input:focus { outline: none; border-color: var(--rl-accent); box-shadow: var(--rl-ring); background: var(--rl-surface); }

/* The number a key press maps to, kept at the far edge so the labels stay aligned. */
.ask__key {
    flex-shrink: 0;
    font-family: inherit;
    font-size: 0.7rem;
    color: var(--rl-text-muted);
    background: var(--rl-surface-sunken);
    border-radius: 5px;
    padding: 0.1rem 0.35rem;
    font-variant-numeric: tabular-nums;
}

.ask__foot { display: flex; align-items: center; gap: 0.5rem; }

.ask__hint { font-size: 0.72rem; color: var(--rl-text-muted); margin-right: auto; }

@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .ask__opt--on { background: rgba(111, 155, 255, 0.12); }
}
:root[data-theme="dark"] .ask__opt--on { background: rgba(111, 155, 255, 0.12); }

/* ---- context breakdown ---------------------------------------------------
   One bar for the shape of the spend and a legend for the figures. The slice
   colours are fixed hexes rather than theme tokens: they are a categorical
   scale, and a palette that changed with the theme would make the same slice
   read as a different thing on a dark screen.
-------------------------------------------------------------------------- */
.ctx { display: flex; flex-direction: column; gap: 0.75rem; }

.ctx__total { display: flex; align-items: baseline; gap: 0.4rem; flex-wrap: wrap; }
.ctx__total strong { font-size: 1.35rem; font-variant-numeric: tabular-nums; }
.ctx__total span { font-size: 0.8rem; color: var(--rl-text-muted); }

.ctx__bar {
    display: flex;
    height: 0.65rem;
    border-radius: 999px;
    overflow: hidden;
    background: var(--rl-surface-sunken);
}

.ctx__seg { display: block; height: 100%; }
.ctx__seg + .ctx__seg { border-left: 1px solid var(--rl-surface); }

.ctx__legend { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }

.ctx__legend > li + li { border-top: 1px solid var(--rl-border); }

.ctx__row {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.35rem 0;
    font-size: 0.85rem;
}

.ctx__dot { width: 0.6rem; height: 0.6rem; border-radius: 3px; flex-shrink: 0; }
.ctx__name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ctx__num { font-variant-numeric: tabular-nums; }

.ctx__pct {
    width: 3.4rem;
    text-align: right;
    font-variant-numeric: tabular-nums;
    color: var(--rl-text-muted);
}

/* A small caret column so plain rows and expandable ones line up at the dot. */
.ctx__caret {
    width: 0.7rem;
    flex-shrink: 0;
    font-size: 0.6rem;
    line-height: 1;
    color: var(--rl-text-muted);
    text-align: center;
    transition: transform 0.12s ease;
}

.ctx__count {
    font-size: 0.7rem;
    font-variant-numeric: tabular-nums;
    color: var(--rl-text-muted);
    background: var(--rl-surface-sunken);
    border-radius: 999px;
    padding: 0.05rem 0.4rem;
}

.ctx__group { width: 100%; }
.ctx__group > summary { cursor: pointer; list-style: none; }
.ctx__group > summary::-webkit-details-marker { display: none; }
.ctx__group[open] > summary .ctx__caret { transform: rotate(90deg); }
.ctx__group > summary:hover .ctx__name { color: var(--rl-text); }

.ctx__sub {
    list-style: none;
    margin: 0 0 0.35rem;
    padding: 0 0 0 1.7rem;
    display: flex;
    flex-direction: column;
}

.ctx__sub li {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.2rem 0;
    font-size: 0.78rem;
    color: var(--rl-text-muted);
}

.ctx__free { color: var(--rl-text-muted); }

.ctx__warn {
    margin: 0;
    padding: 0.5rem 0.6rem;
    border: 1px solid var(--rl-danger);
    border-radius: 8px;
    font-size: 0.8rem;
    color: var(--rl-danger);
}

.ctx__note { margin: 0; font-size: 0.75rem; line-height: 1.45; color: var(--rl-text-muted); }

.ctx__seg--messages, .ctx__dot--messages { background: #3b82f6; }
.ctx__seg--toolresults, .ctx__dot--toolresults { background: #f97316; }
.ctx__seg--tools, .ctx__dot--tools { background: #a855f7; }
.ctx__seg--systemprompt, .ctx__dot--systemprompt { background: #eab308; }
.ctx__seg--images, .ctx__dot--images { background: #ec4899; }
.ctx__dot--free { background: var(--rl-surface-sunken); border: 1px solid var(--rl-border); }

/* ---------------------------------------------------------------- generated assets

   The library and the two pages that fill it share one grid. Cards are taller than the Comfy result grid's
   because an asset carries its own provenance — prompt, pipeline, size — which is the point of keeping it. */
.assets {
    display: grid; gap: 0.75rem; margin-top: 0.25rem;
    grid-template-columns: repeat(auto-fill, minmax(13rem, 1fr));
}
.asset {
    margin: 0; display: flex; flex-direction: column; gap: 0.35rem;
    padding: 0.5rem; border: 1px solid var(--rl-border); border-radius: 10px;
    background: var(--rl-surface);
    transition: transform var(--rl-dur) var(--rl-ease), box-shadow var(--rl-dur), border-color var(--rl-dur);
}
.asset:hover {
    transform: translateY(-2px);
    border-color: color-mix(in srgb, var(--rl-accent) 45%, var(--rl-border));
    box-shadow: 0 6px 18px rgba(0, 0, 0, 0.14);
}
.asset__thumb { display: block; border-radius: 8px; overflow: hidden; }
.asset__thumb img {
    width: 100%; height: 8rem; object-fit: contain; display: block;
    border: 1px solid var(--rl-border); border-radius: 8px;
    background-color: var(--rl-surface-sunken);
    /* The same chequerboard the Comfy results use, so transparency reads as transparency. */
    background-image:
        linear-gradient(45deg, rgba(128, 128, 128, 0.18) 25%, transparent 25%),
        linear-gradient(-45deg, rgba(128, 128, 128, 0.18) 25%, transparent 25%),
        linear-gradient(45deg, transparent 75%, rgba(128, 128, 128, 0.18) 75%),
        linear-gradient(-45deg, transparent 75%, rgba(128, 128, 128, 0.18) 75%);
    background-size: 16px 16px;
    background-position: 0 0, 0 8px, 8px -8px, -8px 0;
}
/* A mesh and a zip have no picture, so they get a glyph the same size as one — a grid of mixed kinds still
   lines up, which a text-only row among thumbnails would not. */
.asset__thumb--file {
    height: 8rem; display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 0.3rem; border: 1px solid var(--rl-border); border-radius: 8px;
    background: var(--rl-surface-sunken); color: var(--rl-text-muted); text-decoration: none;
}
.asset__thumb--file svg { width: 2rem; height: 2rem; }
.asset__thumb--file span { font-size: 0.72rem; letter-spacing: 0.05em; }
/* A take's card plays in place. Same footprint as a thumbnail so a mixed library keeps its grid. */
.asset__thumb--audio {
    height: 8rem; display: flex; align-items: center; justify-content: center;
    padding: 0.6rem; border: 1px solid var(--rl-border); border-radius: 8px;
    background: var(--rl-surface-sunken);
}
.asset__thumb--audio audio { width: 100%; }
/* A render plays in place too, at the same footprint. */
.asset__thumb--video {
    height: 8rem; display: flex; align-items: center; justify-content: center;
    border: 1px solid var(--rl-border); border-radius: 8px; background: #000; overflow: hidden;
}
.asset__thumb--video video { width: 100%; height: 100%; object-fit: contain; }
/* A model turns in place, at the same footprint as a thumbnail. The badge sits underneath and only shows if the viewer failed. */
.asset__thumb--mesh {
    position: relative; height: 8rem; border: 1px solid var(--rl-border); border-radius: 8px;
    background: radial-gradient(circle at 50% 35%, var(--rl-surface) 0%, var(--rl-surface-sunken) 100%);
}
.asset__thumb--mesh model-viewer {
    width: 100%; height: 100%; --poster-color: transparent; --progress-bar-color: var(--rl-text-muted);
}
.asset__thumb--mesh .asset__meshfallback { display: none; position: absolute; inset: 0; border: 0; border-radius: 0; }
.asset__thumb--mesh.is-failed model-viewer { display: none; }
.asset__thumb--mesh.is-failed .asset__meshfallback { display: flex; }
.asset__body { display: flex; flex-direction: column; gap: 0.2rem; align-items: flex-start; flex: 1; width: 100%; }
/* Date and Delete share one row at the foot of the card instead of costing a row each. */
.asset__foot { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; width: 100%; margin-top: auto; padding-top: 0.2rem; }
.asset__name { font-size: 0.86rem; font-weight: 600; word-break: break-word; }
.asset__meta { font-size: 0.76rem; color: var(--rl-text-muted); }
.asset__prompt { font-size: 0.78rem; color: var(--rl-text-muted); width: 100%; }
.asset__prompt summary { cursor: pointer; }
.asset__prompt p { margin: 0.3rem 0 0; word-break: break-word; }
.asset__settings { display: grid; grid-template-columns: auto 1fr; gap: 0.1rem 0.6rem; margin: 0.3rem 0 0; }
.asset__settings dt { color: var(--rl-text-muted); }
.asset__settings dd { margin: 0; color: var(--rl-text); word-break: break-word; }
.asset__brief { color: var(--rl-text); }

/* The GLB is the file's own link; this folds the conversions of it away so a mesh row is not a wall of format
   buttons until someone wants one. */
.asset__export { font-size: 0.78rem; width: 100%; }
.asset__export summary { cursor: pointer; color: var(--rl-text-muted); }
.asset__exportList {
    list-style: none; margin: 0.35rem 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: 0.35rem;
}
.asset__exportList a {
    display: inline-block; padding: 0.2rem 0.5rem; border: 1px solid var(--rl-border); border-radius: 6px;
    background: var(--rl-surface-raised); color: var(--rl-text); text-decoration: none;
}
.asset__exportList a:hover { border-color: var(--rl-accent); }

/* A CSV with a typo in it is the normal case, so its problems are listed beside the rows that did parse rather
   than replacing them. */
.batch-problems {
    list-style: none; margin: 0.5rem 0 0; padding: 0.5rem 0.7rem; display: grid; gap: 0.35rem;
    border: 1px solid var(--rl-border); border-radius: 8px;
    background: var(--rl-surface-sunken); font-size: 0.82rem; color: var(--rl-text-muted);
}

/* ---- sidebar sections, favorites, archived, project libraries ------------
   A section heading (Image tools, Voice tools, Admin) is styled like a project
   folder - an icon, bolder, brighter, larger than its rows - because it names
   a place the same way a folder does. Its rows keep the muted weight, and each
   row's fold chevron sits at the far left so every fold in the sidebar opens
   from the same edge.
-------------------------------------------------------------------------- */
.side__more > summary {
    gap: 0.45rem;
    padding: 0.36rem 0.4rem 0.36rem 0.35rem;
    font-size: 0.95rem;
    font-weight: 700;
    letter-spacing: -0.01em;
    text-transform: none;
    color: var(--rl-text);
}

.side__more { margin-top: 0.35rem; }
.side__caret { width: 1rem; height: 1rem; flex: 0 0 1rem; color: var(--rl-text-muted); }

.side__sectionIcon {
    width: 1rem;
    height: 1rem;
    flex-shrink: 0;
    color: var(--rl-accent);
}

.side__sectionName { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* A tool row: [fold chevron][icon label]. The chevron is a sibling in the flow rather than pinned to the
   right edge, so the label is clipped by the row's width and never slides under it. */
.side__tool { gap: 0.1rem; }
.side__tool .side__link { padding-left: 0.35rem; padding-right: 0.4rem; gap: 0.45rem; }

.side__toolToggle {
    position: static;
    flex-shrink: 0;
    width: 1rem;
    height: 1rem;
    margin-left: 0.35rem;
}

.side__toolToggle .side__groupChevron { width: 0.9rem; height: 0.9rem; }
.side__toolToggle--none { cursor: default; pointer-events: none; }

/* The Chat row: a top-level link - same icon column, weight and size as the rows below it. The conversation list
   is not under it: it has a bar of its own beside the chat (ChatShell). [icon] Chat ........ [mic] [+] */
.side__chatRow {
    display: flex;
    align-items: center;
    gap: 0.25rem;
    margin: 0;
    padding-right: 0.4rem;
    border-radius: 6px;
    font-size: 0.86rem;
    color: var(--rl-text-muted);
}

.side__chatRow:hover { color: var(--rl-text); }
.side__chatRow.active { background: var(--rl-surface-sunken); color: var(--rl-text); font-weight: 600; }

.side__chatLink {
    display: flex;
    align-items: center;
    gap: 0.45rem;
    flex: 1;
    min-width: 0;
    padding: 0.36rem 0 0.36rem 0.35rem;
    color: inherit;
    text-decoration: none;
}

.side__chatIcon { color: inherit; }
.side__chat .side__sectionName { flex: 1; min-width: 0; }

.side__chatNew {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 1.5rem;
    height: 1.5rem;
    flex-shrink: 0;
    border-radius: 6px;
    color: var(--rl-text-muted);
}

.side__chatNew:hover { background: var(--rl-border); color: var(--rl-text); }
.side__chatNew.is-on { color: var(--rl-accent); }
.side__chatNew svg { width: 1rem; height: 1rem; }

/* The Tools fold holds the tool groups as folds of their own, one caret-column in from their parent. */
.side__tools > .side__more { margin-left: 1.45rem; margin-top: 0.1rem; }
.side__tools > .side__more > summary { font-size: 0.88rem; }
.side__tools > summary + .side__more { margin-top: 0.2rem; }

/* The favorites toggle sits at the head of the list rather than the foot. */
.side__favToggle { margin-top: 0.1rem; margin-bottom: 0.25rem; }

/* A row with no caret still keeps the caret's width, so its icon lines up with the rows that fold. */
.side__nav .side__link,
.side__nav .side__chatLink { gap: 0.45rem; }
.side__nav .side__link { padding-left: 0.35rem; }
.side__nav .side__link::before,
.side__nav .side__chatLink::before {
    content: "";
    width: 1rem;
    height: 1rem;
    flex: 0 0 1rem;
}
.side__nav .side__tool .side__link::before { content: none; width: 0; flex-basis: 0; }

/* Library stands on its own just above Admin, so it takes the weight of a section heading beside it. */
.side__nav > .side__link--section {
    margin-top: 0.35rem;
    padding: 0.36rem 0.4rem 0.36rem 0.35rem;
    font-size: 0.95rem;
    font-weight: 700;
    letter-spacing: -0.01em;
    color: var(--rl-text);
}
.side__link--section .side__sectionIcon { width: 1rem; height: 1rem; }

/* The rail's stand-in for the search box; the box itself shows while the sidebar is open. */
.side__findBtn { display: none; }
.side__link--sub,
.side__link--plain { padding-left: 0.35rem; }
.side__nav details > .side__link,
.side__nav details > .side__chat { margin-left: 1.45rem; }

.side__toolRuns { margin-left: 2.15rem; }

/* Starred threads. The star sits before the title, outside the actions, so it survives them being hidden. */
.thread__star {
    width: 0.8rem;
    height: 0.8rem;
    flex-shrink: 0;
    color: var(--rl-warning, #e3b341);
}

.icon-btn--star { color: var(--rl-warning, #e3b341); }
.icon-btn--star:hover { color: var(--rl-warning, #e3b341); }


/* Archived threads sit below a rule and step back; the working list above brightens to compensate. */
.side__divider {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    margin: 1.1rem 0.2rem 0.2rem;
    color: var(--rl-text-muted);
    font-size: 0.68rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

.side__divider::before,
.side__divider::after {
    content: "";
    flex: 1;
    height: 1px;
    background: var(--rl-border);
}

.side__threads--archivedShown .thread:not(.thread--archived) .thread__title { color: var(--rl-text); }
.side__threads--archivedShown .side__group--date .side__groupName { color: var(--rl-text); }

.thread--archived .thread__title { color: var(--rl-text-muted); opacity: 0.6; }
.thread--archived:hover .thread__title { opacity: 0.85; }
.thread--archived .thread__star { opacity: 0.6; }
.side__threads--archivedShown .side__divider ~ .side__group--project .side__groupName { opacity: 0.7; }

/* The project's library row: one link under its threads that goes to the files filed under it. */
.thread--library { text-decoration: none; margin-top: 0.1rem; }
.thread--library .thread__title { font-size: 0.8rem; gap: 0.35rem; }
.thread__libraryIcon { width: 0.85rem; height: 0.85rem; flex-shrink: 0; color: var(--rl-accent); }
.thread--library .side__groupCount { margin-right: 0.25rem; }

/* ---- library page --------------------------------------------------------- */
.library__crumb { margin: 0.2rem 0 0; }
.library__controls { display: flex; flex-direction: column; gap: 0.6rem; }

/* Reason: the controls stack in a column, so a flex-basis here would become a 16rem *height* and push the
   search box into the middle of a quarter-screen of empty card. Size to content. */
.library__search {
    position: relative;
    display: flex;
    align-items: center;
    flex: 0 0 auto;
    min-width: 12rem;
}

.library__search > svg {
    position: absolute;
    left: 0.6rem;
    width: 1rem;
    height: 1rem;
    color: var(--rl-text-muted);
    pointer-events: none;
}

.library__search input {
    box-sizing: border-box;
    font: inherit;
    font-size: 0.9rem;
    width: 100%;
    min-height: var(--rl-control-h);
    padding: 0.45rem 1.8rem 0.45rem 2rem;
    border: 1px solid var(--rl-border);
    border-radius: 8px;
    background: var(--rl-surface-raised);
    color: var(--rl-text);
    transition: border-color var(--rl-dur), box-shadow var(--rl-dur), background-color var(--rl-dur);
}
.library__search input:focus-visible {
    outline: none;
    border-color: var(--rl-accent);
    background: var(--rl-surface);
    box-shadow: var(--rl-ring);
}

.library__filters {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 0.6rem 0.9rem;
}

.library__filters > label {
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--rl-text-muted);
}

.library__filters select {
    font: inherit;
    font-size: 0.86rem;
    padding: 0.35rem 0.5rem;
    border: 1px solid var(--rl-border);
    border-radius: 8px;
    background: var(--rl-surface-raised);
    color: var(--rl-text);
    min-width: 9rem;
}

.library__check {
    flex-direction: row !important;
    align-items: center !important;
    gap: 0.4rem !important;
    padding-bottom: 0.45rem;
    text-transform: none !important;
    letter-spacing: 0 !important;
    font-size: 0.86rem !important;
    color: var(--rl-text) !important;
}

.library__clear { align-self: flex-end; }
.library__actions { border-top: 1px solid var(--rl-border); padding-top: 0.6rem; }

.library__group { margin-bottom: 0.75rem; }
.library__groupTitle { display: flex; align-items: center; gap: 0.45rem; }
.library__groupTitle svg { width: 1.05rem; height: 1.05rem; color: var(--rl-accent); }
.library__groupTitle a { color: inherit; text-decoration: none; }
.library__groupTitle a:hover { text-decoration: underline; }

.library__more { display: flex; align-items: center; gap: 1rem; margin: 0.25rem 0 1rem; }

/* File badges in the colour the format is known by, drawn on a tint of the same colour. */
.asset__badge { font-size: 0.72rem; font-weight: 700; letter-spacing: 0.06em; }
.asset__thumb--file { background: color-mix(in srgb, var(--file-tone, var(--rl-text-muted)) 14%, var(--rl-surface-sunken)); color: var(--file-tone, var(--rl-text-muted)); }
.asset__thumb--file svg { color: var(--file-tone, var(--rl-text-muted)); }

.file--red { --file-tone: #e5484d; }
.file--green { --file-tone: #30a46c; }
.file--blue { --file-tone: #3e8ef7; }
.file--orange { --file-tone: #f76b15; }
.file--amber { --file-tone: #e3b341; }
.file--purple { --file-tone: #8e4ec6; }
.file--teal { --file-tone: #12a594; }
.file--pink { --file-tone: #d6409f; }
.file--cyan { --file-tone: #05a2c2; }
.file--slate { --file-tone: #8b8d98; }

.asset__project {
    font: inherit;
    font-size: 0.78rem;
    max-width: 100%;
    padding: 0.25rem 0.4rem;
    border: 1px solid var(--rl-border);
    border-radius: 6px;
    background: var(--rl-surface-raised);
    color: var(--rl-text);
}

.asset__projectLink { display: inline-flex; align-items: center; gap: 0.3rem; text-decoration: none; }
.asset__projectLink svg { width: 0.85rem; height: 0.85rem; color: var(--rl-accent); }

/* ---- favorites and projects pages ----------------------------------------- */
.fav__title, .project__title { display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap; }
.fav__star { width: 0.95rem; height: 0.95rem; flex-shrink: 0; color: var(--rl-warning, #e3b341); vertical-align: -0.1em; }
.fav__meta { margin: 0; display: flex; flex-wrap: wrap; gap: 0.15rem; }
.fav__project { display: inline-flex; align-items: center; gap: 0.3rem; text-decoration: none; }
.fav__project svg, .agent__where svg { width: 0.85rem; height: 0.85rem; color: var(--rl-accent); }
.agent__where { margin: -0.3rem 0 0; }
.agent__where a { display: inline-flex; align-items: center; gap: 0.3rem; text-decoration: none; }

.projects { grid-template-columns: repeat(auto-fill, minmax(min(24rem, 100%), 1fr)); }
.projects__import {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.6rem;
    margin: 0 0 0.85rem;
}
.projects__import input { min-width: min(24rem, 100%); flex: 1; }
.projects__import span { color: var(--rl-text-muted); font-size: 0.85rem; }
.project {
    position: relative; isolation: isolate; overflow: hidden;
    margin-bottom: 0; display: flex; flex-direction: column; gap: 0.6rem;
    transition: border-color var(--rl-dur, 0.15s), box-shadow var(--rl-dur, 0.15s);
}
.project:hover { border-color: color-mix(in srgb, var(--rl-accent) 55%, var(--rl-border)); box-shadow: var(--rl-shadow-md); }
.project:has(.project__link:focus-visible) { outline: 2px solid var(--rl-accent); outline-offset: 2px; }
.project__title svg { width: 1.05rem; height: 1.05rem; color: var(--rl-accent); }
.project__title a { color: inherit; text-decoration: none; }
/* The title link covers the whole card, so a click anywhere opens the project; real controls are lifted above it. */
.project__link::after { content: ""; position: absolute; inset: 0; z-index: 1; }
.project__link:focus-visible { outline: none; }
.project .card__actions, .project__stats a { position: relative; z-index: 2; }

/* The project's own icon takes the folder's place, five times its size. */
.project__icon {
    flex: none; width: 5.25rem; height: 5.25rem; margin-right: 0.35rem; overflow: hidden;
    border-radius: 14px; border: 1px solid var(--rl-border); background: var(--rl-surface-sunken); box-shadow: var(--rl-shadow-sm);
}
.project__icon img, .project__icon video { display: block; width: 100%; height: 100%; object-fit: cover; }

/* The banner fills the card behind everything, under a veil of the card's own colour so the text stays readable. */
.project__banner { position: absolute; inset: 0; z-index: -1; pointer-events: none; }
.project__banner img, .project__banner video { display: block; width: 100%; height: 100%; object-fit: cover; }
.project__banner::after {
    content: ""; position: absolute; inset: 0;
    background: linear-gradient(180deg,
        color-mix(in srgb, var(--rl-surface) 25%, transparent) 0%,
        color-mix(in srgb, var(--rl-surface) 70%, transparent) 40%,
        color-mix(in srgb, var(--rl-surface) 88%, transparent) 100%);
}
.project--banner .project__title, .project--banner .project__desc, .project--banner > .card__meta {
    text-shadow: 0 0 2px var(--rl-surface), 0 1px 4px color-mix(in srgb, var(--rl-surface) 85%, transparent);
}
/* No picture uploaded: the project page's generated art (projects.css .pd-hero--art) in the project's hue, and its initials.
   The base is tinted from the card's own surface rather than the hero's near-black, so a light theme gets a pastel, not grey. */
.project--art .project__banner {
    background:
        radial-gradient(55% 85% at 82% 18%, hsl(var(--pd-hue) 85% 62% / 0.55), transparent 62%),
        radial-gradient(45% 75% at 12% 105%, hsl(calc(var(--pd-hue) + 55) 80% 58% / 0.5), transparent 66%),
        linear-gradient(135deg,
            color-mix(in srgb, hsl(var(--pd-hue) 60% 45%) 35%, var(--rl-surface)),
            color-mix(in srgb, hsl(calc(var(--pd-hue) + 35) 60% 40%) 20%, var(--rl-surface)));
}
.project__icon--initials {
    display: grid; place-items: center; border-color: transparent;
    background: linear-gradient(140deg, hsl(var(--pd-hue) 70% 55%), hsl(calc(var(--pd-hue) + 50) 70% 42%));
    color: #fff; font-size: 1.9rem; font-weight: 700; letter-spacing: 0.02em; text-shadow: 0 1px 3px rgb(0 0 0 / 0.35);
}
/* No backdrop-filter here: it would make the stats box a stacking context and bury the Files link under the card's link. */
.project--banner .project__stats { background: color-mix(in srgb, var(--rl-surface-raised) 90%, transparent); }
.project__desc { margin: 0; color: var(--rl-text); font-size: 0.9rem; }

.project__stats {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(6.5rem, 1fr));
    gap: 0.5rem 0.75rem;
    margin: 0;
    padding: 0.6rem 0.75rem;
    border: 1px solid var(--rl-border);
    border-radius: 8px;
    background: var(--rl-surface-raised);
}

.project__stats dt {
    font-size: 0.66rem;
    font-weight: 600;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--rl-text-muted);
}

.project__stats dd { margin: 0; font-size: 1rem; font-weight: 600; font-variant-numeric: tabular-nums; }
.project__stats dd small { display: block; font-size: 0.7rem; font-weight: 400; color: var(--rl-text-muted); }
.project__stats dd a { color: var(--rl-accent); text-decoration: none; }

.project__folders { display: flex; flex-wrap: wrap; gap: 0.35rem; margin: 0; }
.project__folder { display: inline-flex; align-items: baseline; gap: 0.3rem; padding: 0.1rem 0.45rem; border-radius: 999px; background: var(--rl-surface-sunken); }
.project__folder--off { opacity: 0.55; text-decoration: line-through; }
.project__note { margin: 0; }
.project__created { margin: auto 0 0; font-size: 0.76rem; }

/* ---- installed to a phone ----------------------------------------------- */

/* viewport-fit=cover is what stops an installed app looking like a web page in a box:
   it paints to the edge of the screen. The cost is that the notch, the rounded corners
   and the home indicator now sit over the content, so every edge has to pay the inset
   back. Only in standalone — in an ordinary browser tab the chrome already does this. */
@media (display-mode: standalone) {
    .app__content {
        padding-left: env(safe-area-inset-left);
        padding-right: env(safe-area-inset-right);
        padding-bottom: env(safe-area-inset-bottom);
    }

    .app__content:not(:has(> .chat, > .admin, > .report, > .wbp, > .pe-host, > .cs, > .arcade, > .reader)) {
        padding:
            calc(1.5rem + env(safe-area-inset-top))
            calc(2rem + env(safe-area-inset-right))
            calc(3rem + env(safe-area-inset-bottom))
            calc(2rem + env(safe-area-inset-left));
    }

    .app__sidebar {
        padding-top: env(safe-area-inset-top);
        padding-bottom: env(safe-area-inset-bottom);
    }

    .app__reveal {
        top: calc(0.55rem + env(safe-area-inset-top));
        left: calc(0.55rem + env(safe-area-inset-left));
    }
}

/* A finger is not a mouse pointer. Buttons designed for a cursor are usable but fiddly at
   arm's length on a phone, and the whole point of the mobile case is doing this while not
   sitting down. Keyed off the input device rather than the width, so a touch laptop gets
   it and a narrow desktop window does not. */
@media (pointer: coarse) {
    .btn { min-height: 2.5rem; }
}

/* ---- notification settings ---------------------------------------------- */
.push__actions { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; }
.push__on { margin-right: auto; font-size: 0.9rem; color: var(--rl-text-muted); }
.push__heading { margin: 1.1rem 0 0.4rem; font-size: 0.95rem; }

.push__list { display: flex; flex-direction: column; gap: 0.4rem; margin: 0; padding: 0; list-style: none; }

.push__device {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    padding: 0.55rem 0.7rem;
    border: 1px solid var(--rl-border);
    border-radius: 8px;
    background: var(--rl-surface-sunken);
}

.push__device > div { display: flex; flex-direction: column; gap: 0.1rem; min-width: 0; }
.push__error { color: var(--rl-danger); }

/* ---- project management: kanban boards -----------------------------------
   Components/Pages/Boards. Colours come from a fixed palette stored by name
   (KanbanColors). Each .kb-tint--<name> sets --kb-tint, and whatever wears it
   (a board stripe, a column edge, a label, a badge) mixes from that. Dark
   shades are redefined below so every tint reads on both themes.
-------------------------------------------------------------------------- */
.kb-tint--gray   { --kb-tint: #6b7280; }
.kb-tint--red    { --kb-tint: #d64545; }
.kb-tint--orange { --kb-tint: #dd6b20; }
.kb-tint--yellow { --kb-tint: #b88a00; }
.kb-tint--green  { --kb-tint: #2f9e57; }
.kb-tint--teal   { --kb-tint: #0f9484; }
.kb-tint--blue   { --kb-tint: #2f6df6; }
.kb-tint--purple { --kb-tint: #8b5cf6; }
.kb-tint--pink   { --kb-tint: #d6409f; }

@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .kb-tint--gray   { --kb-tint: #9aa3b2; }
    :root:not([data-theme="light"]) .kb-tint--red    { --kb-tint: #ff7b72; }
    :root:not([data-theme="light"]) .kb-tint--orange { --kb-tint: #ff9f5e; }
    :root:not([data-theme="light"]) .kb-tint--yellow { --kb-tint: #f0c05a; }
    :root:not([data-theme="light"]) .kb-tint--green  { --kb-tint: #5bd08c; }
    :root:not([data-theme="light"]) .kb-tint--teal   { --kb-tint: #35d3bd; }
    :root:not([data-theme="light"]) .kb-tint--blue   { --kb-tint: #6f9bff; }
    :root:not([data-theme="light"]) .kb-tint--purple { --kb-tint: #b794ff; }
    :root:not([data-theme="light"]) .kb-tint--pink   { --kb-tint: #f78fcf; }
}
:root[data-theme="dark"] .kb-tint--gray   { --kb-tint: #9aa3b2; }
:root[data-theme="dark"] .kb-tint--red    { --kb-tint: #ff7b72; }
:root[data-theme="dark"] .kb-tint--orange { --kb-tint: #ff9f5e; }
:root[data-theme="dark"] .kb-tint--yellow { --kb-tint: #f0c05a; }
:root[data-theme="dark"] .kb-tint--green  { --kb-tint: #5bd08c; }
:root[data-theme="dark"] .kb-tint--teal   { --kb-tint: #35d3bd; }
:root[data-theme="dark"] .kb-tint--blue   { --kb-tint: #6f9bff; }
:root[data-theme="dark"] .kb-tint--purple { --kb-tint: #b794ff; }
:root[data-theme="dark"] .kb-tint--pink   { --kb-tint: #f78fcf; }

.kb-btnIcon { width: 1rem; height: 1rem; vertical-align: -0.15rem; }
.btn--on { background: var(--rl-surface-sunken); border-color: var(--rl-accent); }

.kb-check { display: inline-flex; align-items: center; gap: 0.35rem; font-size: 0.88rem; white-space: nowrap; }

.kb-search {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.3rem 0.6rem;
    border: 1px solid var(--rl-border);
    border-radius: 8px;
    background: var(--rl-surface-raised);
    color: var(--rl-text-muted);
}
.kb-search svg { width: 1rem; height: 1rem; flex: none; }
.kb-search input { border: none; background: none; color: var(--rl-text); font: inherit; outline: none; min-width: 10rem; }

.kb-progress {
    display: block;
    height: 6px;
    border-radius: 99px;
    background: var(--rl-surface-sunken);
    overflow: hidden;
}
.kb-progress > span { display: block; height: 100%; background: var(--rl-ok); border-radius: inherit; transition: width 0.2s ease; }
.kb-progress--inline { display: inline-block; width: 3.5rem; height: 5px; vertical-align: middle; }

.kb-swatches { display: flex; flex-wrap: wrap; gap: 0.35rem; }
.kb-swatch {
    width: 1.5rem;
    height: 1.5rem;
    border-radius: 50%;
    border: 2px solid transparent;
    background: var(--kb-tint);
    cursor: pointer;
    padding: 0;
    box-shadow: 0 0 0 1px var(--rl-border);
}
.kb-swatch--none { background: var(--rl-surface-sunken); color: var(--rl-text-muted); font-size: 0.8rem; line-height: 1; }
.kb-swatch--on { border-color: var(--rl-bg); box-shadow: 0 0 0 2px var(--rl-text); }

.kb-label {
    display: inline-flex;
    align-items: center;
    gap: 0.2rem;
    max-width: 100%;
    padding: 0.05rem 0.45rem;
    border-radius: 99px;
    font-size: 0.72rem;
    font-weight: 600;
    line-height: 1.5;
    background: color-mix(in srgb, var(--kb-tint, var(--rl-text-muted)) 18%, transparent);
    color: color-mix(in srgb, var(--kb-tint, var(--rl-text-muted)) 70%, var(--rl-text));
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.kb-label svg { width: 0.8rem; height: 0.8rem; }
.kb-label--toggle { border: 1px dashed color-mix(in srgb, var(--kb-tint) 50%, transparent); cursor: pointer; font-size: 0.8rem; padding: 0.15rem 0.6rem; opacity: 0.7; }
.kb-label--toggle:hover { opacity: 1; }
.kb-label--on { border-style: solid; opacity: 1; background: color-mix(in srgb, var(--kb-tint) 30%, transparent); }

.kb-badge {
    display: inline-flex;
    align-items: center;
    gap: 0.2rem;
    padding: 0 0.4rem;
    border-radius: 6px;
    font-size: 0.72rem;
    font-weight: 600;
    line-height: 1.6;
    background: color-mix(in srgb, var(--kb-tint, var(--rl-text-muted)) 15%, transparent);
    color: color-mix(in srgb, var(--kb-tint, var(--rl-text-muted)) 75%, var(--rl-text));
    white-space: nowrap;
}
.kb-badge svg { width: 0.8rem; height: 0.8rem; }

.kb-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.6rem;
    padding: 3rem 1rem;
    color: var(--rl-text-muted);
    text-align: center;
}
.kb-empty svg { width: 2.5rem; height: 2.5rem; opacity: 0.6; }

/* index */
.kb-index__toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 0.75rem; margin: 0.5rem 0 1rem; }
.kb-index__toolbar select, .kb__toolbar select {
    font: inherit;
    font-size: 0.88rem;
    padding: 0.3rem 0.5rem;
    border: 1px solid var(--rl-border);
    border-radius: 8px;
    background: var(--rl-surface-raised);
    color: var(--rl-text);
}

.kb-tiles { display: grid; grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr)); gap: 1rem; }
.kb-tile {
    position: relative;
    border: 1px solid var(--rl-border);
    border-top: 4px solid var(--kb-tint, var(--rl-border));
    border-radius: var(--rl-radius);
    background: var(--rl-surface);
    transition: box-shadow 0.15s ease, transform 0.15s ease;
}
.kb-tile:hover { box-shadow: 0 6px 20px rgba(0, 0, 0, 0.1); transform: translateY(-1px); }
.kb-tile--archived { opacity: 0.65; }
.kb-tile__link { display: flex; flex-direction: column; gap: 0.5rem; padding: 0.9rem 1rem; color: inherit; text-decoration: none; height: 100%; }
.kb-tile__name { margin: 0; padding-right: 5.5rem; font-size: 1.02rem; overflow-wrap: anywhere; }
.kb-tile__desc {
    margin: 0;
    font-size: 0.85rem;
    color: var(--rl-text-muted);
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
.kb-tile__stats { display: flex; flex-wrap: wrap; gap: 0.35rem 0.8rem; margin: 0; font-size: 0.8rem; color: var(--rl-text-muted); }
.kb-tile__stats .pill { margin-left: 0; }
.kb-tile__overdue { color: var(--rl-danger); font-weight: 600; }
.kb-tile__when { margin: 0; margin-top: auto; font-size: 0.75rem; color: var(--rl-text-muted); }
.kb-tile__actions { position: absolute; top: 0.55rem; right: 0.55rem; display: flex; gap: 0.1rem; opacity: 0; transition: opacity 0.15s ease; }
.kb-tile:hover .kb-tile__actions, .kb-tile:focus-within .kb-tile__actions { opacity: 1; }
.kb-tile__actions .icon-btn--star { opacity: 1; }
@media (hover: none) { .kb-tile__actions { opacity: 1; } }

/* board */
.kb { flex: 1; min-height: 0; display: flex; flex-direction: column; padding: 1rem 1.25rem 0; overflow-y: auto; }
.kb__head { margin-bottom: 0.5rem; }
.kb__crumbs { display: flex; flex-wrap: wrap; align-items: center; gap: 0.35rem; font-size: 0.82rem; color: var(--rl-text-muted); margin-bottom: 0.35rem; }
.kb__crumbs a { color: var(--rl-text-muted); text-decoration: none; }
.kb__crumbs a:hover { color: var(--rl-accent); text-decoration: underline; }
.kb__crumbCard { display: inline-flex; align-items: center; gap: 0.2rem; font-weight: 600; }
.kb__crumbCard svg { width: 0.85rem; height: 0.85rem; }
.kb__titleRow { display: flex; flex-wrap: wrap; align-items: center; gap: 0.6rem; }
.kb__swatch { width: 0.9rem; height: 0.9rem; border-radius: 4px; background: var(--kb-tint, var(--rl-border)); flex: none; }
.kb__title { margin: 0; font-size: 1.35rem; cursor: text; border-radius: 6px; padding: 0 0.2rem; }
.kb__title:hover { background: var(--rl-surface-sunken); }
.kb__titleInput {
    font: inherit;
    font-size: 1.35rem;
    font-weight: 600;
    padding: 0 0.2rem;
    border: 1px solid var(--rl-accent);
    border-radius: 6px;
    background: var(--rl-surface-raised);
    color: var(--rl-text);
    min-width: 16rem;
}
.kb__titleRow .pill { margin-left: 0; }
.kb__stats { display: flex; flex-wrap: wrap; align-items: center; gap: 0.35rem 0.9rem; font-size: 0.82rem; color: var(--rl-text-muted); }
.kb__statBar { display: inline-flex; align-items: center; gap: 0.35rem; }
.kb__statBad { color: var(--rl-danger); font-weight: 600; }
.kb__headActions { display: flex; gap: 0.4rem; margin-left: auto; }
.kb__desc { margin: 0.35rem 0 0; font-size: 0.88rem; color: var(--rl-text-muted); max-width: 60rem; white-space: pre-line; }

.kb-panel { max-width: 52rem; }
.kb-labelList { list-style: none; margin: 0 0 0.75rem; padding: 0; display: flex; flex-direction: column; gap: 0.5rem; }
.kb-labelList li, .kb-labelNew { display: flex; flex-wrap: wrap; align-items: center; gap: 0.6rem; }
.kb-labelList li > .kb-label { min-width: 5rem; justify-content: center; }
.kb-labelList input, .kb-labelNew input {
    font: inherit;
    font-size: 0.88rem;
    padding: 0.3rem 0.5rem;
    border: 1px solid var(--rl-border);
    border-radius: 8px;
    background: var(--rl-surface-raised);
    color: var(--rl-text);
    width: 11rem;
}

.kb__toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 0.6rem; margin: 0.25rem 0 0.75rem; }
.kb__toolbarNote { font-size: 0.8rem; color: var(--rl-text-muted); }

.kb__columns {
    flex: 1 0 auto;
    min-height: 22rem;
    display: flex;
    align-items: flex-start;
    gap: 0.75rem;
    overflow-x: auto;
    padding-bottom: 1rem;
}

.kb-col {
    flex: 0 0 18.5rem;
    max-height: calc(100dvh - 12rem);
    display: flex;
    flex-direction: column;
    border: 1px solid var(--rl-border);
    border-top: 3px solid var(--kb-tint, var(--rl-border));
    border-radius: var(--rl-radius);
    background: var(--rl-surface-raised);
    transition: box-shadow 0.1s ease;
}
.kb-col--over { background: color-mix(in srgb, var(--rl-danger) 7%, var(--rl-surface-raised)); }
.kb-col.kb-over { outline: 2px dashed var(--rl-accent); outline-offset: -3px; }
.kb-col.kb-over--before { box-shadow: -4px 0 0 var(--rl-accent); }
.kb-col.kb-over--after { box-shadow: 4px 0 0 var(--rl-accent); }

.kb-col__head { display: flex; align-items: center; gap: 0.3rem; padding: 0.45rem 0.45rem 0.35rem; cursor: grab; }
.kb-col__head:active { cursor: grabbing; }
.kb-col__name { flex: 1; margin: 0; font-size: 0.92rem; font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.kb-col__fold svg { transform: rotate(90deg); transition: transform 0.15s ease; }
.kb-col__done { width: 1rem; height: 1rem; color: var(--rl-ok); flex: none; }
.kb-col__count {
    font-size: 0.72rem;
    font-weight: 600;
    padding: 0.05rem 0.45rem;
    border-radius: 99px;
    background: var(--rl-surface-sunken);
    color: var(--rl-text-muted);
    white-space: nowrap;
}
.kb-col__count--over { background: var(--rl-danger); color: #fff; }

.kb-col--collapsed { flex-basis: 2.9rem; }
.kb-col--collapsed .kb-col__head { flex-direction: column; padding: 0.45rem 0.2rem 0.75rem; gap: 0.5rem; }
.kb-col--collapsed .kb-col__fold svg { transform: none; }
.kb-col--collapsed .kb-col__name { writing-mode: vertical-rl; white-space: nowrap; flex: none; max-height: 16rem; }

.kb-col__menu {
    margin: 0 0.5rem 0.5rem;
    padding: 0.65rem;
    border: 1px solid var(--rl-border);
    border-radius: 8px;
    background: var(--rl-surface);
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}
.kb-col__menu .field { margin: 0; }
.kb-col__menuRow { display: flex; flex-wrap: wrap; gap: 0.4rem; margin-top: 0.35rem; }
.kb-col__danger { display: flex; flex-direction: column; gap: 0.35rem; padding-top: 0.5rem; border-top: 1px solid var(--rl-border); font-size: 0.85rem; }
.kb-col__danger select {
    font: inherit;
    padding: 0.3rem 0.4rem;
    border: 1px solid var(--rl-border);
    border-radius: 8px;
    background: var(--rl-surface-raised);
    color: var(--rl-text);
}

.kb-col__cards { flex: 1 1 auto; min-height: 2.5rem; overflow-y: auto; display: flex; flex-direction: column; gap: 0.5rem; padding: 0.2rem 0.5rem 0.4rem; }
.kb-col__empty { margin: 0; padding: 0.9rem 0.5rem; text-align: center; font-size: 0.8rem; color: var(--rl-text-muted); border: 1px dashed var(--rl-border); border-radius: 8px; }
.kb-col__foot { padding: 0.25rem 0.5rem 0.5rem; }
.kb-col__add {
    display: flex;
    align-items: center;
    gap: 0.35rem;
    width: 100%;
    padding: 0.4rem 0.5rem;
    border: none;
    border-radius: 8px;
    background: none;
    color: var(--rl-text-muted);
    font: inherit;
    font-size: 0.88rem;
    cursor: pointer;
    text-align: left;
}
.kb-col__add:hover { background: var(--rl-surface-sunken); color: var(--rl-text); }
.kb-col__add svg { width: 1rem; height: 1rem; }
.kb-col__addInput {
    width: 100%;
    font: inherit;
    font-size: 0.9rem;
    padding: 0.45rem 0.55rem;
    border: 1px solid var(--rl-accent);
    border-radius: 8px;
    background: var(--rl-surface);
    color: var(--rl-text);
}
.kb-col--new { background: transparent; border-style: dashed; border-top-width: 1px; padding: 0.4rem; }

.kb-card {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    padding: 0.55rem 0.65rem;
    border: 1px solid var(--rl-border);
    border-radius: 8px;
    background: var(--rl-surface);
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06);
    cursor: grab;
    flex: none;
}
.kb-card:hover { border-color: color-mix(in srgb, var(--rl-accent) 45%, var(--rl-border)); }
.kb-card:focus-visible { outline: 2px solid var(--rl-accent); outline-offset: 1px; }
.kb-card.kb-tint { border-top: 3px solid var(--kb-tint); }
.kb-card--done .kb-card__title { color: var(--rl-text-muted); text-decoration: line-through; text-decoration-thickness: 1px; }
.kb-card--archived { opacity: 0.6; border-style: dashed; }
.kb-card.kb-dragging, .kb-col.kb-dragging { opacity: 0.4; }
.kb-card.kb-over--before::before,
.kb-card.kb-over--after::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    height: 3px;
    border-radius: 2px;
    background: var(--rl-accent);
}
.kb-card.kb-over--before::before { top: -5px; }
.kb-card.kb-over--after::after { bottom: -5px; }
/* During a drag the pointer must hit the card itself, not the links and spans inside it. */
.kb-drag-active .kb-card * { pointer-events: none; }

.kb-card__labels { display: flex; flex-wrap: wrap; gap: 0.25rem; }
.kb-card__title { margin: 0; font-size: 0.9rem; line-height: 1.35; overflow-wrap: anywhere; }
.kb-card__meta { display: flex; flex-wrap: wrap; align-items: center; gap: 0.3rem 0.55rem; }
.kb-card__meta:empty { display: none; }
.kb-card__meta .pill { margin-left: 0; }
.kb-meta { display: inline-flex; align-items: center; gap: 0.2rem; font-size: 0.75rem; color: var(--rl-text-muted); }
.kb-meta svg { width: 0.85rem; height: 0.85rem; }
.kb-meta--done { color: var(--rl-ok); font-weight: 600; }
.kb-meta--est { padding: 0 0.35rem; border-radius: 99px; background: var(--rl-surface-sunken); font-weight: 600; }
.kb-card__sub {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.3rem 0.45rem;
    border-radius: 6px;
    background: var(--rl-surface-sunken);
    color: var(--rl-text);
    font-size: 0.75rem;
    font-weight: 600;
    text-decoration: none;
}
.kb-card__sub:hover { color: var(--rl-accent); }
.kb-card__sub svg { width: 0.9rem; height: 0.9rem; }
.kb-card__sub .kb-progress { flex: 1; }

/* card editor */
.kb-editor { place-items: start center; overflow-y: auto; padding: 2.5rem 1rem; }
.kb-editor__box {
    position: relative;
    width: min(58rem, 100%);
    padding: 1.1rem 1.3rem 1.3rem;
    border: 1px solid var(--rl-border);
    border-top: 4px solid var(--kb-tint, var(--rl-border));
    border-radius: var(--rl-radius);
    background: var(--rl-surface-raised);
    box-shadow: 0 18px 48px rgba(0, 0, 0, 0.35);
}
.kb-editor__head { display: flex; align-items: flex-start; gap: 0.5rem; }
.kb-editor__title {
    flex: 1;
    font: inherit;
    font-size: 1.2rem;
    font-weight: 700;
    padding: 0.2rem 0.35rem;
    border: 1px solid transparent;
    border-radius: 6px;
    background: none;
    color: var(--rl-text);
}
.kb-editor__title:hover { border-color: var(--rl-border); }
.kb-editor__title:focus { border-color: var(--rl-accent); background: var(--rl-surface); outline: none; }
.kb-editor__where { margin: 0.1rem 0 0.9rem 0.4rem; font-size: 0.82rem; color: var(--rl-text-muted); }
.kb-editor__body { display: grid; grid-template-columns: minmax(0, 1fr) 15.5rem; gap: 1.5rem; }
.kb-editor__section { margin-bottom: 1.25rem; }
.kb-editor__section h3, .kb-editor__sub h3, .kb-editor__actions h3 {
    display: flex;
    align-items: center;
    gap: 0.35rem;
    margin: 0 0 0.45rem;
    font-size: 0.8rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--rl-text-muted);
}
.kb-editor__sub h3 svg { width: 1rem; height: 1rem; }
.kb-editor__sectionHead { display: flex; align-items: center; gap: 0.6rem; margin-bottom: 0.45rem; }
.kb-editor__sectionHead h3 { margin: 0; flex: 1; }
.kb-editor__count { font-size: 0.8rem; font-weight: 600; color: var(--rl-text-muted); }
.kb-editor__labels { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.kb-editor__desc, .kb-editor__input {
    width: 100%;
    font: inherit;
    font-size: 0.9rem;
    padding: 0.5rem 0.6rem;
    border: 1px solid var(--rl-border);
    border-radius: 8px;
    background: var(--rl-surface);
    color: var(--rl-text);
    resize: vertical;
}
.kb-editor__input { margin-top: 0.4rem; }
.kb-editor__placeholder {
    width: 100%;
    min-height: 3.5rem;
    padding: 0.6rem;
    border: 1px dashed var(--rl-border);
    border-radius: 8px;
    background: none;
    color: var(--rl-text-muted);
    font: inherit;
    font-size: 0.88rem;
    text-align: left;
    cursor: pointer;
}
.kb-editor__placeholder:hover { background: var(--rl-surface-sunken); }
.kb-editor__md { font-size: 0.9rem; overflow-wrap: anywhere; }
.kb-editor__md > :first-child { margin-top: 0; }
.kb-editor__md > :last-child { margin-bottom: 0; }
.kb-editor__md pre { padding: 0.6rem; border-radius: 8px; background: var(--rl-surface-sunken); overflow-x: auto; }

.kb-checklist { list-style: none; margin: 0.5rem 0 0; padding: 0; display: flex; flex-direction: column; gap: 0.15rem; }
.kb-checklist li { display: flex; align-items: center; gap: 0.45rem; padding: 0.1rem 0.25rem; border-radius: 6px; }
.kb-checklist li:hover { background: var(--rl-surface-sunken); }
.kb-checklist__text { flex: 1; font: inherit; font-size: 0.9rem; padding: 0.2rem 0.3rem; border: 1px solid transparent; border-radius: 6px; background: none; color: var(--rl-text); }
.kb-checklist__text:focus { border-color: var(--rl-accent); background: var(--rl-surface); outline: none; }
.kb-checklist__done .kb-checklist__text { color: var(--rl-text-muted); text-decoration: line-through; }

.kb-comments { list-style: none; margin: 0.75rem 0 0; padding: 0; display: flex; flex-direction: column; gap: 0.75rem; }
.kb-comments li { padding: 0.55rem 0.7rem; border: 1px solid var(--rl-border); border-radius: 8px; background: var(--rl-surface); }
.kb-comments__head { display: flex; align-items: center; gap: 0.45rem; margin-bottom: 0.3rem; font-size: 0.85rem; }
.kb-comments__head .side__avatar { width: 1.5rem; height: 1.5rem; font-size: 0.72rem; }
.kb-comments__when { flex: 1; font-size: 0.75rem; color: var(--rl-text-muted); }

.kb-editor__side .field { margin-bottom: 0.7rem; }
.kb-editor__side .field input, .kb-editor__side .field select { font-size: 0.88rem; }
.kb-editor__side .kb-due { margin-top: 0.2rem; align-self: flex-start; }
.kb-editor__sub, .kb-editor__actions {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    margin-top: 1rem;
    padding-top: 0.8rem;
    border-top: 1px solid var(--rl-border);
}
.kb-editor__sub .card__meta { margin: 0; }
.kb-editor__sub select {
    font: inherit;
    font-size: 0.85rem;
    padding: 0.3rem 0.4rem;
    border: 1px solid var(--rl-border);
    border-radius: 8px;
    background: var(--rl-surface);
    color: var(--rl-text);
}
.kb-editor__stamp { margin: 1rem 0 0; font-size: 0.75rem; color: var(--rl-text-muted); line-height: 1.5; }

/* agenda */
.kb-agenda__group { margin-bottom: 1.5rem; max-width: 60rem; }
.kb-agenda__group h2 { display: flex; align-items: center; gap: 0.5rem; margin: 0 0 0.5rem; font-size: 1rem; }
.kb-agenda__group--overdue h2 { color: var(--rl-danger); }
.kb-agenda__n { font-size: 0.72rem; font-weight: 600; padding: 0.05rem 0.45rem; border-radius: 99px; background: var(--rl-surface-sunken); color: var(--rl-text-muted); }
.kb-agenda__list { list-style: none; margin: 0; padding: 0; border: 1px solid var(--rl-border); border-radius: var(--rl-radius); background: var(--rl-surface); }
.kb-agenda__list li { display: flex; flex-wrap: wrap; align-items: center; gap: 0.35rem 0.75rem; padding: 0.55rem 0.8rem; border-top: 1px solid var(--rl-border); }
.kb-agenda__list li:first-child { border-top: none; }
.kb-agenda__list .kb-due { min-width: 5.5rem; justify-content: center; }
.kb-agenda__title { flex: 1; min-width: 10rem; font-weight: 600; color: var(--rl-text); text-decoration: none; overflow-wrap: anywhere; }
.kb-agenda__title:hover { color: var(--rl-accent); text-decoration: underline; }
.kb-agenda__where { font-size: 0.8rem; color: var(--rl-text-muted); }
.kb-agenda__where a { color: inherit; }

@media (max-width: 780px) {
    .kb { padding: 0.75rem 0.75rem 0; }
    .kb-col { flex-basis: min(85vw, 20rem); max-height: none; }
    .kb__headActions { margin-left: 0; }
    .kb-editor { padding: 0.75rem 0.5rem; }
    .kb-editor__body { grid-template-columns: 1fr; }
    .kb-search input { min-width: 0; width: 100%; }
}

@media (prefers-reduced-motion: reduce) {
    .kb-tile, .kb-progress > span, .kb-col__fold svg { transition: none; }
    .kb-tile:hover { transform: none; }
}

/* Set up this computer. The warning has to be seen for the whole run, since leaving strands the last step. */
.setup__packages { list-style: none; margin: 0 0 1rem; padding: 0; display: grid; gap: 0.6rem; }
.setup__package { display: grid; gap: 0.2rem; padding: 0.6rem 0.8rem; border: 1px solid var(--rl-border); border-radius: 8px; }
.setup__package label { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.3rem 0.6rem; cursor: pointer; }
.setup__package small { color: var(--rl-text-muted); }
.setup__package--missing { opacity: 0.7; }
.setup__package--missing label { cursor: default; }
.setup__check { display: flex; align-items: center; gap: 0.5rem; margin: 0.4rem 0 0.8rem; }
.setup__warning { border-color: var(--rl-warning, #e3b341); position: sticky; top: 0.5rem; z-index: 2; background: var(--rl-surface, inherit); }
.setup__command { display: flex; gap: 0.6rem; align-items: flex-start; }
.setup__command code {
    flex: 1; min-width: 0; padding: 0.6rem 0.8rem; border-radius: 8px; border: 1px solid var(--rl-border);
    background: var(--rl-surface-sunken); overflow-wrap: anywhere; user-select: all; font-size: 0.85rem;
}
.setup__download { justify-content: flex-start; align-items: center; }
.setup__unsigned { color: var(--rl-danger); }
.setup__hash { overflow-wrap: anywhere; font-size: 0.75rem; }
.setup__alternative { margin-top: 0.6rem; }
.setup__alternative summary { cursor: pointer; color: var(--rl-text-muted); }
.setup__phase { margin: 0; }
.setup__detail { margin: 0.15rem 0; overflow-wrap: anywhere; }
.setup__services { margin: 0.6rem 0 0; padding-left: 1.2rem; display: grid; gap: 0.3rem; }
.setup__service--ok strong { color: var(--rl-accent); }
.setup__service--bad { color: var(--rl-danger); }

/* ---- collaboration: invitations and sharing ------------------------------------------------------------ */

.side__count {
    margin-left: auto;
    min-width: 1.25rem;
    padding: 0 0.4rem;
    border-radius: 99px;
    background: var(--rl-accent);
    color: var(--rl-accent-contrast);
    font-size: 0.72rem;
    font-weight: 600;
    line-height: 1.25rem;
    text-align: center;
}

.side__count--mention { background: var(--rl-danger); color: #fff; }

.invite { display: grid; gap: 0.6rem; max-width: 46rem; }
.invite__row { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; }
.invite__pick { flex: 1 1 16rem; display: flex; }
.invite__pick input { flex: 1; min-width: 0; }
.invite__note { width: 100%; resize: vertical; }
.invite__head { font-size: 0.9rem; margin: 1rem 0 0.4rem; }
.invite__list { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.35rem; }
.invite__list li { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; }
.invite__list .pill { margin-left: 0; }
.invite__list .btn { margin-left: auto; }
.invite__who { font-weight: 600; }
.invite__when { color: var(--rl-text-muted); font-size: 0.8rem; }

.invites__list { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.75rem; }
.invites__item { display: flex; gap: 0.75rem; align-items: flex-start; }
.invites__icon { width: 1.25rem; height: 1.25rem; flex-shrink: 0; margin-top: 0.15rem; color: var(--rl-text-muted); }
.invites__body { flex: 1; min-width: 0; }
.invites__what { margin: 0; }
.invites__note {
    margin: 0.35rem 0 0;
    padding: 0.35rem 0.7rem;
    border-left: 3px solid var(--rl-border);
    color: var(--rl-text-muted);
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}
.invites__when { margin: 0.2rem 0 0; font-size: 0.78rem; color: var(--rl-text-muted); }
.invites__actions { display: flex; gap: 0.4rem; flex-shrink: 0; }

@media (max-width: 600px) {
    .invites__item { flex-wrap: wrap; }
    .invites__actions { width: 100%; justify-content: flex-end; }
}

.kb-tile__share { display: flex; flex-wrap: wrap; align-items: center; gap: 0.2rem 0.6rem; margin: 0; font-size: 0.78rem; color: var(--rl-text-muted); }
.kb-tile__share svg { width: 0.95rem; height: 0.95rem; }
.kb__sharedPill { border: 0; cursor: pointer; font: inherit; font-size: 0.7rem; }

/* ---- agents on cards ------------------------------------------------------------------------------------ */

.kb-card__agents { display: flex; flex-wrap: wrap; gap: 0.25rem; margin-top: 0.4rem; }
.kb-agent {
    display: inline-flex;
    align-items: center;
    gap: 0.2rem;
    padding: 0.05rem 0.4rem;
    border-radius: 99px;
    background: var(--rl-surface-sunken);
    color: var(--rl-text-muted);
    font-size: 0.72rem;
    font-weight: 600;
}
.kb-agent svg { width: 0.8rem; height: 0.8rem; }
.kb-agent--running { background: var(--rl-accent); color: var(--rl-accent-contrast); animation: kb-agent-pulse 1.6s ease-in-out infinite; }
.kb-agent--awaitingapproval { background: var(--rl-warning, #e3b341); color: #241a02; }
@keyframes kb-agent-pulse { 50% { opacity: 0.65; } }
@media (prefers-reduced-motion: reduce) { .kb-agent--running { animation: none; } }

.kb-agents__list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.6rem; }
.kb-agents__item { padding: 0.6rem 0.7rem; border: 1px solid var(--rl-border); border-radius: 8px; }
.kb-agents__head { display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem; }
.kb-agents__head svg { width: 1rem; height: 1rem; color: var(--rl-text-muted); }
.kb-agents__head .pill { text-decoration: none; }
.kb-agents__who { margin-left: auto; font-size: 0.78rem; color: var(--rl-text-muted); }
.kb-agents__outcome { margin: 0.35rem 0 0; font-size: 0.82rem; color: var(--rl-text-muted); }
.kb-agents__instructions { margin-top: 0.4rem; font-size: 0.88rem; }
.kb-agents__item .kb-col__menuRow { margin-top: 0.5rem; }
.kb-agents__add { margin-top: 0.6rem; padding: 0.7rem; border: 1px dashed var(--rl-border); border-radius: 8px; }
.kb-agents__add small.warn { display: block; color: var(--rl-danger); margin-bottom: 0.4rem; }
.kb-agents__problem { margin: 0 0 0.5rem; color: var(--rl-danger); font-size: 0.85rem; }

/* ---- user-facing status page ------------------------------------------- */
.pub__banner { font-size: 1.05rem; border-left: 0.35rem solid var(--rl-border); }
.pub__banner--up { border-left-color: var(--rl-ok); }
.pub__banner--partial { border-left-color: var(--rl-warning, #e3b341); }
.pub__banner--down { border-left-color: var(--rl-danger); }
.pub__note { display: block; color: var(--rl-text-muted); font-size: 0.78rem; margin-top: 0.15rem; }
.pub__none { color: var(--rl-text-muted); }


/* ---- collapsed rail -------------------------------------------------------
   The sidebar folded down to icons. Words, carets and the rows inside a fold are hidden; ui.js adds the tooltip a
   hovered icon gets and the flyout a clicked fold opens.
-------------------------------------------------------------------------- */
:root[data-sidebar="collapsed"] .side { width: 3.4rem; }

:root[data-sidebar="collapsed"] .side__top {
    flex-direction: column;
    justify-content: flex-start;
    gap: 0.2rem;
    padding: 0.6rem 0 0.4rem;
}

:root[data-sidebar="collapsed"] .side__toggle { order: -1; margin-left: 0; }

/* The search box has no room on the rail: a glass straight under the toggle opens it in a flyout (ui.js). */
:root[data-sidebar="collapsed"] .side__findBtn { display: inline-flex; order: -1; }
:root[data-sidebar="collapsed"] .side__find { display: none; }

:root[data-sidebar="collapsed"] .side__brand { margin: 0.1rem 0 0; }
:root[data-sidebar="collapsed"] .side__brandMark { margin: 0; width: 1.6rem; height: 1.6rem; }
:root[data-sidebar="collapsed"] .side__product {
    position: relative;
    justify-content: center;
    margin: 0.1rem 0.45rem 0.2rem;
    padding: 0.1rem 0;
}

:root[data-sidebar="collapsed"] .side__brandName,
:root[data-sidebar="collapsed"] .side__label,
:root[data-sidebar="collapsed"] .side__sectionName,
:root[data-sidebar="collapsed"] .side__caret,
:root[data-sidebar="collapsed"] .side__userName,
:root[data-sidebar="collapsed"] .side__chatNew { display: none; }

:root[data-sidebar="collapsed"] .side__scroll { overflow-x: hidden; }
:root[data-sidebar="collapsed"] .side__nav { padding: 0 0.45rem; }

/* The rows inside a fold live in the flyout instead. */
:root[data-sidebar="collapsed"] .side__nav > .side__more > :not(summary),
:root[data-sidebar="collapsed"] .side__nav > .side__productFold > :not(summary),
:root[data-sidebar="collapsed"] .side__main > .side__pm > :not(summary),
:root[data-sidebar="collapsed"] .side__main > .side__tools > :not(summary) { display: none; }

/* Studio Umbra's pages are reached from the eclipse at the top, all but Project management and Tools: each keeps an
   icon of its own on the rail, lifted out of the hidden block and set just above Library (and Admin under it), Project
   management first. Both are order 1, so source order keeps Project management right above Tools. */
:root[data-sidebar="collapsed"] .side__main { display: contents; }
:root[data-sidebar="collapsed"] .side__main > :not(.side__pm):not(.side__tools) { display: none; }
:root[data-sidebar="collapsed"] .side__main > .side__pm,
:root[data-sidebar="collapsed"] .side__main > .side__tools { order: 1; }
:root[data-sidebar="collapsed"] .side__nav > .side__link--section,
:root[data-sidebar="collapsed"] .side__nav > .side__more { order: 2; }

:root[data-sidebar="collapsed"] .side__link {
    position: relative;
    justify-content: center;
    padding: 0.55rem 0;
}

:root[data-sidebar="collapsed"] .side__link svg { width: 1.2rem; height: 1.2rem; }
:root[data-sidebar="collapsed"] .side__nav .side__link::before,
:root[data-sidebar="collapsed"] .side__nav .side__chatLink::before { content: none; width: 0; flex-basis: 0; }

:root[data-sidebar="collapsed"] .side__nav .side__more { margin-top: 0.05rem; }

:root[data-sidebar="collapsed"] .side__nav .side__more > summary {
    justify-content: center;
    padding: 0.55rem 0;
    margin: 0.05rem 0;
    color: var(--rl-text-muted);
}

:root[data-sidebar="collapsed"] .side__chatRow { margin: 0.05rem 0.45rem; padding-right: 0; }
:root[data-sidebar="collapsed"] .side__chatLink { justify-content: center; padding: 0.55rem 0; }

:root[data-sidebar="collapsed"] .side__more > summary .side__sectionIcon {
    width: 1.2rem;
    height: 1.2rem;
    color: inherit;
}

:root[data-sidebar="collapsed"] .side__more > summary:hover { color: var(--rl-text); }

/* Library sits between the folds on the rail, so it wears their muted icon rather than the heading's accent. */
:root[data-sidebar="collapsed"] .side__nav > .side__link--section { margin-top: 0.05rem; padding: 0.55rem 0; color: var(--rl-text-muted); }
:root[data-sidebar="collapsed"] .side__nav > .side__link--section:hover,
:root[data-sidebar="collapsed"] .side__nav > .side__link--section.active { color: var(--rl-text); }
:root[data-sidebar="collapsed"] .side__link--section .side__sectionIcon { color: inherit; }

/* A fold that holds the page being looked at stays lit, since its link is out of sight. */
:root[data-sidebar="collapsed"] .side__nav .side__more:has(.side__link.active) > summary {
    background: var(--rl-surface-sunken);
    color: var(--rl-text);
}

:root[data-sidebar="collapsed"] .side__count {
    position: absolute;
    top: 0.1rem;
    right: 0.2rem;
    min-width: 1rem;
    margin: 0;
    padding: 0 0.25rem;
    font-size: 0.62rem;
    line-height: 1rem;
}

:root[data-sidebar="collapsed"] .side__foot {
    flex-direction: column;
    align-items: stretch;
    gap: 0.2rem;
    padding: 0.5rem 0.45rem;
}

:root[data-sidebar="collapsed"] .side__footRow {
    flex-direction: column;
    align-items: stretch;
    gap: 0.2rem;
}

:root[data-sidebar="collapsed"] .side__foot form { display: flex; justify-content: center; }
:root[data-sidebar="collapsed"] .side__user { flex: none; justify-content: center; padding: 0.3rem 0; }

/* The tooltip and the flyout hang off <body>, outside the sidebar's clipped box. */
.rail-tip {
    position: fixed;
    z-index: 60;
    transform: translateY(-50%);
    padding: 0.3rem 0.6rem;
    border-radius: 6px;
    background: var(--rl-text);
    color: var(--rl-surface);
    font-size: 0.78rem;
    font-weight: 500;
    white-space: nowrap;
    pointer-events: none;
    box-shadow: 0 0.2rem 0.7rem rgba(0, 0, 0, 0.25);
}

.rail-fly {
    position: fixed;
    z-index: 55;
    min-width: 12.5rem;
    max-width: 18rem;
    overflow-y: auto;
    padding: 0.35rem;
    border: 1px solid var(--rl-border);
    border-radius: 10px;
    background: var(--rl-surface-raised);
    box-shadow: 0 0.5rem 1.6rem rgba(0, 0, 0, 0.3);
}

.rail-fly__title,
.rail-fly__head {
    padding: 0.35rem 0.6rem 0.25rem;
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--rl-text-muted);
}

.rail-fly__title { color: var(--rl-text); }
.rail-fly__sep { height: 1px; margin: 0.3rem 0.4rem; background: var(--rl-border); }

.rail-fly__item {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    box-sizing: border-box;
    width: 100%;
    padding: 0.42rem 0.6rem;
    border: 0;
    border-radius: 8px;
    background: none;
    color: var(--rl-text-muted);
    font: inherit;
    font-size: 0.86rem;
    text-align: left;
    text-decoration: none;
    white-space: nowrap;
    cursor: pointer;
}

.rail-fly__item svg { width: 1.05rem; height: 1.05rem; flex-shrink: 0; }
.rail-fly__item:hover,
.rail-fly__item:focus-visible { background: var(--rl-surface-sunken); color: var(--rl-text); outline: none; }
.rail-fly__item.active { background: var(--rl-surface-sunken); color: var(--rl-text); font-weight: 600; }

/* The search flyout: the sidebar's search box, with the pages it finds listed under it. The list scrolls, not the
   flyout, so the box stays in reach. */
.rail-fly--find { display: flex; flex-direction: column; width: 17rem; max-width: 17rem; overflow: hidden; }
.rail-fly--find input {
    margin: 0 0.25rem 0.35rem;
    border: 1px solid var(--rl-border);
    border-radius: 8px;
    background: var(--rl-surface-sunken);
    color: var(--rl-text);
    padding: 0.4rem 0.6rem;
    font: inherit;
    font-size: 0.86rem;
}
.rail-fly--find input:focus { outline: none; border-color: var(--rl-accent); box-shadow: var(--rl-ring); }
.rail-fly__list { min-height: 0; max-height: min(24rem, 60vh); overflow-x: hidden; overflow-y: auto; }
.rail-fly__item.is-on { background: var(--rl-surface-sunken); color: var(--rl-text); }
.rail-fly__item img { width: 1.05rem; height: 1.05rem; flex-shrink: 0; }
.rail-fly__item span,
.rail-fly__item small { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.rail-fly__item small { margin-left: auto; padding-left: 0.6rem; color: var(--rl-text-muted); font-size: 0.72rem; font-weight: 400; }
.rail-fly__empty { margin: 0; padding: 0.4rem 0.6rem; color: var(--rl-text-muted); font-size: 0.82rem; }

/* ---- sound effects page and designer ------------------------------------------------------------------------ */
.sfx-starters { display: grid; gap: .35rem; margin-bottom: 1rem; }
.sfx-starters summary { cursor: pointer; font-weight: 600; font-size: .9rem; }
.sfx-chips { display: flex; flex-wrap: wrap; gap: .35rem; padding: .5rem 0 .2rem; }
.sfx-takes { display: grid; gap: .8rem; }
.sfx-take { display: grid; gap: .45rem; padding: .7rem; border: 1px solid var(--rl-border); border-radius: var(--rl-radius); background: var(--rl-surface-sunken); }
.sfx-take--on { border-color: var(--rl-accent); }
.sfx-take audio { width: 100%; }
.ttm-chips { display: flex; flex-wrap: wrap; gap: .35rem; padding: .25rem 0 .2rem; }
.ttm-songs { display: grid; gap: .8rem; }
.ttm-song { display: grid; gap: .45rem; padding: .7rem; border: 1px solid var(--rl-border); border-radius: var(--rl-radius); background: var(--rl-surface-sunken); }
.ttm-song audio { width: 100%; }
.sfx-wave { margin: .7rem 0; }
.sfx-wave__label { display: block; margin-bottom: .2rem; font-size: .78rem; color: var(--rl-text-muted); }
.sfx-wave__canvas { display: block; width: 100%; height: 96px; border-radius: 10px; background: var(--rl-surface-sunken); --wave-color: var(--rl-accent); }
.sfx-designer input[type=range] { width: 100%; }
.sfx-designer .field--row, .qc .field--row, .sb-edit .field--row { flex-wrap: wrap; align-items: flex-start; }
.sfx-designer .field--row > span, .qc .field--row > span, .sb-edit .field--row > span { flex: 1 1 12rem; min-width: 0; }
.sfx-layer { display: flex; flex-wrap: wrap; align-items: center; gap: .8rem; padding: .45rem 0; border-top: 1px solid var(--rl-border); }

/* A RipCord board the person may look at and not change: the editors say so, and what edits is out of reach (see ChatBoardAccess). */
.kb__viewonly, .wbp__viewonly, .kb-editor__viewonly { margin: 0.4rem 0 0; padding: 0.3rem 0.6rem; border-radius: 6px; font-size: 0.82rem; background: var(--rl-surface-2, rgba(128, 128, 128, 0.14)); color: var(--rl-text-muted); }
.kb-col__foot[hidden], .kb-col--new[hidden] { display: none; }

/* A card's tick box for the Library's "Download for Unreal": over the corner of the card, and the bar that appears once something is ticked. */
.asset { position: relative; }
.asset__select { position: absolute; top: 0.65rem; left: 0.65rem; z-index: 2; display: flex; padding: 0.2rem 0.3rem; border-radius: 6px; background: var(--rl-surface); border: 1px solid var(--rl-border); cursor: pointer; }
.asset__select input { margin: 0; width: 1.05rem; height: 1.05rem; cursor: pointer; }
.library__selection { position: sticky; top: 0.5rem; z-index: 5; display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap; padding: 0.6rem 0.9rem; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.18); }
.library__selectionCount { font-weight: 600; }

/* ---- motion ---------------------------------------------------------------
   The shared keyframes behind the --rl-dur / --rl-ease tokens. A card rises
   in, a page fades in, a scrim fades, a dialog rises. Each page's own
   stylesheet names these rather than defining its own flavour.
-------------------------------------------------------------------------- */
@keyframes rl-fade { from { opacity: 0; } }
@keyframes rl-rise { from { opacity: 0; transform: translateY(8px) scale(0.98); } }
@keyframes rl-pop { from { opacity: 0; transform: scale(0.6); } }

/* ---- small screens, all pages ----------------------------------------------
   The page-specific blocks above keep their own breakpoints; these are the
   shared frames every admin-style page sits in.
-------------------------------------------------------------------------- */
@media (max-width: 780px) {
    .admin { padding: 0.75rem 0.85rem 1.5rem; }
    .admin__head { flex-wrap: wrap; gap: 0.5rem 0.75rem; }
    .admin__head h1 { font-size: 1.2rem; }
    .card { padding: 0.75rem 0.8rem; margin-bottom: 0.7rem; }
    .field--row { flex-wrap: wrap; gap: 0.5rem 1rem; }
    .library__filters > label { flex: 1 1 8rem; }
    .library__filters select { width: 100%; min-width: 0; }
    .assets { grid-template-columns: repeat(auto-fill, minmax(9.5rem, 1fr)); gap: 0.5rem; }
    .asset__thumb img, .asset__thumb--file, .asset__thumb--audio, .asset__thumb--video, .asset__thumb--mesh { height: 6.5rem; }
    .confirm__box { width: 100%; }
}

@media (max-width: 480px) {
    .admin { padding: 0.6rem 0.65rem 1.25rem; }
    .admin__headActions { width: 100%; flex-wrap: wrap; }
    .card__head { flex-direction: column; align-items: stretch; gap: 0.5rem; }
    .assets { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* ---- professional polish -----------------------------------------------------
   Written last so it settles things the page sheets above left open. Four faults, each
   found on a real page:
     1. .admin__lede had no rule at all, so every intro paragraph ran the full width.
     2. td.td--actions was display:flex. A flex td leaves the table's layout, so its row
        borders, height and column no longer lined up with the header (Agents, Scheduled).
     3. Tables, empty states and buttons had square or mixed corners.
     4. Number inputs, selects and toolbar inputs stretched to the full card width.
-------------------------------------------------------------------------- */
.admin__head { min-height: 2.4rem; }
.admin__status {
    margin: 0 0 0.75rem; padding: 0.5rem 0.85rem; border-radius: 10px; font-size: 0.9rem;
    background: color-mix(in srgb, var(--rl-text-muted) 8%, transparent);
}
.admin__status--ok { background: color-mix(in srgb, var(--rl-ok) 12%, transparent); border-color: color-mix(in srgb, var(--rl-ok) 45%, var(--rl-border)); }
.admin__status--bad { background: color-mix(in srgb, var(--rl-danger) 10%, transparent); }

/* Tables: a framed, rounded block with a quiet header, even cells, and the actions in their own column. */
.table { border-collapse: separate; border-spacing: 0; }
.table th, .table td { padding: 0.55rem 0.8rem; vertical-align: middle; }
.table th {
    background: var(--rl-surface-raised); white-space: nowrap;
    font-size: 0.72rem; font-weight: 600; letter-spacing: 0.05em; text-transform: uppercase;
}
.table tbody tr:last-child > td { border-bottom: 0; }
.table tbody tr { transition: background var(--rl-dur); }
.table tbody tr:hover > td { background: color-mix(in srgb, var(--rl-surface-sunken) 50%, transparent); }
.admin > .table { border: 1px solid var(--rl-border); border-radius: var(--rl-radius); overflow: hidden; background: var(--rl-surface); }
.table td.td--actions { display: table-cell; width: 1%; text-align: right; white-space: nowrap; }
.td--actions > .btn + .btn { margin-left: 0.35rem; }
.table .roles__desc { max-width: 30rem; margin-top: 0.15rem; font-size: 0.8rem; overflow-wrap: anywhere; }

/* Buttons: one height, one radius, and a danger button that reads as one. */
.btn { line-height: 1.35; }
.btn--small { padding: 0.28rem 0.65rem; font-size: 0.84rem; border-radius: 8px; }
.btn--danger { border-color: color-mix(in srgb, var(--rl-danger) 40%, var(--rl-border)); }
.btn--danger:hover { background: color-mix(in srgb, var(--rl-danger) 12%, var(--rl-surface-raised)); }
.card__actions { align-items: center; }
.card__actions input:not([type="checkbox"]), .admin__headActions input:not([type="checkbox"]) {
    font: inherit; padding: 0.4rem 0.65rem; min-width: 12rem;
    border: 1px solid var(--rl-border); border-radius: 8px; background: var(--rl-surface-raised); color: var(--rl-text);
    transition: border-color var(--rl-dur), box-shadow var(--rl-dur);
}
.card__actions input:not([type="checkbox"]):focus-visible, .admin__headActions input:not([type="checkbox"]):focus-visible {
    outline: none; border-color: var(--rl-accent); box-shadow: var(--rl-ring);
}
.card__actions label { display: inline-flex; align-items: center; gap: 0.35rem; font-size: 0.88rem; color: var(--rl-text-muted); cursor: pointer; }
.pill { display: inline-flex; align-items: center; line-height: 1.4; }

/* Form controls that held one short value no longer stretch across the card. */
.field input[type="number"] { width: 8rem; max-width: 100%; }
.field input[type="color"] { cursor: pointer; }
.field select { max-width: 24rem; }

/* Lists of roles/agents: the action sits in the middle of the row, not at the top of it. */
.roles__item { align-items: center; padding: 0.55rem 0.8rem; }

/* Projects: title and buttons on one line, the stat block a clean 3-up grid. */
.project .card__head { flex-wrap: nowrap; align-items: center; gap: 0.75rem; }
.project .card__head h2 { margin: 0; min-width: 0; font-size: 1.05rem; flex-wrap: nowrap; }
.project__title a { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.project__title .pill { flex: none; margin-left: 0; }
.project .card__actions { flex: none; gap: 0.4rem; }
/* On a phone the head stacks (see .card__head at 480px); stretch it so a long name ellipsises instead of spilling both ways. */
@media (max-width: 480px) { .project .card__head { align-items: stretch; } }
.project__stats { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0.7rem 0.75rem; padding: 0.7rem 0.85rem; border-radius: 10px; }
.project__stats dt { margin-bottom: 0.1rem; }
.library__controls { padding: 0.6rem 0.85rem; }

/* Empty states: a quiet dashed frame instead of a tall blank slab. */
.kb-empty {
    padding: 1.5rem 1rem; margin: 0.5rem 0 1rem;
    border: 1px dashed var(--rl-border); border-radius: var(--rl-radius);
    background: color-mix(in srgb, var(--rl-surface-raised) 50%, transparent);
}
.kb-index > h2 { margin: 1.1rem 0 0.2rem; font-size: 1.05rem; }
.kb-index > .card__hint { margin-bottom: 0.4rem; }

/* The sidebar's edge and its small print. */
.app__sidebar { box-shadow: 2px 0 14px rgba(0, 0, 0, 0.08); }

/* ---- sign-in -------------------------------------------------------------
   /Account/Login in the site's own face. One card centred in the content column; the
   Identity scaffold's Bootstrap grid, floating labels and blue buttons are gone. The
   shared pieces (.auth__or, .auth__providers, .auth__error) are also used by the
   external-provider picker, so they live here rather than in a scoped sheet. */
.auth {
    min-height: 100%;
    display: grid;
    place-items: center;
    padding: 2rem 1rem;
}
.auth__card.card {
    width: min(100%, 24rem);
    padding: 1.6rem 1.5rem 1.3rem;
    margin: 0;
    box-shadow: 0 18px 48px -24px color-mix(in srgb, var(--rl-accent) 35%, transparent),
                0 2px 8px -4px rgba(0, 0, 0, 0.25);
}
.auth__head { text-align: center; margin-bottom: 1.1rem; }
.auth__head h1 { margin: 0 0 0.2rem; font-size: 1.45rem; }
.auth__head .card__meta { margin: 0; }
.auth__mark { display: block; margin: -0.4rem auto 0.4rem; width: min(15rem, 80%); }
.auth__mark .brand-wordmark { width: 100%; height: auto; }
.auth__form { display: flex; flex-direction: column; }
.auth__form .field { max-width: none; margin-bottom: 0.85rem; }
.auth__labelRow { display: flex; align-items: baseline; justify-content: space-between; gap: 0.5rem; }
.auth__small { font-size: 0.82rem; color: var(--rl-text-muted); }
.auth__small:hover { color: var(--rl-accent); }
.auth__error { color: var(--rl-danger); font-size: 0.82rem; }
.auth__error:empty { display: none; }
.auth__check {
    display: flex;
    align-items: flex-start;
    gap: 0.5rem;
    margin: 0 0 1rem;
    font-size: 0.9rem;
    color: var(--rl-text-muted);
    cursor: pointer;
}
.auth__check input { accent-color: var(--rl-accent); width: 1rem; height: 1rem; margin: 0.2rem 0 0; flex: none; }
.auth__submit { width: 100%; padding: 0.6rem 0.9rem; font-size: 1rem; }
.auth__or {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    margin: 1rem 0 0.75rem;
    color: var(--rl-text-muted);
    font-size: 0.78rem;
    text-transform: uppercase;
    letter-spacing: 0.06em;
}
.auth__or::before, .auth__or::after { content: ""; flex: 1; height: 1px; background: var(--rl-border); }
.auth__passkey { width: 100%; display: inline-flex; align-items: center; justify-content: center; gap: 0.45rem; padding: 0.6rem 0.9rem; font-size: 1rem; }
.auth__check a { color: var(--rl-accent); }
.auth__check span { line-height: 1.4; }
.auth__passkey svg { width: 1rem; height: 1rem; }
.auth__external { margin: 0; }
.auth__providers { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.auth__providers .btn { flex: 1 1 8rem; }
.auth__foot {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 0.5rem;
    margin-top: 1.1rem;
    font-size: 0.85rem;
    color: var(--rl-text-muted);
}
.auth__status { margin-bottom: 0.9rem; }

/* ---- studioumbra:// links (app-links.js) ---------------------------------
   The question a browser asks before a link leaves the site for the desktop
   client, and the page a link that could not be resolved lands on (/go).
-------------------------------------------------------------------------- */
.cs-link--app { font-weight: 600; text-decoration-style: dotted; }
.cs-link--app::before { content: "◐ "; font-size: 0.85em; }
.applink {
    border: 1px solid var(--rl-border);
    border-radius: var(--rl-radius);
    background: var(--rl-surface);
    color: var(--rl-text);
    padding: 1.4rem 1.5rem;
    width: min(92vw, 440px);
    box-shadow: 0 24px 60px -20px rgba(0, 0, 0, 0.5);
}
.applink::backdrop { background: rgba(0, 0, 0, 0.45); }
.applink__title { font-size: 1.1rem; margin: 0 0 0.6rem; }
.applink__address { font-family: Consolas, "Cascadia Mono", monospace; font-size: 0.85rem; word-break: break-all; color: var(--rl-accent); margin: 0 0 0.6rem; }
.applink__text { color: var(--rl-text-muted); font-size: 0.92rem; margin: 0 0 0.9rem; }
.applink__remember { display: flex; gap: 0.5rem; align-items: center; font-size: 0.88rem; color: var(--rl-text-muted); margin-bottom: 1rem; }
.applink__actions { display: flex; gap: 0.5rem; justify-content: flex-end; flex-wrap: wrap; }
.go { max-width: 560px; margin: 4rem auto; padding: 0 1rem; }
.go__title { font-size: 1.4rem; margin-bottom: 0.6rem; }
.go__text { color: var(--rl-text-muted); }
.go__address code { word-break: break-all; }

/* ---- reduced motion ----------------------------------------------------------
   One switch for the whole app: every transition and animation collapses to a
   single frame. Progress bars and meters still reach their end value; they
   just stop easing there. Kept last so it outranks everything above.
-------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }
}

/* ---- Studio Umbra portal (/portal) ------------------------------------------------------------------------------ */
.portal { max-width: 72rem; margin: 0 auto; padding: 1.5rem 1.5rem 3rem; }
.portal__hero { display: flex; flex-wrap: wrap; align-items: center; gap: 1rem 1.25rem; padding: 1.25rem 1.4rem; border: 1px solid var(--rl-border);
                border-radius: var(--rl-radius); background: linear-gradient(135deg, color-mix(in srgb, var(--rl-accent) 14%, var(--rl-surface)), var(--rl-surface)); }
.portal__intro { flex: 1 1 16rem; }
.portal__intro h1 { margin: 0; font-size: 1.7rem; }
.portal__intro p { margin: .25rem 0 0; color: var(--rl-text-muted); }
.portal__stats { display: flex; flex-wrap: wrap; align-items: center; gap: .75rem; }
.portal__stat { display: inline-flex; align-items: center; gap: .45rem; padding: .45rem .8rem; border-radius: 999px; background: var(--rl-surface);
                border: 1px solid var(--rl-border); font-size: .9rem; }
.portal__stat strong { font-size: 1.05rem; }
.portal__dot { width: .6rem; height: .6rem; border-radius: 50%; background: var(--rl-ok); box-shadow: 0 0 0 3px color-mix(in srgb, var(--rl-ok) 25%, transparent); }
.portal__studio { display: inline-flex; align-items: center; gap: .4rem; }
.portal__studio svg { width: 1rem; height: 1rem; }
.portal__group { margin-top: 1.75rem; }
.portal__group h2 { margin: 0 0 .6rem; font-size: .8rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--rl-text-muted); }
.portal__grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(17rem, 1fr)); gap: .9rem; }
.portal__tool { display: flex; flex-direction: column; gap: .45rem; margin: 0; }
.portal__tool header { display: flex; align-items: center; gap: .55rem; }
.portal__tool h3 { margin: 0; font-size: 1.05rem; }
.portal__tool h3 a { color: inherit; text-decoration: none; }
.portal__tool h3 a:hover { color: var(--rl-accent); }
.portal__tool p { margin: 0; color: var(--rl-text-muted); font-size: .9rem; line-height: 1.45; }
.portal__icon { width: 1.35rem; height: 1.35rem; color: var(--rl-accent); flex: none; }
.portal__links { display: flex; flex-wrap: wrap; gap: .35rem; margin: auto 0 0; padding: .35rem 0 0; list-style: none; }
.portal__links a { display: inline-block; padding: .2rem .6rem; border-radius: 999px; border: 1px solid var(--rl-border); font-size: .82rem;
                   color: var(--rl-text); text-decoration: none; background: var(--rl-surface-sunken); }
.portal__links a:hover { border-color: var(--rl-accent); color: var(--rl-accent); }

/* ---- the finish: one set of form controls, focus, depth and scrolling for the whole site --------------------------
   Every rule here is wrapped in :where(), so it weighs nothing: a component that styles its own control keeps its look,
   and only what had nothing (a bare <input>, Blazor's InputText, a .form-control) picks this up. ---- */
:root {
    --rl-control-h: 2.375rem;
    --rl-ring: 0 0 0 3px color-mix(in srgb, var(--rl-accent) 24%, transparent);
    --rl-border-strong: color-mix(in srgb, var(--rl-text) 24%, var(--rl-border));
    --rl-shadow-sm: 0 1px 2px rgb(0 0 0 / .06), 0 1px 1px rgb(0 0 0 / .04);
    --rl-shadow-md: 0 6px 18px rgb(0 0 0 / .14), 0 2px 4px rgb(0 0 0 / .06);
    --rl-shadow-lg: 0 24px 60px rgb(0 0 0 / .32), 0 6px 16px rgb(0 0 0 / .12);
    --rl-ease-out: cubic-bezier(.16, 1, .3, 1);
}

/* The page does not rubber-band, and a sideways swipe is not "back": Alt+Left/Right, the mouse's back and forward
   buttons and the browser's own buttons do that. Inner panes keep their own scrolling. */
html, body { overscroll-behavior: none; }

:where(input:not([type=checkbox], [type=radio], [type=range], [type=color], [type=file], [type=button], [type=submit], [type=reset], [type=image], [type=hidden]), select, textarea):where(:not([class]), .valid, .modified, .invalid, .form-control, .rl-input) {
    box-sizing: border-box;
    min-height: var(--rl-control-h);
    padding: .45rem .7rem;
    border: 1px solid var(--rl-border);
    border-radius: 8px;
    background: var(--rl-surface-raised);
    color: var(--rl-text);
    font: inherit;
    line-height: 1.4;
    transition: border-color var(--rl-dur), box-shadow var(--rl-dur), background-color var(--rl-dur);
}
:where(textarea):where(:not([class]), .valid, .modified, .invalid, .form-control, .rl-input) { min-height: 4.5rem; resize: vertical; }
:where(select):where(:not([class]), .valid, .modified, .invalid, .form-control, .rl-input) { padding-right: 2rem; cursor: pointer; }
:where(input, select, textarea):where(:not([class]), .valid, .modified, .invalid, .form-control, .rl-input):hover:not(:disabled, :focus) { border-color: var(--rl-border-strong); }
:where(input, select, textarea):where(:not([class]), .valid, .modified, .invalid, .form-control, .rl-input):focus-visible {
    outline: none;
    border-color: var(--rl-accent);
    background: var(--rl-surface);
    box-shadow: var(--rl-ring);
}
:where(input, select, textarea):where(:not([class]), .valid, .modified, .invalid, .form-control, .rl-input):disabled { opacity: .6; cursor: not-allowed; background: var(--rl-surface-sunken); }
:where(input, select, textarea):where(.modified.invalid, [aria-invalid="true"]) { border-color: var(--rl-danger); }
:where(input, select, textarea):where(.modified.invalid, [aria-invalid="true"]):focus-visible { box-shadow: 0 0 0 3px color-mix(in srgb, var(--rl-danger) 22%, transparent); }
:where(input, textarea)::placeholder { color: var(--rl-text-muted); opacity: .8; }

/* Checks, radios, sliders and progress take the accent rather than the browser's blue. */
:where(input[type=checkbox], input[type=radio], input[type=range], progress, meter) { accent-color: var(--rl-accent); }
:where(input[type=checkbox], input[type=radio]) { width: 1rem; height: 1rem; margin: 0; cursor: pointer; vertical-align: -.15em; }
:where(input[type=checkbox], input[type=radio]):disabled { cursor: not-allowed; }
:where(input[type=range]) { cursor: pointer; }
:where(input[type=color]) {
    inline-size: 2.6rem; block-size: 2.1rem; padding: 3px; border: 1px solid var(--rl-border); border-radius: 8px;
    background: var(--rl-surface-raised); cursor: pointer; transition: border-color var(--rl-dur), box-shadow var(--rl-dur);
}
:where(input[type=color]):hover { border-color: var(--rl-border-strong); }
:where(input[type=color]):focus-visible { outline: none; border-color: var(--rl-accent); box-shadow: var(--rl-ring); }
input[type=color]::-webkit-color-swatch-wrapper { padding: 0; }
input[type=color]::-webkit-color-swatch { border: 0; border-radius: 5px; }
input[type=color]::-moz-color-swatch { border: 0; border-radius: 5px; }
input[type=search]::-webkit-search-cancel-button { cursor: pointer; }

/* The .field controls match the bare ones, with the same hover. */
.field input:hover:not(:disabled, :focus, [type=checkbox], [type=radio], [type=file], [type=range], [type=color]),
.field select:hover:not(:disabled, :focus), .field textarea:hover:not(:disabled, :focus) { border-color: var(--rl-border-strong); }
.field input:disabled, .field select:disabled, .field textarea:disabled { opacity: .6; cursor: not-allowed; }

/* A labelled checkbox with a line of explanation under its title. */
.rl-check, .cs-check { display: flex; align-items: flex-start; gap: .65rem; margin: .5rem 0 .9rem; cursor: pointer; max-width: 46rem; }
.rl-check input, .cs-check input { margin-top: .2rem; flex: none; }
.rl-check span, .cs-check span { display: flex; flex-direction: column; gap: .15rem; }
.rl-check small, .cs-check small { color: var(--rl-text-muted); font-size: .85rem; line-height: 1.4; }

/* Keyboard focus is always visible, and only for the keyboard. */
:where(a, summary, [tabindex]:not([tabindex="-1"])):focus-visible { outline: 2px solid var(--rl-accent); outline-offset: 2px; border-radius: 4px; }
.icon-btn:focus-visible, .ghost-btn:focus-visible { outline: 2px solid var(--rl-accent); outline-offset: 2px; }

::selection { background: color-mix(in srgb, var(--rl-accent) 32%, transparent); }

/* Thin, quiet scrollbars that follow the theme. */
* { scrollbar-width: thin; scrollbar-color: color-mix(in srgb, var(--rl-text) 24%, transparent) transparent; }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { border: 3px solid transparent; border-radius: 999px; background: color-mix(in srgb, var(--rl-text) 22%, transparent) padding-box; }
::-webkit-scrollbar-thumb:hover { background: color-mix(in srgb, var(--rl-text) 36%, transparent) padding-box; }
::-webkit-scrollbar-corner { background: transparent; }

/* Buttons: one height, a little depth on the filled ones, and the two sizes the pages ask for. */
.btn { line-height: 1.3; }
.btn--primary, .btn--destructive { box-shadow: var(--rl-shadow-sm), inset 0 1px 0 rgb(255 255 255 / .12); }
.btn--primary:hover:not(:disabled), .btn--destructive:hover:not(:disabled) { box-shadow: var(--rl-shadow-md), inset 0 1px 0 rgb(255 255 255 / .14); }
.btn:hover:not(:disabled) { border-color: var(--rl-border-strong); }
.btn--primary:hover:not(:disabled), .btn--destructive:hover:not(:disabled) { border-color: transparent; }
.btn--sm { padding: .25rem .6rem; font-size: .85rem; }
.btn--ghost { background: transparent; border-color: transparent; color: var(--rl-text-muted); }
.btn--ghost:hover:not(:disabled) { background: var(--rl-surface-sunken); color: var(--rl-text); border-color: transparent; }

/* Account pages still say btn-primary / btn-danger. Same filled buttons as btn--primary / btn--destructive, not Bootstrap. */
.btn.btn-primary {
    background: var(--rl-accent);
    color: var(--rl-accent-contrast);
    border-color: transparent;
    font-weight: 600;
    box-shadow: var(--rl-shadow-sm), inset 0 1px 0 rgb(255 255 255 / .12);
}
.btn.btn-primary:hover:not(:disabled),
.btn.btn-primary:focus {
    background: var(--rl-accent);
    color: var(--rl-accent-contrast);
    border-color: transparent;
    filter: brightness(1.1);
}
.btn.btn-danger {
    background: var(--rl-danger);
    color: #fff;
    border-color: transparent;
    font-weight: 600;
    box-shadow: var(--rl-shadow-sm), inset 0 1px 0 rgb(255 255 255 / .12);
}
.btn.btn-danger:hover:not(:disabled),
.btn.btn-danger:focus {
    background: var(--rl-danger);
    color: #fff;
    border-color: transparent;
    filter: brightness(1.1);
}
.btn:focus, .btn:active:focus, .btn-link.nav-link:focus { box-shadow: none; }

/* Manage account: side nav + main, without Bootstrap columns. */
.manage__body {
    display: grid;
    grid-template-columns: minmax(11rem, 14rem) minmax(0, 1fr);
    gap: 1.25rem 1.75rem;
    align-items: start;
}
.manage__side { min-width: 0; }
.manage__main { min-width: 0; }
.manage__nav {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
}
.manage__nav .side__link {
    display: flex;
    width: 100%;
    text-decoration: none;
    border-radius: 8px;
}
.manage__actions { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; margin: 0.75rem 0; }
.manage__codes { display: flex; flex-direction: column; gap: 0.25rem; margin: 0.75rem 0; }
@media (max-width: 720px) {
    .manage__body { grid-template-columns: 1fr; }
}

/* Bootstrap's form-control focus is a different blue. These are the same fields as the rest of the app. */
.form-control,
.form-select {
    min-height: var(--rl-control-h);
    border-radius: 8px;
    border-color: var(--rl-border);
    background-color: var(--rl-surface-raised);
    color: var(--rl-text);
}
.form-control:focus,
.form-select:focus {
    color: var(--rl-text);
    background-color: var(--rl-surface);
    border-color: var(--rl-accent);
    outline: none;
    box-shadow: var(--rl-ring);
}
.form-check-input:focus { border-color: var(--rl-accent); box-shadow: var(--rl-ring); }

/* Cards sit on the page rather than being drawn on it. */
.card { box-shadow: var(--rl-shadow-sm); }

/* ---- tools still running after their page was left (ToolRunsHost) ---------------------------------------------- */
.rl-runs { position: fixed; right: 1rem; bottom: 1rem; z-index: 900; display: flex; flex-direction: column; gap: .4rem; max-width: min(22rem, calc(100vw - 2rem)); }
.rl-runs__item {
    display: flex; align-items: center; gap: .6rem; padding: .5rem .55rem .5rem .75rem; border: 1px solid var(--rl-border); border-radius: 12px;
    background: color-mix(in srgb, var(--rl-surface) 92%, transparent); backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
    box-shadow: var(--rl-shadow-md); font-size: .88rem; animation: rl-runs-in .28s var(--rl-ease-out) both;
}
.rl-runs__spin { flex: none; width: .95rem; height: .95rem; border-radius: 50%; border: 2px solid color-mix(in srgb, var(--rl-accent) 25%, transparent); border-top-color: var(--rl-accent); animation: rl-spin .8s linear infinite; }
.rl-runs__title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--rl-text); font-weight: 600; text-decoration: none; }
.rl-runs__title:hover { color: var(--rl-accent); }
.rl-runs__time { flex: none; color: var(--rl-text-muted); font-variant-numeric: tabular-nums; font-size: .8rem; }
.rl-runs__stop { flex: none; padding: .2rem .55rem; border: 1px solid var(--rl-border); border-radius: 8px; background: var(--rl-surface-raised); color: var(--rl-danger); font: inherit; font-size: .8rem; font-weight: 600; cursor: pointer; transition: background var(--rl-dur), border-color var(--rl-dur); }
.rl-runs__stop:hover { background: color-mix(in srgb, var(--rl-danger) 12%, var(--rl-surface)); border-color: color-mix(in srgb, var(--rl-danger) 45%, transparent); }
@keyframes rl-runs-in { from { opacity: 0; transform: translateY(10px) scale(.97); } }
@keyframes rl-spin { to { transform: rotate(360deg); } }
