/* Sprinto admin + (later) user-facing pages: a "cozy literary" design system.
 *
 * Warm paper-and-ink palette, a characterful serif for headings over a clean
 * sans for data. Everything is driven by the design tokens in :root, so a theme
 * is just a different token block. We ship the warm-dark "night writing" theme
 * as the default because the existing stat charts hardcode dark-tuned colours;
 * the light "paper" theme is staged below under [data-theme="paper"] and will
 * become the default for user-facing pages once the charts read from tokens. */

:root {
  /* Warm dark ("night writing") */
  --paper: #1c1813;
  --surface: #26201a;
  --surface-2: #2f2820;
  --ink: #efe6d6;
  --muted: #a99d89;
  --border: #3a3128;
  --accent: #e0653a;        /* terracotta / sunset */
  --accent-strong: #c8552e;
  --accent-soft: rgba(224, 101, 58, .16);
  --amber: #e0a44a;
  --good: #79b18d;
  --bad: #d9776a;
  --link: #e9a06a;

  --font-serif: "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, "Times New Roman", serif;
  --font-sans: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --font-mono: ui-monospace, "Cascadia Code", Consolas, "Liberation Mono", monospace;

  --radius: 9px;
  --radius-sm: 6px;
  --shadow: 0 1px 2px rgba(0, 0, 0, .35), 0 6px 22px rgba(0, 0, 0, .22);
}

/* Staged light "paper" theme (not yet default; opt in with <html data-theme="paper">). */
:root[data-theme="paper"] {
  --paper: #f6f0e4;
  --surface: #fdfaf2;
  --surface-2: #efe7d6;
  --ink: #2b2420;
  --muted: #7a6f5e;
  --border: #e2d7c2;
  --accent: #d65a30;
  --accent-strong: #bb4a24;
  --accent-soft: rgba(214, 90, 48, .14);
  --amber: #b9842f;
  --good: #3f7c5f;
  --bad: #b5462f;
  --link: #c2531f;
  --shadow: 0 1px 2px rgba(80, 60, 30, .08), 0 6px 22px rgba(80, 60, 30, .10);
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: 15px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}

/* ---- Top bar ---------------------------------------------------------- */
.topbar {
  display: flex;
  align-items: center;
  gap: 1.1rem;
  padding: .65rem 1.4rem;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  position: sticky;
  top: 0;
  z-index: 5;
  flex-wrap: wrap;
}
.brand {
  font-family: var(--font-serif);
  font-size: 1.28rem;
  font-weight: 600;
  color: var(--ink);
  text-decoration: none;
  letter-spacing: .2px;
  display: inline-flex;
  align-items: baseline;
  gap: .45rem;
  white-space: nowrap;
}
.brand:hover { text-decoration: none; }
.brand .star { color: var(--accent); }
.brand .tag {
  font-family: var(--font-sans);
  font-size: .6rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .18em;
  color: var(--accent);
  border: 1px solid var(--accent);
  padding: .08rem .4rem;
  border-radius: 99px;
  align-self: center;
}
/* Brand stacks the live database name under "Sprinto" (filled by the heartbeat),
   in an obvious yellow so admins always know which database they're looking at. */
.brandcol { display: flex; flex-direction: column; justify-content: center; line-height: 1.05; }
.dbname { font-size: .66rem; font-weight: 700; letter-spacing: .04em; color: var(--amber); margin-left: 1.5rem; margin-top: .1rem; }
.dbname::before { content: "▾ "; opacity: .6; }
.dbname:empty { display: none; }
.topbar nav { display: flex; gap: .1rem; flex-wrap: wrap; flex: 1; }
.topbar nav a {
  color: var(--muted);
  text-decoration: none;
  padding: .28rem .6rem;
  border-radius: var(--radius-sm);
  font-size: .92rem;
}
.topbar nav a:hover { color: var(--ink); background: var(--surface-2); text-decoration: none; }
.topbar nav a.active { color: var(--accent); background: var(--accent-soft); }
/* The heartbeat readout ("19 ms / 100 ms": browser round-trip / Discord gateway
   ping). flex-basis:100% inside the wrapping topbar parks it on its own row
   under the tabs, whatever its DOM position, so it never squeezes the nav.
   Hidden until the first heartbeat fills it, so it costs no row when empty. */
#rtt {
  color: var(--muted);
  font-size: .8rem;
  white-space: nowrap;
  flex: 1 0 100%;
  text-align: right;
  margin-top: -.35rem;
  overflow: hidden;
  text-overflow: ellipsis;
}
#rtt:empty { display: none; }

/* ---- Layout + headings ------------------------------------------------ */
.container { max-width: 1180px; margin: 0 auto; padding: 1.5rem 1.4rem 4rem; }

