/* ═════════════════════════════════════════════════════════════════════════════
   Casey Office — the design system.

   The application's ONLY stylesheet. No build step, no framework: the tokens
   below are the whole theme, and every rule after them reads from these.

   ── THE RULE THAT ORGANISES THE COLOUR ──────────────────────────────────────

   COLOUR THAT MOVES IS STATE. COLOUR THAT SITS IS IDENTITY.

   The previous palette had six status hues and eight department hues on one
   screen, plus the accent: fifteen colours, and two of the pairs collided —
   "paid" was the same orange as the accent, "conversion" the same green as
   "done". You could not tell whether a colour was telling you WHO somebody is or
   WHAT is happening to them.

   Now there are four state colours and they mean one thing each:

     grey    nothing is happening
     orange  something is happening NOW — the brand colour is the live colour,
             so the one thing moving on screen is the one thing you should look
             at. "thinking" is the same orange, pulsing; it was never a
             different fact from "working".
     green   finished well
     red     needs you

   Queued stopped being a colour at all. It is a count, and it is drawn as one.

   Department colour is a FAMILY: eight hues held at the same low chroma and
   similar lightness, so they read as labels rather than alerts. They appear only
   where identity is the question — the dot beside a name, the portrait edge, the
   floor legend — and never as a fill, a chip or a piece of text.

   ── THE REST OF THE LANGUAGE ────────────────────────────────────────────────

   Surfaces are a warm near-black ramp; depth is the step between them, so panels
   need no outer shadow. Structure is a 1px INSET hairline rather than a border,
   so hover and selection thicken a line without moving a pixel of layout.
   Shadows are hard, unblurred, and reserved for things that genuinely float:
   the overlay, the drawer, a toast. Corners are nearly square — 3px — because
   the art beside them is sampled.

   Press Start 2P is the BRAND face. It is legible at 14px and hopeless at 9, so
   it now appears in exactly two places: the wordmark and room titles. Section
   labels, which carry real information, moved to Inter in upper case with
   letter-spacing — they read at a glance and survive a phone.

   ── THEME ───────────────────────────────────────────────────────────────────

   Dark is the default and the design's home. Light is a full second palette,
   not an inversion: it follows the system preference unless the founder has
   chosen, and their choice is stamped on <html data-theme>. Every text colour in
   both themes clears WCAG AA (4.5:1) against every surface it is used on, and
   every department hue clears 3:1 — measured, not estimated.
   ═════════════════════════════════════════════════════════════════════════════ */

:root {
  /* ── surfaces: a warm ink ramp ─────────────────────────────────────────── */
  --o-bg:        #0A0A0C;
  --o-surface:   #121217;
  --o-panel:     #191920;
  --o-raised:    #22222B;
  --o-sunken:    #070709;

  --o-line:        rgba(255, 255, 255, .07);
  --o-line-strong: rgba(255, 255, 255, .17);

  /* ── text: three levels, all AA on every surface above ─────────────────── */
  --o-text:   #F5F2EC;   /* 15.6:1 on panel */
  --o-text-2: #ACA69C;   /*  7.2:1 */
  --o-text-3: #918B80;   /*  5.2:1 on panel, 4.7:1 on raised */

  /* ── Casey orange: the brand, and the colour of "now" ──────────────────── */
  --o-accent:        #F97316;
  --o-accent-bright: #FB923C;
  --o-accent-dark:   #B4530F;
  --o-accent-soft:   rgba(249, 115, 22, .12);
  --o-accent-line:   rgba(249, 115, 22, .42);
  --o-on-accent:     #0D0A07;   /* 7.0:1 — the accent is a LIGHT surface */

  /* ── state: four facts ─────────────────────────────────────────────────── */
  /* "now" is Casey orange — but the accent is a LIGHT colour, and on white it
     measures 2.8:1, under the 3:1 a meaningful mark needs. So the STATE colour
     is its own token: the accent in the dark, its darker sibling in the light.
     The button keeps the bright orange, because there the orange is the surface
     and the ink is dark. */
  --o-active:   var(--o-accent);
  --o-idle:     #918B80;
  --o-working:  var(--o-active);
  --o-thinking: var(--o-active);
  --o-queued:   #ACA69C;
  --o-done:     #5FBF8A;
  --o-blocked:  #F0796F;

  --o-good:     #5FBF8A;
  --o-bad:      #F0796F;
  --o-good-soft: rgba(95, 191, 138, .12);
  --o-bad-soft:  rgba(240, 121, 111, .12);

  /* ── identity: one muted family, never a state ─────────────────────────── */
  --o-dept-seo:        #6E93C9;
  --o-dept-content:    #8BA86B;
  --o-dept-social:     #9C87C8;
  --o-dept-paid:       #C08A5A;
  --o-dept-conversion: #5FA391;
  --o-dept-analytics:  #6A9FB5;
  --o-dept-visual:     #C2A055;
  --o-dept-retention:  #C07C7C;

  /* ── type ──────────────────────────────────────────────────────────────── */
  --o-font-display: 'Press Start 2P', monospace;
  --o-font-ui: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
  --o-font-mono: 'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, monospace;

  /* ── space: one scale, used everywhere ─────────────────────────────────── */
  --o-s1: 4px;
  --o-s2: 6px;
  --o-s3: 8px;
  --o-s4: 12px;
  --o-s5: 16px;
  --o-s6: 20px;
  --o-s7: 28px;

  --o-radius:    3px;
  --o-radius-sm: 2px;

  --o-hair:        inset 0 0 0 1px var(--o-line);
  --o-hair-strong: inset 0 0 0 1px var(--o-line-strong);
  --o-shadow:      4px 4px 0 rgba(0, 0, 0, .5);
  --o-shadow-sm:   2px 2px 0 rgba(0, 0, 0, .38);

  --o-step: 90ms;

  /* How much of the window the on-screen keyboard is covering. app.js measures
     it through visualViewport; declared here so the rules that read it work on
     a browser where nothing ever sets it. */
  --o-kb: 0px;

  --o-rail: 58px;
  --o-cc: 400px;
  --o-strip: 94px;
  --o-titlebar: 40px;

  color-scheme: dark;
}

/* ── light ─────────────────────────────────────────────────────────────────
   Paper, not inverted ink: the surfaces get warmer as they rise rather than
   lighter, the accent darkens for text (orange on white is 2.9:1 and unusable)
   while staying itself as a surface, and the department family is re-mixed at
   the same relationships. Follows the system unless a choice was made. */
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) { color-scheme: light; }
}
:root[data-theme="light"] { color-scheme: light; }

@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) {
    --o-bg:        #F2EFE9;
    --o-surface:   #F7F5F1;
    --o-panel:     #FFFFFF;
    --o-raised:    #EFEBE4;
    --o-sunken:    #EAE6DE;

    --o-line:        rgba(20, 16, 11, .10);
    --o-line-strong: rgba(20, 16, 11, .22);

    --o-text:   #1A181C;
    --o-text-2: #565049;
    --o-text-3: #6C665E;

    --o-accent-bright: #B4530F;
    --o-accent-soft:   rgba(249, 115, 22, .14);
    --o-accent-line:   rgba(180, 83, 15, .45);
    --o-active:        #B4530F;   /* 4.6:1 on white — the accent itself is 2.8 */

    --o-idle:     #6C665E;
    --o-queued:   #565049;
    --o-done:     #1F7A4D;
    --o-blocked:  #C0392B;
    --o-good:     #1F7A4D;
    --o-bad:      #C0392B;
    --o-good-soft: rgba(31, 122, 77, .12);
    --o-bad-soft:  rgba(192, 57, 43, .12);

    --o-dept-seo:        #3C6FB0;
    --o-dept-content:    #5B7A3C;
    --o-dept-social:     #7355A8;
    --o-dept-paid:       #A2601F;
    --o-dept-conversion: #2E7D6B;
    --o-dept-analytics:  #36718C;
    --o-dept-visual:     #8A6A16;
    --o-dept-retention:  #A54A4A;

    --o-shadow:    4px 4px 0 rgba(26, 24, 28, .14);
    --o-shadow-sm: 2px 2px 0 rgba(26, 24, 28, .10);
  }
}

