/* Commons design tokens — "Unified Craft": sand and paper warmth over a
   grounded moss/bronze depth, one palette flexing across both modes.
   All colors come from here. */
:root {
  /* Surfaces — deep warm charcoal with a moss undertone */
  --bg-app: #1a1b17;
  --bg-panel: #21221d;
  --bg-panel-raised: #272822;
  --bg-canvas: #141511;
  --canvas-dot: #3d3e35; /* present on the canvas without competing with the work */
  --bg-input: #2b2c25;
  --bg-hover: #2b2c25;
  --bg-active: #33342c;

  /* Borders */
  --border-subtle: #2a2b24;
  --border-default: #363730;
  --border-strong: #46473d;

  /* Text — warm off-whites, not clinical gray */
  --text-primary: #edebe0;
  --text-secondary: #a3a195;
  --text-tertiary: #8a887c; /* 4.8:1 on --bg-app; #716f64 was 3.4:1 */
  --text-inverse: #1a1b17;

  /* Burnt orange. Deliberately redder than --comment bronze (#c9944a) so the
     brand's two warm roles stay separable: accent = what you can act on,
     bronze = where discussion lives. */
  --accent: #c8743f;
  --accent-hover: #d98a54;
  --accent-muted: rgba(200, 116, 63, 0.16);

  /* Semantic — status colors from the brand sheet; comment warmth is bronze */
  --success: #58a86b;
  --warning: #d9a03f;
  --danger: #d0655c;
  --comment: #c9944a;

  /* Type, shared with trycommons.app and bundled (lib/fonts.ts): Fraunces,
     soft and wonky, for display moments; Figtree for the tool; Shantell Sans,
     a marker hand, for tips and empty states only, never for people's work. */
  --font-display: "Fraunces", "Iowan Old Style", "Palatino", Georgia, serif;
  --font-ui: "Figtree", -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", sans-serif;
  --font-hand: "Shantell Sans", "Marker Felt", "Comic Sans MS", cursive;
  --font-mono: "SF Mono", "JetBrains Mono", ui-monospace, monospace;
  --text-xs: 11px;
  --text-sm: 12px;
  --text-md: 13px;
  --text-lg: 15px;
  --text-xl: 20px;

  /* Shape */
  --radius-sm: 4px;
  --radius-md: 8px;
  --radius-lg: 12px;
  --shadow-panel: 0 8px 32px rgba(8, 8, 4, 0.55);

  /* Pressable buttons, as on the site: an edge and a ledge the button sinks
     into. Primary wears the bright ember with ink type in both modes. */
  --btn-edge: #0c0d0a;
  --btn-ledge: #0c0d0a;
  --btn-primary-bg: #d98a54;
  --btn-primary-bg-hover: #e39a68;
  --btn-primary-ink: #1a1b17;

  /* Sheets: anything you can pick up (panels, popovers, the canvas dock, the
     minimap, the screens on the canvas) carries the same ink edge and a
     ledge, with a soft drop for things that float. */
  --radius-sheet: 14px;
  /* In dark mode an ink edge vanishes into the canvas, so sheets take a warm
     grey edge over a black ledge; in light mode both are ink. */
  --sheet-edge: #5d5e53;
  --sheet-ledge: #0c0d0a;
  --shadow-sheet: 0 4px 0 var(--sheet-ledge), 0 16px 36px rgba(8, 8, 4, 0.5);
  --shadow-card: 0 1px 2px rgba(8, 8, 4, 0.3), 0 10px 26px rgba(8, 8, 4, 0.28);
  --shadow-screen: 0 8px 16px rgba(0, 0, 0, 0.35);
}

/* Light mode — sand and paper. Applied via data-theme on <html>
   (lib/theme.ts); the default follows the system appearance. */
:root[data-theme="light"] {
  /* Surfaces */
  --bg-app: #f3f0e8;
  --bg-panel: #fbf9f4;
  --bg-panel-raised: #f6f3ec;
  --bg-canvas: #f3f0e8; /* the site's sand: ink-edged screens no longer need a darker table */
  --canvas-dot: #d3cdbc;
  --bg-input: #ece8dd;
  --bg-hover: #ece9df;
  --bg-active: #e2ddcf;

  /* Borders */
  --border-subtle: #e6e2d6;
  --border-default: #d8d3c4;
  --border-strong: #beb8a5;

  /* Text */
  --text-primary: #26251e;
  --text-secondary: #5c5a4f;
  --text-tertiary: #6f6d60; /* 4.6:1 on --bg-app; #8f8d80 was 3.2:1 */
  --text-inverse: #fbf9f4;

  /* Accent: deeper ember for contrast on paper */
  --accent: #9c531f;
  --accent-hover: #84441a;
  --accent-muted: rgba(156, 83, 31, 0.13);

  /* Semantic — darkened for light backgrounds */
  --success: #2e7d4f;
  --warning: #a8721f;
  --danger: #bf4940;
  --comment: #a8712c;

  --shadow-panel: 0 8px 32px rgba(50, 45, 25, 0.15);

  --btn-edge: #26251e;
  --btn-ledge: #26251e;
  --btn-primary-ink: #26251e;
  --sheet-edge: #26251e;
  --sheet-ledge: #26251e;
  --shadow-sheet: 0 4px 0 var(--sheet-ledge), 0 16px 36px rgba(60, 50, 25, 0.16);
  --shadow-card: 0 1px 2px rgba(60, 50, 25, 0.05), 0 10px 26px rgba(60, 50, 25, 0.07);
  --shadow-screen: 0 8px 16px rgba(60, 50, 25, 0.16);
}
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

/* ---------- Motion system ----------
   Two durations, one curve. Motion is for state transitions and perceived
   speed only (entrances, feedback, crossfades) — never decoration. Every
   animation here is killed by the prefers-reduced-motion block at the end. */
:root {
  --dur-fast: 120ms; /* hover/press feedback */
  --dur-med: 200ms; /* entrances, crossfades */
  --ease-out: cubic-bezier(0.2, 0, 0, 1);
}
@keyframes rise-in {
  from {
    opacity: 0;
    transform: translateY(6px);
  }
}
@keyframes pop-in {
  from {
    opacity: 0;
    transform: translateY(-4px) scale(0.98);
  }
}
@keyframes fade-in {
  from {
    opacity: 0;
  }
}
@keyframes shimmer {
  from {
    background-position: 200% 0;
  }
  to {
    background-position: -200% 0;
  }
}
@keyframes panel-in-left {
  from {
    opacity: 0;
    transform: translateX(-14px);
  }
}
@keyframes panel-in-right {
  from {
    opacity: 0;
    transform: translateX(14px);
  }
}
/* Perceived speed: a shimmering placeholder reads as "already working",
   where a spinner or text reads as "waiting". */
.skeleton {
  background: linear-gradient(
    100deg,
    var(--bg-panel) 40%,
    color-mix(in srgb, var(--text-tertiary) 12%, var(--bg-panel)) 50%,
    var(--bg-panel) 60%
  );
  background-size: 200% 100%;
  animation: shimmer 1.4s linear infinite;
  border-radius: var(--radius-sm);
  color: transparent;
}

html,
body,
#root {
  height: 100%;
  overflow: hidden;
}

body {
  font-family: var(--font-ui);
  font-size: var(--text-md);
  color: var(--text-primary);
  background: var(--bg-app);
  -webkit-font-smoothing: antialiased;
  user-select: none;
}

button {
  font: inherit;
  color: inherit;
  background: none;
  border: none;
  cursor: pointer;
}

input,
textarea {
  font: inherit;
  color: var(--text-primary);
  background: var(--bg-input);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-sm);
  padding: 7px 10px;
  outline: none;
}
input:focus,
textarea:focus {
  border-color: var(--accent);
}

/* ---------- App chrome ---------- */

.app {
  display: flex;
  flex-direction: column;
  height: 100%;
}

.titlebar {
  height: 44px;
  flex: none;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 0 16px;
  background: var(--bg-panel);
  border-bottom: 1px solid var(--border-subtle);
  -webkit-app-region: drag;
}
/* Desktop only: clear the traffic lights. The subnav sits below them and
   keeps the window's own inset, so both rows line up in the browser. */
:root[data-desktop="1"] .titlebar {
  padding-left: 84px;
}
.titlebar button,
.titlebar input {
  -webkit-app-region: no-drag;
}
/* Project context bar: the row under the titlebar holding everything scoped
   to the open project (view switcher, server state, setup, tools, share).
   Left edge aligns under the traffic lights — this row sits below them, so
   it takes the window's own inset rather than the tab strip's. */
.subnav {
  height: 40px;
  flex: none;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 0 16px;
  background: var(--bg-panel);
  border-bottom: 1px solid var(--border-subtle);
}
.titlebar .crumb {
  color: var(--text-tertiary);
  /* The one flexible child: shrink to an ellipsis before anything wraps or clips. */
  flex: 0 1 auto;
  min-width: 48px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.titlebar .crumb strong {
  color: var(--text-primary);
  font-weight: 500;
}
/* Controls never shrink; the crumb and spacer absorb narrow windows. */
.titlebar > :not(.crumb):not(.spacer) {
  flex-shrink: 0;
}
/* No overflow clip here: it silently swallowed every titlebar popover
   (they render just past the bar's box). Narrow windows degrade via the
   crumb ellipsis + non-shrink controls instead. */
@media (max-width: 1240px) {
  .titlebar {
    gap: 8px;
  }
  .titlebar .crumb-prefix {
    display: none; /* "Projects /" goes first — the back arrow already says it */
  }
}
.titlebar .spacer {
  flex: 1;
}
/* Subnav: view switcher and server state left, tools and Share right. */
.subnav .spacer {
  flex: 1;
}
.subnav > :not(.spacer) {
  flex-shrink: 0;
}

.btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 12px;
  border-radius: var(--radius-sm);
  font-size: var(--text-sm);
  font-weight: 500;
  color: var(--text-secondary);
  border: 1px solid var(--border-default);
  background: var(--bg-panel-raised);
  white-space: nowrap;
  transition:
    background var(--dur-fast) var(--ease-out),
    border-color var(--dur-fast) var(--ease-out),
    color var(--dur-fast) var(--ease-out),
    transform var(--dur-fast) var(--ease-out);
}
.btn:active:not(:disabled) {
  transform: scale(0.97);
}
.btn:hover {
  color: var(--text-primary);
  background: var(--bg-hover);
  border-color: var(--border-strong);
}
.btn.primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--text-inverse);
}
.btn.primary:hover {
  background: var(--accent-hover);
}
.btn.ghost {
  border-color: transparent;
  background: transparent;
}
.btn.active {
  background: var(--accent-muted);
  border-color: var(--accent);
  color: var(--accent);
}
.btn:disabled {
  opacity: 0.45;
  cursor: default;
}

.seg {
  display: inline-flex;
  background: var(--bg-input);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-sm);
  padding: 2px;
}
.seg button {
  padding: 4px 12px;
  border-radius: 4px;
  font-size: var(--text-sm);
  color: var(--text-secondary);
  transition:
    background var(--dur-fast) var(--ease-out),
    color var(--dur-fast) var(--ease-out);
}
.seg button.on {
  background: var(--bg-active);
  color: var(--text-primary);
}

.avatar {
  width: 24px;
  height: 24px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--text-inverse);
  flex: none;
}
.avatar img {
  width: 100%;
  height: 100%;
  border-radius: 50%;
  object-fit: cover;
}
.avatar-stack {
  display: flex;
}
.avatar-stack .avatar {
  margin-left: -6px;
  border: 2px solid var(--bg-panel);
}
.avatar-stack .avatar:first-child {
  margin-left: 0;
}

.status-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--text-tertiary);
}
.status-dot.ready {
  background: var(--success);
}
.status-dot.starting {
  background: var(--warning);
}
.status-dot.error {
  background: var(--danger);
}

/* ---------- Titlebar popovers (inbox / team) ---------- */

.titlebar-popover {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  width: 340px;
  max-height: 420px;
  overflow-y: auto;
  background: var(--bg-panel);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-panel);
  z-index: 100;
  transform-origin: top right;
  animation: pop-in var(--dur-fast) var(--ease-out);
}

/* Setup conforms to its content: the slot buttons hug their text and the
   popover hugs the buttons. Expanded forms get room via the max clamp. */
.titlebar-popover.setup-pop {
  width: max-content;
  min-width: 216px;
  max-width: 300px;
}
.setup-pop .reveal-form {
  /* Every setting popover is the same width and the same inset, so switching
     between them doesn't reflow the panel under the cursor. */
  min-width: 264px;
  padding: 12px 14px 12px;
  gap: 8px;
}

.team-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 14px;
  border-bottom: 1px solid var(--border-subtle);
  font-size: var(--text-sm);
}
.team-row .who {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}
.team-row .name {
  font-weight: 500;
}
.team-row .email {
  color: var(--text-tertiary);
  font-size: var(--text-xs);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.team-row.pending .name {
  color: var(--text-secondary);
}
.team-invite {
  display: flex;
  gap: 8px;
  padding: 12px 14px;
}
.team-invite input {
  flex: 1;
  min-width: 0;
}

/* ---------- Centered screens (setup / sign-in) ---------- */

.center-screen.hint {
  /* loading text only: waits 150ms so fast loads never show it */
  animation: fade-in var(--dur-med) var(--ease-out) 150ms backwards;
}
.center-screen {
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  gap: 24px;
}
.center-card {
  animation: rise-in var(--dur-med) var(--ease-out);
  width: 380px;
  background: var(--bg-panel);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-lg);
  padding: 28px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}
/* The door wears the brand: the editorial serif that titles the home and
   the marketing page, not the workaday sans of the tool inside. First
   impressions are display moments by definition. */
.center-card h1 {
  font-family: var(--font-display);
  font-size: 27px;
  font-weight: 600;
  letter-spacing: -0.015em;
}
.center-card .lede {
  color: var(--text-secondary);
  margin-top: -6px;
}
.center-card p {
  color: var(--text-secondary);
  line-height: 1.5;
  font-size: var(--text-sm);
}
.center-card label {
  font-size: var(--text-xs);
  color: var(--text-tertiary);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin-bottom: -8px;
}

/* ---------- Project list ---------- */