h1, h2, h3 { font-family: var(--font-serif); font-weight: 600; color: var(--ink); }
h1 { font-size: 1.95rem; margin: .1em 0 .7em; letter-spacing: .2px; }
h1::after {
  content: "";
  display: block;
  width: 2.5rem;
  height: 3px;
  margin-top: .45rem;
  background: var(--accent);
  border-radius: 2px;
  opacity: .85;
}
h2 { font-size: 1.35rem; margin: 1.5em 0 .5em; }
h3 { font-size: 1.1rem; margin: 1.2em 0 .4em; }

a { color: var(--link); text-decoration: none; }
a:hover { text-decoration: underline; text-underline-offset: 2px; }
em { color: var(--muted); font-style: italic; }
code, .mono { font-family: var(--font-mono); font-size: .88em; }
.muted { color: var(--muted); }
hr { border: 0; border-top: 1px solid var(--border); margin: 1.3rem 0; }

/* ---- Tables ----------------------------------------------------------- */
table { border-collapse: collapse; margin: .6em 0; font-size: .92rem; }
td, th {
  border: 1px solid var(--border);
  padding: .38em .75em;
  text-align: left;
  vertical-align: top;
}
th { background: var(--surface-2); font-weight: 600; color: var(--ink); }
tbody tr:hover { background: var(--surface); }
.gauges th { width: 9em; color: var(--muted); font-weight: 500; background: var(--surface); }

/* ---- Buttons + forms -------------------------------------------------- */
button, .btn {
  display: inline-block;
  font-family: var(--font-sans);
  font-size: .9rem;
  background: var(--surface-2);
  color: var(--ink);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: .32em .9em;
  cursor: pointer;
  text-decoration: none;
  transition: background .12s, border-color .12s;
}
button:hover, .btn:hover { background: var(--border); border-color: var(--muted); text-decoration: none; }
.btn-primary { background: var(--accent); border-color: var(--accent); color: #1b1410; font-weight: 600; }
.btn-primary:hover { background: var(--accent-strong); border-color: var(--accent-strong); }
.btn-danger { color: var(--bad); border-color: color-mix(in srgb, var(--bad) 50%, transparent); background: transparent; }
.btn-danger:hover { background: color-mix(in srgb, var(--bad) 14%, transparent); border-color: var(--bad); }

input, select, textarea {
  font-family: var(--font-sans);
  font-size: .92rem;
  background: var(--paper);
  color: var(--ink);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: .38em .6em;
}
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
label { color: var(--muted); font-size: .9rem; }

/* ---- Cards + badges --------------------------------------------------- */
.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1rem 1.15rem;
  box-shadow: var(--shadow);
  margin: .85rem 0;
}
.card-head { display: flex; align-items: center; justify-content: space-between; gap: .6rem; margin-bottom: .5rem; }
.card > :first-child { margin-top: 0; }
.card > :last-child { margin-bottom: 0; }

.badge {
  display: inline-block;
  font-size: .7rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .06em;
  padding: .16em .55em;
  border-radius: 99px;
  border: 1px solid var(--border);
  color: var(--muted);
  vertical-align: middle;
}
.badge-active { color: var(--good); border-color: color-mix(in srgb, var(--good) 45%, transparent); background: color-mix(in srgb, var(--good) 12%, transparent); }
.badge-former { color: var(--amber); border-color: color-mix(in srgb, var(--amber) 45%, transparent); background: color-mix(in srgb, var(--amber) 12%, transparent); }
.badge-accent { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 45%, transparent); background: var(--accent-soft); }
/* Founder tenure marker (a warm gold pill). */
.badge-founder { color: var(--amber); border-color: color-mix(in srgb, var(--amber) 50%, transparent); background: color-mix(in srgb, var(--amber) 14%, transparent); }

/* A small horizontal "key: value" run, lighter than a gauges table. */
.facts { display: flex; flex-wrap: wrap; gap: .35rem 1.4rem; margin: .5rem 0; }
.facts > div { font-size: .92rem; }
.facts dt { color: var(--muted); font-size: .76rem; text-transform: uppercase; letter-spacing: .05em; }
.facts dd { margin: 0; }

/* ---- Notices ---------------------------------------------------------- */
.notice { color: var(--muted); font-style: italic; }
.flash {
  border: 1px solid color-mix(in srgb, var(--good) 45%, transparent);
  background: color-mix(in srgb, var(--good) 12%, transparent);
  color: var(--ink);
  border-radius: var(--radius-sm);
  padding: .55rem .9rem;
  margin: .7rem 0;
}
.flash-error { border-color: color-mix(in srgb, var(--bad) 50%, transparent); background: color-mix(in srgb, var(--bad) 12%, transparent); }

#dead {
  display: none;
  background: color-mix(in srgb, var(--bad) 22%, var(--surface));
  color: #ffd9d3;
  padding: .6em 1.2em;
  border-bottom: 1px solid var(--bad);
  text-align: center;
  font-size: .9rem;
}

