:root {
    --os-accent: #0078d4;
    --os-accent-soft: rgba(0, 120, 212, 0.18);
    --os-bg-grad-1: #0a3a6b;
    --os-bg-grad-2: #1d6fb8;
    --os-bg-grad-3: #6ab1e8;
    --os-fg: #f3f3f3;
    --os-fg-muted: rgba(243, 243, 243, 0.72);
    --os-surface: rgba(32, 32, 32, 0.72);
    --os-surface-border: rgba(255, 255, 255, 0.10);
    --os-radius: 8px;
    --os-radius-sm: 4px;
    --os-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
    --os-tb-height: 48px;
    --os-font: "Segoe UI Variable", "Segoe UI", Inter, system-ui, -apple-system, "Helvetica Neue", Arial, sans-serif;
}

[data-theme="light"] {
    --os-bg-grad-1: #cfe5fb;
    --os-bg-grad-2: #f3f8ff;
    --os-bg-grad-3: #ffffff;
    --os-fg: #1b1b1b;
    --os-fg-muted: rgba(27, 27, 27, 0.65);
    --os-surface: rgba(255, 255, 255, 0.78);
    --os-surface-border: rgba(0, 0, 0, 0.08);
    --os-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
}

* { box-sizing: border-box; }

html, body {
    margin: 0;
    padding: 0;
    height: 100%;
    overflow: hidden;
    font-family: var(--os-font);
    color: var(--os-fg);
    -webkit-font-smoothing: antialiased;
}

body.os-shell {
    background:
        radial-gradient(1200px 800px at 20% 20%, var(--os-bg-grad-3), transparent 60%),
        radial-gradient(1000px 700px at 80% 70%, var(--os-bg-grad-2), transparent 65%),
        linear-gradient(135deg, var(--os-bg-grad-1), var(--os-bg-grad-2));
    background-size: cover;
    background-attachment: fixed;
}

.os-desktop {
    position: fixed;
    inset: 0 0 var(--os-tb-height) 0;
    display: block;
}

.os-taskbar {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    height: var(--os-tb-height);
    display: flex;
    justify-content: center;
    align-items: stretch;
    padding: 0;
    z-index: 10;
}

.os-taskbar-inner {
    width: 100%;
    height: 100%;
    display: grid;
    /* Four columns: left spacer | Start | Pins | tray-side spacer. Tray is
       absolutely-positioned on the right (see below) so the centre cluster
       stays balanced regardless of the tray's width. */
    grid-template-columns: 1fr auto auto 1fr;
    grid-template-rows: 1fr;
    align-items: center;
    padding: 0 12px;
    background: var(--os-surface);
    border-top: 1px solid var(--os-surface-border);
    backdrop-filter: blur(28px) saturate(160%);
    -webkit-backdrop-filter: blur(28px) saturate(160%);
    position: relative;
    overflow: hidden;
}

.os-tb-pins {
    display: flex;
    align-items: center;
    gap: 4px;
    grid-column: 3;
    grid-row: 1;
}

.os-tb-tray {
    /* Pin to the right edge — no grid cell needed; keeps the centre cluster centred. */
    position: absolute;
    right: 12px;
    top: 0;
    bottom: 0;
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 12px;
    color: var(--os-fg);
    font-size: 12px;
    line-height: 1.1;
}

.os-tb-btn {
    appearance: none;
    background: transparent;
    border: 0;
    width: 40px;
    height: 40px;
    border-radius: var(--os-radius-sm);
    color: var(--os-fg);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transition: background 120ms ease;
}

.os-tb-btn:hover { background: rgba(255, 255, 255, 0.08); }
.os-tb-btn:focus-visible { outline: 2px solid var(--os-accent); outline-offset: 2px; }

.os-start-btn {
    grid-column: 2; grid-row: 1;
    width: calc(var(--os-tb-height) - 4px);   /* 44 px in the 48 px bar */
    height: calc(var(--os-tb-height) - 4px);
    padding: 0;
}
.os-start-btn .os-start-glyph {
    width: calc(var(--os-tb-height) - 8px);   /* 40 px glyph — almost edge-to-edge */
    height: calc(var(--os-tb-height) - 8px);
    display: block;
}
/* Polish: brighten slightly on hover, press on click. */
.os-start-btn:hover .os-start-glyph { filter: brightness(1.08); }
.os-start-btn:active .os-start-glyph { filter: brightness(0.92); transform: translateY(0.5px); }

.os-tray-clock {
    text-align: right;
    font-variant-numeric: tabular-nums;
    color: var(--os-fg);
    user-select: none;
}

.os-tray-clock .os-clock-time { display: block; font-size: 12px; }
.os-tray-clock .os-clock-date { display: block; font-size: 11px; color: var(--os-fg-muted); }

.os-tray-btn,
.os-tray-clock-btn {
    appearance: none;
    background: transparent;
    border: 0;
    height: 36px;
    padding: 0 8px;
    border-radius: var(--os-radius-sm);
    color: var(--os-fg);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: background 100ms ease;
    position: relative;
}
.os-tray-btn:hover,
.os-tray-clock-btn:hover { background: rgba(255, 255, 255, 0.08); }
.os-tray-btn:focus-visible,
.os-tray-clock-btn:focus-visible { outline: 2px solid var(--os-accent); outline-offset: 1px; }

.os-temp-pill {
    font-size: 11px;
    padding: 2px 8px;
    border-radius: 10px;
    background: rgba(255,255,255,0.08);
    border: 1px solid var(--os-surface-border);
    font-variant-numeric: tabular-nums;
}
.os-tray-temp[data-status="warning"] .os-temp-pill { background: rgba(255, 170, 50, 0.18); border-color: rgba(255, 170, 50, 0.5); }
.os-tray-temp[data-status="critical"] .os-temp-pill { background: rgba(220, 60, 60, 0.22); border-color: rgba(220, 60, 60, 0.6); }

/* DataForge credit. Same pill vocabulary as the temperature so the tray reads as
   one row; only the status colours differ in meaning. */
.os-tokens-pill {
    font-size: 11px;
    padding: 2px 8px;
    border-radius: 10px;
    background: rgba(255,255,255,0.08);
    border: 1px solid var(--os-surface-border);
    font-variant-numeric: tabular-nums;
}
.os-tray-tokens[data-status="low"]   .os-tokens-pill { background: rgba(255, 170, 50, 0.18); border-color: rgba(255, 170, 50, 0.5); }
.os-tray-tokens[data-status="empty"] .os-tokens-pill { background: rgba(220, 60, 60, 0.22); border-color: rgba(220, 60, 60, 0.6); }
.os-tray-tokens[data-status="error"] .os-tokens-pill { opacity: 0.55; }

/* Update-available marker. A dot, not a count: there is only ever one
   pending version, so a number would say nothing a dot does not. The button is
   hidden entirely unless an update is waiting, so this is only ever visible
   when it means something. */
.os-tray-update { position: relative; }
.os-tray-dot {
    position: absolute;
    top: 5px;
    right: 5px;
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--os-accent);
    box-shadow: 0 0 0 2px var(--os-tb-bg, rgba(0, 0, 0, 0.35));
}

.os-tray-badge {
    position: absolute;
    top: 4px;
    right: 4px;
    background: var(--os-accent);
    color: #fff;
    font-size: 10px;
    line-height: 1;
    padding: 2px 4px;
    border-radius: 8px;
    min-width: 14px;
    text-align: center;
    font-weight: 600;
}

/* ── Flyouts (Quick Settings, Action Center, Calendar) ───────────── */
.os-flyout {
    position: fixed;
    right: 8px;
    bottom: calc(var(--os-tb-height) + 8px);
    width: 360px;
    max-height: calc(100vh - var(--os-tb-height) - 24px);
    background: var(--os-surface);
    backdrop-filter: blur(28px) saturate(160%);
    -webkit-backdrop-filter: blur(28px) saturate(160%);
    border: 1px solid var(--os-surface-border);
    border-radius: 10px;
    box-shadow: var(--os-shadow);
    color: var(--os-fg);
    z-index: 22;
    display: flex;
    flex-direction: column;
    padding: 12px 14px;
    gap: 10px;
    overflow: hidden;
}
.os-flyout[hidden] { display: none; }
.os-flyout-head {
    font-size: 13px;
    font-weight: 600;
    color: var(--os-fg-muted);
    padding-bottom: 4px;
    border-bottom: 1px solid var(--os-surface-border);
}

