:root {
  /* Light paper palette — flipped 2026-05-13 to align with Jason's RSM
     design system. The named role tokens (--bg, --text, etc.) now map
     to ink-on-paper equivalents from tokens.css. Card text + region
     fills + everything that uses these roles flip in one stroke. */
  --bg: var(--paper);                /* #f6f6f6 */
  --bg-panel: var(--card);           /* #ffffff — chat panel */
  --bg-card: var(--card);            /* #ffffff — cards on the canvas */
  --text: var(--ink-1);              /* #1e1e1e */
  --text-strong: var(--ink-0);       /* #1a1919 */
  --text-body: var(--ink-2);         /* #40464d */
  --text-dim: var(--ink-3);          /* #656a71 */
  --text-muted: var(--ink-4);        /* #979aa1 */
  --border: var(--card-edge);        /* #e6e6e6 */
  --border-strong: var(--card-edge-strong); /* #d9d9d9 */
  --accent-cyan: var(--wp-blue);     /* legacy fallback — now Blueberry */
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-body);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  /* Defense: nothing should ever scroll the page itself. The canvas
     viewport, chat panel, and brief tablet all manage their own
     scrolling internally. Without this, scrollIntoView calls on
     transformed canvas children can scroll the body and displace
     the wayfinding overlays (minimap, edge peekers). */
  overflow: hidden;
}

/* Layout: canvas viewport fills the window; the chat panel floats
   over it on the right per Jason's RSM v3.2 reference. The viewport
   extends behind the chat (content can pan behind it); the chat
   itself uses a glass-blur backdrop so the canvas reads through.
   See #chat below for the floating-panel geometry. */
#app {
  position: relative;
  height: 100vh;
  overflow: hidden;
}

#canvas-viewport {
  position: absolute;
  inset: 0;
  overflow: hidden;
  padding: 0;
}

/* Four-zone canvas — the stage is the pannable/zoomable surface that
   panzoom transforms. Zones are absolutely positioned children at
   canonical coordinates. The viewport hides overflow; the stage moves
   underneath. */
#canvas-stage {
  position: absolute;
  inset: 0;
  /* The stage is "infinite" — bigger than any single viewport. Zones
     pin to canonical coords; pan/zoom navigates between them. Sized
     generously so each zone holds Jason's 180px display-font background
     label without clipping or bleed. */
  width: 7400px;
  height: 2400px;
  transform-origin: 0 0;
}

.zone {
  position: absolute;
  background: transparent;
  /* No overflow clip — zones are now large enough that the giant
     background typography fits within bounds and content has room to
     grow. (Earlier overflow: hidden made the typography unreadable
     and clipped legitimate content.) */
  overflow: visible;
}

.zone[data-zone="brief"] {
  left: 4000px; top: 500px;
  width: 1400px; height: 1500px;
}
.zone[data-zone="discovery"] {
  left: 0; top: 500px;
  width: 2200px; height: 1500px;
}
.zone[data-zone="narrative"] {
  left: 2400px; top: 500px;
  width: 1400px; height: 1500px;
}
.zone[data-zone="design"] {
  left: 5600px; top: 500px;
  width: 1800px; height: 1500px;
}

/* Zone labels — giant low-contrast display-font label positioned in
   the canvas whitespace adjacent to each zone (per Jason's RSM v3.2
   reference design — originally italic Garamond, now Recoleta Regular
   as part of the 2026-05-23 type unification). Lives at #canvas-stage
   scope, NOT as a child of the .zone elements, so content can never
   cover the label. Phase 2c.3 polish replaced the inside-the-zone
   .zone-bg-type pattern (which was reliably obliterated by dense
   Discovery cards and the Brief tablet). */
.zone-label {
  position: absolute;
  display: flex;
  flex-direction: column;
  pointer-events: none;
  user-select: none;
  z-index: 0;
}
.zone-label-eyebrow {
  font-family: var(--font-mono);
  font-size: 14px;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(30, 30, 30, 0.32);
  margin-bottom: 6px;
  /* Pulled in slightly from the giant label's letter-spacing so the
     two read as a stack, not as floating fragments. */
  padding-left: 2px;
}
.zone-label-title {
  font-family: var(--font-display);
  font-size: 180px;
  font-weight: 400;
  letter-spacing: -0.04em;
  line-height: 0.85;
  color: rgba(28, 32, 36, 0.07);
  white-space: nowrap;
}

.zone-content {
  position: relative;
  z-index: 1;
  padding: 40px;
}

.zone-placeholder {
  font-family: var(--font-body);
  font-size: 14px;
  color: var(--ink-3);
  font-style: italic;
}

/* Design zone hosts the "Coming soon..." banner until the design
   surface is built. The wrap fills the zone (inset:0 against the
   absolute-positioned .zone parent) so flex-centering works against
   the full zone, not just the content area. Padding override cancels
   .zone-content's 40px so the SVG centers exactly. SVG inlined into
   index.html so Recoleta resolves from the page's @font-face. */
.zone[data-zone="design"] .coming-soon-wrap {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
}

.coming-soon-svg {
  display: block;
  width: 800px;
  height: auto;
}

/* Discovery zone hosts the existing canvas grid */
.zone[data-zone="discovery"] #canvas {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-auto-rows: minmax(220px, auto);
  gap: 16px;
  align-content: start;
  padding: 16px;
  /* Stage handles pan/zoom; the canvas grid no longer transforms itself. */
}

/* Phase-pill navigation — fixed to viewport top-center. Click to snap
   the viewport to that zone. */
#phase-nav {
  position: absolute;
  /* 56px app bar + 16px gap. Was 16px before the app bar landed. */
  top: 72px;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  gap: 4px;
  padding: 4px;
  /* Glassy nav per Jason's RSM v3.2 — slightly translucent with a
     backdrop blur so canvas content reads through it. */
  background: rgba(255, 255, 255, 0.94);
  backdrop-filter: blur(14px) saturate(140%);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
  border: 1px solid var(--card-edge);
  border-radius: 999px;
  box-shadow: var(--shadow-rest);
  z-index: 100;
}
.phase-pill {
  appearance: none;
  background: transparent;
  border: 0;
  padding: 6px 14px;
  border-radius: 999px;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-3);
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease-standard),
              color var(--dur-fast) var(--ease-standard);
}
.phase-pill:hover {
  color: var(--ink-1);
  background: var(--paper);
}
/* Active state: ink-1 black with white text — strong contrast so the
   user can read at a glance which zone they're in. Previously
   --paper-2 / --ink-0 read as a hover-ish neighbor of the inactive
   state. */
.phase-pill.is-active {
  color: var(--card);
  background: var(--ink-1);
}
.phase-pill:focus-visible {
  outline: var(--focus-width) solid var(--focus-ring);
  outline-offset: 2px;
}

/* Minimap — bottom-left wayfinding overlay (moved from bottom-right
   when the chat panel started floating on the right). Renders the
   four zones at small scale and overlays a viewport rect that tracks
   the live stage transform. Same source of truth as canvas-stage.js
   (ZONE_RECTS). */
#minimap {
  position: absolute;
  left: 16px;
  bottom: 16px;
  padding: 8px;
  background: var(--card);
  border: 1px solid var(--card-edge);
  border-radius: 10px;
  box-shadow: var(--shadow-rest);
  z-index: 100;
  user-select: none;
}
.minimap-plot {
  position: relative;
  overflow: hidden;
  border-radius: 4px;
  background: var(--paper);
}
.minimap-zone {
  position: absolute;
  appearance: none;
  border: 1px solid var(--card-edge-strong);
  border-radius: 2px;
  cursor: pointer;
  padding: 0;
  overflow: hidden;
  background: var(--card);
  transition: filter var(--dur-fast) var(--ease-standard),
              border-color var(--dur-fast) var(--ease-standard);
}
.minimap-zone:hover { filter: brightness(0.96); border-color: var(--ink-3); }
.minimap-zone.is-active { border-color: var(--ink-1); }
.minimap-zone:focus-visible {
  outline: var(--focus-width) solid var(--focus-ring);
  outline-offset: 1px;
}
.minimap-zone-label {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-mono);
  font-size: 8px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-3);
  pointer-events: none;
}
/* Tint each zone with its section-family hue at a low-saturation
   wash; keeps the minimap legible against the white frame while
   echoing the canvas's spatial color identity. Brief = Functionality
   teal (the "what to build" zone). */
.minimap-zone.is-brief     { background: var(--hue-functionality-2); }
.minimap-zone.is-discovery { background: var(--paper); }
.minimap-zone.is-narrative { background: var(--hue-narrative-2); }
.minimap-zone.is-design    { background: var(--wp-blue-soft); }
/* Viewport tracker: Blueberry stroke + soft Blueberry wash so it
   reads clearly against both the white minimap surface and the
   ink-toned zone-tile borders. Plain dark-ink stroke (earlier
   shipped) looked too much like a zone border and got lost. */
.minimap-viewport {
  position: absolute;
  border: 2px solid var(--wp-blue);
  background: rgba(56, 88, 233, 0.12);
  pointer-events: none;
  border-radius: 2px;
  transition: none;
}

/* Brief-update toast — floating dark chip top-LEFT of the viewport
   (was top-right; moved when the chat panel started floating on
   the right). Fires on section.close / section.revised (server
   emits `brief.updated`). Click pans to the Brief zone. Multiple
   toasts stack. */
#brief-toast-container {
  position: absolute;
  top: 16px;
  left: 16px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  z-index: 200;
  pointer-events: none;
}
.brief-toast {
  appearance: none;
  border: 0;
  background: var(--ink-0);
  color: var(--card);
  padding: 9px 16px 10px;
  border-radius: 999px;
  box-shadow: var(--shadow-pin);
  cursor: pointer;
  pointer-events: auto;
  display: inline-flex;
  flex-direction: column;
  gap: 2px;
  align-items: flex-start;
  text-align: left;
  font-family: var(--font-body);
  opacity: 0;
  transform: translateX(-8px);
  transition: opacity var(--dur-base) var(--ease-standard),
              transform var(--dur-base) var(--ease-standard),
              background var(--dur-fast) var(--ease-standard);
  max-width: 320px;
}
.brief-toast.is-visible { opacity: 1; transform: translateX(0); }
.brief-toast.is-hiding  { opacity: 0; transform: translateX(-8px); }
.brief-toast:hover { background: var(--ink-1); }
.brief-toast:focus-visible {
  outline: var(--focus-width) solid var(--focus-ring);
  outline-offset: 2px;
}
.brief-toast-eyebrow {
  font-family: var(--font-mono);
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--paper-2);
}
.brief-toast-body {
  font-size: 13px;
  font-weight: 500;
  line-height: 1.3;
}