/* ---- Log-tail frame (unchanged behaviour, tokenised colours) ---------- */
.logframe { max-height: 72vh; overflow: auto; border: 1px solid var(--border); border-radius: var(--radius); margin: .6em 0; }
.logframe table { margin: 0; width: 100%; border: 0; font-size: .88rem; }
.logframe th { position: sticky; top: 0; z-index: 1; background: var(--surface-2); }
.logframe td, .logframe th { border-width: 0 0 1px 0; }
td.id { color: var(--muted); font-variant-numeric: tabular-nums; }
td.wrap { max-width: 52ch; overflow-wrap: anywhere; white-space: pre-wrap; }
td.at { white-space: nowrap; color: var(--muted); font-variant-numeric: tabular-nums; }
.tailoff { color: var(--amber); }

/* htmx: dim a region while a request for it is in flight. */
.htmx-request.search-results, .search-results.htmx-request { opacity: .55; transition: opacity .1s; }

/* ---- Supporters ------------------------------------------------------- */
.search-bar { display: flex; gap: .5rem; flex-wrap: wrap; align-items: center; margin: .2rem 0 1.1rem; }
.search-bar input[type="search"] { flex: 1; min-width: 16rem; }
.results-meta { color: var(--muted); font-size: .85rem; margin: .2rem 0 .7rem; }
.sup-row { display: block; text-decoration: none; color: inherit; padding: .7rem .9rem; }
.sup-row:hover { text-decoration: none; border-color: var(--muted); background: var(--surface-2); }
.sup-row .card-head { margin-bottom: .2rem; }
.sup-name { font-family: var(--font-serif); font-size: 1.08rem; font-weight: 600; color: var(--ink); }
.sup-sub { font-size: .85rem; }
.facts { display: flex; flex-wrap: wrap; gap: .8rem 1.6rem; margin: .3rem 0; }
.facts dt { color: var(--muted); font-size: .72rem; text-transform: uppercase; letter-spacing: .05em; margin-bottom: .1rem; }
.facts dd { margin: 0; font-size: .98rem; }
.action-grid { display: grid; gap: .8rem; grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr)); margin-top: 1rem; }
.action-grid .card { margin: 0; height: 100%; }
.action-grid h3 { margin: 0 0 .5rem; font-size: 1rem; }
.action-row { display: flex; gap: .4rem; flex-wrap: wrap; align-items: center; }
.action-row input { flex: 1; min-width: 8rem; }
.codebox { font-family: var(--font-mono); font-size: 1.1rem; letter-spacing: .04em; background: var(--paper); border: 1px dashed var(--accent); border-radius: var(--radius-sm); padding: .35rem .7rem; display: inline-block; color: var(--ink); }

/* ---- Chart palette ---------------------------------------------------- */
/* Dark = the charts' original tuned values. The SVG keeps emitting those hexes;
   the attribute-selector remaps below route them through these tokens (a CSS
   property overrides an SVG presentation attribute, which can't read var()), so
   paper mode only needs a darker variant per token and the Rust stays untouched. */
:root {
  color-scheme: dark;
  --c-axis: #8b95a3; --c-grid: #262d37; --c-grid2: #1f252e; --c-rule: #475063; --c-inset: #1c2129; --c-warn: #e0a86a;
  --c-blue: #7ab7ff; --c-green: #7dd87d; --c-tan: #d8a87d; --c-periwinkle: #8fa8d8;
  --c-amber: #e0a657; --c-teal: #6cc4b0; --c-red: #d87d7d; --c-purple: #c87dd8;
  --c-pink: #e08bd0; --c-salmon: #d8907d; --c-gold: #e0c06a; --c-aqua: #7dd8c8;
  --c-mint: #8fd8c0; --c-tan2: #c8a87d; --c-khaki: #d8c87d;
}
:root[data-theme="paper"] {
  color-scheme: light;
  --c-axis: #8a7d68; --c-grid: #e6dcc8; --c-grid2: #ece3d0; --c-rule: #d2c4a8; --c-inset: #efe7d6; --c-warn: #b87420;
  --c-blue: #2f6fb0; --c-green: #3f8f4a; --c-tan: #b5713f; --c-periwinkle: #5566b0;
  --c-amber: #b9742a; --c-teal: #2f8f7a; --c-red: #c0443f; --c-purple: #9a4caf;
  --c-pink: #b94fa0; --c-salmon: #c25b46; --c-gold: #a8842a; --c-aqua: #2f9a86;
  --c-mint: #3f9576; --c-tan2: #9c7636; --c-khaki: #97842f;
}
[fill="#8b95a3"] { fill: var(--c-axis); }
[stroke="#262d37"] { stroke: var(--c-grid); }
[stroke="#1f252e"] { stroke: var(--c-grid2); }
[fill="#7ab7ff"] { fill: var(--c-blue); }   [stroke="#7ab7ff"] { stroke: var(--c-blue); }
[fill="#7dd87d"] { fill: var(--c-green); }   [stroke="#7dd87d"] { stroke: var(--c-green); }
[fill="#d8a87d"] { fill: var(--c-tan); }     [stroke="#d8a87d"] { stroke: var(--c-tan); }
[fill="#8fa8d8"] { fill: var(--c-periwinkle); } [stroke="#8fa8d8"] { stroke: var(--c-periwinkle); }
[fill="#e0a657"] { fill: var(--c-amber); }   [stroke="#e0a657"] { stroke: var(--c-amber); }
[fill="#6cc4b0"] { fill: var(--c-teal); }    [stroke="#6cc4b0"] { stroke: var(--c-teal); }
[fill="#d87d7d"] { fill: var(--c-red); }     [stroke="#d87d7d"] { stroke: var(--c-red); }
[fill="#c87dd8"] { fill: var(--c-purple); }
[fill="#e08bd0"] { fill: var(--c-pink); }
[fill="#d8907d"] { fill: var(--c-salmon); }
[fill="#e0c06a"] { fill: var(--c-gold); }
[fill="#7dd8c8"] { fill: var(--c-aqua); }
[fill="#8fd8c0"] { fill: var(--c-mint); }
[fill="#c8a87d"] { fill: var(--c-tan2); }
[fill="#d8c87d"] { fill: var(--c-khaki); }

