/* ============================================================
   Quiet precision — design system
   ============================================================ */
:root {
  /* Color */
  --paper:        #FAFAF7;
  --canvas:       #F1F1EC;
  --ink:          #18181B;
  --ink-soft:     #52525B;
  --ink-mute:     #A1A1AA;
  --line:         #E8E7E2;
  --line-soft:    #EFEEE9;
  --rule:         #DCDBD4;
  --accent:       #2563EB;
  --accent-soft:  #DBEAFE;
  --danger:       #DC2626;

  /* Type */
  --font-ui:      "Geist", -apple-system, BlinkMacSystemFont, "SF Pro Text", system-ui, sans-serif;
  --font-display: "Instrument Serif", "New York", Georgia, serif;
  --t-xs: 11px;
  --t-sm: 13px;
  --t-base: 14px;
  --t-lg: 16px;
  --t-display: 22px;

  /* Spacing (4px scale) */
  --s-1: 4px; --s-2: 8px; --s-3: 12px; --s-4: 16px;
  --s-5: 20px; --s-6: 24px; --s-8: 32px; --s-12: 48px;

  /* Radius */
  --r-sm: 6px; --r-md: 10px; --r-lg: 14px; --r-xl: 20px; --r-pill: 999px;

  /* Shadows */
  --sh-1: 0 1px 2px rgba(20,20,20,0.04);
  --sh-2: 0 1px 2px rgba(20,20,20,0.04), 0 4px 14px rgba(20,20,20,0.05);
  --sh-3: 0 1px 3px rgba(20,20,20,0.06), 0 12px 36px rgba(20,20,20,0.08);
  --sh-paper: 0 1px 1px rgba(20,20,20,0.03), 0 8px 28px rgba(20,20,20,0.06);

  /* Motion */
  --ease: cubic-bezier(0.2, 0.8, 0.2, 1);
}

* { box-sizing: border-box; }

/* Ensure the [hidden] attribute always wins over any display: declared by a
   class selector. Without this, .ctx-menu / .submit-progress / .submit-result
   stay visible-as-empty-shell after `el.hidden = true` because their `display:
   flex` rule has equal specificity to `[hidden] { display: none }` from UA. */
[hidden] { display: none !important; }

html, body {
  margin: 0;
  height: 100%;
  /* Lock document scroll: only #stage scrolls. Without this, a zoomed page
     can overflow the document and bounce-scroll the body, which would push
     the sticky toolbar off-screen on iPad. */
  overflow: hidden;
  font-family: var(--font-ui);
  font-size: var(--t-base);
  font-weight: 400;
  letter-spacing: -0.005em;
  color: var(--ink);
  background: var(--canvas);
  overscroll-behavior: none;
  touch-action: pan-y;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  -webkit-tap-highlight-color: transparent;
}

/* Block text-selection on chrome surfaces (toolbar, prompt-bar shell, page
   gutter, buttons) but leave inputs / textareas / contenteditables alone
   — iOS Safari refuses to show the keyboard otherwise. */
.toolbar, .stage, .page, .layer, .modal-head, .modal-actions, .btn, .ico,
.swatch, .grip, .submit, .page-label {
  -webkit-user-select: none;
  user-select: none;
}

body {
  background-image:
    radial-gradient(circle at 10% 0%, rgba(37, 99, 235, 0.025), transparent 40%),
    radial-gradient(circle at 90% 100%, rgba(202, 138, 4, 0.02), transparent 40%);
}

button, input, textarea {
  font: inherit;
  color: inherit;
}

/* ============================================================
   Toolbar
   ============================================================ */
.toolbar {
  /* `position: fixed` (not sticky) so the toolbar stays pinned on iOS
     Safari / iPadOS regardless of address-bar collapse, soft-keyboard
     transitions, or sub-scroll inside #stage. Sticky degenerates to
     static on iOS in several of these cases and the toolbar visibly
     scrolls off-screen, which is the bug this rule fixes. .stage
     reserves the toolbar's vertical space via matching margin-top. */
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 10;
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 8px var(--s-4);
  background: rgba(250, 250, 247, 0.85);
  backdrop-filter: saturate(140%) blur(12px);
  -webkit-backdrop-filter: saturate(140%) blur(12px);
  border-bottom: 1px solid var(--line);
  height: 52px;
  flex-wrap: nowrap;
  overflow-x: auto;
  scrollbar-width: none;
  touch-action: pan-x;
}

/* Compact mode for narrow viewports (iPad portrait, etc.) so the right-side
   buttons (page nav, clear, delete, plug) fit on screen without horizontal
   scrolling. */
@media (max-width: 920px) {
  .toolbar { gap: 2px; padding: 8px 10px; height: 48px; }
  .tg { padding: 0 4px; }
  .toolbar .ico { width: 28px; height: 28px; }
  .swatch { width: 16px; height: 16px; margin: 0 2px; }
  .icon { width: 16px; height: 16px; }
  .page-label { font-size: 18px; min-width: 40px; padding: 0 4px; }
  .hr { margin: 0 4px; }
  .stage { height: calc(100% - 48px); margin-top: 48px; }
}
@media (max-width: 820px) {
  /* iPad portrait — drop group dividers, tighten everything to fit */
  .toolbar { gap: 0; padding: 6px 6px; }
  .tg { padding: 0 2px; }
  .tg + .tg::before { display: none; }
  .toolbar .ico { width: 26px; height: 26px; }
  .swatch { width: 13px; height: 13px; margin: 0 1px; }
  .icon { width: 14px; height: 14px; }
  .page-label { font-size: 15px; min-width: 28px; padding: 0 2px; }
  .page-label .sep { margin: 0; font-size: 12px; }
  .hr { margin: 0 2px; height: 14px; }
  .size { width: 22px; height: 22px; }
  .size .dot-1 { width: 3px; height: 3px; }
  .size .dot-2 { width: 5px; height: 5px; }
  .size .dot-3 { width: 7px; height: 7px; }
  .tg.project { padding-right: 4px; margin-right: 2px; }
  /* Hide page-style toggle and the visual spacer — rarely needed on portrait */
  .tg.page-styles, .tg.spacer { display: none; }
  /* Tighter stage padding so the 800-wide canvas isn't bleeding off-screen */
  .stage { padding: var(--s-4) var(--s-3) 140px; }
}

/* Even tighter for older iPads / smaller portrait viewports */
@media (max-width: 780px) {
  .toolbar { padding: 6px 4px; }
  .toolbar .ico { width: 24px; height: 24px; }
  .swatch { width: 12px; height: 12px; }
  .size { width: 20px; height: 20px; }
  .icon { width: 13px; height: 13px; }
  .page-label { font-size: 14px; min-width: 26px; }
  /* Clear-page stays visible on narrow screens too (next to Delete in the
     page cluster); the toolbar scrolls horizontally if it runs out of room. */
  .stage { padding: var(--s-3) var(--s-2) 140px; }
}
.toolbar::-webkit-scrollbar { display: none; }

.tg {
  display: flex;
  align-items: center;
  gap: 2px;
  padding: 0 6px;
  position: relative;
  flex-shrink: 0;
}
.tg + .tg::before {
  content: "";
  position: absolute;
  left: 0;
  top: 8px;
  bottom: 8px;
  width: 1px;
  background: var(--line);
}
.tg.spacer { flex: 1; padding: 0; min-width: 8px; }
.tg.spacer::before { display: none; }

/* Icon-only button */
.ico {
  appearance: none;
  background: transparent;
  border: none;
  width: 32px;
  height: 32px;
  border-radius: var(--r-md);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--ink-soft);
  cursor: pointer;
  padding: 0;
  transition: background 120ms var(--ease), color 120ms var(--ease), transform 120ms var(--ease);
}
/* Hover effects only fire on true-hover devices (desktop with mouse).
   On iPad/mobile, tap-then-release would otherwise leave the button
   "stuck" in its inverted hover state until another element is tapped. */
@media (hover: hover) {
  .ico:hover { background: var(--line-soft); color: var(--ink); }
  .ico.subtle:hover { color: var(--danger); background: rgba(220, 38, 38, 0.06); }
}
.ico { -webkit-tap-highlight-color: transparent; }
.ico:active { transform: scale(0.96); }
.ico.active {
  background: var(--ink);
  color: var(--paper);
}
.ico.subtle { color: var(--ink-mute); }

.icon {
  width: 18px;
  height: 18px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
  display: block;
  pointer-events: none;
}

/* Pen options — visible for both pen and shape tools (shared color + size) */
.pen-options {
  display: none;
}
body.tool-pen .pen-options,
body.tool-shape .pen-options {
  display: flex;
}

/* Shape extras — kind picker + line-style picker, visible only in shape tool */
.shape-extras {
  display: none;
}
body.tool-shape .shape-extras {
  display: flex;
}

/* Lasso extras — selection-mode picker (free-form vs rectangular),
   Dead since the lasso mode picker was merged into the lasso TOOL
   button itself (re-click while already active opens the mode picker).
   Kept as a no-op for any legacy references; can be deleted whenever. */
.lasso-extras { display: none; }

.swatch {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--c);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin: 0 3px;
  position: relative;
  transition: transform 120ms var(--ease);
}
.swatch::after {
  content: "";
  position: absolute;
  inset: -4px;
  border-radius: 50%;
  border: 1.5px solid var(--c);
  opacity: 0;
  transition: opacity 150ms var(--ease);
}
.swatch:hover { transform: scale(1.1); }
.swatch.active::after { opacity: 1; }

/* White swatch — needs an explicit border so it's distinguishable from
   the cream toolbar background. Without this, white-on-cream looks
   like an empty slot, not a color choice. */
.swatch-white {
  border: 1.5px solid rgba(0, 0, 0, 0.18);
  /* Override the radial padding from the .active::after pattern so the
     active ring stays a ring, not an outline over the existing border. */
}
.swatch-white::after { border-color: var(--ink); }

/* Custom-color wheel swatch — full conic gradient (the universal
   "color picker" affordance) with a small center dot showing whatever
   custom color the user has picked. The button-vs-span distinction is
   intentional: this one's interactive (opens a popover), so it gets the
   proper button semantics + focus-visible ring. */
.swatch-wheel {
  appearance: none;
  border: none;
  padding: 0;
  background: conic-gradient(from 0deg,
    #ff0000 0deg,
    #ffaa00 45deg,
    #ffff00 90deg,
    #00ff00 135deg,
    #00ffff 180deg,
    #0000ff 225deg,
    #aa00ff 270deg,
    #ff00aa 315deg,
    #ff0000 360deg);
  /* The dot in the middle reveals the current custom color so the user
     gets continuous feedback about what "wheel" means for THEM right now. */
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.swatch-wheel:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.swatch-wheel-dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--cc, #111);
  border: 1.5px solid #fff;
  box-shadow: 0 0 0 0.5px rgba(0, 0, 0, 0.35);
  pointer-events: none;
}

/* ============================================================
   Toolbar picker pattern — single button collapses N options into
   a click-to-expand popover. Used by the color, size, paper-style,
   and device-preview clusters to keep the top bar compact.

   Anatomy:
     .picker-group
       └ .picker-trigger         — the always-visible button
       └ .picker-popover         — anchored beneath, hidden by default
            └ .picker-row        — flex row of the real option buttons

   The picker-trigger inherits the .ico hover state so it reads as a
   sibling to the surrounding toolbar icons, with a small chevron-
   style nudge (a thin accent dot) when its popover is open.
   ============================================================ */
.picker-group {
  position: relative;
  display: inline-flex;
  align-items: center;
}
.picker-trigger {
  appearance: none;
  background: transparent;
  border: 1px solid var(--line);
  width: 32px;
  height: 32px;
  border-radius: 8px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  color: var(--ink);
  padding: 0;
  transition: background 120ms var(--ease),
              border-color 120ms var(--ease),
              transform 120ms var(--ease);
}
.picker-trigger:hover {
  background: var(--ink);
  color: var(--paper);
  border-color: var(--ink);
}
/* Currently-open trigger gets a filled accent state so the user
   can see which picker the visible popover belongs to. */
.picker-trigger[aria-expanded="true"] {
  border-color: var(--accent, #c75e2a);
  box-shadow: 0 0 0 2px rgba(199, 94, 42, 0.18);
}
.picker-trigger:hover .picker-current-paper,
.picker-trigger:hover .picker-current-device {
  /* Icons inside the trigger inherit color; force them to stay
     visible on hover (inverted) instead of disappearing. */
  color: inherit;
}
.picker-trigger .icon { width: 16px; height: 16px; stroke-width: 1.6; }

/* The "current color" indicator inside the color trigger. Filled
   disc that reflects state.penColor; bordered so light colors stay
   visible against the cream toolbar. */
.picker-current-color {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  display: inline-block;
  border: 1px solid rgba(0, 0, 0, 0.12);
  box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04);
}

/* The "current size" indicator inside the size trigger.
   Standalone filled circle whose diameter mirrors the .dot-N
   pen-size scale. Doesn't rely on the .dot CSS family because
   that requires a .size-button parent context — on the trigger
   there's no such parent, so explicit dimensions go here. */
.picker-current-size {
  display: inline-block;
  border-radius: 50%;
  background: currentColor;
  /* Default = medium. JS rewrites the dot-N class on state change. */
  width: 6px;
  height: 6px;
}
.picker-current-size.dot-1 { width: 4px;  height: 4px;  }
.picker-current-size.dot-2 { width: 7px;  height: 7px;  }
.picker-current-size.dot-3 { width: 11px; height: 11px; }

/* Picker popover — floats below the trigger, anchored by JS to the
   trigger's left edge. Same visual language as the version-history
   popover and style-picker so the toolbar feels like one system. */
.picker-popover {
  position: fixed;
  z-index: 60;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 8px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.10), 0 2px 6px rgba(0, 0, 0, 0.06);
  display: flex;
  flex-direction: column;
  gap: 6px;
  opacity: 0;
  transform: translateY(-4px);
  transition: opacity 140ms var(--ease), transform 140ms var(--ease);
  pointer-events: none;
}
.picker-popover.open {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}
.picker-popover[hidden] { display: none; }
.picker-row {
  display: flex;
  align-items: center;
  gap: 4px;
}
/* The swatches row inside the color popover gets slightly more
   breathing room — the swatches are circular and visually heavier
   than the size/paper icons. */
.picker-row-swatches { gap: 6px; }

/* Eyedropper toolbar button. Lives right next to the rainbow swatch
   in the pen-options group, only visible when window.EyeDropper is
   supported (revealed by JS — hidden in HTML by default). Inherits
   the same .ico sizing the other small toolbar buttons use, so it
   reads as a sibling action rather than a special control. */
.swatch-eyedropper {
  width: 28px;
  height: 28px;
  border-radius: 8px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--line);
  background: var(--paper);
  cursor: pointer;
  color: var(--ink);
  transition: background 120ms, color 120ms, border-color 120ms;
}
.swatch-eyedropper:hover {
  background: var(--ink);
  color: var(--paper);
  border-color: var(--ink);
}
.swatch-eyedropper .icon { width: 16px; height: 16px; stroke-width: 1.6; }

/* The native <input type="color"> is positioned on-demand by JS at
   the rainbow swatch's viewport coordinates just before .click().
   Why: Chrome anchors the OS color-picker dialog to the input
   element's on-screen position. Off-screen positioning would make
   the OS picker open out of view — that was the original "color
   picker doesn't work" bug. opacity:0 + pointer-events:none keep
   the input invisible until the click fires; tabindex="-1" +
   aria-hidden="true" (in the HTML) keep it out of the tab order. */
#colorWheelNative {
  position: fixed;
  left: 0;
  top: 0;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

.hr {
  width: 1px;
  height: 18px;
  background: var(--line);
  margin: 0 var(--s-2);
}

/* Size dots */
.size .dot {
  display: block;
  background: currentColor;
  border-radius: 50%;
}
.size .dot-1 { width: 4px; height: 4px; }
.size .dot-2 { width: 7px; height: 7px; }
.size .dot-3 { width: 11px; height: 11px; }

/* Page label — the editorial flourish */
.page-label {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 20px;
  color: var(--ink);
  min-width: 48px;
  text-align: center;
  font-variant-numeric: tabular-nums;
  line-height: 1;
  padding: 0 6px;
  user-select: none;
}
.page-label .sep {
  color: var(--ink-mute);
  margin: 0 1px;
  font-size: 14px;
}

/* Connect plug — slightly more weight */
.ico.plug {
  background: var(--ink);
  color: var(--paper);
}
.ico.plug:hover { background: var(--accent); transform: scale(1.04); }
.ico.plug:active { transform: scale(0.96); }

/* ============================================================
   Stage + Page
   ============================================================ */
.stage {
  /* The toolbar is position:fixed (out of normal flow) — margin-top
     reserves its vertical slot so the stage sits below it instead of
     underlapping it. Height stays viewport - toolbar so the stage's
     own scroll fills the rest of the screen. Both values are kept in
     sync per-breakpoint in the media queries below. */
  height: calc(100% - 52px);
  margin-top: 52px;
  overflow: auto;
  padding: var(--s-8) var(--s-6) 140px;
  display: flex;
  justify-content: center;
  align-items: flex-start;
  scrollbar-width: thin;
  scrollbar-color: var(--ink-mute) transparent;
  touch-action: pan-y;
  -webkit-overflow-scrolling: touch;
}

/* Preview stage replaces the notebook stage when viewMode === 'preview'.
   The iframe fills available space; in Phase 2 we'll layer an annotation
   canvas on top with the same pointer-events handling as #inkCanvas. */
.preview-stage {
  padding: var(--s-4) var(--s-4) 140px;
  align-items: stretch;
}
.preview-frame-wrap {
  flex: 1;
  display: flex;
  background: var(--paper);
  border-radius: 8px;
  box-shadow: var(--sh-paper);
  overflow: hidden;
  max-width: 1100px;
  width: 100%;
  margin: 0 auto;
  min-height: 600px;
  /* Smooth resize when the user toggles between device viewports.
     The iframe inside scales to fit the new max-width, giving an
     animated "device frame" effect. */
  transition: max-width 240ms cubic-bezier(0.2, 0.7, 0.2, 1);
}
/* Device viewport constraints. Sized to match the breakpoints the
   responsive system prompt and Qwen patcher target:
   tablet ≤768px, mobile ≤375px (iPhone SE width — the narrowest
   realistic viewport). */
.preview-frame-wrap[data-device="tablet"] { max-width: 768px; }
.preview-frame-wrap[data-device="mobile"] { max-width: 375px; }

/* Device-toggle button group. Three icons sharing a quiet container,
   active state filled. Matches the visual weight of the annotate-toggle
   sibling group so the toolbar reads as a single horizontal band. */
.device-toggle-wrap {
  display: inline-flex;
  gap: 2px;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  padding: 2px;
}
.device-toggle-wrap[hidden] { display: none; }
.device-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: calc(var(--r-pill) - 2px);
  -webkit-tap-highlight-color: transparent;
  transition: background 120ms var(--ease), color 120ms var(--ease);
}
.device-btn .icon { width: 16px; height: 16px; }
.device-btn:hover { background: var(--line); }
.device-btn.active {
  background: var(--ink);
  color: var(--paper);
}
.preview-frame {
  flex: 1;
  border: 0;
  background: #fff;
  min-height: 100%;
}

.preview-frame-wrap { position: relative; }

/* Annotation overlay = a transparent layer pinned over the iframe at the
   iframe's current visible size. The iframe stays visible underneath; we
   just block its pointer events while annotate is on so strokes land here.
   On annotate-OFF we composite this view into a JPEG and save it as a
   sendable page (just like exporting a notebook page). */
.preview-anno-page {
  position: absolute;
  top: 0;
  left: 0;
  display: none;
  pointer-events: none; /* the per-layer rules below opt in */
  /* Scroll sync translates this element on iframe scroll. Promote to its
     own compositor layer so iPad doesn't paint-shift the strokes per
     frame. */
  will-change: transform;
  transform-origin: 0 0;
}
.annotating .preview-anno-page { display: block; }
/* While annotating, the iframe can't grab clicks/scrolls — events land on
   the overlay's layers instead. The iframe stays visually present. */
.annotating .preview-frame { pointer-events: none; }

/* Per-tool pointer-events for the overlay layers. These selectors are scoped
   under .preview-anno-page so they out-specify the global `.layer { pointer-
   events:none }` rule regardless of source order. Stacking (bottom→top):
   snapshot, image, ink, text, lasso. The ink canvas captures pen/eraser/
   shape; the text + lasso layers take over only in their own tool. */
.preview-anno-page .layer { position: absolute; inset: 0; pointer-events: none; }
.preview-anno-page .preview-anno-ink { pointer-events: auto; touch-action: none; cursor: crosshair; }
.preview-anno-page .text-layer.tool-text { pointer-events: auto; cursor: text; }
body.tool-lasso .preview-anno-page .lasso-layer { pointer-events: auto; cursor: crosshair; }

/* Floating selection action bar for the lasso tool on the annotate
   overlay. Position: fixed so it stays in view regardless of how the
   user has scrolled the preview underneath. */
