/* desk.css — the workspace: agent bars (toto · tutor · …) + the main column.
   each agent bar docks to ANY edge (drag the head), collapses to a floating strip
   (click the head), resizes (inner handle), toggles on/off. the desk is a grid of
   4 edge zones + the center. form only. */

#desk {
  flex: 1; min-height: 0; position: relative; overflow: hidden;
  background: var(--bg);
  display: grid;
  grid-template-columns: auto 1fr auto;
  grid-template-rows: auto 1fr auto;
  grid-template-areas: "t t t" "l m r" "b b b";
}

/* ── edge zones — agent bars live here; an empty zone collapses to nothing ── */
.zone { display: flex; min-width: 0; min-height: 0; position: relative; }   /* collapsed rails float within their zone (middle row), not over the top/bottom */
.zone[data-edge="top"]    { grid-area: t; flex-direction: row; }
.zone[data-edge="bottom"] { grid-area: b; flex-direction: row; }
.zone[data-edge="left"]   { grid-area: l; flex-direction: column; }
.zone[data-edge="right"]  { grid-area: r; flex-direction: column; }
.zone:empty { display: none; }

/* ── an agent bar — header + convo, dockable / collapsible / resizable ── */
.agent-bar {
  display: flex; flex-direction: column; position: relative;
  flex: 1 1 auto; min-width: 0; min-height: 0;   /* shrink to the zone so the convo SCROLLS, not the bar */
  background: var(--surface); border: 1px solid var(--line);
}
.zone[data-edge="left"] .agent-bar:not(.collapsed), .zone[data-edge="right"] .agent-bar:not(.collapsed) { width: var(--ab-size, 340px); min-width: 280px; }   /* the OPEN bar floors at 280 to read; a collapsed rail (46px) opts out */
.zone[data-edge="top"]  .agent-bar, .zone[data-edge="bottom"] .agent-bar { height: var(--ab-size, 240px); }

/* ── the DESK center (Sum 2026-07-09) — ONE stack: cards AND center-docked bots share #card-stack. a desk bot
   behaves exactly like a card — fills, collapses to a bar, scrolls — so the two never fight for space. ── */
#card-stack > .agent-bar { flex: 1 1 auto; min-height: 260px; margin: var(--space-xs); border: 1px solid var(--line); border-radius: var(--r-md); overflow: hidden; }
#card-stack > .agent-bar.collapsed { flex: 0 0 auto; min-height: 46px; }   /* (Sum) a collapsed desk bar keeps a 46px head — never collapses to 0 + vanishes */

/* ── a CARD docked to a WING (Sum 2026-07-09) sizes like a bot bar — fills the zone, drops the desk margin ── */
.zone .card { position: relative; }   /* anchor the .card-resize slider to the card, not the zone */
.zone[data-edge="left"] .card, .zone[data-edge="right"] .card { width: var(--ab-size, 340px); min-width: 280px; flex: 1 1 auto; min-height: 0; margin: 0; }
.zone[data-edge="top"] .card, .zone[data-edge="bottom"] .card { height: var(--ab-size, 240px); flex: 1 1 auto; min-height: 0; margin: 0; }
/* ── a docked CARD collapses on the RIGHT AXIS (Sum 2026-07-09: "sides collapse to x not y; top not collapsing").
      left/right wings → a thin VERTICAL rail (shrink along X); top/bottom → a thin HORIZONTAL strip that KEEPS its width. ── */
.zone[data-edge="left"]  .card.collapsed,
.zone[data-edge="right"] .card.collapsed { width: 46px; min-width: 46px; flex: 1 1 auto; }   /* rail — override the 280px open floor */
.zone[data-edge="left"]  .card.collapsed .card-head,
.zone[data-edge="right"] .card.collapsed .card-head { height: 100%; flex-direction: column; justify-content: flex-start; padding: 12px 4px; gap: 8px; border-bottom: none; }
.card.collapsed .card-gear { display: none; }   /* a collapsed rail is an icon, not a control panel (Sum 2026-07-18) */
.zone[data-edge="top"]    .card.collapsed,
.zone[data-edge="bottom"] .card.collapsed { height: 46px; flex: 1 1 auto; width: auto; }   /* strip — 46px tall but FILLS the width (Sum: "keep width when collapse top/bottom, fill") */

/* ── collapsed card = identity + run-controls only (Sum: "turn off all buttons but stop and go; rest = expand/drag target").
      .card-ctrls holds every tool button (+ monaco's tabs) → hide the whole cluster; keep the icon, the green jewels (go) + the red STOP.
      the emptied head IS the click-to-expand / drag-to-move target (cards.js + card-reorder already own the head). ── */
.card.collapsed .card-ctrls, .card.collapsed .card-grip, .card.collapsed .card-subbar { display: none; }
.card.collapsed .card-jewels { margin-left: auto; }   /* push go+stop to the far end → a big clickable expand/drag gap in the middle */
.card.collapsed .card-resize { display: none; }        /* a rail/strip doesn't resize (matches the bot rail) */

