#overlay-root {
  position: fixed;
  inset: 0;
  z-index: 60;
  pointer-events: none;
}

.overlay {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--duration-medium) var(--ease-standard);
}
.overlay.open {
  opacity: 1;
  pointer-events: auto;
}

/* Version 3, Phase 12 ("Accessibility & Comfort Pass") — this used to be
   `background: transparent; color: inherit; opacity: 0.7`, which read as
   "too low-visibility to notice reliably" in playtesting: no real
   contrast of its own, blending into whatever material (screen/paper/
   cork/panel) happened to sit behind it, with a hover boost that does
   nothing on touch. Now the same glass/wood-chip treatment
   `.hud-corner-controls button` and the touch action buttons already use
   — proven to read clearly over the 3D scene, so it reads clearly here
   too, regardless of which overlay material is behind it. */
.overlay-close {
  position: absolute;
  top: var(--space-3);
  right: var(--space-3);
  background: var(--surface-glass);
  border: 1px solid var(--wood-mid);
  border-radius: var(--radius-sm);
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  color: var(--paper-dark);
  padding: var(--space-1) var(--space-2);
  cursor: pointer;
}
.overlay-close:hover { border-color: var(--brass); color: var(--paper); }

/* ---------- Screen material (computer desk) ---------- */
.overlay--screen {
  background: radial-gradient(ellipse at center, rgba(16,23,26,0.92), rgba(3,5,6,0.98));
}
.overlay--screen .overlay-panel {
  width: min(920px, 92vw);
  height: min(620px, 86vh);
  background: var(--screen-bg);
  border-radius: var(--radius-md);
  border: 6px solid #1c1410;
  box-shadow: 0 0 0 2px var(--wood-mid), 0 30px 80px rgba(0,0,0,0.6), inset 0 0 60px rgba(127,216,196,0.05);
  color: var(--screen-glow);
  font-family: var(--font-mono);
  overflow: hidden;
  position: relative;
}
.overlay--screen .overlay-panel::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: repeating-linear-gradient(to bottom, rgba(255,255,255,0.02) 0px, rgba(255,255,255,0.02) 1px, transparent 1px, transparent 3px);
  mix-blend-mode: overlay;
}
.overlay--screen .screen-header {
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid rgba(127,216,196,0.25);
  display: flex;
  justify-content: space-between;
  font-size: var(--text-xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  opacity: 0.7;
}
.overlay--screen .screen-body { padding: var(--space-4); height: calc(100% - 90px); overflow-y: auto; }
.screen-icons { display: flex; gap: var(--space-4); flex-wrap: wrap; }
.screen-icon {
  width: 140px;
  padding: var(--space-3);
  border: 1px solid rgba(127,216,196,0.3);
  border-radius: var(--radius-md);
  text-align: center;
  cursor: pointer;
  background: rgba(127,216,196,0.04);
  transition: background var(--duration-fast) var(--ease-standard);
}
.screen-icon:hover { background: rgba(127,216,196,0.12); }
.screen-icon .glyph { font-size: 28px; display: block; margin-bottom: var(--space-2); }

/* ---------- Paper material (notebooks) ---------- */
.overlay--paper {
  background: rgba(10,8,6,0.7);
}
.overlay--paper .overlay-panel {
  width: min(640px, 90vw);
  height: min(760px, 88vh);
  background: var(--paper);
  background-image: linear-gradient(rgba(0,0,0,0.02) 1px, transparent 1px);
  background-size: 100% 28px;
  border-radius: 2px;
  box-shadow: var(--shadow-panel), 0 0 0 1px var(--paper-dark);
  color: var(--ink);
  padding: var(--space-4) var(--space-5);
  transform: rotate(-0.3deg);
  position: relative;
  overflow-y: auto;
}
.overlay--paper h2 {
  font-family: var(--font-display);
  font-weight: 500;
  margin-top: 0;
  border-bottom: 1px solid var(--paper-dark);
  padding-bottom: var(--space-2);
}
.overlay--paper textarea {
  width: 100%;
  min-height: 460px;
  border: none;
  background: transparent;
  font-family: var(--font-body);
  font-size: var(--text-md);
  line-height: 28px;
  color: var(--ink);
  resize: vertical;
}
.overlay--paper textarea:focus { outline: none; }
.overlay--paper .paper-meta {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--ink-soft);
}