/* Edge peekers — directional pill on the viewport edge for any zone
   fully off-screen. The minimap is the canonical "where am I"; peekers
   are quick "this way" cues. */
#edge-peekers {
  position: absolute;
  inset: 0;
  pointer-events: none; /* peekers themselves opt back in */
  z-index: 50;
}
.edge-peeker {
  position: absolute;
  /* Edge-anchored: each peeker hugs the edge it points to so the
     arrow/label can't get clipped by the viewport edge. Defaults are
     overridden per data-edge below. */
  transform: translate(-50%, -50%);
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 11px;
  background: var(--card);
  border: 1px solid var(--card-edge);
  border-radius: 999px;
  box-shadow: var(--shadow-rest);
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-3);
  cursor: pointer;
  pointer-events: auto;
  opacity: 1;
  transition: opacity var(--dur-base) var(--ease-standard),
              color var(--dur-fast) var(--ease-standard),
              border-color var(--dur-fast) var(--ease-standard);
}
.edge-peeker:hover { color: var(--ink-0); border-color: var(--ink-3); }
.edge-peeker:focus-visible {
  outline: var(--focus-width) solid var(--focus-ring);
  outline-offset: 2px;
}
.edge-peeker.is-hidden { opacity: 0; pointer-events: none; }
.edge-peeker[data-edge="top"]    { transform: translate(-50%, 0%); }
.edge-peeker[data-edge="bottom"] { transform: translate(-50%, -100%); }
.edge-peeker[data-edge="left"]   { transform: translate(0%, -50%); }
.edge-peeker[data-edge="right"]  { transform: translate(-100%, -50%); }
.edge-peeker-arrow {
  font-family: var(--font-body); /* arrows render cleaner in body font */
  font-size: 13px;
  font-weight: 700;
  line-height: 1;
  color: var(--ink-1);
}

/* Input pill chip row — sits inside the composer, above the textarea */
#input-pills {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
#input-pills:empty { display: none; }

/* Chat — floating glass panel on the right per Jason's RSM v3.2.
   Anchored 16px from each edge, 360px wide, 20px radius, with a
   two-layer ink shadow and a backdrop blur so the canvas reads
   through. The viewport extends behind the panel; the user can
   pan content out from under it. */
#chat {
  position: absolute;
  /* 56px app bar + 16px gap. Was 16px before the app bar landed. */
  top: 72px;
  right: 16px;
  bottom: 16px;
  width: 360px;
  background: rgba(255, 255, 255, 0.86);
  backdrop-filter: blur(14px) saturate(140%);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
  color: var(--ink-1);
  border: 1px solid var(--card-edge);
  border-radius: 20px;
  box-shadow:
    0 4px 14px rgba(28, 32, 36, 0.06),
    0 24px 60px rgba(28, 32, 36, 0.10);
  display: flex;
  flex-direction: column;
  min-height: 0;
  overflow: hidden;
  z-index: 200;
}

#chat-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 14px 16px 12px;
  border-bottom: 1px solid var(--card-edge);
}

/* Identity block — 26px Blueberry rounded mark + single-line
   product name aligned with the mark and the new-session pill.
   Signals "this is a real collaborator, not a chatbox." */
.chat-identity {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}
.chat-identity-mark {
  width: 26px;
  height: 26px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  color: var(--wp-blue);
}
.chat-identity-mark svg { display: block; }

#new-session-btn {
  background: transparent;
  color: var(--ink-3);
  border: 1px solid var(--card-edge-strong);
  border-radius: 999px;
  padding: 4px 10px;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  cursor: pointer;
  flex-shrink: 0;
  transition: color var(--dur-fast) var(--ease-standard), border-color var(--dur-fast) var(--ease-standard);
}
#new-session-btn:hover {
  color: var(--ink-1);
  border-color: var(--ink-3);
}
#new-session-btn:focus-visible {
  outline: var(--focus-width) solid var(--focus-ring);
  outline-offset: var(--focus-offset);
}

#log { flex: 1; min-height: 0; overflow-y: auto; padding: 16px; }

#input-form {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 12px;
  border-top: 1px solid var(--card-edge);
}

#input-form .input-row {
  display: flex;
  align-items: flex-end;
  gap: 8px;
}

/* Pill-shaped input — 22px radius hugs the textarea content. Send
   button collapses to a 32×32 circle to the right of it.
   Auto-grow: min-height matches the two-line invite the textarea
   used to ship with (so empty state still says "you can type a
   lot"); max-height caps at 1/3 of the chat panel's height
   (chat is top:16/bottom:16, so chat height is 100vh-32, and
   1/3 of that scales with the viewport) so the input grows
   generously on tall windows but never eats the panel. main.js's
   autogrowInput keeps the visible height equal to scrollHeight
   up to the cap, then content scrolls internally past it. */
#input {
  flex: 1;
  background: var(--card);
  color: var(--ink-1);
  border: 1px solid var(--card-edge-strong);
  border-radius: 22px;
  padding: 9px 14px;
  font: inherit;
  font-size: 14px;
  line-height: 1.5;
  resize: none;
  min-height: 60px;
  max-height: calc((100vh - 32px) / 3);
  overflow-y: auto;
  transition: border-color var(--dur-fast) var(--ease-standard), box-shadow var(--dur-fast) var(--ease-standard);
}
#input::placeholder { color: var(--ink-4); }
#input:focus-visible {
  outline: none;
  border-color: var(--focus-ring);
  box-shadow: 0 0 0 var(--focus-offset) var(--focus-ring-soft);
}

button[type="submit"] {
  background: var(--wp-blue);
  color: var(--card);
  border: 0;
  border-radius: 50%;
  width: 32px;
  height: 32px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  flex-shrink: 0;
  transition: background var(--dur-fast) var(--ease-standard);
}
button[type="submit"] svg { display: block; }
button[type="submit"]:hover { background: var(--wp-blue-deep); }
button[type="submit"]:focus-visible {
  outline: var(--focus-width) solid var(--focus-ring);
  outline-offset: var(--focus-offset);
}

/* Typography scale — from design-system.html */
.t-h1 { font-size: 22px; font-weight: 700; letter-spacing: -0.3px; line-height: 1.2; color: var(--text-strong); }
.t-h2 { font-size: 16px; font-weight: 600; letter-spacing: -0.1px; line-height: 1.3; color: var(--text-strong); }
.t-h3 { font-size: 14px; font-weight: 600; letter-spacing: 0; line-height: 1.35; color: var(--ink-0); }
.t-label { font-size: 10px; font-weight: 700; letter-spacing: 2.2px; text-transform: uppercase; color: var(--text-dim); }
.t-section-label { font-size: 10px; font-weight: 600; letter-spacing: 2.5px; text-transform: uppercase; color: var(--region-label); }
.t-card-title { font-size: 13px; font-weight: 600; letter-spacing: 0.1px; color: var(--text-strong); line-height: 1.35; }
.t-body { font-size: 12px; font-weight: 400; line-height: 1.45; color: var(--text-body); }
.t-annotation { font-size: 11px; font-weight: 400; line-height: 1.45; color: var(--text-dim); }
.t-chat { font-size: 12.5px; font-weight: 400; line-height: 1.5; color: var(--ink-1); }
.t-chat-speaker { font-size: 10.5px; font-weight: 600; letter-spacing: 1.2px; text-transform: uppercase; color: var(--text-muted); }
.t-chip { font-size: 9.5px; font-weight: 500; letter-spacing: 0.3px; color: var(--text-dim); }
.t-mono { font-family: var(--font-mono); font-size: 10.5px; color: var(--text-dim); }

/* Chat log — bubble layout. Speaker is signaled by alignment + color, no inline label. */
.msg {
  display: flex;
  margin: 28px 0 0;
  font-size: 15px;
  line-height: 1.55;
  color: var(--text-body);
}
.msg:first-child { margin-top: 0; }
.msg.is-followup { margin-top: 8px; }
.msg .bubble {
  max-width: 85%;
  padding: 8px 12px;
  border-radius: 14px;
  border: 1px solid transparent;
  word-wrap: break-word;
  overflow-wrap: anywhere;
}
.msg .bubble-pills {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin-bottom: 6px;
}
.msg.is-ai { justify-content: flex-start; }
.msg.is-ai .bubble {
  background: #f6f6f6;
  border-color: #ececec;
  color: #1e1e1e;
  border-bottom-left-radius: 4px;
}
.msg.is-ai.is-followup .bubble { border-top-left-radius: 4px; }
.msg.is-user { justify-content: flex-end; }
.msg.is-user .bubble {
  background: var(--wp-blue);
  border-color: var(--wp-blue);
  color: #ffffff;
  border-bottom-right-radius: 4px;
}
.msg.is-user.is-followup .bubble { border-top-right-radius: 4px; }
.msg.is-user .inline-pill {
  background: rgba(255, 255, 255, 0.2);
  border-color: rgba(255, 255, 255, 0.55);
  color: #ffffff;
}
.msg.is-error { justify-content: center; }
.msg.is-error .bubble {
  background: #fce8e8;
  border-color: #f0c5c5;
  color: #a03838;
  font-size: 13px;
  max-width: 95%;
  text-align: center;
}

/* Status — ambient progress messages from the server (research
   in flight, etc.). Centered, mono, dim — distinct from the AI/
   user bubble grammar so the user reads them as "the system is
   doing something" rather than "the AI just said something." No
   bubble; the row itself carries the styling. */
.msg.is-status { justify-content: center; margin-top: 12px; }
.msg.is-status .bubble {
  background: transparent;
  border: 0;
  padding: 0;
  max-width: 100%;
  text-align: center;
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: 0.06em;
  color: var(--ink-3);
  text-transform: none;
}
.msg.is-status .bubble::before {
  content: '↳ ';
  color: var(--ink-4);
}

/* A section region — neutral fill + edge per Jason's RSM v3.2 tokens.
   Section color travels on the section-hue dot inside the header; cards
   carry their own section color on the left border. Hidden until the
   region has at least one card. */
.region {
  position: relative;
  background: var(--region-fill);
  border: 1px solid var(--region-edge);
  border-radius: 20px;
  padding: 72px 20px 20px;
  display: none;
}