.home {
  flex: 1;
  overflow-y: auto;
  padding: 40px 48px;
}
.home-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: 24px;
}
.home-header h1 {
  /* Display moments carry the brand's editorial serif; the tool stays sans. */
  font-family: var(--font-display);
  font-size: 24px;
  font-weight: 600;
  letter-spacing: 0.01em;
}
.project-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: 16px;
}
.project-card {
  text-align: left;
  background: var(--bg-panel);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-lg);
  overflow: hidden; /* full-bleed cover clips to the card's radius */
  padding: 0 0 16px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  transition:
    border-color var(--dur-fast) var(--ease-out),
    background var(--dur-fast) var(--ease-out),
    transform var(--dur-fast) var(--ease-out),
    box-shadow var(--dur-fast) var(--ease-out);
}
.project-card:hover {
  border-color: var(--border-strong);
  background: var(--bg-panel-raised);
  transform: translateY(-1px);
  box-shadow: var(--shadow-panel);
}
.project-card:active {
  transform: translateY(0);
}
/* Cards rise in once per load — staggered so the grid reads as assembling
   instantly rather than popping in as one block. */
.project-grid .project-card {
  animation: rise-in var(--dur-med) var(--ease-out) backwards;
}
.project-grid .project-card:nth-child(2) { animation-delay: 30ms; }
.project-grid .project-card:nth-child(3) { animation-delay: 60ms; }
.project-grid .project-card:nth-child(4) { animation-delay: 90ms; }
.project-grid .project-card:nth-child(5) { animation-delay: 120ms; }
.project-grid .project-card:nth-child(n + 6) { animation-delay: 150ms; }
/* Home skeletons: the grid's shape appears before the data does. */
.project-card.skeleton-card {
  pointer-events: none;
  animation: fade-in var(--dur-med) var(--ease-out);
}
.project-card .name {
  font-size: var(--text-lg);
  font-weight: 600;
}
.project-card .meta {
  font-size: var(--text-sm);
  color: var(--text-tertiary);
  display: flex;
  gap: 10px;
}
.project-card .foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-top: 4px;
}
.badge {
  font-size: var(--text-xs);
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--bg-input);
  color: var(--text-secondary);
  border: 1px solid var(--border-subtle);
}
.badge.comments {
  color: var(--comment);
}
.empty-state {
  color: var(--text-tertiary);
  text-align: center;
  padding: 80px 0;
  line-height: 1.7;
}

/* ---------- Canvas ---------- */

.canvas-wrap {
  flex: 1;
  position: relative;
  overflow: hidden;
  background: var(--bg-canvas);
  /* Soft theme-aware dot grid with a whisper of vignette for depth —
     the frames should feel like they sit ON something. */
  background-image: radial-gradient(circle, var(--canvas-dot) 1.2px, transparent 1.2px);
  background-size: 24px 24px;
  box-shadow: inset 0 0 72px color-mix(in srgb, var(--text-primary) 4%, transparent);
}
.canvas-wrap.commenting {
  cursor: crosshair;
}
/* Cursor trail: a second, brighter dot layer masked to a small spotlight that
   eases toward a point just behind the direction of travel and glides onto the
   cursor when the hand stops. Both layers move by transform only — see the
   note in CanvasView for why the mask center is no longer animated.

   The patch: 240px square (2 × 120, and 120 = 5 × 24 so its dots stay in
   phase with the base grid), centred on the point by the negative margin.
   The mask is static — it never recomputes, it just rides the transform. */
.dot-glow {
  position: absolute;
  left: 0;
  top: 0;
  width: 240px;
  height: 240px;
  margin: -120px 0 0 -120px;
  overflow: hidden;
  pointer-events: none;
  opacity: 0;
  will-change: transform;
  transform: translate3d(-400px, -400px, 0);
  -webkit-mask-image: radial-gradient(
    circle 80px at 120px 120px,
    rgba(0, 0, 0, 0.75),
    rgba(0, 0, 0, 0.28) 55%,
    transparent 100%
  );
  mask-image: radial-gradient(
    circle 80px at 120px 120px,
    rgba(0, 0, 0, 0.75),
    rgba(0, 0, 0, 0.28) 55%,
    transparent 100%
  );
  transition:
    opacity 260ms var(--ease-out),
    transform 150ms ease-out;
}
/* Counter-translated by exactly the patch's offset, so the dots it shows stay
   pinned to the canvas grid instead of sliding along with the spotlight.
   Its transition must match the patch's exactly or the two drift apart
   mid-flight and the dots visibly swim. */
.dot-glow-field {
  position: absolute;
  left: 0;
  top: 0;
  /* Its own top-left sits at a fixed (-120, -120) in canvas coordinates, so it
     has to reach 120px past the far edge for the patch to stay filled when the
     cursor is in the bottom-right corner. The slack is a multiple of 24 to
     keep the grid in phase. */
  width: calc(100vw + 480px);
  height: calc(100vh + 480px);
  will-change: transform;
  transform: translate3d(400px, 400px, 0);
  background-image: radial-gradient(
    circle,
    color-mix(in srgb, var(--canvas-dot) 45%, var(--text-primary)) 1.9px,
    transparent 1.9px
  );
  background-size: 24px 24px;
  transition: transform 150ms ease-out;
}
.dot-glow.on {
  opacity: 1;
}
@media (prefers-reduced-motion: reduce) {
  .dot-glow,
  .dot-glow-field {
    transition: opacity 260ms var(--ease-out);
  }
}
.canvas-stage {
  position: absolute;
  top: 0;
  left: 0;
  transform-origin: 0 0;
}

.frame {
  position: absolute;
  background: var(--bg-panel);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  display: flex;
  flex-direction: column;
  overflow: visible;
}
.frame.focused {
  border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent);
}
.frame-header {
  height: 30px;
  flex: none;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 10px;
  font-size: var(--text-sm);
  color: var(--text-secondary);
  border-bottom: 1px solid var(--border-subtle);
  cursor: grab;
  border-radius: var(--radius-md) var(--radius-md) 0 0;
  background: var(--bg-panel-raised);
}
.frame-header:active {
  cursor: grabbing;
}
.frame-header .route {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--text-tertiary);
}
.frame-body {
  position: relative;
  flex: 1;
  border-radius: 0 0 var(--radius-md) var(--radius-md);
  overflow: hidden;
  background: #fff;
}
/* Perceived speed: the last snapshot shows instantly under the booting
   iframe, which fades in over it once loaded — a frame never flashes blank
   white while a dev server or deploy preview wakes up. */
/* Preview appearance: the iframe element's color-scheme propagates into the
   child document as its prefers-color-scheme (CSS Color Adjust), so this is
   what makes embedded apps render light or dark on command. "system" sets
   no rule and the environment decides. */
:root[data-preview-appearance="light"] .frame-body iframe,
:root[data-preview-appearance="light"] .proto-stage iframe {
  color-scheme: light;
}
:root[data-preview-appearance="dark"] .frame-body iframe,
:root[data-preview-appearance="dark"] .proto-stage iframe {
  color-scheme: dark;
}
.frame-body iframe {
  position: relative;
  z-index: 1;
  width: 100%;
  height: 100%;
  border: none;
  display: block;
  background: transparent;
  opacity: 0;
  transition: opacity var(--dur-med) var(--ease-out);
}
.frame-body iframe.loaded {
  opacity: 1;
  background: #fff;
}
.frame-underlay {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top;
  background: #fff;
}
.frame-booting {
  position: absolute;
  inset: 0;
  background: linear-gradient(
    100deg,
    var(--bg-panel) 40%,
    color-mix(in srgb, var(--text-tertiary) 10%, var(--bg-panel)) 50%,
    var(--bg-panel) 60%
  );
  background-size: 200% 100%;
  animation: shimmer 1.4s linear infinite;
}
.frame-body .frame-shield {
  position: absolute;
  inset: 0;
}
/* Comment mode means comment mode: the shield outranks every canvas layer
   (stickers z-4, heatmap, the app itself), so a click can only ever be a
   pin. Without the explicit z, sticker layers sat above it and clicks fell
   through to the prototype. */
.frame-body .frame-shield.commenting {
  z-index: 30;
  cursor: crosshair;
}
.frame-placeholder {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  background: var(--bg-panel);
  color: var(--text-tertiary);
  font-size: var(--text-sm);
}

.pin {
  position: absolute;
  width: 28px;
  height: 28px;
  border-radius: 50% 50% 50% 4px;
  background: var(--comment);
  color: var(--text-inverse);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--text-xs);
  font-weight: 700;
  border: 2px solid var(--bg-canvas);
  transform: translate(-4px, -24px);
  cursor: pointer;
  z-index: 5;
}
.pin {
  transition: filter var(--dur-fast) var(--ease-out);
  animation: fade-in var(--dur-med) var(--ease-out);
}
.pin:hover {
  filter: brightness(1.15);
}
.pin.resolved {
  background: var(--text-tertiary);
}
.pin.selected {
  outline: 2px solid var(--accent);
}

.canvas-toolbar {
  position: absolute;
  bottom: 16px;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  gap: 4px;
  background: var(--bg-panel);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  padding: 4px;
  box-shadow: var(--shadow-panel);
  z-index: 10;
}
.canvas-toolbar .zoom {
  font-size: var(--text-xs);
  color: var(--text-tertiary);
  display: flex;
  align-items: center;
  padding: 0 8px;
  font-variant-numeric: tabular-nums;
}

/* ---------- Thread panel ---------- */

.thread-panel {
  animation: panel-in-right var(--dur-med) var(--ease-out);
  position: absolute;
  top: 12px;
  right: 12px;
  bottom: 12px;
  width: 320px;
  background: var(--bg-panel);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-panel);
  display: flex;
  flex-direction: column;
  z-index: 20;
}
.thread-panel header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 14px;
  border-bottom: 1px solid var(--border-subtle);
  font-weight: 600;
  font-size: var(--text-sm);
}
.thread-messages {
  flex: 1;
  overflow-y: auto;
  padding: 14px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.msg {
  animation: rise-in var(--dur-med) var(--ease-out);
  display: flex;
  gap: 10px;
}
.msg .body {
  flex: 1;
}
.msg .who {
  font-size: var(--text-sm);
  font-weight: 600;
  margin-bottom: 2px;
}
.msg .when {
  font-weight: 400;
  color: var(--text-tertiary);
  font-size: var(--text-xs);
  margin-left: 6px;
}
.msg .text {
  color: var(--text-secondary);
  line-height: 1.5;
  font-size: var(--text-sm);
  user-select: text;
  word-break: break-word;
}
.msg .text .mention {
  color: var(--accent);
  font-weight: 500;
}
.thread-compose {
  border-top: 1px solid var(--border-subtle);
  padding: 12px;
  position: relative;
}
.thread-compose textarea {
  width: 100%;
  resize: none;
  min-height: 60px;
}
.thread-compose .row {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 8px;
}
.mention-menu {
  position: absolute;
  bottom: calc(100% - 6px);
  left: 12px;
  right: 12px;
  background: var(--bg-panel-raised);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-panel);
  overflow: hidden;
  z-index: 30;
}
.mention-menu button {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 8px 12px;
  font-size: var(--text-sm);
  text-align: left;
}
.mention-menu button:hover,
.mention-menu button.hl {
  background: var(--bg-hover);
}

/* ---------- Prototype view ---------- */

.proto {
  flex: 1;
  display: flex;
  flex-direction: column;
  background: var(--bg-canvas);
}
.proto-toolbar {
  height: 42px;
  flex: none;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 0 16px;
  background: var(--bg-panel);
  border-bottom: 1px solid var(--border-subtle);
}
.proto-toolbar select {
  font: inherit;
  color: var(--text-primary);
  background: var(--bg-input);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-sm);
  padding: 5px 8px;
}
.proto-notes {
  position: absolute;
  right: 12px;
  top: 12px;
  bottom: 12px;
  width: 320px;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 14px;
  z-index: 8;
  background: var(--bg-panel);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-panel);
  padding: 12px 14px;
  animation: panel-in-right var(--dur-med) var(--ease-out);
}
.proto-notes-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-weight: 600;
}
.proto-stage {
  flex: 1;
  display: flex;
  align-items: stretch;
  justify-content: center;
  padding: 24px;
  overflow: auto;
}
.proto-device {
  position: relative;
  background: var(--bg-panel);
  border-radius: var(--radius-md);
  border: 1px solid var(--border-strong);
  overflow: hidden;
  display: flex;
}
/* Static stand-in when no live URL exists on this machine. */
.proto-snapshot img {
  flex: 1;
  width: 100%;
  object-fit: contain;
  object-position: top;
  background: #fff;
}
.proto-snapshot-note {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  padding: 6px 12px;
  font-size: var(--text-xs);
  color: var(--text-secondary);
  background: color-mix(in srgb, var(--bg-panel) 92%, transparent);
  border-top: 1px solid var(--border-subtle);
}
.proto-device iframe {
  border: none;
  flex: 1;
  opacity: 0;
  transition: opacity var(--dur-med) var(--ease-out);
}
.proto-device iframe.loaded {
  opacity: 1;
  background: #fff;
}

.hint {
  font-size: var(--text-xs);
  color: var(--text-tertiary);
  /* Popover prose wraps constantly at 340px — without a stated leading each
     body set its own and the panels never lined up. */
  line-height: 1.45;
}

/* ---------- User tests (Prototype tab) ---------- */

.user-tests {
  position: fixed;
  top: 138px; /* titlebar 44 + subnav 40 + proto toolbar 44 + gutter — never over its own button */
  right: 12px;
  bottom: 12px;
  width: 460px;
  overflow-y: auto;
  background: var(--bg-panel);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-panel);
  padding: 14px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  z-index: 30;
  font-size: var(--text-sm);
}
.user-tests-head,
.test-results-head {
  display: flex;
  align-items: center;
  gap: 8px;
}
.user-test-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 12px;
  background: var(--bg-input);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
}
.user-test-main {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.badge.live {
  color: var(--success);
}
.test-form {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 12px;
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  background: var(--bg-input);
}
.test-form input,
.test-form select {
  font: inherit;
  color: var(--text-primary);
  background: var(--bg-panel);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-sm);
  padding: 6px 8px;
}
.test-form-row {
  display: flex;
  align-items: center;
  gap: 8px;
}
.test-form-row select {
  max-width: 200px;
}
.test-results {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 12px;
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  background: var(--bg-input);
}
.test-results code {
  font-size: var(--text-xs);
  user-select: all;
}
.test-task-stats {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 8px 10px;
  background: var(--bg-panel);
  border-radius: var(--radius-sm);
}
.test-task-title {
  display: flex;
  align-items: center;
  gap: 8px;
}
.test-task-nums {
  display: flex;
  gap: 14px;
  color: var(--text-secondary);
  font-size: var(--text-xs);
}
.variant-tag {
  color: var(--accent);
  font-weight: 600;
  min-width: 92px;
}
.test-paths {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.test-sessions {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--text-xs);
}
.test-sessions th,
.test-sessions td {
  text-align: left;
  padding: 4px 8px;
  border-bottom: 1px solid var(--border-subtle);
  color: var(--text-secondary);
}
.test-sessions th {
  color: var(--text-tertiary);
  font-weight: 500;
}