/* ---------- Cork material (pinboard) ---------- */
.overlay--cork {
  background: rgba(10,8,6,0.7);
}
.overlay--cork .overlay-panel {
  width: min(1040px, 94vw);
  height: min(700px, 88vh);
  background: #b28a5b;
  background-image: radial-gradient(rgba(0,0,0,0.08) 1px, transparent 1.4px);
  background-size: 6px 6px;
  border-radius: var(--radius-md);
  border: 14px solid var(--wood-mid);
  box-shadow: var(--shadow-panel);
  padding: var(--space-4);
  overflow-y: auto;
  color: var(--wood-dark);
}
.overlay--cork h2 {
  font-family: var(--font-display);
  color: var(--paper);
  text-shadow: 0 1px 2px rgba(0,0,0,0.4);
  margin-top: 0;
}
.cork-board {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: var(--space-4);
  align-content: start;
}
.cork-note {
  background: #fef6d8;
  padding: var(--space-3);
  border-radius: 2px;
  box-shadow: var(--shadow-medium);
  transform: rotate(-1.2deg);
  font-size: var(--text-sm);
  position: relative;
  min-height: 120px;
}
.cork-note:nth-child(even) { transform: rotate(1deg); background: #eaf0da; }
.cork-note::before {
  content: "";
  position: absolute;
  top: -8px;
  left: 50%;
  transform: translateX(-50%);
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--brass);
  box-shadow: 0 2px 3px rgba(0,0,0,0.4);
}
.cork-note textarea, .cork-note input {
  width: 100%;
  border: none;
  background: transparent;
  font-family: var(--font-body);
  resize: none;
}
.cork-note select {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  border: 1px solid rgba(0,0,0,0.2);
  background: rgba(255,255,255,0.5);
  margin-top: var(--space-2);
}
.cork-add {
  border: 2px dashed rgba(255,255,255,0.4);
  border-radius: 2px;
  min-height: 120px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--paper);
  cursor: pointer;
  background: transparent;
  font-family: var(--font-body);
  font-size: var(--text-sm);
}
.cork-add:hover { background: rgba(255,255,255,0.08); }

/* ---------- Panel material (stereo, workbench, shelving) ---------- */
.overlay--panel {
  background: rgba(10,8,6,0.7);
}
.overlay--panel .overlay-panel {
  width: min(560px, 90vw);
  max-height: 86vh;
  overflow-y: auto;
  background: linear-gradient(180deg, var(--wood-mid), var(--wood-dark));
  border-radius: var(--radius-lg);
  border: 3px solid var(--wood-dark);
  box-shadow: 0 20px 60px rgba(0,0,0,0.55), inset 0 1px 0 rgba(255,255,255,0.05);
  padding: var(--space-4);
  color: var(--paper);
}
.overlay--panel h2 {
  font-family: var(--font-display);
  margin-top: 0;
  color: var(--brass-bright);
}
.panel-row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-bottom: var(--space-3);
}
.panel-row label { width: 90px; font-family: var(--font-mono); font-size: var(--text-xs); color: var(--paper-dark); }
/* Workshop Reliability phase — "resolve the horizontal scrolling
   behaviour currently present inside the Builder phone application."
   Root cause: a flex item's default minimum width is its own content's
   intrinsic size (`min-width: auto`), not zero — for `<input
   type="range">` that intrinsic size is the browser's own default
   slider width (commonly 100-170px), which `flex: 1` alone can shrink
   *from* but never *below*. Inside the ~268px-wide Builder Phone screen
   (see buildmode.css's own `.builder-phone` width), a label plus that
   un-shrinkable slider plus a value readout routinely added up to more
   than the available width, forcing `.builder-phone-screen`'s own
   `overflow-y: auto` to also compute an unwanted horizontal scrollbar
   (an `auto`/`visible` pairing on the two axes forces the visible one to
   `auto` too, per the CSS overflow spec). `min-width: 0` is the standard
   fix — the slider can now shrink all the way down to what's actually
   left after its label and value text, the same "reads a raw number,
   not a pretend zero" trick `.builder-phone-row input[type="number"]`'s
   own `width: 0` already uses one screen over. */
