/* Same palette/type pairing as the .landing page (css custom
   properties below, not the .landing-scoped --l-* ones — this is the
   base theme for the whole app now, not just the marketing view), per
   the user's explicit choice to carry the landing design into the
   room/voting UI itself, not just the entry page. */
:root {
  --bg: #f1ebdf;
  --panel: #fbf8f2;
  --text: #221d17;
  --muted: #5c5346;
  --border: #e3d9c4;
  --accent: #4f46e5;
  --accent-text: #ffffff;
  --success: #15803d;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #1b1711;
    --panel: #262019;
    --text: #f3ece0;
    --muted: #c3b8a6;
    --border: #3a3225;
    --accent: #6d64f0;
    --accent-text: #ffffff;
    --success: #4ade80;
  }
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font-family: "Work Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  background: var(--bg);
  color: var(--text);
}

h1, h2, h3, h4 { font-family: "Instrument Serif", Georgia, serif; font-weight: 400; }

main {
  max-width: 720px;
  margin: 0 auto;
  padding: 0 1rem 3rem;
}

/* The landing page handles its own width (full-bleed breakout) and its
   own bottom spacing (the footer's padding) — main's own padding would
   otherwise leave a thin strip of the wrong background color showing
   below the landing section's breakout. */
body.landing-active main { padding: 0; }

.app-header {
  text-align: center;
  padding: 2.5rem 1rem 1.5rem;
}

.app-header h1 { margin: 0 0 0.25rem; font-size: 1.75rem; }
.tagline { margin: 0; color: var(--muted); }

.panel {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 1.25rem;
  margin-bottom: 1rem;
}

.panel h2 { margin-top: 0; font-size: 1.05rem; }

label { display: block; font-size: 0.85rem; color: var(--muted); margin-bottom: 0.35rem; }
.import-label { margin-top: 1rem; }

input[type="text"], input[type="file"] {
  width: 100%;
  padding: 0.6rem 0.75rem;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--text);
  font-size: 1rem;
}

button {
  font-size: 0.95rem;
  padding: 0.6rem 1.1rem;
  border-radius: 8px;
  border: none;
  background: var(--accent);
  color: var(--accent-text);
  cursor: pointer;
  margin-top: 0.75rem;
}

button.secondary { background: transparent; color: var(--text); border: 1px solid var(--border); }

button.flash { outline: 2px solid var(--success); }

.archive-link { text-align: center; font-size: 0.85rem; }
.archive-link a { color: var(--muted); }

.share-row { display: flex; gap: 0.5rem; }
.share-row input { flex: 1; }
.share-row button { margin-top: 0; white-space: nowrap; }

.participants { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.5rem; }

.participant {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem 0.75rem;
  border-radius: 8px;
  background: var(--bg);
}

.assign-facilitator-btn {
  margin-top: 0;
  padding: 0.25rem 0.6rem;
  font-size: 0.75rem;
  background: transparent;
  color: var(--muted);
  border: 1px solid var(--border);
}
.assign-facilitator-btn:hover { color: var(--text); border-color: var(--text); }

.controls { display: flex; gap: 0.75rem; }
.controls button { margin-top: 0; }

.footnote { color: var(--muted); font-size: 0.8rem; text-align: center; }

code { background: var(--bg); padding: 0.1rem 0.4rem; border-radius: 4px; }

/* --- Freeform canvas ---------------------------------------------------- */

/* The live room is a full-page canvas app-shell: fixed to the
   viewport (so it escapes main's 720px reading width without any
   breakout-margin math) with a sidebar + canvas flex layout. Guarded
   with :not([hidden]) — an ID selector forcing display:flex would
   otherwise beat the `hidden` attribute's own (lower-specificity)
   `display:none` UA rule when the room isn't the active view. */
#view-room.room-layout:not([hidden]) {
  position: fixed;
  inset: 0;
  display: flex;
  background: var(--bg);
}

body.room-active .app-header,
body.landing-active .app-header { display: none; }

.room-sidebar {
  width: 360px;
  flex-shrink: 0;
  overflow-y: auto;
  padding: 1.5rem 1rem;
  border-right: 1px solid var(--border);
}

.facilitator-panel, .timer-panel { display: flex; flex-direction: column; gap: 0.5rem; }
.facilitator-panel button, .timer-panel button { margin-top: 0; }
.facilitator-badge { margin: 0; font-size: 0.9rem; }

/* Nested inside the Participants panel now (not its own top-level
   .panel) — a hairline + bottom margin stands in for the card border
   that used to separate it, so it still reads as its own sub-section
   above the participant list rather than running straight into it. */
#facilitator-panel:not(:empty) { padding-bottom: 0.85rem; margin-bottom: 0.85rem; border-bottom: 1px solid var(--border); }

/* Compact: status lives inline in the h2 (no separate paragraph line)
   and the panel itself takes less padding than a standard .panel —
   this is a one-line label plus a one-line control row, not content
   that needs the room a normal card gets. */
