/*
 * Zingor — Scribe's Vellum Ledger theme (light base).
 * Every color lives here as a token. A dark counterpart can be added later by
 * redefining these same names under, e.g., :root[data-theme="dark"] { ... }
 * without touching any markup. Names referenced inline in templates
 * (--accent, --text-muted, etc.) must keep their spelling.
 */
:root {
  /* Surfaces — aged vellum and fresh page */
  --bg: #ece3ce;            /* aged vellum page ground */
  --surface: #f4eddc;       /* a sheet laid on the page */
  --surface-alt: #e6dbc0;   /* faint ledger band (odd rows) */
  --surface-alt-2: #f1e9d6; /* faint ledger band (even rows) */
  --input-bg: #fbf7ec;      /* fresh, unmarked paper */

  /* Ink */
  --text: #26221a;          /* iron-gall ink */
  --text-muted: #6e6450;    /* faded marginalia */

  /* Brand / rubric */
  --accent: #b23a2e;        /* vermilion rubrication */
  --accent-hover: #cb4a38;
  --on-accent: #f7f1e1;     /* cream text on inked buttons */
  --secondary: #2f5e8c;     /* lapis */
  --secondary-hover: #3a6fa3;

  /* Semantic */
  --success: #4f7a3a;       /* verdigris */
  --danger: #8c2f22;        /* oxblood, distinct from vermilion accent */
  --danger-hover: #a4382a;

  /* Structure */
  --border: #c6b591;        /* inked hairline on vellum */
  --overlay: rgba(38, 34, 26, 0.55);
  --shadow: 0 1px 2px rgba(38, 34, 26, 0.10), 0 2px 10px rgba(38, 34, 26, 0.05);
  --ring: 0 0 0 3px rgba(178, 58, 46, 0.20);

  /* Type */
  --font-display: "Iowan Old Style", "Palatino Linotype", Palatino, "Hoefler Text", Georgia, serif;
  --font-body: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;

  /* Links (anchors) */
  --link: var(--accent);
  --link-visited: #A978D6;
}

/*
 * Dark counterpart — the same ledger read by candlelight: tooled leather and
 * tallow-smoke grounds, ink warmed to legibility, rubric brightened so the
 * vermilion still sings against the dark. Only the color tokens are redefined;
 * type tokens are inherited from :root. Applied by setting data-theme="dark"
 * on the document root.
 */
:root[data-theme="dark"] {
  /* Surfaces — tooled leather and smoke */
  --bg: #1b1813;            /* dark leather binding */
  --surface: #24201a;       /* a page in candlelight */
  --surface-alt: #2c271f;   /* faint ledger band (odd rows) */
  --surface-alt-2: #272219; /* faint ledger band (even rows) */
  --input-bg: #2f2a21;      /* unmarked paper in shadow */

  /* Ink — warmed for legibility on the dark ground */
  --text: #ece2cd;          /* lamplit cream */
  --text-muted: #a39a82;    /* faded marginalia */

  /* Brand / rubric — brightened so vermilion still sings */
  --accent: #e0654f;        /* vermilion rubrication */
  --accent-hover: #ec7660;
  --on-accent: #1b1813;     /* dark ink on lit buttons */
  --secondary: #6ea3d4;     /* lapis */
  --secondary-hover: #82b3e0;

  /* Semantic */
  --success: #84b06a;       /* verdigris */
  --danger: #d76a58;        /* oxblood, lit */
  --danger-hover: #e27c69;

  /* Structure */
  --border: #443c2c;        /* inked hairline on leather */
  --overlay: rgba(0, 0, 0, 0.65);
  --shadow: 0 1px 2px rgba(0, 0, 0, 0.40), 0 2px 10px rgba(0, 0, 0, 0.30);
  --ring: 0 0 0 3px rgba(224, 101, 79, 0.30);

  /* Links (anchors) - Web default colors are too low-contrast with the background in dark mode */
  --link: var(--accent);
  --link-visited:  #b460b4;
}

* { box-sizing: border-box; margin: 0; padding: 0; }
body {
  font-family: var(--font-body);
  background: var(--bg);
  color: var(--text);
  line-height: 1.6;
  min-height: 100vh;
}
/* Development builds (DEBUG=True) get a deliberately clashing page ground so it's
   obvious at a glance you're not on production. Set on <body> so it wins over the
   theme's --bg in both light and dark mode. */