.theme-toggle { background: transparent; border: 1px solid var(--border); color: var(--muted); border-radius: var(--radius-sm); padding: .2rem .5rem; cursor: pointer; font-size: .95rem; line-height: 1; }
.theme-toggle:hover { color: var(--ink); border-color: var(--muted); background: var(--surface-2); }

/* ---- Noveling event enrollment bar ------------------------------------ */
.eventbar { display: flex; align-items: center; gap: .7rem; margin: .8rem 0 .2rem; padding: .5rem .85rem; background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius-sm); }
.eventbar .event-name { font-weight: 600; }
.eventbar form { margin: 0 0 0 auto; }
.eventbar form button { padding: .25rem .8rem; font-size: .82rem; }

/* ---- Noveling view toggle (sprints vs whole novel) -------------------- */
.viewtabs { display: inline-flex; gap: 0; margin: .6rem 0 .2rem; border: 1px solid var(--border); border-radius: var(--radius-sm); overflow: hidden; }
.viewtabs .tab { padding: .3rem .85rem; font-size: .85rem; color: var(--muted); text-decoration: none; background: transparent; border-right: 1px solid var(--border); }
.viewtabs .tab:last-child { border-right: 0; }
.viewtabs .tab:hover { background: var(--surface-2); color: var(--ink); }
.viewtabs .tab.active { background: var(--accent); color: #1b1410; font-weight: 600; }
.viewtabs + .viewtabs { margin-left: .5rem; }

/* ---- Noveling year overview (12 mini-calendars) ----------------------- */
.yeargrid { display: grid; grid-template-columns: repeat(4, 1fr); gap: .9rem; max-width: 760px; margin: .4rem 0 1rem; }
@media (max-width: 680px) { .yeargrid { grid-template-columns: repeat(2, 1fr); } }
.minicard { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: .5rem .55rem; }
.mini-name { font-size: .72rem; font-weight: 600; color: var(--muted); margin-bottom: .35rem; }
.minical { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; }
.minical span { aspect-ratio: 1; border-radius: 2px; }
.minical .mc-cell { background: var(--c-grid); }
.minical .mc-cell.on { background: var(--c-green); }
.minical .mc-pad { background: transparent; }

/* ---- Noveling calendar ------------------------------------------------ */
.calendar { width: 100%; max-width: 660px; table-layout: fixed; }
.calendar th { text-align: center; font-size: .72rem; font-weight: 600; color: var(--muted); background: transparent; border: 0; padding: .2rem; }
.calendar td { height: 3.4em; vertical-align: top; padding: .2rem .3rem; }
.calendar .cal-empty { background: transparent; border-color: transparent; }
.calendar .cal-num { font-size: .7rem; color: var(--muted); }
/* Binary day marker: a check for any sprinting; .goal is the stronger
   daily-goal-reached tier (Phase 2). */
.calendar .cal-check { text-align: center; font-size: 1.3rem; font-weight: 700; line-height: 1; margin-top: .25rem; color: var(--c-blue); }
.calendar .cal-check.goal { color: var(--c-green); }

/* ---- Scenario viewer -------------------------------------------------- */
.notice.bad { color: var(--bad); font-style: normal; font-weight: 600; }

.sim-toolbar { display: flex; flex-wrap: wrap; gap: 1rem; align-items: center; justify-content: space-between; margin: .4rem 0 .8rem; }
.sim-toolbar-right { display: flex; flex-wrap: wrap; align-items: center; gap: 1rem; }
.sim-copyfmt-l { font-size: .8rem; color: var(--muted); display: inline-flex; align-items: center; gap: .3rem; }
.sim-copyfmt-l select { padding: .15rem .3rem; }
.sim-toolbar-left { display: flex; flex-wrap: wrap; align-items: center; gap: 1rem; }
.sim-pick { display: flex; align-items: center; gap: .5rem; margin: 0; }
.sim-pick select { min-width: 14rem; }
.sim-reroll { display: flex; align-items: center; gap: .5rem; margin: 0; }
.sim-seed { font-size: .78rem; font-variant-numeric: tabular-nums; }
.sim-title { margin: .2rem 0; }

/* View toggle (reuses the noveling tab look). */
.sim-tabs { display: inline-flex; border: 1px solid var(--border); border-radius: var(--radius-sm); overflow: hidden; }
.sim-tab { padding: .3rem .9rem; font-size: .85rem; color: var(--muted); background: transparent; border: 0; border-right: 1px solid var(--border); border-radius: 0; cursor: pointer; }
.sim-tab:last-child { border-right: 0; }
.sim-tab:hover { background: var(--surface-2); color: var(--ink); }
.sim-tab.active { background: var(--accent); color: #1b1410; font-weight: 600; }

.sim-stage { border: 1px solid var(--border); border-radius: var(--radius); background: var(--paper); overflow: hidden; }
.sim-controls { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; padding: .55rem .8rem; background: var(--surface-2); border-bottom: 1px solid var(--border); }
.sim-controls .btn { padding: .25rem .6rem; font-size: .85rem; }
.sim-scrub { flex: 1; min-width: 12rem; accent-color: var(--accent); }
.sim-time { font-variant-numeric: tabular-nums; color: var(--muted); font-size: .85rem; min-width: 9ch; text-align: right; }
.sim-speed-l { font-size: .8rem; color: var(--muted); display: inline-flex; align-items: center; gap: .3rem; }
.sim-speed-l select { padding: .15rem .3rem; }

/* The message column — a calm "chat" rendering, not pixel-Discord. */
/* Fixed height (not min/max) so the column never resizes as messages
   accumulate during playback — the box stays put and only its content scrolls. */
.sim-playback { padding: .8rem; display: flex; flex-direction: column; gap: .55rem; height: 68vh; overflow: auto; }
.sim-empty { color: var(--muted); font-style: italic; margin: auto; }
.sim-msg { border: 1px solid var(--border); border-radius: var(--radius-sm); padding: .5rem .7rem; background: var(--surface); max-width: 90%; }
.sim-msg.sim-user { align-self: flex-end; background: var(--accent-soft); border-color: color-mix(in srgb, var(--accent) 35%, var(--border)); }
.sim-msg.sim-bot { align-self: flex-start; }
.sim-msg.sim-eph { opacity: .88; border-style: dashed; }
.sim-msg.sim-recent { box-shadow: 0 0 0 2px var(--accent-soft); border-color: var(--accent); }
.sim-meta { display: flex; align-items: baseline; gap: .5rem; margin-bottom: .15rem; }
.sim-name { font-weight: 600; color: var(--c-blue); }
.sim-user .sim-name { color: var(--accent); }
.sim-kind { font-size: .68rem; text-transform: uppercase; letter-spacing: .05em; color: var(--muted); }
.sim-edited { font-size: .68rem; color: var(--muted); font-style: italic; }
/* "only you" pill: an ephemeral reply only the asker would see in Discord. */
.sim-eph-tag { font-size: .64rem; text-transform: uppercase; letter-spacing: .04em; border-radius: 3px; padding: 0 .35em; background: color-mix(in srgb, var(--c-periwinkle) 20%, transparent); color: var(--ink); white-space: nowrap; }
/* `pre-wrap` keeps the runs of spaces the scoreboard lines ranks up with (the
   body HTML carries `<br>` for newlines, so this only preserves spacing, never
   the template's indentation). Discord keeps these spaces too, so the viewer
   mirrors it; the proportional font still makes the alignment approximate. */
.sim-body { overflow-wrap: anywhere; white-space: pre-wrap; }
.sim-body .mention { color: var(--c-periwinkle); background: color-mix(in srgb, var(--c-periwinkle) 16%, transparent); border-radius: 3px; padding: 0 .2em; font-weight: 600; }
.sim-body .ts { color: var(--c-teal); font-variant-numeric: tabular-nums; }
.sim-body code { background: var(--paper); border-radius: 3px; padding: 0 .25em; font-family: var(--font-mono); font-size: .92em; }

/* Copy-exact-text button. The viewer renders messages as HTML, which drops the
   ** / ` markers the scoreboards line up with (spacing is preserved via the
   body's pre-wrap); this hands back the verbatim Discord payload. Muted by
   default, lit on hover, green on success. Pushed to the right of the meta row
   (flex margin) / log cell. */
.sim-copy { margin-left: auto; padding: 0 .45em; font-size: .64rem; line-height: 1.6; text-transform: uppercase; letter-spacing: .05em; color: var(--muted); background: transparent; border: 1px solid var(--border); border-radius: 3px; cursor: pointer; opacity: .5; transition: opacity .12s, color .12s, border-color .12s; }
.sim-msg:hover .sim-copy, .sim-copy:hover, .sim-copy:focus-visible { opacity: 1; }
.sim-copy:hover { color: var(--ink); border-color: var(--accent); }
.sim-copy.copied { opacity: 1; color: var(--good); border-color: var(--good); }

/* Log view: every create/edit op as a row. */
.sim-log { width: 100%; font-size: .88rem; }
.sim-log td, .sim-log th { vertical-align: top; }
.sim-c-time { white-space: nowrap; color: var(--muted); font-variant-numeric: tabular-nums; }
.sim-c-msg { max-width: 60ch; overflow-wrap: anywhere; }
/* In the log there's no .sim-msg wrapper, so float the button to the cell's
   right and reveal it on row hover. */
.sim-c-msg .sim-copy { float: right; margin: 0 0 .15rem .5rem; opacity: 0; }
.sim-logrow:hover .sim-c-msg .sim-copy, .sim-c-msg .sim-copy:focus-visible { opacity: 1; }
.sim-c-msg .mention { color: var(--c-periwinkle); font-weight: 600; }
.sim-c-msg .ts { color: var(--c-teal); }
.sim-op { font-size: .7rem; text-transform: uppercase; letter-spacing: .04em; border-radius: 3px; padding: 0 .35em; }
.sim-op.op-create { background: color-mix(in srgb, var(--good) 22%, transparent); color: var(--ink); }
.sim-op.op-edit { background: color-mix(in srgb, var(--amber) 22%, transparent); color: var(--ink); }
.sim-note { color: var(--muted); font-style: italic; }
/* Rows are clickable: a click seeks playback to that event's moment. */
.sim-logrow { cursor: pointer; }
.sim-logrow:hover { background: var(--surface-2); }

.sim-hint { font-size: .78rem; margin: .45rem 0 0; }
.sim-free { margin: 1.1rem 0; }
.sim-free summary { cursor: pointer; color: var(--link); }
.sim-free .sim-eg { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: .5rem .7rem; font-family: var(--font-mono); font-size: .82rem; white-space: pre; overflow: auto; }
.sim-freeform textarea { width: 100%; font-family: var(--font-mono); font-size: .85rem; padding: .5rem .6rem; resize: vertical; }
.sim-free-actions { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; }
.sim-freeform .btn { margin-top: .5rem; }

/* ---- Public user site (OAuth login + settings) ------------------------- */
/* A link-styled button (the in-nav "log out" form submit). */
.linklike { background: transparent; border: 0; box-shadow: none; color: var(--link); padding: 0; font: inherit; cursor: pointer; }
.linklike:hover { background: transparent; border: 0; text-decoration: underline; color: var(--accent); }

/* Settings forms: one labelled control per row, labels aligned in a column. */
.setform { margin: .15rem 0 1rem; }
.setform:last-child { margin-bottom: 0; }
.field { display: flex; align-items: center; gap: .7rem; margin: .45rem 0; flex-wrap: wrap; }
.field > label:first-child { flex: 0 0 15rem; max-width: 15rem; color: var(--muted); }
.field input[type="text"] { flex: 1; min-width: 12rem; }
.field .hint { color: var(--muted); font-size: .82rem; }
.setform .actions { margin-top: .55rem; }

/* Diff-based settings: only the overrides set on this layer are shown as rows,
   each removable; the rest are added from a picker. */
.setrows { margin: .15rem 0 .6rem; }
.setrow { display: flex; align-items: center; gap: .6rem; margin: .4rem 0; flex-wrap: wrap; }
.setrow-label { flex: 0 0 13rem; max-width: 13rem; }
/* Post-sprint sub-settings nest under the "Post-sprint text" parent; the header
   row labels the group, and a row is greyed + struck through while post-sprint
   text is Off (the sub-setting then has no effect). */
.setrow-child .setrow-label { flex-basis: 11.8rem; max-width: 11.8rem; padding-left: 1.2rem; }
.setrow-head .setrow-label { font-weight: 600; }
.setrow.is-muted { opacity: .55; }
.setrow.is-muted .setrow-label { text-decoration: line-through; }
.setrow .fieldctl { display: inline-flex; align-items: center; gap: .4rem; margin: 0; }
.btn-x { color: var(--muted); border-color: transparent; padding: .1rem .45rem; line-height: 1; }
.btn-x:hover { color: #e66; border-color: currentColor; }
/* Inherited server defaults on a channel card: clickable chips that promote to
   a per-channel override. */
.inherited { margin: .4rem 0 .7rem; }
.chips { display: flex; flex-wrap: wrap; gap: .4rem; margin-top: .3rem; }
.chip-form { margin: 0; }
.chip { background: var(--chip-bg, rgba(127,127,127,.12)); border: 1px dashed var(--border); border-radius: 999px; padding: .2rem .7rem; cursor: pointer; font-size: .85rem; }
.chip:hover { border-style: solid; }
.addform { display: inline-flex; align-items: center; gap: .4rem; margin: .35rem 0 0; }

/* The signed-in identity chip in the top bar. */
.me { display: inline-flex; align-items: center; gap: .4rem; color: var(--muted); }
.me img { width: 22px; height: 22px; border-radius: 50%; }

/* The servers-you-manage list. */
.server-list { list-style: none; padding: 0; margin: .3rem 0 0; }
.server-list li { padding: .35rem 0; border-bottom: 1px solid var(--border); }
.server-list li:last-child { border-bottom: 0; }

/* Sign-in CTA on the landing page. */
.signin { display: inline-flex; align-items: center; gap: .5rem; }

/* ---- Companion (pet) inventory ---------------------------------------- */
/* A square-tile grid, built to also hold badges / other unlocks later. */
.inventory-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(7rem, 1fr));
  gap: .7rem;
  margin: .5rem 0 1.2rem;
}
.pet-tile-form { margin: 0; }
/* Each tile is a clickable button that makes the pet active. */
.pet-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: .35rem;
  width: 100%;
  aspect-ratio: 1;
  padding: .5rem;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  cursor: pointer;
  text-align: center;
}
.pet-tile:hover { border-color: var(--muted); background: var(--surface); }
.pet-tile.is-active { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-soft); }
.pet-tile.is-new { border-color: var(--good); box-shadow: 0 0 0 2px color-mix(in srgb, var(--good) 35%, transparent); }
/* The centred art slot: an emoji glyph, or a pixel design's <img>. */
.pet-art { font-size: 2.4rem; line-height: 1; }
.pet-art img { width: 2.4rem; height: 2.4rem; image-rendering: pixelated; }
.pet-name { font-size: .82rem; color: var(--ink); overflow-wrap: anywhere; line-height: 1.2; }
/* Pixel-design artwork: crisp scaling, sized for the tile; .pet-art-lg in the editor. */
.pet-art-img { image-rendering: pixelated; width: 2.4rem; height: 2.4rem; object-fit: contain; }
.pet-art-lg { width: 4rem; height: 4rem; }
/* Read-only catalog preview tile (owner "view as"): looks like a tile, not clickable. */
.preview-tile { cursor: default; gap: .25rem; }
.preview-tile .hint { font-size: .72rem; }