:root[data-theme="light"] {
  --o-bg:        #F2EFE9;
  --o-surface:   #F7F5F1;
  --o-panel:     #FFFFFF;
  --o-raised:    #EFEBE4;
  --o-sunken:    #EAE6DE;

  --o-line:        rgba(20, 16, 11, .10);
  --o-line-strong: rgba(20, 16, 11, .22);

  --o-text:   #1A181C;
  --o-text-2: #565049;
  --o-text-3: #6C665E;

  --o-accent-bright: #B4530F;
  --o-accent-soft:   rgba(249, 115, 22, .14);
  --o-accent-line:   rgba(180, 83, 15, .45);
  --o-active:        #B4530F;   /* 4.6:1 on white — the accent itself is 2.8 */

  --o-idle:     #6C665E;
  --o-queued:   #565049;
  --o-done:     #1F7A4D;
  --o-blocked:  #C0392B;
  --o-good:     #1F7A4D;
  --o-bad:      #C0392B;
  --o-good-soft: rgba(31, 122, 77, .12);
  --o-bad-soft:  rgba(192, 57, 43, .12);

  --o-dept-seo:        #3C6FB0;
  --o-dept-content:    #5B7A3C;
  --o-dept-social:     #7355A8;
  --o-dept-paid:       #A2601F;
  --o-dept-conversion: #2E7D6B;
  --o-dept-analytics:  #36718C;
  --o-dept-visual:     #8A6A16;
  --o-dept-retention:  #A54A4A;

  --o-shadow:    4px 4px 0 rgba(26, 24, 28, .14);
  --o-shadow-sm: 2px 2px 0 rgba(26, 24, 28, .10);
}

/* ── reset ───────────────────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }
html, body { height: 100%; }
body {
  margin: 0;
  background: var(--o-bg);
  color: var(--o-text);
  font-family: var(--o-font-ui);
  font-size: 13px;
  line-height: 1.45;
  overflow: hidden;
  -webkit-font-smoothing: antialiased;
}
button, input, select, textarea { font: inherit; color: inherit; }
button { cursor: pointer; }
a { color: var(--o-accent-bright); text-decoration: none; }
a:hover { text-decoration: underline; }
canvas, img.pixel { image-rendering: pixelated; }
:focus-visible { outline: 1px solid var(--o-accent); outline-offset: 2px; }

::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: var(--o-sunken); }
::-webkit-scrollbar-thumb { background: #34343E; }
::-webkit-scrollbar-thumb:hover { background: #44444F; }

/* ── shell ───────────────────────────────────────────────────────────────────
   Four fixed regions. The page never scrolls; each region owns its overflow, so
   the floor, the rooms and the crew can never push each other off screen. */
/* 100dvh, not 100vh. On iOS Safari the layout viewport extends UNDER the
   browser toolbar, so a 100vh column puts its last row — the agent strip —
   partly behind the chrome, and anything positioned against the viewport bottom
   lands lower than the strip it was supposed to sit above. */
.o-app { display: flex; flex-direction: column; height: 100vh; height: 100dvh; }

.o-titlebar {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-shrink: 0;
  height: var(--o-titlebar);
  padding: 0 12px;
  background: var(--o-surface);
  border-bottom: 1px solid var(--o-line-strong);
  user-select: none;
}
.o-mark { width: 22px; height: 22px; display: block; image-rendering: pixelated; }
.o-wordmark {
  font-family: var(--o-font-display);
  font-size: 11px;
  letter-spacing: .04em;
  white-space: nowrap;
}
.o-wordmark b { color: var(--o-accent); font-weight: 400; }
/* The theme control sits with the wordmark, not in a settings page: it is a
   thing you reach for when the light in the room changes. */
.o-theme { min-width: 52px; justify-content: center; text-transform: lowercase; }
.o-titlebar-meta {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-left: auto;
  font-family: var(--o-font-mono);
  font-size: 11px;
  color: var(--o-text-3);
  white-space: nowrap;
}
.o-titlebar-meta b { color: var(--o-text-2); font-weight: 500; }
.o-live {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 2px 8px;
  background: var(--o-panel);
  box-shadow: var(--o-hair);
  font-size: 11px;
}
.o-live::before {
  content: '';
  width: 7px; height: 7px;
  background: var(--o-done);
}
.o-live[data-live='off']::before { background: var(--o-blocked); }
.o-live[data-live='wait']::before { background: var(--o-working); }

.o-mid { position: relative; flex: 1; display: flex; min-height: 0; }

/* ── room rail ───────────────────────────────────────────────────────────────
   Rooms, not pages. Icons with a hover label, because a 58px rail cannot hold a
   word and a floor plan should not be behind a hamburger. */
.o-rail {
  width: var(--o-rail);
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: 8px 0;
  background: var(--o-surface);
  border-right: 1px solid var(--o-line-strong);
  overflow-y: auto;
  overflow-x: hidden;
}
/* Eleven unlabelled glyphs is a memory test. Where the window is wide enough,
   the rail says the words; below that it is icons with a hover label, and on a
   phone it stays narrow because the room needs the width more than the rail
   does. */
.o-rail-icon { display: block; line-height: 1; }
.o-rail-label { display: none; }
.o-strip-sep {
  flex-shrink: 0;
  width: 1px;
  align-self: stretch;
  margin: 4px 2px;
  background: var(--o-line-strong);
}

.o-rail-btn {
  position: relative;
  width: 44px; height: 40px;
  display: grid;
  place-items: center;
  border: none;
  background: transparent;
  color: var(--o-text-2);
  font-size: 16px;
  line-height: 1;
  transition: background var(--o-step) steps(2), color var(--o-step) steps(2);
}
.o-rail-btn:hover { background: var(--o-panel); color: var(--o-text); }
.o-rail-btn.active {
  background: var(--o-accent-soft);
  color: var(--o-accent-bright);
  box-shadow: inset 2px 0 0 var(--o-accent);
}
.o-rail-btn[data-label]::after {
  content: attr(data-label);
  position: absolute;
  left: calc(100% + 6px);
  top: 50%;
  transform: translateY(-50%);
  padding: 3px 8px 2px;
  background: var(--o-raised);
  box-shadow: var(--o-hair-strong);
  color: var(--o-text);
  font-family: var(--o-font-ui);
  font-size: 11px;
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  z-index: 40;
  transition: opacity var(--o-step) ease-out 320ms;
}
.o-rail-btn:hover::after { opacity: 1; }
.o-rail-badge {
  position: absolute;
  top: 3px; right: 4px;
  min-width: 15px;
  padding: 0 3px;
  background: var(--o-accent);
  color: var(--o-on-accent);
  font-family: var(--o-font-mono);
  font-size: 9px;
  line-height: 14px;
  text-align: center;
}
.o-rail-sep { width: 26px; height: 1px; background: var(--o-line); margin: 7px 0; flex-shrink: 0; }
.o-rail-spacer { flex: 1; }

/* ── room host ───────────────────────────────────────────────────────────── */
.o-room {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  background: var(--o-bg);
  overflow: hidden;
}
.o-room-hd {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-shrink: 0;
  padding: 11px 16px;
  border-bottom: 1px solid var(--o-line);
}
/* One of the two places the pixel face is allowed: big enough to be read, and
   the thing that says which room you are in. */
