:root {
  color-scheme: light dark;
  --bg: #16171a;
  --surface: #202226;
  --surface-alt: #2a2d33;
  --text: #eceef1;
  --text-dim: #9ea3ab;
  --accent: #c9a24b;
  --border: #3a3d44;
  --highlight-bg: rgba(201, 162, 75, 0.22);
  --heading-font: "IM Fell Great Primer", Georgia, serif;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
}

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

/* ---- Library page ---- */

.library {
  max-width: 760px;
  margin: 0 auto;
  padding: 3rem 1.5rem 4rem;
}

.library h1 {
  margin-bottom: 0.25rem;
}

.subtitle {
  color: var(--text-dim);
  margin-top: 0;
}

.upload-dropzone {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 120px;
  border: 2px dashed var(--border);
  border-radius: 12px;
  margin: 2rem 0 0.5rem;
  cursor: pointer;
  text-align: center;
  padding: 1rem;
  color: var(--text-dim);
  transition: border-color 0.15s, color 0.15s, background 0.15s;
}

.upload-dropzone.dragover,
.upload-dropzone:hover {
  border-color: var(--accent);
  color: var(--text);
  background: var(--surface);
}

.upload-error {
  color: #e07a7a;
  font-size: 0.9rem;
}

.empty-state {
  color: var(--text-dim);
  margin-top: 2rem;
}

.list-grid {
  list-style: none;
  padding: 0;
  margin: 1.5rem 0 0;
  display: grid;
  gap: 0.75rem;
}

.list-card {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 10px;
  transition: border-color 0.15s;
}

.list-card:hover {
  border-color: var(--accent);
}

.list-card a {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  padding: 1rem 3rem 1rem 1.25rem;
  text-decoration: none;
  color: var(--text);
}

.list-card__meta,
.list-card__date {
  color: var(--text-dim);
  font-size: 0.85rem;
}

.list-card__delete {
  position: absolute;
  top: 0.5rem;
  right: 0.5rem;
  margin: 0;
}

.list-card__delete button {
  background: none;
  border: none;
  color: var(--text-dim);
  font-size: 1.3rem;
  line-height: 1;
  cursor: pointer;
  padding: 0.25rem 0.5rem;
}

.list-card__delete button:hover {
  color: #e07a7a;
}

/* ---- Viewer page ---- */

.viewer-body {
  height: 100vh;
  /* 100vh is the *largest* possible viewport on mobile Safari/Chrome (toolbars
     hidden) — while the address bar is showing, that overshoots the real
     visible area and pushes the bottom of the page off-screen. dvh tracks
     the viewport that's actually visible right now. */
  height: 100dvh;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

.viewer-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.75rem 1.25rem;
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
  gap: 1rem;
}

.back-link {
  text-decoration: none;
  white-space: nowrap;
  flex-shrink: 0;
}

/* flex: 1 1 auto + min-width: 0 lets this shrink to *however much space is
   actually left* between the back-link and the nav — a fixed max-width (the
   previous approach) is a guess that's wrong as soon as the guess is bigger
   than the real leftover space: the child's own ellipsis then computes
   against its wrong, too-large budget, overflows this flex item, and gets
   clipped a second time by this element's own overflow: hidden — since
   everything here is centered, that second clip cuts into both ends of the
   already-ellipsized text, leaving a garbled fragment from the middle. */
.viewer-header__title {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  overflow: hidden;
  flex: 1 1 auto;
  min-width: 0;
}

.viewer-header__title strong {
  display: block;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.viewer-header__title span {
  color: var(--text-dim);
  font-size: 0.85rem;
}

.viewer-header__nav {
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: 0.75rem;
}

.vp-tracker {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-variant-numeric: tabular-nums;
}

.vp-tracker__label {
  font-size: 0.7rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-dim);
}

#vp-counter {
  font-weight: 600;
  color: var(--accent);
  font-size: 1.05rem;
}