.annotate-selection-bar {
  position: fixed;
  bottom: 100px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 1000;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px 8px 14px;
  background: var(--ink);
  color: var(--paper);
  border-radius: 999px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
  font: 500 13px var(--font-body, inherit);
  letter-spacing: 0.01em;
  -webkit-tap-highlight-color: transparent;
}
.annotate-selection-bar .count {
  margin-right: 4px;
  opacity: 0.85;
}
.annotate-selection-bar .action {
  appearance: none;
  border: none;
  background: rgba(255, 255, 255, 0.08);
  color: var(--paper);
  padding: 6px 12px;
  border-radius: 999px;
  font: inherit;
  cursor: pointer;
  transition: background 120ms;
}
.annotate-selection-bar .action:hover { background: rgba(255, 255, 255, 0.18); }
.annotate-selection-bar .action.delete { background: #DC2626; color: #fff; }
.annotate-selection-bar .action.delete:hover { background: #B91C1C; }

/* ─────────────────────────────────────────────────────────────────────
   Direct-edit mode. Mirrors annotate-mode styling so the two toggles
   feel like siblings, but uses a different overlay layer that sits
   on top of the iframe and renders hover/selection rings absolutely.
   The layer itself is pointer-events:none — the iframe still gets
   real clicks; we listen via the iframe document.
   ──────────────────────────────────────────────────────────────────── */
.edit-toggle-wrap { display: inline-flex; }
.edit-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  -webkit-tap-highlight-color: transparent;
}
.edit-btn.active {
  background: var(--line);
  color: var(--ink);
  border-color: var(--line);
}

.preview-edit-layer {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 5;
}
.preview-edit-layer[hidden] { display: none; }

/* Hover ring — dashed, low-contrast. Shown while the cursor is over
   an interactable element but before a click commits a selection. */
.pe-hover {
  position: absolute;
  border: 1.5px dashed rgba(37, 99, 235, 0.65); /* blue-600 @ 65% */
  border-radius: 3px;
  pointer-events: none;
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.45) inset;
  transition: opacity 80ms;
}

/* Selection outline — solid, higher contrast. Persists until the user
   clicks elsewhere or presses Esc. */
.pe-selection {
  position: absolute;
  border: 2px solid #2563EB; /* blue-600 */
  border-radius: 3px;
  pointer-events: none;
  box-shadow:
    0 0 0 1px rgba(255, 255, 255, 0.6) inset,
    0 4px 14px rgba(37, 99, 235, 0.18);
}
/* Move pip — top-left circular handle. Visible whenever the selection
   ring is shown; drag to translate the underlying element. Sits a few
   pixels OUTSIDE the ring so it doesn't overlap text/icons. */
.pe-move-pip {
  position: absolute;
  top: -10px;
  left: -10px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: #2563EB;
  border: 2px solid #fff;
  box-shadow: 0 2px 6px rgba(37, 99, 235, 0.35);
  pointer-events: auto;
  cursor: move;
  touch-action: none; /* pointer events instead of touch scrolling */
}
.pe-move-pip:hover { background: #1D4ED8; }
/* SE resize handle — bottom-right square. Drag to resize. */
.pe-handle {
  position: absolute;
  width: 14px;
  height: 14px;
  background: #fff;
  border: 2px solid #2563EB;
  border-radius: 3px;
  pointer-events: auto;
  touch-action: none;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12);
}
.pe-handle-se { bottom: -8px; right: -8px; cursor: nwse-resize; }
.pe-handle:hover { background: #EFF6FF; }
/* While dragging/resizing, prevent text selection in the whole document
   so the user's gesture doesn't accidentally highlight stuff. */
body.pe-gesturing { user-select: none; -webkit-user-select: none; }

/* Floating action bar for direct edit. Position: fixed so it stays in
   view as the iframe scrolls. Stacks below modals but above everything
   else (z-index 1000 mirrors the annotate selection bar). */
.preview-edit-bar {
  position: fixed;
  /* Sit just above the soft keyboard / home-bar safe-area on iPad. The
     env() fallback to 100px keeps the desktop position unchanged. */
  bottom: calc(env(safe-area-inset-bottom, 0px) + 100px);
  left: 50%;
  /* translate3d (not translateX) promotes the bar to its own GPU layer.
     Without this, iOS Safari can leave a paint ghost of the bar when
     the visual viewport transitions (e.g. soft keyboard collapse after
     a save), which manifested as a duplicate offset bar. */
  transform: translate3d(-50%, 0, 0);
  will-change: transform;
  z-index: 1000;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px 8px 14px;
  background: var(--ink);
  color: var(--paper);
  border-radius: 999px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
  font: 500 13px var(--font-body, inherit);
  letter-spacing: 0.01em;
  -webkit-tap-highlight-color: transparent;
  max-width: 90vw;
}
.preview-edit-bar .label {
  opacity: 0.85;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 320px;
}
.preview-edit-bar .action {
  appearance: none;
  border: none;
  background: rgba(255, 255, 255, 0.08);
  color: var(--paper);
  padding: 6px 12px;
  border-radius: 999px;
  font: inherit;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  transition: background 120ms, opacity 120ms;
}
.preview-edit-bar .action:hover:not(:disabled) { background: rgba(255, 255, 255, 0.18); }
.preview-edit-bar .action:disabled { opacity: 0.35; cursor: default; }
.preview-edit-bar .action.delete:not(:disabled) { background: rgba(220, 38, 38, 0.85); color: #fff; }
.preview-edit-bar .action.delete:not(:disabled):hover { background: #B91C1C; }
.preview-edit-bar .action.save:not(:disabled) { background: #2563EB; color: #fff; }
.preview-edit-bar .action.save:not(:disabled):hover { background: #1D4ED8; }
.preview-edit-bar .action .icon { width: 16px; height: 16px; }

/* While edit mode is on, neutralize the iframe's own pointer behavior
   ONLY for things that would steal the click (anchors, buttons). The
   listener stops propagation already, but a CSS cursor hint helps. */
body.preview-editing .preview-frame { cursor: pointer; }

/* ─────────────────────────────────────────────────────────────────────
   Public share dialog.

   Editorial in tone — the URL is the artifact, set in a monospace
   serif-friendly column with generous breathing room around it. State
   pill on the right shows On/Off with a hairline border (NOT a brand-
   ish toggle switch — too SaaS). Action buttons are quiet rectangles
   that lean on the existing ink/paper palette.
   ──────────────────────────────────────────────────────────────────── */
.share-dialog { width: min(520px, calc(100% - 32px)); }
.share-body {
  margin: var(--s-3) 0 var(--s-4);
}
.share-loading {
  padding: var(--s-5) 0;
  text-align: center;
  color: var(--ink-mute);
  font-style: italic;
  font-size: 13px;
}
.share-loading-err { color: #B91C1C; font-style: normal; }

.share-status {
  padding: var(--s-3) 0 var(--s-4);
  border-bottom: 1px solid var(--line);
  margin-bottom: var(--s-4);
}
.share-status-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: 4px;
}
.share-status-label {
  font: 500 11px var(--font-ui);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-mute);
}
.share-status-pill {
  display: inline-flex;
  align-items: center;
  padding: 4px 10px;
  border: 1px solid var(--ink);
  background: var(--ink);
  color: var(--paper);
  font: 600 11px var(--font-ui);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.share-status-pill-off {
  background: transparent;
  color: var(--ink-mute);
  border-color: var(--line);
}
.share-status-sub {
  font: 400 13px/1.5 var(--font-body);
  color: var(--ink-soft);
}

.share-url-row {
  display: grid;
  grid-template-columns: 1fr auto auto;
  gap: var(--s-2);
  align-items: stretch;
  margin-bottom: var(--s-4);
}
.share-url-input {
  appearance: none;
  border: 1px solid var(--line);
  border-radius: 0; /* hard geometry */
  background: var(--paper);
  padding: 10px 12px;
  font: 400 12px/1.4 var(--font-mono, ui-monospace, SFMono-Regular, monospace);
  color: var(--ink);
  width: 100%;
  /* Tabular numerals + letter-spacing make the token look intentional,
     not just a wall of base64. */
  letter-spacing: 0.02em;
  outline: none;
  transition: border-color 120ms;
}
.share-url-input:focus { border-color: var(--ink); }

.share-action {
  appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 10px 14px;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: 0;
  font: 500 12px var(--font-ui);
  letter-spacing: 0.04em;
  color: var(--ink);
  cursor: pointer;
  text-decoration: none;
  transition: background 120ms, color 120ms, border-color 120ms;
  white-space: nowrap;
}
.share-action .icon { width: 14px; height: 14px; }
.share-action:hover { background: var(--ink); color: var(--paper); border-color: var(--ink); }
.share-action-primary {
  background: var(--ink);
  color: var(--paper);
  border-color: var(--ink);
}
.share-action-primary:hover { background: #000; border-color: #000; }
.share-action-full { width: 100%; padding: 12px 14px; }
.share-action-flash {
  background: var(--accent) !important;
  border-color: var(--accent) !important;
  color: var(--paper) !important;
}

.share-meta-actions {
  display: flex;
  justify-content: space-between;
  gap: var(--s-3);
  padding-top: var(--s-3);
  border-top: 1px solid var(--line);
}
.share-action-link {
  appearance: none;
  background: transparent;
  border: 0;
  padding: 6px 0;
  font: 500 11px var(--font-ui);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-mute);
  cursor: pointer;
  transition: color 120ms;
}
.share-action-link:hover { color: var(--ink); }
.share-action-danger:hover { color: #B91C1C; }

.share-fineprint {
  margin: var(--s-3) 0 0;
  padding-top: var(--s-3);
  border-top: 1px solid var(--line);
  font: 400 11px/1.55 var(--font-body);
  color: var(--ink-mute);
}

/* Mobile-friendly: stack URL input + buttons vertically when there's no
   room for the 3-column grid. */
@media (max-width: 520px) {
  .share-url-row {
    grid-template-columns: 1fr;
  }
  .share-action { justify-content: center; }
}

/* Inspector cluster inside the action bar — font controls + color swatch.
   Sits between the label and the action buttons; hidden for non-text
   selections (toggled via JS). */
.pe-inspector {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 0 4px;
  border-left: 1px solid rgba(255, 255, 255, 0.12);
  border-right: 1px solid rgba(255, 255, 255, 0.12);
  margin: 0 4px;
}
.pe-inspector[hidden] { display: none; }
.pe-inspector .action.subtle {
  background: rgba(255, 255, 255, 0.05);
  padding: 4px 10px;
  font: 600 14px/1 var(--font-body, inherit);
  min-width: 28px;
}
.pe-inspector .action.subtle:hover:not(:disabled) { background: rgba(255, 255, 255, 0.18); }
.pe-font-value {
  font: 500 12px/1 var(--font-mono, ui-monospace, SFMono-Regular, monospace);
  min-width: 36px;
  text-align: center;
  opacity: 0.92;
}
.pe-swatch-wrap {
  position: relative;
  display: inline-flex;
  align-items: center;
  cursor: pointer;
  margin-left: 4px;
}
.pe-swatch {
  display: inline-block;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  border: 2px solid rgba(255, 255, 255, 0.85);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
}
.pe-swatch-wrap input[type="color"] {
  /* Native picker — we want the swatch to look like the trigger, so the
     real input is invisible but still clickable. */
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  cursor: pointer;
  border: 0;
  padding: 0;
}

/* Annotation-preview thumbnail in the submit dialog. Replaces the
   page-selector strip when the user submits from Preview + Annotate.
   The image is the composite (current canonical + ink strokes) at full
   content height, scaled down to fit the dialog. */
.annotation-preview-wrap {
  display: flex;
  justify-content: center;
  padding: 8px;
  background: var(--line-soft);
  border-radius: 6px;
  max-height: 260px;
  overflow: auto;
}
.annotation-preview-img {
  max-width: 100%;
  max-height: 240px;
  height: auto;
  width: auto;
  display: block;
  border: 1px solid var(--line);
  border-radius: 4px;
  background: #fff;
}

/* Inline text-editor input that spawns at the click position when the
   text tool is used on the preview overlay. Mirrors the iPad-style
   "tap to type" flow from the notebook's text layer. */
.preview-text-editor {
  position: absolute;
  z-index: 4;
  background: transparent;
  border: 1px dashed var(--ink-mute);
  border-radius: 2px;
  padding: 1px 4px;
  outline: none;
  font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
  font-weight: 500;
  min-width: 80px;
  pointer-events: auto;
  -webkit-tap-highlight-color: transparent;
}

/* Annotate toolbar button. Mirrors the .ico style for the other
   toolbar icons but tucks an inline label next to it. Active state
   inverts to match the view-toggle "selected" treatment. */
.annotate-toggle-wrap { display: inline-flex; }
.annotate-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  -webkit-tap-highlight-color: transparent;
}
.annotate-btn.active {
  /* Soft selected state, matching the view-toggle pill. */
  background: var(--line);
  color: var(--ink);
  border-color: var(--line);
}

/* Version history cluster: small undo button + monospace version label.
   Shown only when the Preview view is active and the project has at
   least one canonical generated. Sits in its own .tg group alongside
   the annotate toggle so the toolbar stays organized. */
.version-history-wrap {
  position: relative;
  display: inline-flex;
  align-items: center;
}
/* The trigger is an icon + monospace label fused into one pill button. */
.version-btn {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  width: auto;
  padding: 0 9px 0 7px;
  border-radius: var(--r-pill);
}
.version-btn .version-label {
  font: 500 11px var(--font-mono, ui-monospace, SFMono-Regular, monospace);
  color: var(--ink-mute);
  letter-spacing: 0.03em;
  user-select: none;
  white-space: nowrap;
}
.version-btn[aria-expanded="true"] {
  background: var(--line-soft);
  color: var(--ink);
}
.version-btn[aria-expanded="true"] .version-label { color: var(--ink-soft); }

/* Dropdown panel. Floats below the trigger, right-aligned so it never
   spills off the toolbar's right edge. */
.version-menu {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  z-index: 60;
  width: 248px;
  max-width: min(248px, calc(100vw - 24px));
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--sh-paper);
  padding: 6px;
  animation: versionMenuIn 0.14s var(--ease);
}
@keyframes versionMenuIn {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: translateY(0); }
}
.version-menu-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 2px 4px 6px;
  border-bottom: 1px solid var(--line-soft);
  margin-bottom: 4px;
}
.version-menu-title {
  font: 600 11px var(--font-ui);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-mute);
}
.version-list {
  list-style: none;
  margin: 0;
  padding: 0;
  max-height: 320px;
  overflow-y: auto;
}
.version-item {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 8px 9px;
  border: none;
  background: none;
  border-radius: var(--r-md);
  cursor: pointer;
  text-align: left;
  color: var(--ink-soft);
  font: 500 13px var(--font-ui);
  transition: background 0.12s var(--ease);
}
.version-item:hover { background: var(--line-soft); color: var(--ink); }
.version-item .v-num {
  font: 600 12px var(--font-mono, ui-monospace, monospace);
  color: var(--ink);
  min-width: 30px;
}
.version-item .v-meta {
  margin-left: auto;
  font-size: var(--t-xs);
  color: var(--ink-mute);
  white-space: nowrap;
}
.version-item .v-check { width: 16px; height: 16px; color: var(--accent); flex: none; visibility: hidden; }
.version-item.current { background: var(--accent-soft); }
.version-item.current .v-num,
.version-item.current { color: var(--accent); }
.version-item.current .v-check { visibility: visible; }
.version-menu .ico.ghost:disabled { opacity: 0.35; cursor: not-allowed; }

/* Notebook / Preview toggle in the toolbar. Sits in its own .tg group
   alongside page-styles. Uses a segmented-control aesthetic — both
   buttons in a single pill, active state inverted. */
.view-toggle {
  display: inline-flex;
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 2px;
  background: transparent;
}
.view-toggle-btn {
  appearance: none;
  border: none;
  background: transparent;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 6px 10px;
  border-radius: 999px;
  color: var(--ink-soft);
  cursor: pointer;
  transition: background 120ms, color 120ms;
  -webkit-tap-highlight-color: transparent;
}
.view-toggle-btn .icon { width: 16px; height: 16px; stroke-width: 1.6; }
/* Only apply :hover styling on devices with true hover capability so
   tap-then-release on iPad/mobile doesn't leave a sticky inverted
   button. The :active rule below covers the brief press feedback. */
@media (hover: hover) {
  .view-toggle-btn:not(:disabled):hover { background: var(--line-soft); color: var(--ink); }
}
.view-toggle-btn.active {
  /* Subtle "selected" — soft pill, dark icon. Avoids the full black
     inversion that on touch devices looked indistinguishable from a
     stuck hover state. */
  background: var(--line);
  color: var(--ink);
}
.view-toggle-btn:disabled {
  cursor: not-allowed;
  opacity: 0.5;
}

.page {
  position: relative;
  background: var(--paper);
  width: 800px;
  height: 1100px;
  box-shadow: var(--sh-paper);
  border-radius: 4px;
  overflow: hidden;
  flex-shrink: 0;
  touch-action: none;
  /* Subtle paper grain */
  background-image:
    radial-gradient(circle at 25% 25%, rgba(0,0,0,0.012) 1px, transparent 1.5px),
    radial-gradient(circle at 75% 75%, rgba(0,0,0,0.012) 1px, transparent 1.5px);
  background-size: 6px 6px, 7px 7px;
}

.page--blank {
  background-image:
    radial-gradient(circle at 25% 25%, rgba(0,0,0,0.012) 1px, transparent 1.5px),
    radial-gradient(circle at 75% 75%, rgba(0,0,0,0.012) 1px, transparent 1.5px);
}

.page--ruled {
  background-image:
    radial-gradient(circle at 25% 25%, rgba(0,0,0,0.012) 1px, transparent 1.5px),
    linear-gradient(to bottom, transparent calc(36px - 1px), var(--rule) calc(36px - 1px), var(--rule) 36px);
  background-size: 6px 6px, 100% 36px;
  background-position: 0 0, 0 12px;
}

.page--grid {
  background-image:
    radial-gradient(circle at 25% 25%, rgba(0,0,0,0.012) 1px, transparent 1.5px),
    linear-gradient(to right, var(--rule) 1px, transparent 1px),
    linear-gradient(to bottom, var(--rule) 1px, transparent 1px);
  background-size: 6px 6px, 28px 28px, 28px 28px;
}

.layer {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  display: block;
  touch-action: none;
}
#inkCanvas { pointer-events: auto; touch-action: none; }

.overlay { position: absolute; inset: 0; pointer-events: none; }

/* ============================================================
   Text layer
   ============================================================ */
.text-layer { pointer-events: none; }
.text-layer.tool-text { pointer-events: auto; cursor: text; }

/* Lasso layer */
.lasso-layer {
  pointer-events: none;
  touch-action: none;
}
body.tool-lasso .lasso-layer {
  pointer-events: auto;
  cursor: crosshair;
}
.lasso-layer svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}
.lasso-path {
  fill: rgba(37, 99, 235, 0.07);
  stroke: var(--accent);
  stroke-width: 1.5;
  stroke-dasharray: 6 4;
  vector-effect: non-scaling-stroke;
}
.lasso-selection {
  position: absolute;
  border: 1.5px dashed var(--accent);
  background: rgba(37, 99, 235, 0.05);
  border-radius: 3px;
  pointer-events: auto;
  cursor: move;
  touch-action: none;
}
.lasso-grip {
  position: absolute;
  bottom: -14px;
  right: -14px;
  width: 22px;
  height: 22px;
  background: var(--paper);
  border: 2px solid var(--accent);
  border-radius: 50%;
  cursor: nwse-resize;
  touch-action: none;
  box-shadow: 0 1px 3px rgba(0,0,0,0.15);
  pointer-events: auto;
}

/* When the chrome is shown OUTSIDE the lasso tool (e.g. shape-tool tap-
   to-select), the chrome body must NOT swallow pointer events — the
   user needs to be able to grab the shape underneath to drag it with
   the shape tool's own move logic. The interactive handles (grip,
   rotate, menu) re-establish pointer-events: auto on themselves so
   only the inert chrome rectangle becomes click-through. */
body:not(.tool-lasso) .lasso-selection {
  pointer-events: none;
  cursor: default;
}

/* Ellipsis menu button on the selection chrome — opens Copy/Cut/Delete/etc. */
.lasso-menu-btn {
  position: absolute;
  top: -14px;
  right: -14px;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: var(--ink);
  border: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 2px;
  padding: 0;
  pointer-events: auto;
  touch-action: manipulation;
  box-shadow: 0 1px 3px rgba(0,0,0,0.2);
  transition: transform 120ms var(--ease), background 120ms var(--ease);
}
.lasso-menu-btn:hover { background: var(--accent); transform: scale(1.06); }
.lasso-menu-btn:active { transform: scale(0.94); }
.lasso-menu-btn span {
  width: 3px;
  height: 3px;
  background: var(--paper);
  border-radius: 50%;
}
/* Invisible 44px hit-area for iOS minimum touch target */
.lasso-menu-btn::before {
  content: "";
  position: absolute;
  inset: -8px;
}
/* Invisible hit-area so finger taps near the grip still grab it (iOS 44pt). */
.lasso-grip::before {
  content: "";
  position: absolute;
  inset: -14px;
}

/* Rotate handle — sits centered above the selection chrome on a short
   stem. Same diameter as the resize grip so the chrome feels balanced.
   Visual cue: a curved-arrow icon could be added later; for now the
   filled accent dot reads as "an interactive control". */
.lasso-rotate {
  position: absolute;
  top: -32px;
  left: 50%;
  transform: translateX(-50%);
  width: 18px;
  height: 18px;
  background: var(--paper);
  border: 2px solid var(--accent);
  border-radius: 50%;
  cursor: grab;
  touch-action: none;
  box-shadow: 0 1px 3px rgba(0,0,0,0.15);
  pointer-events: auto;
}
.lasso-rotate:active { cursor: grabbing; }
/* Stem connecting the rotate handle to the chrome top — purely visual. */
.lasso-rotate::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 100%;
  width: 1.5px;
  height: 14px;
  background: var(--accent);
  transform: translateX(-50%);
}
/* iOS 44pt invisible hit area. */
.lasso-rotate::before {
  content: "";
  position: absolute;
  inset: -14px;
}