.o-room-title {
  font-family: var(--o-font-display);
  font-size: 14px;
  line-height: 22px;
  letter-spacing: .02em;
  white-space: nowrap;      /* the pixel face is wide: "The floor" wrapped */
  flex-shrink: 0;
}
.o-room-sub {
  font-size: 12px;
  color: var(--o-text-3);
  min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.o-room-actions { margin-left: auto; display: flex; gap: 6px; flex-shrink: 0; }
.o-room-body {
  flex: 1;
  min-width: 0;          /* without this, one long line of agent output sets the
                            width of the room and the page scrolls sideways */
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  padding: 16px;
}
.o-room-body--bleed { padding: 0; overflow: hidden; position: relative; }

/* ── primitives ──────────────────────────────────────────────────────────── */
.o-panel {
  background: var(--o-panel);
  box-shadow: var(--o-hair);
  padding: var(--o-s4) var(--o-s5);
  border-radius: var(--o-radius);
}
.o-panel--sunken { background: var(--o-sunken); }
.o-panel-hd {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 4px 10px;
  margin: calc(var(--o-s4) * -1) calc(var(--o-s5) * -1) var(--o-s4);
  padding: var(--o-s3) var(--o-s5) 7px;
  background: var(--o-raised);
  box-shadow: inset 0 -1px 0 var(--o-line);
  border-radius: var(--o-radius) var(--o-radius) 0 0;
}
/* The section label carries real information — "3 briefs from Casey", "2 change
   it" — and it was set in an 8px pixel face, which is a decoration pretending to
   be a word. Inter, upper case, letter-spaced: same rhythm, legible at arm's
   length on a phone. */
.o-label {
  font-family: var(--o-font-ui);
  font-size: 11px;
  font-weight: 600;
  line-height: 15px;
  letter-spacing: .07em;
  color: var(--o-text-3);
  text-transform: uppercase;
  min-width: 0;
  overflow-wrap: anywhere;
}
.o-panel-hd .o-label { color: var(--o-text-2); }
.o-panel-hd-meta { margin-left: auto; font-family: var(--o-font-mono); font-size: 11px; color: var(--o-text-3); }

.o-grid { display: grid; gap: 12px; }
.o-grid--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.o-grid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.o-grid--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
/* `min(Xpx, 100%)`, not a bare minimum: auto-fill honours the minimum even when
   the container is narrower than it, so a 320px floor overflowed a 303px phone
   column by exactly the difference. */
.o-grid--auto { grid-template-columns: repeat(auto-fill, minmax(min(240px, 100%), 1fr)); }
.o-grid--wide { grid-template-columns: repeat(auto-fill, minmax(min(320px, 100%), 1fr)); }
.o-stack { display: flex; flex-direction: column; gap: 12px; }
.o-split { display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: 12px; align-items: start; }

.o-kpi { background: var(--o-panel); box-shadow: var(--o-hair); border-radius: var(--o-radius); padding: var(--o-s4) var(--o-s5) 13px; }
.o-kpi-label { font-family: var(--o-font-ui); font-size: 11px; font-weight: 600; letter-spacing: .07em; color: var(--o-text-3); text-transform: uppercase; }
.o-kpi-value {
  margin-top: 7px;
  font-family: var(--o-font-mono);
  font-size: 25px;
  line-height: 30px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--o-text);
}
.o-kpi-sub { margin-top: 3px; font-size: 11px; color: var(--o-text-3); }
.o-kpi-sub.up { color: var(--o-done); }
.o-kpi-sub.down { color: var(--o-blocked); }

.o-row {
  display: block;
  width: 100%;
  text-align: left;
  padding: 9px 11px;
  background: var(--o-panel);
  box-shadow: var(--o-hair);
  border: none;
  border-radius: var(--o-radius);
  margin-bottom: var(--o-s2);
}
.o-row:last-child { margin-bottom: 0; }
.o-row--flat { background: transparent; box-shadow: inset 0 -1px 0 var(--o-line); border-radius: 0; margin-bottom: 0; }
button.o-row { transition: background var(--o-step) steps(2); }
button.o-row:hover { background: var(--o-raised); }
.o-row-hd { display: flex; align-items: baseline; gap: 9px; }
.o-row-title { flex: 1; min-width: 0; font-size: 13px; color: var(--o-text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.o-row-meta { flex-shrink: 0; font-family: var(--o-font-mono); font-size: 11px; color: var(--o-text-3); }
.o-row-body { margin-top: 4px; font-size: 12px; line-height: 17px; color: var(--o-text-2); overflow-wrap: anywhere; }
.o-row-actions { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }

.o-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--o-s2);
  flex-shrink: 0;
  padding: 2px var(--o-s3);
  background: var(--o-panel);
  box-shadow: var(--o-hair-strong);
  border-radius: 999px;
  font-size: 11px;
  line-height: 16px;
  color: var(--o-text);
  white-space: nowrap;
}
/* The pip is the state. Four colours, and the only one that moves is the one
   that means "now" — so on a screen with twenty-five employees, the eye lands on
   the two that are working. */
.o-chip::before {
  content: '';
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--chip, var(--o-idle));
}
.o-chip--plain::before { display: none; }
.o-chip[data-status='running'], .o-chip[data-status='working'],
.o-chip[data-status='thinking'] {
  --chip: var(--o-active);
  color: var(--o-text);
  box-shadow: inset 0 0 0 1px var(--o-accent-line);
  background: var(--o-accent-soft);
}
.o-chip[data-status='running']::before, .o-chip[data-status='working']::before,
.o-chip[data-status='thinking']::before { animation: o-pulse 1.5s ease-in-out infinite; }
.o-chip[data-status='queued'] { --chip: var(--o-queued); box-shadow: var(--o-hair-strong); }
.o-chip[data-status='done'] { --chip: var(--o-done); box-shadow: inset 0 0 0 1px var(--o-done); }
.o-chip[data-status='error'], .o-chip[data-status='blocked'] {
  --chip: var(--o-blocked);
  box-shadow: inset 0 0 0 1px var(--o-blocked);
  background: var(--o-bad-soft);
}
.o-chip[data-status='idle'] { box-shadow: var(--o-hair-strong); color: var(--o-text-2); }
.o-chip[data-status='accent'] { --chip: var(--o-accent); }

@keyframes o-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50%      { opacity: .45; transform: scale(.82); }
}

.o-tag {
  display: inline-block;
  padding: 1px 6px;
  border-radius: var(--o-radius-sm);
  background: var(--o-raised);
  box-shadow: var(--o-hair);
  font-family: var(--o-font-mono);
  font-size: 10px;
  line-height: 15px;
  color: var(--o-text-2);
}
.o-tag--accent { background: var(--o-accent); color: var(--o-on-accent); box-shadow: none; }
/* Identity, not state: a small square (states are round), in the muted family. */
.o-dept-dot { width: 7px; height: 7px; flex-shrink: 0; display: inline-block; border-radius: 1px; }

.o-btn {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  flex-shrink: 0;
  height: 28px;
  padding: 0 11px;
  border: none;
  background: var(--o-raised);
  box-shadow: var(--o-hair-strong);
  border-radius: var(--o-radius);
  color: var(--o-text);
  font-size: 12px;
  font-weight: 500;
  line-height: 1;
  white-space: nowrap;
  transition: transform var(--o-step) steps(2), background var(--o-step) steps(2);
}
.o-btn:hover { background: color-mix(in srgb, var(--o-raised) 80%, var(--o-text) 20%); }
.o-btn:active { transform: translateY(1px); }
.o-btn--primary { background: var(--o-accent); color: var(--o-on-accent); font-weight: 600; box-shadow: inset 0 0 0 1px var(--o-accent-dark); }
.o-btn--primary:hover { background: var(--o-accent-bright); }
.o-btn--danger { background: var(--o-bad); color: #FFF9F8; font-weight: 600; box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .25); }
.o-btn--ghost { background: transparent; box-shadow: var(--o-hair); color: var(--o-text-2); }
.o-btn--ghost:hover { background: var(--o-raised); color: var(--o-text); }
.o-btn--sm { height: 24px; padding: 0 var(--o-s3); font-size: 11px; }
.o-btn:disabled { background: var(--o-panel); color: var(--o-text-3); box-shadow: var(--o-hair); transform: none; cursor: default; }

.o-tabs { display: flex; flex-wrap: wrap; gap: 4px; }
.o-tab {
  flex: 0 1 auto;
  padding: 4px 10px;
  border: none;
  border-radius: var(--o-radius);
  background: var(--o-panel);
  box-shadow: var(--o-hair);
  color: var(--o-text-2);
  font-size: 12px;
  white-space: nowrap;
}
.o-tab:hover { color: var(--o-text); }
.o-tab.active { background: var(--o-accent); color: var(--o-on-accent); box-shadow: none; }
.o-tab-count { margin-left: 5px; font-family: var(--o-font-mono); font-size: 10px; opacity: .85; }

.o-input, .o-select, .o-textarea {
  width: 100%;
  padding: 6px 9px;
  background: var(--o-sunken);
  box-shadow: var(--o-hair);
  border: none;
  border-radius: var(--o-radius-sm);   /* iOS rounds its own way; this is ours */
  color: var(--o-text);
  font-size: 12px;
  -webkit-appearance: none;
  appearance: none;
}
.o-textarea { resize: vertical; min-height: 76px; line-height: 18px; font-family: var(--o-font-ui); }
.o-input:focus, .o-select:focus, .o-textarea:focus { outline: none; box-shadow: inset 0 0 0 2px var(--o-accent-dark); }
.o-field { display: flex; flex-direction: column; gap: 4px; }
/* A short form — connecting a server, mostly. Two columns where there is room,
   one where there is not, and never a field narrower than a URL deserves. */
.o-form { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr)); gap: 10px; margin: 10px 0; }
.o-field-label { font-size: 11px; color: var(--o-text-3); }
.o-toolbar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 12px; }