.timer-panel { padding: 0.85rem 1.25rem; gap: 0.4rem; }
.timer-panel h2 { font-size: 0.95rem; }
.timer-status { font-weight: 400; font-size: 0.85rem; color: var(--muted); font-variant-numeric: tabular-nums; }
.timer-controls { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; }
.timer-input-group { display: flex; align-items: center; gap: 0.5rem; }
/* Pushed to the row's right edge — margin-left: auto on a flex child
   claims the rest of the row's width, same trick space-between would
   use, but still holds its place (rather than reflowing to the start)
   if the row wraps on a narrow sidebar. */
.timer-actions { display: flex; gap: 0.5rem; margin-left: auto; }
.timer-unit { color: var(--muted); font-size: 0.85rem; }
.timer-controls input[type="number"] {
  width: 4rem;
  background: var(--panel);
  border: 1px solid var(--border);
  color: var(--text);
  border-radius: 6px;
  padding: 0.45rem 0.5rem;
  font: inherit;
}

.notes-list { display: flex; flex-direction: column; gap: 1rem; }
.notes-list-lane h3 { margin: 0 0 0.4rem; font-size: 0.85rem; color: var(--muted); }
.notes-list-lane ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.25rem; }
.notes-list-item {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 0.5rem;
  padding: 0.35rem 0.5rem;
  border-radius: 6px;
  font-size: 0.85rem;
  cursor: pointer;
}
.notes-list-item:hover { background: var(--bg); }
.notes-list-item .vote-count { color: var(--muted); font-size: 0.75rem; flex-shrink: 0; }
.notes-list-text { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Visual affordance only — the whole <li> is draggable (simplest with
   native HTML5 DnD), but a plain click still reaches App.focusNote
   fine: the browser suppresses the click event for a gesture that
   actually dragged, so the two never fight over which was meant. */
.notes-list-handle { flex-shrink: 0; color: var(--muted); cursor: grab; line-height: 1; }
.notes-list-item.dragging { opacity: 0.4; }
.notes-list-lane {
  border-radius: 8px;
  transition: background-color 100ms ease;
}
.notes-list-lane.drag-over { background: color-mix(in srgb, var(--accent) 12%, transparent); }
/* An empty lane has nothing to give it height/visibility otherwise —
   still a valid drop target (it's in the DOM precisely so it can be
   one), this just makes that obvious. */
.notes-list-lane ul:empty { min-height: 1.6rem; }
.notes-list-lane ul:empty::after { content: "Drop a sticky here"; color: var(--muted); font-size: 0.78rem; font-style: italic; }

/* Wider than the reading-width column, centered on the viewport,
   capped so it never overflows a narrow screen — the classic
   margin-left:50% + translateX(-50%) breakout. Sibling panels
   (participants/action-items/chat/share) are untouched, still 720px. */
.canvas-viewport {
  position: relative;
  overflow: hidden;
  touch-action: none;
  height: 560px;
  width: 1200px;
  max-width: calc(100vw - 2rem);
  margin-left: 50%;
  transform: translateX(-50%);
  margin-bottom: 1rem;
  border: 1px solid var(--border);
  border-radius: 12px;
  background-image: radial-gradient(circle, var(--border) 1px, transparent 1px);
  background-size: 24px 24px;
  cursor: grab;
}
/* Prevent the browser's native text-selection from kicking in during
   ANY canvas drag gesture (pan, note drag, section resize, section
   move) — without this, a drag that passes over any text highlights
   it instead of just moving/resizing. Unconditional rather than
   scoped to specific gesture-state classes (an earlier version only
   covered panning/note-drag and missed section-resize entirely,
   which is exactly the bug this generalizes away) — form elements
   (.note-edit, .section-title-input) remain selectable/editable
   regardless, since user-select:none on an ancestor doesn't prevent
   normal text-input editing inside a real <textarea>/<input>. */
.canvas-viewport {
  user-select: none;
  -webkit-user-select: none;
}

.canvas-viewport.panning { cursor: grabbing; }

/* Live room only — fills the flex space next to .room-sidebar instead
   of sitting as a fixed-size breakout panel (that's still how the
   archive viewer's .canvas-viewport behaves, untouched). */
.canvas-viewport.room-canvas {
  flex: 1;
  height: 100%;
  width: auto;
  max-width: none;
  margin: 0;
  transform: none;
  border: none;
  border-radius: 0;
}

.canvas-surface { position: absolute; top: 0; left: 0; transform-origin: 0 0; }

.sections-layer, .notes-layer, .cursors-layer { position: absolute; top: 0; left: 0; }

/* pointer-events:none on the bulk is load-bearing, not just visual —
   it's what lets panning and double-click-to-create-a-note pass
   straight through a section's blank interior to the surface beneath,
   while still letting its title/remove/resize-handle (each explicitly
   pointer-events:auto) be interactive. */
.section {
  position: absolute;
  background: var(--panel);
  border: 1px dashed var(--border);
  border-radius: 12px;
  pointer-events: none;
}

/* The drag handle for moving a whole section — a full-width band
   across the top (not just a tight fit around the title text), so
   "grab anywhere along the top part" actually works rather than
   requiring a precise grab on small text. The tinted background (its
   section's color, set inline via JS) doubles as a hint that this
   strip is a distinct, interactive control from the rest of the
   section. Grab cursor reinforces it further. Double-click still
   reaches the native dblclick listener unaffected by being draggable
   (a real double-click's two clicks each register as a zero-movement,
   harmless no-op move-gesture; canvas.js's pointerdown handler
   explains this isn't a conflict). */
.section-title-bar {
  /* Not position:sticky — this canvas pans via a CSS transform on
     surfaceEl, not native scroll, so there's no scrollport for sticky
     to track; it silently never did anything here. canvas.js's
     updateStickyTitleBars() recreates the "stays visible at the
     viewport's top while scrolled into a tall section" effect
     manually via a translateY, keyed off this element staying in
     normal flow (position:static, the default) within the section. */
  left: 0;
  right: 0;
  width: 100%;
  box-sizing: border-box;
  padding: 0.6rem 0.75rem;
  display: flex;
  align-items: center;
  gap: 0.3rem;
  pointer-events: auto;
  cursor: grab;
  border-top-left-radius: 11px;
  border-top-right-radius: 11px;
}
.section-title-bar:active { cursor: grabbing; }
.section-title-bar:hover { outline: 2px solid var(--accent); outline-offset: -2px; }

/* A note or the pointer currently sits inside this section — purely
   local/visual (like .note.selected), toggled by render.js's
   highlightSectionAt. */
.section.hover-target { border-color: var(--accent); border-style: solid; }

.section-title {
  margin: 0;
  color: var(--muted);
  font-size: 0.9rem;
  cursor: text;
}

.section-rename-btn {
  margin-top: 0;
  width: 1.4rem;
  height: 1.4rem;
  padding: 0;
  background: transparent;
  border: none;
  font-size: 0.75rem;
  line-height: 1;
  opacity: 0.5;
  cursor: pointer;
  flex-shrink: 0;
}
.section-rename-btn:hover { opacity: 1; }

.section-title-input {
  flex: 1;
  min-width: 0;
  font-size: 0.9rem;
  padding: 0.15rem 0.4rem;
  pointer-events: auto;
}

.section-remove {
  position: absolute;
  top: 0.3rem;
  right: 0.3rem;
  pointer-events: auto;
}

.section-resize-handle {
  position: absolute;
  right: 0;
  bottom: 0;
  width: 16px;
  height: 16px;
  cursor: nwse-resize;
  pointer-events: auto;
  touch-action: none;
  border-right: 2px solid var(--muted);
  border-bottom: 2px solid var(--muted);
  border-bottom-right-radius: 4px;
  opacity: 0.5;
}
.section-resize-handle:hover { opacity: 1; }

.section-draw-preview {
  position: absolute;
  border: 2px dashed var(--accent);
  border-radius: 8px;
  pointer-events: none;
}

.canvas-tool-controls { position: absolute; top: 0.75rem; left: 0.75rem; display: flex; gap: 0.3rem; }
.canvas-tool-controls button { margin-top: 0; padding: 0.4rem 0.7rem; }
.canvas-tool-controls .tool-btn { background: var(--panel); color: var(--text); border: 1px solid var(--border); }
.canvas-tool-controls .tool-btn.active { background: var(--accent); color: var(--accent-text); border-color: var(--accent); }

/* Session-wide (not per-note) toggle: while active, every new sticky
   is created without an author name attached. Overlays the canvas's
   top-right corner, the one open corner — top-left is the tool
   controls, bottom-right is zoom. */
/* Unmissable while it applies, gone the instant it doesn't — the
   corner toggle alone (small, easy to not notice) left people unsure
   whether it was currently on. Centered top so it doesn't compete
   with the tool controls (left) or the toggle itself (right). */
.anon-banner {
  position: absolute;
  top: 0.75rem;
  left: 50%;
  transform: translateX(-50%);
  margin: 0;
  padding: 0.4rem 0.9rem;
  background: var(--accent);
  color: var(--accent-text);
  font-size: 0.8rem;
  font-weight: 600;
  border-radius: 999px;
  box-shadow: 0 4px 10px -4px rgba(0, 0, 0, 0.3);
  pointer-events: none;
}
.canvas-mode-controls { position: absolute; top: 0.75rem; right: 0.75rem; display: flex; gap: 0.3rem; }
.canvas-mode-controls .mode-btn {
  margin-top: 0;
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.4rem 0.7rem;
  background: var(--panel);
  color: var(--text);
  border: 1px solid var(--border);
}
.canvas-mode-controls .mode-btn.active { background: var(--accent); color: var(--accent-text); border-color: var(--accent); }

/* State reads from the dot's fill, not just the button's overall
   color — a shape change survives colorblindness and low-contrast
   displays that a hue swap alone doesn't. Hollow ring off, solid
   filled dot on. */
.mode-btn-dot {
  width: 0.55rem;
  height: 0.55rem;
  border-radius: 50%;
  border: 1.5px solid currentColor;
  background: transparent;
  flex-shrink: 0;
}
.canvas-mode-controls .mode-btn.active .mode-btn-dot { background: currentColor; }

.canvas-viewport.drawing-section { cursor: crosshair; }

.note {
  position: absolute;
  width: 180px;
  /* Square-ish rather than a wide rectangle — a short note looks
     roughly square; longer text still grows taller than this. */
  min-height: 180px;
  background: var(--bg);
  border: 1px solid var(--border);
  /* Overrides just the left side's width — border-left-color defaults
     to the same var(--border) above unless JS sets an inline
     borderLeftColor for a note currently inside a colored section. */
  border-left: 4px solid var(--border);
  border-radius: 8px;
  padding: 0.6rem 2rem 0.6rem 0.6rem;
  font-size: 0.9rem;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
  cursor: grab;
  touch-action: none;
}
.note.dragging { cursor: grabbing; z-index: 1000; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.18); }