.region:has(.card) {
  display: block;
  animation: region-fade-in 260ms ease-out both;
}

@keyframes region-fade-in {
  from { opacity: 0; transform: translateY(4px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* Region header — RSM v3.2 treatment: dot + display-font title.
   Sits inside the region top-left, replacing the old uppercase mono
   chip that overlapped the border. (Originally Garamond italic;
   moved to Recoleta Regular in the 2026-05-23 type unification.)
   Eyebrow ("blurb · N cards") and hairline rule are future
   enhancements; the header structure accommodates them as siblings
   of .region-title. */
.region-header {
  position: absolute;
  left: 20px;
  top: 16px;
  right: 20px;
  display: flex;
  align-items: baseline;
  gap: 12px;
  pointer-events: none;
}

.region-dot {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--accent);
  align-self: center;
  flex-shrink: 0;
}

.region-title {
  margin: 0;
  font-family: var(--font-display);
  font-size: 32px;
  font-weight: 400;
  letter-spacing: -0.02em;
  line-height: 1;
  color: var(--ink-1);
  flex-shrink: 0;
}

/* Eyebrow — "N cards" count next to the region title, in mono
   uppercase. Updates live via canvas.js's updateRegionEyebrow.
   Empty when the region has no cards (the region itself is hidden
   in that case via the :has(.card) rule above). */
.region-eyebrow {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-3);
  flex-shrink: 0;
  white-space: nowrap;
  align-self: center;
}
.region-eyebrow:empty { display: none; }

/* Hairline — 1px rule extending from the eyebrow to the right edge
   of the region header. Visually anchors the title group inside
   the region's surface. */
.region-hairline {
  flex: 1 1 auto;
  height: 1px;
  background: var(--card-edge);
  align-self: center;
  min-width: 12px;
}

.region-cards {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  min-height: 140px;
}

.region-empty {
  font-style: italic;
  color: var(--text-muted);
  font-size: 11px;
  align-self: center;
}

/* Card — RSM v3.2 design system. White (--card) background, ink text,
   3px section-color left stripe, 14px border-radius (Jason's spec uses
   a custom radius outside the standard scale). Header is a chrome row
   with role icon + SectionTag chip; title and body sit below.
   Width matches Jason's InsightCard (320px); role-specific overrides
   below adjust where the spec varies. */
.card {
  position: relative;
  width: 320px;
  background: var(--card);
  border: 1px solid var(--card-edge);
  border-radius: 14px;
  padding: 18px 20px 16px;
  box-shadow: var(--shadow-rest);
  transition: box-shadow var(--dur-base) var(--ease-standard);
}
.card:hover {
  box-shadow: var(--shadow-pin);
}

.card-chrome,
.card-header {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 12px;
}
/* Research and upload cards still use the older .card-header structure
   with the title inside the header row. Keep that layout working until
   those special card types are rebuilt with the chrome+tag pattern. */
.card-header {
  align-items: flex-start;
}
.card-header .card-title {
  margin: 0;
}

.card-icon {
  color: var(--card-color, var(--accent-cyan));
  line-height: 1;
  flex-shrink: 0;
  display: inline-flex;
}
.card-icon svg {
  width: 16px;
  height: 16px;
  display: block;
}

/* RSM SectionTag — pill-shaped chip with the section's light-tint
   background and a saturated dot. Mono uppercase text. The dot uses
   --card-color (saturated section hue); the chip uses --card-color-2
   (light tint, set per-card from --hue-<id>-2 in tokens.css). */
.card-tag {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 9px 3px 7px;
  border-radius: 999px;
  background: var(--card-color-2, var(--card-edge));
  color: var(--ink-1);
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  line-height: 1;
  flex-shrink: 1;
  min-width: 0;
}
.card-tag-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--card-color, var(--accent-cyan));
  flex-shrink: 0;
}
.card-tag-text {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.card-title {
  font-family: var(--font-body);
  font-size: 17px;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--ink-1);
  line-height: 1.25;
  margin: 0 0 8px;
  word-break: break-word;
  text-wrap: pretty;
}

.card-body {
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 400;
  line-height: 1.5;
  color: var(--ink-2);
}

/* ─── OG thumbnail on link/reference cards ──────────────────────────
   Inserted by canvas.js between .card-chrome and .card-title when a
   card.enrich event (live or replayed) carries an og.image. Bleeds
   past the card's 20px horizontal padding so the image runs edge-to-
   edge; capped at 160px tall and object-fit: cover so portraits and
   landscapes both behave. */
.card-thumbnail {
  margin: 0 -20px 12px;
  overflow: hidden;
}
.card-thumbnail img {
  display: block;
  width: 100%;
  height: auto;
  max-height: 160px;
  object-fit: cover;
  background: rgba(0, 0, 0, 0.06);
}

/* Role-specific card treatments — RSM v3.2 design system. The default
   .card rule above defines the primary treatment (200px width, 3px
   section-color left stripe, white background, ink text). Each role
   below overrides where it diverges.

   Cards do NOT switch wholesale by role — they inherit the default
   chrome and adjust specifics. This keeps the visual system coherent. */

/* note — sticky-note fragment, narrower than default to read as a
   fragment, not a paragraph. Slightly larger body type per Jason's spec. */
.card[data-role="note"] {
  width: 220px;
}
.card[data-role="note"] .card-body {
  font-size: 14px;
}

/* Per-role widths per Jason's RSM v3.2 spec. The default .card width
   is 320 (InsightCard); the variants below narrow toward what each
   role's content typically wants. */
.card[data-role="reference"] { width: 280px; }
.card[data-role="quote"]     { width: 300px; }
.card[data-role="gap"]       { width: 280px; }
.card[data-role="upload"]    { width: 240px; }

/* gap — open-question card. Cream-tinted background distinguishes it
   from solved/captured cards on the wall; the gap-section hue carries
   on the chip. */
.card[data-role="gap"] {
  background: #fff8f5;
}

/* distinctive — hero/differentiator point. The ONE card kind that
   uses section hue on the chrome (saturated 2px border + 3px outer halo
   in the section's light pair). Larger display-serif italic title to
   signal that this is a loud moment. Reserved for genuine standouts.
   300px width per Jason's spec — slightly narrower than the default. */
.card[data-role="distinctive"] {
  width: 300px;
  border: 2px solid var(--card-color, var(--accent-cyan));
  box-shadow: var(--shadow-rest), 0 0 0 3px var(--card-color-2, var(--card-color-soft));
  padding: 14px 16px;
}
.card[data-role="distinctive"]:hover {
  box-shadow: var(--shadow-pin), 0 0 0 3px var(--card-color-2, var(--card-color-soft));
}
.card[data-role="distinctive"] .card-title {
  font-family: var(--font-display);
  font-size: 24px;
  font-weight: 400;
  letter-spacing: -0.02em;
  line-height: 1.1;
}

/* State classes — drafting is the only currently-supported state */
.card.is-placeholder { opacity: 0.6; }
.card.is-drafting {
  outline: var(--focus-width) solid var(--drafting);
  outline-offset: var(--focus-offset);
  animation: card-drafting-pulse var(--dur-ambient) var(--ease-standard) infinite;
}
@keyframes card-drafting-pulse {
  0%, 100% { box-shadow: var(--shadow-rest), 0 0 0 3px var(--drafting-soft); }
  50%      { box-shadow: var(--shadow-rest), 0 0 0 6px var(--drafting-soft); }
}
.card.is-pill-active {
  outline: var(--focus-width) solid var(--pill-active);
  outline-offset: var(--focus-offset);
}

/* Pills — user's draft selection (chip row) and transcript-inline */
.input-pill {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px 8px;
  border-radius: 12px;
  font-size: 11px;
  background: rgba(217, 163, 58, 0.15);
  border: 1px solid #d9a33a;
  color: #7a4f00;
  cursor: default;
}
.input-pill .x {
  opacity: 0.55;
  font-size: 12px;
  line-height: 1;
  cursor: pointer;
  user-select: none;
}
.input-pill .x:hover { opacity: 1; }

.inline-pill {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 1px 7px;
  border-radius: 10px;
  font-size: 11px;
  background: rgba(217, 163, 58, 0.16);
  border: 1px solid #d9a33a;
  color: #7a4f00;
  font-family: inherit;
  /* Buttons default to text-align: center via the UA stylesheet.
     Force left-align so wrapped pill titles read naturally instead
     of centering their second line. */
  text-align: left;
}
.inline-pill::before { content: '◉'; font-size: 10px; opacity: 0.8; }
/* Pills are rendered as <button> when clickable (see chat.js). Strip
   the default button chrome and add a subtle hover/focus signal so
   they read as the same chip as the read-only <span> variant. */
button.inline-pill {
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease-standard),
              border-color var(--dur-fast) var(--ease-standard);
}
button.inline-pill:hover {
  background: rgba(217, 163, 58, 0.28);
  border-color: #b8861f;
}
button.inline-pill:focus-visible {
  outline: var(--focus-width) solid var(--focus-ring);
  outline-offset: 2px;
}

/* Empty regions stay hidden during loading. The earlier "ghost
   placeholder" reveal during `.is-loading` was disorienting in practice —
   sections flashed in then collapsed when content arrived. Keep the canvas
   tight: only regions with at least one card are visible. */
#canvas-viewport.is-loading:has(.card) .region:not(:has(.card)) {
  display: none;
}

/* Vestigial ghost-region placeholder — never rendered under current rules
   (the region is display:none before this can apply) but left in for
   reference until the next style sweep. */
#canvas-viewport.is-loading:has(.card) .region:not(:has(.card)) .region-cards::after {
  content: '';
  display: block;
  width: 200px;
  height: 96px;
  border-radius: 6px;
  border-left: 3px solid var(--border-sec);
  background: linear-gradient(
    90deg,
    rgba(255,255,255,0.03) 0%,
    rgba(255,255,255,0.08) 50%,
    rgba(255,255,255,0.03) 100%
  );
  background-size: 200% 100%;
  animation: card-shimmer 1.8s ease-in-out infinite;
}

