:root {
  /* Sampled from the app icon: a d20 drawn in navy ink on warm paper, with one
     gold face. Navy is the app; the paper is the ground the whole interface
     sits on, which is why the neutrals below are warm rather than gray.

     The gold stays in the mark and does not become a token. An amber already
     means something here -- it is the wash on an encounter -- and a second
     amber that meant "belongs to the app" would put two unrelated messages in
     one colour. */
  --accent: #1c3d63;
  --accent-dark: #0e253c;   /* the ink of the mark, exactly */
  --accent-soft: #b6c6d8;
  --accent-bg: #edf1f6;

  /* Urgency, which is not the brand. Before these were the same variable, so
     an overdue badge and a tag chip were the same color and the interface had
     only one thing it could say. Danger marks work that is late or blocked;
     accent marks what belongs to the app. */
  --danger: #b3283c;
  --danger-bg: #fbeaed;

  /* Warm neutrals: the icon's paper, carried through every panel and rule, so
     navy sits on them the way the ink sits on the card it was drawn on. */
  --gray-50: #faf7f0;
  --gray-100: #f3efe5;
  --gray-200: #e6e0d3;
  --gray-300: #d5cdbd;
  --gray-500: #6d665a;   /* 5.6:1 on --surface -- this carries most of the small metadata text */
  --gray-600: #5b544a;
  --gray-700: #47423a;
  --gray-900: #1f1c17;
  --border: var(--gray-200);

  /* What a card, a panel and an input sit on, and what the page sits on behind
     them. Named rather than written as #fff throughout, because the dark
     theme's whole job is to change these two. */
  --surface: #fffdf8;
  --page: #f6f2e9;

  /* Text sitting on the accent itself. The mark's paper rather than pure white,
     so a button and the brand are lit the same. On the dark theme the accent is
     the light colour, so this has to flip with it or every label disappears. */
  --on-accent: #fdfbf5;

  /* The header is its own pair rather than the accent, because on the dark
     theme the accent becomes the light colour -- and a bar that inverts while
     everything below it stays dark is the one thing the eye cannot ignore. */
  --header-bg: var(--accent-dark);
  --header-fg: #efe9db;

  --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}

/* There are two themes, and they are the palette above and the one below. The
   app inherited four more accent presets from the work-notes app it was forked
   from; they moved the brand colour to something the mark is not, which is a
   choice worth having only if the brand is arbitrary. This one is drawn in
   navy. */

/* ---- Dark ---------------------------------------------------------------
   Not a preset: a whole second palette, because this app is read in dim rooms
   at a table where the screen is the brightest thing in front of everyone.
   Every token is redefined here rather than a few patched, so nothing is left
   at a light-theme value and quietly glowing.

   Danger keeps its meaning and only its lightness moves -- "late" must not
   change what it says with the decor, but it does have to stay readable on a
   dark ground. */
[data-theme="dark"] {
  --on-accent: #101820;
  --header-bg: #101a26;
  --header-fg: #e8e2d3;

  --accent: #7fa9d6;
  --accent-dark: #a3c4e6;   /* on dark, the "stronger" accent is the lighter one */
  --accent-soft: #2f4560;
  --accent-bg: #1b2734;

  --danger: #f2879a;
  --danger-bg: #3a1e25;

  /* The neutrals turn cool here. Warm paper is a lit surface -- there is no
     such thing as a warm dark room -- so the dark theme keeps the ink and
     drops the page it was printed on. */
  --gray-50:  #14181e;
  --gray-100: #1b2029;
  --gray-200: #29303b;
  --gray-300: #3a424f;
  --gray-500: #949cab;      /* 5.9:1 on --surface -- still carries the small text */
  --gray-600: #a9b1bf;
  --gray-700: #c3c9d4;
  --gray-900: #e9ecf1;
  --border: var(--gray-200);

  --surface: #1b2029;
  --page: #14181e;

  color-scheme: dark;
}

/* ---- The three kinds of segment ------------------------------------------
   A wash, not a color scheme: enough to read the shape of a night at a glance
   -- where the fights are, how much talking stands between them -- without
   turning the list into bunting. Anything wearing data-kind gets a pair, and
   everything downstream reads --kind and --kind-bg without knowing which kind
   it got. Deliberately not the accent and not danger: those two already mean
   "belongs to the app" and "this is late", and a scene is neither.

   Dark repeats the pair rather than tinting the light one, for the reason the
   whole dark palette exists -- a pale wash on a dark row is a lamp. */