/* Stacked form layout for settings popovers (Preview URL etc.) */
.popover-form {
  width: 380px;
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.popover-form .form-field {
  display: flex;
  flex-direction: column;
  gap: 5px;
}
.popover-form .form-field > label {
  font-size: var(--text-sm);
  font-weight: 600;
}
.popover-form .form-field > label .hint {
  font-weight: 400;
  margin-left: 6px;
}
.popover-form .form-field .hint {
  line-height: 1.45;
}
.popover-form .form-field code {
  font-size: var(--text-xs);
}
.popover-form input {
  width: 100%;
  box-sizing: border-box;
  font: inherit;
  color: var(--text-primary);
  background: var(--bg-input);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-sm);
  padding: 8px 10px;
}
.popover-form input:focus {
  outline: none;
  border-color: var(--accent);
}
.popover-form .form-error {
  font-size: var(--text-xs);
  color: var(--danger, #f87171);
}
.popover-form .form-actions {
  display: flex;
  gap: 8px;
  justify-content: flex-end;
}

/* Home search + command palette */
.home-search {
  flex: 0 1 320px;
  margin-left: auto;
  margin-right: 12px;
  font: inherit;
  color: var(--text-primary);
  background: var(--bg-input);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-sm);
  padding: 7px 10px;
}
.home-search:focus {
  outline: none;
  border-color: var(--accent);
}
.badge.setup {
  color: var(--danger, #f87171);
}
.palette-overlay {
  animation: fade-in var(--dur-fast) var(--ease-out);
  position: fixed;
  inset: 0;
  background: color-mix(in srgb, var(--bg-canvas) 60%, transparent);
  display: flex;
  justify-content: center;
  padding-top: 14vh;
  z-index: 80;
}
.palette {
  transform-origin: top center;
  animation: pop-in var(--dur-fast) var(--ease-out);
  width: 520px;
  height: fit-content;
  background: var(--bg-panel);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-panel);
  overflow: hidden;
}
.palette input {
  width: 100%;
  box-sizing: border-box;
  font: inherit;
  font-size: var(--text-md);
  color: var(--text-primary);
  background: transparent;
  border: none;
  border-bottom: 1px solid var(--border-subtle);
  padding: 14px 16px;
  outline: none;
}
.palette-results {
  max-height: 320px;
  overflow-y: auto;
  padding: 6px;
}
.palette-results button {
  transition: background var(--dur-fast) var(--ease-out);
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  width: 100%;
  text-align: left;
  font: inherit;
  color: var(--text-primary);
  background: none;
  border: none;
  border-radius: var(--radius-sm);
  padding: 9px 10px;
  cursor: pointer;
}
.palette-results button.hl {
  background: var(--bg-hover);
}
.palette-name {
  font-weight: 500;
}
.palette-empty {
  padding: 12px 10px;
}

/* Workspaces: grouped home + titlebar popover */
.workspace-section {
  margin-bottom: 28px;
}
.workspace-heading {
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--text-secondary);
  margin: 0 0 10px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
/* Workspace headings double as fold toggles: chevron rotates, a count
   stands in for the hidden grid. */
button.workspace-toggle {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  cursor: pointer;
  transition: color var(--dur-fast) var(--ease-out);
}
button.workspace-toggle:hover {
  color: var(--text-primary);
}
.workspace-toggle svg {
  transition: transform var(--dur-fast) var(--ease-out);
}
.workspace-toggle.folded svg {
  transform: rotate(-90deg); /* closed: chevron points at the name */
}
.workspace-toggle .ws-count {
  font-weight: 400;
  color: var(--text-tertiary);
  text-transform: none;
  letter-spacing: normal;
}
.workspaces-popover {
  min-width: 320px;
}
.workspace-row {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 10px 14px;
  border-bottom: 1px solid var(--border-subtle);
}
.workspace-row:last-of-type {
  border-bottom: none;
}
.workspace-row-head {
  display: flex;
  align-items: baseline;
  gap: 8px;
}
.workspace-row input {
  font: inherit;
  color: var(--text-primary);
  background: var(--bg-input);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-sm);
  padding: 6px 8px;
}

/* Linked emails in the account menu */
.linked-email-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 2px 8px 2px 14px;
}
.linked-email-row .email {
  font-size: var(--text-xs);
  color: var(--text-secondary);
}

/* Pilot pulse (MET-1) in the Team popover */
.pilot-pulse {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 10px 14px 12px;
  border-top: 1px solid var(--border-subtle);
}

/* Message image attachments (before/after snapshots) */
.msg-images {
  display: flex;
  gap: 6px;
  margin-top: 6px;
}
.msg-images button {
  flex: 1;
  min-width: 0;
  padding: 0;
  border: 1px solid var(--border-default);
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: none;
  cursor: zoom-in;
}
.msg-images img {
  display: block;
  width: 100%;
  max-height: 180px;
  object-fit: cover;
  object-position: top;
}

/* Current-vs-draft compare (PRJ-14) */
.compare-overlay {
  animation: fade-in var(--dur-fast) var(--ease-out);
  position: fixed;
  inset: 0;
  background: color-mix(in srgb, var(--bg-canvas) 75%, transparent);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 50;
}
.compare-modal {
  width: min(1400px, calc(100vw - 48px));
  height: calc(100vh - 96px);
  display: flex;
  flex-direction: column;
  background: var(--bg-panel);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-panel);
  overflow: hidden;
}
.compare-head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 14px;
  border-bottom: 1px solid var(--border-subtle);
  font-size: var(--text-sm);
}
.compare-panes {
  flex: 1;
  display: flex;
  gap: 1px;
  background: var(--border-subtle);
  min-height: 0;
}
.compare-pane {
  flex: 1;
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bg-canvas);
}
.compare-pane iframe {
  border: 0;
  width: 100%;
  height: 100%;
  background: #fff;
}
.compare-pane .badge {
  position: absolute;
  top: 10px;
  left: 10px;
  z-index: 2;
}
.compare-pane .badge.draft {
  color: var(--accent);
}

/* Auto-update restart pill */
.update-chip {
  position: fixed;
  bottom: 16px;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 8px 10px 8px 16px;
  background: var(--bg-panel);
  border: 1px solid var(--border-default);
  border-radius: 999px;
  box-shadow: var(--shadow-panel);
  font-size: var(--text-sm);
  z-index: 60;
  animation: pop-in var(--dur-med) var(--ease-out);
}
/* Informational states (checking, up to date, couldn't reach the feed):
   no button, so the pill keeps even padding and a quieter voice. */
.update-chip.quiet {
  padding: 8px 16px;
  color: var(--text-secondary);
}

.canvas-toolbar .zoom-step {
  padding: 4px 10px;
  font-size: var(--text-md);
  line-height: 1;
}
.frame-snapshot {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top;
  filter: saturate(0.92);
}

/* Test-click overlay on canvas frames */
.heatmap-layer {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  z-index: 3;
}
.heatmap-dot {
  position: absolute;
  width: 14px;
  height: 14px;
  margin: -7px 0 0 -7px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(74, 111, 220, 0.85), rgba(74, 111, 220, 0));
}
.heatmap-dot.miss {
  background: radial-gradient(circle, rgba(235, 137, 52, 0.9), rgba(235, 137, 52, 0));
}
.heatmap-chip {
  position: absolute;
  top: 12px;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 8px 6px 14px;
  background: var(--bg-panel);
  border: 1px solid var(--border-default);
  border-radius: 999px;
  box-shadow: var(--shadow-panel);
  font-size: var(--text-xs);
  color: var(--text-secondary);
  z-index: 30;
}

/* Agent sessions panel */
.agent-panel {
  animation: panel-in-right var(--dur-med) var(--ease-out);
  position: fixed;
  top: 96px; /* titlebar (44px) + subnav (40px) + gutter — panels live in the canvas, never over the chrome */
  /* Contextual, not constant: the home has no subnav, so the same panel
     hangs 52px too low there — it looked reserved for chrome that view
     doesn't have. :has() keys the offset off the chrome actually present. */
  right: 12px;
  bottom: 12px;
  width: 380px;
  background: var(--bg-panel);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-panel);
  display: flex;
  flex-direction: column;
  z-index: 30;
}
.app:not(:has(.subnav)) .agent-panel {
  top: 56px;
}
.agent-panel header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 14px;
  border-bottom: 1px solid var(--border-subtle);
  font-weight: 600;
  font-size: var(--text-sm);
}
.agent-status {
  margin-left: 8px;
  padding: 1px 8px;
  border-radius: 999px;
  font-size: var(--text-xs);
  font-weight: 500;
  color: var(--text-secondary);
  background: var(--bg-input);
}
.agent-status.running,
.agent-status.starting {
  color: var(--accent);
  background: var(--accent-muted);
}
.agent-status.idle {
  color: var(--success);
}
.agent-status.error {
  color: var(--danger);
}
.agent-tabs {
  display: flex;
  gap: 4px;
  padding: 8px 10px 0;
  overflow-x: auto;
  flex: none;
}
.agent-tabs button {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border-radius: var(--radius-sm);
  font-size: var(--text-xs);
  color: var(--text-secondary);
  white-space: nowrap;
  max-width: 160px;
  overflow: hidden;
  text-overflow: ellipsis;
}
.agent-tabs button.on {
  background: var(--bg-active);
  color: var(--text-primary);
}
.agent-status-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  flex: none;
  background: var(--text-tertiary);
}
.agent-status-dot.running,
.agent-status-dot.starting {
  background: var(--accent);
}
.agent-status-dot.idle {
  background: var(--success);
}
.agent-status-dot.error {
  background: var(--danger);
}
.agent-session-title {
  flex: none;
  padding: 10px 14px 0;
  font-size: var(--text-sm);
  font-weight: 500;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.agent-session-title .route {
  margin-left: 8px;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--text-tertiary);
}
.agent-transcript {
  flex: 1;
  overflow-y: auto;
  padding: 12px 14px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.agent-item {
  font-size: var(--text-md);
  line-height: 1.5;
  white-space: pre-wrap;
  word-break: break-word;
}
.agent-item.prompt {
  align-self: flex-end;
  max-width: 90%;
  padding: 8px 12px;
  border-radius: var(--radius-md);
  background: var(--accent-muted);
  color: var(--text-primary);
  font-size: var(--text-sm);
}
.agent-item.tool {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--text-tertiary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.agent-item.failed {
  font-size: var(--text-sm);
  color: var(--danger);
}
.agent-item.done {
  font-size: var(--text-xs);
  color: var(--text-secondary);
  border-top: 1px solid var(--border-subtle);
  padding-top: 8px;
}
.agent-item.done .files {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin-top: 6px;
}
.agent-item.done code {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  padding: 1px 6px;
  border-radius: var(--radius-sm);
  background: var(--bg-input);
  color: var(--text-secondary);
}
.agent-composer {
  flex: none;
  display: flex;
  gap: 8px;
  align-items: flex-end;
  padding: 10px 12px;
  border-top: 1px solid var(--border-subtle);
}
.agent-composer textarea {
  flex: 1;
  min-height: 34px;
  max-height: 120px;
  resize: vertical;
  padding: 8px 10px;
  border-radius: var(--radius-md);
  background: var(--bg-input);
  border: 1px solid var(--border-default);
  color: var(--text-primary);
  font-family: var(--font-ui);
  font-size: var(--text-md);
}
.agent-empty {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 24px;
  line-height: 1.6;
}
.agent-spectator {
  flex: none;
  padding: 10px 14px;
  border-top: 1px solid var(--border-subtle);
  font-size: var(--text-sm);
}

/* Project card canvas thumbnail */
.project-thumb {
  width: 100%;
  aspect-ratio: 16 / 10;
  background: var(--bg-canvas);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  display: block;
  transition: border-color 0.12s ease;
}
.project-thumb.empty {
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-tertiary);
  font-size: var(--text-xs);
}
.project-card:hover .project-thumb {
  border-color: var(--border-default);
}

/* Multiplayer cursors */
.presence-cursor {
  position: absolute;
  z-index: 18;
  pointer-events: none;
  display: flex;
  align-items: flex-start;
  gap: 2px;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.4));
}
.presence-cursor .tag {
  margin-top: 12px;
  padding: 2px 7px;
  border-radius: 999px;
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--text-inverse);
  white-space: nowrap;
}

/* Canvas orientation: sections, far-zoom labels, minimap */
.section-region {
  position: absolute;
  background: color-mix(in srgb, var(--text-primary) 2%, transparent);
  border: 1px solid var(--border-subtle);
  border-radius: 20px;
  pointer-events: none;
}
.section-label {
  position: absolute;
  pointer-events: none;
  font-size: var(--text-md);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-secondary);
  white-space: nowrap;
}
/* Caption under each frame at far zoom — plain text, contact-sheet style. */
.frame-farlabel {
  position: absolute;
  pointer-events: none;
  z-index: 12;
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--text-secondary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.minimap {
  position: absolute;
  right: 12px;
  bottom: 12px;
  width: 176px;
  height: 120px;
  background: var(--bg-panel);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-panel);
  z-index: 19; /* below the thread panel */
  cursor: crosshair;
  overflow: hidden;
}
.minimap svg {
  width: 100%;
  height: 100%;
  display: block;
}