.vp-tracker__reset {
  background: none;
  border: 1px solid var(--border);
  color: var(--text-dim);
  border-radius: 50%;
  width: 1.6rem;
  height: 1.6rem;
  font-size: 0.95rem;
  line-height: 1;
  cursor: pointer;
}

.vp-tracker__reset:hover {
  color: var(--text);
  border-color: var(--accent);
}

.settings-menu {
  position: relative;
}

.settings-cog {
  background: none;
  border: 1px solid var(--border);
  color: var(--text-dim);
  border-radius: 50%;
  width: 2rem;
  height: 2rem;
  font-size: 1.1rem;
  line-height: 1;
  cursor: pointer;
}

.settings-cog:hover,
.settings-cog[aria-expanded="true"] {
  color: var(--text);
  border-color: var(--accent);
}

.settings-popover {
  position: absolute;
  top: calc(100% + 0.5rem);
  right: 0;
  z-index: 50;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 0.75rem;
  min-width: 9rem;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2);
  flex-direction: column;
  gap: 0.25rem;
}

.settings-popover:not([hidden]) {
  display: flex;
}

.settings-popover__label {
  margin: 0 0 0.25rem;
  font-size: 0.7rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-dim);
}

.settings-option {
  background: none;
  border: none;
  border-radius: 6px;
  color: var(--text);
  font: inherit;
  text-align: left;
  padding: 0.4rem 0.5rem;
  cursor: pointer;
}

.settings-option:hover {
  background: var(--surface-alt);
}

.settings-option[aria-current="true"] {
  color: var(--accent);
  font-weight: 600;
}

.card-viewport {
  position: relative;
  flex: 1;
  min-height: 0;
  display: flex;
}

.card-track {
  flex: 1;
  display: flex;
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  -webkit-overflow-scrolling: touch;
  outline: none;
}

.card-loading {
  margin: auto;
  color: var(--text-dim);
}

.unit-card {
  flex: 0 0 100%;
  width: 100%;
  scroll-snap-align: start;
  overflow-y: auto;
  /* NOTE: touch-action: pan-y was tried here to help the horizontal
     card-swipe win ambiguous diagonal drags, but that was wrong — touch-action
     restricts what *this* element may do, it does not hand the disallowed
     axis off to card-track. Since unit-card fills virtually the whole
     touchable area, pan-y disabled horizontal panning almost everywhere,
     breaking the swipe outright. Reverted. */
  /* Without this, pulling past scrollTop 0 triggers the browser's own
     elastic bounce-back animation, which races our pull-to-jump JS for the
     same touch — and normally wins, since a later preventDefault() can't
     un-start a scroll iOS Safari already committed to. Disabling the native
     bounce here removes that race entirely. This one is x/y-independent, so
     it doesn't affect horizontal swipe. */
  overscroll-behavior-y: none;
}

.unit-card__inner {
  height: 100%;
  margin: 0 auto;
  /* Left/right padding clears the full-height prev/next arrow strips (3.5rem wide). */
  padding: 1.5rem 4rem 3rem;
  /* Set by the card-width setting (cog menu); "none" = fluid, the default. */
  max-width: var(--card-max-width, none);
}

.unit-card__pills {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin-bottom: 0.5rem;
}

.unit-card__badge {
  display: inline-block;
  font-size: 0.7rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--accent);
  border: 1px solid var(--accent);
  border-radius: 999px;
  padding: 0.15rem 0.6rem;
}

.unit-card__badge--accent {
  color: var(--bg);
  background: var(--accent);
}

.unit-card__name {
  display: flex;
  align-items: baseline;
  gap: 0.4rem;
  margin: 0 0 0.15rem;
  font-family: var(--heading-font);
}

.unit-card__name-link {
  font-size: 0.6em;
  color: var(--text-dim);
  text-decoration: none;
  opacity: 0;
  transition: opacity 0.15s ease, color 0.15s ease;
}

.unit-card__name:hover .unit-card__name-link {
  opacity: 1;
}