[data-kind="scene"]     { --kind: #4d6f9e; --kind-bg: #eef2f8; }
[data-kind="encounter"] { --kind: #96692a; --kind-bg: #faf3e6; }
[data-kind="combat"]    { --kind: #9c3a47; --kind-bg: #fbeef0; }

[data-theme="dark"] [data-kind="scene"]     { --kind: #9db6dd; --kind-bg: #232a35; }
[data-theme="dark"] [data-kind="encounter"] { --kind: #d8b070; --kind-bg: #2e2820; }
[data-theme="dark"] [data-kind="combat"]    { --kind: #e2919b; --kind-bg: #322128; }

/* Anything measured -- dates, times, counts, ages, due badges. Tabular figures
   line up in a column, and the shift in voice marks the difference between
   something written and something recorded. */
.mono { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }

/* One focus style everywhere. There was none before. */
:where(a,
button,
input,
select,
textarea,
[tabindex]):focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
  border-radius: 3px;
}

* { box-sizing: border-box; }

/* The hidden attribute has to beat a class that sets display. Without this a
   rule like `.segment-body { display: flex }` silently outranks the browser's
   own `[hidden] { display: none }`, and every panel the app hides by attribute
   is drawn anyway. */
[hidden] { display: none !important; }

body {
  margin: 0;
  background: var(--page);
  color: var(--gray-900);
}

/* ---- application header -------------------------------------------------
   A single bar wearing the icon's own background color, so the brand mark's
   square dissolves into it and leaves the flask. Everything is one row at a
   fixed height: the bar is chrome, and chrome that reflows is chrome you keep
   looking at. Links that used to sit here now live in the two menus. */
.app-header {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  height: 46px;
  padding: 0 0.85rem;
  background: var(--header-bg);
  color: var(--header-fg);
  position: sticky;
  top: 0;
  z-index: 40;
}
.brand {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  color: var(--header-fg);
  text-decoration: none;
  font-weight: 650;
  font-size: 0.95rem;
  letter-spacing: -0.01em;
  white-space: nowrap;
}
.brand img { display: block; border-radius: 3px; }

/* The line at the bottom of every page. Quiet by design: it is a statement of
   record, not something to read. */
.app-footer {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.15rem 0.6rem;
  padding: 1.75rem 1rem 1.25rem;
  border-top: 1px solid var(--border);
  color: var(--gray-500);
  font-size: 0.72rem;
}
.app-footer-build {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}

.navbtn {
  display: flex;
  align-items: center;
  gap: 0.3rem;
  height: 28px;
  padding: 0 0.55rem;
  border: 0;
  border-radius: 5px;
  background: transparent;
  color: var(--header-fg);
  font: inherit;
  font-size: 0.85rem;
  cursor: pointer;
  white-space: nowrap;
}
.navbtn:hover,
.nav-menu.is-open .navbtn { background: rgba(255, 255, 255, 0.16); }
.navbtn .caret { font-size: 0.6rem; opacity: 0.7; }

.nav-menu { position: relative; }
/* Pushes a menu group to the far edge -- put this on the first item of the
   trailing group only; a second auto margin on the item after it would split
   the free space between both instead of collapsing the gap between them. */
.nav-menu-end { margin-left: auto; }
/* Right-edge menus open leftward from their button instead of the usual
   left-aligned drop, so they don't run off the side of the screen. Separate
   from .nav-menu-end because a menu can need this without also being the
   thing that pushes the group -- Links, sitting next to Tasks. */
.menu-right .menu { left: auto; right: 0; }

.iconbtn {
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  border: 0;
  border-radius: 5px;
  background: transparent;
  color: var(--header-fg);
  opacity: 0.85;
  cursor: pointer;
}
.iconbtn:hover { background: rgba(255, 255, 255, 0.16); opacity: 1; }
.logout-form { display: flex; margin: 0; }

/* ---- header menus ---- */
.menu {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  z-index: 50;
  min-width: 13rem;
  padding: 0.3rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 7px;
  box-shadow: 0 10px 26px rgba(33, 29, 40, 0.18);
}
.menu[hidden] { display: none; }
.menu a,
.menu button {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  width: 100%;
  padding: 0.35rem 0.5rem;
  border: 0;
  border-radius: 4px;
  background: none;
  color: var(--gray-900);
  font: inherit;
  font-size: 0.83rem;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
}
.menu a:hover,
.menu button:hover { background: var(--accent-bg); color: var(--accent-dark); }
.menu .hint { color: var(--gray-500); font-family: var(--font-mono); font-size: 0.7rem; }
.menu-label {
  padding: 0.3rem 0.5rem 0.2rem;
  font-size: 0.65rem;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--gray-500);
}

/* ---- header search ---- */
.nav-search { position: relative; flex: 1 1 auto; max-width: 34rem; }
.nav-search-icon {
  position: absolute;
  left: 0.5rem;
  top: 50%;
  transform: translateY(-50%);
  color: var(--on-accent);
  opacity: 0.65;
  pointer-events: none;
}
.header-search-input {
  width: 100%;
  height: 28px;
  padding: 0 0.55rem 0 1.75rem;
  border: 1px solid rgba(255, 255, 255, 0.28);
  border-radius: 5px;
  background: rgba(255, 255, 255, 0.12);
  color: var(--on-accent);
  font: inherit;
  font-size: 0.85rem;
}
.header-search-input::placeholder { color: rgba(255, 255, 255, 0.68); }
.header-search-input:focus { background: rgba(255, 255, 255, 0.2); outline-color: var(--header-fg); }

/* ---- page title row ----
   The bar carries the app's name, so a page's own name goes here rather than
   competing with it. */
.page-title {
  padding: 0.55rem 1rem 0;
}
.page-title h1 {
  margin: 0;
  font-size: 1.05rem;
  font-weight: 600;
  color: var(--gray-900);
}

/* Generic button-styled link/button (export/backup actions, page controls). */
.btn,
.btn-action {
  display: inline-block;
  border: 1px solid var(--gray-300);
  background: var(--gray-100);
  color: var(--gray-700);
  border-radius: 6px;
  padding: 0.35rem 0.75rem;
  font-size: 0.82rem;
  font-family: inherit;
  text-decoration: none;
  cursor: pointer;
}
.btn:hover,
.btn-action:hover { border-color: var(--accent); color: var(--accent); text-decoration: none; }

/* For the one action in a panel that is the point of it -- the others stay
   quiet so the recommended one does not have to shout. */
.btn-primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-accent);
}
.btn-primary:hover { background: var(--accent-dark); border-color: var(--accent-dark); color: var(--on-accent); }

main {
  padding: 0.75rem 0.75rem 3rem;
}

.panel {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 0.7rem 0.8rem;
  margin-bottom: 0.7rem;
}
/* A panel heading is a label, not a title: small, plum, and followed by a rule
   that runs to the panel's summary on the right. Costs a fifth of the vertical
   space the old shouting headings did, and carries a fact besides. */
.panel h2 {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin: 0 0 0.55rem;
  font-size: 0.66rem;
  font-weight: 650;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--accent);
}
/* The first heading in a panel gets its air from the panel's own padding. A
   later one is starting a new group inside the panel and has to make its own,
   or it reads as a caption on the block above it rather than a heading for the
   block below. */
.panel h2:not(:first-child) { margin-top: 1.5rem; }

.panel h2::after {
  content: "";
  order: 1;
  flex: 1;
  height: 1px;
  background: var(--border);
}
.panel-summary {
  order: 2;
  flex: 0 0 auto;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: 0.7rem;
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--gray-500);
}

/* #tag chips. Drawn over text that still reads "#tag" underneath (see
   tag-chips.js), so they stay inline and keep the surrounding line's size --
   a chip that changed the line height would reflow a note on every edit. */
.tag-chip {
  color: var(--accent);
  text-decoration: none;
  background: var(--accent-bg);
  border-radius: 3px;
  padding: 0 0.2rem;
  white-space: nowrap;
}
.tag-chip:hover { text-decoration: underline; }

/* Day stack */
#load-more-wrap { margin-bottom: 0.5rem; }
#load-more {
  background: var(--gray-100);
  border: 1px solid var(--gray-300);
  border-radius: 6px;
  padding: 0.4rem 0.75rem;
  cursor: pointer;
  font-size: 0.8rem;
}

.day { position: relative; }

.matrix-item {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  background: var(--surface);
  border: 1px solid var(--gray-200);
  border-radius: 4px;
  padding: 0.2rem 0.35rem;
  font-size: 0.78rem;
  cursor: grab;
}
.matrix-item.migrated { opacity: 0.5; }

.modal-actions .danger { color: var(--accent); }

