/* ================================================================
   SPACEODD - canvas.css
   The Canvas view (public/canvas.js, docs/canvas-view.md).
   Every rule here is scoped under .ws-canvas, .cv- or #cv-, so nothing in Standard is
   restyled (test:canvas-view holds that). Colours, radii, type and motion all come from the
   workspace's own tokens (style.css, THE CANVAS LOOK): there is no second palette.
   ================================================================ */

/* Amber words: the status amber mixed toward the ink so small type reads at 4.5:1 or better on its
   tint in light mode (5.1:1); in dark mode the mix only lightens an amber that already reads at 7:1. */
.cv-wall, #cv-head, .cv-shell-toggle, .cv-palette { --cv-warn-ink: color-mix(in srgb, var(--status-warn) 65%, var(--text1)); }

/* ── The view toggle: top right, just before the profile picture, sized to it ─────────────
   The header is bare (no pills) and the avatar is 28px: a 30px segmented control, a quiet track
   with the chosen view raised on it. */
#workspace-view .cv-shell-toggle {
  flex: none; display: inline-flex; align-items: center; gap: 0; align-self: center;
  height: 30px; padding: 2px; box-sizing: border-box; margin-right: 4px;
  border-radius: 999px; background: color-mix(in srgb, var(--panel-3) 70%, transparent);
}
#workspace-view .cv-shell-toggle[hidden] { display: none; }
#workspace-view .cv-shell-toggle > button {
  display: inline-flex; align-items: center; gap: 6px; height: 26px; padding: 0 11px; border: 0; border-radius: 999px;
  background: transparent; color: var(--text2); cursor: pointer;
  font: 500 12px/1 var(--font-body); letter-spacing: -0.005em;
  transition: background-color 150ms ease, color 150ms ease, box-shadow 150ms ease;
}
#workspace-view .cv-shell-toggle > button:hover { color: var(--text1); }
#workspace-view .cv-shell-toggle > button.is-on { background: var(--panel); color: var(--text1); font-weight: 600; box-shadow: 0 1px 2px rgba(50, 46, 40, .12), inset 0 0 0 1px color-mix(in srgb, var(--chrome-line) 40%, transparent); }
[data-theme="dark"] #workspace-view .cv-shell-toggle > button.is-on { box-shadow: 0 1px 2px rgba(0, 0, 0, .4), inset 0 0 0 1px color-mix(in srgb, var(--chrome-line) 60%, transparent); }
#workspace-view .cv-shell-toggle > button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ── The breadcrumb in Canvas: Projects / project / Canvas (/ card) ─────────────────────── */
#workspace-view:not(.ws-canvas) .cv-bc-sep, #workspace-view:not(.ws-canvas) .cv-bc-canvas { display: none; }
#workspace-view.ws-canvas .workspace-header-left .cv-bc-sep { display: inline; flex: none; }
#workspace-view.ws-canvas .workspace-header-left .ws-bc-current { font-weight: 400; color: var(--text2); }
#workspace-view.ws-canvas .cv-bc-canvas {
  flex: none; padding: 2px 6px; margin: 0 -6px; border: 0; border-radius: 6px; background: transparent;
  font: inherit; color: var(--text2); cursor: pointer; white-space: nowrap;
}
#workspace-view.ws-canvas .cv-bc-canvas:hover:not(:disabled) { color: var(--text1); background: color-mix(in srgb, var(--panel-3) 60%, transparent); }
#workspace-view.ws-canvas .cv-bc-canvas:disabled { cursor: default; font-weight: 600; color: var(--text1); }
#workspace-view.ws-canvas .cv-bc-canvas:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ── Standard never carries the Canvas elements: outside the view they take no room at all ─ */
#workspace-view:not(.ws-canvas) #cv-wall,
#workspace-view:not(.ws-canvas) #cv-head,
#workspace-view:not(.ws-canvas) #cv-grip { display: none !important; }

/* ── The frame: wall on the left, the open module on the right ─────────────────────────── */
/* Three columns of the Standard body become a grid: the wall, then a head row over the module.
   The module stays #workspace-content, so every module renders where it always has. */
#workspace-view.ws-canvas .workspace-body {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 0;
  grid-template-rows: auto minmax(0, 1fr);
  column-gap: var(--gap); row-gap: var(--gap);
}
#workspace-view.ws-canvas[data-cv-mode="panel"] .workspace-body { grid-template-columns: minmax(0, 1fr) clamp(480px, var(--cv-panel-w, 50%), 70%); }
/* The glass header floats over the body (style.css, GLASS); each column clears it by 80px. Here the
   body itself starts under it, the wall is a clean sheet (no fade over its top), and the module column
   starts under its own head bar, so its 80px of Standard padding becomes a hair. */
#workspace-view.ws-canvas .workspace-body { padding-top: 80px; }
#workspace-view.ws-canvas::before { display: none; }
#app-screen #workspace-view.ws-canvas .workspace-main,
#workspace-view.ws-canvas .workspace-main:has(.pst-studio) { padding-top: 6px; scroll-padding-top: 70px; }
#workspace-view.ws-canvas #workspace-content.wc-script-mode { padding-top: 6px; }
#workspace-view.ws-canvas[data-cv-mode="focus"] .workspace-body { grid-template-columns: 0 minmax(0, 1fr); column-gap: 0; }
#workspace-view.ws-canvas #left-sidebar,
/* The Guide and its lightbulb are Classic's: the wall has its own tips and its first-visit hint. */
#workspace-view.ws-canvas #guide-rail,
#workspace-view.ws-canvas #guide-bulb,
#workspace-view.ws-canvas .ws-drawer-backdrop { display: none !important; }
#workspace-view.ws-canvas #cv-wall { grid-column: 1; grid-row: 1 / 3; }
#workspace-view.ws-canvas #cv-head { grid-column: 2; grid-row: 1; }
#workspace-view.ws-canvas #workspace-content { grid-column: 2; grid-row: 2; min-height: 0; min-width: 0; }
#workspace-view.ws-canvas[data-cv-open="none"] #cv-head,
#workspace-view.ws-canvas[data-cv-open="none"] #workspace-content,
#workspace-view.ws-canvas[data-cv-open="none"] #cv-grip { display: none; }
#workspace-view.ws-canvas[data-cv-mode="focus"] #cv-wall,
#workspace-view.ws-canvas[data-cv-mode="focus"] #cv-grip { display: none; }
/* The shot panel (a 380px column on the right, style.css) pushes the head aside as it pushes the content. */
@media (min-width: 901px) {
  #workspace-view.ws-canvas:has(#workspace-content.panel-open) #cv-head { margin-right: 380px; }
}
/* The module slides in; the wall never moves. */
#workspace-view.ws-canvas #cv-head,
#workspace-view.ws-canvas #workspace-content { animation: cv-in var(--rp-dur, 300ms) var(--rp-ease, ease) both; }
@keyframes cv-in { from { opacity: 0; transform: translateX(16px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) {
  #workspace-view.ws-canvas #cv-head, #workspace-view.ws-canvas #workspace-content { animation: none; }
}

/* ── Motion: a card grows into its panel and shrinks back into its card (canvas.js _cvMorph) ──
   View Transitions: pictures of the page before and after the switch, moved on the compositor.
   html.cv-vt is on only while one runs; cv-morph is the open module (the card on the wall takes
   the name on the side where the module is not shown). The rest of the page holds still. */
#workspace-view.ws-canvas.cv-morphed #cv-head,
#workspace-view.ws-canvas.cv-morphed #workspace-content { animation: none; }
html.cv-vt { --cv-vt-ease: cubic-bezier(0.32, 0.72, 0, 1); --cv-vt-dur: 460ms; }
html.cv-vt--close { --cv-vt-dur: 400ms; }
html.cv-vt--mode { --cv-vt-dur: 420ms; }
html.cv-vt--swap, html.cv-vt--fade { --cv-vt-dur: 280ms; }
html.cv-vt #workspace-view.ws-canvas #workspace-content { view-transition-name: cv-morph; }
html.cv-vt #workspace-view.ws-canvas #cv-head { view-transition-name: cv-head; }
html.cv-vt #workspace-view.ws-canvas #cv-wall { view-transition-name: cv-wall; }
html.cv-vt #workspace-view.ws-canvas #cv-toolbar { view-transition-name: cv-toolbar; }
html.cv-vt #workspace-view.ws-canvas #cv-map { view-transition-name: cv-map; }
html.cv-vt #workspace-view.ws-canvas > .ws-sample-banner { view-transition-name: cv-pill-sample; }
html.cv-vt #workspace-view.ws-canvas > .generation-banner { view-transition-name: cv-pill-gen; }
html.cv-vt #workspace-view.ws-canvas > .ws-view-only-banner { view-transition-name: cv-pill-share; }
html.cv-vt::view-transition-old(root) { animation: none; opacity: 0; }
html.cv-vt::view-transition-new(root) { animation: none; }
html.cv-vt::view-transition-group(*) { animation-duration: var(--cv-vt-dur); animation-timing-function: var(--cv-vt-ease); }
html.cv-vt::view-transition-group(cv-wall) { animation: none; z-index: 0; }
html.cv-vt::view-transition-group(cv-morph) { z-index: 5; }
html.cv-vt::view-transition-group(cv-head) { z-index: 5; }
html.cv-vt::view-transition-group(cv-toolbar), html.cv-vt::view-transition-group(cv-map),
html.cv-vt::view-transition-group(cv-pill-sample), html.cv-vt::view-transition-group(cv-pill-gen), html.cv-vt::view-transition-group(cv-pill-share) { z-index: 6; }

/* The box is painted (the module has no ground of its own) and clips its pictures, which keep
   their size, held at the top left: the card's face rides the corner as the box opens around it. */
html.cv-vt::view-transition-image-pair(cv-morph) { overflow: hidden; border-radius: 16px; background: var(--cv-vt-page, var(--bg)); }
html.cv-vt::view-transition-old(cv-morph), html.cv-vt::view-transition-new(cv-morph) { width: 100%; height: 100%; object-fit: none; object-position: left top; }
/* Open: the card's face goes as it grows; the module comes up through it. */
html.cv-vt--open::view-transition-image-pair(cv-morph) { animation: cv-vt-round-open var(--cv-vt-dur) var(--cv-vt-ease) both; }
/* The module itself scales with the box (a small panel, not its empty corner); the card's face
   keeps its size. */
html.cv-vt--open::view-transition-new(cv-morph), html.cv-vt--close::view-transition-old(cv-morph) { object-fit: cover; }
html.cv-vt--open::view-transition-old(cv-morph) { animation: cv-vt-out calc(var(--cv-vt-dur) * 0.42) ease-out both; }
html.cv-vt--open::view-transition-new(cv-morph) { animation: cv-vt-in calc(var(--cv-vt-dur) * 0.5) ease-out calc(var(--cv-vt-dur) * 0.12) both; }
html.cv-vt--open::view-transition-new(cv-head) { animation: cv-vt-drop calc(var(--cv-vt-dur) * 0.7) var(--cv-vt-ease) calc(var(--cv-vt-dur) * 0.25) both; }
/* Close: the module goes as it shrinks; the card's face comes back as it lands. */
html.cv-vt--close::view-transition-image-pair(cv-morph) { animation: cv-vt-round-close var(--cv-vt-dur) var(--cv-vt-ease) both; }
html.cv-vt--close::view-transition-old(cv-morph) { animation: cv-vt-out calc(var(--cv-vt-dur) * 0.55) ease-in calc(var(--cv-vt-dur) * 0.2) both; }
html.cv-vt--close::view-transition-new(cv-morph) { animation: cv-vt-in calc(var(--cv-vt-dur) * 0.45) ease-out calc(var(--cv-vt-dur) * 0.4) both; }
html.cv-vt--close::view-transition-old(cv-head), html.cv-vt--fade::view-transition-old(cv-head) { animation: cv-vt-out 140ms ease-out both; }
/* The wall: the new one is live from the first frame (it may pan to keep a card in view), and the
   old picture keeps its width under it, showing only the strip the new wall no longer covers,
   until the module has grown over it. Closing, the new wall is the wider: the old one shows nothing. */
html.cv-vt::view-transition-image-pair(cv-wall) { overflow: visible; isolation: auto; }
html.cv-vt::view-transition-old(cv-wall), html.cv-vt::view-transition-new(cv-wall) { height: 100%; object-fit: none; object-position: left top; mix-blend-mode: normal; }
html.cv-vt::view-transition-old(cv-wall) { width: var(--cv-vt-wall-w, 100%); clip-path: inset(0 0 0 var(--cv-vt-wall-new, 0px)); animation: cv-vt-late-out var(--cv-vt-dur) linear both; }
html.cv-vt::view-transition-new(cv-wall) { width: 100%; animation: none; }
/* Another card while one is open: the panel stays; its page changes, the new one rising a touch. */
html.cv-vt--swap::view-transition-old(cv-morph), html.cv-vt--swap::view-transition-old(cv-head) { animation: cv-vt-out 140ms ease-out both; }
html.cv-vt--swap::view-transition-new(cv-morph) { animation: cv-vt-rise 260ms var(--cv-vt-ease) 60ms both; }
html.cv-vt--swap::view-transition-new(cv-head) { animation: cv-vt-in 200ms ease-out 60ms both; }
/* Close with the card out of sight: the module fades, settling back a touch. */
html.cv-vt--fade::view-transition-old(cv-morph) { animation: cv-vt-sink 240ms ease-in both; }
/* Panel and full window: the module's box grows or shrinks; the wall steps out or back. */
html.cv-vt--mode::view-transition-old(cv-morph) { animation: cv-vt-out calc(var(--cv-vt-dur) * 0.3) ease-out both; }
html.cv-vt--mode::view-transition-new(cv-morph) { animation: cv-vt-in calc(var(--cv-vt-dur) * 0.45) ease-out calc(var(--cv-vt-dur) * 0.18) both; }
html.cv-vt--mode::view-transition-group(cv-head) { animation: none; }
html.cv-vt--mode::view-transition-old(cv-head) { animation: cv-vt-out 120ms ease-out both; }
html.cv-vt--mode::view-transition-new(cv-head) { animation: cv-vt-in 220ms ease-out 140ms both; }
html.cv-vt--mode::view-transition-old(cv-wall):only-child { width: 100%; clip-path: none; animation: cv-vt-leave calc(var(--cv-vt-dur) * 0.6) var(--cv-vt-ease) both; }
html.cv-vt--mode::view-transition-new(cv-wall):only-child { animation: cv-vt-return var(--cv-vt-dur) var(--cv-vt-ease) both; }
@keyframes cv-vt-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes cv-vt-out { from { opacity: 1; } to { opacity: 0; } }
@keyframes cv-vt-late-out { 0%, 72% { opacity: 1; } 100% { opacity: 0; } }
@keyframes cv-vt-drop { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: none; } }
@keyframes cv-vt-rise { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
@keyframes cv-vt-sink { from { opacity: 1; transform: none; } to { opacity: 0; transform: scale(0.985) translateY(6px); } }
@keyframes cv-vt-leave { from { opacity: 1; transform: none; } to { opacity: 0; transform: translateX(-28px); } }
@keyframes cv-vt-return { from { opacity: 0; transform: translateX(-28px); } to { opacity: 1; transform: none; } }
@keyframes cv-vt-round-open { from { border-radius: 14px; background: var(--cv-vt-card, var(--panel)); box-shadow: 0 2px 6px rgba(40, 30, 20, 0.08); } 45% { box-shadow: 0 24px 60px rgba(40, 30, 20, 0.16); } to { border-radius: 16px; background: var(--cv-vt-page, var(--bg)); box-shadow: 0 0 0 rgba(40, 30, 20, 0); } }
@keyframes cv-vt-round-close { from { border-radius: 16px; background: var(--cv-vt-page, var(--bg)); box-shadow: 0 0 0 rgba(40, 30, 20, 0); } 55% { box-shadow: 0 24px 60px rgba(40, 30, 20, 0.16); } to { border-radius: 14px; background: var(--cv-vt-card, var(--panel)); box-shadow: 0 2px 6px rgba(40, 30, 20, 0.08); } }
@media (prefers-reduced-motion: reduce) {
  html.cv-vt::view-transition-group(*), html.cv-vt::view-transition-old(*), html.cv-vt::view-transition-new(*) { animation: none !important; }
}

/* ── The grip between the wall and the panel ───────────────────────────────────────────── */
#workspace-view.ws-canvas #cv-grip {
  grid-column: 2; grid-row: 1 / 3; justify-self: start; align-self: stretch;
  position: relative; z-index: 6; width: 14px; margin-left: calc(-7px - var(--gap) / 2);
  cursor: col-resize; touch-action: none; outline: 0;
}
#workspace-view.ws-canvas #cv-grip::before {
  content: ''; position: absolute; left: 5px; top: 50%; width: 4px; height: 44px; margin-top: -22px;
  border-radius: 2px; background: var(--chrome-line); opacity: 0; transition: opacity 160ms ease;
}
#workspace-view.ws-canvas #cv-grip:hover::before, #workspace-view.ws-canvas #cv-grip:focus-visible::before, #workspace-view.ws-canvas.cv-resizing #cv-grip::before { opacity: 1; }
#workspace-view.ws-canvas #cv-grip:focus-visible::before { background: var(--accent); }
#workspace-view.ws-canvas.cv-resizing { cursor: col-resize; user-select: none; }
#workspace-view.ws-canvas.cv-resizing .cv-world { transition: none; }
#workspace-view.ws-canvas.cv-resizing #workspace-content { pointer-events: none; }