.o-bar { height: 6px; background: var(--o-sunken); box-shadow: var(--o-hair); overflow: hidden; }
.o-bar > i { display: block; height: 100%; background: var(--o-accent); }
.o-hbar { display: grid; grid-template-columns: 110px minmax(0, 1fr) 74px; gap: 9px; align-items: center; margin-bottom: 6px; }
.o-hbar-label { font-size: 11px; color: var(--o-text-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.o-hbar-track { height: 14px; background: var(--o-sunken); box-shadow: var(--o-hair); }
.o-hbar-fill { height: 100%; background: var(--o-accent); }
.o-hbar-val { font-family: var(--o-font-mono); font-size: 11px; color: var(--o-text-2); text-align: right; font-variant-numeric: tabular-nums; }

.o-table { width: 100%; border-collapse: collapse; font-size: 12px; }
.o-table th {
  text-align: left;
  padding: 7px 10px;
  background: var(--o-raised);
  font-family: var(--o-font-display);
  font-size: 7px;
  letter-spacing: .05em;
  color: var(--o-text-3);
  text-transform: uppercase;
  font-weight: 400;
}
.o-table td { padding: 8px 10px; border-top: 1px solid var(--o-line); color: var(--o-text-2); vertical-align: top; }
.o-table tr:hover td { background: var(--o-panel); }
.o-table td b { color: var(--o-text); font-weight: 600; }

.o-empty {
  padding: 22px 14px;
  background: var(--o-panel);
  box-shadow: var(--o-hair);
  color: var(--o-text-3);
  font-size: 13px;
  line-height: 19px;
}
.o-empty b { color: var(--o-text-2); font-weight: 600; }
.o-note { font-size: 11px; line-height: 16px; color: var(--o-text-3); }
.o-mono { font-family: var(--o-font-mono); font-variant-numeric: tabular-nums; }
.o-scroll { overflow-y: auto; }

.o-check { display: flex; align-items: flex-start; gap: 9px; padding: 7px 2px; border-bottom: 1px solid var(--o-line); }
.o-check:last-child { border-bottom: none; }
.o-check input { margin: 2px 0 0; accent-color: var(--o-accent); width: 14px; height: 14px; }
.o-check-text { flex: 1; min-width: 0; font-size: 12px; line-height: 18px; color: var(--o-text-2); }
.o-check.done .o-check-text { color: var(--o-text-3); text-decoration: line-through; }

/* Agent output and any markdown an agent produced. Monospace, because it is a
   machine's writing and the reader is checking it, not enjoying it. */
.o-stream {
  margin: 0;
  max-width: 100%;
  font-family: var(--o-font-mono);
  font-size: 12px;
  line-height: 18px;
  color: var(--o-text-2);
  white-space: pre-wrap;
  /* `anywhere`, not `break-word`: a markdown table rule (|----|----|) and a
     gid://shopify/Product/… are single unbreakable tokens wider than a phone. */
  overflow-wrap: anywhere;
}
/* ── reading ─────────────────────────────────────────────────────────────────
   The same rules everywhere prose appears: Casey's side of the conversation, a
   brief, and a 900-word blog post waiting for approval. Those are the longest
   things in the product and they were set at 12.5/19 in a column as wide as the
   window — which is why drafts were skimmed rather than read.

   65 characters, 13px, 21px leading, and headings that are actually headings:
   the pixel face at 9px was decoration on top of somebody's article. */
.o-md {
  font-size: 13px;
  line-height: 21px;
  color: var(--o-text-2);
  overflow-wrap: anywhere;
  max-width: 65ch;
}
.o-md h1, .o-md h2, .o-md h3 {
  font-family: var(--o-font-ui);
  color: var(--o-text);
  letter-spacing: -.005em;
  margin: var(--o-s6) 0 var(--o-s3);
}
.o-md h1 { font-size: 17px; line-height: 24px; font-weight: 650; }
.o-md h2 { font-size: 15px; line-height: 22px; font-weight: 650; }
.o-md h3 { font-size: 13px; line-height: 20px; font-weight: 650; text-transform: uppercase; letter-spacing: .06em; color: var(--o-text-2); }
.o-md > :first-child { margin-top: 0; }
.o-md p { margin: 0 0 var(--o-s4); }
.o-md ul, .o-md ol { margin: 0 0 var(--o-s4); padding-left: var(--o-s6); }
.o-md li { margin-bottom: var(--o-s2); }
.o-md code { font-family: var(--o-font-mono); font-size: 12px; background: var(--o-sunken); border-radius: var(--o-radius-sm); padding: 1px 5px; }
.o-md pre {
  background: var(--o-sunken);
  padding: var(--o-s4);
  border-radius: var(--o-radius);
  max-width: 100%;
  overflow-x: auto;
  box-shadow: var(--o-hair);
  line-height: 18px;
}
.o-md pre code { background: none; padding: 0; }
.o-md strong { color: var(--o-text); font-weight: 650; }
.o-md a { color: var(--o-accent-bright); text-decoration: underline; text-underline-offset: 2px; }
.o-md hr { border: none; border-top: 1px solid var(--o-line); margin: var(--o-s6) 0; }

/* A staged draft is read, not scanned: it gets the full column. */
.o-md--full { max-width: none; }

/* ── the score ───────────────────────────────────────────────────────────────
   Every staged item carries a number from the editor. It belongs where the eye
   lands first and it has to be readable across a tray: the number itself, on the
   colour of the verdict. */
.o-score {
  display: inline-flex;
  align-items: baseline;
  gap: 3px;
  flex-shrink: 0;
  padding: 2px var(--o-s3);
  border-radius: 999px;
  font-family: var(--o-font-mono);
  font-size: 12px;
  font-weight: 500;
  line-height: 17px;
  background: var(--o-raised);
  box-shadow: var(--o-hair-strong);
  color: var(--o-text-2);
}
.o-score b { font-size: 13px; color: var(--o-text); font-weight: 600; }
.o-score-of { font-size: 10px; color: var(--o-text-3); }
.o-score[data-verdict='pass'] { background: var(--o-good-soft); box-shadow: inset 0 0 0 1px var(--o-good); }
.o-score[data-verdict='pass'] b { color: var(--o-good); }
.o-score[data-verdict='below'] { background: var(--o-bad-soft); box-shadow: inset 0 0 0 1px var(--o-blocked); }
.o-score[data-verdict='below'] b { color: var(--o-blocked); }

/* A before/after: what is live, then what the agent wrote. The proposed side is
   the one being decided on, so it is the one that is lit. */
.o-diff { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--o-s4); }
.o-diff-side { min-width: 0; }
.o-diff-side .o-md { max-width: none; }
.o-diff-now .o-panel { opacity: .72; }
.o-diff-next .o-panel { box-shadow: inset 0 0 0 1px var(--o-accent-line); }
@media (max-width: 760px) { .o-diff { grid-template-columns: minmax(0, 1fr); } }

/* ── the floor ───────────────────────────────────────────────────────────── */
.o-floor { position: absolute; inset: 0; background: var(--o-sunken); overflow: hidden; }
#office-canvas {
  display: block;
  width: 100%;
  height: 100%;
  cursor: grab;
  /* The canvas handles its own pan and pinch; without this the browser scrolls
     the page underneath the gesture and the floor jitters. */
  touch-action: none;
  -webkit-user-select: none;
  user-select: none;
}
.o-floor-overlay {
  position: absolute;
  left: 12px; bottom: 12px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
  max-width: min(520px, calc(100% - 24px));
  pointer-events: none;
}