.panel-row input[type="range"] { flex: 1; min-width: 0; }
/* Version 4, Phase 2 ("Playtesting Notes, Continued") — the identical bug
   class as the range-input fix directly above, just never given the same
   treatment for `<select>`: a `.panel-row` dropdown's own intrinsic width
   (driven by its longest `<option>` text) can't shrink below that size on
   its own, which forced `.wardrobe-form`'s two-column grid wider than its
   880px-max panel — see WardrobeApp.js's own model/material dropdowns —
   producing horizontal scroll instead of the dropdown simply fitting. */
.panel-row select { flex: 1; min-width: 0; }

/* ---------- Wardrobe overlay material ---------- */
/* Reuses the exact same createWardrobeApp() the computer's Wardrobe app
   mounts — see src/ui/overlays/WardrobeOverlay.js. Version 3, Phase 9
   ("Creative Flow") — playtesting read this overlay as "noticeably
   rougher... closer to an early prototype than something built for the
   player," and the first-guess fix (just shrink the declared width from
   880px down to the standard panel's own 560px) turned out not to be the
   real bug at all: live measurement showed the panel rendering at
   1280px regardless of what width was declared here. The actual root
   cause was two compounding mistakes, only visible once actually
   measured rather than reasoned about from the CSS alone:
     1. `WardrobeOverlay.js`'s own `mount()` added a second class,
        `wardrobe-overlay-panel`, onto the *same* element this rule
        targets — and that class's own (now-removed) rule declared
        `flex: 1`. Since this panel is itself a flex child of `.overlay`
        (`align-items/justify-content: center`), `flex: 1`'s implicit
        `flex-basis: 0%` overrode the `width` declared here entirely,
        letting the panel grow to fill nearly the whole viewport no
        matter what width this rule asked for.
     2. `display: flex` here had no `flex-direction`, defaulting to
        `row` — so the `<h2>` heading and `.builder-root` (the two
        children `WardrobeApp.js`'s own mount() appends directly into
        this panel) laid out side by side rather than stacked. Measured
        live: the heading rendered as a squeezed 114px-wide column
        stretched to the panel's full height, with `.builder-root`
        beside it, not above it.
   Fixed by removing the conflicting class/rule entirely (this panel
   never needed to flex-grow — every other overlay material's own panel
   just takes its explicit width/height and lets `.overlay`'s own
   centering do the rest) and adding the missing `flex-direction:
   column` so the heading actually stacks above the split preview/form
   layout the way it visually always should have. Width now matches the
   standard `.overlay--panel` material (560px) — the same scale every
   other piece of furniture already uses. */
.overlay--wardrobe {
  background: rgba(10, 8, 6, 0.72);
}
/* Version 3, Phase 14 ("Further Environmental Polish") — "way too
   small... it just needed everything inside of it to have a better
   layout scheme." The 560px width above traces back to fixing a
   genuine, different bug (an accidental 1280px render — see this
   section's own comment above): once that width started actually being
   respected, it turned out to be the wrong width for *this* app's own
   richer content specifically, not just a return to correct. Widened to
   a scale that gives the form room to lay out in two columns (see
   `.wardrobe-form` below) rather than one narrow scrolling stack
   regardless of how wide the panel itself is — still well short of the
   old 1280px accident. */
.overlay--wardrobe .overlay-panel {
  width: min(880px, 92vw);
  height: min(720px, 88vh);
  background: linear-gradient(180deg, var(--wood-mid), var(--wood-dark));
  border-radius: var(--radius-lg);
  border: 3px solid var(--wood-dark);
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.55), inset 0 1px 0 rgba(255, 255, 255, 0.05);
  padding: var(--space-3);
  display: flex;
  flex-direction: column;
}