/* ── The head over the module: where you are, expand, close ────────────────────────────── */
#cv-head {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  height: 52px; padding: 0 10px 0 12px; box-sizing: border-box;
  border-radius: 999px; background: var(--panel);
}
#cv-head .cv-head-left, #cv-head .cv-head-right { display: flex; align-items: center; gap: 8px; min-width: 0; }
#cv-head .cv-head-icon { flex: none; display: grid; place-items: center; width: 28px; height: 28px; border-radius: 8px; background: var(--panel-2); color: var(--text2); }
#cv-head .cv-head-icon .nav-ico { width: 15px; height: 15px; display: block; }
#cv-head .cv-head-name { margin: 0; min-width: 0; font: 600 15px/1.2 var(--font-heading); letter-spacing: -0.01em; color: var(--text1); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
#cv-head .cv-back { gap: 6px; }
#cv-head .cv-minimap {
  position: relative; width: 72px; height: 52px; padding: 0; margin-right: 6px; border: 0; border-radius: 8px;
  background: var(--panel-2); cursor: pointer; overflow: hidden;
}
#cv-head .cv-minimap:hover { background: var(--panel-3); }
#cv-head .cv-minimap:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
#cv-head .cv-minimap i { position: absolute; border-radius: 1.5px; background: color-mix(in srgb, var(--text3) 55%, transparent); }
#cv-head .cv-minimap i.is-open { background: var(--status-info); }

/* ── The wall ──────────────────────────────────────────────────────────────────────────── */
.cv-wall {
  position: relative; display: flex; flex-direction: column; min-width: 0; min-height: 0;
  border-radius: var(--panel-radius); overflow: hidden;
  background-color: var(--canvas);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--chrome-line) 32%, transparent);
}
/* The ground is a shade warmer than the app's canvas (owner, 2026-11-08): paper rather than grey.
   Set on the wall only, so Classic keeps its colour; the spiral holes and focus rings inside the
   wall read --canvas and follow it. */
#workspace-view.ws-canvas .cv-wall { --canvas: #f2ede4; }
[data-theme="dark"] #workspace-view.ws-canvas .cv-wall { --canvas: #16130f; }
/* The grid (canvas.js _cvGridSync): faint squares, a plus at every fourth crossing, under the world. */
.cv-grid { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
/* Faded (owner 2026-11-08: the plusses looked busy zoomed out): lighter at rest, lighter still at
   the middle and far zooms, where the plusses crowd together and the cards are small. */
.cv-wall { --cv-grid-line: 24%; --cv-grid-mark: 48%; }
.cv-wall[data-cv-zoom="mid"] { --cv-grid-line: 20%; --cv-grid-mark: 32%; }
.cv-wall[data-cv-zoom="far"] { --cv-grid-line: 16%; --cv-grid-mark: 20%; }
[data-theme="dark"] .cv-wall { --cv-grid-line: 32%; --cv-grid-mark: 60%; }
[data-theme="dark"] .cv-wall[data-cv-zoom="mid"] { --cv-grid-line: 26%; --cv-grid-mark: 40%; }
[data-theme="dark"] .cv-wall[data-cv-zoom="far"] { --cv-grid-line: 20%; --cv-grid-mark: 26%; }
.cv-grid-line { fill: none; stroke: color-mix(in srgb, var(--chrome-line) var(--cv-grid-line, 24%), transparent); stroke-width: 1; shape-rendering: crispEdges; }
.cv-grid-mark { fill: none; stroke: color-mix(in srgb, var(--chrome-line) var(--cv-grid-mark, 48%), transparent); stroke-width: 1.2; stroke-linecap: round; }
/* The viewport pans and zooms one world by a transform written inside a frame (canvas.js). The
   ground is a hand; a card is a pointer; the space bar makes everything a hand. No transition
   while the pointer or the wheel drives it: only a glide the code asks for (Fit all, auto-pan). */
.cv-viewport { flex: 1; position: relative; overflow: hidden; cursor: grab; touch-action: none; overscroll-behavior: contain; }
.cv-wall.is-panning .cv-viewport, .cv-wall.is-panning .cv-viewport * { cursor: grabbing !important; }
.cv-wall.cv-space .cv-viewport, .cv-wall.cv-space .cv-viewport * { cursor: grab !important; }
.cv-world { position: absolute; left: 0; top: 0; transform-origin: 0 0; will-change: transform; }
@media (prefers-reduced-motion: no-preference) { .cv-world.cv-animate { transition: transform 340ms var(--rp-ease, ease); } }
.cv-lines { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; pointer-events: none; }
.cv-wall {
  /* The flow lines are green: a live connection. Amber is kept for out of date, so a line that
     turns amber says something it carries has changed. */
  --cv-line: color-mix(in srgb, var(--status-ok) 62%, var(--text3));
  --cv-line-hot: var(--status-ok);
}
.cv-line {
  fill: none; stroke: color-mix(in srgb, var(--cv-line) 70%, transparent); stroke-width: 1.6;
  stroke-linecap: round; stroke-linejoin: round; vector-effect: non-scaling-stroke;
  transition: stroke 180ms ease, stroke-width 180ms ease, opacity 180ms ease;
}
/* Out of date: amber, flowing from the source toward the card it changes. */
.cv-line.is-stale { stroke: var(--status-warn); stroke-width: 2; stroke-dasharray: 7 6; animation: cv-flow 1.1s linear infinite; }
@keyframes cv-flow { to { stroke-dashoffset: -26; } }
.cv-line.is-ghost { stroke: color-mix(in srgb, var(--text3) 45%, transparent); stroke-dasharray: 2 6; opacity: .75; }
/* The arrows along a line: small chevrons in the line's colour. */
.cv-arrow { fill: none; stroke: var(--cv-line); stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; vector-effect: non-scaling-stroke; transition: stroke 180ms ease, opacity 180ms ease; }
.cv-arrow.is-stale { stroke: var(--status-warn); }
.cv-arrow.is-ghost { display: none; }
/* A hovered card: its lines and arrows stronger, the rest step back. */
.cv-wall.has-hot .cv-line, .cv-wall.has-hot .cv-arrow { opacity: .4; }
.cv-wall.has-hot .cv-line.is-hot { opacity: 1; stroke: var(--cv-line-hot); stroke-width: 2; }
.cv-wall.has-hot .cv-arrow.is-hot { opacity: 1; stroke: var(--cv-line-hot); stroke-width: 2; }
.cv-wall.has-hot .cv-line.is-hot.is-stale, .cv-wall.has-hot .cv-arrow.is-hot.is-stale { stroke: var(--status-warn); }
.cv-port { fill: var(--panel); stroke: var(--cv-line); stroke-width: 1.5; vector-effect: non-scaling-stroke; transition: fill 180ms ease, stroke 180ms ease; }
.cv-port--a { fill: var(--cv-line); }
.cv-port.is-stale { stroke: var(--status-warn); }
.cv-port--a.is-stale { fill: var(--status-warn); }
.cv-wall.has-hot .cv-port.is-hot { stroke: var(--cv-line-hot); }
.cv-wall.has-hot .cv-port--a.is-hot { fill: var(--cv-line-hot); }
@media (prefers-reduced-motion: reduce) { .cv-line.is-stale { animation: none; } }
.cv-zones, .cv-cards { position: absolute; inset: 0; }
.cv-zone {
  position: absolute; box-sizing: border-box; border-radius: 20px;
  border: 1.5px dashed color-mix(in srgb, var(--chrome-line) 85%, transparent);
}
.cv-zone--script { border-style: solid; border-color: transparent; }
.cv-zone-label {
  position: absolute; left: 16px; top: 11px; display: inline-flex; align-items: center; gap: 7px;
  font: 500 12px/1 var(--font-mono); color: var(--text3); letter-spacing: 0.01em;
}
.cv-zone-label kbd {
  font: 500 10px/1 var(--font-mono); padding: 3px 6px; border-radius: 6px;
  background: var(--panel-2); color: var(--text3); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--chrome-line) 50%, transparent);
}
/* The foot: − 84% + and Fit all, in the wall's bottom-left corner; a map bottom-right when part
   of the wall is out of view. */