/* Overlays: welcome card + shortcuts cheat sheet */
.overlay-scrim {
  animation: fade-in var(--dur-fast) var(--ease-out);
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.55);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 50;
}
.overlay-card {
  animation: pop-in var(--dur-med) var(--ease-out);
  width: 440px;
  max-width: calc(100vw - 48px);
  max-height: calc(100vh - 96px);
  overflow-y: auto;
  background: var(--bg-panel);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-panel);
}
.overlay-card header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 16px;
  border-bottom: 1px solid var(--border-subtle);
  font-weight: 600;
  font-size: var(--text-lg);
}
kbd {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  padding: 2px 6px;
  border-radius: var(--radius-sm);
  background: var(--bg-input);
  border: 1px solid var(--border-strong);
  color: var(--text-secondary);
}
.shortcut-list {
  padding: 8px 0;
}
.shortcut-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 8px 16px;
  font-size: var(--text-md);
}
.shortcut-row .keys {
  display: flex;
  gap: 4px;
}
.welcome-body {
  padding: 14px 16px;
  font-size: var(--text-md);
  line-height: 1.6;
}
.welcome-lead {
  color: var(--text-primary);
  font-weight: 500;
}
.welcome-body ul {
  margin: 12px 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.welcome-body li {
  animation: rise-in var(--dur-med) var(--ease-out) backwards;
}
.welcome-body li:nth-child(2) {
  animation-delay: 60ms;
}
.welcome-body li:nth-child(3) {
  animation-delay: 120ms;
}
.welcome-body li strong {
  color: var(--text-primary);
}
.welcome-actions {
  display: flex;
  justify-content: flex-end;
  padding: 0 16px 16px;
}

/* Repo-holder nudge: teammates can't see frames until a preview exists */
.nudge-banner {
  animation: pop-in var(--dur-med) var(--ease-out);
  flex: none;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 16px;
  background: var(--accent-muted);
  border-bottom: 1px solid var(--border-subtle);
  font-size: var(--text-sm);
  color: var(--text-primary);
}
.nudge-banner span {
  flex: 1;
}

/* Project card cover: the project's own screens on a little table (see the
   brand layer at the end). Uploaded covers fill it edge to edge. */
.project-cover {
  position: relative;
  width: 100%;
  overflow: hidden;
}

/* Popover menus (add-project, account) + sharing member rows */
.popover-menu {
  padding: 6px;
  display: flex;
  flex-direction: column;
}
.popover-menu > button {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  text-align: left;
  padding: 8px 10px;
  border-radius: var(--radius-sm);
  font-size: var(--text-sm);
}
.popover-menu > button:hover {
  background: var(--bg-hover);
}
.popover-menu .team-row {
  border-bottom: 1px solid var(--border-subtle);
  margin-bottom: 4px;
}
.member-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 4px;
  font-size: var(--text-sm);
  cursor: pointer;
}
.member-row input {
  accent-color: var(--accent);
}

.agent-item.done .draft-row {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 8px;
}
.agent-item.done .draft-row .failed {
  color: var(--danger);
  font-size: var(--text-xs);
}

/* Git setup preflight banner (home) */
.nudge-banner.card {
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  align-items: flex-start;
}
.setup-rows {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.setup-row {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.setup-row code {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  background: var(--bg-input);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-sm);
  padding: 1px 6px;
  user-select: text;
}

/* ── Narration (NAR): review queue panel, citations, canvas Notes layer ── */
.narration-panel {
  left: auto;
  right: 12px;
  width: 420px;
  outline: none;
}
.narration-panel header .btn {
  margin-left: 8px;
}
/* One-line purpose under a panel's title row. */
.panel-sub {
  padding: 0 16px 10px;
  border-bottom: 1px solid var(--border-subtle);
}
.narration-body {
  flex: 1;
  overflow-y: auto;
  padding: 8px 10px 14px;
}
.narration-progress {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 8px;
  font-size: var(--text-sm);
  color: var(--text-secondary);
  font-family: var(--font-mono);
}
.narration-confidence {
  margin: 8px 4px;
  padding: 10px 12px;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  background: var(--bg-input);
  font-size: var(--text-sm);
  color: var(--text-secondary);
}
.narration-section {
  margin: 14px 4px 6px;
  font-size: var(--text-xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-tertiary);
}
.narration-item {
  animation: rise-in var(--dur-med) var(--ease-out) backwards;
  transition:
    border-color var(--dur-fast) var(--ease-out),
    background var(--dur-fast) var(--ease-out);
  margin: 6px 2px;
  padding: 10px 12px;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  cursor: default;
}
.narration-item.hl {
  border-color: var(--accent);
  background: var(--bg-input);
}
.narration-item.published {
  opacity: 0.85;
}
.narration-place {
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--text-tertiary);
  margin-bottom: 4px;
}
.narration-text {
  font-size: var(--text-sm);
  line-height: 1.45;
}
.narration-citations {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin-top: 8px;
}
.narration-actions {
  display: flex;
  gap: 6px;
  margin-top: 10px;
}
.narration-reasons {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 8px;
}
.narration-item textarea {
  width: 100%;
  min-height: 84px;
  margin-top: 4px;
  padding: 8px 10px;
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  background: var(--bg-input);
  color: var(--text-primary);
  font-size: var(--text-sm);
  line-height: 1.45;
  resize: vertical;
}
.narration-reasons .chip {
  padding: 2px 10px;
  border: 1px solid var(--border-default);
  border-radius: 999px;
  background: transparent;
  color: var(--text-secondary);
  font-size: var(--text-xs);
  cursor: pointer;
}
.narration-reasons .chip.on {
  border-color: var(--accent);
  color: var(--accent);
}
.citation {
  display: inline-block;
  padding: 1px 8px;
  border-radius: 999px;
  border: 1px solid var(--border-subtle);
  background: var(--bg-input);
  color: var(--text-tertiary);
  font-size: var(--text-xs);
  font-family: var(--font-mono);
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.citation.inferred {
  border-style: dashed;
  color: var(--warning, #d9a03f);
  border-color: color-mix(in srgb, var(--warning, #d9a03f) 45%, transparent);
  background: transparent;
}
.citation.unverified {
  color: var(--danger);
  border-color: color-mix(in srgb, var(--danger) 45%, transparent);
}

/* Canvas Notes layer: approved rationale under each screen. Canvas-scaled
   like frames — readable at working zoom, quiet at far zoom. */
.frame-notes {
  position: absolute;
  display: flex;
  flex-direction: column;
  gap: 18px;
  z-index: 3;
}
/* Notes are the designer speaking, so they look like speech: a bubble with
   a tail pointing up at the screen it's about, one deliberately tight
   corner (the comic-lettering tell), the editorial serif for the voice,
   and a hand-placed lean that alternates down the stack. Not a card. A
   card is what a system emits; a bubble is what a person says. */
.frame-note {
  position: relative;
  padding: 13px 16px 14px;
  border: 1.5px solid color-mix(in srgb, var(--comment) 38%, transparent);
  border-radius: 20px 22px 22px 4px;
  background: color-mix(in srgb, var(--comment) 8%, var(--bg-panel));
  color: var(--text-secondary);
  font-family: var(--font-display);
  font-size: 13.5px;
  line-height: 1.55;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.18), 0 8px 20px rgba(0, 0, 0, 0.12);
  transform: rotate(-0.6deg);
  transition: transform var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out);
}
.frame-note::before {
  content: "";
  position: absolute;
  top: -7px;
  left: 30px;
  width: 13px;
  height: 13px;
  background: inherit;
  border-left: inherit;
  border-top: inherit;
  border-radius: 4px 0 0 0;
  transform: rotate(45deg);
}
.frame-note:nth-child(even) {
  transform: rotate(0.6deg);
  border-radius: 22px 20px 4px 22px;
}
.frame-note:nth-child(even)::before {
  left: auto;
  right: 30px;
}
.frame-note:hover {
  transform: rotate(0deg);
}
/* Click the words, edit the words: the bubble itself becomes the editor.
   Same face, same tail — only the ring says it's live. */
.frame-note textarea {
  width: 100%;
  border: 0;
  background: none;
  resize: vertical;
  font: inherit;
  color: var(--text-primary);
  line-height: inherit;
  padding: 0;
  outline: none;
  min-height: 60px;
}
.frame-note.editing {
  border-color: var(--accent);
  transform: rotate(0deg);
}
.frame-note .note-hint {
  display: block;
  margin-top: 6px;
  font-family: var(--font-sans, inherit);
  font-size: var(--text-xs);
  color: var(--text-tertiary);
}
@media (prefers-reduced-motion: reduce) {
  .frame-note,
  .frame-note:nth-child(even) {
    transform: none;
  }
}
.frame-note .citation {
  margin-left: 8px;
}

/* ---------- Reduced motion ----------
   The entire motion system is an enhancement. When the OS asks for less,
   everything lands instantly; shimmer placeholders become static fills. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

.narration-item:nth-child(n + 2) { animation-delay: 30ms; }
.narration-item:nth-child(n + 4) { animation-delay: 60ms; }

@keyframes pin-pulse {
  50% {
    opacity: 0.55;
  }
}
.pin.pending {
  pointer-events: none;
  animation: pin-pulse 1.1s var(--ease-out) infinite;
}

/* ---------- Titlebar icon language ----------
   Chrome controls are 28px icon buttons in one stroke family (components/
   icons.tsx); labels live in tooltips + the ? cheat sheet. Content surfaces
   stay text-first. */
.icon-btn {
  position: relative;
  width: 28px;
  height: 28px;
  padding: 0;
  justify-content: center;
}
.icon-btn svg {
  flex: none;
}
.count-badge {
  position: absolute;
  top: -3px;
  right: -3px;
  min-width: 14px;
  height: 14px;
  padding: 0 3px;
  border-radius: 999px;
  background: var(--accent);
  color: var(--text-inverse);
  font-size: 9px;
  font-weight: 700;
  line-height: 14px;
  text-align: center;
}
.count-badge.live {
  background: var(--success);
}
.attention-dot {
  position: absolute;
  top: 2px;
  right: 2px;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--warning);
  border: 2px solid var(--bg-panel);
}
/* The name+path block became a button so the row can act as a switcher; it
   must keep reading as text, not as a control, until you're over it. */
.server-open {
  background: none;
  border: 0;
  padding: 0;
  text-align: left;
  font: inherit;
  color: inherit;
  cursor: pointer;
  border-radius: var(--radius-sm);
}
.server-open.plain {
  cursor: default;
}
.server-open:not(.plain):hover .name {
  color: var(--accent);
}

.tb-divider {
  width: 1px;
  height: 18px;
  background: var(--border-default);
  flex: none;
  /* Fixed height in a stretch container would pin it to the top; the toolbars
     it sits in don't all center their children. */
  align-self: center;
  margin: 0 2px;
}
.btn.primary svg {
  color: var(--text-inverse);
}

/* Titlebar avatars sit among 16px stroke icons — 24px reads heavy there.
   Everywhere else (pins, member rows, team lists) stays 24px. */
.titlebar .avatar {
  width: 22px;
  height: 22px;
  font-size: 9px;
}

/* Home titlebar search: compact sibling of the icon buttons. */
.titlebar-search {
  width: 240px;
  padding: 5px 10px;
  font-size: var(--text-sm);
  -webkit-app-region: no-drag;
}

/* Centered home search — macOS-toolbar convention. Absolute so the uneven
   left/right clusters can't pull it off center. */
.titlebar {
  position: relative;
}
.titlebar-search.centered {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  width: 300px;
}

/* Creation lives in the grid: a quiet tile at the end of each workspace's
   row, where the new project will actually land. */
.new-project-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 180px;
  border: 1px dashed var(--border-default);
  border-radius: var(--radius-lg);
  color: var(--text-secondary);
  background: transparent;
  transition:
    border-color var(--dur-fast) var(--ease-out),
    color var(--dur-fast) var(--ease-out),
    background var(--dur-fast) var(--ease-out);
}
.new-project-tile:hover {
  border-color: var(--accent);
  color: var(--text-primary);
  background: var(--bg-panel);
}
.new-project-tile:disabled {
  opacity: 0.6;
}

/* ---------- Popover kit (components/popover.tsx) ----------
   Menus read as content: section labels, identity rows, quiet trailing
   actions, and inputs that exist only while in use. */
.pop-section {
  padding: 10px 14px 4px;
  font-size: var(--text-xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-tertiary);
}
.reveal-trigger {
  display: flex;
  width: fit-content;
  margin: 2px 14px 10px;
  justify-content: flex-start;
  color: var(--text-secondary);
  /* Dashed slot = "something can go here", same language as the new-project
     tile and the avatar-add circle — plain text didn't read as actionable.
     Once the slot is filled (connected), the border goes solid. */
  border: 1px dashed var(--border-strong);
}
/* Outrank .btn.ghost's transparent border. */
.btn.ghost.reveal-trigger {
  border: 1px dashed var(--border-strong);
}
.btn.ghost.reveal-trigger.connected {
  border-style: solid;
}
.btn.ghost.reveal-trigger:hover {
  background: transparent;
  border-color: var(--accent);
  color: var(--accent);
}
.reveal-trigger:hover {
  background: transparent;
  border-color: var(--accent);
  color: var(--accent);
}
.seg button svg {
  display: block;
}
.reveal-form {
  padding: 4px 14px 10px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.workspace-row .reveal-form {
  padding: 4px 0 2px;
}
.reveal-form-row {
  display: flex;
  gap: 6px;
}
.reveal-form-row input {
  flex: 1;
  min-width: 0;
}
.avatar.pending-avatar {
  background: var(--bg-input);
  color: var(--text-tertiary);
  border: 1px dashed var(--border-strong);
}
.quiet-action {
  color: var(--text-tertiary);
  font-size: var(--text-xs);
}
.quiet-action:hover {
  color: var(--danger);
}
.ws-line {
  display: flex;
  align-items: center;
  gap: 8px;
}
.avatar.avatar-add {
  background: transparent;
  border: 1px dashed var(--border-strong);
  color: var(--text-secondary);
  cursor: pointer;
  font-size: 13px;
  transition:
    border-color var(--dur-fast) var(--ease-out),
    color var(--dur-fast) var(--ease-out);
}
.avatar.avatar-add:hover {
  border-color: var(--accent);
  color: var(--accent);
}
.slack-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 2px 10px;
  border-radius: 999px;
  border: 1px solid var(--border-subtle);
  color: var(--text-tertiary);
  font-size: var(--text-xs);
  transition:
    border-color var(--dur-fast) var(--ease-out),
    color var(--dur-fast) var(--ease-out);
}
.slack-chip.on {
  color: var(--text-secondary);
}
.slack-chip:hover,
.slack-chip.active {
  border-color: var(--border-strong);
  color: var(--text-primary);
}
.ws-github {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  margin-top: 8px;
}
.ws-github-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  width: 100%;
  color: var(--text-secondary);
  font-size: var(--text-xs);
}
/* Secondary to the account it sits beside — not a peer of Connect. */
.ws-github-row .btn.ghost {
  padding: 1px 6px;
  font-size: var(--text-xs);
  color: var(--text-tertiary);
}
.ws-github-row .btn.ghost:hover {
  color: var(--text-primary);
}
/* The rows set the left edge here, so drop reveal-trigger's popover inset. */
.ws-github .reveal-trigger {
  margin: 6px 0 0;
}
.pilot-pulse {
  border-top: 1px solid var(--border-subtle);
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding-bottom: 12px;
}
.pilot-pulse .hint {
  padding: 0 14px;
}