.ab-head {
  flex-shrink: 0; height: var(--bar-h);
  display: flex; align-items: center; gap: 8px; padding: 0 12px;
  background: var(--head, var(--surface-2)); border-bottom: 1px solid var(--line);
  cursor: grab; user-select: none;
}
.ab-head:active { cursor: grabbing; }
.ab-grip  { color: var(--head-ink, var(--text-dim)); font-size: 16px; }

/* ── THE HEAD STANDARD (Sum 2026-07-10) — one icon SLOT, one gear, one button size, all in tokens so zoom
   scales them and there are no hardcoded px. the GP car set the bar: --head-icon (the reserved SLOT width)
   = 64 so the gear column lines up across every card; icons are BLOWN UP to --head-icon-h to fill the head
   ("blow up other icons if y room"), then centered in the 64 slot by object-fit. collapsed rails opt out. */
:root {
  --head-icon:   64px;   /* the shared icon SLOT width — every head reserves this much so controls line up */
  --head-icon-h: 40px;   /* blown-up icon height — fills the 46px head; square icons center in the 64 slot */
  --head-gear:   28px;   /* (Sum 2026-07-11) bumped 22→28 — tools fill the bar; gear same size everywhere */
  --head-btn:    34px;   /* control buttons — plus · monkey · stop — one size */
  --bar-h:       46px;   /* (Sum 2026-07-11) the head/bar height — cards AND bots, one number */
  --head-pad:    12px;   /* (Sum 2026-07-11) head side padding — so the right cluster lines up across cards + bots (CLI/bots are 12; preview was 14) */
  --head-gap:    8px;    /* head flex gap — same reason */
  /* the run controls (green start/restart · red stop) — ONE size everywhere: cards · CLI · bots · preview.
     bots were 30×26 (smaller than CLI's 36×30); tokenized so they all match. */
  --run-btn-w:   44px;   /* (Sum 2026-07-11) bumped 36→44 — the domes read bigger; every green + red grows together */
  --run-btn-h:   34px;   /* bumped 30→34 */
  --run-btn-fs:  15px;
  --run-btn-pad: 11px;
  /* (Sum 2026-07-11) TABS — fill the bar Y (were floating small in the middle) + bigger font to match.
     one number drives every tab: monaco file tabs · monaco dock chips · CLI tabs. bar is --bar-h (46). */
  --tab-h:   38px;
  --fs-tab:  18px;   /* (Sum 2026-07-11) bumped 16→18 — tabs read bigger; nothing tiny */
}
.ab-icon  { width: var(--head-icon-h); height: var(--head-icon-h); min-width: var(--head-icon); object-fit: contain; -webkit-user-drag: none; user-select: none; }   /* (Sum) no NATIVE img ghost-drag — the icon is a clean grab handle for the head, not a draggable image */
/* collapsed / vertical rails: drop the wide slot so the icon doesn't overflow a narrow rail */
/* (Sum 2026-07-11) collapsed / rail icons — bigger, fill the bar (was --head-icon-h 40 → 46) */
.agent-bar.collapsed .ab-icon, .agent-bar[data-orient="vertical"] .ab-icon,
.card.collapsed .card-icon-img, .card[data-orient="vertical"] .card-icon-img { min-width: 0; width: 46px; height: 46px; }
/* (Sum 2026-07-11) a collapsed BOT on the DESK (center) renders as a real bar, not 0-tall — so the
   one-desk-occupant accordion leaves a clickable bar behind, never a vanished bot. wings keep rail sizing. */
.agent-bar.collapsed:not([data-edge]), .agent-bar.collapsed[data-edge="center"] { height: var(--bar-h); min-height: var(--bar-h); flex: 0 0 var(--bar-h); }
.agent-bar.collapsed:not([data-edge]) .ab-head, .agent-bar.collapsed[data-edge="center"] .ab-head { height: var(--bar-h); }
/* (Sum 2026-07-11) collapsed CENTER cards (CLI · preview) get the SAME hard pin as the bots → the bars match
   height, no more taller-card gap. wing-docked cards keep their own zone rules above. */
.card.collapsed:not([data-edge]), .card.collapsed[data-edge="center"] { height: var(--bar-h); min-height: var(--bar-h); flex: 0 0 var(--bar-h); }
/* (Sum 2026-07-11) belt+suspenders — ANY collapsed occupant IN #card-stack (the center desk) pins to the bar height,
   even if a stray data-edge slipped on. this was the learn-suite two-open bug: .collapsed set, but not shrinking. */
#card-stack > .card.collapsed, #card-stack > .agent-bar.collapsed { height: var(--bar-h); min-height: var(--bar-h); flex: 0 0 var(--bar-h); }
.card-icon-img { -webkit-user-drag: none; }   /* same: a card icon never starts the browser's image-drag (it "stuck to the cursor") */
.ab-icon-css { display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; }  /* a drawn {css} icon — an svg fills the 26px box */
/* (Sum 2026-07-10) THE GEAR — ONE shared cards/gear/gear.png everywhere (bots · cards · app-cards · calendar · cli).
   cross-platform / PC-ready (the ⚙️ emoji rendered Apple-3D on Mac, wrong on Windows). grayscale on the clean skins
   so the coral center doesn't pop where "nothing fun" is the rule. */