/* Randomize row under the edit fields. */
.pet-roll { display: flex; flex-wrap: wrap; align-items: center; gap: .4rem; margin: .35rem 0 .1rem; }
.pet-roll .btn { padding: .25rem .6rem; font-size: .82rem; }

/* ---- Pets manager: master-detail ------------------------------------- */
/* Detail beside the roster on desktop; stacked (detail first) on a narrow
   screen, so tapping a tile reveals the detail right above it. */
.pets-layout { display: flex; flex-direction: column; gap: 1rem; }
.pets-detail { order: -1; }            /* mobile: detail above the roster */
.pets-roster { min-width: 0; }
@media (min-width: 46rem) {
  .pets-layout { flex-direction: row; align-items: flex-start; }
  .pets-roster { flex: 1 1 0; order: 0; }
  .pets-detail { flex: 1.25 1 0; order: 0; position: sticky; top: 1rem; }
}
/* The roster grid: smaller tiles than the standalone inventory grid so the
   roster sits comfortably beside the detail. */
.roster-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(5rem, 1fr));
  gap: .5rem;
}
/* A roster tile that's the sprint companion (its name already carries a ✓). */
.pet-tile.is-sitting { border-color: color-mix(in srgb, var(--good) 60%, var(--border)); }
.seat-tile .pet-art, .egg-tile .pet-art { opacity: .9; }
/* The hatching tile sits in the roster; its bar sits under the egg glyph. */
.hatch-tile { cursor: default; }