/* ---------- Companion overlay material (the resident) ---------- */
/* Version 3, Phase 6 ("The Workshop Remembers") — "the dialogue interface
   should better support watching Bubble while interacting... expressions,
   colour changes and animation should remain visible instead of being
   obscured by large centred dialogue panels." Every other overlay material
   above is a full-screen backdrop with a large centred panel, correct for
   a document or a screen to read but not for a character to actually
   watch while talking with them. This one deliberately isn't that: no
   backdrop at all (the room, and Bubble, stay exactly as visible as
   before opening it), and the panel itself sits docked in a corner rather
   than claiming the centre of the screen Bubble is standing in. */
.overlay--companion {
  background: transparent;
  align-items: flex-end;
  justify-content: flex-end;
  padding: var(--space-4);
}
.overlay--companion .overlay-panel {
  width: min(380px, 88vw);
  max-height: min(64vh, 560px);
  display: flex;
  flex-direction: column;
  background: rgba(16, 28, 26, 0.82);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border-radius: var(--radius-lg);
  border: 1px solid rgba(127, 216, 196, 0.32);
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.4), 0 0 32px rgba(127, 216, 196, 0.08);
  padding: var(--space-3);
  color: var(--paper);
}
.overlay--companion h2 {
  font-family: var(--font-display);
  margin: 0 0 var(--space-2);
  color: var(--teal-bright);
  flex-shrink: 0;
  /* Version 3, Phase 8a — room for the context-usage toggle button
     (.resident-usage-button) as a second child, right-aligned. */
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
}
.overlay--companion .resident-conversation-messages {
  max-height: none;
  flex: 1;
  min-height: 80px;
}

/* ---------- Environment panel (the window) ---------- */
.environment-panel .environment-summary {
  font-size: var(--text-md);
  font-weight: 600;
  margin: 0 0 4px;
}
.environment-panel .environment-wind {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--paper-dark);
  margin: 0 0 var(--space-3);
}
.environment-mode-tabs {
  display: flex;
  gap: 6px;
  margin-bottom: var(--space-3);
  flex-wrap: wrap;
}
.environment-mode-tabs button {
  flex: 1;
  min-width: 100px;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  padding: 8px 10px;
  border-radius: var(--radius-sm);
  border: 1px solid rgba(255, 255, 255, 0.25);
  background: transparent;
  color: inherit;
  cursor: pointer;
}
.environment-mode-tabs button.active {
  border-color: var(--brass-bright);
  background: rgba(184, 134, 59, 0.3);
}
.environment-weather-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.environment-weather-grid button {
  font-family: var(--font-mono);
  font-size: 0.8rem;
  padding: 6px 12px;
  border-radius: 999px;
  cursor: pointer;
  border: 1px solid rgba(255, 255, 255, 0.25);
  background: transparent;
  color: inherit;
}
.environment-weather-grid button.active {
  border-color: var(--brass-bright);
  background: rgba(184, 134, 59, 0.3);
}
.environment-status {
  font-size: var(--text-sm);
  color: var(--paper-dark);
  line-height: 1.4;
  margin-bottom: var(--space-3);
}
.environment-status-error {
  color: #e0a084;
}
.environment-note {
  font-size: var(--text-sm);
  color: var(--paper-dark);
  font-style: italic;
}
.environment-refresh-button {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  padding: 8px 14px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--brass);
  background: rgba(184, 134, 59, 0.15);
  color: inherit;
  cursor: pointer;
}
.environment-refresh-button:hover { background: rgba(184, 134, 59, 0.28); }
.environment-refresh-button:disabled { opacity: 0.5; cursor: default; }
.dial-button {
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: radial-gradient(circle at 35% 30%, var(--brass-bright), var(--brass));
  border: 2px solid var(--wood-dark);
  cursor: pointer;
  box-shadow: 0 3px 6px rgba(0,0,0,0.4);
  color: var(--wood-dark);
  font-weight: 700;
}
.track-list { list-style: none; padding: 0; margin: var(--space-3) 0; }
.track-list li {
  padding: var(--space-2);
  border-radius: var(--radius-sm);
  cursor: pointer;
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  display: flex;
  justify-content: space-between;
}
.track-list li:hover { background: rgba(255,255,255,0.06); }
.track-list li.active { background: rgba(184,134,59,0.25); color: var(--brass-bright); }