@keyframes card-shimmer {
  0%   { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

/* ─── Research card ──────────────────────────────────────────────── */

.card.is-research .card-header {
  align-items: center;
}

.card.is-research .research-trace {
  margin-top: 10px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.card.is-research .research-queries,
.card.is-research .research-sources {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.card.is-research .research-query {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 11px;
  color: var(--text-dim);
  font-style: italic;
}
.card.is-research .research-query::before {
  content: '›';
  color: var(--text-muted);
  font-style: normal;
  font-weight: 600;
}
.card.is-research .research-query.is-pending::after {
  content: '';
  width: 8px;
  height: 8px;
  border-radius: 50%;
  border: 1.5px solid rgba(28,32,36,0.12);
  border-top-color: var(--card-color, var(--accent-cyan));
  animation: research-spin 700ms linear infinite;
  margin-left: 4px;
}

@keyframes research-spin {
  to { transform: rotate(360deg); }
}

.card.is-research .research-source {
  display: flex;
  align-items: flex-start;
  gap: 6px;
  font-size: 11px;
  color: var(--text-body);
  line-height: 1.35;
}
.card.is-research .research-source .favicon {
  width: 14px;
  height: 14px;
  border-radius: 2px;
  flex-shrink: 0;
  margin-top: 1px;
}
.card.is-research .research-source .hostname {
  color: var(--text-dim);
  font-weight: 600;
  margin-right: 4px;
}
.card.is-research .research-source .snippet {
  color: var(--text-body);
}

.card.is-research .research-citations {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin-top: 10px;
  padding-top: 8px;
  border-top: 1px solid var(--border);
}

.card.is-research .citation-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 6px;
  border-radius: 10px;
  font-size: 10px;
  background: var(--paper);
  border: 1px solid var(--border);
  color: var(--text-dim);
}
.card.is-research .citation-chip .favicon {
  width: 10px;
  height: 10px;
  border-radius: 1px;
}

/* Research cards need more width for the trace (sources + citations).
   Matches Jason's ResearchLinksCard width (340px). */
.card.is-research {
  width: 340px;
}

/* ─── Upload card ────────────────────────────────────────────────── */

.card.is-upload .card-body {
  padding: 0;
  color: var(--text-body);
}

.card.is-upload .upload-image {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 4px;
  background: rgba(0, 0, 0, 0.25);
}

.card.is-upload .upload-preview {
  margin: 0;
  max-height: 160px;
  overflow: hidden;
  padding: 8px 10px;
  border-radius: 4px;
  background: rgba(0, 0, 0, 0.28);
  border: 1px solid var(--border);
  font-family: var(--font-mono);
  font-size: 11px;
  line-height: 1.5;
  color: var(--text-body);
  white-space: pre-wrap;
  word-break: break-word;
  position: relative;
}
.card.is-upload .upload-preview::after {
  content: '';
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 32px;
  background: linear-gradient(to bottom, transparent, var(--bg-card));
  pointer-events: none;
}

.card.is-upload .upload-meta {
  display: flex;
  justify-content: space-between;
  margin-top: 8px;
  padding-top: 8px;
  border-top: 1px solid var(--border);
  font-size: 10px;
  color: var(--text-muted);
  letter-spacing: 0.3px;
}
.card.is-upload .upload-meta .filename {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 140px;
}

/* Drop target: active while a file is being dragged over a region's grid. */
.region-cards.is-drop-target {
  outline: 2px dashed var(--accent, rgba(255, 215, 120, 0.6));
  outline-offset: 4px;
  background: rgba(255, 215, 120, 0.04);
}

/* Chat thinking indicator — Plan 9 */
.msg.msg-thinking { opacity: 0.75; }
.msg.msg-thinking .thinking-dots {
  display: inline-flex;
  gap: 4px;
  vertical-align: middle;
}
.msg.msg-thinking .thinking-dots span {
  width: 6px; height: 6px;
  border-radius: 3px;
  background: currentColor;
  opacity: 0.35;
  animation: thinking-pulse 1.2s infinite ease-in-out;
}
.msg.msg-thinking .thinking-dots span:nth-child(2) { animation-delay: 0.15s; }
.msg.msg-thinking .thinking-dots span:nth-child(3) { animation-delay: 0.3s; }
@keyframes thinking-pulse {
  0%, 70%, 100% { opacity: 0.3; transform: translateY(0); }
  35% { opacity: 1; transform: translateY(-2px); }
}

/* Chat message internal formatting — Plan 9 */
.msg .msg-body p { margin: 0 0 0.5em 0; }
.msg .msg-body p:last-child { margin-bottom: 0; }
.msg .msg-body ul,
.msg .msg-body ol { margin: 0.25em 0 0.5em 1.2em; padding: 0; }
.msg .msg-body li { margin: 0.125em 0; }
.msg .msg-body code {
  font-family: var(--font-mono);
  font-size: 0.92em;
  background: rgba(0, 0, 0, 0.06);
  padding: 0 0.25em;
  border-radius: 2px;
}
.msg.is-user .msg-body code { background: rgba(255, 255, 255, 0.18); }
.msg .msg-body strong { font-weight: 600; }
.msg .msg-body em { font-style: italic; }

/* Phase 2c.2 — Brief zone (inline tablet). Replaces the old #brief-modal
   from Plan 9. Renders inside the Brief zone of the four-zone canvas.
   Tablet device chrome (cream gradient frame, soft camera notch) with
   a TOC sidebar + reading body inside. */

.zone[data-zone="brief"] #brief-zone-content {
  position: absolute;
  inset: 60px 40px 40px;
}

.brief-tablet {
  position: relative;
  width: 100%;
  height: 100%;
  isolation: isolate;
}

.brief-meta {
  position: absolute;
  top: -36px;
  left: 4px;
  display: flex;
  align-items: center;
  gap: 12px;
  z-index: 2;
}
.brief-meta-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--wp-blue);
}
.brief-meta-title {
  font-family: var(--font-mono);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-1);
}

.brief-tablet-frame {
  position: absolute;
  inset: -20px;
  border-radius: 32px;
  background: linear-gradient(180deg, #f8f6f0 0%, #ece9e0 100%);
  box-shadow: 0 20px 40px rgba(28,32,36,0.10), 0 60px 140px rgba(28,32,36,0.20);
  border: 1px solid var(--card-edge-strong);
  pointer-events: none;
  z-index: 0;
}
.brief-tablet-notch {
  position: absolute;
  top: -10px;
  left: 50%;
  transform: translateX(-50%);
  width: 60px;
  height: 5px;
  border-radius: 3px;
  background: rgba(0,0,0,0.18);
  z-index: 1;
}

.brief-tablet-inner {
  position: absolute;
  inset: 0;
  background: var(--paper);
  border-radius: 18px;
  overflow: hidden;
  display: grid;
  grid-template-columns: 240px 1fr;
  /* 1fr row binds children to the inner's full height so the
     brief-body can scroll in place when content overflows.
     Without this, the row sizes to auto and the body grows past
     the device frame instead of scrolling internally. */
  grid-template-rows: 1fr;
  border: 1px solid var(--card-edge-strong);
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.6);
  z-index: 1;
}

.brief-toc {
  background: var(--region-fill);
  border-right: 1px solid var(--card-edge);
  padding: 54px 0 40px 30px;
  font-family: var(--font-body);
  overflow-y: auto;
  min-height: 0;
}
.brief-toc-eyebrow {
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin-bottom: 18px;
}
.brief-toc-list {
  list-style: none;
  padding: 0;
  margin: 0;
}
/* TOC items derive live from the brief body's H1/H2 headings
   (see brief-zone.js → buildTOC). H1 = top-level entry, H2 =
   indented. Click scrolls the body to that heading. */
.brief-toc-item {
  position: relative;
  padding: 7px 14px;
  margin-left: -2px;
  border-left: 2px solid transparent;
  font-size: 13px;
  line-height: 1.35;
  color: var(--ink-2);
  cursor: pointer;
  transition: color var(--dur-fast) var(--ease-standard),
              border-left-color var(--dur-fast) var(--ease-standard);
}
.brief-toc-item[data-level="1"] {
  font-weight: 600;
  color: var(--ink-1);
  margin-top: 8px;
}
.brief-toc-item[data-level="1"]:first-child { margin-top: 0; }
.brief-toc-item[data-level="2"] {
  padding-left: 28px;
  font-size: 12.5px;
}
.brief-toc-item:hover {
  color: var(--ink-0);
  border-left-color: var(--card-edge-strong);
}
.brief-toc-item.is-active {
  border-left-color: var(--wp-blue);
  color: var(--wp-blue-deep);
  font-weight: 600;
}

.brief-body {
  /* position: relative makes brief-body the offsetParent of its
     heading children, so target.offsetTop in the TOC click handler
     is the heading's y within the body — exactly what we want to
     pass to bodyEl.scrollTo. */
  position: relative;
  padding: 54px 56px;
  overflow-y: auto;
  /* min-height: 0 lets this grid child shrink below content size,
     which is what makes overflow-y: auto actually engage.
     Without it the grid expands the row to fit content and scroll
     never triggers — classic flex/grid + overflow trap. */
  min-height: 0;
  font-family: var(--font-body);
  color: var(--ink-1);
  line-height: 1.55;
  font-size: 14px;
}
.brief-body h1 {
  font-family: var(--font-display);
  font-size: 32px;
  font-weight: 400;
  letter-spacing: -0.02em;
  line-height: 1.1;
  margin: 0 0 18px;
  color: var(--ink-0);
  /* Breathing room when the TOC click jumps to this heading. */
  scroll-margin-top: 36px;
}
.brief-body h2 {
  font-family: var(--font-display);
  font-size: 22px;
  font-weight: 400;
  letter-spacing: -0.01em;
  line-height: 1.2;
  margin: 28px 0 10px;
  color: var(--ink-0);
  scroll-margin-top: 36px;
}
.brief-body h3 {
  font-size: 15px;
  font-weight: 600;
  margin: 20px 0 6px;
  color: var(--ink-1);
}
.brief-body p { margin: 0 0 14px; }
.brief-body ul,
.brief-body ol { margin: 8px 0 14px 22px; }
.brief-body hr {
  border: 0;
  border-top: 1px solid var(--card-edge);
  margin: 28px 0;
}
.brief-body code {
  font-family: var(--font-mono);
  background: var(--region-fill);
  padding: 0 4px;
  border-radius: 3px;
}
.brief-body a {
  color: var(--wp-blue);
  text-decoration: underline;
}
.brief-placeholder {
  font-style: italic;
  color: var(--ink-3);
}

/* Plan 10 — upload toast */
#upload-toast-container {
  position: absolute;
  left: 50%;
  bottom: 24px;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column-reverse;
  gap: 8px;
  z-index: 8000;
  pointer-events: none;
}