.unit-card__name-link:hover {
  color: var(--accent);
}

.unit-card__subhead {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  color: var(--text-dim);
  margin-top: 0;
}

.unit-card__formations {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 0.4rem;
  margin-left: auto;
}

/* ---- Game tracker (per-unit status) ---- */

.unit-card__tracker {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 0.5rem 0.75rem;
  margin: 0.75rem 0 1.25rem;
}

.unit-tracker__label {
  font-size: 0.7rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-dim);
}

.tracker-status-group {
  display: inline-flex;
  border: 1px solid var(--border);
  border-radius: 6px;
  overflow: hidden;
}

.tracker-status-btn {
  font: inherit;
  font-size: 0.85rem;
  background: var(--surface-alt);
  color: var(--text-dim);
  border: none;
  border-right: 1px solid var(--border);
  padding: 0.3rem 0.65rem;
  cursor: pointer;
}

.tracker-status-btn:last-child {
  border-right: none;
}

.tracker-status-btn:hover {
  color: var(--text);
}

.tracker-status-btn[aria-pressed="true"] {
  background: var(--accent);
  color: var(--bg);
  font-weight: 600;
}

.tracker-banner-btn {
  font: inherit;
  font-size: 0.85rem;
  background: var(--surface-alt);
  color: var(--text-dim);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 0.3rem 0.65rem;
  cursor: pointer;
}

.tracker-banner-btn:hover {
  color: var(--text);
}

.tracker-banner-btn[aria-pressed="true"] {
  background: var(--accent);
  color: var(--bg);
  border-color: var(--accent);
  font-weight: 600;
}

.unit-tracker__points {
  margin-left: auto;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--accent);
  font-variant-numeric: tabular-nums;
}

.stats-table {
  width: 100%;
  table-layout: fixed;
  border-collapse: collapse;
  margin: 1rem 0 1.5rem;
  font-size: 0.85rem;
}

.stats-table th,
.stats-table td {
  border: 1px solid var(--border);
  padding: 0.35rem 0.5rem;
  text-align: center;
  overflow-wrap: break-word;
}

/* M/WS/BS/S/T/W/I/A/Ld: narrow and identical on every card, however long the
   unit's name is — the name column (the only one left unsized) takes the rest.
   Plain percentages (not em/clamp) so they actually shrink with the viewport
   instead of forcing a fixed-width table that doesn't fit on small screens. */
.stats-table th:not(:first-child),
.stats-table td:not(:first-child) {
  width: 7%;
  padding-left: 0.25rem;
  padding-right: 0.25rem;
}

.stats-table .stats-name {
  text-align: left;
  color: var(--text);
}

/* Placeholder "-" values (no stat/AP/etc.) are visibly faded so real values stand out. */
.stats-table .cell-muted,
.weapon-table .cell-muted {
  opacity: 0.35;
}

/* A characteristic changed by an Attribute Roll pick (see that section below) —
   highlighted so it's obvious at a glance that the printed profile no longer
   applies as-is; the tooltip (title attribute) names what caused it. */
.stat-modified {
  background: var(--highlight-bg);
  font-weight: 600;
  color: var(--text);
}

.stats-missing {
  color: var(--text-dim);
  font-style: italic;
}

.special-rules h4 {
  margin: 0 0 0.35rem;
  font-size: 0.75rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-dim);
}

/* ---- Wargear ---- */

.wargear-details {
  margin: 1.5rem 0;
}

.wargear-details summary {
  cursor: pointer;
  font-family: var(--heading-font);
  font-size: 0.85rem;
  color: var(--accent);
  padding: 0.35rem 0;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 1rem;
}

.wargear-total {
  color: var(--text-dim);
  font-size: 0.85rem;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}

.wargear-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.85rem;
  margin-top: 0.5rem;
}

.wargear-table th,
.wargear-table td {
  border: 1px solid var(--border);
  padding: 0.35rem 0.6rem;
  text-align: left;
}