/* The detail pane: a soft inset panel with the focused pet's stats + actions. */
.pets-detail {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: .9rem 1rem;
}
.detail-hint { margin: .2rem 0; }
.detail-head { display: flex; align-items: center; gap: .6rem; margin-bottom: .4rem; }
.detail-head h3 { margin: 0; }
.detail-art { font-size: 2.4rem; line-height: 1; }
.detail-art .pet-art-img { width: 3rem; height: 3rem; }
.pet-stats { list-style: none; margin: .3rem 0 .6rem; padding: 0; color: var(--ink); }
.pet-stats li { padding: .1rem 0; font-size: .9rem; }
.detail-actions { display: flex; flex-wrap: wrap; gap: .4rem; margin-top: .6rem; align-items: center; }
.detail-actions .inline-form { margin: 0; }
.inline-form { display: inline; }

/* The "pet pet" heart button + the little heart it floats on click. */
.pet-heart { font-size: 1rem; line-height: 1; color: var(--accent); }
.floating-heart {
  position: absolute;
  pointer-events: none;
  font-size: 1.4rem;
  animation: heart-float 1s ease-out forwards;
}
@keyframes heart-float {
  from { opacity: 1; transform: translateY(0) scale(.8); }
  to   { opacity: 0; transform: translateY(-2.5rem) scale(1.3); }
}

