/* Porthol shared UI styles — consume the --ctrl-* theme tokens emitted by ThemeHead.
   Shipped as a static web asset: _content/Enso.Components/enso.css */

html, body {
    margin: 0;
    padding: 0;
    height: 100%;
    font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
    font-size: 14px;
    background: var(--ctrl-surface-base, #1a1a1a);
    color: var(--ctrl-text-primary, #e8e8e8);
}

a { color: var(--ctrl-text-link, #4a90d9); }

/* ── Shell ─────────────────────────────────────────────────────────────── */
.enso-shell {
    display: flex;
    height: 100vh;
    overflow: hidden;
}

/* ── SideNav (collapsible) ─────────────────────────────────────────────── */
.sidenav {
    display: flex;
    flex-direction: column;
    background: var(--ctrl-feature-a, #0d2137);
    color: var(--ctrl-feature-a-on, #c0d8f0);
    border-right: 1px solid var(--ctrl-border-default, #3a3a3a);
    transition: width 0.12s ease, flex-basis 0.12s ease;
}
.sidenav--expanded { flex: 0 0 220px; width: 220px; }
.sidenav--collapsed { flex: 0 0 56px; width: 56px; }

.sidenav__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    padding: 12px;
}
.sidenav--collapsed .sidenav__header { flex-direction: column; gap: 8px; align-items: center; }

.sidenav__brand {
    display: flex;
    align-items: center;
    gap: 8px;
    color: inherit;
    text-decoration: none;
    overflow: hidden;
}
.sidenav__logo {
    width: 24px; height: 24px; flex: 0 0 24px;
    background-color: currentColor;
    -webkit-mask: url("images/noun-enso.svg") center / contain no-repeat;
    mask: url("images/noun-enso.svg") center / contain no-repeat;
}
.sidenav__title { font-size: 16px; font-weight: 600; letter-spacing: 0.5px; white-space: nowrap; }

.sidenav__toggle {
    background: none;
    border: none;
    color: inherit;
    cursor: pointer;
    padding: 4px;
    border-radius: var(--ctrl-border-radius-sm, 3px);
    flex: 0 0 auto;
    line-height: 0;
}
.sidenav__toggle:hover { background: var(--ctrl-fill-hover, #3d3d3d); }

.sidenav__icon {
    width: 20px; height: 20px; flex: 0 0 20px;
    fill: none; stroke: currentColor; stroke-width: 2;
    stroke-linecap: round; stroke-linejoin: round;
}
/* Most nav icons are stroked outlines. A few are sourced as filled glyphs (Material-style), where the
   outline is drawn by the fill itself — those need the stroke defaults turned off or they render as a
   blob with no fill at all. */
.sidenav__icon--filled { fill: currentColor; stroke: none; }

.sidenav__nav { display: flex; flex-direction: column; gap: 2px; padding: 8px; }
.sidenav__item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 10px;
    border-radius: var(--ctrl-border-radius-sm, 3px);
    color: inherit;
    text-decoration: none;
    white-space: nowrap;
    overflow: hidden;
}
.sidenav__item:hover { background: var(--ctrl-fill-hover, #3d3d3d); }
.sidenav__item--active { background: var(--ctrl-accent-container, #1e3a5f); color: var(--ctrl-accent-on, #fff); }
.sidenav--collapsed .sidenav__item { justify-content: center; }
.sidenav--collapsed .sidenav__label,
.sidenav--collapsed .sidenav__title,
.sidenav--collapsed .sidenav__user-info { display: none; }

.sidenav__spacer { flex: 1; }

.sidenav__footer { display: flex; flex-direction: column; gap: 8px; padding: 10px; border-top: 1px solid var(--ctrl-border-default, #3a3a3a); }
.sidenav--collapsed .sidenav__footer { align-items: center; }
.sidenav__version { font-size: 11px; opacity: 0.55; padding-left: 4px; }
.sidenav--collapsed .sidenav__version { display: none; }
.sidenav__user { display: flex; align-items: center; gap: 8px; overflow: hidden; }
.sidenav__avatar {
    width: 30px; height: 30px; flex: 0 0 30px;
    border-radius: 50%;
    background: var(--ctrl-accent-default, #4a90d9);
    color: var(--ctrl-accent-on, #fff);
    display: flex; align-items: center; justify-content: center;
    font-weight: 600; font-size: 13px;
}
.sidenav__user-info { display: flex; flex-direction: column; overflow: hidden; }
.sidenav__user-name { font-size: 13px; white-space: nowrap; }
.sidenav__user-sub { font-size: 11px; opacity: 0.7; white-space: nowrap; }

/* ── Node config panel ─────────────────────────────────────────────────── */
.settings-subhead { margin: 24px 0 8px; font-size: 14px; color: var(--ctrl-text-secondary, #a0a0a0); }
.ncp { max-width: 560px; }
.ncp-field { display: flex; flex-direction: column; gap: 4px; margin-bottom: 12px; }
.ncp-field > label { font-size: 12px; color: var(--ctrl-text-secondary, #a0a0a0); }
.ncp-toggle { display: inline-flex; border: 1px solid var(--ctrl-border-default, #3a3a3a); border-radius: var(--ctrl-border-radius-sm, 3px); overflow: hidden; width: fit-content; }
.ncp-toggle button { border: none; border-radius: 0; background: var(--ctrl-surface-raised, #242424); padding: 6px 16px; }
.ncp-toggle button.active { background: var(--ctrl-accent-default, #4a90d9); color: var(--ctrl-accent-on, #fff); }
.ncp-actions { display: flex; align-items: center; gap: 10px; margin-top: 8px; }
/* Hosted mode (hosted services S4-5a): the signed-in person, a refusal, and the confirmation before the app closes. */
.ncp-person { display: flex; align-items: center; gap: 10px; }
.ncp-error { color: var(--ctrl-status-error, #e06c6c); font-size: 12px; }
.ncp-confirm { border: 1px solid var(--ctrl-accent-default, #4a90d9); border-radius: var(--ctrl-border-radius-sm, 3px); padding: 10px 12px; margin-top: 8px; display: flex; flex-direction: column; gap: 8px; }

/* ── Splash ────────────────────────────────────────────────────────────── */
.splash {
    position: fixed;
    inset: 0;
    z-index: 9999;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--ctrl-surface-base, #1a1a1a);
    transition: opacity 0.4s ease;
}
.splash--fade { opacity: 0; pointer-events: none; }
.splash__content { display: flex; flex-direction: column; align-items: center; gap: 24px; }
.splash__logo {
    width: 200px; height: 200px;
    background-color: var(--ctrl-accent-default, #4a90d9);
    -webkit-mask: url("images/noun-enso.svg") center / contain no-repeat;
    mask: url("images/noun-enso.svg") center / contain no-repeat;
    animation: splash-pulse 1.8s ease-in-out infinite;
}
.splash__title { font-size: 34px; font-weight: 600; letter-spacing: 2px; color: var(--ctrl-text-primary, #e8e8e8); }
@keyframes splash-pulse {
    0%, 100% { opacity: 0.7; transform: scale(1); }
    50%      { opacity: 1;   transform: scale(1.04); }
}
.splash__bar-track { width: 180px; height: 3px; background: var(--ctrl-fill-default, #333); border-radius: 2px; overflow: hidden; }
.splash__bar-fill {
    height: 100%;
    background: var(--ctrl-accent-default, #4a90d9);
    border-radius: 2px;
    animation: splash-progress 1.2s ease-out forwards;
}
@keyframes splash-progress { from { width: 0; } to { width: 100%; } }
.splash__status { font-size: 12px; color: var(--ctrl-text-secondary, #a0a0a0); }
.splash__version { font-size: 12px; color: var(--ctrl-text-disabled, #666); letter-spacing: 0.5px; }
/* A hosted start that failed (hosted services S4-5a): the reason and what to do. */
.splash__problem { max-width: 420px; text-align: center; color: var(--ctrl-text-primary, #e8e8e8); font-size: 13px; }
.splash__actions { display: flex; gap: 10px; justify-content: center; }

.enso-shell__main {
    flex: 1;
    display: flex;
    flex-direction: column;
    min-width: 0;
}

/* The top bar is shell chrome, not content, so it draws from the Feature group like the nav rail beside it
   (feature-a) rather than from the content surfaces. feature-dim is that group's recessed tone, which puts the
   bar and the rail in one family and leaves the surface-* steps to do their own job in the panes below — the
   frame tree took surface-dim, and a top bar wearing the same token read as part of the same stack.
   Moved off surface-dim 2026-07-21. */
.enso-shell__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 8px 16px;
    background: var(--ctrl-feature-dim, #162a3e);
    color: var(--ctrl-feature-dim-on, #c0d8f0);
    border-bottom: 1px solid var(--ctrl-border-default, #3a3a3a);
}

/* Everything inside the bar takes the BAR's ink rather than the content palette's. text-primary and
   text-secondary are chosen to sit on a surface-* background, and this bar is no longer one — it moved to
   feature-dim, whose "on" colour is the only one guaranteed to be readable against it in every theme. The
   picker rule is scoped because that class is also used standalone in the theme builder, where the content
   palette is still the right answer; the title only ever appears here. */
.enso-shell__header .enso-theme-pick { color: inherit; }

/* The current window's name, sitting opposite the theme picker. */
.enso-shell__title {
    font-size: 22.5px; /* 50% larger than the base 15px */
    font-weight: 700;
    color: inherit;
}

.enso-theme-pick {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 12px;
    color: var(--ctrl-text-secondary, #a0a0a0);
}

.enso-shell__content {
    flex: 1;
    overflow: auto;
    padding: 24px;
}

/* Full-bleed pages manage their own panes and scrolling: the content host drops its padding and its scroll so a
   page rooted here can fill the viewport top-to-bottom and split itself into columns. The frame view (.ws), the
   grid builder (.gb) and the glyph studio (.gst) are all tool pages of this shape. Without this a page's
   height:100% sits inside a padded auto-scrolling block and collapses to content height at the top.
   (The studio uses .gst, not .gs — .gs is an unrelated global list class, and sharing it leaked flex-direction.) */
.enso-shell__content:has(> .ws),
.enso-shell__content:has(> .gb),
.enso-shell__content:has(> .gst),
.enso-shell__content:has(> .ed-page) {
    padding: 0;
    overflow: hidden;
}

h1 { font-size: 22px; }
.lead { color: var(--ctrl-text-secondary, #a0a0a0); max-width: 70ch; }

/* ── Form controls ─────────────────────────────────────────────────────── */
button,
select {
    background: var(--ctrl-fill-default, #333);
    color: var(--ctrl-text-primary, #e8e8e8);
    border: 1px solid var(--ctrl-border-default, #3a3a3a);
    border-radius: var(--ctrl-border-radius-sm, 3px);
    padding: 6px 12px;
    font-size: 13px;
    cursor: pointer;
}

button:hover:not(:disabled),
select:hover {
    background: var(--ctrl-fill-hover, #3d3d3d);
}

button:disabled {
    opacity: 0.5;
    cursor: default;
}

/* ── Node status panel ─────────────────────────────────────────────────── */
.host-status {
    max-width: 560px;
    padding: 16px 20px;
    background: var(--ctrl-surface-raised, #242424);
    border: 1px solid var(--ctrl-border-default, #3a3a3a);
    border-radius: var(--ctrl-border-radius-md, 6px);
}

.host-status__header {
    display: flex;
    align-items: center;
    gap: 8px;
}

.host-status__header h3 { margin: 0; font-size: 15px; }

.host-status__dot {
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background: var(--ctrl-text-disabled, #666);
}

.host-status__dot.is-running { background: var(--ctrl-status-success, #4caf50); }
.host-status__dot.is-stopped { background: var(--ctrl-text-disabled, #666); }

.host-status__grid {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 6px 16px;
    margin: 14px 0;
}

.host-status__grid dt { color: var(--ctrl-text-secondary, #a0a0a0); }
.host-status__grid dd { margin: 0; word-break: break-all; }
.host-status__grid code { font-size: 12px; }

.host-status__actions {
    display: flex;
    gap: 8px;
}

.host-status__error {
    margin-top: 12px;
    color: var(--ctrl-status-error, #ef5350);
}

/* ── Workspace alert: a call the host did not answer, above the tree (2026-10-09) ── */
.ws-alert {
    display: flex;
    align-items: center;
    gap: 12px;
    margin: 8px 12px 0;
    padding: 8px 12px;
    color: var(--ctrl-status-error, #ef5350);
    background: var(--ctrl-surface-raised, #242424);
    border: 1px solid var(--ctrl-status-error, #ef5350);
    border-radius: var(--ctrl-border-radius-sm, 3px);
}
.ws-alert__text { flex: 1; min-width: 0; overflow-wrap: anywhere; }
.ws-alert__actions { display: flex; gap: 8px; flex: none; }

/* ── Cockpit ───────────────────────────────────────────────────────────── */
.muted { color: var(--ctrl-text-secondary, #a0a0a0); }
.cockpit-error {
    color: var(--ctrl-status-error, #ef5350);
    background: var(--ctrl-surface-raised, #242424);
    border: 1px solid var(--ctrl-status-error, #ef5350);
    border-radius: var(--ctrl-border-radius-sm, 3px);
    padding: 8px 12px;
}

.cockpit-create { display: flex; gap: 8px; margin: 12px 0; }
.cockpit-create input { flex: 0 0 280px; }

input, textarea {
    background: var(--ctrl-surface-raised, #242424);
    color: var(--ctrl-text-primary, #e8e8e8);
    border: 1px solid var(--ctrl-border-default, #3a3a3a);
    border-radius: var(--ctrl-border-radius-sm, 3px);
    padding: 6px 8px;
    font: inherit;
}
input:focus, textarea:focus, select:focus { outline: 1px solid var(--ctrl-border-focus, #4a90d9); }

.cockpit-table { width: 100%; border-collapse: collapse; }
.cockpit-table th, .cockpit-table td {
    text-align: left;
    padding: 8px 10px;
    border-bottom: 1px solid var(--ctrl-border-default, #3a3a3a);
}
.cockpit-table th { color: var(--ctrl-text-secondary, #a0a0a0); font-weight: 600; }
.cockpit-actions { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }

.state-badge {
    display: inline-block;
    padding: 2px 8px;
    border-radius: 999px;
    font-size: 12px;
    border: 1px solid var(--ctrl-border-default, #3a3a3a);
}
.state-inactive  { color: var(--ctrl-text-secondary, #a0a0a0); }
.state-closed    { color: var(--ctrl-text-secondary, #a0a0a0); }
.state-read      { color: var(--ctrl-status-info, #4a90d9); border-color: var(--ctrl-status-info, #4a90d9); }
.state-readwrite { color: var(--ctrl-status-success, #4caf50); border-color: var(--ctrl-status-success, #4caf50); }
.state-faulted   { color: var(--ctrl-status-error, #ef5350); border-color: var(--ctrl-status-error, #ef5350); }

button.danger { border-color: var(--ctrl-status-error, #ef5350); color: var(--ctrl-status-error, #ef5350); }
button.sm { padding: 2px 8px; font-size: 12px; }
button.link {
    background: none; border: none; color: var(--ctrl-text-link, #4a90d9);
    padding: 4px 0; cursor: pointer; text-align: left;
}
button.link:hover { text-decoration: underline; background: none; }

/* Inspector */
.inspector-head { display: flex; align-items: baseline; gap: 16px; }
.inspector-head .back { font-size: 13px; }
.inspector {
    display: grid;
    grid-template-columns: 1fr 1fr 2fr;
    gap: 16px;
    margin-top: 12px;
}
.inspector-pane {
    background: var(--ctrl-surface-raised, #242424);
    border: 1px solid var(--ctrl-border-default, #3a3a3a);
    border-radius: var(--ctrl-border-radius-md, 6px);
    padding: 12px;
    min-height: 200px;
}
.pane-head { display: flex; align-items: center; justify-content: space-between; }
.pane-head h3 { margin: 0 0 8px; font-size: 14px; }
.folder-create { display: flex; gap: 6px; margin-bottom: 10px; }
.folder-create input { flex: 1; min-width: 0; }
.pick-list { list-style: none; margin: 0; padding: 0; }
.pick-list li {
    display: flex; align-items: center; justify-content: space-between;
    padding: 2px 6px; border-radius: var(--ctrl-border-radius-sm, 3px);
}
.pick-list li.active { background: var(--ctrl-accent-container, #1e3a5f); }
.gdx {
    background: var(--ctrl-surface-dim, #141414);
    border: 1px solid var(--ctrl-border-default, #3a3a3a);
    border-radius: var(--ctrl-border-radius-sm, 3px);
    padding: 10px; overflow: auto; font-size: 12px; max-height: 60vh;
    white-space: pre; color: var(--ctrl-text-primary, #e8e8e8);
}
.io { margin-top: 20px; }
.io summary { cursor: pointer; color: var(--ctrl-text-secondary, #a0a0a0); }
.io-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-top: 12px; }
.io-grid textarea { width: 100%; box-sizing: border-box; }
.io-grid h4 { margin: 0 0 6px; }
.elem-actions { display: flex; gap: 6px; align-items: center; }
label.inline { display: inline-flex; align-items: center; gap: 6px; margin: 6px 0; }

/* Diagnostics */
.diag { display: grid; grid-template-columns: 1fr 3fr; gap: 16px; margin-top: 12px; }
.diag textarea { width: 100%; box-sizing: border-box; font-family: ui-monospace, monospace; }
.grid-scroll { overflow: auto; max-height: 60vh; margin-top: 12px; }
.grid-scroll table { font-size: 12px; }
.grid-scroll td { white-space: nowrap; max-width: 320px; overflow: hidden; text-overflow: ellipsis; }

/* ── Settings tabs ─────────────────────────────────────────────────────── */
.settings-tabs {
    display: flex;
    gap: 4px;
    border-bottom: 1px solid var(--ctrl-border-default, #3a3a3a);
    margin: 8px 0 18px;
}
.settings-tab {
    background: none;
    border: none;
    border-bottom: 2px solid transparent;
    border-radius: 0;
    padding: 8px 14px;
    color: var(--ctrl-text-secondary, #a0a0a0);
    cursor: pointer;
}
.settings-tab:hover { background: none; color: var(--ctrl-text-primary, #e8e8e8); }
.settings-tab.active {
    color: var(--ctrl-text-primary, #e8e8e8);
    border-bottom-color: var(--ctrl-accent-default, #4a90d9);
}

/* ── Theme builder ─────────────────────────────────────────────────────── */
.tb-bar { display: flex; align-items: center; gap: 10px; margin-bottom: 16px; flex-wrap: wrap; }
.tb-group {
    border: 1px solid var(--ctrl-border-default, #3a3a3a);
    border-radius: var(--ctrl-border-radius-md, 6px);
    margin: 0 0 14px;
    padding: 8px 14px 14px;
}
.tb-group legend { color: var(--ctrl-text-secondary, #a0a0a0); font-size: 12px; padding: 0 6px; }
.tb-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 10px; }
.tb-field { display: flex; flex-direction: column; gap: 4px; }
.tb-field > label { font-size: 12px; color: var(--ctrl-text-secondary, #a0a0a0); }
.tb-color { display: flex; gap: 6px; align-items: center; }
.tb-swatch {
    width: 34px; height: 30px; padding: 0;
    border: 1px solid var(--ctrl-border-default, #3a3a3a);
    border-radius: var(--ctrl-border-radius-sm, 3px);
    cursor: pointer; background: none;
}
.tb-text { flex: 1; min-width: 0; font-family: ui-monospace, monospace; font-size: 12px; }

/* A colour the native picker cannot hold (oklch(), a named colour). Same footprint as .tb-swatch so the row
   doesn't shift, but inert — it reports the colour, it doesn't edit it. Added 2026-07-21. */
.tb-chip {
    width: 34px; height: 30px; flex: none;
    border: 1px solid var(--ctrl-border-default, #3a3a3a);
    border-radius: var(--ctrl-border-radius-sm, 3px);
    /* Checkerboard behind the fill, so a transparent or partly-transparent value reads as transparent
       rather than as "the panel colour". */
    background-image: linear-gradient(45deg, #8884 25%, transparent 25%, transparent 75%, #8884 75%),
                      linear-gradient(45deg, #8884 25%, transparent 25%, transparent 75%, #8884 75%);
    background-size: 10px 10px;
    background-position: 0 0, 5px 5px;
}

.tb-lch {
    flex: none; width: 26px; height: 30px; padding: 0; cursor: pointer;
    background: none; color: var(--ctrl-text-secondary, #a0a0a0);
    border: 1px solid var(--ctrl-border-default, #3a3a3a);
    border-radius: var(--ctrl-border-radius-sm, 3px);
    font-size: 13px; line-height: 1;
}
.tb-lch:hover { color: var(--ctrl-text-primary, #e8e8e8); background: var(--ctrl-fill-hover, #2c2c2c); }
.tb-lch--on {
    color: var(--ctrl-accent-on, #fff);
    background: var(--ctrl-accent-default, #4a90d9);
    border-color: var(--ctrl-accent-default, #4a90d9);
}

.tb-bad { font-size: 11px; color: var(--ctrl-status-error, #e05252); }

.tb-lchbox {
    display: flex; flex-direction: column; gap: 4px;
    margin-top: 4px; padding: 8px;
    border: 1px solid var(--ctrl-border-default, #3a3a3a);
    border-radius: var(--ctrl-border-radius-sm, 4px);
    background: var(--ctrl-surface-raised, #222);
}
.tb-lchrow { display: flex; align-items: center; gap: 8px; }
.tb-lchrow input[type="range"] { flex: 1; min-width: 0; }
.tb-lchkey { width: 12px; flex: none; font-size: 11px; color: var(--ctrl-text-secondary, #a0a0a0); }
.tb-lchval {
    width: 44px; flex: none; text-align: right;
    font-family: ui-monospace, monospace; font-size: 11px; color: var(--ctrl-text-secondary, #a0a0a0);
}
.tb-lchfoot { display: flex; align-items: center; justify-content: flex-end; gap: 8px; margin-top: 2px; }
.tb-warn { margin-right: auto; font-size: 11px; color: var(--ctrl-status-warning, #d9a441); }
.tb-bake {
    padding: 2px 8px; font-size: 11px; cursor: pointer;
    color: var(--ctrl-text-secondary, #a0a0a0); background: none;
    border: 1px solid var(--ctrl-border-default, #3a3a3a);
    border-radius: var(--ctrl-border-radius-sm, 3px);
}
.tb-bake:hover { color: var(--ctrl-text-primary, #e8e8e8); background: var(--ctrl-fill-hover, #2c2c2c); }

/* ───────────────────────────────────────────────────────────────────────────
   Management surfaces (nm__*) — shared by the Node and Namespace pages.
   Relocated here from HostManagerPage's scoped CSS (2026-06-28) when the single
   Node Manager was split into two scoped surfaces: Blazor CSS isolation does not
   cascade, so the shared tab/table/badge styles must live in one global sheet.
   ─────────────────────────────────────────────────────────────────────────── */
.nm { padding: 1.1rem 1.4rem; color: var(--ctrl-text-primary, #1f2328); }

.nm__bar { display: flex; align-items: center; gap: 1.25rem; border-bottom: 1px solid var(--ctrl-border-default, #e5e7eb); padding-bottom: .5rem; margin-bottom: 1rem; }
.nm__title { margin: 0; font-size: 1.25rem; }
.nm__sub { font-size: .85rem; color: var(--ctrl-text-secondary, #6b7280); font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
.nm__tabs { display: flex; gap: .25rem; flex-wrap: wrap; }
.nm__spacer { flex: 1; }

.nm__tab {
    background: none;
    border: none;
    border-bottom: 2px solid transparent;
    padding: .35rem .65rem;
    font-size: .95rem;
    font-weight: 600;
    color: var(--ctrl-text-secondary, #6b7280);
    cursor: pointer;
}
.nm__tab:hover { color: var(--ctrl-text-primary, #1f2328); }
.nm__tab.is-active { color: var(--ctrl-text-emphasis, #3949ab); border-bottom-color: var(--ctrl-accent-default, #3949ab); }

/* Dev / sysadmin tabs (raw storage internals — Store / Diagnostics): flagged red so a normal user steers clear. */
.nm__tab--dev { color: var(--ctrl-status-error, #ef5350); }
.nm__tab--dev:hover { color: var(--ctrl-status-error, #ef5350); }
.nm__tab--dev.is-active { color: var(--ctrl-status-error, #ef5350); border-bottom-color: var(--ctrl-status-error, #ef5350); }

.nm__status { padding: .5rem .75rem; border-radius: .375rem; margin-bottom: 1rem; font-size: .9rem; }
.nm__status.is-ok  { background: #e6f5ea; color: #1a7f37; border: 1px solid #aedcb9; }
.nm__status.is-err { background: #fbe9e9; color: #b3261e; border: 1px solid #f0b4b0; }

.nm__muted { color: var(--ctrl-text-secondary, #6b7280); padding: .6rem 0; }

.nm__split { display: grid; grid-template-columns: 16rem 1fr; gap: 1.25rem; align-items: start; }

.nm__master { display: flex; flex-direction: column; gap: .25rem; }
.nm__pl {
    text-align: left;
    background: none;
    border: 1px solid transparent;
    border-radius: .4rem;
    padding: .5rem .6rem;
    cursor: pointer;
    display: flex;
    flex-direction: column;
    gap: .1rem;
}
.nm__pl:hover { background: var(--ctrl-surface-dim, #f6f7f9); }
.nm__pl.is-sel { background: #eef2ff; border-color: #c7d2fe; }
.nm__pl-name { font-weight: 600; font-size: .92rem; }
.nm__pl-meta { font-size: .74rem; color: var(--ctrl-text-secondary, #6b7280); font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }

.nm__detail-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: .6rem; }
.nm__detail-head h2 { margin: 0; font-size: 1.1rem; }

.nm__filter { display: flex; align-items: center; gap: .3rem; }
.nm__filter-lbl { font-size: .78rem; color: var(--ctrl-text-secondary, #6b7280); margin-right: .15rem; }
.nm__chip {
    background: none;
    border: 1px solid var(--ctrl-border-default, #d1d5db);
    border-radius: 1rem;
    padding: .12rem .55rem;
    font-size: .76rem;
    cursor: pointer;
    color: var(--ctrl-text-secondary, #6b7280);
}
.nm__chip.is-on { background: var(--ctrl-accent-default, #3949ab); color: var(--ctrl-accent-on, #fff); border-color: var(--ctrl-accent-default, #3949ab); }

.nm__h2 { font-size: 1.05rem; margin: 0 0 .6rem; }

.nm__tree { display: inline-flex; align-items: center; gap: .35rem; }
.nm__tree-mark { color: var(--ctrl-text-secondary, #9ca3af); font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
.nm__actor { font-weight: 600; font-size: .9rem; }

.nm__table { width: 100%; border-collapse: collapse; font-size: .9rem; }
.nm__table th, .nm__table td { text-align: left; padding: .55rem .5rem; border-bottom: 1px solid var(--ctrl-border-default, #e5e7eb); vertical-align: top; }
.nm__table th { font-weight: 600; color: var(--ctrl-text-secondary, #6b7280); }

.nm__name { font-weight: 600; }
.nm__key  { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .78rem; color: var(--ctrl-text-secondary, #6b7280); }
.nm__desc { font-size: .82rem; color: var(--ctrl-text-secondary, #6b7280); margin-top: .15rem; }
/* The universal target type is written "*", but a monospace asterisk at .78rem sits up at cap height and reads
   as a speck of dust rather than a value. Size it up and drop it onto the baseline so the column scans.
   Added 2026-07-26. */
.nm__any  { font-size: 1.15rem; line-height: 1; vertical-align: -.22em; }
.nm__mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .8rem; }
.nm__actions { white-space: nowrap; }

.nm__kind, .nm__disp { display: inline-block; padding: .1rem .5rem; border-radius: 1rem; font-size: .72rem; font-weight: 600; }
.nm__kind { background: #eef2ff; color: #3949ab; white-space: nowrap; }
.nm__kind--staticservice { background: #ecfdf5; color: #047857; }
.nm__kind--sharedservice { background: #fef3c7; color: #92660a; }
.nm__kind--viewbuilder   { background: #f3e8ff; color: #7e22ce; }

.nm__disp { background: #eef2ff; color: #3949ab; }
.nm__disp--active   { background: #e6f5ea; color: #1a7f37; }
.nm__disp--quiesced { background: #fef3c7; color: #92660a; }
.nm__disp--drained  { background: #f3f4f6; color: #6b7280; }
.nm__disp--faulted  { background: #fbe9e9; color: #b3261e; }

/* A binding whose offering is not in the catalog: still configured, cannot resolve. Dimmed rather than hidden
   — it is real, it is why the views are missing, and it is the thing to act on. Added 2026-07-21. */
.nm__disp--missing  { background: #fbe9e9; color: #b3261e; margin-left: .45rem; }
.nm__row--dead .nm__key { opacity: .6; }
.nm__warn {
    padding: .55rem .75rem; margin-bottom: .85rem; font-size: .9rem;
    background: #fef3c7; color: #92660a; border: 1px solid #f3d38a; border-radius: .375rem;
}

.nm__in { padding: .3rem .45rem; border: 1px solid var(--ctrl-border-default, #d1d5db); border-radius: .3rem; font-size: .85rem; margin-right: .35rem; min-width: 9rem; }

.nm__btn { padding: .35rem .7rem; border: 1px solid var(--ctrl-accent-default, #3949ab); background: var(--ctrl-accent-default, #3949ab); color: var(--ctrl-accent-on, #fff); border-radius: .3rem; font-size: .85rem; cursor: pointer; margin-right: .35rem; }
.nm__btn:hover:not(:disabled) { filter: brightness(.95); }
.nm__btn:disabled { opacity: .5; cursor: default; }
.nm__btn--ghost { background: transparent; color: var(--ctrl-text-emphasis, #3949ab); }
.nm__btn--sm { padding: .2rem .5rem; font-size: .78rem; }

/* ───────────────────────────────────────────────────────────────────────────
   Frame side panel — the reusable TabbedPanel container (tp__*), its palettes
   (pal__*), and the composed panel (sp__*). Lives in this global sheet, not in
   per-component scoped CSS, because Blazor CSS isolation does not cascade and
   these classes are shared across TabbedPanel / TabPanel / ActionPalette /
   ViewPalette / FrameSidePanel (same reason the nm__* / settings-tab styles
   are here). Prefix note: tp = tabbed panel, pal = palette, sp = side panel.
   ─────────────────────────────────────────────────────────────────────────── */

/* ── TabbedPanel (reusable tab container) ──────────────────────────────────── */
.tp {
    display: flex;
    flex-direction: column;
    height: 100%;
    min-height: 0;
    background: var(--ctrl-surface-raised, #242424);
    border-left: 1px solid var(--ctrl-border-default, #3a3a3a);
    box-sizing: border-box;
}
.tp-strip {
    display: flex;
    gap: 2px;
    flex: none;
    padding: 0 8px;
    border-bottom: 1px solid var(--ctrl-border-default, #3a3a3a);
    background: var(--ctrl-surface-dim, #141414);
}
.tp-tab {
    display: flex; align-items: center; gap: 7px;
    background: none; border: none; border-bottom: 2px solid transparent; border-radius: 0;
    padding: 10px 14px;
    font: 600 12.5px/1 ui-sans-serif, system-ui;
    color: var(--ctrl-text-secondary, #a0a0a0);
    cursor: pointer;
}
.tp-tab-ic { display: inline-flex; line-height: 0; opacity: .85; }
.tp-tab:hover:not(:disabled) { background: none; color: var(--ctrl-text-primary, #e8e8e8); }
.tp-tab:disabled { opacity: .4; cursor: default; }
.tp-tab--on {
    color: var(--ctrl-text-primary, #e8e8e8);
    border-bottom-color: var(--ctrl-accent-default, #4a90d9);
    background: color-mix(in srgb, var(--ctrl-accent-default, #4a90d9) 8%, transparent);
}
/* The body hosts the tab bodies (tp-panels) and, when one is up, an overlay that replaces them (the resource
   picker). The scroll that used to sit on tp-body moves to tp-panels so an overlay can manage its own scrolling
   region; tp-panels--hidden keeps the tabs in the tree (registered) while the overlay shows. */
.tp-body { flex: 1; min-height: 0; display: flex; flex-direction: column; }
.tp-panels { flex: 1; min-height: 0; overflow: auto; }
.tp-panels--hidden { display: none; }
.tp-overlay { flex: 1; min-height: 0; display: flex; flex-direction: column; }
.tp-panel {
    display: flex;
    flex-direction: column;
    gap: 18px;
    padding: 16px;
    box-sizing: border-box;
}

/* ── Side panel composition (FrameSidePanel) ───────────────────────────────── */
.sp { height: 100%; min-height: 0; }
.sp-empty { margin: 0; color: var(--ctrl-text-secondary, #a0a0a0); font: 13px/1.4 ui-sans-serif, system-ui; }

/* Header: the kind badge leading the object title (the title in the accent colour) on one row, the overflow
   menu at the far right, and the optional type beneath. The path is not repeated here — the Details tab
   carries it. */
.sp-obj { display: flex; flex-direction: column; gap: 8px; }
.sp-obj-titlerow { display: flex; align-items: center; gap: 10px; }
.sp-obj-name {
    margin: 0; flex: 1; min-width: 0;
    font: 800 21px/1.15 ui-sans-serif, system-ui; color: var(--ctrl-accent-default, #4a90d9); word-break: break-word;
}

.sp-obj-menuwrap { position: relative; flex: none; }
.sp-obj-menu {
    display: inline-flex; align-items: center; justify-content: center;
    width: 34px; height: 34px; padding: 0; border-radius: var(--ctrl-border-radius-sm, 6px);
    background: var(--ctrl-surface-dim, #141414); border: 1px solid var(--ctrl-border-default, #3a3a3a);
    color: var(--ctrl-text-secondary, #a0a0a0); cursor: pointer;
}
.sp-obj-menu:hover:not(:disabled) { background: var(--ctrl-fill-hover, #3d3d3d); color: var(--ctrl-text-primary, #e8e8e8); }
.sp-menu {
    position: absolute; top: 40px; right: 0; z-index: 20; min-width: 150px;
    display: flex; flex-direction: column; padding: 4px;
    background: var(--ctrl-surface-raised, #242424); border: 1px solid var(--ctrl-border-default, #3a3a3a);
    border-radius: var(--ctrl-border-radius-sm, 6px); box-shadow: 0 6px 20px rgba(0, 0, 0, .35);
}
.sp-menu-item {
    display: flex; align-items: center; gap: 8px; text-align: left; width: 100%;
    background: none; border: none; border-radius: var(--ctrl-border-radius-sm, 4px);
    padding: 7px 9px; font: 500 13px/1 ui-sans-serif, system-ui; color: var(--ctrl-text-primary, #e8e8e8); cursor: pointer;
}
.sp-menu-item:hover:not(:disabled) { background: var(--ctrl-fill-hover, #3d3d3d); }
.sp-menu-item:disabled { opacity: .5; cursor: default; }

/* A rule setting the destructive entry apart from the ordinary ones above it. Added 2026-07-24. */
.sp-menu-sep { height: 1px; margin: 4px 2px; background: var(--ctrl-border-default, #3a3a3a); }
.sp-menu-item--danger { color: var(--ctrl-status-error, #e05a4e); }
.sp-menu-item--danger:hover:not(:disabled) {
    background: color-mix(in srgb, var(--ctrl-status-error, #e05a4e) 14%, transparent);
}

/* The delete confirm banner — a deletion cannot be undone, so it names what will go and holds the real
   Delete behind a second click. Tinted with the error colour so it reads as the serious step it is.
   Added 2026-07-24. */
.sp-confirm {
    display: flex; flex-direction: column; gap: 8px; margin-top: 10px; padding: 10px 12px;
    border-radius: var(--ctrl-border-radius-sm, 6px);
    border: 1px solid color-mix(in srgb, var(--ctrl-status-error, #e05a4e) 45%, transparent);
    background: color-mix(in srgb, var(--ctrl-status-error, #e05a4e) 10%, transparent);
}
.sp-confirm-text { font: 500 12.5px/1.4 ui-sans-serif, system-ui; color: var(--ctrl-text-primary, #e8e8e8); }
.sp-confirm-err { font: 500 12px/1.4 ui-sans-serif, system-ui; color: var(--ctrl-status-error, #e05a4e); }
.sp-confirm-actions { display: flex; gap: 8px; }
.sp-confirm-cancel, .sp-confirm-go {
    padding: 6px 14px; border-radius: var(--ctrl-border-radius-sm, 5px); cursor: pointer;
    font: 600 12.5px/1 ui-sans-serif, system-ui; border: 1px solid transparent;
}
.sp-confirm-cancel {
    background: none; border-color: var(--ctrl-border-default, #3a3a3a); color: var(--ctrl-text-secondary, #a0a0a0);
}
.sp-confirm-cancel:hover:not(:disabled) { color: var(--ctrl-text-primary, #e8e8e8); }
.sp-confirm-go { background: var(--ctrl-status-error, #e05a4e); color: #fff; }
.sp-confirm-go:hover:not(:disabled) { filter: brightness(1.08); }
.sp-confirm-cancel:disabled, .sp-confirm-go:disabled { opacity: .5; cursor: default; }

.sp-obj-tags { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
/* Feature B fills the kind pill, Feature B On writes on it — the same pill as .nh-badge (nav header) and
   .wn-badge (nav pane), so the three move together. The border used to outline a translucent accent tint;
   against a solid fill it now just carries the token, keeping the box metrics without drawing an edge.
   Moved off the accent tokens 2026-07-21. */
.sp-obj-badge {
    display: inline-block; flex: none; padding: 3px 9px; border-radius: 6px;
    font: 800 9.5px/1.3 ui-sans-serif, system-ui; letter-spacing: .12em;
    color: var(--ctrl-feature-b-on, #fff);
    background: var(--ctrl-feature-b, #2a4a6b);
    border: 1px solid var(--ctrl-feature-b, #2a4a6b);
}
.sp-obj-type { font: 600 11px/1 ui-monospace, Menlo, Consolas, monospace; color: var(--ctrl-text-secondary, #a0a0a0); }
.sp-obj-copied { font: 600 11px/1 ui-sans-serif, system-ui; color: var(--ctrl-status-success, #4caf50); }

.sp-divider { height: 1px; background: var(--ctrl-border-default, #3a3a3a); margin: 2px 0; }

/* The Item tab: what a workspace app has selected inside itself (a list item) — its label, its details, and
   the way to remove it (see FrameSidePanel.razor / WorkspaceDetailState). */
.sp-field { display: flex; flex-direction: column; gap: 6px; }
.sp-field-label {
    font: 700 9px/1 ui-sans-serif, system-ui; letter-spacing: .1em; text-transform: uppercase;
    color: var(--ctrl-text-secondary, #a0a0a0);
}
.sp-field-input, .sp-field-text {
    width: 100%;
    background: var(--ctrl-surface-dim, #141414); border: 1px solid var(--ctrl-border-default, #3a3a3a);
    border-radius: var(--ctrl-border-radius-sm, 6px); color: var(--ctrl-text-primary, #e8e8e8);
    padding: 8px 10px; font: 500 13px/1.4 ui-sans-serif, system-ui;
}
.sp-field-input:focus, .sp-field-text:focus { outline: none; border-color: var(--ctrl-accent-default, #4a90d9); }
.sp-field-text { min-height: 110px; resize: vertical; }
.sp-field-check { width: 17px; height: 17px; align-self: flex-start; accent-color: var(--ctrl-accent-default, #4a90d9); }

.sp-remove {
    display: inline-flex; align-items: center; justify-content: center; gap: 8px; align-self: flex-start;
    padding: 8px 14px; border-radius: var(--ctrl-border-radius-sm, 6px); cursor: pointer;
    background: none; border: 1px solid var(--ctrl-border-default, #3a3a3a);
    font: 600 12.5px/1 ui-sans-serif, system-ui; color: var(--ctrl-text-secondary, #a0a0a0);
}
.sp-remove:hover { color: var(--ctrl-status-error, #ef5350); border-color: var(--ctrl-status-error, #ef5350); }

.sp-details { margin: 0; display: flex; flex-direction: column; gap: 10px; }
.sp-detail { display: flex; flex-direction: row; align-items: baseline; gap: 10px; }
.sp-detail dt {
    flex: none; width: 42px;
    font: 700 9px/1 ui-sans-serif, system-ui; letter-spacing: .1em; text-transform: uppercase;
    color: var(--ctrl-text-secondary, #a0a0a0);
}
.sp-detail dd { flex: 1; min-width: 0; margin: 0; font: 13px/1.35 ui-sans-serif, system-ui; color: var(--ctrl-text-primary, #e8e8e8); word-break: break-word; }
.sp-mono { font-family: ui-monospace, Menlo, Consolas, monospace; }

/* A failed edge write, shown just above the Model in the Details tab. */
.sp-edge-err { font: 500 12px/1.4 ui-sans-serif, system-ui; color: var(--ctrl-status-error, #e05a4e); margin-bottom: 6px; }

/* The Config tab (folders only): the folder's accepted-types list. Chips are the current policy; the "Anything"
   chip is the exclusive "*" entry and is styled in the accent so it reads as a different KIND of entry rather
   than just another type. Removing the last chip leaves the folder accepting nothing — .fc-warn says so, since
   it is easy to reach by accident. See FolderConfigPanel.razor. */
.fc { display: flex; flex-direction: column; gap: 14px; }
.fc-sec { display: flex; flex-direction: column; gap: 8px; }
.fc-h {
    margin: 0;
    font: 700 9px/1 ui-sans-serif, system-ui; letter-spacing: .1em; text-transform: uppercase;
    color: var(--ctrl-text-secondary, #a0a0a0);
}
.fc-note { margin: 0; font: 12px/1.4 ui-sans-serif, system-ui; color: var(--ctrl-text-secondary, #a0a0a0); }

.fc-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.fc-chip {
    display: inline-flex; align-items: center; gap: 6px; padding: 4px 6px 4px 9px;
    border-radius: var(--ctrl-border-radius-sm, 6px);
    background: var(--ctrl-surface-dim, #2a2a2a);
    border: 1px solid var(--ctrl-border-default, #3a3a3a);
    font: 600 11px/1.3 ui-monospace, Menlo, Consolas, monospace;
    color: var(--ctrl-text-primary, #e8e8e8);
}
.fc-chip-any {
    padding: 4px 10px;
    color: var(--ctrl-accent-default, #4a90d9);
    background: color-mix(in srgb, var(--ctrl-accent-default, #4a90d9) 14%, transparent);
    border-color: color-mix(in srgb, var(--ctrl-accent-default, #4a90d9) 35%, transparent);
    font-family: ui-sans-serif, system-ui;
}
.fc-chip-label { word-break: break-all; }
.fc-chip-x {
    display: inline-flex; align-items: center; justify-content: center;
    width: 18px; height: 18px; padding: 0; flex: none;
    border: none; border-radius: 4px; background: none;
    color: var(--ctrl-text-secondary, #a0a0a0); cursor: pointer;
}
.fc-chip-x:hover { background: var(--ctrl-surface-base, #1e1e1e); color: var(--ctrl-text-primary, #e8e8e8); }

.fc-warn {
    display: flex; align-items: flex-start; gap: 8px; padding: 8px 10px;
    border-radius: var(--ctrl-border-radius-sm, 6px);
    background: color-mix(in srgb, var(--ctrl-accent-default, #4a90d9) 8%, transparent);
    border: 1px solid color-mix(in srgb, var(--ctrl-accent-default, #4a90d9) 28%, transparent);
    font: 12px/1.4 ui-sans-serif, system-ui; color: var(--ctrl-text-primary, #e8e8e8);
}

.fc-add { display: flex; gap: 6px; }
.fc-input {
    flex: 1; min-width: 0; padding: 6px 9px;
    border-radius: var(--ctrl-border-radius-sm, 6px);
    background: var(--ctrl-surface-base, #1e1e1e);
    border: 1px solid var(--ctrl-border-default, #3a3a3a);
    color: var(--ctrl-text-primary, #e8e8e8);
    font: 12px/1.3 ui-monospace, Menlo, Consolas, monospace;
}
.fc-input:focus { outline: none; border-color: var(--ctrl-accent-default, #4a90d9); }
.fc-quick { display: flex; flex-wrap: wrap; gap: 6px; }
.fc-btn {
    flex: none; padding: 6px 10px;
    border-radius: var(--ctrl-border-radius-sm, 6px);
    background: var(--ctrl-surface-dim, #2a2a2a);
    border: 1px solid var(--ctrl-border-default, #3a3a3a);
    color: var(--ctrl-text-primary, #e8e8e8);
    font: 600 12px/1.3 ui-sans-serif, system-ui; cursor: pointer;
}
.fc-btn:hover:not(:disabled) { border-color: var(--ctrl-accent-default, #4a90d9); }
.fc-btn:disabled { opacity: .45; cursor: default; }
.fc-btn-quiet { font-weight: 500; }
.fc-err { margin: 0; font: 12px/1.4 ui-sans-serif, system-ui; color: var(--ctrl-status-error, #d9534a); }

/* The folder's own appearance: a live preview beside the same three controls the Head tab gives a resource.
   The preview is deliberately large — it is the thing being chosen, and a 15px chip is too small to judge a
   colour by. Added 2026-07-24. */
.fc-appear { display: flex; align-items: flex-start; gap: 14px; }
.fc-appear-fields { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 10px; }
.fc-appear-field { display: flex; flex-direction: column; gap: 5px; }
.fc-appear-label {
    font: 700 9px/1 ui-sans-serif, system-ui; letter-spacing: .1em; text-transform: uppercase;
    color: var(--ctrl-text-secondary, #a0a0a0);
}
.fc-appear-swatch {
    flex: none; width: 52px; height: 52px;
    display: inline-flex; align-items: center; justify-content: center;
    border-radius: var(--ctrl-border-radius-md, 8px);
    box-shadow: inset 0 0 0 1px rgb(0 0 0 / 18%);
}
.fc-appear-swatch--svg { overflow: hidden; box-shadow: inset 0 0 0 1px var(--ctrl-border-default, #3a3a3a); }
.fc-appear-swatch--svg > svg { width: 100%; height: 100%; display: block; }
.fc-appear-swatch--none {
    background: var(--ctrl-fill-default, #262626);
    color: var(--ctrl-design-2, #5db45f);
    box-shadow: inset 0 0 0 1px var(--ctrl-border-default, #3a3a3a);
}
.fc-appear-colour { display: flex; align-items: center; gap: 8px; }
.fc-appear-pick {
    width: 46px; height: 30px; flex: none; padding: 2px; cursor: pointer; background: none;
    border: 1px solid var(--ctrl-border-default, #3a3a3a);
    border-radius: var(--ctrl-border-radius-sm, 4px);
}
.fc-appear-svg { min-height: 62px; resize: vertical; font-family: ui-monospace, Menlo, Consolas, monospace; }

/* Display columns (A4c) — an ordered list of Key → Slot rows with reorder + remove. */
.fc-cols { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 4px; }
.fc-col {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 4px 6px;
    border: 1px solid var(--ctrl-border-default, #333);
    border-radius: 6px;
    background: var(--ctrl-surface-dim, #232323);
}
.fc-col-order { display: flex; flex-direction: column; gap: 1px; }
.fc-col-move {
    display: flex;
    padding: 0;
    border: none;
    background: none;
    color: var(--ctrl-text-secondary, #a0a0a0);
    cursor: pointer;
    line-height: 0;
}
.fc-col-move:hover:not(:disabled) { color: var(--ctrl-text-primary, #e8e8e8); }
.fc-col-move:disabled { opacity: .3; cursor: default; }
.fc-col-key { font: 13px/1.3 ui-sans-serif, system-ui; color: var(--ctrl-text-primary, #e8e8e8); word-break: break-all; }
.fc-col-reads { margin-left: auto; font: 11px/1.3 ui-sans-serif, system-ui; color: var(--ctrl-text-secondary, #a0a0a0); }

/* ── Structure navigator (GdxStructure, in the Details tab) ────────────────────
   The object and its nested elements: a bold dot per level, the element name, and its type pushed to the
   right. Indentation alone carries the nesting — no guide lines, no chrome. */
.gs { display: flex; flex-direction: column; gap: 14px; }

/* The heading is a row, not just text: a reading can carry an action of its own (the Details panel hangs Edit
   off the Model heading) and it belongs beside the title rather than floating above it. Added 2026-07-24. */
.gs-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.gs-head-text {
    font: 700 11px/1 ui-sans-serif, system-ui; letter-spacing: .09em; text-transform: uppercase;
    color: var(--ctrl-text-secondary, #a0a0a0);
}
.gs-head-actions { display: flex; align-items: center; gap: 8px; flex: none; }

.gs-empty { margin: 0; color: var(--ctrl-text-secondary, #a0a0a0); font: 12px/1.4 ui-sans-serif, system-ui; }

.gs-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 12px; }
.gs-row { display: flex; align-items: center; gap: 10px; }
.gs-key { display: flex; align-items: center; gap: 10px; min-width: 0; }
.gs-dot { width: 9px; height: 9px; border-radius: 50%; flex: none; background: var(--ctrl-accent-default, #4a90d9); }
.gs-dot--0 { background: #e8e8e8; }
.gs-dot--1 { background: #4a9eff; }
.gs-dot--2 { background: #a06cf0; }
.gs-dot--3 { background: #39c0a8; }
.gs-dot--4 { background: #e0913c; }
.gs-name { font: 700 13.5px/1.2 ui-sans-serif, system-ui; color: var(--ctrl-text-primary, #e8e8e8); word-break: break-word; }
.gs-type {
    margin-left: auto; padding-left: 14px; flex: none;
    font: 500 12.5px/1.2 ui-sans-serif, system-ui; color: var(--ctrl-text-secondary, #8c8c8c);
}

/* The Properties profile's trailing column. Same slot as the type, but monospaced and in the reading colour:
   a type is a label about the element, a value is the element's content, and they should not look alike when
   the Full profile puts them in the same column on adjacent rows. Added 2026-07-21. */
.gs-value {
    margin-left: auto; padding-left: 14px; flex: none;
    max-width: 55%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    font: 500 12.5px/1.2 ui-monospace, Menlo, Consolas, monospace;
    color: var(--ctrl-text-primary, #e8e8e8);
}

/* Column mode (Columns="true"): names down one column, values down another on their own slightly lifted
   background, so the values read as a column you can run your eye down instead of a tag trailing each name.
   Rows sit tight together — the tinted cells do the separating that the gap used to. The name cell keeps its
   inline padding-left for depth, which beats the shorthand below. Added 2026-07-24. */
.gs-list--cols { gap: 2px; }
/* column-gap sets the cells apart so the row reads as distinct columns rather than one continuous band — the
   grid-template-columns is set inline per row (it varies with which columns are toggled on). Changed 2026-07-24. */
.gs-list--cols .gs-row { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 45%); column-gap: 6px; row-gap: 0; align-items: stretch; }
.gs-list--cols .gs-key { padding: 6px 10px 6px 0; }
.gs-list--cols .gs-name { word-break: break-word; }
.gs-list--cols .gs-value,
.gs-list--cols .gs-type {
    margin-left: 0; padding: 6px 10px; flex: auto; max-width: none;
    display: flex; align-items: center;
    overflow: hidden; text-overflow: clip; white-space: normal; word-break: break-word;
    background: var(--ctrl-surface-dim, #2a2a2a);
    border-radius: var(--ctrl-border-radius-sm, 4px);
}

/* A value cell wraps rather than ellipsising — in a column this narrow a truncated value tells you nothing —
   but one long blob (a spectral curve is 31 comma-separated numbers) would otherwise own the whole panel.
   Ten lines, then it clips: enough to recognise what you are looking at, and the full text is on the cell's
   title. The inner span is what gets line-clamped, since the cell itself is a flex box. Added 2026-07-24. */
.gs-list--cols .gs-value > span,
.gs-list--cols .gs-type > span {
    display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 10;
    overflow: hidden; min-width: 0;
}

/* A pickable cell (an edge's resource, or a predicate/type cell) is a button, not text: clicking it opens the
   respective chooser. It keeps the column cell's look but reads as actionable — a pointer, an accent border on
   hover — and shows a link-toned placeholder ("Choose…"/"Set…") when empty, so the cell is still discoverable.
   Added 2026-07-24. */
.gs-list--cols button.gs-pick {
    cursor: pointer; text-align: left; width: 100%;
    border: 1px solid transparent;
    font: 500 12.5px/1.2 ui-monospace, Menlo, Consolas, monospace;
    color: var(--ctrl-text-primary, #e8e8e8);
}
.gs-list--cols button.gs-pick:hover:not(:disabled) {
    border-color: var(--ctrl-accent-default, #4a90d9);
    background: color-mix(in srgb, var(--ctrl-accent-default, #4a90d9) 12%, var(--ctrl-surface-dim, #2a2a2a));
}
.gs-list--cols button.gs-pick:disabled { opacity: .6; cursor: default; }
.gs-edge-empty { color: var(--ctrl-accent-default, #4a90d9); font-style: italic; }

/* The predicate/type column toggles on the Model heading — small checkbox labels sitting left of the Edit
   button. Added 2026-07-24. */
.gs-toggle { display: inline-flex; align-items: center; gap: 5px; cursor: pointer;
    font: 600 11px/1 ui-sans-serif, system-ui; color: var(--ctrl-text-secondary, #a0a0a0); }
.gs-toggle input { cursor: pointer; margin: 0; }
.gs-toggle:hover { color: var(--ctrl-text-primary, #e8e8e8); }

/* ── Action surface (ActionRunner) ──────────────────────────────────────────
   An action opened on its own surface, under the same breadcrumb a view gets. Replaced an input row wedged
   under the action grid, which had to stay small enough not to disturb the grid — the wrong constraint for the
   thing you are actually doing. Added 2026-07-21. */
.ar { display: flex; flex-direction: column; gap: 14px; }
.ar-desc { margin: 0; font: 13px/1.45 ui-sans-serif, system-ui; color: var(--ctrl-text-secondary, #a0a0a0); }
.ar-input { display: flex; flex-direction: column; gap: 10px; }
.ar-field { display: flex; flex-direction: column; gap: 6px; }
.ar-label {
    font: 700 10px/1 ui-sans-serif, system-ui; letter-spacing: .1em; text-transform: uppercase;
    color: var(--ctrl-text-secondary, #a0a0a0);
}
.ar-colorrow { display: flex; align-items: center; gap: 10px; }
.ar-color {
    width: 52px; height: 34px; padding: 2px; cursor: pointer; background: none;
    border: 1px solid var(--ctrl-border-default, #3a3a3a);
    border-radius: var(--ctrl-border-radius-sm, 4px);
}
.ar-hex { font: 600 12.5px/1 ui-monospace, Menlo, Consolas, monospace; color: var(--ctrl-text-primary, #e8e8e8); }
.ar-text {
    padding: 8px 10px; font: 13px/1.2 ui-sans-serif, system-ui;
    color: var(--ctrl-text-primary, #e8e8e8); background: var(--ctrl-fill-default, #262626);
    border: 1px solid var(--ctrl-border-default, #3a3a3a);
    border-radius: var(--ctrl-border-radius-sm, 4px);
}

/* Big enough to actually judge the colour — the reason this got its own surface rather than a row. */
.ar-swatch {
    height: 84px; border-radius: var(--ctrl-border-radius-md, 6px);
    border: 1px solid var(--ctrl-border-default, #3a3a3a);
}

.ar-actions { display: flex; }
.ar-run {
    padding: 8px 16px; cursor: pointer;
    font: 600 13px/1 ui-sans-serif, system-ui;
    color: var(--ctrl-accent-on, #fff);
    background: var(--ctrl-accent-default, #4a90d9);
    border: 1px solid var(--ctrl-accent-default, #4a90d9);
    border-radius: var(--ctrl-border-radius-sm, 5px);
}
.ar-run:hover:not(:disabled) { background: var(--ctrl-accent-hover, #5ba0e9); }
.ar-run:disabled { opacity: .5; cursor: default; }

.ar-msg { margin: 0; font: 500 12.5px/1.45 ui-sans-serif, system-ui; }
.ar-msg--ok  { color: var(--ctrl-status-success, #4caf50); }
.ar-msg--bad { color: var(--ctrl-status-error, #e05252); }

/* The Details tab's Edit affordance — a resource only, and it opens the workspace editor rather than acting
   inside this panel. Added 2026-07-21. */
.sp-edit {
    display: inline-flex; align-items: center; gap: 8px; align-self: flex-start;
    padding: 7px 12px; cursor: pointer;
    font: 600 12.5px/1 ui-sans-serif, system-ui;
    color: var(--ctrl-accent-on, #fff);
    background: var(--ctrl-accent-default, #4a90d9);
    border: 1px solid var(--ctrl-accent-default, #4a90d9);
    border-radius: var(--ctrl-border-radius-sm, 5px);
}
.sp-edit:hover { background: var(--ctrl-accent-hover, #5ba0e9); border-color: var(--ctrl-accent-hover, #5ba0e9); }

/* Sitting on the Model heading rather than on a line of its own, so it shrinks to match an 11px label instead
   of dominating it. Added 2026-07-24. */
.sp-edit--inline { gap: 6px; padding: 4px 10px; font-size: 11.5px; }

/* ── Workspace app header icon ───────────────────────────────────────────────
   Every workspace app leads its header with its own glyph — the same AppIcon the frame list and the tree
   render for that app, in a 63px tile — so the folder, list, grid, document and table workspaces all open
   looking like themselves. One rule here rather than per-app scoped copies: it is the same treatment
   everywhere, and this is unscoped, so it reaches the svg AppIcon renders without each app needing a ::deep of
   its own (the folder and table apps keep their own scoped copies for their bespoke layouts). Added 2026-07-24. */
.app-head-icon {
    flex: none; width: 63px; height: 63px;
    display: flex; align-items: center; justify-content: center;
    border-radius: var(--ctrl-border-radius-md, 10px);
    background: var(--ctrl-fill-default, #262626);
    color: var(--ctrl-design-3, #e0a23a);
}
.app-head-icon svg { width: 100%; height: 100%; }

/* ── Workspace resource editor (ResourceEditor) ─────────────────────────────
   Fills the centre pane while a resource is open for editing: a bar naming what is being edited with the
   save/close actions, then the row editor beside its element inspector. Added 2026-07-21. Modified 2026-10-07: the
   prefix is rse-, because the row editor inside it uses re- for its own classes and these rules clipped it. */
.rse { display: flex; flex-direction: column; height: 100%; min-height: 0; }
.rse-bar {
    flex: none; display: flex; align-items: center; gap: 12px;
    padding: 10px 14px;
    border-bottom: 1px solid var(--ctrl-border-default, #3a3a3a);
    background: var(--ctrl-surface-raised, #242424);
}
.rse-what { display: flex; align-items: baseline; gap: 8px; min-width: 0; }
.rse-label {
    font: 700 10px/1 ui-sans-serif, system-ui; letter-spacing: .1em; text-transform: uppercase;
    color: var(--ctrl-text-secondary, #a0a0a0);
}
.rse-id {
    font: 600 13px/1.2 ui-monospace, Menlo, Consolas, monospace; color: var(--ctrl-text-primary, #e8e8e8);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.rse-dirty { color: var(--ctrl-accent-default, #4a90d9); font-size: 18px; line-height: 1; }
.rse-actions { margin-left: auto; display: flex; gap: 8px; flex: none; }
.rse-btn {
    padding: 6px 14px; cursor: pointer;
    font: 600 12.5px/1 ui-sans-serif, system-ui;
    color: var(--ctrl-text-primary, #e8e8e8); background: none;
    border: 1px solid var(--ctrl-border-default, #3a3a3a);
    border-radius: var(--ctrl-border-radius-sm, 5px);
}
.rse-btn:hover:not(:disabled) { background: var(--ctrl-fill-hover, #2c2c2c); }
.rse-btn:disabled { opacity: .5; cursor: default; }
.rse-btn--primary {
    color: var(--ctrl-accent-on, #fff);
    background: var(--ctrl-accent-default, #4a90d9);
    border-color: var(--ctrl-accent-default, #4a90d9);
}
.rse-btn--primary:hover:not(:disabled) { background: var(--ctrl-accent-hover, #5ba0e9); }

.rse-error {
    flex: none; margin: 0; padding: 8px 14px;
    font: 500 12px/1.4 ui-sans-serif, system-ui; color: var(--ctrl-status-error, #e05252);
    border-bottom: 1px solid var(--ctrl-border-default, #3a3a3a);
}
.rse-empty { margin: 0; padding: 14px; color: var(--ctrl-text-secondary, #a0a0a0); font: 13px/1.4 ui-sans-serif, system-ui; }

.rse-split { flex: 1; min-height: 0; display: flex; }
.rse-main { flex: 1; min-width: 0; min-height: 0; overflow: auto; padding: 12px 14px; }
.rse-side {
    flex: 0 0 300px; min-height: 0; overflow: auto; padding: 12px 14px;
    border-left: 1px solid var(--ctrl-border-default, #3a3a3a);
    background: var(--ctrl-surface-dim, #141414);
}
.rse-side-head {
    font: 700 11px/1 ui-sans-serif, system-ui; letter-spacing: .09em; text-transform: uppercase;
    color: var(--ctrl-text-secondary, #a0a0a0); margin-bottom: 10px;
}

/* An open view fills the Main tab under the object header. Its breadcrumb is a back pill (returns to the
   actions and views) beside the open view's name, ringed and in the accent colour (see FrameSidePanel.razor). */
/* The tab body spaces its blocks 18px apart, which leaves the crumbs floating between their two rules. Pull
   them back in so the breadcrumb reads as one band with its separators rather than three loose rows. */
.sp-crumbs { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; margin: -9px 0; }
.sp-crumb-back {
    display: inline-flex; align-items: center; gap: 6px; flex: none;
    padding: 7px 14px 7px 11px; border-radius: 999px;
    background: var(--ctrl-surface-dim, #141414); border: 1px solid var(--ctrl-border-default, #3a3a3a);
    font: 600 12.5px/1 ui-sans-serif, system-ui; color: var(--ctrl-text-primary, #e8e8e8); cursor: pointer;
}
.sp-crumb-back:hover:not(:disabled) { background: var(--ctrl-fill-hover, #3d3d3d); }
.sp-crumb-now {
    display: inline-flex; align-items: center; gap: 9px; min-width: 0;
    font: 700 14px/1.2 ui-sans-serif, system-ui; color: var(--ctrl-accent-default, #4a90d9); word-break: break-word;
}
.sp-crumb-ring {
    width: 10px; height: 10px; flex: none;
    border: 2px solid currentColor; border-radius: 50%;
}

/* The rule under the breadcrumb, separating "where you are" from the thing itself. Its own class rather than
   the plain .sp-divider because the crumbs carry a -9px bottom margin (see above), which would otherwise
   pull the rule up against them — the top margin here is what pays that back. Added 2026-07-25. */
.sp-crumb-rule { height: 1px; background: var(--ctrl-border-default, #3a3a3a); margin: 11px 0 0; }

.sp-view-render { display: flex; flex-direction: column; min-height: 0; }

/* ── Mosaic view components (mosaic:group / text / form / field) ────────────────
   The server sends a tree of components and the client renders it; until now nothing styled that output, so
   a view arrived as bare divs and inline <label>s — which is why an embedded form ran its fields along one
   line instead of stacking them. These are the app's clothes for the shared vocabulary, deliberately reusing
   the same tokens and proportions as the action surface (.ar-*) so a server-driven form and a hand-built one
   look like the same product. Added 2026-07-25. */
.m-group { display: flex; flex-direction: column; gap: 10px; min-width: 0; }

/* A label/value pair. It WRAPS rather than truncating: in a panel this narrow an event's timestamp and its
   message do not fit on one line, and a log you have to widen a pane to read is not a log. */
.m-text { display: flex; flex-wrap: wrap; align-items: baseline; gap: 3px 10px; min-width: 0; }
.m-text-label {
    font: 600 11.5px/1.35 ui-sans-serif, system-ui;
    color: var(--ctrl-text-secondary, #a0a0a0); word-break: break-word;
}
.m-text-value {
    font: 13px/1.4 ui-sans-serif, system-ui;
    color: var(--ctrl-text-primary, #e8e8e8); word-break: break-word;
}

/* A section rule. Its margin is deliberately larger than the group's own gap: the point of a divider is that
   what follows is a DIFFERENT thing, and a rule with the same spacing as everything else does not say that. */
.m-divider { height: 0; margin: 6px 0; border-top: 1px solid var(--ctrl-border-default, #3a3a3a); }

/* A form: fields stacked, each label above its control, and the submit set apart from the last field so it
   reads as the end of the form rather than another row of it. */
.m-form { display: flex; flex-direction: column; gap: 12px; min-width: 0; }
.m-field { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.m-field-label {
    font: 700 10px/1 ui-sans-serif, system-ui; letter-spacing: .1em; text-transform: uppercase;
    color: var(--ctrl-text-secondary, #a0a0a0);
}
.m-field-value { font: 13px/1.4 ui-sans-serif, system-ui; color: var(--ctrl-text-primary, #e8e8e8); }
.m-field-input {
    width: 100%; box-sizing: border-box;
    padding: 8px 10px; font: 13px/1.2 ui-sans-serif, system-ui;
    color: var(--ctrl-text-primary, #e8e8e8); background: var(--ctrl-fill-default, #262626);
    border: 1px solid var(--ctrl-border-default, #3a3a3a);
    border-radius: var(--ctrl-border-radius-sm, 4px);
}
.m-field-input:focus-visible { outline: none; border-color: var(--ctrl-accent-default, #4a90d9); }
.m-field-input:disabled { opacity: .55; cursor: default; }
.m-field-multiline { min-height: 68px; line-height: 1.45; resize: vertical; }
.m-field-color {
    width: 52px; height: 34px; padding: 2px; cursor: pointer; background: none;
    border: 1px solid var(--ctrl-border-default, #3a3a3a);
    border-radius: var(--ctrl-border-radius-sm, 4px);
}

.m-form-actions { display: flex; margin: 4px 0 2px; }
.m-form-submit {
    padding: 8px 16px; cursor: pointer;
    font: 600 13px/1 ui-sans-serif, system-ui;
    color: var(--ctrl-accent-on, #fff);
    background: var(--ctrl-accent-default, #4a90d9);
    border: 1px solid var(--ctrl-accent-default, #4a90d9);
    border-radius: var(--ctrl-border-radius-sm, 5px);
}
.m-form-submit:hover:not(:disabled) { background: var(--ctrl-accent-hover, #5ba0e9); }
.m-form-submit:disabled { opacity: .5; cursor: default; }

.m-form-msg { margin: 0; font: 500 12.5px/1.45 ui-sans-serif, system-ui; color: var(--ctrl-status-success, #4caf50); }
.m-form-msg--bad { color: var(--ctrl-status-error, #e05252); }

/* The action/view component space — a distinct region below the main view where a chosen view or action
   renders, or the palettes rest. The top border separates it from the main view above. Added 2026-07-22. */
.sp-avspace {
    display: flex; flex-direction: column; gap: 10px;
    margin-top: 10px; padding-top: 12px;
    border-top: 1px solid var(--ctrl-border-default, #3a3a3a);
    min-height: 0;
}

/* The preview colour the color action writes, at rest in the space. */
.sp-preview { display: flex; align-items: center; gap: 9px; }
.sp-preview-swatch {
    width: 24px; height: 24px; flex: none;
    border-radius: 6px; border: 1px solid var(--ctrl-border-default, #3a3a3a);
}
.sp-preview-label { font-size: 12px; color: var(--ctrl-text-secondary, #a0a0a0); }

/* ── Palettes (ActionPalette + ViewPalette): a grid of circular icon buttons ─── */
.pal { display: flex; flex-direction: column; gap: 12px; }
.pal-head {
    font: 700 11px/1 ui-sans-serif, system-ui; letter-spacing: .09em; text-transform: uppercase;
    color: var(--ctrl-text-secondary, #a0a0a0);
}
.pal-empty { margin: 0; color: var(--ctrl-text-secondary, #a0a0a0); font: 12px/1.4 ui-sans-serif, system-ui; }

.pal-grid { display: grid; gap: 16px 8px; }
.pal-grid--actions { grid-template-columns: repeat(4, 1fr); }

/* Views read as cards, two across: a round-rect holding a circular icon chip with the name (and what the
   view shows) to its right — the "linked actions" shape from the design. */
.pal-grid--views { grid-template-columns: repeat(2, 1fr); gap: 10px; }
.pal-card {
    display: flex; align-items: center; gap: 11px; text-align: left; min-width: 0;
    padding: 11px 12px; border-radius: var(--ctrl-border-radius-md, 10px);
    background: var(--ctrl-surface-dim, #141414); border: 1px solid var(--ctrl-border-default, #3a3a3a);
    cursor: pointer;
}
.pal-card:hover:not(:disabled) { background: var(--ctrl-fill-hover, #3d3d3d); }
.pal-card:disabled { opacity: .45; cursor: default; }
.pal-card-ic {
    display: inline-flex; align-items: center; justify-content: center; flex: none;
    width: 34px; height: 34px; border-radius: 50%; border: 1px solid transparent;
}
.pal-card-text { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.pal-card-label {
    font: 700 13px/1.2 ui-sans-serif, system-ui; color: var(--ctrl-text-primary, #e8e8e8);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.pal-card-desc {
    font: 500 11.5px/1.25 ui-sans-serif, system-ui; color: var(--ctrl-text-secondary, #a0a0a0);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* One cell = a circular icon chip over a centred label. */
.pal-cell {
    display: flex; flex-direction: column; align-items: center; gap: 8px;
    background: none; border: none; padding: 2px 0; cursor: pointer;
}
.pal-cell:hover:not(:disabled) { background: none; }
.pal-cell:disabled { opacity: .45; cursor: default; }

.pal-ic {
    display: inline-flex; align-items: center; justify-content: center;
    width: 52px; height: 52px; border-radius: 50%;
    border: 1px solid transparent; transition: filter .12s ease;
}
.pal-cell:hover:not(:disabled) .pal-ic { filter: brightness(1.15); }

.pal-ic--accent {
    color: var(--ctrl-accent-default, #4a90d9);
    background: color-mix(in srgb, var(--ctrl-accent-default, #4a90d9) 16%, transparent);
    border-color: color-mix(in srgb, var(--ctrl-accent-default, #4a90d9) 38%, transparent);
}
.pal-ic--neutral {
    color: var(--ctrl-text-secondary, #a0a0a0);
    background: color-mix(in srgb, var(--ctrl-text-primary, #e8e8e8) 8%, transparent);
    border-color: var(--ctrl-border-default, #3a3a3a);
}
.pal-ic--danger {
    color: var(--ctrl-status-error, #ef5350);
    background: color-mix(in srgb, var(--ctrl-status-error, #ef5350) 15%, transparent);
    border-color: color-mix(in srgb, var(--ctrl-status-error, #ef5350) 38%, transparent);
}
.pal-label { font: 500 12px/1.2 ui-sans-serif, system-ui; color: var(--ctrl-text-primary, #e8e8e8); text-align: center; }

.pal-note { margin: 0; font: italic 12px/1.4 ui-sans-serif, system-ui; color: var(--ctrl-text-secondary, #a0a0a0); }

/* Inline name field revealed for actions that require input. */
.pal-input-row { display: flex; align-items: center; gap: 6px; }
.pal-input { flex: 1; min-width: 0; }
.pal-input-ok { flex: none; padding: 6px 12px; font-size: 12px; }
.pal-input-cancel { flex: none; padding: 6px 9px; font-size: 12px; }