.wargear-table th.wargear-table__points,
.wargear-table td.wargear-table__points,
.wargear-table th.wargear-table__qty,
.wargear-table td.wargear-table__qty {
  text-align: right;
  white-space: nowrap;
}

.wargear-table td.wargear-table__points,
.wargear-table td.wargear-table__qty {
  color: var(--text-dim);
}

.wargear-table__total-row td {
  border-top: 2px solid var(--border);
  font-weight: 600;
  color: var(--text);
}

/* Shared across the whole site: any flavor/intro text pulled from tow.whfb.app.
   Kept as one class so a future "hide flavor text" toggle is a single rule. */
.flavour-text {
  font-style: italic;
  color: var(--text-dim);
  font-size: 0.85rem;
  margin: 0 0 0.5rem;
  line-height: 1.45;
}

.rule-body-text {
  font-size: 0.85rem;
  margin: 0 0 0.5rem;
  line-height: 1.45;
}

.calc-heading {
  margin: 0 0 0.5rem;
  font-size: 0.7rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-dim);
}

.calc-step {
  font-size: 0.85rem;
  margin: 0 0 0.5rem;
  line-height: 1.45;
}

.modal-divider {
  border: none;
  border-top: 1px solid var(--border);
  margin: 1rem 0;
}

.rule-text--loading {
  color: var(--text-dim);
  font-style: italic;
  margin: 0;
}

/* ---- Weapon profile table ---- */

.weapon-table {
  width: 100%;
  table-layout: fixed;
  /* "separate" (not "collapse"): with collapse, the browser merges each pair
     of adjoining cell borders into one shared, centered line — and resolves
     that centering independently for the header row vs. body rows, which for
     fractional (percentage-based) column widths lands the shared line at a
     different sub-pixel offset in each, rendering as a border that's visibly
     thicker/a different shade on that seam. Separate borders are each drawn
     by exactly one cell, so there's nothing to mis-align. */
  border-collapse: separate;
  border-spacing: 0;
  font-size: 0.85rem;
}

.weapon-table th,
.weapon-table td {
  border-right: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
  padding: 0.35rem 0.5rem;
  text-align: left;
  vertical-align: top;
  overflow-wrap: break-word;
}

.weapon-table thead th {
  border-top: 1px solid var(--border);
}

.weapon-table th:first-child,
.weapon-table td:first-child {
  border-left: 1px solid var(--border);
}

/* Name/Range/S/AP stay just wide enough for their (short) content and line up
   the same on every card; Special Rules is left unset so it always claims
   whatever's left — that's where the real content (rule chips) lives. Plain
   percentages (not em/clamp) so they actually shrink with the viewport instead
   of forcing a fixed-width table that doesn't fit on small screens.
   Defined on a shared <colgroup> (rather than nth-child) so the Ranged and
   Melee tables' columns still line up even though the melee table merges
   Name+Range into one colspan="2" cell. */
.weapon-table__col-name {
  width: 22%;
}

.weapon-table__col-range {
  width: 13%;
}

.weapon-table__col-s,
.weapon-table__col-ap {
  width: 7%;
}

.weapon-table__name {
  font-weight: 600;
}

.weapon-table th.weapon-table__center,
.weapon-table td.weapon-table__center {
  text-align: center;
}

/* Chips flow left-to-right and wrap, rather than stacking one-per-line —
   stacking made a row with several special rules (e.g. 5 on a Handgun)
   balloon to several hundred px tall while its neighbouring row (1 rule)
   stayed short, making the whole table look wildly uneven. */
.weapon-table__rules {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 0.3rem;
}

/* Matches .rule-chip's padding/border box so a "-" placeholder cell is exactly
   as tall as a cell holding one rule chip, rather than collapsing to bare
   text height. */
.weapon-table__rules .cell-muted {
  display: inline-block;
  padding: 0.1rem 0.55rem;
  border: 1px solid transparent;
}