/* The appearance editor's full category grid (scrollable). */
.appearance-grid {
  max-height: 18rem;
  overflow-y: auto;
  margin: .5rem 0;
  padding: .4rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
.cat-head { margin: .5rem 0 .25rem; font-size: .78rem; color: var(--muted); text-transform: capitalize; font-weight: 600; }
.cat-head:first-child { margin-top: 0; }
.cat-row { display: flex; flex-wrap: wrap; gap: .3rem; }
.emoji-cell {
  font-size: 1.3rem;
  line-height: 1;
  padding: .25rem .35rem;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  cursor: pointer;
}
.emoji-cell:hover { border-color: var(--accent); background: var(--surface); }
.emoji-cell.is-chosen { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-soft); }

/* A hatching egg's loading bar: fills 0->100% over a fixed 10-minute-looking
   window (the inline animation-delay starts it in the past so it completes
   exactly when the egg actually hatches). */
.hatch-bar {
  width: 100%;
  height: .5rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  overflow: hidden;
}
.hatch-bar > span {
  display: block;
  height: 100%;
  width: 0;
  background: var(--accent);
  animation-name: hatch-fill;
  animation-timing-function: linear;
  animation-fill-mode: forwards;
}
@keyframes hatch-fill { from { width: 0; } to { width: 100%; } }