/* Migration page */
.migrate-item {
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 0.75rem;
  margin: 0.75rem 0;
}
.migrate-item h3 { margin: 0 0 0.5rem; font-size: 0.9rem; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
.migrate-item summary { font-size: 0.78rem; color: var(--gray-500); cursor: pointer; }
.migrate-sql,
.migrate-error {
  background: var(--gray-50);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 0.6rem;
  font-size: 0.72rem;
  line-height: 1.45;
  overflow-x: auto;
  white-space: pre-wrap;
  word-break: break-word;
}
.migrate-error { border-color: var(--accent); background: var(--accent-bg); color: var(--gray-900); }
.migrate-ok { font-size: 0.85rem; color: var(--gray-700); }
.migrate-run-form {
  display: flex;
  align-items: flex-end;
  gap: 0.6rem;
  flex-wrap: wrap;
  margin-top: 0.6rem;
}
.migrate-run-form label {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  font-size: 0.75rem;
  color: var(--gray-700);
  flex: 1 1 20rem;
}
.migrate-run-form input[type="text"] {
  border: 1px solid var(--gray-300);
  border-radius: 4px;
  padding: 0.35rem 0.5rem;
  font-size: 0.82rem;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}

#notes-overlay-slot { flex: 1; display: flex; min-height: 0; }

/* Modals */
dialog.modal {
  border: none;
  border-radius: 8px;
  padding: 1.25rem;
  width: 360px;
  max-width: 90vw;
}
dialog.modal::backdrop { background: rgba(0, 0, 0, 0.35); }
dialog.modal h3 { margin: 0 0 1rem; font-size: 1rem; }
dialog.modal h4 { margin: 1.25rem 0 0.5rem; font-size: 0.85rem; color: var(--gray-700); }
dialog.modal label {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  font-size: 0.8rem;
  color: var(--gray-700);
  margin-bottom: 0.75rem;
}
dialog.modal input[type="text"],
dialog.modal input[type="date"],
dialog.modal input[type="number"],
dialog.modal select {
  border: 1px solid var(--gray-300);
  border-radius: 4px;
  padding: 0.35rem 0.5rem;
  font-size: 0.85rem;
  font-family: inherit;
}
.modal-hint { font-size: 0.75rem; color: var(--gray-500); margin: 0 0 0.5rem; }

.modal-actions { display: flex; justify-content: flex-end; gap: 0.5rem; margin-top: 0.5rem; }
.modal-actions button {
  border-radius: 4px;
  padding: 0.4rem 0.8rem;
  font-size: 0.8rem;
  cursor: pointer;
  border: 1px solid var(--gray-300);
  background: var(--gray-100);
}
.modal-actions .modal-primary { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }

/* Mobile: kanban board becomes a single vertical stack of columns instead of
   a grid, since a 2-up grid of 5 columns is still too cramped on a phone. */
@media (max-width: 600px) {
  /* The bar stays one row on a phone -- that is what collapsing eleven links
     into two menus bought. The brand's wordmark is the only thing that goes,
     since the icon beside it already says where you are. */
  .app-header { gap: 0.4rem; padding: 0 0.5rem; }
  .brand { font-size: 0; gap: 0; }
  .brand img { margin-right: 0.1rem; }
  .header-search-input::placeholder { color: transparent; }
  main { padding: 0.5rem 0.5rem 2rem; }
  
  
}

/* ---- People / per-person view ---- */

/* Generic button chrome for buttons outside the header nav (note skeletons,
   delete affordances). The .app-header nav rule is more specific, so the
   header keeps its own look. */
.btn-action {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 0.3rem 0.6rem;
  font: inherit;
  font-size: 0.78rem;
  color: var(--gray-700);
  cursor: pointer;
}
.btn-action:hover { border-color: var(--accent); color: var(--accent); }
.btn-action:disabled { opacity: 0.5; cursor: default; }

.note-template {
  border: 1px solid var(--border);
  border-radius: 6px;
  margin-bottom: 0.8rem;
  overflow: hidden;
}

/* Results dropdown for the header search field, which is styled up in the header block. */
.header-search-results {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  right: 0;
  z-index: 30;
  max-height: 65vh;
  overflow-y: auto;
  padding: 0.25rem 0;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 6px;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.12);
}
/* Results are ranked across kinds rather than grouped, so the kind is a badge
   on each row instead of a heading above a run of them. */
.header-search-kind {
  display: block;
  font-size: 0.62rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--gray-500);
}
.header-search-filters,
.header-search-notice {
  padding: 0.35rem 0.75rem;
  font-size: 0.7rem;
  color: var(--gray-500);
  border-bottom: 1px solid var(--border);
}
.header-search-filters { color: var(--accent); }
.header-search-result {
  display: block;
  margin: 0;
  padding: 0.35rem 0.75rem;
  border-left: none;
  text-decoration: none;
  color: var(--gray-900);
  font-size: 0.82rem;
}
.header-search-result:hover { background: var(--gray-100); text-decoration: none; color: var(--gray-900); }
.header-search-title { display: block; }
.header-search-title mark { background: var(--accent-bg); color: inherit; padding: 0 1px; border-radius: 2px; }

.header-search-snippet { display: block; font-size: 0.72rem; color: var(--gray-500); }
.header-search-empty { padding: 0.5rem 0.75rem; font-size: 0.8rem; color: var(--gray-500); }

/* Logout is a POST action but is visually part of the navigation. */

/* ---- Pages: the world as a tree of markdown documents ---------------------
   Two columns at a width worth designing for, one below it. The tree is the
   primary interface, so it keeps its width and the document takes the rest. */
.pages-layout {
  display: grid;
  grid-template-columns: minmax(220px, 300px) minmax(0, 1fr);
  gap: 1rem;
  align-items: start;
}
@media (max-width: 800px) { .pages-layout { grid-template-columns: 1fr; } }

.pages-tree-head { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; }
.pages-tree-head h2 { margin: 0; }

.pages-filter { display: flex; flex-wrap: wrap; gap: 0.3rem; margin: 0.6rem 0 0.8rem; }
.pages-filter .chip {
  border: 1px solid var(--gray-300); border-radius: 999px; background: var(--surface);
  padding: 0.15rem 0.6rem; font: inherit; font-size: 0.75rem; cursor: pointer; color: var(--gray-600);
}
.pages-filter .chip:hover { border-color: var(--gray-500); }
.pages-filter .chip.is-active { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }

.pages-tree-list,
.pages-tree-list ul { list-style: none; margin: 0; padding: 0; }
.pages-tree-list ul { margin-left: 0.8rem; border-left: 1px solid var(--gray-200); padding-left: 0.4rem; }
.page-node-row {
  display: flex; align-items: baseline; gap: 0.4rem; width: 100%;
  border: 0; background: none; text-align: left; font: inherit; font-size: 0.85rem;
  padding: 0.22rem 0.35rem; border-radius: 4px; cursor: pointer; color: inherit;
}
.page-node-row:hover { background: var(--gray-100); }
.page-node-row.is-current { background: var(--accent-bg); color: var(--accent-dark); font-weight: 600; }
.page-node-kind {
  font-size: 0.62rem; text-transform: uppercase; letter-spacing: 0.04em;
  color: var(--gray-500); flex: none; min-width: 3.6em;
}

.page-head { display: flex; align-items: center; gap: 0.6rem; margin-bottom: 0.75rem; }
.page-head h2 { margin: 0; flex: 1 1 auto; cursor: text; }
.page-head h2:hover { text-decoration: underline dotted var(--gray-300); }

.page-source {
  width: 100%; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.82rem; line-height: 1.55; border: 1px solid var(--accent-soft);
  border-radius: 6px; padding: 0.7rem; resize: vertical; background: var(--surface);
}
.page-body { min-height: 6rem; cursor: text; }
.page-status { min-height: 1.1rem; margin: 0.4rem 0 0; }

.page-backlinks { margin-top: 1.5rem; border-top: 1px solid var(--gray-200); padding-top: 0.8rem; }
.page-backlinks h3 { font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--gray-500); margin: 0 0 0.4rem; }
.page-backlinks ul { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 0.4rem 1rem; font-size: 0.85rem; }

/* ---- Rendered markdown ---------------------------------------------------
   A page link and a redlink differ by one border: a dotted underline says the
   page is owed rather than missing, which is the state a DM spends the week in. */
.pagelink { color: var(--accent-dark); text-decoration: none; border-bottom: 1px solid var(--accent-soft); }
.pagelink:hover { border-bottom-color: var(--accent); }
.pagelink.is-redlink { color: var(--gray-500); border-bottom: 1px dotted var(--gray-400); }
.pagelink.is-redlink:hover { color: var(--accent); border-bottom-color: var(--accent); }