.weapon-table__note-marker {
  color: var(--accent);
  font-size: 0.7em;
  margin-left: 0.2rem;
}

/* No native list bullets — each item's own marker (matching the asterisk next
   to its weapon's name above) serves as the bullet instead, so there's a
   single, unambiguous indicator rather than a bullet plus a separate marker. */
.weapon-notes {
  list-style: none;
  margin: 1rem 0 0;
  padding-left: 0;
  font-size: 0.85rem;
  color: var(--text-dim);
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}

.weapon-notes__item {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
}

.weapon-notes__item .weapon-table__note-marker {
  flex-shrink: 0;
  font-size: 0.9em;
  margin-left: 0;
}

.rule-chip {
  font: inherit;
  font-variant-caps: small-caps;
  font-size: 0.85rem;
  background: var(--surface-alt);
  border: 1px solid var(--border);
  color: var(--accent);
  border-radius: 999px;
  padding: 0.1rem 0.55rem;
  cursor: pointer;
}

.rule-chip:hover {
  border-color: var(--accent);
}

.rule-chip--plain {
  color: var(--text-dim);
  cursor: default;
}

/* A rule chip granted by an Attribute Roll pick, rather than printed on the
   weapon/unit itself — same shape, dashed border to mark it as conditional. */
.rule-chip--granted {
  border-style: dashed;
}

/* ---- Armour save ---- */

.armour-save {
  width: 100%;
  margin-top: 0.5rem;
  border-collapse: collapse;
  font-size: 0.85rem;
}

.armour-save th,
.armour-save td {
  border: 1px solid var(--border);
  padding: 0.3rem 0.6rem;
  text-align: center;
}