.upload-toast {
  display: inline-flex;
  align-items: center;
  gap: 14px;
  padding: 14px 22px;
  background: var(--wp-blue);
  color: #ffffff;
  font-size: 16px;
  font-weight: 500;
  border-radius: 8px;
  box-shadow: var(--shadow-pin);
  opacity: 0;
  transform: translateY(12px);
  transition: opacity 200ms ease-out, transform 200ms ease-out;
  pointer-events: auto;
  max-width: 480px;
}
.upload-toast.is-visible {
  opacity: 1;
  transform: translateY(0);
}
.upload-toast.is-hiding {
  opacity: 0;
  transform: translateY(8px);
}
.upload-toast.is-error {
  background: #d97843;
  color: #ffffff;
}

.upload-toast-icon {
  font-size: 22px;
  line-height: 1;
}

.upload-toast-filename {
  font-weight: 600;
  letter-spacing: 0.1px;
}

.upload-toast-spinner {
  width: 16px;
  height: 16px;
  border-radius: 8px;
  border: 2px solid rgba(255, 255, 255, 0.35);
  border-top-color: #ffffff;
  animation: upload-toast-spin 700ms linear infinite;
}

@keyframes upload-toast-spin {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}

/* Plan 10 — upload popover */
.upload-popover-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(10, 12, 16, 0.45);
  z-index: 8500;
  opacity: 0;
  transition: opacity 150ms ease-out;
}
.upload-popover-backdrop.is-visible { opacity: 1; }

.upload-popover {
  position: fixed;
  z-index: 8600;
  min-width: 320px;
  max-width: 380px;
  padding: 16px;
  background: var(--bg-card);
  color: var(--text);
  border: 1px solid var(--border-strong);
  border-radius: 10px;
  box-shadow: var(--shadow-lift);
  display: flex;
  flex-direction: column;
  gap: 12px;
  opacity: 0;
  transform: scale(0.97);
  transition: opacity 150ms ease-out, transform 150ms ease-out;
}
.upload-popover.is-visible {
  opacity: 1;
  transform: scale(1);
}

.upload-popover-file {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  background: var(--paper);
  border-radius: 6px;
}

.upload-popover-icon { font-size: 20px; line-height: 1; }
.upload-popover-filename { font-size: 13px; font-weight: 500; }

.upload-popover-question {
  font-size: 13px;
  color: var(--text-body);
  line-height: 1.4;
}

.upload-popover-actions {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.upload-popover-actions button {
  background: transparent;
  color: var(--text);
  border: 1px solid var(--border-strong);
  border-radius: 6px;
  padding: 9px 12px;
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
  text-align: left;
}
.upload-popover-actions button:hover { background: var(--paper); }
.upload-popover-actions .upload-popover-attach {
  background: var(--wp-blue);
  color: #ffffff;
  border-color: var(--wp-blue);
}
.upload-popover-actions .upload-popover-attach:hover {
  background: var(--wp-blue-deep);
  border-color: var(--wp-blue-deep);
}

/* ─── Onboarding modal keyframes ─────────────────────────────────── */

@keyframes ob-fade-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}
@keyframes ob-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50%       { opacity: 0.45; transform: scale(0.85); }
}
@keyframes ob-shimmer {
  0%   { background-position: -200px 0; }
  100% { background-position: 400px 0; }
}
@keyframes ob-stack-slide-up {
  0%   { transform: translate(-50%, -50%) rotate(-1deg); opacity: 1; }
  32%  { transform: translate(-50%, -50%) rotate(-1deg); opacity: 1; }
  80%  { transform: translate(-50%, -210%) rotate(-1deg); opacity: 0.4; }
  100% { transform: translate(-50%, -210%) rotate(-1deg); opacity: 0; }
}
@keyframes ob-zone-in {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: translateY(0); }
}
@keyframes ob-eyebrow-out {
  0%, 32% { opacity: 1; }
  50%, 100% { opacity: 0; }
}
@keyframes ob-eyebrow-in {
  0%, 50% { opacity: 0; }
  72%, 100% { opacity: 1; }
}
@keyframes ob-replay-in {
  0%, 80% { opacity: 0; transform: translateY(4px); }
  100%    { opacity: 1; transform: translateY(0); }
}
@keyframes ob-bubble-in {
  from { opacity: 0; transform: translateY(8px) scale(0.96); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}

/* ─── Onboarding overlay wrapper ─────────────────────────────────── */
#ob-overlay {
  position: fixed;
  inset: 0;
  z-index: 9000;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(246, 246, 246, 0.30);
  backdrop-filter: blur(10px) saturate(120%);
  -webkit-backdrop-filter: blur(10px) saturate(120%);
  transition: opacity 300ms ease;
}

/* Plan 10 — supporter badge on cards */
.card .card-badge {
  position: absolute;
  top: 8px;
  right: 10px;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.4px;
  padding: 2px 7px;
  border-radius: 9px;
  background: rgba(255, 255, 255, 0.08);
  color: rgba(255, 255, 255, 0.85);
  border: 1px solid rgba(255, 255, 255, 0.12);
  pointer-events: none;
  user-select: none;
}

/* ============================================================
   Narrative zone — BeatCard column (Compose mode)
   ============================================================
   Visual spec ported from Jason's design-system/narrative-handoff/
   (narrative.jsx + cards.jsx). Vertical column of BeatCards in beat
   order, top-to-bottom, against the giant display-font zone label. The
   column is empty until the AI emits json:narrative.compose; the
   reveal animation lights it up at the end of Refine mode.
*/

/* Zone container — center the column and give it room to breathe.
   Overrides the .zone-content default padding for the narrative zone
   only; other zones (Brief tablet, Discovery grid, Design surface)
   keep their existing layouts. */
.zone[data-zone="narrative"] .zone-content {
  padding: 80px 0 60px;
  display: flex;
  justify-content: center;
}

.narrative-zone {
  width: 600px;             /* breathing room around the 480px cards */
  display: flex;
  flex-direction: column;
  align-items: center;
  /* Surface CSS vars for the BeatCard chip so it picks up the
     narrative hue without touching the per-card style. */
  --card-color: var(--hue-narrative);
  --card-color-2: var(--hue-narrative-2);
}

.narrative-beats {
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 100%;
}

/* ─── BeatCard ────────────────────────────────────────────────── */
.beat-card {
  width: 480px;
  padding: 0;
  position: relative;
  background: var(--card);
  color: var(--ink-1);
  border: 1px solid var(--card-edge);
  border-radius: 14px;
  box-shadow: 0 1px 2px rgba(26, 25, 25, 0.04);
  overflow: hidden;
  transition: box-shadow 200ms var(--ease-standard);
}

.beat-card-header {
  padding: 14px 20px 14px;
}

.beat-card-meta {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 8px;
  min-height: 18px;          /* preserve header height when no status */
}

.beat-card-seq {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 500;
  color: var(--ink-3);
  letter-spacing: 0.14em;
  flex-shrink: 0;
}

/* Per-card status indicator removed — cards no longer surface
   drafting / done state. The narrative's locked-ness is a system
   concept, not a card-level visual. */

.beat-card-title {
  font-family: var(--font-body);
  font-size: 17px;
  font-weight: 600;
  letter-spacing: -0.01em;
  line-height: 1.2;
  color: var(--ink-1);
  margin: 0;
  text-wrap: pretty;
}

.beat-card-hairline {
  height: 1px;
  background: var(--card-edge);
  margin: 0 20px;
}

.beat-card-body {
  padding: 14px 20px 16px;
}

.beat-card-why {
  font-size: 13px;
  line-height: 1.5;
  color: var(--ink-2);
  margin: 0;
  text-wrap: pretty;
}

/* ─── Draft Copy block ──────────────────────────────────────────
   Recoleta-set sample copy with a 2-line preview clamp. Show More
   releases the clamp; the card grows, the column below shifts
   down. The push behavior is handled by the .narrative-beats
   flex column — when this card grows, the next BeatConnector and
   subsequent .beat-card simply re-flow.
*/
.beat-card-draft {
  margin-top: 14px;
  padding: 12px 14px;
  background: var(--paper);
  border-radius: 8px;
}

.beat-card-draft-eyebrow {
  font-family: var(--font-mono);
  font-size: 9px;
  font-weight: 500;
  letter-spacing: 0.14em;
  color: var(--wp-blue-deep);
  text-transform: uppercase;
  margin-bottom: 6px;
}

.beat-card-draft-text {
  font-family: var(--font-display);
  font-size: 16px;
  line-height: 1.35;
  letter-spacing: -0.01em;
  color: var(--ink-1);
  text-wrap: pretty;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.beat-card.is-expanded .beat-card-draft-text {
  display: block;
  -webkit-line-clamp: unset;
  overflow: visible;
}

.beat-card-draft-toggle {
  appearance: none;
  border: none;
  background: transparent;
  padding: 0;
  margin-top: 8px;
  cursor: pointer;
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--wp-blue-deep);
}

/* ─── Connector ─────────────────────────────────────────────────
   Persistent slot occupant between adjacent beats. Always sits in
   the gap. When cards reflow (expand / reorder / add / remove),
   connectors ride along with the cards above and below them.
*/
.beat-connector {
  width: 1px;
  height: 36px;
  position: relative;
  background: var(--card-edge-strong);
  margin: 0 auto;
}

.beat-connector-dot {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--card-edge-strong);
}

/* ─── End-of-page marker ────────────────────────────────────────
   Closes the column with a small dot + 'end of page' mono label.
   Per Jason's design — the visual full-stop on the story.
*/
.narrative-end-marker {
  margin-top: 32px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
}

.narrative-end-marker-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--ink-3);
}

.narrative-end-marker-label {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-3);
}

/* ─── Stack-descent reveal animation ────────────────────────────
   At end of Refine mode the full beat set arrives as a stack
   lifted above card 01's destination. Stack order back-to-front:
   01, 02, 03, …, 07 — card 07 is what's visible on top. The
   whole stack descends at constant velocity. Card 01 (bottom of
   the stack) hits its destination first and is "left behind."
   The remaining stack continues down, depositing card 02 at
   position 2, then 03 at position 3, and so on until card 07
   lands at position 7.

   Per-card `--card-start-y` and `--card-duration` are set inline
   by narrative-zone.js: distances scale linearly with target
   position, durations scale with distances, so all cards share
   the same velocity through the entire descent.
*/

