/* The shared design system for the CarrollAutomation applications.

   Three applications, one shell: a workshop for writing a novel, the rules
   reference behind it, and the panels for running the game at the table. What
   they have in common is the shape of their content -- every screen is a
   register of entries. Chapters, facts, notes, spells, monsters, combatants,
   log lines. So the component this file is built around is the entry: a ruled
   record with a strong first line and quiet particulars, which is what a table
   becomes when the screen is too narrow to be a table.

   Two temperatures. Chrome is cool and compact, because it is an instrument and
   should get out of the way. The manuscript is warm and roomy, because it is
   read for an hour at a time. That is the only place the palette shifts, and it
   is the difference between operating and reading.

   Two postures. The reference and the workshop are used at a desk with a
   pointer. The game panels are used standing up, one-handed, in the middle of a
   fight -- so controls are sized for a thumb from 44px up, and the thing that
   is happening now is never in doubt. */

/* ==========================================================================
   1. Tokens
   ========================================================================== */

:root {
  /* Declared so the browser paints its own parts -- caret, selection,
     scrollbars, and any control we have not styled -- in the right theme.
     Without it a dark page keeps a light-mode caret and light scrollbars. */
  color-scheme: light dark;

  /* ---- surfaces ----
     Cool greys for the instrument. Four rungs, and they mean different things:
     `paper` is the page behind everything, `panel` is a raised working surface,
     `sunken` is a well cut into one, `panel-alt` is a quiet band inside it. */
  --paper:      #eef2f8;
  --panel:      #ffffff;
  --panel-alt:  #f4f7fb;
  --sunken:     #e3e9f2;
  /* The navigation's own ground: the sidebar and the phone's top bar. */
  --chrome:     #ffffff;

  /* The one warm surface: a page of the manuscript. Long-form prose is read
     for an hour at a time and a cool grey ground makes that colder work than
     it needs to be. Nothing instrumental uses this. */
  --reading:    #fbfaf7;
  --reading-ink:#22201d;

  /* ---- ink ---- */
  --ink:        #121826;
  --ink-soft:   #46526b;
  /* Secondary text: breadcrumbs, help, paths, particulars. Dark enough to
     clear 4.5:1 on the lightest surface it is ever set on. */
  --ink-faint:  #5b6780;

  /* ---- lines ----
     `rule` divides things that are different, `rule-soft` separates things
     that are the same. Getting that backwards is what makes a list look like
     a spreadsheet. */
  --rule:       #c7d0de;
  --rule-soft:  #dde3ec;

  /* ---- accent ----
     The palette is "Signal" (2026-09-25): blue for what you act on and where
     you go, orange as the second voice -- the other speaker, the other
     state, the thing that wants a look. Dark mode is where it was chosen;
     light mode keeps the same two hues, darkened to read on white. */
  --accent:     #1d5bd0;
  --accent-lift:#2f6fe0;
  --accent-bg:  #e8effa;
  --accent-2:   #a14907;
  --accent-2-bg:#f6ede6;
  /* Text set *on* the accents. The dark theme's are light enough that white
     falls to about 3:1, so they flip to a deep navy there. */
  --on-accent:  #ffffff;
  --on-accent-2:#ffffff;
  --focus:      #2f6fe0;

  /* ---- status ----
     One vocabulary for all three applications: a chapter's memory, a note's
     state, a hit point bar, an encounter's difficulty. Each is dark enough to
     clear AA on the page ground as well as on a panel, because status turns up
     in meta lines as often as it does inside cards. */
  --good:       #157347;
  --good-bg:    #e8f1ed;
  --warn:       #9a4f06;
  --warn-bg:    #f5ede6;
  --danger:     #bf2438;
  --danger-bg:  #f9e9eb;
  --info:       #1d5bd0;
  --info-bg:    #e8effa;

  /* ---- type ----
     Spectral reads long-form on a screen and sets headings with enough weight
     to hold a page. IBM Plex Sans is the instrument voice: engineered rather
     than neutral, and its lining figures are properly tabular, which matters
     on every initiative count, hit point total and word count here. */
  --serif: Spectral, Georgia, "Times New Roman", serif;
  --sans: "IBM Plex Sans", "Helvetica Neue", Arial, sans-serif;

  /* A major-third scale, with the display sizes fluid so a phone is not handed
     a 36px title in a 390px viewport. */
  --t-micro:  0.75rem;
  --t-meta:   0.8125rem;
  --t-small:  0.875rem;
  --t-body:   1rem;
  --t-read:   1.0625rem;
  --t-lead:   1.125rem;
  --t-h3:     clamp(1.1875rem, 1.1rem + 0.4vw, 1.375rem);
  --t-h2:     clamp(1.4375rem, 1.25rem + 0.9vw, 1.75rem);
  --t-h1:     clamp(1.75rem, 1.35rem + 1.9vw, 2.25rem);

  /* ---- space ----
     One scale, so vertical rhythm is a decision made once rather than a rem
     value guessed at each use. */
  --s-1: 0.25rem; --s-2: 0.5rem;  --s-3: 0.75rem; --s-4: 1rem;
  --s-5: 1.5rem;  --s-6: 2rem;    --s-7: 3rem;    --s-8: 4rem;

  --radius:    5px;
  --radius-sm: 3px;
  --radius-lg: 8px;

  /* Elevation is used twice in the whole system -- for things that float over
     the page. Panels are told apart by their border, not by a shadow. */
  --lift: 0 1px 2px rgb(16 24 40 / 0.06), 0 8px 24px rgb(16 24 40 / 0.10);

  /* The reach of the content column, and of a line of prose. Prose is capped
     near 68 characters; the serif takes a little more than a sans would. */
  --measure-page: 76rem;
  --measure-read: 34rem;
  /* One measure for everything on a list page -- the heading's action, the
     record list, the table. Sharing it is what gives such a page a single right
     edge instead of three that nearly line up. */
  --measure-list: 62rem;

  --tap: 44px;   /* the smallest a control gets where a thumb is expected */

  /* How much of the top of the screen sticky chrome occupies: nothing on the
     wide layout, where navigation is a column, and the compact bar's height on
     a phone. Anything that pins itself to the top -- a table heading, a
     sidecar, a scrolled-to anchor -- has to clear it, so it lives in one place.
     Set alongside the shell, further down. */
  --head-h: 6.3rem;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --paper: #0f1420; --panel: #151c2b; --panel-alt: #1c2537; --sunken: #0b0f18;
    --chrome: #0b0f18;
    --reading: #171d29; --reading-ink: #e7e4de;
    --ink: #e5ebf5; --ink-soft: #a4b1c8; --ink-faint: #8593ad;
    --rule: #2b3650; --rule-soft: #212a3d;
    --accent: #5b9cff; --accent-lift: #7fb3ff; --accent-bg: #20304d;
    --accent-2: #f6a05a; --accent-2-bg: #393133;
    --on-accent: #04122b; --on-accent-2: #04122b; --focus: #5b9cff;
    --good: #4fcf95; --good-bg: #1e393c;
    --warn: #f2b451; --warn-bg: #383431;
    --danger: #f26d7d; --danger-bg: #382938;
    --info: #7fc3ff; --info-bg: #26374d;
    --lift: 0 1px 2px rgb(0 0 0 / 0.4), 0 8px 24px rgb(0 0 0 / 0.45);
  }
}