/* Share popover link rows: icon, title + plain-speech sub, quiet trailing. */
.link-row {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  text-align: left;
  padding: 9px 14px;
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease-out);
}
.link-row:hover {
  background: var(--bg-hover);
}
.link-row > svg {
  flex: none;
  color: var(--text-tertiary);
}
.lr-text {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}
.lr-title {
  font-size: var(--text-sm);
  font-weight: 500;
}
.lr-sub {
  font-size: var(--text-xs);
  color: var(--text-tertiary);
}
.field-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 0 14px 12px;
}
.select-wrap {
  position: relative;
  display: inline-flex;
  flex: 1;
}
.select-wrap select {
  appearance: none;
  -webkit-appearance: none;
  width: 100%;
  background: var(--bg-input);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-sm);
  padding: 6px 28px 6px 10px;
  color: var(--text-primary);
  font: inherit;
  font-size: var(--text-sm);
  cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease-out);
}
.select-wrap select:hover {
  border-color: var(--border-strong);
}
.select-wrap::after {
  content: "⌄";
  position: absolute;
  right: 10px;
  top: 50%;
  transform: translateY(-60%);
  color: var(--text-tertiary);
  pointer-events: none;
}

/* Inbox as a side panel: a queue to work through, not a menu. */
.inbox-panel {
  width: 380px;
}
.inbox-body {
  flex: 1;
  overflow-y: auto;
}
.inbox-empty {
  padding: 32px 16px;
  text-align: center;
}
.inbox-item {
  display: block;
  width: 100%;
  text-align: left;
  padding: 12px 14px;
  border-bottom: 1px solid var(--border-subtle);
  transition: background var(--dur-fast) var(--ease-out);
}
.inbox-item:hover {
  background: var(--bg-hover);
}
.inbox-item.read {
  opacity: 0.55;
}
.inbox-item-head {
  font-size: var(--text-sm);
  font-weight: 600;
  display: flex;
  gap: 6px;
  align-items: baseline;
}
.inbox-item-body {
  font-size: var(--text-sm);
  color: var(--text-secondary);
  margin-top: 3px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Card rename: pen fades in on hover; the cover's name becomes an input.
   Colors here are cover-local (covers are brand gradients, not theme
   surfaces), matching the existing white-on-gradient name. */
.project-card {
  position: relative;
}
.card-edit {
  position: absolute;
  top: 26px;
  right: 26px;
  z-index: 2;
  opacity: 0;
  /* Ribbon covers vary from sand to charcoal — panel-toned chips stay
     readable over any of them in both themes. */
  color: var(--text-primary);
  background: color-mix(in srgb, var(--bg-panel) 88%, transparent);
  border-color: var(--border-subtle);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.18);
  transition: opacity var(--dur-fast) var(--ease-out);
}
.project-card:hover .card-edit,
.card-edit:focus-visible {
  opacity: 1;
}
.card-edit:hover {
  background: var(--bg-panel);
  color: var(--text-primary);
}