/* Stage 0 — instant: card sits at the lifted start position.
   No transition; this is the paint that establishes the deck. */
.beat-card.is-descending {
  transform: translateY(var(--card-start-y, 0px));
  transition: none;
  /* Deeper shadow during descent so the layered cards read as a
     deck rather than a single floating card. Cleanup restores the
     normal box-shadow on each card. */
  box-shadow:
    0 1px 0 rgba(26, 25, 25, 0.08),
    0 8px 22px rgba(26, 25, 25, 0.14),
    0 1px 2px rgba(26, 25, 25, 0.04);
}

/* Stage 1 — descent. Linear easing keeps the stack locked together
   at a single velocity; per-card duration determines stop time. */
.beat-card.is-descending.is-descending-go {
  transform: translateY(0);
  transition: transform var(--card-duration, 1500ms) linear;
}

/* Connectors + end-marker invisible during descent; they fade in
   as the card on the downstream side of each connector lands.
   Base rule defines the fade-in transition for when is-deal-pending
   is removed; the .is-deal-pending rule hides instantly with no
   transition so the descent doesn't show ghost connectors. */
.beat-connector,
.narrative-end-marker {
  transition: opacity 350ms ease-out;
}
.beat-connector.is-deal-pending,
.narrative-end-marker.is-deal-pending {
  opacity: 0;
  transition: opacity 0s;
}

/* ─── Regen animation ───────────────────────────────────────────
   When the AI re-emits narrative.compose after the initial reveal
   (any substantive change during Compose mode), the current beat
   column fades out together, then the reveal animation runs
   against the new beats. Total ~3.5s — earned because something
   meaningful just changed.
*/
.narrative-beats.is-fading-out > * {
  animation: beat-fade-out 400ms var(--ease-exit) forwards;
}

@keyframes beat-fade-out {
  0%   { opacity: 1; transform: scale(1); }
  100% { opacity: 0; transform: scale(0.98); }
}

/* ─── Content edit glow ─────────────────────────────────────────
   When the AI rewrites one or more beats' content during Compose
   mode, the client diffs against the current beats and runs this
   in-place glow + content-swap animation on each changed card.
   All changed cards animate simultaneously — one coordinated
   moment, not a sequence. The text swap happens at peak glow
   (~1313ms) so the change reads as "the card just updated" rather
   than a visible re-render. ~2625ms total — paced to match the
   canvas swoop motion's slower, more luxurious feel. */
.beat-card.is-editing {
  animation: beat-edit-glow 2625ms cubic-bezier(0.4, 0, 0.2, 1) forwards;
}

@keyframes beat-edit-glow {
  0% {
    transform: scale(1);
    box-shadow: 0 1px 2px rgba(26, 25, 25, 0.04);
  }
  50% {
    transform: scale(1.025);
    box-shadow:
      0 0 0 4px var(--wp-blue-soft),
      0 0 32px rgba(56, 88, 233, 0.32),
      0 6px 16px rgba(26, 25, 25, 0.10);
  }
  100% {
    transform: scale(1);
    box-shadow: 0 1px 2px rgba(26, 25, 25, 0.04);
  }
}

/* ═══════════════════════════════════════════════════════════════════
 * App Bar — shared component (workspace + project modes)
 * ═══════════════════════════════════════════════════════════════════ */

.app-bar {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 56px;
  background: var(--card);
  border-bottom: 1px solid var(--card-edge);
  display: flex;
  align-items: center;
  padding: 0 20px;
  gap: 16px;
  z-index: 50;
}
.app-bar-brand {
  display: flex;
  align-items: center;
  gap: 14px;
  min-width: 0;
  text-decoration: none;
  color: inherit;
  cursor: pointer;
}
.app-bar-lockup {
  display: block;
  height: 26px;
  width: auto;
  flex-shrink: 0;
}
.app-bar-spacer { flex: 1; }
.app-bar-user {
  display: flex;
  align-items: center;
}

/* ═══════════════════════════════════════════════════════════════════
 * Workspace Nav — shared component (workspace + project modes)
 * ═══════════════════════════════════════════════════════════════════ */

/* Panel (expanded) */
.workspace-nav-panel {
  position: fixed;
  top: 70px;
  bottom: 14px;
  left: 14px;
  width: 252px;
  z-index: 40;
  display: flex;
  flex-direction: column;
  background: var(--card);
  border: 1px solid var(--card-edge);
  border-radius: 20px;
  box-shadow:
    0 4px 14px rgba(26,25,25,0.06),
    0 24px 60px rgba(26,25,25,0.10);
  overflow: hidden;
}

/* Header strip */
.workspace-nav-header {
  display: flex;
  align-items: center;
}
/* Toggle button — sized + positioned to match .workspace-nav-launcher
   exactly. Sits at the panel's top-left so when the panel covers the
   launcher position, the user clicks the same viewport coords. The
   -1px top/left offset cancels the panel's 1px border so the hamburger
   icon doesn't visually shift on toggle. */
.workspace-nav-close {
  position: relative;
  top: -1px;
  left: -1px;
  width: 44px;
  height: 44px;
  border-radius: 20px;
  border: none;
  background: transparent;
  color: var(--ink-2);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background 140ms;
}
.workspace-nav-close:hover { background: var(--paper); color: var(--ink-1); }

/* Nav list */
.workspace-nav-list {
  padding: 6px 8px 18px;
  flex: 1;
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: var(--card-edge-strong) transparent;
}
.workspace-nav-list::-webkit-scrollbar { width: 6px; }
.workspace-nav-list::-webkit-scrollbar-thumb {
  background: var(--card-edge-strong);
  border-radius: 3px;
}

/* Nav row */
.workspace-nav-row {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 8px 10px;
  border-radius: 8px;
  border: none;
  background: transparent;
  color: var(--ink-2);
  font: inherit;
  cursor: pointer;
  text-align: left;
  transition: background 140ms, color 140ms;
}
.workspace-nav-row:hover { background: var(--paper); color: var(--ink-1); }
.workspace-nav-row.is-active {
  background: var(--wp-blue-soft);
  color: var(--wp-blue-deep);
}
.workspace-nav-row.is-active .workspace-nav-label { font-weight: 600; }

.workspace-nav-icon {
  display: flex;
  align-items: center;
  flex-shrink: 0;
}

.workspace-nav-label {
  flex: 1;
  font-size: 13.5px;
  letter-spacing: -0.005em;
}

/* Chevron */
.workspace-nav-chevron-wrap {
  display: flex;
  align-items: center;
}
.workspace-nav-chevron-wrap .workspace-nav-chevron {
  transition: transform 200ms cubic-bezier(.2,.7,.2,1);
}
.workspace-nav-chevron-wrap.is-expanded .workspace-nav-chevron {
  transform: rotate(90deg);
}

/* Depth levels */
.workspace-nav-row.workspace-nav-d0 .workspace-nav-label { font-size: 14px; font-weight: 500; }
.workspace-nav-row.workspace-nav-d1 { padding-left: 32px; }
.workspace-nav-row.workspace-nav-d1 .workspace-nav-label { font-size: 13px; }
.workspace-nav-row.workspace-nav-d2 { padding-left: 48px; }
.workspace-nav-row.workspace-nav-d2 .workspace-nav-label {
  font-size: 12px;
  font-family: var(--font-mono);
  font-weight: 400;
  letter-spacing: 0.02em;
  color: var(--ink-3);
}
.workspace-nav-row.workspace-nav-d2:hover .workspace-nav-label { color: var(--ink-1); }
.workspace-nav-row.workspace-nav-d2.is-active .workspace-nav-label {
  color: var(--wp-blue-deep);
  font-weight: 500;
}

.workspace-nav-submenu {
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: 2px 0 6px;
}

/* Footer */
.workspace-nav-footer {
  padding: 12px 16px;
  border-top: 1px solid var(--card-edge);
  display: flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.06em;
  color: var(--ink-3);
}
.workspace-nav-live-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: #33F078;
}

/* Launcher (collapsed) */
.workspace-nav-launcher {
  position: fixed;
  top: 70px;
  left: 14px;
  width: 44px;
  height: 44px;
  border-radius: 20px;
  background: var(--card);
  border: 1px solid var(--card-edge);
  color: var(--ink-2);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 40;
  box-shadow:
    0 1px 2px rgba(26,25,25,0.04),
    0 4px 12px rgba(26,25,25,0.08);
  transition: transform 140ms, box-shadow 140ms;
}
.workspace-nav-launcher:hover {
  transform: translateY(-1px);
  box-shadow:
    0 2px 4px rgba(26,25,25,0.08),
    0 8px 18px rgba(26,25,25,0.10);
}

/* ═══════════════════════════════════════════════════════════════════
 * Workspace shell — relationship-level workspace layout
 * ═══════════════════════════════════════════════════════════════════ */

.workspace-shell {
  /* Font tokens now live in tokens.css :root globally — Recoleta / Inter /
     DM Mono apply across both the canvas and the workspace. The per-class
     override that used to live here is no longer needed. */
  width: 100%;
  min-height: 100%;
  background: linear-gradient(180deg, #efeeec 0%, #e6e4df 100%);
  background-attachment: fixed;
  color: var(--ink-1);
  font-family: var(--font-body);
  position: relative;
  overflow-x: hidden;
}
.workspace-shell::before {
  content: '';
  position: fixed;
  inset: 0;
  background-image: radial-gradient(rgba(120,110,90,0.07) 1px, transparent 1px);
  background-size: 22px 22px;
  opacity: 0.5;
  pointer-events: none;
}

/* Content area — independently scrollable, shifts right when nav is open.
   html/body overflow:hidden is set globally for the project canvas, so the
   workspace content must manage its own scroll within a fixed container. */
.workspace-content {
  position: fixed;
  top: 56px;
  left: 0;
  right: 0;
  bottom: 0;
  overflow-y: auto;
  z-index: 2;
  transition: left 280ms cubic-bezier(.2,.7,.2,1);
}
.workspace-shell.is-nav-open .workspace-content {
  left: 280px;
}

/* ─── My Business page ─────────────────────────────────────────── */

.workspace-page {
  max-width: 920px;
  margin: 0 auto;
  padding: 56px 60px 96px;
  position: relative;
  z-index: 2;
}

.workspace-page-header {
  padding-bottom: 48px;
  border-bottom: 1px solid var(--card-edge);
  margin-bottom: 56px;
}
.workspace-page-eyebrow {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-3);
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 18px;
}
.workspace-page-dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--wp-blue);
}
.workspace-page-title {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 88px;
  line-height: 0.96;
  letter-spacing: -0.02em;
  margin: 0 0 22px;
}
.workspace-page-title em {
  font-style: normal;
  color: var(--wp-blue);
}
.workspace-page-sub {
  font-size: 18px;
  line-height: 1.55;
  color: var(--ink-2);
  margin: 0;
  max-width: 620px;
}