/* Faint hover indication — which sticky the pointer is over. Distinct
   from .selected below (a persistent click-driven state) so the two
   don't visually compete when hovering a note that's also selected. */
.note:hover:not(.dragging) { outline: 2px solid var(--muted); outline-offset: 1px; }

/* Click-to-select — purely local/visual, never synced (see
   setSelectedNote's doc comment in render.js). A stronger, accent
   colored ring distinguishes it from the plain hover outline above. */
.note.selected { outline: 2px solid var(--accent); outline-offset: 1px; box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 20%, transparent); }

.note-footer { display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap; margin-top: 0.5rem; }

.note-vote-pill {
  display: inline-flex;
  align-items: center;
  gap: 0.2rem;
  margin-top: 0;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 0.05rem 0.5rem;
  font-size: 0.75rem;
  color: var(--muted);
  cursor: pointer;
}
.note-vote-pill.voted { background: var(--accent); color: var(--accent-text); border-color: var(--accent); }

/* Notes reuse the same reaction widget chat messages use
   (.react-toggle/.reaction-chip/.react-btn/.chat-react-picker) —
   visually identical, legitimate CSS reuse. .note has its own
   top-right .note-remove button though, so the toggle can't sit in
   the chat version's absolute corner position — it flows inline in
   .note-footer instead. */