.wide-list { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.wide-list li {
  background: rgba(255,255,255,0.04);
  border-radius: var(--radius-sm);
  padding: var(--space-3);
}
.wide-list .item-title { font-weight: 600; }
.wide-list .item-meta { font-family: var(--font-mono); font-size: var(--text-xs); color: var(--paper-dark); }
/* Version 3, Phase 3 — full notes and calculations attached to a finished
   project (see ArchiveOverlay.js's own buildArchiveEntry()). */
.archive-notes {
  margin: var(--space-2) 0 0;
  font-size: var(--text-sm);
  line-height: 1.5;
  color: var(--paper);
  white-space: pre-wrap;
}
.archive-calculations {
  list-style: none;
  padding: 0;
  margin: var(--space-2) 0 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.archive-calculations li {
  display: flex;
  justify-content: space-between;
  gap: var(--space-2);
  font-size: var(--text-xs);
}
.empty-state {
  text-align: center;
  color: var(--paper-dark);
  padding: var(--space-5) var(--space-3);
  font-style: italic;
}

.field-row { display: flex; gap: var(--space-2); margin-top: var(--space-3); }
.field-row input, .field-row select {
  font-family: var(--font-body);
  font-size: var(--text-sm);
  padding: var(--space-2);
  border-radius: var(--radius-sm);
  border: 1px solid var(--wood-mid);
  background: rgba(255,255,255,0.08);
  color: var(--paper);
}
/* Same Firefox native-option-list fix as .builder-shape-select's own
   comment in builder.css — this <select> shares the identical light-
   text-on-transparent-dark styling that goes unreadable the moment
   Firefox opens it with its own native (light) popup background. */
.field-row select option {
  color: var(--ink);
  background: var(--paper);
}
.field-row button {
  font-family: var(--font-body);
  font-weight: 600;
  background: var(--brass);
  color: var(--wood-dark);
  border: none;
  border-radius: var(--radius-sm);
  padding: var(--space-2) var(--space-3);
  cursor: pointer;
}

/* ---------- Rest Nook ---------- */
.rest-nook-reminder {
  cursor: pointer;
}
.rest-nook-dismiss-hint {
  cursor: pointer;
  font-size: var(--text-xs);
  color: var(--paper-dark);
  margin-top: var(--space-3);
}
/* Applied to the overlay's own panel element itself, not just its inner
   text — dismissing needs to hide the whole thing, not leave an empty,
   still-visible panel behind. The overlay's own open/closed state is
   untouched; this is purely visual, which is also why pointer-events is
   turned off alongside the fade, so the now-invisible panel doesn't keep
   intercepting clicks meant for whatever's behind it. */
.rest-nook-panel-dismissed {
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--duration-medium) var(--ease-standard);
}
/* "The screen should immediately return to normal." The actual reported
   bug — .overlay--panel's own full-screen dark background lives on the
   outer .overlay element, a separate element from .rest-nook-panel-
   dismissed above, which only ever fades panelEl itself. Fading this
   one too is what actually clears the screen back to normal, not just
   hides the card that was sitting on top of it. */
.rest-nook-backdrop-dismissed {
  background: transparent !important;
  pointer-events: none;
  transition: background var(--duration-medium) var(--ease-standard);
}

/* Version 3, Phase 3 — the reading chair's own "Read" tab. Appended as a
   sibling of .overlay-panel (see RestNookOverlay.js's own comment on why),
   so it's untouched by .rest-nook-panel-dismissed's own opacity/pointer-
   events even while the arrival reminder is fully faded out — it needs its
   own explicit hidden state instead, revealed once dismiss() reveals it. */
.rest-nook-read-tab {
  position: absolute;
  bottom: 48px;
  left: 50%;
  transform: translateX(-50%);
  background: rgba(58, 42, 30, 0.6);
  border: 1px solid rgba(184, 134, 59, 0.4);
  border-radius: 999px;
  padding: 8px 20px;
  color: var(--paper);
  font-family: var(--font-body);
  font-size: var(--text-sm);
  cursor: pointer;
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--duration-medium) var(--ease-standard);
}
.rest-nook-read-tab.visible { opacity: 0.85; pointer-events: auto; }
.rest-nook-read-tab:hover { opacity: 1; }