/* Module card */
.workspace-mod {
  background: var(--card);
  border: 1px solid var(--card-edge);
  border-radius: 14px;
  box-shadow: var(--shadow-rest, 0 1px 2px rgba(28,32,36,0.04), 0 2px 6px rgba(28,32,36,0.06));
  padding: 36px 40px;
  margin-bottom: 24px;
  scroll-margin-top: 100px;
}
.workspace-mod-eyebrow {
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-2);
  margin-bottom: 14px;
}
.workspace-mod-dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  flex-shrink: 0;
}
.workspace-mod-rule {
  flex: 1;
  height: 1px;
  background: var(--card-edge);
}
.workspace-mod-title {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 38px;
  line-height: 1.05;
  letter-spacing: -0.02em;
  margin: 0 0 24px;
}
.workspace-mod-standfirst {
  font-family: var(--font-display);
  font-size: 22px;
  line-height: 1.32;
  letter-spacing: -0.01em;
  color: var(--ink-2);
  margin: 0 0 32px;
  max-width: 600px;
}

/* Detail key/value rows */
.workspace-details-grid {
  display: flex;
  flex-direction: column;
}
.workspace-detail {
  display: grid;
  grid-template-columns: 200px 1fr;
  gap: 24px;
  padding: 18px 0;
  border-bottom: 1px solid var(--card-edge);
  scroll-margin-top: 100px;
}
.workspace-detail:last-child { border-bottom: none; padding-bottom: 0; }
.workspace-detail-label {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-3);
  align-self: start;
  padding-top: 2px;
}
.workspace-detail-value {
  font-size: 16px;
  line-height: 1.55;
  color: var(--ink-1);
}

/* Audience cards */
.workspace-audience-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px;
}
.workspace-audience-card {
  background: var(--paper);
  border: 1px solid var(--card-edge);
  border-radius: 10px;
  padding: 18px 20px;
  position: relative;
}
.workspace-audience-dot {
  position: absolute;
  top: 18px;
  left: 20px;
  width: 9px;
  height: 9px;
  border-radius: 50%;
}
.workspace-audience-name {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 22px;
  letter-spacing: -0.012em;
  margin: 0 0 4px;
  padding-left: 18px;
}
.workspace-audience-size {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-3);
  padding-left: 18px;
  margin-bottom: 10px;
}
.workspace-audience-why {
  font-size: 14px;
  line-height: 1.5;
  color: var(--ink-2);
  margin: 0;
}

/* Team list */
.workspace-team {
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.workspace-team-row {
  display: flex;
  align-items: center;
  gap: 14px;
}
.workspace-team-avatar {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  color: white;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 11px;
  font-weight: 700;
  flex-shrink: 0;
}
.workspace-team-name {
  font-family: var(--font-display);
  font-size: 18px;
  letter-spacing: -0.01em;
  color: var(--ink-1);
}
.workspace-team-role {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin-top: 2px;
}

/* Competitor groups */
.workspace-comp-group {
  margin-bottom: 28px;
  scroll-margin-top: 100px;
}
.workspace-comp-group:last-of-type { margin-bottom: 0; }
.workspace-comp-group-label {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin: 0 0 12px;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--card-edge);
}
.workspace-comp-list {
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.workspace-comp-row {
  display: grid;
  grid-template-columns: 200px 160px 1fr;
  gap: 20px;
  align-items: baseline;
}
.workspace-comp-name {
  font-family: var(--font-display);
  font-size: 19px;
  letter-spacing: -0.01em;
  color: var(--ink-1);
}
.workspace-comp-loc {
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: 0.1em;
  color: var(--ink-3);
}
.workspace-comp-note {
  font-size: 14px;
  line-height: 1.5;
  color: var(--ink-2);
}

/* Brief — inline document */
.workspace-brief-eyebrow {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin-bottom: 16px;
}
.workspace-brief-doctitle {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 60px;
  line-height: 1.0;
  letter-spacing: -0.022em;
  margin: 0 0 22px;
  color: var(--ink-1);
}
.workspace-brief-doctitle em {
  font-style: normal;
  color: var(--wp-blue);
}
.workspace-brief-standfirst {
  font-size: 15px;
  line-height: 1.55;
  color: var(--ink-2);
  margin: 0;
  max-width: 620px;
}
.workspace-brief-rule {
  height: 1px;
  background: var(--card-edge);
  margin: 36px 0 32px;
}
.workspace-brief-chapter {
  margin-bottom: 36px;
  scroll-margin-top: 100px;
}
.workspace-brief-chapter:last-of-type { margin-bottom: 0; }
.workspace-brief-chapter-head {
  display: flex;
  align-items: baseline;
  gap: 14px;
  margin-bottom: 10px;
}
.workspace-brief-num {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.16em;
  color: var(--ink-3);
}
.workspace-brief-chapter-title {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 30px;
  line-height: 1.05;
  letter-spacing: -0.015em;
  margin: 0;
  color: var(--ink-1);
}
.workspace-brief-lead {
  font-family: var(--font-display);
  font-size: 18px;
  line-height: 1.4;
  letter-spacing: -0.005em;
  color: var(--ink-1);
  margin: 0 0 12px;
}
.workspace-brief-body {
  font-size: 14px;
  line-height: 1.62;
  color: var(--ink-2);
  margin: 0;
  max-width: 580px;
}

/* ═══════════════════════════════════════════════════════════════════
 * Projects page
 * ═══════════════════════════════════════════════════════════════════ */

.workspace-projects-page {
  max-width: 1320px;
}

/* Page header — flex row with title left, stats right */
.workspace-pr-header {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 32px;
  margin-bottom: 48px;
  padding-bottom: 20px;
  border-bottom: 1px solid var(--card-edge);
}
.workspace-pr-header-meta {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-3);
  text-align: right;
  white-space: nowrap;
}
.workspace-pr-header-meta strong {
  display: block;
  font-weight: 400;
  color: var(--ink-1);
  font-size: 24px;
  font-family: var(--font-display);
  text-transform: none;
  letter-spacing: -0.005em;
  margin-bottom: 2px;
}

/* Section divider (In flight / Complete) */
.workspace-pr-section {
  display: flex;
  align-items: baseline;
  gap: 14px;
  margin: 32px 0 20px;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-2);
}
.workspace-pr-section-rule {
  flex: 1;
  height: 1px;
  background: var(--card-edge);
}
.workspace-pr-section-count { color: var(--ink-4); font-weight: 500; }

/* Grid */
.workspace-pr-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 20px;
}

/* Project card */
.workspace-pr-card {
  background: var(--card);
  border: 1px solid var(--card-edge);
  border-radius: 14px;
  box-shadow: var(--shadow-rest, 0 1px 2px rgba(28,32,36,0.04), 0 2px 6px rgba(28,32,36,0.06));
  padding: 20px 22px 22px;
  display: flex;
  flex-direction: column;
  cursor: pointer;
  transition: box-shadow 200ms cubic-bezier(.2,.7,.2,1),
              transform 200ms cubic-bezier(.2,.7,.2,1),
              border-color 200ms;
  position: relative;
}
.workspace-pr-card:hover {
  box-shadow: var(--shadow-pin, 0 2px 4px rgba(28,32,36,0.08), 0 8px 18px rgba(28,32,36,0.10));
  transform: translateY(-2px);
  border-color: var(--card-edge-strong);
}
.workspace-pr-card:focus-visible {
  outline: none;
  box-shadow:
    0 0 0 2px var(--focus-ring, var(--wp-blue)),
    0 0 0 6px var(--focus-ring-soft, rgba(56,88,233,0.20));
}

/* Preview thumbnail area */
.workspace-pr-preview {
  position: relative;
  width: 100%;
  aspect-ratio: 5/3;
  background: linear-gradient(180deg, #efeeec 0%, #e6e4df 100%);
  border-radius: 10px;
  border: 1px solid var(--card-edge);
  overflow: hidden;
  margin-bottom: 16px;
}
.workspace-pr-preview svg { display: block; width: 100%; height: 100%; }

/* Status pill */
.workspace-pr-status {
  position: absolute;
  top: 8px;
  right: 8px;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px 8px 3px 7px;
  border-radius: 999px;
  font-family: var(--font-mono);
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  background: rgba(255, 255, 255, 0.94);
  border: 1px solid var(--card-edge);
  color: var(--ink-2);
  backdrop-filter: blur(6px);
}
.workspace-pr-status-dot {
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--ink-4);
}
.workspace-pr-status.is-drafting .workspace-pr-status-dot {
  background: var(--wp-blue);
  animation: workspace-pr-pulse 1.4s ease-in-out infinite;
}
.workspace-pr-status.is-active .workspace-pr-status-dot { background: #33F078; }
.workspace-pr-status.is-drafting { color: var(--wp-blue-deep); }
.workspace-pr-status.is-complete {
  color: var(--ink-2);
  background: rgba(255, 255, 255, 0.96);
}
.workspace-pr-status.is-complete svg { color: var(--wp-blue-deep); }
@keyframes workspace-pr-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50%      { opacity: 0.45; transform: scale(0.7); }
}

/* Card body */
.workspace-pr-card-section {
  display: flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-mono);
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin-bottom: 8px;
}
.workspace-pr-card-section-dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  flex-shrink: 0;
}
.workspace-pr-card-title {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 24px;
  line-height: 1.15;
  letter-spacing: -0.012em;
  color: var(--ink-1);
  margin: 0 0 16px;
}
.workspace-pr-card-meta {
  margin-top: auto;
  padding-top: 14px;
  border-top: 1px solid var(--card-edge);
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.1em;
  color: var(--ink-3);
}
.workspace-pr-card-meta-edited { letter-spacing: 0.06em; }
.workspace-pr-card-meta-counts {
  display: flex;
  align-items: center;
  gap: 6px;
}
.workspace-pr-meta-count { color: var(--ink-2); font-weight: 500; }
.workspace-pr-meta-dot { color: var(--ink-4); }

/* Demo-only cards — hover works, but no click action */
.workspace-pr-card.is-demo-only { cursor: default; }

