/* Clusters concept mockup (src/clusters.ts, issue #89). Everything here is scoped to
   `.terminal-screen[data-clusters]` or to `cl-` classes; surfaces, pills, window cards,
   tooltips and the toast come from packages/ui and are not restyled. */

/* The screen around the canvas never scrolls. With the viewport no longer an `overflow: clip` box (below), the browser
   may scroll the nearest scroll container to bring a focused terminal into view, which moved the whole canvas sideways
   by up to a column and kept off-screen terminals alive (#126). `clip` clips like `hidden` and cannot be scrolled. */
.terminal-screen[data-clusters] { overflow: clip; }
/* The canvas is clipped to its box, with 12 px more at the top: a window flush with the top edge (a lone window, the first
   row) carries its lock glyph on the frame line, and the shackle rises above that line (#126, D195). The clip is
   `overflow: clip` on `.panes`, the viewport's parent, whose box reaches 12 px above the viewport (negative margin, the same
   amount as padding) while the viewport sits `inset: 12px` from the padding edge: the viewport's own rectangle, which the
   camera measures, is unchanged. Keeping clipping on the parent leaves the viewport free of a backdrop root, so a
   Frosted window's `backdrop-filter` can sample the canvas behind it (#256). `overflow: clip` does not make a backdrop root, cannot be scrolled (the first rule above), and
   needs no `overflow-clip-margin`. Nothing between a window and the canvas backdrop may take a clip-path, filter, mask,
   opacity or blend mode, or the blur goes blind again. */
.terminal-screen[data-clusters][data-canvas="on"] .panes { overflow: clip; margin-top: -0.75rem; padding-top: 0.75rem; }
.cl-viewport { position: absolute; inset: 0.75rem 0 0 0; overflow: visible; touch-action: none; -webkit-user-select: none; user-select: none; }
.cl-viewport[data-panning] { cursor: grabbing; }
/* While the camera moves each window is a layer of its own: it is rastered once at its size and the compositor only scales it,
   where one shared layer was rastered again as the zoom changed (the windows' shadows, headers and borders: on a GPU the raster
   workers took 233 ms of every second of a zoom with 54 windows, 119 with this). The hint goes at rest, so nothing is soft there. */
.cl-world[data-moving] .ui-window { will-change: transform; }
.cl-world { position: absolute; left: 0; top: 0; width: 0; height: 0; transform-origin: 0 0; }
.cl-overlay { position: absolute; left: 0; top: 0; width: 1px; height: 1px; overflow: visible; pointer-events: none; z-index: 12; }

/* ---- a view ---------------------------------------------------------------------------- */
.cl { position: absolute; left: 0; top: 0; pointer-events: none; }
.cl[data-lifted] { z-index: 8; }
.cl[data-lifted] .cl-region { box-shadow: var(--shadow); }
.cl-region {
  position: absolute; border-radius: 1.75rem; border: 1px solid var(--overlay-line);
  background: color-mix(in oklab, var(--fg) 3%, transparent);
  opacity: 0; pointer-events: none; transition: opacity 220ms var(--ease-out, ease-out);
}
.cl-world[data-zoom="out"] .cl-region { opacity: 1; pointer-events: auto; cursor: pointer; }

/* The label keeps a readable size at any zoom: --label-inv is 1 / zoom (clamped), applied with the
   individual `scale` and `translate` properties so the pill's own press transform stays free. It changes on every
   frame of a zoom, so it does not inherit: only the label restyles, not its icon, name, meta and menu. */