/* Prototype: route drawer + toolbar */
.proto-body {
  flex: 1;
  display: flex;
  min-height: 0;
}
.route-drawer {
  width: 224px;
  flex: none;
  overflow-y: auto;
  border-right: 1px solid var(--border-subtle);
  background: var(--bg-panel);
  padding: 6px 0 12px;
  animation: panel-in-left var(--dur-med) var(--ease-out);
}
.route-row {
  display: flex;
  align-items: baseline;
  gap: 8px;
  width: 100%;
  text-align: left;
  padding: 6px 14px;
  border-left: 2px solid transparent;
  transition: background var(--dur-fast) var(--ease-out);
}
.route-row:hover {
  background: var(--bg-hover);
}
.route-row.on {
  border-left-color: var(--accent);
  background: var(--bg-active);
}
.route-row-title {
  font-size: var(--text-sm);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.route-row-path {
  margin-left: auto;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--text-tertiary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 45%;
}
.proto-current {
  max-width: 200px;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* User tests: real empty states instead of afterthought paragraphs. */
.ut-empty {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
  padding: 18px 4px 12px;
}
.ut-empty strong {
  font-size: var(--text-md);
}
.ut-empty .hint {
  line-height: 1.5;
}

/* Offscreen frames skip paint entirely (explicit sizes make this safe);
   iframes stay alive — only rendering is skipped, not loading. */
.frame {
  content-visibility: auto;
}

.reveal-label {
  font-size: var(--text-sm);
  font-weight: 600;
}

/* A setting's current value: quiet mono, truncated, full value on hover. */
/* The value doubles as a control now, so it has to stop looking like a
   disabled input: no button chrome, but a real hover state and a pointer. */
.setting-value.linked {
  background: none;
  border: 0;
  padding: 0;
  text-align: left;
  cursor: pointer;
  transition: color var(--dur-fast) var(--ease-out);
}
.setting-value.linked:hover {
  color: var(--accent);
  text-decoration: underline;
}
.setting-value {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--text-secondary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 260px;
}

/* Split view switcher: the Prototype segment's device tray extends the
   selected pill itself — same background, same width, joined corners. The
   current device lives in the pill; the tray offers only the others. */
.seg-wrap {
  position: relative;
}
.seg button.menu-open {
  border-bottom-left-radius: 0;
  border-bottom-right-radius: 0;
}
.device-menu {
  /* Anchored to the pill, which sits inset by the seg's 2px padding + 1px
     border; buttons share the pill's padding so widths match intrinsically. */
  position: absolute;
  top: calc(100% - 3px);
  right: 3px;
  display: flex;
  flex-direction: column;
  background: var(--bg-active);
  border-radius: 4px;
  border-top-left-radius: 0;
  border-top-right-radius: 0;
  box-shadow: var(--shadow-panel);
  z-index: 100;
  transform-origin: top;
  animation: unfold var(--dur-fast) var(--ease-out);
}
@keyframes unfold {
  from {
    transform: scaleY(0.8);
    opacity: 0;
  }
}
.device-menu button {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 6px 12px;
  color: var(--text-secondary);
  transition: color var(--dur-fast) var(--ease-out);
}
.device-menu button:hover {
  color: var(--text-primary);
  background: color-mix(in srgb, var(--text-primary) 7%, transparent);
}
.device-menu button:last-child {
  border-bottom-left-radius: 4px;
  border-bottom-right-radius: 4px;
}

/* The locked-state preview field is permanent — no dismiss, no side padding. */
.ut-empty .reveal-form {
  padding: 2px 0 0;
  align-self: stretch;
}
.ut-empty .reveal-form-row .btn.ghost {
  display: none;
}

/* Port viewer rows: dot, name · :port, path in mono, quiet Stop. */
.server-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 14px;
  border-bottom: 1px solid var(--border-subtle);
  font-size: var(--text-sm);
}
.server-row:last-of-type {
  border-bottom: none;
}
.server-row .who {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}
.server-row .name {
  font-weight: 500;
}
.server-row .email {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--text-tertiary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.server-port {
  font-family: var(--font-mono);
  font-weight: 400;
  color: var(--text-secondary);
}

/* Project switcher: name + dev status as one element; recents in the menu. */
.proj-switcher {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 5px 10px;
  border-radius: var(--radius-sm);
  font-size: var(--text-sm);
  color: var(--text-primary);
  transition: background var(--dur-fast) var(--ease-out);
}
.proj-switcher:hover {
  background: var(--bg-hover);
}
.proj-switcher svg {
  color: var(--text-tertiary);
}
.switcher-menu {
  left: 0;
  right: auto;
  width: 260px;
  transform-origin: top left;
}
.switcher-status {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 14px;
  border-bottom: 1px solid var(--border-subtle);
}
.switcher-status > .hint {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Tidy toggle: while the switch is in flight, everything positioned on
   the stage glides between layouts; the class drops right after so drags
   and live updates stay 1:1. */
.layout-anim .frame,
.layout-anim .frame-notes,
.layout-anim .pin,
.layout-anim .frame-farlabel,
.layout-anim .section-region,
.layout-anim .section-label {
  transition:
    left 380ms var(--ease-out),
    top 380ms var(--ease-out),
    width 380ms var(--ease-out),
    height 380ms var(--ease-out);
}

/* Full-bleed cover: flush left/top/right; body content keeps its inset. */
.project-cover {
  position: relative;
  border-radius: 0;
  overflow: hidden;
}
.cover-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.project-card .meta,
.project-card .foot {
  padding: 0 16px;
}
.card-actions {
  position: absolute;
  top: 10px;
  right: 10px;
  z-index: 2;
  display: flex;
  gap: 6px;
}
.card-actions .card-edit {
  position: static;
}
.new-project-tile {
  padding: 0 16px;
}

/* Inline link inside hints (e.g. Slack's webhook guide). */
.hint-link {
  color: var(--accent);
  font-size: inherit;
  padding: 0;
  transition: color var(--dur-fast) var(--ease-out);
}
.hint-link:hover {
  text-decoration: underline;
}

.reveal-trigger {
  display: flex;
  align-items: center;
  gap: 8px;
}
.reveal-trigger svg {
  color: var(--text-tertiary);
  flex: none;
}
.reveal-check {
  color: var(--success);
  display: inline-flex;
}

/* ---------- Home organization: status, pins, archive, needs-you ---------- */

/* A pinned card keeps its pin visible; unpinned cards reveal it on hover. */
.card-edit.pinned {
  opacity: 1;
  color: var(--accent);
}

/* Card foot: left-aligned plain-text signals (no pills), avatars right. */
.foot-signals {
  display: flex;
  gap: 12px;
  align-items: center;
  min-width: 0;
}
.signal {
  font-size: var(--text-xs);
  color: var(--text-secondary);
  white-space: nowrap;
}
.signal.warn {
  color: var(--warning);
}
.signal.comment {
  color: var(--comment);
}
.status-wrap {
  position: relative;
  display: inline-flex;
}
.status-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 2px 0;
  font-size: var(--text-xs);
  color: var(--text-secondary);
  transition: color var(--dur-fast) var(--ease-out);
}
.status-chip:hover {
  color: var(--text-primary);
}
/* Unlabeled projects: the affordance only appears on card hover, and it
   trails the other signals so revealing it never reflows them. */
.status-chip.unset {
  color: var(--text-tertiary);
  opacity: 0;
  transition: opacity var(--dur-fast) var(--ease-out);
}
.project-card:hover .status-chip.unset,
.status-chip.unset:focus-visible {
  opacity: 1;
}
.status-swatch {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  flex: none;
}
.menu-backdrop {
  position: fixed;
  inset: 0;
  z-index: 90;
}
.status-menu {
  position: absolute;
  bottom: calc(100% + 6px);
  left: 0;
  display: flex;
  flex-direction: column;
  padding: 3px;
  min-width: 130px;
  background: var(--bg-panel);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-panel);
  z-index: 95;
  transform-origin: bottom left;
  animation: pop-in var(--dur-fast) var(--ease-out);
}
.status-menu button {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 9px;
  border-radius: 4px;
  font-size: var(--text-sm);
  color: var(--text-secondary);
  text-align: left;
}
.status-menu button:hover {
  background: var(--bg-hover);
  color: var(--text-primary);
}
.status-menu button.on {
  color: var(--text-primary);
  background: var(--bg-active);
}

/* Needs-you strip: a return prompt, quieter than the project grid. */
.needs-you {
  margin-bottom: 28px;
}
.needs-you-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.needs-you-card {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 7px 12px 7px 8px;
  background: var(--bg-panel);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  transition:
    border-color var(--dur-fast) var(--ease-out),
    background var(--dur-fast) var(--ease-out);
}
.needs-you-card:hover {
  border-color: var(--accent);
  background: var(--bg-hover);
}
.ny-swatch {
  width: 30px;
  height: 22px;
  border-radius: 4px;
  flex: none;
}
.ny-name {
  font-size: var(--text-sm);
  color: var(--text-primary);
  font-weight: 500;
}
.ny-reason {
  font-size: var(--text-xs);
  color: var(--accent);
}

/* Archived: tucked behind a per-workspace disclosure, cards dimmed. */
.archived-toggle {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  margin-top: 10px;
  font-size: var(--text-xs);
  color: var(--text-tertiary);
}
.archived-toggle:hover {
  color: var(--text-secondary);
}
.archived-toggle svg {
  transition: transform var(--dur-fast) var(--ease-out);
}
.archived-grid {
  margin-top: 12px;
}
.archived-grid .project-card {
  opacity: 0.55;
  transition: opacity var(--dur-fast) var(--ease-out);
}
.archived-grid .project-card:hover {
  opacity: 1;
}
.restore-btn {
  margin-left: auto;
  font-size: var(--text-xs);
  padding: 2px 8px;
}

/* ---------- Open-project tabs (Figma-style) ---------- */

.tab-strip {
  display: flex;
  align-items: center;
  gap: 2px;
  min-width: 0;
  overflow: hidden;
  -webkit-app-region: no-drag;
}
.tab {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  height: 28px;
  padding: 0 10px;
  border-radius: var(--radius-sm);
  font-size: var(--text-sm);
  color: var(--text-secondary);
  max-width: 170px;
  min-width: 0;
  transition:
    background var(--dur-fast) var(--ease-out),
    color var(--dur-fast) var(--ease-out);
}
.tab:hover {
  background: var(--bg-hover);
  color: var(--text-primary);
}
.tab.active {
  background: var(--bg-active);
  color: var(--text-primary);
  box-shadow: inset 0 0 0 1px var(--border-default);
}
.tab-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.tab-x {
  display: inline-flex;
  align-items: center;
  font-size: 10px;
  color: var(--text-tertiary);
  opacity: 0;
  transition: opacity var(--dur-fast) var(--ease-out);
  flex: none;
}
.tab:hover .tab-x {
  opacity: 1;
}
.tab-x:hover {
  color: var(--text-primary);
}
.tab-home,
.tab-add {
  padding: 0 8px;
  flex: none;
}
.tab-add {
  font-size: var(--text-lg);
  color: var(--text-tertiary);
}

/* Collapsed titlebar search: expands in from the icon, right-aligned. */
/* Grows out of the search icon's footprint — a touch slower than the
   standard entrance so the expansion itself is legible. */
@keyframes search-expand {
  from {
    width: 34px;
    opacity: 0.3;
  }
}
.titlebar-search.flyout {
  width: 240px;
  animation: search-expand 320ms var(--ease-out);
}

/* Prototype toolbar address chip: screen name + path, like a frame header. */
.proto-address {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: var(--text-xs);
  background: var(--bg-input);
  padding: 4px 10px;
  border-radius: 6px;
  max-width: 320px;
  overflow: hidden;
  white-space: nowrap;
}
.proto-address .pa-name {
  color: var(--text-secondary);
  font-weight: 500;
}
.proto-address .pa-path {
  font-family: var(--font-mono);
  color: var(--text-tertiary);
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ── GitHub connection panel ─────────────────────────────────────────────
   Findings read as prose, not as a status table. The dot carries severity so
   the sentence never has to start with "ERROR:", and the verdict sits above
   the evidence because most people want the answer and not the audit. */
.connection-body {
  padding: 14px 16px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.connection-progress {
  margin: 0;
  font-size: var(--text-md);
  color: var(--text-secondary);
}
.connection-verdict {
  margin: 0;
  font-size: var(--text-lg);
  font-weight: 600;
  line-height: 1.35;
}
.connection-finding {
  display: grid;
  grid-template-columns: 10px 1fr;
  gap: 10px;
  align-items: start;
}
.connection-finding .dot {
  width: 8px;
  height: 8px;
  margin-top: 6px;
  border-radius: 50%;
  background: var(--text-tertiary);
}
.connection-finding.ok .dot {
  background: var(--success);
}
.connection-finding.warn .dot {
  background: var(--warning);
}
.connection-finding.blocked .dot {
  background: var(--danger);
}
.connection-title {
  font-size: var(--text-md);
  font-weight: 500;
}
/* Resolved checks are context for the one that matters, so they recede. */
.connection-finding.ok .connection-title {
  font-weight: 400;
  color: var(--text-secondary);
}
.connection-detail {
  margin: 3px 0 0;
  font-size: var(--text-sm);
  color: var(--text-secondary);
  line-height: 1.45;
}
.connection-finding.ok .connection-detail {
  color: var(--text-tertiary);
}
.connection-finding .btn {
  margin-top: 7px;
}
.connection-actions {
  display: flex;
  justify-content: flex-end;
  padding: 12px 16px;
  border-top: 1px solid var(--border-subtle);
}

/* Reads as a link inside the popover's prose, not a third competing button. */
/* An action in a popover is a button, not a hyperlink cosplaying one:
   the text-only look wrapped badly and read as prose (the author's call). */
.diagnose-link {
  align-self: flex-start;
  padding: 4px 10px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  font-size: var(--text-xs);
  color: var(--text-primary);
}

/* ── Publish panel ───────────────────────────────────────────────────────
   The file list is the safeguard, so it gets real estate and a scroll of its
   own rather than a disclosure triangle. */
.publish-body {
  padding: 14px 16px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.publish-summary {
  margin: 0;
  font-size: var(--text-md);
  line-height: 1.45;
}
.publish-warning {
  margin: 0;
  font-size: var(--text-sm);
  line-height: 1.45;
  color: var(--warning);
}
.publish-files {
  margin: 0;
  padding: 6px 0;
  list-style: none;
  max-height: 220px;
  overflow-y: auto;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  background: var(--bg-input);
}
.publish-files li {
  display: flex;
  gap: 10px;
  align-items: baseline;
  padding: 3px 10px;
  font-size: var(--text-sm);
  font-family: var(--font-mono);
}
.publish-files .state {
  flex: none;
  width: 58px;
  color: var(--text-tertiary);
  font-size: var(--text-xs);
}
.publish-files .path {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.publish-files li.risky .path {
  color: var(--warning);
}
.publish-message {
  width: 100%;
}
.publish-error {
  margin: 0;
  font-size: var(--text-sm);
  line-height: 1.45;
  color: var(--danger);
}
.connection-actions {
  gap: 8px;
}

/* States a limit rather than hiding one: a guest who sees no reply box should
   know the link is why, not think the thread is broken. */
.thread-readonly {
  margin: 0;
  padding: 10px 14px;
  font-size: var(--text-sm);
  line-height: 1.45;
  color: var(--text-tertiary);
  border-top: 1px solid var(--border-subtle);
}

/* ── Guest mode (/g/<token>) ─────────────────────────────────────────────
   Almost no chrome: the canvas is the page, and the one bar says whose work
   this is and why some things are missing. */
.guest-shell {
  position: fixed;
  inset: 0;
  display: flex;
  flex-direction: column;
  background: var(--bg-canvas);
}
.guest-bar {
  flex: none;
  display: flex;
  align-items: baseline;
  gap: 12px;
  padding: 10px 16px;
  border-bottom: 1px solid var(--border-subtle);
  background: var(--bg-panel);
}
.guest-project {
  font-weight: 600;
  font-size: var(--text-md);
}
.guest-note {
  font-size: var(--text-sm);
  color: var(--text-tertiary);
}
.guest-canvas {
  flex: 1;
  /* .canvas-wrap is flex:1, so its parent has to be a flex container or it
     collapses to zero height and the canvas renders into nothing. */
  display: flex;
  flex-direction: column;
  position: relative;
  min-height: 0;
}
.guest-message {
  margin: auto;
  padding: 32px;
  max-width: 420px;
  text-align: center;
  color: var(--text-secondary);
}
.guest-message h1 {
  margin: 0 0 8px;
  font-size: var(--text-lg);
  color: var(--text-primary);
}

/* ── Archive send-off ────────────────────────────────────────────────────
   Pure chrome, zero layout: the overlay sits above everything, ignores the
   mouse, and removes itself. Particle colors are inline (the project's own
   brand), so only the bones live here. */
.celebrate-overlay {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 200;
  /* Isolate the burst from the page: its layout and paint can never dirty
     the grid underneath, so the card list isn't recalculated mid-animation. */
  contain: strict;
}
.celebrate-particle {
  position: absolute;
  will-change: transform, opacity;
}
.vacuum-void {
  position: absolute;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  /* A point of nothing: darker than the canvas, with a faint event-horizon
     edge so it reads on both themes. */
  background: radial-gradient(circle, rgb(0 0 0 / 0.92) 55%, rgb(0 0 0 / 0) 72%);
  box-shadow: 0 0 0 1.5px color-mix(in srgb, var(--text-primary) 35%, transparent);
  will-change: transform, opacity;
}
.vacuum-ghost {
  position: absolute;
  margin: 0;
  pointer-events: none;
  will-change: transform, opacity;
  /* The clone is a snapshot, not a live card — its subtree never needs to
     re-lay-out, and promoting it once keeps the whole scale on the GPU. */
  contain: layout paint;
  backface-visibility: hidden;
}

/* ── Guest composer ──────────────────────────────────────────────────────
   Two fields and a button: name once, message every time. Reuses input
   chrome from the app so a guest's composer feels native, not bolted on. */
.guest-composer {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 10px 14px;
  border-top: 1px solid var(--border-subtle);
}
.guest-composer input,
.guest-composer textarea {
  width: 100%;
  background: var(--bg-input);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  color: var(--text-primary);
  font-size: var(--text-sm);
  padding: 7px 10px;
  font-family: inherit;
  resize: vertical;
}
.guest-composer-name {
  max-width: 200px;
}
.guest-composer-error {
  font-size: var(--text-xs);
  color: var(--danger);
}
.guest-composer-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
}

/* ── Deploy history ──────────────────────────────────────────────────────
   Rows are links to old versions, so they behave like rows, not buttons. */
.deploy-history {
  display: flex;
  flex-direction: column;
  max-height: 260px;
  overflow-y: auto;
  margin: 0 -6px;
}
.deploy-row {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 5px 6px;
  background: none;
  border: 0;
  border-radius: var(--radius-sm);
  cursor: pointer;
  font-size: var(--text-sm);
  color: var(--text-secondary);
  text-align: left;
}
.deploy-row:hover {
  background: var(--bg-hover);
  color: var(--text-primary);
}
.deploy-dot {
  flex: none;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--text-tertiary);
  align-self: center;
}
.deploy-dot.prod {
  background: var(--success);
}
.deploy-when {
  flex: none;
  width: 64px;
  color: var(--text-tertiary);
  font-size: var(--text-xs);
}
.deploy-branch {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.deploy-sha {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--text-tertiary);
}
.deploy-current {
  margin-left: auto;
  font-size: var(--text-xs);
  color: var(--success);
}
.guest-reload {
  margin-left: auto;
}

/* Cloud-agent setup checklist inside the transcript. */
.cloud-setup p { margin: 0 0 6px; }
.cloud-setup ol { margin: 0 0 6px 18px; padding: 0; }
.cloud-setup li { margin-bottom: 4px; }
.cloud-setup code {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  background: var(--bg-input);
  padding: 1px 5px;
  border-radius: var(--radius-sm);
}
/* Recruit toggle reads as one control: box, label, then the rate. */
.test-recruit {
  display: flex;
  align-items: center;
  gap: 7px;
  cursor: pointer;
  white-space: nowrap;
}

/* ── Flow view ───────────────────────────────────────────────────────────
   The edge layer lives in stage coordinates; overflow must stay visible or
   pan clips every path at the origin tile. */
.flow-edges {
  position: absolute;
  left: 0;
  top: 0;
  overflow: visible;
  pointer-events: none;
}
.edge-delete {
  background: var(--bg-panel);
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  font-size: var(--text-xs);
  padding: 4px 12px;
  box-shadow: var(--shadow-panel);
  color: var(--danger);
  white-space: nowrap;
}
.flow-edge.selected .flow-edge-label {
  font-weight: 600;
}
.flow-edge-label {
  fill: var(--text-secondary);
  font-size: 13px;
  font-family: var(--font-mono);
  paint-order: stroke;
  stroke: var(--bg-canvas);
  stroke-width: 4px;
}
.flow-bar {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 8px 16px;
  border-bottom: 1px solid var(--border-subtle);
  background: var(--bg-panel);
}
.flow-bar .hint {
  flex: 1;
}

/* Destructive ghost button — restrained until hovered, so it never shouts. */
.btn.ghost.danger {
  color: var(--danger);
}
.btn.ghost.danger:hover {
  background: color-mix(in srgb, var(--danger) 14%, transparent);
}
.delete-warn {
  color: var(--danger);
  /* The most consequential sentence in the app was set smaller than
     "archived 3d ago". It reads at least as loud as what it replaced. */
  font-size: var(--text-sm);
  line-height: 1.35;
}

/* ── Flow v2: connect form + crawl review ────────────────────────────────── */
.flow-connect {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 16px;
  border-bottom: 1px solid var(--border-subtle);
  background: var(--bg-panel);
  flex-wrap: wrap;
}
.flow-connect select,
.flow-connect input {
  background: var(--bg-input);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-sm);
  color: var(--text-primary);
  font-size: var(--text-sm);
  padding: 4px 8px;
}
.flow-review {
  width: 640px;
  max-width: calc(100vw - 48px);
}
.flow-review-body {
  padding: 12px 16px;
  display: flex;
  flex-direction: column;
  gap: 14px;
  max-height: 70vh;
  overflow-y: auto;
}
.flow-proposal {
  display: grid;
  grid-template-columns: 200px 1fr;
  gap: 14px;
  align-items: start;
}
.flow-proposal img {
  width: 200px;
  border-radius: var(--radius-md);
  border: 1px solid var(--border-subtle);
}
.flow-proposal-title {
  font-weight: 600;
  font-size: var(--text-md);
}
.flow-proposal-actions {
  display: flex;
  gap: 8px;
  margin-top: 10px;
}

/* A state frame is a condition of a route, not a route — say so on the frame. */
.badge.state {
  background: color-mix(in srgb, var(--warning) 22%, transparent);
  color: var(--warning);
}

/* ── Monorepo app picker ─────────────────────────────────────────────────
   Which app in this repo is this project? Asked once, at creation, when a
   repo turns out to hold more than one app — a web app and a mobile app are
   two right answers, and picking silently is how you end up looking at web
   pages when you expected a phone. */
/* In a dialog the form sits under a header, so it takes the card's gutters
   rather than the popover's tighter ones. */
.overlay-card .reveal-form {
  padding: 12px 16px 16px;
  gap: 8px;
}
.repo-apps {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin: 2px -6px 0;
}
.repo-app {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 5px 6px;
  background: none;
  border: 0;
  border-radius: var(--radius-sm);
  cursor: pointer;
  text-align: left;
  font-size: var(--text-sm);
  color: var(--text-secondary);
}
.repo-app:hover:not(:disabled) {
  background: var(--bg-hover);
  color: var(--text-primary);
}
.repo-app:disabled {
  cursor: default;
}
.repo-app-name {
  font-weight: 500;
}
.repo-app-kind {
  font-size: var(--text-xs);
  color: var(--text-tertiary);
}
/* "suggested" / "reading…" — a recommendation and a progress note, not a
   success, so they take the app's accent rather than the green. */
.repo-app-current {
  margin-left: auto;
  font-size: var(--text-xs);
  color: var(--accent);
}

.variant-booting {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  text-align: center;
  color: var(--text-tertiary);
  font-size: var(--text-xs);
}

/* ── Exploration layer ───────────────────────────────────────────────────
   Stamps, dots, and the what-if spark on frame headers. Present stamps show
   always; the add-palette reveals on header hover, so a quiet canvas stays
   quiet and an inhabited one wears its judgment. */
.stamp {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  padding: 2px 7px;
  border: 1px solid transparent;
  border-radius: 999px;
  background: none;
  cursor: pointer;
  font-size: 13.5px;
  line-height: 1.4;
}
.stamp i {
  font-style: normal;
  font-size: var(--text-xs);
  color: var(--text-secondary);
}
.stamp.mine {
  border-color: color-mix(in srgb, var(--accent) 45%, transparent);
  background: color-mix(in srgb, var(--accent) 12%, transparent);
}
.stamp.vote {
  color: var(--accent);
  font-size: 12px;
}
.stamp.ghost {
  /* Present at rest, awake on hover. Fully hidden tested as undiscoverable —
     the layer might as well not exist if nobody ever hovers a header. */
  opacity: 0.4;
  filter: grayscale(1);
  transition: opacity var(--dur-fast) var(--ease-out), filter var(--dur-fast) var(--ease-out);
}
.frame-header:hover .stamp.ghost {
  opacity: 0.85;
}
.frame-header .stamp.ghost:hover {
  opacity: 1;
  filter: none;
}
.stamp.ghost.whatif {
  color: var(--accent);
  filter: none;
  font-size: 15px;
  opacity: 0.55;
}
.frame-actions {
  display: inline-flex;
  align-items: center;
  gap: 1px;
}
.badge.variant {
  background: color-mix(in srgb, var(--accent) 20%, transparent);
  color: var(--accent);
}

/* ── First-ever screen: one centered composition ─────────────────────────
   A stranded tile top-left plus a floating hint saying the same thing was
   two half-empty-states; a brand-new account gets one whole one. */
.empty-hero {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: 10px;
  padding-bottom: 8vh;
}
.empty-hero h2 {
  font-family: var(--font-display);
  font-size: 24px;
  font-weight: 600;
}
.empty-hero .hint {
  max-width: 46ch;
}
.empty-hero-tile {
  margin-top: 14px;
  width: 380px;
  padding: 36px 20px;
}

/* ── Getting started (first-run checklist) ───────────────────────────────
   Self-completing: checks come from lib/firsts marks at the real action
   sites, never from a Next button. Lives at the head of the home grid,
   retires itself when done or dismissed. */
.getting-started {
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-lg);
  padding: 14px 18px 16px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  /* Sized to its three lines, not to the page: full-bleed made it read as
     a giant empty tile and crowded the first section heading beneath it. */
  max-width: 600px;
  margin-bottom: 30px;
}
/* Over the canvas: a quiet panel bottom-left, solid so the dot grid doesn't
   swallow the dashes, clear of the toolbar and minimap. */
.getting-started.in-project {
  position: absolute;
  left: 14px;
  bottom: 64px;
  width: 320px;
  margin-bottom: 0;
  background: var(--bg-panel);
  border-style: solid;
  border-color: var(--border-default);
  box-shadow: var(--shadow-panel);
  z-index: 24;
}
.gs-head {
  display: flex;
  align-items: center;
  gap: 10px;
  font-weight: 600;
}
.gs-head > :first-child {
  flex: 1;
}
.gs-phase {
  color: var(--accent);
  font-weight: 500;
}
.gs-count {
  font-size: var(--text-xs);
  color: var(--text-tertiary);
  font-weight: 500;
}
.gs-track {
  height: 3px;
  border-radius: 2px;
  background: var(--bg-input);
  overflow: hidden;
}
.gs-track span {
  display: block;
  height: 100%;
  background: var(--accent);
  border-radius: 2px;
  transition: width var(--dur-med) var(--ease-out);
}
.gs-step {
  display: flex;
  gap: 10px;
  align-items: baseline;
}
.gs-step.done {
  opacity: 0.55;
}
.gs-step .hint {
  display: block;
}
.gs-check {
  color: var(--accent);
  font-weight: 600;
}

/* ── Welcome doors ─────────────────────────────────────────────────── */
.welcome-doors {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 14px 0 4px;
}
.welcome-door {
  text-align: left;
  padding: 12px 14px;
  background: var(--bg-input);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  cursor: pointer;
  display: flex;
  flex-direction: column;
  gap: 3px;
  transition: border-color var(--dur-fast) var(--ease-out), background var(--dur-fast) var(--ease-out);
}
.welcome-door:hover {
  border-color: var(--accent);
  background: var(--bg-hover);
}
.welcome-door span {
  color: var(--text-secondary);
  font-size: var(--text-sm);
}

/* ── Sticker mode dock ───────────────────────────────────────────────────
   The palette while S-mode is on: docked above the toolbar, one selection,
   the hint says the contract. Reuses the bloom-item glyph buttons. */
.sticker-dock {
  position: absolute;
  bottom: 64px;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  gap: 2px;
  padding: 6px 12px;
  background: var(--bg-panel);
  border: 1px solid var(--border-default);
  border-radius: 999px;
  box-shadow: var(--shadow-panel);
  z-index: 40;
  animation: pop-in var(--dur-med) var(--ease-out);
}
.sticker-dock .hint {
  margin-left: 8px;
}
.bloom-item.selected {
  background: color-mix(in srgb, var(--accent) 22%, transparent);
  transform: scale(1.15);
}
.sticker-toggle {
  font-size: 15px;
}

/* ── Thrown stickers + the bloom palette ─────────────────────────────────
   Reactions are placed objects, not header state: right-click blooms a
   palette AT the cursor, picking one sticks it where you aimed, with a
   small drop. Fun lives in gesture; chrome is where fun goes to die. */
.bloom-scrim {
  position: fixed;
  inset: 0;
  z-index: 60;
}
.bloom-palette {
  position: fixed;
  transform: translate(-50%, -110%);
  display: flex;
  gap: 2px;
  padding: 5px 7px;
  background: var(--bg-panel);
  border: 1px solid var(--border-default);
  border-radius: 999px;
  box-shadow: var(--shadow-panel);
  animation: bloom-in 140ms var(--ease-out);
}
@keyframes bloom-in {
  from {
    opacity: 0;
    transform: translate(-50%, -110%) scale(0.7);
  }
  to {
    opacity: 1;
    transform: translate(-50%, -110%) scale(1);
  }
}
.bloom-item {
  width: 30px;
  height: 30px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 15px;
  background: none;
  border: 0;
  border-radius: 50%;
  cursor: pointer;
  transition: transform var(--dur-fast) var(--ease-out), background var(--dur-fast) var(--ease-out);
}
.bloom-item:hover {
  background: var(--bg-hover);
  transform: scale(1.25);
}
.bloom-item.whatif {
  color: var(--accent);
}
.sticker-layer {
  position: absolute;
  inset: 0;
  z-index: 4;
  pointer-events: none;
}
.sticker-layer > * {
  pointer-events: auto;
}
.sticker.thrown {
  position: absolute;
  transform: translate(-50%, -50%) rotate(-4deg);
  font-size: 22px;
  background: none;
  border: 0;
  cursor: default;
  filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.3));
  /* Squash-and-stretch landing: overshoot in, one wiggle, settle. */
  animation: sticker-land 420ms cubic-bezier(0.34, 1.56, 0.64, 1);
}
.sticker-art {
  width: 34px;
  height: 34px;
  display: block;
  pointer-events: none;
}
.sticker.thrown .sticker-art {
  width: 44px;
  height: 44px;
}
.sticker.thrown.peeling {
  animation: sticker-peel 240ms var(--ease-out) forwards;
}
@keyframes sticker-land {
  0% {
    opacity: 0;
    transform: translate(-50%, -60%) scale(1.7) rotate(-14deg);
  }
  55% {
    opacity: 1;
    transform: translate(-50%, -50%) scale(0.88) rotate(2deg);
  }
  75% {
    transform: translate(-50%, -50%) scale(1.06) rotate(-7deg);
  }
  100% {
    transform: translate(-50%, -50%) scale(1) rotate(-4deg);
  }
}
@keyframes sticker-peel {
  0% {
    transform: translate(-50%, -50%) rotate(-4deg) scale(1);
    opacity: 1;
  }
  60% {
    transform: translate(-50%, -62%) rotate(10deg) scale(1.12);
    opacity: 1;
    filter: drop-shadow(0 10px 12px rgba(0, 0, 0, 0.35));
  }
  100% {
    transform: translate(-50%, -110%) rotate(24deg) scale(0.6);
    opacity: 0;
  }
}
.sticker-ghost {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 80;
  pointer-events: none;
  opacity: 0.85;
  filter: drop-shadow(0 3px 6px rgba(0, 0, 0, 0.25));
}
.sticker-ghost .sticker-art {
  width: 40px;
  height: 40px;
}
.sticker.thrown:nth-child(even) {
  transform: translate(-50%, -50%) rotate(5deg);
}
.sticker.thrown.mine {
  cursor: pointer;
}
@keyframes sticker-drop {
  from {
    opacity: 0;
    transform: translate(-50%, -50%) scale(1.8) rotate(-12deg);
  }
  to {
    opacity: 1;
    transform: translate(-50%, -50%) scale(1) rotate(-4deg);
  }
}
.sticker-cluster {
  position: absolute;
  left: 8px;
  bottom: 8px;
  display: flex;
  gap: 4px;
}
.gif-sticker.thrown {
  transform: translate(-50%, -50%) rotate(2deg);
  animation: sticker-drop 260ms var(--ease-out);
}
@media (prefers-reduced-motion: reduce) {
  .bloom-palette,
  .sticker.thrown,
  .gif-sticker.thrown {
    animation: none;
  }
}

