/* Mobile first: everything outside the @media block at the bottom is the phone layout.
   The one breakpoint (60rem) is where the sidebar stops being a drawer and becomes a column. */

*, *::before, *::after { box-sizing: border-box; }
:root { color-scheme: dark; font-family: Georgia, serif; }
body { margin: 0; background: #14121a; color: #d8d2c8; }
body.drawer-open { overflow: hidden; }

/* Phone: one column, in document order. `min-width: 0` on the children is what stops the
   trace's monospace content from setting the page's minimum width and scrolling it sideways. */
#app { display: flex; flex-direction: column; min-height: 100vh; }
#app > * { min-width: 0; }

.topbar { display: flex; flex-wrap: wrap; gap: 0.5rem; justify-content: space-between;
  align-items: center; padding: 0.6rem 0.9rem; padding-left: max(0.9rem, env(safe-area-inset-left));
  padding-right: max(0.9rem, env(safe-area-inset-right));
  border-bottom: 1px solid #2c2836; font-size: 0.9rem; }
/* Takes the rest of the toggle's row and wraps its own badges, rather than pushing itself
   onto a line of its own. */
.clock { flex: 1; min-width: 0; }
.clock-elapsed, .clock-cost { margin-left: 0.4rem; padding: 0.05rem 0.35rem;
  border-radius: 0.6rem; background: #2c2836; font-size: 0.78rem; opacity: 0.8;
  white-space: nowrap; }

/* Drawer. Off-canvas until app.js sets [data-drawer-open] on #app; above the breakpoint the
   transform and the fixed positioning are both dropped and this is a plain column. */
.drawer-toggle { background: #221e2e; color: inherit; border: 1px solid #3a3450;
  padding: 0.3rem 0.7rem; cursor: pointer; font: inherit; line-height: 1; }
.drawer-backdrop { position: fixed; inset: 0; background: #0a0810; opacity: 0.6;
  border: 0; padding: 0; z-index: 1; }
.sidebar { position: fixed; z-index: 2; top: 0; bottom: 0; left: 0; width: min(20rem, 86vw);
  padding: 1rem; padding-left: max(1rem, env(safe-area-inset-left));
  background: #191622; border-right: 1px solid #2c2836; overflow-y: auto;
  transform: translateX(-100%); transition: transform 0.2s ease; }
#app[data-drawer-open] .sidebar { transform: none; }
.sidebar h2 { margin-top: 0; font-size: 1rem; letter-spacing: 0.05em; }
/* Controls that used to crowd the topbar; they live here at every width. */
.sidebar-tools { display: flex; flex-wrap: wrap; gap: 0.75rem; align-items: center;
  margin-top: 1.5rem; padding-top: 0.75rem; border-top: 1px solid #2c2836; }

.stage { flex: 1; padding: 1rem; padding-left: max(1rem, env(safe-area-inset-left));
  padding-right: max(1rem, env(safe-area-inset-right)); }
/* One reading measure for every turn state, so prose doesn't reflow when the turn lands. */
.stage > * { max-width: 44rem; margin-inline: auto; }
.pools, .effects { list-style: none; padding: 0; margin: 0 0 1rem; }
.pool, .effect { display: flex; gap: 0.5rem; padding: 0.2rem 0; font-size: 0.9rem; }
.pool-value, .effect-deltas { margin-left: auto; opacity: 0.7; }
.panel { margin: 0 0 1rem; }
.panel h3 { margin: 0 0 0.3rem; font-size: 0.8rem; letter-spacing: 0.05em;
  opacity: 0.6; text-transform: uppercase; }
.panel-items { display: grid; grid-template-columns: auto 1fr; gap: 0.15rem 0.6rem;
  margin: 0; font-size: 0.9rem; }
.panel-items dt { opacity: 0.7; }
.panel-items dd { margin: 0; overflow-wrap: anywhere; }
/* Shorthand carries the full name in its title — mark it as hoverable. */
abbr.shorthand { text-decoration: underline dotted; text-underline-offset: 0.2em;
  cursor: help; }
.narration p { line-height: 1.6; }
/* Roll badge. No CSS animation anywhere here on purpose: a re-render must never replay
   the roll. The only motion is app.js repainting .roll-dice while the spin runs. */
.roll-badge { display: flex; flex-wrap: wrap; gap: 0.7rem; align-items: baseline;
  margin-bottom: 1.2rem; padding: 0.5rem 0.9rem; background: #1c1928;
  border: 1px solid #2c2836; border-left: 3px solid #3a3450;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.85rem; }
.roll-badge.good { border-left-color: #6f9f7a; }
.roll-badge.bad { border-left-color: #a06a70; }
.roll-check { letter-spacing: 0.08em; text-transform: uppercase; opacity: 0.75; }
.roll-dice { font-size: 1.8rem; line-height: 1; font-variant-numeric: tabular-nums;
  min-width: 2.4ch; text-align: center; color: #f0e8dc; }
.roll-badge.rolling .roll-dice { color: #a89ec8; }
/* Everything the dice haven't decided yet stays dark until the number stops. */
.roll-badge.rolling .roll-mods, .roll-badge.rolling .roll-total,
.roll-badge.rolling .roll-vs, .roll-badge.rolling .roll-band { visibility: hidden; }
.roll-mods, .roll-vs { opacity: 0.7; }
.roll-band { margin-left: auto; }
.roll-badge.good .roll-band { color: #9fd0a8; }
.roll-badge.bad .roll-band { color: #e0a0a8; }
.roll-margin { margin-left: 0.35rem; opacity: 0.7; }
/* Mid-turn the stage holds nothing but the action being resolved and its roll. */
.stage.awaiting { display: flex; flex-direction: column; align-items: center;
  gap: 1rem; padding-top: 3rem; text-align: center; }
.stage.awaiting > * { width: 100%; }
.stage.awaiting .roll-badge { margin-bottom: 0; }
/* …and this turn's narration as it streams. Left-aligned while the rest of the awaiting
   stage is centred, so prose doesn't jump when the turn lands. The trailing block is static:
   app.js repaints this node constantly and motion would restart each time (same reason the
   roll badge has none). */
.narration.live { text-align: left; }
.narration.live:empty { display: none; }
.stage.awaiting:has(.narration.live.streaming) { padding-top: 1.5rem; }
.narration.live.streaming p:last-child::after { content: "▌"; margin-left: 0.15rem;
  opacity: 0.55; }
.pending-action { font-style: italic; opacity: 0.7; }
.pending-action::before { content: "› "; }
.begin { display: flex; flex-direction: column; align-items: center; gap: 1rem;
  margin-top: 4rem; text-align: center; }
.begin-hint { opacity: 0.6; font-style: italic; margin: 0; }
.begin-button { background: #221e2e; color: inherit; border: 1px solid #3a3450;
  padding: 0.7rem 2.5rem; cursor: pointer; font: inherit; letter-spacing: 0.15em; }
.begin-button:hover:not([disabled]) { border-color: #6a5f8f; }
.begin-button[disabled] { opacity: 0.4; cursor: default; }
.options { display: flex; flex-direction: column; gap: 0.5rem; margin-top: 1.5rem; }
.option, .free-action button {
  background: #221e2e; color: inherit; border: 1px solid #3a3450;
  padding: 0.6rem 0.9rem; min-height: 2.75rem; text-align: left; cursor: pointer; font: inherit;
}
.option:hover:not([disabled]) { border-color: #6a5f8f; }
.option[disabled], .free-action [disabled] { opacity: 0.4; cursor: default; }
/* The label gets the full width; the odds and the trait share the line under it. */
.option { display: flex; flex-direction: column; gap: 0.3rem; }
.option-foot { display: flex; align-items: baseline; gap: 0.75rem; }
.option-detail { margin-left: auto; opacity: 0.6; font-size: 0.8rem; white-space: nowrap; }
.option-odds { opacity: 0.6; font-size: 0.8rem; font-variant-numeric: tabular-nums; }
.free-action { display: flex; gap: 0.5rem; }
.free-action input { flex: 1; min-width: 0; background: #1a1724; color: inherit;
  border: 1px solid #3a3450; padding: 0.6rem; font: inherit; }
.difficulty { display: flex; gap: 0.5rem; align-items: center; }
.difficulty button { background: #221e2e; color: inherit; border: 1px solid #3a3450;
  width: 1.8rem; min-height: 1.8rem; cursor: pointer; }
.status.error { margin-top: 1rem; padding: 0.75rem; border: 1px solid #7a3040;
  color: #e0a0a8; white-space: pre-wrap; font-family: monospace; font-size: 0.85rem;
  overflow-wrap: anywhere; }
.status.busy { margin-top: 1rem; opacity: 0.6; font-style: italic; }
.game-over { margin-top: 2rem; font-size: 1.5rem; letter-spacing: 0.3em; text-align: center; }
.empty { display: none; }
.start { max-width: 560px; margin: 2rem auto; padding: 0 1rem;
  padding-left: max(1rem, env(safe-area-inset-left));
  padding-right: max(1rem, env(safe-area-inset-right)); width: 100%; }
.start h1 { letter-spacing: 0.15em; font-size: 1.4rem; }
.games { list-style: none; padding: 0; }
.game a { display: flex; flex-direction: column; gap: 0.2rem; padding: 0.75rem 0.9rem;
  margin-bottom: 0.5rem; border: 1px solid #3a3450; background: #221e2e;
  color: inherit; text-decoration: none; }
.game a:hover { border-color: #6a5f8f; }
.game { display: flex; gap: 0.5rem; align-items: stretch; }
.game a { flex: 1; min-width: 0; }
.game-title { overflow-wrap: anywhere; }
.game-delete { background: #221e2e; color: #a08090; border: 1px solid #3a3450;
  margin-bottom: 0.5rem; padding: 0 0.7rem; cursor: pointer; font: inherit; }
.game-delete:hover { border-color: #7a3040; color: #e0a0a8; }
.game-detail { opacity: 0.6; font-size: 0.8rem; overflow-wrap: anywhere; }
.game-create { display: flex; flex-direction: column; gap: 0.5rem; margin-top: 2rem; }
.game-create h3 { margin: 0; font-size: 0.9rem; opacity: 0.7; }
.game-create input { background: #1a1724; color: inherit; border: 1px solid #3a3450;
  padding: 0.5rem; font: inherit; font-size: 0.85rem; min-width: 0; }
.game-create button { background: #221e2e; color: inherit; border: 1px solid #3a3450;
  padding: 0.5rem; min-height: 2.75rem; cursor: pointer; font: inherit; align-self: flex-start; }
.layer-picker { border: 1px solid #3a3450; padding: 0.6rem; display: flex;
  flex-direction: column; gap: 0.5rem; min-width: 0; }
.layer-picker legend { font-size: 0.8rem; opacity: 0.7; padding: 0 0.3rem; }
.layer-list { list-style: none; padding: 0; margin: 0; display: flex;
  flex-direction: column; gap: 0.3rem; }
.layer-choice { display: flex; align-items: center; gap: 0.4rem; }
.layer-choice label { display: flex; align-items: center; gap: 0.4rem; flex: 1;
  min-width: 0; cursor: pointer; }
.layer-choice input[type="radio"] { accent-color: #7a6fb0; }
.layer-url { font-size: 0.8rem; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; }
.layer-remove { background: #221e2e; color: #a08090;
  border: 1px solid #3a3450; padding: 0 0.5rem; cursor: pointer; font: inherit;
  font-size: 0.75rem; align-self: center; }
.layer-remove:hover { border-color: #7a3040; color: #e0a0a8; }
.layer-add { display: flex; gap: 0.5rem; }
.layer-add input { flex: 1; }
.layer-add button { align-self: center; }
.characters { display: flex; flex-direction: column; gap: 0.5rem; }
.character { background: #221e2e; color: inherit; border: 1px solid #3a3450;
  padding: 0.6rem 0.9rem; min-height: 2.75rem; text-align: left; cursor: pointer; font: inherit; }
.character:hover { border-color: #6a5f8f; }
.character.last-played { border-color: #6a8f5f; background: #232c22; }
.last-played-badge { float: right; margin-left: 0.5rem; font-size: 0.75rem;
  border: 1px solid #6a8f5f; color: #a8d0a0; padding: 0 0.3rem; }
.stale-badge { float: right; opacity: 0.7; font-size: 0.75rem; border: 1px solid #7a6030;
  color: #d8b060; padding: 0 0.3rem; }
.stale-notice { margin: 0.5rem 0 1rem; padding: 0.6rem; border: 1px solid #7a6030;
  color: #d8b060; font-size: 0.85rem; }
.doc-refresh { display: flex; flex-direction: column; gap: 0.5rem; margin-top: 2rem; }
.doc-refresh h3 { margin: 0; font-size: 0.9rem; opacity: 0.7; }
.doc-refresh input[type="text"] { background: #1a1724; color: inherit;
  border: 1px solid #3a3450; padding: 0.5rem; font: inherit; font-size: 0.85rem; min-width: 0; }
.doc-refresh > button { background: #221e2e; color: inherit; border: 1px solid #3a3450;
  padding: 0.5rem; min-height: 2.75rem; cursor: pointer; font: inherit; align-self: flex-start; }
.doc-notice { opacity: 0.7; font-size: 0.85rem; }
.doc-refreshed { list-style: none; margin: 0; padding: 0; display: flex;
  flex-direction: column; gap: 0.3rem; font-size: 0.8rem; }
.doc-refreshed-layer { opacity: 0.7; }
.doc-refreshed-url { color: #9a8fd0; overflow-wrap: anywhere; }
.doc-refreshed-time { opacity: 0.5; }
.setup h2 { font-size: 1rem; letter-spacing: 0.05em; }
.setup-chat-log { display: flex; flex-direction: column; gap: 0.5rem; margin-bottom: 1rem; }
.setup-msg { padding: 0.6rem 0.9rem; border: 1px solid #3a3450; line-height: 1.5;
  white-space: pre-wrap; max-width: 85%; overflow-wrap: anywhere; }
.setup-msg.assistant { background: #221e2e; align-self: flex-start; }
.setup-msg.player { background: #1a2430; border-color: #34505a; align-self: flex-end; }
.setup-chat { display: flex; gap: 0.5rem; }
.setup-chat input { flex: 1; min-width: 0; background: #1a1724; color: inherit;
  border: 1px solid #3a3450; padding: 0.6rem; font: inherit; }
.setup-chat button { background: #221e2e; color: inherit; border: 1px solid #3a3450;
  padding: 0.6rem 0.9rem; cursor: pointer; font: inherit; }
.empty-list { opacity: 0.6; font-style: italic; }
.login { display: flex; gap: 0.5rem; margin-top: 1rem; }
.login input { flex: 1; min-width: 0; background: #1a1724; color: inherit;
  border: 1px solid #3a3450; padding: 0.6rem; font: inherit; }
.login button { background: #221e2e; color: inherit; border: 1px solid #3a3450;
  padding: 0.6rem 0.9rem; cursor: pointer; font: inherit; }
.topbar-tools { display: flex; gap: 1rem; align-items: center; }
.version { opacity: 0.45; font-size: 0.75rem; font-family: monospace; align-self: center; }
.trace-toggle, .undo, .redo { background: #221e2e; color: inherit; border: 1px solid #3a3450;
  padding: 0.35rem 0.7rem; cursor: pointer; font: inherit; font-size: 0.85rem; }
.trace-toggle.open { border-color: #6a5f8f; }
.undo:hover:enabled, .redo:hover:enabled { border-color: #6a5f8f; }
.undo:disabled, .redo:disabled { opacity: 0.4; cursor: default; }
/* One control in two halves — undo and redo sit against each other, not a gap apart. */
.undo { margin-right: -1rem; border-right-width: 0; }
.setup-head { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
.setup-debug { margin-top: 1rem; border: 1px solid #2c2836; }

/* Trace. Anything that can't wrap scrolls inside its own block, never the page. */
.trace { padding: 1rem 0.9rem; padding-left: max(0.9rem, env(safe-area-inset-left));
  padding-right: max(0.9rem, env(safe-area-inset-right));
  border-top: 1px solid #2c2836; font-family: monospace; font-size: 0.8rem; min-width: 0; }
.trace h2 { font-family: Georgia, serif; font-size: 0.9rem; letter-spacing: 0.05em; }
.trace-timing { opacity: 0.6; font-size: 0.85em; }
.trace-entry { border: 1px solid #2c2836; margin-bottom: 0.4rem; padding: 0.3rem 0.5rem;
  min-width: 0; }
.trace-entry > summary { cursor: pointer; display: flex; flex-wrap: wrap; gap: 0.4rem 0.75rem;
  align-items: baseline; }
.trace-index { opacity: 0.5; }
.trace-module { opacity: 0.85; overflow-wrap: anywhere; }
/* Status ends the first line whatever else wrapped: the summary row is read status-first. */
.trace-status { margin-left: auto; }
.trace-status.ok { color: #7fae7f; }
.trace-status.aborted { color: #d8b060; }
.trace-status.failed { color: #e0a0a8; }
.trace-status.running { color: #8fb8d8; animation: trace-pulse 1.2s ease-in-out infinite; }
@keyframes trace-pulse { 50% { opacity: 0.35; } }
.trace-errors { color: #e0a0a8; white-space: pre-wrap; margin: 0.4rem 0;
  overflow-wrap: anywhere; }
.trace-reason { color: #d8b060; margin: 0.4rem 0; overflow-wrap: anywhere; }
.trace-diff { list-style: none; padding: 0.4rem 0; margin: 0; }
.trace-diff li { padding: 0.1rem 0; overflow-wrap: anywhere; }
.diff-before { color: #a08090; }
.diff-after { color: #7fae7f; }
.trace-copy { background: #221e2e; color: inherit; border: 1px solid #3a3450;
  padding: 0.25rem 0.6rem; cursor: pointer; font: inherit; font-size: 0.85em; }
.trace-json { margin: 0.2rem 0; min-width: 0; }
.trace-json summary { cursor: pointer; opacity: 0.7; }
.trace-json pre { white-space: pre-wrap; overflow-wrap: anywhere; background: #1a1724;
  padding: 0.5rem; margin: 0.3rem 0; max-height: 60vh; overflow: auto; }
.probe { border: 1px solid #2c2836; padding: 0.5rem; margin-bottom: 0.6rem; }
.probe h3 { font-family: Georgia, serif; font-size: 0.85rem; letter-spacing: 0.05em;
  margin: 0 0 0.4rem; }
.probe-controls { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; }
.probe-controls select, .probe-controls button { background: #221e2e; color: inherit;
  border: 1px solid #3a3450; padding: 0.3rem 0.5rem; font: inherit; max-width: 100%; }
.probe-controls button:disabled { opacity: 0.5; cursor: default; }
.probe-hint { opacity: 0.55; font-size: 0.85em; margin: 0.35rem 0 0; }
.probe-error { color: #e0a0a8; margin-top: 0.4rem; overflow-wrap: anywhere; }
.probe-rows { margin: 0.5rem 0 0; }
.probe-rows dt { opacity: 0.6; }
.probe-rows dd { margin: 0 0 0.3rem; overflow-wrap: anywhere; }

@media (min-width: 60rem) {
  /* The sidebar stops being a drawer: same markup, placed as a column. The shell is centred
     and capped; the stage keeps its reading measure, so the slack goes to the sidebar. */
  #app {
    display: grid;
    grid-template: "topbar topbar" auto "sidebar stage" 1fr "trace trace" auto
      / minmax(280px, 20rem) 1fr;
    max-width: 1100px; margin: 0 auto;
  }
  .topbar { grid-area: topbar; padding: 0.75rem 1rem; }
  .drawer-toggle, .drawer-backdrop { display: none; }
  .sidebar { grid-area: sidebar; position: static; width: auto; transform: none;
    transition: none; background: none; padding: 1rem; }
  .stage { grid-area: stage; padding: 1.5rem; }
  .stage.awaiting { padding-top: 6rem; }
  .stage.awaiting:has(.narration.live.streaming) { padding-top: 2rem; }
  .trace { grid-area: trace; padding: 1rem; }
  .start { grid-area: 1 / 1 / -1 / -1; margin: 3rem auto; }
  .panel-items, .probe-rows { display: grid; grid-template-columns: auto 1fr;
    gap: 0.15rem 0.6rem; }
  .probe-rows dd { margin: 0; }
  .trace-json pre { max-height: 24rem; }
}