/* Quick Settings */
.os-qs-row { display: flex; gap: 8px; flex-wrap: wrap; }
.os-qs-toggle {
    appearance: none;
    background: rgba(255,255,255,0.06);
    border: 1px solid var(--os-surface-border);
    border-radius: 8px;
    color: var(--os-fg);
    padding: 10px 12px;
    cursor: pointer;
    display: inline-flex; align-items: center; gap: 8px;
    font: inherit; font-size: 13px;
}
.os-qs-toggle:hover { background: rgba(255,255,255,0.12); }
.os-qs-toggle.active { background: var(--os-accent-soft); border-color: var(--os-accent); color: var(--os-fg); }
.os-qs-section { display: flex; flex-direction: column; gap: 6px; }
.os-qs-section-head { font-size: 12px; color: var(--os-fg-muted); }
.os-qs-accents { display: flex; gap: 8px; }
.os-qs-accent {
    width: 28px; height: 28px;
    border-radius: 50%;
    border: 2px solid transparent;
    cursor: pointer;
    padding: 0;
    background: transparent;
}
.os-qs-accent.selected { border-color: #fff; box-shadow: 0 0 0 2px var(--os-accent); }
.os-qs-wallpapers {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(64px, 1fr));
    gap: 8px;
    max-height: 200px;
    overflow-y: auto;
}
.os-qs-wallpaper {
    aspect-ratio: 16 / 10;
    border-radius: 6px;
    border: 2px solid transparent;
    cursor: pointer;
    padding: 0;
    background: rgba(255,255,255,0.04) center/cover no-repeat;
    transition: border-color 120ms ease, background-color 120ms ease;
    position: relative;
}
.os-qs-wallpaper:hover { border-color: rgba(255,255,255,0.3); }
.os-qs-wallpaper.selected { border-color: #fff; box-shadow: 0 0 0 2px var(--os-accent); }
.os-qs-wallpaper-none {
    background: rgba(255,255,255,0.04);
    display: flex;
    align-items: center;
    justify-content: center;
    color: rgba(255,255,255,0.6);
    font-size: 18px;
    line-height: 1;
    font-weight: 300;
}
.os-qs-wallpaper-none::before {
    content: '\00d7';   /* × — clearly "no background" */
}
.os-qs-wallpaper-none:hover { color: #fff; background: rgba(255,255,255,0.08); }
.os-qs-wallpapers-empty {
    grid-column: 1 / -1;
    margin: 0;
    font-size: 12px;
    color: var(--os-fg-muted);
}
.os-qs-wallpapers-empty code {
    font-size: 11px;
    background: rgba(255,255,255,0.08);
    padding: 1px 4px;
    border-radius: 3px;
}
.os-qs-actions { display: flex; gap: 8px; padding-top: 4px; border-top: 1px solid var(--os-surface-border); }
.os-qs-actions button {
    appearance: none;
    flex: 1;
    background: rgba(255,255,255,0.06);
    border: 1px solid var(--os-surface-border);
    color: var(--os-fg);
    border-radius: 6px;
    padding: 8px;
    cursor: pointer;
    font: inherit; font-size: 12px;
}
.os-qs-actions button:hover { background: rgba(255,255,255,0.14); }

/* Action Center */
.os-ac-list { display: flex; flex-direction: column; gap: 6px; overflow-y: auto; flex: 1; }
.os-ac-empty { color: var(--os-fg-muted); font-size: 12px; margin: 12px 0; text-align: center; }
.os-ac-item {
    background: rgba(255,255,255,0.06);
    border: 1px solid var(--os-surface-border);
    border-radius: 8px;
    padding: 10px 12px;
    display: flex;
    flex-direction: column;
    gap: 4px;
}
.os-ac-item.severity-critical { border-color: rgba(220, 60, 60, 0.55); background: rgba(220, 60, 60, 0.16); }
.os-ac-item.severity-warning  { border-color: rgba(255, 170, 50, 0.55); background: rgba(255, 170, 50, 0.14); }
.os-ac-item-meta { display: flex; justify-content: space-between; font-size: 11px; color: var(--os-fg-muted); }
.os-ac-item-title { font-size: 13px; font-weight: 600; }
.os-ac-item-body { font-size: 12px; color: var(--os-fg-muted); }

/* Calendar */
.os-calendar { width: 320px; }
.os-cal-now { font-size: 28px; font-weight: 300; letter-spacing: -0.02em; }
.os-cal-month-head {
    display: flex;
    justify-content: space-between;
    align-items: center;
    font-size: 13px;
    font-weight: 600;
}
.os-cal-month-head button {
    appearance: none; background: transparent; border: 0; color: var(--os-fg);
    width: 28px; height: 28px; border-radius: 6px; cursor: pointer; font-size: 16px;
}
.os-cal-month-head button:hover { background: rgba(255,255,255,0.08); }
.os-cal-grid {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: 2px;
    font-size: 12px;
    text-align: center;
}
.os-cal-dow { color: var(--os-fg-muted); font-weight: 600; padding: 4px 0; }
.os-cal-day { padding: 6px 0; border-radius: 6px; cursor: default; }
.os-cal-day.muted { color: var(--os-fg-muted); opacity: 0.4; }
.os-cal-day.today { background: var(--os-accent); color: #fff; font-weight: 600; }

/* Analog clock inside the calendar flyout */
.os-clock-mini {
    --osc-face: #161b22;
    --osc-edge: #2a313a;
    --osc-tick: #4a525c;
    --osc-tick-major: #c9d1d9;
    --osc-hand: #e6edf3;
    --osc-second: #f97583;
    --osc-pivot: #f97583;
    --osc-text: #8b949e;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    padding: 4px 0 6px;
    border-bottom: 1px solid var(--os-surface-border);
}
.os-clock-mini.light {
    --osc-face: #ffffff;
    --osc-edge: #d0d7de;
    --osc-tick: #8b949e;
    --osc-tick-major: #1f2328;
    --osc-hand: #1f2328;
    --osc-text: #57606a;
}
.os-clock-mini-face {
    width: 168px;
    height: 168px;
    filter: drop-shadow(0 6px 18px rgba(0,0,0,0.35));
}
.os-clock-mini-face svg { width: 100%; height: 100%; display: block; }
.os-clock-mini-digital {
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum";
    font-size: 22px;
    font-weight: 600;
    letter-spacing: 1px;
    /* Digits sit on the flyout PANEL (OS-themed), not on the clock face, so
       colour them with the OS foreground var: this makes them follow the OS
       light/dark toggle like every other element, instead of the clock-face
       var (--osc-hand) which only tracks the clock's own Light/Dark button. */
    color: var(--os-fg);
    font-family: "SF Mono", "Menlo", "Consolas", ui-monospace, monospace;
    line-height: 1;
}
.os-clock-mini-digital .os-clock-mini-ampm {
    font-size: 12px;
    font-weight: 500;
    color: var(--os-fg-muted);
    margin-left: 4px;
}
.os-clock-mini-circle { fill: var(--osc-face); stroke: var(--osc-edge); stroke-width: 2; }
.os-clock-mini-inner  { fill: none; stroke: var(--osc-edge); stroke-width: 1; opacity: 0.5; }
.os-clock-mini-tick       { stroke: var(--osc-tick); stroke-width: 1.5; stroke-linecap: round; }
.os-clock-mini-tick-major { stroke: var(--osc-tick-major); stroke-width: 3; stroke-linecap: round; }
.os-clock-mini-numeral {
    fill: var(--osc-tick-major);
    font-size: 12px;
    font-weight: 500;
    text-anchor: middle;
    dominant-baseline: central;
    font-family: "Helvetica Neue", Arial, sans-serif;
}
.os-clock-mini.roman .os-clock-mini-numeral {
    font-family: "Times New Roman", serif;
    font-style: italic;
    font-weight: 400;
}
.os-clock-mini-hand   { stroke-linecap: round; }
.os-clock-mini-hour   { stroke: var(--osc-hand); stroke-width: 5; }
.os-clock-mini-minute { stroke: var(--osc-hand); stroke-width: 3; }
.os-clock-mini-second { stroke: var(--osc-second); stroke-width: 1.5; }
.os-clock-mini-pivot-outer { fill: var(--osc-hand); }
.os-clock-mini-pivot-inner { fill: var(--osc-pivot); }
.os-clock-mini-controls { display: flex; gap: 6px; }
.os-clock-mini-btn {
    appearance: none;
    background: rgba(255,255,255,0.04);
    border: 1px solid var(--os-surface-border);
    color: var(--os-fg-muted);
    padding: 5px 12px;
    border-radius: 999px;
    cursor: pointer;
    font: inherit;
    font-size: 12px;
    transition: background-color 120ms ease, color 120ms ease, border-color 120ms ease;
}
.os-clock-mini-btn:hover {
    background: rgba(255,255,255,0.1);
    color: var(--os-fg);
    border-color: rgba(255,255,255,0.2);
}
.os-clock-mini-btn.active {
    background: var(--os-accent);
    border-color: var(--os-accent);
    color: #fff;
}

/* ── Pinned taskbar apps ─────────────────────────────────────────── */
.os-tb-pin {
    appearance: none;
    background: transparent;
    border: 0;
    min-width: 40px;
    height: 40px;
    padding: 0 6px;
    border-radius: var(--os-radius-sm);
    color: var(--os-fg);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    cursor: pointer;
    position: relative;
    transition: background 120ms ease, max-width 180ms ease, padding 180ms ease;
}
.os-tb-pin:hover { background: rgba(255, 255, 255, 0.08); }
.os-tb-pin:focus-visible { outline: 2px solid var(--os-accent); outline-offset: 2px; }
.os-tb-pin img,
.os-tb-pin svg { width: 22px; height: 22px; display: block; flex-shrink: 0; }
.os-tb-pin .os-pin-fallback {
    width: 22px; height: 22px;
    border-radius: 5px;
    background: linear-gradient(135deg, #4a8be6, #2563c4);
    color: #fff;
    display: inline-flex; align-items: center; justify-content: center;
    font-size: 11px; font-weight: 700; letter-spacing: 0.02em;
    flex-shrink: 0;
}
.os-tb-pin-label {
    font-size: 12px;
    line-height: 1.1;
    color: var(--os-fg);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 0;
    opacity: 0;
    transition: max-width 180ms ease, opacity 140ms ease, margin 180ms ease;
    margin-right: -6px; /* collapses gap when hidden */
    pointer-events: none;
}
/* Show the label only when the pin's app is running (pinned-and-running, or
   transient pins which start with `running` always applied). */
.os-tb-pin.running .os-tb-pin-label {
    max-width: 180px;
    opacity: 1;
    margin-right: 0;
    padding-right: 2px;
}

/* ── Start menu ──────────────────────────────────────────────────── */
.os-startmenu {
    position: fixed;
    left: 50%;
    bottom: calc(var(--os-tb-height) + 10px);
    transform: translateX(-50%) translateY(8px);
    width: min(640px, calc(100vw - 32px));
    height: min(680px, calc(100vh - var(--os-tb-height) - 32px));
    background: var(--os-surface);
    backdrop-filter: blur(28px) saturate(160%);
    -webkit-backdrop-filter: blur(28px) saturate(160%);
    border: 1px solid var(--os-surface-border);
    border-radius: var(--os-radius);
    box-shadow: var(--os-shadow);
    color: var(--os-fg);
    z-index: 20;
    display: flex;
    flex-direction: column;
    padding: 18px 20px 12px;
    opacity: 0;
    pointer-events: none;
    transition: opacity 140ms ease, transform 140ms ease;
}
.os-startmenu.open {
    opacity: 1;
    pointer-events: auto;
    transform: translateX(-50%) translateY(0);
}
.os-startmenu[hidden] { display: none; }

.os-sm-search {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 12px;
    margin-bottom: 16px;
    background: rgba(255, 255, 255, 0.06);
    border: 1px solid var(--os-surface-border);
    border-radius: 18px;
    color: var(--os-fg-muted);
}
.os-sm-search input {
    flex: 1;
    background: transparent;
    border: 0;
    outline: 0;
    color: var(--os-fg);
    font: inherit;
    font-size: 13px;
}
.os-sm-search input::placeholder { color: var(--os-fg-muted); }

.os-sm-section { margin-bottom: 14px; }
.os-sm-section:last-of-type { flex: 1; min-height: 0; display: flex; flex-direction: column; margin-bottom: 8px; }
.os-sm-section-head {
    font-size: 12px;
    font-weight: 600;
    color: var(--os-fg-muted);
    margin-bottom: 8px;
    padding-left: 4px;
}

.os-sm-pinned {
    display: grid;
    grid-template-columns: repeat(6, 1fr);
    gap: 6px;
}

.os-app-tile {
    appearance: none;
    background: transparent;
    border: 1px solid transparent;
    border-radius: var(--os-radius-sm);
    color: var(--os-fg);
    padding: 12px 6px 10px;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    cursor: pointer;
    text-align: center;
    transition: background 120ms ease, border-color 120ms ease;
}
.os-app-tile:hover { background: rgba(255, 255, 255, 0.08); border-color: var(--os-surface-border); }
.os-app-tile:focus-visible { outline: 2px solid var(--os-accent); outline-offset: 2px; }
.os-app-tile-icon {
    width: 36px; height: 36px;
    display: inline-flex; align-items: center; justify-content: center;
    border-radius: 8px;
    background: linear-gradient(135deg, #4a8be6, #2563c4);
    color: #fff;
    font-weight: 700;
    font-size: 14px;
}
.os-app-tile-icon img { width: 28px; height: 28px; }
.os-app-tile-name {
    font-size: 11px;
    line-height: 1.2;
    color: var(--os-fg);
    overflow: hidden;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    word-break: break-word;
}

.os-sm-all {
    list-style: none;
    margin: 0;
    padding: 0;
    overflow-y: auto;
    flex: 1;
}
.os-sm-all li { margin: 0; }
.os-sm-row {
    appearance: none;
    width: 100%;
    background: transparent;
    border: 0;
    color: var(--os-fg);
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 6px 8px;
    border-radius: var(--os-radius-sm);
    cursor: pointer;
    text-align: left;
    font: inherit;
    font-size: 13px;
}
.os-sm-row:hover { background: rgba(255, 255, 255, 0.08); }
.os-sm-row-icon {
    width: 22px; height: 22px;
    border-radius: 5px;
    background: linear-gradient(135deg, #4a8be6, #2563c4);
    color: #fff;
    display: inline-flex; align-items: center; justify-content: center;
    font-size: 10px; font-weight: 700;
    flex-shrink: 0;
}
.os-sm-row-icon img { width: 18px; height: 18px; }

/* ── App Categorisation Engine: Start menu sections ──────────────────────
   Only active when .os-sm-all carries .categorised; the legacy flat list is
   untouched so engine-off parity is structural, not just tested. */
.os-sm-all.categorised > li.os-sm-cat { margin: 0 0 2px; }

.os-sm-cat-head {
    appearance: none;
    width: 100%;
    background: transparent;
    border: 0;
    color: var(--os-fg-muted);
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 6px 8px;
    border-radius: var(--os-radius-sm);
    cursor: pointer;
    text-align: left;
    font: inherit;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
}
.os-sm-cat-head:hover { background: rgba(255, 255, 255, 0.06); color: var(--os-fg); }
.os-sm-cat-head:focus-visible { outline: 2px solid var(--os-accent, #0078d4); outline-offset: -2px; }

.os-sm-cat-chevron {
    display: inline-block;
    flex-shrink: 0;
    width: 10px;
    font-size: 13px;
    line-height: 1;
    transform: rotate(90deg);
    transition: transform 140ms ease;
}
.os-sm-cat.collapsed > .os-sm-cat-head .os-sm-cat-chevron { transform: rotate(0deg); }

.os-sm-cat-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.os-sm-cat-count {
    flex-shrink: 0;
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 0;
    padding: 1px 6px;
    border-radius: 9px;
    background: rgba(255, 255, 255, 0.10);
    color: var(--os-fg-muted);
}

.os-sm-cat-body { list-style: none; margin: 0 0 6px; padding: 0; }
.os-sm-cat-body .os-sm-row { padding-left: 18px; }

/* The system drawer reads as a separator, not a peer of the real categories. */
.os-sm-cat-drawer {
    margin-top: 8px !important;
    padding-top: 8px;
    border-top: 1px solid rgba(255, 255, 255, 0.08);
}

:root[data-theme="light"] .os-sm-cat-head:hover { background: rgba(0, 0, 0, 0.05); }
:root[data-theme="light"] .os-sm-cat-count { background: rgba(0, 0, 0, 0.08); }
:root[data-theme="light"] .os-sm-cat-drawer { border-top-color: rgba(0, 0, 0, 0.10); }

.os-sm-foot {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    padding-top: 8px;
    border-top: 1px solid var(--os-surface-border);
    color: var(--os-fg-muted);
    font-size: 12px;
}

/* ── Windows host & windows ──────────────────────────────────────── */
.os-windows {
    position: fixed;
    inset: 0 0 var(--os-tb-height) 0;
    pointer-events: none;
    z-index: 5;
}
.os-windows > * { pointer-events: auto; }

.os-win {
    position: absolute;
    background: var(--os-surface);
    border: 1px solid var(--os-surface-border);
    border-radius: var(--os-radius);
    box-shadow: 0 12px 36px rgba(0, 0, 0, 0.45);
    overflow: hidden;
    display: flex;
    flex-direction: column;
    color: var(--os-fg);
    backdrop-filter: blur(18px) saturate(140%);
    -webkit-backdrop-filter: blur(18px) saturate(140%);
    transition: opacity 120ms ease;
}
.os-win.minimized { display: none; }
.os-win.focused { box-shadow: 0 16px 48px rgba(0, 0, 0, 0.55); border-color: rgba(255,255,255,0.18); }
.os-win.maximized { border-radius: 0; box-shadow: none; }

.os-win-chrome {
    display: flex;
    align-items: center;
    justify-content: space-between;
    height: 32px;
    padding: 0 0 0 12px;
    background: rgba(0, 0, 0, 0.22);
    border-bottom: 1px solid var(--os-surface-border);
    color: var(--os-fg);
    font-size: 12px;
    font-weight: 500;
    user-select: none;
    cursor: grab;
    flex-shrink: 0;
}
.os-win-chrome:active { cursor: grabbing; }
.os-win-title {
    flex: 1;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    letter-spacing: 0.01em;
}
.os-win-controls { display: flex; align-items: stretch; }
.os-win-btn {
    appearance: none;
    background: transparent;
    border: 0;
    width: 44px; height: 32px;
    color: var(--os-fg);
    cursor: pointer;
    display: inline-flex; align-items: center; justify-content: center;
    transition: background 100ms ease, color 100ms ease;
}
.os-win-btn:hover { background: rgba(255,255,255,0.08); }
.os-win-btn.os-win-close:hover { background: #c42b1c; color: #fff; }

.os-win-frame {
    flex: 1;
    border: 0;
    width: 100%;
    background: #fff;
}
body.os-wm-interacting .os-win-frame { pointer-events: none; }
body.os-wm-interacting .os-assistant-host iframe { pointer-events: none; }
body.os-wm-interacting { user-select: none; cursor: grabbing; }

/* Resize handles */
.os-win-resize {
    position: absolute;
    background: transparent;
    z-index: 1;
}
.os-win.maximized .os-win-resize { display: none; }
.os-win-resize.n  { top: 0;    left: 10px; right: 10px; height: 5px; cursor: ns-resize; }
.os-win-resize.s  { bottom: 0; left: 10px; right: 10px; height: 5px; cursor: ns-resize; }
.os-win-resize.e  { right: 0;  top: 10px; bottom: 10px; width: 5px; cursor: ew-resize; }
.os-win-resize.w  { left: 0;   top: 10px; bottom: 10px; width: 5px; cursor: ew-resize; }
.os-win-resize.ne { top: 0;    right: 0;  width: 12px; height: 12px; cursor: nesw-resize; }
.os-win-resize.nw { top: 0;    left: 0;   width: 12px; height: 12px; cursor: nwse-resize; }
.os-win-resize.se { bottom: 0; right: 0;  width: 12px; height: 12px; cursor: nwse-resize; }
.os-win-resize.sw { bottom: 0; left: 0;   width: 12px; height: 12px; cursor: nesw-resize; }

/* Snap preview overlay */
.os-snap-overlay {
    position: fixed;
    background: rgba(0, 120, 212, 0.18);
    border: 2px solid rgba(0, 120, 212, 0.55);
    border-radius: 6px;
    pointer-events: none;
    z-index: 4;
    transition: all 80ms ease;
}
.os-snap-overlay[hidden] { display: none; }

/* Alt+Tab switcher */
.os-altswitch {
    position: fixed;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    background: var(--os-surface);
    border: 1px solid var(--os-surface-border);
    border-radius: 12px;
    backdrop-filter: blur(28px) saturate(160%);
    -webkit-backdrop-filter: blur(28px) saturate(160%);
    box-shadow: var(--os-shadow);
    padding: 16px;
    z-index: 30;
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    max-width: 80vw;
}
.os-altswitch[hidden] { display: none; }
.os-altswitch-item {
    min-width: 160px;
    padding: 10px 14px;
    border-radius: 8px;
    background: rgba(255, 255, 255, 0.05);
    border: 2px solid transparent;
    color: var(--os-fg);
    font-size: 12px;
}
.os-altswitch-item.active {
    border-color: var(--os-accent);
    background: rgba(0, 120, 212, 0.18);
}
.os-altswitch-title {
    display: block;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}

/* ── Lock screen ─────────────────────────────────────────────────── */
body.os-lock {
    margin: 0;
    height: 100vh;
    overflow: hidden;
    color: #fff;
    font-family: var(--os-font);
    position: relative;
    background: #0a3a6b;
}
.os-lock-bg {
    position: fixed;
    inset: 0;
    background:
        radial-gradient(1200px 800px at 20% 30%, #6ab1e8, transparent 60%),
        radial-gradient(1000px 700px at 80% 70%, #1d6fb8, transparent 65%),
        linear-gradient(135deg, #0a3a6b, #1d6fb8);
    z-index: 0;
}
.os-lock-clock {
    position: fixed;
    top: 8vh;
    left: 8vw;
    z-index: 1;
    color: #fff;
    text-shadow: 0 2px 16px rgba(0,0,0,0.4);
    user-select: none;
}
.os-lock-time {
    display: block;
    font-size: clamp(72px, 12vw, 144px);
    font-weight: 200;
    line-height: 1;
    letter-spacing: -0.04em;
    font-variant-numeric: tabular-nums;
}
.os-lock-date {
    display: block;
    font-size: clamp(18px, 2vw, 24px);
    font-weight: 400;
    margin-top: 6px;
    opacity: 0.92;
}
.os-lock-card {
    position: relative;
    z-index: 2;
    width: min(360px, calc(100vw - 32px));
    margin: 0 auto;
    height: 100vh;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 14px;
    text-align: center;
}
.os-lock-avatar-large {
    width: 96px;
    height: 96px;
    border-radius: 50%;
    background: linear-gradient(135deg, #4a8be6, #2563c4);
    color: #fff;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 32px;
    font-weight: 600;
    letter-spacing: 0.02em;
    box-shadow: 0 8px 24px rgba(0,0,0,0.4);
}
.os-lock-fullname {
    font-size: 20px;
    font-weight: 500;
    margin-bottom: 6px;
}
.os-lock-users {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(88px, 1fr));
    gap: 12px;
    width: 100%;
    margin-bottom: 8px;
}
.os-lock-user {
    appearance: none;
    background: rgba(255,255,255,0.08);
    border: 1px solid rgba(255,255,255,0.18);
    border-radius: 10px;
    color: #fff;
    padding: 10px 8px;
    cursor: pointer;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
}
.os-lock-user:hover { background: rgba(255,255,255,0.16); }
.os-lock-avatar {
    width: 40px; height: 40px; border-radius: 50%;
    background: linear-gradient(135deg, #4a8be6, #2563c4);
    color: #fff;
    display: inline-flex; align-items: center; justify-content: center;
    font-size: 14px; font-weight: 600;
}
.os-lock-username { font-size: 12px; }
.os-lock-form {
    display: flex;
    flex-direction: column;
    gap: 10px;
    width: 100%;
}
.os-lock-field { display: flex; flex-direction: column; gap: 6px; text-align: left; }
.os-lock-field span { font-size: 12px; color: rgba(255,255,255,0.78); }
.os-lock-field input {
    appearance: none;
    background: rgba(255,255,255,0.92);
    border: 0;
    border-radius: 6px;
    padding: 10px 12px;
    font: inherit;
    font-size: 14px;
    color: #1b1b1b;
    outline: none;
}
.os-lock-field input:focus { box-shadow: 0 0 0 2px rgba(255,255,255,0.7); }
.os-lock-error {
    margin: 0;
    color: #ffd1c4;
    font-size: 12px;
}
.os-lock-submit {
    appearance: none;
    background: rgba(255,255,255,0.92);
    border: 0;
    border-radius: 6px;
    padding: 10px 14px;
    font: inherit;
    font-size: 14px;
    font-weight: 500;
    color: #0a3a6b;
    cursor: pointer;
}
.os-lock-submit:hover { background: #fff; }
.os-lock-instance {
    position: fixed;
    bottom: 16px;
    right: 16px;
    font-size: 12px;
    color: rgba(255,255,255,0.78);
    z-index: 2;
}

/* ── Start-menu power menu ───────────────────────────────────────── */
.os-sm-power-wrap { position: relative; }
.os-sm-power-btn {
    appearance: none;
    background: transparent;
    border: 0;
    width: 32px; height: 32px;
    border-radius: var(--os-radius-sm);
    color: var(--os-fg);
    cursor: pointer;
    display: inline-flex; align-items: center; justify-content: center;
}
.os-sm-power-btn:hover { background: rgba(255,255,255,0.08); }
.os-sm-power-menu {
    position: absolute;
    right: 0;
    bottom: 100%;
    margin-bottom: 6px;
    min-width: 160px;
    background: var(--os-surface);
    border: 1px solid var(--os-surface-border);
    border-radius: 8px;
    backdrop-filter: blur(28px) saturate(160%);
    -webkit-backdrop-filter: blur(28px) saturate(160%);
    box-shadow: var(--os-shadow);
    padding: 4px;
    display: flex;
    flex-direction: column;
    z-index: 25;
}
.os-sm-power-menu[hidden] { display: none; }
.os-sm-power-menu button {
    appearance: none;
    background: transparent;
    border: 0;
    color: var(--os-fg);
    text-align: left;
    padding: 8px 12px;
    border-radius: 6px;
    cursor: pointer;
    font: inherit;
    font-size: 13px;
}
.os-sm-power-menu button:hover { background: rgba(255,255,255,0.08); }

/* ── Assistant dock (iframe overlay) ─────────────────────────────────── */
/* The host is a small rectangle in the bottom-right of the desktop. The
   iframe inside it is `pointer-events: auto` so clicks land on the
   visualizer / drawer / panels normally. The rest of the desktop stays
   clickable because the iframe doesn't cover it. */
.os-assistant-host {
    position: fixed;
    left: 0;
    bottom: var(--os-tb-height);
    width: 750px;
    height: min(820px, calc(100vh - var(--os-tb-height) - 16px));
    max-width: 100vw;
    z-index: 4;
    pointer-events: none;
}
.os-assistant-host iframe {
    width: 100%;
    height: 100%;
    border: 0;
    background: transparent;
    pointer-events: auto;
}
.os-assistant-host.obstructed {
    /* Detector hides the dock when a window covers it; the taskbar mini-pill
       (.os-tb-assistant-mini) becomes the visible affordance instead. */
    display: none;
}

/* Phone/tablet docks are taller than --os-tb-height (48px) and extend through
   the home-indicator safe area, so the host's bottom must clear the *real*
   dock height — otherwise the text-drawer (pinned to bottom:0 inside the
   iframe) overlaps the dock. --os-dock-h is the live measured taskbar height,
   published by shell.js once Start/pins/tray have laid out, so the drawer
   tracks the dock's settled end-state (the static fallbacks cover the brief
   pre-JS window and mirror the declared dock heights). */
body.os-mode-phone .os-assistant-host {
    bottom: var(--os-dock-h, calc(60px + env(safe-area-inset-bottom, 0px)));
    height: min(820px, calc(100vh - var(--os-dock-h, calc(60px + env(safe-area-inset-bottom, 0px))) - 16px));
}
body.os-mode-tablet .os-assistant-host {
    bottom: var(--os-dock-h, calc(64px + env(safe-area-inset-bottom, 0px)));
    height: min(820px, calc(100vh - var(--os-dock-h, calc(64px + env(safe-area-inset-bottom, 0px))) - 16px));
}

/* ── Taskbar left slot — mini assistant pill (mirror of right tray) ── */
.os-tb-leftslot {
    position: absolute;
    left: 12px;
    top: 0; bottom: 0;
    display: flex;
    align-items: center;
    pointer-events: none;
}
.os-tb-leftslot > * { pointer-events: auto; }
.os-tb-assistant-mini {
    background: rgba(255,255,255,0.06);
    border: 1px solid var(--os-surface-border);
    border-radius: var(--os-radius-sm);
    color: var(--os-fg);
    height: 36px;
    padding: 0 6px 0 0;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 12px;
    max-width: min(480px, 42vw);
    overflow: hidden;
}
.os-tb-assistant-mini[hidden] { display: none; }
.os-tb-assistant-mini-main {
    appearance: none;
    background: transparent;
    border: 0;
    border-radius: var(--os-radius-sm) 0 0 var(--os-radius-sm);
    color: inherit;
    height: 100%;
    padding: 0 8px 0 10px;
    display: inline-flex;
    align-items: center;
    gap: 10px;
    cursor: pointer;
    font: inherit;
    font-size: 12px;
    transition: background 100ms ease;
    min-width: 0;
    overflow: hidden;
}
.os-tb-assistant-mini-main:hover { background: rgba(255,255,255,0.12); }
.os-tb-assistant-mini-mic {
    appearance: none;
    flex-shrink: 0;
    height: 24px;
    min-width: 24px;
    padding: 0 10px;
    border: 0;
    border-radius: var(--os-radius-sm);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    font: inherit;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.06em;
    transition: background 120ms ease;
}
.os-tb-assistant-mini-mic.mic-idle {
    background: rgba(255,255,255,0.06);
    border: 1px solid var(--os-surface-border);
    color: var(--os-fg);
}
.os-tb-assistant-mini-mic.mic-idle:hover { background: rgba(255,255,255,0.12); }
.os-tb-assistant-mini-mic.mic-connecting {
    background: rgba(255,255,255,0.10);
    color: var(--os-fg);
    animation: os-tb-mini-mic-pulse 1.2s ease-in-out infinite;
}
.os-tb-assistant-mini-mic.mic-active { background: #ef4444; color: #fff; padding: 0 8px; }
.os-tb-assistant-mini-mic.mic-active:hover { background: #dc2626; }
.os-tb-assistant-mini-mic.mic-error {
    background: rgba(251,146,60,0.15);
    color: #fb923c;
    border: 1px solid rgba(251,146,60,0.3);
}
.os-tb-assistant-mini-mic.mic-error:hover { background: rgba(251,146,60,0.25); }
@keyframes os-tb-mini-mic-pulse {
    0%, 100% { opacity: 1; }
    50%      { opacity: 0.45; }
}
.os-tb-assistant-mini-cog {
    appearance: none;
    flex-shrink: 0;
    height: 24px;
    width: 24px;
    padding: 0;
    border: 0;
    border-radius: 12px;
    background: rgba(255,255,255,0.06);
    color: var(--os-fg);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transition: background 100ms ease;
}
.os-tb-assistant-mini-cog:hover { background: rgba(255,255,255,0.16); }
.os-tb-assistant-mini-wave {
    width: 96px;
    height: 22px;
    display: block;
    flex-shrink: 0;
    opacity: 0.95;
}
.os-tb-assistant-mini-caption {
    display: inline-flex;
    align-items: baseline;
    gap: 6px;
    min-width: 0;
    overflow: hidden;
    white-space: nowrap;
}
.os-tb-assistant-mini-who {
    color: var(--os-accent);
    font-weight: 600;
    flex-shrink: 0;
}
.os-tb-assistant-mini-text {
    color: var(--os-fg);
    opacity: 0.85;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
}
.os-tb-assistant-mini-caption:empty,
.os-tb-assistant-mini-text:empty { display: none; }

/* Running indicators on taskbar pins */
.os-tb-pin { position: relative; }
.os-tb-pin.running::after {
    content: "";
    position: absolute;
    bottom: 2px;
    left: 50%;
    transform: translateX(-50%);
    width: 12px;
    height: 2px;
    border-radius: 1px;
    background: var(--os-accent);
    opacity: 0.7;
}
.os-tb-pin.running.focused::after {
    width: 22px;
    opacity: 1;
}

/* ╔══════════════════════════════════════════════════════════════════╗
   ║  Mobile & PWA foundations (Phase 1 / Phase 2 / Phase 3)         ║
   ║  Desktop styles above are unchanged. Everything below scopes    ║
   ║  to body.os-mode-tablet / .os-mode-phone, or to the .os-standalone
   ║  class set when the page is launched as a PWA.                  ║
   ╚══════════════════════════════════════════════════════════════════╝ */

:root {
    /* Resolved safe-area insets. Fallbacks are 0 so desktop is unaffected. */
    --os-safe-top:    env(safe-area-inset-top, 0px);
    --os-safe-right:  env(safe-area-inset-right, 0px);
    --os-safe-bottom: env(safe-area-inset-bottom, 0px);
    --os-safe-left:   env(safe-area-inset-left, 0px);
}

/* When running as an installed PWA, suppress browser-style overscroll glow
   and use the dynamic viewport so the iOS URL bar doesn't crop content. */
body.os-standalone,
body.os-mode-phone,
body.os-mode-tablet {
    overscroll-behavior: none;
}
body.os-mode-phone,
body.os-mode-tablet {
    /* Use dvh where supported so iOS Safari URL bar resize doesn't crop
       the shell. Falls back to 100vh for older engines. */
    height: 100vh;
    height: 100dvh;
}
body.os-mode-phone .os-lock-bg,
body.os-mode-tablet .os-lock-bg {
    height: 100dvh;
}

/* ╔══════════════════════════════════════════════════════════════════╗
   ║  Phase 2 — Phone shell (max-width: 640px, coarse pointer)       ║
   ║                                                                  ║
   ║  Reuses the existing taskbar / Start menu / flyouts / windows   ║
   ║  DOM but restyles them as a bottom dock, fullscreen Home grid,  ║
   ║  bottom sheets, and fullscreen window sheets respectively.     ║
   ╚══════════════════════════════════════════════════════════════════╝ */

body.os-mode-phone .os-desktop {
    /* Background only; no icons grid on phone. Pull-to-refresh / overscroll
       glow already suppressed via overscroll-behavior on body. */
    padding: 0;
}

/* Lock-screen ─────────────────────────────────────────────────────── */
body.os-mode-phone.os-lock {
    background-position: center;
    background-size: cover;
}
body.os-mode-phone .os-lock-clock {
    top: calc(env(safe-area-inset-top, 0px) + 28px);
    left: 0; right: 0;
    text-align: center;
    transform: none;
    padding: 0 20px;
}
body.os-mode-phone .os-lock-clock .os-lock-time { font-size: 64px; }
body.os-mode-phone .os-lock-clock .os-lock-date { font-size: 16px; }
body.os-mode-phone .os-lock-card {
    left: max(env(safe-area-inset-left, 0px), 16px);
    right: max(env(safe-area-inset-right, 0px), 16px);
    bottom: max(env(safe-area-inset-bottom, 0px), 24px);
    top: auto;
    transform: none;
    width: auto;
    max-width: none;
    padding: 24px 20px;
    border-radius: 18px;
}
body.os-mode-phone .os-lock-users {
    flex-direction: column;
    gap: 8px;
}
body.os-mode-phone .os-lock-user {
    min-height: 56px;
    width: 100%;
    justify-content: flex-start;
    padding: 12px 14px;
}
body.os-mode-phone .os-lock-form input,
body.os-mode-phone .os-lock-form .os-lock-submit {
    min-height: 48px;
    font-size: 16px; /* prevent iOS zoom-on-focus */
}

/* Dock (the existing .os-taskbar restyled) ──────────────────────────── */
body.os-mode-phone .os-taskbar {
    /* Flush bottom edge; height extends through the safe-area so the bar
       visually meets the home indicator. The inner padding-bottom carries
       the safe-area space so content stays touch-reachable. */
    bottom: 0;
    left: 0;
    right: 0;
    width: 100%;
    max-width: none;
    height: calc(60px + env(safe-area-inset-bottom, 0px));
    border-radius: 0;
    padding: 0;
    backdrop-filter: blur(24px) saturate(140%);
    -webkit-backdrop-filter: blur(24px) saturate(140%);
}
body.os-mode-phone .os-taskbar-inner {
    /* Flex so DOM order (Start → Pins → Tray) is visual order. The desktop
       grid would otherwise drop the tray (which loses its absolute-position
       when its container becomes static) into column 1, pushing it left. */
    display: flex;
    align-items: center;
    grid-template-columns: none;
    padding: 6px max(env(safe-area-inset-left, 0px), 8px) calc(env(safe-area-inset-bottom, 0px) + 6px) max(env(safe-area-inset-right, 0px), 8px);
    gap: 6px;
    justify-content: flex-start;
    border-radius: 0;
}
body.os-mode-phone .os-start-btn { grid-column: auto; flex: 0 0 auto; }
body.os-mode-phone .os-tb-pins { grid-column: auto; }
/* Hide the assistant mini-caption that hugs the left of the taskbar; on phone
   we don't have horizontal room. Drag-drive bridge still emits notifications
   via the bell / sheet. */
body.os-mode-phone .os-tb-leftslot { display: none; }

body.os-mode-phone .os-start-btn {
    min-width: 56px;
    min-height: 52px;
    border-radius: 14px;
}
body.os-mode-phone .os-start-btn .os-start-glyph { width: 36px; height: 36px; }

body.os-mode-phone .os-tb-pins {
    flex: 1;
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: none;
    gap: 6px;
    padding: 0 4px;
    scroll-snap-type: x mandatory;
}
body.os-mode-phone .os-tb-pins::-webkit-scrollbar { display: none; }
body.os-mode-phone .os-tb-pin {
    min-width: 52px;
    min-height: 52px;
    border-radius: 14px;
    scroll-snap-align: center;
}
body.os-mode-phone .os-tb-pin img,
body.os-mode-phone .os-tb-pin svg { width: 28px; height: 28px; }

body.os-mode-phone .os-tb-tray { gap: 4px; padding-right: 2px; }
body.os-mode-phone .os-tray-btn {
    min-width: 48px;
    min-height: 48px;
    padding: 0 10px;
    border-radius: 14px;
}
body.os-mode-phone .os-tray-btn svg { width: 18px; height: 18px; }
/* Temperature pill: keep, but a touch smaller so the dock isn't cramped. */
body.os-mode-phone .os-tray-temp .os-temp-pill { font-size: 12px; }
/* Drop the inline clock label — calendar still opens via the clock button. */
body.os-mode-phone .os-tray-clock-btn .os-clock-time,
body.os-mode-phone .os-tray-clock-btn .os-clock-date { display: none; }
body.os-mode-phone .os-tray-clock-btn .os-clock {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 14px;
    font-weight: 600;
}
body.os-mode-phone .os-tray-clock-btn::before {
    content: "";
    display: inline-block;
    width: 18px; height: 18px;
    background: currentColor;
    -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><circle cx='8' cy='8' r='6' fill='none' stroke='black' stroke-width='1.4'/><path d='M8 4 V8 L11 10' stroke='black' stroke-width='1.4' fill='none' stroke-linecap='round'/></svg>") center/contain no-repeat;
            mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><circle cx='8' cy='8' r='6' fill='none' stroke='black' stroke-width='1.4'/><path d='M8 4 V8 L11 10' stroke='black' stroke-width='1.4' fill='none' stroke-linecap='round'/></svg>") center/contain no-repeat;
}

/* Start menu → Home grid that clears the dock ────────────────────────
   The Start menu must NOT cover the dock — otherwise the Start button
   itself, the pins, and the clock all disappear under z-index:20, leaving
   the user with no visible way to dismiss the menu and no notification
   access while it's open. Bottom is clipped to the dock height so the
   dock peeks through. */
body.os-mode-phone .os-startmenu {
    left: 0;
    right: 0;
    top: 0;
    bottom: calc(60px + env(safe-area-inset-bottom, 0px));
    width: auto;
    height: auto;
    max-width: none;
    max-height: none;
    border-radius: 0;
    transform: none !important;
    padding:
        calc(env(safe-area-inset-top, 0px) + 16px)
        max(env(safe-area-inset-right, 0px), 16px)
        16px
        max(env(safe-area-inset-left, 0px), 16px);
    overflow: hidden;
}
body.os-mode-phone .os-startmenu.open { transform: none; }
/* The 16px font stays: iOS zooms the page when an input under 16px takes
   focus. The box around it does not have to be 44px tall to hold a 16px line,
   and at 62px plus a 16px margin the search was 78px of a menu that has to fit
   a phone on its side. 46px instead, which is a third of a row of apps back. */
body.os-mode-phone .os-sm-search {
    padding: 4px 12px;
    margin-bottom: 8px;
}
body.os-mode-phone .os-sm-search input {
    font-size: 16px;
    min-height: 34px;
}
body.os-mode-phone .os-sm-pinned {
    grid-template-columns: repeat(4, 1fr);
    gap: 12px;
}
body.os-mode-phone .os-sm-tile {
    min-height: 88px;
}
body.os-mode-phone .os-sm-tile img,
body.os-mode-phone .os-sm-tile svg { width: 36px; height: 36px; }
body.os-mode-phone .os-sm-all li { min-height: 56px; }

/* Flyouts → bottom sheets ─────────────────────────────────────────────
   IMPORTANT: scope to :not([hidden]) so display:flex doesn't beat the
   default .os-flyout[hidden] { display:none } on closed flyouts. */
body.os-mode-phone .os-flyout:not([hidden]) {
    left: max(env(safe-area-inset-left, 0px), 8px);
    right: max(env(safe-area-inset-right, 0px), 8px);
    top: auto;
    bottom: calc(72px + env(safe-area-inset-bottom, 0px));
    width: auto;
    max-width: none;
    max-height: 70vh;
    max-height: 70dvh;
    border-radius: 22px;
    transform-origin: bottom center;
    overflow: hidden;
    display: flex;
    flex-direction: column;
}
body.os-mode-phone .os-flyout::before {
    /* Drag handle. Visual only — actual swipe-to-dismiss is a polish task. */
    content: "";
    display: block;
    width: 44px;
    height: 4px;
    border-radius: 2px;
    background: rgba(255,255,255,0.35);
    margin: 8px auto 4px;
    flex: 0 0 auto;
}
body.os-mode-phone[data-theme="light"] .os-flyout::before { background: rgba(0,0,0,0.25); }
body.os-mode-phone .os-flyout-head {
    font-size: 18px;
    text-align: center;
    padding: 8px 16px 12px;
}
body.os-mode-phone .os-flyout > *:not(.os-flyout-head):not(.os-cal-month-head) {
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
}

/* Touch-friendly sizing inside sheets. */
body.os-mode-phone .os-qs-toggle,
body.os-mode-phone .os-qs-actions button,
body.os-mode-phone .os-ac-item,
body.os-mode-phone .os-home-tabs button {
    min-height: 48px;
}
body.os-mode-phone .os-qs-accents .os-qs-accent,
body.os-mode-phone .os-qs-wallpapers .os-qs-wallpaper {
    width: 56px; height: 56px;
}

/* Windows → fullscreen sheets ────────────────────────────────────────── */
body.os-mode-phone .os-windows {
    /* Constrain the window host so windows can't bleed past the safe area. */
    position: fixed;
    inset: 0;
    pointer-events: none; /* children re-enable */
}
body.os-mode-phone .os-win {
    /* wm.js skips applyRect() in mobile modes so no inline geometry to fight. */
    left: 0;
    top: 0;
    right: 0;
    bottom: 0;
    width: 100%;
    height: 100%;
    border-radius: 0;
    border: none;
    box-shadow: none;
    pointer-events: auto;
    padding-top: env(safe-area-inset-top, 0px);
    padding-bottom: calc(84px + env(safe-area-inset-bottom, 0px));
}
body.os-mode-phone .os-win-chrome {
    cursor: default;
    min-height: 48px;
    padding: 0 8px 0 14px;
}
body.os-mode-phone .os-win-chrome:active { cursor: default; }
body.os-mode-phone .os-win-title { font-size: 15px; font-weight: 600; }
body.os-mode-phone .os-win-btn {
    min-width: 44px;
    min-height: 44px;
}
/* Hide minimize + maximize on phone — only Close remains. Minimize is
   redundant because the dock + Start return the user to Home. */
body.os-mode-phone .os-win-btn[data-win-min],
body.os-mode-phone .os-win-btn[data-win-max] { display: none; }
/* No drag handles, no resize affordances on phone. */
body.os-mode-phone .os-win-resize { display: none !important; }
body.os-mode-phone .os-win.minimized { display: none; }

/* Snap overlay is meaningless on phone. */
body.os-mode-phone .os-snap-overlay { display: none !important; }
body.os-mode-phone .os-altswitch { display: none !important; }

/* Reduced-motion respect (applies in all modes but worth restating). */
@media (prefers-reduced-motion: reduce) {
    body.os-mode-phone .os-flyout,
    body.os-mode-phone .os-startmenu,
    body.os-mode-phone .os-win {
        transition: none !important;
        animation: none !important;
    }
}

/* Safety-net: minimum touch target on coarse pointers. */
@media (pointer: coarse) {
    body.os-mode-phone :is(button, [role="button"], a, input[type="checkbox"], input[type="radio"]) {
        min-height: 44px;
    }
    /*
     * ...but not the docked Assistant's own compact chrome.
     *
     * That floor is a touch-target rule for the shell, and it reached into an
     * embedded app whose toolbar is 22px by design, inside a 26px strip. The
     * controls then stood 18px taller than the strip that holds them: on an
     * iPhone the BEGIN/stop square and the settings cog ballooned in portrait
     * and were correct in landscape, because portrait is phone mode and
     * landscape (844px) is tablet mode, where this rule does not apply. Same
     * story for the cli tab's own toolbar and its session chips.
     *
     * Scoped as an override rather than by narrowing the selector above, so the
     * floor itself is untouched everywhere else, including the rest of the
     * Assistant. The app's own CSS decides these sizes, and it already decides
     * them identically in both orientations.
     */
    body.os-mode-phone #os-assistant
        :is(.text-drawer-bar, #cli-term .toolbar, #cli-tabstrip)
        :is(button, [role="button"], a) {
        min-height: 0;
    }
}

/* ╔══════════════════════════════════════════════════════════════════╗
   ║  Phase 3 — Tablet shell (641–1023px, or coarse pointer up to    ║
   ║  1279px). Hybrid: keeps the bottom taskbar (enlarged for touch) ║
   ║  but renders windows fullscreen-by-default. No drag/resize.     ║
   ╚══════════════════════════════════════════════════════════════════╝ */

body.os-mode-tablet .os-taskbar {
    /* 64px, matching the space .os-windows reserves below every window (see the
       tablet .os-windows rule). They used to disagree by 8px, which showed as a
       strip of desktop between every app and the bar; the bar takes the space
       rather than the reserve giving it back, because a touch target that size
       is the reason phone mode runs a taller dock too. Everything positioned
       above the bar below moves with it. */
    height: calc(64px + env(safe-area-inset-bottom, 0px));
    bottom: 0;
    left: 0;
    right: 0;
    width: 100%;
    max-width: none;
    border-radius: 0;
}
body.os-mode-tablet .os-taskbar-inner {
    display: flex;
    align-items: center;
    grid-template-columns: none;
    padding: 4px max(env(safe-area-inset-left, 0px), 10px) calc(env(safe-area-inset-bottom, 0px) + 4px) max(env(safe-area-inset-right, 0px), 10px);
    border-radius: 0;
    gap: 6px;
}
body.os-mode-tablet .os-tb-tray {
    position: static;
    margin-left: auto;
}
body.os-mode-tablet .os-start-btn { grid-column: auto; flex: 0 0 auto; }
body.os-mode-tablet .os-tb-pins { grid-column: auto; }
/* The left slot (the docked assistant pill) is absolutely positioned, which the
   desktop grid pays for with a spacer column and phone mode sidesteps by hiding
   it. Tablet did neither: it switched the bar to flex and left the pill floating
   over the flow, so Start and the running-app pins were drawn straight through
   it. Measured with two apps running: 46px of overlap with Start, 236px with the
   pins. Give it a place in the row and let the pins be the part that gives. */
body.os-mode-tablet .os-tb-leftslot {
    position: static;
    flex: 0 1 auto;
    min-width: 0;
}
body.os-mode-tablet .os-tb-assistant-mini { max-width: min(280px, 32vw); }
body.os-mode-tablet .os-tb-pins {
    flex: 1 1 auto;
    min-width: 0;
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: none;
}
body.os-mode-tablet .os-tb-pins::-webkit-scrollbar { display: none; }
body.os-mode-tablet .os-start-btn,
body.os-mode-tablet .os-tb-btn,
body.os-mode-tablet .os-tray-btn {
    min-width: 48px;
    min-height: 48px;
    border-radius: 12px;
}
body.os-mode-tablet .os-tb-pin {
    min-width: 48px;
    min-height: 48px;
    border-radius: 12px;
}
body.os-mode-tablet .os-tb-pin img,
body.os-mode-tablet .os-tb-pin svg { width: 26px; height: 26px; }
body.os-mode-tablet .os-tray-btn svg { width: 18px; height: 18px; }

/* Start menu: drawer from the bottom, full-width with rounded top. ──── */
body.os-mode-tablet .os-startmenu {
    left: max(env(safe-area-inset-left, 0px), 12px);
    right: max(env(safe-area-inset-right, 0px), 12px);
    width: auto;
    max-width: none;
    bottom: calc(80px + env(safe-area-inset-bottom, 0px));   /* bar + 16 */
    height: min(70vh, 70dvh);
    transform: none !important;
    border-radius: 18px;
}
body.os-mode-tablet .os-startmenu.open { transform: none !important; }
body.os-mode-tablet .os-sm-pinned { grid-template-columns: repeat(6, 1fr); }
body.os-mode-tablet .os-sm-tile { min-height: 96px; }
body.os-mode-tablet .os-sm-search {
    padding: 4px 12px;
    margin-bottom: 8px;
}
body.os-mode-tablet .os-sm-search input { font-size: 16px; min-height: 34px; }

/* ── A phone on its side ────────────────────────────────────────────────────
   Measured on the reported device: 1000x460 in landscape, 460x998 in portrait.
   Portrait is phone mode and works: a full-screen sheet, square corners, the
   app list scrolling inside it. Landscape is 1000px wide, which is TABLET mode,
   and tablet mode is a floating 322px panel: search 44 + pinned 101 + folders
   68 + footer 41 left the All-apps section at exactly 0px, holding 1922px of
   apps, with the panel overflow:visible so the footer spilled out below it.
   Nothing under Pinned could be reached.

   A phone on its side has a tablet's width and a phone's height, so key off the
   height and give it the sheet portrait already has. An iPad in landscape is
   768px tall and never enters this block. Nothing here changes the panel that
   taller tablets get. */
@media (max-height: 560px) {
    body.os-mode-tablet .os-startmenu {
        left: 0;
        right: 0;
        top: 0;
        bottom: calc(64px + env(safe-area-inset-bottom, 0px));   /* the dock */
        width: auto;
        height: auto;
        max-width: none;
        max-height: none;
        border-radius: 0;
        padding:
            calc(env(safe-area-inset-top, 0px) + 12px)
            max(env(safe-area-inset-right, 0px), 16px)
            12px
            max(env(safe-area-inset-left, 0px), 16px);
        /* The sheet does not scroll; the All-apps list is the one scroller,
           exactly as in portrait. */
        overflow: hidden;
    }

    /* And the content stops eating the sheet, in both modes. A landscape phone
       is wide: the pinned apps fit in one row of eight instead of wrapping. */
    body:is(.os-mode-phone, .os-mode-tablet) .os-sm-pinned {
        grid-template-columns: repeat(4, 1fr);
        gap: 8px;
    }
    body:is(.os-mode-phone, .os-mode-tablet) .os-sm-tile {
        min-height: 56px;
        padding: 6px 4px 5px;
        gap: 3px;
    }
    body:is(.os-mode-phone, .os-mode-tablet) .os-sm-tile img,
    body:is(.os-mode-phone, .os-mode-tablet) .os-sm-tile svg,
    body:is(.os-mode-phone, .os-mode-tablet) .os-app-tile-icon {
        width: 28px;
        height: 28px;
    }
    body:is(.os-mode-phone, .os-mode-tablet) .os-app-tile-name {
        -webkit-line-clamp: 1;
        font-size: 10px;
    }
    body:is(.os-mode-phone, .os-mode-tablet) .os-sm-search { padding: 2px 10px; margin-bottom: 6px; }
    body:is(.os-mode-phone, .os-mode-tablet) .os-sm-search input { min-height: 30px; }
    body:is(.os-mode-phone, .os-mode-tablet) .os-sm-section { margin-bottom: 8px; }
    body:is(.os-mode-phone, .os-mode-tablet) .os-sm-section-head { font-size: 11px; margin-bottom: 2px; }
    body:is(.os-mode-phone, .os-mode-tablet) .os-sm-cat { padding: 6px 8px; }
    body:is(.os-mode-phone, .os-mode-tablet) .os-sm-all li { min-height: 44px; }
    /* The footer is 41-69px of a 396px sheet. Keep the control, drop the caption. */
    body:is(.os-mode-phone, .os-mode-tablet) .os-sm-foot { padding: 6px 16px; margin-top: 4px; }

    /* ── Two columns ───────────────────────────────────────────────────────
       A phone on its side has 1000px of width and about 300px of usable
       height, and the sheet stacked five blocks down the short axis: search,
       Pinned, Folders, the app list and the footer. The list is last in that
       queue and there was nothing left by the time it was reached, measured at
       0 to 91px for 48 apps.

       So lay it across the long axis instead. Search spans the top, the footer
       spans the bottom, Pinned and Folders take a narrow left column, and the
       app list takes the whole right column at the full height between them.

       Placed by class, never by :nth-child, so re-ordering the markup cannot
       silently scramble it. Row 3 is the flexible one, and the list spans rows
       2 and 3 so it gets Pinned's height plus everything left over. */
    body:is(.os-mode-phone, .os-mode-tablet) .os-startmenu {
        display: grid;
        grid-template-columns: minmax(200px, 34%) 1fr;
        /*  1 search | 2 pinned | 3 folders + the rest | 4 editbar | 5 dropzones | 6 footer
            minmax(0, 1fr), not 1fr: a plain fr row keeps an automatic minimum
            the size of its content, so on the shortest screens the row refused
            to shrink and pushed the footer, and the power control with it, off
            the bottom of the sheet. */
        grid-template-rows: auto minmax(0, auto) minmax(0, 1fr) auto auto auto;
        column-gap: 16px;
        align-content: stretch;
    }
    body:is(.os-mode-phone, .os-mode-tablet) .os-sm-search {
        grid-column: 1 / -1;
        grid-row: 1;
    }
    /* Pinned: the section that is neither the folders one nor the last div. */
    body:is(.os-mode-phone, .os-mode-tablet)
        .os-sm-section:not(.os-sm-folders-section):not(:last-of-type) {
        grid-column: 1;
        grid-row: 2;
        /* stretch, not start: an item that keeps its content height when its
           row is squeezed simply overflows into the next one, which put the
           folders block on top of the footer at 844x390. Sized by the row, with
           its own scroller inside. */
        min-height: 0;
        overflow: hidden;
    }
    body:is(.os-mode-phone, .os-mode-tablet) .os-sm-folders-section {
        grid-column: 1;
        grid-row: 3;
        min-height: 0;
        overflow: hidden;
    }
    /* The left column's own scrollers, for when its row is shorter than its
       content rather than the other way round. */
    body:is(.os-mode-phone, .os-mode-tablet) .os-sm-pinned,
    body:is(.os-mode-phone, .os-mode-tablet) .os-sm-folders {
        overflow-y: auto;
        min-height: 0;
        scrollbar-width: none;
    }
    body:is(.os-mode-phone, .os-mode-tablet) .os-sm-pinned::-webkit-scrollbar,
    body:is(.os-mode-phone, .os-mode-tablet) .os-sm-folders::-webkit-scrollbar { display: none; }
    /* All apps: the last div child, which is what the base stylesheet also
       uses to find it. min-height:0 lets it shrink so its own list scrolls. */
    body:is(.os-mode-phone, .os-mode-tablet) .os-sm-section:last-of-type {
        grid-column: 2;
        grid-row: 2 / 4;
        min-height: 0;
        margin-bottom: 0;
        overflow: hidden;
    }
    /* The list is a flex child, and a flex child's automatic minimum is its
       content: without this it will not shrink, it just overflows whatever it
       is in. It is the scroller, so it is allowed to be smaller than its rows. */
    body:is(.os-mode-phone, .os-mode-tablet) .os-sm-all { min-height: 0; }
    /* Drilling into a folder hides the search and both left-column sections,
       so the folder view takes the whole middle band. */
    body:is(.os-mode-phone, .os-mode-tablet) .os-sm-folderview {
        grid-column: 1 / -1;
        grid-row: 2 / 4;
        min-height: 0;
    }
    body:is(.os-mode-phone, .os-mode-tablet) .os-sm-editbar   { grid-column: 1 / -1; grid-row: 4; }
    body:is(.os-mode-phone, .os-mode-tablet) .os-sm-dropzones { grid-column: 1 / -1; grid-row: 5; }
    body:is(.os-mode-phone, .os-mode-tablet) .os-sm-foot      { grid-column: 1 / -1; grid-row: 6; }
}

/* ── The shortest landscape phones ──────────────────────────────────────────
   Below about 380px of viewport height (a 667x375 phone on its side, less its
   home-indicator inset), a left column of labelled pinned tiles wraps to two
   rows and pushes the footer, and with it the power control, off the sheet.
   Drop the labels: the icons stay, the row halves, and the app list gets the
   difference. Measured: power control back inside the sheet, list 2 rows to 4. */
@media (max-height: 380px) {
    body:is(.os-mode-phone, .os-mode-tablet) .os-sm-pinned {
        grid-template-columns: repeat(6, 1fr);
        gap: 6px;
    }
    body:is(.os-mode-phone, .os-mode-tablet) .os-app-tile-name { display: none; }
    body:is(.os-mode-phone, .os-mode-tablet) .os-sm-tile { min-height: 40px; padding: 5px 2px; }
}

/* Flyouts: side drawer anchored to the bottom-right. ───────────────────
   Scoped to :not([hidden]) so the desktop's display:none default still
   applies when the flyout is closed. */
body.os-mode-tablet .os-flyout:not([hidden]) {
    right: max(env(safe-area-inset-right, 0px), 12px);
    left: auto;
    bottom: calc(72px + env(safe-area-inset-bottom, 0px));   /* bar + 8 */
    width: min(420px, calc(100vw - 24px));
    max-height: min(70vh, 70dvh);
    border-radius: 18px;
    display: flex;
    flex-direction: column;
}

/* Windows: fullscreen by default; tap maximize-toggle to split 50/50. ── */
body.os-mode-tablet .os-windows {
    position: fixed;
    inset: 0 0 calc(64px + env(safe-area-inset-bottom, 0px)) 0;
    pointer-events: none;
}
body.os-mode-tablet .os-win {
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    border-radius: 0;
    border: none;
    pointer-events: auto;
}
/* Allow .os-win.tiled-l / .tiled-r if WM ever sets them — kept generic so a
   future tile gesture works without further CSS. Default tablet has only one
   visible window at a time. */
body.os-mode-tablet .os-win.tiled-l {
    left: 0;
    width: 50%;
}
body.os-mode-tablet .os-win.tiled-r {
    left: 50%;
    width: 50%;
}
body.os-mode-tablet .os-win-chrome { min-height: 44px; }
body.os-mode-tablet .os-win-btn { min-width: 44px; min-height: 44px; }
/* No drag on tablet either. Minimize stays — sends to background; Maximize
   button hidden because windows are already fullscreen. */
body.os-mode-tablet .os-win-btn[data-win-max] { display: none; }
body.os-mode-tablet .os-win-resize { display: none !important; }
body.os-mode-tablet .os-snap-overlay,
body.os-mode-tablet .os-altswitch { display: none !important; }

/* ╔══════════════════════════════════════════════════════════════════╗
   ║  Phase 4 — App switcher + gesture polish                         ║
   ║  Phone/tablet only. Long-press Start opens a card stack; tap a   ║
   ║  card to focus; swipe-left/right to close. Bottom sheets accept  ║
   ║  swipe-down on the drag handle to dismiss.                       ║
   ╚══════════════════════════════════════════════════════════════════╝ */

.os-switcher { display: none; }
body.os-mode-phone .os-switcher,
body.os-mode-tablet .os-switcher {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 90;
    pointer-events: none;
}
.os-switcher[hidden] { display: none !important; }
body.os-mode-phone .os-switcher:not([hidden]),
body.os-mode-tablet .os-switcher:not([hidden]) { pointer-events: auto; }

.os-switcher-backdrop {
    position: absolute;
    inset: 0;
    background: rgba(0, 0, 0, 0.42);
    backdrop-filter: blur(14px) saturate(120%);
    -webkit-backdrop-filter: blur(14px) saturate(120%);
    opacity: 0;
    transition: opacity 160ms ease;
}
.os-switcher.open .os-switcher-backdrop { opacity: 1; }

.os-switcher-stack {
    position: absolute;
    inset: 0;
    overflow-y: auto;
    overflow-x: hidden;
    -webkit-overflow-scrolling: touch;
    padding:
        calc(env(safe-area-inset-top, 0px) + 32px)
        max(env(safe-area-inset-right, 0px), 16px)
        calc(96px + env(safe-area-inset-bottom, 0px))
        max(env(safe-area-inset-left, 0px), 16px);
    display: flex;
    flex-direction: column;
    gap: 14px;
    align-items: center;
}
.os-switcher-card {
    width: min(520px, 100%);
    min-height: 140px;
    border-radius: 18px;
    background: var(--os-surface);
    border: 1px solid var(--os-surface-border);
    box-shadow: 0 10px 28px rgba(0, 0, 0, 0.32);
    color: var(--os-fg);
    padding: 14px 16px;
    display: flex;
    flex-direction: column;
    gap: 10px;
    cursor: pointer;
    user-select: none;
    touch-action: pan-y;
    transform: translateX(0);
    transition: transform 220ms cubic-bezier(.2,.7,.2,1), opacity 220ms ease;
    opacity: 1;
}
.os-switcher-card.dragging {
    transition: none;
}
.os-switcher-card.removing {
    opacity: 0;
    transform: translateX(120%);
    pointer-events: none;
}
.os-switcher-card-head {
    display: flex;
    align-items: center;
    gap: 10px;
    font-weight: 600;
    font-size: 15px;
}
.os-switcher-card-icon {
    width: 28px; height: 28px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 8px;
    background: rgba(255,255,255,0.08);
    flex: 0 0 auto;
}
.os-switcher-card-icon img,
.os-switcher-card-icon svg { width: 22px; height: 22px; }
.os-switcher-card-meta { color: var(--os-fg-muted); font-size: 12px; font-weight: 400; }
.os-switcher-card-body {
    flex: 1;
    border-radius: 10px;
    background: rgba(255,255,255,0.04);
    color: var(--os-fg-muted);
    padding: 12px;
    font-size: 12px;
    line-height: 1.5;
}
[data-theme="light"] .os-switcher-card-body { background: rgba(0,0,0,0.04); }
[data-theme="light"] .os-switcher-card-icon { background: rgba(0,0,0,0.06); }
.os-switcher-card-close {
    align-self: flex-end;
    min-height: 36px;
    min-width: 64px;
    background: transparent;
    border: 1px solid var(--os-surface-border);
    color: var(--os-fg);
    border-radius: 10px;
    font: inherit;
    cursor: pointer;
    padding: 0 12px;
}
.os-switcher-card-close:hover { background: rgba(255,255,255,0.06); }
[data-theme="light"] .os-switcher-card-close:hover { background: rgba(0,0,0,0.06); }
.os-switcher-empty {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    margin: 0;
    color: rgba(255,255,255,0.7);
    font-size: 15px;
}

/* The drag handle on bottom sheets is purely visual today; Phase 4 wires it
   to a swipe-down-to-dismiss gesture. We highlight it on active touch. */
body.os-mode-phone .os-flyout {
    /* y-translate driven by JS during swipe; default identity. */
    transform: translateY(var(--os-sheet-dy, 0));
    transition: transform 220ms cubic-bezier(.2,.7,.2,1);
}
body.os-mode-phone .os-flyout.os-sheet-dragging {
    transition: none;
}

/* ╔══════════════════════════════════════════════════════════════════╗
   ║  Fix-pass: phone tray collapses to clock + system popover       ║
   ║                                                                  ║
   ║  Desktop & tablet are unaffected — the popover is display:none   ║
   ║  unless body.os-mode-phone is set, and the tray-button hides     ║
   ║  only apply in phone mode.                                       ║
   ╚══════════════════════════════════════════════════════════════════╝ */

/* On phone, the tray is a single visible control: the clock button.
   All other tray buttons are hidden by default — they're surfaced via the
   tray popover, or via the actions inside the existing flyouts. */
body.os-mode-phone .os-tb-tray {
    /* Override the desktop absolute-right positioning so the tray no longer
       overlaps the pins area. Drop into the inline flow at the right end. */
    position: static;
    margin-left: auto;
    gap: 0;
    padding: 0;
}
body.os-mode-phone [data-os-temp],
body.os-mode-phone [data-os-bell],
body.os-mode-phone [data-os-home],
body.os-mode-phone [data-os-theme] {
    display: none !important;
}
body.os-mode-phone [data-os-clock-btn] {
    min-height: 48px;
    padding: 0 14px;
    border-radius: 14px;
    background: rgba(255,255,255,0.06);
    border: 1px solid var(--os-surface-border);
}
body.os-mode-phone [data-os-clock-btn]:hover { background: rgba(255,255,255,0.12); }
body.os-mode-phone[data-theme="light"] [data-os-clock-btn] { background: rgba(0,0,0,0.04); }
body.os-mode-phone[data-theme="light"] [data-os-clock-btn]:hover { background: rgba(0,0,0,0.08); }

/* Bring the clock label back: the earlier phone rule masked it. */
body.os-mode-phone [data-os-clock-btn]::before { display: none; }
body.os-mode-phone [data-os-clock-btn] .os-clock-time {
    display: inline-block;
    font-size: 14px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}
body.os-mode-phone [data-os-clock-btn] .os-clock-date {
    display: none;
}
/* Notification dot on the collapsed clock when there are pending notifs. */
body.os-mode-phone [data-os-clock-btn].has-notifs::after {
    content: "";
    position: absolute;
    top: 4px;
    right: 4px;
    width: 8px; height: 8px;
    background: var(--os-accent);
    border-radius: 50%;
}

/* Pins area gets the remaining flex room and scrolls. */
body.os-mode-phone .os-tb-pins {
    flex: 1 1 0;
    min-width: 0;
}

/* System popover (phone only) ─────────────────────────────────────── */
.os-tray-popover {
    /* Off by default everywhere; phone mode promotes to fixed flex. */
    display: none;
}
body.os-mode-phone .os-tray-popover:not([hidden]) {
    display: flex;
    flex-direction: column;
    position: fixed;
    right: max(env(safe-area-inset-right, 0px), 8px);
    bottom: calc(60px + env(safe-area-inset-bottom, 0px) + 8px);
    min-width: 200px;
    background: var(--os-surface);
    backdrop-filter: blur(24px) saturate(160%);
    -webkit-backdrop-filter: blur(24px) saturate(160%);
    border: 1px solid var(--os-surface-border);
    border-radius: 14px;
    box-shadow: 0 12px 28px rgba(0,0,0,0.32);
    z-index: 25;
    padding: 6px;
    gap: 2px;
}
.os-tray-popover-item {
    appearance: none;
    background: transparent;
    border: 0;
    color: var(--os-fg);
    font: inherit;
    font-size: 14px;
    text-align: left;
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 0 12px;
    min-height: 44px;
    border-radius: 10px;
    cursor: pointer;
    position: relative;
}
.os-tray-popover-item:hover { background: rgba(255,255,255,0.08); }
[data-theme="light"] .os-tray-popover-item:hover { background: rgba(0,0,0,0.05); }
.os-tray-popover-item svg { flex: 0 0 auto; }
.os-tray-popover-item span { flex: 1; }
.os-tray-popover-badge {
    background: var(--os-accent);
    color: #fff;
    font-size: 11px;
    padding: 2px 6px;
    border-radius: 8px;
    font-weight: 600;
    flex: 0 0 auto !important;
}

/* The earlier dock CSS pushed flyouts up by 84px to clear the floating pill.
   The dock is now flush and ~60px tall — bring sheets closer. */
body.os-mode-phone .os-flyout:not([hidden]) {
    bottom: calc(60px + env(safe-area-inset-bottom, 0px) + 8px);
}
body.os-mode-phone .os-win {
    padding-bottom: calc(60px + env(safe-area-inset-bottom, 0px));
}

/* OS context menu ─────────────────────────────────────────────────── */
.os-ctxmenu {
    position: fixed;
    min-width: 220px;
    max-width: 320px;
    background: var(--os-surface);
    backdrop-filter: blur(24px) saturate(160%);
    -webkit-backdrop-filter: blur(24px) saturate(160%);
    border: 1px solid var(--os-surface-border);
    border-radius: 12px;
    box-shadow: 0 14px 36px rgba(0,0,0,0.42);
    padding: 6px;
    z-index: 9000;
    display: flex;
    flex-direction: column;
    gap: 2px;
    color: var(--os-fg);
    font: inherit;
    font-size: 13px;
    user-select: none;
    animation: os-ctxmenu-in 80ms ease-out;
}
@keyframes os-ctxmenu-in {
    from { opacity: 0; transform: translateY(-4px) scale(0.98); }
    to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
    .os-ctxmenu { animation: none; }
}
.os-ctxmenu-sub { z-index: 9100; }
.os-ctxmenu-sep {
    border: 0;
    border-top: 1px solid var(--os-surface-border);
    margin: 4px 4px;
    opacity: 0.6;
}
.os-ctxmenu-item {
    appearance: none;
    background: transparent;
    border: 0;
    color: inherit;
    font: inherit;
    font-size: 13px;
    text-align: left;
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 0 10px;
    min-height: 34px;
    border-radius: 8px;
    cursor: pointer;
    position: relative;
    line-height: 1.2;
}
.os-ctxmenu-item:hover:not([disabled]),
.os-ctxmenu-item:focus-visible {
    background: rgba(255,255,255,0.10);
    outline: none;
}
[data-theme="light"] .os-ctxmenu-item:hover:not([disabled]),
[data-theme="light"] .os-ctxmenu-item:focus-visible {
    background: rgba(0,0,0,0.06);
}
.os-ctxmenu-item[disabled] { opacity: 0.42; cursor: default; }
.os-ctxmenu-item.danger    { color: #ff6b6b; }
.os-ctxmenu-item.checked .os-ctxmenu-check { color: var(--os-accent); }
.os-ctxmenu-lead {
    flex: 0 0 18px;
    width: 18px;
    height: 18px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: inherit;
}
.os-ctxmenu-lead svg { width: 14px; height: 14px; }
.os-ctxmenu-check { font-size: 13px; }
.os-ctxmenu-label    { flex: 1 1 auto; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.os-ctxmenu-shortcut { flex: 0 0 auto; opacity: 0.55; font-size: 11px; margin-left: 12px; }
.os-ctxmenu-chevron  { flex: 0 0 auto; opacity: 0.55; font-size: 14px; margin-left: 6px; }

/* Bottom-sheet variant — phone (and any narrow viewport in Phase 5). */
.os-ctxmenu.os-ctxmenu-sheet {
    position: fixed;
    left: 8px;
    right: 8px;
    bottom: calc(8px + env(safe-area-inset-bottom, 0px));
    top: auto;
    max-width: none;
    min-width: 0;
    border-radius: 16px;
    padding: 8px;
    animation: os-ctxmenu-sheet-in 160ms ease-out;
}
.os-ctxmenu.os-ctxmenu-sheet .os-ctxmenu-item { min-height: 48px; font-size: 15px; }
.os-ctxmenu.os-ctxmenu-sheet .os-ctxmenu-lead { flex-basis: 24px; width: 24px; height: 24px; }
.os-ctxmenu.os-ctxmenu-sheet .os-ctxmenu-lead svg { width: 18px; height: 18px; }
@keyframes os-ctxmenu-sheet-in {
    from { opacity: 0; transform: translateY(16px); }
    to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
    .os-ctxmenu.os-ctxmenu-sheet { animation: none; }
}
.os-ctxmenu-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(0,0,0,0.32);
    z-index: 8990;
    animation: os-ctxmenu-fade 120ms ease-out;
}
@keyframes os-ctxmenu-fade {
    from { opacity: 0; }
    to   { opacity: 1; }
}

/* Block the native menu on the desktop background, taskbar, start menu
   and window chrome — keeps the OS feeling integrated. Inputs and iframes
   keep the native menu (ctxmenu.js skips registered surfaces for them). */
body.os-shell #os-desktop,
body.os-shell #os-taskbar,
body.os-shell #os-startmenu,
body.os-shell #os-windows .os-win-titlebar { -webkit-touch-callout: none; }

/* Stacked sheet submenu — Phase 5. Sits above the parent sheet so the back
   item brings the user one level back without dismissing the whole menu. */
.os-ctxmenu.os-ctxmenu-sheet-stacked {
    z-index: 9050;
    animation: os-ctxmenu-sheet-slide-in 200ms ease-out;
}
@keyframes os-ctxmenu-sheet-slide-in {
    from { opacity: 0; transform: translateX(24px); }
    to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
    .os-ctxmenu.os-ctxmenu-sheet-stacked { animation: none; }
}

/* When the sheet is open, hide submenu chevrons since the stacked sheet is the
   navigation affordance. Keeps the layout cleaner on touch. */
.os-ctxmenu.os-ctxmenu-sheet .os-ctxmenu-chevron { font-size: 18px; opacity: 0.7; }

/* ── Desktop shortcuts ─────────────────────────────────────────────
   Snap grid on desktop (≥1024px). Paged variant for tablet/phone is
   appended in Phase 4 (CSS just below this block, scoped by body.os-mode-*). */
.os-desktop-icons {
    position: absolute;
    inset: 0;
    padding: 16px;
    display: grid;
    /* Fixed-width cells so stored {col,row} positions stay visually stable
       across renders. Phone/tablet (`.paged`) overrides below. */
    grid-template-columns: repeat(auto-fill, 96px);
    grid-auto-rows: 88px;
    gap: 12px;
    overflow-y: auto;
    overflow-x: hidden;
    align-content: start;
}
.os-desktop-icons.paged { grid-template-columns: none; grid-auto-rows: auto; align-content: stretch; padding: 0; }

.os-desktop-shortcut {
    appearance: none;
    background: transparent;
    border: 0;
    color: inherit;
    font: inherit;
    padding: 6px 4px;
    border-radius: 10px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: flex-start;
    gap: 6px;
    cursor: pointer;
    transition: background 160ms ease, transform 160ms ease;
    text-align: center;
    width: 100%;
    /* Required so touch pointermove on a tile delivers events to JS for the
       long-press drag instead of being consumed by the scroller. */
    touch-action: none;
}
/* Snap-cell hint shown under the pointer while dragging in desktop mode. */
.os-desktop-drop-cell {
    position: absolute;
    pointer-events: none;
    border-radius: 10px;
    background: rgba(0,120,212,0.18);
    box-shadow: inset 0 0 0 2px var(--os-accent);
    transition: left 80ms ease, top 80ms ease;
    z-index: 7999;
}
.os-desktop-shortcut:hover { background: rgba(255,255,255,0.10); }
[data-theme="light"] .os-desktop-shortcut:hover { background: rgba(0,0,0,0.06); }
.os-desktop-shortcut:focus-visible { outline: 2px solid var(--os-accent); outline-offset: 2px; }
.os-desktop-shortcut.selected { background: rgba(0,120,212,0.22); box-shadow: inset 0 0 0 2px var(--os-accent); }
.os-desktop-shortcut.stale { opacity: 0.55; }

.os-desktop-icon {
    width: 48px; height: 48px;
    display: inline-flex; align-items: center; justify-content: center;
    color: #fff;
}
.os-desktop-icon svg, .os-desktop-icon img { width: 40px; height: 40px; }

.os-desktop-label {
    font-size: 12px;
    line-height: 1.25;
    color: #fff;
    text-shadow: 0 1px 2px rgba(0,0,0,0.55);
    background: rgba(0,0,0,0.22);
    -webkit-backdrop-filter: blur(2px);
    backdrop-filter: blur(2px);
    padding: 1px 6px;
    border-radius: 6px;
    max-width: 100%;
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    overflow: hidden;
    text-overflow: ellipsis;
    word-break: break-word;
}
[data-theme="light"] .os-desktop-label { background: rgba(0,0,0,0.36); }

.os-desktop-empty {
    grid-column: 1 / -1;
    align-self: center;
    text-align: center;
    color: #fff;
    opacity: 0.72;
    text-shadow: 0 1px 2px rgba(0,0,0,0.65);
    font-size: 13px;
    margin: 0;
}

@media (prefers-reduced-motion: reduce) {
    .os-desktop-shortcut { transition: none; }
}

/* Paged variant — tablet & phone (`body.os-mode-tablet`, `body.os-mode-phone`).
   Horizontal scroll-snap carousel with one grid per page. */
body.os-mode-tablet .os-desktop-icons.paged,
body.os-mode-phone  .os-desktop-icons.paged {
    display: flex;
    overflow-x: auto;
    overflow-y: hidden;
    scroll-snap-type: x mandatory;
    scroll-behavior: smooth;
    /* Reserve room at the bottom for the pager dots strip. */
    padding-bottom: 28px;
}
body.os-mode-tablet .os-desktop-page,
body.os-mode-phone  .os-desktop-page {
    flex: 0 0 100%;
    scroll-snap-align: center;
    scroll-snap-stop: always;
    display: grid;
    padding: 18px 18px 8px;
    gap: 14px 8px;
    align-content: start;
}
/* Phone: 4 cols × 5 rows = 20 tiles per page */
body.os-mode-phone .os-desktop-page  { grid-template-columns: repeat(4, 1fr); grid-auto-rows: 84px; }
/* Tablet portrait: 5×4 = 20; landscape: 6×4 = 24 (orientation handled by media query below) */
body.os-mode-tablet .os-desktop-page { grid-template-columns: repeat(5, 1fr); grid-auto-rows: 92px; }
@media (orientation: landscape) {
    body.os-mode-tablet .os-desktop-page { grid-template-columns: repeat(6, 1fr); }
}

/* Pager strip — dots above the taskbar */
.os-desktop-pager {
    position: absolute;
    left: 0; right: 0;
    bottom: 8px;
    display: flex;
    justify-content: center;
    align-items: center;
    gap: 8px;
    pointer-events: none; /* dots opt back in below */
}
body.os-mode-desktop .os-desktop-pager { display: none; }

.os-desktop-pager-dot {
    pointer-events: auto;
    width: 6px; height: 6px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: rgba(255,255,255,0.45);
    cursor: pointer;
    transition: background 160ms ease, transform 160ms ease, width 160ms ease, height 160ms ease;
}
.os-desktop-pager-dot:hover { background: rgba(255,255,255,0.75); }
.os-desktop-pager-dot.active {
    width: 8px; height: 8px;
    background: var(--os-accent);
}
[data-theme="light"] .os-desktop-pager-dot { background: rgba(0,0,0,0.32); }
[data-theme="light"] .os-desktop-pager-dot:hover { background: rgba(0,0,0,0.55); }
@media (prefers-reduced-motion: reduce) {
    body.os-mode-tablet .os-desktop-icons.paged,
    body.os-mode-phone  .os-desktop-icons.paged { scroll-behavior: auto; }
    .os-desktop-pager-dot { transition: none; }
}

/* Paged tile gets a bit more room on touch — labels widen */
body.os-mode-tablet .os-desktop-shortcut .os-desktop-label,
body.os-mode-phone  .os-desktop-shortcut .os-desktop-label { font-size: 11.5px; }

/* Desktop drag & drop visuals */
body.os-desktop-dragging                       { cursor: grabbing; user-select: none; -webkit-user-select: none; }
body.os-desktop-dragging .os-desktop-shortcut  { transition: transform 160ms ease, opacity 120ms ease; }
.os-desktop-shortcut-dragging                  { opacity: 0.18; }
.os-desktop-shortcut-ghost                     { opacity: 0.88; transform: scale(1.04); box-shadow: 0 12px 28px rgba(0,0,0,0.45); border-radius: 12px; background: rgba(255,255,255,0.06); -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px); }
[data-theme="light"] .os-desktop-shortcut-ghost { background: rgba(0,0,0,0.04); }
.os-desktop-shortcut.os-desktop-drop-before { box-shadow: inset 3px 0 0 var(--os-accent); }
.os-desktop-shortcut.os-desktop-drop-after  { box-shadow: inset -3px 0 0 var(--os-accent); }

@media (prefers-reduced-motion: reduce) {
    body.os-desktop-dragging .os-desktop-shortcut { transition: none; }
}

/* Inline assistant — Phase 4. Positions the assistant UI as a native part
   of the desktop. The aside wrapper is pointer-events:none full-rect; only
   the actual visible UI children opt back into pointer-events:auto. This is
   the fix for the iframe-overlay tile-blocking bug captured in
   2026-05-14-desktop-drag-drop.md. */
#os-assistant {
    position: fixed;
    left: 0;
    bottom: var(--os-tb-height);
    width: 750px;
    height: min(820px, calc(100vh - var(--os-tb-height) - 16px));
    max-width: 100vw;
    /* Sits above the taskbar (z:10) so its drawer paints on top of it.
       Sits below windows (z:5+ but they're a sibling stacking context
       via .os-windows wrapper). */
    z-index: 11;
    pointer-events: none;   /* wrapper does not catch clicks */
}
/* Lift the bottom-stack above the taskbar so the drawer isn't hidden behind it. */
#os-assistant #bottom-stack {
    bottom: var(--os-tb-height) !important;
}
/* On phone/tablet the dock is taller than --os-tb-height (48px) and grows once
   Start/pins/tray lay out (plus the home-indicator safe-area on devices), so
   the drawer must rest on the dock's *measured* height — --os-dock-h, published
   live by shell.js — not the desktop bar height. Without this the drawer
   overlaps the dock by the 48px→dock-height difference. */
body.os-mode-phone #os-assistant #bottom-stack,
body.os-mode-tablet #os-assistant #bottom-stack {
    bottom: var(--os-dock-h, calc(60px + env(safe-area-inset-bottom, 0px))) !important;
}
#os-assistant.obstructed { display: none; }
/* Re-enable pointer events on the actual interactive children so users can
   click the visualizer, drawer, mic button. Empty space passes through to
   the desktop. The settings-panel and eq-panel are intentionally NOT in
   this list — voice.css manages their pointer-events via .open / [hidden]
   so they only catch clicks when actually visible. */
#os-assistant #bottom-stack,
#os-assistant header { pointer-events: auto; }
/* The visualizer is a large transparent canvas (472x200) anchored bottom-left.
   With pointer-events:auto it swallows touch-drags/scroll on app windows that
   sit under it (e.g. ForgeInbox on phone). Keep it purely visual — click-through.
   The drawer still opens via the "▸ text" toggle in the drawer bar. */
#os-assistant #visualizer { pointer-events: none !important; }

/* Override voice.php's body-keyed rules where they need to apply to the aside
   instead. voice.php uses `body.voice-mode-os-dock` selectors; we set that
   class on #os-assistant directly, so most rules already match via
   `.voice-mode-os-dock .descendant`. The exceptions are the few selectors
   that target `body.voice-mode-os-dock` itself — restated here against
   `#os-assistant`. */
#os-assistant {
    background: transparent;
    color: var(--os-fg, #f3f3f3);
}
/* ── App Categorisation Engine: category edit mode (PRD §6.2) ────────────
   Only active while #os-startmenu carries .cat-editing. */

.os-sm-editbar {
    display: none;
    align-items: center;
    gap: 8px;
    padding: 6px 8px;
    margin-bottom: 6px;
    border-radius: var(--os-radius-sm);
    background: rgba(0, 120, 212, 0.14);
    border: 1px solid rgba(0, 120, 212, 0.35);
    font-size: 11px;
}
#os-startmenu.cat-editing .os-sm-editbar { display: flex; }
.os-sm-editbar-status { flex: 1; min-width: 0; color: var(--os-fg); }
.os-sm-editbar button {
    appearance: none;
    border: 1px solid rgba(255, 255, 255, 0.18);
    background: transparent;
    color: var(--os-fg);
    font: inherit;
    font-size: 11px;
    padding: 3px 9px;
    border-radius: 6px;
    cursor: pointer;
}
.os-sm-editbar button:hover:not(:disabled) { background: rgba(255, 255, 255, 0.10); }
.os-sm-editbar button:disabled { opacity: 0.4; cursor: default; }
.os-sm-editbar button[data-cat-done] { background: var(--os-accent, #0078d4); border-color: transparent; color: #fff; }

/* Rows become grabbable and show provenance. */
#os-startmenu.cat-editing .os-sm-row { cursor: grab; }
#os-startmenu.cat-editing .os-sm-row:active { cursor: grabbing; }
.os-sm-row-pin {
    margin-left: auto;
    flex-shrink: 0;
    font-size: 10px;
    opacity: 0.75;
    display: none;
}
#os-startmenu.cat-editing .os-sm-row.anchored .os-sm-row-pin { display: inline; }

/* Drop targets. */
#os-startmenu.cat-editing .os-sm-cat { border-radius: var(--os-radius-sm); }
#os-startmenu.cat-editing .os-sm-cat.drop-target {
    outline: 2px dashed var(--os-accent, #0078d4);
    outline-offset: -2px;
    background: rgba(0, 120, 212, 0.10);
}
.os-sm-row.os-sm-row-dragging { opacity: 0.35; }
.os-sm-row-ghost {
    box-shadow: 0 8px 22px rgba(0, 0, 0, 0.45);
    background: var(--os-surface, #202020);
    border-radius: var(--os-radius-sm);
    opacity: 0.95;
}

/* The "new category" and "reset to rule" drop zones only exist in edit mode. */
.os-sm-dropzones { display: none; gap: 6px; margin: 6px 0 2px; }
#os-startmenu.cat-editing .os-sm-dropzones { display: flex; }
.os-sm-dropzone {
    flex: 1;
    text-align: center;
    padding: 7px 6px;
    font-size: 10px;
    border: 1px dashed rgba(255, 255, 255, 0.25);
    border-radius: var(--os-radius-sm);
    color: var(--os-fg-muted);
}
.os-sm-dropzone.drop-target {
    border-color: var(--os-accent, #0078d4);
    background: rgba(0, 120, 212, 0.14);
    color: var(--os-fg);
}

:root[data-theme="light"] .os-sm-editbar button { border-color: rgba(0, 0, 0, 0.18); }
:root[data-theme="light"] .os-sm-dropzone { border-color: rgba(0, 0, 0, 0.22); }
:root[data-theme="light"] .os-sm-row-ghost { background: #fff; }

/* Edit-categories affordance in the "All apps" heading. Scoped to that heading
   only: .os-sm-section-head is shared with "Pinned", which must not change. */
.os-sm-section-head[data-os-sm-all-head] { display: flex; align-items: center; gap: 6px; }
.os-sm-edit-cats {
    appearance: none;
    margin-left: auto;
    background: transparent;
    border: 0;
    color: inherit;
    opacity: 0.55;
    cursor: pointer;
    font-size: 12px;
    line-height: 1;
    padding: 2px 4px;
    border-radius: 4px;
}
.os-sm-edit-cats:hover { opacity: 1; background: rgba(255, 255, 255, 0.10); }
#os-startmenu.cat-editing .os-sm-edit-cats { opacity: 1; color: var(--os-accent, #0078d4); }
:root[data-theme="light"] .os-sm-edit-cats:hover { background: rgba(0, 0, 0, 0.07); }

/* ── App Categorisation Engine: management pane (PRD §6.4) ─────────────────
   Scoped tokens rather than rgba() guesses. The pane sits on its own opaque
   surface, so every colour is stated per theme instead of relying on whatever
   happens to be behind it. Light values follow the usual accessible ramp
   (#0f172a text, #475569 muted, #e2e8f0 borders) so contrast clears 4.5:1.

   NOTE: use div, never <header>/<footer>, inside this pane. apps/assistant/
   voice.css styles bare `header` globally and it is loaded into /os/, which
   silently painted this dialog's title bar near-black. */
.os-catman {
    --cm-bg:         #1b1b1b;
    --cm-fg:         #f3f3f3;
    --cm-muted:      rgba(243, 243, 243, 0.72);
    --cm-line:       rgba(255, 255, 255, 0.14);
    --cm-col:        rgba(255, 255, 255, 0.05);
    --cm-card:       rgba(255, 255, 255, 0.10);
    --cm-card-hover: rgba(255, 255, 255, 0.18);
    --cm-field:      rgba(255, 255, 255, 0.08);
    --cm-danger:     #ff7b72;

    position: fixed; inset: 0; z-index: 9000;
    display: flex; align-items: center; justify-content: center;
}
:root[data-theme="light"] .os-catman {
    --cm-bg:         #ffffff;
    --cm-fg:         #0f172a;
    --cm-muted:      #475569;
    --cm-line:       #e2e8f0;
    --cm-col:        #f1f5f9;
    --cm-card:       #ffffff;
    --cm-card-hover: #e2e8f0;
    --cm-field:      #ffffff;
    --cm-danger:     #b91c1c;
}
.os-catman[hidden] { display: none; }
.os-catman-backdrop { position: absolute; inset: 0; background: rgba(0, 0, 0, 0.55); }

.os-catman-panel {
    position: relative;
    width: min(920px, 92vw);
    height: min(640px, 86vh);
    display: flex; flex-direction: column;
    background: var(--cm-bg);
    color: var(--cm-fg);
    border: 1px solid var(--cm-line);
    border-radius: 12px;
    box-shadow: 0 24px 60px rgba(0, 0, 0, 0.45);
    overflow: hidden;
}

.os-catman-head {
    display: flex; align-items: center;
    padding: 12px 14px;
    background: var(--cm-bg);
    color: var(--cm-fg);
    border-bottom: 1px solid var(--cm-line);
}
.os-catman-head h2 { margin: 0; font-size: 14px; font-weight: 600; flex: 1; color: var(--cm-fg); }
.os-catman-x {
    appearance: none; background: transparent; border: 0;
    color: var(--cm-fg); font-size: 22px; line-height: 1; cursor: pointer;
    width: 32px; height: 32px; border-radius: 6px;
    display: inline-flex; align-items: center; justify-content: center;
}
.os-catman-x:hover { background: var(--cm-card-hover); }
.os-catman-x:focus-visible { outline: 2px solid var(--os-accent, #0078d4); outline-offset: 2px; }

.os-catman-tabs { display: flex; gap: 2px; padding: 8px 10px 0; border-bottom: 1px solid var(--cm-line); }
.os-catman-tabs button {
    appearance: none; background: transparent; border: 0;
    border-bottom: 2px solid transparent;
    color: var(--cm-muted); font: inherit; font-size: 12px;
    padding: 7px 12px; cursor: pointer; border-radius: 6px 6px 0 0;
    transition: color 160ms ease, background-color 160ms ease;
}
.os-catman-tabs button:hover { color: var(--cm-fg); background: var(--cm-col); }
.os-catman-tabs button[aria-selected="true"] { color: var(--cm-fg); border-bottom-color: var(--os-accent, #0078d4); }
.os-catman-tabs button:focus-visible { outline: 2px solid var(--os-accent, #0078d4); outline-offset: -2px; }

.os-catman-body { flex: 1; min-height: 0; overflow: auto; padding: 12px 14px; }
.os-catman-pane[hidden] { display: none; }

.os-catman-foot {
    display: flex; align-items: center; gap: 8px;
    padding: 10px 14px;
    border-top: 1px solid var(--cm-line);
    background: var(--cm-bg);
}
.os-catman-msg { flex: 1; font-size: 11px; color: var(--cm-muted); }
.os-catman-msg.bad { color: var(--cm-danger); font-weight: 600; }
.os-catman-foot button, .os-catman-add {
    appearance: none; background: transparent;
    border: 1px solid var(--cm-line);
    color: var(--cm-fg); font: inherit; font-size: 12px;
    padding: 6px 12px; border-radius: 6px; cursor: pointer;
    transition: background-color 160ms ease;
}
.os-catman-foot button:hover, .os-catman-add:hover { background: var(--cm-card-hover); }
.os-catman-foot button:focus-visible, .os-catman-add:focus-visible { outline: 2px solid var(--os-accent, #0078d4); outline-offset: 2px; }
.os-catman-foot button.danger { border-color: var(--cm-danger); color: var(--cm-danger); }

/* ── Board: wraps, never scrolls sideways ───────────────────────────────── */
.os-catman-board { display: flex; flex-wrap: wrap; gap: 10px; align-items: flex-start; }
.os-catman-col {
    flex: 1 1 200px; max-width: 240px;
    background: var(--cm-col);
    border: 1px solid transparent;
    border-radius: 8px; padding: 8px;
    transition: background-color 140ms ease, border-color 140ms ease;
}
.os-catman-col.drop-target { border-color: var(--os-accent, #0078d4); background: rgba(0, 120, 212, 0.12); }
.os-catman-col-head { font-size: 11px; font-weight: 700; margin-bottom: 8px; color: var(--cm-muted); letter-spacing: 0.03em; }
.os-catman-col-list { display: flex; flex-direction: column; gap: 5px; min-height: 34px; }
.os-catman-card {
    display: flex; align-items: center; gap: 6px;
    background: var(--cm-card);
    border: 1px solid var(--cm-line);
    color: var(--cm-fg);
    border-radius: 6px; padding: 7px 9px; font-size: 12px;
    cursor: grab; user-select: none;
    transition: background-color 140ms ease, border-color 140ms ease;
}
.os-catman-card:hover { background: var(--cm-card-hover); }
.os-catman-card:active { cursor: grabbing; }
.os-catman-card.os-catman-card-dragging { opacity: 0.35; }
.os-catman-card-ghost {
    background: var(--cm-card); color: var(--cm-fg);
    border: 1px solid var(--os-accent, #0078d4); border-radius: 6px;
    box-shadow: 0 10px 26px rgba(0, 0, 0, 0.4); opacity: 0.95;
}
.os-catman-pin { margin-left: auto; font-size: 10px; color: var(--os-accent, #0078d4); }
.os-catman-board-hint { font-size: 11px; color: var(--cm-muted); margin: 0 0 10px; }

/* ── Categories tab ─────────────────────────────────────────────────────── */
.os-catman-cats { display: flex; flex-direction: column; gap: 6px; }
.os-catman-cat { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.os-catman-cat input[type="text"] { flex: 1 1 160px; min-width: 0; }
.os-catman-cat input, .os-catman-cat select {
    background: var(--cm-field); border: 1px solid var(--cm-line);
    color: var(--cm-fg); border-radius: 6px; padding: 6px 8px; font: inherit; font-size: 12px;
}
.os-catman-cat input:focus-visible, .os-catman-cat select:focus-visible,
.os-catman-rule select:focus-visible { outline: 2px solid var(--os-accent, #0078d4); outline-offset: 1px; }
.os-catman-cat .c-sort { width: 74px; }
.os-catman-cat .c-flag { font-size: 11px; display: flex; align-items: center; gap: 5px; white-space: nowrap; color: var(--cm-fg); cursor: pointer; }
.os-catman-cat .c-del {
    appearance: none; background: transparent; border: 1px solid var(--cm-danger);
    color: var(--cm-danger); font: inherit; font-size: 11px;
    padding: 5px 10px; border-radius: 6px; cursor: pointer;
}
.os-catman-cat .c-del:disabled { opacity: 0.4; cursor: not-allowed; }
.os-catman-add { margin-top: 10px; }

/* ── Rules tab ──────────────────────────────────────────────────────────── */
.os-catman-hint { font-size: 11px; color: var(--cm-muted); margin: 0 0 10px; }
.os-catman-rules { list-style: decimal inside; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 5px; }
.os-catman-rule {
    display: flex; align-items: center; gap: 8px;
    background: var(--cm-col); border: 1px solid var(--cm-line);
    border-radius: 6px; padding: 6px 9px;
}
.os-catman-rule select {
    background: var(--cm-field); border: 1px solid var(--cm-line);
    color: var(--cm-fg); border-radius: 6px; padding: 4px 6px; font: inherit; font-size: 11px;
}
.os-catman-rule .r-preds { flex: 1; min-width: 0; font-size: 11px; color: var(--cm-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.os-catman-rule button {
    appearance: none; background: transparent; border: 1px solid var(--cm-line);
    color: var(--cm-fg); border-radius: 6px; width: 28px; height: 26px;
    font-size: 12px; cursor: pointer; padding: 0;
}
.os-catman-rule button:hover:not(:disabled) { background: var(--cm-card-hover); }
.os-catman-rule button:disabled { opacity: 0.35; cursor: not-allowed; }
.os-catman-rule .r-del { border-color: var(--cm-danger); color: var(--cm-danger); }

/* ── Preview tab ────────────────────────────────────────────────────────── */
.os-catman-preview { width: 100%; border-collapse: collapse; font-size: 12px; color: var(--cm-fg); }
.os-catman-preview th {
    text-align: left; font-size: 10px; text-transform: uppercase; letter-spacing: 0.05em;
    color: var(--cm-muted); padding: 6px; border-bottom: 1px solid var(--cm-line);
}
.os-catman-preview td { padding: 6px; border-bottom: 1px solid var(--cm-line); }
.os-catman-preview .why { font-size: 11px; color: var(--cm-muted); }
.os-catman-preview .why.via-override { color: var(--os-accent, #0078d4); font-weight: 600; }
.os-catman-preview .why.via-fallback { color: #b45309; font-weight: 600; }
:root[data-theme="dark"] .os-catman-preview .why.via-fallback { color: #e3b341; }

/* ── Desktop category folders (Phase 7) ─────────────────────────────────── */
.os-folder-icon {
    display: grid;
    grid-template-columns: 1fr 1fr;
    grid-template-rows: 1fr 1fr;
    gap: 2px;
    width: 100%; height: 100%;
    padding: 3px;
    border-radius: 8px;
    background: rgba(255, 255, 255, 0.14);
}
.os-folder-cell {
    display: flex; align-items: center; justify-content: center;
    background: linear-gradient(135deg, #4a8be6, #2563c4);
    color: #fff; border-radius: 3px;
    font-size: 7px; font-weight: 700; line-height: 1;
    overflow: hidden;
}
.os-folder-pop {
    position: fixed;
    left: 50%; top: 50%;
    transform: translate(-50%, -50%);
    z-index: 8500;
    width: min(460px, 88vw);
    max-height: 70vh;
    overflow: auto;
    background: var(--os-surface, #1b1b1b);
    color: var(--os-fg);
    border: 1px solid rgba(255, 255, 255, 0.12);
    border-radius: 12px;
    box-shadow: 0 20px 50px rgba(0, 0, 0, 0.5);
    padding: 12px;
}
.os-folder-pop-head { font-size: 12px; font-weight: 600; margin-bottom: 10px; }
.os-folder-pop-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; }
.os-folder-app {
    appearance: none; background: transparent; border: 0; color: inherit;
    display: flex; flex-direction: column; align-items: center; gap: 5px;
    padding: 8px 4px; border-radius: 8px; cursor: pointer; font: inherit; font-size: 11px;
    text-align: center;
}
.os-folder-app:hover { background: rgba(255, 255, 255, 0.08); }
.os-folder-app span:last-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; width: 100%; }
.os-folder-empty { grid-column: 1 / -1; font-size: 11px; opacity: 0.6; margin: 0; }
:root[data-theme="light"] .os-folder-pop { background: #fff; border-color: rgba(0,0,0,0.12); }
:root[data-theme="light"] .os-folder-icon { background: rgba(0, 0, 0, 0.10); }
:root[data-theme="light"] .os-folder-app:hover { background: rgba(0, 0, 0, 0.06); }

/* DataForge account flyout. Reuses .os-flyout chrome; only the inner rows are new. */
.os-tokens-flyout { min-width: 320px; max-width: 380px; }
.os-tokens-body { padding: 4px 0; max-height: 60vh; overflow-y: auto; }
.os-tokens-cred {
    padding: 10px 14px;
    border-bottom: 1px solid var(--os-surface-border);
}
.os-tokens-cred:last-child { border-bottom: 0; }
.os-tokens-cred-head { display: flex; align-items: center; gap: 8px; }
.os-tokens-label { font-size: 12px; font-weight: 600; }
.os-tokens-active {
    font-size: 9px; letter-spacing: 0.08em; padding: 1px 5px; border-radius: 3px;
    background: var(--os-accent); color: #fff;
}
.os-tokens-figure { font-size: 18px; font-variant-numeric: tabular-nums; margin-top: 2px; }
.os-tokens-sub  { font-size: 11px; opacity: 0.7; margin-top: 3px; line-height: 1.5; }
.os-tokens-acct { font-size: 10px; opacity: 0.5; margin-top: 4px; }
.os-tokens-err  { font-size: 13px; opacity: 0.6; }
.os-tokens-bar {
    margin-top: 5px; height: 4px; border-radius: 2px;
    background: rgba(255,255,255,0.12); overflow: hidden;
}
.os-tokens-bar > i { display: block; height: 100%; background: var(--os-accent); }
.os-tokens-stamp { padding: 6px 14px 2px; font-size: 10px; opacity: 0.45; }
.os-tokens-actions {
    display: flex; gap: 8px; padding: 10px 14px;
    border-top: 1px solid var(--os-surface-border);
}
.os-tokens-btn {
    flex: 1; text-align: center; text-decoration: none;
    font-size: 11px; padding: 6px 8px; border-radius: 6px;
    border: 1px solid var(--os-surface-border);
    color: inherit; background: rgba(255,255,255,0.06);
}
.os-tokens-btn:hover { background: rgba(255,255,255,0.12); }

.os-tokens-rowacts { display: flex; gap: 6px; margin-top: 8px; }
.os-tokens-connect { padding: 10px 14px; border-top: 1px solid var(--os-surface-border); }
.os-tokens-connect-form { margin-top: 8px; display: flex; flex-direction: column; gap: 6px; }
/* A light overlay, like .os-sm-search: NOT a dark fill. A heavy dark fill
   reads as a disabled control on the light theme, which is exactly what these
   are not. */
.os-tokens-connect-form input {
    font: inherit; font-size: 11px; padding: 7px 9px; border-radius: 6px;
    border: 1px solid var(--os-surface-border);
    background: rgba(255, 255, 255, 0.08);
    color: var(--os-fg);
}
.os-tokens-connect-form input::placeholder { color: var(--os-fg-muted); }
.os-tokens-connect-form input:focus {
    outline: none;
    border-color: var(--os-accent);
    background: rgba(255, 255, 255, 0.14);
}
:root[data-theme="light"] .os-tokens-connect-form input {
    background: #fff;
    border-color: rgba(0, 0, 0, 0.22);
}
:root[data-theme="light"] .os-tokens-connect-form input:focus {
    border-color: var(--os-accent);
    background: #fff;
}
.os-tokens-mini {
    font: inherit; font-size: 11px; padding: 4px 10px; border-radius: 6px;
    border: 1px solid var(--os-surface-border);
    background: rgba(255,255,255,0.06); color: inherit; cursor: pointer;
}
.os-tokens-mini:hover:not(:disabled) { background: rgba(255,255,255,0.14); }
.os-tokens-mini:disabled { opacity: 0.5; cursor: default; }
.os-tokens-mini.danger:hover:not(:disabled) { background: rgba(220,60,60,0.25); border-color: rgba(220,60,60,0.6); }
.os-tokens-msg { font-size: 11px; opacity: 0.75; }
.os-tokens-msg.bad { color: #ff8f8f; opacity: 1; }

.os-tokens-apps { padding: 10px 14px; border-top: 1px solid var(--os-surface-border); }
.os-tokens-apps-head { font-size: 10px; letter-spacing: 0.06em; opacity: 0.6; margin-bottom: 6px; text-transform: uppercase; }
.os-tokens-assign { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-top: 5px; font-size: 11px; }
.os-tokens-assign select {
    font: inherit; font-size: 11px; padding: 3px 6px; border-radius: 5px; max-width: 58%;
    border: 1px solid var(--os-surface-border);
    background: rgba(255, 255, 255, 0.08); color: var(--os-fg);
}
:root[data-theme="light"] .os-tokens-assign select {
    background: #fff; border-color: rgba(0, 0, 0, 0.22);
}

/* [hidden] has to beat the display rule on .os-tray-btn above: an author class
   selector outranks the UA's [hidden] { display: none }, so without this the
   attribute is inert and the button stays on screen. The same guard already
   exists for .os-flyout, .os-startmenu, .os-snap-overlay and .os-altswitch -
   I simply forgot it for the tray, and a box with no credential showed the
   icon anyway. */
.os-tray-btn[hidden],
.os-tray-popover-item[hidden] { display: none; }

.os-tray-tokens[data-status="signed-out"] .os-tokens-pill { opacity: 0.55; }

.os-tokens-mini.primary { background: var(--os-accent); border-color: var(--os-accent); color: #fff; font-weight: 600; }
.os-tokens-mini.primary:hover:not(:disabled) { filter: brightness(1.1); background: var(--os-accent); }
.os-tokens-link {
    appearance: none; background: none; border: 0; padding: 8px 0 0;
    color: var(--os-fg-muted); font: inherit; font-size: 11px;
    text-decoration: underline; cursor: pointer;
}
.os-tokens-link:hover { color: var(--os-fg); }

/* Same trap as .os-tray-btn: this rule sets display, so it outranks the UA's
   [hidden] and the collapsed form stayed on screen. Guard every element in this
   panel that is toggled by the attribute. */
.os-tokens-connect-form[hidden],
.os-tokens-msg[hidden],
.os-tokens-apps[hidden] { display: none; }

/* Ingestion model seats: same row shape as the account picker. The account
   sub-row only appears when a DataForge model is chosen and more than one
   account is connected. */
.os-tokens-seat span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.os-tokens-seat select { max-width: 56%; }
.os-tokens-seat select:disabled { opacity: 0.6; }
.os-tokens-seat-acct span { padding-left: 12px; opacity: 0.65; }
.os-tokens-seats-note { font-size: 10px; opacity: 0.55; margin-top: 7px; }
.os-tokens-seats-note.bad { color: #ff8f8f; opacity: 1; }

.os-tokens-chips { display: flex; gap: 5px; margin-top: 7px; }
.os-tokens-chip {
    font-size: 9px; letter-spacing: 0.05em; text-transform: uppercase;
    padding: 2px 6px; border-radius: 3px;
    border: 1px solid var(--os-surface-border); background: rgba(255,255,255,0.07);
}
.os-tokens-chip.off { opacity: 0.35; text-decoration: line-through; }
:root[data-theme="light"] .os-tokens-chip { background: rgba(0,0,0,0.05); }

/* ── Start menu folder shelf + drill-in view ─────────────────────────────── */
/* Wraps, no scrollbar. Folders are sized to their label rather than forced into
   fixed columns, so a curated handful packs onto one or two rows. Nothing is ever
   hidden off to the side, which a horizontal scroller does. */
.os-sm-folders {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}
.os-sm-folders > .os-sm-folder { flex: 0 1 auto; max-width: 100%; }

/* Edit mode is about rearranging the list, so the shelf steps aside there and
   gives its height back. */
#os-startmenu.cat-editing .os-sm-folders-section { display: none; }
.os-sm-folder {
    appearance: none;
    background: rgba(255, 255, 255, 0.05);
    border: 1px solid transparent;
    border-radius: var(--os-radius-sm);
    color: var(--os-fg);
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 7px 9px;
    cursor: pointer;
    font: inherit;
    font-size: 12px;
    text-align: left;
    min-width: 0;
}
.os-sm-folder:hover { background: rgba(255, 255, 255, 0.10); }
.os-sm-folder:focus-visible { outline: 2px solid var(--os-accent, #0078d4); outline-offset: -2px; }
.os-sm-folder .os-folder-icon { width: 30px; height: 30px; flex-shrink: 0; }
.os-sm-folder .os-folder-cell { font-size: 7px; }
.os-sm-folder-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.os-sm-folder-count {
    flex-shrink: 0;
    font-size: 10px;
    font-weight: 600;
    padding: 1px 6px;
    border-radius: 9px;
    background: rgba(255, 255, 255, 0.10);
    color: var(--os-fg-muted);
}

/* While a folder is open the rest of the menu steps aside. */
#os-startmenu.folder-open .os-sm-folders-section,
#os-startmenu.folder-open .os-sm-section,
#os-startmenu.folder-open .os-sm-editbar,
#os-startmenu.folder-open .os-sm-dropzones { display: none; }
#os-startmenu.folder-open .os-sm-search { display: none; }

.os-sm-folderview {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-height: 0;
}
.os-sm-folderview[hidden] { display: none; }

.os-sm-fv-back {
    appearance: none;
    background: transparent;
    border: 0;
    border-bottom: 1px solid rgba(255, 255, 255, 0.08);
    color: var(--os-fg);
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 6px;
    margin-bottom: 6px;
    cursor: pointer;
    font: inherit;
    font-size: 13px;
    font-weight: 600;
    text-align: left;
}
.os-sm-fv-back:hover { background: rgba(255, 255, 255, 0.06); }
.os-sm-fv-back span[aria-hidden] { font-size: 18px; line-height: 1; opacity: 0.7; }
.os-sm-fv-count {
    margin-left: auto;
    font-size: 11px;
    font-weight: 500;
    color: var(--os-fg-muted);
}

.os-sm-fv-list {
    list-style: none;
    margin: 0;
    padding: 0;
    overflow-y: auto;
    flex: 1;
    min-height: 0;
}
.os-sm-fv-row {
    appearance: none;
    width: 100%;
    background: transparent;
    border: 0;
    color: var(--os-fg);
    display: flex;
    align-items: flex-start;
    gap: 12px;
    padding: 9px 8px;
    border-radius: var(--os-radius-sm);
    cursor: pointer;
    font: inherit;
    text-align: left;
}
.os-sm-fv-row:hover { background: rgba(255, 255, 255, 0.08); }
.os-sm-fv-row:focus-visible { outline: 2px solid var(--os-accent, #0078d4); outline-offset: -2px; }

/* The bigger icon is the point of this view. */
.os-sm-fv-icon { flex-shrink: 0; }
.os-sm-fv-icon .os-app-tile-icon {
    width: 40px; height: 40px;
    border-radius: 9px;
    font-size: 15px;
    display: inline-flex; align-items: center; justify-content: center;
    background: linear-gradient(135deg, #4a8be6, #2563c4);
    color: #fff; font-weight: 700;
}
.os-sm-fv-icon .os-app-tile-icon img { width: 30px; height: 30px; }

.os-sm-fv-text { display: flex; flex-direction: column; gap: 3px; min-width: 0; flex: 1; }
.os-sm-fv-name { font-size: 13px; font-weight: 600; }
.os-sm-fv-desc {
    font-size: 11px;
    line-height: 1.45;
    color: var(--os-fg-muted);
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

:root[data-theme="light"] .os-sm-folder { background: rgba(0, 0, 0, 0.04); }
:root[data-theme="light"] .os-sm-folder:hover { background: rgba(0, 0, 0, 0.08); }
:root[data-theme="light"] .os-sm-folder-count { background: rgba(0, 0, 0, 0.08); }
:root[data-theme="light"] .os-sm-fv-back { border-bottom-color: rgba(0, 0, 0, 0.10); }
:root[data-theme="light"] .os-sm-fv-back:hover { background: rgba(0, 0, 0, 0.05); }
:root[data-theme="light"] .os-sm-fv-row:hover { background: rgba(0, 0, 0, 0.06); }

/* ── First-run Claude connection prompt ────────────────────────────────────
   Follows .os-catman: scoped tokens per theme rather than rgba() guesses over
   whatever happens to be behind the panel, and the same modal tier.

   NOTE, as on .os-catman: use div, never <header>/<footer>, inside this pane.
   apps/assistant/voice.css styles bare `header` globally and is loaded into
   /os/, which silently painted catman's title bar near-black. */
.os-claudeconn {
    --cc-bg:     #1b1b1b;
    --cc-fg:     #f3f3f3;
    --cc-muted:  rgba(243, 243, 243, 0.72);
    --cc-line:   rgba(255, 255, 255, 0.14);
    --cc-term:   #0d0d0d;
    --cc-accent: #58a6ff;

    position: fixed; inset: 0; z-index: 9000;
    display: flex; align-items: center; justify-content: center;
}
:root[data-theme="light"] .os-claudeconn {
    --cc-bg:     #ffffff;
    --cc-fg:     #0f172a;
    --cc-muted:  #475569;
    --cc-line:   #e2e8f0;
    --cc-term:   #0d0d0d;   /* the terminal stays dark in both themes */
    --cc-accent: #1f6feb;
}
.os-claudeconn[hidden] { display: none; }
.os-claudeconn-backdrop { position: absolute; inset: 0; background: rgba(0, 0, 0, 0.62); }

.os-claudeconn-panel {
    position: relative;
    width: min(500px, 94vw);
    height: min(600px, 88vh);
    display: flex; flex-direction: column;
    background: var(--cc-bg);
    color: var(--cc-fg);
    border: 1px solid var(--cc-line);
    border-radius: 12px;
    box-shadow: 0 24px 60px rgba(0, 0, 0, 0.45);
    overflow: hidden;
}

.os-claudeconn-head {
    display: flex; align-items: center; justify-content: space-between;
    gap: 12px; padding: 12px 14px;
    border-bottom: 1px solid var(--cc-line);
    flex: none;
}
.os-claudeconn-head h2 { margin: 0; font-size: 15px; font-weight: 600; }
.os-claudeconn-x {
    background: none; border: 0; cursor: pointer;
    color: var(--cc-muted); font-size: 20px; line-height: 1;
    padding: 0 4px; border-radius: 6px;
}
.os-claudeconn-x:hover { color: var(--cc-fg); }

.os-claudeconn-intro {
    flex: none; padding: 10px 14px;
    font-size: 12.5px; line-height: 1.45; color: var(--cc-muted);
}

/* Definite height, not height:100% inside an auto-height parent. The Assistant's
   drawer terminal learned this the hard way: an indefinite parent let xterm grow
   to its own content height, and the fit then sized the pty to the oversized
   box. min-height:0 is what lets this flex child actually shrink. */
.os-claudeconn-term {
    flex: 1 1 auto; min-height: 0;
    margin: 0 12px; padding: 6px 8px;
    background: var(--cc-term);
    border: 1px solid var(--cc-line);
    border-radius: 8px;
    overflow: hidden;
}
.os-claudeconn-term .xterm { box-sizing: border-box; padding-right: 12px; }
.os-claudeconn-term .xterm-viewport { background: var(--cc-term) !important; }
.os-claudeconn-term .xterm-rows {
    font-kerning: none;
    font-variant-ligatures: none;
    letter-spacing: 0 !important;
    white-space: pre;
}

.os-claudeconn-actions {
    flex: none;
    display: flex; gap: 8px; padding: 10px 12px 0;
}
.os-claudeconn-actions button {
    flex: 1 1 auto; cursor: pointer; font: inherit; font-size: 12.5px;
    color: var(--cc-fg); background: transparent;
    border: 1px solid var(--cc-line); border-radius: 6px; padding: 7px 10px;
}
.os-claudeconn-actions button:hover:not(:disabled) { border-color: var(--cc-accent); color: var(--cc-accent); }
.os-claudeconn-actions button:disabled { opacity: 0.45; cursor: default; }

.os-claudeconn-foot {
    flex: none;
    display: flex; align-items: center; justify-content: space-between;
    gap: 12px; padding: 10px 14px;
}
.os-claudeconn-msg { font-size: 12px; color: var(--cc-muted); }
.os-claudeconn-foot button {
    cursor: pointer; font: inherit; font-size: 12px;
    color: var(--cc-fg); background: transparent;
    border: 1px solid var(--cc-line); border-radius: 6px;
    padding: 5px 12px;
}
.os-claudeconn-foot button:hover { border-color: var(--cc-accent); color: var(--cc-accent); }

/* ── The interlock ─────────────────────────────────────────────────────────
   Two conduits drive in from the edges, latch, and discharge. Sits above the
   modal tier because it replaces the modal, and below the tour HUD (99000)
   because the tour has already finished by the time this can fire. */
.os-conduit {
    position: fixed; inset: 0; z-index: 9400;
    display: flex; align-items: center; justify-content: center;
    background: radial-gradient(circle at 50% 50%, rgba(10, 20, 40, 0.82), rgba(0, 0, 0, 0.94));
    animation: os-conduit-scrim 2900ms ease-out forwards;
    pointer-events: none;
}
.os-conduit svg { width: min(760px, 92vw); height: auto; overflow: visible; }

/* Each half drives in, then recoils very slightly on the latch. */
.os-conduit-left  { animation: os-conduit-drive-l 900ms cubic-bezier(0.5, 0, 0.35, 1) forwards; }
.os-conduit-right { animation: os-conduit-drive-r 900ms cubic-bezier(0.5, 0, 0.35, 1) forwards; }

/* The flash at contact. */
.os-conduit-flash { opacity: 0; animation: os-conduit-flash 520ms ease-out 860ms forwards; }

/* Power running along the joined path, drawn as a dash chasing down the line. */
.os-conduit-stream {
    stroke-dasharray: 26 120;
    stroke-dashoffset: 900;
    opacity: 0;
    animation: os-conduit-stream 1500ms linear 900ms forwards;
}

/* Branch bolts, staggered so they read as discharge rather than a single event. */
/* Staggered so the discharge reads as a sustained surge rather than one blink. */
.os-conduit-bolt { opacity: 0; }
.os-conduit-bolt-1 { animation: os-conduit-bolt 1100ms ease-out  920ms forwards; }
.os-conduit-bolt-2 { animation: os-conduit-bolt 1100ms ease-out 1000ms forwards; }
.os-conduit-bolt-3 { animation: os-conduit-bolt 1100ms ease-out 1090ms forwards; }
.os-conduit-bolt-4 { animation: os-conduit-bolt 1100ms ease-out 1170ms forwards; }
.os-conduit-bolt-5 { animation: os-conduit-bolt 1100ms ease-out 1260ms forwards; }
.os-conduit-bolt-6 { animation: os-conduit-bolt 1100ms ease-out 1340ms forwards; }
.os-conduit-glow   { opacity: 0; animation: os-conduit-glow 1900ms ease-out 880ms forwards; }
/* The white core runs slightly behind the coloured one, so the stream has depth. */
.os-conduit-stream-2 { animation-delay: 1020ms; }
.os-conduit-ring { opacity: 0; animation: os-conduit-ring 760ms ease-out 880ms forwards; }

@keyframes os-conduit-scrim {
    0%   { opacity: 0; }
    12%  { opacity: 1; }
    82%  { opacity: 1; }
    100% { opacity: 0; }
}
@keyframes os-conduit-drive-l {
    0%   { transform: translateX(-58%); }
    78%  { transform: translateX(2%); }
    100% { transform: translateX(0); }
}
@keyframes os-conduit-drive-r {
    0%   { transform: translateX(58%); }
    78%  { transform: translateX(-2%); }
    100% { transform: translateX(0); }
}
@keyframes os-conduit-flash {
    0%   { opacity: 0;   r: 16px; }
    28%  { opacity: 1;   r: 58px; }
    100% { opacity: 0;   r: 118px; }
}
@keyframes os-conduit-stream {
    0%   { opacity: 0; stroke-dashoffset: 900; }
    10%  { opacity: 1; }
    90%  { opacity: 1; }
    100% { opacity: 0; stroke-dashoffset: 0; }
}
@keyframes os-conduit-bolt {
    0%   { opacity: 0; }
    18%  { opacity: 1; }
    34%  { opacity: 0.25; }
    52%  { opacity: 0.95; }
    100% { opacity: 0; }
}
@keyframes os-conduit-ring {
    0%   { opacity: 0;    r: 44px;  stroke-width: 7; }
    22%  { opacity: 0.5;  }
    100% { opacity: 0;    r: 205px; stroke-width: 0.5; }
}
@keyframes os-conduit-glow {
    0%   { opacity: 0; }
    22%  { opacity: 0.85; }
    100% { opacity: 0; }
}

/* Reduced motion: the moment still deserves to be marked, so the connected
   state is shown and simply held, rather than driven and discharged. */
@media (prefers-reduced-motion: reduce) {
    .os-conduit,
    .os-conduit-left, .os-conduit-right, .os-conduit-flash,
    .os-conduit-stream, .os-conduit-bolt, .os-conduit-glow,
    .os-conduit-ring {
        animation: none;
    }
    .os-conduit-ring { opacity: 0; }
    .os-conduit { opacity: 1; }
    .os-conduit-left, .os-conduit-right { transform: none; }
    .os-conduit-stream { opacity: 1; stroke-dashoffset: 0; stroke-dasharray: none; }
    .os-conduit-glow   { opacity: 0.6; }
    .os-conduit-flash, .os-conduit-bolt { opacity: 0; }
}

/* Reading new information: the master pause above the seat pickers.
   When paused the seat rows dim and go inert. They stay VISIBLE rather than
   hidden, because they are the explanation of what is paused. The three
   app/credential rows above are deliberately untouched: those are features the
   user invokes in the moment, and pausing ingestion must never break them. */
/* Ingestion off: the model pickers leave the panel. `hidden` is set in script;
   this guard stops the block's own display rule from winning over it. */
.os-tokens-seats[hidden] { display: none !important; }
.os-tokens-ingest select {
    font-weight: 600;
}

/* A notification that can be acted on gets a button. Conditions the box
   computes for itself (hot CPU, full disk) have none, because dismissing them
   would not make them untrue. */
.os-ac-item-actions {
    display: flex;
    justify-content: flex-end;
    margin-top: 8px;
}
.os-ac-item-act {
    background: rgba(255, 255, 255, .10);
    border: 1px solid rgba(255, 255, 255, .18);
    border-radius: 6px;
    color: inherit;
    cursor: pointer;
    font: inherit;
    font-size: 12px;
    padding: 5px 12px;
}
.os-ac-item-act:hover { background: rgba(255, 255, 255, .16); }
.os-ac-item-act:disabled { opacity: .5; cursor: default; }

/* Ingestion master switch. A real switch rather than a dropdown: it is one
   decision with two states, and the seat pickers below it dim when it is off. */
.os-ingest-row { align-items: center; }
.os-switch { position: relative; display: inline-flex; flex: none; }
.os-switch input {
    position: absolute; inset: 0; width: 100%; height: 100%;
    margin: 0; opacity: 0; cursor: pointer; z-index: 1;
}
.os-switch-track {
    width: 38px; height: 22px; border-radius: 11px;
    background: rgba(255, 255, 255, .18);
    border: 1px solid rgba(255, 255, 255, .22);
    transition: background .15s ease;
    display: inline-block; position: relative;
}
.os-switch-track::after {
    content: ""; position: absolute; top: 2px; left: 2px;
    width: 16px; height: 16px; border-radius: 50%;
    background: #fff; transition: transform .15s ease;
}
.os-switch input:checked + .os-switch-track { background: #2f7d32; border-color: #2f7d32; }
.os-switch input:checked + .os-switch-track::after { transform: translateX(16px); }
.os-switch input:focus-visible + .os-switch-track { outline: 2px solid currentColor; outline-offset: 2px; }
.os-switch input:disabled { cursor: default; }
.os-switch input:disabled + .os-switch-track { opacity: .5; }