.ctx-divider {
  height: 1px;
  background: var(--line);
  margin: 4px 6px;
  pointer-events: none;
}

.text-box {
  position: absolute;
  font-family: var(--font-ui);
  font-size: 18px;
  line-height: 1.45;
  color: var(--ink);
  padding: 4px 6px;
  outline: none;
  border: 1px dashed transparent;
  border-radius: 2px;
  background: transparent;
  white-space: pre-wrap;
  word-wrap: break-word;
  /* overflow: visible so the ••• and resize grip can extend past the box. */
  overflow: visible;
  -webkit-user-select: text;
  user-select: text;
  pointer-events: auto;
  min-width: 24px;
  min-height: 24px;
  transition: border-color 120ms var(--ease), background 120ms var(--ease);
  touch-action: auto;
}
.text-box:hover { border-color: rgba(37, 99, 235, 0.25); }
.text-box.editing {
  border-color: var(--accent);
  background: rgba(255, 255, 255, 0.6);
  cursor: text;
}
.text-box.selected {
  border-color: var(--accent);
  border-style: dashed;
  cursor: move;
}
.text-box[data-empty="true"]:not(.editing) .text-content::before {
  content: 'Text…';
  color: var(--ink-mute);
  font-style: italic;
  pointer-events: none;
}
.text-content {
  outline: none;
  white-space: pre-wrap;
  word-wrap: break-word;
  min-height: 1em;
  -webkit-user-select: text;
  user-select: text;
}
.text-box .text-grip {
  display: none;
  position: absolute;
  bottom: -7px;
  right: -7px;
  width: 12px;
  height: 12px;
  background: var(--paper);
  border: 1.5px solid var(--accent);
  border-radius: 50%;
  cursor: nwse-resize;
  touch-action: none;
}
.text-box.selected .text-grip,
.text-box.editing .text-grip { display: block; }
/* iOS 44pt hit-area so the 12px visual grip is easy to grab on touch. */
.text-box .text-grip::before {
  content: "";
  position: absolute;
  inset: -14px;
}

/* ••• actions menu button on selected/editing text boxes. Mirrors the
   lasso selection's menu button for visual consistency. */
.text-box .text-menu-btn {
  display: none;
  position: absolute;
  top: -14px;
  right: -14px;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--ink);
  border: none;
  cursor: pointer;
  align-items: center;
  justify-content: center;
  gap: 2px;
  padding: 0;
  pointer-events: auto;
  touch-action: manipulation;
  box-shadow: 0 1px 3px rgba(0,0,0,0.2);
  transition: transform 120ms var(--ease), background 120ms var(--ease);
  z-index: 2;
}
.text-box.selected .text-menu-btn,
.text-box.editing .text-menu-btn { display: flex; }
.text-box .text-menu-btn:hover { background: var(--accent); transform: scale(1.06); }
.text-box .text-menu-btn:active { transform: scale(0.94); }
.text-box .text-menu-btn span {
  width: 3px;
  height: 3px;
  background: var(--paper);
  border-radius: 50%;
}
.text-box .text-menu-btn::before {
  content: "";
  position: absolute;
  inset: -8px;
}

/* ============================================================
   Image selection chrome
   ============================================================ */
.image-handle {
  position: absolute;
  pointer-events: auto;
  outline: 1.5px dashed var(--accent);
  background: transparent;
  cursor: move;
}
.image-handle .grip {
  position: absolute;
  width: 12px;
  height: 12px;
  background: var(--paper);
  border: 1.5px solid var(--accent);
  border-radius: 50%;
  bottom: -7px;
  right: -7px;
  cursor: nwse-resize;
}

/* ============================================================
   Prompt bar — floating, draggable
   ============================================================ */
.prompt-bar {
  position: fixed;
  bottom: var(--s-6);
  left: 50%;
  transform: translateX(-50%);
  width: min(680px, calc(100vw - 24px));
  /* Card layout: grip handle → large textarea → action row, stacked. */
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 6px;
  padding: 0 12px 12px;
  background: rgba(255, 255, 255, 0.82);
  backdrop-filter: saturate(160%) blur(20px);
  -webkit-backdrop-filter: saturate(160%) blur(20px);
  border: 1px solid var(--line);
  border-radius: 26px;
  box-shadow: var(--sh-3);
  z-index: 30;
  transition: box-shadow 200ms var(--ease);
  touch-action: none;
}
.prompt-bar.dragging {
  box-shadow: var(--sh-3), 0 0 0 1px var(--accent-soft);
  cursor: grabbing;
}
.prompt-bar[hidden] { display: none; }

/* During a prompt-bar/FAB drag, force the grabbing cursor everywhere and
   block pointer events on the preview iframe + canvases so they can't
   intercept pointermove. Pointer capture already redirects to the grip,
   but on some desktop browsers the iframe still shows a cursor flicker —
   this gives a clean, consistent drag feel. */
body.dragging-prompt { cursor: grabbing !important; }
body.dragging-prompt * { cursor: grabbing !important; }
body.dragging-prompt iframe { pointer-events: none !important; }

/* Collapsed-state floating action button. Same default anchor as the bar
   (bottom-center) so the user finds it where the bar used to be. Tap to
   expand; press-and-drag to reposition (shares saved position with the
   bar). Round 48px target — large enough for touch, small enough to not
   crowd the canvas. */
.prompt-fab {
  position: fixed;
  bottom: var(--s-6);
  left: 50%;
  transform: translateX(-50%);
  width: 48px;
  height: 48px;
  border-radius: 50%;
  background: var(--ink);
  color: var(--paper);
  border: 0;
  cursor: grab;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* Layered shadow: a tight 1px contact shadow keeps the button visually
     anchored to the canvas, a 4-8px mid shadow gives it depth, and a soft
     20px halo lifts it off the page. Slightly darker than --sh-3 because
     a dark FAB needs more contrast against the paper bg to read as "lifted". */
  box-shadow:
    0 1px 2px rgba(20, 20, 20, 0.10),
    0 4px 10px rgba(20, 20, 20, 0.10),
    0 12px 24px rgba(20, 20, 20, 0.12);
  z-index: 30;
  touch-action: none;
  transition: transform 140ms var(--ease), box-shadow 200ms var(--ease);
  -webkit-tap-highlight-color: transparent;
}
.prompt-fab:hover {
  transform: translateX(-50%) translateY(-1px) scale(1.04);
  box-shadow:
    0 2px 4px rgba(20, 20, 20, 0.12),
    0 8px 18px rgba(20, 20, 20, 0.14),
    0 18px 36px rgba(20, 20, 20, 0.16);
}
.prompt-fab:active { cursor: grabbing; transform: translateX(-50%) scale(0.98); }
.prompt-fab.dragging {
  box-shadow:
    0 4px 10px rgba(20, 20, 20, 0.16),
    0 14px 30px rgba(20, 20, 20, 0.20),
    0 0 0 1px var(--accent-soft);
  cursor: grabbing;
}
.prompt-fab .icon { width: 20px; height: 20px; stroke-width: 1.75; }
.prompt-fab[hidden] { display: none; }

/* Collapse button — detached to the bar's top-right corner, OUTSIDE the pill
   but still a child of it so it always travels with the bar when dragged.
   Click → bar hidden, FAB shown. Pulling it out of the input row gives the
   textarea more room without crowding the inline controls. */
.prompt-collapse-btn {
  position: absolute;
  /* Top-right corner badge. In the card layout the submit button lives at
     the BOTTOM-right, so the collapse "×" is now diagonally opposite it —
     no risk of mistaking close for send on touch devices. */
  top: -10px;
  right: -10px;
  appearance: none;
  background: var(--paper, #fff);
  border: 1px solid var(--line);
  width: 26px;
  height: 26px;
  border-radius: 50%;
  color: var(--ink-mute);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  box-shadow: 0 2px 6px rgba(20, 20, 20, 0.14);
  z-index: 2;
  transition: background 120ms var(--ease), color 120ms var(--ease), transform 120ms var(--ease);
}
.prompt-collapse-btn:hover { background: var(--ink); color: var(--paper); transform: scale(1.08); }
.prompt-collapse-btn .icon { width: 13px; height: 13px; stroke-width: 2; }

.grip {
  width: 28px;
  height: 36px;
  border-radius: var(--r-pill);
  background: transparent;
  border: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--ink-mute);
  cursor: grab;
  flex-shrink: 0;
  padding: 0;
  transition: color 120ms var(--ease), background 120ms var(--ease);
  touch-action: none;
}
.grip:hover { color: var(--ink-soft); background: var(--line-soft); }
.grip:active { cursor: grabbing; }
.grip .icon { width: 16px; height: 16px; stroke-width: 1.75; }

/* Prompt-bar grip — a slim top handle (like a bottom-sheet grabber) that
   spans the card's full width. ID-scoped so it overrides the shared .grip
   metrics above without affecting the image-resize handles. The grabber bar
   itself is the ::before pill. */
#promptGrip {
  width: 100%;
  height: 22px;
  border-radius: 26px 26px 0 0;
  background: transparent;
}
#promptGrip::before {
  content: "";
  width: 40px;
  height: 4px;
  border-radius: 999px;
  background: var(--line);
  transition: background 120ms var(--ease);
}
#promptGrip:hover { background: transparent; }
#promptGrip:hover::before { background: var(--ink-mute); }

.prompt-bar textarea {
  width: 100%;
  border: none;
  outline: none;
  resize: none;
  background: transparent;
  font-family: var(--font-ui);
  /* 16px floor — iOS Safari auto-zooms inputs below 16px on focus. */
  font-size: 16px;
  line-height: 1.55;
  /* Generous default height — this is the focus of the card, so it reads as
     a roomy writing area even when empty. Grows with content up to the cap. */
  padding: 4px 8px;
  min-height: 66px;
  max-height: 220px;
  overflow-y: auto;
  -webkit-user-select: text;
  user-select: text;
  color: var(--ink);
  touch-action: auto;
}
.prompt-bar textarea::placeholder {
  color: var(--ink-mute);
}

/* Action row beneath the textarea: tool buttons clustered left, submit
   pinned right (mirrors the reference layout). */
.prompt-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-2);
  padding: 0 2px;
}
.prompt-actions-left {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  min-width: 0;
}

.submit {
  appearance: none;
  background: var(--ink);
  color: var(--paper);
  border: none;
  width: 48px;
  height: 48px;
  border-radius: 50%;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  transition: background 120ms var(--ease), transform 120ms var(--ease);
}

/* Code-paste button sits between the textarea and the submit circle.
   Quieter than the submit (outlined, not filled) so the eye still goes
   to "send" as the primary action. Same 36px footprint so the bar's
   vertical rhythm stays uniform. */
.prompt-code-btn {
  appearance: none;
  background: transparent;
  color: var(--ink);
  border: 1px solid var(--line);
  width: 40px;
  height: 40px;
  border-radius: 50%;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  font: 600 12px ui-monospace, Menlo, Consolas, monospace;
  letter-spacing: -.02em;
  transition: background 120ms var(--ease), border-color 120ms var(--ease), transform 120ms var(--ease);
}
.prompt-code-btn:hover { background: var(--ink); color: var(--paper); border-color: var(--ink); }
.prompt-code-btn:active { transform: scale(0.94); }
.submit:hover { background: var(--accent); transform: rotate(-8deg) scale(1.04); }
.submit:active { transform: scale(0.94); }
.submit .icon { width: 20px; height: 20px; stroke-width: 2; }

/* Submit-disabled state — applied when no style is picked on a new
   project. Keeps the circle visible but mutes it so the user clearly
   sees there's a gate. Hover doesn't animate while disabled. */
.submit:disabled,
.submit[aria-disabled="true"] {
  background: var(--line);
  color: var(--ink-muted, #888);
  cursor: not-allowed;
  transform: none;
}
.submit:disabled:hover,
.submit[aria-disabled="true"]:hover { background: var(--line); transform: none; }

/* ── Prompt-bar side buttons (Style picker + Max Mode) ───────────────
   Sit immediately inside the bar, flanking the textarea. Same 36px
   square footprint as .prompt-code-btn / .submit so the bar's rhythm
   stays uniform. The style button can stretch to fit a label once a
   style is picked ("Style: Slide deck"); max-mode is always icon-only. */
.prompt-side-btn {
  appearance: none;
  background: transparent;
  color: var(--ink);
  border: 1px solid var(--line);
  width: 40px;
  height: 40px;
  border-radius: 50%;
  padding: 0;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  flex-shrink: 0;
  font: 500 12px var(--font-body, system-ui);
  letter-spacing: -.005em;
  transition: background 120ms var(--ease), border-color 120ms var(--ease),
              color 120ms var(--ease), transform 120ms var(--ease);
}
.prompt-side-btn .icon { width: 18px; height: 18px; stroke-width: 1.75; }
.prompt-side-btn:hover { background: var(--ink); color: var(--paper); border-color: var(--ink); }
.prompt-side-btn:active { transform: scale(0.96); }

/* Icon-only style button — the selected style is surfaced via the button's
   tooltip (title) rather than an inline label, keeping the action row clean
   and matching the other circular controls. */
#styleBtn { width: 40px; padding: 0; justify-content: center; }
#styleBtn .style-btn-label { display: none; }
/* When unset (no style picked yet), pulse softly to draw attention.
   Stops the moment a style is chosen. */
#styleBtn.unset {
  border-color: var(--accent, #c75e2a);
  color: var(--accent, #c75e2a);
  animation: style-pulse 2.4s ease-in-out infinite;
}
@keyframes style-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(199, 94, 42, 0.0); }
  50%      { box-shadow: 0 0 0 4px rgba(199, 94, 42, 0.10); }
}

/* Max Mode is icon-only and toggles to a filled active state. When
   armed, the button "lights up" with the accent color and a subtle
   glow — communicates the boost is staged for the next submit. */
.max-mode-btn {
  width: 40px;
  padding: 0;
  justify-content: center;
}
.max-mode-btn[aria-pressed="true"] {
  background: var(--accent, #c75e2a);
  color: var(--paper);
  border-color: var(--accent, #c75e2a);
  box-shadow: 0 0 0 3px rgba(199, 94, 42, 0.18);
}
.max-mode-btn[aria-pressed="true"] .icon { fill: var(--paper); }
.max-mode-btn:disabled,
.max-mode-btn[aria-disabled="true"] {
  opacity: 0.35;
  cursor: not-allowed;
}

/* ── Style picker popover ───────────────────────────────────────────
   A floating panel that opens above the prompt bar. JS handles
   positioning + open/close; CSS handles look + transition. */
.style-popover {
  position: fixed;
  z-index: 200;
  width: min(640px, calc(100vw - 32px));
  max-height: min(620px, 78vh);
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: 16px;
  box-shadow: 0 18px 48px rgba(0, 0, 0, 0.18), 0 2px 6px rgba(0, 0, 0, 0.06);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 160ms var(--ease), transform 160ms var(--ease);
  pointer-events: none;
}
.style-popover.open {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}
.style-popover[hidden] { display: none; }

.style-popover-head {
  padding: 16px 18px 12px;
  border-bottom: 1px solid var(--line);
  position: relative;
}
.style-popover-title {
  font: 600 15px var(--font-body, system-ui);
  letter-spacing: -.01em;
}
.style-popover-sub {
  font: 400 12.5px var(--font-body, system-ui);
  color: var(--ink-muted, #777);
  margin-top: 4px;
  line-height: 1.45;
  padding-right: 28px;
}
#stylePickerClose {
  position: absolute;
  top: 10px;
  right: 10px;
}

.style-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: 10px;
  padding: 14px;
  overflow-y: auto;
}
.style-loading {
  grid-column: 1 / -1;
  text-align: center;
  color: var(--ink-muted, #777);
  font-size: 13px;
  padding: 24px;
}

.style-card {
  appearance: none;
  background: transparent;
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 12px 12px 10px;
  text-align: left;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-height: 96px;
  transition: background 140ms var(--ease), border-color 140ms var(--ease),
              transform 140ms var(--ease);
}
.style-card:hover {
  background: var(--paper-2, #faf8f3);
  border-color: var(--ink);
  transform: translateY(-1px);
}
.style-card.selected {
  border-color: var(--accent, #c75e2a);
  background: rgba(199, 94, 42, 0.06);
}
.style-card-icon {
  font-size: 20px;
  line-height: 1;
}
.style-card-name {
  font: 600 13px var(--font-body, system-ui);
  letter-spacing: -.005em;
}
.style-card-desc {
  font: 400 12px var(--font-body, system-ui);
  color: var(--ink-muted, #777);
  line-height: 1.4;
}

/* Mobile: tighter grid so two cards fit per row. The JS positioner
   already clamps the popover width to (viewport - 24px), so we don't
   override left/right here — that would fight the dynamic positioning
   that follows the prompt bar as it's dragged. */
@media (max-width: 640px) {
  .style-popover {
    max-height: 78vh;
  }
  .style-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

/* ============================================================
   Modals
   ============================================================ */
.modal {
  border: none;
  border-radius: var(--r-lg);
  padding: var(--s-5) var(--s-6) var(--s-5);
  width: min(440px, calc(100% - 32px));
  background: var(--paper);
  color: var(--ink);
  box-shadow: var(--sh-3);
}
.modal::backdrop {
  background: rgba(20, 20, 22, 0.32);
  backdrop-filter: blur(4px);
}
.modal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin: 0 0 var(--s-2);
}
.modal h3 {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 24px;
  font-weight: 400;
  margin: 0;
  color: var(--ink);
  letter-spacing: -0.01em;
}
.muted {
  color: var(--ink-soft);
  font-size: var(--t-sm);
  margin: 0 0 var(--s-3);
}

.field {
  display: block;
  margin-top: var(--s-4);
}
.field span {
  display: block;
  font-size: var(--t-xs);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-mute);
  margin-bottom: var(--s-2);
}
.field input {
  width: 100%;
  padding: var(--s-2) 0 var(--s-2);
  border: none;
  border-bottom: 1px solid var(--line);
  background: transparent;
  font-size: 16px;
  color: var(--ink);
  outline: none;
  transition: border-color 150ms var(--ease);
  -webkit-user-select: text;
  user-select: text;
  touch-action: auto;
}
.field input:focus { border-color: var(--accent); }

.modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--s-2);
  margin-top: var(--s-6);
}

.btn {
  appearance: none;
  background: transparent;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  padding: 8px 16px;
  font-size: var(--t-sm);
  color: var(--ink-soft);
  cursor: pointer;
  transition: all 120ms var(--ease);
}
.btn:hover { background: var(--line-soft); color: var(--ink); }
.btn.primary {
  background: var(--ink);
  color: var(--paper);
  border-color: var(--ink);
}
.btn.primary:hover { background: var(--accent); border-color: var(--accent); }
.btn.danger {
  border-color: rgba(220, 38, 38, 0.35);
  color: var(--danger);
}
.btn.danger:hover { background: rgba(220, 38, 38, 0.08); }

.ico.ghost {
  width: 28px; height: 28px;
}
.ico.ghost .icon { width: 16px; height: 16px; }

.url-box {
  background: var(--canvas);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: var(--s-3);
  font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
  font-size: 13px;
  word-break: break-all;
  color: var(--ink);
  margin: 0;
}

/* Submit dialog states */
.field textarea {
  width: 100%;
  padding: var(--s-2) 0;
  border: none;
  border-bottom: 1px solid var(--line);
  background: transparent;
  font-family: var(--font-ui);
  font-size: 16px;
  line-height: 1.5;
  color: var(--ink);
  resize: vertical;
  outline: none;
  -webkit-user-select: text;
  user-select: text;
}
.field textarea[readonly] {
  color: var(--ink-soft);
  background: var(--canvas);
  padding: var(--s-2) var(--s-3);
  border-radius: var(--r-md);
  border: 1px solid var(--line);
}

.submit-progress {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s-4);
  padding: var(--s-8) 0;
}
.spinner {
  width: 32px;
  height: 32px;
  border: 2px solid var(--line);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: spin 700ms linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
.progress-text {
  margin: 0;
  color: var(--ink-soft);
  font-size: var(--t-sm);
  font-variant-numeric: tabular-nums;
}
.submit-progress-simple {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s-4);
}

/* ============================================================
   Rich loading stage — shown only while the Anthropic API call
   is in flight. A page draws itself, a pen tracks the last rule,
   phase + hint text rotate, an elapsed counter ticks.
   ============================================================ */
.api-loading {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s-4);
  width: 100%;
  padding: var(--s-3) 0 var(--s-2);
}
.api-loading[hidden] { display: none; }

.api-loading-art {
  position: relative;
}