.rest-nook-menu {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  margin: var(--space-3) 0;
}
.rest-nook-menu-item {
  text-align: left;
  font-family: var(--font-body);
  font-size: var(--text-md);
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md);
  border: 1px solid rgba(255, 255, 255, 0.12);
  background: rgba(255, 255, 255, 0.04);
  color: var(--paper);
  cursor: pointer;
  transition: background var(--duration-fast) var(--ease-standard);
}
.rest-nook-menu-item:hover { background: rgba(255, 255, 255, 0.1); }

.rest-nook-back {
  display: block;
  background: transparent;
  border: none;
  color: var(--paper-dark);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  cursor: pointer;
  padding: 0 0 var(--space-3);
}
.rest-nook-back:hover { color: var(--paper); }

/* Minimal typography for the same rendered docs/HISTORY.md markdown
   workshop://history shows in the Browser — scoped to this class rather
   than reusing browser-pages.css (that stylesheet is the Computer/Browser
   surface's own, loaded only inside its iframe; this is a plain in-page
   DOM panel, so it gets its own small ruleset here instead, per this
   project's own "one file per interface surface" CSS convention). */
.rest-nook-story-body {
  font-size: var(--text-sm);
  line-height: 1.6;
}
.rest-nook-story-body h1,
.rest-nook-story-body h2,
.rest-nook-story-body h3,
.rest-nook-story-body h4 {
  font-family: var(--font-display);
  color: var(--brass-bright);
  margin: var(--space-4) 0 var(--space-2);
}
.rest-nook-story-body h1:first-child,
.rest-nook-story-body h2:first-child { margin-top: 0; }
.rest-nook-story-body p { margin: 0 0 var(--space-3); }
.rest-nook-story-body ul { margin: 0 0 var(--space-3); padding-left: var(--space-4); }
.rest-nook-story-body code {
  font-family: var(--font-mono);
  background: rgba(255, 255, 255, 0.08);
  padding: 1px 4px;
  border-radius: 3px;
}
.rest-nook-story-body pre {
  background: rgba(0, 0, 0, 0.25);
  padding: var(--space-3);
  border-radius: var(--radius-sm);
  overflow-x: auto;
}
.rest-nook-story-body a { color: var(--brass-bright); }
.rest-nook-story-body hr { border: none; border-top: 1px solid rgba(255, 255, 255, 0.15); margin: var(--space-4) 0; }
.rest-nook-story-body summary { cursor: pointer; color: var(--brass-bright); margin: var(--space-2) 0; }

/* ---------- Resident Conversation ---------- */
/* Version 3, Phase 14 ("Further Environmental Polish") — swapped which
   half of this surface clamps at 4 lines. It used to be each individual
   message bubble (its own `max-height`/scrollbar below `.resident-
   message`), while the input stayed a plain single-line field — a long
   reply got its own nested scrollbar even though the outer `.resident-
   conversation-messages` list was already the real scroll owner for the
   whole conversation. Messages now render in full (no per-message
   clamp); the *input* is what grows and caps at 4 lines with its own
   scrollbar instead, so a long draft can't push the conversation itself
   out of view — see `.resident-conversation-input-row textarea` below. */
.resident-conversation-messages {
  display: flex;
  flex-direction: column;
  gap: 10px;
  max-height: 340px;
  overflow-y: auto;
  padding: 4px 2px;
  margin-bottom: var(--space-3);
}
.resident-message {
  max-width: 80%;
  padding: 10px 14px;
  border-radius: 14px;
  font-size: var(--text-sm);
  line-height: 1.5;
  white-space: pre-wrap;
}
.resident-message-player {
  align-self: flex-end;
  background: var(--brass);
  color: var(--wood-dark);
  border-bottom-right-radius: 4px;
}
.resident-message-resident {
  align-self: flex-start;
  background: rgba(127, 216, 196, 0.14);
  border: 1px solid rgba(127, 216, 196, 0.3);
  color: var(--paper);
  border-bottom-left-radius: 4px;
}
/* "Keep this subtle and in keeping with Bubble's personality." Three
   dots easing between a dim and a soft glow, one after another — a
   gentle pulse, not a spinning loader borrowed from a generic app. */