.oz-gear-ico { width: var(--head-gear); height: var(--head-gear); object-fit: contain; display: block; -webkit-user-drag: none; }
.ab-gear, .card-gear { background: none; border: 0; padding: 2px; cursor: pointer; display: inline-flex; align-items: center; flex-shrink: 0; }
[data-tier="clean"] .oz-gear-ico { filter: grayscale(1); }
[data-skin="clinical"] .oz-gear-ico { filter: grayscale(1) brightness(.55); }   /* darken for the light skin so it reads */
.ab-icon-css svg { width: var(--head-icon-h); height: var(--head-icon-h); display: block; }   /* square + centered in the wide slot (min-width from .ab-icon), not stretched to fill it */
.ab-emoji { font-size: 24px; line-height: 1; }
/* ICON-ONLY (2026-07-09): the bot's name is hidden in the EXPANDED head — the avatar is the identity,
   the name is the head's title="…" tooltip. the collapsed-to-edge rules below re-show it (more specific)
   as the bot's ONLY identity when collapsed to a strip with no room for the icon. */
.ab-name  { display: none; font-size: var(--fs-label); font-weight: 700; color: var(--head-ink, var(--text)); letter-spacing: 1px; }

/* the head's right cluster — jewels · STOP · ＋. the .ab-fill spacer eats the slack first,
   so they sit together at the right (the ＋'s old margin-left:auto becomes a no-op). jewels
   + STOP are the SHARED chrome the cards wear (.card-jewels/.cj/.card-stop) — written once.
   the STOP rides a touch smaller to fit the 46px bar head, still fat + red. all three (and
   the spacer) hide on a collapsed RAIL — no room. */
.ab-fill { flex: 1; min-width: 0; }
.agent-bar .ab-head .card-jewels { flex-shrink: 0; }
.agent-bar .ab-head .card-stop { min-width: var(--run-btn-w); height: var(--run-btn-h); padding: 0 var(--run-btn-pad); font-size: var(--run-btn-fs); }   /* (Sum 2026-07-11) bots now match CLI — same --run-btn tokens */
.agent-bar.collapsed .ab-fill { display: none; }   /* the spacer is the expanded strip's device only */
/* (Sum 2026-07-11) COLLAPSED bot = icon + the HIL cluster ONLY (🟢 bless · jewels · 🔴 stop). hide gear · ＋add ·
   🧠esp · permission — expand to touch those. leaves start/stop working on the collapsed bar, like the cards do. */
.agent-bar.collapsed:not([data-orient="vertical"]) .ab-head > *:not(.ab-icon):not(.ab-name):not(.card-jewels):not(.ab-bless):not(.card-stop) { display: none !important; }
/* (Sum 2026-07-09) the HIL cluster (🟢 bless · jewels · 🔴 stop) ALWAYS hugs the far end — x+ right (bottom when
   vertical) — even collapsed. margin-auto pushes it there without the spacer + beats the collapsed justify:center. */
.ab-head .ab-bless { margin-left: auto; }
.agent-bar[data-orient="vertical"] .ab-head .ab-bless { margin-left: 0; margin-top: auto; }

/* jewels + the STOP stay LIVE on a collapsed rail too — the halt is always reachable. they
   gain the bar's rotational awareness: on a vertical RAIL the head stacks into a column and
   the jewels stack down it with the bar; the STOP is a square — no rotation, it just rides
   along. (.collapsed .ab-head already centers the cluster in the rail.) */
.agent-bar[data-orient="vertical"] .ab-head { flex-direction: column; gap: 10px; }
.agent-bar[data-orient="vertical"] .card-jewels { flex-direction: column; }

.agent-bar .convo { flex: 1; min-height: 0; }
.agent-bar .convo .msg-bot { color: var(--text); }
.agent-bar .convo .msg-u   { color: var(--accent); }
.agent-bar .convo .msg-stream:empty::before { color: var(--text-dim); }
/* (Sum 2026-07-10) the NURSE signs in blood — her chart + everything she says is RED. also rescues her contrast
   on the dark corporate skin (a lighter red on dark · a deeper red on the light clinical skin). */