/* Mini-game canvas shown while the model works. */
.api-loading-game {
  width: 100%;
  display: flex;
  justify-content: center;
}
.loading-game-canvas {
  width: 360px;
  max-width: 100%;
  height: 120px;
  background: var(--paper, #FAFAF7);
  border: 1px solid var(--line);
  border-radius: 10px;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.6);
  cursor: pointer;
  touch-action: none;            /* taps = jump, not scroll/zoom */
  -webkit-tap-highlight-color: transparent;
  user-select: none;
}
.api-loading-page {
  fill: url(#apiPageGrad);
  stroke: var(--line);
  stroke-width: 1;
  filter: drop-shadow(0 4px 10px rgba(20, 20, 20, 0.06));
}
.api-loading-page-3 {
  animation: api-page-fan 6s var(--ease) infinite;
  transform-origin: 106px 72px;
  opacity: 0.55;
}
.api-loading-page-2 {
  animation: api-page-fan 6s var(--ease) infinite;
  animation-delay: -2s;
  transform-origin: 102px 68px;
  opacity: 0.78;
}
.api-loading-page-1 {
  animation: api-page-fan 6s var(--ease) infinite;
  animation-delay: -4s;
  transform-origin: 98px 64px;
}
@keyframes api-page-fan {
  0%, 100% { transform: rotate(-1.5deg) translateY(0); }
  50%      { transform: rotate(1deg) translateY(-2px); }
}

/* Each rule "draws" left → right, then erases when the cycle restarts.
   stroke-dasharray + dashoffset is the trick. */
.api-loading-rules {
  /* All rules animate together but with a per-rule delay set inline. */
}
.api-loading-rule {
  stroke-dasharray: 110;
  stroke-dashoffset: 110;
  animation: api-rule-draw 4s var(--ease) infinite;
  animation-delay: var(--d, 0s);
}
@keyframes api-rule-draw {
  0%   { stroke-dashoffset: 110; opacity: 0; }
  10%  { opacity: 1; }
  60%  { stroke-dashoffset: 0; opacity: 1; }
  85%  { stroke-dashoffset: 0; opacity: 1; }
  100% { stroke-dashoffset: 0; opacity: 0; }
}

/* Pen "writes" along the bottom rule, drifting back and forth. */
.api-loading-pen {
  animation: api-pen-glide 4s var(--ease) infinite;
  animation-delay: 2s;
}
@keyframes api-pen-glide {
  0%   { transform: translate(50px, 102px); opacity: 0; }
  10%  { opacity: 1; }
  60%  { transform: translate(120px, 102px); opacity: 1; }
  85%  { transform: translate(120px, 102px); opacity: 1; }
  100% { transform: translate(50px, 102px); opacity: 0; }
}

.api-loading-text {
  text-align: center;
  min-height: 56px;
}
.api-loading-phase {
  margin: 0 0 4px;
  font-family: var(--font-display);
  font-style: italic;
  font-size: 22px;
  color: var(--ink);
  transition: opacity 360ms var(--ease), transform 360ms var(--ease);
}
.api-loading-phase.fading {
  opacity: 0;
  transform: translateY(-2px);
}
.api-loading-hint {
  margin: 0;
  color: var(--ink-mute);
  font-size: var(--t-sm);
  transition: opacity 360ms var(--ease) 60ms;
}
.api-loading-hint.fading { opacity: 0; }

/* Indeterminate "breathing" meter — never claims a percentage so it can't
   lie about progress. A bar slides left ⇄ right inside a track. */
.api-loading-meter {
  width: 200px;
  height: 2px;
  background: var(--line-soft);
  border-radius: 999px;
  overflow: hidden;
  position: relative;
}
.api-loading-meter-fill {
  position: absolute;
  height: 100%;
  width: 40%;
  background: var(--ink);
  border-radius: 999px;
  animation: api-meter-slide 1.6s ease-in-out infinite;
}
@keyframes api-meter-slide {
  0%   { left: -40%; }
  50%  { left: 100%; }
  100% { left: 100%; }
}

.api-loading-elapsed {
  margin: 0;
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: var(--t-xs);
  color: var(--ink-mute);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.5px;
}

/* ============================================================
   @-mention picker for prompt textarea + canvas text boxes
   ============================================================ */
.mention-menu {
  position: fixed;
  z-index: 9999;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  box-shadow: 0 8px 24px rgba(20, 20, 20, 0.12);
  padding: 4px;
  max-height: 280px;
  overflow-y: auto;
  font-family: var(--font-ui);
}
.mention-menu[hidden] { display: none; }
.mention-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-2);
  width: 100%;
  padding: 8px 10px;
  border: none;
  background: transparent;
  border-radius: var(--r-sm);
  cursor: pointer;
  text-align: left;
  font: inherit;
  font-size: var(--t-sm);
  color: var(--ink);
  transition: background 80ms var(--ease);
}
.mention-row:hover,
.mention-row.active {
  background: var(--accent-soft);
}
.mention-row.active {
  outline: 1px solid rgba(37, 99, 235, 0.35);
}
.mention-name {
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 12px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  flex: 1;
}
.mention-source {
  flex-shrink: 0;
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.8px;
  color: var(--ink-mute);
  padding: 2px 6px;
  border-radius: var(--r-pill);
  background: var(--line-soft);
  font-family: var(--font-ui);
}
.mention-source-outputs {
  background: rgba(37, 99, 235, 0.1);
  color: var(--accent);
}
.mention-source-inputs {
  background: rgba(218, 165, 32, 0.12);
  color: #B8860B;
}

/* Attachment picker — the paperclip button's dropdown. Reuses .mention-menu /
   .mention-row visuals; this adds the empty-state hint shown when the project
   has no anchor files yet. */
.attach-empty {
  padding: 14px 12px;
  font-size: var(--t-sm);
  line-height: 1.45;
  color: var(--ink-mute);
  text-align: center;
}

/* ── Credit system: Usage page + limit modal ─────────────────────────── */
.usage-h3 { font-family: var(--font-display); font-style: italic; font-size: 22px; margin: 0 0 var(--s-3); color: var(--ink); }

.credit-balance-card { border: 1px solid var(--line); border-radius: var(--r-md); padding: var(--s-5); background: var(--paper); }
.credit-balance-card.is-out { border-color: var(--accent); }
.credit-balance-num { font-size: 15px; color: var(--ink-mute); }
.credit-balance-num span { font-family: var(--font-display); font-size: 40px; color: var(--ink); font-style: normal; }
.credit-balance-num em { font-style: normal; }
.credit-bar { height: 8px; border-radius: 999px; background: var(--line-soft); margin: var(--s-3) 0 6px; overflow: hidden; }
.credit-bar-fill { height: 100%; background: var(--ink); border-radius: 999px; transition: width .3s var(--ease); }
.credit-balance-card.is-out .credit-bar-fill { background: var(--accent); }
.credit-balance-sub { font-size: var(--t-sm); color: var(--ink-mute); }
.credit-out-note { margin-top: var(--s-3); font-size: var(--t-sm); color: var(--ink); }
.credit-buy-btn { border: 0; background: var(--accent); color: #fff; border-radius: var(--r-pill); padding: 6px 14px; font: inherit; font-size: var(--t-sm); cursor: pointer; margin-left: 6px; }

.credit-history { margin-top: var(--s-4); }
.credit-history-label { font-size: var(--t-xs); text-transform: uppercase; letter-spacing: 1.2px; color: var(--ink-mute); margin-bottom: var(--s-2); }
.credit-grant-list { list-style: none; padding: 0; margin: 0; }
.credit-grant-row { display: flex; align-items: center; gap: var(--s-3); padding: 8px 0; border-bottom: 1px solid var(--line-soft); font-size: var(--t-sm); }
.credit-grant-amt { font-weight: 600; color: var(--ink); min-width: 56px; }
.credit-grant-reason { color: var(--ink-soft); flex: 1; }
.credit-grant-date { color: var(--ink-mute); }
.credit-grant-empty { color: var(--ink-mute); font-size: var(--t-sm); padding: 8px 0; }

.usage-pipeline-tabs { display: flex; gap: 4px; background: var(--line-soft); padding: 4px; border-radius: var(--r-pill); width: fit-content; margin: var(--s-5) 0 var(--s-4); }
.usage-tab { border: 0; background: transparent; color: var(--ink-soft); font: inherit; font-size: var(--t-sm); padding: 6px 16px; border-radius: var(--r-pill); cursor: pointer; }
.usage-tab.active { background: var(--paper); color: var(--ink); box-shadow: 0 1px 3px rgba(0,0,0,.1); }

.credit-spend-total { font-size: 15px; color: var(--ink-mute); margin-bottom: var(--s-3); }
.credit-spend-total span { font-family: var(--font-display); font-size: 32px; color: var(--ink); }
.credit-bar-chart { display: flex; align-items: flex-end; gap: 4px; height: 140px; padding-top: var(--s-2); }
.credit-bar-col { flex: 1; min-width: 6px; display: flex; flex-direction: column; align-items: center; justify-content: flex-end; height: 100%; }
.credit-bar-stick { width: 60%; max-width: 28px; background: var(--ink); border-radius: 4px 4px 0 0; }
.credit-bar-day { font-size: 9px; color: var(--ink-mute); margin-top: 4px; white-space: nowrap; }

.credit-activity-list { list-style: none; padding: 0; margin: 0; }
.credit-activity-row { display: flex; align-items: center; gap: var(--s-3); padding: 10px 0; border-bottom: 1px solid var(--line-soft); font-size: var(--t-sm); }
.credit-activity-date { color: var(--ink-mute); min-width: 84px; }
.credit-activity-proj { flex: 1; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.credit-activity-credits { font-variant-numeric: tabular-nums; color: var(--ink-soft); }
.usage-empty { color: var(--ink-mute); font-size: var(--t-sm); padding: var(--s-3) 0; }

.credit-modal-backdrop { position: fixed; inset: 0; z-index: 400; display: flex; align-items: center; justify-content: center; background: rgba(15,20,25,.45); backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px); }
.credit-modal-backdrop[hidden] { display: none; }
.credit-modal { background: var(--paper); border: 1px solid var(--line); border-radius: var(--r-lg, 14px); padding: var(--s-6); max-width: 380px; width: calc(100vw - 32px); box-shadow: 0 18px 48px rgba(0,0,0,.22); }
.credit-modal h3 { font-family: var(--font-display); font-style: italic; font-size: 24px; margin: 0 0 var(--s-2); }
.credit-modal p { color: var(--ink-soft); font-size: var(--t-base); line-height: 1.55; margin: 0 0 var(--s-4); }
.credit-modal-actions { display: flex; gap: var(--s-2); justify-content: flex-end; }

/* /-skill picker rows: name + description on the left, /slug on the right */
.skill-menu .skill-row {
  align-items: flex-start;
  padding: 10px 12px;
  gap: var(--s-3);
}
.skill-menu .skill-row-main {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
  flex: 1;
}
.skill-menu .skill-name {
  font-family: var(--font-ui);
  font-size: var(--t-sm);
  font-weight: 500;
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.skill-menu .skill-desc {
  font-size: var(--t-xs);
  color: var(--ink-mute);
  line-height: 1.4;
  white-space: normal;
}
.skill-menu .skill-slug {
  flex-shrink: 0;
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 11px;
  color: var(--ink-mute);
  background: var(--line-soft);
  border: 1px solid var(--line);
  padding: 2px 7px;
  border-radius: var(--r-pill);
  align-self: center;
}
.skill-menu .skill-row.active .skill-slug {
  background: var(--paper);
  color: var(--ink);
  border-color: rgba(37, 99, 235, 0.35);
}

/* ============================================================
   Usage / analytics — full-page overlay opened from side panel
   ============================================================ */
.usage-screen {
  position: fixed;
  inset: 0;
  z-index: 260;
  background: var(--paper);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.usage-body {
  flex: 1;
  overflow-y: auto;
  padding: var(--s-8) var(--s-12);
  scroll-behavior: smooth;
}
.usage-loading {
  text-align: center;
  color: var(--ink-mute);
  padding: var(--s-12);
  font-style: italic;
}
.usage-content {
  max-width: 1080px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: var(--s-8);
}
.usage-section h3 {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 22px;
  color: var(--ink);
  font-weight: 400;
  margin: 0 0 var(--s-3);
}
.usage-section .muted-row {
  color: var(--ink-mute);
  font-size: var(--t-sm);
  margin: 0 0 var(--s-3);
}

/* Stat cards grid */
.usage-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: var(--s-3);
}
.usage-card {
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: var(--s-4) var(--s-5);
  display: flex;
  flex-direction: column;
  gap: 4px;
  transition: border-color 120ms var(--ease);
}
.usage-card:hover { border-color: var(--ink-mute); }
.usage-card-label {
  font-size: var(--t-xs);
  color: var(--ink-mute);
  text-transform: uppercase;
  letter-spacing: 1.2px;
}
.usage-card-value {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 32px;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
  line-height: 1.05;
}
.usage-card-sub {
  font-size: var(--t-xs);
  color: var(--ink-mute);
  font-variant-numeric: tabular-nums;
}
.usage-card.accent {
  background: var(--ink);
  border-color: var(--ink);
}
.usage-card.accent .usage-card-label { color: rgba(250, 250, 247, 0.6); }
.usage-card.accent .usage-card-value { color: var(--paper); }
.usage-card.accent .usage-card-sub { color: rgba(250, 250, 247, 0.6); }

/* Chart panel */
.usage-chart-panel {
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: var(--s-5);
  background: var(--paper);
}
.usage-chart-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: var(--s-4);
  flex-wrap: wrap;
  gap: var(--s-2);
}
.usage-chart-toggle {
  display: inline-flex;
  background: var(--line-soft);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  padding: 3px;
  gap: 2px;
}
.usage-chart-toggle button {
  background: transparent;
  border: none;
  padding: 5px 14px;
  border-radius: var(--r-pill);
  font: inherit;
  font-size: var(--t-sm);
  color: var(--ink-mute);
  cursor: pointer;
  transition: all 120ms var(--ease);
}
.usage-chart-toggle button.active {
  background: var(--paper);
  color: var(--ink);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06);
}
.usage-chart {
  width: 100%;
  height: 320px;
  display: block;
  font-family: var(--font-ui);
}
.usage-chart .axis-line { stroke: var(--line); stroke-width: 1; }
.usage-chart .grid-line { stroke: var(--line-soft); stroke-width: 1; stroke-dasharray: 2 4; }
.usage-chart .axis-text { fill: var(--ink-mute); font-size: 11px; font-variant-numeric: tabular-nums; }
.usage-chart .data-line { stroke: var(--ink); stroke-width: 1.6; fill: none; }
.usage-chart .data-line.output { stroke: var(--accent); }
.usage-chart .data-area { fill: var(--accent); fill-opacity: 0.08; }
.usage-chart .data-point { fill: var(--paper); stroke: var(--ink); stroke-width: 1.6; }
.usage-chart .data-point.output { stroke: var(--accent); }
.usage-chart .chart-empty { fill: var(--ink-mute); font-style: italic; }
/* Stacked bar charts (daily cost / daily tokens). bar-in is the headline
   value (Notebook output / Tokens in), bar-out is the secondary slice on top. */
/* ────────────────────────────────────────────────────────────────────
   Editorial bar chart.

   Aesthetic commitment: financial-page columns set in a magazine. Hard
   90° rectangles (no border-radius — Lego-block territory). Two-tone
   stacked fill: ink for input (the "ground", the immovable cost), the
   blue-violet accent for output (the "value", what was produced). The
   columns are hit-targeted across their full slot so taps on iPad land
   reliably even when the visible bar is thin.

   Selection model: clicking a column SELECTS it; sibling columns dim to
   30% opacity (focus mode, FT-style "all eyes on this datum"); the
   selected column gets a subtle ink underline drawn via a CSS triangle
   that points down at the ledger that just opened. No animations on the
   bars themselves — the geometry IS the design; movement would dilute
   the typographic confidence.
   ──────────────────────────────────────────────────────────────────── */
.usage-chart .bar-in  { fill: var(--ink); transition: fill 120ms var(--ease); }
.usage-chart .bar-out { fill: var(--accent); transition: fill 120ms var(--ease); }
.usage-chart .bar-col { cursor: pointer; outline: none; }
.usage-chart .bar-col .bar-hit { fill: transparent; }
.usage-chart .bar-col:hover .bar-in  { fill: #000; }
.usage-chart .bar-col:hover .bar-out { fill: color-mix(in srgb, var(--accent) 88%, #000 12%); }
.usage-chart .bar-col:focus-visible .bar-hit {
  stroke: var(--accent);
  stroke-width: 1.5;
  stroke-dasharray: 3 3;
}

/* When ANY day is selected, dim every other column so the eye lands on
   the chosen one. The selected column itself stays full-opacity AND
   gets a tiny ink "tick" pointed at the ledger below — a hand-set
   editorial mark that says "this row is being inspected." */
#usageBody.has-day-selected .bar-col:not(.selected) .bar-in,
#usageBody.has-day-selected .bar-col:not(.selected) .bar-out {
  opacity: 0.22;
}
.usage-chart .bar-col.selected .bar-in  { fill: #000; }
.usage-chart .bar-col.selected .bar-out { fill: color-mix(in srgb, var(--accent) 82%, #000 18%); }

/* ────────────────────────────────────────────────────────────────────
   Day ledger panel.

   This is the centerpiece. When the user clicks a bar, a panel slides
   into view below the chart. It reads like a hand-set ledger: the date
   in a big serif headline, the totals as a dateline, then the day's
   submissions stacked as rows with monospaced tabular numbers.

   No card chrome, no zebra striping. Hairline rules between rows.
   Generous whitespace. Numbers right-align with optical correction.
   ──────────────────────────────────────────────────────────────────── */
.usage-day-ledger {
  position: relative;
  padding: var(--s-6) var(--s-6) var(--s-5);
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  background: var(--paper);
  /* Slide-in motion. Subtle — the panel descends 8px and fades. No
     bounce, no spring; this is editorial, not theme-park. */
  animation: ledger-slide-in 220ms cubic-bezier(0.2, 0.7, 0.2, 1);
}
@keyframes ledger-slide-in {
  from { opacity: 0; transform: translateY(-8px); }
  to   { opacity: 1; transform: translateY(0); }
}

.ledger-close {
  position: absolute;
  top: var(--s-4);
  right: var(--s-4);
  appearance: none;
  background: transparent;
  border: 1px solid transparent;
  color: var(--ink-mute);
  width: 32px;
  height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  border-radius: 0; /* hard geometry continues */
  transition: color 120ms var(--ease), border-color 120ms var(--ease);
}
.ledger-close:hover {
  color: var(--ink);
  border-color: var(--line);
}
.ledger-close .icon { width: 18px; height: 18px; }

/* Header: serif dateline left, totals right. The date is intentionally
   oversized — it should read as a chapter heading. */
.ledger-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-5);
  padding-bottom: var(--s-4);
  margin-bottom: var(--s-4);
  border-bottom: 1px solid var(--ink);
}
.ledger-dateline { display: flex; align-items: baseline; gap: var(--s-4); }
.ledger-month-day {
  font: 400 56px/1 var(--font-display, Georgia, 'Tiempos Text', serif);
  letter-spacing: -0.02em;
  color: var(--ink);
  font-feature-settings: "lnum", "tnum";
}
.ledger-weekday {
  font: 500 11px/1 var(--font-ui, -apple-system, sans-serif);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-mute);
}

.ledger-totals {
  display: flex;
  align-items: baseline;
  gap: var(--s-4);
}
.ledger-total-cost {
  font: 400 32px/1 var(--font-display, Georgia, serif);
  font-style: italic;
  color: var(--ink);
  font-feature-settings: "lnum", "tnum";
}
.ledger-total-meta {
  font: 500 11px/1 var(--font-ui, -apple-system, sans-serif);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-mute);
}

/* Submission rows — five-column grid with tabular numbers. The columns
   collapse on narrow screens but keep the visual rhythm. */
.ledger-list {
  list-style: none;
  margin: 0;
  padding: 0;
}
.ledger-row {
  display: grid;
  grid-template-columns: 60px 1fr 1fr 140px 80px;
  gap: var(--s-4);
  align-items: baseline;
  padding: var(--s-3) 0;
  border-bottom: 1px solid var(--line-soft, var(--line));
  font-size: 13px;
  color: var(--ink);
  /* Stagger the row reveal on first paint of the ledger so the eye
     reads top-down — a hand-set ledger being filled in one row at a
     time. */
  animation: ledger-row-in 320ms cubic-bezier(0.2, 0.7, 0.2, 1) both;
}
.ledger-row:nth-child(1) { animation-delay: 40ms; }
.ledger-row:nth-child(2) { animation-delay: 80ms; }
.ledger-row:nth-child(3) { animation-delay: 120ms; }
.ledger-row:nth-child(4) { animation-delay: 160ms; }
.ledger-row:nth-child(5) { animation-delay: 200ms; }
.ledger-row:nth-child(6) { animation-delay: 240ms; }
.ledger-row:nth-child(7) { animation-delay: 280ms; }
.ledger-row:nth-child(8) { animation-delay: 320ms; }
.ledger-row:nth-child(n+9) { animation-delay: 360ms; }
@keyframes ledger-row-in {
  from { opacity: 0; transform: translateY(4px); }
  to   { opacity: 1; transform: translateY(0); }
}
.ledger-row:last-child { border-bottom: 0; }

.ledger-time {
  font: 400 12px/1 var(--font-mono, ui-monospace, SFMono-Regular, monospace);
  color: var(--ink-mute);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.04em;
}
.ledger-model {
  font: 500 13px/1.3 var(--font-ui, -apple-system, sans-serif);
  color: var(--ink);
}
.ledger-project {
  font: 400 13px/1.3 var(--font-ui, -apple-system, sans-serif);
  color: var(--ink-mute);
  font-style: italic;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ledger-tokens {
  font: 400 12px/1 var(--font-mono, ui-monospace, SFMono-Regular, monospace);
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  text-align: right;
  display: inline-flex;
  align-items: baseline;
  justify-content: flex-end;
  gap: 6px;
  white-space: nowrap;
}
.ledger-tokens .sep {
  color: var(--ink-mute);
  font-size: 11px;
  padding: 0 1px;
}
.ledger-cost {
  font: 400 13px/1 var(--font-display, Georgia, serif);
  font-feature-settings: "lnum", "tnum";
  color: var(--ink);
  text-align: right;
  white-space: nowrap;
}

.ledger-empty {
  list-style: none;
  padding: var(--s-5) 0;
  font-style: italic;
  color: var(--ink-mute);
  text-align: center;
}
.ledger-hint {
  margin: var(--s-4) 0 0;
  font: 400 11px/1.5 var(--font-ui, -apple-system, sans-serif);
  color: var(--ink-mute);
  letter-spacing: 0.04em;
  text-align: center;
}

/* Narrow-screen / iPad portrait: stack into two visual lines per row.
   The grid collapses to time + model on top, project + tokens + cost on
   the bottom. Keeps the data legible without horizontal scrolling. */
@media (max-width: 720px) {
  .ledger-head { flex-direction: column; align-items: flex-start; gap: var(--s-3); }
  .ledger-month-day { font-size: 44px; }
  .ledger-total-cost { font-size: 24px; }
  .ledger-row {
    grid-template-columns: 60px 1fr 90px;
    grid-template-areas:
      "time   model    cost"
      "time   project  tokens";
    row-gap: 2px;
    column-gap: var(--s-3);
  }
  .ledger-time    { grid-area: time;    align-self: center; }
  .ledger-model   { grid-area: model; }
  .ledger-project { grid-area: project; font-size: 11px; }
  .ledger-tokens  { grid-area: tokens; text-align: left; justify-content: flex-start; font-size: 11px; }
  .ledger-cost    { grid-area: cost; }
}

/* Toolbar above the cards: stale-cache pill on the left, view toggle right. */
.usage-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  margin-bottom: var(--s-4);
  flex-wrap: wrap;
}
.usage-toolbar-left  { display: flex; align-items: center; gap: var(--s-2); min-height: 32px; }
.usage-toolbar-right { display: flex; align-items: center; gap: var(--s-2); }
.usage-stale-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: var(--t-xs);
  color: var(--ink-mute);
  background: var(--line-soft);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  padding: 4px 10px;
  font-style: italic;
}