*, *::before, *::after { box-sizing: border-box; }

/* Any class that sets `display` outranks the user agent's [hidden] rule, so
   hiding an element by attribute silently stops working the moment it is also
   laid out. Enforce it once here rather than remembering it at every use. */
[hidden] { display: none !important; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important; animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important; scroll-behavior: auto !important;
  }
}

/* ==========================================================================
   2. Document
   ========================================================================== */

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0; background: var(--paper); color: var(--ink);
  font: 400 var(--t-body)/1.55 var(--sans);
  -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility;
  /* A heading scrolled to lands below the sticky chrome rather than under it. */
  scroll-padding-top: calc(var(--head-h) + var(--s-4));
}

a { color: var(--accent); text-underline-offset: 2px; text-decoration-thickness: 1px; }
a:hover { color: var(--accent-lift); }

:focus-visible {
  outline: 2px solid var(--focus); outline-offset: 2px; border-radius: var(--radius-sm);
}

h1, h2, h3, h4 { font-family: var(--serif); font-weight: 600; line-height: 1.2;
                 letter-spacing: -0.011em; margin: 0; }

::selection { background: var(--accent-bg); }

/* ==========================================================================
   3. The shell
   --------------------------------------------------------------------------
   A column on the left carrying the hierarchy the content sits inside: the
   site, the three registers, the thing you have open, and its sections. That
   hierarchy is real and was previously flattened into two rows of tabs, where
   eleven sections had to scroll sideways to be reached.

   No icons. Writing, Games and Reference are three short words with no obvious
   pictures, and inventing some would be decoration standing in for a label. The
   register you are in is marked in the gutter instead -- the same device the
   encounter tracker uses for whose turn it is.

   Below 62rem the column becomes a drawer and a single compact bar takes the
   top, because a phone has no room for navigation beside the content.
   ========================================================================== */

:root { --rail-w: 4.75rem; --nav-w: 14.5rem; --tabbar-h: 0rem; --head-h: 0rem; }

.skip {
  position: absolute; left: var(--s-3); top: var(--s-3); z-index: 100;
  transform: translateY(-200%); background: var(--panel); color: var(--ink);
  border: 1px solid var(--rule); border-radius: var(--radius);
  padding: var(--s-2) var(--s-4); font-family: var(--sans); font-size: var(--t-small);
}
.skip:focus { transform: none; }

.shell { display: grid; grid-template-columns: var(--rail-w) var(--nav-w) minmax(0, 1fr); }
/* A grid item's automatic minimum is its content, so a wide table inside the
   content column makes the *item* wider than its track while the track stays
   put -- the page then scrolls sideways with no element obviously at fault.
   The track already says minmax(0, ...); the item has to say it too. */