.markdown { line-height: 1.6; }
.markdown > :first-child { margin-top: 0; }
.markdown h1,
.markdown h2,
.markdown h3,
.markdown h4,
.markdown h5,
.markdown h6 {
  margin: 1.4em 0 0.4em; line-height: 1.25;
}
.markdown h1 { font-size: 1.35rem; }
.markdown h2 { font-size: 1.12rem; }
.markdown h3 { font-size: 0.98rem; }
.markdown h4,
.markdown h5,
.markdown h6 { font-size: 0.88rem; color: var(--gray-600); }
.markdown p { margin: 0 0 0.85em; }
.markdown ul,
.markdown ol { margin: 0 0 0.85em; padding-left: 1.4em; }
.markdown li { margin-bottom: 0.2em; }
.markdown blockquote {
  margin: 0 0 0.85em; padding: 0.1em 0 0.1em 0.9em;
  border-left: 3px solid var(--accent-soft); color: var(--gray-600);
}
.markdown code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.85em; background: var(--gray-100); padding: 0.1em 0.3em; border-radius: 3px;
}
.markdown pre {
  background: var(--gray-100); border: 1px solid var(--gray-200); border-radius: 6px;
  padding: 0.7rem; overflow-x: auto; margin: 0 0 0.85em;
}
.markdown pre code { background: none; padding: 0; font-size: 0.8rem; }
.markdown hr { border: 0; border-top: 1px solid var(--gray-200); margin: 1.4em 0; }
.markdown img { max-width: 100%; height: auto; border-radius: 6px; }
/* Wide tables scroll inside the document rather than widening the page. */
.markdown table { border-collapse: collapse; font-size: 0.85rem; margin: 0 0 0.85em; display: block; overflow-x: auto; }
.markdown th,
.markdown td { border: 1px solid var(--gray-200); padding: 0.35rem 0.55rem; text-align: left; }
.markdown thead th { background: var(--gray-100); }

/* ---- Games and segments --------------------------------------------------
   A game is its date plus an ordered list of segments. The list is the screen:
   everything else on the page is a header for it. */
.game-head { display: flex; align-items: flex-end; flex-wrap: wrap; gap: 0.75rem; margin-bottom: 0.9rem; }
.game-head h2 { margin: 0; margin-right: auto; }
.game-field { display: flex; flex-direction: column; gap: 0.15rem; font-size: 0.72rem; color: var(--gray-500); }
.game-field input { font: inherit; font-size: 0.85rem; padding: 0.25rem 0.4rem; border: 1px solid var(--gray-300); border-radius: 4px; }
.game-field-wide input { min-width: 18rem; }
.game-status { min-width: 4rem; }

.segment-add { display: flex; gap: 0.4rem; margin-bottom: 0.8rem; }
.segment-add input[type="text"] { flex: 1 1 auto; font: inherit; font-size: 0.9rem; padding: 0.35rem 0.5rem; border: 1px solid var(--gray-300); border-radius: 5px; }
.segment-add select { font: inherit; font-size: 0.85rem; border: 1px solid var(--gray-300); border-radius: 5px; }

.segment-list { list-style: none; margin: 0; padding: 0; }
/* Clipped, so the header's wash takes the row's corners with it whether the
   segment is open or shut -- otherwise a tinted bar squares off the top of a
   rounded row. */
.segment { border: 1px solid var(--gray-200); border-radius: 6px; overflow: hidden; margin-bottom: 0.5rem; background: var(--surface); }
/* The header carries the kind, so the list can be skimmed as a shape. Only the
   bar is washed -- the body underneath stays on the plain surface, because it is
   text to read rather than a thing to recognise. */
.segment-bar {
  display: flex; align-items: center; gap: 0.6rem; padding: 0.5rem 0.6rem; cursor: pointer;
  background: var(--kind-bg, transparent);
}
.segment-grip { color: var(--gray-300); cursor: grab; letter-spacing: -0.25em; }
.segment-kind {
  font-size: 0.62rem; text-transform: uppercase; letter-spacing: 0.05em;
  color: var(--kind, var(--accent-dark)); background: var(--surface);
  padding: 0.1rem 0.4rem; border-radius: 999px; flex: none;
}
.segment-title { font-weight: 600; font-size: 0.9rem; }
.segment-flags { font-size: 0.72rem; color: var(--gray-500); font-style: italic; }
.segment-actions { margin-left: auto; display: flex; align-items: center; gap: 0.3rem; }
.btn-quiet {
  border: 1px solid var(--gray-300); background: var(--surface); border-radius: 4px;
  font: inherit; font-size: 0.72rem; padding: 0.15rem 0.45rem; cursor: pointer; color: var(--gray-600);
}
.btn-quiet:hover { border-color: var(--accent); color: var(--accent-dark); }

/* A cut segment stays visible: the gap in the game is the point of keeping it. */
.segment.is-cut { opacity: 0.6; }
.segment.is-cut .segment-title { text-decoration: line-through; }
.segment.is-played { border-left: 3px solid var(--accent); }

.segment-body { display: flex; flex-wrap: wrap; gap: 1rem; padding: 0 0.7rem 0.7rem; }
.segment-half { flex: 1 1 260px; min-width: 0; }
/* The rail is hidden entirely when nothing belongs beside this segment, which
   flex handles without leaving an empty column behind. */
.segment-rail { flex: 1 1 220px; min-width: 0; border-left: 1px solid var(--gray-200); padding-left: 0.9rem; }
.segment-half h4 {
  font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--gray-500); margin: 0 0 0.3rem;
}
.segment-half .markdown { min-height: 3rem; cursor: text; font-size: 0.88rem; }
.segment-source {
  width: 100%; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.8rem; line-height: 1.55; border: 1px solid var(--accent-soft);
  border-radius: 5px; padding: 0.5rem; resize: vertical;
}

/* The party, across the top of the game and of the running screen. A row that
   scrolls sideways rather than a grid that reflows: five characters at a table
   is five cards, and they should stay the same size whatever else is on screen. */
.party-bar { display: flex; gap: 0.5rem; overflow-x: auto; margin-bottom: 0.8rem; padding-bottom: 0.3rem; }
.party-block {
  flex: 0 0 auto; width: 13rem; max-height: 9rem; overflow-y: auto;
  border: 1px solid var(--gray-200); border-radius: 6px;
  padding: 0.4rem 0.55rem; background: var(--surface);
}

.game-stack-list { list-style: none; margin: 0; padding: 0; }
.game-row + .game-row { border-top: 1px solid var(--gray-200); }
.game-row-link {
  display: flex; align-items: baseline; gap: 0.75rem; padding: 0.5rem 0.3rem;
  text-decoration: none; color: inherit;
}
.game-row-link:hover { background: var(--gray-100); }
.game-row-number { font-weight: 600; font-size: 0.88rem; flex: none; min-width: 5rem; }
.game-row-world { font-size: 0.82rem; color: var(--gray-600); font-style: italic; }
.game-row-facts { margin-left: auto; font-size: 0.75rem; color: var(--gray-500); }
/* The game being prepped is the one you came here for. */
.game-row.is-unplayed .game-row-number { color: var(--accent-dark); }
.game-row.is-unplayed .game-row-number::after { content: " · prepping"; font-weight: 400; font-size: 0.7rem; color: var(--gray-500); }
/* The campaign so far, folded into one line above the recent games. */
.game-row-earlier { padding: 0.3rem 0.3rem 0.45rem; }
.game-row-earlier button { width: 100%; text-align: left; border-style: dashed; }

/* ---- Info blocks ---------------------------------------------------------
   Reference, random tables and stat lines are one thing, so they get one list
   and one editor. A block's settings live with its contents rather than in a
   dialog: what it says and where it shows up are the same decision. */
.block-add { display: flex; gap: 0.4rem; margin-bottom: 0.8rem; }
.block-add input { flex: 1 1 auto; font: inherit; font-size: 0.9rem; padding: 0.35rem 0.5rem; border: 1px solid var(--gray-300); border-radius: 5px; }