/* ============================================================
   Project Files dialog
   Left column = current anchors (ordered),
   Right column = available files + upload button.
   ============================================================ */
.project-files-intro {
  font-size: var(--t-sm);
  line-height: 1.55;
  margin: 0 0 var(--s-4);
}
.project-files-cols {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--s-4);
  margin: 0 0 var(--s-4);
}
@media (max-width: 740px) {
  .project-files-cols { grid-template-columns: 1fr; }
}
.project-files-col {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--paper);
  min-height: 360px;
  max-height: 60vh;
  overflow: hidden;
}
.project-files-col-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-2);
  padding: var(--s-3) var(--s-4);
  border-bottom: 1px solid var(--line);
  background: var(--line-soft);
}
.pf-col-title {
  display: flex; align-items: baseline; gap: var(--s-2);
  font-size: var(--t-sm);
  color: var(--ink);
  font-weight: 500;
}
.pf-col-meta {
  font-size: var(--t-xs);
  color: var(--ink-mute);
  font-variant-numeric: tabular-nums;
  font-weight: 400;
}
.pf-col-actions { display: flex; gap: 6px; }
/* Compact button variant for the column header */
.btn.small {
  padding: 5px 12px;
  font-size: var(--t-xs);
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.btn.small.ghost-btn {
  padding: 5px 8px;
}
.project-files-list {
  flex: 1;
  overflow-y: auto;
  padding: var(--s-2);
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.project-files-empty {
  text-align: center;
  font-size: var(--t-sm);
  padding: var(--s-5);
  font-style: italic;
  color: var(--ink-mute);
}

/* ROW: two-line layout. Top line owns the full width for the name. */
.project-files-row {
  display: grid;
  grid-template-columns: auto 1fr auto;   /* badge | name+meta | actions */
  align-items: center;
  gap: var(--s-3);
  padding: 10px 12px;
  border-radius: var(--r-md);
  border: 1px solid var(--line-soft);
  background: var(--paper);
  user-select: none;
  font-size: var(--t-sm);
  transition: all 120ms var(--ease);
}
.project-files-row:hover {
  border-color: var(--line);
  background: var(--line-soft);
}
.project-files-row.selected {
  background: rgba(37, 99, 235, 0.04);
  border-color: rgba(37, 99, 235, 0.25);
}

.project-files-row .pf-order {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 18px;
  color: var(--ink);
  width: 28px;
  text-align: center;
  font-variant-numeric: tabular-nums;
}
.project-files-row .pf-info {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
}
.project-files-row .pf-name {
  font-family: var(--font-ui);
  font-size: var(--t-sm);
  font-weight: 500;
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.project-files-row .pf-meta {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: var(--t-xs);
  color: var(--ink-mute);
  font-variant-numeric: tabular-nums;
  flex-wrap: wrap;
}
.project-files-row .pf-source {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.8px;
  padding: 1px 6px;
  border-radius: var(--r-pill);
  font-weight: 500;
}
.project-files-row .pf-source.outputs { background: rgba(37,99,235,0.10); color: var(--accent); }
.project-files-row .pf-source.inputs  { background: rgba(218,165,32,0.12); color: #B8860B; }
.project-files-row .pf-actions {
  display: flex;
  gap: 2px;
  align-items: center;
}
.project-files-row .pf-action {
  width: 28px; height: 28px;
  border: none; background: transparent;
  cursor: pointer;
  border-radius: 6px;
  color: var(--ink-mute);
  display: flex; align-items: center; justify-content: center;
  transition: all 120ms var(--ease);
}
.project-files-row .pf-action:hover { background: var(--paper); color: var(--ink); border: 1px solid var(--line); }
.project-files-row .pf-action.danger:hover { background: rgba(220,38,38,0.08); color: var(--danger); border-color: rgba(220,38,38,0.25); }
.project-files-row .pf-action .icon { width: 13px; height: 13px; }
.project-files-row .pf-action[disabled] { opacity: 0.3; cursor: not-allowed; }

/* "+ Add" pill in available column */
.project-files-row .pf-add {
  font: inherit;
  font-size: var(--t-xs);
  background: var(--ink);
  color: var(--paper);
  border: 1px solid var(--ink);
  padding: 6px 14px;
  border-radius: var(--r-pill);
  cursor: pointer;
  white-space: nowrap;
  transition: all 120ms var(--ease);
}
.project-files-row .pf-add:hover { background: var(--accent); border-color: var(--accent); }
.project-files-row.in-anchors {
  opacity: 0.45;
  background: var(--line-soft);
}
.project-files-row.in-anchors .pf-add {
  background: transparent;
  color: var(--ink-mute);
  border-color: var(--line);
  cursor: default;
}

.pf-upload-progress {
  font-size: var(--t-xs);
  padding: 8px var(--s-4);
  border-top: 1px solid var(--line);
  background: var(--line-soft);
}
.pf-summary {
  font-size: var(--t-xs);
  margin-right: auto;
}

/* Recent submissions table */
.usage-table-wrap {
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  overflow: hidden;
  background: var(--paper);
}
.usage-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--t-sm);
}
.usage-table thead th {
  background: var(--line-soft);
  color: var(--ink-mute);
  text-transform: uppercase;
  font-size: 10px;
  letter-spacing: 1.2px;
  font-weight: 500;
  padding: 10px var(--s-3);
  text-align: left;
  white-space: nowrap;
  border-bottom: 1px solid var(--line);
}
.usage-table tbody td {
  padding: 10px var(--s-3);
  border-bottom: 1px solid var(--line-soft);
  color: var(--ink);
  vertical-align: top;
  font-variant-numeric: tabular-nums;
}
.usage-table tbody tr:last-child td { border-bottom: none; }
.usage-table tbody tr:hover { background: var(--line-soft); }
.usage-table .mode-pill {
  display: inline-block;
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.8px;
  padding: 2px 8px;
  border-radius: var(--r-pill);
  font-weight: 500;
}
.usage-table .mode-pill.api { background: rgba(37, 99, 235, 0.1); color: var(--accent); }
.usage-table .mode-pill.chrome { background: rgba(218, 165, 32, 0.12); color: #B8860B; }
.usage-table .col-when { color: var(--ink-mute); white-space: nowrap; }
.usage-table .col-project { font-weight: 500; max-width: 220px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.usage-table .col-num { text-align: right; }
.usage-table .col-dim { color: var(--ink-mute); }
@media (max-width: 820px) {
  .usage-body { padding: var(--s-5) var(--s-4); }
  .usage-card-value { font-size: 26px; }
  .usage-table .col-project { max-width: 140px; }
}

.submit-result {
  padding: var(--s-4) 0 0;
}
.result-text {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 22px;
  margin: 0 0 var(--s-2);
  color: var(--ink);
}
.result-text.error {
  color: var(--danger);
}

/* ============================================================
   Context menu (paste / copy / cut / delete)
   ============================================================ */
.ctx-menu {
  position: fixed;
  z-index: 100;
  min-width: 220px;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--sh-3);
  padding: 6px;
  display: flex;
  flex-direction: column;
  gap: 1px;
  -webkit-user-select: none;
  user-select: none;
  touch-action: none;
}

.ctx-item {
  appearance: none;
  background: transparent;
  border: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  padding: 12px 14px;
  min-height: 44px;
  border-radius: 8px;
  font-size: 15px;
  color: var(--ink);
  cursor: pointer;
  text-align: left;
  transition: background 80ms var(--ease);
}
.ctx-item:hover { background: var(--line-soft); }
.ctx-item:active { background: var(--accent-soft); }
.ctx-item.disabled {
  color: var(--ink-mute);
  cursor: not-allowed;
}
.ctx-item.disabled:hover { background: transparent; }
.ctx-item.danger { color: var(--danger); }
.ctx-item.danger:hover { background: rgba(220, 38, 38, 0.07); }
.ctx-hint {
  color: var(--ink-mute);
  font-size: 11px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}

/* Color-swatch left rail. Square, hairline border so light/cream colors
   are visible against the paper background. The label that follows the
   swatch uses tabular monospace so hex codes align in a clean column. */
.ctx-item .ctx-swatch {
  display: inline-block;
  width: 18px;
  height: 18px;
  border: 1px solid rgba(0, 0, 0, 0.18);
  flex-shrink: 0;
  /* Subtle inner highlight so pure-black swatches read as 3D rather
     than a hole punched in the menu. */
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.6) inset;
}
.ctx-item.has-swatch .ctx-label {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 13px;
  letter-spacing: 0.02em;
  color: var(--ink-soft);
}

/* Connect / submit / outputs dialogs */
.modal.modal-wide {
  width: min(480px, calc(100% - 32px));
}
/* Project files dialog needs much more breathing room — two parallel
   columns of file rows with names + meta. */
#projectFilesDialog.modal.modal-wide {
  width: min(960px, calc(100% - 32px));
  max-width: 960px;
}

.connect-section {
  border-top: 1px solid var(--line);
  padding: var(--s-4) 0;
}
.connect-section:first-of-type { border-top: none; padding-top: var(--s-2); }
.section-head {
  display: flex; align-items: center; gap: var(--s-2);
  margin-bottom: var(--s-2);
}
.section-label {
  font-size: var(--t-xs);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--ink-mute);
  font-weight: 500;
}

/* Connection status dot */
.status-dot {
  display: inline-block;
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--ink-mute);
  flex-shrink: 0;
}
.status-dot.status-on  { background: #16A34A; box-shadow: 0 0 0 3px rgba(22,163,74,0.15); }
.status-dot.status-off { background: var(--ink-mute); }
.status-dot.status-busy { background: #CA8A04; animation: pulse 1.2s ease-in-out infinite; }
@keyframes pulse { 50% { opacity: 0.45; } }

/* Plug button — small dot in the corner shows session state */
.ico.plug { position: relative; }
.plug-status {
  position: absolute;
  top: 2px; right: 2px;
  width: 8px; height: 8px;
  border-radius: 50%;
  border: 1.5px solid var(--ink);
  background: transparent;
  pointer-events: none;
}
.plug-status.status-on  { background: #16A34A; border-color: #16A34A; }
.plug-status.status-busy { background: #CA8A04; border-color: #CA8A04; }

/* iOS-style toggle */
.toggle-row {
  display: flex; align-items: center;
  margin-top: var(--s-3);
}
.toggle {
  display: inline-flex; align-items: center; gap: var(--s-3);
  cursor: pointer;
  -webkit-user-select: none; user-select: none;
}
.toggle input { position: absolute; opacity: 0; pointer-events: none; }
.toggle .track {
  width: 44px; height: 26px;
  background: var(--line);
  border-radius: 999px;
  position: relative;
  transition: background 200ms var(--ease);
}
.toggle .thumb {
  position: absolute;
  top: 2px; left: 2px;
  width: 22px; height: 22px;
  background: var(--paper);
  border-radius: 50%;
  box-shadow: 0 1px 3px rgba(0,0,0,0.2);
  transition: transform 200ms var(--ease);
}
.toggle input:checked + .track {
  background: #16A34A;
}
.toggle input:checked + .track .thumb {
  transform: translateX(18px);
}
.toggle-label {
  font-size: var(--t-sm);
  color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
}

.bridge-mode-row {
  display: flex; gap: 8px;
}
.radio-pill {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 12px;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  cursor: pointer;
  font-size: var(--t-sm);
  color: var(--ink-soft);
  -webkit-user-select: none; user-select: none;
}
.radio-pill input { margin: 0; }
.radio-pill:has(input:checked) {
  background: var(--ink); color: var(--paper); border-color: var(--ink);
}

.claude-chooser {
  background: var(--canvas);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: var(--s-3);
  margin-top: var(--s-3);
}
.claude-chooser .modal-actions { margin-top: var(--s-3); }

/* Page multi-select strip in the submit dialog */
.field-label-row {
  display: flex; align-items: center; justify-content: space-between;
  width: 100%;
}
.field-hint { font-size: var(--t-xs); color: var(--ink-mute); font-weight: 400; text-transform: none; letter-spacing: 0; }
.link-btn {
  background: none; border: none; padding: 0;
  color: var(--accent); font: inherit; cursor: pointer;
}
.link-btn:hover { text-decoration: underline; }

/* Page picker / browser grid. Used by the Open-in-Notebook picker, the
   Submit dialog's "Pages to send" multi-select, AND the new Pages Browser
   that lets the user jump to or delete any notebook page. A real 2D grid
   with generous thumbnails — small horizontal-strip thumbs are unreadable
   and the touch targets are too small to reliably select on iPad. */
.page-selector {
  display: grid;
  /* Each tile is fixed-size (160px) rather than 1fr stretch — that
     keeps thumbnails consistent at any dialog width, including the
     narrow iPad-portrait layout, instead of ballooning to one column
     full-width with a giant aspect-ratio'd image. */
  grid-template-columns: repeat(auto-fill, 160px);
  justify-content: start;
  gap: 14px;
  padding: 8px 0;
  max-height: min(60vh, 540px);
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: thin;
}
.page-thumb {
  position: relative;
  cursor: pointer;
  -webkit-user-select: none;
  user-select: none;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  /* Touch target — entire card is clickable, not just the image. */
  padding: 4px;
  border-radius: 8px;
  width: 160px;
  transition: background 100ms var(--ease);
}
.page-thumb:hover { background: rgba(0,0,0,0.03); }
.page-thumb-img {
  width: 100%;
  aspect-ratio: 800 / 1100;   /* matches the notebook PAGE_W/PAGE_H */
  height: auto;
  object-fit: contain;
  border: 2px solid var(--line);
  border-radius: 6px;
  background: var(--paper);
  display: block;
  pointer-events: none;       /* click passes through to .page-thumb */
  transition: border-color 120ms var(--ease), box-shadow 120ms var(--ease);
}
.page-thumb.selected .page-thumb-img {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.18);
}
.page-thumb-check {
  position: absolute;
  top: 10px; right: 10px;
  width: 22px; height: 22px;
  border-radius: 50%;
  background: var(--paper);
  border: 1.5px solid var(--line);
  display: flex; align-items: center; justify-content: center;
  pointer-events: none;
  transition: all 120ms var(--ease);
}
.page-thumb.selected .page-thumb-check {
  background: var(--accent);
  border-color: var(--accent);
}
.page-thumb.selected .page-thumb-check::after {
  content: "";
  width: 6px; height: 11px;
  border: solid white;
  border-width: 0 2px 2px 0;
  transform: rotate(45deg) translate(-1px, -2px);
}
.page-thumb-label {
  text-align: center;
  font-size: 12px;
  color: var(--ink-mute);
  margin-top: 6px;
  font-variant-numeric: tabular-nums;
}

/* Pages Browser variant: tap-to-navigate by default. Each tile has a
   `.page-thumb-nav` modifier so the navigation-only look is distinct
   from the multi-select picker. The `.current` modifier highlights
   which page is open. */
.page-thumb.page-thumb-nav .page-thumb-img { border-color: transparent; }
.page-thumb.page-thumb-nav.current .page-thumb-img {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.18);
}
/* Default Pages-Browser navigation mode: hide the check (no selection
   affordance). Order matters — the select-mode rule below uses higher
   specificity to override. */
.page-thumb.page-thumb-nav .page-thumb-check { display: none; }
.page-selector.select-mode .page-thumb.page-thumb-nav .page-thumb-check {
  display: flex;
}

/* Pages Browser dialog chrome. Sized larger on iPad/desktop and goes
   near-full-screen on phones so the grid breathes. */
.pages-browser {
  width: min(820px, calc(100vw - 24px));
  max-width: 100vw;
}
.pages-browser-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px;
}
.pages-browser-head-actions {
  display: flex; align-items: center; gap: 8px;
}
.btn.btn-sm { padding: 6px 12px; font-size: 13px; }
.btn.ghost {
  background: transparent;
  color: var(--ink);
  border: 1px solid var(--line);
}
.btn.ghost:hover { background: rgba(0,0,0,0.04); }
.btn.ghost.active {
  background: var(--ink);
  color: var(--paper);
  border-color: var(--ink);
}
.btn.danger {
  background: #e11;
  color: #fff;
  border: 1px solid #e11;
}
.btn.danger:hover { background: #c10; border-color: #c10; }
.btn.danger:disabled {
  background: var(--line);
  color: var(--ink-mute);
  border-color: var(--line);
  cursor: not-allowed;
}

/* Bottom action bar — only visible during select mode. Sticks to the
   bottom of the dialog so a tall scrolling grid doesn't bury it. */
.pages-browser-actions {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 0 4px;
  margin-top: 12px;
  border-top: 1px solid var(--line);
  position: sticky;
  bottom: 0;
  background: var(--paper);
}

/* Mobile / iPad portrait: dialog goes near-fullscreen, grid expands to
   fill width, tiles slightly larger so the touch targets stay generous.
   The header/actions remain sticky so the user always has Select +
   Delete within thumb reach. */
@media (max-width: 640px) {
  /* IMPORTANT: gate ALL these styles on [open]. `display: flex` here
     overrides the UA's `dialog:not([open]) { display: none }` and would
     leave the dialog VISIBLE on iPhone-width viewports even when not
     opened. The bug it caused: random "Pages" dialog appearing on screen
     during text-box / keyboard interactions on iPhone (not iPad / Android,
     because those viewports are >640px and this @media never fires). */
  .pages-browser[open] {
    width: 100vw;
    max-width: 100vw;
    max-height: 100vh;
    height: 100vh;
    margin: 0;
    border-radius: 0;
    padding: 16px;
    display: flex;
    flex-direction: column;
  }
  .pages-browser .page-selector {
    max-height: none;
    flex: 1;
    grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  }
  .pages-browser .page-thumb {
    width: auto;
  }
  .pages-browser-actions {
    margin-top: auto;
    padding-bottom: calc(8px + env(safe-area-inset-bottom, 0));
  }
}
.page-selector-loading {
  font-size: var(--t-sm);
  color: var(--ink-mute);
  padding: 12px 0;
}

/* Model picker — pill row of radio buttons inside submit dialog */
.model-picker {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  background: var(--line-soft);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  padding: 4px;
}
.model-option {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: 8px 12px;
  border-radius: var(--r-pill);
  cursor: pointer;
  user-select: none;
  transition: all 120ms var(--ease);
  position: relative;
}
.model-option input { display: none; }
.model-option:hover { background: var(--paper); }
.model-option:has(input:checked) {
  background: var(--ink);
  color: var(--paper);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15);
}
.model-option .model-name {
  font-size: var(--t-sm);
  font-weight: 500;
}
.model-option .model-tag {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.8px;
  color: var(--ink-mute);
}
.model-option:has(input:checked) .model-tag {
  color: rgba(250, 250, 247, 0.6);
}

/* Cached files (read-only) summary in submit dialog */
.cached-files-list {
  display: flex;
  flex-direction: column;
  gap: 2px;
  max-height: 140px;
  overflow-y: auto;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: var(--s-2);
  background: var(--paper);
}
.cached-file-row {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: var(--s-2);
  padding: 6px 10px;
  border-radius: var(--r-sm);
  font-size: var(--t-sm);
}
.cached-file-row .cf-order {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 13px;
  color: var(--ink-mute);
  min-width: 22px;
  text-align: center;
  font-variant-numeric: tabular-nums;
}
.cached-file-row .cf-name {
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 12px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--ink);
}
/* Image-anchor thumbnail — shown only for PNG/JPEG/etc. uploads so the
   user can visually confirm which file will go up. Square contain-fit so
   logos/photos with any aspect ratio render predictably; checkerboard
   background reveals transparency for PNGs / SVGs. */