.agent-bar[data-agent="nurse"] .convo .msg-bot,
.agent-bar[data-agent="nurse"] .convo .esp-body,
.agent-bar[data-agent="nurse"] .convo .inj-body { color: #e63950 !important; }
[data-skin="clinical"] .agent-bar[data-agent="nurse"] .convo .msg-bot,
[data-skin="clinical"] .agent-bar[data-agent="nurse"] .convo .esp-body,
[data-skin="clinical"] .agent-bar[data-agent="nurse"] .convo .inj-body { color: #c81e2c !important; }

/* ── resize handle — how much screen this agent gets ──────────────────
   IC BREAK · design-rules: "no resize sliders between columns."
   oz bans them — its dashboards scroll on their own, a slider only fights them.
   toto2 inverts the reason: here the slider IS the thesis.
   the user dials how much room the agent takes, because the agent's whole
   job is to get out of the way — the handle is how it steps back.
   broken on purpose. this note is the why. */
/* ALWAYS-ON + findable, ≥30px (design-rules palsy minimum): a subtle gray bar + a soft glow +
   a bold grip glyph — ⇔ on the side edges (drag width), ⇕ on top/bottom (drag height). no box.
   toto + every agent wear this gray by default; a skinned card can tint its own (learn → blue). */
.ab-resize {
  position: absolute; z-index: 2;
  display: flex; align-items: center; justify-content: center;
  background: var(--surface-2); color: var(--text);
  font-size: var(--fs-ui); font-weight: 700; line-height: 1;
  box-shadow: inset 0 0 9px -3px rgba(255,255,255,0.18);
  transition: background .12s, box-shadow .12s;
}
.ab-resize:hover { background: var(--line); box-shadow: inset 0 0 12px -2px rgba(255,255,255,0.34); }
.agent-bar[data-edge="left"]   .ab-resize { top: 0; bottom: 0; right: 0;  width: 30px;  cursor: col-resize; }
.agent-bar[data-edge="right"]  .ab-resize { top: 0; bottom: 0; left: 0;   width: 30px;  cursor: col-resize; }
.agent-bar[data-edge="top"]    .ab-resize { left: 0; right: 0; bottom: 0; height: 30px; cursor: row-resize; }
.agent-bar[data-edge="bottom"] .ab-resize { left: 0; right: 0; top: 0;    height: 30px; cursor: row-resize; }
.agent-bar[data-edge="left"]  .ab-resize::before,
.agent-bar[data-edge="right"] .ab-resize::before { content: '⇔'; }
.agent-bar[data-edge="top"]    .ab-resize::before,
.agent-bar[data-edge="bottom"] .ab-resize::before { content: '⇕'; }

/* ── the CARD wing slider — same gray edge handle a bot wears; OFF unless the card is docked to a wing (Sum 2026-07-09) ── */
.card-resize { display: none; position: absolute; z-index: 4; align-items: center; justify-content: center;
  background: var(--surface-2); color: var(--text); font-size: var(--fs-ui); font-weight: 700; line-height: 1;
  box-shadow: inset 0 0 9px -3px rgba(255,255,255,0.18); transition: background .12s, box-shadow .12s; }
.card-resize:hover { background: var(--line); box-shadow: inset 0 0 12px -2px rgba(255,255,255,0.34); }
.zone[data-edge="left"]   .card:not(.collapsed) > .card-resize { display: flex; top: 0; bottom: 0; right: 0;  width: 30px;  cursor: col-resize; }
.zone[data-edge="right"]  .card:not(.collapsed) > .card-resize { display: flex; top: 0; bottom: 0; left: 0;   width: 30px;  cursor: col-resize; }
.zone[data-edge="top"]    .card:not(.collapsed) > .card-resize { display: flex; left: 0; right: 0; bottom: 0; height: 30px; cursor: row-resize; }
.zone[data-edge="bottom"] .card:not(.collapsed) > .card-resize { display: flex; left: 0; right: 0; top: 0;    height: 30px; cursor: row-resize; }
.zone[data-edge="left"]   .card > .card-resize::before,
.zone[data-edge="right"]  .card > .card-resize::before { content: '⇔'; }
.zone[data-edge="top"]    .card > .card-resize::before,
.zone[data-edge="bottom"] .card > .card-resize::before { content: '⇕'; }
/* inset the OPEN card's content so the 30px slider sits in a gutter, not over the editor (mirrors the bot's head/convo margins) */
.zone[data-edge="left"]   .card:not(.collapsed) { padding-right: 30px; }
.zone[data-edge="right"]  .card:not(.collapsed) { padding-left: 30px; }
.zone[data-edge="top"]    .card:not(.collapsed) { padding-bottom: 30px; }
.zone[data-edge="bottom"] .card:not(.collapsed) { padding-top: 30px; }

/* ── give the resize handle its OWN 30px gutter — so it never covers the bar's SKIN (the head
   plastic + the convo frame/bevel), only ever sits BESIDE it. margin the content (head + convo)
   off the handle edge; the absolute handle fills the gutter. collapsed rails opt out (handle hidden). ── */
.agent-bar:not(.collapsed)[data-edge="right"] .ab-head,
.agent-bar:not(.collapsed)[data-edge="right"] .convo    { margin-left:   30px; }
.agent-bar:not(.collapsed)[data-edge="left"]  .ab-head,
.agent-bar:not(.collapsed)[data-edge="left"]  .convo    { margin-right:  30px; }
.agent-bar:not(.collapsed)[data-edge="top"]    .convo   { margin-bottom: 30px; }
.agent-bar:not(.collapsed)[data-edge="bottom"] .ab-head { margin-top:    30px; }

/* ── collapsed — show only the header. two modes, by zone occupancy: ──
   SOLO in its zone → a thin RAIL floating on the edge (holds NO space).
   SHARING the zone → a thin IN-FLOW strip that stacks beside the others (no overlap). */
.agent-bar.collapsed { background: var(--surface); border: 1px solid var(--line); }
.agent-bar.collapsed .convo, .agent-bar.collapsed .ab-resize, .agent-bar.collapsed .ab-grip { display: none; }
.agent-bar.collapsed .ab-head { height: 100%; border-bottom: none; justify-content: center; }   /* whole rail is the click-to-expand target */

/* solo → a thin RAIL, IN-FLOW in its zone (lives in its own grid row/col → never
   overlaps another edge's bar, even when both are collapsed). holds a slim 46px. */
.agent-bar.collapsed[data-solo="1"][data-edge="left"],
.agent-bar.collapsed[data-solo="1"][data-edge="right"]  { width: 46px;  flex: 1 1 auto; }
.agent-bar.collapsed[data-solo="1"][data-edge="top"],
.agent-bar.collapsed[data-solo="1"][data-edge="bottom"] { height: 46px; flex: 1 1 auto; }

/* sharing → in-flow, thin in the stacking direction, stacks with the rest */
.agent-bar.collapsed[data-solo="0"] { flex: 0 0 46px; }

/* the name shows only on a WIDE strip (top/bottom solo · left/right sharing) */
.agent-bar.collapsed .ab-name { display: none; }
.agent-bar.collapsed[data-edge="top"][data-solo="1"]    .ab-head,
.agent-bar.collapsed[data-edge="bottom"][data-solo="1"] .ab-head,
.agent-bar.collapsed[data-edge="left"][data-solo="0"]   .ab-head,
.agent-bar.collapsed[data-edge="right"][data-solo="0"]  .ab-head { justify-content: flex-start; }
/* (Sum 2026-07-10) collapsed bots are ICON-ONLY too — a collapsed rail no longer re-shows its name (any bot,
   any wing). the avatar carries the identity; the name is a hover tooltip. keeps rails clean + consistent. */

/* ── the main column — cards mount here; the brand shows when empty ── */
#main-col {
  grid-area: m; min-width: 0; min-height: 0;
  display: flex; flex-direction: column; position: relative; overflow: hidden;
}
#card-stack { flex: 1; min-height: 0; display: flex; flex-direction: column; overflow-y: auto; }
.brand-wrap { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; user-select: none; padding: var(--space-lg); }

/* ── a card — header + body + bottom resize bar (makeCard). MIGRATION-SAFE via :has():
   not-yet-converted cards (no .card-body) keep flex:1 fill — untouched. makeCard cards
   (have .card-body) HOLD their height + push down; the desk (#card-stack) scrolls to find
   them. NO max-height — a card grows as tall as you drag it (the resize clamps the body at
   1200px) and the desk scrolls for days. an 85vh cap used to let the body outgrow the card,
   shoving the resize bar off-screen → card stuck maximized but too short to use. ── */
.card { display: flex; flex-direction: column; margin: var(--space-xs); background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-md); overflow: hidden; }
.card:not(:has(.card-body)) { flex: 1; min-height: 200px; }
/* NEW CARD MODEL (Sum, 2026-07-02): fullscreen OR a bar, one card at a time. no bottom slider, no half-sizes.
   an open card FILLS the desk; collapsed → a bar (just the head). (was: flex:0 0 auto + a dragged 320px body.) */