/* THE STATE OF THE OFFICE, over the office. Three lines at most, each one a
   sentence with something you can press in it — who is working, what is waiting
   for you, what is stuck. It sits on the floor rather than above it because the
   floor IS the answer; this is the caption. */
.o-floor-status {
  display: flex;
  flex-direction: column;
  gap: var(--o-s2);
  padding: var(--o-s3) var(--o-s4);
  background: color-mix(in srgb, var(--o-surface) 92%, transparent);
  backdrop-filter: blur(6px);
  box-shadow: var(--o-hair-strong);
  border-radius: var(--o-radius);
  pointer-events: auto;
  max-width: 100%;
}
.o-floor-status:empty { display: none; }
.o-floor-line { display: flex; align-items: baseline; gap: var(--o-s3); }
.o-floor-line-text { font-size: 12px; line-height: 17px; color: var(--o-text-2); min-width: 0; }
.o-floor-who {
  border: none;
  background: none;
  padding: 0;
  color: var(--o-accent-bright);
  font: inherit;
  cursor: pointer;
}
.o-floor-who:hover { text-decoration: underline; }

.o-floor-key {
  display: flex;
  flex-wrap: wrap;          /* eight department names do not fit one phone row */
  gap: 6px 9px;
  padding: 5px 9px;
  background: color-mix(in srgb, var(--o-surface) 92%, transparent);
  border-radius: var(--o-radius);
  box-shadow: var(--o-hair);
  font-size: 11px;
  color: var(--o-text-3);
  pointer-events: auto;
}
.o-floor-key span { display: inline-flex; align-items: center; gap: 5px; }
.o-floor-key i { width: 7px; height: 7px; display: block; border-radius: 1px; }
.o-floor-hint {
  position: absolute;
  right: 12px; top: 12px;
  padding: 4px 9px;
  background: rgba(13, 13, 16, .88);
  box-shadow: var(--o-hair);
  font-size: 11px;
  color: var(--o-text-3);
  pointer-events: none;
}

/* ── command centre ──────────────────────────────────────────────────────── */
.o-cc {
  width: var(--o-cc);
  min-width: 0;
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  min-height: 0;
  background: var(--o-surface);
  border-left: 1px solid var(--o-line-strong);
}
.o-cc-hd {
  display: flex;
  gap: 9px;
  align-items: center;
  flex-shrink: 0;
  padding: 8px 10px;
  border-bottom: 1px solid var(--o-line);
}
/* ── the portrait ────────────────────────────────────────────────────────────
   Twenty-five people drawn from the same 18×28 sprite sheet, and the founder has
   to tell them apart in a strip on a phone. The face does most of it; the rest
   is the frame.

   Each portrait sits on a wash of its DEPARTMENT colour — a few percent, enough
   that Sonja and Marit read as "search" and "content" before you have read
   either name, and nowhere near enough to be mistaken for a state. When somebody
   is working the frame lights in the active colour, so the same portrait carries
   identity and state without a second badge. */
.o-portrait {
  --p: var(--o-idle);
  flex-shrink: 0;
  overflow: hidden;
  display: flex;
  align-items: flex-start;          /* crop feet, never the face */
  justify-content: center;
  border-radius: var(--o-radius-sm);
  background:
    linear-gradient(to bottom,
      color-mix(in srgb, var(--p) 22%, var(--o-panel)),
      color-mix(in srgb, var(--p) 7%, var(--o-panel)));
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--p) 38%, transparent);
}
.o-portrait[data-dept='seo']        { --p: var(--o-dept-seo); }
.o-portrait[data-dept='content']    { --p: var(--o-dept-content); }
.o-portrait[data-dept='social']     { --p: var(--o-dept-social); }
.o-portrait[data-dept='paid']       { --p: var(--o-dept-paid); }
.o-portrait[data-dept='conversion'] { --p: var(--o-dept-conversion); }
.o-portrait[data-dept='analytics']  { --p: var(--o-dept-analytics); }
.o-portrait[data-dept='visual']     { --p: var(--o-dept-visual); }
.o-portrait[data-dept='retention']  { --p: var(--o-dept-retention); }
.o-portrait[data-dept='office']     { --p: var(--o-accent); }