.giphy-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 6px;
}
.giphy-grid img {
  width: 100%;
  height: 72px;
  object-fit: cover;
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: transform var(--dur-fast) var(--ease-out);
}
.giphy-grid img:hover {
  transform: scale(1.05);
}

.note-edit {
  float: right;
  margin: -2px -4px 2px 6px;
  padding: 2px 5px;
  background: none;
  border: 0;
  border-radius: var(--radius-sm);
  color: var(--text-tertiary);
  cursor: pointer;
  opacity: 0;
  transition: opacity var(--dur-fast) var(--ease-out);
}
/* ── Panel glow ──────────────────────────────────────────────────────────
   A warm point of light under the cursor on panels and popovers. Parented to
   <body> in viewport coordinates rather than to the panel, so it spills past
   the panel's edges instead of being clipped dead at the border by its scroll
   container — a lit surface, not a rectangle full of gradient.

   Bigger and fainter than the first pass: at this size the falloff does the
   work, and anything stronger competes with the content it's meant to light. */
.panel-glow {
  position: fixed;
  left: 0;
  top: 0;
  width: 460px;
  height: 460px;
  margin: -230px 0 0 -230px;
  border-radius: 50%;
  pointer-events: none;
  /* Above the panels it lights (popovers sit at 100) but below modal scrims,
     so it never washes over a dialog. */
  z-index: 120;
  opacity: 0;
  will-change: transform, opacity;
  transform: translate3d(-9999px, -9999px, 0);
  background: radial-gradient(
    circle at center,
    color-mix(in srgb, var(--accent) 9%, transparent) 0%,
    color-mix(in srgb, var(--accent) 4%, transparent) 40%,
    transparent 72%
  );
  /* Unhurried both ways. Cutting the light the instant a cursor twitched read
     as a flicker; now it dims where it stood while you move on. */
  transition: opacity 650ms var(--ease-out);
}
.panel-glow.on {
  opacity: 1;
  transition: opacity 900ms var(--ease-out);
}


/* ── Restored (excision casualties, recovered from v0.2.136) ──────────────
   Two stylesheet amputations took bystanders: the v0.2.137 duplicate-region
   excision (coverage panel, mono, linking-snippet, gif stack) and the
   v0.2.140 reaction-bar removal (the emoji burst). A selector-level diff
   against the old tag found every casualty; this block restores them. */







.gif-stack {
  position: absolute;
  right: 8px;
  bottom: 8px;
  display: flex;
  align-items: flex-end;
  gap: 6px;
  z-index: 4;
}

.gif-more {
  font-size: var(--text-xs);
  color: var(--text-tertiary);
}