/* The toolbar: one floating bar at the foot of the wall, Figma's way. Square 32px buttons with a
   20px icon; the active tool is drawn in ink; groups split by hairlines. It stays put while the
   wall pans and zooms under it. With the demo banner on screen it sits above the banner. */
.cv-toolbar {
  position: absolute; left: 50%; bottom: 16px; transform: translateX(-50%); z-index: 3;
  display: flex; align-items: center; gap: 2px; height: 44px; padding: 0 6px; box-sizing: border-box;
  border-radius: 14px; background: var(--panel);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--chrome-line) 45%, transparent), var(--paper-shadow), 0 10px 30px rgba(50, 46, 40, .14);
  white-space: nowrap; user-select: none;
}
[data-theme="dark"] .cv-toolbar { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--chrome-line) 60%, transparent), var(--paper-shadow), 0 10px 30px rgba(0, 0, 0, .5); }
/* Any pill at the foot (the sample banner, a read or render in progress, a view-only share): the
   toolbar sits above it, never under it. */
#workspace-view:has(> .ws-sample-banner) .cv-toolbar, #workspace-view:has(> .generation-banner) .cv-toolbar, #workspace-view:has(> .ws-view-only-banner) .cv-toolbar { bottom: calc(var(--cv-foot, 60px) + 16px); }
.cv-tb-group, .cv-zoomctl { display: flex; align-items: center; gap: 2px; }
.cv-tb {
  display: grid; place-items: center; width: 32px; height: 32px; padding: 0; border: 0; border-radius: 9px;
  background: transparent; color: var(--text2); cursor: pointer; transition: background-color 120ms ease, color 120ms ease;
}
.cv-tb:hover:not(:disabled) { background: var(--panel-2); color: var(--text1); }
.cv-tb.is-on { background: var(--text1); color: var(--panel); }
.cv-tb.is-on:hover { background: var(--text1); color: var(--panel); }
.cv-tb:disabled { opacity: .35; cursor: default; }
.cv-tb:focus-visible, .cv-zoompct:focus-visible, .cv-tb-find:focus-visible, .cv-chip:focus-visible, .cv-map:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.cv-tb-sep { width: 1px; height: 20px; margin: 0 6px; background: color-mix(in srgb, var(--chrome-line) 60%, transparent); flex: none; }
.cv-zoompct {
  min-width: 48px; height: 32px; padding: 0 4px; border: 0; border-radius: 9px; background: transparent; cursor: pointer;
  font: 500 12px/1 var(--font-mono); color: var(--text1); font-variant-numeric: tabular-nums;
}
.cv-zoompct:hover { background: var(--panel-2); }
.cv-tb-find {
  display: flex; align-items: center; gap: 8px; height: 32px; padding: 0 8px 0 9px; border: 0; border-radius: 9px;
  background: var(--panel-2); color: var(--text2); cursor: pointer; font: 500 12.5px/1 var(--font-body);
}
.cv-tb-find:hover { color: var(--text1); background: var(--panel-3); }
.cv-tb-find kbd { font: 500 10.5px/1 var(--font-mono); padding: 3px 5px; border-radius: 5px; background: var(--panel); color: var(--text2); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--chrome-line) 50%, transparent); }
/* View only: the editing tools are not drawn. */
.cv-toolbar.is-readonly #cv-note-btn, .cv-toolbar.is-readonly #cv-undo-btn, .cv-toolbar.is-readonly #cv-redo-btn,
.cv-toolbar.is-readonly #cv-tidy-btn, .cv-toolbar.is-readonly .cv-tb-sep--edit { display: none; }
/* A narrow wall (a panel open beside it): the bar keeps the tools and the zoom; Go to keeps only its icon. */
.cv-wall { container-type: inline-size; }
@container (max-width: 640px) {
  .cv-tb-find span, .cv-tb-find kbd { display: none; }
  .cv-tb-find { padding: 0 6px; background: transparent; }
}
@container (max-width: 520px) {
  .cv-toolbar #cv-tidy-btn, .cv-toolbar #cv-redo-btn { display: none; }
}
/* A phone's width: a finger already pans (no Select or Hand to pick) and the map would cover the
   wall it maps. With a mouse the zoom buttons stay; on a touch screen a pinch zooms. */
@container (max-width: 440px) {
  .cv-toolbar .cv-tb-tools, .cv-toolbar .cv-tb-sep--tools { display: none; }
  .cv-map { display: none; }
  .cv-toolbar { padding: 0 4px; }
  .cv-tb-sep { margin: 0 3px; }
  .cv-chip { left: 50%; transform: translateX(-50%); bottom: 70px; }
  #workspace-view:has(> .ws-sample-banner) .cv-chip, #workspace-view:has(> .generation-banner) .cv-chip, #workspace-view:has(> .ws-view-only-banner) .cv-chip { bottom: calc(var(--cv-foot, 60px) + 70px); }
}
@media (hover: none) and (pointer: coarse) {
  .cv-toolbar .cv-tb-tools, .cv-toolbar .cv-tb-sep--tools { display: none; }
  .cv-tb { width: 40px; height: 40px; }
  .cv-toolbar { height: 52px; }
  .cv-tb-find { height: 40px; }
}
@container (max-width: 440px) { @media (hover: none) and (pointer: coarse) { .cv-toolbar .cv-tb[data-step] { display: none; } } }
/* A phone: the header trail keeps project / Canvas (the head bar names the open card), the
   project name gives way first; the head drops its map (Back to canvas does the same); the sample
   banner tightens so the wall keeps its height. */
@media (max-width: 640px) {
  #workspace-view.ws-canvas .workspace-header-left .ws-bc-tab-sep,
  #workspace-view.ws-canvas .workspace-header-left .ws-bc-tab { display: none !important; }
  #workspace-view.ws-canvas .workspace-header-left { min-width: 0; overflow: hidden; }
  #workspace-view.ws-canvas .workspace-header-left .ws-bc-current { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  #cv-head { gap: 6px; padding: 0 6px; }
  #cv-head .cv-minimap { display: none; }
  #workspace-view.ws-canvas > .ws-sample-banner { padding: 8px 12px; gap: 6px 10px; font-size: .78rem; line-height: 1.35; }
  #workspace-view.ws-canvas > .ws-sample-banner .btn { min-height: 34px; padding: 0 14px; font-size: .78rem; }
}
/* Held on a touch screen: the card or sticky is lifted, ready to move. */
.cv-card.is-held, .cv-note.is-held { transform: scale(1.03); box-shadow: inset 0 0 0 1px var(--cv-edge-hot, transparent), var(--cv-sh-lift); transition: transform 140ms var(--rp-ease, ease), box-shadow 140ms ease; }
/* The resolved-notes chip, bottom left. */
.cv-chip {
  position: absolute; left: 16px; bottom: 22px; z-index: 3; height: 32px; padding: 0 12px; border: 0; border-radius: 999px;
  background: var(--panel); color: var(--text2); box-shadow: var(--paper-shadow); cursor: pointer; font: 500 12px/1 var(--font-body);
}
.cv-chip[hidden] { display: none; }
.cv-chip:hover { color: var(--text1); }
#workspace-view:has(> .ws-sample-banner) .cv-chip, #workspace-view:has(> .generation-banner) .cv-chip, #workspace-view:has(> .ws-view-only-banner) .cv-chip { bottom: calc(var(--cv-foot, 60px) + 22px); }
/* Hand tool: the pointer is a hand everywhere on the wall. */
.cv-wall.cv-tool-hand .cv-viewport, .cv-wall.cv-tool-hand .cv-viewport * { cursor: grab !important; }
.cv-map {
  position: absolute; right: 16px; bottom: 16px; z-index: 2; width: 96px; height: 73.5px; padding: 0; border: 0; border-radius: 8px; /* in the corner: no help bulb on the wall */
  background: var(--panel); box-shadow: var(--paper-shadow); cursor: pointer; overflow: hidden;
  opacity: 0; pointer-events: none; transform: translateY(6px); transition: opacity 180ms ease, transform 180ms var(--rp-ease, ease);
}
.cv-map.is-needed { opacity: 1; pointer-events: auto; transform: none; }
.cv-map-cells i { position: absolute; border-radius: 1.5px; background: color-mix(in srgb, var(--text3) 45%, transparent); }
.cv-map-cells i.is-open { background: var(--status-info); }
.cv-map-view { position: absolute; box-sizing: border-box; border: 1.5px solid var(--text1); border-radius: 2px; background: color-mix(in srgb, var(--text1) 6%, transparent); pointer-events: none; }
@media (prefers-reduced-motion: reduce) { .cv-map { transition: none; } }