body[data-debug] { background: #98de95; }

a {
  color: var(--link);
}

a:link {
  /* for anchors that have a destination, not just a named target (i.e. #foo) */
  /* intentionally left blank */
}

a:visited {

  color: var(--link-visited);
}

a:focus {
  /* intentionally left blank: use defaults */
}

a:hover {
  color: var(--accent-hover);
}

a:active {
  /* intentionally left blank: use defaults */
}

.container { max-width: 1280px; margin: 0 auto; padding: 1.25rem 1rem; }
header {
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  box-shadow: var(--shadow);
  padding: 0.75rem 1.25rem;
  display: flex;
  justify-content: space-between;
  align-items: center;
}
/* Brand wordmark — set in the display serif, inked rather than rubricated */
.logo {
  font-family: var(--font-display);
  font-size: 1.3rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--text) !important;
  text-decoration: none;
}
header > a:hover { color: var(--accent); }
nav a.btn { color: var(--on-accent); text-decoration: none; margin-left: 1rem; }
/* Show the icon for the active theme; the other stays hidden. Light is the
   default (:root), dark is applied via :root[data-theme="dark"]. */
.theme-icon-dark { display: none; }
:root[data-theme="dark"] .theme-icon-light { display: none; }
:root[data-theme="dark"] .theme-icon-dark { display: inline; }
h1, h2, h3 { font-family: var(--font-display); margin-bottom: 0.5rem; line-height: 1.25; }
h1 { color: var(--accent); letter-spacing: 0.01em; }
.section {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 4px;
  box-shadow: var(--shadow);
  padding: 1rem 1.1rem;
  margin-bottom: 1.1rem;
  /* Breathing room above a section jumped to from the table of contents. */
  scroll-margin-top: 1rem;
}
/* Jumping to the topmost section should land at the top of the page, so the
   character's name and toolbar come along with it. Asking for a full viewport
   of margin is unsatisfiable for the first section — nothing is above it to
   scroll past — so the browser clamps to scroll position 0. That beats a fixed
   offset guessed from the header's height, and it follows a reorder for free. */
[data-sortable="sections"] > [data-section]:first-child { scroll-margin-top: 100vh; }
/* The character sheet: a sticky table of contents in a left rail, sections beside it. */
.sheet-layout {
  display: grid;
  grid-template-columns: 11rem minmax(0, 1fr);
  gap: 1.5rem;
  align-items: start;
}
.sheet-toc {
  position: sticky;
  top: 1rem;
  max-height: calc(100vh - 2rem);
  overflow-y: auto;
}
/* An index in the margin: quieter than a section heading, same hand. */
.sheet-toc-title {
  font-family: var(--font-display);
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--text-muted);
  font-variant: small-caps;
  letter-spacing: 0.09em;
  border-bottom: 1px solid var(--border);
  padding-bottom: 0.3rem;
  margin-bottom: 0.4rem;
}
.sheet-toc-list { list-style: none; }
.sheet-toc-list a {
  display: block;
  padding: 0.25rem 0.5rem;
  border-left: 2px solid transparent;
  font-size: 0.9rem;
  line-height: 1.35;
  text-decoration: none;
}
/* In-page jumps are navigation, not destinations — don't mark them visited. */
.sheet-toc-list a,
.sheet-toc-list a:visited { color: var(--text-muted); }
.sheet-toc-list a:hover { color: var(--accent-hover); }
/* The section currently under the top of the viewport (set by sheet-toc.js). */
.sheet-toc-list a[aria-current] {
  color: var(--accent);
  border-left-color: var(--accent);
  background: var(--surface);
}
@media (max-width: 900px) {
  /* Too narrow for a rail: the contents become a row of jump links above the sheet. */
  .sheet-layout { grid-template-columns: minmax(0, 1fr); }
  .sheet-toc { position: static; max-height: none; }
  .sheet-toc-list { display: flex; flex-wrap: wrap; gap: 0.15rem 0.5rem; }
  .sheet-toc-list a { border-left: none; padding: 0.25rem 0.35rem; }
  .sheet-toc-list a[aria-current] { background: none; }
}
@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}
/* The signature: rubricated section headings — vermilion small-caps over a faded rule */
.section h2 {
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--accent);
  font-variant: small-caps;
  letter-spacing: 0.09em;
  border-bottom: 1px solid var(--border);
  padding-bottom: 0.3rem;
  margin-bottom: 0.85rem;
}
table { width: 100%; border-collapse: collapse; font-variant-numeric: tabular-nums; }
th, td { padding: 0.4rem 0.6rem; text-align: left; border-bottom: 1px solid var(--border); }
th {
  color: var(--text-muted);
  font-weight: normal;
  font-size: 0.78rem;
  font-variant: small-caps;
  letter-spacing: 0.06em;
}
.field-unknown {
  cursor: pointer;
  color: var(--text-muted);
  font-style: italic;
}
.field-unknown:hover { color: var(--accent); }
.field-value { font-weight: bold; }
.field-value--prose { font-weight: normal; }
input, select, textarea {
  background: var(--input-bg);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 0.3rem 0.5rem;
  font-size: 0.9rem;
  font-family: inherit;
}
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: var(--ring);
}
button, .btn {
  background: var(--accent);
  color: var(--on-accent);
  border: none;
  border-radius: 4px;
  padding: 0.4rem 0.8rem;
  cursor: pointer;
  font-size: 0.9rem;
  font-family: inherit;
  text-decoration: none;
  display: inline-block;
}
button:hover, .btn:hover { background: var(--accent-hover); }
button:focus-visible, .btn:focus-visible { outline: none; box-shadow: var(--ring); }
button:disabled { opacity: 0.5; cursor: not-allowed; }
.btn-small { padding: 0.2rem 0.5rem; font-size: 0.8rem; }
.btn-danger { background: var(--danger); }
.btn-danger:hover { background: var(--danger-hover); }
.btn-secondary { background: var(--surface-alt); color: var(--text); border: 1px solid var(--border); }
.btn-secondary:hover { background: var(--bg); }
.toolbar { display: flex; align-items: center; gap: 0.5rem; margin-bottom: 1rem; }
.wiki-controls { display: inline-flex; align-items: center; gap: 0.5rem; }
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
.grid-3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 1rem; }
.modifier-positive { color: var(--success); }
.modifier-negative { color: var(--accent); }
/* Flex so whitespace-only text between the score, the slash and the percentile
   forms no flex item, keeping percentile Strength tight ("18/10"). The pieces
   stay separate elements because each is its own htmx-editable field. */