.block-list { list-style: none; margin: 0; padding: 0; }
.block-item { border: 1px solid var(--gray-200); border-radius: 6px; margin-bottom: 0.5rem; background: var(--surface); }
.block-bar { display: flex; align-items: center; gap: 0.6rem; padding: 0.5rem 0.6rem; cursor: pointer; }
.block-title { font-weight: 600; font-size: 0.9rem; }
.block-views { font-size: 0.72rem; color: var(--gray-500); }
.block-actions { margin-left: auto; display: flex; align-items: center; gap: 0.3rem; }
.block-rolled,
.rail-rolled {
  margin: 0 0.6rem 0.5rem; padding: 0.4rem 0.6rem; background: var(--accent-bg);
  border-radius: 5px; font-size: 0.85rem; color: var(--accent-dark);
}
.block-editor { padding: 0 0.7rem 0.7rem; }
.block-source {
  width: 100%; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.8rem; line-height: 1.55; border: 1px solid var(--accent-soft);
  border-radius: 5px; padding: 0.5rem; resize: vertical;
}
.block-rendered { min-height: 3rem; cursor: text; font-size: 0.88rem; }
.block-settings { display: flex; flex-wrap: wrap; gap: 0.4rem 1.1rem; margin-top: 0.6rem; font-size: 0.78rem; color: var(--gray-600); }
.block-settings label { display: flex; align-items: center; gap: 0.3rem; }

/* A block called for by name inside a body. The block itself is in the rail;
   this is what tells the writer the call landed. */
.blockref {
  font-size: 0.8rem; color: var(--accent-dark); background: var(--accent-bg);
  border-radius: 4px; padding: 0.05rem 0.35rem; text-decoration: none;
}
.blockref:hover { background: var(--accent-soft); }
.blockref.is-missing { color: var(--gray-500); background: var(--gray-100); border: 1px dashed var(--gray-300); }

.rail-block + .rail-block { margin-top: 0.9rem; }
.rail-block h5,
.party-block h5 {
  display: flex; align-items: center; gap: 0.4rem; margin: 0 0 0.25rem;
  font-size: 0.78rem; font-weight: 600;
}
.rail-block h5 .rail-roll,
.party-block h5 .rail-roll { margin-left: auto; }
.rail-block-body { font-size: 0.82rem; }
.rail-rolled { margin: 0 0 0.4rem; }

/* ---- Dice ----------------------------------------------------------------
   The tray sits over the corner of whatever page you are on, because rolling is
   something you do *during* something else — reading a scene, running a fight —
   and it must never be a place you go.

   The dice are flat polygons rather than anything pretending to be three
   dimensional. A real tumbling d20 needs a 3D engine, and this app has no build
   step to bring one through; a silhouette that changes its face fast and lands
   with a bounce reads as a thrown die at the size it is actually shown. */
.dice-tray {
  position: fixed; right: 1rem; bottom: 1rem; z-index: 50;
  width: 19rem; max-width: calc(100vw - 2rem);
  padding: 0.6rem 0.7rem 0.7rem;
  border: 1px solid var(--border); border-radius: 8px;
  background: var(--surface); color: var(--gray-900);
  box-shadow: 0 6px 24px rgb(0 0 0 / 0.18);
}
.dice-tray-head { display: flex; align-items: center; margin: 0 0 0.4rem; font-size: 0.78rem; letter-spacing: 0.05em; text-transform: uppercase; color: var(--gray-600); }
.dice-tray-head .dice-close { margin-left: auto; }

.dice-stage { min-height: 5.5rem; }
.dice-label { margin: 0 0 0.35rem; font-size: 0.84rem; }
.dice-sub { margin-left: 0.4rem; font-size: 0.72rem; color: var(--gray-500); }

.dice-pool { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; }
.dice-set { display: flex; flex-wrap: wrap; gap: 0.25rem; }
/* The set that was thrown away under advantage stays on screen: seeing what you
   dodged is most of the reason for rolling twice. */
.dice-set.is-dropped { opacity: 0.4; }

.die { position: relative; display: inline-grid; place-items: center; width: 2.4rem; height: 2.4rem; }
.die svg { position: absolute; inset: 0; width: 100%; height: 100%; }
.die polygon { fill: var(--accent-bg); stroke: var(--accent); stroke-width: 5; stroke-linejoin: round; }
.die.is-dropped polygon { fill: var(--gray-100); stroke: var(--gray-300); }
.die-face {
  position: relative; font-family: var(--font-mono); font-size: 0.86rem;
  font-variant-numeric: tabular-nums; color: var(--gray-900);
}
.die.is-rolling { animation: die-tumble 0.28s linear infinite; }
.die.is-landed { animation: die-land 0.3s ease-out 1; }

@keyframes die-tumble {
  0%   { transform: rotate(0deg) scale(1); }
  50%  { transform: rotate(180deg) scale(1.12); }
  100% { transform: rotate(360deg) scale(1); }
}
@keyframes die-land {
  0%   { transform: scale(1.25); }
  60%  { transform: scale(0.94); }
  100% { transform: scale(1); }
}
/* Somebody who has asked for less movement gets the number, immediately. */
@media (prefers-reduced-motion: reduce) {
  .die.is-rolling, .die.is-landed { animation: none; }
}

.dice-total { display: flex; align-items: baseline; gap: 0.5rem; margin: 0.5rem 0 0; }
.dice-sum { font-family: var(--font-mono); font-size: 1.6rem; line-height: 1; }
.dice-working { font-size: 0.72rem; color: var(--gray-500); }
.dice-crit { font-size: 0.72rem; font-weight: 600; }
.dice-crit.is-max { color: var(--accent); }
.dice-crit.is-min { color: var(--danger); }

.dice-history { list-style: none; margin: 0.6rem 0 0; padding: 0.5rem 0 0; border-top: 1px solid var(--border); font-size: 0.74rem; color: var(--gray-600); }
.dice-history li { display: flex; gap: 0.4rem; padding: 0.05rem 0; }
.dice-history b { min-width: 1.8rem; text-align: right; color: var(--gray-900); }
.dice-history li.is-max b { color: var(--accent); }
.dice-history li.is-min b { color: var(--danger); }

.dice-controls { margin-top: 0.6rem; padding-top: 0.5rem; border-top: 1px solid var(--border); }
.dice-buttons { display: flex; flex-wrap: wrap; gap: 0.25rem; }
.dice-pick {
  flex: 1 1 auto; font: inherit; font-size: 0.74rem; padding: 0.25rem 0.3rem;
  border: 1px solid var(--gray-300); border-radius: 4px;
  background: var(--surface); color: var(--gray-700); cursor: pointer;
}
.dice-pick:hover { border-color: var(--accent); color: var(--accent); }
.dice-fields { display: flex; align-items: center; gap: 0.3rem; margin-top: 0.4rem; font-size: 0.72rem; color: var(--gray-500); }
.dice-count, .dice-mod {
  width: 3rem; font: inherit; font-size: 0.78rem; padding: 0.15rem 0.25rem;
  border: 1px solid var(--gray-300); border-radius: 4px;
  background: var(--surface); color: var(--gray-900);
}
.dice-adv { display: flex; margin-left: auto; }
.dice-advantage {
  font: inherit; font-size: 0.7rem; padding: 0.15rem 0.4rem;
  border: 1px solid var(--gray-300); background: var(--surface); color: var(--gray-600); cursor: pointer;
}
.dice-advantage:first-child { border-radius: 4px 0 0 4px; }
.dice-advantage:last-child { border-radius: 0 4px 4px 0; }
.dice-advantage + .dice-advantage { border-left: none; }
.dice-advantage.is-on { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }

/* Anything on a sheet that can be thrown. It reads as a number until you point
   at it, because the sheet is mostly for reading. */
.roll {
  font: inherit; font-variant-numeric: tabular-nums; padding: 0 0.2rem;
  border: 1px solid transparent; border-radius: 4px;
  background: none; color: inherit; cursor: pointer;
}
.roll:hover { border-color: var(--accent); color: var(--accent); }

/* ---- The odds on a check -------------------------------------------------
   A computed block. The curve is the point: where it falls off is the DC worth
   setting, and that is a shape rather than a number, which is why it is drawn
   at all. Sized to sit in the rail beside a segment without scrolling. */
.party-odds { display: flex; flex-direction: column; gap: 0.35rem; }
.odds-check {
  font: inherit; font-size: 0.76rem; padding: 0.2rem 0.3rem;
  border: 1px solid var(--gray-300); border-radius: 4px;
  background: var(--surface); color: var(--gray-900);
}
.odds-figure { line-height: 0; }
.odds-chart { width: 100%; height: 5.5rem; overflow: visible; }
.odds-area { fill: var(--accent); opacity: 0.16; }
.odds-line { fill: none; stroke: var(--accent); stroke-width: 2; vector-effect: non-scaling-stroke; }
.odds-half, .odds-tick { stroke: var(--border); stroke-width: 1; vector-effect: non-scaling-stroke; }
.odds-tick-label { fill: var(--gray-500); font-size: 8px; text-anchor: middle; }
.odds-at { stroke: var(--gray-500); stroke-width: 1; stroke-dasharray: 2 2; vector-effect: non-scaling-stroke; }
.odds-at-dot { fill: var(--accent); }
.odds-answer { margin: 0; font-size: 0.76rem; color: var(--gray-600); }
.odds-answer b { font-size: 0.95rem; color: var(--gray-900); }
.odds-dc { width: 100%; accent-color: var(--accent); }
.odds-party { margin: 0; font-size: 0.68rem; color: var(--gray-500); }

/* ---- Recordings ----------------------------------------------------------
   The night's audio, under the party and above the scenes -- roughly where it
   sits in the evening. The native player is used as it comes: it already knows
   how to scrub, and a hand-built one would be a worse copy of something every
   browser ships. */
.recordings { margin-bottom: 0.8rem; }
.recording { padding: 0.4rem 0 0.5rem; border-bottom: 1px solid var(--border); }
.recording-head { display: flex; align-items: baseline; gap: 0.5rem; margin: 0 0 0.25rem; font-size: 0.84rem; }
.recording-meta { font-size: 0.72rem; color: var(--gray-500); }
.recording-head .recording-detach { margin-left: auto; }
.recording audio { width: 100%; height: 2.2rem; }
.recording-offer { margin: 0.5rem 0 0; }
.recording-attach { font-size: 0.74rem; }

/* ---- Quotes --------------------------------------------------------------
   A library of lines, so the line is what the eye lands on: the quote is set
   larger than anything around it and the attribution is small underneath, which
   is the shape a quotation has had for a very long time. */
.run-quote {
  display: flex; align-items: center; gap: 0.5rem; margin: 0.4rem 0 0;
  padding: 0.3rem 0.5rem; font-size: 0.78rem;
  border: 1px solid var(--accent-soft); border-radius: 5px; background: var(--accent-bg);
}
.run-quote-text { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.run-quote-speaker { font: inherit; font-size: 0.78rem; padding: 0.15rem 0.35rem; border: 1px solid var(--gray-300); border-radius: 4px; background: var(--surface); color: var(--gray-900); }

.quote-add { display: flex; flex-wrap: wrap; gap: 0.4rem; margin-bottom: 1rem; }
.quote-add input[name="quote"] { flex: 1 1 20rem; }
.quote-add input, .quote-add select {
  font: inherit; font-size: 0.9rem; padding: 0.35rem 0.5rem;
  border: 1px solid var(--gray-300); border-radius: 5px;
  background: var(--surface); color: var(--gray-900);
}
.quote-game + .quote-game { margin-top: 1.2rem; }
.quote-game h3 {
  display: flex; align-items: baseline; gap: 0.5rem; margin: 0 0 0.5rem;
  font-size: 0.78rem; letter-spacing: 0.05em; text-transform: uppercase; color: var(--gray-600);
}
.quote-date { font-size: 0.72rem; color: var(--gray-500); }
.quote-list { list-style: none; margin: 0; padding: 0; }
.quote { padding: 0.5rem 0 0.6rem; border-bottom: 1px solid var(--border); }
.quote-text {
  margin: 0; font-size: 1rem; line-height: 1.4; color: var(--gray-900);
  border-left: 2px solid var(--accent-soft); padding-left: 0.6rem;
}
.quote-by { display: flex; align-items: baseline; gap: 0.5rem; margin: 0.25rem 0 0 0.6rem; font-size: 0.78rem; color: var(--gray-600); }
.quote-speaker { cursor: text; }
.quote-speaker::before { content: "— "; }
.quote-speaker.is-empty { color: var(--gray-500); font-style: italic; }
.quote-speaker-input { font: inherit; font-size: 0.78rem; padding: 0.1rem 0.3rem; border: 1px solid var(--gray-300); border-radius: 4px; background: var(--surface); color: var(--gray-900); }
.quote-context { color: var(--gray-500); }
.quote-by .quote-delete { margin-left: auto; }

/* ---- Calendar ------------------------------------------------------------
   Months at a glance, all of them at once. Sized so a year fits on a laptop
   screen without scrolling past the shape of it, which is the only reason to
   draw a calendar rather than list dates. */
.calendar-months {
  display: grid; gap: 1rem;
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
}
.cal-month h3 { margin: 0 0 0.35rem; font-size: 0.8rem; letter-spacing: 0.04em; text-transform: uppercase; color: var(--gray-600); }
.cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 1px; }
.cal-weekday { font-size: 0.6rem; text-align: center; color: var(--gray-500); padding-bottom: 0.15rem; }
.cal-day {
  position: relative; min-height: 2.1rem; padding: 0.15rem 0.2rem;
  font-size: 0.68rem; color: var(--gray-500);
  border: 1px solid var(--border); border-radius: 3px; background: var(--surface);
}
.cal-day.is-today { border-color: var(--gray-500); }
/* A session is the one thing on this screen worth seeing from across the room,
   so it takes the accent outright rather than a tint of it. */
.cal-day.is-played {
  display: block; text-decoration: none;
  background: var(--accent); border-color: var(--accent); color: var(--on-accent);
}
.cal-day.is-played:hover { background: var(--accent-dark); }
.cal-day b { display: block; font-size: 0.66rem; font-weight: 600; }
.run-search-all { flex: 0 0 auto; margin-left: auto; font-size: 0.7rem; text-decoration: none; }

.calendar-unplayed { margin-top: 0.9rem; font-size: 0.8rem; color: var(--gray-600); }

/* ---- Characters ----------------------------------------------------------
   A card is a glance and the sheet under it is the whole record. The two are
   the same numbers, so the card is deliberately the smallest arrangement of
   them that still answers what gets asked mid-sentence: AC, hit points, what
   they notice, and where they fall in the order.

   Modifiers are set in bold and the scores beneath them are not, because the
   modifier is the number a DM says out loud and the score is only where it came
   from. */