/* ── Cards ─────────────────────────────────────────────────────────────────────────────── */
/* One anatomy (2026-11-08): a recessed well that shows the project, then a caption row with the
   module's icon, its name and one number. The well sits --cv-inset in from the card's edges and its
   corners are the card's minus that inset, so the two edges run parallel.
   Four elevations, each a stack of soft shadows tinted toward the warm canvas: resting, hover (the
   edge darkens and the shadow deepens; the card does not move), open (a 2px ring) and dragging
   (lifted, 2% larger, tilted a hair). A press shrinks the card to 98%. */
.cv-wall {
  --cv-sh-rest: 0 0.5px 1px rgba(60, 50, 35, .10), 0 2px 4px rgba(60, 50, 35, .06);
  --cv-sh-hover: 0 1px 2px rgba(60, 50, 35, .10), 0 4px 8px rgba(60, 50, 35, .07), 0 10px 22px rgba(60, 50, 35, .07);
  --cv-sh-lift: 0 2px 4px rgba(60, 50, 35, .10), 0 8px 16px rgba(60, 50, 35, .08), 0 20px 40px rgba(60, 50, 35, .12);
}
[data-theme="dark"] .cv-wall {
  --cv-sh-rest: 0 0.5px 1px rgba(0, 0, 0, .40), 0 2px 4px rgba(0, 0, 0, .30);
  --cv-sh-hover: 0 1px 2px rgba(0, 0, 0, .40), 0 4px 8px rgba(0, 0, 0, .32), 0 10px 22px rgba(0, 0, 0, .32);
  --cv-sh-lift: 0 2px 4px rgba(0, 0, 0, .45), 0 8px 16px rgba(0, 0, 0, .38), 0 20px 40px rgba(0, 0, 0, .45);
}
.cv-card {
  --cv-edge: color-mix(in srgb, var(--chrome-line) 40%, transparent);
  --cv-edge-hot: color-mix(in srgb, var(--chrome-line) 75%, transparent);
  --cv-r: 14px; --cv-inset: 6px;
  position: absolute; box-sizing: border-box; display: flex; flex-direction: column;
  padding: 0; border-radius: var(--cv-r);
  background: var(--panel); color: var(--text1);
  box-shadow: inset 0 0 0 1px var(--cv-edge), var(--cv-sh-rest);
  cursor: pointer; outline: 0; overflow: hidden; text-align: left;
  transition: box-shadow 160ms ease-out, transform 120ms ease-out;
}
.cv-card:hover { box-shadow: inset 0 0 0 1px var(--cv-edge-hot), var(--cv-sh-hover); }
.cv-card:active { transform: scale(.98); }
.cv-card:focus-visible { box-shadow: inset 0 0 0 1px var(--cv-edge), var(--cv-sh-rest), 0 0 0 2px var(--canvas), 0 0 0 4px var(--accent); }
.cv-card.is-open, .cv-card.is-open:hover { box-shadow: inset 0 0 0 2px var(--status-info), var(--cv-sh-hover); }
.cv-card.is-stale { box-shadow: inset 0 0 0 1.5px var(--status-warn), var(--cv-sh-rest); }
.cv-card.is-stale:hover { box-shadow: inset 0 0 0 1.5px var(--status-warn), var(--cv-sh-hover); }
.cv-card.is-stale.is-open { box-shadow: inset 0 0 0 1.5px var(--status-warn), var(--cv-sh-hover), 0 0 0 3px var(--status-info); }
.cv-card.is-ghost {
  background: color-mix(in srgb, var(--panel) 40%, transparent);
  box-shadow: none; border: 1.5px dashed color-mix(in srgb, var(--chrome-line) 80%, transparent);
  cursor: default;
}
.cv-card.is-ghost:hover { box-shadow: none; }
.cv-card.is-ghost:active { transform: none; }
@media (prefers-reduced-motion: reduce) { .cv-card { transition: none; } .cv-card:active { transform: none; } }
/* Out of the viewport: the box and its shadow stay, nothing inside is painted. */
.cv-card.is-culled > * { visibility: hidden; }
.cv-wall.is-panning .cv-card, .cv-wall.is-panning .cv-card:hover { transform: none; transition: none; }
.cv-card.is-ghost .cv-card-name, .cv-card.is-ghost .cv-card-icon { color: var(--text3); }

/* The Script card is a screenplay page: US Letter proportions (8.5 x 11, set in canvas.js) with a
   column of punched holes down the left edge (2026-11-08): the card is the paper, a hole every 18px. */
.cv-card--script { border-radius: 5px var(--cv-r) var(--cv-r) 5px;
  /* The page darkens a touch toward the binding, as paper does where it curls into the wire. */
  background: linear-gradient(90deg, color-mix(in srgb, var(--text1) 6%, var(--panel)) 0, var(--panel) 30px); }
/* The card is the paper: the well is the page itself, with no second frame inside it. */
.cv-card--script .cv-well { margin: 0 0 0 30px; border-radius: 0; background: transparent; box-shadow: none; }
.cv-card--script .cv-page { padding: 16px 14px 12px 4px; }
.cv-card--script .cv-cap { padding-left: 34px; }
/* The binding: a punched hole every 16px down the bound edge, and a thin wire from each hole looping
   over the edge. The holes show the wall through; the wire is brushed metal, a light catching its
   middle, with a soft shadow on the paper. */
.cv-spiral { position: absolute; left: 0; top: 10px; bottom: 10px; width: 26px; z-index: 2; pointer-events: none;
  /* Punched holes (owner, 2026-11-08: circles only, no wire): the wall shows through each one, a thin
     edge round it and, in the light theme, a faint light lip under it where the paper was cut. */
  background:
    radial-gradient(circle at 14px 9.7px, transparent 0 3.35px, rgba(255, 255, 255, .55) 3.5px, transparent 3.9px) 0 0 / 26px 18px repeat-y,
    radial-gradient(circle at 14px 9px, color-mix(in srgb, var(--text1) 5%, var(--canvas)) 0 3.05px, color-mix(in srgb, var(--text1) 34%, var(--canvas)) 3.2px 3.45px, transparent 3.65px) 0 0 / 26px 18px repeat-y; }
[data-theme="dark"] .cv-spiral { background: radial-gradient(circle at 14px 9px, var(--canvas) 0 3.05px, color-mix(in srgb, var(--text1) 22%, var(--canvas)) 3.2px 3.4px, transparent 3.6px) 0 0 / 26px 18px repeat-y; }
.cv-card--script.is-ghost .cv-spiral { opacity: .45; }
.cv-wall[data-cv-zoom="far"] .cv-card--script .cv-far { padding-left: 44px; }

.cv-well { position: relative; flex: 1; min-height: 0; margin: var(--cv-inset) var(--cv-inset) 0; border-radius: calc(var(--cv-r) - var(--cv-inset)); background: var(--panel-2); overflow: hidden; }
.cv-cap { flex: none; height: 32px; box-sizing: border-box; display: flex; align-items: center; gap: 7px; padding: 0 12px 0 12px; min-width: 0; }
.cv-card-icon { flex: none; display: grid; place-items: center; width: 16px; height: 16px; color: var(--text3); }
.cv-card-icon .nav-ico { width: 15px; height: 15px; display: block; }
.cv-card-name { flex: 0 1 auto; min-width: 0; margin: 0; font: 600 13.5px/1.15 var(--font-heading); letter-spacing: -0.01em; color: var(--text1); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cv-cap .cv-metric { margin-left: auto; }
.cv-cap .cv-review { flex: none; margin-left: 8px; height: 24px; padding: 0 10px; }
/* The status badge sits in the caption, on the right, before the number. */
.cv-cap .cv-pill { margin-left: auto; flex: 0 1 auto; min-width: 0; height: 20px; padding: 0 8px 0 7px; overflow: hidden; text-overflow: ellipsis; }
.cv-cap .cv-pill + .cv-metric { margin-left: 8px; }
.cv-cap .cv-card-name { flex-shrink: 0; max-width: 60%; }

/* Status: a pill with a dot. Words, not colour alone; inks are mixed toward the text colour so
   small type stays readable on its tint. Shown only while a card needs the person (canvas.js CV_LOUD). */
.cv-wall { --cv-ok-ink: color-mix(in srgb, var(--status-ok) 62%, var(--text1)); --cv-info-ink: color-mix(in srgb, var(--status-info) 58%, var(--text1)); }
.cv-pill {
  flex: none; display: inline-flex; align-items: center; gap: 6px; height: 22px; padding: 0 9px 0 8px; border-radius: 999px;
  font: 500 11px/1 var(--font-body); white-space: nowrap; background: var(--panel-2); color: var(--text2);
}
.cv-pill i { width: 6px; height: 6px; border-radius: 50%; background: var(--text3); flex: none; }
.cv-pill--progress { color: var(--cv-info-ink); }
.cv-pill--progress i { background: var(--status-info); }
.cv-pill--busy { color: var(--cv-info-ink); }
.cv-pill--busy i { background: var(--status-info); animation: cv-pulse 1.2s ease-in-out infinite; }
.cv-pill--ok { color: var(--cv-ok-ink); }
.cv-pill--ok i { background: var(--status-ok); }
.cv-pill--warn { color: var(--cv-warn-ink); }
.cv-pill--warn i { background: var(--status-warn); }
@media (prefers-reduced-motion: reduce) { .cv-pill--busy i { animation: none; } }

/* How far along: a strip along the card's bottom edge, while it is part done. */
.cv-meter { position: absolute; left: 0; right: 0; bottom: 0; height: 3px; background: var(--panel-2); overflow: hidden; pointer-events: none; }
.cv-meter i { display: block; height: 100%; background: var(--text3); transition: width 400ms var(--rp-ease, ease); }
.cv-meter--progress i, .cv-meter--busy i { background: var(--status-info); }
.cv-meter--warn i { background: var(--status-warn); }
.cv-metric { flex: none; font: 400 11px/1 var(--font-mono); color: var(--text2); font-variant-numeric: tabular-nums; white-space: nowrap; }
.cv-metric b { font-weight: 600; color: var(--text1); }

/* An empty card: the outline of what it will hold, in dashes, and one line. */
.cv-well--ghost { background: transparent; box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--chrome-line) 35%, transparent); }
.cv-ghost { position: absolute; inset: 8px; display: grid; gap: 5px; }
.cv-ghost i { border-radius: 4px; border: 1.5px dashed color-mix(in srgb, var(--chrome-line) 75%, transparent); }
.cv-ghost--frames { grid-template-columns: repeat(4, minmax(0, 1fr)); grid-template-rows: repeat(2, 1fr); }
.cv-ghost--tiles { grid-template-columns: 1fr 1fr; grid-template-rows: 1fr 1fr; }
.cv-ghost--portraits { grid-template-columns: repeat(3, 56px); justify-content: center; align-content: center; gap: 8px; }
.cv-ghost--portraits i { height: 56px; }
.cv-ghost--places { grid-template-columns: 1.4fr 1fr; grid-template-rows: 1fr 1fr; }
.cv-ghost--places i:first-child { grid-row: span 2; }
.cv-ghost--strips { grid-auto-flow: column; grid-auto-columns: 1fr; inset: 10px 10px 22px; gap: 3px; }
.cv-ghost--rows, .cv-ghost--bars, .cv-ghost--lines { align-content: center; gap: 7px; }
.cv-ghost--rows i { height: 22px; }
.cv-ghost--bars i { height: 8px; border-radius: 4px; }
.cv-ghost--bars i:nth-child(2) { width: 62%; } .cv-ghost--bars i:nth-child(3) { width: 34%; }
.cv-ghost--lines { inset: 18px 16px; align-content: start; gap: 10px; }
.cv-ghost--lines i { height: 0; border-width: 1.5px 0 0; border-radius: 0; }
.cv-ghost--lines i:first-child { width: 60%; justify-self: center; margin-bottom: 6px; }
.cv-ghost--numbers { grid-template-columns: 1fr 1fr; grid-template-rows: 1fr 1fr; }
.cv-ghost--frame { grid-template: 1fr / 1fr; }
.cv-card--poster .cv-ghost--frame { inset: 10px 14px; }
.cv-ghost-msg { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px; padding: 10px; text-align: center; }
.cv-ghost-msg > span { padding: 4px 9px; border-radius: 10px; background: var(--panel); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--chrome-line) 45%, transparent); font: 500 11px/1.3 var(--font-body); color: var(--text2); max-width: 100%; }
.cv-ghost-actions { display: flex; gap: 6px; }
/* Being filled (the read, a batch): the app's one loader over the outline, in the text colour. */
.cv-well--busy .cv-ghost-msg { color: var(--text3); }
.cv-well--busy .cv-ghost-msg .gen-loader { --gl-size: 24px; --gl-arm: 7px; }
.cv-card.is-ghost .cv-well--ghost { box-shadow: none; }