.card:has(.card-body)       { flex: 1 1 auto; min-height: 260px; }   /* the OPEN card fills the desk (min 260 so it stays usable under a stack of bars) */
.card.collapsed             { flex: 0 0 auto; min-height: 0; }        /* collapsed = a BAR (just the head) */

/* SOLO card — a one-card standalone (Jem · Wellness · Nurse): the card IS the desk. it fills the space, drops
   the window chrome + the ⇕ resize slider; the bot floats over it. (multi-card suites keep the slider.) */
html[data-solo] #card-stack           { overflow: hidden; }
html[data-solo] .card:has(.card-body) { flex: 1 1 auto; min-height: 0; margin: 0; border: 0; border-radius: 0; }
html[data-solo] .card > .card-body    { flex: 1 1 auto; height: auto; }
html[data-solo] .card-bottom          { display: none; }

.card-head { flex-shrink: 0; height: var(--bar-h); display: flex; align-items: center; gap: 10px; padding: 0 14px; background: var(--surface-2); border-bottom: 1px solid var(--line); cursor: pointer; }
/* arrow removed (Sum: superfluous — the whole head IS the collapse handle); click head → collapse still works */
.card.collapsed > *:not(.card-head) { display: none; }
.card.collapsed .card-head { border-bottom: none; }

/* The Port Side View — the card's TOP BAR is the skin (Sum 2026-07-22): the FUN skins wear the dawn
   ocean + island (like tiptap's shelf header); the BORING skins get a solid dusk color. */