.cached-file-row .cf-thumb {
  flex-shrink: 0;
  width: 22px;
  height: 22px;
  object-fit: contain;
  border-radius: 4px;
  border: 1px solid var(--line);
  background-color: var(--paper);
  background-image:
    linear-gradient(45deg, rgba(0,0,0,0.04) 25%, transparent 25%, transparent 75%, rgba(0,0,0,0.04) 75%),
    linear-gradient(45deg, rgba(0,0,0,0.04) 25%, transparent 25%, transparent 75%, rgba(0,0,0,0.04) 75%);
  background-size: 8px 8px;
  background-position: 0 0, 4px 4px;
}
.cached-file-row .cf-source {
  flex-shrink: 0;
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.8px;
  padding: 2px 7px;
  border-radius: var(--r-pill);
  font-family: var(--font-ui);
}
.cached-file-row .cf-source.outputs { background: rgba(37,99,235,0.10); color: var(--accent); }
.cached-file-row .cf-source.inputs  { background: rgba(218,165,32,0.12); color: #B8860B; }
.cached-file-row .cf-source.skill {
  background: rgba(124, 58, 237, 0.10);   /* violet */
  color: #6D28D9;
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  text-transform: none;
  letter-spacing: 0;
  font-size: 11px;
}
.cached-file-row .cf-desc {
  font-size: var(--t-xs);
  color: var(--ink-mute);
  margin-top: 2px;
}
.cached-files-empty {
  text-align: center;
  font-size: var(--t-xs);
  color: var(--ink-mute);
  font-style: italic;
  padding: var(--s-3);
}

/* Attach-source tabs (HTML / Files) inside submit dialog */
.attach-source-tabs {
  display: inline-flex;
  background: var(--line-soft);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  padding: 3px;
  margin-bottom: var(--s-2);
  gap: 2px;
}
.attach-source-tab {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 14px;
  border-radius: var(--r-pill);
  font-size: var(--t-sm);
  color: var(--ink-mute);
  cursor: pointer;
  user-select: none;
  transition: all 120ms var(--ease);
}
.attach-source-tab:has(input:checked) {
  background: var(--paper);
  color: var(--ink);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06);
}
.attach-source-tab input { display: none; }
.attach-source-tab small { font-size: 11px; }

/* HTML attachments list (Chrome bridge submit) */
.attach-html-list {
  display: flex;
  flex-direction: column;
  gap: 2px;
  max-height: 180px;
  overflow-y: auto;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: var(--s-2);
  background: var(--paper);
}
.attach-html-row {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: var(--s-2);
  padding: 6px 8px;
  border-radius: var(--r-sm);
  cursor: pointer;
  font-size: var(--t-sm);
  user-select: none;
}
.attach-html-row:hover { background: var(--hover); }
.attach-html-row .attach-html-cb {
  width: 16px; height: 16px; margin: 0;
  accent-color: var(--accent, #1a1a1a);
}
.attach-html-name {
  font-family: var(--mono, ui-monospace, monospace);
  font-size: 12px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.attach-html-meta {
  font-size: 11px;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.attach-html-empty {
  font-size: var(--t-sm);
  padding: 6px 4px;
}

/* Outputs modal */
.outputs-actions {
  display: flex; gap: var(--s-2); margin: var(--s-3) 0;
}
.outputs-actions .btn {
  display: inline-flex; align-items: center; gap: 6px;
}
.outputs-list {
  max-height: 50vh; overflow-y: auto;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: 4px;
}
.output-item {
  display: flex; align-items: center; gap: var(--s-3);
  padding: var(--s-3);
  border-radius: var(--r-sm);
  border: 1px solid transparent;
  transition: background 100ms var(--ease);
}
.output-item:hover { background: var(--canvas); }
.output-item + .output-item { border-top: 1px solid var(--line); }
.output-info { flex: 1; min-width: 0; }
.output-name {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 13px;
  color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  -webkit-user-select: text; user-select: text;
}
.output-meta {
  font-size: var(--t-xs); color: var(--ink-mute); margin-top: 2px;
  font-variant-numeric: tabular-nums;
}
.output-actions { display: flex; gap: 4px; flex-shrink: 0; }
.output-actions .btn { padding: 6px 10px; font-size: var(--t-xs); }
.output-actions .btn .icon { width: 12px; height: 12px; }
.outputs-loading {
  text-align: center; color: var(--ink-mute);
  padding: var(--s-6); font-size: var(--t-sm);
}

/* Outputs button badge */
.outputs-badge {
  position: absolute;
  top: -2px; right: -2px;
  min-width: 14px; height: 14px;
  border-radius: 7px;
  background: var(--accent);
  color: var(--paper);
  font-size: 9px;
  font-weight: 600;
  display: flex; align-items: center; justify-content: center;
  padding: 0 3px;
  pointer-events: none;
}
/* Small unfilled dot indicator that lights up when a new file arrives in
   Outputs / Inputs since the user last opened that panel. Replaces the
   noisy count badge. */
.new-file-dot {
  position: absolute;
  top: 4px; right: 4px;
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 2px var(--paper);
  pointer-events: none;
  animation: nf-dot-in 0.25s var(--ease);
}
@keyframes nf-dot-in {
  from { transform: scale(0); opacity: 0; }
  to   { transform: scale(1); opacity: 1; }
}
.ico { position: relative; }

.connect-info {
  display: flex;
  flex-direction: column;
  gap: var(--s-3);
  margin-top: var(--s-3);
}

.connect-loading {
  text-align: center;
  color: var(--ink-mute);
  font-size: var(--t-sm);
  padding: var(--s-8) 0;
}

.connect-card {
  display: grid;
  grid-template-columns: 140px 1fr;
  gap: var(--s-4);
  padding: var(--s-3);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--paper);
  align-items: center;
}

.connect-card .qr {
  width: 140px;
  height: 140px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.connect-card .qr svg { width: 100%; height: auto; display: block; }

.connect-card .info {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
}
.connect-card .info .iface {
  font-size: var(--t-xs);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-mute);
}
.connect-card .info .url {
  font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
  font-size: 13px;
  color: var(--ink);
  word-break: break-all;
  -webkit-user-select: text;
  user-select: text;
}
.connect-card .copy {
  align-self: flex-start;
  font-size: var(--t-xs);
  padding: 4px 10px;
  border-radius: var(--r-pill);
  border: 1px solid var(--line);
  background: transparent;
  color: var(--ink-soft);
  cursor: pointer;
  transition: all 120ms var(--ease);
}
.connect-card .copy:hover { background: var(--ink); color: var(--paper); border-color: var(--ink); }
.connect-card .copy.copied { background: var(--accent); color: var(--paper); border-color: var(--accent); }

.connect-note {
  font-size: var(--t-xs);
  color: var(--ink-mute);
  text-align: center;
  margin: 0;
}

/* ============================================================
   Project chrome — toolbar group, side panel, welcome screen
   ============================================================ */
.tg.project {
  display: flex;
  align-items: center;
  gap: var(--s-1);
  padding-right: var(--s-2);
  margin-right: var(--s-2);
  border-right: 1px solid var(--line);
}
.project-name {
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--r-pill);
  padding: 4px 12px;
  font-family: var(--font-ui);
  font-size: var(--t-sm);
  color: var(--ink-soft);
  cursor: pointer;
  max-width: 180px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  transition: all 120ms var(--ease);
}
.project-name:hover { background: var(--line-soft); color: var(--ink); }
@media (max-width: 820px) {
  .project-name { display: none; }
}

/* Side panel for switching/creating projects */
.projects-panel {
  position: fixed;
  top: 0;
  left: 0;
  bottom: 0;
  /* Widened from 320px → 380px so the project rows have room for
     four action buttons (Files / Share / Rename / Delete) plus the
     project name without crowding. Also gives the global panel-row
     links (Inputs / Outputs / Connect / Usage / Help) generous
     horizontal breathing room — the UI rule is no clutter. */
  width: 380px;
  max-width: 90vw;
  background: var(--paper);
  border-right: 1px solid var(--line);
  box-shadow: 4px 0 24px rgba(0, 0, 0, 0.06);
  z-index: 200;
  display: flex;
  flex-direction: column;
  transform: translateX(0);
  transition: transform 220ms var(--ease);
}
.projects-panel[hidden] {
  display: flex !important;
  transform: translateX(-105%);
  pointer-events: none;
}
.projects-panel-scrim {
  position: fixed;
  inset: 0;
  background: rgba(20, 20, 20, 0.18);
  z-index: 190;
  opacity: 1;
  transition: opacity 220ms var(--ease);
}
.projects-panel-scrim[hidden] {
  display: block !important;
  opacity: 0;
  pointer-events: none;
}
.projects-panel-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--s-4) var(--s-5);
  border-bottom: 1px solid var(--line);
}
.projects-panel-head h3 {
  margin: 0;
  font-family: var(--font-display);
  font-style: italic;
  font-size: 24px;
  color: var(--ink);
  font-weight: 400;
}
.projects-panel-actions {
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
  padding: var(--s-4) var(--s-5);
  border-bottom: 1px solid var(--line);
}
/* Action buttons in the side panel — center the LABEL within the button
   irrespective of the leading icon's footprint. Without this, the icon
   would push the text slightly off-center. Position the icon absolutely
   on the left so the text occupies the full button width and centers
   geometrically. */
.projects-panel-actions .btn {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
}
.projects-panel-actions .btn .icon {
  position: absolute;
  left: 16px;
  top: 50%;
  transform: translateY(-50%);
}
/* Shared flat-row link used for Projects toggle + Usage & Help links in
   the sidebar. All three share the same flat-row visual treatment: an
   icon at the left, an uppercase letter-spaced label, and (for the
   Projects toggle only) a chevron at the right that rotates 90° on
   expand. The .panel-row-stack wrapper takes flex:1 so the list, when
   expanded, can grow inside it. */
.panel-row-stack {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-height: 0;
}
.panel-row-link {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  width: 100%;
  padding: var(--s-3) var(--s-5);
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--line);
  cursor: pointer;
  color: var(--ink);
  font: 600 12px/1 var(--font-body, system-ui);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  text-align: left;
  -webkit-tap-highlight-color: transparent;
}
.panel-row-link:hover { background: rgba(0, 0, 0, 0.03); }
.panel-row-link:focus-visible { outline: 2px solid var(--accent, #6366f1); outline-offset: -2px; }
.panel-row-link .panel-row-icon {
  width: 16px;
  height: 16px;
  color: var(--ink-muted, currentColor);
  opacity: 0.75;
  flex: 0 0 16px;
  /* SVG defaults: keep paths outlined (stroke) rather than filled, matching
     the rest of the app's monochrome icon language. Without this, <use>
     of symbols with circle/path elements renders as solid fills. */
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
}
.panel-row-link .panel-row-icon[data-fill="solid"] { fill: currentColor; stroke: none; }
.panel-row-link .panel-row-label { flex: 1; }
.panel-row-link .panel-row-chev {
  width: 14px;
  height: 14px;
  color: var(--ink-muted, currentColor);
  opacity: 0.6;
  transition: transform 160ms ease;
}
.panel-row-link[aria-expanded="true"] .panel-row-chev { transform: rotate(90deg); }
/* Connection dot for the cc-bridge row — green when configured. */
.panel-row-link .cc-bridge-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #16a34a;
  margin-left: auto;
  box-shadow: 0 0 0 2px rgba(22, 163, 74, 0.18);
}
.panel-row-link .cc-bridge-dot[hidden] { display: none; }

/* New-file indicator for the Inputs / Outputs rows. Same accent-blue
   dot the old top-right toolbar buttons used; restyled here for the
   panel-row layout. Sits at the row's far right, margin-left:auto
   pushing it past the label. */
.panel-row-link .panel-row-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--accent, #2563eb);
  margin-left: auto;
  box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.18);
}
.panel-row-link .panel-row-dot[hidden] { display: none; }

.projects-panel-list {
  flex: 1;
  overflow-y: auto;
  padding: var(--s-2);
}
.projects-panel-list[hidden] { display: none; }
.projects-panel-list .muted { padding: var(--s-3) var(--s-4); }

.project-row {
  display: grid;
  /* 5 columns: info (flex) + 4 action buttons (Files / Share /
     Rename / Delete). Was 4 columns before Share was added, which
     wrapped Delete onto a second line. Keep this in sync with the
     button count rendered in renderProjectsPanelList(). */
  grid-template-columns: 1fr auto auto auto auto;
  align-items: center;
  gap: 2px;
  padding: var(--s-3) var(--s-4);
  border-radius: var(--r-md);
  cursor: pointer;
  user-select: none;
  transition: background 120ms var(--ease);
}
.project-row:hover { background: var(--line-soft); }
.project-row.active {
  background: var(--accent-soft);
}
.project-row.active .project-row-name { color: var(--ink); font-weight: 500; }
.project-row-info {
  min-width: 0;
}
.project-row-name {
  font-size: var(--t-sm);
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.project-row-meta {
  font-size: var(--t-xs);
  color: var(--ink-mute);
  font-variant-numeric: tabular-nums;
  margin-top: 2px;
}
.project-row-action {
  background: transparent;
  border: none;
  cursor: pointer;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--ink-mute);
  transition: all 120ms var(--ease);
}
.project-row-action:hover { background: var(--paper); color: var(--ink); }
.project-row-action.danger:hover { background: rgba(220, 38, 38, 0.08); color: var(--danger); }
.project-row-action .icon { width: 14px; height: 14px; }
/* Badge over the folder icon to show anchor count */
.project-row-action {
  position: relative;
}
.project-row-badge {
  position: absolute;
  top: 0;
  right: 0;
  min-width: 14px;
  height: 14px;
  padding: 0 4px;
  border-radius: 7px;
  background: var(--ink);
  color: var(--paper);
  font-size: 9px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  line-height: 1;
}
.project-row.active .project-row-badge {
  background: var(--accent);
}

/* Welcome screen */
.welcome-screen {
  position: fixed;
  inset: 0;
  z-index: 250;
  background:
    radial-gradient(circle at 20% 30%, rgba(37, 99, 235, 0.08) 0, transparent 50%),
    radial-gradient(circle at 80% 70%, rgba(220, 38, 38, 0.05) 0, transparent 50%),
    var(--paper);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--s-6);
  overflow-y: auto;
}
.welcome-card {
  width: 100%;
  max-width: 480px;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--r-xl);
  padding: var(--s-12) var(--s-8) var(--s-8);
  box-shadow: 0 20px 60px rgba(20, 20, 20, 0.08);
  text-align: center;
}
.welcome-brand { margin-bottom: var(--s-8); }
.welcome-logo {
  width: 56px;
  height: 56px;
  margin: 0 auto var(--s-4);
  border-radius: 14px;
  background: var(--ink);
  color: var(--paper);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-display);
  font-style: italic;
  font-size: 32px;
  box-shadow: 0 4px 12px rgba(20, 20, 20, 0.15);
}
.welcome-title {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 400;
  font-size: 44px;
  margin: 0 0 var(--s-2);
  color: var(--ink);
  letter-spacing: -0.5px;
}
.welcome-subtitle {
  font-size: var(--t-base);
  color: var(--ink-soft);
  margin: 0;
}
.welcome-create {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  width: 100%;
  background: var(--ink);
  color: var(--paper);
  border: none;
  border-radius: var(--r-pill);
  padding: 14px 24px;
  font-family: var(--font-ui);
  font-size: var(--t-base);
  cursor: pointer;
  margin-bottom: var(--s-6);
  transition: all 120ms var(--ease);
}
.welcome-create:hover { background: var(--accent); transform: translateY(-1px); }
.welcome-create .icon { width: 16px; height: 16px; }
.welcome-recent { text-align: left; }
.welcome-recent-head {
  font-size: var(--t-xs);
  color: var(--ink-mute);
  text-transform: uppercase;
  letter-spacing: 1.4px;
  margin-bottom: var(--s-2);
  padding-left: var(--s-2);
}
.welcome-projects {
  display: flex;
  flex-direction: column;
  gap: 2px;
  max-height: 280px;
  overflow-y: auto;
}
.welcome-project-row {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-3) var(--s-4);
  border-radius: var(--r-md);
  cursor: pointer;
  background: transparent;
  border: 1px solid transparent;
  text-align: left;
  font-family: var(--font-ui);
  transition: all 120ms var(--ease);
  width: 100%;
}
.welcome-project-row:hover {
  background: var(--line-soft);
  border-color: var(--line);
}
.welcome-project-row .project-row-name { font-size: var(--t-base); }
.welcome-empty {
  font-size: var(--t-sm);
  text-align: center;
  padding: var(--s-4);
}

/* Footer of the projects side panel — Help button anchors to the bottom. */
/* .projects-panel-foot removed — Usage & Help now use .panel-row-link
   inside .panel-row-stack so they visually match the Projects toggle. */

/* ============================================================
   Chat history — right-side collapsible panel
   ============================================================
   Floating toggle button sits at the right edge of the viewport when
   a project is open. Clicking opens a 320px-wide panel that lists every
   prompt the user sent (server-side chat_history table) with a "Revert
   to v N" button. Same scrim pattern as the projects panel. */
.history-toggle {
  position: fixed;
  right: var(--s-4);
  top: 50%;
  transform: translateY(-50%);
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: var(--paper);
  border: 1px solid var(--line);
  color: var(--ink-soft);
  cursor: pointer;
  z-index: 25;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-shadow:
    0 1px 2px rgba(20, 20, 20, 0.06),
    0 4px 12px rgba(20, 20, 20, 0.08);
  transition: transform 140ms var(--ease), box-shadow 200ms var(--ease), color 120ms var(--ease);
  -webkit-tap-highlight-color: transparent;
}
.history-toggle:hover {
  color: var(--ink);
  transform: translateY(-50%) scale(1.05);
  box-shadow:
    0 2px 4px rgba(20, 20, 20, 0.08),
    0 8px 20px rgba(20, 20, 20, 0.12);
}
.history-toggle .icon { width: 18px; height: 18px; stroke-width: 1.75; }
.history-toggle[hidden] { display: none; }

.history-panel {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  width: min(360px, 92vw);
  background: var(--paper);
  border-left: 1px solid var(--line);
  box-shadow: var(--sh-3);
  z-index: 120;
  display: flex;
  flex-direction: column;
  transform: translateX(0);
  transition: transform 200ms var(--ease);
}
.history-panel[hidden] { display: none; }

.history-panel-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--s-4) var(--s-5);
  border-bottom: 1px solid var(--line);
}
.history-panel-head h3 {
  margin: 0;
  font-family: var(--font-display);
  font-style: italic;
  font-size: 24px;
  color: var(--ink);
  font-weight: 400;
}

.history-panel-list {
  flex: 1;
  overflow-y: auto;
  padding: var(--s-3);
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
}
.history-empty { padding: var(--s-4); text-align: center; }

.history-entry {
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: var(--s-3);
  background: var(--paper);
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
}
.history-entry-meta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: 11px;
  color: var(--ink-mute);
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.history-entry-versions {
  font-weight: 600;
  color: var(--ink-soft);
  font-family: var(--font-ui);
  letter-spacing: 0;
}
.history-entry-prompt {
  font-size: 13px;
  line-height: 1.4;
  color: var(--ink);
  word-break: break-word;
  white-space: pre-wrap;
}
/* Collapsed state — JS clamps the text via JS truncation + ellipsis on
   the body span, so we don't need -webkit-line-clamp anymore. The
   ".history-prompt-toggle" button sits inline at the end. */
.history-entry-prompt.collapsed .history-entry-prompt-body,
.history-entry-prompt.expanded  .history-entry-prompt-body {
  /* No layout difference between states for the body itself — the
     difference is purely the text content (preview vs full) that JS
     swaps in. Class kept for any future styling hooks. */
}
.history-prompt-toggle {
  appearance: none;
  background: transparent;
  border: none;
  padding: 2px 0 0;
  margin-left: 4px;
  font: 500 12px var(--font-ui, system-ui);
  color: var(--accent, #c75e2a);
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 2px;
}
.history-prompt-toggle:hover { color: var(--ink); }
.history-entry-actions {
  display: flex;
  justify-content: flex-end;
}
.history-revert-btn {
  appearance: none;
  background: transparent;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  padding: 4px 10px;
  font-size: 11px;
  font-weight: 600;
  color: var(--ink-soft);
  cursor: pointer;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  transition: background 120ms var(--ease), color 120ms var(--ease);
}
.history-revert-btn:hover { background: var(--ink); color: var(--paper); border-color: var(--ink); }
.history-revert-btn:disabled { opacity: 0.4; cursor: not-allowed; }

.history-panel-scrim {
  position: fixed;
  inset: 0;
  background: rgba(20, 20, 20, 0.18);
  z-index: 115;
  -webkit-tap-highlight-color: transparent;
}
.history-panel-scrim[hidden] { display: none; }

/* ============================================================
   Help dialog — full-screen how-to with TOC + screenshots
   ============================================================ */
.help-screen {
  position: fixed;
  inset: 0;
  z-index: 260;
  background: var(--paper);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.help-head {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--s-4) var(--s-6);
  border-bottom: 1px solid var(--line);
  background: var(--paper);
}
/* Cluster of action buttons on the right of the usage screen header.
   Currently: Refresh + Close. Both are .ico ghost so they pick up the
   standard hover / active states. */
.usage-head-actions {
  display: flex;
  align-items: center;
  gap: 4px;
}
/* While a refresh is in flight, spin the icon so the user knows the
   server's being re-queried. The class is added/removed in main.js
   around the await fetch('/api/usage') call. */
#usageRefresh.spinning .icon {
  animation: spin 0.8s linear infinite;
}
#usageRefresh:disabled { opacity: 0.5; cursor: default; }