/* Text inside a card */
.cv-stat { margin: 0; font: 500 13px/1.3 var(--font-body); color: var(--text1); display: flex; align-items: center; gap: 8px; flex-wrap: wrap; padding: 12px; }
.cv-sub { margin: 0; font: 400 11.5px/1.4 var(--font-body); color: var(--text3); }
.cv-sub--warn { color: var(--cv-warn-ink); }
.cv-title { margin: 0 0 8px; font: 700 13px/1.2 var(--font-screenplay); color: var(--text1); letter-spacing: 0.02em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; text-align: center; text-transform: uppercase; }
.cv-mono { font: 500 11.5px/1 var(--font-mono); color: var(--text2); }
.cv-mono.is-over { color: var(--cv-warn-ink); }
.cv-page { position: absolute; inset: 0; padding: 16px 14px; overflow: hidden; display: flex; flex-direction: column; gap: 6px; }
.cv-scenes { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 6px; }
.cv-scenes li { display: flex; align-items: baseline; gap: 8px; font: 400 11px/1.3 var(--font-screenplay); color: var(--text2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cv-scenes li .cv-mono { flex: none; width: 14px; text-align: right; color: var(--text3); font-size: 10.5px; }
.cv-scenes .cv-more, .cv-shotrows .cv-more { color: var(--text3); font: 400 10.5px/1 var(--font-body); }

/* Pictures: lazy, small, cached by the browser. Empty boxes keep the shape. */
.cv-thumb { display: block; width: 100%; height: 100%; object-fit: cover; border-radius: 4px; background: var(--panel-3); }
.cv-thumb--empty { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--chrome-line) 45%, transparent); background: var(--panel-3); }
/* Storyboard: the frames fill the well. */
.cv-frames { position: absolute; inset: 8px; display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); align-content: start; gap: 6px; }
.cv-frame { margin: 0; position: relative; border-radius: 4px; overflow: hidden; }
.cv-frame .cv-thumb { aspect-ratio: 16 / 9; height: auto; }
.cv-frame.is-stale .cv-thumb { box-shadow: inset 0 0 0 2px var(--status-warn); }
.cv-frame.is-3d .cv-thumb { filter: saturate(.85); }
/* The shot number and size ride on the frame, so three rows fit the well. */
.cv-frame figcaption { position: absolute; left: 4px; bottom: 4px; padding: 2px 5px; border-radius: 3px; max-width: calc(100% - 8px); box-sizing: border-box; background: color-mix(in srgb, var(--panel) 86%, transparent); font: 400 9.5px/1.2 var(--font-body); color: var(--text2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cv-frame figcaption .cv-mono { font-size: 9.5px; color: var(--text1); }
.cv-frame--more { display: flex; align-items: center; justify-content: center; aspect-ratio: 16 / 9; align-self: start; border-radius: 4px; background: var(--panel-3); font: 500 12px/1 var(--font-mono); color: var(--text2); }
/* A frame being drawn: the app's loader over it, the picture dimmed under. */
.cv-frame.is-gen .cv-thumb, .cv-place.is-gen img, .cv-portrait.is-gen .cv-portrait-pic > :not(.cv-frame-gen) { opacity: .55; }
.cv-frame-gen { position: absolute; inset: 0; display: grid; place-items: center; color: var(--text1); pointer-events: none; }
.cv-frame-gen .gen-loader { filter: drop-shadow(0 0 2px var(--panel)); }
.cv-frame-scene { position: absolute; left: 4px; top: 4px; padding: 2px 5px; border-radius: 4px; background: color-mix(in srgb, var(--panel) 88%, transparent); font: 500 9px/1 var(--font-mono); color: var(--text2); }
/* Cast: a square portrait per person, in a grid. Four or fewer sit in one row of large squares;
   more go in two rows of four (six close up). */
.cv-portraits { --sq: 56px; position: absolute; inset: 8px; display: grid; grid-template-columns: repeat(var(--cols, 4), var(--sq)); justify-content: center; align-content: center; gap: 8px; }
.cv-portraits--grid { --sq: 44px; gap: 6px 8px; }
.cv-portrait { margin: 0; display: flex; flex-direction: column; align-items: center; gap: 4px; min-width: 0; }
.cv-portrait-pic { position: relative; width: var(--sq); height: var(--sq); border-radius: 4px; overflow: hidden; display: grid; place-items: center; background: var(--panel-3); box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .06); }
.cv-portrait-pic img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 20%; display: block; }
.cv-portrait-pic b { font: 600 12px/1 var(--font-mono); color: var(--text2); }
.cv-portrait figcaption { max-width: calc(var(--sq) + 6px); font: 400 9px/1.1 var(--font-mono); color: var(--text3); text-transform: uppercase; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cv-portrait--more .cv-portrait-pic { background: transparent; box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--chrome-line) 60%, transparent); }
/* Locations: the master views, the first one large. */
.cv-places { position: absolute; inset: 8px; display: grid; gap: 5px; grid-template-columns: 1fr; grid-template-rows: 1fr; }
.cv-places--2 { grid-template-columns: 1fr 1fr; }
.cv-places--3 { grid-template-columns: 1.4fr 1fr; grid-template-rows: 1fr 1fr; }
.cv-places--3 .cv-place:first-child { grid-row: span 2; }
.cv-place { margin: 0; position: relative; border-radius: 4px; overflow: hidden; background: var(--panel-3); min-height: 0; }
.cv-place img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; }
.cv-place figcaption { position: absolute; left: 4px; right: 4px; bottom: 4px; padding: 2px 5px; border-radius: 3px; width: fit-content; max-width: calc(100% - 8px); box-sizing: border-box; background: color-mix(in srgb, var(--panel) 88%, transparent); font: 400 9px/1.2 var(--font-mono); color: var(--text2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Look: the reference frame fills the well, the palette along its foot. */
.cv-look { position: absolute; inset: 0; }
.cv-look-thumb { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; }
.cv-swatches { position: absolute; left: 0; right: 0; bottom: 0; height: 16px; display: flex; }
.cv-swatches i { flex: 1; display: block; }
/* Breakdown: the production stripboard. */
.cv-stripboard { position: absolute; inset: 10px 10px 36px; display: flex; gap: 2px; }
.cv-strip-s { flex: 1 1 0; min-width: 2px; max-width: 18px; border-radius: 2px; box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .14); }
.cv-strip-s--int-day { background: #fbfaf6; } .cv-strip-s--ext-day { background: #f2d65e; }
.cv-strip-s--int-night { background: #86aae8; } .cv-strip-s--ext-night { background: #86c993; }
/* The key: every colour the board uses, two to a row, so none is ever cut off. */
.cv-strip-key { position: absolute; left: 10px; right: 10px; bottom: 7px; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 4px 8px; font: 400 9px/1 var(--font-mono); color: var(--text3); white-space: nowrap; }
.cv-strip-k { display: inline-flex; align-items: center; gap: 4px; }
.cv-strip-k::before { content: ''; width: 7px; height: 7px; border-radius: 2px; box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .18); }
.cv-strip-k--int-day::before { background: #fbfaf6; } .cv-strip-k--ext-day::before { background: #f2d65e; }
.cv-strip-k--int-night::before { background: #86aae8; } .cv-strip-k--ext-night::before { background: #86c993; }
/* Shot List: the first shots, one row each. */
.cv-shotrows { position: absolute; inset: 9px; margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 5px; }
.cv-shotrows li:not(.cv-more) { display: grid; grid-template-columns: 30px 30px minmax(0, 1fr); gap: 6px; align-items: center; padding: 6px 7px; border-radius: 4px; background: var(--panel); font: 400 10px/1.1 var(--font-mono); color: var(--text2); }
.cv-shotrows .cv-mono { font-size: 10px; font-weight: 700; color: var(--text1); }
.cv-shot-bits { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: var(--text3); }
.cv-shotrows .cv-more { padding-left: 7px; }
/* Analytics: screen presence as bars. */
.cv-bars { position: absolute; inset: 12px; margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; justify-content: center; gap: 9px; }
.cv-bars li { display: grid; grid-template-columns: 62px minmax(0, 1fr) 18px; align-items: center; gap: 7px; font: 400 10px/1 var(--font-mono); color: var(--text2); }
.cv-bar-name { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cv-bar { height: 6px; border-radius: 3px; background: var(--panel-3); overflow: hidden; }
.cv-bar i { display: block; height: 100%; border-radius: 3px; background: var(--text2); }
.cv-bar-n { text-align: right; color: var(--text3); font-variant-numeric: tabular-nums; }
/* Logistics: four numbers. */
.cv-kv { position: absolute; inset: 12px 14px; margin: 0; display: grid; grid-template-columns: 1fr 1fr; gap: 8px 12px; align-content: center; }
.cv-kv div { display: flex; flex-direction: column; gap: 3px; }
.cv-kv dd { margin: 0; font: 600 20px/1 var(--font-heading); letter-spacing: -0.02em; color: var(--text1); font-variant-numeric: tabular-nums; }
.cv-kv dt { font: 400 9.5px/1.2 var(--font-mono); color: var(--text3); }

/* ── Mid: 40% to 75%. The preview and the name, nothing small. ─────────────────────────── */
.cv-wall[data-cv-zoom="mid"] .cv-cap { height: 40px; }
.cv-wall[data-cv-zoom="mid"] .cv-card-name { font-size: 19px; }
.cv-wall[data-cv-zoom="mid"] .cv-card-icon { width: 20px; height: 20px; }
.cv-wall[data-cv-zoom="mid"] .cv-card-icon .nav-ico { width: 19px; height: 19px; }
.cv-wall[data-cv-zoom="mid"] .cv-metric, .cv-wall[data-cv-zoom="mid"] .cv-review,
.cv-wall[data-cv-zoom="mid"] .cv-frame figcaption, .cv-wall[data-cv-zoom="mid"] .cv-portrait figcaption,
.cv-wall[data-cv-zoom="mid"] .cv-place figcaption, .cv-wall[data-cv-zoom="mid"] .cv-strip-key,
.cv-wall[data-cv-zoom="mid"] .cv-shot-bits,
.cv-wall[data-cv-zoom="mid"] .cv-bar-name, .cv-wall[data-cv-zoom="mid"] .cv-bar-n, .cv-wall[data-cv-zoom="mid"] .cv-frame-scene,
.cv-wall[data-cv-zoom="mid"] .cv-scenes, .cv-wall[data-cv-zoom="mid"] .cv-sub, .cv-wall[data-cv-zoom="mid"] .cv-more { display: none; }
.cv-wall[data-cv-zoom="mid"] .cv-portraits { --sq: 60px; }
.cv-wall[data-cv-zoom="mid"] .cv-portraits--grid { --sq: 50px; }
.cv-wall[data-cv-zoom="mid"] .cv-title { font-size: 18px; white-space: normal; margin-top: 30%; }
.cv-wall[data-cv-zoom="mid"] .cv-bars li { grid-template-columns: minmax(0, 1fr); }
.cv-wall[data-cv-zoom="mid"] .cv-bars { gap: 14px; }
.cv-wall[data-cv-zoom="mid"] .cv-bar { height: 10px; border-radius: 5px; }
.cv-wall[data-cv-zoom="mid"] .cv-kv dd { font-size: 28px; }
.cv-wall[data-cv-zoom="mid"] .cv-kv dt { font-size: 13px; line-height: 1.1; }
.cv-wall[data-cv-zoom="mid"] .cv-kv { gap: 6px 10px; }
.cv-wall[data-cv-zoom="mid"] .cv-stripboard { inset: 10px; }
.cv-wall[data-cv-zoom="mid"] .cv-shotrows li:not(.cv-more) { grid-template-columns: 1fr 1fr; padding: 8px; font-size: 14px; }
.cv-wall[data-cv-zoom="mid"] .cv-shotrows .cv-mono { font-size: 14px; }
.cv-wall[data-cv-zoom="mid"] .cv-cap .cv-pill { height: 28px; font-size: 14px; padding: 0 11px 0 9px; }
.cv-wall[data-cv-zoom="mid"] .cv-cap .cv-pill i { width: 8px; height: 8px; }
.cv-wall[data-cv-zoom="mid"] .cv-ghost-msg > span { font-size: 14px; padding: 6px 10px; }
.cv-wall[data-cv-zoom="mid"] .cv-card--poster .cv-ghost-msg > span, .cv-wall[data-cv-zoom="mid"] .cv-card--moodboard .cv-ghost-msg > span { font-size: 12px; }

/* ── Far: under 40%. No preview: the name and one big number, so the film reads at a glance. ── */
.cv-far { flex: 1; min-height: 0; display: flex; flex-direction: column; justify-content: center; gap: 8px; padding: 18px 22px; overflow: hidden; }
.cv-far-name { font: 600 30px/1.05 var(--font-heading); letter-spacing: -0.02em; color: var(--text1); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cv-far-num { font: 700 46px/1 var(--font-mono); letter-spacing: -0.02em; color: var(--text1); font-variant-numeric: tabular-nums; }
.cv-far-unit { font: 400 22px/1.15 var(--font-body); color: var(--text3); overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
.cv-far-state { align-self: flex-start; padding: 6px 14px; border-radius: 999px; font: 600 20px/1.1 var(--font-body); background: var(--panel-2); color: var(--text2); }
.cv-far-state--warn { color: var(--cv-warn-ink); background: var(--status-warn-dim); }
.cv-far-state--progress, .cv-far-state--busy { color: var(--cv-info-ink); background: var(--status-info-dim); }
.cv-wall[data-cv-zoom="far"] .cv-meter { height: 8px; }
.cv-wall[data-cv-zoom="far"] .cv-zone-label { font-size: 20px; }
.cv-wall[data-cv-zoom="far"] .cv-zone-label kbd { font-size: 16px; padding: 5px 9px; }
.cv-wall[data-cv-zoom="far"] .cv-card--poster .cv-far, .cv-wall[data-cv-zoom="far"] .cv-card--moodboard .cv-far { padding: 14px 14px; }
.cv-wall[data-cv-zoom="far"] .cv-card--poster .cv-far-name, .cv-wall[data-cv-zoom="far"] .cv-card--moodboard .cv-far-name { font-size: 24px; }

/* ── Close: over 120%. The same previews, with more in them (canvas.js _cvRich). ─────────── */
.cv-wall[data-cv-zoom="close"] .cv-frames { grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 5px; }
.cv-wall[data-cv-zoom="close"] .cv-frame figcaption { font-size: 8px; }
.cv-wall[data-cv-zoom="close"] .cv-portraits--grid { --sq: 30px; gap: 5px; }
.cv-wall[data-cv-zoom="close"] .cv-portrait figcaption { font-size: 7.5px; }
.cv-wall[data-cv-zoom="close"] .cv-scenes { gap: 4px; }
.cv-wall[data-cv-zoom="close"] .cv-bars { gap: 5px; }
.cv-wall[data-cv-zoom="close"] .cv-shotrows { gap: 3px; }
.cv-wall[data-cv-zoom="close"] .cv-shotrows li:not(.cv-more) { padding: 3px 6px; }

/* ── Modules at panel width: the few layouts that assume the whole page ────────────────── */
/* Poster: the studio's tool rail goes under the stage, as it does on a tablet. */
#workspace-view.ws-canvas[data-cv-mode="panel"] .workspace-main:has(.pst-studio) { display: block; overflow: auto; padding: 0 0 16px; }
#workspace-view.ws-canvas[data-cv-mode="panel"] .ws-sheet > .pst-studio { grid-template-columns: 1fr; }
#workspace-view.ws-canvas[data-cv-mode="panel"] .pst-studio > .pst-panel { grid-template-columns: 1fr; order: 2; }
#workspace-view.ws-canvas[data-cv-mode="panel"] .pst-rail { flex-direction: row; border-right: 0; border-bottom: 1px solid var(--border); overflow-x: auto; }
#workspace-view.ws-canvas[data-cv-mode="panel"] .pst-tool { flex: 1 0 56px; }
/* Stacked, the stage has no height of its own, and the poster's frame is sized from it
   (.pst-canvas is a size container): left alone it measured 0 tall and the poster all but
   vanished. Give it the visible height of the panel, as the tablet rule gives it 68vh. */
#workspace-view.ws-canvas[data-cv-mode="panel"] .pst-canvas { flex: none; width: 100%; height: clamp(320px, calc(100dvh - 290px), 760px); }
#workspace-view.ws-canvas[data-cv-mode="panel"] .pst-work { flex-direction: column; padding: 14px; }
#workspace-view.ws-canvas[data-cv-mode="panel"] .pst-work > .pst-versions { width: auto; }
#workspace-view.ws-canvas[data-cv-mode="panel"] .pst-work .pst-versions-row { flex-direction: row; }
/* Moodboard: two across instead of four or three. */
#workspace-view.ws-canvas[data-cv-mode="panel"] .mb-grid--cast { grid-template-columns: repeat(2, minmax(0, 1fr)); }
#workspace-view.ws-canvas[data-cv-mode="panel"] .mb-grid--loc { grid-template-columns: repeat(2, minmax(0, 1fr)); }
#workspace-view.ws-canvas[data-cv-mode="panel"] .mb-grid--style { --mb-cols: 2; }

/* ── Staleness (phase 3): the source card says what changed; Review lists the frames ───── */
.cv-badge--ok { background: var(--status-ok-dim); color: var(--status-ok); }
.cv-card.is-source { box-shadow: var(--cv-sh-rest), 0 0 0 1.5px color-mix(in srgb, var(--status-warn) 55%, transparent); }
.cv-card.is-source.is-open { box-shadow: var(--cv-sh-hover), 0 0 0 1.5px var(--status-warn), 0 0 0 4px var(--status-info); }
.cv-changed {
  position: absolute; z-index: 3; left: 6px; right: 6px; top: 6px; display: block; width: auto; padding: 6px 9px; border: 0; border-radius: 6px; text-align: left; cursor: pointer;
  box-shadow: 0 1px 3px rgba(0, 0, 0, .12);
  background: var(--status-warn-dim); color: var(--cv-warn-ink); font: 500 11.5px/1.3 var(--font-body);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; transition: filter 140ms ease;
}
.cv-changed:hover { filter: brightness(0.97); }
.cv-changed:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.cv-wall[data-cv-zoom="mid"] .cv-changed { font-size: 15px; padding: 8px 12px; }

.cv-rvsheet {
  position: absolute; top: 12px; right: 12px; bottom: 12px; z-index: 4; width: min(400px, calc(100% - 24px));
  display: flex; flex-direction: column; border-radius: var(--panel-radius); background: var(--panel);
  box-shadow: var(--paper-shadow), 0 18px 48px rgba(50, 46, 40, .16); overflow: hidden;
  animation: cv-in var(--rp-dur, 300ms) var(--rp-ease, ease) both;
}
[data-theme="dark"] .cv-rvsheet { box-shadow: var(--paper-shadow), 0 18px 48px rgba(0, 0, 0, .45); }
@media (prefers-reduced-motion: reduce) { .cv-rvsheet { animation: none; } }
.cv-rv-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; padding: 18px 18px 12px; border-bottom: 1px solid var(--panel-2); }
.cv-rv-title { margin: 0 0 4px; font: 600 16px/1.2 var(--font-heading); letter-spacing: -0.01em; color: var(--text1); }
.cv-rv-list { flex: 1; min-height: 0; overflow: auto; margin: 0; padding: 6px 10px; list-style: none; }
.cv-rv-row { display: grid; grid-template-columns: 112px minmax(0, 1fr); gap: 12px; padding: 10px 8px; border-radius: 10px; }
.cv-rv-row + .cv-rv-row { border-top: 1px solid var(--panel-2); }
.cv-rv-thumb { padding: 0; border: 0; background: none; cursor: pointer; border-radius: 6px; align-self: start; }
.cv-rv-thumb .cv-thumb { aspect-ratio: 16 / 9; height: auto; box-shadow: inset 0 0 0 2px var(--status-warn); }
.cv-rv-thumb:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.cv-rv-text { min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.cv-rv-head { margin: 0; font: 500 13px/1.2 var(--font-body); color: var(--text1); display: flex; align-items: center; gap: 6px; }
.cv-rv-up { font: 500 10.5px/1 var(--font-body); padding: 3px 6px; border-radius: 999px; background: var(--panel-2); color: var(--text2); }
.cv-rv-why { margin: 0; font: 400 12px/1.35 var(--font-body); color: var(--cv-warn-ink); }
.cv-rv-acts { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 4px; }
.cv-rv-empty { margin: 0; padding: 24px 18px; font: 400 13px/1.4 var(--font-body); color: var(--text3); }
.cv-rv-foot { display: flex; justify-content: flex-start; gap: 8px; padding: 12px 18px; /* left: the help bulb sits bottom-right */ border-top: 1px solid var(--panel-2); background: var(--panel); }

/* ── Layout and notes (phase 4) ───────────────────────────────────────────────────────── */
/* A card being dragged rides above the others with no hover lift; a card a note would pin to glows. */
.cv-wall.is-dragging, .cv-wall.is-dragging * { cursor: grabbing !important; user-select: none; }
/* Dragging: picked up. Lifted, 2% larger, tilted a hair. A resize stays flat so its edges track the pointer. */
.cv-card.is-dragging { z-index: 6; transition: none; transform: scale(1.02) rotate(-0.6deg) !important; box-shadow: inset 0 0 0 1px var(--cv-edge-hot), var(--cv-sh-lift); }
.cv-wall.is-resizing .cv-card.is-dragging { transform: none !important; }
@media (prefers-reduced-motion: reduce) { .cv-card.is-dragging { transform: none !important; } }
.cv-card.is-drop { box-shadow: var(--cv-sh-hover), 0 0 0 2px var(--accent); }
.cv-resize {
  position: absolute; right: 4px; bottom: 4px; width: 16px; height: 16px; cursor: nwse-resize; opacity: 0; transition: opacity 140ms ease;
  background: linear-gradient(135deg, transparent 50%, color-mix(in srgb, var(--text3) 60%, transparent) 50%, color-mix(in srgb, var(--text3) 60%, transparent) 58%, transparent 58%, transparent 70%, color-mix(in srgb, var(--text3) 60%, transparent) 70%, color-mix(in srgb, var(--text3) 60%, transparent) 78%, transparent 78%);
}
.cv-card:hover .cv-resize, .cv-card.is-dragging .cv-resize { opacity: 1; }

/* Stickies (2026-11-08, after the owner's reference): pastel paper with a soft lift, a title and
   when it was last changed, a hairline rule, what it holds, and a toolbar along the foot. The paper
   colours are the stickies' own and the ink is dark on all of them, in either theme (a sticky is
   content, like a picture). */
.cv-notes { position: absolute; inset: 0; pointer-events: none; }
.cv-note {
  --cv-note: #faf5dc; --cv-note-ink: #2b2925; --cv-note-soft: rgba(43, 41, 37, .58); --cv-note-rule: rgba(43, 41, 37, .14);
  position: absolute; z-index: 3; box-sizing: border-box; display: flex; flex-direction: column;
  padding: 12px 12px 6px; pointer-events: auto; cursor: default; outline: 0; overflow: hidden;
  border-radius: 3px; color: var(--cv-note-ink); background: var(--cv-note);
  box-shadow: 0 1px 2px rgba(40, 32, 20, .08), 0 8px 18px -8px rgba(40, 32, 20, .24);
  transition: box-shadow 160ms ease-out;
}
.cv-note--yellow { --cv-note: #faf5dc; }
.cv-note--orange { --cv-note: #fbe8d8; }
.cv-note--green  { --cv-note: #e3f2e5; }
.cv-note--blue   { --cv-note: #e3f1f8; }
.cv-note--violet { --cv-note: #e7e8f7; }
.cv-note--pink   { --cv-note: #fae5e5; }
.cv-note--grey   { --cv-note: #eeede9; }
.cv-note:hover { box-shadow: 0 1px 2px rgba(40, 32, 20, .10), 0 12px 24px -8px rgba(40, 32, 20, .30); }
.cv-note.is-selected { z-index: 6; overflow: visible; box-shadow: 0 0 0 2px var(--status-info), 0 12px 24px -8px rgba(40, 32, 20, .30); }
.cv-note:focus-visible { box-shadow: 0 0 0 2px var(--canvas), 0 0 0 4px var(--accent), 0 12px 24px -8px rgba(40, 32, 20, .30); }
.cv-note.is-dragging { z-index: 7; box-shadow: 0 0 0 2px var(--status-info), 0 20px 38px -10px rgba(40, 32, 20, .38); }
.cv-note.is-drop-target { box-shadow: 0 0 0 2px var(--status-info), 0 0 0 6px color-mix(in srgb, var(--status-info) 25%, transparent); }
.cv-note.is-resolved { opacity: .62; }
.cv-note.is-resolved .cv-note-title, .cv-note.is-resolved .cv-note-text { text-decoration: line-through; text-decoration-color: rgba(43, 41, 37, .45); }
/* Head: the title, resolve and delete; then when and who; then the rule. */
.cv-note-head { flex: none; display: flex; align-items: flex-start; gap: 2px; min-width: 0; }
.cv-note-title { flex: 1; min-width: 0; outline: 0; font: 500 13.5px/1.25 var(--font-body); letter-spacing: -0.01em; color: var(--cv-note-ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; cursor: inherit; padding: 1px 0; }
.cv-note-title[contenteditable="true"] { white-space: normal; text-overflow: clip; cursor: text; }
.cv-note-title:empty::before { content: attr(data-placeholder); color: var(--cv-note-soft); }
.cv-note-hb { flex: none; display: grid; place-items: center; width: 22px; height: 22px; margin: -3px -3px 0 0; padding: 0; border: 0; border-radius: 6px; background: transparent; color: var(--cv-note-soft); cursor: pointer; }
.cv-note-hb + .cv-note-hb { margin-left: 2px; }
.cv-note-hb:hover { background: rgba(43, 41, 37, .07); color: var(--cv-note-ink); }
.cv-note:not(:hover):not(.is-selected) .cv-note-hb { opacity: 0; }
.cv-note-meta { flex: none; margin: 3px 0 0; font: 400 9.5px/1.25 var(--font-body); color: var(--cv-note-soft); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cv-note-meta::after { content: ''; display: block; height: 1px; margin-top: 8px; background: var(--cv-note-rule); }
/* Body: what it holds. */
.cv-note-body { position: relative; flex: 1; min-height: 0; margin-top: 8px; overflow: hidden; }
.cv-note-text {
  height: 100%; outline: 0; overflow: hidden; white-space: pre-wrap; overflow-wrap: anywhere; cursor: inherit;
  font: 400 12px/1.45 var(--font-body); color: var(--cv-note-ink);
}
.cv-note-text--xl { font-size: 17px; line-height: 1.25; font-weight: 500; letter-spacing: -0.01em; }
.cv-note-text--l { font-size: 14px; line-height: 1.35; }
.cv-note-text--m { font-size: 12px; }
.cv-note-text--s { font-size: 11px; line-height: 1.4; }
.cv-note-text[contenteditable="true"] { cursor: text; overflow-y: auto; }
.cv-note-text:empty::before { content: attr(data-placeholder); color: var(--cv-note-soft); }
/* The selection ring is the sticky's focus mark: no second box round the words while writing. */
#workspace-view .cv-note [contenteditable]:focus, #workspace-view .cv-note [contenteditable]:focus-visible { outline: none; box-shadow: none; }
/* A checklist. */
.cv-checks { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 4px; max-height: 100%; overflow: hidden; }
.cv-check-row { display: flex; align-items: flex-start; gap: 6px; min-width: 0; }
.cv-check { flex: none; display: grid; place-items: center; width: 15px; height: 15px; margin-top: 0; padding: 0; border: 0; background: transparent; color: var(--cv-note-ink); cursor: pointer; }
.cv-check:disabled { cursor: default; }
.cv-check .cv-check-mark { opacity: 0; }
.cv-check-row.is-done .cv-check .cv-check-mark { opacity: 1; }
.cv-check-t { flex: 1; min-width: 0; outline: 0; font: 400 11.5px/1.3 var(--font-body); color: var(--cv-note-ink); overflow-wrap: anywhere; cursor: inherit; }
.cv-check-t[contenteditable="true"] { cursor: text; }
.cv-check-t:empty::before { content: attr(data-placeholder); color: var(--cv-note-soft); }
.cv-check-row.is-done .cv-check-t { text-decoration: line-through; color: var(--cv-note-soft); }
.cv-check-add { padding: 2px 0; border: 0; background: transparent; font: 500 11px/1.2 var(--font-body); color: var(--cv-note-soft); cursor: pointer; }
.cv-check-add:hover { color: var(--cv-note-ink); }
/* A drawing. */
.cv-note-draw { position: absolute; inset: 0; width: 100%; height: 100%; display: block; overflow: visible; }
.cv-note-draw path { fill: none; stroke: var(--cv-note-ink); stroke-width: 1.6px; stroke-linecap: round; stroke-linejoin: round; vector-effect: non-scaling-stroke; }
.cv-note-draw.is-live { cursor: crosshair; touch-action: none; }
/* A picture. */
.cv-note-img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; border-radius: 2px; }
.cv-note-drop { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px; padding: 0; border: 1.5px dashed var(--cv-note-rule); border-radius: 4px; background: transparent; color: var(--cv-note-soft); cursor: pointer; font: 500 11px/1.2 var(--font-body); }
.cv-note-drop small { font-size: 9.5px; font-weight: 400; }
.cv-note-drop:hover { color: var(--cv-note-ink); border-color: var(--cv-note-soft); }
.cv-note-hint { position: absolute; inset: 0; display: grid; place-items: center; font: 400 11px/1.3 var(--font-body); color: var(--cv-note-soft); pointer-events: none; }
.cv-note-mini-row { position: absolute; right: 4px; bottom: 4px; display: flex; gap: 4px; }
.cv-note-mini { padding: 3px 7px; border: 0; border-radius: 6px; background: color-mix(in srgb, var(--cv-note) 80%, #fff); box-shadow: 0 0 0 1px var(--cv-note-rule); font: 500 10px/1.1 var(--font-body); color: var(--cv-note-ink); cursor: pointer; }
.cv-note-mini:hover { background: #fff; }
/* The toolbar along the foot: text, picture, drawing, checklist, colour. */
.cv-note-tb { flex: none; display: flex; align-items: center; justify-content: space-between; gap: 2px; margin: 6px -4px 0; height: 26px; }
.cv-note-tbb { flex: none; display: grid; place-items: center; width: 26px; height: 24px; padding: 0; border: 0; border-radius: 6px; background: transparent; color: var(--cv-note-soft); cursor: pointer; }
.cv-note-tbb:hover { background: rgba(43, 41, 37, .07); color: var(--cv-note-ink); }
.cv-note-tbb.is-on { color: var(--cv-note-ink); background: rgba(43, 41, 37, .09); }
.cv-note-sw { width: 14px; height: 14px; border-radius: 3px; background: linear-gradient(135deg, #fae5e5, #e7e8f7 50%, #e3f1f8); box-shadow: inset 0 0 0 1px rgba(43, 41, 37, .35); }
.cv-note:not(:hover):not(.is-selected) .cv-note-tb { opacity: .45; }
.cv-note-colors { position: absolute; left: 8px; right: 8px; bottom: 36px; z-index: 2; display: flex; justify-content: space-between; padding: 6px 8px; border-radius: 10px; background: var(--panel); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--chrome-line) 45%, transparent), 0 8px 20px rgba(40, 32, 20, .18); }
.cv-note-dot { flex: none; width: 16px; height: 16px; padding: 0; border: 0; border-radius: 50%; cursor: pointer; background: var(--cv-note); box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .18); }
.cv-note-dot--yellow { --cv-note: #f3e7a6; } .cv-note-dot--orange { --cv-note: #f5cba7; } .cv-note-dot--green { --cv-note: #bfe3c4; }
.cv-note-dot--blue { --cv-note: #bfe0f1; } .cv-note-dot--violet { --cv-note: #c9cbef; } .cv-note-dot--pink { --cv-note: #f3c4c4; } .cv-note-dot--grey { --cv-note: #d8d6d0; }
.cv-note-dot.is-on { box-shadow: 0 0 0 2px var(--panel), 0 0 0 3.5px var(--text1); }
.cv-note-hb:focus-visible, .cv-note-tbb:focus-visible, .cv-note-dot:focus-visible, .cv-check:focus-visible, .cv-check-add:focus-visible, .cv-note-mini:focus-visible, .cv-note-drop:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.cv-note-pin { position: absolute; top: -4px; left: 50%; width: 9px; height: 9px; margin-left: -4.5px; border-radius: 50%; background: #c0392b; box-shadow: 0 1px 2px rgba(0, 0, 0, .35), inset 0 -2px 0 rgba(0, 0, 0, .2); }
/* The resize corner of a selected sticky. */
.cv-note-resize { position: absolute; right: -5px; bottom: -5px; width: 12px; height: 12px; border-radius: 3px; background: var(--panel); box-shadow: 0 0 0 2px var(--status-info); cursor: nwse-resize; }
.cv-wall.is-dragging .cv-note-colors { display: none; }
/* Further out, the words that matter grow and the small print goes. */
.cv-wall[data-cv-zoom="mid"] .cv-note-meta, .cv-wall[data-cv-zoom="far"] .cv-note-meta,
.cv-wall[data-cv-zoom="mid"] .cv-note-tb, .cv-wall[data-cv-zoom="far"] .cv-note-tb,
.cv-wall[data-cv-zoom="mid"] .cv-note-hb, .cv-wall[data-cv-zoom="far"] .cv-note-hb { display: none; }
.cv-wall[data-cv-zoom="mid"] .cv-note-title { font-size: 18px; }
.cv-wall[data-cv-zoom="far"] .cv-note-title { font-size: 26px; white-space: normal; }
.cv-wall[data-cv-zoom="far"] .cv-note-body { margin-top: 10px; }

/* The foot's new buttons, and the count of open notes on the header toggle in Standard. */
#workspace-view .cv-shell-toggle .cv-toggle-count {
  display: inline-flex; align-items: center; justify-content: center; min-width: 18px; height: 18px; margin-left: 7px; padding: 0 5px; box-sizing: border-box;
  border-radius: 999px; background: var(--status-warn-dim); color: var(--cv-warn-ink); font: 600 11px/1 var(--font-mono);
}
.cv-zone.is-loose { border-color: transparent; }

/* ── Phase 5: the palette, the first-run hint, item marks, screen-reader text ──────────── */
.cv-palette { position: fixed; inset: 0; z-index: 9000; }
.cv-pal-back { position: absolute; inset: 0; background: color-mix(in srgb, var(--canvas) 55%, transparent); backdrop-filter: blur(2px); }
.cv-pal-box {
  position: absolute; left: 50%; top: 14vh; width: min(620px, calc(100vw - 32px)); transform: translateX(-50%);
  display: flex; flex-direction: column; max-height: 64vh; overflow: hidden;
  border-radius: var(--panel-radius); background: var(--panel); box-shadow: var(--paper-shadow), 0 24px 64px rgba(50, 46, 40, .22);
  animation: cv-pal-in 160ms var(--rp-ease, ease) both;
}
[data-theme="dark"] .cv-pal-box { box-shadow: var(--paper-shadow), 0 24px 64px rgba(0, 0, 0, .55); }
@keyframes cv-pal-in { from { opacity: 0; transform: translate(-50%, -6px); } to { opacity: 1; transform: translate(-50%, 0); } }
@media (prefers-reduced-motion: reduce) { .cv-pal-box { animation: none; } }
.cv-pal-input { width: 100%; box-sizing: border-box; height: 56px; padding: 0 20px; border: 0; border-bottom: 1px solid var(--panel-2); outline: 0; background: transparent; color: var(--text1); font: 500 16px/1 var(--font-body); }
.cv-pal-input::placeholder { color: var(--text3); }
.cv-pal-list { flex: 1; min-height: 0; overflow: auto; margin: 0; padding: 6px; list-style: none; }
.cv-pal-kind { padding: 10px 12px 4px; font: 500 11px/1 var(--font-mono); color: var(--text3); letter-spacing: .02em; }
.cv-pal-item { display: flex; align-items: baseline; justify-content: space-between; gap: 16px; padding: 9px 12px; border-radius: 8px; cursor: pointer; }
.cv-pal-item.is-sel { background: var(--panel-2); }
.cv-pal-label { min-width: 0; font: 500 14px/1.3 var(--font-body); color: var(--text1); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cv-pal-hint { flex: none; max-width: 45%; font: 400 12px/1.3 var(--font-body); color: var(--text2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cv-pal-empty { padding: 18px 12px; font: 400 13px/1.4 var(--font-body); color: var(--text2); }
.cv-pal-foot { display: flex; gap: 16px; padding: 10px 16px; border-top: 1px solid var(--panel-2); font: 400 11.5px/1 var(--font-body); color: var(--text2); }
.cv-pal-foot kbd, .cv-hint kbd { font: 500 10.5px/1 var(--font-mono); padding: 3px 5px; margin-right: 2px; border-radius: 5px; background: var(--panel-2); color: var(--text2); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--chrome-line) 50%, transparent); }

.cv-hint {
  position: absolute; left: 0; right: 0; margin: 0 auto; top: 16px; z-index: 4; width: min(440px, calc(100% - 32px)); box-sizing: border-box; /* centred by margin: cv-in's transform would undo a translate */
  padding: 16px 18px 14px; border-radius: var(--panel-radius); background: var(--panel); box-shadow: var(--paper-shadow), 0 14px 36px rgba(50, 46, 40, .14);
  animation: cv-in var(--rp-dur, 300ms) var(--rp-ease, ease) both;
}
@media (prefers-reduced-motion: reduce) { .cv-hint { animation: none; } }
.cv-hint-title { margin: 0 0 8px; font: 600 15px/1.25 var(--font-heading); color: var(--text1); letter-spacing: -0.01em; }
.cv-hint ul { margin: 0 0 12px; padding-left: 18px; display: flex; flex-direction: column; gap: 6px; font: 400 13px/1.4 var(--font-body); color: var(--text2); }

/* An item a link or the palette went to, marked for a moment. */
#workspace-view.ws-canvas .cv-flash { animation: cv-flash 1.6s ease both; }
@keyframes cv-flash { 0%, 60% { box-shadow: 0 0 0 3px var(--accent); } 100% { box-shadow: 0 0 0 3px transparent; } }
@media (prefers-reduced-motion: reduce) { #workspace-view.ws-canvas .cv-flash { animation: none; outline: 3px solid var(--accent); } }
.cv-sr { position: absolute !important; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
.cv-cost { font-variant-numeric: tabular-nums; color: var(--text2); }

/* The module area takes the focus when a card opens (for the keyboard); it is a region, not a control: no outline. */
#workspace-view.ws-canvas #workspace-content:focus { outline: none; }

/* ── Media cards: the Pitch Deck (16:9), the Moodboard (square), the Poster (2:3) ───────────
   The picture fills the well, which has the shape of what it holds (canvas.js _cvWellH). */
.cv-media { position: absolute; inset: 0; overflow: hidden; background: var(--panel-2); }
.cv-media-img { display: block; width: 100%; height: 100%; object-fit: cover; }
.cv-media-empty { position: absolute; inset: 0; display: grid; place-items: center; padding: 12px; text-align: center; }
.cv-media-empty span { font: 400 11px/1.35 var(--font-body); color: var(--text2); }
/* The deck's first page, scaled from 1920x1080 to the well. */
.cv-deck-page { position: absolute; inset: 0; overflow: hidden; }
.cv-deck-mini { position: absolute; left: 0; top: 0; width: 1920px; height: 1080px; transform-origin: 0 0; pointer-events: none; }
.cv-deck-mini .slide { box-shadow: none; }
/* The poster as the studio draws it, filling its well. */
.cv-card--poster .pst { position: absolute; inset: 0; width: 100%; height: 100%; aspect-ratio: auto; border-radius: 0; box-shadow: none; cursor: inherit; pointer-events: none; }

/* ── The invitation to try Canvas, under the view toggle (2026-11-08) ─────────────────────── */
#cv-promo.cv-promo {
  position: fixed; z-index: 800; width: 320px; max-width: calc(100vw - 24px); box-sizing: border-box; padding: 14px 16px 14px;
  border-radius: 14px; background: var(--panel); color: var(--text1);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--chrome-line) 45%, transparent), 0 2px 4px rgba(40, 32, 20, .08), 0 18px 40px rgba(40, 32, 20, .18);
  animation: cv-promo-in 260ms cubic-bezier(.2, .8, .2, 1) both;
}
[data-theme="dark"] #cv-promo.cv-promo { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--chrome-line) 60%, transparent), 0 18px 40px rgba(0, 0, 0, .5); }
#cv-promo.cv-promo::before { content: ''; position: absolute; top: -6px; left: calc(var(--cv-promo-arrow, 260px) - 6px); width: 12px; height: 12px; background: var(--panel); transform: rotate(45deg); border-radius: 2px; box-shadow: -1px -1px 0 0 color-mix(in srgb, var(--chrome-line) 45%, transparent); }
@keyframes cv-promo-in { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { #cv-promo.cv-promo { animation: none; } }
#cv-promo .cv-promo-art { margin: 0 0 10px; padding: 6px 4px; border-radius: 10px; background: var(--canvas); }
#cv-promo .cv-promo-art svg { display: block; }
#cv-promo .cv-promo-c { fill: var(--panel); stroke: color-mix(in srgb, var(--chrome-line) 70%, transparent); stroke-width: 1; }
#cv-promo .cv-promo-s { fill: #fae5e5; stroke: rgba(43, 41, 37, .2); stroke-width: 1; }
#cv-promo .cv-promo-lines path { fill: none; stroke: var(--status-ok); stroke-width: 1.6; stroke-linecap: round; }
#cv-promo .cv-promo-kicker { margin: 0 0 2px; font: 600 11px/1.2 var(--font-mono); letter-spacing: .06em; text-transform: uppercase; color: var(--cv-ok-ink, var(--status-ok)); }
#cv-promo .cv-promo-title { margin: 0 0 6px; font: 600 17px/1.2 var(--font-heading); letter-spacing: -0.01em; }
#cv-promo .cv-promo-text { margin: 0 0 12px; font: 400 13px/1.45 var(--font-body); color: var(--text2); }
#cv-promo .cv-promo-acts { display: flex; justify-content: flex-end; gap: 8px; }
/* The toggle the invitation points at wears a ring while it shows. */
#cv-shell-toggle.is-promo { box-shadow: 0 0 0 2px var(--status-ok); animation: cv-promo-ring 1.8s ease-in-out infinite; }
@keyframes cv-promo-ring { 0%, 100% { box-shadow: 0 0 0 2px var(--status-ok); } 50% { box-shadow: 0 0 0 5px color-mix(in srgb, var(--status-ok) 35%, transparent); } }
@media (prefers-reduced-motion: reduce) { #cv-shell-toggle.is-promo { animation: none; } }

/* ── Starter tips: regular stickies by the groups (2026-11-08) ──────────────────────────────
   A tip is a .cv-note with the sticky's own look; only its foot differs (a link, not the toolbar),
   and it sits under the person's own notes. */
.cv-note.cv-note--tip { z-index: 2; }
.cv-note-tb--tip { justify-content: flex-start; margin: 6px 0 0; }
.cv-tip-go { padding: 2px 0; border: 0; background: transparent; font: 600 11px/1.2 var(--font-body); color: var(--cv-note-ink); cursor: pointer; text-decoration: underline; text-decoration-color: rgba(43, 41, 37, .3); text-underline-offset: 2px; }
.cv-tip-go:hover { text-decoration-color: currentColor; }
.cv-tip-go:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.cv-wall[data-cv-zoom="far"] .cv-note--tip { display: none; }
/* A tip's delete button is always there (a sticky's shows on hover), at the middle zoom too. */
.cv-wall .cv-notes .cv-note.cv-note--tip .cv-tip-del { opacity: 1; }
.cv-wall[data-cv-zoom="mid"] .cv-notes .cv-note.cv-note--tip .cv-tip-del { display: grid; width: 30px; height: 30px; }
.cv-wall[data-cv-zoom="mid"] .cv-note--tip .cv-tip-del svg { width: 20px; height: 20px; }