.resident-message-thinking {
  display: flex;
  align-items: center;
  gap: 5px;
  padding: 12px 16px;
}
.resident-message-thinking span {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--screen-glow);
  opacity: 0.3;
  animation: resident-thinking-pulse 1.3s ease-in-out infinite;
}
.resident-message-thinking span:nth-child(2) { animation-delay: 0.18s; }
.resident-message-thinking span:nth-child(3) { animation-delay: 0.36s; }
@keyframes resident-thinking-pulse {
  0%, 80%, 100% { opacity: 0.3; transform: scale(0.85); }
  40% { opacity: 0.9; transform: scale(1); }
}
/* "Better waiting behaviour while models load... improved loading
   feedback." A quiet, small line — reassurance, not an alert — shown
   only once a reply has genuinely been taking a while. */
.resident-thinking-hint {
  font-size: var(--text-xs);
  color: var(--paper-dark);
  margin: 2px 0 0 4px;
  font-style: italic;
}
/* Version 3, Phase 8b — a Workshop Function's own honest "Bubble did X"
   note, distinct from a chat bubble the same way the error row above is —
   quiet, small, never claiming to be something the resident said. */
.resident-function-note {
  font-size: var(--text-xs);
  color: var(--teal-bright);
  margin: -2px 0 0 4px;
  font-style: italic;
}
/* Version 3, Phase 8a — a failed turn's own row, distinct from an
   ordinary chat bubble so it doesn't read as something Bubble actually
   said. Same error colour .environment-status-error already uses. */
.resident-message-error {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-xs);
  color: #e0a084;
  padding: 4px 6px;
}
.resident-message-error button {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  background: rgba(224, 160, 132, 0.15);
  border: 1px solid rgba(224, 160, 132, 0.4);
  color: #e0a084;
  border-radius: 999px;
  padding: 4px 12px;
  cursor: pointer;
  flex-shrink: 0;
}
.resident-message-error button:hover { background: rgba(224, 160, 132, 0.28); }
/* Version 3, Phase 8a — the small "ⓘ" toggle in the heading, and the
   popup it shows/hides. Plain document flow, not absolutely positioned —
   consistent with the rest of this small companion card. */
.resident-usage-button {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  background: transparent;
  border: 1px solid rgba(127, 216, 196, 0.4);
  color: var(--teal-bright);
  border-radius: 50%;
  width: 22px;
  height: 22px;
  line-height: 1;
  cursor: pointer;
  flex-shrink: 0;
}
.resident-usage-button:hover { background: rgba(127, 216, 196, 0.12); }
.resident-usage-popup {
  font-size: var(--text-xs);
  color: var(--paper-dark);
  background: rgba(255, 255, 255, 0.05);
  border-radius: var(--radius-sm);
  padding: 8px 10px;
  margin: 0 0 var(--space-2);
  line-height: 1.4;
}
.resident-conversation-input-row {
  display: flex;
  align-items: flex-end;
  gap: 8px;
}
.resident-conversation-input-row textarea {
  flex: 1;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.15);
  border-radius: 18px;
  padding: 10px 16px;
  color: var(--paper);
  font-family: var(--font-body);
  font-size: var(--text-sm);
  line-height: 1.5;
  resize: none;
  /* Version 3, Phase 14 — grows with the draft (see the JS side's own
     auto-resize on input), capped at ~4 visible lines with its own
     scrollbar beyond that — the same em-based, 4-line cap the message
     bubbles above used to carry individually; see this section's own
     top comment for why it moved here instead. */
  max-height: calc(1.5em * 4 + 20px); /* + the row's own vertical padding */
  overflow-y: auto;
}
.resident-conversation-input-row textarea:focus { outline: none; border-color: var(--teal-bright); }
.resident-conversation-input-row button {
  font-family: var(--font-body);
  font-weight: 600;
  background: var(--brass);
  color: var(--wood-dark);
  border: none;
  border-radius: 999px;
  padding: 10px 20px;
  cursor: pointer;
}
.resident-conversation-input-row button:disabled { opacity: 0.5; cursor: default; }
