/* =========================================================================
   ONYX Design System v2 — Beckett Law Firm HUB
   -------------------------------------------------------------------------
   A calm, precise, content-first interface. Colors and fonts come from the
   Theme Kit (/theme.css, Admin > Theme Kit); this file owns the spatial
   system, elevation, typography rhythm, motion, and every component.

   Principles: clarity over ornament, generous and consistent whitespace on an
   8pt grid, hairline separators over heavy borders, soft layered shadows for
   depth, one restrained accent, quiet motion. Build inside .panel / .card,
   use .btn variants, escape all output.
   ========================================================================= */

:root {
    /* ---- Theme bridge: legacy aliases -> themeable /theme.css tokens ------ */
    --hub-950: var(--color-sidebar-bg, #14181f);
    --ink:      var(--color-text, #1d1d20);
    --slate:    var(--color-text-muted, #6b7078);
    --parchment:var(--color-bg, #f5f5f7);
    --surface:  var(--color-surface, #ffffff);
    --line:     var(--color-border, #e6e6ea);
    --gold:     var(--color-accent, #daac30);
    --gold-deep:var(--color-accent-hover, #b8901f);
    --danger:   var(--color-danger, #dc3131);

    /* ---- Spatial system (8pt grid) --------------------------------------- */
    --sp-1: 4px;  --sp-2: 8px;  --sp-3: 12px; --sp-4: 16px; --sp-5: 20px;
    --sp-6: 24px; --sp-7: 32px; --sp-8: 40px; --sp-9: 48px; --sp-10: 64px;

    /* ---- Radii (continuous, generous) — Apple: small consistent scale, concentric corners ---- */
    --r-2xs: 4px;
    --r-xs: 6px;
    --r-sm: var(--radius-sm, 8px);
    --r-md: var(--radius, 12px);
    --r-lg: 16px;
    --r-xl: 22px;
    --r-full: 999px;

    /* ---- Status colors — the Theme Kit already defines --color-danger/-success/-warning (per-mode);
       these two only fill the gaps the theme does NOT define, seeded with Apple system hues. ---- */
    --color-info:    #0a84ff;   /* Apple blue — not a theme token */
    --color-purple:  #af52de;   /* Apple purple — not a theme token */

    /* ---- Type scale ------------------------------------------------------- */
    --fs-caption: 12.5px; --fs-sm: 13.5px; --fs-base: 15px; --fs-md: 16px;
    --fs-lg: 18px; --fs-xl: 22px; --fs-2xl: 28px; --fs-3xl: 34px;
    --lh-tight: 1.2; --lh-snug: 1.35; --lh-base: 1.55;
    --fw-regular: 400; --fw-medium: 500; --fw-semibold: 600; --fw-bold: 700;

    /* ---- Elevation (soft, layered) --------------------------------------- */
    --el-1: 0 1px 2px rgba(20,22,28,.05);
    --el-2: 0 1px 2px rgba(20,22,28,.04), 0 6px 18px rgba(20,22,28,.06);
    --el-3: 0 2px 8px rgba(20,22,28,.06), 0 16px 40px rgba(20,22,28,.12);

    /* ---- Motion ----------------------------------------------------------- */
    --ease: cubic-bezier(.4, 0, .2, 1);
    --dur: .18s;
    --dur-slow: .28s;

    /* ---- Structure -------------------------------------------------------- */
    --container: 1200px;
    --sidebar-w: var(--sidebar-width, 256px);
    --topbar-h: 60px;

    /* ---- Z-index scale ---------------------------------------------------- */
    --z-topbar: 50; --z-sidebar: 60; --z-popover: 70; --z-toast: 1000;

    /* ---- Hairline separators (theme-aware) -------------------------------- */
    --hairline: color-mix(in srgb, var(--color-border, #e6e6ea) 90%, transparent);
    --fill-subtle: color-mix(in srgb, var(--color-text, #1d1d20) 5%, transparent);
    --fill-hover: color-mix(in srgb, var(--color-text, #1d1d20) 7%, transparent);
}

/* =============================== Reset/base ============================== */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
    margin: 0;
    font-family: var(--font-body, "DM Sans", system-ui, -apple-system, "Segoe UI", sans-serif);
    font-size: var(--fs-base);
    line-height: var(--lh-base);
    color: var(--ink);
    background: var(--parchment);
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    text-rendering: optimizeLegibility;
}
h1, h2, h3, h4 { margin: 0 0 var(--sp-3); font-family: var(--font-display, "Playfair Display", Georgia, serif); color: var(--color-heading, #14181f);
    font-weight: var(--fw-semibold); line-height: var(--lh-snug); letter-spacing: -.01em; }
h1 { font-size: var(--fs-2xl); letter-spacing: -.02em; }
h2 { font-size: var(--fs-xl); }
h3 { font-size: var(--fs-lg); }
h4 { font-size: var(--fs-md); }
p { margin: 0 0 var(--sp-4); }
a { color: var(--color-link, #254c77); text-decoration: none; transition: color var(--dur) var(--ease); }
a:hover { color: var(--color-link-hover, #b8901f); }
small { font-size: var(--fs-sm); }
.mono { font-family: var(--font-mono, ui-monospace, monospace); font-size: .88em; }
.muted { color: var(--slate); }
.lead { font-size: var(--fs-md); color: var(--slate); margin: calc(-1 * var(--sp-1)) 0 var(--sp-5); line-height: var(--lh-base); }
.eyebrow { font-size: var(--fs-caption); letter-spacing: .09em; text-transform: uppercase; color: var(--slate); font-weight: var(--fw-semibold); }
hr { border: 0; border-top: 1px solid var(--hairline); margin: var(--sp-6) 0; }

::selection { background: color-mix(in srgb, var(--gold) 30%, transparent); }
:focus-visible { outline: 2px solid var(--color-focus, #254c77); outline-offset: 2px; border-radius: var(--r-xs); }

/* Quiet, thin scrollbars */
* { scrollbar-width: thin; scrollbar-color: color-mix(in srgb, var(--slate) 45%, transparent) transparent; }
*::-webkit-scrollbar { width: 10px; height: 10px; }
*::-webkit-scrollbar-thumb { background: color-mix(in srgb, var(--slate) 35%, transparent); border-radius: var(--r-full); border: 3px solid transparent; background-clip: content-box; }
*::-webkit-scrollbar-thumb:hover { background: color-mix(in srgb, var(--slate) 55%, transparent); background-clip: content-box; }

.skip-link { position: absolute; left: -9999px; top: 0; background: var(--gold); color: #14181f; padding: var(--sp-2) var(--sp-4); z-index: 100; border-radius: 0 0 var(--r-sm) 0; }
.skip-link:focus { left: 0; }

/* ================================ Brand ================================= */
.brand { display: flex; align-items: center; gap: var(--sp-3); }
.brand-mark { font-family: var(--font-display, "Playfair Display", Georgia, serif); font-weight: var(--fw-bold);
    font-size: 1.5rem; letter-spacing: .12em; color: #fff; line-height: 1; }
.brand-vein { display: block; height: 2px; flex: 1; border-radius: var(--r-full);
    background: linear-gradient(90deg, var(--gold), transparent); opacity: .8; }
.brand-sub { font-size: var(--fs-caption); letter-spacing: .16em; text-transform: uppercase; color: var(--color-sidebar-muted, #8f9aa3); }
.brand-lg .brand-mark { font-size: 2rem; }

/* =============================== App shell ============================== */
.shell { display: grid; grid-template-columns: var(--sidebar-w) 1fr; min-height: 100vh; }

.sidebar {
    display: flex; flex-direction: column; gap: var(--sp-2);
    background: var(--color-sidebar-bg, #14181f);
    color: var(--color-sidebar-text, #e9eaec);
    padding: var(--sp-6) var(--sp-4) var(--sp-4);
    position: sticky; top: 0; height: 100vh;
}
.sidebar > .brand { padding: 0 var(--sp-2) var(--sp-5); }
.side-nav { display: flex; flex-direction: column; gap: 2px; overflow-y: auto; flex: 1; margin: 0 calc(-1 * var(--sp-1)); padding: 0 var(--sp-1); }
.nav-group { margin-top: var(--sp-4); padding: 0 var(--sp-3) var(--sp-1); font-size: var(--fs-caption);
    letter-spacing: .08em; text-transform: uppercase; color: var(--color-sidebar-muted, #8f9aa3); }
.side-nav a {
    display: flex; align-items: center; gap: var(--sp-3);
    padding: var(--sp-2) var(--sp-3); border-radius: var(--r-sm);
    color: color-mix(in srgb, var(--color-sidebar-text, #e9eaec) 82%, transparent);
    font-size: var(--fs-base); font-weight: var(--fw-medium); line-height: 1.3;
    position: relative; transition: background var(--dur) var(--ease), color var(--dur) var(--ease);
}
.side-nav a .icon { font-size: 1.2rem; opacity: .85; }
.side-nav a:hover { background: rgba(255,255,255,.06); color: #fff; }
.side-nav a.active { background: rgba(255,255,255,.1); color: #fff; }
.side-nav a.active::before { content: ""; position: absolute; left: 0; top: 50%; transform: translateY(-50%);
    width: 3px; height: 18px; border-radius: var(--r-full); background: var(--gold); }

.side-foot { margin-top: auto; padding-top: var(--sp-3); border-top: 1px solid rgba(255,255,255,.08); }
.side-user { display: flex; flex-direction: column; gap: 1px; padding: var(--sp-2) var(--sp-3); }
.side-user-name { font-weight: var(--fw-semibold); color: #fff; font-size: var(--fs-sm); }
.side-user-role { font-size: var(--fs-caption); color: var(--color-sidebar-muted, #8f9aa3); text-transform: capitalize; }
.side-foot form { margin: var(--sp-1) 0 0; }
.side-foot .btn { width: 100%; }

.main-col { display: flex; flex-direction: column; min-width: 0; min-height: 100vh; }
.topbar {
    position: sticky; top: 0; z-index: var(--z-topbar);
    display: flex; align-items: center; gap: var(--sp-4);
    height: var(--topbar-h); padding: 0 var(--sp-6);
    background: color-mix(in srgb, var(--color-topbar-bg, #fff) 82%, transparent);
    backdrop-filter: saturate(180%) blur(20px); -webkit-backdrop-filter: saturate(180%) blur(20px);
    border-bottom: 1px solid var(--hairline);
}
.page-title { font-size: var(--fs-lg); font-weight: var(--fw-semibold); margin: 0; color: var(--color-heading, #14181f);
    min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; letter-spacing: -.01em; }
.topbar-actions { margin-left: auto; display: flex; align-items: center; gap: var(--sp-2); flex: 0 0 auto; }

.content { width: 100%; max-width: var(--container); margin: 0 auto; padding: var(--sp-7) var(--sp-6); flex: 1; }
.foot { padding: var(--sp-5) var(--sp-6); font-size: var(--fs-caption); color: var(--slate); text-align: center; border-top: 1px solid var(--hairline); }

/* Topbar icon buttons (theme toggle + notification bell share this look) */
.theme-toggle, .notif-trigger {
    width: 36px; height: 36px; border-radius: var(--r-sm); border: 1px solid var(--hairline);
    background: var(--color-surface, #fff); cursor: pointer; display: inline-flex; align-items: center;
    justify-content: center; color: var(--ink); transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease), transform var(--dur) var(--ease);
}
.theme-toggle:hover, .notif-trigger:hover { background: var(--fill-subtle); border-color: color-mix(in srgb, var(--color-border) 70%, var(--ink)); }
.theme-toggle:active, .notif-trigger:active { transform: scale(.94); }
.theme-toggle .icon { font-size: 1.15rem; color: var(--gold); }
.theme-toggle .icon::before { content: "\e51c"; }
:root[data-theme="dark"] .theme-toggle .icon::before { content: "\e518"; }

/* =============================== Buttons ================================ */
.btn {
    display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-2);
    padding: var(--sp-2) var(--sp-5); min-height: 40px;
    font-family: inherit; font-size: var(--fs-base); font-weight: var(--fw-medium); line-height: 1;
    border: 1px solid transparent; border-radius: var(--r-sm); cursor: pointer; text-decoration: none;
    transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease),
        color var(--dur) var(--ease), transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
    white-space: nowrap; user-select: none;
}
.btn:active { transform: scale(.98); }
.btn .icon { font-size: 1.1rem; }
.btn:disabled, .btn[aria-disabled="true"] { opacity: .5; cursor: not-allowed; }

.btn-primary { background: var(--color-primary, #254c77); color: var(--color-primary-contrast, #fff); box-shadow: var(--el-1); }
.btn-primary:hover { background: var(--color-primary-hover, #1d3c5f); color: var(--color-primary-contrast, #fff); }

.btn-secondary { background: var(--fill-subtle); color: var(--ink); }
.btn-secondary:hover { background: var(--fill-hover); }

.btn-ghost { background: transparent; color: var(--ink); border-color: var(--hairline); }
.btn-ghost:hover { background: var(--fill-subtle); border-color: color-mix(in srgb, var(--color-border) 60%, var(--ink)); }

.btn-gold, .btn-accent { background: var(--gold); color: var(--color-accent-contrast, #202329); box-shadow: var(--el-1); }
.btn-gold:hover, .btn-accent:hover { background: var(--gold-deep); color: var(--color-accent-contrast, #202329); }

.btn-danger { background: var(--danger); color: #fff; }
.btn-danger:hover { background: color-mix(in srgb, var(--danger) 85%, #000); color: #fff; }

.btn-sm { min-height: 32px; padding: var(--sp-1) var(--sp-3); font-size: var(--fs-sm); border-radius: var(--r-xs); }
.btn-block { width: 100%; }
.btn-icon { width: 40px; min-height: 40px; padding: 0; }

/* =========================== Panels & cards ============================= */
.panel, .card {
    background: var(--surface); border: 1px solid var(--hairline); border-radius: var(--r-lg);
    box-shadow: var(--el-2); padding: var(--sp-6); margin-bottom: var(--sp-5);
}
.panel > h2:first-child, .panel > h3:first-child, .card > h2:first-child, .card > h3:first-child { margin-top: 0; }
.panel > :last-child, .card > :last-child { margin-bottom: 0; }

.empty { padding: var(--sp-9) var(--sp-5); text-align: center; color: var(--slate); }
.empty .icon { font-size: 2.4rem; opacity: .4; display: block; margin: 0 auto var(--sp-3); }

/* ================================ Forms ================================= */
label, .field-label { display: block; font-size: var(--fs-sm); font-weight: var(--fw-medium); color: var(--color-heading, #14181f); margin-bottom: var(--sp-2); }
.field { margin-bottom: var(--sp-4); }
input[type="text"], input[type="email"], input[type="password"], input[type="number"], input[type="search"],
input[type="url"], input[type="date"], input[type="datetime-local"], select, textarea, .input, .select, .textarea {
    width: 100%; font-family: inherit; font-size: var(--fs-base); color: var(--ink);
    background: var(--surface); border: 1px solid var(--color-border, #e6e6ea); border-radius: var(--r-sm);
    padding: var(--sp-2) var(--sp-3); min-height: 40px; transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
    -webkit-appearance: none; appearance: none;
}
textarea, .textarea { min-height: 96px; resize: vertical; line-height: var(--lh-base); padding: var(--sp-3); }
input:focus, select:focus, textarea:focus, .input:focus, .select:focus, .textarea:focus {
    outline: none; border-color: var(--color-focus, #254c77);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-focus, #254c77) 18%, transparent);
}
input::placeholder, textarea::placeholder { color: color-mix(in srgb, var(--slate) 85%, transparent); }
select, .select { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%236b7078' d='M3 4.5 6 8l3-3.5z'/%3E%3C/svg%3E");
    background-repeat: no-repeat; background-position: right var(--sp-3) center; padding-right: var(--sp-7); }
.form-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-4) var(--sp-5); }
.form-grid .span-2 { grid-column: span 2; }
.form-help, .hint { font-size: var(--fs-sm); color: var(--slate); margin-top: var(--sp-2); }
.form-actions { display: flex; gap: var(--sp-3); align-items: center; margin-top: var(--sp-5); flex-wrap: wrap; }
.req { color: var(--danger); }

/* Checkbox / radio / switch */
.checkbox, .radio { display: inline-flex; align-items: center; gap: var(--sp-2); font-weight: var(--fw-regular); cursor: pointer; margin-bottom: var(--sp-2); }
.checkbox input, .radio input { width: 18px; height: 18px; accent-color: var(--color-primary, #254c77); margin: 0; }
.switch { position: relative; display: inline-flex; align-items: center; gap: var(--sp-3); cursor: pointer; }
.switch input { position: absolute; opacity: 0; }
.switch-track { width: 44px; height: 26px; border-radius: var(--r-full); background: var(--fill-hover); position: relative; transition: background var(--dur) var(--ease); flex: 0 0 auto; }
.switch-track::after { content: ""; position: absolute; top: 3px; left: 3px; width: 20px; height: 20px; border-radius: 50%; background: #fff; box-shadow: var(--el-1); transition: transform var(--dur) var(--ease); }
.switch input:checked + .switch-track { background: var(--color-primary, #254c77); }
.switch input:checked + .switch-track::after { transform: translateX(18px); }

/* ================================ Tables ================================ */
.table-wrap { overflow-x: auto; border-radius: var(--r-lg); }
table, .table { width: 100%; border-collapse: collapse; font-size: var(--fs-base); }
thead th { text-align: left; font-size: var(--fs-caption); letter-spacing: .06em; text-transform: uppercase;
    color: var(--slate); font-weight: var(--fw-semibold); padding: var(--sp-3) var(--sp-4); border-bottom: 1px solid var(--hairline); }
tbody td { padding: var(--sp-3) var(--sp-4); border-bottom: 1px solid var(--hairline); vertical-align: middle; }
tbody tr:last-child td { border-bottom: 0; }
tbody tr { transition: background var(--dur) var(--ease); }
tbody tr:hover { background: var(--fill-subtle); }
.table-striped tbody tr:nth-child(even) { background: color-mix(in srgb, var(--color-text) 2.5%, transparent); }

/* ============================ Badges & chips =========================== */
.badge { display: inline-flex; align-items: center; gap: var(--sp-1); padding: 2px var(--sp-2); border-radius: var(--r-full);
    font-size: var(--fs-caption); font-weight: var(--fw-semibold); line-height: 1.5; letter-spacing: .01em; }
.badge-ok, .badge-info { background: var(--color-success-bg, #eef7e3); color: var(--color-success-text, #3f6b1f); }
.badge-info { background: color-mix(in srgb, var(--color-primary) 12%, transparent); color: var(--color-primary, #254c77); }
.badge-beta { background: var(--color-warning-bg, #f8efd6); color: var(--color-warning-text, #8a6a15); }
.badge-off, .badge-neutral { background: var(--fill-hover); color: var(--slate); }

/* =============================== Alerts ================================= */
.alert { display: flex; gap: var(--sp-3); padding: var(--sp-4); border-radius: var(--r-md); margin-bottom: var(--sp-5);
    font-size: var(--fs-base); border: 1px solid transparent; }
.alert .icon { flex: 0 0 auto; font-size: 1.2rem; margin-top: 1px; }
.alert-success, .alert-ok { background: var(--color-success-bg, #eef7e3); color: var(--color-success-text, #3f6b1f); border-color: color-mix(in srgb, var(--color-success, #91ce58) 40%, transparent); }
.alert-danger, .alert-error { background: var(--color-danger-bg, #fbe9e9); color: var(--color-danger-text, #a5231f); border-color: color-mix(in srgb, var(--color-danger, #dc3131) 35%, transparent); }
.alert-warning { background: var(--color-warning-bg, #f8efd6); color: var(--color-warning-text, #8a6a15); border-color: color-mix(in srgb, var(--color-warning, #daac30) 40%, transparent); }
.alert-info { background: color-mix(in srgb, var(--color-primary) 8%, transparent); color: var(--color-primary, #254c77); border-color: color-mix(in srgb, var(--color-primary) 25%, transparent); }

/* ========================= Dashboard app grid ========================== */
.app-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: var(--sp-5); }
.app-card {
    display: flex; flex-direction: column; gap: var(--sp-3); padding: var(--sp-6);
    background: var(--surface); border: 1px solid var(--hairline); border-radius: var(--r-lg);
    box-shadow: var(--el-1); text-decoration: none; color: var(--ink);
    transition: transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease), border-color var(--dur) var(--ease);
    position: relative; overflow: hidden;
}
.app-card:hover { transform: translateY(-3px); box-shadow: var(--el-3); border-color: color-mix(in srgb, var(--gold) 45%, var(--hairline)); color: var(--ink); }
.app-card-head { display: flex; align-items: center; justify-content: space-between; }
.app-card .avatar, .app-card .doc-icon { width: 44px; height: 44px; border-radius: var(--r-md); display: inline-flex;
    align-items: center; justify-content: center; background: color-mix(in srgb, var(--color-primary) 10%, transparent); color: var(--color-primary, #254c77); }
.app-card .avatar .icon { font-size: 1.5rem; }
.app-name { font-size: var(--fs-md); font-weight: var(--fw-semibold); color: var(--color-heading, #14181f); }
.app-desc { font-size: var(--fs-sm); color: var(--slate); line-height: var(--lh-snug); flex: 1; }
.app-open { display: inline-flex; align-items: center; gap: var(--sp-1); font-size: var(--fs-sm); font-weight: var(--fw-medium); color: var(--color-link, #254c77); }
.app-card:hover .app-open { gap: var(--sp-2); }

/* ===================== Menu / dropdown / tabs ========================== */
.menu { display: flex; flex-direction: column; gap: 2px; }
.menu a, .menu-item { display: block; padding: var(--sp-2) var(--sp-3); border-radius: var(--r-sm); color: var(--ink); text-decoration: none; }
.menu a:hover, .menu-item:hover { background: var(--fill-subtle); }
.menu-sep { height: 1px; background: var(--hairline); margin: var(--sp-2) 0; }

.dropdown { position: relative; display: inline-block; }
.dropdown > summary { list-style: none; cursor: pointer; }
.dropdown > summary::-webkit-details-marker { display: none; }
.dropdown-menu { position: absolute; z-index: var(--z-popover); margin-top: var(--sp-2); min-width: 200px; padding: var(--sp-1);
    background: var(--surface); border: 1px solid var(--hairline); border-radius: var(--r-md); box-shadow: var(--el-3); }
.dropdown-menu.dd-right { right: 0; }
.dropdown-menu a, .dropdown-item { display: block; padding: var(--sp-2) var(--sp-3); border-radius: var(--r-sm); color: var(--ink); text-decoration: none; font-size: var(--fs-base); }
.dropdown-menu a:hover, .dropdown-item:hover { background: var(--fill-subtle); }

.tabs { margin-bottom: var(--sp-5); }
.tab-list { display: flex; gap: var(--sp-1); border-bottom: 1px solid var(--hairline); margin-bottom: var(--sp-5); }
.tab { padding: var(--sp-3) var(--sp-4); border: 0; background: none; cursor: pointer; font: inherit; font-weight: var(--fw-medium);
    color: var(--slate); border-bottom: 2px solid transparent; margin-bottom: -1px; transition: color var(--dur) var(--ease), border-color var(--dur) var(--ease); }
.tab:hover { color: var(--ink); }
.tab[aria-selected="true"], .tab.is-active { color: var(--color-primary, #254c77); border-bottom-color: var(--gold); }
.tab-panel[hidden] { display: none; }

.accordion { border: 1px solid var(--hairline); border-radius: var(--r-md); overflow: hidden; }
.accordion > summary { padding: var(--sp-3) var(--sp-4); cursor: pointer; font-weight: var(--fw-medium); list-style: none; }
.accordion > summary::-webkit-details-marker { display: none; }
.accordion[open] > summary { border-bottom: 1px solid var(--hairline); }
.accordion-body { padding: var(--sp-4); }

blockquote { margin: 0 0 var(--sp-4); padding: var(--sp-2) var(--sp-4); border-left: 3px solid var(--gold); color: var(--slate); }

/* ============================ Media / figures ========================== */
figure { margin: 0 0 var(--sp-4); }
.figure-caption { font-size: var(--fs-sm); color: var(--slate); margin-top: var(--sp-2); }
.aspect { position: relative; width: 100%; border-radius: var(--r-md); overflow: hidden; background: var(--fill-subtle); }
.aspect-16x9 { aspect-ratio: 16 / 9; } .aspect-4x3 { aspect-ratio: 4 / 3; }
.aspect > img, .aspect > video, .aspect > iframe { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; border: 0; }
img { max-width: 100%; height: auto; }

/* ============================ Layout helpers =========================== */
.cluster { display: flex; flex-wrap: wrap; gap: var(--sp-3); align-items: center; }
.pager { display: flex; gap: var(--sp-2); align-items: center; justify-content: center; margin-top: var(--sp-5); }
.link-muted { color: var(--slate); } .link-muted:hover { color: var(--ink); }

/* ============================ Notifications ============================= */
.notif { position: relative; display: inline-flex; }
.notif-trigger { list-style: none; position: relative; }
.notif-trigger::-webkit-details-marker { display: none; }
.notif-trigger .icon { font-size: 1.2rem; color: var(--gold); }
.notif[open] .notif-trigger { background: var(--fill-hover); border-color: color-mix(in srgb, var(--color-border) 60%, var(--ink)); }
.notif-badge { position: absolute; top: -5px; right: -5px; min-width: 17px; height: 17px; padding: 0 4px;
    border-radius: var(--r-full); background: var(--danger); color: #fff; font-size: 10.5px; line-height: 17px;
    text-align: center; font-weight: var(--fw-bold); box-shadow: 0 0 0 2px var(--color-topbar-bg, #fff); }

.notif-panel { position: absolute; top: calc(100% + var(--sp-2)); right: 0; width: 344px; max-width: calc(100vw - var(--sp-5));
    background: var(--surface); border: 1px solid var(--hairline); border-radius: var(--r-lg); box-shadow: var(--el-3);
    z-index: var(--z-popover); overflow: hidden; display: flex; flex-direction: column; }
.notif-head { padding: var(--sp-3) var(--sp-4); border-bottom: 1px solid var(--hairline); font-weight: var(--fw-semibold);
    color: var(--color-heading, #14181f); font-size: var(--fs-base); }
.notif-list { max-height: min(62vh, 440px); overflow-y: auto; overscroll-behavior: contain; }
.notif-empty { padding: var(--sp-8) var(--sp-4); color: var(--slate); font-size: var(--fs-sm); text-align: center; }
.notif-item { display: flex; align-items: flex-start; gap: var(--sp-3); padding: var(--sp-3) var(--sp-4);
    border-bottom: 1px solid var(--hairline); color: var(--ink); transition: background var(--dur) var(--ease); }
.notif-item:last-child { border-bottom: 0; }
.notif-item:hover { background: var(--fill-subtle); }
.notif-item.is-unread { background: color-mix(in srgb, var(--color-primary) 6%, transparent); }
.notif-item.is-unread:hover { background: color-mix(in srgb, var(--color-primary) 9%, transparent); }
.notif-item-main { flex: 1; min-width: 0; cursor: pointer; }
.notif-item-title { font-size: var(--fs-sm); color: var(--ink); overflow-wrap: anywhere; line-height: var(--lh-snug); }
.notif-item-meta { font-size: var(--fs-caption); color: var(--slate); margin-top: 2px; text-transform: capitalize; }
.notif-item-clear { flex: 0 0 auto; background: none; border: 0; cursor: pointer; color: var(--slate); padding: 3px;
    line-height: 0; border-radius: var(--r-xs); transition: color var(--dur) var(--ease), background var(--dur) var(--ease); }
.notif-item-clear:hover { color: var(--danger); background: var(--fill-hover); }
.notif-item-clear .icon { font-size: 1rem; }
.notif-foot { padding: var(--sp-3) var(--sp-4); border-top: 1px solid var(--hairline); text-align: left; }
.notif-clear-all { background: none; border: 0; padding: 0; cursor: pointer; font: inherit; font-size: var(--fs-sm);
    color: var(--color-link, #254c77); }
.notif-clear-all:hover { color: var(--color-link-hover, #b8901f); text-decoration: underline; }

/* Toasts */
.toast-region { position: fixed; bottom: var(--sp-5); right: var(--sp-5); left: auto; display: flex; flex-direction: column;
    gap: var(--sp-2); z-index: var(--z-toast); width: 344px; max-width: calc(100vw - var(--sp-7)); }
.toast { display: flex; align-items: flex-start; gap: var(--sp-3); padding: var(--sp-3) var(--sp-4);
    background: var(--surface); border: 1px solid var(--hairline); border-radius: var(--r-md); box-shadow: var(--el-3);
    animation: hub-toast-in var(--dur-slow) var(--ease); }
.toast .icon { flex: 0 0 auto; margin-top: 1px; }
.toast-body { flex: 1; min-width: 0; font-size: var(--fs-sm); color: var(--ink); }
.toast-title { font-weight: var(--fw-semibold); color: var(--color-heading, #14181f); display: block; margin-bottom: 1px; }
.toast-close { background: none; border: 0; cursor: pointer; color: var(--slate); padding: 2px; line-height: 0; border-radius: var(--r-xs); }
.toast-close:hover { color: var(--ink); background: var(--fill-hover); }
.toast-ok { border-left: 3px solid var(--color-success, #91ce58); } .toast-ok .icon { color: var(--color-success-text, #3f6b1f); }
.toast-error { border-left: 3px solid var(--danger); } .toast-error .icon { color: var(--danger); }
@keyframes hub-toast-in { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }

/* ==================== Login / minimal / error / installer ============== */
.minimal { display: flex; min-height: 100vh; }
.minimal-stage { margin: auto; width: 100%; max-width: 440px; padding: var(--sp-6); }
.login-card, .install-card { background: var(--surface); border: 1px solid var(--hairline); border-radius: var(--r-xl);
    box-shadow: var(--el-3); padding: var(--sp-8) var(--sp-7); text-align: center; }
.login-card .brand, .install-card .brand { justify-content: center; margin-bottom: var(--sp-5); }
.login-copy { color: var(--slate); margin-bottom: var(--sp-6); }
.login-fine { font-size: var(--fs-caption); color: var(--slate); margin-top: var(--sp-6); }
.login-card .btn, .install-card .btn { width: 100%; }
.error-code { font-family: var(--font-display, "Playfair Display", serif); font-size: 4rem; color: var(--gold);
    margin: var(--sp-2) 0; line-height: 1; }
.install-card { max-width: 560px; text-align: left; }
.install-card .brand { justify-content: flex-start; }
.check-list { list-style: none; margin: 0 0 var(--sp-5); padding: 0; font-size: var(--fs-base); }
.check-list li, .check-row { padding: var(--sp-2) 0; border-bottom: 1px solid var(--hairline); display: flex; align-items: center; gap: var(--sp-2); }
.check-ok { color: var(--color-success-text, #3f6b1f); } .check-fail { color: var(--color-danger-text, #a5231f); }
.install-steps { text-align: left; font-size: var(--fs-base); line-height: 1.6; padding-left: var(--sp-5); }
.install-card h2 { color: var(--gold); margin: var(--sp-5) 0 var(--sp-2); font-size: var(--fs-md); }

/* =============================== Responsive ============================= */
.menu-toggle { display: none; background: none; border: 0; padding: var(--sp-1); cursor: pointer; color: var(--ink); border-radius: var(--r-sm); }
.menu-toggle span { display: block; width: 22px; height: 2px; background: currentColor; margin: var(--sp-2) 0; border-radius: var(--r-2xs); transition: transform var(--dur) var(--ease); }

@media (max-width: 900px) {
    .shell { grid-template-columns: 1fr; }
    .sidebar { position: fixed; z-index: var(--z-sidebar); width: min(84vw, var(--sidebar-w)); height: 100vh;
        transform: translateX(-100%); transition: transform var(--dur-slow) var(--ease); box-shadow: var(--el-3); }
    .sidebar.open { transform: translateX(0); }
    .menu-toggle { display: inline-flex; flex-direction: column; }
    .content { padding: var(--sp-5) var(--sp-4); }
    .topbar { padding: 0 var(--sp-4); }
    .form-grid { grid-template-columns: 1fr; }
    .form-grid .span-2 { grid-column: span 1; }
}
@media (max-width: 620px) {
    .notif-panel { position: fixed; top: calc(var(--topbar-h) + var(--sp-1)); left: var(--sp-3); right: var(--sp-3); width: auto; max-width: none; }
    .toast-region { left: var(--sp-3); right: var(--sp-3); width: auto; bottom: var(--sp-3); }
    h1 { font-size: var(--fs-xl); }

    /* Stacked, labeled table rows on small screens */
    thead { display: none; }
    table, tbody, tr, td { display: block; width: 100%; }
    tbody tr { border: 1px solid var(--hairline); border-radius: var(--r-md); margin-bottom: var(--sp-3);
        padding: var(--sp-2); background: var(--surface); box-shadow: var(--el-1); }
    tbody td { border: 0; padding: var(--sp-1) var(--sp-3); }
    tbody td[data-th]::before { content: attr(data-th); display: block; font-size: var(--fs-caption);
        letter-spacing: .08em; text-transform: uppercase; color: var(--slate); margin-bottom: 2px; }
}

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after { animation-duration: .001ms !important; transition-duration: .001ms !important; }
}

/* ====================================================================
   PRESERVED SUBSYSTEMS (functional; consume the same themed tokens)
   ==================================================================== */

/* ===================== Theme Kit editor (Phase 3) ===================== */
.tk .lead { margin-bottom: var(--sp-4); }
.tk-layout { display: grid; grid-template-columns: 1fr 380px; gap: var(--sp-6); align-items: start; }
.tk-controls { min-width: 0; display: flex; flex-direction: column; gap: var(--sp-5); }
.tk-modebar { display: inline-flex; gap: var(--sp-1); padding: var(--sp-1); background: var(--color-surface-alt); border-radius: var(--radius-sm); margin-bottom: var(--sp-4); }
.tk-mode { border: 0; background: transparent; padding: var(--sp-2) var(--sp-4); border-radius: var(--radius-sm); cursor: pointer;
    font: inherit; font-size: .85rem; color: var(--color-text-muted); }
.tk-mode.is-active { background: var(--color-surface); color: var(--color-text); box-shadow: var(--shadow); }
.tk-group { font-size: .78rem; text-transform: uppercase; letter-spacing: .12em; color: var(--color-text-muted);
    margin: var(--sp-4) 0 var(--sp-2); }
.tk-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-3) var(--sp-4); }
.tk-field { display: flex; flex-direction: column; gap: var(--sp-1); font-size: .8rem; font-weight: 600; min-width: 0; }
.tk-field-label { color: var(--color-text); }
.tk-color-row { display: flex; align-items: center; gap: var(--sp-2); }
.tk-color { width: 34px; height: 34px; padding: 0; border: 1px solid var(--color-border); border-radius: var(--radius-sm);
    background: none; cursor: pointer; flex: 0 0 auto; }
.tk-color::-webkit-color-swatch-wrapper { padding: 2px; }
.tk-color::-webkit-color-swatch { border: none; border-radius: var(--r-2xs); }
.tk-hex { flex: 1 1 auto; min-width: 0; font-family: var(--font-mono); font-size: .8rem; font-weight: 400;
    padding: var(--sp-2) var(--sp-2); border: 1px solid var(--color-border); border-radius: var(--radius-sm);
    background: var(--color-surface); color: var(--color-text); }
.tk-input { font: inherit; font-weight: 400; font-size: .82rem; padding: var(--sp-2) var(--sp-2); border: 1px solid var(--color-border);
    border-radius: var(--radius-sm); background: var(--color-surface); color: var(--color-text); width: 100%; }
.tk-hex:focus, .tk-input:focus { outline: 2px solid var(--color-focus); outline-offset: 1px; border-color: var(--color-focus); }
.tk-reset { flex: 0 0 auto; width: 30px; height: 30px; border: 1px solid var(--color-border); background: var(--color-surface);
    border-radius: var(--radius-sm); cursor: pointer; color: var(--color-text-muted); display: inline-flex;
    align-items: center; justify-content: center; }
.tk-reset:hover { color: var(--color-accent); border-color: var(--color-accent); }
.tk-reset .icon { font-size: 1rem; }
.tk-actions { display: flex; align-items: center; gap: var(--sp-3); margin-top: var(--sp-5); padding-top: var(--sp-4); border-top: 1px solid var(--color-border); }
.tk-dirty { font-size: .8rem; }
.tk-hidden-form { display: none; }

/* Live preview */
.tk-preview-wrap { position: sticky; top: 1rem; }
.tk-preview-head { display: flex; justify-content: space-between; align-items: baseline;
    font-size: .78rem; text-transform: uppercase; letter-spacing: .1em; color: var(--color-text-muted); margin-bottom: var(--sp-2); }
.tk-preview { border: 1px solid var(--color-border); border-radius: var(--radius); overflow: hidden; background: var(--color-bg); }
/* Preview mini-components consume the SAME tokens; JS sets them on #tkPreview so
   edits show live for the mode being edited. */
.tkp-shell { display: grid; grid-template-columns: 92px 1fr; min-height: 300px; background: var(--color-bg); }
.tkp-side { background: var(--color-sidebar-bg); color: var(--color-sidebar-text); padding: var(--sp-3) var(--sp-2); }
.tkp-brand { font-family: var(--font-display); font-weight: 700; letter-spacing: .18em; font-size: .8rem; margin-bottom: var(--sp-3); color: var(--color-sidebar-text); }
.tkp-navitem { font-size: .66rem; padding: var(--sp-1) var(--sp-1); border-radius: var(--r-2xs); color: var(--color-sidebar-muted); margin-bottom: var(--sp-1); }
.tkp-navitem.is-active { background: rgba(255, 255, 255, .08); color: var(--color-accent); }
.tkp-main { display: flex; flex-direction: column; min-width: 0; }
.tkp-topbar { background: var(--color-topbar-bg); border-bottom: 1px solid var(--color-border); padding: var(--sp-2) var(--sp-3); }
.tkp-title { font-family: var(--font-display); font-style: var(--title-style); font-weight: var(--title-weight); font-size: 1.3rem; color: var(--color-heading); }
.tkp-body { padding: var(--sp-3); background: var(--color-bg); flex: 1; }
.tkp-card { background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius); box-shadow: var(--shadow); padding: var(--sp-3); }
.tkp-h { font-family: var(--font-display); font-weight: var(--heading-weight); color: var(--color-heading); margin: 0 0 var(--sp-1); font-size: 1.05rem; }
.tkp-p { font-family: var(--font-body); color: var(--color-text); font-size: .76rem; line-height: 1.5; margin: 0 0 var(--sp-2); }
.tkp-link { color: var(--color-link); text-decoration: underline; }
.tkp-row { display: flex; gap: var(--sp-2); flex-wrap: wrap; margin-bottom: var(--sp-2); }
.tkp-btn { font-family: var(--font-body); font-size: var(--text-button-size); font-weight: var(--text-button-weight);
    border: 1px solid transparent; border-radius: var(--radius-sm); padding: var(--sp-1) var(--sp-3); cursor: default; }
.tkp-btn-primary { background: var(--color-primary); color: var(--color-primary-contrast); }
.tkp-btn-accent { background: var(--color-accent); color: var(--color-accent-contrast); }
.tkp-btn-ghost { background: transparent; border-color: var(--color-border); color: var(--color-text); }
.tkp-alert { font-size: .72rem; padding: var(--sp-2) var(--sp-2); border-radius: var(--radius-sm); margin-bottom: var(--sp-2); }
.tkp-alert-ok { background: var(--color-success-bg); color: var(--color-success-text); }
.tkp-alert-err { background: var(--color-danger-bg); color: var(--color-danger-text); }
.tkp-table { width: 100%; border-collapse: collapse; font-size: .72rem; color: var(--color-text); }
.tkp-table td { border-top: 1px solid var(--color-border); padding: var(--sp-1) var(--sp-2); }

@media (max-width: 900px) {
    .tk-layout { grid-template-columns: 1fr; }
    .tk-preview-wrap { position: static; }
    .tk-grid { grid-template-columns: 1fr; }
}


/* ============================================================================
   ONYX RICH COMPONENTS (Phase 5)
   Presentation shells only. The actual media, documents, and AI/chat responses
   are confidential and MUST be supplied by apps via authenticated, access-
   controlled, audited same-origin routes (media/docs) and server-side secret
   proxies (chat). These components never hold a key or a public link.
   All same-origin, so CSP is unchanged EXCEPT the document viewer's optional
   inline embed mode, which needs the hosting route to allow same-origin frames
   (SecurityHeaders::allowFrameSources(["'self'"])). See docs/components.md.
   ============================================================================ */

/* ---- Media player (video + audio; custom themed controls) ---- */
.player { background: var(--color-secondary); border-radius: var(--radius); overflow: hidden; color: #fff; position: relative; }
.player-media { display: block; width: 100%; background: #000; }
.player.audio-only { background: var(--color-surface); color: var(--color-text); border: 1px solid var(--color-border); }
.player.audio-only .player-controls { position: static; background: transparent; }
.player-title { font-family: var(--font-body); font-size: .85rem; padding: var(--sp-2) var(--sp-3); color: inherit; }
.player-controls { display: flex; align-items: center; gap: var(--sp-2); padding: var(--sp-2) var(--sp-3);
    background: linear-gradient(transparent, rgba(0,0,0,.55)); }
.player.audio-only .player-controls { color: var(--color-text); }
.player-btn { background: none; border: 0; color: inherit; cursor: pointer; padding: var(--sp-1); line-height: 0; display: inline-flex; }
.player-btn .icon { font-size: 1.3rem; }
.player-btn:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; border-radius: var(--r-2xs); }
.player-seek { flex: 1; accent-color: var(--color-accent); cursor: pointer; height: 4px; }
.player-time { font-variant-numeric: tabular-nums; font-size: .75rem; opacity: .9; white-space: nowrap; }

/* ---- Document viewer ---- */
/* Card mode (default, CSP-clean): metadata + access-controlled actions. */
.doc-card { display: flex; align-items: center; gap: var(--sp-4); padding: var(--sp-4); border: 1px solid var(--color-border);
    border-radius: var(--radius); background: var(--color-surface); }
.doc-icon { flex: 0 0 auto; width: 44px; height: 44px; border-radius: var(--radius-sm); background: var(--color-surface-alt);
    display: inline-flex; align-items: center; justify-content: center; color: var(--color-primary); }
.doc-icon .icon { font-size: 1.5rem; }
.doc-meta { flex: 1; min-width: 0; }
.doc-name { font-weight: 600; color: var(--color-heading); word-break: break-word; }
.doc-sub { font-size: .8rem; color: var(--color-text-muted); }
.doc-actions { display: flex; gap: var(--sp-2); flex-shrink: 0; }
/* Inline mode (opt-in; requires route to allow same-origin frames). */
.doc-frame { border: 1px solid var(--color-border); border-radius: var(--radius); overflow: hidden; background: var(--color-surface-alt); }
.doc-toolbar { display: flex; align-items: center; gap: var(--sp-2); padding: var(--sp-2) var(--sp-3); background: var(--color-surface);
    border-bottom: 1px solid var(--color-border); }
.doc-toolbar .doc-name { flex: 1; font-size: .88rem; }
.doc-frame iframe { display: block; width: 100%; height: 70vh; border: 0; background: var(--color-surface-alt); }

/* ---- Chat / AI interface ---- */
/* The chat is a BOUNDED box: it fills its container but never grows with its
   content. The log scrolls inside; the input row stays pinned at the bottom.
   height:100% fills a parent that has a definite height; max-height caps it in
   a parent that doesn't, so it can never run off the screen either way. */
.chat { position: relative; display: flex; flex-direction: column; height: 100%; min-height: 320px;
    max-height: calc(100dvh - var(--topbar-h) - var(--sp-9)); border: 1px solid var(--color-border);
    border-radius: var(--radius); background: var(--color-surface); overflow: hidden; }
/* min-height:0 lets the log shrink below its content size, so it SCROLLS instead
   of growing the box and pushing the input row off-screen. */
.chat-log { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: var(--sp-4); display: flex; flex-direction: column; gap: var(--sp-3); }
.chat-msg { display: flex; gap: var(--sp-2); max-width: 88%; }
.chat-msg-user { align-self: flex-end; flex-direction: row-reverse; }
.chat-avatar { flex: 0 0 auto; width: 30px; height: 30px; border-radius: 50%; display: inline-flex; align-items: center;
    justify-content: center; font-size: .95rem; }
/* CR-046 (v1.39.2): the AI-icon avatar sits on the theme SURFACE (white in light, dark surface in dark) with a
   subtle ring so it stays legible against the chat background. */
.chat-msg-assistant .chat-avatar { background: var(--color-surface); color: var(--color-accent); box-shadow: inset 0 0 0 1px var(--color-border); }
.chat-msg-user .chat-avatar { background: var(--color-primary); color: var(--color-primary-contrast); }
.chat-bubble { padding: var(--sp-2) var(--sp-3); border-radius: var(--radius); font-size: .92rem; line-height: 1.5;
    white-space: pre-wrap; word-wrap: break-word; }
/* CR-059: the AGENT response has NO bubble fill — it reads as plain text on the chat window background
   (`.chat` = --color-surface). Only the USER turn keeps a coloured bubble. Text colour + padding are
   unchanged; the bubble radius is moot without a fill. */
.chat-msg-assistant .chat-bubble { background: transparent; color: var(--color-text); }
.chat-msg-user .chat-bubble { background: var(--color-primary); color: var(--color-primary-contrast); border-top-right-radius: var(--r-2xs); }
.chat-msg-system { align-self: center; max-width: 100%; }
.chat-msg-system .chat-bubble { background: transparent; color: var(--color-text-muted); font-size: .82rem; font-style: italic; }

/* CR-060: chat LAUNCH (empty) screen. `.chat--launch` (set by JS while the conversation has no messages,
   when mount({launch}) is on) centres the composer in the chat area with an optional greeting above it —
   a fresh, clean start. On the first message the class drops and the composer returns to its normal bottom
   position (Claude/ChatGPT pattern). Opt-in — no effect without the class. */
.chat-launch { display: none; }
.chat--launch { justify-content: center; }
.chat--launch .chat-log { display: none; }
.chat--launch .chat-launch { display: block; text-align: center; padding-inline: var(--sp-4); margin-block-end: var(--sp-4); }
.chat-launch-title { font-size: var(--fs-xl, 1.5rem); font-weight: 650; line-height: 1.25; color: var(--color-text); }
.chat-launch-subtitle { margin-block-start: var(--sp-1); font-size: var(--fs-md, .95rem); color: var(--color-text-muted); }
.chat--launch .chat-input-row { border-top: 0; align-self: center; width: 100%; max-width: 44rem; margin-inline: auto; }

.chat-typing { display: inline-flex; gap: 3px; padding: var(--sp-3) var(--sp-3); }
.chat-typing span { width: 6px; height: 6px; border-radius: 50%; background: var(--color-text-muted); opacity: .5;
    animation: hub-chat-blink 1s infinite; }
.chat-typing span:nth-child(2) { animation-delay: .2s; } .chat-typing span:nth-child(3) { animation-delay: .4s; }
@keyframes hub-chat-blink { 0%, 60%, 100% { opacity: .3; } 30% { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .chat-typing span { animation: none; } }
/* CR-028: rotating "working" status line beside the typing dots. Subtle, muted, no layout jank
   (fixed line-height; the text swaps in place). Fades each phrase in; reduced-motion → no fade. */
.chat-thinking { align-self: center; padding: 0 var(--sp-1); font-size: .82rem; line-height: 1.2;
    color: var(--color-text-muted); font-style: italic; animation: hub-think-fade .35s ease-out; }
@keyframes hub-think-fade { from { opacity: 0; } to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .chat-thinking { animation: none; } }
/* flex:0 0 auto -> the composer never shrinks and is always pinned at the bottom. */
.chat-input-row { flex: 0 0 auto; display: flex; gap: var(--sp-2); padding: var(--sp-3); border-top: 1px solid var(--color-border); background: var(--color-surface); }
.chat-input { flex: 1; resize: none; font: inherit; font-size: .92rem; padding: var(--sp-2) var(--sp-3); max-height: 120px;
    border: 1px solid var(--color-border); border-radius: var(--radius-sm); background: var(--color-surface); color: var(--color-text); }
.chat-input:focus { outline: none; border-color: var(--color-focus); box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-focus) 20%, transparent); }
.chat-send { flex: 0 0 auto; align-self: flex-end; }
.chat[aria-busy="true"] .chat-send { opacity: .55; pointer-events: none; }
/* CR-016: composer Stop affordance — the send control while a reply is generating. */
.chat-send.chat-send-stop { background: var(--color-danger, #b23b3b); border-color: var(--color-danger, #b23b3b); }
.chat-send.is-stopping    { opacity: .7; cursor: default; }
/* CR-048: opt-in composer secondary action(s) beside Send (e.g. "Branch & send") — a ghost/outline control. */
.chat-composer-action { flex: 0 0 auto; align-self: flex-end; display: inline-flex; align-items: center; justify-content: center;
    gap: var(--sp-1); border: 1px solid var(--color-border); background: var(--color-surface); color: var(--color-text);
    border-radius: var(--radius, 8px); padding: var(--sp-2) var(--sp-3); font: inherit; font-size: .9rem; cursor: pointer; }
.chat-composer-action:hover:not(:disabled) { background: var(--color-surface-alt); border-color: var(--color-accent); color: var(--color-accent); }
.chat-composer-action:focus-visible { outline: 2px solid var(--color-focus); outline-offset: 1px; }
.chat-composer-action:disabled { opacity: .5; cursor: default; }
.chat-composer-action-icon { font-size: 1.05em; line-height: 1; }
.chat[aria-busy="true"] .chat-composer-action { opacity: .55; pointer-events: none; }

/* --- CR-049: composer controls bar (NEW DEFAULT layout) ------------------------------------------
   A rounded container holding the autosizing textarea on top and a baseline controls row beneath it
   (left + right zones). Send is a square, rounded, arrow-icon button pinned rightmost. Applied via the
   .chat-input-row--bar class the mount adds; composerBar:false keeps the legacy single row above. */
.chat-input-row--bar {
    flex-direction: column; align-items: stretch; gap: var(--sp-1, 4px);
    padding: var(--sp-2, var(--sp-2)) var(--sp-2, var(--sp-2)) var(--sp-2, var(--sp-2)) var(--sp-3, var(--sp-3));
    border: 1px solid var(--color-border, #d7dee7); border-radius: var(--r-lg, 16px);
    background: var(--color-surface, #fff);
    transition: border-color var(--dur, .15s) var(--ease, ease), box-shadow var(--dur, .15s) var(--ease, ease);
}
.chat-input-row--bar:focus-within { border-color: var(--color-focus); box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-focus) 18%, transparent); }
/* the textarea is now the ink surface inside the container — no border/background of its own */
.chat-input-row--bar .chat-input {
    flex: 1 1 auto; width: 100%; border: 0; background: transparent; box-shadow: none;
    padding: 2px 2px; max-height: 40vh; /* JS also caps to composerMaxHeight (default 40vh) */
}
.chat-input-row--bar .chat-input:focus { outline: none; border: 0; box-shadow: none; }

.chat-controls { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-2, var(--sp-2)); }
.chat-controls-left, .chat-controls-right { display: flex; align-items: center; gap: var(--sp-1, 4px); min-width: 0; }

/* dev controls (button / attach / select / mic) — quiet ghost buttons on the 8pt grid; a 32px (--sp-7)
   control height, a --sp-2 horizontal inset for text pills, and a 20px (--sp-5) glyph throughout. */
.chat-ctl {
    flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-1, 4px);
    min-width: var(--sp-7, 32px); height: var(--sp-7, 32px); padding: 0 var(--sp-2, var(--sp-2));
    border: 0; border-radius: var(--r-full, 999px);
    background: transparent; color: var(--color-text-muted, #667); font: inherit; font-size: .9rem; cursor: pointer;
    transition: background var(--dur, .12s) var(--ease, ease), color var(--dur, .12s) var(--ease, ease);
}
.chat-ctl-attach { padding: 0; width: var(--sp-7, 32px); }   /* the (+) is icon-only */
.chat-ctl:hover:not(:disabled) { background: var(--fill-hover, var(--color-surface-alt, #e3e8ef)); color: var(--color-text, #1a1a1a); }
.chat-ctl:focus-visible { outline: 2px solid var(--color-focus); outline-offset: 1px; }
.chat-ctl:disabled { opacity: .5; cursor: default; }
.chat-ctl-icon { flex: 0 0 auto; font-size: var(--sp-5, 20px); line-height: 1; }
/* the glyph class sits ON the svg (inline-SVG controls) AND may wrap one (HubGlyph) — size both forms */
svg.chat-ctl-icon, .chat-ctl-icon svg { width: var(--sp-5, 20px); height: var(--sp-5, 20px); display: block; }
.chat-ctl-label { white-space: nowrap; }
/* CR-049 fix: an icon-only control (attach, mic, a Send-to-branch button) is a SQUARE icon button, so the
   send-mechanism controls cluster consistently beside the square Send. */
.chat-ctl--icon-only { width: var(--sp-7, 32px); min-width: var(--sp-7, 32px); height: var(--sp-7, 32px); padding: 0; border-radius: var(--r-sm, 8px); gap: 0; }
/* CR-049 P2: select dropdown (caret) + mic active state */
.chat-ctl-select { gap: var(--sp-1); }
.chat-ctl-caret { flex: 0 0 auto; opacity: .7; }
.chat-ctl-caret { width: 16px; height: 16px; }
.chat-ctl-caret svg { width: 16px; height: 16px; display: block; }
.chat-ctl-mic.is-active { background: color-mix(in srgb, var(--color-danger, #b23b3b) 16%, transparent); color: var(--color-danger, #b23b3b); }

/* Send — square, rounded, arrow icon; the required rightmost control. Scoped under .chat-input-row--bar
   so the HUB's square sizing wins over a host's own .chat-send / .btn padding + min-width (which would
   otherwise stretch it), regardless of stylesheet load order. */
.chat-input-row--bar .chat-send--square {
    flex: 0 0 auto; align-self: center; box-sizing: border-box;
    /* On-grid 32px control (--sp-7); concentric corners: container --r-lg(16) minus the row's --sp-2(8)
       padding = --r-sm(8). 20px glyph in a 32px box → 6px optical inset (snug, not swimming). */
    width: var(--sp-7, 32px); height: var(--sp-7, 32px);
    min-width: var(--sp-7, 32px); max-width: var(--sp-7, 32px);
    min-height: var(--sp-7, 32px); max-height: var(--sp-7, 32px); aspect-ratio: 1 / 1; padding: 0;
    border: 0; border-radius: var(--r-sm, 8px); display: inline-flex; align-items: center; justify-content: center;
    background: var(--color-primary, #254c77); color: var(--color-primary-contrast, #fff); cursor: pointer;
    font-size: 0;   /* neutralize any inherited text metrics from a host .btn */
}
.chat-send--square:hover { background: color-mix(in srgb, var(--color-primary, #254c77) 88%, #000); }
.chat-send--square:focus-visible { outline: 2px solid var(--color-focus); outline-offset: 2px; }
/* size only — do NOT set display here, or it would override the arrow/stop show-hide below */
.chat-send--square svg.chat-send-icon, .chat-send--square svg.chat-send-stopicon,
.chat-send--square .chat-send-icon svg, .chat-send--square .chat-send-stopicon svg { width: var(--sp-5, 20px); height: var(--sp-5, 20px); }
.chat-send--square .chat-send-stopicon { display: none; }   /* arrow by default */
.chat-send--square.chat-send-stop { background: var(--color-danger, #b23b3b); }
.chat-send--square.chat-send-stop .chat-send-icon { display: none; }   /* swap arrow → stop square */
.chat-send--square.chat-send-stop .chat-send-stopicon { display: inline-flex; }
.chat-send--square.is-stopping { opacity: .7; cursor: default; }
.chat[aria-busy="true"] .chat-input-row--bar .chat-send--square { opacity: .55; pointer-events: none; }
/* the composerActions ghost button sits comfortably in the bar's right zone */
.chat-input-row--bar .chat-composer-action { align-self: center; padding: var(--sp-2) var(--sp-3); }
/* CR-049 fix: an icon-only composerAction (Send-to-branch, label:'') is a SQUARE icon button in the send
   cluster — same on-grid metrics as Send: 32px box, --r-sm concentric corner, 20px glyph. */
.chat-input-row--bar .chat-composer-action--icon { width: var(--sp-7, 32px); min-width: var(--sp-7, 32px); height: var(--sp-7, 32px); padding: 0; gap: 0; border-radius: var(--r-sm, 8px); }
.chat-input-row--bar .chat-composer-action--icon .chat-composer-action-icon { font-size: var(--sp-5, 20px); }
.chat-input-row--bar .chat-composer-action--icon .chat-composer-action-icon svg { width: var(--sp-5, 20px); height: var(--sp-5, 20px); display: block; }
/* CR-052: send-mechanism buttons (Send, a secondary send action such as "Branch & send", and the mic/voice
   control) sit SQUARE with only their icon, and EXPAND on hover/focus to reveal their label — growing
   right→left (the icon stays put; the label opens to its left). Because the right zone is right-anchored
   (.chat-controls is space-between), the whole float-right cluster reflows as a button expands. One
   behaviour across all three, and every icon+label send action a dev adds gets it automatically. */
.chat-input-row--bar .chat-send-btn {
    box-sizing: border-box; overflow: hidden; flex: 0 0 auto;
    display: inline-flex; align-items: center; justify-content: center;
    height: var(--sp-7, 32px); min-width: var(--sp-7, 32px); max-width: var(--sp-7, 32px);
    width: auto; aspect-ratio: auto; padding: 0; gap: 0;
    transition: max-width var(--dur, .18s) var(--ease, ease), padding var(--dur, .18s) var(--ease, ease),
                background var(--dur, .12s) var(--ease, ease), color var(--dur, .12s) var(--ease, ease);
}
.chat-input-row--bar .chat-send-btn:hover,
.chat-input-row--bar .chat-send-btn:focus-visible,
.chat-input-row--bar .chat-send-btn:focus-within {
    max-width: 12rem; padding: 0 var(--sp-3, 12px);
}
/* The reveal label: clipped to zero width by default (button stays square), opens on hover/focus. Sets
   its own rem font-size so the Send button's font-size:0 (host-metric guard) doesn't collapse it. */
.chat-send-btn .chat-send-label {
    display: inline-block; max-width: 0; overflow: hidden; white-space: nowrap;
    font-size: .82rem; line-height: 1; opacity: 0;
    transition: max-width var(--dur, .18s) var(--ease, ease), opacity var(--dur, .18s) var(--ease, ease),
                margin var(--dur, .18s) var(--ease, ease);
}
.chat-send-btn:hover .chat-send-label,
.chat-send-btn:focus-visible .chat-send-label,
.chat-send-btn:focus-within .chat-send-label {
    max-width: 8rem; opacity: 1; margin-left: var(--sp-1, 4px);   /* label sits to the RIGHT of the icon */
}
/* Send stays busy-hidden when the chat is generating (matches the pre-CR-052 rule). */
@media (prefers-reduced-motion: reduce) { .chat-input-row--bar, .chat-ctl, .chat-send-btn, .chat-send-label { transition: none; } }

/* ---- Chat thread sidebar (hub-thread-list) — Phase 5 addition for OnyxGPT ----
   Renders conversations supplied by the app (which fetches them from Azure).
   No thread data is stored in the HUB. Pairs with .chat via .chat-workspace. */
.chat-workspace { display: grid; grid-template-columns: 260px 1fr; gap: var(--sp-4); align-items: stretch; }
.hub-thread-list{ display: flex; flex-direction: column; border: 1px solid var(--color-border);
    border-radius: var(--radius); background: var(--color-surface); overflow: hidden; max-height: 78vh; }
.thread-head { display: flex; align-items: center; gap: var(--sp-2); padding: var(--sp-2) var(--sp-3); border-bottom: 1px solid var(--color-border); }
.thread-head .thread-title-label { font-weight: 600; color: var(--color-heading); flex: 1; font-size: .9rem; }
.thread-new { flex: 0 0 auto; }
/* v1.61.2: clip horizontally (was `overflow-y:auto`, which makes overflow-x compute to `auto` → a stray
   horizontal scrollbar in the collapsed clamp, where opacity:0 labels/toolbar still take layout width). A
   vertical conversation list never needs to scroll sideways — titles clip via ellipsis. */
.thread-scroll { overflow: hidden auto; flex: 1; padding: var(--sp-2); }
.thread-item { display: flex; align-items: center; gap: var(--sp-2); padding: var(--sp-2) var(--sp-2); border-radius: var(--radius-sm);
    cursor: pointer; color: var(--color-text); }
.thread-item:hover { background: var(--color-surface-alt); }
.thread-item.is-active { background: var(--color-surface-alt); box-shadow: inset 3px 0 0 0 var(--color-accent); }
.thread-item-main { flex: 1; min-width: 0; }
.thread-item-title { font-size: .88rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: var(--color-text); }
/* Date line — hosts the date and (for a parent with branches) the relocated branch-count on the SAME line,
   so the title above keeps full horizontal width. */
.thread-item-time { font-size: .72rem; color: var(--color-text-muted); display: flex; align-items: center; gap: var(--sp-2); min-width: 0; }
/* date pinned LEFT (fills the line, truncates if long); the branch pill is pushed to the RIGHT of the same line */
.thread-item-date { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.thread-rename-input { width: 100%; font: inherit; font-size: .88rem; padding: var(--sp-1) var(--sp-1);
    border: 1px solid var(--color-focus); border-radius: var(--r-2xs); background: var(--color-surface); color: var(--color-text); }
.thread-actions { flex: 0 0 auto; }
.thread-actions .dropdown-menu { min-width: 150px; }
/* CR-053: per-branch (child) ⋮ actions — same control as a conversation row, compact for the dense tree. */
.thread-child-actions { flex: 0 0 auto; }
.thread-child-actions.btn-icon { width: var(--sp-6, 24px); min-height: var(--sp-6, 24px); height: var(--sp-6, 24px); }
.thread-child-actions .icon { font-size: 1.05rem; }
.thread-empty { padding: var(--sp-4) var(--sp-3); color: var(--color-text-muted); font-size: .85rem; text-align: center; }

/* ============================================================================
   CR-044: branch (child-item) navigation under a conversation — opt-in
   Tri-zone row (children-mode): [⋮ actions LEFT] [title = open] [branch drawer toggle RIGHT].
   A lazy, independently-foldable branch tree renders in a drawer below the row.
   ========================================================================== */
.thread-item-wrap { display: block; }
/* CR-044 P2: panel-level expand/collapse-all toolbar (top of the list) */
.thread-tree-toolbar { display: flex; gap: 4px; justify-content: flex-end; padding: 0 2px 4px; }
.thread-tree-btn { font-size: .72rem; color: var(--color-text-muted); background: transparent; border: 0;
    cursor: pointer; padding: 2px var(--sp-2); border-radius: var(--r-xs); }
.thread-tree-btn:hover { color: var(--color-text); background: var(--color-surface-2, var(--color-surface-alt)); }
.thread-tree-btn:focus-visible { outline: 2px solid var(--color-focus); outline-offset: 1px; }
/* The branch mechanism — the dedicated control that opens the child drawer (does NOT open the convo). The
   WHOLE thing (split glyph + count + expand chevron) is one system-coloured PILL on the date line, pushed to
   the right, so the title above keeps full width and the count sits with its control. */
.thread-branch-toggle { flex: 0 0 auto; margin-left: auto; display: inline-flex; align-items: center; gap: 2px;
    border: 0; cursor: pointer; padding: 2px var(--sp-2); border-radius: var(--r-full); line-height: 1.4;
    font-size: .7rem; font-weight: var(--fw-semibold, 600);
    background: color-mix(in srgb, var(--color-primary, #254c77) 12%, transparent); color: var(--color-primary, #254c77); }
.thread-branch-toggle:hover { background: color-mix(in srgb, var(--color-primary, #254c77) 22%, transparent); }
.thread-branch-toggle:focus-visible { outline: 2px solid var(--color-focus); outline-offset: 1px; }
.thread-branch-toggle.is-open { background: var(--color-primary, #254c77); color: var(--color-primary-contrast, #fff); }
.thread-branch-split { width: 1.05em; height: 1.05em; flex: 0 0 auto; }
.thread-branch-count-n { line-height: 1; }
.thread-branch-caret { font-size: 1em; transition: transform .15s ease; }
.thread-branch-toggle.is-open .thread-branch-caret { transform: rotate(90deg); }

/* ============================================================================
   CR-066: viewport-clamp row anatomy (children-mode). Every row leads with a fixed ICON so the collapsed
   clamp reads as one icon column; labels sit to the right and are hidden by the clamp. Parents = a 'chat'
   icon (36px); children = a smaller 'arrow_split' icon, RIGHT-ALIGNED to the parent icon's right edge, with
   the hierarchy connector at the drawer's far-left (2px) and all titles (parent + child) sharing one left
   edge (uniform icon→text gap).
   ========================================================================== */
/* Parent row (children-mode): [chat icon] [main]. gear + date + branch-pill ride the date line inside main.
   pad-left = --sp-1 (4px): inside .thread-scroll's --sp-2 (8px) padding that puts the 36px icon's LEFT at
   12px — the same inset as the header toggle and (via the child rule) the child icons ⇒ one clean column. */
.thread-item.is-childrenmode { gap: var(--sp-2); padding: var(--sp-1) var(--sp-2) var(--sp-1) var(--sp-1); }
.thread-item-icon { flex: 0 0 auto; box-sizing: border-box; width: 36px; height: 36px;
    display: inline-flex; align-items: center; justify-content: center; border-radius: var(--r-md);
    border: 1px solid var(--color-border); background: color-mix(in srgb, var(--color-primary, #254c77) 12%, transparent); color: var(--color-primary, #254c77); }
.thread-item-icon .icon { font-size: 1.2rem; line-height: 1; }
/* the ⚙ actions gear leads the date line, before the date (relocated from the old left ⋮) */
.thread-item-time > .thread-actions { flex: 0 0 auto; }
.thread-item.is-childrenmode .thread-actions.btn-icon { width: var(--sp-6, 24px); height: var(--sp-6, 24px); min-height: var(--sp-6, 24px); }

/* the drawer + foldable branch tree — the connector is a single guide line at the drawer's FAR LEFT (2px). */
.thread-children { position: relative; padding: 2px 0 var(--sp-1); }
.thread-children::before { content: ''; position: absolute; left: 2px; top: 0; bottom: var(--sp-2);
    width: 2px; background: var(--color-border); }
.thread-children-loading, .thread-children-empty { padding: var(--sp-1) var(--sp-2) var(--sp-1) var(--sp-4); font-size: .8rem; color: var(--color-text-muted); }
/* Child row: pad-left = --sp-4 (16px) so the 24px icon's RIGHT edge meets the 36px parent icon's right edge
   (4+36 = 16+24 = 40 from the row's content box); gap --sp-2 (8px) ⇒ every title's left edge = 48px. Deeper
   nesting indents by --depth (icon+label shift together, gap preserved) — the connector marks the hierarchy. */
.thread-child { display: flex; align-items: center; gap: var(--sp-2); cursor: pointer; color: var(--color-text);
    font-size: .84rem; border-radius: var(--radius-sm); padding: var(--sp-1) var(--sp-2);
    padding-inline-start: calc(var(--sp-4) + var(--depth, 0) * var(--sp-4)); }
.thread-child:hover { background: var(--color-surface-alt); }
.thread-child.is-active { background: var(--color-surface-alt); box-shadow: inset 3px 0 0 0 var(--color-accent); }
/* the smaller 'arrow_split' leading icon (a button when the branch has sub-branches — click to fold). */
.thread-child-icon { flex: 0 0 auto; box-sizing: border-box; width: 24px; height: 24px;
    display: inline-flex; align-items: center; justify-content: center; border-radius: var(--r-sm);
    border: 1px solid var(--color-border); background: color-mix(in srgb, var(--color-primary, #254c77) 12%, transparent); color: var(--color-primary, #254c77);
    padding: 0; }
.thread-child-icon.is-toggle { cursor: pointer; }
.thread-child-icon.is-toggle:hover { background: color-mix(in srgb, var(--color-primary, #254c77) 22%, transparent); }
.thread-child-icon.is-toggle:focus-visible { outline: 2px solid var(--color-focus); outline-offset: 1px; }
.thread-child-icon.is-toggle.is-collapsed { opacity: .72; }   /* folded cue */
.thread-child-split { width: 15px; height: 15px; flex: 0 0 auto; }
.thread-child-label { flex: 1 1 auto; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* NOTIFY P4 (v1.69.4): the red-dot notification toast on the icon SQUARE of a parent/child row, at its TOP-RIGHT
   (matches the notebook toast — HubTabs NOTIFY P2 — after the notebook rail's 90° rotation). Dev-driven + dev-scoped
   (all-items / parents-only); --color-danger drives the dot. The square becomes the positioning context only when flagged. */
.thread-item-icon.has-notify, .thread-child-icon.has-notify { position: relative; }
.thread-notify { position: absolute; z-index: 2; top: -2px; right: -2px; width: 9px; height: 9px;
    border-radius: 50%; background: var(--color-danger, #e5484d); box-shadow: 0 0 0 2px var(--color-surface, #eef1f4); }
.thread-child-actions { margin-left: auto; }   /* ⚙ gear options on the RIGHT of the child row */
@media (prefers-reduced-motion: reduce) { .thread-branch-caret { transition: none; } }

/* ---- Viewport clamp: when the enclosing HubPanel is COLLAPSED (and not peeking), only the leading icon
   column shows — every label/date/gear/pill is hidden; a peek (is-peek) reveals them again. ---- */
.hub-panel.is-collapsed:not(.is-peek) .thread-item-main,
.hub-panel.is-collapsed:not(.is-peek) .thread-child-label,
.hub-panel.is-collapsed:not(.is-peek) .thread-child-actions,
.hub-panel.is-collapsed:not(.is-peek) .thread-arch-label,
.hub-panel.is-collapsed:not(.is-peek) .thread-tree-toolbar { opacity: 0; pointer-events: none; }
/* CR-067 (v1.61.1): the archive toggle is an ICON+TEXT button (matches New chat / the row icons), so it
   clamps in the collapsed side-panel — the 36px icon sits in the shared icon column, the label hides.
   foot pad-left --sp-2 (8) + toggle pad-left --sp-1 (4) = the same 12px inset as the list rows. */
.thread-foot { padding: var(--sp-2); border-top: 1px solid var(--color-border); }
.thread-archived-toggle { display: flex; align-items: center; gap: var(--sp-2); width: 100%; box-sizing: border-box;
    background: none; border: 0; color: var(--color-text); cursor: pointer; font: inherit; font-size: .88rem;
    padding: var(--sp-1); border-radius: var(--radius-sm); text-align: left; }
.thread-archived-toggle:hover { background: var(--color-surface-alt); color: var(--color-text); }
.thread-arch-icon { flex: 0 0 auto; box-sizing: border-box; width: 36px; height: 36px;
    display: inline-flex; align-items: center; justify-content: center; border-radius: var(--r-md);
    border: 1px solid var(--color-border); background: color-mix(in srgb, var(--color-primary, #254c77) 12%, transparent); color: var(--color-primary, #254c77); }
.thread-arch-icon .icon { font-size: 1.2rem; line-height: 1; }
.thread-arch-label { flex: 1 1 auto; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
@media (max-width: 760px) { .chat-workspace { grid-template-columns: 1fr; } .hub-thread-list{ max-height: 300px; } }

/* ---- Thread sidebar per-card status (for async/long-running jobs) ---- */
.thread-status { display: inline-flex; align-items: center; gap: var(--sp-1); margin-top: var(--sp-1);
    font-size: .7rem; color: var(--color-text-muted); }
.thread-status-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--color-accent);
    animation: hub-thread-pulse 1.2s ease-in-out infinite; }
@keyframes hub-thread-pulse { 0%, 100% { opacity: .35; } 50% { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .thread-status-dot { animation: none; } }
.thread-item.is-processing .thread-item-title { padding-right: var(--sp-1); }


/* ============================================================================
   ONYX LOADER (v1.5.1) — light, request-lifecycle status indicator.
   Client-side presentation only. Presets draw with currentColor and read speed
   from --loader-speed, so an admin can retune COLOR and SPEED without touching
   code (the admin panel arrives in a later phase; these vars are the surface).
   States: idle · sending · awaiting · receiving · error · timeout.
   ============================================================================ */
.hub-loader{
    --loader-color: var(--color-primary);
    --loader-speed: 1.3s;
    --loader-size: 26px;
    display: inline-flex; align-items: center; gap: var(--sp-2); color: var(--loader-color);
    min-height: var(--loader-size);
}
.hub-loader-anim{ display: inline-flex; align-items: center; justify-content: center;
    width: var(--loader-size); height: var(--loader-size); flex: 0 0 auto; }
.hub-loader-anim i{ display: block; }
.hub-loader-msg{ font-size: .82rem; color: var(--color-text-muted); }

/* State handling: active states animate; idle rests dim; error/timeout are static (no spinner). */
.hub-loader[data-state="idle"] .hub-loader-anim{ opacity: .3; }
.hub-loader[data-state="idle"] .hub-loader-anim i{ animation-play-state: paused; }
.hub-loader[data-state="error"] .hub-loader-anim,
.hub-loader[data-state="timeout"] .hub-loader-anim{ display: none; }
.hub-loader[data-state="error"] .hub-loader-msg{ color: var(--color-danger); }
.hub-loader[data-state="timeout"] .hub-loader-msg{ color: var(--color-text); }

/* Preset: dots (default, lightest) */
.hub-loader[data-preset="dots"] .hub-loader-anim{ gap: 15%; }
.hub-loader[data-preset="dots"] .hub-loader-anim i{ width: 22%; aspect-ratio: 1; border-radius: 50%;
    background: currentColor; animation: hub-loader-dots var(--loader-speed) ease-in-out infinite; }
.hub-loader[data-preset="dots"] .hub-loader-anim i:nth-child(2){ animation-delay: calc(var(--loader-speed) * .16); }
.hub-loader[data-preset="dots"] .hub-loader-anim i:nth-child(3){ animation-delay: calc(var(--loader-speed) * .32); }
@keyframes hub-loader-dots { 0%, 80%, 100% { opacity: .3; transform: scale(.75); } 40% { opacity: 1; transform: scale(1); } }

/* Preset: bars (adapted from the provided sliding-bars example) */
.hub-loader[data-preset="bars"] .hub-loader-anim{ gap: 14%; align-items: flex-end; }
.hub-loader[data-preset="bars"] .hub-loader-anim i{ width: 20%; height: 100%; border-radius: var(--r-2xs);
    background: currentColor; transform-origin: bottom; animation: hub-loader-bars var(--loader-speed) ease-in-out infinite; }
.hub-loader[data-preset="bars"] .hub-loader-anim i:nth-child(2){ animation-delay: calc(var(--loader-speed) * .15); }
.hub-loader[data-preset="bars"] .hub-loader-anim i:nth-child(3){ animation-delay: calc(var(--loader-speed) * .30); }
@keyframes hub-loader-bars { 0%, 100% { transform: scaleY(.35); opacity: .5; } 50% { transform: scaleY(1); opacity: 1; } }

/* Preset: ring (single light arc) */
.hub-loader[data-preset="ring"] .hub-loader-anim i{ width: 100%; height: 100%; border-radius: 50%;
    border: 3px solid currentColor; border-right-color: transparent; box-sizing: border-box;
    animation: hub-loader-ring var(--loader-speed) linear infinite; }
@keyframes hub-loader-ring { to { transform: rotate(360deg); } }

/* Respect reduced motion: stop spinners, keep a calm static indicator visible. */
@media (prefers-reduced-motion: reduce) {
    .hub-loader-anim i{ animation: none !important; opacity: .8; }
    .hub-loader[data-preset="ring"] .hub-loader-anim i{ border-right-color: currentColor; opacity: .5; }
}

/* Loader image mode (uploaded custom art, rendered via <img>) */
.hub-loader-img{ width: var(--loader-size); height: var(--loader-size); object-fit: contain; display: block; }
.hub-loader[data-state="idle"] .hub-loader-img{ opacity: .4; }
.hub-loader[data-state="error"] .hub-loader-img,
.hub-loader[data-state="timeout"] .hub-loader-img{ display: none; }

/* Custom reviewed loader: "orbit" (registered in LoaderRegistry). Light, a
   single dot circling — currentColor + --loader-speed keep it admin-tunable. */
.hub-loader[data-preset="orbit"] .hub-loader-anim{ position: relative; }
.hub-loader[data-preset="orbit"] .hub-loader-anim i{
    position: absolute; inset: 0; margin: auto; width: 26%; height: 26%; border-radius: 50%;
    background: currentColor; animation: hub-loader-orbit var(--loader-speed) linear infinite; }
@keyframes hub-loader-orbit {
    from { transform: rotate(0deg) translateX(150%); }
    to   { transform: rotate(360deg) translateX(150%); }
}


/* ============================================================================
   REFINEMENTS (v1.7.1) — sidebar controls, native inputs, dismissible alerts
   ============================================================================ */

/* Bug: logout/ghost buttons inside the DARK sidebar need light text */
.side-foot .btn-ghost, .sidebar .btn-ghost {
    color: var(--color-sidebar-text, #e9eaec); border-color: rgba(255,255,255,.2); background: transparent;
}
.side-foot .btn-ghost:hover, .sidebar .btn-ghost:hover {
    background: rgba(255,255,255,.09); border-color: rgba(255,255,255,.35); color: #fff;
}

/* Native file input -> themed button */
input[type="file"] { font: inherit; font-size: var(--fs-sm); color: var(--slate); max-width: 100%; }
input[type="file"]::file-selector-button {
    margin-right: var(--sp-3); padding: var(--sp-2) var(--sp-4); min-height: 36px;
    font: inherit; font-weight: var(--fw-medium); font-size: var(--fs-sm);
    background: var(--fill-subtle); color: var(--ink); border: 1px solid var(--hairline);
    border-radius: var(--r-sm); cursor: pointer; transition: background var(--dur) var(--ease);
}
input[type="file"]::file-selector-button:hover { background: var(--fill-hover); }

/* Native color input -> tidy rounded swatch */
input[type="color"] {
    -webkit-appearance: none; appearance: none; width: 46px; height: 38px; min-height: 0;
    padding: 3px; border: 1px solid var(--color-border, #e6e6ea); border-radius: var(--r-sm);
    background: var(--surface); cursor: pointer; vertical-align: middle;
}
input[type="color"]::-webkit-color-swatch-wrapper { padding: 0; }
input[type="color"]::-webkit-color-swatch { border: none; border-radius: var(--r-xs); }
input[type="color"]::-moz-color-swatch { border: none; border-radius: var(--r-xs); }

/* Dismissible alerts (JS adds .alert-close; success/info auto-dismiss) */
.alert { position: relative; align-items: flex-start; }
.alert-close { flex: 0 0 auto; margin-left: auto; background: none; border: 0; cursor: pointer; padding: 2px;
    line-height: 0; color: currentColor; opacity: .6; border-radius: var(--r-xs); transition: opacity var(--dur) var(--ease); }
.alert-close:hover { opacity: 1; }
.alert-close .icon { font-size: 1rem; }
.alert.is-leaving { opacity: 0; transform: translateY(-4px); transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease); }


/* ============================================================================
   CONTAINED APP PAGES (v1.9.0)
   Set per app in Admin -> Apps (Layout = "Contained"). The HUB owns this, not the app.
   ($fullHeight in View::render() remains as an explicit override.)

   The HUB shell is UNCHANGED: sidebar, topbar and FOOTER all remain. The content
   container simply occupies exactly the space left over (viewport - topbar - footer),
   the DOCUMENT never scrolls, and the app fills that container 100%. The only thing
   that scrolls is the app's own inner region (e.g. the chat log).
   ========================================================================== */
.main-col.is-fill { height: 100dvh; min-height: 0; overflow: hidden; }
/* The content container takes the leftover height and does not scroll itself. */
.main-col.is-fill .content { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column;
    overflow: hidden; padding-top: var(--sp-5); padding-bottom: var(--sp-5); }
/* Topbar and footer keep their natural height and stay visible. */
.main-col.is-fill .topbar, .main-col.is-fill .foot { flex: 0 0 auto; }

/* The app surface fills the container completely. Mark your app root .app-surface,
   or use .chat / .chat-workspace directly — all stretch to fill and are allowed to
   shrink (min-height:0) so their INNER region scrolls rather than the page. */
.main-col.is-fill .app-surface,
.main-col.is-fill .chat,
.main-col.is-fill .chat-workspace { flex: 1 1 auto; min-height: 0; height: auto; max-height: none; }
.main-col.is-fill .chat-workspace > * { min-height: 0; }
/* CR-002: fill the container (lift the 78vh cap), and keep scrolling inside
   .thread-scroll so the head/foot stay pinned — not on the outer list. */
.main-col.is-fill .hub-thread-list{ height: 100%; max-height: none; min-height: 0; overflow: hidden; }

/* Generic full-height surface for non-chat apps */
.app-surface { display: flex; flex-direction: column; min-height: 0; }

@media (max-width: 900px) {
    /* Stack the thread sidebar above the chat on small screens */
    .chat-workspace { grid-template-columns: 1fr; }
    .main-col.is-fill .chat-workspace { display: flex; flex-direction: column; }
}


/* ============================================================================
   CHAT: top fade + jump-to-latest (v1.9.1)
   ========================================================================== */
/* Soft fade at the top of the log so messages dissolve into the container edge
   instead of being sliced off. Sits above the log, ignores clicks, and uses the
   chat's own surface colour so it works in light and dark themes. */
.chat::before {
    content: ""; position: absolute; top: 0; left: 1px; right: 1px; height: 16px;
    background: linear-gradient(to bottom, var(--color-surface) 0%, transparent 100%);
    pointer-events: none; z-index: 2; border-radius: var(--radius) var(--radius) 0 0;
}
/* v1.35.12: composed usages with a title header above the chat (HubChat mount({topFade:false}),
   e.g. notebook tabs) suppress the chat-box top fade — the fade belongs to the header baseline
   (.hub-tab-header::after) instead of the chat box top. */
.chat.chat--no-topfade::before { content: none; }
/* Jump-to-latest: only shown when the reader has scrolled away from the newest
   message. Sits just above the composer. */
/* Floats over the BASELINE OF THE MESSAGE LOG, horizontally centred, so it reads
   as belonging to the conversation rather than to the composer next to it. */
.chat-jump {
    position: absolute; left: 50%; bottom: calc(var(--chat-composer-h, 64px) + var(--sp-3));
    z-index: 3; display: inline-flex; align-items: center; justify-content: center;
    width: 34px; height: 34px; border-radius: 50%; cursor: pointer;
    background: var(--color-surface); color: var(--color-text);
    border: 1px solid var(--color-border); box-shadow: var(--el-2, 0 4px 12px rgba(0,0,0,.12));
    opacity: 0; visibility: hidden; transform: translate(-50%, 6px);
    transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease), visibility var(--dur);
}
.chat-jump.is-visible { opacity: 1; visibility: visible; transform: translate(-50%, 0); }
.chat-jump:hover { background: var(--fill-hover, var(--color-surface-alt)); }
.chat-jump:focus-visible { outline: 2px solid var(--color-focus); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) { .chat-jump { transition: none; } }

/* ============================================================================
   BRANDING (v1.10.0) — uploaded logo / favicon / AI chat icon
   ========================================================================== */
.brand-logo { display: block; max-width: 100%; }
.brand-logo img, .brand-logo svg { display: block; max-width: 100%; height: auto; max-height: 46px; }
/* Recolour monochrome SVG logos from the Branding colour pickers. If no colour is
   set, --logo-color is undefined and the artwork keeps its own colours. */
.brand-logo svg [fill]:not([fill="none"]),
.brand-logo svg path:not([fill]),
.brand-logo svg circle:not([fill]),
.brand-logo svg rect:not([fill]),
.brand-logo svg polygon:not([fill]) { fill: var(--logo-color, currentColor); }
.brand-logo svg [stroke]:not([stroke="none"]) { stroke: var(--logo-color, currentColor); }
/* Only recolour when a colour was actually chosen. */
:root:not([style*="--logo-color"]) .brand-logo svg { }
/* Dark-mode logo swap (only when a dark file was uploaded) */
.brand-logo .brand-logo-dark { display: none; }
[data-theme="dark"] .brand-logo.has-dark .brand-logo-light { display: none; }
[data-theme="dark"] .brand-logo.has-dark .brand-logo-dark { display: block; }

/* AI chat icon colour (applies to the default glyph) */
.chat-msg-assistant .chat-avatar .icon { color: var(--ai-icon-color, var(--color-accent)); }

/* Admin preview tiles */
.brand-preview { padding: var(--sp-4); border: 1px dashed var(--color-border); border-radius: var(--r-sm);
    background: var(--color-surface); display: inline-flex; align-items: center; justify-content: center; min-width: 140px; }
.brand-preview img { max-width: 200px; max-height: 64px; height: auto; display: block; }
.brand-preview-dark { background: #1a1b1e; }

/* App links in the sidebar carry a small Beta tag without breaking the row */
.side-nav a .badge-beta { margin-left: auto; font-size: .62rem; padding: 1px var(--sp-2); opacity: .9; }
.side-nav a { display: flex; align-items: center; gap: var(--sp-2); }

/* ============================================================================
   AVATAR (v1.12.0) — Microsoft photo with initials fallback
   ========================================================================== */
.avatar { position: relative; flex: 0 0 auto; width: 34px; height: 34px; border-radius: 50%;
    overflow: hidden; background: var(--color-accent, #daac30); color: #fff;
    display: inline-flex; align-items: center; justify-content: center; font-weight: 600; font-size: .8rem; }
/* Initials show when there's no <img> (none rendered, or it 404'd and removed itself) */
.avatar::before { content: attr(data-initials); position: absolute; inset: 0;
    display: flex; align-items: center; justify-content: center; }
.avatar img { position: relative; z-index: 1; width: 100%; height: 100%; object-fit: cover; }
/* CR-045: size modifiers for HubAvatar.render({size:'sm'|'lg'}) */
.avatar-sm { width: 26px; height: 26px; font-size: .68rem; }
.avatar-lg { width: 46px; height: 46px; font-size: 1rem; }
.side-user { display: flex; align-items: center; gap: var(--sp-3); }
.side-user-text { display: flex; flex-direction: column; min-width: 0; }
.side-user-name, .side-user-role { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Chat: user bubble avatar can reuse the same photo via a component (see docs) */
.chat-avatar.has-photo { background-size: cover; background-position: center; }
.chat-avatar.has-photo .icon { display: none; }


/* CR-003: user photo in chat bubbles, with initials fallback.
   The <img> sits above the initials; if it 404s it removes itself (JS) and the
   ::before initials show through. Assistant avatars are unaffected. */
.chat-avatar { position: relative; overflow: hidden; }
.chat-avatar[data-initials]::before {
    content: attr(data-initials); position: absolute; inset: 0;
    display: flex; align-items: center; justify-content: center;
    font-size: .7rem; font-weight: 600; line-height: 1;
}
.chat-avatar img { position: relative; z-index: 1; width: 100%; height: 100%; object-fit: cover; }
/* CR-046: the assistant AI icon is contained + centred (not full-bleed like the user photo) — matches the
   prior 64% background sizing. */
.chat-avatar img.chat-ai-icon { width: 64%; height: 64%; object-fit: contain; }

/* ============================================================================
   CHAT RICH FORMAT (v1.11.5, CR-001) — prose + components for assistant messages
   rendered via OnyxMarkdown. Scoped to .chat-bubble.is-markdown so nothing leaks.
   ========================================================================== */
/* CR-061: markdown prose refined for a clean, easy-to-read, Apple-inspired chat answer — comfortable
   line-height + block rhythm, real list indentation with visible markers, and headings in the SYSTEM
   font with clear section spacing (the global h1–h4 rule paints them in the Playfair serif display
   face, which reads wrong inside a chat answer — reset here). */
.chat-bubble.is-markdown { line-height: 1.6; }
.chat-bubble.is-markdown > :first-child { margin-top: 0; }
.chat-bubble.is-markdown > :last-child { margin-bottom: 0; }
.chat-bubble.is-markdown p { margin: 0 0 .75em; }

/* Lists — a clear hanging indent, explicit markers, comfortable row rhythm; nested lists tuck in. */
.chat-bubble.is-markdown ul,
.chat-bubble.is-markdown ol { margin: .4em 0 .75em; padding-left: 1.5em; }
.chat-bubble.is-markdown ul { list-style: disc outside; }
.chat-bubble.is-markdown ol { list-style: decimal outside; }
.chat-bubble.is-markdown li { margin: .3em 0; padding-left: .2em; }
.chat-bubble.is-markdown li::marker { color: var(--color-text-muted, #667); }
.chat-bubble.is-markdown li > ul,
.chat-bubble.is-markdown li > ol { margin: .3em 0 .15em; }

/* Headings — system/body font (not the serif display), chat-appropriate scale, generous top padding
   so each section breathes; the first block stays flush (the :first-child reset above). */
.chat-bubble.is-markdown h1,
.chat-bubble.is-markdown h2,
.chat-bubble.is-markdown h3,
.chat-bubble.is-markdown h4 {
    font-family: var(--font-body, system-ui, -apple-system, "Segoe UI", sans-serif);
    color: var(--color-text); font-weight: var(--fw-semibold, 600);
    line-height: 1.3; letter-spacing: -.01em; margin: 1.15em 0 .45em;
}
.chat-bubble.is-markdown h1 { font-size: 1.35em; }
.chat-bubble.is-markdown h2 { font-size: 1.2em; }
.chat-bubble.is-markdown h3 { font-size: 1.08em; }
.chat-bubble.is-markdown h4 { font-size: 1em; text-transform: uppercase; letter-spacing: .03em; color: var(--color-text-muted, #667); }

/* Eyebrow / kicker label */
.chat-bubble.is-markdown .md-eyebrow {
    font-size: .72em; font-weight: 700; text-transform: uppercase; letter-spacing: .08em;
    color: var(--color-accent, #daac30); margin: .2em 0 .1em;
}

/* Inline + block code */
.chat-bubble.is-markdown code {
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace; font-size: .9em;
    background: var(--fill-subtle, rgba(127,127,127,.14)); padding: .1em .35em; border-radius: var(--r-2xs);
}
.chat-bubble.is-markdown .md-code {
    margin: 0 0 .75em; border: 1px solid var(--color-border); border-radius: var(--r-sm, 6px); overflow: hidden;
}
.chat-bubble.is-markdown .md-code-head {
    display: flex; align-items: center; justify-content: space-between;
    padding: .3em .6em; background: var(--fill-subtle, rgba(127,127,127,.14));
    border-bottom: 1px solid var(--color-border);
}
.chat-bubble.is-markdown .md-code-lang { font-size: .72em; text-transform: uppercase; letter-spacing: .05em; opacity: .7; }
.chat-bubble.is-markdown .md-code-copy {
    font-size: .72em; padding: .12em .5em; border: 1px solid var(--color-border);
    background: var(--color-surface); border-radius: var(--r-2xs); cursor: pointer; color: var(--color-text);
}
.chat-bubble.is-markdown .md-code-copy:hover { background: var(--fill-hover, var(--color-surface-alt)); }
.chat-bubble.is-markdown .md-code pre { margin: 0; padding: .7em .8em; overflow-x: auto; }
.chat-bubble.is-markdown .md-code pre code { background: none; padding: 0; font-size: .88em; }

/* Blockquote */
.chat-bubble.is-markdown blockquote {
    margin: 0 0 .75em; padding: .1em 0 .1em .8em; border-left: 3px solid var(--color-border);
    color: var(--color-text-muted, inherit);
}

/* Callouts / admonitions (Note / Tip / Warning / Important / Caution) */
.chat-bubble.is-markdown .md-callout {
    display: flex; gap: .55em; margin: 0 0 .75em; padding: .6em .7em;
    border: 1px solid var(--color-border); border-left-width: 3px; border-radius: var(--r-sm, 6px);
    background: var(--fill-subtle, rgba(127,127,127,.08));
}
.chat-bubble.is-markdown .md-callout-icon .icon { font-size: 1.1em; }
.chat-bubble.is-markdown .md-callout-body { min-width: 0; }
.chat-bubble.is-markdown .md-callout-body > :last-child { margin-bottom: 0; }
.chat-bubble.is-markdown .md-callout-label { font-weight: 700; font-size: .82em; text-transform: uppercase; letter-spacing: .04em; margin-bottom: .15em; }
/* callout hues → Apple system status tokens (was Tailwind-ish #3b82f6/#16a34a/#d97706/#7c3aed/#dc2626) */
.chat-bubble.is-markdown .md-callout-note     { border-left-color: var(--color-info); }
.chat-bubble.is-markdown .md-callout-note     .md-callout-icon .icon,
.chat-bubble.is-markdown .md-callout-note     .md-callout-label { color: var(--color-info); }
.chat-bubble.is-markdown .md-callout-tip      { border-left-color: var(--color-success, #34c759); }
.chat-bubble.is-markdown .md-callout-tip      .md-callout-icon .icon,
.chat-bubble.is-markdown .md-callout-tip      .md-callout-label { color: var(--color-success, #34c759); }
.chat-bubble.is-markdown .md-callout-warning  { border-left-color: var(--color-warning); }
.chat-bubble.is-markdown .md-callout-warning  .md-callout-icon .icon,
.chat-bubble.is-markdown .md-callout-warning  .md-callout-label { color: var(--color-warning); }
.chat-bubble.is-markdown .md-callout-important { border-left-color: var(--color-purple); }
.chat-bubble.is-markdown .md-callout-important .md-callout-icon .icon,
.chat-bubble.is-markdown .md-callout-important .md-callout-label { color: var(--color-purple); }
.chat-bubble.is-markdown .md-callout-caution  { border-left-color: var(--color-danger, #ff3b30); }
.chat-bubble.is-markdown .md-callout-caution  .md-callout-icon .icon,
.chat-bubble.is-markdown .md-callout-caution  .md-callout-label { color: var(--color-danger, #ff3b30); }

/* Tables */
.chat-bubble.is-markdown .md-table { border-collapse: collapse; width: 100%; margin: 0 0 .75em; font-size: .92em; }
.chat-bubble.is-markdown .md-table th,
.chat-bubble.is-markdown .md-table td { border: 1px solid var(--color-border); padding: .35em .55em; text-align: left; vertical-align: top; }
.chat-bubble.is-markdown .md-table thead th { background: var(--fill-subtle, rgba(127,127,127,.12)); font-weight: 700; }

/* Accordion */
.chat-bubble.is-markdown details { margin: 0 0 .75em; border: 1px solid var(--color-border); border-radius: var(--r-sm, 6px); padding: .1em .6em; }
.chat-bubble.is-markdown summary { cursor: pointer; font-weight: 600; padding: .35em 0; }
.chat-bubble.is-markdown .md-accordion-body { padding: .2em 0 .4em; }
.chat-bubble.is-markdown .md-accordion-body > :last-child { margin-bottom: 0; }

/* Horizontal rule + links */
.chat-bubble.is-markdown hr { border: 0; border-top: 1px solid var(--color-border); margin: .8em 0; }
.chat-bubble.is-markdown a { color: var(--color-link, var(--color-accent)); text-decoration: underline; }

/* ============================================================================
   THREAD SECTIONS (v1.11.8, CR-009) — grouped/sectioned conversation list.
   Only active when the app passes `groups` to OnyxThreads.mount().
   ========================================================================== */
.thread-section { margin-bottom: var(--sp-1); }
.thread-section-head {
    display: flex; align-items: center; gap: var(--sp-1); padding: var(--sp-1) var(--sp-2);
    cursor: pointer; user-select: none; border-radius: var(--r-sm, 6px);
    color: var(--color-text-muted, inherit); font-size: .82em; font-weight: 700;
    text-transform: uppercase; letter-spacing: .03em;
}
.thread-section-head:hover { background: var(--fill-subtle, rgba(127,127,127,.1)); }
.thread-section-head:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 1px; }
.thread-section-caret { font-size: 1.1em; transition: transform .15s ease; }
.thread-section-head.is-collapsed .thread-section-caret { transform: rotate(-90deg); }
.thread-section-label { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.thread-section-count {
    flex: 0 0 auto; font-size: .9em; font-weight: 600; opacity: .7;
    background: var(--fill-subtle, rgba(127,127,127,.14)); border-radius: var(--r-full); padding: 0 .45em; min-width: 1.5em; text-align: center;
}
/* CR-074: the per-project GEAR on a section header — sits at the far right (the label's flex-grow pushes it past
   the count), never shrinks, and shows the standard icon-button affordance shared with the ⋮ menus. */
.thread-section-actions { flex: 0 0 auto; }
.thread-section-body { padding-left: var(--sp-1); }
.thread-section-empty { padding: var(--sp-1) var(--sp-2) var(--sp-2); font-size: .82em; opacity: .55; font-style: italic; }
.dropdown-label { padding: var(--sp-1) var(--sp-3) var(--sp-1); font-size: .72em; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; opacity: .55; }

/* ============================================================================
   IN-CHAT ANNOTATION LAYER (v1.11.8, CR-005) — window.OnyxAnnotate.
   Colours are classes (hl-1..hl-6); no inline styles. Highlight spans wrap text.
   ========================================================================== */
.hub-hl{ border-radius: var(--r-2xs); padding: .02em 0; box-decoration-break: clone; -webkit-box-decoration-break: clone; }
.hub-hl-1{ background: rgba(255, 214,   0, .40); }  /* amber  */
.hub-hl-2{ background: rgba( 52, 199, 89, .34); }  /* green  */
.hub-hl-3{ background: rgba( 10, 132,255, .30); }  /* blue   */
.hub-hl-4{ background: rgba(255, 105,180, .32); }  /* pink   */
.hub-hl-5{ background: rgba(175, 82,222, .30); }  /* purple */
.hub-hl-6{ background: rgba(142, 142,147, .34); }  /* grey   */

.hub-annotate-toolbar{
    position: absolute; z-index: 40; display: inline-flex; gap: var(--sp-1); align-items: center;
    padding: var(--sp-1) var(--sp-2); background: var(--color-surface, #fff); border: 1px solid var(--color-border);
    border-radius: var(--r-md, 8px); box-shadow: var(--shadow-md, 0 4px 16px rgba(0,0,0,.18));
}
.hub-hl-swatch{ width: 20px; height: 20px; border-radius: 50%; border: 1px solid rgba(0,0,0,.15); cursor: pointer; padding: 0; }
.hub-hl-swatch.hub-hl-1{ background: #ffd600; } .hub-hl-swatch.hub-hl-2{ background: #34c759; }
.hub-hl-swatch.hub-hl-3{ background: #0a84ff; } .hub-hl-swatch.hub-hl-4{ background: #ff69b4; }
.hub-hl-swatch.hub-hl-5{ background: #af52de; } .hub-hl-swatch.hub-hl-6{ background: #8e8e93; }
.hub-annotate-action{
    font-size: .8em; padding: .15em .5em; border: 1px solid var(--color-border);
    background: var(--color-surface); border-radius: var(--r-xs); cursor: pointer; color: var(--color-text);
}
.hub-annotate-action:hover{ background: var(--fill-hover, var(--color-surface-alt)); }
/* CR-018 Phase B: colour swatches in the create control SELECT a colour (for Highlight + Clip). */
.hub-annotate-toolbar .hub-hl-swatch.is-selected,
.hub-annotate-tokenctrl .hub-hl-swatch.is-selected{ outline: 2px solid var(--color-accent); outline-offset: 1px; }
.hub-annotate-flash{ animation: hub-annotate-flash 1.2s ease; }
@keyframes hub-annotate-flash { 0%, 100% { background: transparent; } 25% { background: rgba(255, 214, 0, .45); } }

/* ============================================================================
   ANNOTATION GUTTER (v1.11.10, CR-005 increment 2) — landmark strip.
   The surface OnyxAnnotate mounts on must be position:relative for markers to
   align (they position within its scrollable content). Opt-in via opts.gutter.
   ========================================================================== */
.hub-annotate-gutter{ position: absolute; top: 0; right: 0; width: 14px; pointer-events: none; }
.hub-annotate-marker{
    position: absolute; right: 3px; width: 8px; height: 8px; padding: 0; border-radius: 50%;
    border: 1px solid rgba(0,0,0,.2); cursor: pointer; pointer-events: auto; background: var(--color-border);
}
.hub-annotate-marker:hover{ transform: scale(1.35); }
.hub-annotate-marker-note,
.hub-annotate-marker-clip{ border-radius: var(--r-2xs); }          /* notes / clips: square-ish (CR-018: a clip renders exactly as a note) */
.hub-annotate-marker-bookmark{ width: 6px; height: 12px; border-radius: var(--r-2xs); }  /* bookmarks: tall */
.hub-annotate-marker.hub-hl-1{ background: #ffd600; } .hub-annotate-marker.hub-hl-2{ background: #34c759; }
.hub-annotate-marker.hub-hl-3{ background: #0a84ff; } .hub-annotate-marker.hub-hl-4{ background: #ff69b4; }
.hub-annotate-marker.hub-hl-5{ background: #af52de; } .hub-annotate-marker.hub-hl-6{ background: #8e8e93; }
.hub-annotate-marker.is-uncertain{ opacity: .5; outline: 1px dashed var(--color-text-muted); }
.hub-annotate-marker.is-orphaned{ display: none; }

/* ============================================================================
   BOOKMARK INLINE PIN (v1.11.17, CR-005 increment 4) — a point anchor rendered
   in the text flow (a bookmark has no span to paint). Wraps no text; sized by em
   so it tracks the surrounding type. Coloured by the hl-* class like the markers.
   ========================================================================== */
.hub-annotate-pin{
    display: inline-block; width: .55em; height: .9em; margin: 0 .12em; vertical-align: -.12em;
    cursor: pointer; background: var(--color-text-muted, #8e8e93);
    clip-path: polygon(0 0, 100% 0, 100% 100%, 50% 78%, 0 100%);   /* bookmark ribbon */
    border-radius: var(--r-2xs);
}
.hub-annotate-pin:hover, .hub-annotate-pin:focus{ outline: 2px solid var(--color-accent); outline-offset: 1px; }
.hub-annotate-pin.hub-hl-1{ background: #ffd600; } .hub-annotate-pin.hub-hl-2{ background: #34c759; }
.hub-annotate-pin.hub-hl-3{ background: #0a84ff; } .hub-annotate-pin.hub-hl-4{ background: #ff69b4; }
.hub-annotate-pin.hub-hl-5{ background: #af52de; } .hub-annotate-pin.hub-hl-6{ background: #8e8e93; }

/* ============================================================================
   ANNOTATION CARD (v1.11.11, CR-005 increment 3) — inline CRUD card.
   ========================================================================== */
.hub-annotate-card{
    position: absolute; z-index: 45; right: 20px; min-width: 220px; max-width: 300px;
    padding: var(--sp-2); background: var(--color-surface, #fff); border: 1px solid var(--color-border);
    border-radius: var(--r-md, 8px); box-shadow: var(--shadow-md, 0 4px 16px rgba(0,0,0,.18));
    display: flex; flex-direction: column; gap: var(--sp-2);
}
.hub-annotate-comment{
    width: 100%; min-height: 48px; resize: vertical; font: inherit; font-size: .9em;
    padding: .4em; border: 1px solid var(--color-border); border-radius: var(--r-sm, 6px);
    background: var(--color-bg, #fff); color: var(--color-text);
}
.hub-annotate-card-colours{ display: flex; gap: var(--sp-1); }
.hub-annotate-card-colours .hub-hl-swatch.is-selected{ outline: 2px solid var(--color-accent); outline-offset: 1px; }
.hub-annotate-card-tags{ display: flex; flex-wrap: wrap; gap: var(--sp-1); align-items: center; }
.hub-annotate-tag{
    font-size: .78em; padding: .1em .5em; border: 1px solid var(--color-border);
    background: var(--color-surface); border-radius: var(--r-full); cursor: pointer; color: var(--color-text-muted, inherit);
}
.hub-annotate-tag.is-on{ background: var(--color-accent); color: var(--color-primary-contrast, #fff); border-color: var(--color-accent); }
.hub-annotate-tag-add{ font-size: .78em; padding: .1em .4em; border: 1px dashed var(--color-border); border-radius: var(--r-full); background: transparent; width: 70px; color: var(--color-text); }
.hub-annotate-card-actions{ display: flex; flex-wrap: wrap; gap: var(--sp-1); }
.hub-annotate-card-btn{
    font-size: .8em; padding: .2em .6em; border: 1px solid var(--color-border);
    background: var(--color-surface); border-radius: var(--r-xs); cursor: pointer; color: var(--color-text);
}
.hub-annotate-card-btn:hover{ background: var(--fill-hover, var(--color-surface-alt)); }
.hub-annotate-card-btn.danger{ color: #dc2626; border-color: #dc2626; }

/* ============================================================================
   STREAMING CARET (v1.11.12, CR-012) — token-by-token assistant message.
   ========================================================================== */
.hub-stream-caret{
    display: inline-block; width: 2px; height: 1em; margin-left: 1px; vertical-align: text-bottom;
    background: var(--color-accent, currentColor); animation: hub-stream-blink 1s steps(2, start) infinite;
}
@keyframes hub-stream-blink { 0%, 100% { opacity: 1; } 50% { opacity: 0; } }


/* ===== CR-032: OnyxChat turn-navigation rail (opt-in mount({turnRail:true})) ===== */
/* The chat root is the positioning context (like .chat-jump). The rail sits scroll-adjacent on the right;
   one thin marker per user turn; a preview popover shows the prompt on hover/focus. */
.chat-turn-rail {
    /* CR-056: the marker cluster is vertically CENTERED in the chat window. Symmetric top/bottom insets keep
       the band centred on the panel's vertical midline (was top:12%/bottom:25% — asymmetric); the bottom inset
       clears the composer, the top inset clears the log's top fade; `justify-content:center` centres the
       markers within that band so the cluster sits at the true middle instead of piling at the top edge. */
    position: absolute; top: 12%; right: 6px; bottom: 12%; z-index: 3;
    display: flex; flex-direction: column; justify-content: center; gap: 2px;
    padding: 2px; overflow: hidden; pointer-events: none;
}
.chat-turn-marker {
    pointer-events: auto; flex: 0 0 auto; width: 14px; height: 5px; padding: 0;
    border: 0; border-radius: var(--r-2xs); cursor: pointer;
    background: var(--color-border, #ccc); opacity: .6; transition: opacity .12s, width .12s;
}
.chat-turn-marker:hover, .chat-turn-marker:focus-visible { opacity: 1; width: 20px; background: var(--color-accent, #254c77); }
.chat-turn-marker:focus-visible { outline: 2px solid var(--color-focus, var(--color-accent, #254c77)); outline-offset: 2px; }
/* CR-043: annotation sub-markers share the ONE rail. Same line thickness (height) as a turn tick, but
   SHORTER and right-anchored so they read as indented sub-sections UNDER their turn, in page order; each
   adopts its annotation's colour (bookmarks are colour-agnostic → neutral). They are jump links only. */
.chat-turn-marker--anno { width: 9px; align-self: flex-end; opacity: .55; }
.chat-turn-marker--anno:hover, .chat-turn-marker--anno:focus-visible { opacity: 1; width: 12px; }
.chat-turn-marker--anno.hub-hl-1 { background: #ffd600; } .chat-turn-marker--anno.hub-hl-2 { background: #34c759; }
.chat-turn-marker--anno.hub-hl-3 { background: #0a84ff; } .chat-turn-marker--anno.hub-hl-4 { background: #ff69b4; }
.chat-turn-marker--anno.hub-hl-5 { background: #af52de; } .chat-turn-marker--anno.hub-hl-6 { background: #8e8e93; }
/* CR-043 P2: clips echo the gutter's square-ish marker; a bookmark (colour-agnostic) is a HOLLOW ring so it
   reads distinctly from a filled highlight while keeping the same line footprint. */
.chat-turn-marker--clip { border-radius: var(--r-2xs); }
.chat-turn-marker--bookmark { background: transparent; box-shadow: inset 0 0 0 1.5px var(--color-text-muted, #8e8e93); }
.chat-turn-preview {
    position: absolute; right: 28px; z-index: 4; max-width: 18rem;
    padding: .4em .6em; border-radius: var(--r-xs); font-size: .82em; line-height: 1.35;
    background: var(--color-bg, #fff); color: var(--color-text, #1a1a1a);
    border: 1px solid var(--color-border, #ddd); box-shadow: var(--shadow-lg, 0 8px 24px rgba(0,0,0,.18));
    pointer-events: none;
}
.chat-msg.is-flash { animation: hub-turn-flash 900ms ease-out; }
@keyframes hub-turn-flash { 0% { background: var(--color-accent-weak, rgba(37,76,119,.14)); } 100% { background: transparent; } }
@media (prefers-reduced-motion: reduce) { .chat-turn-marker { transition: none; } .chat-msg.is-flash { animation: none; } }

/* ===== CR-033: OnyxChat whole-turn action affordance (opt-in BY REGISTRATION) ===== */
/* A per-message hover indicator (top corner, away from the avatar) opens a menu of the
   whole-turn actions apps registered via OnyxChat.registerTurnAction(). The chat root is the
   positioning context; the menu is appended to root and placed inline (like .chat-jump / the rail). */
.chat-msg { position: relative; }
.chat-turn-actions {
    position: absolute; top: 0; right: 6px; z-index: 3;
    display: inline-flex; align-items: center; justify-content: center;
    width: 22px; height: 22px; padding: 0; border: 0; border-radius: var(--r-full, 999px);
    background: var(--color-surface-alt, #eef1f5); color: var(--color-text-muted, #667);
    cursor: pointer; opacity: 0; transition: opacity .12s var(--ease, ease), background .12s var(--ease, ease);
}
.chat-msg-user .chat-turn-actions { right: auto; left: 6px; }   /* user bubbles are right-aligned */
.chat-msg:hover .chat-turn-actions,
.chat-msg:focus-within .chat-turn-actions,
.chat-turn-actions:focus-visible { opacity: 1; }
.chat-turn-actions:hover { background: var(--fill-hover, var(--color-surface-alt, #e3e8ef)); color: var(--color-text, #1a1a1a); }
.chat-turn-actions:focus-visible { outline: 2px solid var(--color-focus, var(--color-accent, #254c77)); outline-offset: 2px; }
.chat-turn-actions .icon { font-size: 18px; line-height: 1; }
.chat-turn-menu {
    position: absolute; z-index: 6; min-width: 12rem; max-width: 20rem;
    padding: 4px; border-radius: var(--r-md, 8px);
    background: var(--color-bg, #fff); color: var(--color-text, #1a1a1a);
    border: 1px solid var(--color-border, #ddd); box-shadow: var(--shadow-lg, 0 8px 24px rgba(0,0,0,.18));
}
.chat-turn-menu-item {
    display: flex; align-items: center; gap: .5em; width: 100%;
    padding: .5em .6em; border: 0; border-radius: var(--r-sm, 6px);
    background: transparent; color: inherit; font: inherit; text-align: left; cursor: pointer;
}
.chat-turn-menu-item:hover, .chat-turn-menu-item:focus-visible { background: var(--fill-hover, var(--color-surface-alt, #eef1f5)); }
.chat-turn-menu-item:focus-visible { outline: 2px solid var(--color-focus, var(--color-accent, #254c77)); outline-offset: -2px; }
.chat-turn-menu-icon { flex: 0 0 auto; font-size: 18px; line-height: 1; opacity: .85; }
.chat-turn-menu-label { flex: 1 1 auto; min-width: 0; }
@media (prefers-reduced-motion: reduce) { .chat-turn-actions { transition: none; } }

/* --- CR-047: opt-in under-turn action bar -------------------------------------------------------
   Renders UNDER each user/agent turn (a wrapped, full-width row of the flex .chat-msg). Role-mirrored
   per the owner's spec: the controls align to the turn's PROFILE-IMAGE side and the timestamp (P2) sits
   at the furthest/least-important end. For the agent (avatar left) the cluster is left; for the user
   (avatar right, .chat-msg is row-reverse) the cluster is right. Reveal on hover / focus-within
   (industry standard), always visible on touch. */
/* BUG-004 (v1.51.1): the under-turn bar needs a full-width row of its own. The old `flex-wrap:wrap` did
   that but let flexbox place the (wide, max-content) bubble on the NEXT line below the avatar — so a long
   AGENT reply rendered with its avatar sitting ON TOP of the bubble (short user prompts still fit beside
   their avatar, hence only the agent side broke). Use a 2-row grid instead: avatar + bubble share row 1
   (avatar never wraps off), the bar spans row 2. Content-hugging bubbles and the user/agent mirror are
   preserved; only `.has-turn-bar` messages are grid (plain messages stay flex). */
.chat-msg.has-turn-bar {
    display: grid; grid-template-columns: auto minmax(0, 1fr);
    align-items: start; column-gap: var(--sp-2);
}
.chat-msg.has-turn-bar > .chat-avatar   { grid-column: 1; grid-row: 1; }
.chat-msg.has-turn-bar > .chat-bubble   { grid-column: 2; grid-row: 1; justify-self: start; }
.chat-msg.has-turn-bar > .chat-turn-bar { grid-column: 1 / -1; grid-row: 2; }
/* user turns mirror: avatar on the right, bubble hugs the right edge (matches the flex row-reverse look) */
.chat-msg-user.has-turn-bar { grid-template-columns: minmax(0, 1fr) auto; }
.chat-msg-user.has-turn-bar > .chat-avatar { grid-column: 2; grid-row: 1; }
.chat-msg-user.has-turn-bar > .chat-bubble { grid-column: 1; grid-row: 1; justify-self: end; }
/* CR-057: avatars off — no avatar element renders, so collapse the grid to a single content column
   (otherwise the empty avatar track + column-gap would indent the bubble). Plain (non-turn-bar) messages
   need no rule: with only the bubble present, the flex row's gap has no sibling to act on. */
.chat--no-avatars .chat-msg.has-turn-bar,
.chat--no-avatars .chat-msg-user.has-turn-bar { grid-template-columns: minmax(0, 1fr); column-gap: 0; }
.chat--no-avatars .chat-msg.has-turn-bar > .chat-bubble { grid-column: 1; }
.chat-turn-bar {
    flex: 0 0 100%; width: 100%;
    display: flex; align-items: center; justify-content: space-between; gap: var(--sp-2);
    margin-top: 3px; min-height: 22px;
    opacity: 0; transition: opacity .12s var(--ease, ease);
}
.chat-msg-user .chat-turn-bar { flex-direction: row-reverse; }   /* mirror: cluster on the user's (right) avatar side */
.chat-msg:hover .chat-turn-bar,
.chat-msg:focus-within .chat-turn-bar { opacity: 1; }
@media (hover: none) { .chat-turn-bar { opacity: 1; } }           /* touch: no hover to reveal on */
@media (prefers-reduced-motion: reduce) { .chat-turn-bar { transition: none; } }

/* CR-051: uniform icon boxes — a 16px glyph centred in a 24px box that is transparent until
   hover (a light-grey chip), each box with a 1px margin. Margin owns the spacing (gap: 0). */
.chat-turn-bar-actions { display: inline-flex; align-items: center; gap: 0; }
.chat-turn-bar-btn {
    flex: 0 0 auto; width: 24px; height: 24px; margin: 1px; padding: 0; border: 0; border-radius: var(--r-xs, 6px);
    background: transparent; color: var(--color-text-muted, #667);
    display: inline-flex; align-items: center; justify-content: center;
    cursor: pointer; transition: background .12s var(--ease, ease), color .12s var(--ease, ease);
}
.chat-turn-bar-btn:hover { background: var(--fill-hover, var(--color-surface-alt, #e3e8ef)); color: var(--color-text, #1a1a1a); }
.chat-turn-bar-btn:focus-visible { outline: 2px solid var(--color-focus, var(--color-accent, #254c77)); outline-offset: 1px; }
.chat-turn-bar-btn.is-copied { color: var(--color-success-text, #3f6b1f); }
/* All turn-bar glyphs render at 16px — font glyphs (.icon, sized by font-size) and any inline SVG alike. */
.chat-turn-bar-icon { font-size: 16px; line-height: 1; }
.chat-turn-bar-icon.icon { font-size: 16px; }
.chat-turn-bar-icon svg { width: 16px; height: 16px; display: block; }
.chat-turn-bar-fallback { font-size: var(--fs-caption, .78rem); line-height: 1; padding: 0 4px; white-space: nowrap; }
/* CR-047 P2: the timestamp — furthest / least-important element (bar's far end via space-between). */
.chat-turn-bar-time { flex: 0 0 auto; font-size: var(--fs-caption, .78rem); line-height: 1; color: var(--color-text-muted, #667); white-space: nowrap; }

/* ---- Profile Settings (CR-050) --------------------------------------------
   Per-user settings page, reached from the nav profile image. Tokens only. */

/* The nav profile block is now a link (entry point to /me/settings). */
.side-foot .side-user { text-decoration: none; color: inherit; border-radius: var(--r-md);
    transition: background var(--dur) var(--ease); }
.side-foot .side-user:hover { background: rgba(255, 255, 255, .06); }
.side-foot .side-user:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }

.profile { display: flex; flex-direction: column; gap: var(--sp-6); max-width: 720px; }
.profile-head { display: flex; flex-direction: column; gap: var(--sp-1); }
.profile-sub { margin: 0; color: var(--slate); font-size: var(--fs-sm); }
.profile-card { display: flex; flex-direction: column; gap: var(--sp-4); }

/* Account (read-only identity from Microsoft) */
.profile-identity { display: grid; grid-template-columns: minmax(96px, max-content) 1fr;
    gap: var(--sp-2) var(--sp-5); margin: 0; }
.profile-identity > div { display: contents; }
.profile-identity dt { color: var(--slate); font-size: var(--fs-sm); }
.profile-identity dd { margin: 0; color: var(--ink); }
.profile-note { margin: 0; color: var(--slate); font-size: var(--fs-caption); }

/* Profile photo */
.profile-avatar-row { display: flex; align-items: flex-start; gap: var(--sp-5); }
.profile-avatar-row .avatar { width: 72px; height: 72px; font-size: var(--fs-xl); }
.profile-avatar-copy { display: flex; flex-direction: column; gap: var(--sp-2); min-width: 0; }
.profile-avatar-copy > p { margin: 0; }
.profile-avatar-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-3);
    margin-top: var(--sp-1); }
.profile-upload-form { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-3); }
.profile-file-input { max-width: 240px; }

@media (max-width: 620px) {
    .profile-avatar-row { flex-direction: column; }
}