.note .react-toggle { position: static; width: 1.5rem; height: 1.5rem; }
.note .note-reactions:not(:empty) { display: inline-flex; gap: 0.25rem; }

.note.editing { cursor: text; padding: 0.6rem; }
.note-edit {
  display: block;
  width: 100%;
  min-height: 3rem;
  border: none;
  background: transparent;
  color: var(--text);
  font: inherit;
  resize: none;
  padding: 0;
}
.note-edit:focus { outline: none; }

.note-text { display: block; word-break: break-word; }
.note-author { display: block; margin-top: 0.3rem; color: var(--muted); font-size: 0.75rem; }

.note-remove, .section-remove {
  margin-top: 0;
  width: 1.5rem;
  height: 1.5rem;
  padding: 0;
  background: transparent;
  color: var(--muted);
  border: none;
  border-radius: 50%;
  font-size: 1rem;
  line-height: 1;
}
.note-remove { position: absolute; top: 0.3rem; right: 0.3rem; }
.note-remove:hover, .section-remove:hover { background: var(--panel); color: var(--text); }

.note-form { display: flex; gap: 0.5rem; align-items: flex-end; }
.note-form input { flex: 1; }
.note-form button { margin-top: 0; white-space: nowrap; }

.cursor-label {
  position: absolute;
  pointer-events: none;
  display: flex;
  align-items: center;
  gap: 0.3rem;
  transform: translate(-2px, -2px);
}
.cursor-dot { width: 10px; height: 10px; border-radius: 50%; background: var(--accent); border: 2px solid var(--panel); }
.cursor-name {
  background: var(--accent);
  color: var(--accent-text);
  font-size: 0.7rem;
  padding: 0.05rem 0.4rem;
  border-radius: 999px;
  white-space: nowrap;
}

.canvas-zoom-controls { position: absolute; bottom: 0.75rem; right: 0.75rem; display: flex; gap: 0.3rem; }
.canvas-zoom-controls button { margin-top: 0; padding: 0.4rem 0.7rem; }

/* --- Action items ------------------------------------------------------ */

.action-item-list { list-style: none; margin: 0 0 0.75rem; padding: 0; display: flex; flex-direction: column; gap: 0.5rem; }

.action-item {
  position: relative;
  display: flex;
  align-items: center;
  gap: 0.6rem;
  background: var(--bg);
  border-radius: 8px;
  padding: 0.5rem 2rem 0.5rem 0.6rem;
  font-size: 0.9rem;
}

.action-item.done .action-item-text { text-decoration: line-through; color: var(--muted); }