.score-base { display: inline-flex; align-items: baseline; }
.score-set-pct { margin-left: 0.35rem; }
/* base -> modified ability score, with the net modifier total stacked over a
   direction arrow. Color comes from .modifier-positive/negative on the wrapper. */
/* The wrapper stays inline (not inline-flex) so the modified score is ordinary
   inline text sharing the base score's baseline; an inline-flex wrapper would
   take its baseline from the two-line marker and sink the score below it. */
.score-delta { margin-left: 0.35rem; white-space: nowrap; }
/* vertical-align centers the two-line stack against the scores. Left at the
   default baseline it would hang low: a column flex container takes its
   baseline from its first line, so the total would sit on the baseline and the
   arrow entirely below it. */
.score-delta-marker { display: inline-flex; flex-direction: column; align-items: center; line-height: 1; margin-right: 0.15rem; vertical-align: middle; }
.score-delta-total { font-size: 0.7em; font-weight: 600; }
.score-delta-arrow { font-size: 0.9em; line-height: 1; }
.score-modified { font-weight: 600; }
.inline-form { display: inline-flex; gap: 0.3rem; align-items: center; }
/* The add-condition target is an ability-score picker; target only applies to
   ability conditions, so hide it whenever another modifier type is selected.
   option:checked tracks the live selection, so this needs no JS. The backend
   stores NULL for target on non-ability conditions regardless of what the
   hidden picker submits, so this rule only governs appearance. */
.condition-add-form:not(:has(select[name="modifier_type"] option[value="ability"]:checked)) .condition-target {
  display: none;
}
/* Scope only refines Strength (via the encumbrance context), so show the scope
   picker only when the condition targets Strength — i.e. both the modifier type
   is ability and the target is strength. Chained :has() is an AND. Same as
   above, the backend nulls scope outside this case, so this only affects UI. */
.condition-add-form:not(:has(select[name="modifier_type"] option[value="ability"]:checked):has(select[name="target"] option[value="strength"]:checked)) .condition-scope {
  display: none;
}
ul.spell-list { list-style: none; padding: 0; }
.htmx-indicator { display: none; }
.htmx-request .htmx-indicator { display: inline; }
.flash {
  padding: 0.5rem 0.7rem;
  background: var(--surface-alt);
  border-left: 3px solid var(--accent);
  border-radius: 0 4px 4px 0;
  margin-bottom: 0.5rem;
}
/* Severity variants for .flash (see partials/_notice.html). Base .flash is the
 * neutral/info look; these recolor the accent stripe. */
.flash--success { border-left-color: var(--success); }
.flash--danger { border-left-color: var(--danger); }
/* ---- Reusable sheet structure ----
 * Two row families share one vocabulary across every section:
 *   .sheet-row  — a horizontal row in a flex list (inventory, conditions, spells)
 *   .kv-table / .data-table — table-based rows (identity, HP, abilities, sage)
 * Styling here is keyed to these classes (and data-* attributes), never to the
 * #section-* ids, which are reserved as htmx swap targets. */