.coverage-body {
  flex: 1;
  overflow-y: auto;
  padding: 12px 14px;
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.coverage-row.headline {
  font-size: var(--text-md);
  color: var(--text-primary);
}

.coverage-section {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.coverage-item {
  color: var(--text-secondary);
  font-size: var(--text-xs);
}

.mono {
  font-family: var(--font-mono);
}

.linking-snippet {
  margin: 4px 0 0;
  padding: 8px 10px;
  background: var(--bg-input);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  line-height: 1.45;
  color: var(--text-secondary);
  max-height: 200px;
  overflow: auto;
  white-space: pre;
}


/* ── The emoji burst (canonical, single copy) ──────────────────────────── */
.emoji-burst {
  position: fixed;
  z-index: 200;
  pointer-events: none;
}
.emoji-burst span {
  position: absolute;
  font-size: 18px;
  animation: burst-bit 650ms var(--ease-out) forwards;
}
@keyframes burst-bit {
  from {
    opacity: 1;
    transform: translate(0, 0) scale(1);
  }
  to {
    opacity: 0;
    transform: translate(var(--dx), var(--dy)) scale(0.4);
  }
}

/* ── Toasts — the alert() retirement. One-line notices that never steal
      focus: stack bottom-center, self-dismiss, click to dismiss sooner. ── */
.toast-host {
  position: fixed;
  bottom: 24px;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  z-index: 400;
  pointer-events: none;
}
.toast {
  pointer-events: auto;
  display: flex;
  align-items: center;
  gap: 14px;
  max-width: 440px;
  padding: 10px 16px;
  border-radius: 10px;
  background: var(--bg-panel-raised);
  border: 1px solid var(--border-strong);
  color: var(--text-primary);
  font-size: var(--text-sm);
  line-height: 1.5;
  box-shadow: 0 10px 32px rgba(0, 0, 0, 0.5);
  animation: toast-in 260ms cubic-bezier(0.2, 0.9, 0.3, 1.15);
}
.toast-action {
  flex-shrink: 0;
  border: none;
  background: transparent;
  color: var(--accent);
  font-size: var(--text-sm);
  font-weight: 600;
  cursor: pointer;
  padding: 2px 6px;
  border-radius: 6px;
}
.toast-action:hover {
  background: var(--accent-muted);
  color: var(--accent-hover);
}
.toast.error {
  border-color: var(--danger);
}
.toast.leaving {
  opacity: 0;
  transform: translateY(8px);
  transition:
    opacity 240ms ease,
    transform 240ms ease;
}
@keyframes toast-in {
  from {
    opacity: 0;
    transform: translateY(12px) scale(0.96);
  }
}

/* A frame whose Figma render failed carries the fix in its header. */
.frame-fix {
  padding: 1px 8px;
  font-size: var(--text-xs);
  border: 1px solid var(--accent);
  border-radius: 6px;
  color: var(--accent);
  background: var(--accent-muted);
  cursor: pointer;
  white-space: nowrap;
}
.frame-fix:hover {
  color: var(--accent-hover);
  border-color: var(--accent-hover);
}
.gs-tip {
  display: block;
  margin-top: 10px;
  padding-top: 8px;
  border-top: 1px solid var(--border-subtle);
}

/* ── Option-drag frame duplication ───────────────────────────────────────
   copy badge, the drag ghost, and the remove control on a copy's header. */
.badge.copy {
  background: color-mix(in srgb, var(--text-secondary) 20%, transparent);
  color: var(--text-secondary);
}
.frame-ghost {
  position: absolute;
  border: 1.5px dashed var(--accent);
  border-radius: 8px;
  background: color-mix(in srgb, var(--accent) 8%, transparent);
  display: flex;
  align-items: flex-start;
  padding: 6px 10px;
  font-size: var(--text-xs);
  color: var(--accent);
  pointer-events: none;
  z-index: 20;
}
.frame-del {
  border: none;
  background: transparent;
  color: var(--text-tertiary);
  cursor: pointer;
  font-size: 11px;
  line-height: 1;
  padding: 2px 4px;
  border-radius: 4px;
  opacity: 0;
  transition: opacity 120ms ease;
}
/* The delete control stays out of the way until you're on the frame. */
.frame-header:hover .frame-del,
.frame.focused .frame-del {
  opacity: 1;
}
.frame-del:hover {
  color: var(--danger);
  background: color-mix(in srgb, var(--danger) 14%, transparent);
}

/* ── Brand layer (2026-09-27): the trycommons.app language, carried in ────
   Pressable buttons, die-cut stickers, the marker hand for tips and empty
   states, and a front door that looks like the site. The canvas and dense
   toolbars stay calm on purpose: the screens people review are the hero.
   Fonts come from lib/fonts.ts; the tokens from theme.css. */

/* Pressable buttons. Secondary rests on a soft ledge; primary wears the
   bright ember with ink type and an ink edge it sinks into. Ghost (toolbar)
   buttons stay flat. */
.btn {
  border-radius: 8px;
  border-color: var(--border-strong);
  box-shadow: 0 2px 0 var(--border-strong);
  transition:
    background var(--dur-fast) var(--ease-out),
    border-color var(--dur-fast) var(--ease-out),
    color var(--dur-fast) var(--ease-out),
    transform 120ms var(--ease-out),
    box-shadow 120ms var(--ease-out);
}
.btn:hover:not(:disabled) {
  transform: translateY(-1px);
  box-shadow: 0 3px 0 var(--border-strong);
}
.btn:active:not(:disabled) {
  transform: translateY(2px);
  box-shadow: 0 0 0 var(--border-strong);
}
.btn.primary {
  background: var(--btn-primary-bg);
  border: 1.5px solid var(--btn-edge);
  color: var(--btn-primary-ink);
  font-weight: 600;
  box-shadow: 0 2px 0 var(--btn-ledge);
}
.btn.primary:hover:not(:disabled) {
  background: var(--btn-primary-bg-hover);
  box-shadow: 0 3px 0 var(--btn-ledge);
}
.btn.primary:active:not(:disabled) {
  box-shadow: 0 0 0 var(--btn-ledge);
}
.btn.ghost,
.btn.ghost:hover:not(:disabled),
.btn.active {
  box-shadow: none;
  transform: none;
}
.btn.ghost:active:not(:disabled) {
  box-shadow: none;
  transform: scale(0.97);
}
.btn:disabled {
  transform: none;
}

/* Die-cut stickers: a white edge and a soft drop, as on the site. */
.sticker.thrown {
  filter: drop-shadow(1.5px 0 0 #fff) drop-shadow(-1.5px 0 0 #fff) drop-shadow(0 1.5px 0 #fff)
    drop-shadow(0 -1.5px 0 #fff) drop-shadow(0 4px 6px rgba(0, 0, 0, 0.28));
}
.sticker-ghost {
  filter: drop-shadow(1.5px 0 0 #fff) drop-shadow(-1.5px 0 0 #fff) drop-shadow(0 1.5px 0 #fff)
    drop-shadow(0 -1.5px 0 #fff) drop-shadow(0 4px 8px rgba(0, 0, 0, 0.22));
}

/* The marker hand: tips and empty states only. Controls inside them keep
   the interface face, and nobody's own words are ever set in it. */
.gs-tip,
.empty-hero .hint,
.empty-state,
.sticker-dock .hint {
  font-family: var(--font-hand);
  font-weight: 500;
}
.gs-tip {
  font-size: 13.5px;
}
.empty-hero .hint {
  font-size: 15px;
  line-height: 1.5;
}
.empty-state {
  font-size: 15px;
}
.empty-state button,
.empty-hero .hint button,
.gs-tip button,
.gs-tip kbd {
  font-family: var(--font-ui);
}

/* The front door (sign-in, setup): the site's paper-and-dots canvas and a
   card built like its sheets, with a sticker slapped on the corner. */
.center-screen {
  background-color: var(--bg-app);
  background-image: radial-gradient(circle, var(--canvas-dot) 1.2px, transparent 1.4px);
  background-size: 24px 24px;
}
.center-card {
  position: relative;
  width: 400px;
  padding: 32px;
  border: 1.5px solid var(--btn-edge);
  border-radius: 20px;
  box-shadow:
    0 6px 0 var(--btn-ledge),
    0 24px 48px rgba(0, 0, 0, 0.12);
}
.center-card h1 {
  font-size: 34px;
  letter-spacing: -0.02em;
  line-height: 1.05;
}
.center-card .lede {
  font-size: var(--text-lg);
}
.center-card::before {
  content: "\1F525" / "";
  position: absolute;
  top: -20px;
  right: -16px;
  font-size: 38px;
  line-height: 1;
  transform: rotate(14deg);
  pointer-events: none;
  filter: drop-shadow(1.5px 0 0 #fff) drop-shadow(-1.5px 0 0 #fff) drop-shadow(0 1.5px 0 #fff)
    drop-shadow(0 -1.5px 0 #fff) drop-shadow(0 5px 7px rgba(0, 0, 0, 0.25));
}
.center-card .btn {
  justify-content: center;
}
.center-card .btn.primary {
  padding: 10px 14px;
  font-size: var(--text-lg);
}

/* ── Brand layer, part two (2026-09-28): the working chrome ──────────────
   Part one dressed the edges, and the canvas screen still read as the old
   app. This carries the site's one structural idea into the tool: anything
   you can pick up has an ink edge and a ledge. That means the screens on
   the canvas (sheets on a table), everything that floats over them, and the
   tab you're on. The view switcher becomes a row of keys with the current
   one pressed in ink, Share is the one ember action in the project chrome,
   and section names speak Fraunces. The user's own screens stay unpainted. */

/* Screens on the canvas. Canvas units on purpose: the edge thins to a
   hairline as you zoom out, the way a printed sheet would. */
.frame {
  border-color: var(--sheet-edge);
  border-radius: 12px;
  box-shadow:
    0 0 0 1px var(--sheet-edge),
    0 6px 0 1px var(--sheet-ledge);
}
.frame.focused {
  border-color: var(--sheet-edge);
  box-shadow:
    0 0 0 5px var(--btn-primary-bg),
    0 0 0 6px var(--sheet-edge),
    0 10px 0 6px var(--sheet-ledge);
}
.frame-header {
  border-radius: 11px 11px 0 0;
  background: var(--bg-panel);
  border-bottom-color: var(--border-default);
  color: var(--text-tertiary);
}
.frame-header > span:first-child {
  color: var(--text-primary);
  font-weight: 600;
}
.frame-body {
  border-radius: 0 0 11px 11px;
}
.frame-ghost {
  border-radius: 12px;
}
.section-label {
  font-family: var(--font-display);
  font-size: 24px;
  font-weight: 600;
  line-height: 1.1;
  letter-spacing: -0.01em;
  text-transform: none;
  color: var(--text-primary);
}
.section-label::first-letter {
  text-transform: uppercase;
}

/* Everything that floats is a sheet. */
.titlebar-popover,
.mention-menu,
.proto-notes,
.user-tests,
.palette,
.compare-modal,
.thread-panel,
.agent-panel,
.overlay-card,
.status-menu,
.getting-started.in-project,
.canvas-toolbar,
.minimap,
.toast,
.update-chip,
.heatmap-chip,
.sticker-dock,
.bloom-palette {
  border: 1.5px solid var(--sheet-edge);
  box-shadow: var(--shadow-sheet);
}
.titlebar-popover,
.proto-notes,
.user-tests,
.palette,
.compare-modal,
.thread-panel,
.agent-panel,
.overlay-card,
.getting-started.in-project,
.canvas-toolbar,
.minimap {
  border-radius: var(--radius-sheet);
}
.mention-menu,
.status-menu,
.toast {
  border-radius: 10px;
}

/* The canvas dock: a toggled mode is a key held down, in ink. */
.canvas-toolbar {
  padding: 5px;
  gap: 3px;
}
.canvas-toolbar .btn.active,
.canvas-toolbar .btn.active:hover {
  background: var(--text-primary);
  border-color: transparent;
  color: var(--bg-panel);
}
.canvas-toolbar .zoom {
  font-weight: 600;
  color: var(--text-secondary);
}

/* The view switcher: a quiet trough with the current view pressed in ink.
   No outline here: a heavy edge on controls inside the bars read as clunky
   beside the ghost tools, so edges stay on things that float. The border
   stays, transparent, so the device tray's offsets hold. The Prototype
   key's tray hangs from its left edge now that Prototype comes first (it
   had been anchored right since Flow joined the row, so it opened under
   Flow). */
.seg {
  background: var(--bg-input);
  border: 1.5px solid transparent;
  border-radius: 10px;
  gap: 2px;
}
.seg button {
  border-radius: 7px;
}
.seg button:hover:not(.on) {
  background: color-mix(in srgb, var(--text-primary) 7%, transparent);
  color: var(--text-primary);
}
.seg button.on {
  background: var(--text-primary);
  color: var(--bg-panel);
}
.device-menu {
  left: 3.5px;
  right: auto;
  top: calc(100% - 3.5px);
  background: var(--text-primary);
  border-radius: 0 0 7px 7px;
}
.device-menu button {
  color: color-mix(in srgb, var(--bg-panel) 70%, transparent);
}
.device-menu button:hover {
  color: var(--bg-panel);
  background: color-mix(in srgb, var(--bg-panel) 14%, transparent);
}
.device-menu button:last-child {
  border-radius: 0 0 7px 7px;
}

/* The tab you're on: a quiet sand pill, for the same reason. */
.tab {
  border-radius: 8px;
}
.tab.active {
  background: var(--bg-active);
  color: var(--text-primary);
  font-weight: 600;
  box-shadow: none;
}

/* Share: the project chrome's one ember action. It stays pressed while its
   popover is open. */
.subnav .share-btn {
  height: 28px;
  padding: 0 12px 0 10px;
  border-radius: 8px;
}
.btn.primary svg {
  color: inherit;
}
.btn.primary[aria-expanded="true"],
.btn.primary[aria-expanded="true"]:hover {
  transform: translateY(2px);
  box-shadow: 0 0 0 var(--btn-ledge);
}

/* The running app in Prototype sits on the same ledge as the canvas screens. */
.proto-device {
  border: 1.5px solid var(--sheet-edge);
  border-radius: var(--radius-sheet);
  box-shadow: 0 6px 0 var(--sheet-ledge);
}

/* Home: project cards. The cover is a little table holding the project's
   own screens, each shaped like its frame (components/ScreensCover). At rest
   the card is quiet, a hairline and a soft shadow; picking it up (hover)
   brings the sheet edge and ledge. */
.home-header h1 {
  font-size: 30px;
  letter-spacing: -0.01em;
}
.project-card {
  gap: 0;
  padding: 0 0 14px;
  border: 1px solid var(--border-default);
  border-radius: 16px;
  box-shadow: var(--shadow-card);
}
.project-card:hover {
  border-color: var(--sheet-edge);
  background: var(--bg-panel);
  transform: translateY(-3px);
  box-shadow: 0 5px 0 var(--sheet-ledge), var(--shadow-card);
}
.project-card:active {
  transform: translateY(1px);
  box-shadow: 0 1px 0 var(--sheet-ledge);
}
.project-card.skeleton-card {
  box-shadow: none;
}
.project-cover {
  aspect-ratio: 19 / 10; /* ScreensCover's TABLE */
  border-bottom: 1px solid var(--border-subtle);
  background-color: var(--bg-canvas);
  background-image: radial-gradient(circle, var(--canvas-dot) 1px, transparent 1.2px);
  background-size: 16px 16px;
}
.screens-cover {
  position: absolute;
  inset: 0;
}
.cover-screen {
  position: absolute;
  overflow: hidden;
  background: #fff; /* the user's app surface, as on the canvas */
  box-shadow:
    0 0 0 1.5px var(--sheet-edge),
    0 3px 0 1.5px var(--sheet-ledge),
    var(--shadow-screen);
}
.cover-screen img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top;
}
/* Captured nothing yet: a blank page in the frame's shape, top bar only. It
   is a placeholder, not the user's app, so it takes the theme's colors. */
.cover-blank {
  position: absolute;
  inset: 0;
  background: var(--bg-panel);
}
.cover-blank::before {
  content: "";
  position: absolute;
  inset: 8% 10% auto;
  height: 6%;
  border-radius: 3px;
  background: var(--border-default);
}
/* The newest open comment, pinned where it was left. */
.cover-pin {
  position: absolute;
  width: 16px;
  height: 16px;
  border-radius: 50% 50% 50% 3px;
  background: var(--comment);
  box-shadow:
    0 0 0 1.5px #fff,
    0 2px 5px rgba(0, 0, 0, 0.3);
  transform: translate(-3px, -100%);
}
.cover-empty {
  position: absolute;
  inset: 14px 18px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1.5px dashed var(--border-strong);
  border-radius: 12px;
  font: 500 14px/1.3 var(--font-hand);
  color: var(--text-tertiary);
}
.card-name {
  padding: 12px 16px 2px;
  font: 600 19px/1.2 var(--font-display);
  letter-spacing: -0.01em;
  color: var(--text-primary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.card-rename {
  margin: 8px 11px 0;
  padding: 3px 5px;
  font: 600 18px/1.2 var(--font-display);
  color: var(--text-primary);
}
.project-card .meta {
  margin-top: 2px;
}
.card-said {
  margin: 6px 16px 0;
  font: 500 13.5px/1.35 var(--font-hand);
  color: var(--text-secondary);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.card-said b {
  font-weight: 500;
  color: var(--comment);
}
.project-card .foot {
  margin-top: 10px;
}

/* Sentence case everywhere, as on the site: no tracked capitals. Group
   headings on home speak Fraunces like the canvas section names; small
   labels stay in the interface face. */
.workspace-heading {
  font-family: var(--font-display);
  font-size: 20px;
  letter-spacing: -0.005em;
  text-transform: none;
  color: var(--text-primary);
}
.pop-section,
.narration-section,
.center-card label {
  text-transform: none;
  letter-spacing: 0;
  font-size: var(--text-sm);
}