.o-card[data-state='running'] .o-portrait,
.o-cc-hd .o-portrait[data-state='running'] {
  box-shadow: inset 0 0 0 1px var(--o-active), 0 0 0 1px var(--o-active);
}
.o-portrait canvas { display: block; image-rendering: pixelated; }
.o-portrait--lg { width: 38px; height: 48px; }
.o-portrait--sm { width: 30px; height: 38px; }
.o-cc-id { flex: 1; min-width: 0; }
.o-cc-name {
  font-family: var(--o-font-display);
  font-size: 9px;
  line-height: 15px;
  text-transform: uppercase;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.o-cc-role { font-size: 11px; color: var(--o-accent-bright); }
.o-cc-blurb {
  font-size: 11px;
  line-height: 15px;
  color: var(--o-text-3);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.o-cc-actions { display: flex; flex-direction: column; gap: 5px; align-items: flex-end; flex-shrink: 0; }
.o-cc-close { display: none; }
.o-cc-tabs { display: flex; gap: 4px; padding: 7px 10px; flex-shrink: 0; border-bottom: 1px solid var(--o-line); overflow-x: auto; }
.o-cc-body { flex: 1; min-width: 0; min-height: 0; overflow-y: auto; overflow-x: hidden; padding: 11px; }
/* The message queue. The textarea grows, the send column does not — a composer
   whose button moves as you type is a composer people mis-tap. */
.o-cc-foot {
  flex-shrink: 0;
  display: flex;
  gap: 7px;
  align-items: stretch;
  padding: 8px 10px;
  border-top: 1px solid var(--o-line);
  background: var(--o-surface);
}
.o-cc-foot .o-textarea { flex: 1; min-width: 0; resize: none; }

/* ── agent strip ─────────────────────────────────────────────────────────────
   One size for every card, Casey included. A strip whose cards differ in size
   reads as broken before it reads as hierarchy; Casey is marked by SURFACE — an
   accent wash and an accent hairline — not by being bigger. */
.o-strip {
  display: flex;
  gap: 8px;
  align-items: center;
  flex-shrink: 0;
  height: var(--o-strip);
  padding: 8px 10px;
  background: var(--o-surface);
  border-top: 1px solid var(--o-line-strong);
  overflow-x: auto;
  overflow-y: hidden;
}
.o-card {
  display: flex;
  gap: 7px;
  flex-shrink: 0;
  width: 198px;
  height: 72px;
  padding: 6px 7px;
  border: none;
  border-radius: var(--o-radius);
  background: var(--o-panel);
  box-shadow: var(--o-hair);
  text-align: left;
  transition: transform var(--o-step) steps(2, end), box-shadow var(--o-step) steps(2, end);
}
.o-card:hover { transform: translateY(-1px); box-shadow: var(--o-hair-strong), var(--o-shadow-sm); }
.o-card.selected { box-shadow: var(--o-hair), 0 0 0 2px var(--o-accent); }
.o-card.boss { background: var(--o-accent-soft); box-shadow: inset 0 0 0 1px var(--o-accent); }
.o-card.boss.selected { box-shadow: inset 0 0 0 1px var(--o-accent), 0 0 0 2px var(--o-accent); }
/* An employee who is working is lit from the side. This is the only motion in
   the strip, so the two people actually running are findable in a row of
   twenty-five without reading a word. */
.o-card[data-state='running'] { box-shadow: var(--o-hair), inset 3px 0 0 var(--o-active); }
.o-card[data-state='running'].selected { box-shadow: inset 3px 0 0 var(--o-active), 0 0 0 2px var(--o-accent); }
.o-card[data-state='error'] { box-shadow: var(--o-hair), inset 3px 0 0 var(--o-bad); }
.o-card[data-state='done'] { box-shadow: var(--o-hair), inset 3px 0 0 var(--o-good); }
.o-card[data-state='queued'] { box-shadow: var(--o-hair), inset 3px 0 0 var(--o-text-3); }
.o-card-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.o-card-name {
  display: flex;
  align-items: center;
  gap: 5px;
  font-family: var(--o-font-display);
  font-size: 8px;
  line-height: 12px;
  text-transform: uppercase;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.o-card-boss { padding: 1px 4px 0; background: var(--o-accent); color: var(--o-on-accent); font-size: 7px; line-height: 11px; }
.o-card-role { font-size: 11px; line-height: 14px; color: var(--o-text-3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.o-card-foot { margin-top: auto; display: flex; align-items: center; gap: 6px; }
.o-card-note {
  margin-left: auto;
  padding: 0 5px;
  background: var(--o-queued);
  color: var(--o-on-accent);
  font-family: var(--o-font-mono);
  font-size: 10px;
  line-height: 15px;
}

/* ── overlay: a room that needs the whole window (a full agent run, an item
   being edited) opens over the shell rather than replacing it. ───────────── */
.o-overlay {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 32px;
  background: rgba(6, 6, 8, .72);
}
.o-overlay-panel {
  width: min(900px, 100%);
  max-height: 100%;
  display: flex;
  flex-direction: column;
  background: var(--o-surface);
  box-shadow: var(--o-hair-strong), var(--o-shadow);
}
.o-overlay-hd { display: flex; align-items: center; gap: 10px; padding: 10px 12px; border-bottom: 1px solid var(--o-line); flex-shrink: 0; }
.o-overlay-body { flex: 1; min-height: 0; overflow-y: auto; padding: 14px; }
.o-overlay-ft { display: flex; gap: 7px; justify-content: flex-end; padding: 10px 12px; border-top: 1px solid var(--o-line); flex-shrink: 0; }

/* ── responsive ──────────────────────────────────────────────────────────────
   The crew panel is the first thing to go: on a narrow screen the room content
   is what the user came for, and the selected agent is still one tap away in
   the strip. */
@media (max-width: 1280px) { :root { --o-cc: 340px; } }
@media (max-width: 1080px) {
  .o-cc { display: none; }
  /* ABSOLUTE inside .o-mid, not fixed to the viewport. Fixed positioning is
     measured against the layout viewport, which on iOS extends under Safari's
     toolbar — so `bottom: var(--o-strip)` put the drawer's last lines on top of
     the agent strip. Anchored to the row it belongs to, it cannot overlap the
     strip no matter what the browser chrome does. */
  .o-cc.open {
    display: flex;
    position: absolute;
    top: 0; right: 0; bottom: 0;
    /* Never leave a sliver of backdrop that is too thin to hit: on a phone the
       drawer simply takes everything the rail does not. */
    width: min(380px, calc(100% - var(--o-rail)));
    z-index: 50;
    box-shadow: -4px 0 0 rgba(0, 0, 0, .4);
  }
  /* A drawer that covers the room must be closable from inside it. */
  .o-cc-close { display: inline-flex !important; }
  /* The backdrop starts AFTER the rail. Covering the rail too meant a phone user
     had to dismiss the drawer before they could change rooms — two taps for one
     intention, and the first tap looked like it had done nothing. */
  .o-cc-backdrop {
    position: absolute;
    top: 0; left: var(--o-rail); right: 0; bottom: 0;
    z-index: 49;
    background: rgba(6, 6, 8, .5);
  }
}
@media (max-width: 760px) {
  .o-grid--2, .o-grid--3, .o-grid--4 { grid-template-columns: minmax(0, 1fr); }
  .o-split { grid-template-columns: minmax(0, 1fr); }
  .o-room-body { padding: 12px; }
  .o-titlebar-meta > *:not(.o-live) { display: none; }
}

/* A phone. The title bar is the failure point here: wordmark, clock, pulse and
   the crew button do not fit in 375px, and the one that was being pushed off the
   edge was the crew button — the ONLY way to reach the selected agent once the
   panel becomes a drawer. So everything optional goes, and the two controls that
   must survive are pinned against shrinking. */
@media (max-width: 620px) {
  .o-clock { display: none; }

  /* THE CHROME PROBLEM. Titlebar + room header + a 94px strip took a third of a
     390px screen before a word of content. The strip keeps its job — every
     employee, one tap away — on a third of the height: portrait, first name, and
     the state as a ring rather than a chip. Whoever is working is lit, so the
     row still answers "is anything happening" at a glance. */
  :root { --o-strip: 62px; }
  .o-strip { gap: 6px; padding: 6px 8px; }
  .o-card {
    width: 62px;
    height: 52px;
    padding: 3px;
    flex-direction: column;
    align-items: center;
    gap: 1px;
  }
  /* The portrait is drawn at 2× into a canvas with inline dimensions, so it has
     to be told to shrink — and the size it shrinks to is 1:1 with the source
     art, 18×28, which is the one scale pixel art is never mushy at. */
  .o-card .o-portrait--sm { width: 18px; height: 28px; }
  .o-card .o-portrait--sm canvas { width: 18px !important; height: 28px !important; }
  .o-card-role, .o-card-foot, .o-card-boss, .o-card .o-dept-dot { display: none; }
  .o-card-main { align-items: center; width: 100%; }
  .o-card-name {
    width: 100%;
    justify-content: center;
    font-size: 9px;
    line-height: 11px;
    letter-spacing: 0;
    text-transform: none;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  /* State is the whole card edge here, because there is no room for a chip. */
  .o-card[data-state='running'] { box-shadow: inset 0 0 0 2px var(--o-active); }
  .o-card[data-state='error']   { box-shadow: inset 0 0 0 2px var(--o-bad); }
  .o-card[data-state='done']    { box-shadow: inset 0 0 0 2px var(--o-good); }
  .o-card[data-state='queued']  { box-shadow: inset 0 0 0 2px var(--o-text-3); }
  .o-titlebar { gap: 8px; padding: 0 8px; }
  .o-titlebar-meta { gap: 8px; min-width: 0; }
  .o-wordmark { font-size: 8px; overflow: hidden; text-overflow: ellipsis; }
  #o-crew-toggle { flex-shrink: 0; margin-left: auto; }
  .o-live { flex-shrink: 0; }
  /* Cards keep their width so two fit side by side and the third peeks, which is
     what tells a thumb the strip scrolls. */
  :root { --o-strip: 86px; }
  .o-strip { height: var(--o-strip); padding: 6px 8px; }
  .o-card { height: 66px; }
  .o-rail { width: 48px; }
  :root { --o-rail: 48px; }
  .o-rail-btn { width: 40px; height: 42px; }     /* 42px: a thumb target, not a mouse one */
  /* Two rows, not three: the title and what you can DO with the room share the
     first line, and the sentence that describes it goes underneath. On a 390px
     screen the header was costing 96px before any content. */
  .o-room-hd { padding: 8px 12px; flex-wrap: wrap; gap: 6px 8px; align-items: center; }
  .o-room-hd .o-room-actions { order: 2; margin-left: auto; }
  .o-room-hd .o-room-sub { order: 3; flex-basis: 100%; font-size: 11px; }
  /* On a phone the row heading runs out of room and the title — the part that
     says WHO or WHAT the row is — was the half being clipped to "Sonj…", while
     the meta and the chip kept their full width. Let the heading wrap instead:
     the title takes the first line whole, the meta and the chip follow. */
  .o-row-hd { flex-wrap: wrap; row-gap: 4px; }
  .o-row-hd .o-row-title { flex: 1 0 100%; white-space: normal; overflow: visible; }
  .o-overlay { padding: 0; }
  .o-overlay-panel { max-height: 100%; height: 100%; }
}

/* Hover labels are a pointer affordance. On a touch screen they fire on tap and
   then linger over the room, so they are simply off. */
@media (hover: none) {
  .o-rail-btn[data-label]::after { display: none; }
}
/* ── motion ──────────────────────────────────────────────────────────────────
   Two durations and one idea: everything that moves is telling you something
   changed. --o-step is a press, a hover, a tab — 90ms, stepped rather than eased
   because the art beside it is sampled. The state pulse is 1.5s and is the ONLY
   thing that loops, because a loop is how you say "still working" without a
   spinner.

   Under prefers-reduced-motion the pulse stops and the dot simply stays lit: the
   information survives, the movement does not. Nothing here is decorative, so
   nothing is lost by switching it off. */
@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; animation: none !important; }
  /* The pulse was carrying "this is the live one". Without the animation the
     dot must still read as lit rather than as any other dot. */
  .o-chip[data-status='running']::before,
  .o-chip[data-status='working']::before,
  .o-chip[data-status='thinking']::before {
    opacity: 1;
    box-shadow: 0 0 0 3px var(--o-accent-soft);
  }
}

/* ═══ touch ═══════════════════════════════════════════════════════════════════
   Everything below applies to a finger, not to a mouse. Two separate problems
   are solved here and they are easy to confuse:

   1. THE FOCUS ZOOM. iOS Safari zooms the page when you focus a form control
      whose text is smaller than 16px — and it does not zoom back out. Typing a
      message in the chat left the whole office magnified and scrolled sideways.
      The fix is the font size, NOT `maximum-scale=1`: disabling pinch zoom to
      stop the focus zoom takes a real accessibility affordance away from
      everybody to work around a rule that only applies to inputs.

   2. TAP TARGETS. 24px controls are fine under a cursor and miserable under a
      thumb, which lands in a circle roughly 9mm across.
   ═══════════════════════════════════════════════════════════════════════════ */
@media (pointer: coarse) {
  .o-input, .o-select, .o-textarea {
    font-size: 16px;             /* the whole fix for the typing zoom */
    line-height: 21px;
    padding: 7px 10px;
  }
  .o-btn { height: 34px; padding: 0 13px; font-size: 13px; }
  .o-btn--sm { height: 30px; padding: 0 11px; font-size: 12px; }
  .o-tab { padding: 8px 12px; font-size: 13px; }
  .o-check { padding: 10px 2px; }
  .o-check input { width: 18px; height: 18px; }
  .o-card { height: 76px; }
  /* A row that opens something is a button; give it a thumb's worth of height. */
  button.o-row { padding: 12px 11px; }
  /* The composer is one line until you write more than one. 16px keeps iOS from
     zooming; the height is ours. */
  .o-talk-input { min-height: 42px; height: 42px; }
  .o-talk-foot .o-btn { height: 42px; }
}

/* The hardware cut-outs. `viewport-fit=cover` lets the app paint under them, so
   the padding has to be paid back here or the last agent card sits under the
   home indicator and the rail's last room under the notch. */
@supports (padding: env(safe-area-inset-bottom)) {
  .o-strip {
    height: auto;
    min-height: var(--o-strip);
    padding-bottom: calc(8px + env(safe-area-inset-bottom));
  }
  .o-cc-foot { padding-bottom: calc(8px + env(safe-area-inset-bottom)); }
  .o-titlebar { padding-left: calc(12px + env(safe-area-inset-left)); padding-right: calc(12px + env(safe-area-inset-right)); }
  .o-rail { padding-bottom: calc(8px + env(safe-area-inset-bottom)); }
}

/* The on-screen keyboard. `--o-kb` is the overlap app.js measures through
   visualViewport; on browsers that shrink the layout themselves it stays 0 and
   this costs nothing. Without it the composer is under the keyboard and you
   type blind. */
.o-app { height: 100vh; height: calc(100dvh - var(--o-kb, 0px)); }

/* ── toasts ──────────────────────────────────────────────────────────────────
   Approving an item, starting a run and resetting the office all used to happen
   in silence unless you were looking at the one panel that changed. */
.o-toasts {
  position: fixed;
  right: 12px;
  bottom: calc(var(--o-strip) + 12px + env(safe-area-inset-bottom, 0px));
  z-index: 70;
  display: flex;
  flex-direction: column;
  gap: 6px;
  align-items: flex-end;
  pointer-events: none;
}
.o-toast {
  max-width: min(360px, calc(100vw - 24px));
  padding: 8px 11px;
  background: var(--o-raised);
  box-shadow: var(--o-hair-strong), var(--o-shadow);
  color: var(--o-text);
  font-size: 12px;
  line-height: 17px;
  animation: o-toast-in 120ms steps(3) both;
}
.o-toast b { color: var(--o-accent-bright); font-weight: 600; }
.o-toast[data-kind='bad'] { box-shadow: inset 0 0 0 1px var(--o-blocked), var(--o-shadow); }
.o-toast[data-kind='good'] { box-shadow: inset 0 0 0 1px var(--o-done), var(--o-shadow); }
@keyframes o-toast-in { from { transform: translateY(6px); opacity: 0; } to { transform: none; opacity: 1; } }

/* ── the shortcut sheet ────────────────────────────────────────────────────── */
.o-keys { display: grid; grid-template-columns: auto 1fr; gap: 6px 12px; align-items: baseline; }
.o-key {
  font-family: var(--o-font-mono);
  font-size: 11px;
  padding: 1px 6px;
  background: var(--o-sunken);
  box-shadow: var(--o-hair-strong);
  white-space: nowrap;
}

/* A phone on its side has about 375px of height to spend, and the crew strip was
   taking a quarter of it. Trim the furniture, keep the content. */
@media (max-height: 480px) {
  :root { --o-strip: 74px; --o-titlebar: 34px; }
  .o-card { height: 60px; width: 172px; }
  .o-card-role { display: none; }
  .o-cc-blurb { display: none; }
  .o-room-hd { padding: 7px 12px; }
  .o-floor-key { display: none; }      /* the legend is a luxury at this height */
}

/* ── the conversation ────────────────────────────────────────────────────────
   The room the product is for. Two columns where there is room: the thread, and
   what Casey is working from. One column on a phone, with the context collapsed
   above the thread, because a sidebar at 390px is a second screen nobody
   scrolls to.

   The composer is pinned and the thread scrolls under it. --o-kb is the height
   the on-screen keyboard is covering, measured by app.js through visualViewport,
   so the composer sits on top of the keyboard instead of behind it. */
.o-talk {
  flex: 1;               /* the room is a flex column; this claims what is left */
  display: grid;
  grid-template-columns: minmax(0, 1fr) 300px;
  gap: var(--o-s5);
  height: 100%;
  min-height: 0;
  padding: var(--o-s5);
}
.o-talk-main {
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
  background: var(--o-panel);
  box-shadow: var(--o-hair);
  border-radius: var(--o-radius);
  overflow: hidden;
}
.o-talk-scroll {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  padding: var(--o-s5);
  display: flex;
  flex-direction: column;
  gap: var(--o-s4);
  scroll-behavior: smooth;
}
.o-talk-side {
  min-width: 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: var(--o-s4);
}

/* A turn. The founder's own words sit in an accent-washed block on the right;
   Casey's fill the column, because his are the ones you read properly. */
.o-bubble { max-width: 100%; }
.o-bubble-who {
  font-family: var(--o-font-ui);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--o-text-3);
  margin-bottom: var(--o-s1);
}
.o-bubble--me {
  align-self: flex-end;
  max-width: 88%;
  background: var(--o-accent-soft);
  box-shadow: inset 0 0 0 1px var(--o-accent-line);
  border-radius: var(--o-radius);
  padding: var(--o-s3) var(--o-s4);
}
.o-bubble--me .o-bubble-who { color: var(--o-accent-bright); }
.o-bubble .o-md { font-size: 13px; line-height: 20px; }
.o-bubble .o-md p:first-child { margin-top: 0; }
.o-bubble .o-md p:last-child { margin-bottom: 0; }
.o-talk-doing {
  margin-top: var(--o-s2);
  font-family: var(--o-font-mono);
  font-size: 11px;
  color: var(--o-accent-bright);
}

.o-talk-open { padding: var(--o-s5) 0; max-width: 560px; }
.o-talk-open-hd {
  font-family: var(--o-font-display);
  font-size: 14px;
  line-height: 24px;
  margin-bottom: var(--o-s4);
}
.o-talk-open-body { font-size: 13px; line-height: 20px; color: var(--o-text-2); margin-bottom: var(--o-s3); }
.o-talk-open-body b { color: var(--o-text); }
.o-talk-plan-title { font-size: 13px; font-weight: 600; margin-bottom: var(--o-s1); }

.o-talk-foot {
  flex-shrink: 0;
  display: flex;
  gap: var(--o-s3);
  align-items: flex-end;
  padding: var(--o-s3) var(--o-s4);
  border-top: 1px solid var(--o-line);
  background: var(--o-surface);
}
.o-talk-input {
  flex: 1;
  min-width: 0;
  resize: none;
  /* One line to start, growing to six. The old composer opened at 76px — a
     mailbox for a message that is usually eight words. */
  min-height: 38px;
  height: 38px;
  max-height: 160px;
  padding: 9px 11px;
  line-height: 20px;
  font-size: 13px;
}
.o-talk-foot .o-btn { height: 38px; padding: 0 var(--o-s5); }

@media (max-width: 900px) {
  .o-talk { grid-template-columns: minmax(0, 1fr); gap: var(--o-s4); padding: var(--o-s4); }
  /* The context goes UNDER the conversation on a narrow screen: the thread is
     what you came for, and the plan is what you check afterwards. */
  .o-talk-side { order: 2; overflow: visible; }
  .o-talk-main { order: 1; min-height: 62vh; }
}
@media (max-width: 620px) {
  .o-talk { padding: var(--o-s3); gap: var(--o-s3); }
  .o-talk-scroll { padding: var(--o-s4); gap: var(--o-s3); }
  .o-talk-main { min-height: 58vh; }
}

/* In the conversation, the crew panel would show Casey's plan next to the plan
   the room is already showing. One of them has to go, and it is the one that was
   a drawer. Specialists are still a tap away in the strip. */
[data-room='talk'] .o-cc { display: none; }
[data-room='talk'] #o-crew-toggle { display: none; }

/* ── a rail that says what it is ─────────────────────────────────────────────
   Eleven unlabelled glyphs is a memory test, and above 1360px the window has
   room for the words. Below that the rail stays narrow with its hover label,
   because the room needs the width more than the rail does.

   This block lives at the END of the file on purpose: a media query carries no
   extra specificity, so placed above the base rules it loses to them. */
@media (min-width: 1360px) {
  :root { --o-rail: 152px; }
  .o-rail { align-items: stretch; padding: var(--o-s3) var(--o-s2); gap: 2px; }
  .o-rail-btn {
    width: auto;
    height: 34px;
    display: flex;
    align-items: center;
    justify-content: flex-start;
    gap: var(--o-s4);
    padding: 0 var(--o-s4);
    border-radius: var(--o-radius);
  }
  .o-rail-icon { width: 18px; text-align: center; }
  .o-rail-label { display: block; font-size: 12px; letter-spacing: .01em; white-space: nowrap; }
  .o-rail-btn[data-label]::after { content: none; }   /* the label is not a tooltip now */
  .o-rail-badge { position: static; margin-left: auto; }
  .o-rail-sep { width: auto; margin: var(--o-s2) var(--o-s4); }
}

/* ── the funnel ──────────────────────────────────────────────────────────────
   The one shape in the product that is not a list. Four stages, each bar as wide
   as the share of people who reached it, and the loss between them written out —
   because "0.14% conversion" is a statistic and "714 of them left at the cart"
   is a problem. */
.o-funnel { display: flex; flex-direction: column; gap: var(--o-s4); }
.o-funnel-stage { display: flex; flex-direction: column; gap: var(--o-s2); }
.o-funnel-hd { display: flex; align-items: baseline; gap: var(--o-s3); flex-wrap: wrap; }
.o-funnel-label {
  font-family: var(--o-font-ui);
  font-size: 11px; font-weight: 600; letter-spacing: .07em; text-transform: uppercase;
  color: var(--o-text-3);
  min-width: 150px;
}
.o-funnel-value { font-family: var(--o-font-mono); font-size: 17px; line-height: 22px; color: var(--o-text); }
.o-funnel-note { font-size: 12px; color: var(--o-text-3); }
.o-funnel-track { height: 12px; background: var(--o-sunken); border-radius: var(--o-radius-sm); box-shadow: var(--o-hair); overflow: hidden; }
.o-funnel-fill {
  height: 100%;
  background: var(--o-accent);
  border-radius: var(--o-radius-sm);
  transition: width 420ms cubic-bezier(.2, .8, .3, 1);
}
/* The bottom of the funnel is the only figure that is money, so it is the only
   one that is green — or, when it is nothing, obviously nothing. */
.o-funnel-fill[data-last='yes'] { background: var(--o-good); }
.o-funnel-drop { font-size: 12px; color: var(--o-bad); }

@media (max-width: 620px) {
  .o-funnel-label { min-width: 0; }
  .o-funnel-hd { gap: var(--o-s2); }
  .o-funnel-note { flex-basis: 100%; }
}

/* ── the pipeline ────────────────────────────────────────────────────────────
   Content's own shape: three columns, not three panels. The count is the thing
   you read from across the room, the titles underneath are what you check. */
.o-pipe { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--o-s5); }
.o-pipe-col { min-width: 0; display: flex; flex-direction: column; gap: var(--o-s2); }
.o-pipe-hd { display: flex; align-items: baseline; gap: var(--o-s3); }
.o-pipe-count { font-family: var(--o-font-mono); font-size: 26px; line-height: 30px; color: var(--o-text); }
.o-pipe-count[data-tone='good'] { color: var(--o-good); }
.o-pipe-count[data-tone='active'] { color: var(--o-active); }
.o-pipe-label {
  font-family: var(--o-font-ui);
  font-size: 11px; font-weight: 600; letter-spacing: .07em; text-transform: uppercase;
  color: var(--o-text-3);
}
.o-pipe-blurb { font-size: 12px; color: var(--o-text-3); margin-bottom: var(--o-s2); }
@media (max-width: 760px) {
  .o-pipe { grid-template-columns: minmax(0, 1fr); gap: var(--o-s5); }
}

/* A turn that never happened. Not Casey's words, not styled like them. */
.o-bubble--failed {
  align-self: flex-start;
  max-width: 88%;
  padding: var(--o-s3) var(--o-s4);
  border-radius: var(--o-radius);
  background: var(--o-bad-soft);
  box-shadow: inset 0 0 0 1px var(--o-bad);
}
.o-bubble--failed .o-bubble-who { color: var(--o-bad); }
.o-bubble-fail { font-size: 13px; line-height: 19px; color: var(--o-text); }
.o-bubble-hint { margin-top: var(--o-s2); font-size: 12px; line-height: 18px; color: var(--o-text-2); }

/* ── plugging something in ───────────────────────────────────────────────────
   One field, one button. The field is wide because what goes in it is a URL
   somebody copied, and a URL in a 200px box is a URL you cannot check. */
.o-connect { display: flex; gap: var(--o-s3); align-items: stretch; }
.o-connect-input { flex: 1; min-width: 0; font-family: var(--o-font-mono); font-size: 12px; }
.o-connect .o-btn { flex-shrink: 0; padding: 0 var(--o-s5); }
@media (max-width: 620px) {
  .o-connect { flex-direction: column; }
  .o-connect .o-btn { width: 100%; justify-content: center; height: 38px; }
}

/* The clock on a reply in progress, and the time it took once it is done. */
.o-talk-clock,
.o-talk-took {
  margin-left: var(--o-s2);
  font-family: var(--o-font-mono);
  font-size: 10px;
  letter-spacing: 0;
  text-transform: none;
}
.o-talk-clock { color: var(--o-active); }
.o-talk-took { color: var(--o-text-3); }