/* Project-switching loading scrim.
   ────────────────────────────────────────────────────────────────────
   Shown over the entire viewport while openProject() runs — covers
   the awkward gap where the OLD project's canvas is still on screen
   but the user has already asked to switch to a different one. On
   touch devices this is critical: without it, taps feel broken
   ("did my tap register?"). The card is intentionally small and
   centered so the user immediately recognizes it as transient. */
.project-loading-scrim {
  position: fixed;
  inset: 0;
  z-index: 9999;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(15, 20, 25, 0.45);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  /* Smooth fade-in so the scrim doesn't pop jarringly on every
     project click. Fade-out happens via the .hidden attribute (which
     is `display:none` from UA styles, so we cut to 0 — fine because
     by the time we hide, the new project has already rendered). */
  animation: project-loading-in 140ms ease-out;
}
@keyframes project-loading-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}
.project-loading-card {
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--r-lg, 14px);
  padding: 22px 28px;
  display: flex;
  align-items: center;
  gap: 14px;
  box-shadow: 0 12px 36px rgba(0, 0, 0, 0.18);
  min-width: 220px;
}
.project-loading-spinner {
  width: 22px;
  height: 22px;
  border: 2.5px solid var(--line);
  border-top-color: var(--ink);
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
  flex-shrink: 0;
}
.project-loading-text {
  font: 500 14px/1.4 var(--font-body, inherit);
  color: var(--ink);
}
.help-brand { display: flex; align-items: center; gap: var(--s-4); }
.help-logo {
  width: 40px;
  height: 40px;
  border-radius: 10px;
  background: var(--ink);
  color: var(--paper);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-display);
  font-style: italic;
  font-size: 24px;
}
.help-eyebrow {
  font-size: var(--t-xs);
  color: var(--ink-mute);
  text-transform: uppercase;
  letter-spacing: 1.4px;
}
.help-title {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 26px;
  color: var(--ink);
  margin-top: -2px;
}
.help-body {
  flex: 1;
  display: grid;
  grid-template-columns: 240px 1fr;
  overflow: hidden;
}
.help-toc {
  border-right: 1px solid var(--line);
  padding: var(--s-6) var(--s-4);
  overflow-y: auto;
  background: var(--line-soft);
}
.help-toc-eyebrow {
  font-size: var(--t-xs);
  color: var(--ink-mute);
  text-transform: uppercase;
  letter-spacing: 1.4px;
  padding: 0 var(--s-3) var(--s-3);
}
.help-toc ol {
  list-style: none;
  padding: 0;
  margin: 0;
  counter-reset: help-step;
}
.help-toc li {
  counter-increment: help-step;
}
.help-toc a {
  display: flex;
  gap: var(--s-2);
  align-items: baseline;
  padding: 8px var(--s-3);
  border-radius: var(--r-sm);
  text-decoration: none;
  color: var(--ink-soft);
  font-size: var(--t-sm);
  transition: all 120ms var(--ease);
}
.help-toc a::before {
  content: counter(help-step, decimal-leading-zero);
  font-family: var(--font-display);
  font-style: italic;
  font-size: 13px;
  color: var(--ink-mute);
  font-variant-numeric: tabular-nums;
}
.help-toc a:hover { background: var(--paper); color: var(--ink); }
.help-toc a.active {
  background: var(--paper);
  color: var(--ink);
  box-shadow: inset 2px 0 0 var(--ink);
}
.help-toc-foot {
  margin-top: var(--s-4);
  padding-top: var(--s-3);
  border-top: 1px solid var(--line);
}
.help-play-btn {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  background: var(--paper);
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  padding: 8px 14px;
  font: inherit;
  font-size: var(--t-sm);
  cursor: pointer;
  transition: all 120ms var(--ease);
}
.help-play-btn:hover { background: var(--ink); color: var(--paper); border-color: var(--ink); }
.help-play-btn.playing { background: var(--ink); color: var(--paper); border-color: var(--ink); }
.help-play-btn .help-play-icon { width: 12px; height: 12px; flex-shrink: 0; }

/* Horizontal slide deck — outer column hides overflow, inner track is flexbox
   that shifts left by N×100% to reveal slide N. */
.help-content {
  position: relative;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
.help-stage {
  flex: 1;
  overflow: hidden;
  position: relative;
}
.help-track {
  display: flex;
  height: 100%;
  transition: transform 360ms cubic-bezier(.2, .8, .2, 1);
  will-change: transform;
}
.help-section {
  flex: 0 0 100%;
  width: 100%;
  height: 100%;
  overflow-y: auto;
  padding: var(--s-8) var(--s-12);
  scroll-behavior: smooth;
}
.help-section > * { max-width: 720px; margin-left: auto; margin-right: auto; }
.help-section h2 { max-width: 720px; }

/* Slide-deck controls (prev / counter / next) at the bottom of the stage. */
.help-slide-nav {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-4);
  padding: var(--s-3) var(--s-4);
  border-top: 1px solid var(--line);
  background: var(--paper);
}
.help-slide-btn {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  border: 1px solid var(--line);
  background: var(--paper);
  color: var(--ink-soft);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: all 120ms var(--ease);
}
.help-slide-btn:hover:not(:disabled) {
  background: var(--ink);
  color: var(--paper);
  border-color: var(--ink);
}
.help-slide-btn:disabled {
  opacity: 0.35;
  cursor: not-allowed;
}
.help-slide-btn .icon { width: 14px; height: 14px; }
.help-slide-counter {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 18px;
  color: var(--ink);
  min-width: 80px;
  text-align: center;
  font-variant-numeric: tabular-nums;
}
.help-slide-counter .help-slide-of { color: var(--ink-mute); margin: 0 4px; }
.help-progress {
  height: 2px;
  background: var(--line-soft);
  position: relative;
  overflow: hidden;
}
.help-progress-bar {
  position: absolute;
  inset: 0 auto 0 0;
  width: 0;
  background: var(--ink);
  transition: width 8000ms linear;
}
.help-progress-bar.idle { transition: none; }
.help-section-eyebrow {
  font-size: var(--t-xs);
  color: var(--ink-mute);
  text-transform: uppercase;
  letter-spacing: 1.4px;
  font-variant-numeric: tabular-nums;
  margin-bottom: var(--s-2);
}
.help-section h2 {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 36px;
  font-weight: 400;
  color: var(--ink);
  margin: 0 0 var(--s-4);
  letter-spacing: -0.5px;
}
.help-section p {
  font-size: var(--t-base);
  line-height: 1.6;
  color: var(--ink-soft);
  margin: 0 0 var(--s-4);
}
.help-section p strong { color: var(--ink); font-weight: 500; }
.help-section ul {
  font-size: var(--t-base);
  line-height: 1.7;
  color: var(--ink-soft);
  padding-left: var(--s-5);
  margin: 0 0 var(--s-4);
}
.help-section li + li { margin-top: 4px; }
.help-figure {
  margin: var(--s-5) 0;
}
.help-figure img {
  width: auto;
  height: auto;
  max-width: min(100%, 420px);
  max-height: 300px;
  display: block;
  margin: 0 auto;
  object-fit: contain;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--canvas);
}
.help-figure figcaption {
  font-size: var(--t-xs);
  color: var(--ink-mute);
  margin-top: var(--s-2);
  text-align: center;
  font-style: italic;
}
.help-figure.figure-zoom img {
  max-width: 480px;
  margin: 0 auto;
}