/* Generic horizontal row used in flex lists. */
.sheet-row { display: flex; align-items: center; gap: 1rem; padding: 0.3rem 0.4rem; }
.sheet-row--between { justify-content: space-between; }
.sheet-row--grab { cursor: grab; }
/* Opt-in zebra striping for a list of sibling rows. */
.striped > .sheet-row:nth-child(odd) { background: var(--surface-alt); }
.striped > .sheet-row:nth-child(even) { background: var(--surface-alt-2); }

/* Key/value tables: a label in the th, its value in the td. These hold sparse
   label/value pairs, so cap their width rather than letting them stretch across
   the full (wide) container and open up big gaps between label and value. */
.kv-table { width: 100%; max-width: 820px; }

/* Tabular data with zebra-striped body rows (e.g. sage studies, party inventory). */
.data-table tbody tr:nth-child(odd) { background: var(--surface-alt); }
.data-table tbody tr:nth-child(even) { background: var(--surface-alt-2); }

/* Zebra striping by <tbody> group instead of by row: a container and its
   contents share one stripe (character inventory). */
.group-striped > tbody:nth-of-type(odd) { background: var(--surface-alt); }
.group-striped > tbody:nth-of-type(even) { background: var(--surface-alt-2); }

/* A titled block within a section (notes fields, HP groups). A stable
 * data-subsection key makes these individually addressable for reordering. */
.subsection { margin-bottom: 0.75rem; }
.subsection:last-child { margin-bottom: 0; }

/* A heading row that keeps the rubricated full-width underline while letting a
   control (e.g. a Reorder toggle) sit on the same line as the section title. */
.section-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  border-bottom: 1px solid var(--border);
  padding-bottom: 0.3rem;
  margin-bottom: 0.85rem;
}
.section-head h2 {
  border-bottom: none;
  padding-bottom: 0;
  margin-bottom: 0;
}
.section-head-controls {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-shrink: 0;
}