.shell > * { min-width: 0; }

/* The rail: the four areas, always there. The deepest ground on the page, so
   the area's column beside it reads as the thing you are in. */
.site-rail {
  position: sticky; top: 0; height: 100dvh; z-index: 30;
  display: flex; flex-direction: column; align-items: center; gap: var(--s-2);
  padding: var(--s-4) 0; background: var(--chrome); border-right: 1px solid var(--rule);
}
.site-rail-mark {
  font-family: var(--serif); font-weight: 600; font-size: var(--t-body); color: var(--accent);
  text-decoration: none; margin-bottom: var(--s-3); padding: var(--s-1) var(--s-2);
}
.site-rail-item {
  display: flex; flex-direction: column; align-items: center; gap: 0.2rem;
  width: 3.9rem; padding: 0.55rem 0; border-radius: var(--radius-lg);
  color: var(--ink-faint); text-decoration: none;
  font-family: var(--sans); font-size: var(--t-micro); font-weight: 500;
}
.site-rail-item:hover { color: var(--ink); background: var(--panel-alt); }
.site-rail-item[aria-current] { color: var(--accent); background: var(--accent-bg); font-weight: 600; }

/* The area's column: its pages, then the sections of what is open. */
.sidenav {
  position: sticky; top: 0; height: 100dvh; overflow-y: auto; overscroll-behavior: contain;
  background: var(--panel); border-right: 1px solid var(--rule);
  padding: var(--s-5) 0 var(--s-6); display: flex; flex-direction: column; gap: var(--s-1);
}
.area-title {
  margin: 0 var(--s-5) var(--s-2); font-family: var(--serif); font-size: var(--t-h3);
  font-weight: 500; color: var(--ink); line-height: 1.2;
}
.nav-group {
  margin: var(--s-4) var(--s-5) var(--s-1); font-family: var(--sans); font-size: var(--t-micro);
  font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-faint);
}
.apps .apps-empty { padding: var(--s-1) var(--s-5); font-size: var(--t-small); color: var(--ink-faint); }
.apps a.is-external::after { content: " \2197"; color: var(--ink-faint); font-size: 0.85em; }

/* The site's own name, quiet: it is the parent of the three registers rather
   than a headline, so it is set small and the second half is dropped back. */
.mark {
  font-family: var(--serif); font-size: var(--t-small); font-weight: 600;
  letter-spacing: -0.01em; color: var(--ink); text-decoration: none;
  padding: 0 var(--s-5); line-height: 1.2;
}
.mark span { color: var(--ink-faint); font-weight: 400; }
.mark:hover { color: var(--accent); }

.apps, .side-sections { list-style: none; margin: 0; padding: 0; }
.apps a, .side-sections a {
  display: block; text-decoration: none; font-family: var(--sans);
  border-left: 3px solid transparent;
}
.apps a {
  font-size: var(--t-body); font-weight: 500; color: var(--ink-soft);
  padding: var(--s-2) var(--s-5) var(--s-2) calc(var(--s-5) - 3px);
}
.apps a:hover { color: var(--ink); background: var(--panel-alt); }
.apps a[aria-current] {
  color: var(--ink); border-left-color: var(--accent); background: var(--accent-bg);
}
/* The backlog is a tool, not a register: smaller, and set off from the three. */

.side-context { border-top: 1px solid var(--rule-soft); padding-top: var(--s-4); margin-top: var(--s-4); }
.side-context-name { margin: 0 var(--s-5) var(--s-2); }
.side-context-name a {
  font-family: var(--serif); font-size: var(--t-body); font-weight: 600;
  color: var(--ink); text-decoration: none; line-height: 1.25; display: block;
}
.side-context-name a:hover { color: var(--accent); }
.side-sections a {
  font-size: var(--t-small); color: var(--ink-soft);
  padding: 0.3rem var(--s-5) 0.3rem calc(var(--s-5) - 3px);
}
.side-sections a:hover { color: var(--ink); background: var(--panel-alt); }
.side-sections a[aria-current="page"] {
  color: var(--accent); font-weight: 600; border-left-color: var(--accent);
}

/* The compact bar, and the button that opens the drawer. Both are absent from
   the wide layout entirely rather than hidden by opacity. */