.armour-save th {
  color: var(--text-dim);
  font-weight: 600;
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

/* ---- Special rules accordion ---- */

.special-rules {
  border-top: 1px solid var(--border);
  padding-top: 1rem;
}

.accordion-item {
  border-bottom: 1px solid var(--border);
}

.accordion-trigger {
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  color: var(--accent);
  font: inherit;
  font-size: 0.85rem;
  font-variant-caps: small-caps;
  letter-spacing: 0.02em;
  font-weight: 600;
  padding: 0.6rem 1.5rem 0.6rem 0;
  cursor: pointer;
  position: relative;
}

.accordion-trigger::after {
  content: '+';
  position: absolute;
  right: 0.25rem;
  top: 50%;
  transform: translateY(-50%);
  font-weight: 400;
  color: var(--text-dim);
}

.accordion-trigger[aria-expanded="true"]::after {
  content: '\2212';
}

.accordion-panel {
  padding: 0 0 0.75rem;
}

/* A special rule granted by an Attribute Roll pick rather than printed on the
   unit itself (e.g. Gaze of the Gods' Stupidity). */
.accordion-trigger__badge {
  display: inline-block;
  margin-left: 0.5rem;
  font-size: 0.65rem;
  font-weight: 400;
  font-variant-caps: normal;
  letter-spacing: 0.02em;
  text-transform: none;
  color: var(--text-dim);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 0.05rem 0.5rem;
}

/* ---- Attribute rolls (D6-table rules like Darkforged Weapon, Gaze of the Gods) ---- */

.attribute-rolls {
  border-top: 1px solid var(--border);
  padding-top: 1rem;
  margin-bottom: 1rem;
}

.attribute-roll-table {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 0.6rem 0.75rem;
  margin-bottom: 0.75rem;
}

.attribute-roll-table:last-child {
  margin-bottom: 0;
}

.attribute-roll-table__heading {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-family: var(--heading-font);
  font-size: 1rem;
  margin-bottom: 0.5rem;
}

/* Folded with nothing logged yet — just the header row, so the margin that
   normally separates it from the summary/turn log below would otherwise leave
   an uneven gap under an otherwise-empty box. */
.attribute-roll-table__heading--flush {
  margin-bottom: 0;
}

/* The title grows to fill the row, pushing the + button and fold chevron
   (see below) into a cluster flush against the right edge. */
.attribute-roll-fold-trigger {
  flex: 1 1 auto;
  min-width: 0;
  font: inherit;
  font-family: var(--heading-font);
  font-size: 1rem;
  background: none;
  border: none;
  color: var(--text);
  padding: 0;
  cursor: pointer;
  text-align: left;
}

/* Disclosure chevron for folding a recurring table (e.g. Gaze of the Gods)
   down to its summary strip — a separate control at the very right of the
   header (buttons can't nest inside attribute-roll-fold-trigger), drawn from
   borders rather than a font glyph so it renders identically everywhere. */
.attribute-roll-fold-chevron {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.6rem;
  height: 1.6rem;
  background: none;
  border: none;
  cursor: pointer;
}

.attribute-roll-fold-chevron::before {
  content: '';
  display: inline-block;
  width: 0.4em;
  height: 0.4em;
  border-right: 2px solid var(--text-dim);
  border-bottom: 2px solid var(--text-dim);
  transform: rotate(45deg);
  transition: transform 0.15s ease;
}

.attribute-roll-fold-chevron:hover::before {
  border-color: var(--accent);
}

.attribute-roll-fold-chevron[aria-expanded="true"]::before {
  transform: rotate(-135deg);
}

/* The header's own "+" (add turn) sits in its own single-button step group, so
   it looks like the same control as the -/+ pair below rather than a one-off. */
.attribute-roll-table__heading .attribute-roll-turn-steps {
  margin-top: 0;
}

/* Folded view: one cell per turn logged so far, 3-wide at full size (wrapping
   to as many rows as needed), narrowing further as the card does. */
.attribute-roll-summary {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0.4rem;
}

.attribute-roll-summary__cell {
  background: var(--surface-alt);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 0.3rem 0.5rem;
  font-size: 0.78rem;
  color: var(--text-dim);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

@media (max-width: 560px) {
  .attribute-roll-summary {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 380px) {
  .attribute-roll-summary {
    grid-template-columns: minmax(0, 1fr);
  }
}

.attribute-roll-select {
  font: inherit;
  font-size: 0.85rem;
  background: var(--surface-alt);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 0.3rem 0.6rem;
  cursor: pointer;
  max-width: 100%;
}

.attribute-roll-select:hover {
  border-color: var(--accent);
}

.attribute-roll-note {
  margin: 0.5rem 0 0;
  font-size: 0.85rem;
  color: var(--text-dim);
  line-height: 1.4;
}

/* Title + dropdown + result text: one line when there's room; the note is the
   first to wrap onto its own line as the card narrows (title+dropdown keep
   sharing a line down to quite narrow widths), then fully stacked with the
   dropdown at full width once the card is genuinely mobile-narrow. */
.attribute-roll-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.3rem 0.75rem;
}

.attribute-roll-row .attribute-roll-table__heading,
.attribute-roll-row .attribute-roll-turn__label {
  flex: 0 0 auto;
  margin-bottom: 0;
}

.attribute-roll-row .attribute-roll-select {
  flex: 0 1 auto;
}

.attribute-roll-row .attribute-roll-note {
  flex: 1 1 220px;
  margin: 0;
}

@media (max-width: 480px) {
  .attribute-roll-row {
    flex-direction: column;
    align-items: stretch;
  }

  .attribute-roll-row .attribute-roll-select {
    width: 100%;
  }

  /* .attribute-roll-note's 220px flex-basis is a *width* hint for row layout —
     switching to flex-direction: column turns that same basis into a height,
     ballooning the note (and the whole panel) with empty space. */
  .attribute-roll-row .attribute-roll-note {
    flex-basis: auto;
  }
}

/* [hidden] overrides for these three: each also sets its own `display` (flex/
   grid) for the visible case, which would otherwise beat the UA stylesheet's
   [hidden] { display: none } on specificity and leave them showing anyway. */
.attribute-roll-turns[hidden],
.attribute-roll-turn-steps[hidden],
.attribute-roll-summary[hidden],
.attribute-roll-fold-chevron[hidden] {
  display: none;
}

.attribute-roll-turns {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

.attribute-roll-turn {
  padding-bottom: 0.6rem;
  border-bottom: 1px solid var(--border);
}

.attribute-roll-turn:last-child {
  border-bottom: none;
  padding-bottom: 0;
}

.attribute-roll-turn__label {
  font-size: 0.75rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-dim);
  min-width: 4.5rem;
}

/* The -/+ pair below the turn log adds a new turn or removes the last one —
   a step control, not two unrelated actions, so grouped like the tracker's
   status buttons rather than shown as separate buttons. */
.attribute-roll-turn-steps {
  display: inline-flex;
  border: 1px solid var(--border);
  border-radius: 6px;
  overflow: hidden;
  margin-top: 0.6rem;
}

.attribute-roll-turn-step {
  font: inherit;
  font-size: 0.95rem;
  background: var(--surface-alt);
  color: var(--text-dim);
  border: none;
  border-right: 1px solid var(--border);
  padding: 0.3rem 0.9rem;
  line-height: 1;
  cursor: pointer;
}

.attribute-roll-turn-step:last-child {
  border-right: none;
}

.attribute-roll-turn-step:hover:not(:disabled) {
  color: var(--text);
  border-color: var(--accent);
}

.attribute-roll-turn-step:disabled {
  opacity: 0.4;
  cursor: default;
}

/* ---- Rule modal ---- */

.rule-modal {
  position: fixed;
  inset: 0;
  z-index: 100;
  align-items: center;
  justify-content: center;
}

.rule-modal:not([hidden]) {
  display: flex;
}

.rule-modal__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.55);
}