.character-board {
  display: grid; gap: 0.6rem;
  grid-template-columns: repeat(auto-fill, minmax(15.5rem, 1fr));
}
.pc-card, .party-pc {
  border: 1px solid var(--gray-200); border-radius: 6px;
  padding: 0.5rem 0.6rem; background: var(--surface);
}
.pc-card { cursor: pointer; }
.pc-card:hover { border-color: var(--gray-300); }
.pc-card.is-open { border-color: var(--accent); box-shadow: inset 2px 0 0 var(--accent); }
.pc-card.is-dragging { opacity: 0.45; }
.party-pc { flex: 0 0 auto; width: 13rem; display: block; color: inherit; text-decoration: none; }
.party-pc:hover { border-color: var(--accent); }

.pc-card h5, .party-pc h5 {
  display: flex; align-items: baseline; gap: 0.4rem; margin: 0;
  font-size: 0.9rem; font-weight: 600;
}
.pc-player { margin-left: auto; font-size: 0.7rem; font-weight: 400; color: var(--gray-500); }
.pc-subtitle { margin: 0.1rem 0 0.45rem; font-size: 0.76rem; color: var(--gray-600); }

.pc-stats { display: flex; gap: 0.75rem; margin-bottom: 0.45rem; }
.pc-stat { display: flex; flex-direction: column; line-height: 1.15; }
.pc-stat b { font-size: 0.92rem; }
.pc-stat span { font-size: 0.62rem; letter-spacing: 0.05em; color: var(--gray-500); }

.pc-abilities { display: grid; grid-template-columns: repeat(6, 1fr); gap: 0.15rem; }
.pc-ability {
  display: flex; flex-direction: column; align-items: center; line-height: 1.2;
  padding: 0.2rem 0; border-radius: 4px; background: var(--gray-100);
}
.pc-ability-name { font-size: 0.56rem; letter-spacing: 0.04em; color: var(--gray-500); }
.pc-ability-mod { font-size: 0.82rem; }
.pc-ability-score { font-size: 0.62rem; color: var(--gray-500); }

/* ---- The sheet -----------------------------------------------------------
   Everything a standard sheet carries, in the order it is asked for: who they
   are, what they can take and dish out, what they are good at, then the prose.
   The derived numbers sit next to the boxes they come from rather than in a
   summary somewhere else, so it is obvious which score moved which total. */
.character-sheet .sheet-identity,
.character-sheet .sheet-combat {
  display: grid; gap: 0.5rem 0.7rem; margin-bottom: 0.8rem;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
}
.character-sheet label { display: flex; flex-direction: column; gap: 0.15rem; font-size: 0.74rem; color: var(--gray-600); }
.character-sheet label.wide { grid-column: span 2; }
.character-sheet input[type="text"],
.character-sheet input[type="number"],
.character-sheet select {
  font: inherit; font-size: 0.86rem; padding: 0.3rem 0.4rem;
  border: 1px solid var(--gray-300); border-radius: 5px;
  background: var(--surface); color: var(--gray-900);
}
.sheet-derived { align-self: end; font-size: 0.76rem; color: var(--gray-600); }
.sheet-derived b { color: var(--gray-900); }

.sheet-columns { display: grid; gap: 1.2rem 2rem; grid-template-columns: repeat(auto-fit, minmax(20rem, auto)); justify-content: start; margin-bottom: 1rem; }
.character-sheet h3 { margin: 0 0 0.4rem; font-size: 0.78rem; letter-spacing: 0.05em; text-transform: uppercase; color: var(--gray-600); }
.character-sheet h3 .modal-hint { text-transform: none; letter-spacing: 0; }

.ability-row { display: grid; grid-template-columns: 5.5rem 3.4rem 2rem auto; align-items: center; gap: 0.4rem; padding: 0.15rem 0; }
.ability-name { font-size: 0.8rem; }
.ability-mod { font-size: 0.9rem; }
/* Wins over the stacked label above, which is the shape every other field on
   the sheet wants and the only one this row does not. */
.character-sheet .ability-save { flex-direction: row; align-items: baseline; gap: 0.3rem; font-size: 0.72rem; }

.skill-row { display: grid; grid-template-columns: 1.1rem 1.1rem minmax(0, 1fr) 2rem 2.2rem; align-items: center; gap: 0.3rem; padding: 0.08rem 0; font-size: 0.78rem; max-width: 24rem; }
.skill-row.is-proficient .skill-name { font-weight: 600; }
.skill-ability { font-size: 0.62rem; text-transform: uppercase; color: var(--gray-500); }
.skill-total { text-align: right; }

.sheet-prose { display: grid; gap: 0.9rem; grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr)); }
.prose-rendered { min-height: 2.2rem; font-size: 0.85rem; cursor: text; }
.prose-source { width: 100%; font: inherit; font-size: 0.85rem; padding: 0.4rem; border: 1px solid var(--gray-300); border-radius: 5px; background: var(--surface); color: var(--gray-900); }
.sheet-footer { margin: 1rem 0 0; }
.btn-quiet.danger { color: var(--danger); }

/* ---- The running screen --------------------------------------------------
   Three rails, and the middle one is the whole point: what was prepped on the
   left, what is being written in the middle, what has to be reached for on the
   right. Full height, because at the table the page never scrolls to something
   else. */
body.running { height: 100vh; display: flex; flex-direction: column; overflow: hidden; }
.run-layout {
  flex: 1 1 auto; min-height: 0;
  display: grid;
  grid-template-columns: minmax(180px, 240px) minmax(0, 1fr) minmax(220px, 320px);
  gap: 0.9rem;
  padding: 0.9rem;
}
@media (max-width: 900px) {
  body.running { height: auto; overflow: visible; }
  .run-layout { grid-template-columns: 1fr; }
}
.run-rail { min-height: 0; overflow-y: auto; display: flex; flex-direction: column; gap: 1rem; }
.run-rail-head {
  display: flex; align-items: center; gap: 0.4rem; margin: 0 0 0.4rem;
  font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.06em; color: var(--gray-500);
}
.run-rail-actions { margin-left: auto; display: flex; gap: 0.25rem; }
.run-game-number { margin-left: auto; text-transform: none; letter-spacing: 0; }

/* Finding a name mid-play. Small, above the list, and never in the way of it. */
.run-search { display: flex; align-items: center; gap: 0.4rem; margin: 0 0 0.4rem; }
.run-search input {
  flex: 1 1 auto; min-width: 0; font: inherit; font-size: 0.8rem;
  padding: 0.25rem 0.4rem; border: 1px solid var(--gray-300); border-radius: 4px;
  background: var(--surface); color: inherit;
}
.run-search-count { font-size: 0.65rem; color: var(--gray-500); white-space: nowrap; }

.run-segments { list-style: none; margin: 0; padding: 0; }
/* While a search is running the segments holding the word are marked and the
   rest recede -- still readable, because the DM has to be able to keep their
   place in the night while looking something up. */
.run-segments.is-searching .run-segment:not(.is-match) { opacity: 0.35; }
.run-segments.is-searching .run-segment.is-match .run-segment-row {
  box-shadow: inset 2px 0 0 var(--accent);
  font-weight: 600;
}
/* The word itself, wherever it can be marked up. The log is a textarea and
   cannot be, so Enter selects it there instead. */