@property --label-inv { syntax: "<number>"; inherits: false; initial-value: 1; }
.cl-label {
  --pill-h: 1.875rem; position: absolute; z-index: 2; pointer-events: auto; gap: 0.5rem; padding: 0 0.75rem 0 0.625rem;
  color: var(--fg); font: var(--ui-type-button); letter-spacing: var(--ui-tracking); white-space: nowrap; cursor: grab; touch-action: none;
  transform-origin: 0 0; scale: var(--label-inv); translate: 0 calc(var(--label-inv) * -2.5rem);
}
.cl-label-icon { display: grid; place-items: center; color: var(--fg-2); }
.cl-label-icon svg { width: 1rem; height: 1rem; fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
.cl-label-meta { color: var(--ui-text-secondary); font-weight: 450; }
.cl-world[data-zoom="in"] .cl-label-meta { display: none; }
.cl-world[data-zoom="in"] .cl-label { opacity: 0.8; }
@media (pointer: coarse) { .cl-label { --pill-h: 1.875rem; } }

.cl-content { position: absolute; pointer-events: none; }
.cl-cols { position: relative; display: flex; gap: 12px; width: 100%; height: 100%; }
.cl-lift { position: absolute; inset: 0; z-index: 6; pointer-events: none; overflow: visible; }

/* A column: windows stack and the stack scrolls on its own. The padding and negative margin give
   window shadows room inside the scroller without changing the layout. In px, like the canvas's other geometry (canvas.css). */
.cl-col {
  position: relative; flex: none; box-sizing: border-box; width: calc(var(--w) + 28px); height: calc(100% + 28px); margin: -14px; padding: 14px;
  display: flex; flex-direction: column; gap: 12px; overflow-x: clip; overflow-y: auto; overscroll-behavior: contain;
  pointer-events: none; touch-action: pan-y;
}
.cl-col::-webkit-scrollbar { display: none; }
.cl-col > .ui-window { flex: 1 1 0; min-height: 10rem; pointer-events: auto; }
/* The column scroller clips at its padding edge, so a window's shadow must end inside it (themes.ts columnShadow): the
   standard shadow reached 20 px below the card, was cut flat at 14 px and showed as a square backing at the corners. */
.cl-col > .ui-window, .cl-group > .cl-col > .ui-window { box-shadow: var(--shadow-column, var(--shadow)); }
.cl-col > .ui-window[style*="height"] { flex: none; }
/* A streamed window settling into its remote window's shape (windows/frame.ts shapeWindow): the frame eases, never jumps. */
.cl-col.is-shaping { transition: width 180ms var(--ease-out, ease-out); }
.cl-col > .ui-window.is-shaping { transition: height 180ms var(--ease-out, ease-out); }
@media (prefers-reduced-motion: reduce) { .cl-col.is-shaping, .cl-col > .ui-window.is-shaping { transition: none; } }
/* A group (D212): columns that a spanning window links share one grid. The group is the scroller, sized and padded as a
   column is, so its tracks line up with plain columns; its columns have no box, so their windows are grid items. */
.cl-group {
  position: relative; flex: none; box-sizing: border-box; display: grid; column-gap: 12px; row-gap: 12px;
  height: calc(100% + 28px); margin: -14px; padding: 14px;
  overflow-x: clip; overflow-y: auto; overscroll-behavior: contain; scrollbar-width: none;
  pointer-events: none; touch-action: pan-y;
}
.cl-group::-webkit-scrollbar { display: none; }
.cl-group > .cl-col { display: contents; }
.cl-group > .cl-col > .ui-window { min-width: 0; min-height: 10rem; pointer-events: auto; }
.cl-world[data-snap] .cl-group { overflow-y: hidden; }
.cl-viewport[data-input-lock] .cl-group { touch-action: none; overflow-y: hidden; }
/* `data-hit`, not `data-lod`: pointer-events is inherited, so flipping it mid-zoom restyled every element under every window (38-47 ms in one frame). The camera sets data-hit when it comes to rest. */
.cl-world[data-hit="thumb"] .ui-window { pointer-events: none; }
.cl-world[data-lod="thumb"] .ui-window-actions, .cl-world[data-lod="thumb"] .ui-window-lock, .cl-world[data-lod="thumb"] .cl-port { opacity: 0 !important; }
/* No fade for the thumbnail swap: it happens in the middle of a zoom, where a 160 ms fade started a transition on every
   window's action bar in one frame (style work for about 180-450 elements), and a crossfade is not seen while the world scales. */
.ui-window.is-lifted { z-index: 20; pointer-events: none; box-shadow: var(--overlay-shadow-large, var(--shadow)); }
.ui-window[data-kind="node"] { cursor: default; }
.ui-window-pill[data-grab] { cursor: grab; touch-action: none; }
.ui-window-pill[data-grab]:active { cursor: grabbing; }
/* The landing cue on the node an arrow snaps onto: the border brightens to the accent at once (a 120 ms flash from the
   foreground colour, ease-out). It stays for reduced motion, which keeps the colour and drops the movement. */
.ui-window[data-drop] { border-color: var(--accent); animation: cl-land 120ms var(--ease-out, cubic-bezier(0.23, 1, 0.32, 1)); }
@keyframes cl-land { from { border-color: var(--fg); } }

/* The soft outline of where a dragged window will land. */
.cl-slot {
  flex: none; box-sizing: border-box; border-radius: var(--r-lg, 1.125rem);
  border: 1px solid color-mix(in oklab, var(--accent) 40%, transparent); background: color-mix(in oklab, var(--accent) 6%, transparent);
}
.cl-ghost {
  position: absolute; left: 0; top: 0; z-index: 0; border-radius: 1.75rem; pointer-events: none; opacity: 0; transition: opacity 160ms ease;
  border: 1.5px dashed color-mix(in oklab, var(--accent) 55%, transparent); background: color-mix(in oklab, var(--accent) 5%, transparent);
}
.cl-ghost[data-on] { opacity: 1; }

/* ---- window bodies by kind --------------------------------------------------------------- */
.cl-preview { overscroll-behavior: auto; touch-action: pan-y; overflow: hidden; }
/* A blinking cursor is a compositor-only opacity change on a layer of its own (`will-change`, and `contain` so nothing
   outside it is laid out or painted because of it): a blink must never repaint the text beside it, or raster what
   lies under glass. Only the active window's cursor blinks, and only while that window is on screen: an idle page
   with many windows then has nothing animating at all (#121: 55 idle frame drops on a phone with three blinks). */
.cl-cursor {
  display: inline-block; width: 0.6em; height: 1.15em; margin-left: 1px; vertical-align: text-bottom; background: var(--fg); opacity: 0.75;
  contain: strict; will-change: opacity;
  animation: cl-blink 1.1s steps(1) infinite paused;
}
.ui-window[data-parked]:not([data-active]):not([data-goo]) { visibility: hidden; } /* memory.ts: a screen or more away */
.ui-window[data-active][data-onscreen] .cl-cursor { animation-play-state: running; }
@keyframes cl-blink { 50% { opacity: 0; } }
.cl-logs { color: var(--fg-2); }
.cl-log-time { color: var(--fg-3); }
.cl-log-src { color: var(--accent); }
.cl-log-warn { color: var(--warn, #e5c07b); }
.cl-log-error { color: var(--danger, #e06c75); }
/* A log view scrolls by itself (the preview class hides overflow for thumbnails) and keeps its scrollbar quiet. */
.cl-logs { scrollbar-width: thin; white-space: pre-wrap; overflow-wrap: anywhere; overflow-y: auto; overscroll-behavior: contain; touch-action: pan-y; }
.cl-log-jump { position: absolute; right: 0.75rem; bottom: 0.75rem; z-index: 2; }
.cl-log-jump[hidden] { display: none; }

.cl-list { position: absolute; inset: 0; display: flex; flex-direction: column; gap: 2px; padding: 0.375rem 0.5rem 0.75rem; overflow: auto; }
.cl-row {
  display: grid; grid-template-columns: 0.625rem auto minmax(0, 1fr) auto; align-items: center; gap: 0.625rem; flex: none; height: var(--ui-row-h, 2rem); padding: 0 0.625rem;
  border: 0; border-radius: 0.5rem; 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);
}
.cl-row:active { transform: scale(0.98); }
@media (hover: hover) and (pointer: fine) { .cl-row:hover { background: var(--hover); } }
.cl-state { width: 0.375rem; height: 0.375rem; border-radius: 50%; background: var(--ok); }
.cl-row[data-state="restarting"] .cl-state { background: var(--danger, var(--fg-3)); }
.cl-row[data-state="exited"] .cl-state { background: transparent; box-shadow: inset 0 0 0 1.5px var(--fg-3); }
.cl-name { font-weight: 500; }
.cl-image { min-width: 0; overflow: hidden; color: var(--ui-text-secondary); text-overflow: ellipsis; white-space: nowrap; }
.cl-cpu { color: var(--fg-3); font-variant-numeric: tabular-nums; }

.cl-node-meta { display: grid; gap: 1px; padding: 0.25rem 0.875rem 0.625rem; font: var(--ui-type-label); letter-spacing: var(--ui-tracking); }
.cl-node-detail { color: var(--fg); }
/* Two lines at most: "188 ms, through fra-relay" is longer than a card is wide, and a cut-off figure reads as a wrong one. */
.cl-node-sub { color: var(--ui-text-secondary); overflow: hidden; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; overflow-wrap: anywhere; }
/* Arrows pass under the cards, so a line that crosses a card is hidden by it instead of cutting through its text. */
.cl-graph-arrows { position: absolute; left: 0; top: 0; z-index: 0; width: 1px; height: 1px; overflow: visible; pointer-events: none; }
.cl-world[data-lod="thumb"] .cl-node-meta { opacity: 1; }

/* ---- arrows ------------------------------------------------------------------------------ */
.cl-arrow { --arrow: var(--accent); transition: opacity 200ms ease; }
.cl-arrow[data-tone="quiet"] { --arrow: color-mix(in oklab, var(--accent) 80%, var(--fg)); }
.cl-arrow-line { fill: none; stroke: var(--arrow); stroke-width: 1.5; stroke-linecap: round; vector-effect: non-scaling-stroke; }
.cl-arrow-head { fill: var(--arrow); stroke: none; }
/* Editing a grant (Mesh): hover thickens the line and shows a grab handle on each end; a wide invisible line makes it easy to hit. */
.cl-arrow-hit { fill: none; stroke: transparent; stroke-width: 1rem; vector-effect: non-scaling-stroke; pointer-events: stroke; }
.cl-arrow-handle { fill: var(--bg); stroke: var(--arrow); stroke-width: 1.5px; vector-effect: non-scaling-stroke; pointer-events: none; cursor: grab; opacity: 0; transition: opacity 140ms ease; }
/* Only the hovered arrow's handles take the pointer, so arrows that share an end never fight over it. */
.cl-arrow:hover .cl-arrow-handle { pointer-events: all; }
.cl-arrow:hover .cl-arrow-handle, .cl-arrow[data-editing] .cl-arrow-handle { opacity: 1; }
.cl-arrow:hover .cl-arrow-line { stroke-width: 0.15625rem; }
.cl-arrow:hover .cl-arrow-head { filter: none; }
.cl-arrow[data-editing] { opacity: 0.25; }
.cl-arrow-handle:active { cursor: grabbing; }
/* Touch: no hover, so a tap on an arrow selects it and only then does it show its two handles (rings that leave the
   arrowhead visible); a wider invisible line makes it easy to hit. */
.cl-arrow[data-selected] .cl-arrow-handle { pointer-events: all; opacity: 1; }
.cl-arrow-handle { fill: transparent; }
@media (pointer: coarse) { .cl-arrow-hit { stroke-width: 1.875rem; } }
.cl-arrow[data-dashed] .cl-arrow-line { stroke-dasharray: 5 5; }
.cl-arrow[data-fresh], .cl-arrow[data-leaving] { opacity: 0; }
.cl-arrow[data-live]:not([data-snapped]) .cl-arrow-line { opacity: 0.8; }
.cl-arrow-label { fill: var(--ui-text-secondary); font: 450 0.75rem var(--font-ui, "Geist Mono", ui-monospace, monospace); paint-order: stroke; stroke: var(--bg); stroke-width: 0.1875rem; stroke-linejoin: round; pointer-events: none; }
.cl-arrow-label[display="none"] { display: none; }
/* On-demand arrows: faint, and they fade in one after another. */
.cl-reach .cl-arrow { opacity: 0.55; transition: opacity 240ms ease calc(var(--i, 0) * 50ms); }
.cl-reach[data-open="false"] .cl-arrow { opacity: 0; transition-delay: 0ms; }

/* The connector next to a pill: shown while arrows are, drag it onto another terminal. */
.cl-port {
  position: relative; display: grid; flex: none; place-items: center; width: 1.5rem; height: 1.5rem; margin-left: -2px; padding: 0;
  border: 1px solid color-mix(in oklab, var(--accent) 70%, transparent); border-radius: 50%; background: var(--accent-soft); color: var(--accent);
  opacity: 0; transform: scale(0.6); pointer-events: none; cursor: crosshair; touch-action: none;
  transition: opacity 150ms ease, transform 220ms var(--ease-out, cubic-bezier(0.23, 1, 0.32, 1));
}
.cl-port svg { width: 0.875rem; height: 0.875rem; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.ui-window[data-reach] .cl-port { opacity: 1; transform: none; pointer-events: auto; }
.cl-port:active { transform: scale(0.94); }
@media (pointer: coarse) { .cl-port { width: 2rem; height: 2rem; } .cl-port::after { content: ""; position: absolute; inset: -0.375rem; } }

.cl-edges { position: absolute; inset: 0; z-index: 4; pointer-events: none; }
.cl-edge { --pill-h: 1.875rem; position: absolute; left: 0; top: 0; display: none; gap: 0.375rem; padding: 0 0.75rem 0 0.5rem; pointer-events: auto; color: var(--fg); font: var(--ui-type-label); letter-spacing: var(--ui-tracking); cursor: pointer; }
.cl-edge[data-on] { display: inline-flex; }
.cl-edge-arrow { display: grid; place-items: center; width: 1rem; height: 1rem; color: var(--accent); }
.cl-edge-arrow svg { width: 1rem; height: 1rem; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }

/* ---- hover summary (desktop, zoomed out) ------------------------------------------------- */
.cl-summary {
  position: fixed; z-index: 1001; min-width: 13.75rem; max-width: 18.75rem; box-sizing: border-box; padding: 0.75rem; border-radius: 0.875rem; pointer-events: none;
  overflow-y: auto; overscroll-behavior: contain;
  opacity: 0; transform: scale(0.96); transition: opacity 150ms ease-out, transform 180ms var(--ease-out, cubic-bezier(0.23, 1, 0.32, 1));
}
.cl-summary[hidden] { display: none; }
.cl-summary[data-open="true"] { opacity: 1; transform: none; pointer-events: auto; }
.cl-summary-rows { display: grid; gap: 2px; }
/* A window row in the card: the view list's row, taller when its detail wraps (values wrap, never truncate). */
.cl-summary-pick { height: auto; min-height: var(--ui-row-h, 2rem); padding: 0.375rem 0.5rem; }
.cl-summary-pick .cl-viewrow-name { flex: 0 1 auto; max-width: 55%; overflow-wrap: anywhere; font-weight: 500; }
.cl-summary-pick .cl-viewrow-meta { flex: 1 1 0; min-width: 0; overflow-wrap: anywhere; text-align: right; }
.cl-summary-all { margin-top: 0.375rem; }
.cl-summary-head { display: flex; align-items: center; gap: 0.5rem; margin-bottom: 0.375rem; font: var(--ui-type-name); letter-spacing: var(--ui-tracking); }
.cl-summary-icon { display: grid; color: var(--fg-2); }
.cl-summary-icon svg { width: 1rem; height: 1rem; fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
.cl-summary-row { display: flex; align-items: center; justify-content: space-between; gap: 1rem; min-height: var(--row, 1.75rem); border-top: 1px solid var(--line); font: var(--ui-type-label); letter-spacing: var(--ui-tracking); }
.cl-summary-k { color: var(--fg); }
.cl-summary-v { color: var(--ui-text-secondary); }

/* ---- empty view -------------------------------------------------------------------------- */
.cl-empty {
  position: absolute; inset: 0; display: grid; place-content: center; justify-items: center; gap: 0.375rem; padding: 1.5rem; text-align: center; pointer-events: none;
  border: 1.5px dashed var(--overlay-line); border-radius: var(--r-lg, 1.125rem);
}
.cl-empty > * { pointer-events: auto; margin: 0; }
.cl-empty-slot { display: none; }
.cl-empty-title { font: var(--ui-type-name); letter-spacing: var(--ui-tracking); }
.cl-empty-hint { color: var(--ui-text-secondary); font: var(--ui-type-label); letter-spacing: var(--ui-tracking); }
.cl-empty-add { --pill-h: var(--ui-control-h, 1.875rem); gap: 0.375rem; margin-top: 0.625rem !important; padding: 0 0.875rem 0 0.625rem; color: var(--fg); font: var(--ui-type-button); letter-spacing: var(--ui-tracking); cursor: pointer; }
.cl-empty-add svg { width: 1rem; height: 1rem; fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; }


/* ---- pill morphs: the floating terminal and the view list ---------------------------------------- */
.cl-float { position: absolute; z-index: 9; display: block; overflow: hidden; border-radius: var(--r-lg, 1.125rem); color: var(--fg); }
.cl-float .ui-window { height: 100%; border: 0; border-radius: 0; background: none; box-shadow: none; }
.cl-viewlist { position: absolute; z-index: 9; display: flex; flex-direction: column; padding: 0.375rem; border-radius: var(--r-lg, 1.125rem); }
.cl-viewlist-rows { display: grid; gap: 2px; margin: 0; padding: 0; list-style: none; }
.cl-viewrow {
  display: flex; align-items: center; gap: 0.625rem; width: 100%; height: var(--ui-row-h, 2rem); padding: 0 0.625rem; border: 0; border-radius: 0.625rem; 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);
}
.cl-viewrow:active { transform: scale(0.98); }
@media (hover: hover) and (pointer: fine) { .cl-viewrow:hover { background: var(--hover); } }
.cl-viewrow[aria-current="true"] { background: var(--hover); }
.cl-viewrow-icon { display: grid; flex: none; color: var(--fg-2); }
.cl-viewrow-icon svg { width: var(--ui-icon, 1rem); height: var(--ui-icon, 1rem); fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
.cl-viewrow-name { flex: 1; min-width: 0; font-weight: 500; }
.cl-viewrow-meta { color: var(--ui-text-secondary); }
.cl-viewrow-keys { display: inline-flex; gap: 0.25rem; }
.cl-viewrow-keys kbd { min-width: 1.375rem; padding: 0.25rem 0.375rem; border-radius: 0.375rem; background: var(--hover); color: var(--fg-2); font: 500 var(--text-caption, 0.6875rem)/1 var(--font-ui, "Geist Mono", ui-monospace, monospace); text-align: center; }
.cl-viewrow-search { margin-top: 0.25rem; border-radius: 0 0 0.625rem 0.625rem; box-shadow: 0 -1px 0 var(--line); }
.cl-viewrow-search .cl-viewrow-name { font-weight: 450; color: var(--ui-text-secondary); }
.cl-badge {
  position: absolute; z-index: 9; translate: -50% 0; padding: 0.375rem 0.75rem; border-radius: 999px; pointer-events: none; white-space: nowrap;
  font: var(--ui-type-label); letter-spacing: var(--ui-tracking); opacity: 0; transform: translateY(-0.25rem) scale(0.96);
  transition: opacity 180ms ease, transform 220ms var(--ease-out, cubic-bezier(0.23, 1, 0.32, 1));
}
.cl-badge[hidden] { display: none; }
.cl-badge[data-open="true"] { opacity: 1; transform: none; }

/* ---- mockup scaffolding: a small tag in the top-right corner (scenes live in the palette) ---- */
.cl-mockup { position: absolute; z-index: 6; top: calc(0.625rem + env(safe-area-inset-top) + var(--pill-h, 2.375rem) + 0.25rem); right: var(--gutter); height: 1.375rem; padding: 0 0.5625rem; color: var(--fg-3); font: var(--ui-type-eyebrow); letter-spacing: 0.02em; text-transform: uppercase; cursor: pointer; }
/* On a desktop it sits in the top row, left of the canvas controls, so it never covers a window. */
@media (hover: hover) and (pointer: fine) and (min-width: 960px) { .cl-mockup { top: calc(0.625rem + env(safe-area-inset-top) + (var(--pill-h, 2.375rem) - 1.375rem) / 2); right: calc(var(--gutter) + 3 * (var(--pill-h, 2.375rem) + 0.625rem)); } }

/* The snap toggle takes the slot of the disabled "+" in the top bar's right group. */
.cl-snap, .cl-viewsbtn { color: var(--fg-2); }
.cl-viewsbtn svg, .cl-snap svg { stroke-width: 1.6; }
/* On touch the toolbar has these (canvas row), and its row replaces the frame lock. */
/* Where the canvas buttons sit beside "+", the top bar keeps room for all three (--side, style.css) so the pill, centred on
   the screen, ends before them. Below 600 px the pill would be left under ~300 px, so the buttons give way instead (the
   bottom toolbar has both actions) and the pill keeps the room; no text is cut. */
.terminal-screen[data-canvas="on"] .top { --side: calc(3 * var(--pill-h) + 0.75rem); }
@media (max-width: 600px) { .cl-snap, .cl-viewsbtn { display: none; } .terminal-screen[data-canvas="on"] .top { --side: var(--pill-h); } }
@media (pointer: coarse) { .terminal-screen[data-canvas="on"] .top { --side: var(--pill-h); } }
@media (pointer: coarse) { .cl-snap, .cl-viewsbtn { display: none; } .terminal-screen[data-clusters] .ui-window-lock { display: none; } }
.cl-snap[aria-pressed="true"] { color: var(--accent); }
.cl-snap svg { width: var(--ui-icon, 1rem); height: var(--ui-icon, 1rem); }
.cl-world[data-snap] .cl-label, .cl-world[data-snap] .cl-region { opacity: 0; pointer-events: none; }
.cl-viewport[data-snap] { touch-action: pan-x pan-y; }
/* Canvas lock on: columns do not scroll by hand; windows step (wheel, two fingers, toolbar) and one finger scrolls the terminal. */
.cl-world[data-snap] .cl-col { overflow-y: hidden; }

@media (prefers-reduced-motion: reduce) {
  .ui-window[data-active] .cl-cursor { animation: none; }
  .cl-region, .cl-port, .cl-badge, .cl-summary { transition: opacity 120ms ease; transform: none; }
}

/* On a phone the host matters more than the user, as on the product canvas: drop `user@` in the pill. */
@media (max-width: 480px) { .cl-viewport .ui-window-user, .cl-float .ui-window-user { display: none; } }

.cl-holder { position: absolute; left: -9999px; top: 0; width: 27.5rem; height: 35rem; visibility: hidden; pointer-events: none; }
.cl-col .ui-window-body > .terminal-host { position: absolute; inset: 0; }
.terminal-screen[data-clusters] .capsule-rtt { display: none; }
.terminal-screen[data-clusters] .capsule-session { color: var(--fg-3); }
@media (max-width: 480px) { .cl-noun { display: none; } }

/* During a split the two frames are drawn by the goo layer (clusters-split.ts); the windows keep only their contents. */
.ui-window[data-goo] { transition: none; background: transparent; border-color: transparent; box-shadow: none; -webkit-backdrop-filter: none; backdrop-filter: none; }
/* The two frames after the silhouette ends: the real windows are back, their borders do not fade in under the silhouette. */
.ui-window[data-goo-end] { transition: none !important; }
.cl-goo { z-index: 0; }

/* Settings view: a section's content fills its window (the shared settings pane, scrolled by itself). */
.cl-col .ui-window-body > .ui-settings-pane { position: absolute; inset: 0; }
.bm-sec.cl-divided { margin-top: 0.5rem; padding-top: 0.5rem; box-shadow: 0 -1px 0 var(--line); }
.cl-viewlist-rows > li.cl-divided { margin-top: 0.25rem; padding-top: 0.25rem; box-shadow: 0 -1px 0 var(--line); }

/* The one-time "Continue from <device>?" chip: the shared pill, centred under the top pill, fades on its own. */
.cl-continue { position: absolute; z-index: 9; left: 50%; top: calc(env(safe-area-inset-top) + 0.625rem + var(--pill-h, 2.375rem) + 0.625rem); translate: -50% 0; gap: 0.25rem; padding: 0 0.25rem 0 0.875rem; white-space: nowrap; font: var(--ui-type-label); letter-spacing: var(--ui-tracking); opacity: 0; transform: translateY(-0.375rem) scale(0.97); transition: opacity 200ms ease, transform 240ms var(--ease-out, cubic-bezier(0.23, 1, 0.32, 1)); }
.cl-continue[data-open="true"] { opacity: 1; transform: none; }
.cl-continue-text { padding-right: 0.375rem; }
@media (prefers-reduced-motion: reduce) { .cl-continue { transform: none; } }

/* ---- sharing (clusters-share.ts): the dialog is the shared overlay material; chips, switch and pill are the shared ones ---- */
.cl-share { position: absolute; z-index: 9; display: flex; flex-direction: column; border-radius: var(--r-lg, 1.125rem); color: var(--fg); }
.cl-share-head { display: flex; align-items: center; height: var(--pill-h, 2.375rem); padding: 0 0.875rem; font: var(--ui-type-name); letter-spacing: var(--ui-tracking); }
.cl-share-body { display: grid; gap: 0.375rem; padding: 0 0.875rem 0.25rem; }
.cl-share-label { margin: 0.375rem 0 0; color: var(--ui-text-secondary); font: var(--ui-type-eyebrow); letter-spacing: 0.02em; text-transform: uppercase; }
.cl-share-views { display: flex; flex-wrap: wrap; gap: 0.375rem; }
.cl-share-publish { display: flex; align-items: center; justify-content: space-between; min-height: var(--ui-row-h, 2rem); font: var(--ui-type-label); letter-spacing: var(--ui-tracking); }
.cl-share-note { margin: 0.25rem 0 0; color: var(--ui-text-secondary); font: var(--ui-type-label); font-size: var(--text-secondary, 0.75rem); letter-spacing: var(--ui-tracking); }
.cl-share-foot { display: flex; justify-content: flex-end; padding: 0.375rem 0.625rem 0.625rem; }
.cl-share-send { --pill-h: var(--ui-control-h, 1.875rem); color: var(--accent); }
.cl-share-send:disabled { color: var(--fg-3); opacity: 0.6; }
.cl-unlink { position: absolute; z-index: 9; left: 50%; top: calc(env(safe-area-inset-top) + 0.625rem + var(--pill-h, 2.375rem) + 0.625rem); translate: -50% 0; gap: 0.5rem; padding: 0 0.875rem 0 0.625rem; white-space: nowrap; color: var(--fg); font: var(--ui-type-label); letter-spacing: var(--ui-tracking); cursor: pointer; opacity: 0; transform: translateY(-0.375rem) scale(0.97); transition: opacity 200ms ease, transform 240ms var(--ease-out, cubic-bezier(0.23, 1, 0.32, 1)); }
.cl-unlink[data-open="true"] { opacity: 1; transform: none; }
.cl-unlink[hidden] { display: none; }
.cl-unlink svg { width: var(--ui-icon, 1rem); height: var(--ui-icon, 1rem); fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
.cl-team { margin-left: 0.375rem; }
.cl-noaccess { position: absolute; inset: 0; display: grid; place-content: center; justify-items: center; gap: 0.375rem; padding: 1.5rem; text-align: center; }
.cl-noaccess-title { margin: 0; color: var(--fg-2); font: var(--ui-type-name); letter-spacing: var(--ui-tracking); }
.cl-noaccess-hint { margin: 0; max-width: 28ch; color: var(--ui-text-secondary); font: var(--ui-type-label); letter-spacing: var(--ui-tracking); }
.cl-noaccess svg { width: 1.375rem; height: 1.375rem; fill: none; stroke: var(--fg-3); stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
.cl-world[data-snap] .cl-label-menu { display: none; }
.cl-label-menu { display: grid; place-items: center; width: 1.375rem; height: 1.375rem; margin-right: -0.375rem; padding: 0; border: 0; border-radius: 50%; background: none; color: var(--fg-3); cursor: pointer; }
.cl-label-menu svg { width: 0.875rem; height: 0.875rem; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; }
@media (hover: hover) and (pointer: fine) { .cl-label-menu:hover { background: var(--hover); color: var(--fg); } }

/* A Mesh node has no input to lock, so it has no lock and its title pill may use the whole header. */
.ui-window[data-kind="node"] .ui-window-lock { display: none; }
.ui-window[data-kind="node"] .ui-window-pill { max-width: none; }

/* The transparency comparison (canvas/mock/transparency.ts): busy content under four card samples and four window samples. */
.cl-compare { position: absolute; inset: 0; z-index: 30; display: grid; align-content: center; overflow: hidden; background: linear-gradient(115deg, color-mix(in srgb, var(--mesh-1) 46%, var(--bg)), color-mix(in srgb, var(--mesh-2) 46%, var(--bg)) 35%, color-mix(in srgb, var(--mesh-3) 46%, var(--bg)) 70%, color-mix(in srgb, var(--mesh-4) 46%, var(--bg))); }
.cl-compare-busy { position: absolute; inset: 0; margin: 0; padding: 1rem 1.25rem; overflow: hidden; color: var(--fg-2); font: var(--fw-body) 0.875rem/1.5 var(--font-mono, var(--font-face)); white-space: pre; opacity: 0.95; }
.cl-compare-busy .cl-c-prompt { color: var(--accent); }
.cl-compare-busy .cl-c-ok { color: var(--ok); }
.cl-compare-busy .cl-c-bad { color: var(--danger); }
.cl-compare-grid { position: relative; display: grid; gap: 0.75rem; width: min(1180px, calc(100% - 2rem)); margin: 0 auto; }
.cl-compare-h { margin: 0.5rem 0.25rem 0; padding: 0.1875rem 0.625rem; width: max-content; border-radius: 999px; background: var(--overlay-solid); color: var(--fg); font: var(--ui-type-eyebrow); letter-spacing: 0.04em; text-transform: uppercase; }
.cl-compare-row { display: grid; grid-template-columns: repeat(4, 1fr); gap: 0.75rem; }
@media (max-width: 720px) { .cl-compare-row { grid-template-columns: repeat(2, 1fr); } }
.cl-compare-card, .cl-compare-window { display: grid; align-content: start; gap: 0.25rem; min-height: 7.5rem; padding: 0.875rem; border-radius: var(--r-lg, 1.125rem); border: 1px solid var(--overlay-line); font: var(--ui-type-label); }
.cl-compare-window { box-shadow: var(--shadow); color: var(--fg); }
.cl-compare-sample { margin-top: 0.375rem; }
.cl-compare-window .cl-c-prompt { color: var(--accent); }
.cl-compare-window .cl-c-dim { color: var(--fg-2); }
.cl-compare-close { position: absolute; top: calc(0.625rem + env(safe-area-inset-top)); right: var(--gutter, 0.75rem); z-index: 2; }

/* Input locked to a terminal: touch on the strip does nothing (the terminal scrolls itself, terminal-touch.ts), so a
   finger outside it, or one that runs past its end, cannot scroll the column or chain to the canvas (#113). */
.cl-viewport[data-input-lock] .cl-col { touch-action: none; overflow-y: hidden; }

/* A phone at full width: the canvas area runs edge to edge, so a window of 99% of the screen is centred with a hairline
   of margin, not cut by the 12 px gutters (D191). The top bar and the key tray keep their margins. */
.terminal-screen[data-window-edge] .panes { left: env(safe-area-inset-left, 0px); right: env(safe-area-inset-right, 0px); }

/* The summary card is a card of its own (counts and labels), so it takes no halo from the overlay material. */
.cl-summary, .cl-summary * { text-shadow: none; }