[data-card="scroll"] .card-head { background: #b1a0c4; }
:root[data-skin="oz"] [data-card="scroll"] .card-head,
:root[data-skin="unicorn"] [data-card="scroll"] .card-head {
  background-image: linear-gradient(rgba(0,0,0,.22), rgba(0,0,0,.03)), url('../cards/scroll/psv-topbar.png');
  background-size: cover, cover; background-position: center, center bottom; background-repeat: no-repeat, no-repeat;
}
/* boring skins (clean tier) — a NEWSPAPER HEADLINE BORDER (Sum 2026-07-22): black masthead rules top +
   bottom on the dusk ground. the ground stays dusk (not cream) because the PSV icon is a WHITE ship cutout
   that would vanish on paper; the rules give the "newspaper" read without losing the ship. (the drawn dusk
   ship psv-topbar-boring.svg is kept in cards/scroll/ if we ever want the illustration back.) */
[data-tier="clean"] [data-card="scroll"] .card-head { background: #6b5f8c !important; border-top: 2px solid #14110c; border-bottom: 4px double #14110c; }
[data-card="scroll"] .card-title { display: none !important; }

/* ── THE NEWSIE'S TOP BAR (the agent-bar head) — same idiom as the PSV card-head (Sum 2026-07-22):
   FUN skins wear the 1899 street (full-width cover, the source art is ~1024×250); BORING skins get the
   true newspaper masthead — cream paper + black headline rules (the CAP icon reads on cream, unlike the
   ship). --head-ink recolors the .ab-name so it stays legible on each ground. */
.agent-bar[data-agent="newsie"] .ab-head { background: #2f2942; --head-ink: #f4efe6; }
:root[data-skin="oz"] .agent-bar[data-agent="newsie"] .ab-head,
:root[data-skin="unicorn"] .agent-bar[data-agent="newsie"] .ab-head {
  background-image: linear-gradient(rgba(20,14,30,.34), rgba(20,14,30,.06)), url('../agents/newsie/newsie-topbar.png');
  background-size: cover, cover; background-position: center, center; background-repeat: no-repeat, no-repeat;
}
:root[data-skin="clinical"] .agent-bar[data-agent="newsie"] .ab-head,
:root[data-skin="corporate"] .agent-bar[data-agent="newsie"] .ab-head,
[data-tier="clean"] .agent-bar[data-agent="newsie"] .ab-head {
  background: #f6f3ec !important; --head-ink: #14110c;
  border-top: 2px solid #14110c; border-bottom: 4px double #14110c;
}

/* ── THE GAMER'S TOP BAR — the dead-mall arcade on fun skins; a plain dark cabinet face on boring
   (the joystick icon reads on dark, so no cream masthead here). same idiom as the newsie. (Sum 2026-07-22) */
.agent-bar[data-agent="gamer"] .ab-head { background: #17161f; --head-ink: #e9e6f2; }
:root[data-skin="oz"] .agent-bar[data-agent="gamer"] .ab-head,
:root[data-skin="unicorn"] .agent-bar[data-agent="gamer"] .ab-head {
  background-image: linear-gradient(rgba(10,8,16,.42), rgba(10,8,16,.12)), url('../agents/gamer/gamer-topbar.png');
  background-size: cover, cover; background-position: center, center; background-repeat: no-repeat, no-repeat;
}
:root[data-skin="clinical"] .agent-bar[data-agent="gamer"] .ab-head,
:root[data-skin="corporate"] .agent-bar[data-agent="gamer"] .ab-head,
[data-tier="clean"] .agent-bar[data-agent="gamer"] .ab-head {
  background: #23222c !important; --head-ink: #e9e6f2; border-bottom: 3px solid #3a3a48;
}

/* drag-to-reorder — the ⠿ grip on every card head (card-reorder.js); the card lifts while held */
.card-grip { flex-shrink: 0; color: var(--text-dim); font-size: 16px; line-height: 1; cursor: grab; user-select: none; }
.card-grip:hover { color: var(--text); }
.card-grip:active { cursor: grabbing; }
.card-dragging { position: relative; z-index: 5; box-shadow: 0 14px 34px rgba(0,0,0,0.55); }

/* the SUBBAR — interior top-bar rows (the deliberate multi-row break, see cards.js). a column of
   rows; collapses with the card (the .collapsed rule above hides everything but .card-head), so a
   collapse leaves the main bar + its jewels + STOP. each card skins its own subbar rows. */
.card-subbar { flex-shrink: 0; display: flex; flex-direction: column; }

/* head pieces — icon · title · your controls (grow) · jewels · STOP · ▾ */
.card-icon { font-size: 34px; line-height: 1; flex-shrink: 0; min-width: var(--head-icon); text-align: center; }   /* emoji icon — blown up + centered in the shared slot */
.card-icon-img { width: var(--head-icon-h); height: var(--head-icon-h); min-width: var(--head-icon); object-fit: contain; flex-shrink: 0; }
/* ICON-ONLY (2026-07-09): the card's name is hidden — the icon is the identity, the name is the
   card-head's title="…" tooltip. the span stays in the DOM (card-reorder reads its text · skins hook it). */
.card-title { display: none; font-size: var(--fs-label); font-weight: 700; color: var(--text); flex-shrink: 0; }
.card-ctrls { flex: 1; min-width: 0; display: flex; align-items: center; gap: 6px; }
.card-jewels { display: flex; gap: 4px; flex-shrink: 0; }
.cj { width: 10px; height: 10px; border-radius: 50%; background: var(--cj-off); box-shadow: inset 0 1px 2px rgba(0,0,0,0.6); }
.cj-g.lit { background: var(--cj-green); box-shadow: 0 0 7px rgba(46,204,64,0.85); }
.cj-a.lit { background: var(--cj-amber); box-shadow: 0 0 7px rgba(255,176,0,0.85); }
.cj-r.lit { background: var(--cj-red); box-shadow: 0 0 7px rgba(255,65,54,0.85); }
/* (Sum 2026-07-10) the wait-glow BREATHES — slow, gentle, longing. fade up soft to bright, ease back down to
   near-dark, ~2.6s each way. a lover's "come here," not a blink, not an alarm, not a zit you need to pop. */
.cj.blink { animation: cj-breathe 2.6s ease-in-out infinite; }
@keyframes cj-breathe { 0%,100% { opacity: .45; filter: brightness(.7); } 50% { opacity: 1; filter: brightness(1.15); } }

/* the big fat red STOP — unmissable; dark + idle until a run is live (setJewel enables it) */
.card-stop { flex-shrink: 0; min-width: var(--run-btn-w); height: var(--run-btn-h); padding: 0 var(--run-btn-pad); border: none; border-radius: 6px; background: var(--inj-red); color: #fff; font-size: var(--run-btn-fs); font-weight: 800; cursor: pointer; box-shadow: 0 3px 0 var(--stop-shadow); }
.card-stop:hover:not(:disabled) { background: var(--stop-hover); }
.card-stop:active:not(:disabled) { transform: translateY(2px); box-shadow: none; }
.card-stop:disabled { background: var(--stop-dis-bg); color: var(--stop-dis-text); cursor: default; box-shadow: 0 3px 0 var(--stop-dis-shadow); }

/* the body — your content; FILLS the card (fullscreen model). */
.card-body { flex: 1 1 auto; min-height: 0; position: relative; overflow: hidden; display: flex; flex-direction: column; }
/* an <iframe> card (ba3n2 · webview) carries a 300px intrinsic size, so in the flex body it renders narrow
   and short, leaving a white gap. card-body is position:relative → pin any direct iframe edge-to-edge. */
.card-body > iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }

/* the bottom resize slider is GONE — cards are fullscreen or a bar, no dragging to half-sizes. */
.card-bottom { display: none; }
.card-bottom--dead { flex-shrink: 0; height: 30px; cursor: ns-resize; background: var(--surface-2); border-top: 1px solid var(--line); box-shadow: inset 0 3px 9px -3px rgba(255,255,255,0.16); background-size: cover; background-position: center; display: flex; align-items: center; padding-left: 10px; }
.card-bottom::before { content: '⇕'; font-size: 15px; font-weight: 700; color: var(--text); }   /* bold contrast, left-aligned (design-rules) */
.card-bottom:hover { box-shadow: inset 0 3px 12px -2px rgba(255,255,255,0.3); }
/* while resizing a card, iframes (woodshop · preview) must not eat the drag — else mouseup never lands and it sticks */
body.resizing iframe { pointer-events: none; }
body.resizing { cursor: ns-resize; }
.card-bottom:hover::before { color: var(--accent); }

/* shared convo + the soon stub */
.card .convo { flex: 1; min-height: 0; }
.card .convo .msg-bot { color: var(--text); }
.card .convo .msg-u   { color: var(--accent); }
.card .convo .msg-stream:empty::before { color: var(--text-dim); }
.card-soon { flex: 1; display: grid; place-items: center; color: var(--text-dim); font-size: var(--fs-label); letter-spacing: 1px; }

/* the tag — one clean dog-collar tag (the empty-desk brand). */
.tag { width: min(42vw, 340px); height: auto; display: block; margin: 0 auto; }
.tag-edge circle, .tag-edge rect { fill: var(--accent); }
.tag-face circle, .tag-face rect { fill: var(--surface-2); }
.tag-hole { fill: var(--bg); }
.tag-text { fill: var(--text); font-family: var(--font); font-weight: 800; letter-spacing: 8px; font-size: 40px; }
.brand-sub { margin-top: var(--space-lg); font-size: var(--fs-label); color: var(--text-dim); letter-spacing: 1px; }

/* ── dock hint — the edge the bar will snap to while you drag it ── */
#desk.docking { cursor: grabbing; }
#desk::after { content: ''; position: absolute; background: var(--accent); opacity: 0; pointer-events: none; transition: opacity .1s; z-index: 40; }
#desk.docking[data-dock-hint="left"]::after   { left: 0;  top: 0; bottom: 0; width: 6px;  opacity: .9; }
#desk.docking[data-dock-hint="right"]::after  { right: 0; top: 0; bottom: 0; width: 6px;  opacity: .9; }
#desk.docking[data-dock-hint="top"]::after    { left: 0; right: 0; top: 0;    height: 6px; opacity: .9; }
#desk.docking[data-dock-hint="bottom"]::after { left: 0; right: 0; bottom: 0; height: 6px; opacity: .9; }
/* (Sum 2026-07-09) drag a bar to the CENTER → back to the DESK. the drop zone LIGHTS UP (🦴) even when cards cover
   the middle — it's an overlay on top, so you always know you found it. */
#desk.docking[data-dock-hint="center"]::before {
  content: '🦴  back to desk'; position: absolute; left: 26%; right: 26%; top: 32%; bottom: 32%;
  display: flex; align-items: center; justify-content: center; gap: 10px;
  border: 3px dashed var(--accent); border-radius: 22px; background: rgba(255,255,255,0.06);
  color: var(--text); font: 800 16px/1 var(--font); letter-spacing: 2px; text-transform: uppercase;
  box-shadow: 0 0 0 100vmax rgba(0,0,0,0.28) inset, 0 8px 40px rgba(0,0,0,0.45);
  z-index: 60; pointer-events: none;
}

/* no close button on a card — close it from the DECK (tap a lit tile), its launcher toggle, or the HOOP. */
/* the hoop — bottom-center; appears while you drag a card; toss the card in to close it (swish). */
/* The hoop must clear the 46px bottom DOCK strip (nearestEdge) — overlapping them meant dragging a
   panel home past bottom-centre either closed it or re-docked it. (Sum 2026-07-21) */
.card-hoop { position: fixed; left: 50%; bottom: 68px; width: 72px; height: 60px; color: var(--accent); opacity: 0; pointer-events: none; transform: translateX(-50%) translateY(22px) scale(.9); transition: opacity .15s, transform .15s; z-index: 60; }
.card-hoop.show { opacity: .82; transform: translateX(-50%) translateY(0) scale(1); }
.card-hoop.hot  { opacity: 1; transform: translateX(-50%) translateY(0) scale(1.14); }
.card-hoop.hot svg { filter: drop-shadow(0 0 9px var(--accent)); }
.card-hoop svg { width: 100%; height: 100%; display: block; }
.card-toss { opacity: .66; }   /* the card you're aiming into the hoop */
/* the undo toast after a hoop shot — "are you sure", the non-blocking way (nothing's lost, it's local). */
.card-toss-toast { position: fixed; left: 50%; bottom: 86px; transform: translateX(-50%) translateY(8px); z-index: 70; display: flex; align-items: center; gap: 12px; background: rgba(20,18,30,.96); border: 1px solid rgba(179,136,255,.4); border-radius: 10px; padding: 9px 14px; color: #e8e3ff; font: var(--fs-ui) system-ui; box-shadow: 0 8px 28px rgba(0,0,0,.5); opacity: 0; transition: opacity .18s, transform .18s; }
.card-toss-toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }
.card-toss-toast b { color: #c9b3ff; }
.ctt-undo { background: rgba(124,77,255,.26); border: 1px solid rgba(179,136,255,.5); color: #e8e3ff; border-radius: 7px; padding: 5px 12px; font: 600 var(--fs-ui) system-ui; cursor: pointer; }
.ctt-undo:hover { background: rgba(124,77,255,.42); }

/* 🧠 ESP toggle on every bar — dim when off, purple-lit when reading the mind */
.ab-esp { background: transparent; border: none; color: var(--text-dim); cursor: pointer; padding: 3px 5px; border-radius: 6px; display: inline-flex; align-items: center; flex-shrink: 0; }
.ab-esp:hover { color: var(--text); background: var(--surface-2); }
.ab-esp.on { color: #b388ff; }
.ab-esp.on svg { filter: drop-shadow(0 0 4px rgba(179,136,255,.7)); }

/* ── WING AWARENESS (Sum 2026-07-11) — a card docked to a SIDE wing becomes a vertical rail. its horizontal
   banner art reads broken sideways, so the learn-suite cards (writer · learn) drop the banner on a vertical rail
   and keep a clean head + upright icon. (Monaco keeps its own pre-rotated -vert banner.) A proper rotated banner
   per card is the polish; this stops the broken look. ── */
.p-richtext[data-orient="vertical"] .card-head,
.card-learn[data-orient="vertical"] .card-head,
.p-learn[data-orient="vertical"] .card-head { background-image: none !important; background: var(--surface-2); }

/* ♿ JEWELS ARE SHAPES, not just colors (Sum 2026-07-14 — WCAG 1.4.1, colorblind-safe):
   green = ● circle (runs) · amber = ▲ triangle (edited/waiting) · red = ■ square (stopped/crashed).
   the shape survives any color blindness; the glow stays for everyone else. */
.cj { width: 11px; height: 11px; }
.cj-a { border-radius: 2px; clip-path: polygon(50% 0, 100% 100%, 0 100%); }
.cj-r { border-radius: 2px; }
/* the STOP button squares up to match — red plastic mass (physical-object law), same family as the green dome */
.card-stop { border-radius: 5px; background: #b3262a; color: #fff; box-shadow: 0 3px 0 #6e1416; }
.card-stop:active { transform: translateY(2px); box-shadow: 0 1px 0 #6e1416; }

/* the STOP button is now the REAL red plastic square (orange-gen two-step, 2026-07-14) — the toy-mass
   twin of the green dome. png face replaces the css square + the octagon glyph; still square = shape-safe. */
.card-stop { background: transparent url('stop.png') center / contain no-repeat; border-radius: 0; box-shadow: none; }
.card-stop .stop-glyph { display: none; }
.card-stop:active { transform: translateY(2px); }