mark.find-hit { background: var(--accent-soft); color: inherit; border-radius: 2px; padding: 0 0.1em; }
.run-segment { display: flex; align-items: center; gap: 0.2rem; }
.run-segment-row {
  flex: 1 1 auto; display: flex; align-items: baseline; gap: 0.4rem; min-width: 0;
  border: 0; background: none; font: inherit; font-size: 0.85rem; text-align: left;
  padding: 0.3rem 0.35rem; border-radius: 4px; cursor: pointer; color: inherit;
}
.run-segment-row:hover { background: var(--gray-100); }
.run-segment.is-active .run-segment-row { background: var(--accent-bg); color: var(--accent-dark); font-weight: 600; }
.run-segment.is-played .run-segment-title { color: var(--gray-500); }
.run-segment.is-cut { opacity: 0.5; }
.run-segment.is-cut .run-segment-title { text-decoration: line-through; }
.run-segment-kind { font-size: 0.6rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--kind, var(--gray-500)); flex: none; }
.run-segment-title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The marks only matter for the segment being run, so they stay out of the way
   until the row is under the cursor or is the one in play. */
.run-segment-marks { display: none; gap: 0.2rem; }
.run-segment:hover .run-segment-marks,
.run-segment.is-active .run-segment-marks { display: flex; }
.run-keys { margin-top: auto; font-size: 0.68rem; line-height: 1.7; }
.run-keys kbd {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.9em;
  border: 1px solid var(--gray-300); border-bottom-width: 2px; border-radius: 3px; padding: 0 0.25rem;
}

.run-center { display: flex; flex-direction: column; min-height: 0; gap: 0.5rem; }
/* In the running column the party is a strip, not a panel: the log is what the
   height belongs to. */
.run-center > .party-bar { flex: 0 0 auto; margin-bottom: 0; }
.run-center .party-block { max-height: 6.5rem; }
.run-center-head { display: flex; align-items: baseline; gap: 0.5rem; }
.run-center-head h2 { margin: 0; font-size: 1.05rem; }
.run-kind {
  font-size: 0.62rem; text-transform: uppercase; letter-spacing: 0.05em;
  color: var(--kind, var(--accent-dark)); background: var(--kind-bg, var(--accent-bg));
  padding: 0.1rem 0.4rem; border-radius: 999px;
}
.run-status { margin-left: auto; }
.run-log {
  flex: 1 1 auto; min-height: 12rem; width: 100%; resize: none;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.9rem; line-height: 1.6; padding: 0.8rem;
  border: 1px solid var(--gray-300); border-radius: 6px; background: var(--surface);
}
.run-log:focus { border-color: var(--accent); outline: none; }
.run-prep { flex: 0 0 auto; max-height: 32vh; overflow-y: auto; font-size: 0.85rem; }
.run-prep summary {
  cursor: pointer; font-size: 0.72rem; text-transform: uppercase;
  letter-spacing: 0.06em; color: var(--gray-500);
}

.init-list { list-style: none; margin: 0 0 0.5rem; padding: 0; }
.init-row {
  display: flex; align-items: center; gap: 0.4rem;
  padding: 0.25rem 0.3rem; border-radius: 4px; font-size: 0.83rem;
}
.init-row.is-turn { background: var(--accent-bg); box-shadow: inset 2px 0 0 var(--accent); }
.init-row.is-pc .init-name { font-weight: 600; }
.init-order { min-width: 1.6rem; text-align: right; font-variant-numeric: tabular-nums; color: var(--gray-600); }
.init-name { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.init-hp { display: flex; align-items: baseline; gap: 0.1rem; font-variant-numeric: tabular-nums; }
.init-hp-current {
  width: 2.4rem; text-align: right; font: inherit; font-size: 0.83rem;
  border: 1px solid transparent; background: transparent; border-radius: 3px; padding: 0 0.15rem;
}
.init-hp-current:hover,
.init-hp-current:focus { border-color: var(--gray-300); background: var(--surface); }
.init-hp-max { color: var(--gray-500); font-size: 0.76rem; }
.init-add { display: flex; gap: 0.25rem; }
.init-add input {
  font: inherit; font-size: 0.8rem; padding: 0.2rem 0.35rem;
  border: 1px solid var(--gray-300); border-radius: 4px; min-width: 0;
}
.init-add input[name="name"] { flex: 1 1 auto; }

/* ---- The plot board ------------------------------------------------------
   Four columns of storylines. A card is a title you can read at a glance and a
   body you open when you want the detail — the same rendered-then-source swap
   every other body in the app uses. */
.plot-columns { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 0.7rem; }
@media (max-width: 900px) { .plot-columns { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px) { .plot-columns { grid-template-columns: 1fr; } }

.plot-column h3 {
  display: flex; align-items: baseline; gap: 0.35rem; margin: 0 0 0.4rem;
  font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--gray-500);
}
.plot-count { font-variant-numeric: tabular-nums; color: var(--gray-400); }
.plot-add-input {
  width: 100%; font: inherit; font-size: 0.8rem; padding: 0.25rem 0.4rem; margin-bottom: 0.4rem;
  border: 1px solid var(--gray-200); border-radius: 4px; background: var(--gray-100);
}
.plot-add-input:focus { background: var(--surface); border-color: var(--accent); outline: none; }
.plot-cards { min-height: 2.5rem; display: flex; flex-direction: column; gap: 0.35rem; }

.plot-card {
  border: 1px solid var(--gray-200); border-radius: 5px; background: var(--surface);
  padding: 0.4rem 0.5rem; cursor: pointer;
}
.plot-card:hover { border-color: var(--gray-300); }
.plot-card-bar { display: flex; align-items: baseline; gap: 0.35rem; }
.plot-card-title { flex: 1 1 auto; font-size: 0.84rem; line-height: 1.35; }
.plot-card-expand { color: var(--gray-400); font-size: 0.8rem; }
.plot-card-delete { visibility: hidden; flex: none; }
.plot-card:hover .plot-card-delete { visibility: visible; }
/* The body is only on screen while the card is open, so a column of threads
   stays scannable. */
.plot-card-body,
.plot-card-source { display: none; }
.plot-card.is-open .plot-card-body:not([hidden]),
.plot-card .plot-card-source:not([hidden]) { display: block; }
.plot-card-body {
  margin-top: 0.4rem; padding-top: 0.4rem; border-top: 1px solid var(--gray-200);
  font-size: 0.8rem;
}
.plot-card-source {
  width: 100%; margin-top: 0.4rem; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.76rem; line-height: 1.5; border: 1px solid var(--accent-soft);
  border-radius: 4px; padding: 0.4rem; resize: vertical;
}

/* The pages this campaign owes itself: names written at the table that nobody
   has written a page for. Sits under the tree because it is the same list one
   step earlier — a page that does not exist yet. */
.pages-owed { margin-top: 1.2rem; border-top: 1px solid var(--gray-200); padding-top: 0.7rem; }
.pages-owed h3 {
  display: flex; align-items: baseline; gap: 0.35rem; margin: 0 0 0.4rem;
  font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--gray-500);
}
.owed-list { list-style: none; margin: 0; padding: 0; }
.owed-list li { display: flex; align-items: baseline; gap: 0.35rem; }
.owed-title {
  border: 0; background: none; font: inherit; font-size: 0.83rem; text-align: left;
  padding: 0.2rem 0.35rem; border-radius: 4px; cursor: pointer;
  color: var(--gray-500); border-bottom: 1px dotted var(--gray-300);
}
.owed-title:hover { color: var(--accent-dark); border-bottom-color: var(--accent); }
.owed-count { font-size: 0.7rem; color: var(--gray-300); font-variant-numeric: tabular-nums; }