/* ── "See it on the canvas" launcher (replaces the auto-advance button) ── */
.help-tour-btn {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  width: 100%;
  margin-bottom: var(--s-4, 16px);
  padding: var(--s-3);
  background: var(--ink, #18181b);
  color: #fff;
  border: none;
  border-radius: var(--r-md, 10px);
  font-size: var(--t-sm, .875rem);
  font-weight: 600;
  cursor: pointer;
  transition: transform .12s ease, opacity .12s ease;
}
.help-tour-btn:hover { opacity: .9; }
.help-tour-btn:active { transform: scale(.98); }
.help-tour-icon { width: 18px; height: 18px; flex: none; }

/* ── Guided coachmark tour ───────────────────────────────────────── */
/* Transparent layer that swallows clicks so the app behind can't be
   touched mid-tour; the dimming itself is the spotlight's huge box-shadow. */
.tour-backdrop { position: fixed; inset: 0; z-index: 99999; cursor: default; }
.tour-spot {
  position: fixed;
  z-index: 100000;
  border-radius: 12px;
  box-shadow: 0 0 0 9999px rgba(17, 17, 22, .55);
  pointer-events: none;
  transition: left .3s cubic-bezier(.4,0,.2,1), top .3s cubic-bezier(.4,0,.2,1),
              width .3s cubic-bezier(.4,0,.2,1), height .3s cubic-bezier(.4,0,.2,1);
}
.tour-spot::after {
  content: "";
  position: absolute;
  inset: -3px;
  border: 2px solid var(--accent, #2563eb);
  border-radius: 14px;
  animation: tourPulse 1.6s ease-in-out infinite;
}
@keyframes tourPulse {
  0%, 100% { opacity: .95; transform: scale(1); }
  50%      { opacity: .35; transform: scale(1.035); }
}
.tour-pop {
  position: fixed;
  z-index: 100001;
  width: 300px;
  max-width: calc(100vw - 24px);
  background: var(--surface, #fff);
  color: var(--ink, #18181b);
  border: 1px solid var(--line, #e4e4e7);
  border-radius: var(--r-lg, 14px);
  box-shadow: 0 18px 48px rgba(0, 0, 0, .22);
  padding: var(--s-4, 16px);
  transition: left .3s cubic-bezier(.4,0,.2,1), top .3s cubic-bezier(.4,0,.2,1);
}
.tour-pop-step {
  font-size: var(--t-xs, .75rem);
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--ink-mute, #71717a);
  margin-bottom: var(--s-2, 6px);
}
.tour-pop-title { font-size: 1.05rem; font-weight: 700; margin-bottom: var(--s-1, 4px); }
.tour-pop-body { font-size: var(--t-sm, .875rem); line-height: 1.55; color: var(--ink, #18181b); }
.tour-pop-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-2, 6px);
  margin-top: var(--s-4, 16px);
}
.tour-pop-nav-right { display: flex; gap: var(--s-2, 6px); }
.tour-skip {
  background: none; border: none;
  color: var(--ink-mute, #71717a);
  font-size: var(--t-sm, .875rem);
  cursor: pointer; padding: var(--s-2, 6px) 0;
}
.tour-skip:hover { color: var(--ink, #18181b); }
.tour-prev, .tour-next {
  padding: var(--s-2, 6px) var(--s-3, 12px);
  border-radius: var(--r-md, 10px);
  font-size: var(--t-sm, .875rem);
  cursor: pointer;
  border: 1px solid var(--line, #e4e4e7);
  background: var(--surface, #fff);
  color: var(--ink, #18181b);
}
.tour-prev:disabled { opacity: .4; cursor: default; }
.tour-next { background: var(--ink, #18181b); color: #fff; border-color: var(--ink, #18181b); font-weight: 600; }
@media (prefers-reduced-motion: reduce) {
  .tour-spot, .tour-pop { transition: none; }
  .tour-spot::after { animation: none; }
}
.help-callout {
  background: var(--accent-soft);
  border-left: 3px solid var(--accent);
  padding: var(--s-3) var(--s-4);
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
  font-size: var(--t-sm);
  line-height: 1.6;
  color: var(--ink);
  margin: var(--s-4) 0;
}
.help-callout strong { font-weight: 500; }
.help-kbd {
  display: inline-block;
  padding: 1px 6px;
  background: var(--paper);
  border: 1px solid var(--line);
  border-bottom-width: 2px;
  border-radius: 4px;
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 11px;
  color: var(--ink);
}

@media (max-width: 820px) {
  /* Mobile / portrait-tablet: stack a compact top bar (Start Tour + a
     single-row, horizontally-scrollable section "pill" strip) over the
     scrolling content. The vertical sidebar TOC used to collapse into a
     180px scroll box that ate a third of the screen and competed with the
     content's own scroll — the pill strip keeps jump-navigation while
     reclaiming that vertical space. */
  .help-body {
    /* minmax(0, 1fr): a bare 1fr track grows to its items' min-content
       width — and the 11-pill strip's min-content is ~1100px, which blew
       the column (and every child: toc, content, sections, slide-nav) far
       past the viewport, clipping the right side. minmax(0, …) pins the
       track minimum to 0 so it can't exceed the body's width. */
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto minmax(0, 1fr);
  }
  .help-toc {
    min-width: 0;
    border-right: none;
    border-bottom: 1px solid var(--line);
    max-height: none;
    overflow: visible;
    padding: var(--s-3) var(--s-4);
    display: flex;
    flex-direction: column;
    gap: var(--s-3);
  }
  .help-tour-btn { margin-bottom: 0; padding: 10px var(--s-3); }
  .help-toc-eyebrow { display: none; }
  .help-toc ol {
    display: flex;
    flex-direction: row;
    flex-wrap: nowrap;
    gap: var(--s-2);
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    /* Full-bleed scroll: cancel the toc's side padding so the first/last
       pill can sit flush with the content edge, then re-add it inside. */
    margin: 0 calc(-1 * var(--s-4));
    padding: 2px var(--s-4);
  }
  .help-toc ol::-webkit-scrollbar { display: none; }
  .help-toc li { flex: 0 0 auto; }
  .help-toc a {
    white-space: nowrap;
    border: 1px solid var(--line);
    background: var(--paper);
    border-radius: var(--r-pill);
    padding: 6px 14px;
  }
  /* Drop the leading "01" counter inside pills — the bottom slide counter
     already shows position, and bare titles keep the pills tight. */
  .help-toc a::before { display: none; }
  .help-toc a:hover { background: var(--paper); color: var(--ink); }
  .help-toc a.active {
    background: var(--ink);
    color: var(--paper);
    border-color: var(--ink);
    box-shadow: none;
  }

  /* Padding lives on the section, not the content column, so the progress
     bar and slide-nav stay full-width. min-width:0 lets the column shrink
     to the viewport (see .help-body note). */
  .help-content { padding: 0; min-width: 0; }
  .help-section { padding: var(--s-6) var(--s-5); }
  .help-section h2 { font-size: 28px; }
  .help-figure img { max-height: 260px; }
}

@media (max-width: 520px) {
  .help-head { padding: var(--s-3) var(--s-4); }
  .help-logo { width: 34px; height: 34px; font-size: 20px; border-radius: 9px; }
  .help-title { font-size: 22px; }
  .help-section { padding: var(--s-5) var(--s-4); }
  .help-section h2 { font-size: 24px; }
  .help-figure { margin: var(--s-4) 0; }
  .help-figure img { max-height: 220px; }
  .help-slide-nav { padding: var(--s-2) var(--s-3); }
  .help-slide-counter { font-size: 16px; min-width: 64px; }
}

/* ============================================================
   Searchable model picker (lives in connect dialog)
   ============================================================ */
.model-search-row {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-2) var(--s-3);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--paper);
  margin-bottom: var(--s-3);
}
.model-search-row input {
  flex: 1;
  border: 0;
  background: transparent;
  outline: none;
  font: inherit;
  font-size: var(--t-sm);
  color: var(--ink);
  padding: 4px 0;
}
.model-search-row input::placeholder { color: var(--ink-mute); }
.model-search-row .ico { width: 28px; height: 28px; }

.model-selected-pill {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-2) var(--s-3);
  background: var(--accent-soft);
  border: 1px solid color-mix(in srgb, var(--accent) 25%, transparent);
  border-radius: var(--r-md);
  margin-bottom: var(--s-3);
  font-size: var(--t-sm);
}
.model-selected-pill::before {
  content: "✓";
  color: var(--accent);
  font-weight: 600;
}
.model-selected-pill strong { font-weight: 600; color: var(--ink); }
.model-selected-pill .meta { color: var(--ink-soft); margin-left: auto; }

.model-list {
  max-height: 360px;
  overflow-y: auto;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: #fff;
}
.model-row {
  display: grid;
  grid-template-columns: 1fr auto;
  grid-template-areas:
    "name      meta"
    "subtitle  modalities";
  gap: 2px var(--s-3);
  padding: var(--s-3);
  border-bottom: 1px solid var(--line-soft);
  cursor: pointer;
  transition: background 0.12s var(--ease);
}
.model-row:last-child { border-bottom: 0; }
.model-row:hover { background: var(--paper); }
.model-row.selected {
  background: var(--accent-soft);
  box-shadow: inset 3px 0 0 var(--accent);
}
.model-row .row-name {
  grid-area: name;
  font-size: var(--t-sm);
  font-weight: 500;
  color: var(--ink);
  display: flex;
  align-items: center;
  gap: var(--s-2);
}
.model-row .row-provider {
  font-size: var(--t-xs);
  color: var(--ink-mute);
  text-transform: lowercase;
  font-weight: 400;
}
.model-row .row-meta {
  grid-area: meta;
  font-size: var(--t-xs);
  color: var(--ink-soft);
  text-align: right;
  white-space: nowrap;
  align-self: center;
}
.model-row .row-meta strong { font-weight: 600; color: var(--ink); }
.model-row .row-subtitle {
  grid-area: subtitle;
  font-size: var(--t-xs);
  color: var(--ink-mute);
}
.model-row .row-modalities {
  grid-area: modalities;
  display: flex;
  gap: 4px;
  align-items: center;
  justify-content: flex-end;
  color: var(--ink-mute);
}
.model-row .row-modalities .icon { width: 12px; height: 12px; }

/* Submit dialog: thin status pill that replaces the old radio picker */
.submit-model-pill {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-2) var(--s-3);
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  font-size: var(--t-sm);
}
.submit-model-pill-name { font-weight: 500; color: var(--ink); }
.submit-model-pill-meta { font-size: var(--t-xs); margin-left: auto; }
.submit-model-pill.empty {
  background: #fff5e6;
  border-color: #f1c177;
}
.submit-model-pill.empty .submit-model-pill-name { color: #b06b00; }

.link-btn {
  background: none;
  border: 0;
  color: var(--accent);
  font: inherit;
  font-size: var(--t-xs);
  padding: 0;
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 2px;
}

/* ============================================================
   Token-budget side handle
   ============================================================ */
.budget-handle {
  position: fixed;
  right: 0;
  top: 50%;
  transform: translateY(-50%);
  z-index: 30;
  display: flex;
  align-items: stretch;
}
.budget-handle-tab {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  background: #fff;
  border: 1px solid var(--line);
  border-right: 0;
  border-radius: var(--r-md) 0 0 var(--r-md);
  padding: var(--s-3) 6px;
  box-shadow: -2px 4px 14px rgba(20,20,20,0.06);
  cursor: pointer;
  color: var(--accent);
  transition: background 0.15s var(--ease), box-shadow 0.15s var(--ease);
}
.budget-handle-tab:hover { background: var(--paper); box-shadow: -4px 6px 18px rgba(20,20,20,0.10); }
.budget-tab-ring { display: block; line-height: 0; }
.budget-tab-pct {
  font-size: 11px;
  font-weight: 600;
  color: var(--ink-soft);
  letter-spacing: 0.02em;
}
.budget-handle.over .budget-handle-tab,
.budget-handle.over .budget-tab-pct { color: var(--danger); }
.budget-handle.warn .budget-handle-tab,
.budget-handle.warn .budget-tab-pct { color: #c2871a; }

.budget-panel {
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--r-lg) 0 0 var(--r-lg);
  box-shadow: -8px 16px 40px rgba(20,20,20,0.10);
  padding: var(--s-4);
  width: 280px;
  max-height: 80vh;
  overflow-y: auto;
}
.budget-panel-head {
  display: flex;
  align-items: flex-start;
  gap: var(--s-2);
  margin-bottom: var(--s-3);
}
.budget-model-line { flex: 1; min-width: 0; }
.budget-model-name { font-weight: 600; font-size: var(--t-base); color: var(--ink); display: block; }
.budget-model-context { font-size: var(--t-xs); color: var(--ink-soft); }
.budget-model-tier {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-mute);
  margin-left: 4px;
}
.budget-ring-wrap {
  position: relative;
  display: flex;
  justify-content: center;
  align-items: center;
  margin: var(--s-3) 0 var(--s-4);
}
.budget-ring { display: block; }
.budget-ring-text {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}
.budget-ring-text strong {
  font-size: 22px;
  font-weight: 600;
  color: var(--ink);
  line-height: 1.1;
}
.budget-ring-text .muted { font-size: var(--t-xs); }
.budget-stats {
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
  margin: 0 0 var(--s-3);
  font-size: var(--t-sm);
}
.budget-stats > div {
  display: flex;
  justify-content: space-between;
  padding: 6px 0;
  border-bottom: 1px solid var(--line-soft);
}
.budget-stats > div:last-child { border-bottom: 0; }
.budget-stats dt { color: var(--ink-soft); margin: 0; }
.budget-stats dd { margin: 0; font-weight: 500; color: var(--ink); }
.budget-hint { font-size: var(--t-xs); margin: 0 0 var(--s-3); }
.budget-change-btn { width: 100%; }

@media (max-width: 820px) {
  .budget-panel { width: 240px; }
  .budget-handle-tab { padding: var(--s-2) 4px; }
}

/* ============================================================
   Claude Remote — read-only pill on iPad, editable form on desktop
   ============================================================ */
.remote-readonly-pill {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-3);
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  font-size: var(--t-sm);
}
.remote-readonly-pill code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12px;
  color: var(--ink);
  word-break: break-all;
}

/* Tiny capture button sitting between the prompt textarea and the submit
   arrow on the prompt bar. Visible only when bridge mode = chrome. */
#captureHtmlPromptBtn {
  width: 32px;
  height: 32px;
  flex: none;
  color: var(--ink-soft);
}
#captureHtmlPromptBtn:hover { color: var(--accent); }
#captureHtmlPromptBtn.busy { opacity: 0.6; pointer-events: none; }
#captureHtmlPromptBtn.success { color: #16a34a; }

/* ============================================================
   Output-type picker (HTML / PDF) — submit dialog
   ============================================================ */
.output-type-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: var(--s-2);
}
.output-type-pill {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-3);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: #fff;
  cursor: pointer;
  transition: border-color 0.12s var(--ease), background 0.12s var(--ease);
}
.output-type-pill input { display: none; }
.output-type-pill:hover { background: var(--paper); }
.output-type-pill:has(input:checked) {
  border-color: var(--accent);
  background: var(--accent-soft);
  box-shadow: inset 0 0 0 1px var(--accent);
}
.output-type-pill .ot-icon {
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--r-sm);
  background: var(--paper);
  color: var(--ink-soft);
  flex: none;
}
.output-type-pill:has(input:checked) .ot-icon {
  background: #fff;
  color: var(--accent);
}
.output-type-pill .ot-icon .icon { width: 18px; height: 18px; }
.output-type-pill .ot-meta {
  display: flex;
  flex-direction: column;
  line-height: 1.25;
  min-width: 0;
}
.output-type-pill .ot-meta strong {
  font-size: var(--t-sm);
  font-weight: 600;
  color: var(--ink);
}
.output-type-pill .ot-meta small {
  font-size: var(--t-xs);
}

/* ============================================================
   Claude Code session library
   ============================================================ */
.remote-sessions-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-bottom: var(--s-3);
  max-height: 360px;
  overflow-y: auto;
}
.rs-row {
  display: grid;
  grid-template-columns: 12px 1fr auto;
  align-items: center;
  gap: var(--s-2) var(--s-3);
  padding: var(--s-3);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: #fff;
}
.rs-row.selected {
  border-color: var(--accent);
  background: var(--accent-soft);
  box-shadow: inset 0 0 0 1px var(--accent);
}
.rs-dot {
  width: 8px; height: 8px;
  border-radius: 50%;
  flex: none;
  align-self: center;
  margin-top: 2px;
}
.rs-dot-active   { background: #16a34a; box-shadow: 0 0 0 2px rgba(22,163,74,0.18); }
.rs-dot-inactive { background: #d4d4d4; }
.rs-main {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.rs-label {
  font-size: var(--t-sm);
  font-weight: 500;
  color: var(--ink);
}
.rs-url {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 11px;
  color: var(--ink-soft);
  word-break: break-all;
}
.rs-meta {
  font-size: var(--t-xs);
}
.rs-actions {
  display: flex;
  gap: var(--s-1);
  align-items: center;
  flex: none;
}
.rs-actions .btn { padding: 6px 12px; font-size: var(--t-xs); }
.remote-add-details {
  border: 1px dashed var(--line);
  border-radius: var(--r-md);
  padding: var(--s-2) var(--s-3);
  background: var(--paper);
}
.remote-add-details summary {
  cursor: pointer;
  font-size: var(--t-sm);
  color: var(--accent);
  user-select: none;
}
.remote-add-details summary::marker { color: var(--accent); }
.remote-add-details[open] {
  background: #fff;
  border-style: solid;
}

/* Submit-dialog anchor toggle row — checkbox version of cached-file-row.
   Per-submission only: unchecking excludes that anchor from the next
   send but never modifies the project's anchor list. */
.cached-file-toggle {
  cursor: pointer;
  user-select: none;
}
.cached-file-toggle:hover { background: var(--paper); }
.cached-file-toggle .cf-checkbox {
  margin: 0 var(--s-2) 0 0;
  flex: none;
  width: 16px;
  height: 16px;
  cursor: pointer;
  accent-color: var(--accent);
}
.cached-file-toggle:has(.cf-checkbox:not(:checked)) .cf-name {
  text-decoration: line-through;
  opacity: 0.55;
}
.cached-file-toggle:has(.cf-checkbox:not(:checked)) .cf-source {
  opacity: 0.55;
}

/* ============================================================
   Copilot — side-panel LLM chat
   ============================================================
   Anatomy:
     #copilotTab        — vertical sticker on the right edge of the
                          viewport. Always present in notebook view;
                          hidden when the panel is open.
     #copilotPanel      — slide-in drawer, right-anchored. Header
                          (title + clear + close) → scrollable
                          messages → optional attachments row →
                          input form (textarea + actions).
     #copilotPagePicker — modal dialog for picking which notebook
                          pages to attach to the next message.

   On mobile the panel goes full-width; the tab gets a slimmer touch
   target. Everything uses the file's design tokens (--paper, --ink,
   --accent, --line, --r-*) so the chrome blends with the rest of
   the app rather than feeling bolted on. */

/* Sticker tab — vertical pill on the right edge of the viewport.
   The rotated label reads bottom-to-top, top-to-bottom is left to
   the screen reader. Stays in notebook view only; hidden when the
   panel is open OR when the user is in preview mode (rule applied
   from main.js via the data-view attribute on the body, same pattern
   as the device toggle). */
.copilot-tab {
  position: fixed;
  top: 50%;
  right: 0;
  transform: translateY(-50%);
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 36px;
  min-height: 130px;
  padding: 14px 0;
  background: var(--paper);
  color: var(--ink);
  border: 1px solid var(--line);
  border-right: none;
  border-radius: 14px 0 0 14px;
  box-shadow: -4px 4px 16px rgba(0, 0, 0, 0.06);
  cursor: pointer;
  z-index: 25;
  transition: transform 160ms var(--ease), background 140ms var(--ease),
              box-shadow 160ms var(--ease);
}
.copilot-tab:hover {
  background: var(--ink);
  color: var(--paper);
  box-shadow: -6px 6px 22px rgba(0, 0, 0, 0.12);
}
.copilot-tab .icon { width: 18px; height: 18px; stroke-width: 1.5; }
.copilot-tab-label {
  font: 600 11px var(--font-ui, system-ui);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  writing-mode: vertical-rl;
  transform: rotate(180deg);
  white-space: nowrap;
}
/* Hide the tab when (a) the user is in preview mode, or (b) the
   panel is open. Both flags get toggled from main.js / copilot.js.
   The body.copilot-open class is owned by copilot.js. */
body[data-view="preview"] .copilot-tab,
body.copilot-open .copilot-tab { display: none; }

/* Side panel — anchored to the right edge. Drawer-style slide-in:
   when hidden the panel is offset 100% to the right and pointer-
   events disabled; when .open it translates back in. Width is
   420px on desktop, full-width on mobile.

   Layout: header (fixed) → messages (flex: 1, scrollable) →
   attachments row (auto height, only when files attached) → input
   form (fixed). The body.copilot-open class is owned by copilot.js. */
.copilot-panel {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  width: min(420px, 100vw);
  background: var(--paper);
  border-left: 1px solid var(--line);
  box-shadow: -12px 0 36px rgba(0, 0, 0, 0.10);
  display: flex;
  flex-direction: column;
  z-index: 28;
  transform: translateX(100%);
  transition: transform 220ms var(--ease);
  pointer-events: none;
}
.copilot-panel.open {
  transform: translateX(0);
  pointer-events: auto;
}
.copilot-panel[hidden] { display: none; }

.copilot-head {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  padding: 14px 14px 12px;
  border-bottom: 1px solid var(--line);
}
.copilot-head-title {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 1;
  min-width: 0;
  font: 600 14px var(--font-ui, system-ui);
}
.copilot-head-title .icon { width: 18px; height: 18px; stroke-width: 1.5; }
.copilot-head-model {
  font: 400 11px var(--font-ui, system-ui);
  color: var(--ink-mute);
  margin-left: 4px;
}

.copilot-messages {
  flex: 1;
  overflow-y: auto;
  padding: 14px;
  display: flex;
  flex-direction: column;
  gap: 14px;
  scroll-behavior: smooth;
}

.copilot-empty {
  margin: auto;
  max-width: 320px;
  text-align: center;
  color: var(--ink-soft);
  padding: 20px;
}
.copilot-empty-title {
  font: 500 15px var(--font-ui, system-ui);
  color: var(--ink);
  margin-bottom: 8px;
}
.copilot-empty-sub {
  font: 400 12.5px var(--font-ui, system-ui);
  color: var(--ink-mute);
  line-height: 1.55;
}
.copilot-empty-sub strong { color: var(--ink-soft); font-weight: 500; }
.copilot-messages:not(:empty) .copilot-empty { display: none; }
/* The :empty trick falls apart once any message gets appended, so
   copilot.js explicitly hides the empty-state element when the
   first message lands. The selector above keeps it correct on
   initial paint before that runs. */

/* Each message bubble. Variant on role: user is the accented column
   on the right, assistant flows along the left. Assistant messages
   also surface a small action row beneath them with "Send to canvas"
   and a copy button. */
.copilot-msg {
  max-width: 88%;
  padding: 10px 12px;
  border-radius: 14px;
  font: 400 13.5px var(--font-ui, system-ui);
  line-height: 1.5;
  white-space: pre-wrap;
  word-wrap: break-word;
}
.copilot-msg.role-user {
  align-self: flex-end;
  background: var(--ink);
  color: var(--paper);
  border-bottom-right-radius: 4px;
}
.copilot-msg.role-assistant {
  align-self: flex-start;
  background: var(--canvas);
  color: var(--ink);
  border-bottom-left-radius: 4px;
}
/* Multi-line content in messages — basic markdown-ish rendering
   (copilot.js may insert <strong>/<em>/<code>/<pre> nodes). */
.copilot-msg code {
  background: rgba(0, 0, 0, 0.06);
  padding: 1px 5px;
  border-radius: 4px;
  font: 500 12.5px ui-monospace, Menlo, Consolas, monospace;
}
.copilot-msg pre {
  background: rgba(0, 0, 0, 0.06);
  padding: 10px 12px;
  border-radius: 8px;
  overflow-x: auto;
  margin: 6px 0;
  font: 500 12.5px ui-monospace, Menlo, Consolas, monospace;
}
.copilot-msg pre code { background: none; padding: 0; }
/* GFM tables rendered in the copilot chat. */
.copilot-msg .cp-table {
  border-collapse: collapse;
  width: 100%;
  margin: 8px 0;
  font-size: 12.5px;
  display: block;
  overflow-x: auto;
}
.copilot-msg .cp-table th,
.copilot-msg .cp-table td {
  border: 1px solid rgba(0, 0, 0, 0.12);
  padding: 5px 9px;
  text-align: left;
  white-space: nowrap;
}
.copilot-msg .cp-table th { background: rgba(0, 0, 0, 0.05); font-weight: 600; }
.copilot-msg .cp-table tr:nth-child(even) td { background: rgba(0, 0, 0, 0.025); }
/* KaTeX: let wide display math (e.g. matrices) scroll inside the narrow
   panel instead of overflowing it. */
.copilot-msg .katex-display { overflow-x: auto; overflow-y: hidden; margin: 6px 0; padding-bottom: 2px; }
.copilot-msg .katex { font-size: 1.02em; }
/* Per-block message rendering + highlight-to-select (Send to canvas). */
.copilot-msg-text .cp-block { padding: 1px 4px; border-radius: 5px; }
.copilot-msg-text .cp-block + .cp-block { margin-top: 7px; }
.copilot-msg.highlighting .cp-block { cursor: pointer; transition: background 120ms, box-shadow 120ms; }
.copilot-msg.highlighting .cp-block:hover { background: rgba(250, 204, 21, 0.16); box-shadow: inset 0 0 0 1px rgba(234, 179, 8, 0.35); }
.copilot-msg .cp-block.cp-hl { background: rgba(250, 204, 21, 0.38); box-shadow: inset 0 0 0 1px rgba(234, 179, 8, 0.45); }
.copilot-msg-action.cp-hl-toggle.active { background: rgba(250, 204, 21, 0.22); color: #8a6300; }
.copilot-msg img.copilot-msg-attachment {
  max-width: 200px;
  max-height: 200px;
  display: block;
  margin: 4px 0;
  border: 1px solid var(--line);
  border-radius: 8px;
}

.copilot-msg-actions {
  display: flex;
  gap: 6px;
  align-self: flex-start;
  margin-top: -8px;
  margin-left: 4px;
}
.copilot-msg-action {
  appearance: none;
  background: transparent;
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 4px 10px 4px 8px;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font: 500 11.5px var(--font-ui, system-ui);
  color: var(--ink-soft);
  cursor: pointer;
  transition: background 120ms var(--ease), border-color 120ms var(--ease),
              color 120ms var(--ease);
}
.copilot-msg-action:hover {
  background: var(--ink);
  color: var(--paper);
  border-color: var(--ink);
}
.copilot-msg-action .icon { width: 14px; height: 14px; stroke-width: 1.5; }

/* Server-emitted notice (e.g. "vision disabled this turn"). Sits
   between messages, small + muted so it informs without dominating. */
.copilot-notice {
  align-self: stretch;
  font: 400 12px var(--font-ui, system-ui);
  color: var(--ink-mute);
  background: var(--canvas);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 8px 10px;
  line-height: 1.4;
}

/* Streaming indicator shown beneath the assistant message currently
   being composed. Pulses to communicate "more is coming". */
.copilot-typing {
  align-self: flex-start;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 0 4px;
  color: var(--ink-mute);
  font-size: 12px;
}
.copilot-typing::before {
  content: '';
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--ink-mute);
  animation: copilot-pulse 1s ease-in-out infinite;
}
@keyframes copilot-pulse {
  0%, 100% { opacity: 0.3; transform: scale(0.9); }
  50%      { opacity: 1;   transform: scale(1.1); }
}

/* Attachments row — small thumbnails of the pages staged for the
   next message. Each chip has a remove (×) button. Shown only when
   at least one attachment is staged. */
.copilot-attachments {
  display: flex;
  gap: 8px;
  padding: 8px 14px 4px;
  border-top: 1px solid var(--line);
  overflow-x: auto;
}
.copilot-attachment-chip {
  position: relative;
  width: 56px;
  height: 56px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--canvas);
  overflow: hidden;
  flex-shrink: 0;
}
.copilot-attachment-chip img {
  width: 100%; height: 100%; object-fit: cover;
}
.copilot-attachment-chip .remove {
  position: absolute;
  top: -6px;
  right: -6px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  border: 0;
  background: var(--ink);
  color: var(--paper);
  font: 700 11px var(--font-ui, system-ui);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
}

/* Input form — fixed at the bottom of the panel. Layout: textarea
   on top, action row below. The action row has paperclip (attach
   pages), globe (web search toggle), and send button. */
.copilot-input {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 10px 14px 14px;
  border-top: 1px solid var(--line);
  background: var(--paper);
}
.copilot-input textarea {
  width: 100%;
  resize: none;
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 10px 12px;
  font: 400 14px var(--font-ui, system-ui);
  line-height: 1.5;
  background: var(--paper);
  color: var(--ink);
  outline: none;
  max-height: 140px;
  transition: border-color 120ms var(--ease);
}
.copilot-input textarea:focus { border-color: var(--ink); }
.copilot-input textarea::placeholder { color: var(--ink-mute); }

.copilot-input-actions {
  display: flex;
  align-items: center;
  gap: 8px;
}
.copilot-action-btn {
  appearance: none;
  background: transparent;
  border: 1px solid var(--line);
  width: 32px;
  height: 32px;
  border-radius: 50%;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--ink);
  position: relative;
  transition: background 120ms var(--ease), color 120ms var(--ease),
              border-color 120ms var(--ease);
}
.copilot-action-btn:hover {
  background: var(--ink);
  color: var(--paper);
  border-color: var(--ink);
}
.copilot-action-btn .icon { width: 15px; height: 15px; stroke-width: 1.5; }
.copilot-action-btn:disabled { opacity: 0.4; cursor: not-allowed; }

/* Toggle variant: globe icon. When aria-pressed=true, fills with
   the accent color to communicate "web search ON". */
.copilot-toggle[aria-pressed="true"] {
  background: var(--accent);
  color: var(--paper);
  border-color: var(--accent);
}

.copilot-attach-count {
  position: absolute;
  top: -4px;
  right: -4px;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  background: var(--accent);
  color: var(--paper);
  border-radius: 8px;
  font: 600 10px var(--font-ui, system-ui);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
}

.copilot-send {
  appearance: none;
  margin-left: auto;
  background: var(--ink);
  color: var(--paper);
  border: 0;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: background 120ms var(--ease), transform 120ms var(--ease);
}
.copilot-send:hover { background: var(--accent); transform: rotate(-8deg) scale(1.04); }
.copilot-send:active { transform: scale(0.94); }
.copilot-send:disabled { background: var(--line); color: var(--ink-mute); cursor: not-allowed; transform: none; }
.copilot-send .icon { width: 16px; height: 16px; stroke-width: 2; }

/* Page picker — grid of thumbnails with checkbox-on-image semantics.
   Each tile shows the page index in the corner and a checkmark when
   selected. */
.copilot-page-picker {
  width: min(540px, 92vw);
}
.copilot-page-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  gap: 10px;
  max-height: 56vh;
  overflow-y: auto;
  padding: 4px;
  margin: 10px 0 14px;
}
.copilot-page-loading {
  grid-column: 1 / -1;
  text-align: center;
  color: var(--ink-mute);
  padding: 30px;
}
.copilot-page-tile {
  position: relative;
  aspect-ratio: 4 / 5;
  border: 2px solid var(--line);
  border-radius: 10px;
  overflow: hidden;
  background: var(--canvas);
  cursor: pointer;
  transition: border-color 140ms var(--ease), transform 140ms var(--ease);
}
.copilot-page-tile:hover { transform: translateY(-1px); border-color: var(--ink); }
.copilot-page-tile.selected { border-color: var(--accent); }
.copilot-page-tile img {
  width: 100%; height: 100%; object-fit: contain;
  display: block; background: white;
}
.copilot-page-tile .page-label {
  position: absolute;
  top: 6px; left: 6px;
  padding: 2px 8px;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: 999px;
  font: 600 10px var(--font-ui, system-ui);
  color: var(--ink);
}
.copilot-page-tile .page-check {
  position: absolute;
  top: 6px; right: 6px;
  width: 22px; height: 22px;
  border-radius: 50%;
  background: var(--ink);
  color: var(--paper);
  display: none;
  align-items: center;
  justify-content: center;
  font: 700 14px var(--font-ui, system-ui);
  line-height: 1;
}
.copilot-page-tile.selected .page-check { display: inline-flex; }

/* File picker (CSV / HTML attach) — simpler list layout than the page
   picker because data files don't have a useful thumbnail. Each row
   shows the filename, source pill (Input vs Output), and a checkmark
   when selected. */
.copilot-file-picker { width: min(480px, 92vw); }
.copilot-file-list {
  list-style: none;
  padding: 0;
  margin: 10px 0 14px;
  max-height: 56vh;
  overflow-y: auto;
  border: 1px solid var(--line);
  border-radius: 10px;
}
.copilot-file-loading {
  padding: 24px;
  text-align: center;
  color: var(--ink-mute);
  font-size: 13px;
}
.copilot-file-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 14px;
  cursor: pointer;
  border-bottom: 1px solid var(--line);
  transition: background 120ms var(--ease);
}
.copilot-file-row:last-child { border-bottom: none; }
.copilot-file-row:hover { background: var(--canvas); }
.copilot-file-row.selected { background: rgba(199, 94, 42, 0.06); }
.copilot-file-row-name {
  flex: 1;
  font: 500 13px var(--font-ui, system-ui);
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.copilot-file-row-kind {
  font: 600 10px var(--font-ui, system-ui);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--canvas);
  color: var(--ink-soft);
  border: 1px solid var(--line);
}
.copilot-file-row.selected .copilot-file-row-kind {
  background: var(--accent);
  color: var(--paper);
  border-color: var(--accent);
}
.copilot-file-row-check {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--ink);
  color: var(--paper);
  font: 700 12px var(--font-ui, system-ui);
  display: none;
  align-items: center;
  justify-content: center;
  line-height: 1;
}
.copilot-file-row.selected .copilot-file-row-check { display: inline-flex; }
.copilot-file-empty {
  padding: 30px 20px;
  text-align: center;
  color: var(--ink-mute);
  font-size: 13px;
}

/* Mobile adjustments: full-width panel, slimmer tab, smaller page
   tiles. Triggered at the same 640px breakpoint as the rest of the
   responsive overrides in this file. */
@media (max-width: 640px) {
  .copilot-tab {
    width: 30px;
    min-height: 110px;
    padding: 10px 0;
  }
  .copilot-tab-label { font-size: 10px; }
  .copilot-panel {
    width: 100vw;
    border-left: none;
  }
  .copilot-page-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