.rule-modal__box {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  max-width: 480px;
  width: calc(100% - 2.5rem);
  max-height: 80vh;
  overflow-y: auto;
  padding: 1.5rem;
  font-size: 0.85rem;
}

.rule-modal__box h3 {
  margin-top: 0;
  padding-right: 1.5rem;
}

.rule-modal__close {
  position: absolute;
  top: 0.75rem;
  right: 0.75rem;
  background: none;
  border: none;
  color: var(--text-dim);
  font-size: 1.4rem;
  line-height: 1;
  cursor: pointer;
}

.rule-modal__close:hover {
  color: var(--text);
}

.nav-arrow {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 3.5rem;
  background: none;
  border: none;
  color: var(--text);
  font-size: 1.6rem;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background-color 0.15s ease;
}

.nav-arrow--prev { left: 0; }
.nav-arrow--next { right: 0; }

.nav-arrow:hover { background-color: rgba(0, 0, 0, 0.35); }

.nav-arrow[hidden] { display: none; }

@media (max-width: 640px) {
  .nav-arrow { display: none; }

  /* No arrows to clear on mobile — give the content the space back. */
  .unit-card__inner { padding-left: 1.25rem; padding-right: 1.25rem; }

  /* Narrow columns (M/WS/BS/.../AP) have just enough room for their 1-2
     character values at the desktop font size/padding — on a phone that's not
     enough, and a mid-word wrap (e.g. "AP" -> "A"/"P", "-1" -> "-"/"1") reads
     as broken. Shrinking the font and tightening just those columns' padding
     (rather than every column's) buys back enough width to fit without
     wrapping, and nowrap is a hard backstop even if it doesn't. */
  .stats-table,
  .weapon-table {
    font-size: 0.72rem;
  }

  .stats-table th:not(:first-child),
  .stats-table td:not(:first-child) {
    padding-left: 0.1rem;
    padding-right: 0.1rem;
    white-space: nowrap;
  }

  .weapon-table__col-name { width: 27%; }
  .weapon-table__col-range { width: 15%; }
  .weapon-table__col-s,
  .weapon-table__col-ap { width: 8%; }

  .weapon-table th.weapon-table__center,
  .weapon-table td.weapon-table__center {
    padding-left: 0.15rem;
    padding-right: 0.15rem;
    white-space: nowrap;
  }

  /* The 5-button status pill (Alive/Fleeing/.../Dead) doesn't fit its
     labels in less space and none of them can wrap (each is one word) —
     shrinking them buys enough room on most phones; the scrollbar is a
     fallback for anything narrower rather than letting it overflow the card. */
  .tracker-status-group {
    max-width: 100%;
    overflow-x: auto;
  }

  .tracker-status-btn {
    font-size: 0.75rem;
    padding: 0.3rem 0.45rem;
    white-space: nowrap;
  }
}

