/** Shared presentation for @infraternal/ui browser primitives. */

/* One type scale per platform (TASTE.md section 2). Desktop follows the macOS sizes, touch the iOS
   sizes. Every product surface (top bar, sidebar, palette, tooltip, toast, windows) uses these
   tokens rather than its own pixel values. --row is the list row height (macOS sidebar 28 px, iOS 44 px). --text-input stays 16 px on touch so iOS never zooms. */
:root { --ui-press-scale: 0.97; --ui-press-scale-small: 0.94; --ui-press-scale-soft: 0.98; --text-body: 13px; --text-secondary: 12px; --text-caption: 11px; --text-input: 13px; --row: 28px; }
@media (pointer: coarse) { :root { --text-body: 16px; --text-secondary: 15px; --text-caption: 13px; --text-input: 16px; --row: 44px; } }

/* Focus rings appear only while the person navigates with the keyboard (focus-ring.ts sets
   data-focus-ring="on"); clicks, taps and shortcuts that move focus in script draw none. */
:focus-visible { outline: none; }
[data-focus-ring="on"] :focus-visible { outline: 2px solid var(--focus-ring, var(--accent, #7a9cff)); outline-offset: 2px; }
.countup { font-variant-numeric: tabular-nums; }
.busy-count { font: var(--fw-label, 500) 13px/1 var(--font-face, "Maple Mono NF", ui-monospace, monospace); color: rgb(0 0 0 / 0.6); }
/* Icons (icons.ts) follow the product's icon scale: 16 px on desktop, 18 px on touch, 1.5 stroke. */
.ui-icon { width: 16px; height: 16px; flex: none; fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
@media (pointer: coarse) { .ui-icon { width: 18px; height: 18px; } }
.terminal-text-overlay { position: absolute; z-index: 2; pointer-events: none; }

/* The card tint is read by the element that carries it and by nothing inside it, so it does not inherit: a change then
   restyles the glass surfaces and not their descendants too (themes.ts `installLevelRules` sets it per element). */
@property --overlay { syntax: "*"; inherits: false; }
@property --ui-overlay-halo { syntax: "*"; inherits: false; }
@property --ui-overlay-blur { syntax: "*"; inherits: false; }

/* The one material's blur and saturation: surfaces and pills read these two tokens, so tuning them
   here tunes every floating thing at once. */
:root { --ui-overlay-blur: 16px; --ui-overlay-saturation: 160%; }

/* One theme-aware glass material for every floating surface (D72, D160): popovers, sheets,
   palette, sidebar and tooltips, never in-page controls. No top-edge highlight (TASTE §1).
   Instances can tune the blur, saturation, border or shadow through custom properties. */
.ui-overlay-surface, .ui-panel {
  background: var(--ui-overlay-background, var(--overlay));
  border: 1px solid var(--ui-overlay-border, var(--overlay-line));
  box-shadow: var(--ui-overlay-shadow, var(--shadow));
  color: var(--ui-overlay-color, var(--fg));
  /* A soft halo in the tint colour keeps text legible over a dense terminal without adding tint. */
  text-shadow: 0 0 1px var(--ui-overlay-halo, transparent), 0 0 2px var(--ui-overlay-halo, transparent), 0 0 3px var(--ui-overlay-halo, transparent), 0 0 4px var(--ui-overlay-halo, transparent), 0 0 6px var(--ui-overlay-halo, transparent), 0 0 10px var(--ui-overlay-halo, transparent);
  -webkit-backdrop-filter: blur(var(--ui-overlay-blur)) saturate(var(--ui-overlay-saturation));
  backdrop-filter: blur(var(--ui-overlay-blur)) saturate(var(--ui-overlay-saturation));
}
/* On the thin material secondary text is a step stronger than on a page (contrast over a busy terminal comes
   from text colour and the halo, never from more tint). */
.ui-overlay-surface, .ui-panel { --ui-text-secondary: color-mix(in oklab, var(--fg) 80%, var(--bg)); }
/* Form controls reset text-shadow in the browser's own styles; they take the halo too. */
.ui-overlay-surface :is(button, input, textarea, select) { text-shadow: inherit; }
.ui-overlay-surface[data-overlay-size="large"] { --ui-overlay-shadow: var(--overlay-shadow-large, var(--shadow)); }
/* Trigger labels stay one line; only point previews carrying this per-call attribute may wrap. */
.tip-label { display: block; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tip-pos[data-tip-lines="2"], .tip-measure[data-tip-lines="2"] { --tip-content-max: min(258px, calc(100vw - 38px)); }
.tip-pos[data-tip-lines="2"] .tip { max-width: calc(var(--tip-content-max) + 22px); }
.tip-pos[data-tip-lines="2"] .tip-label:not(.is-leaving) { display: -webkit-box; max-height: 2.4em; overflow: hidden; overflow-wrap: anywhere;
  -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; text-overflow: ellipsis; white-space: normal; }
.tip-measure[data-tip-lines="2"] { box-sizing: border-box; max-width: var(--tip-content-max); line-height: 1.2; overflow-wrap: anywhere; white-space: normal; }
.ui-button {
  min-height: var(--ui-button-height, 44px);
  padding: var(--ui-button-padding, 0 12px);
  border-radius: var(--ui-button-radius, 8px);
  color: var(--fg);
  font: inherit;
  cursor: pointer;
  transition: background 140ms ease, color 140ms ease;
}
/* Optional button kinds share states and motion; instances supply only geometry
   and typography through variables. Plain ui-button callers keep their defaults. */
.ui-button[data-button] {
  display: inline-flex;
  min-width: var(--ui-button-min-width, 0);
  height: var(--ui-button-height, 44px);
  align-items: center;
  justify-content: center;
  gap: var(--ui-button-gap, 10px);
  border: 0;
  font: var(--ui-button-font, inherit);
  letter-spacing: var(--ui-button-letter-spacing, normal);
  transition: var(--ui-button-transition, transform 140ms var(--ease-out, ease), background-color 160ms ease, color 120ms ease);
}
/* A solid fill carries no text halo: the glass halo (above) would glow into the fill around every glyph, the
   "blurred or doubled" label of the disabled and enabled primary alike (TASTE: button text is crisp in every state). */
.ui-button[data-button="primary"] { background: var(--ui-button-background, var(--fg)); color: var(--ui-button-color, var(--bg)); text-shadow: none; }
.ui-button[data-button="secondary"] { background: var(--ui-button-background, none); color: var(--ui-button-color, var(--fg-2, var(--fg))); }
.ui-button[data-button]:active:not(:disabled) { transform: scale(var(--ui-press-scale)); }
.ui-button[data-button]:disabled { cursor: default; }
/* Disabled solid buttons remain opaque over content beneath their surface. */
/* Lower emphasis comes from colour alone. The label keeps at least 3:1 against its fill in both themes: the fill is a
   quiet tint of the foreground, and the label a stronger one (measured in test/top-centre.spec.ts). */
.ui-button[data-button="primary"]:disabled { background: var(--ui-button-disabled-background, color-mix(in oklab, var(--fg) 12%, var(--bg))); color: var(--ui-button-disabled-color, color-mix(in oklab, var(--fg) 58%, var(--bg))); }
@media (hover: hover) and (pointer: fine) { .ui-button[data-button="secondary"]:hover:not(:disabled) { color: var(--ui-button-hover-color, var(--fg)); } }
.ui-source-icon { display: block; width: 16px; height: 16px; flex: none; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }

/* One native switch keeps pointer, keyboard, and assistive-technology state aligned. */
.ui-switch {
  position: relative;
  display: inline-block;
  width: 48px;
  height: 46px;
  flex: none;
  margin: 0;
  border: 0;
  border-radius: 999px;
  appearance: none;
  background: transparent;
  color: inherit;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.ui-switch::before, .ui-switch::after {
  position: absolute;
  top: 50%;
  content: "";
  transform: translateY(-50%);
}
.ui-switch::before {
  left: 1px;
  width: 46px;
  height: 26px;
  border: 1px solid var(--overlay-line);
  border-radius: 999px;
  background: var(--overlay-solid);
  transition: background 180ms ease-out, border-color 180ms ease-out;
}
.ui-switch::after {
  left: 4px;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--fg-2);
  box-shadow: 0 1px 2px rgb(0 0 0 / 0.18);
  transition: transform 180ms cubic-bezier(0.23, 1, 0.32, 1), background 180ms ease-out;
}
.ui-switch:checked::before { border-color: var(--accent); background: var(--accent); }
.ui-switch:checked::after { transform: translate(20px, -50%); background: var(--bg); }
.ui-switch:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.ui-switch:disabled { cursor: default; opacity: 0.5; }

/* The shared toast can carry a safe, contextual action without cloning overlay material. */
.ui-toast {
  position: fixed;
  z-index: 900;
  right: max(12px, env(safe-area-inset-right));
  bottom: max(16px, env(safe-area-inset-bottom));
  left: max(12px, env(safe-area-inset-left));
  display: flex;
  width: max-content;
  max-width: min(520px, calc(100% - 24px));
  min-height: 54px;
  align-items: center;
  gap: 12px;
  margin-inline: auto;
  padding: 7px 9px 7px 12px;
  border-radius: 15px;
  opacity: 1;
  /* No transform at rest: settled toast text stays on the pixel grid. */
  transform: none;
  transform-origin: center bottom;
  transition: opacity 170ms ease-out, transform 190ms cubic-bezier(0.23, 1, 0.32, 1);
}
/* A toast is a small card of its own, not text over a busy terminal: the material's halo would only smear its glyphs. */
.ui-toast, .ui-toast * { text-shadow: none; }
.ui-toast[hidden] { display: none !important; }
.ui-toast[data-open="false"] { opacity: 0; transform: translateY(8px) scale(0.97); pointer-events: none; }
/* One type scale per platform (docs/design/TASTE.md): desktop 13 px body, touch 16 px. */
.ui-toast { min-height: 44px; gap: 10px; padding: 6px 8px 6px 12px; border-radius: 14px; }
/* A long message with actions stacks them under it (toast.ts sets data-stacked) instead of squeezing the text beside them. */
/* Explicit cells: mark | message | close on the first row, the actions on the second. Auto-placement left the glyph, text and
   close in separate rows with an empty column between them at phone width. */
.ui-toast[data-stacked="true"] { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: start; column-gap: 10px; row-gap: 0; }
.ui-toast[data-stacked="true"] .ui-toast-marker, .ui-toast[data-stacked="true"] .ui-toast-glyph { grid-column: 1; grid-row: 1; margin-top: 4px; }
.ui-toast[data-stacked="true"] .ui-toast-message { grid-column: 2; grid-row: 1; padding-top: 5px; padding-bottom: 5px; }
.ui-toast[data-stacked="true"] .ui-toast-close { grid-column: 3; grid-row: 1; }
.ui-toast[data-stacked="true"] .ui-toast-actions { grid-column: 2 / -1; grid-row: 2; justify-self: end; }
.ui-toast-message { min-width: 0; color: var(--fg); font-size: var(--text-body); font-weight: var(--fw-body); line-height: 1.4; }
.ui-toast-marker { display: grid; width: 22px; height: 22px; flex: none; place-items: center; }
.ui-toast-marker .tick { width: 20px; height: 20px; }
.ui-toast-marker .tick-fill { background: var(--ok); }
.ui-toast-marker .tick svg { width: 14px; height: 14px; stroke: var(--bg); }
.ui-toast-actions { display: flex; flex: none; align-items: center; gap: 2px; }
.ui-toast-actions[hidden] { display: none !important; }
.ui-toast-action { min-height: 32px; flex: none; padding: 0 8px; border-radius: 8px; color: var(--accent) !important; font-size: var(--text-body); font-weight: var(--fw-label); white-space: nowrap; }
.ui-toast-action[hidden] { display: none !important; }
.ui-toast-marker .tick { width: 18px; height: 18px; }
/* A glyph in place of the tick (toast icon): neutral, so a waiting state never reads as success. */
.ui-toast-glyph { display: grid; width: 22px; height: 22px; flex: none; place-items: center; color: var(--fg-2); }
.ui-toast-glyph[hidden] { display: none; }
.ui-toast[data-tone="error"] .ui-toast-glyph { color: var(--danger); }
.ui-toast-glyph svg { fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.ui-toast[data-icon="true"] .ui-toast-marker { display: none; }
/* The optional dismiss control: a quiet icon, the same height as the action. */
.ui-toast-close { display: grid; width: 32px; min-height: 32px; flex: none; place-items: center; padding: 0; border-radius: 8px; color: var(--fg-3); }
.ui-toast-close[hidden] { display: none; }
.ui-toast-close svg { fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; }
@media (hover: hover) and (pointer: fine) { .ui-toast-close:hover { background: var(--hover); color: var(--fg); } }
@media (pointer: coarse) {
  .ui-toast-close { width: 44px; min-height: 44px; }
  .ui-toast { min-height: 54px; gap: 12px; padding: 7px 9px 7px 12px; border-radius: 15px; }
  .ui-toast-action { min-height: 44px; padding: 0 10px; }
  .ui-toast-marker .tick { width: 20px; height: 20px; }
}

@media (pointer: coarse) {
  .terminal-screen .ui-toast {
    bottom: calc(var(--keybar-inset, 0px) + var(--keybar-height, 0px) + var(--keybar-gap, 0px) + 10px + env(safe-area-inset-bottom));
  }
}
@media (prefers-reduced-motion: reduce) {
  .ui-switch::before, .ui-switch::after { transition: none; }
  .ui-spinner { animation: none; }
  .ui-toast { transition: opacity 150ms ease; transform: none; }
  .ui-toast[data-open="false"] { transform: none; }
}

@media (prefers-reduced-transparency: reduce) {
  .ui-overlay-surface {
    --ui-overlay-background: color-mix(in srgb, var(--overlay) 55%, var(--bg));
    --ui-overlay-blur: 0px;
    --ui-overlay-saturation: 100%;
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
}
@media (prefers-contrast: more) {
  .ui-overlay-surface { --ui-overlay-border: color-mix(in srgb, var(--fg) 72%, transparent); }
}

/* One spring drives the fill, tick draw-on and label treatment. */
.tick { position: relative; display: inline-grid; place-items: center; flex: none; width: 20px; height: 20px;
  border-radius: 6px; box-shadow: inset 0 0 0 var(--ui-tick-border-width, 1.5px) var(--ui-tick-border-color, rgb(255 255 255 / 0.35)); color: #000; will-change: transform; }
.tick-fill { position: absolute; inset: 0; border-radius: inherit; background: var(--fg); transform: scale(0); will-change: transform; }
.tick svg { position: relative; width: 78%; height: 78%; overflow: visible; }
.tick path { fill: none; stroke: currentColor; stroke-width: 2.6; stroke-linecap: round; stroke-linejoin: round; stroke-dasharray: 1; stroke-dashoffset: 1; }
.visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; opacity: 0; appearance: none; -webkit-appearance: none; margin: 0; }
.check { position: relative; transition: transform 140ms var(--ease); }
.check[data-pressed] { transform: scale(0.98); }
[data-focus-ring="on"] .check input:focus-visible + .tick { outline: 2px solid rgb(255 255 255 / 0.6); outline-offset: 2px; }

/* One reusable spinner mark; hosts tune size and color through instance variables. */
.ui-spinner {
  display: inline-block;
  width: var(--ui-spinner-size, 16px);
  height: var(--ui-spinner-size, 16px);
  flex: none;
  border: var(--ui-spinner-border-width, 1.5px) solid color-mix(in oklab, var(--ui-spinner-color, var(--accent)) 30%, transparent);
  border-top-color: var(--ui-spinner-color, var(--accent));
  border-radius: 50%;
  animation: ui-spinner-turn 720ms linear infinite;
}
.ui-spinner[hidden] { display: none !important; }
@keyframes ui-spinner-turn { to { transform: rotate(1turn); } }

/* A quiet, non-interactive step mark composes shared spinner and tick behavior. */
.ui-step-status {
  --ui-step-status-size: 20px;
  display: inline-grid;
  width: var(--ui-step-status-size);
  height: var(--ui-step-status-size);
  flex: none;
  place-items: center;
  color: var(--fg-3);
}
.ui-step-status > .ui-step-status-pending,
.ui-step-status > .ui-step-status-tick,
.ui-step-status > .ui-step-status-failed,
.ui-step-status > .ui-spinner { grid-area: 1 / 1; }
.ui-step-status-pending {
  width: var(--ui-step-status-dot-size, 7px);
  height: var(--ui-step-status-dot-size, 7px);
  border: 1.5px solid currentColor;
  border-radius: 50%;
}
.ui-step-status-tick { display: none; width: 18px; height: 18px; color: var(--bg); }
.ui-step-status-tick .tick-fill { background: var(--ok); }
.ui-step-status-tick .tick path { stroke: var(--bg); }
.ui-step-status-failed { display: none; width: 15px; height: 15px; fill: none; stroke: var(--danger); stroke-width: 1.8; stroke-linecap: round; }
.ui-step-status[data-state="running"] .ui-step-status-pending,
.ui-step-status[data-state="running"] .ui-step-status-tick,
.ui-step-status[data-state="running"] .ui-step-status-failed,
.ui-step-status[data-state="done"] .ui-step-status-pending,
.ui-step-status[data-state="done"] .ui-step-status-failed,
.ui-step-status[data-state="failed"] .ui-step-status-pending,
.ui-step-status[data-state="failed"] .ui-step-status-tick { display: none; }
.ui-step-status[data-state="done"] .ui-step-status-tick { display: grid; }
.ui-step-status[data-state="failed"] .ui-step-status-failed { display: block; }

/* Glitch text is monochrome and settles after its welcome interval. */
.glitch { position: relative; display: inline-block; white-space: nowrap; }
.glitch::before, .glitch::after { content: attr(data-text); position: absolute; top: 0; left: 0; width: 100%; color: inherit; background: #000; overflow: hidden; clip-path: inset(0 0 0 0); pointer-events: none; }
.glitch::after { transform: translateX(6px); text-shadow: var(--g-after-shadow); animation: ui-glitch-bands var(--g-after-dur, 1.5s) infinite linear alternate-reverse; }
.glitch::before { transform: translateX(-6px); text-shadow: var(--g-before-shadow); animation: ui-glitch-bands var(--g-before-dur, 1s) infinite linear alternate-reverse; }
.glitch.is-settled::before, .glitch.is-settled::after { animation: none; opacity: 0; transition: opacity 260ms var(--ease); }
@keyframes ui-glitch-bands {
  0% { clip-path: inset(20% 0 50% 0); } 10% { clip-path: inset(15% 0 55% 0); } 20% { clip-path: inset(30% 0 40% 0); }
  30% { clip-path: inset(10% 0 60% 0); } 40% { clip-path: inset(25% 0 35% 0); } 50% { clip-path: inset(40% 0 20% 0); }
  60% { clip-path: inset(15% 0 55% 0); } 70% { clip-path: inset(30% 0 40% 0); } 80% { clip-path: inset(20% 0 50% 0); }
  90% { clip-path: inset(25% 0 35% 0); } 100% { clip-path: inset(30% 0 40% 0); }
}

/* DECISION 103: one glyph cell morphs to a tick for 900 ms, then returns. */
.icon-action { display: inline-grid; place-items: center; }
.swap { display: inline-grid; width: 17px; height: 17px; }
.swap svg { grid-area: 1 / 1; width: 100%; height: 100%; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; transition: opacity 150ms var(--ease), transform 150ms var(--ease), filter 150ms var(--ease); }
.swap .is-done { opacity: 0; transform: scale(0.6); filter: blur(2px); stroke-width: 2.2; }
.swap .is-done path { stroke-dasharray: 1; stroke-dashoffset: 1; transition: stroke-dashoffset 0ms 150ms; }
[data-done] { color: var(--fg); }
[data-done] svg { transition-duration: 200ms; }
[data-done] .is-idle { opacity: 0; transform: scale(0.6); filter: blur(2px); }
[data-done] .is-done { opacity: 1; transform: none; filter: none; }
[data-done] .is-done path { stroke-dashoffset: 0; transition: stroke-dashoffset 260ms var(--ease) 50ms; }
[data-instant] .swap svg, [data-instant] .is-done path { transition: none; }
@media (prefers-reduced-motion: reduce) {
  .check[data-pressed] { transform: none; transition: none; }
  .swap svg, [data-done] .is-idle, .swap .is-done, [data-done] .is-done { transform: none; filter: none; }
  .swap .is-done path { stroke-dashoffset: 0; transition: none; }
}

/* ---------------------------------------------------------------------------------------
   Type roles, taken from the sign-in card's computed values (the owner's benchmark,
   docs/design/TASTE.md §2): the same on desktop and touch. Only row and control heights
   follow the platform (macOS scale on desktop, 44 px targets on touch).
     name    600 14px/1     -0.01em  (auth-mark)
     body    450 14px/1.55  -0.01em  (auth-lede)
     label   450 13px/1.55  -0.01em  (auth-label)
     button  500 13.5px/1            (auth-back, auth-text-button)
     eyebrow 500 12px/1     +0.02em  (auth-eyebrow)
   --------------------------------------------------------------------------------------- */
:root {
  --ui-type-name: 600 14px/1 var(--font-ui, "Geist Mono", ui-monospace, monospace);
  --ui-type-body: 450 14px/1.55 var(--font-ui, "Geist Mono", ui-monospace, monospace);
  --ui-type-label: 450 13px/1.55 var(--font-ui, "Geist Mono", ui-monospace, monospace);
  --ui-type-button: 500 13.5px/1 var(--font-ui, "Geist Mono", ui-monospace, monospace);
  --ui-type-eyebrow: 500 12px/1 var(--font-ui, "Geist Mono", ui-monospace, monospace);
  --ui-tracking: -0.01em;
  /* Secondary text matches the sign-in card's secondary (rgb 161 161 170 on ink) and
     adapts to every theme, instead of the dimmer --fg-2. */
  --ui-text-secondary: color-mix(in oklab, var(--fg) 70%, var(--bg));
  --ui-row-h: 32px;
  --ui-control-h: 30px;
  --ui-icon: 16px;
}
@media (pointer: coarse) {
  :root { --ui-row-h: 44px; --ui-control-h: 44px; --ui-icon: 18px; }
}

/* Window card (window-card.ts): the pane card's surface plus a quiet header. */
.ui-window {
  position: relative; display: flex; flex-direction: column; min-width: 0; min-height: 0;
  /* Not clipped: the lock's shackle rises over the frame line. The body clips its own content. */
  background: var(--bg);
  border: 1px solid var(--overlay-line); border-radius: var(--r-lg, 18px); box-shadow: var(--shadow);
  transition: border-color 180ms var(--ease-out, ease-out);
}
/* Window transparency (themes.ts WINDOW_LEVELS): Solid is the line above, with no alpha and no filter. The other
   levels read one fill token; only Frosted adds a backdrop blur, and only on windows that are on screen
   (canvas/memory.ts sets data-onscreen), because many large blurred windows cost frames and memory. */
/* The fills and the Frosted blur are literal rules written from WINDOW_LEVELS (themes.ts `installLevelRules`). */
/* Solid cards paint no blur at all. */
:root[data-card="solid"] .ui-overlay-surface, :root[data-card="solid"] .ui-panel, :root[data-card="solid"] .ui-pill { -webkit-backdrop-filter: none; backdrop-filter: none; }
.ui-window[data-active] { border-color: color-mix(in oklab, var(--accent) 50%, var(--overlay-line)); }
.ui-window-head { display: flex; flex: none; align-items: center; gap: 8px; padding: 8px 8px 0 8px; -webkit-touch-callout: none; -webkit-user-select: none; user-select: none; }
.ui-window-pill {
  display: inline-flex; align-items: center; gap: 8px; min-width: 0; height: var(--ui-control-h); padding: 0 10px 0 8px;
  border: 1px solid var(--overlay-line); border-radius: 999px; background: transparent; color: var(--fg);
  font: var(--ui-type-button); letter-spacing: var(--ui-tracking); white-space: nowrap; cursor: pointer;
  /* The lock owns the exact centre of the frame line: the pill never reaches past half the header minus the lock's hit area. */
  max-width: calc(50% - 26px); line-height: 1.4; /* room for descenders: the title clips its overflow */
  transition: background-color 140ms ease, transform 140ms var(--ease-out, ease-out);
}
.ui-window-pill:active { transform: scale(var(--ui-press-scale)); }
.ui-window-mark { display: grid; flex: none; place-items: center; width: var(--ui-icon); height: var(--ui-icon); color: var(--fg-2); }
.ui-window-mark svg { width: 100%; height: 100%; fill: currentColor; stroke: none; }
/* Line glyphs (a flow's mark, not a host platform) draw as the 1.5 stroke icons everywhere else. */
.ui-window-mark svg.is-line { fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
/* The setup ring (setup-ring.ts) sits over the mark's system icon and has exactly the mark's size, so the pill never changes
   width. Opacity and transform only; `data-setup` on the mark says which of ring, tick, hidden (a flight carries a copy)
   or rest the slot is in, and hides the icon underneath until the ring is done. */
.ui-window-mark { position: relative; }
.ui-window-mark[data-setup] > svg { transition: opacity 220ms cubic-bezier(0.23, 1, 0.32, 1); }
.ui-window-mark[data-setup="ring"] > svg, .ui-window-mark[data-setup="tick"] > svg, .ui-window-mark[data-setup="hidden"] > svg { opacity: 0; }
.ui-setup-ring { position: absolute; inset: 0; display: block; color: var(--fg-2); pointer-events: none; transition: opacity 220ms cubic-bezier(0.23, 1, 0.32, 1); }
.ui-setup-ring[data-hidden="true"], .ui-setup-ring[data-phase="rest"] { opacity: 0; }
.ui-setup-ring[data-instant] { transition: none; }
.ui-setup-ring > svg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; fill: none; }
.ui-setup-arcs { transition: opacity 150ms cubic-bezier(0.23, 1, 0.32, 1), transform 150ms cubic-bezier(0.23, 1, 0.32, 1); }
.ui-setup-track, .ui-setup-fill { fill: none; stroke: currentColor; stroke-width: 3; stroke-linecap: round; }
.ui-setup-track { opacity: 0.25; }
.ui-setup-fill { opacity: 0; transition: opacity 220ms cubic-bezier(0.23, 1, 0.32, 1), stroke 160ms ease; }
.ui-setup-fill[data-state="done"] { opacity: 1; stroke: var(--fg); }
.ui-setup-fill[data-state="running"] { opacity: 0.55; animation: ui-setup-pulse 1100ms ease-in-out infinite alternate; }
.ui-setup-fill[data-state="failed"] { opacity: 1; stroke: var(--danger); }
@keyframes ui-setup-pulse { from { opacity: 0.3; } to { opacity: 0.9; } }
.ui-setup-check { opacity: 0; transform: scale(0.7); transition: opacity 150ms cubic-bezier(0.23, 1, 0.32, 1), transform 150ms cubic-bezier(0.23, 1, 0.32, 1); }
.ui-setup-check path { fill: none; stroke: var(--ok); stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; stroke-dasharray: 1; stroke-dashoffset: 1; transition: stroke-dashoffset 150ms cubic-bezier(0.23, 1, 0.32, 1); }
.ui-setup-ring[data-phase="tick"] .ui-setup-arcs, .ui-setup-ring[data-phase="rest"] .ui-setup-arcs { opacity: 0; transform: scale(0.7); }
.ui-setup-ring[data-phase="tick"] .ui-setup-check, .ui-setup-ring[data-phase="rest"] .ui-setup-check { opacity: 1; transform: none; }
.ui-setup-ring[data-phase="tick"] .ui-setup-check path, .ui-setup-ring[data-phase="rest"] .ui-setup-check path { stroke-dashoffset: 0; }
/* A flying copy of the ring (setup-pill.on-demand.ts) is a fixed box moved by transform only. */
.ui-setup-fly { position: fixed; left: 0; top: 0; z-index: 90; pointer-events: none; transform-origin: 0 0; color: var(--fg); }
@media (prefers-reduced-motion: reduce) {
  .ui-setup-fill[data-state="running"] { animation: none; opacity: 0.55; }
  .ui-setup-arcs, .ui-setup-check { transition: opacity 120ms ease; }
  .ui-setup-ring[data-phase="tick"] .ui-setup-arcs, .ui-setup-ring[data-phase="rest"] .ui-setup-arcs, .ui-setup-check { transform: none; }
  .ui-setup-check path { transition: none; }
  .ui-setup-fill, .ui-setup-ring, .ui-window-mark[data-setup] > svg { transition-duration: 120ms; }
}
.ui-window-title { display: inline-flex; min-width: 0; overflow: hidden; }
.ui-window-user { color: var(--ui-text-secondary); font-weight: var(--fw-secondary); }
.ui-window-host { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
/* The title gives way before the actions do, and the session name before the host: it shrinks a hundred times faster. */
.ui-window-title { flex: 0 1 auto; }
.ui-window-session { flex: 0 100 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; color: var(--fg-3); font-weight: var(--fw-secondary); }
/* In a narrow header the login goes first (it is the quietest part of the name). */
.ui-window-head { container-type: inline-size; }
@container (max-width: 560px) { .ui-window-user { display: none; } }
.ui-window-session::before { content: "·"; margin-right: 8px; color: var(--fg-3); }
.ui-window-dot { width: 6px; height: 6px; flex: none; border-radius: 50%; background: var(--ok); }
.ui-window[data-status="connecting"] .ui-window-dot { background: var(--fg-3); }
.ui-window[data-status="none"] .ui-window-dot { display: none; }
.ui-window[data-status="offline"] .ui-window-dot { background: transparent; box-shadow: inset 0 0 0 1.5px var(--fg-3); }
/* The header's actions are one pill (the shared material, without its blur: windows stay cheap) holding icon
   buttons, the same height as the title pill and on the same line. The pill never shrinks (the title truncates
   first). On touch both pills are 36 px tall and each button reaches a 44 px square through an invisible hit area (see below). */
.ui-window-actions {
  display: flex; flex: none; align-items: center; gap: 2px; margin-left: auto; height: var(--ui-control-h); padding: 0 2px;
  border: 1px solid var(--overlay-line); border-radius: 999px; background: var(--overlay); box-shadow: none;
}
.ui-window-actions:empty { display: none; }
.ui-window-action {
  position: relative; display: grid; place-items: center; width: calc(var(--ui-control-h) - 6px); height: calc(var(--ui-control-h) - 6px); padding: 0; border: 0; border-radius: 50%;
  background: none; color: var(--fg-3); cursor: pointer; transition: background-color 140ms ease, color 140ms ease, transform 140ms var(--ease-out, ease-out);
}
/* On touch there is no frame lock to leave room for, so the pill may use the whole header less the actions: the host's name is
   never cut while there is room. The session name is secondary and goes whole (never as a lone "· …") in a phone-width card. */
@media (pointer: coarse) {
  .ui-window-pill { max-width: 100%; }
  @container (max-width: 400px) { .ui-window-session { display: none; } }
  /* One visible size for every header pill (TASTE: window header pills are one size): the title pill and the actions pill are
     36 px tall, and the 44 px touch target comes from invisible padding around each (::after), never from a bigger pill. */
  .ui-window-pill, .ui-window-actions { height: 36px; }
  .ui-window-pill { position: relative; }
  /* The header is a container (layout containment makes it its own stacking level), so it must sit above the body itself:
     otherwise the body covers the 4 px of hit area below the pill and the pill is a 40 px target, not 44. */
  .ui-window-head { position: relative; z-index: 1; }
  .ui-window-pill::after { content: ""; position: absolute; inset: -6px -3px; }
  .ui-window-actions { gap: 0; padding: 0 3px; position: relative; z-index: 1; } /* above the body, so the hit area below the pill is not covered by it */
  .ui-window-action { width: 30px; height: 30px; }
  .ui-window-action::after { content: ""; position: absolute; inset: -7px; } /* 44 px square around the 30 px button */
}
.ui-window-action:active { transform: scale(var(--ui-press-scale)); }
/* A column at the weight floor is only a few dozen pixels wide (a layout drawn on a wide screen, shown on a phone).
   Chrome never overlaps or clips there: the title text goes first, then the whole title pill, then the actions. The
   window itself stays reachable through the canvas row and the search palette. */
@container (max-width: 150px) { .ui-window-title, .ui-window-dot { display: none; } }
@container (max-width: 84px) { .ui-window-pill { display: none; } }
@container (max-width: 40px) { .ui-window-actions { display: none; } }
.ui-window-action svg { width: var(--ui-icon); height: var(--ui-icon); fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
/* Title first (TASTE): in a header this narrow the actions give way to one ⋯, so the title keeps its room. A card with one
   action never collapses (the actions container says so with data-collapsible). The ⋯ opens the same actions in a menu. */
.ui-window-action.ui-window-more { display: none; }
@container (max-width: 320px) {
  .ui-window-actions[data-collapsible] > .ui-window-action:not(.ui-window-more) { display: none; }
  .ui-window-actions[data-collapsible] > .ui-window-action.ui-window-more { display: grid; }
  /* The title's room is the pill's half of the header: the window's mark and session name give way first, at any narrow
     width (a half on a phone too), so the title is never squeezed out. */
  .ui-window-session { display: none; }
  /* The mark names the machine's system and leads the pill on a phone, whatever the width (TASTE: the window pill names the machine by its system). */
  @media (pointer: fine) { .ui-window-mark { display: none; } }
}
/* The anchored popover (anchored-popover.ts) floats inside its stage at the coordinates the placement computes. */
.ui-anchored-popover { position: absolute; z-index: 6; overflow: hidden; border-radius: 14px; }
.ui-window-menu { display: flex; flex-direction: column; gap: 2px; min-width: 168px; padding: 6px; }
.ui-window-menu-item {
  display: flex; align-items: center; gap: 10px; width: 100%; height: 34px; padding: 0 10px; border: 0; border-radius: 10px;
  background: none; color: var(--fg-2); font: var(--ui-type-button); letter-spacing: var(--ui-tracking); white-space: nowrap; text-align: left; cursor: pointer;
}
.ui-window-menu-item svg { width: var(--ui-icon); height: var(--ui-icon); flex: none; fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
@media (pointer: coarse) { .ui-window-menu-item { height: 44px; } }
@media (hover: hover) and (pointer: fine) { .ui-window-menu-item:hover { background: var(--hover); color: var(--fg); } }
.ui-window-body { position: relative; flex: 1; min-height: 0; overflow: hidden; border-radius: 0 0 calc(var(--r-lg, 18px) - 1px) calc(var(--r-lg, 18px) - 1px); }
@media (hover: hover) and (pointer: fine) {
  .ui-window-pill:hover { background: var(--hover); }
  .ui-window-action:hover { background: var(--hover); color: var(--fg-2); }
}

/* Capsule tabs (capsule-tabs.ts): flat track, one gliding capsule. */
.ui-tabs { position: relative; display: inline-flex; gap: 2px; padding: 3px; border-radius: 999px; background: var(--hover); isolation: isolate; }
.ui-tabs-capsule {
  position: absolute; z-index: -1; top: 3px; bottom: 3px; left: 0; border-radius: 999px;
  background: var(--overlay-solid); box-shadow: 0 1px 2px rgb(0 0 0 / 0.14), 0 0 0 1px var(--overlay-line);
}
.ui-tab {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px; min-width: 0; height: calc(var(--ui-control-h) - 6px);
  padding: 0 12px; border: 0; border-radius: 999px; background: none; color: var(--fg-2); cursor: pointer;
  font: var(--ui-type-button); letter-spacing: var(--ui-tracking); white-space: nowrap; transition: color 160ms ease;
}
.ui-tab[aria-selected="true"] { color: var(--fg); }
.ui-tabs[data-compact] .ui-tab[aria-selected="false"] > span { display: none; }
.ui-tabs[data-compact] .ui-tab[aria-selected="false"] { padding: 0 10px; }
.ui-tab svg { width: var(--ui-icon); height: var(--ui-icon); flex: none; fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }

/* Key/value list (kv-list.ts): keys left, values right, every cell copies. */
.ui-kv { display: flex; flex-direction: column; }
.ui-kv-row {
  display: grid; grid-template-columns: auto minmax(0, 1fr); column-gap: 16px; align-items: baseline;
  min-height: var(--ui-row-h); padding: 4px 0; border-bottom: 1px solid var(--line);
}
.ui-kv-row:last-child { border-bottom: 0; }
/* Titled sections inside one list: the quiet eyebrow of the settings headings, no box or rule of its own. */
.ui-kv-section + .ui-kv-section { margin-top: 12px; }
.ui-kv-rows { display: flex; flex-direction: column; }
.ui-kv-group { padding: 6px 0 2px; color: var(--ui-text-secondary); font: var(--ui-type-eyebrow); letter-spacing: 0.02em; text-transform: uppercase; }
.ui-kv-cell {
  display: block; min-width: 0; margin: 0; padding: 0; border: 0; background: none; color: inherit; cursor: pointer; text-align: left; font: inherit;
  transition: transform 140ms var(--ease-out, ease-out);
  -webkit-user-select: text; user-select: text; -webkit-tap-highlight-color: transparent; touch-action: manipulation;
}
.ui-kv-key { justify-self: start; color: var(--ui-text-secondary); font: var(--ui-type-label); letter-spacing: var(--ui-tracking); white-space: nowrap; }
.ui-kv-key { display: grid; justify-items: start; }
.ui-kv-hint { color: var(--fg-3); font: var(--ui-type-label); font-size: 11px; letter-spacing: var(--ui-tracking); white-space: nowrap; }
.ui-kv-value { justify-self: end; color: var(--fg); font: var(--ui-type-body); letter-spacing: var(--ui-tracking); text-align: right; }
.ui-kv-cell:active { transform: scale(var(--ui-press-scale-soft)); }
/* The highlighted run: 6 px of even padding either side (cancelled by a -6 px margin so the text
   still lines up with the row's edges), cloned across wrapped lines. */
.ui-kv-text {
  display: inline; margin: 0 -6px; padding: 3px 6px; border-radius: 6px; overflow-wrap: anywhere; transition: background-color 120ms ease;
  -webkit-box-decoration-break: clone; box-decoration-break: clone;
}
/* The tick takes no room until a copy happens, so the highlight never carries empty space. */
.ui-kv-text .ui-inline-tick { width: 0; height: 12px; margin-right: 0; overflow: hidden; vertical-align: -1px; transition: width 150ms var(--ease-out, ease-out), margin 150ms var(--ease-out, ease-out); }

.ui-kv-cell[data-done] .ui-kv-text .ui-inline-tick { width: 12px; }
.ui-kv-key[data-done] .ui-kv-text .ui-inline-tick { margin-left: 6px; }
.ui-kv-value[data-done] .ui-kv-text .ui-inline-tick { margin-right: 6px; }
.ui-kv-meter { grid-column: 1 / -1; position: relative; height: 3px; margin: 4px 0 2px; overflow: hidden; border-radius: 2px; background: var(--line); }
.ui-kv-meter::after { content: ""; position: absolute; inset: 0; border-radius: inherit; background: var(--accent); transform: scaleX(var(--meter, 0)); transform-origin: left; }
@media (hover: hover) and (pointer: fine) {
  .ui-kv-cell:hover .ui-kv-text { background: var(--hover); }
}
@media (prefers-reduced-motion: reduce) {
  .ui-window-pill:active, .ui-window-action:active { transform: none; }
}

/* DECISION 120 icon slot for branched-menu hosts. */
.bm-icon { display: grid; place-items: center; width: 16px; height: 16px; flex: none; color: var(--fg-2); }
.bm-icon svg { display: block; width: 15px; height: 15px; fill: currentColor; stroke: none; }

/* Shared step transitions (pane-stepper.ts): leaving panes stack over the incoming one. */
.ui-pane-viewport { position: relative; }
.ui-pane-viewport.is-moving { overflow: hidden; }
.ui-pane.is-leaving { position: absolute; top: 0; right: 0; left: 0; pointer-events: none; }

/* Chip field (chip-input.ts): chips wrap inside a field that grows with a spring. */
.ui-chip-field {
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: rgb(from var(--fg) r g b / 0.05);
  cursor: text;
  transition: border-color 120ms ease, background-color 120ms ease;
}
.ui-chip-field:focus-within { border-color: rgb(from var(--fg) r g b / 0.28); background: rgb(from var(--fg) r g b / 0.09); }
.ui-chip-flow { display: flex; min-height: 46px; flex-wrap: wrap; align-items: center; gap: 6px; padding: 6px 8px 6px var(--ui-chip-field-inset, 14px); }
.ui-chip-text {
  min-width: 8ch;
  height: 32px;
  flex: 1 1 8ch;
  padding: 0;
  border: 0;
  outline: none;
  background: transparent;
  color: var(--fg);
  font: var(--fw-body) 16px/1 var(--font-ui, "Geist Mono", ui-monospace, monospace);
  letter-spacing: 0.01em;
}
/* With chips present the input needs room for a few characters only; it grows on its own row
   when it has to wrap, so no blank line is left under the chips. */
.ui-chip-flow:has(.ui-chip) .ui-chip-text { min-width: 4ch; flex-basis: 4ch; }
.ui-chip-text::placeholder { color: var(--fg-3); opacity: 1; }
.ui-chip {
  display: inline-flex;
  max-width: 100%;
  height: 30px;
  align-items: center;
  gap: 6px;
  padding: 0 4px 0 10px;
  border-radius: 999px;
  background: rgb(from var(--fg) r g b / 0.08);
  color: var(--fg);
  font: var(--fw-label) 13px/1 var(--font-ui, "Geist Mono", ui-monospace, monospace);
  letter-spacing: -0.01em;
  transform-origin: left center;
  transition: background-color 120ms ease;
}
.ui-chip > svg:first-child { width: 14px; height: 14px; flex: none; fill: currentColor; color: var(--fg-2); }
.ui-chip > svg.is-line:first-child { fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
.ui-chip-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ui-chip-remove { display: grid; width: 22px; height: 22px; flex: none; place-items: center; padding: 0; border: 0; border-radius: 999px; background: transparent; color: var(--fg-3); cursor: pointer; }
.ui-chip-remove svg { width: 12px; height: 12px; fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; }
@media (hover: hover) and (pointer: fine) { .ui-chip-remove:hover { background: rgb(from var(--fg) r g b / 0.1); color: var(--fg); } }
@media (pointer: coarse) { .ui-chip { height: 34px; font-size: 15px; } .ui-chip-remove { width: 28px; height: 28px; } }
/* Pill: the one container for floating capsule controls (the top-bar capsule, icon buttons, the
   sidebar toggle, session tabs). Instances set only their size and content layout; the material,
   hairline, shadow, radius and press feedback live here so they can never drift apart. */
.ui-pill {
  appearance: none; display: inline-flex; align-items: center; justify-content: center; height: var(--pill-h, 38px);
  border: 1px solid var(--overlay-line) !important; border-radius: 999px; background: var(--overlay); box-shadow: var(--shadow);
  -webkit-backdrop-filter: blur(var(--ui-overlay-blur)) saturate(var(--ui-overlay-saturation)); backdrop-filter: blur(var(--ui-overlay-blur)) saturate(var(--ui-overlay-saturation));
  transition: transform 140ms var(--ease-out, cubic-bezier(0.23, 1, 0.32, 1));
}
.ui-pill:active:not(:disabled) { transform: scale(var(--ui-press-scale)); }
.ui-pill-icon { width: var(--pill-h, 38px); padding: 0; color: var(--fg-2); }
.ui-pill-icon svg { display: block; fill: none; stroke: currentColor; stroke-width: 1.6; }
@media (hover: hover) and (pointer: fine) { .ui-pill:hover:not(:disabled) { background: var(--overlay-solid); } }
@media (prefers-reduced-motion: reduce) { .ui-pill:active:not(:disabled) { transform: none; } }

/* Window: see the window card block above; terminals draw into a block canvas. */
.ui-window canvas { display: block; }

/* Panel: the same material for large floating surfaces such as the sidebar; only the radius differs. */
.ui-panel { border-radius: var(--r-lg, 18px); }

/* Window resize handles (window-resize.ts): invisible hit strips on the card's edges for a
   mouse; a finger gets four corner grips only after a press-and-hold on the header. */
.ui-window-resize { position: absolute; z-index: 5; touch-action: none; }
.ui-window-resize[data-edge="n"], .ui-window-resize[data-edge="s"] { left: 18px; right: 18px; height: 6px; }
.ui-window-resize[data-edge="e"], .ui-window-resize[data-edge="w"] { top: 18px; bottom: 18px; width: 6px; }
.ui-window-resize[data-edge="n"] { top: 0; cursor: ns-resize; }
.ui-window-resize[data-edge="s"] { bottom: 0; cursor: ns-resize; }
.ui-window-resize[data-edge="e"] { right: 0; cursor: ew-resize; }
.ui-window-resize[data-edge="w"] { left: 0; cursor: ew-resize; }
.ui-window-resize[data-edge$="e"][data-edge^="n"], .ui-window-resize[data-edge$="w"][data-edge^="s"] { cursor: nesw-resize; }
.ui-window-resize[data-edge$="w"][data-edge^="n"], .ui-window-resize[data-edge$="e"][data-edge^="s"] { cursor: nwse-resize; }
.ui-window-resize[data-edge="ne"], .ui-window-resize[data-edge="nw"], .ui-window-resize[data-edge="se"], .ui-window-resize[data-edge="sw"] { width: 18px; height: 18px; }
.ui-window-resize[data-edge="ne"] { top: 0; right: 0; }
.ui-window-resize[data-edge="nw"] { top: 0; left: 0; }
.ui-window-resize[data-edge="se"] { bottom: 0; right: 0; }
.ui-window-resize[data-edge="sw"] { bottom: 0; left: 0; }
@media (pointer: coarse) { .ui-window-resize { display: none; } }

/* Touch mode: an accent outline and four corner brackets that hug the card's rounded corners,
   each with a 44 px hit area. Edges stay inert so a finger cannot resize by accident. */
.ui-window[data-resize="touch"] { border-color: var(--accent); box-shadow: var(--shadow), 0 0 0 1px var(--accent); }
.ui-window[data-resize="touch"] .ui-window-resize { display: block; }
.ui-window[data-resize="touch"] .ui-window-resize[data-edge]:not([data-edge="ne"], [data-edge="nw"], [data-edge="se"], [data-edge="sw"]) { display: none; }
.ui-window[data-resize="touch"] .ui-window-resize[data-edge="ne"], .ui-window[data-resize="touch"] .ui-window-resize[data-edge="nw"],
.ui-window[data-resize="touch"] .ui-window-resize[data-edge="se"], .ui-window[data-resize="touch"] .ui-window-resize[data-edge="sw"] {
  width: 44px; height: 44px; cursor: auto;
  animation: ui-grip-in 220ms var(--ease-out, cubic-bezier(0.23, 1, 0.32, 1)) both;
}
.ui-window[data-resize="touch"] .ui-window-resize::after {
  content: ""; position: absolute; width: 22px; height: 22px; border: 0 solid var(--accent);
  transition: transform 140ms var(--ease-out, ease-out);
}
.ui-window[data-resize="touch"] .ui-window-resize:active::after { transform: scale(1.12); }
.ui-window[data-resize="touch"] .ui-window-resize[data-edge="ne"]::after { top: 6px; right: 6px; border-width: 3px 3px 0 0; border-top-right-radius: 14px; }
.ui-window[data-resize="touch"] .ui-window-resize[data-edge="nw"]::after { top: 6px; left: 6px; border-width: 3px 0 0 3px; border-top-left-radius: 14px; }
.ui-window[data-resize="touch"] .ui-window-resize[data-edge="se"]::after { bottom: 6px; right: 6px; border-width: 0 3px 3px 0; border-bottom-right-radius: 14px; }
.ui-window[data-resize="touch"] .ui-window-resize[data-edge="sw"]::after { bottom: 6px; left: 6px; border-width: 0 0 3px 3px; border-bottom-left-radius: 14px; }
@keyframes ui-grip-in { from { opacity: 0; } to { opacity: 1; } }

/* Sidebar footer (sidebar-foot.ts): the person's pill and the settings pill share one height, so
   their centres sit on one line. Pills are the shared `.ui-pill`; only layout lives here. */
.ui-sidebar-foot { --pill-h: 40px; display: flex; flex: none; align-items: center; justify-content: space-between; gap: 8px; margin-top: auto; padding: 9px 0 0; box-shadow: 0 -1px 0 var(--line); }
.ui-sidebar-foot[data-variant="b"] { --pill-h: 32px; }
@media (pointer: coarse) { .ui-sidebar-foot, .ui-sidebar-foot[data-variant="b"] { --pill-h: 44px; } }
.ui-sidebar-foot .ui-pill { cursor: pointer; font: var(--ui-type-label); letter-spacing: var(--ui-tracking); line-height: 1; white-space: nowrap; transition: transform 140ms var(--ease-out, ease-out), background-color 140ms ease, box-shadow 160ms ease; }
@media (hover: hover) and (pointer: fine) { .ui-sidebar-foot .ui-pill:hover:not(:active) { box-shadow: var(--shadow), 0 2px 8px -2px rgb(0 0 0 / 0.18); } }
.ui-sidebar-user { flex: 0 1 auto; min-width: 0; max-width: calc(100% - var(--pill-h) - 8px); justify-content: flex-start; gap: 10px; padding: 0 14px 0 5px; text-align: left; }
.ui-sidebar-foot[data-variant="b"] .ui-sidebar-user { padding-right: 12px; }
.ui-sidebar-avatar {
  display: grid; flex: none; place-items: center; width: calc(var(--pill-h) - 10px); height: calc(var(--pill-h) - 10px); border-radius: 50%; color: #fff;
  background: linear-gradient(145deg, oklch(from var(--accent) calc(l + 0.06) c calc(h + var(--ui-avatar-hue, 0))), oklch(from var(--accent) calc(l - 0.12) c calc(h + var(--ui-avatar-hue, 0) + 28)));
  font: 600 12px/1 var(--font-ui, "Geist Mono", ui-monospace, monospace); text-shadow: 0 1px 1px rgb(0 0 0 / 0.25);
}
.ui-sidebar-foot[data-variant="b"] .ui-sidebar-avatar { box-shadow: 0 0 0 1.5px color-mix(in oklab, var(--accent) 40%, transparent); }
@media (pointer: coarse) { .ui-sidebar-avatar { font-size: 14px; } .ui-sidebar-foot .ui-pill { font-size: var(--text-secondary); } }
.ui-sidebar-names { display: grid; min-width: 0; gap: 3px; }
.ui-sidebar-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; color: var(--fg); font-weight: var(--fw-label); }
.ui-sidebar-secondary { min-width: 0; overflow: hidden; text-overflow: ellipsis; color: var(--ui-text-secondary); font-size: 11px; }
@media (pointer: coarse) { .ui-sidebar-secondary { font-size: 12px; } }
.ui-sidebar-settings svg { stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }

/* Window lock (lock-toggle.ts, window-card.ts): the padlock glyph itself, no chip. It is centred on
   the window's top frame line: the shackle rises above the line and the body hangs just below it, so
   it looks anchored on the frame. Invisible until the pointer is near the top edge, then 35 %;
   locked, it stays at 0.9 with an accent tint and the window's hairline is stronger. The button is a
   44 px invisible hit area. On touch there is no frame lock: locking lives in the toolbar and the
   palette. */
.ui-window-lock {
  position: absolute; z-index: 6; top: -22px; left: 50%; width: 44px; height: 44px; margin-left: -22px;
  display: grid; place-items: center; padding: 0; border: 0; border-radius: 10px; background: none; color: var(--fg);
  opacity: 0; pointer-events: none; cursor: pointer; transition: opacity 160ms ease-out, color 160ms ease-out;
}
.ui-window-lock svg { width: 16px; height: 16px; transform: translateY(0.5px); fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; overflow: visible; }
.ui-window[data-lock-near] .ui-window-lock { opacity: 0.35; pointer-events: auto; }
.ui-window[data-locked] .ui-window-lock { opacity: 0.9; pointer-events: auto; color: var(--accent); }
.ui-window-lock:focus-visible { opacity: 0.8; }
@media (hover: hover) and (pointer: fine) { .ui-window .ui-window-lock:hover { opacity: 0.8; } }
.ui-window-lock:active svg { transform: translateY(0.5px) scale(var(--ui-press-scale-small)); }
.ui-window[data-locked] { border-color: color-mix(in oklab, var(--accent) 78%, var(--overlay-line)); box-shadow: var(--shadow), 0 0 0 1px color-mix(in oklab, var(--accent) 28%, transparent); }
@media (pointer: coarse) { .ui-window-lock { display: none; } }
@media (prefers-reduced-motion: reduce) { .ui-window-lock { transition: opacity 160ms ease; } }
.ui-window[data-status="none"] .ui-window-lock { display: none; }

/* Shortcuts view (shortcuts-view.ts): the capsule's morph target. The material is the shared
   overlay surface; only the layout lives here. */
.ui-shortcuts { position: absolute; z-index: 7; display: flex; flex-direction: column; min-height: 0; border-radius: var(--r-lg, 18px); color: var(--fg); outline: none; }
.ui-shortcuts-head { display: flex; flex: none; align-items: center; justify-content: space-between; gap: 12px; height: var(--pill-h, 38px); padding: 0 8px 0 14px; font: var(--ui-type-name); letter-spacing: var(--ui-tracking); }
.ui-shortcuts-body { min-height: 0; overflow: auto; overscroll-behavior: contain; padding: 4px 14px 14px; }
.ui-shortcuts-section + .ui-shortcuts-section { margin-top: 12px; }
.ui-shortcuts-title { margin: 0; padding: 6px 0 2px; color: var(--ui-text-secondary); font: var(--ui-type-eyebrow); letter-spacing: 0.02em; text-transform: uppercase; }
.ui-shortcuts-list { display: grid; margin: 0; padding: 0; list-style: none; }
.ui-shortcuts-row { display: flex; align-items: center; justify-content: space-between; gap: 16px; min-height: var(--row, 28px); border-bottom: 1px solid var(--line); }
.ui-shortcuts-row:last-child { border-bottom: 0; }
.ui-shortcuts-label { min-width: 0; color: var(--fg); font: var(--ui-type-label); letter-spacing: var(--ui-tracking); }
.ui-shortcuts-keys { display: inline-flex; flex: none; gap: 4px; }
.ui-shortcuts kbd { min-width: 22px; padding: 4px 6px; border-radius: 6px; background: var(--hover); color: var(--fg-2); font: 500 var(--text-caption)/1 var(--font-ui, "Geist Mono", ui-monospace, monospace); text-align: center; }
/* An alternate binding: the same keycap, quieter, so the primary reads first (TASTE: hierarchy by weight and tone). */
.ui-shortcuts kbd.ui-shortcuts-alt { color: var(--fg-3); opacity: 0.7; }
@media (pointer: coarse) { .ui-shortcuts-label { font-size: var(--text-body); } .ui-shortcuts-head { padding-right: 14px; } }

/* Settings overlay (settings-overlay.ts): one centred card in the shared overlay material. */
.ui-settings { position: absolute; z-index: 8; top: 50%; left: 50%; display: flex; flex-direction: column; width: min(560px, calc(100% - 24px)); max-height: min(520px, calc(100% - 24px)); margin: 0; translate: -50% -50%; border-radius: var(--r-lg, 18px); color: var(--fg); outline: none; }
.ui-settings-head { display: flex; flex: none; align-items: center; justify-content: space-between; height: 52px; padding: 0 8px 0 18px; }
.ui-settings-title { font: var(--ui-type-name); letter-spacing: var(--ui-tracking); }
.ui-settings-nav-row:active { transform: scale(var(--ui-press-scale)); }
@media (hover: hover) and (pointer: fine) { .ui-settings-nav-row:hover { background: var(--hover); } }
.ui-settings-main { display: flex; min-height: 0; flex: 1; border-top: 1px solid var(--line); }
.ui-settings-nav { display: flex; flex: none; flex-direction: column; gap: 2px; width: 150px; padding: 8px; border-right: 1px solid var(--line); }
.ui-settings-nav-row { display: flex; align-items: center; gap: 8px; height: var(--ui-row-h, 32px); padding: 0 8px; border: 0; border-radius: 8px; background: none; color: var(--fg-2); font: var(--ui-type-label); letter-spacing: var(--ui-tracking); text-align: left; cursor: pointer; transition: background-color 140ms ease, transform 140ms var(--ease-out, ease-out); }
.ui-settings-nav-row[aria-current="true"] { background: var(--hover); color: var(--fg); }
.ui-settings-nav-row svg { width: var(--ui-icon); height: var(--ui-icon); flex: none; fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
.ui-settings-pane { min-width: 0; flex: 1; overflow: auto; padding: 6px 18px 14px; overscroll-behavior: contain; }
@media (max-width: 520px) {
  .ui-settings-main { flex-direction: column; }
  .ui-settings-nav { width: auto; flex-direction: row; border-right: 0; border-bottom: 1px solid var(--line); }
}

/* ---- connection indicator (src/connection-indicator.ts) ------------------------------------
   One 16 px box; each glyph is its own layer. The shown glyph is chosen by transitions on
   opacity and transform only, so a state change retargets from the current frame. Looping
   animations are paused (never removed) when hidden, so they cannot jump. */
.ui-conn { position: relative; display: inline-block; flex: none; width: var(--ui-conn-size, 16px); height: var(--ui-conn-size, 16px); vertical-align: middle; cursor: default; -webkit-tap-highlight-color: transparent; }
.ui-conn[hidden] { display: none; }
@media (pointer: coarse) { .ui-conn::after { content: ""; position: absolute; inset: -8px; } }
.ui-conn-g { position: absolute; inset: 0; display: grid; place-items: center; opacity: 0; transform: scale(0.6); pointer-events: none;
  transition: opacity 160ms ease-out, transform 240ms var(--ease-out, cubic-bezier(0.23, 1, 0.32, 1)); }
.ui-conn-g svg, .ui-conn-pulse, .ui-conn-spin { grid-area: 1 / 1; width: 100%; height: 100%; overflow: visible; fill: none; }
/* Glyph parts opt in to a stroke; an inherited page-wide svg stroke must not outline the filled ones. */
.ui-conn-g svg { stroke: none; }
.ui-conn-g svg * { stroke: none; }
.ui-conn[data-state="connected"] .ui-conn-g-connected,
.ui-conn[data-state="slow"] .ui-conn-g-slow,
.ui-conn[data-state="connecting"] .ui-conn-g-reconnecting,
.ui-conn[data-state="reconnecting"] .ui-conn-g-reconnecting,
.ui-conn[data-state="resumed"] .ui-conn-g-resumed,
.ui-conn[data-state="approximate"] .ui-conn-g-approximate,
.ui-conn[data-state="offline"] .ui-conn-g-offline { opacity: 1; transform: none; }

.ui-conn-g-connected .core { fill: var(--ok); }
.ui-conn-g-connected .halo { fill: color-mix(in oklab, var(--ok) 20%, transparent); }
.ui-conn-g-slow .core { fill: var(--warn); }
.ui-conn-g-slow .halo { fill: color-mix(in oklab, var(--warn) 22%, transparent); }
.ui-conn .ui-conn-g-slow .ring { stroke: var(--warn); stroke-width: 1.2; }
.ui-conn-pulse { position: absolute; inset: 0; transform-origin: 50% 50%; opacity: 0; animation: ui-conn-pulse 1700ms cubic-bezier(0.23, 1, 0.32, 1) 3; animation-play-state: paused; }
/* The breath restarts by switching to an identical animation under another name (`data-breath`), which needs no style flush. */
.ui-conn[data-breath="1"] .ui-conn-pulse { animation-name: ui-conn-pulse-b; }
.ui-conn[data-state="slow"] .ui-conn-pulse { animation-play-state: running; will-change: transform, opacity; }
@keyframes ui-conn-pulse { 0% { opacity: 0.7; transform: scale(1); } 70%, 100% { opacity: 0; transform: scale(2.1); } }
@keyframes ui-conn-pulse-b { 0% { opacity: 0.7; transform: scale(1); } 70%, 100% { opacity: 0; transform: scale(2.1); } }

.ui-conn .ui-conn-g-reconnecting .track { stroke: var(--fg-3); stroke-width: 1.4; opacity: 0.4; }
.ui-conn .ui-conn-g-reconnecting .arc { stroke: var(--fg-2); stroke-width: 1.7; stroke-linecap: round; stroke-dasharray: 28 72; }
.ui-conn-spin { position: absolute; inset: 0; animation: ui-conn-turn 900ms linear infinite; animation-play-state: paused; }
.ui-conn[data-state="reconnecting"] .ui-conn-spin, .ui-conn[data-state="connecting"] .ui-conn-spin { animation-play-state: running; }
@keyframes ui-conn-turn { to { transform: rotate(1turn); } }

.ui-conn-g-resumed .disc { fill: color-mix(in oklab, var(--ok) 22%, transparent); }
.ui-conn .ui-conn-g-resumed .tick { stroke: var(--ok); stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
/* The tick arrives with a little overshoot, because something just happened. */
.ui-conn[data-state="resumed"] .ui-conn-g-resumed { transition-timing-function: ease-out, cubic-bezier(0.34, 1.4, 0.64, 1); }

.ui-conn .ui-conn-g-approximate .frame { stroke: var(--fg-2); stroke-width: 1.4; }
.ui-conn-g-approximate .fill { fill: var(--fg-2); opacity: 0.55; }
.ui-conn .ui-conn-g-approximate .foot { stroke: var(--fg-2); stroke-width: 1.4; stroke-linecap: round; }

.ui-conn .ui-conn-g-offline .ring { stroke: var(--fg-2); stroke-width: 1.4; opacity: 0.8; }
.ui-conn .ui-conn-g-offline .slash { stroke: var(--fg-2); stroke-width: 1.4; stroke-linecap: round; opacity: 0.8; }

/* Reduced motion: glyphs cross-fade; nothing moves or loops. The reconnecting arc stays as a static mark. */
@media (prefers-reduced-motion: reduce) {
  .ui-conn-g { transform: none; transition: opacity 200ms ease; }
  .ui-conn-pulse, .ui-conn-spin { animation: none; }
  .ui-conn-pulse { display: none; }
}

/* Settings, Toolbar (toolbar-settings.ts): rows of controls with a grip, an icon, a name and a switch. */
.ui-toolbar-settings { display: grid; gap: 14px; }
.ui-ts-title { margin: 0; padding: 6px 0 2px; color: var(--ui-text-secondary); font: var(--ui-type-eyebrow); letter-spacing: 0.02em; text-transform: uppercase; }
.ui-ts-list { display: grid; margin: 0; padding: 0; list-style: none; }
.ui-ts-item { position: relative; display: flex; align-items: center; gap: 10px; min-height: var(--ui-row-h, 32px); border-bottom: 1px solid var(--line); background: transparent; }
.ui-ts-item:last-child { border-bottom: 0; }
.ui-ts-item[data-dragging] { z-index: 2; background: var(--overlay-solid); border-radius: 10px; box-shadow: var(--shadow); }
.ui-ts-grip { display: grid; flex: none; place-items: center; width: var(--ui-control-h, 30px); height: var(--ui-control-h, 30px); padding: 0; border: 0; border-radius: 8px; background: none; color: var(--fg-3); cursor: grab; touch-action: none; }
.ui-ts-grip svg { width: var(--ui-icon, 16px); height: var(--ui-icon, 16px); fill: none; stroke: currentColor; stroke-linecap: round; }
.ui-ts-item[data-dragging] .ui-ts-grip { cursor: grabbing; }
.ui-ts-icon { display: grid; flex: none; color: var(--fg-2); }
.ui-ts-icon svg { width: var(--ui-icon, 16px); height: var(--ui-icon, 16px); fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
.ui-ts-label { flex: 1; min-width: 0; color: var(--fg); font: var(--ui-type-label); letter-spacing: var(--ui-tracking); }
.ui-ts-reset { justify-self: start; min-height: var(--ui-control-h, 30px); padding: 0 12px; border: 1px solid var(--overlay-line); border-radius: 999px; background: none; color: var(--fg); font: var(--ui-type-button); letter-spacing: var(--ui-tracking); cursor: pointer; transition: background-color 140ms ease, transform 140ms var(--ease-out, ease-out); }
.ui-ts-reset:active { transform: scale(var(--ui-press-scale, 0.97)); }
@media (hover: hover) and (pointer: fine) { .ui-ts-reset:hover, .ui-ts-grip:hover { background: var(--hover); } }

/* Settings, Shortcuts (shortcuts-settings.ts): the registry's rows; a row is a button that listens for the new keys. */
.ui-shortcuts-settings { display: grid; gap: 12px; }
.ui-ss-list { display: grid; margin: 0; padding: 0; list-style: none; }
.ui-ss-row { position: relative; display: flex; flex-wrap: wrap; align-items: center; border-bottom: 1px solid var(--line); }
.ui-ss-row:last-child { border-bottom: 0; }
.ui-ss-main { display: flex; flex: 1; align-items: center; justify-content: space-between; gap: 16px; min-height: var(--row, 28px); padding: 0; border: 0; background: none; color: var(--fg); font: var(--ui-type-label); letter-spacing: var(--ui-tracking); text-align: left; cursor: pointer; }
.ui-ss-main:disabled { cursor: default; color: var(--ui-text-secondary); }
.ui-ss-keys { display: inline-flex; flex: none; gap: 4px; }
.ui-ss-keys[data-capturing] { color: var(--accent); }
.ui-ss-keys kbd { min-width: 22px; padding: 4px 6px; border-radius: 6px; background: var(--hover); color: var(--fg-2); font: var(--fw-label, 500) var(--text-caption, 11px)/1 var(--font-ui); text-align: center; }
.ui-ss-reset { display: grid; place-items: center; width: var(--ui-control-h, 30px); height: var(--ui-control-h, 30px); margin-left: 6px; padding: 0; border: 0; border-radius: 8px; background: none; color: var(--fg-3); cursor: pointer; }
.ui-ss-reset svg { width: var(--ui-icon, 16px); height: var(--ui-icon, 16px); fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
.ui-ss-warn { display: flex; flex: 1 0 100%; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px; padding: 4px 0 8px; color: var(--warn, var(--fg-2)); font: var(--ui-type-label); letter-spacing: var(--ui-tracking); }
.ui-ss-actions { display: inline-flex; gap: 6px; }
.ui-ss-actions button { min-height: var(--ui-control-h, 30px); padding: 0 12px; border: 1px solid var(--overlay-line); border-radius: 999px; background: none; color: var(--fg); font: var(--ui-type-button); cursor: pointer; }
@media (hover: hover) and (pointer: fine) { .ui-ss-reset:hover, .ui-ss-actions button:hover, .ui-ss-main:not(:disabled):hover { background: var(--hover); } }
.ui-ts-source { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.ui-ts-tag { padding: 3px 9px; border-radius: 999px; background: var(--hover); color: var(--ui-text-secondary); font: var(--ui-type-eyebrow); letter-spacing: 0.02em; text-transform: uppercase; }
.ui-ts-tag[data-device="true"] { background: var(--accent-soft); color: var(--accent); }
.ui-ts-tag, .ui-ts-source .ui-ts-reset, .ui-ss-actions button { white-space: nowrap; }
.keyedit-list::before { white-space: nowrap; }

/* Canvas backdrop (canvas-backdrop.ts): the gradient is fixed to the screen and visible everywhere; the dots are light marks on it and move with the camera. */
.ui-canvas-backdrop {
  --ui-dot-size: 1.4px; --ui-dot-gap: 22px; --ui-dot-opacity: var(--dot-opacity, 0.35); --ui-gradient-opacity: var(--gradient-opacity, 0.5);
  --ui-dot-color: var(--dot-color, color-mix(in oklab, var(--mesh-3) 40%, white));
  position: absolute; inset: 0; z-index: 0; overflow: hidden; pointer-events: none;
}
/* The three backgrounds (Settings, Appearance). Soft is the defaults above: the theme's gentle gradient. Plain is one
   solid colour with the dots in a quiet tint of the text colour. Mesh is the theme's full mesh gradient (the same
   image the rest of the app paints), fixed to the screen, with the dots moving over it. */
:root[data-bg="none"] .ui-canvas-backdrop { --ui-gradient-opacity: 0; --ui-dot-opacity: 0.2; --ui-dot-color: color-mix(in srgb, var(--fg) 60%, var(--bg)); }
:root[data-bg="mesh"] .ui-canvas-backdrop { --ui-gradient-opacity: 1; --ui-dot-fill: var(--mesh-image, linear-gradient(115deg, var(--mesh-1), var(--mesh-2) 35%, var(--mesh-3) 70%, var(--mesh-4))) center / cover no-repeat; }
.ui-canvas-backdrop-fill { position: absolute; inset: 0; opacity: var(--ui-gradient-opacity); background: var(--ui-dot-fill, linear-gradient(115deg, var(--mesh-1), var(--mesh-2) 35%, var(--mesh-3) 70%, var(--mesh-4))); }
.ui-canvas-backdrop-layer[hidden] { display: none; }
.ui-canvas-backdrop-layer {
  position: absolute; left: 0; top: 0; will-change: transform, opacity;
  background-image: radial-gradient(circle at 50% 50%, color-mix(in srgb, var(--ui-dot-color) calc(var(--ui-dot-opacity) * 100%), transparent) calc(var(--ui-dot-size) / 2), transparent calc(var(--ui-dot-size) / 2 + 0.7px));
}

/* Settings windows (settings-panels.ts): choice rows with a tick, device rows, headings; every colour, size and radius is a token. */
.ui-sp { display: grid; gap: 14px; }
.ui-sp-list { display: grid; grid-template-columns: minmax(0, 1fr); gap: 2px; }
.ui-sp-row { display: flex; align-items: center; gap: 10px; width: 100%; min-height: var(--ui-row-h, 32px); padding: 0 8px; border: 0; border-radius: 8px; background: none; color: var(--fg); font: var(--ui-type-label); letter-spacing: var(--ui-tracking); text-align: left; cursor: pointer; transition: background-color 140ms ease, transform 140ms var(--ease-out, ease-out); }
.ui-sp-row:active { transform: scale(var(--ui-press-scale, 0.97)); }
@media (hover: hover) and (pointer: fine) { .ui-sp-row:hover { background: var(--hover); } }
.ui-sp-swatch { position: relative; flex: none; width: 18px; height: 18px; border-radius: 50%; border: 1px solid var(--overlay-line); }
.ui-sp-swatch::after { content: ""; position: absolute; right: 2px; bottom: 2px; width: 6px; height: 6px; border-radius: 50%; background: var(--dot); }
/* Name and hint share a line: the name is one unbreakable line and the hint sits right-aligned beside it. When the list's
   widest row does not fit (decided per list in fitHints) the list is `data-stacked` and every hint sits under its name,
   left-aligned, balanced and allowed two lines. The tick stays centred on the row. */
.ui-sp-text { display: flex; flex: 1; align-items: baseline; column-gap: 10px; min-width: 0; padding: 5px 0; }
.ui-sp-text .ui-sp-label { flex: none; max-width: 100%; margin-right: auto; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ui-sp-text .ui-sp-hint { flex: none; white-space: nowrap; color: var(--ui-text-secondary); }
.ui-sp-text .ui-sp-hint:empty { display: none; }
[data-stacked] > .ui-sp-row .ui-sp-text { flex-direction: column; align-items: flex-start; row-gap: 1px; }
[data-stacked] > .ui-sp-row .ui-sp-hint { white-space: normal; text-wrap: balance; max-width: 100%; }
.ui-sp-hint { color: var(--ui-text-secondary); }
.ui-sp-tick { width: var(--ui-icon, 16px); height: var(--ui-icon, 16px); flex: none; fill: none; stroke: var(--accent); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; opacity: 0; transform: scale(0.6); transition: opacity 150ms ease, transform 180ms var(--ease-out, ease-out); }
.ui-sp-row[data-current] .ui-sp-tick { opacity: 1; transform: none; }
.ui-sp-devices { display: grid; margin: 0; padding: 0; list-style: none; }
.ui-sp-device { display: flex; align-items: center; gap: 10px; min-height: 52px; border-bottom: 1px solid var(--line); }
.ui-sp-device:last-child { border-bottom: 0; }
.ui-sp-kind { position: relative; display: grid; flex: none; place-items: center; width: 28px; height: 28px; color: var(--fg-2); }
.ui-sp-kind svg { width: var(--ui-icon, 16px); height: var(--ui-icon, 16px); fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
.ui-sp-dot { position: absolute; right: 1px; bottom: 1px; width: 7px; height: 7px; border-radius: 50%; background: var(--ok); box-shadow: 0 0 0 2px var(--bg); }
.ui-sp-dot[data-online="false"] { background: var(--fg-3); }
/* A static row (a fact and its action): no press scale or hover fill, because the row itself does nothing. The action fades
   instead of vanishing so the words keep their place. */
.ui-sp-status { cursor: default; min-height: var(--ui-row-h, 32px); }
.ui-sp-status:active { transform: none; }
@media (hover: hover) and (pointer: fine) { .ui-sp-status:hover { background: none; } }
.ui-sp-status-action { flex: none; transition: opacity 160ms ease, background-color 140ms ease, transform 140ms var(--ease-out, ease-out); }
.ui-sp-status-action[data-shown="false"] { opacity: 0; pointer-events: none; }
@media (prefers-reduced-motion: reduce) { .ui-sp-status-action { transition: none; } }
.ui-sp-names { display: grid; flex: 1; min-width: 0; gap: 2px; }
.ui-sp-name-row { display: flex; align-items: baseline; gap: 8px; min-width: 0; }
.ui-sp-name { min-width: 0; max-width: 100%; padding: 0 2px; margin: 0 -2px; border: 0; border-radius: 6px; background: none; outline: none; color: var(--fg); font: var(--ui-type-label); font-weight: var(--fw-label, 500); letter-spacing: var(--ui-tracking); }
.ui-sp-name:focus { background: var(--hover); }
.ui-sp-here { color: var(--ui-text-secondary); font: var(--ui-type-label); font-size: var(--text-secondary, 12px); white-space: nowrap; }
.ui-sp-sub { color: var(--ui-text-secondary); font: var(--ui-type-label); font-size: var(--text-secondary, 12px); letter-spacing: var(--ui-tracking); }
.ui-sp-actions { display: flex; flex: none; gap: 2px; }
.ui-ts-tag, .ui-ts-source .ui-ts-reset, .ui-ss-actions button { white-space: nowrap; }
.keyedit-list::before { white-space: nowrap; }

/* A chip used as a toggle (a view to share, a filter): pressed is a quiet accent fill, nothing else changes. */
.ui-chip[aria-pressed] { padding-right: 10px; cursor: pointer; }
.ui-chip[aria-pressed="true"] { background: var(--accent-soft); color: var(--accent); }

/* Item states (branched.ts `state` and `mark`, and the session manager): an item is open (filled dot), detached
   (hollow dot) or exited (hollow square). A mark follows: new output is a quiet dot, a finished command a tick
   or a cross, so the exit code beside it reads "✓ 0" or "✗ 1". One drawing, used by every list of such items. */
.ui-state-dot { display: grid; flex: none; place-items: center; width: 18px; }
.ui-state-dot::before { content: ""; box-sizing: border-box; width: 7px; height: 7px; border-radius: 50%; }
.ui-state-dot[data-state="open"]::before { background: var(--fg-2); }
.ui-state-dot[data-state="detached"]::before { box-shadow: inset 0 0 0 1.5px var(--fg-3); }
.ui-state-dot[data-state="exited"]::before { width: 6px; height: 6px; border-radius: 1.5px; box-shadow: inset 0 0 0 1.5px var(--fg-3); }
.bm-leaf[aria-current] .ui-state-dot[data-state="open"]::before { background: var(--accent); }
.ui-mark { flex: none; width: 12px; height: 12px; }
.ui-mark.is-new { animation: ui-mark-in 180ms var(--ease-out, ease-out); }
.ui-mark[data-mark="output"] { width: 6px; height: 6px; margin: 0 3px; border-radius: 50%; background: var(--accent); }
.ui-mark[data-mark="ok"], .ui-mark[data-mark="fail"] { background: currentColor; -webkit-mask: var(--ui-mark-icon) center / contain no-repeat; mask: var(--ui-mark-icon) center / contain no-repeat; }
.ui-mark[data-mark="ok"] { color: var(--ok); --ui-mark-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12.5l4.5 4.5L19 7.5'/%3E%3C/svg%3E"); }
/* Ended: the session is gone without an exit code to report. Quiet, not an error: a stopped square. */
.ui-mark[data-mark="ended"] { color: var(--fg-2); background: currentColor; -webkit-mask: var(--ui-mark-icon) center / contain no-repeat; mask: var(--ui-mark-icon) center / contain no-repeat; --ui-mark-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.5' stroke-linejoin='round'%3E%3Crect x='6.5' y='6.5' width='11' height='11' rx='2.5'/%3E%3C/svg%3E"); }
.ui-mark[data-mark="fail"] { color: var(--danger); --ui-mark-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3' stroke-linecap='round'%3E%3Cpath d='M6.5 6.5l11 11M17.5 6.5l-11 11'/%3E%3C/svg%3E"); }
/* In a branched menu the mark sits on the right, against the note beside it. */
.bm-leaf .ui-mark { margin-left: auto; }
.bm-leaf .ui-mark[data-mark="output"] { margin-right: 3px; }
.bm-leaf .ui-mark + .bm-meta { margin-left: 6px; }
@keyframes ui-mark-in { from { opacity: 0; transform: scale(0.5); } }
@media (prefers-reduced-motion: reduce) { .ui-mark.is-new { animation: none; } }
/* A long leaf name gives way to its marks and notes instead of wrapping onto a second line. */
.bm-leaf .bm-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* A caption between groups inside one section: the host above its sessions. It has no branch of its own. */
.bm-subhead { display: flex; align-items: center; height: var(--row); padding: 0 8px; color: var(--fg-2); font: var(--fw-label) var(--text-caption)/1.2 var(--font-mono); letter-spacing: 0; }

/* Entity chips (entity-chip.ts): a recognised thing named in text. Text-height, on the baseline, one unit when the line
   wraps, never animated. The fill is set inline from the palette tokens, so a theme switch recolours it with no script. */
.ui-entity {
  display: inline-flex; align-items: center; gap: 0.3em; box-sizing: border-box; max-width: 100%; padding: 0 0.55em 0 0.45em;
  border-radius: 999px; font-weight: 500; line-height: 1.35; white-space: nowrap; text-shadow: none; vertical-align: baseline;
  letter-spacing: 0; transition: none; animation: none;
}
.ui-entity-name { overflow: hidden; text-overflow: ellipsis; }
.ui-entity-glyph { flex: none; width: 0.9em; height: 0.9em; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.ui-entity-glyph.ui-entity-fill { fill: currentColor; stroke: none; }
.ui-entity-initial { flex: none; min-width: 0.9em; text-align: center; font-size: 0.9em; font-weight: 600; line-height: 1; }
.ui-entity-unit { white-space: nowrap; }

/* ---- scroll bars only while scrolling (packages/ui/src/scrollbars.ts; TASTE §4) ------------------------------ */
/* Every scroll container except terminals: no bar at rest, a thin thumb while `data-scrolling` is set, which the
   helper clears 800 ms after the last scroll. The thumb is --fg-2 at 45%, and clearing it fades the colour out.
   Two engines, two property sets, because each one ignores the other's:
   - Firefox reads scrollbar-width and scrollbar-color.
   - Chromium and Safari ignore ::-webkit-scrollbar once scrollbar-width is set (Chromium 121+), so they get the
     webkit parts instead. The buttons are hidden: a classic bar draws arrows on Windows and Linux, which is chrome.
   :where() keeps the Firefox rules at zero specificity, so a surface that sets its own scrollbar-width still wins. */
/* Only on a fine pointer (desktop, laptop): touch devices already draw overlay bars that take no layout space.
   A reserved 8 px bar would move the phone's key tray and its targets. */
@media (hover: hover) and (pointer: fine) {
@supports (-moz-appearance: none) {
  :where(*:not(.terminal-host)) { scrollbar-width: thin; scrollbar-color: transparent transparent; transition: scrollbar-color 240ms cubic-bezier(0.23, 1, 0.32, 1); }
  :where(*:not(.terminal-host))[data-scrolling] { scrollbar-color: color-mix(in srgb, var(--fg-2) 45%, transparent) transparent; }
}
@supports not (-moz-appearance: none) {
  *:not(.terminal-host)::-webkit-scrollbar { width: 8px; height: 8px; background: transparent; }
  *:not(.terminal-host)::-webkit-scrollbar-button { display: none; }
  *:not(.terminal-host)::-webkit-scrollbar-track { background: transparent; }
  *:not(.terminal-host)::-webkit-scrollbar-thumb { background: transparent; border-radius: 4px; transition: background-color 240ms cubic-bezier(0.23, 1, 0.32, 1); }
  *:not(.terminal-host)[data-scrolling]::-webkit-scrollbar-thumb { background: color-mix(in srgb, var(--fg-2) 45%, transparent); }
}
}
@media (prefers-reduced-motion: reduce) {
  :where(*:not(.terminal-host)), *:not(.terminal-host)::-webkit-scrollbar-thumb { transition: none; }
}

/* Inbox (inbox.ts): the sidebar's card of things that wait on the person. It sits above the footer, inside the
   sidebar's 8 px padding, as a quiet tinted card (no second glass layer inside the glass). Items stack; each is a
   sentence, a quiet line and one action. Only opacity and transform move, and the transform is cleared at rest. */
.ui-inbox {
  display: grid; flex: none; gap: 4px; margin: auto 0 8px; padding: 8px;
  border-radius: calc(var(--r-lg, 18px) - 8px); background: color-mix(in oklab, var(--accent) 9%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--accent) 22%, transparent);
  transition: opacity 160ms var(--ease-out, ease-out), transform 180ms var(--ease-out, ease-out);
}
/* A card of its own, like the toast: the glass halo would only smear its glyphs (and the solid action takes none). */
.ui-inbox, .ui-inbox * { text-shadow: none; }
.ui-inbox[hidden] { display: none; }
.ui-inbox[data-open="false"] { opacity: 0; transform: translateY(6px); pointer-events: none; }
.ui-inbox ~ .ui-sidebar-foot { margin-top: 0; }
.ui-inbox-header { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 2px 6px 0; color: var(--ui-text-secondary); font-size: var(--text-caption); font-weight: var(--fw-label); line-height: 1.4; }
.ui-inbox-count { font-variant-numeric: tabular-nums; }
.ui-inbox-list { display: grid; gap: 2px; }
.ui-inbox-row {
  display: flex; align-items: center; gap: 8px; width: 100%; min-height: var(--row); padding: 0 6px; border: 0; border-radius: 8px;
  background: none; color: var(--fg); font: inherit; font-size: var(--text-body); font-weight: var(--fw-label); text-align: left; cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: background-color 120ms ease, transform 140ms var(--ease-out, ease-out);
}
@media (hover: hover) and (pointer: fine) { .ui-inbox-row:hover { background: color-mix(in oklab, var(--fg) 6%, transparent); } }
.ui-inbox-row:active { transform: scale(var(--ui-press-scale-soft)); }
.ui-inbox-glyph, .ui-inbox-chevron { flex: none; width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.ui-inbox-glyph { color: var(--accent); }
.ui-inbox-chevron { margin-left: auto; width: 14px; height: 14px; color: var(--ui-text-secondary); transition: transform 180ms var(--ease-out, ease-out); }
.ui-inbox-item[data-expanded="true"] .ui-inbox-chevron { transform: rotate(90deg); }
.ui-inbox-title { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The panel's height is a grid track, so opening is a plain CSS transition: it can be reversed mid-way and costs no script. */
.ui-inbox-panel { display: grid; grid-template-rows: 0fr; visibility: hidden; transition: grid-template-rows 180ms var(--ease-out, ease-out), visibility 0s 180ms; }
.ui-inbox-item[data-expanded="true"] .ui-inbox-panel { grid-template-rows: 1fr; visibility: visible; transition-delay: 0s; }
.ui-inbox-panel-inner { display: grid; gap: 8px; min-height: 0; overflow: hidden; padding: 0 6px 0 30px; opacity: 0; transition: opacity 150ms ease, padding-bottom 180ms var(--ease-out, ease-out); }
.ui-inbox-item[data-expanded="true"] .ui-inbox-panel-inner { opacity: 1; padding-bottom: 6px; }
.ui-inbox-body { margin: 0; padding-top: 2px; color: var(--ui-text-secondary); font-size: var(--text-secondary); line-height: 1.4; }
.ui-inbox-actions { display: flex; justify-content: flex-end; gap: 6px; }
.ui-inbox-action { --ui-button-height: 28px; --ui-button-font: 500 var(--text-secondary)/1 var(--font-ui, "Geist Mono", ui-monospace, monospace); min-height: 28px; padding: 0 12px; border-radius: 8px; }
@media (pointer: coarse) { .ui-inbox { padding: 6px; } .ui-inbox-action { --ui-button-height: 44px; min-height: 44px; } }
@media (prefers-reduced-motion: reduce) { .ui-inbox, .ui-inbox[data-open="false"] { transform: none; transition: opacity 150ms ease; } .ui-inbox-panel, .ui-inbox-panel-inner, .ui-inbox-chevron, .ui-inbox-row { transition-duration: 1ms; } .ui-inbox-panel-inner { transition: opacity 150ms ease; } }