/* Coming-soon notice — centered overlay card */
.workspace-coming-soon {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 100;
  width: 340px;
  background: var(--card);
  border: 1px solid var(--card-edge);
  border-radius: 20px;
  box-shadow: 0 8px 24px rgba(26,25,25,0.10), 0 32px 64px rgba(26,25,25,0.14);
  overflow: hidden;
  animation: workspace-toast-in 320ms cubic-bezier(.16,1,.3,1);
}
.workspace-coming-soon.is-leaving {
  opacity: 0;
  transform: translate(-50%, -50%) scale(0.96);
  transition: opacity 300ms, transform 300ms;
}

/* Band — top label strip */
.workspace-coming-soon-band {
  padding: 10px 32px;
  background: var(--wp-blue-soft, #eff2ff);
  border-bottom: 1px solid var(--card-edge);
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--wp-blue-deep, #1d35b4);
}

/* Body — shares the same horizontal padding as the band */
.workspace-coming-soon-body {
  padding: 28px 32px 32px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 20px;
}
.workspace-coming-soon-mark {
  width: 32px;
  height: 32px;
  flex-shrink: 0;
}
.workspace-coming-soon-text {
  font-family: var(--font-display);
  font-size: 20px;
  line-height: 1.25;
  letter-spacing: -0.01em;
  color: var(--ink-1);
  text-align: center;
}
.workspace-coming-soon-link {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.04em;
  color: var(--wp-blue);
  text-decoration: none;
  transition: color 140ms;
}
.workspace-coming-soon-link:hover {
  color: var(--wp-blue-deep);
}

@keyframes workspace-toast-in {
  from { opacity: 0; transform: translate(-50%, -50%) scale(0.94); }
  to   { opacity: 1; transform: translate(-50%, -50%) scale(1); }
}

/* ═══════════════════════════════════════════════════════════════════
 * Tutorial Card — internal tester orientation
 * ═══════════════════════════════════════════════════════════════════ */

.tutorial-card {
  position: fixed;
  top: 72px;
  right: 18px;
  width: 296px;
  background: var(--card);
  border: 1px solid var(--card-edge);
  border-radius: 14px;
  box-shadow:
    0 1px 2px rgba(28,32,36,0.04),
    0 8px 24px rgba(28,32,36,0.10),
    0 24px 60px rgba(28,32,36,0.08);
  overflow: hidden;
  z-index: 60;
}

.tutorial-card-band {
  padding: 10px 20px;
  background: var(--wp-blue);
  color: #ffffff;
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  line-height: 1.2;
}

.tutorial-card-body {
  padding: 16px 20px;
}

.tutorial-card-text {
  margin: 0;
  font-family: var(--font-body);
  font-size: 13px;
  line-height: 1.5;
  color: var(--ink-2);
}

.tutorial-card-link {
  appearance: none;
  border: none;
  background: transparent;
  padding: 0;
  margin-top: 16px;
  cursor: pointer;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.06em;
  color: var(--wp-blue);
  text-align: left;
  line-height: 1.4;
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  transition: color 140ms;
}
.tutorial-card-link:hover {
  color: var(--wp-blue-deep);
}

/* ═══════════════════════════════════════════════════════════════════
 * Workspace Onboarding — Studio first-visit welcome modal
 * Single slide. Sits over the workspace shell on first nav-link click.
 * Design source: design-system/studio-onboarding-handoff/
 * ═══════════════════════════════════════════════════════════════════ */

.workspace-onboarding {
  position: fixed;
  inset: 0;
  z-index: 200;
  font-family: var(--font-body);
}
.workspace-onboarding[hidden] { display: none; }

.workspace-onboarding-scrim {
  position: absolute;
  inset: 0;
  background: rgba(246, 246, 246, 0.28);
  backdrop-filter: blur(var(--wob-blur, 6px)) saturate(120%);
  -webkit-backdrop-filter: blur(var(--wob-blur, 6px)) saturate(120%);
  opacity: 0;
  transition: opacity 320ms cubic-bezier(.2,.7,.2,1);
}
.workspace-onboarding.is-open .workspace-onboarding-scrim { opacity: 1; }

.workspace-onboarding-stage {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  padding: 24px;
}

.workspace-onboarding-modal {
  width: 880px;
  max-width: calc(100vw - 48px);
  height: 480px;
  max-height: calc(100vh - 48px);
  background: var(--card, #ffffff);
  border-radius: 14px;
  border: 1px solid var(--card-edge, #e6e6e6);
  box-shadow:
    0 6px 12px rgba(28,32,36,0.10),
    0 20px 36px rgba(28,32,36,0.14),
    0 60px 140px rgba(28,32,36,0.22);
  display: flex;
  overflow: hidden;
  position: relative;
  pointer-events: auto;
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 560ms cubic-bezier(.16,1,.3,1), transform 560ms cubic-bezier(.16,1,.3,1);
}
.workspace-onboarding.is-open .workspace-onboarding-modal {
  opacity: 1;
  transform: translateY(0);
}

/* ─── Left half — paper-textured display ──────────────────────────── */

.workspace-onboarding-left {
  flex: 0 0 440px;
  position: relative;
  overflow: hidden;
  border-right: 1px solid var(--card-edge, #e6e6e6);
  background-color: var(--paper, #f6f6f6);
  background-image:
    radial-gradient(rgba(120,110,90,0.06) 1px, transparent 1px),
    radial-gradient(rgba(120,110,90,0.04) 1px, transparent 1px);
  background-size: 22px 22px, 11px 11px;
  background-position: 0 0, 5px 5px;
  padding: 40px 32px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
}

.workspace-onboarding-eyebrow-row {
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.workspace-onboarding-eyebrow-dot {
  width: 8px;
  height: 8px;
  border-radius: 999px;
  background: var(--wp-blue);
  flex: 0 0 auto;
}
.workspace-onboarding-eyebrow-text { flex: 0 0 auto; }
.workspace-onboarding-eyebrow-rule {
  flex: 1;
  height: 1px;
  background: var(--card-edge, #e6e6e6);
}

.workspace-onboarding-display {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 56px;
  line-height: 1.0;
  letter-spacing: -0.025em;
  color: var(--ink-1);
}
.workspace-onboarding-display-accent { color: var(--wp-blue); }

.workspace-onboarding-sigil {
  display: flex;
  align-items: center;
  gap: 8px;
  align-self: flex-end;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-4);
}
.workspace-onboarding-sigil-rule {
  flex: 1;
  min-width: 60px;
  height: 1px;
  background: var(--card-edge, #e6e6e6);
  display: inline-block;
}

/* ─── Right half — copy + CTA ────────────────────────────────────── */

.workspace-onboarding-right {
  flex: 0 0 440px;
  background: var(--card, #ffffff);
  padding: 40px 40px 32px;
  display: flex;
  flex-direction: column;
  position: relative;
}

.workspace-onboarding-right-eyebrow-row {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 24px;
}
.workspace-onboarding-right-eyebrow {
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--wp-blue-deep);
  background: var(--wp-blue-soft);
  padding: 4px 10px;
  border-radius: 999px;
}

.workspace-onboarding-headline {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 30px;
  line-height: 1.12;
  letter-spacing: -0.018em;
  color: var(--ink-1);
  margin-bottom: 18px;
  text-wrap: pretty;
}

.workspace-onboarding-body {
  flex: 1;
  font-family: var(--font-body);
  font-size: 14px;
  line-height: 1.58;
  color: var(--ink-2);
  text-wrap: pretty;
}

.workspace-onboarding-cta-row {
  margin-top: 18px;
  position: relative;
  height: 44px;
}
.workspace-onboarding-cta {
  position: absolute;
  right: 0;
  top: 50%;
  transform: translateY(-50%);
  background: var(--ink-1);
  color: var(--paper, #f6f6f6);
  border: none;
  padding: 12px 22px;
  min-width: 184px;
  border-radius: 999px;
  font-family: var(--font-body);
  font-size: 13.5px;
  font-weight: 500;
  letter-spacing: -0.005em;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  box-shadow:
    0 2px 6px rgba(56,88,233,0.18),
    0 0 0 3px rgba(56,88,233,0.18);
  transition: transform 140ms cubic-bezier(.2,.7,.2,1), box-shadow 200ms cubic-bezier(.2,.7,.2,1);
}
.workspace-onboarding-cta:hover {
  transform: translateY(calc(-50% - 1px));
  box-shadow:
    0 4px 10px rgba(56,88,233,0.22),
    0 0 0 4px rgba(56,88,233,0.22);
}
.workspace-onboarding-cta:focus-visible {
  outline: none;
  box-shadow:
    0 2px 6px rgba(56,88,233,0.18),
    0 0 0 4px rgba(56,88,233,0.36);
}
.workspace-onboarding-cta-arrow { opacity: 0.85; }

.workspace-onboarding-dismiss {
  position: absolute;
  top: 14px;
  right: 16px;
  width: 28px;
  height: 28px;
  border-radius: 999px;
  background: transparent;
  border: none;
  color: var(--ink-4);
  cursor: pointer;
  font-family: var(--font-body);
  font-size: 18px;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: color 140ms, background 140ms;
}
.workspace-onboarding-dismiss:hover {
  color: var(--ink-1);
  background: var(--paper, #f6f6f6);
}
.workspace-onboarding-dismiss:focus-visible {
  outline: 2px solid var(--wp-blue);
  outline-offset: 2px;
}

/* ─── Responsive — stack at narrow widths ─────────────────────────── */

@media (max-width: 720px) {
  .workspace-onboarding-modal {
    width: 100%;
    height: auto;
    max-height: calc(100vh - 48px);
    flex-direction: column;
    overflow-y: auto;
  }
  .workspace-onboarding-left,
  .workspace-onboarding-right {
    flex: 0 0 auto;
  }
  .workspace-onboarding-left {
    border-right: none;
    border-bottom: 1px solid var(--card-edge, #e6e6e6);
    padding: 28px 24px;
    gap: 28px;
  }
  .workspace-onboarding-display { font-size: 40px; }
  .workspace-onboarding-right { padding: 28px 24px 24px; }
  .workspace-onboarding-headline { font-size: 24px; }
  .workspace-onboarding-cta-row { height: auto; margin-top: 22px; }
  .workspace-onboarding-cta {
    position: static;
    transform: none;
    width: 100%;
  }
  .workspace-onboarding-cta:hover { transform: translateY(-1px); }
}