@media (max-width: 360px) {
  /* Even with the squeeze above, "WS" alone (the widest 2-letter header)
     needs ~18-19px at this font size — a hair more than 7% of a ~320px-wide
     card leaves after padding, so it visibly overlaps the next column's
     border. The name column has plenty of slack to give up a little more. */
  .stats-table th:not(:first-child),
  .stats-table td:not(:first-child) {
    width: 8%;
  }
}

/* ---- Pull-down-to-jump indicator ---- */

.pull-indicator {
  position: fixed;
  top: 3.5rem;
  left: 50%;
  transform: translateX(-50%) translateY(-12px);
  opacity: 0;
  display: flex;
  gap: 0.4rem;
  padding: 0.45rem 0.9rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 999px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.18);
  z-index: 90;
  pointer-events: none;
  transition: transform 0.25s ease, opacity 0.25s ease;
}

.pull-indicator--dragging {
  transition: none;
}

.pull-indicator__dot {
  width: 0.55rem;
  height: 0.55rem;
  border-radius: 50%;
  background: var(--border);
  transition: background 0.1s ease, transform 0.1s ease;
}

.pull-indicator__dot--lit {
  background: var(--accent);
  transform: scale(1.3);
}

/* ---- Unit picker (jump-to-unit sheet) ---- */

.unit-picker {
  position: fixed;
  inset: 0;
  z-index: 95;
  display: none;
}

.unit-picker:not([hidden]) {
  display: block;
}

.unit-picker__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.5);
  opacity: 0;
  transition: opacity 0.22s ease;
}

.unit-picker--open .unit-picker__backdrop {
  opacity: 1;
}

.unit-picker__panel {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  border-radius: 0 0 16px 16px;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
  transform: translateY(-100%);
  transition: transform 0.25s ease;
  max-height: 80vh;
  overflow-y: auto;
  padding: 1rem 1.25rem 1.5rem;
}

.unit-picker--open .unit-picker__panel {
  transform: translateY(0);
}

.unit-picker__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 0.75rem;
}

.unit-picker__header h3 {
  margin: 0;
  font-family: var(--heading-font);
}

.unit-picker__close {
  background: none;
  border: none;
  color: var(--text-dim);
  font-size: 1.4rem;
  line-height: 1;
  cursor: pointer;
}

.unit-picker__close:hover {
  color: var(--text);
}

.unit-picker__grid {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.unit-picker__item {
  background: var(--surface-alt);
  border: 1px solid var(--border);
  border-radius: 8px;
  color: var(--text);
  font: inherit;
  text-align: left;
  padding: 0.6rem 0.75rem;
  cursor: pointer;
  white-space: nowrap;
}

.unit-picker__item:hover {
  border-color: var(--accent);
}

.unit-picker__item[aria-current="true"] {
  border-color: var(--accent);
  color: var(--accent);
  font-weight: 600;
}

@media (prefers-color-scheme: light) {
  :root {
    --bg: #f7f5f0;
    --surface: #ffffff;
    --surface-alt: #f0eee8;
    --text: #232323;
    --text-dim: #6b6b6b;
    --accent: #8a6a1f;
    --border: #ddd8cc;
    --highlight-bg: rgba(138, 106, 31, 0.16);
  }
}