.topbar { display: none; }
.topbar-title {
  font-family: var(--sans); font-size: var(--t-small); color: var(--ink-faint);
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.topbar-title b { color: var(--ink); font-weight: 600; }
.topbar-title b:not(:only-child)::after {
  content: ""; display: inline-block; width: 1px; height: 0.85em;
  background: var(--rule); margin: 0 var(--s-2); vertical-align: -0.08em;
}
.sidenav-veil {
  position: fixed; inset: 0; z-index: 50; background: rgb(11 14 19 / 0.45);
}
.menu-btn {
  width: var(--tap); height: var(--tap); flex: none; display: grid; place-items: center;
  background: none; border: 1px solid var(--rule); border-radius: var(--radius);
  color: var(--ink); cursor: pointer;
}
.menu-bars, .menu-bars::before, .menu-bars::after {
  display: block; width: 1.05rem; height: 1.5px; background: currentColor;
  border-radius: 1px;
}
.menu-bars { position: relative; }
.menu-bars::before, .menu-bars::after { content: ""; position: absolute; left: 0; }
.menu-bars::before { top: -5px; }
.menu-bars::after { top: 5px; }

/* `width: 100%` and not just `max-width`: auto inline margins on a grid item
   stop it stretching to its track and size it to fit-content instead, which is
   how the content column ended up wider than the column it sits in. */
main {
  width: 100%; max-width: var(--measure-page); margin-inline: auto;
  padding: var(--s-6) var(--s-6) var(--s-8);
}
main.narrow { max-width: 46rem; }

@media (max-width: 62rem) {
  :root { --head-h: 3.5rem; --tabbar-h: 4.25rem; }

  .shell { grid-template-columns: minmax(0, 1fr); }

  /* The rail becomes the tab bar: along the bottom, where the thumb is. */
  .site-rail {
    position: fixed; top: auto; bottom: 0; left: 0; right: 0; height: auto; z-index: 45;
    flex-direction: row; justify-content: space-around; gap: 0;
    padding: var(--s-1) var(--s-2) calc(var(--s-1) + env(safe-area-inset-bottom));
    border-right: 0; border-top: 1px solid var(--rule);
  }
  .site-rail-mark { display: none; }
  .site-rail-item { flex: 1; width: auto; min-height: var(--tap); max-width: 6rem; }

  .topbar {
    position: sticky; top: 0; z-index: 40; display: flex; align-items: center;
    gap: var(--s-3); height: var(--head-h); padding: 0 var(--s-4);
    background: var(--chrome); border-bottom: 1px solid var(--rule);
  }

  .sidenav {
    position: fixed; top: 0; bottom: 0; left: 0; width: min(17rem, 82vw);
    z-index: 60; transform: translateX(-101%); box-shadow: var(--lift);
    padding-top: var(--s-5);
    /* Hidden, not merely moved: a drawer pushed off the side of the screen is
       still in the tab order, so a keyboard lands in navigation nobody can see.
       Visibility is stepped at the end of the slide so the animation still
       plays going out. */
    visibility: hidden;
    transition: transform 0.18s ease, visibility 0s linear 0.18s;
  }
  body.nav-open .sidenav {
    transform: none; visibility: visible; transition-delay: 0s;
  }

  main { padding: var(--s-5) var(--s-4) calc(var(--s-8) + var(--tabbar-h)); }
}

/* Focus: a page that wants the whole screen -- the table -- drops the rail and
   the column on a wide screen too, and keeps only the compact bar, whose menu
   opens the column as a drawer, as on a phone. A page asks for it with
   {% block shell_class %} is-focus{% endblock %}. */
@media (min-width: 62.0625rem) {
  .shell.is-focus { grid-template-columns: minmax(0, 1fr); --head-h: 3.5rem; }
  .shell.is-focus .site-rail { display: none; }
  .shell.is-focus .topbar {
    position: sticky; top: 0; z-index: 40; display: flex; align-items: center;
    gap: var(--s-3); height: var(--head-h); padding: 0 var(--s-4);
    background: var(--chrome); border-bottom: 1px solid var(--rule);
  }
  .shell.is-focus .sidenav {
    position: fixed; top: 0; bottom: 0; left: 0; width: var(--nav-w);
    z-index: 60; transform: translateX(-101%); box-shadow: var(--lift);
    visibility: hidden; transition: transform 0.18s ease, visibility 0s linear 0.18s;
  }
  body.nav-open .shell.is-focus .sidenav { transform: none; visibility: visible; transition-delay: 0s; }
}

/* Collapsed (2026-10-04, Alex: "Sidebar on the left should be collapsible everywhere... default should be collapsed"):
   on a wide screen the area's column is shown only once asked for -- the rail's toggle, or a page's own
   [data-nav-toggle] -- and stays as asked in this browser (html.nav-shown, set before the page paints). */
@media (min-width: 62.0625rem) {
  html:not(.nav-shown) .shell:not(.is-focus) { grid-template-columns: var(--rail-w) minmax(0, 1fr); }
  html:not(.nav-shown) .shell:not(.is-focus) .sidenav { display: none; }
}
/* An area with no column of pages (AI, 2026-10-14): the rail and the page, nothing between. */
@media (min-width: 62.0625rem) {
  .shell.no-pages:not(.is-focus) { grid-template-columns: var(--rail-w) minmax(0, 1fr); }
}
.rail-toggle { margin-top: auto; display: grid; place-items: center; width: 2.75rem; height: 2.25rem; padding: 0;
  background: none; border: 1px solid transparent; border-radius: var(--radius); color: var(--ink-faint); cursor: pointer; }
.rail-toggle:hover { color: var(--ink); background: var(--panel-alt); }
.rail-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.rail-toggle svg { transition: transform 0.18s ease; }
html.nav-shown .rail-toggle svg { transform: scaleX(-1); }
@media (max-width: 62rem) { .rail-toggle { display: none; } }
@media (prefers-reduced-motion: reduce) { .rail-toggle svg { transition: none; } }
.nav-toggle { display: inline-grid; place-items: center; width: 2.25rem; height: 2.25rem; flex: none;
  background: none; border: 1px solid var(--rule); border-radius: var(--radius); color: var(--ink); cursor: pointer; }
.nav-toggle:hover { border-color: var(--accent); }
@media (max-width: 62rem) { .nav-toggle { display: none; } }  /* the top bar's menu does it there */

@media (max-width: 48rem) {
  .apps a, .side-sections a { padding-top: var(--s-3); padding-bottom: var(--s-3); }
}

/* ==========================================================================
   4. Page furniture
   ========================================================================== */

/* A row of filters over a list. This used to be the site navigation as well;
   now it is only ever a filter, and it scrolls sideways under a thumb rather
   than wrapping to a second cramped line. */
.tabs {
  display: flex; gap: 0; margin-bottom: var(--s-5);
  border-bottom: 1px solid var(--rule); overflow-x: auto;
  scrollbar-width: none; -webkit-overflow-scrolling: touch;
}
.tabs::-webkit-scrollbar { display: none; }
.tabs a {
  font-family: var(--sans); font-size: var(--t-small); font-weight: 500;
  text-decoration: none; color: var(--ink-soft); white-space: nowrap;
  padding: var(--s-3); border-bottom: 2px solid transparent;
}
.tabs a:hover { color: var(--ink); }
.tabs a[aria-current="page"] { color: var(--ink); border-bottom-color: var(--accent); }

.page-head { margin-bottom: var(--s-6); max-width: 44rem; }
.page-head h2 { font-size: var(--t-h1); margin-bottom: var(--s-2); }
.page-head p { color: var(--ink-soft); margin: 0; font-size: var(--t-body); }

.crumbs {
  font-family: var(--sans); font-size: var(--t-meta); color: var(--ink-faint);
  margin: 0 0 var(--s-3);
}
.crumbs a { color: var(--ink-faint); }

/* Particulars under a heading: what this thing's status, date and setting are.
   Labelled pairs rather than a middot-joined string -- the label is what makes
   "13th of Eleint" mean something, and pairs wrap without stranding a dot at
   the start of a line. */
.facts-line {
  display: flex; flex-wrap: wrap; gap: var(--s-2) var(--s-5); margin: 0 0 var(--s-4);
  padding: 0; list-style: none; font-family: var(--sans); font-size: var(--t-meta);
}
.facts-line > * { display: flex; gap: 0.4em; align-items: baseline; min-width: 0; }
.facts-line .k { color: var(--ink-faint); }
.facts-line .v { color: var(--ink); font-weight: 500; }

.section-head {
  display: flex; align-items: baseline; gap: var(--s-3); flex-wrap: wrap;
  margin: 0 0 var(--s-4); padding-bottom: var(--s-2);
  border-bottom: 1px solid var(--rule);
}
.section-head h3 { font-size: var(--t-h3); }
.section-head .spacer, .section-head > :last-child.btn-row { margin-left: auto; }
.section-head > .btn:last-child { margin-left: auto; }
@media (max-width: 40rem) {
  /* The action drops below the heading rather than squeezing it. */
  .section-head { align-items: stretch; }
  .section-head > .btn:last-child, .section-head > :last-child.btn-row {
    margin-left: 0; width: 100%;
  }
  .section-head > .btn:last-child { justify-content: center; }
}

/* ==========================================================================
   5. Controls
   ========================================================================== */

input:not([type="checkbox"]):not([type="radio"]), select, textarea, button {
  font-family: inherit; font-size: var(--t-body);
}

/* A control that sets one of colour/background must set both: a rule that sets
   only a background leaves the user agent's black text on it, which is
   invisible in dark mode and invisible in the markup too. */
input:not([type="checkbox"]):not([type="radio"]), select, textarea {
  color: var(--ink); background: var(--panel);
  border: 1px solid var(--rule); border-radius: var(--radius);
  padding: 0.5rem 0.6rem; min-height: 2.5rem;
}
input:hover, select:hover, textarea:hover { border-color: var(--ink-faint); }
input:focus, select:focus, textarea:focus { border-color: var(--accent); }
select { padding-right: var(--s-6); }
textarea { line-height: 1.55; resize: vertical; min-height: 4rem; }
::placeholder { color: var(--ink-faint); opacity: 1; }

/* iOS zooms the page in when a focused field is under 16px, which reads as the
   layout jumping. Holding the base size prevents it. */
@media (max-width: 48rem) {
  input:not([type="checkbox"]):not([type="radio"]), select, textarea {
    font-size: 1rem; min-height: var(--tap);
  }
}

.toolbar {
  display: flex; gap: var(--s-3); flex-wrap: wrap; align-items: flex-end;
  margin-bottom: var(--s-5);
}
.field { display: flex; flex-direction: column; gap: var(--s-1); min-width: 0; }
.field label {
  font-family: var(--sans); font-size: var(--t-meta); color: var(--ink-soft);
}
.field input:not([type="checkbox"]):not([type="radio"]),
.field select, .field textarea,
form.stack input:not([type="checkbox"]):not([type="radio"]),
form.stack select, form.stack textarea { width: 100%; }
.field input[type="search"] { min-width: 14rem; }
/* Direct child on purpose: .field lays its contents out in a column, where a
   flex-basis is a height, and this rule inherited by a nested input made every
   such search box 20rem tall. */
.toolbar > input[type="search"] { flex: 1 1 20rem; max-width: 30rem; }

.btn {
  font-family: var(--sans); font-size: var(--t-small); font-weight: 500;
  border-radius: var(--radius); cursor: pointer; white-space: nowrap;
  padding: 0.5rem 0.85rem; min-height: 2.25rem;
  border: 1px solid var(--rule); background: var(--panel); color: var(--ink);
  text-decoration: none; display: inline-flex; align-items: center;
  justify-content: center; gap: var(--s-2);
  transition: border-color 0.12s ease, color 0.12s ease, background-color 0.12s ease;
}
.btn:hover { border-color: var(--accent); color: var(--accent); }
.btn:active { background: var(--panel-alt); }
.btn[disabled], .btn:disabled { opacity: 0.5; cursor: default; }
.btn-primary { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.btn-primary:hover { background: var(--accent-lift); border-color: var(--accent-lift);
                     color: var(--on-accent); }
.btn-quiet { border-color: transparent; background: transparent; color: var(--ink-soft); }
.btn-quiet:hover { background: var(--panel-alt); }
.btn-small { font-size: var(--t-meta); padding: 0.25rem 0.55rem; min-height: 1.9rem; }
.btn-row { display: flex; gap: var(--s-2); align-items: center; flex-wrap: wrap; }

/* Where a thumb is expected, controls grow to the platform minimum. */
@media (max-width: 48rem) {
  .btn { min-height: var(--tap); padding: 0.55rem 0.9rem; }
  .btn-small { min-height: 2.25rem; }
}

form.stack { display: grid; gap: var(--s-4); }
form.stack .row { display: grid; gap: var(--s-1); }
form.stack label { font-family: var(--sans); font-size: var(--t-small); font-weight: 500; }
form.stack .help, .help {
  font-family: var(--sans); font-size: var(--t-meta); color: var(--ink-faint); margin: 0;
}
form.stack .errorlist {
  margin: var(--s-1) 0 0; padding-left: 1.1rem; color: var(--danger);
  font-family: var(--sans); font-size: var(--t-meta);
}
form.stack .two { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s-4); }
@media (max-width: 40rem) { form.stack .two { grid-template-columns: minmax(0, 1fr); } }

.messages { list-style: none; margin: 0 0 var(--s-5); padding: 0; display: grid; gap: var(--s-2); }
.messages li {
  font-family: var(--sans); font-size: var(--t-small);
  padding: var(--s-3) var(--s-4); border-radius: var(--radius);
  border: 1px solid var(--rule); border-left: 3px solid var(--good);
  background: var(--panel);
}

/* ==========================================================================
   6. Entries and tables
   --------------------------------------------------------------------------
   A register of records. Rules between rows rather than a card each, because
   twenty identical rounded boxes with the same shadow is a list pretending to
   be something more interesting than a list.

   Reference tables stay tables at every width: you read a bestiary by
   comparing armour class down a column, and stacking the cells destroys the
   comparison. They scroll sideways under the thumb with the name column
   pinned. Short record tables -- a cast list, a session list -- carry
   `data-stack` and become entries on a narrow screen, because there the record
   matters and the columns do not.
   ========================================================================== */

/* Held to a measure. A six-column table of three sessions stretched across the
   full page puts its headings a long way from the values under them and reads
   as mostly empty; the reference's own tables opt out, because forty columns of
   statblock really do want the width. */
.table-wrap {
  background: var(--panel); border: 1px solid var(--rule); border-radius: var(--radius);
  overflow-x: auto; -webkit-overflow-scrolling: touch; max-width: var(--measure-list);
}
table.data { width: 100%; border-collapse: collapse; font-family: var(--sans); }
table.data th {
  text-align: left; font-weight: 600; font-size: var(--t-meta); color: var(--ink-soft);
  padding: var(--s-2) var(--s-3); border-bottom: 1px solid var(--rule);
  white-space: nowrap; background: var(--panel-alt);
  /* Pinned below the sticky chrome rather than behind it. */
  position: sticky; top: var(--head-h); z-index: 1;
}
table.data td {
  padding: 0.55rem var(--s-3); border-bottom: 1px solid var(--rule-soft);
  font-size: var(--t-small); vertical-align: baseline;
}
table.data tbody tr:last-child td { border-bottom: 0; }
table.data tbody tr:hover td { background: var(--accent-bg); }
table.data a { text-decoration: none; }
table.data a:hover { text-decoration: underline; }

.col-name { font-family: var(--serif); font-size: var(--t-body); font-weight: 500;
            min-width: 10rem; }
/* The heading has to sit over its own numbers. A right-aligned column under a
   left-aligned heading is the commonest way a table stops being readable. */
.col-num, th.col-num { text-align: right; font-variant-numeric: tabular-nums;
                       white-space: nowrap; }
.col-sm { white-space: nowrap; }
/* Short values -- a range, a school, a casting time -- read as one token and
   should not be broken across lines by a long neighbour in the same row. */
.col-mid { white-space: nowrap; }
.col-src { color: var(--ink-faint); white-space: nowrap; font-size: var(--t-meta); }
.col-wide { color: var(--ink-soft); min-width: 16rem; }

@media (max-width: 48rem) {
  /* Tabular, but readable with a thumb: the name stays put while the
     particulars scroll under it. */
  table.data:not(.data-stack) th:first-child,
  table.data:not(.data-stack) td:first-child {
    position: sticky; left: 0; background: var(--panel);
    box-shadow: 1px 0 0 var(--rule-soft);
  }
  table.data:not(.data-stack) th:first-child { background: var(--panel-alt); z-index: 2; }
  table.data:not(.data-stack) tbody tr:hover td:first-child { background: var(--accent-bg); }
  table.data td { padding: 0.6rem var(--s-3); }
}

@media (max-width: 40rem) {
  /* The record, not the row. Each cell prints its own column heading from
     `data-label`, so the markup needs no duplicate mobile copy. */
  table.data.data-stack, table.data.data-stack tbody, table.data.data-stack tr,
  table.data.data-stack td { display: block; width: auto; }
  table.data.data-stack thead { display: none; }
  table.data.data-stack tr {
    padding: var(--s-3) var(--s-4); border-bottom: 1px solid var(--rule-soft);
  }
  table.data.data-stack tbody tr:hover td { background: none; }
  table.data.data-stack td { border: 0; padding: 0.1rem 0; display: flex;
                             gap: var(--s-3); justify-content: space-between; }
  table.data.data-stack td[data-label]::before {
    content: attr(data-label); color: var(--ink-faint); font-size: var(--t-meta);
    flex: none;
  }
  table.data.data-stack td.col-name {
    display: block; font-size: var(--t-lead); margin-bottom: var(--s-1);
  }
  table.data.data-stack td.col-name::before { content: none; }
  table.data.data-stack td:empty { display: none; }
}

/* A list of records that was never a table: worlds, games, books, sections.

   Left-aligned and held to a reading measure. The previous version put the name
   at one edge of a 76rem row and a tally at the other, with nine hundred pixels
   of nothing between them -- and it placed them in the wrong order besides:
   `grid-row` on the tally made it row-locked, and the grid algorithm places
   row-locked items before auto ones, so the tally took the first column and the
   name was pushed into the second. Nothing here is auto-placed now.

   A record reads as three lines: what it is called, what it is, and the handful
   of numbers that say how far along it is. */

.records { max-width: var(--measure-list); border-top: 1px solid var(--rule); }
.record {
  display: grid; gap: var(--s-2); padding: var(--s-4) var(--s-3);
  border-bottom: 1px solid var(--rule-soft); text-decoration: none; color: inherit;
}
.record:hover { background: var(--panel); }
.record:hover .record-title { color: var(--accent); }
.record-title {
  display: flex; align-items: baseline; gap: var(--s-3); flex-wrap: wrap;
  font-family: var(--serif); font-size: 1.25rem; font-weight: 600;
  line-height: 1.2; letter-spacing: -0.012em; color: var(--ink);
}
.record-blurb { color: var(--ink-soft); font-size: var(--t-small); margin: 0; }
.record-facts {
  display: flex; flex-wrap: wrap; gap: var(--s-1) var(--s-4);
  font-family: var(--sans); font-size: var(--t-meta); color: var(--ink-soft);
}
.record-facts > span { display: flex; gap: 0.4em; align-items: baseline; }
.record-facts .k { color: var(--ink-faint); }
.record-facts b { font-weight: 500; color: var(--ink); font-variant-numeric: tabular-nums; }

/* A page heading with the one action that makes a new one. Kept on the heading
   rather than stranded under the list, where it moves further away the more
   records there are. */
.page-bar {
  display: flex; align-items: flex-start; gap: var(--s-4); flex-wrap: wrap;
  max-width: var(--measure-list); margin-bottom: var(--s-5);
}
.page-bar .page-head { margin-bottom: 0; }
.page-bar > .btn, .page-bar > .btn-row { margin-left: auto; flex: none; }
@media (max-width: 40rem) {
  .page-bar > .btn, .page-bar > .btn-row { margin-left: 0; width: 100%; }
  .page-bar > .btn { justify-content: center; }
}

/* ==========================================================================
   7. Panels, status and empty states
   ========================================================================== */

.sidecar {
  background: var(--panel); border: 1px solid var(--rule);
  border-radius: var(--radius); padding: var(--s-4);
}
.sidecar h4 {
  font-family: var(--sans); font-size: var(--t-small); font-weight: 600;
  color: var(--ink-soft); margin: 0 0 var(--s-3);
}
.sidecar + .sidecar { margin-top: var(--s-4); }

.linklist { display: flex; flex-wrap: wrap; gap: var(--s-2); }
.linklist a, .linklist span {
  font-family: var(--sans); font-size: var(--t-meta);
  border: 1px solid var(--rule); border-radius: var(--radius);
  padding: 0.2rem 0.5rem; text-decoration: none; color: var(--ink);
}
.linklist a:hover { border-color: var(--accent); color: var(--accent); }

dl.facts {
  margin: 0; display: grid; grid-template-columns: max-content minmax(0, 1fr);
  gap: var(--s-2) var(--s-4); font-family: var(--sans); font-size: var(--t-small);
}
dl.facts dt { color: var(--ink-faint); }
dl.facts dd { margin: 0; }

.pill {
  display: inline-flex; align-items: center; font-family: var(--sans);
  font-size: var(--t-micro); font-weight: 500; padding: 0.15rem 0.5rem;
  border-radius: 999px; border: 1px solid var(--rule); color: var(--ink-soft);
  background: var(--panel-alt); white-space: nowrap;
}
.pill-good   { color: var(--good);   border-color: var(--good);   background: var(--good-bg); }
.pill-warn   { color: var(--warn);   border-color: var(--warn);   background: var(--warn-bg); }
.pill-danger { color: var(--danger); border-color: var(--danger); background: var(--danger-bg); }
.pill-info   { color: var(--info);   border-color: var(--info);   background: var(--info-bg); }

.blank-slate {
  border: 1px dashed var(--rule); border-radius: var(--radius);
  padding: var(--s-7) var(--s-5); text-align: center; background: var(--panel);
}
.blank-slate h3 { font-size: var(--t-h3); margin-bottom: var(--s-2); }
.blank-slate p { color: var(--ink-soft); margin: 0 auto var(--s-5); max-width: 32rem; }

.state { padding: var(--s-7) var(--s-4); text-align: center; color: var(--ink-soft); }
.state h3 { font-size: var(--t-h3); margin-bottom: var(--s-1); color: var(--ink); }
.empty { color: var(--ink-faint); font-style: italic; }
.filepath { font-family: var(--sans); font-size: var(--t-meta); color: var(--ink-faint);
            word-break: break-all; }
.missing { color: var(--warn); }

/* The framed document a page hands you to copy: a brief, a report, a card. */
.doc { background: var(--panel); border: 1px solid var(--rule);
       border-radius: var(--radius); overflow: hidden; }
.doc-head {
  display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap;
  padding: var(--s-3) var(--s-4); background: var(--panel-alt);
  border-bottom: 1px solid var(--rule);
}
.doc-head h3 { font-family: var(--sans); font-size: var(--t-small); font-weight: 600; }
.doc-path { font-family: var(--sans); font-size: var(--t-meta); color: var(--ink-faint);
            min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.doc-head .btn { margin-left: auto; flex: none; }

/* ==========================================================================
   8. Prose
   --------------------------------------------------------------------------
   The warm surface. Everything here is read rather than operated, so the
   measure is short, the leading is long, and the ink is a touch softer than
   the interface's.
   ========================================================================== */

.prose {
  font-family: var(--serif); font-size: var(--t-read); line-height: 1.7;
  max-width: var(--measure-read);
}
.prose > :first-child { margin-top: 0; }
.prose > :last-child { margin-bottom: 0; }
.prose h3 { font-family: var(--sans); font-size: var(--t-small); font-weight: 600;
            color: var(--ink-soft); margin: var(--s-5) 0 var(--s-2); }
.prose p { margin: 0 0 var(--s-4); }
.prose ul, .prose ol { margin: 0 0 var(--s-4); padding-left: 1.2rem; }
.prose li { margin-bottom: var(--s-1); }
.prose blockquote {
  margin: 0 0 var(--s-4); padding: var(--s-3) var(--s-4);
  background: var(--panel-alt); border-left: 3px solid var(--rule);
  font-size: var(--t-body);
}
.prose code { font-size: 0.9em; background: var(--panel-alt); padding: 0.1em 0.35em;
              border-radius: var(--radius-sm); }
.prose table {
  width: 100%; border-collapse: collapse; font-family: var(--sans);
  font-size: var(--t-small); margin: 0 0 var(--s-4);
}
.prose th, .prose td { text-align: left; padding: var(--s-1) var(--s-2);
                       border-bottom: 1px solid var(--rule-soft); }
.prose th { color: var(--ink-soft); border-bottom-color: var(--rule); }

/* Announced to a screen reader, absent from the page. */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