/* ---- "Your sprints" card ---------------------------------------------- */
.sprint-list { list-style: none; margin: .3rem 0 0; padding: 0; display: flex; flex-direction: column; gap: .5rem; }
.sprint-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .5rem .8rem;
  padding: .5rem .7rem;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
.sprint-where { font-weight: 600; }
.sprint-you { margin-left: auto; color: var(--ink); }

/* ---- Notices: nav bell + unread dot ----------------------------------- */
.nav-bell { position: relative; display: inline-flex; align-items: center; padding: .28rem .6rem; border-radius: var(--radius-sm); color: var(--muted); }
.nav-bell:hover { color: var(--ink); background: var(--surface-2); text-decoration: none; }
.nav-bell .bell-icon { font-size: 1.05rem; line-height: 1; }
.nav-bell .bell-dot {
  position: absolute; top: .15rem; right: .34rem;
  width: .5rem; height: .5rem; border-radius: 99px;
  background: var(--accent); border: 1px solid var(--surface);
}
.visually-hidden {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ---- Notices: modal dialog -------------------------------------------- */
.modal-overlay {
  position: fixed; inset: 0; z-index: 50;
  display: flex; align-items: flex-start; justify-content: center;
  padding: 4rem 1rem 1rem;
  background: rgba(0, 0, 0, .45);
}
.modal-dialog {
  background: var(--surface); color: var(--ink);
  border: 1px solid var(--border); border-radius: var(--radius);
  box-shadow: var(--shadow);
  width: 100%; max-width: 32rem; max-height: 80vh; overflow: auto;
  padding: 1rem 1.15rem; outline: none;
}
.modal-head { display: flex; align-items: center; justify-content: space-between; gap: .6rem; }
.modal-head h2 { margin: 0; }
.modal-close {
  background: transparent; border: 0; color: var(--muted);
  font-size: 1.1rem; line-height: 1; padding: .2rem .4rem; cursor: pointer;
}
.modal-close:hover { color: var(--ink); background: var(--surface-2); }
.modal-actions { margin-top: .9rem; display: flex; justify-content: flex-end; }

/* ---- Notices: inbox list ---------------------------------------------- */
.notice-list { list-style: none; margin: .6rem 0 0; padding: 0; }
.notice-item {
  display: flex; align-items: baseline; gap: .6rem; justify-content: space-between;
  padding: .55rem .2rem; border-bottom: 1px solid var(--border);
}
.notice-item:last-child { border-bottom: 0; }
.notice-item.is-unread { border-left: 3px solid var(--accent); padding-left: .55rem; }
.notice-icon { font-size: 1.2rem; line-height: 1.4; }
.notice-main { display: flex; flex-direction: column; gap: .1rem; flex: 1; }
.notice-title { font-weight: 600; color: var(--ink); }
.notice-body { color: var(--muted); font-size: .92rem; }