.action-item-check {
  display: inline-block;
  width: 1rem;
  height: 1rem;
  border-radius: 4px;
  border: 1px solid var(--border);
  flex-shrink: 0;
}
.action-item-check.done { background: var(--accent); border-color: var(--accent); }

.carried-badge {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 0.05rem 0.5rem;
  font-size: 0.7rem;
  color: var(--muted);
  white-space: nowrap;
}

/* --- Chat (same shape as planning-poker's) ----------------------------- */

.chat-log {
  list-style: none;
  margin: 0 0 0.75rem;
  padding: 1rem 0 0;
  max-height: 14rem;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.chat-message {
  position: relative;
  background: var(--bg);
  border-radius: 8px;
  padding: 0.4rem 2.2rem 0.4rem 0.6rem;
  font-size: 0.9rem;
}

.chat-message-self { border-left: 3px solid var(--accent); }

.chat-meta { display: block; color: var(--muted); font-size: 0.75rem; margin-bottom: 0.15rem; }

.chat-text { word-break: break-word; }
.chat-text a { color: var(--accent); }

.chat-reactions:not(:empty) { display: block; margin-top: 0.3rem; }

.react-toggle {
  position: absolute;
  top: 0.3rem;
  right: 0.4rem;
  margin-top: 0;
  width: 1.75rem;
  height: 1.75rem;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  color: var(--muted);
  border: none;
  border-radius: 50%;
}

.react-toggle:hover { background: var(--panel); color: var(--text); }

.reaction-chip {
  display: inline-block;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 0.05rem 0.4rem;
  margin-right: 0.25rem;
  font-size: 0.8rem;
}

.chat-react-picker {
  position: absolute;
  top: -0.9rem;
  right: 0.4rem;
  display: flex;
  gap: 0.2rem;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 0.2rem;
  opacity: 0;
  pointer-events: none;
  transition: opacity 120ms ease;
}

/* Neither chat messages nor notes reveal the picker on hover — its
   rendered height (padding + a row of 2.5rem buttons) fully covers
   .react-toggle's own position (both anchored at the same right:0.4rem,
   the picker extending up and over it), so a hover-revealed picker
   makes the toggle that's supposed to open it permanently unclickable
   — confirmed live, the toggle was dead on arrival for chat messages.
   Only reachable via the explicit always-visible .react-toggle click
   (.picker-open) — for notes it's additionally what keeps hovering a
   note from blocking its double-click-to-edit gesture. */
.chat-message.picker-open .chat-react-picker,
.note.picker-open .chat-react-picker,
.chat-react-picker:focus-within {
  opacity: 1;
  pointer-events: auto;
}

/* --- Mobile sidebar drawer --------------------------------------------- */

/* Hidden entirely above the mobile breakpoint — only the media query
   below gives these display/position/z-index. */
.sidebar-hamburger, .sidebar-backdrop { display: none; }

@media (max-width: 768px) {
  .sidebar-hamburger {
    display: block;
    position: absolute;
    top: 0.75rem;
    left: 0.75rem;
    z-index: 101;
    width: 2.25rem;
    height: 2.25rem;
    padding: 0;
    background: var(--panel);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: 8px;
    font-size: 1.1rem;
    line-height: 1;
  }

  /* Nudges the Select/Draw-section toolbar down so it doesn't overlap
     the hamburger's top-left slot — the one in-scope toolbar change
     needed to fix the mobile collision, not a general resize pass. */
  .canvas-tool-controls { top: 3.25rem; }

  /* Below both button rows (hamburger+toggle at top:0.75rem, the
     toolbar at top:3.25rem) instead of centered between them — at
     this width the full sentence has nowhere to shrink-wrap to
     without colliding with one side or the other. */
  .anon-banner {
    top: 5.75rem;
    left: 0.75rem;
    right: 0.75rem;
    transform: none;
    max-width: none;
    width: auto;
    text-align: center;
    font-size: 0.72rem;
  }

  /* Removed from flex flow so .room-canvas (flex:1) fills the whole
     screen underneath it — this is what actually fixes "the mobile
     user couldn't see the board at all." */
  .room-sidebar {
    position: fixed;
    top: 0;
    left: 0;
    bottom: 0;
    z-index: 100;
    width: 85vw;
    max-width: 360px;
    background: var(--bg);
    transform: translateX(-100%);
    transition: transform 0.2s ease;
  }

  #view-room.sidebar-open .room-sidebar { transform: translateX(0); }

  .sidebar-backdrop {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 90;
    background: rgba(0, 0, 0, 0.4);
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.2s ease;
  }

  #view-room.sidebar-open .sidebar-backdrop { opacity: 1; pointer-events: auto; }
}

.react-btn {
  margin-top: 0;
  width: 2.5rem;
  height: 2.5rem;
  padding: 0;
  background: transparent;
  border: none;
  border-radius: 50%;
  font-size: 1.1rem;
  line-height: 1;
}

.react-btn:hover, .react-btn:active { background: var(--bg); }

.chat-form { display: flex; gap: 0.5rem; }
.chat-form input { flex: 1; }

/* ========================================================================
   Landing page (view-create, shown for a fresh top-level visit — a
   shared room link skips straight to view-join, which keeps the plain
   minimal form untouched). Scoped under .landing throughout, including
   its own --l-* token namespace, so none of this can leak into or
   collide with the room app's existing --bg/--text/--accent tokens.
   ======================================================================== */

.landing {
  --l-ink: #221d17;
  --l-ink-soft: #5c5346;
  --l-board: #f1ebdf;
  --l-board-raised: #fbf8f2;
  --l-line: #e3d9c4;
  --l-indigo: #4f46e5;
  --l-cyan: #0891b2;
  --l-emerald: #059669;
  --l-amber: #d97706;
  --l-red: #dc2626;
  --l-violet: #7c3aed;
  --l-shadow: 32 25% 18%;
}

@media (prefers-color-scheme: dark) {
  .landing {
    --l-ink: #f3ece0;
    --l-ink-soft: #c3b8a6;
    --l-board: #1b1711;
    --l-board-raised: #262019;
    --l-line: #3a3225;
    --l-indigo: #6d64f0;
    --l-cyan: #22b8bc;
    --l-emerald: #34d399;
    --l-amber: #f3a429;
    --l-red: #f0675f;
    --l-violet: #a78bfa;
    --l-shadow: 20 60% 1%;
  }
}

/* Full-bleed breakout from main's 720px reading column — the classic
   width:100vw + margin-left:50% + translateX(-50%) trick, same idiom
   as .canvas-viewport's breakout above but to the full viewport rather
   than a fixed width. overflow-x:hidden on body is the standard
   safety net for this trick's scrollbar-width edge case. */
body { overflow-x: hidden; }
.landing {
  width: 100vw;
  margin-left: 50%;
  transform: translateX(-50%);
  background: var(--l-board);
  color: var(--l-ink);
  font-family: "Work Sans", -apple-system, BlinkMacSystemFont, sans-serif;
}

.landing h1, .landing h2, .landing h3, .landing h4 {
  font-family: "Instrument Serif", Georgia, serif;
  font-weight: 400;
  text-wrap: balance;
  margin: 0;
}
.landing em { font-style: italic; color: var(--l-indigo); }

.landing .label {
  font-size: 0.74rem;
  font-weight: 600;
  letter-spacing: 0.09em;
  text-transform: uppercase;
}

/* padding-left/right only, deliberately never a padding shorthand —
   .wrap is combined directly onto .hero/.topbar/footer ("hero wrap"
   etc. on one element), and a shorthand here would set padding-top/
   bottom to 0 as a side effect, silently overriding whatever vertical
   padding that other class declares whenever this rule (or one of its
   own mobile overrides) happens to come later in the cascade — exactly
   what broke .hero's own padding-top on mobile before this fix. */
.landing .wrap { max-width: 1100px; margin: 0 auto; padding-left: 1.75rem; padding-right: 1.75rem; }

.landing .topbar { display: flex; align-items: center; justify-content: space-between; padding: 1.75rem 1.75rem 0; max-width: 1100px; margin: 0 auto; }
.landing .wordmark { font-family: "Instrument Serif", serif; font-size: 1.5rem; }
.landing .topbar nav a {
  text-decoration: none;
  font-family: "Work Sans", sans-serif;
  font-size: 0.85rem;
  color: var(--l-ink-soft);
  border: 1px solid var(--l-line);
  padding: 0.5rem 1rem;
  border-radius: 999px;
  transition: border-color 150ms ease, color 150ms ease;
}
.landing .topbar nav a:hover, .landing .topbar nav a:focus-visible { border-color: var(--l-indigo); color: var(--l-indigo); }

/* padding-top/bottom only, for the same reason .wrap above only sets
   left/right — this element is always "hero wrap" together, and the
   two rules need to compose regardless of source order, not fight
   over which one's shorthand wins. */
/* align-items: start, not center — center would re-balance the row
   (and visibly shift .board-frame) every time .carry-over opens or
   closes and changes the left column's height. */
.landing .hero { display: grid; grid-template-columns: 1fr 1fr; gap: 2.5rem; align-items: start; padding-top: 3.5rem; padding-bottom: 5rem; }
.landing .hero h1 { font-size: clamp(2.7rem, 5vw, 4.4rem); line-height: 1.04; letter-spacing: -0.01em; }
.landing-eyebrow { color: var(--l-indigo); display: block; margin-bottom: 0.9rem; }
.landing .hero p.lede { margin: 1.4rem 0 0; font-size: 1.14rem; line-height: 1.6; color: var(--l-ink-soft); max-width: 30rem; }

/* The real functional create-room form, styled to sit in the hero
   rather than behind a scroll/click — a visitor can start typing
   immediately with no extra navigation. */
.landing .create-form {
  margin-top: 2rem;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1rem 1.25rem;
  max-width: 30rem;
}
.landing .create-form label { width: 100%; color: var(--l-ink-soft); font-family: "Work Sans", sans-serif; margin-bottom: -0.4rem; }
.landing .create-form input[type="text"] {
  flex: 1;
  min-width: 12rem;
  background: var(--l-board-raised);
  border: 1px solid var(--l-line);
  color: var(--l-ink);
  padding: 0.75rem 0.9rem;
  border-radius: 8px;
  font-size: 1rem;
  font-family: "Work Sans", sans-serif;
}
.landing .create-form button {
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  background: var(--l-ink);
  color: var(--l-board);
  font-weight: 600;
  font-size: 1rem;
  padding: 0.85rem 1.5rem;
  border-radius: 8px;
  border: none;
  margin-top: 0;
  box-shadow: 0 10px 22px -12px hsl(var(--l-shadow) / 0.55);
  transition: transform 150ms ease, box-shadow 150ms ease;
}
.landing .create-form button:hover, .landing .create-form button:focus-visible { transform: translateY(-1px); box-shadow: 0 14px 26px -12px hsl(var(--l-shadow) / 0.6); }
.landing .create-form .meta { font-size: 0.85rem; color: var(--l-ink-soft); font-family: "Work Sans", sans-serif; flex-basis: 100%; }
.landing .create-form .meta strong { color: var(--l-ink); font-weight: 600; }

.landing .carry-over { margin-top: 1.1rem; max-width: 30rem; font-size: 0.85rem; color: var(--l-ink-soft); }
.landing .carry-over summary { cursor: pointer; }
.landing .carry-over input[type="file"] { margin-top: 0.6rem; font-size: 0.85rem; color: var(--l-ink-soft); }
.landing .carry-over .import-label { color: var(--l-ink-soft); margin: 0.6rem 0 0.3rem; }

.landing .archive-link { margin-top: 0.9rem; font-size: 0.85rem; text-align: left; }
.landing .archive-link a { color: var(--l-ink-soft); }

/* --- sticky cluster --- */
.landing .board-frame {
  position: relative;
  height: 23rem;
  border-radius: 16px;
  background:
    radial-gradient(circle at 18% 22%, hsl(var(--l-shadow) / 0.05) 0, transparent 2.6%),
    radial-gradient(circle at 62% 70%, hsl(var(--l-shadow) / 0.05) 0, transparent 2.6%),
    radial-gradient(circle at 80% 18%, hsl(var(--l-shadow) / 0.05) 0, transparent 2.6%),
    var(--l-board-raised);
  border: 1px solid var(--l-line);
}
/* Deliberately not theme-reactive: a sticky note is paper, and paper
   doesn't invert when the room goes dark — each note blends a token
   hue into a fixed near-white, with fixed dark-brown ink on top, a
   pairing that's self-consistent in both page themes (always dark
   text on light paper) even though it doesn't itself swap themes. */
.landing .sticky {
  position: absolute;
  width: 9.5rem;
  padding: 0.85rem 0.9rem 1rem;
  border-radius: 3px;
  font-size: 0.88rem;
  line-height: 1.35;
  color: #241f17;
  font-family: "Work Sans", sans-serif;
  box-shadow: 0 12px 20px -10px hsl(var(--l-shadow) / 0.45);
}
.landing .sticky .who { display: block; margin-top: 0.55rem; font-size: 0.72rem; color: #4a4336; }
.landing .s1 { top: 1.4rem; left: 2.4rem; background: color-mix(in srgb, var(--l-indigo) 22%, #fdfbff); transform: rotate(-6deg); z-index: 3; }
.landing .s2 { top: 0.6rem; left: 11.8rem; background: color-mix(in srgb, var(--l-cyan) 20%, #fbfefe); transform: rotate(4deg); z-index: 2; }
.landing .s3 { top: 9.4rem; left: 1.2rem; background: color-mix(in srgb, var(--l-amber) 24%, #fffdf8); transform: rotate(3deg); z-index: 2; }
.landing .s4 { top: 10.6rem; left: 12.6rem; background: color-mix(in srgb, var(--l-emerald) 20%, #fbfefc); transform: rotate(-4deg); z-index: 3; }
.landing .s5 { top: 5.6rem; left: 6.6rem; background: color-mix(in srgb, var(--l-violet) 18%, #fdfcff); transform: rotate(-2deg); z-index: 4; }
.landing .s6 { top: 15.4rem; left: 7rem; background: color-mix(in srgb, var(--l-red) 18%, #fffbfa); transform: rotate(5deg); z-index: 2; }

/* --- template strip --- */
.landing .template-strip { border-top: 1px solid var(--l-line); border-bottom: 1px solid var(--l-line); padding: 3.4rem 0; }
.landing .template-strip .label { color: var(--l-indigo); display: block; margin-bottom: 0.4rem; }
.landing .template-strip .strip-head { margin-bottom: 1.8rem; }
.landing .template-strip .strip-head h3 { font-size: 1.3rem; }
.landing .template-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1.1rem; }
.landing .template-card {
  background: var(--l-board-raised);
  border: 1px solid var(--l-line);
  border-radius: 10px;
  padding: 1.2rem 1.15rem 1.35rem;
}
.landing .template-card h4 { font-size: 1.18rem; margin: 0 0 0.85rem; }
.landing .chips { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.landing .chip {
  display: inline-flex;
  align-items: center;
  font-size: 0.76rem;
  font-weight: 600;
  font-family: "Work Sans", sans-serif;
  padding: 0.3rem 0.6rem;
  border-radius: 999px;
  background: color-mix(in srgb, var(--c) 16%, var(--l-board-raised));
  border: 1px solid color-mix(in srgb, var(--c) 35%, transparent);
  color: var(--c);
}

/* --- steps --- */
.landing .steps { padding-top: 5.5rem; padding-bottom: 5.5rem; } /* also "steps wrap" combined — see .wrap's comment */
.landing .section-head { max-width: 32rem; margin-bottom: 3rem; }
.landing .section-head .label { color: var(--l-indigo); display: block; margin-bottom: 0.9rem; }
.landing .section-head h2 { font-size: clamp(1.9rem, 3vw, 2.5rem); }
.landing .step-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1.3rem; }
.landing .step {
  background: var(--l-board-raised);
  border: 1px solid var(--l-line);
  border-top: 4px solid var(--c);
  border-radius: 10px;
  padding: 1.6rem 1.4rem 1.8rem;
}
.landing .step h3 { font-family: "Work Sans", sans-serif; font-weight: 650; font-size: 1.02rem; margin-bottom: 0.55rem; }
.landing .step p { margin: 0; font-size: 0.9rem; line-height: 1.5; color: var(--l-ink-soft); font-family: "Work Sans", sans-serif; }

/* --- trust band --- */
.landing .trust { background: var(--l-ink); color: var(--l-board); border-radius: 18px; margin: 1rem 0 5.5rem; padding: 3.2rem 3rem; }
.landing .trust-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 2.3rem 2.8rem; }
.landing .trust h2 { font-size: 1.6rem; grid-column: 1 / -1; max-width: 30rem; margin-bottom: 0.4rem; }
.landing .trust-item .label { color: var(--l-amber); display: block; margin-bottom: 0.55rem; }
.landing .trust-item p {
  margin: 0;
  font-size: 0.93rem;
  line-height: 1.55;
  font-family: "Work Sans", sans-serif;
  /* .trust's background is the inverse of the page (var(--l-ink)), so
     body text derives from var(--l-board) rather than a literal — a
     hardcoded light value would go invisible once dark mode flips
     --l-ink to a light color and this band flips polarity with it. */
  color: color-mix(in srgb, var(--l-board) 82%, var(--l-ink) 18%);
}

/* --- footer --- */
.landing footer { border-top: 1px solid var(--l-line); margin-top: 1rem; text-align: center; }
.landing footer .footer-band {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.7rem;
}
.landing footer .footer-band--top { min-height: 8rem; border-bottom: 1px solid var(--l-line); }
.landing footer .footer-band--bottom { min-height: 6rem; padding-bottom: 1rem; }
.landing footer .footer-word { font-family: "Instrument Serif", serif; font-size: 1.6rem; }
.landing footer .tagline { font-size: 1.02rem; color: var(--l-ink-soft); font-family: "Work Sans", sans-serif; }
.landing footer .fine {
  font-size: 0.92rem;
  color: var(--l-ink-soft);
  font-family: "Work Sans", sans-serif;
  margin: 0 auto;
  max-width: 30rem;
}
.landing footer .fine a { color: var(--l-indigo); text-decoration: none; border-bottom: 1px solid currentColor; font-weight: 600; }

@media (max-width: 880px) {
  .landing .hero { grid-template-columns: 1fr; padding-top: 2.5rem; }
  /* Tall enough to contain the deepest sticky (s6, whose text wraps
     to 3 lines at this narrower width — confirmed by measuring its
     actual rendered bottom edge, not guessed) with margin to spare —
     19rem clipped it, spilling s6 over the content below. */
  .landing .board-frame { order: -1; height: 24rem; }
  .landing .sticky { width: 7.6rem; font-size: 0.78rem; }
  .landing .step-grid { grid-template-columns: 1fr 1fr; }
  .landing .template-grid { grid-template-columns: 1fr 1fr; }
  .landing .trust-grid { grid-template-columns: 1fr; }
  .landing .trust { padding: 2.3rem 1.6rem; }
}
@media (max-width: 560px) {
  .landing .step-grid { grid-template-columns: 1fr; }
  .landing .template-grid { grid-template-columns: 1fr; }
  .landing .topbar { padding: 1.5rem 1.25rem 0; }
  .landing .wrap { padding-left: 1.25rem; padding-right: 1.25rem; }
}
.chat-form button { margin-top: 0; white-space: nowrap; }