.badge {
  display: inline-block;
  font-size: 0.7em;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  padding: 0.1em 0.5em;
  border-radius: 0.75em;
  vertical-align: middle;
}
.badge-inactive {
  background: var(--danger, #b02a2a);
  color: #fff;
}
.badge-sync {
  background: var(--success, #4f7a3a);
  color: #fff;
}

/* While a list is in reorder mode, its rows are draggable handles: the open-hand
   cursor covers the whole row (beating inner clickable/edit cursors and any
   inline cursor SortableJS leaves behind), and during an active drag the
   closed-fist cursor is shown everywhere via the body.zingor-dragging flag. */
body:not(.zingor-dragging) .reordering[data-sortable="abilities"] [data-row],
body:not(.zingor-dragging) .reordering[data-sortable="abilities"] [data-row] *,
body:not(.zingor-dragging) .reordering[data-sortable="notes"] [data-subsection],
body:not(.zingor-dragging) .reordering[data-sortable="notes"] [data-subsection] *,
body:not(.zingor-dragging) .reordering[data-sortable="sections"] > [data-section] h2 { cursor: grab !important; }
body.zingor-dragging * { cursor: grabbing !important; }
.reordering[data-sortable="abilities"] [data-row]:hover,
.reordering[data-sortable="notes"] [data-subsection]:hover { background: var(--surface-alt); }

/* Inventory rows for container contents indent by nesting depth; --depth is
   set inline on the cell (0 for top-level items, matching the base td pad). */
.cell-indent { padding-left: calc(0.6rem + var(--depth, 0) * 1.5rem); }
/* Container collapse (inventory-collapse.js): rows inside a collapsed container. */
tr.collapse-hidden { display: none; }
/* Fixed column widths: display:none rows stop informing auto table layout, so
   without these the columns would reflow on every collapse/expand (and the
   caret would move out from under the pointer). Widths on the header cells
   size the whole column; in the read-only 4-column variant the leftover
   width is distributed proportionally, which is fine. */
.fixed-cols { table-layout: fixed; }
.fixed-cols .col-name { width: 36%; }
.fixed-cols .col-weight { width: 22%; }
.fixed-cols .col-flag { width: 8%; }
.fixed-cols .col-actions { width: 18%; }
/* Sage studies render one table per field, so their columns only line up with
   each other when the widths are pinned rather than derived from each table's
   own longest study name. */
.fixed-cols .col-study { width: 42%; }
.fixed-cols .col-points { width: 18%; }
.fixed-cols .col-status { width: 22%; }
/* Caret toggle on a container row: plain glyph, not an accent button. */
.collapse-toggle {
  background: none;
  border: none;
  color: var(--text-muted);
  cursor: pointer;
  padding: 0 0.2rem;
  font-size: 0.8rem;
}
.collapse-toggle:hover { background: none; color: var(--accent-hover); }
/* How full a container is, beside its capacity. Muted like the capacity itself
   until the contents outweigh it, when it turns to the danger ink. */
.capacity-fill { color: var(--text-muted); }
.capacity-fill--over { color: var(--danger); }
.drop-target { outline: 2px dashed var(--accent); border-radius: 4px; background: var(--surface-alt); }
th.sortable { cursor: pointer; }
th.sortable:hover { color: var(--accent-hover); }

/* ---- Utilities (replace former inline styles) ---- */
.muted { color: var(--text-muted); }
.clickable { cursor: pointer; }
.inline { display: inline; }
.block { display: block; }
.no-shrink { flex-shrink: 0; }
.flex-1 { flex: 1; }
.w-full { width: 100%; }
.text-meta { font-size: 0.9rem; }
.text-sm { font-size: 0.85rem; }
.text-xs { font-size: 0.8rem; }
.subhead { font-size: 0.95rem; }
.empty-note { color: var(--text-muted); font-size: 0.9rem; }
.w-4 { width: 4rem; }
.w-5 { width: 5em; }
.w-6 { width: 6rem; }
.w-8 { width: 8rem; }
.w-10 { width: 10rem; }
.w-12 { width: 12rem; }
.w-min-14 { min-width: 14rem; }
.mb-sm { margin-bottom: 0.5rem; }
.mb-md { margin-bottom: 0.75rem; }
.mb-lg { margin-bottom: 1rem; }
.mt-xs { margin-top: 0.25rem; }
.mt-sm { margin-top: 0.5rem; }
.mt-md { margin-top: 0.75rem; }
.mt-lg { margin-top: 2rem; }

/* ---- Components (replace former inline styles) ---- */
.row-between { display: flex; justify-content: space-between; align-items: center; }
.auth-card { max-width: 400px; margin: 2rem auto; }
.auth-footer { margin-top: 0.75rem; font-size: 0.9rem; }
.build-info {
  position: fixed;
  bottom: 0.4rem;
  right: 0.6rem;
  font-size: 0.75rem;
  color: var(--text-muted);
  opacity: 0.7;
  pointer-events: none;
  font-family: var(--font-body);
}
/* A search box and its results, held at the top of the viewport by search-pin.js.
   The reserved viewport is what lets the pin hold: results are the last thing on
   the page, so without it a search that matches little shortens the page past the
   pinned scroll position and the browser clamps, sliding the box away. One
   viewport is exactly enough for "region at the top" to stay reachable; a full
   result set dwarfs it and the rule costs nothing. */
.search-pin {
  min-height: 100vh;
  /* Breathing room above the heading once the region is pinned, as .section has. */
  scroll-margin-top: 1rem;
}
.form-error { color: var(--accent); margin-bottom: 0.5rem; }
.field-label { display: block; margin-bottom: 0.25rem; color: var(--text-muted); font-size: 0.85rem; }
.field-error { color: var(--accent); font-size: 0.85rem; }
.owner-tag { font-size: 0.7em; color: var(--text-muted); }
.item-actions { display: inline-flex; align-items: center; gap: 0.5rem; }
.modal-overlay {
  position: fixed;
  inset: 0;
  background: var(--overlay);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1000;
}
.modal-box {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 6px;
  box-shadow: var(--shadow);
  padding: 1.5rem;
  max-width: 700px;
  width: 90%;
  max-height: 80vh;
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}
.modal-title { color: var(--accent); margin: 0; }
/* In-page confirmation dialog for hx-confirm requests (confirm-dialog.js).
   A native <dialog>, so `margin: auto` restores the centering that the
   global margin reset removes. */
.confirm-dialog {
  margin: auto;
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 6px;
  box-shadow: var(--shadow);
  padding: 1.5rem;
  max-width: 26rem;
  width: 90%;
}
.confirm-dialog::backdrop { background: var(--overlay); }
.confirm-dialog-question { margin-bottom: 1rem; }
.confirm-dialog-actions {
  display: flex;
  justify-content: flex-end;
  gap: 0.5rem;
}

.wiki-export-area {
  flex: 1;
  min-height: 400px;
  font-family: monospace;
  font-size: 0.85rem;
  resize: vertical;
  background: var(--input-bg);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 0.5rem;
}

/* Feedback form fields (Django-rendered ids) */
#id_title, #id_contact { width: 100%; }
#id_description { width: 100%; resize: vertical; }

summary {
  cursor: pointer;
}
