:root {
  /* Every breakpoint in this file was a max-width, so nothing had an opinion
     about a wide screen and the layout simply kept stretching. This is the
     reading column: the header band and the workspace both align their
     contents to it, and it binds only above roughly 1400px. */
  --content-max: 88rem;
  color-scheme: light;
  --ink: #171717;
  --muted: #66605a;
  --line: #d8d0c7;
  --paper: #fffdf8;
  --panel: #f6f1e8;
  --accent: #9b2f21;
  --accent-dark: #6e1f17;
  --warn: #8c5a00;
  --bad: #9b1c1c;
  --ok: #2f6b3a;
  --focus: #1c4f9b;
  font-family: Georgia, "Times New Roman", serif;
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
}

button,
input,
select {
  font: inherit;
}

button {
  border: 1px solid var(--accent-dark);
  background: var(--accent);
  color: white;
  padding: 0.55rem 0.85rem;
  cursor: pointer;
  /* Touch targets: the Add buttons are used one-handed on a phone. */
  min-height: 2.4rem;
  transition: background-color 120ms ease, border-color 120ms ease, color 120ms ease;
}

button:hover:not(:disabled) {
  background: var(--accent-dark);
}

/* Pressed state reads as pressed rather than merely darker. */
button:active:not(:disabled) {
  background: var(--accent-dark);
  transform: translateY(1px);
}

button.secondary {
  background: transparent;
  color: var(--accent-dark);
}

button.secondary:hover:not(:disabled) {
  background: var(--accent-dark);
  color: white;
}

button:disabled {
  cursor: not-allowed;
  opacity: 0.55;
}

/* Nothing should suggest a disabled control will respond. */
button:disabled:hover {
  background: var(--accent);
}

button.secondary:disabled:hover {
  background: transparent;
  color: var(--accent-dark);
}

/*
 * One focus treatment for everything. :focus-visible so a mouse click does not
 * leave a ring behind, but a keyboard traversal is obvious on both the
 * parchment and the white panels - the stylesheet previously had no focus
 * style at all, so keyboard users could not tell where they were.
 */
:where(button, input, select, textarea, summary, a, [tabindex]):focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 2px;
  border-radius: 1px;
}

.app-header {
  display: flex;
  align-items: end;
  justify-content: space-between;
  gap: 1rem;
  /* The coloured band still runs edge to edge; only what sits on it is
     constrained. Without this, space-between threw Clear and Print to the far
     side of a 1600px screen, a long way from the title they belong to. */
  padding: 1.25rem max(clamp(1rem, 3vw, 2rem), calc((100% - var(--content-max)) / 2));
  border-bottom: 2px solid var(--ink);
  background: #efe3d3;
}

.eyebrow {
  margin: 0 0 0.2rem;
  color: var(--muted);
  text-transform: uppercase;
  font-size: 0.75rem;
  letter-spacing: 0.08em;
}

h1,
h2,
h3,
p {
  margin-top: 0;
}

h1 {
  margin-bottom: 0;
  font-size: clamp(1.45rem, 3vw, 2.4rem);
}

h2 {
  margin-bottom: 0;
  font-size: 1.25rem;
}

.header-actions {
  display: flex;
  gap: 0.5rem;
}

.app-shell {
  padding: 1rem clamp(1rem, 3vw, 2rem);
  max-width: var(--content-max);
  margin-inline: auto;
}

.controls {
  display: grid;
  /* Capped rather than fractional. A text box does not become more useful for
     being 500px wide, and the fr units meant the army picker and roster name
     grew without limit. Slack now collects to the right of the row instead. */
  grid-template-columns: minmax(14rem, 22rem) minmax(12rem, 20rem) 10rem;
  justify-content: start;
  gap: 0.75rem;
  margin-bottom: 1rem;
}

.house-rules-control {
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
  gap: 0.55rem;
  /* The banner holds one compact line of controls. Spanning the full row
     turned it into a wide empty strip on a desktop screen. */
  max-width: 54rem;
  border: 1px dashed #76558a;
  background: #f8f3fb;
  padding: 0.55rem 0.7rem;
}

.house-rules-info {
  flex: none;
  width: 1.5rem;
  height: 1.5rem;
  min-height: 0;
  padding: 0;
  border-radius: 50%;
  font-size: 0.8rem;
  font-weight: 700;
  font-style: italic;
  font-family: Georgia, "Times New Roman", serif;
  line-height: 1;
}

.house-rules-card .house-rules-list {
  margin: 0.5rem 0 0;
  padding-left: 1.1rem;
  display: grid;
  gap: 0.45rem;
}

.house-rules-card .house-rules-list strong {
  color: var(--ink);
}

.house-rules-switch {
  display: flex;
  min-width: 0;
  align-items: center;
  gap: 0.5rem;
  color: #533664;
}

.controls .house-rules-switch input {
  width: auto;
  min-height: 1rem;
  accent-color: #68407b;
}

label {
  display: grid;
  gap: 0.25rem;
  color: var(--muted);
  font-weight: 700;
}

input,
select {
  width: 100%;
  border: 1px solid var(--line);
  background: white;
  color: var(--ink);
  padding: 0.5rem;
  transition: border-color 120ms ease;
}

input:hover:not(:disabled),
select:hover:not(:disabled) {
  border-color: var(--muted);
}

input:focus,
select:focus {
  border-color: var(--focus);
}

select:not(:disabled) {
  cursor: pointer;
}

input:disabled,
select:disabled {
  background: var(--panel);
  color: var(--muted);
  cursor: not-allowed;
}

/*
 * A locked option is a checked, disabled checkbox - equipment the entry must
 * take. It should read as settled rather than broken, so it keeps full
 * contrast and only loses its pointer.
 */
.option-row input[type="checkbox"] {
  width: auto;
  cursor: pointer;
}

.option-row input[type="checkbox"]:disabled {
  cursor: default;
  opacity: 1;
}

.option-row:has(input[type="checkbox"]:disabled) {
  color: var(--muted);
}

.option-row:has(input[type="checkbox"]:not(:disabled)):hover {
  color: var(--accent-dark);
}

.status {
  min-height: 1.4rem;
  color: var(--muted);
  margin-bottom: 0.75rem;
}

.workspace {
  display: grid;
  grid-template-columns: minmax(18rem, 0.9fr) minmax(24rem, 1.3fr);
  gap: 1rem;
  align-items: start;
}

.workspace-toolbar {
  grid-column: 1 / -1;
  display: grid;
  gap: 0.2rem;
  border: 1px solid var(--line);
  background: var(--paper);
  padding: 0.4rem 0.6rem;
}

.workspace-toolbar-line {
  display: flex;
  align-items: center;
  justify-content: flex-end;
}

.workspace-tabs,
.add-feedback {
  display: none;
}

.unit-browser {
  grid-column: 1;
  grid-row: 2;
}

.roster-panel {
  grid-column: 2;
  grid-row: 2;
}

.unit-browser,
.roster-panel {
  border: 1px solid var(--line);
  background: var(--panel);
  min-height: 20rem;
}

.panel-heading {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.75rem;
  border-bottom: 1px solid var(--line);
}

.panel-heading input {
  max-width: 16rem;
}

.minimal-view-control {
  display: inline-flex;
  flex-direction: row;
  align-items: center;
  gap: 0.4rem;
  min-height: 2.4rem;
  color: var(--accent-dark);
  font-weight: 700;
}

.panel-heading .minimal-view-control input {
  width: auto;
  min-height: 1rem;
  margin: 0;
}

.unit-list,
.roster-list {
  padding: 0.75rem;
}

.category-block + .category-block {
  margin-top: 1rem;
}

.category-title {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 0.4rem;
  min-height: 2.4rem;
  margin: 0;
  color: var(--accent-dark);
  cursor: pointer;
  font-size: 1rem;
  border-bottom: 1px solid var(--line);
  padding: 0.3rem 0.35rem;
  list-style: none;
  transition: color 120ms ease, background-color 120ms ease;
}

.category-title::-webkit-details-marker,
.role-title::-webkit-details-marker {
  display: none;
}

.category-title::before,
.role-title::before {
  content: "\25b8";
  line-height: 1;
  transition: transform 120ms ease;
}

.category-block[open] > .category-title::before,
.role-block[open] > .role-title::before {
  transform: rotate(90deg);
}

.category-block[open] > .category-title {
  margin-bottom: 0.45rem;
}

.category-summary {
  color: var(--muted);
  font-size: 0.82rem;
  font-weight: 400;
  text-transform: none;
  white-space: nowrap;
}

.role-block + .role-block {
  margin-top: 0.7rem;
}

.role-title {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 0.35rem;
  min-height: 2.4rem;
  margin: 0;
  color: var(--muted);
  cursor: pointer;
  font-size: 0.88rem;
  padding: 0.25rem 0.35rem;
  list-style: none;
  text-transform: uppercase;
  transition: color 120ms ease, background-color 120ms ease;
}

.role-block[open] > .role-title {
  margin-bottom: 0.35rem;
}

.category-title:hover,
.role-title:hover {
  color: var(--accent-dark);
  background: #efe3d3;
}

.category-title:active,
.role-title:active {
  background: var(--panel);
}

.unit-row,
.roster-card {
  background: white;
  border: 1px solid var(--line);
  padding: 0.7rem;
}

.unit-row + .unit-row,
.roster-card + .roster-card {
  margin-top: 0.55rem;
}

.unit-row {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 0.75rem;
  transition: border-color 120ms ease, background-color 120ms ease;
}

.unit-row-details {
  display: flex;
  min-width: 0;
  min-height: 0;
  align-content: center;
  flex-wrap: wrap;
  gap: 0.1rem 0.55rem;
  border: 0;
  background: transparent;
  color: inherit;
  cursor: pointer;
  padding: 0;
  text-align: left;
}

.unit-row-details:hover {
  color: var(--accent-dark);
}

.unit-row-details .unit-title {
  flex: 0 0 100%;
}

.unit-row:hover {
  border-color: var(--accent);
  background: var(--panel);
}

/* A row containing the focused Add button should look reachable too. */
.unit-row:focus-within {
  border-color: var(--accent);
}

.unit-title,
.roster-title {
  font-weight: 700;
}

.roster-title,
.attachment-group-heading,
.attached-indication,
.print-unit strong {
  overflow-wrap: anywhere;
}

.meta {
  color: var(--muted);
  font-size: 0.9rem;
}

.source-status {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  color: var(--muted);
  font-size: 0.72rem;
  font-weight: 400;
  line-height: 1.2;
  white-space: nowrap;
}

.source-badges {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem 0.65rem;
}

.source-status::before {
  width: 0.4rem;
  height: 0.4rem;
  border: 1px solid currentColor;
  border-radius: 50%;
  content: "";
  flex: 0 0 auto;
}

.source-status.sourced {
  color: #456454;
}

.source-status.sourced::before,
.source-status.book-sourced::before {
  background: currentColor;
}

.source-status.book-sourced {
  color: #596373;
}

.source-status.audit {
  color: var(--warn);
  font-weight: 700;
}

.source-status.audit::before {
  border-radius: 1px;
  background: currentColor;
}

.source-status.faq-overridden {
  color: var(--accent-dark);
  font-weight: 700;
}

.source-status.faq-overridden::before {
  border-radius: 1px;
  background: currentColor;
  transform: rotate(45deg);
}

.source-status.house-rule-enabled {
  color: #68407b;
  font-weight: 700;
}

.source-status.house-rule-enabled::before {
  border-radius: 0;
  background: transparent;
  transform: rotate(45deg);
}

/* WH-091: a character standing in a regiment is drawn inside the regiment's
   group rather than in the Characters block. The rule is only ever visual - the
   entry, its points and its category are untouched. */
.roster-attachment-group {
  display: grid;
  gap: 0.4rem;
  border-left: 3px solid var(--accent);
  background: var(--panel);
  padding: 0.4rem 0 0.4rem 0.5rem;
  /* One group has to end visibly before the next begins, so the gap between
     them is larger than the gap between cards inside one. */
  margin-bottom: 0.9rem;
}

/* Alternating the rail is what tells two adjacent groups apart when both are
   attached to similarly named regiments. */
.roster-attachment-group[data-attachment-parity="1"] {
  border-left-color: var(--focus);
}

.roster-attachment-group[data-attachment-parity="1"] .attachment-group-heading {
  color: var(--focus);
}

.attachment-group-heading {
  font-weight: 700;
  color: var(--accent-dark);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-size: 0.7rem;
}

/* The characters sit above the regiment they are with, and are stepped in so
   the relationship reads at a glance rather than needing the heading. */
.roster-attachment-group .roster-card {
  margin-left: 0.75rem;
}

.roster-attachment-group .roster-card:last-child {
  margin-left: 0;
}

.attached-indication {
  color: var(--accent-dark);
  font-weight: 700;
}

.attachment-control {
  min-width: 0;
}

.attachment-control select {
  max-width: 100%;
}

/* WH-097: a grid item defaults to min-width:auto, so it refuses to shrink below
   its content's min-content width. The roster is grids inside grids - workspace,
   card, topline, build controls, option sections, group lists - so one wide
   thing deep inside held the whole column open and the page scrolled sideways
   on a phone. WH-071's long rune headings, "Weapon Runes - Runic hammer (one- or
   two-handed)", are what made it visible.

   Measured at 375px: the workspace column was 556px inside a 351px grid. With
   these, the app's scrollWidth is exactly 375 and the card 331, with nothing
   overflowing. Each of these is a grid or flex container whose child must be
   allowed to shrink so the overflow-x containers inside can do their job. */
#roster-panel,
#unit-panel,
.roster-card,
.roster-topline,
.roster-identity,
.build-controls,
.option-kind-sections,
.grouped-options,
.option-group,
.group-item-list,
.group-item-result,
.selection-summary {
  min-width: 0;
}

.roster-card {
  position: relative;
  display: grid;
  gap: 0.4rem;
}

.roster-card.is-minimised {
  min-height: 3.1rem;
  padding: 0.35rem 0.5rem;
}

.roster-card.is-minimised + .roster-card.is-minimised {
  margin-top: 0.15rem;
}

.roster-compact-summary {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 0.5rem;
  min-width: 0;
  min-height: 2.4rem;
  padding-right: 5.2rem;
}

.roster-compact-points {
  white-space: nowrap;
}

.roster-topline {
  display: grid;
  grid-template-columns: minmax(9rem, 0.7fr) minmax(20rem, 1.3fr);
  align-items: start;
  gap: 0.75rem;
}

.roster-entry-name {
  max-width: 24rem;
  margin-top: 0.35rem;
}

.roster-entry-name input {
  min-width: 0;
}

.roster-profile {
  min-width: 0;
  overflow-x: auto;
  font-size: 0.78rem;
}

.roster-profile .profile-table th,
.roster-profile .profile-table td {
  padding: 0.15rem 0.22rem;
}

.roster-card-actions {
  position: absolute;
  z-index: 1;
  top: 0.35rem;
  right: 0.35rem;
  display: flex;
  gap: 0.1rem;
}

button.roster-minimise,
button.roster-remove {
  display: grid;
  width: 2.4rem;
  min-height: 2.4rem;
  place-items: center;
  border-color: transparent;
  background: transparent;
  color: var(--accent-dark);
  padding: 0;
  font-family: Arial, sans-serif;
  font-size: 1.35rem;
}

button.roster-minimise {
  font-size: 1.1rem;
}

button.roster-minimise:hover:not(:disabled),
button.roster-remove:hover:not(:disabled) {
  border-color: var(--accent-dark);
  background: var(--accent-dark);
  color: white;
}

.roster-remove-confirmation {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.45rem 0.75rem;
  border: 1px solid var(--warn);
  background: #fff7e6;
  color: var(--warn);
  padding: 0.45rem;
  font-weight: 700;
}

.roster-remove-confirmation[hidden] {
  display: none;
}

.roster-remove-actions {
  display: flex;
  gap: 0.4rem;
}

.roster-controls,
.option-row {
  display: flex;
  /* Was align-items: center with flex-wrap. The label is one flex item, so a
     label too long for the line did not wrap its text - it moved wholesale to
     the next line and left the checkbox stranded on its own, which is what
     "Double-handed Weapon (2 pts/model) - max 1" looked like on a phone.
     Letting the label shrink makes the text wrap inside it instead, and start
     keeps the box on the first line rather than centred against a tall block. */
  align-items: start;
  gap: 0.35rem;
}

.option-row > span {
  min-width: 0;
}

.option-row input[type="checkbox"] {
  flex: none;
  margin-top: 0.2rem;
}

.option-row input[type="number"] {
  flex: 0 0 5rem;
  width: 5rem;
  min-height: 2.4rem;
}

.countable-option-block,
.option-carrier-rows {
  display: grid;
  gap: 0.35rem;
}

.option-carrier-rows {
  padding-left: 1.25rem;
}

/* --- WH-115 --------------------------------------------------------------

   The army books print EQUIPMENT before OPTIONS: what the model already
   carries, inside its points, before anything is bought. This is that line. It
   is deliberately quiet - it is a statement, not a control, and the eye should
   pass over it on the way to the things that can be changed. */
.unit-equipment {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.4rem;
  margin: 0.5rem 0 0.25rem;
  padding: 0.4rem 0.6rem;
  border-left: 3px solid var(--line);
  background: #fbf7ef;
  font-size: 0.9rem;
}

.unit-equipment-label {
  color: var(--accent-dark);
  font-size: 0.78rem;
  font-weight: 700;
  text-transform: uppercase;
}

.unit-equipment-text {
  min-width: 0;
  color: var(--ink);
}

/* A set is "one of the following weapons" - the same checkboxes, fenced and
   titled, so the exclusivity is visible before it is discovered by clicking. */
.option-set {
  margin-top: 0.5rem;
  padding: 0.4rem 0.5rem;
  border: 1px dashed var(--line);
}

.option-set-label {
  margin-bottom: 0.3rem;
  color: var(--muted);
  font-size: 0.78rem;
  font-weight: 700;
  text-transform: uppercase;
}

.roster-controls input {
  width: 5rem;
}

.roster-controls input[type="radio"] {
  width: auto;
  margin: 0;
}

.general-nomination {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  min-height: 2rem;
  color: var(--accent-dark);
  font-weight: 700;
}

.general-replacement {
  flex-basis: 100%;
}

.options {
  display: grid;
  gap: 0.35rem;
}

.build-controls {
  display: grid;
  gap: 0.3rem;
}

.option-kind-sections {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(5.75rem, 1fr));
  gap: 0.3rem;
}

.option-kind-sections:has(> .option-kind-details[open]) {
  grid-template-columns: 1fr;
}

.build-section,
.option-kind-details {
  background: #fffdfa;
  border: 1px solid var(--line);
  padding: 0.3rem;
}

.selection-summary {
  display: grid;
  gap: 0.25rem;
}

.build-heading,
.group-heading {
  color: var(--accent-dark);
  font-size: 0.82rem;
  font-weight: 700;
  text-transform: uppercase;
}

.grouped-options {
  display: grid;
}

.magic-search {
  display: grid;
  gap: 0.25rem;
  border-bottom: 1px solid var(--line);
  padding: 0.45rem 0.15rem;
}

.magic-search label {
  color: var(--accent-dark);
  font-size: 0.82rem;
  text-transform: uppercase;
}

.magic-search-results,
.group-item-list {
  display: grid;
  gap: 0.25rem;
  padding: 0.15rem;
}

.magic-search-results {
  max-height: min(24rem, 55vh);
  overflow-y: auto;
  overscroll-behavior: contain;
  /* WH-084: the max-height above was never the problem. This is a grid, and a
     grid with a constrained height stretches its rows to fill it - so twenty
     matches became twenty slivers instead of a scrollable list, helped by
     .group-item-result carrying overflow: hidden, which lets a row shrink below
     its own content. Pinning the rows to their natural height is what makes the
     overflow real and the scrolling happen. */
  align-content: start;
  grid-auto-rows: min-content;
}

.magic-search-results[hidden],
.grouped-options[hidden] {
  display: none;
}

.magic-search-status {
  color: var(--muted);
  font-size: 0.78rem;
  padding: 0.15rem;
}

.group-item-result {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  width: 100%;
  overflow: hidden;
  border: 1px solid var(--line);
  background: white;
}

button.group-item-details {
  display: grid;
  gap: 0.1rem;
  min-width: 0;
  border: 0;
  background: transparent;
  color: var(--ink);
  padding: 0.45rem;
  text-align: left;
}

button.group-item-details:hover:not(:disabled) {
  background: var(--panel);
  color: var(--accent-dark);
}

.group-item-result-topline {
  display: flex;
  justify-content: space-between;
  gap: 0.5rem;
}

.group-item-result-name {
  font-weight: 700;
}

.group-item-result-category {
  font-size: 0.78rem;
  color: var(--accent-dark);
  font-weight: 700;
  text-transform: uppercase;
}

button.group-item-equip {
  align-self: stretch;
  border-width: 0 0 0 1px;
  border-color: var(--line);
  padding: 0.4rem 0.65rem;
}

.group-item-quantity {
  display: grid;
  align-content: center;
  gap: 0.15rem;
  border-left: 1px solid var(--line);
  padding: 0.3rem 0.45rem;
  color: var(--muted);
  font-size: 0.72rem;
  font-weight: 700;
  text-align: center;
}

.group-item-quantity input {
  width: 4rem;
  min-width: 0;
}

.group-item-included {
  align-self: center;
  padding: 0.4rem 0.65rem;
}

.option-group {
  border-top: 1px solid var(--line);
}

.option-group:first-child {
  border-top: 0;
}

.option-group > summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  cursor: pointer;
  padding: 0.55rem 0.15rem;
  transition: color 120ms ease, background-color 120ms ease;
}

.option-group > summary > span:first-child {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.group-capacity {
  flex: 0 0 auto;
  white-space: nowrap;
}

.group-heading-meta {
  display: flex;
  flex: 0 0 auto;
  gap: 0.4rem;
  white-space: nowrap;
}

.option-group > summary:hover {
  color: var(--accent-dark);
  background: var(--panel);
}

.option-group[open] > summary {
  border-bottom: 1px solid var(--line);
}

.option-kind-details > summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.25rem;
  cursor: pointer;
  color: var(--accent-dark);
  font-size: 0.78rem;
  font-weight: 700;
  padding: 0.3rem 0.15rem;
  transition: color 120ms ease, background-color 120ms ease;
}

.option-kind-details > summary > span:first-child {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.option-kind-count {
  flex: 0 0 auto;
}

.option-kind-details > summary:hover,
.reference-details summary:hover {
  color: var(--accent-dark);
  background: var(--panel);
}

.selected-items {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem;
}

.item-chip {
  display: inline-flex;
  overflow: hidden;
  border-color: var(--line);
  border-style: solid;
  border-width: 1px;
  background: #efe3d3;
}

button.item-chip-details,
button.item-chip-remove {
  min-height: 2.4rem;
  border: 0;
  background: transparent;
  color: var(--ink);
  font-size: 0.82rem;
}

button.item-chip-details {
  padding: 0.2rem 0.4rem;
}

button.item-chip-remove {
  min-width: 2.4rem;
  border-left: 1px solid var(--line);
  padding: 0.2rem 0.45rem;
  font-family: Arial, sans-serif;
  font-size: 1.05rem;
}

button.item-chip-details:hover:not(:disabled),
button.item-chip-remove:hover:not(:disabled) {
  background: var(--accent-dark);
  color: white;
}

.restriction-dot {
  color: var(--warn);
  font-size: 0.78rem;
  font-weight: 700;
}

.option-row {
  color: var(--muted);
}

.option-row input[type="checkbox"] {
  width: auto;
}

.profiles {
  display: grid;
  gap: 0.3rem;
  font-size: 0.9rem;
}

.reference-details {
  border-top: 1px solid var(--line);
  padding-top: 0.45rem;
}

.reference-details summary {
  color: var(--muted);
  cursor: pointer;
  font-size: 0.88rem;
  font-weight: 700;
  padding: 0.2rem 0.15rem;
  transition: color 120ms ease, background-color 120ms ease;
}

.source-refs {
  display: grid;
  gap: 0.25rem;
  border-top: 1px solid var(--line);
  padding-top: 0.55rem;
}

.source-link {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
  font-size: 0.85rem;
}

.faq-override {
  display: grid;
  gap: 0.25rem;
  border-left: 3px solid var(--accent-dark);
  padding: 0.25rem 0 0.25rem 0.65rem;
  font-size: 0.85rem;
  line-height: 1.35;
}

.faq-override-heading {
  color: var(--accent-dark);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.house-rule-variant {
  display: grid;
  gap: 0.25rem;
  border-left: 3px dashed #76558a;
  padding: 0.25rem 0 0.25rem 0.65rem;
  color: var(--ink);
  font-size: 0.85rem;
  line-height: 1.35;
}

.house-rule-variant-heading,
.house-rule-inline {
  color: #68407b;
  font-weight: 700;
}

.house-rule-suggestion {
  /* The FAQ prints the suggested points one per line, and those lines are the
     substance of the suggestion - collapsing them into a paragraph buries the
     numbers a player is deciding on. */
  white-space: pre-line;
}

.house-rule-variant-heading {
  font-size: 0.72rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.house-rule-inline {
  padding: 0.25rem 0.15rem;
  font-size: 0.78rem;
}

.profile-table {
  width: 100%;
  border-collapse: collapse;
  background: #fffdfa;
}

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

.item-card-dialog {
  width: min(34rem, calc(100vw - 1.5rem));
  max-width: none;
  max-height: calc(100vh - 1.5rem);
  max-height: calc(100dvh - 1.5rem);
  margin: auto;
  overflow: auto;
  border: 2px solid var(--ink);
  background: #fffdf8;
  color: var(--ink);
  padding: 0;
  box-shadow: 0 1rem 3rem rgb(23 23 23 / 35%);
}

.item-card-dialog::backdrop {
  background: rgb(23 23 23 / 58%);
}

.armies-dialog {
  width: min(48rem, calc(100vw - 1.5rem));
  max-width: none;
  max-height: calc(100dvh - 1.5rem);
  margin: auto;
  overflow: auto;
  border: 2px solid var(--ink);
  background: #fffdf8;
  color: var(--ink);
  padding: 0;
  box-shadow: 0 1rem 3rem rgb(23 23 23 / 35%);
}

.armies-dialog::backdrop {
  background: rgb(23 23 23 / 58%);
}

.armies-dialog-content {
  position: relative;
  display: grid;
  gap: 1rem;
  padding: clamp(1rem, 4vw, 1.6rem);
}

.armies-list {
  display: grid;
  gap: 0.65rem;
}

.saved-army-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 0.75rem;
  align-items: center;
  border: 1px solid var(--line);
  background: var(--panel);
  padding: 0.75rem;
}

.saved-army-summary {
  min-width: 0;
}

.saved-army-name {
  margin: 0 0 0.2rem;
  overflow-wrap: anywhere;
}

.saved-army-name-input {
  width: 100%;
  margin-bottom: 0.25rem;
}

.saved-army-meta,
.saved-army-date {
  color: var(--muted);
  font-size: 0.85rem;
}

.saved-army-error {
  margin-top: 0.25rem;
  color: var(--bad);
  font-weight: 700;
}

.saved-army-actions {
  display: grid;
  grid-template-columns: repeat(2, minmax(5rem, 1fr));
  gap: 0.35rem;
}

.army-drop-zone {
  display: grid;
  gap: 0.5rem;
  border: 2px dashed var(--line);
  background: var(--panel);
  padding: 0.8rem;
  text-align: center;
}

.army-drop-zone.dragging {
  border-color: var(--accent-dark);
  background: #efe3d3;
}

.army-drop-zone input {
  margin-inline: auto;
  max-width: 100%;
}

.item-card {
  position: relative;
  display: grid;
  gap: 0.8rem;
  padding: clamp(1rem, 4vw, 1.6rem);
}

button.item-card-close {
  position: absolute;
  z-index: 1;
  top: 0.5rem;
  right: 0.5rem;
  display: grid;
  width: 2.4rem;
  min-height: 2.4rem;
  place-items: center;
  border-color: transparent;
  background: transparent;
  color: var(--ink);
  padding: 0;
  font-family: Arial, sans-serif;
  font-size: 1.35rem;
}

button.item-card-close:hover:not(:disabled) {
  border-color: var(--accent-dark);
  background: var(--accent-dark);
  color: white;
}

.item-card-topline {
  display: flex;
  min-height: 1.5rem;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.75rem;
  padding-right: 2.55rem;
  font-size: 0.76rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.item-card-category {
  color: var(--accent-dark);
}

.item-card-cost {
  flex: 0 0 auto;
}

.item-card-title {
  margin: 0;
  border-bottom: 4px solid var(--ink);
  padding-bottom: 0.45rem;
  font-size: clamp(1.55rem, 7vw, 2.35rem);
  line-height: 1;
}

.item-card-description {
  font-size: 1rem;
  line-height: 1.5;
}

.item-card-description p {
  margin: 0;
  white-space: pre-line;
}

.item-card-description p + p {
  margin-top: 0.75rem;
}

.item-card-profiles {
  overflow-x: auto;
}

.item-card-profile-note {
  line-height: 1.4;
}

.item-card-maximum {
  color: var(--muted);
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.item-card-sources {
  border-top: 1px solid var(--line);
  color: var(--muted);
  padding-top: 0.45rem;
  font-size: 0.82rem;
}

.item-card-sources summary {
  cursor: pointer;
  font-weight: 700;
}

.item-card-sources .source-link {
  overflow-wrap: anywhere;
}

.item-card-footer {
  display: grid;
  gap: 0.8rem;
  border-top: 2px solid var(--ink);
  padding-top: 0.75rem;
}

.item-card-restrictions {
  text-align: center;
  font-size: 0.76rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  line-height: 1.35;
  text-transform: uppercase;
}

.item-card-equip {
  min-width: 6rem;
  justify-self: end;
}

.totals {
  color: var(--muted);
  font-weight: 700;
  white-space: nowrap;
}

.composition-heading {
  display: flex;
  align-items: baseline;
  gap: 0.45rem;
  color: var(--muted);
  font-size: 0.75rem;
  line-height: 1.1;
}

.composition-heading strong {
  color: var(--ink);
  font-size: 0.82rem;
}

.composition-summary table {
  width: 100%;
  border-collapse: collapse;
  table-layout: fixed;
  font-size: 0.78rem;
  font-variant-numeric: tabular-nums;
  line-height: 1.15;
}

.composition-summary th,
.composition-summary td {
  padding: 0.08rem 0.25rem;
}

.composition-summary th {
  overflow: hidden;
  text-align: left;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.composition-summary thead {
  color: var(--muted);
  font-size: 0.7rem;
}

.composition-summary thead th:not(:first-child),
.composition-summary td {
  text-align: right;
}

.composition-summary thead th:nth-child(2) {
  width: 4rem;
}

.composition-summary thead th:nth-child(3),
.composition-summary thead th:nth-child(4) {
  width: 5rem;
}

.composition-summary thead th:nth-child(5) {
  width: 5.5rem;
}

.composition-summary tbody tr + tr {
  border-top: 1px solid var(--line);
}

.composition-row-outside {
  background: #fff0f0;
  color: var(--bad);
}

.composition-status {
  display: inline-grid;
  width: 1em;
  height: 1em;
  place-items: center;
  margin-right: 0.15rem;
  border-radius: 50%;
  font-family: Arial, sans-serif;
  font-size: 0.72rem;
  font-weight: 700;
  line-height: 1;
}

.composition-row-outside .composition-status {
  background: var(--bad);
  color: white;
}

.warnings {
  padding: 0.75rem 0.75rem 0;
}

.warning {
  border-left: 4px solid var(--warn);
  background: #fff5dc;
  padding: 0.5rem 0.65rem;
  margin-bottom: 0.45rem;
}

.warning.error {
  border-left-color: var(--bad);
  background: #fff0f0;
}

.print-view {
  display: none;
}

.attribution {
  color: var(--muted);
  padding: 1rem max(clamp(1rem, 3vw, 2rem), calc((100% - var(--content-max)) / 2)) 2rem;
  font-size: 0.9rem;
}

/*
 * Links are the one interactive type the first pass missed. Source references
 * appear on cards over white and parchment, and the attribution sits on the
 * paper footer, so the hover has to read on all three: underline carries it
 * where colour alone would be marginal against the muted footer text.
 */
a {
  color: var(--accent-dark);
  text-decoration-thickness: 1px;
  text-underline-offset: 0.15em;
  transition: color 120ms ease, background-color 120ms ease;
}

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

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

.attribution a {
  color: var(--accent-dark);
}

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

@media (prefers-reduced-motion: reduce) {
  /* WH-101: the flight is decoration and goes entirely. The count and the
     button acknowledgement are information, so they stay - they just arrive
     without movement. */
  .flight-ghost,
  .workspace-tab-count.is-landing {
    animation: none;
  }

  .flight-ghost {
    display: none;
  }

  .unit-row.is-added {
    transition: none;
  }

  *,
  *::before,
  *::after {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
  }

  button:active:not(:disabled) {
    transform: none;
  }
}

/* --- WH-105: the wide layout ---------------------------------------------

   Two problems that are the same problem. The page opened with content in the
   top fifth of the screen and nothing below it, then grew into a long scroll
   as a roster filled up - because the panels had a min-height, no ceiling and
   no scroll of their own, so they pushed the document taller instead of using
   the space already on screen.

   Above the tab breakpoint the page becomes one viewport-tall column and the
   panels scroll inside themselves. Nothing here guesses how much room the
   header, the controls and the composition table take: body is a flex column,
   the workspace is the part that grows, and min-height: 0 is what lets a flex
   or grid child shrink below its content so the overflow lands in the panel
   rather than on the document. A first pass used calc(100vh - 15rem) and that
   number was simply wrong - the toolbar alone is taller than it allowed for. */
/* The viewport-tall column needs a viewport tall enough to hold a header, the
   controls and two panels with their 12rem floors. WH-123: a Pixel 8a held
   sideways is 915px wide and about 310px tall once the browser has its share,
   and it met the 861px width gate - the workspace was laid out at 0px and the
   footer sat where the panels should be. Below 640px of height the page
   flows and scrolls like the phone layout does, and only the width-driven
   rules - columns, the composition tiles - apply. */
@media (min-width: 861px) and (min-height: 640px) {
  /* height, not min-height. WH-105 shipped min-height: 100dvh and the panels
     never scrolled once they had anything in them - a minimum is a floor, and
     the moment the roster outgrew the viewport the document simply got
     taller. The structural tests could not see that; a headless browser at
     1280px could: 6,848px of page for eight units, and zero panel scroll.
     A fixed height is the ceiling the panels need to have overflow at all. */
  body {
    display: flex;
    flex-direction: column;
    height: 100dvh;
  }

  /* The shell scrolls only in the degenerate case - a window too short for
     the header, controls and composition table to fit above the panels' floor.
     On any ordinary desktop window it never does, and the panels take over. */
  .app-shell {
    flex: 1;
    display: flex;
    flex-direction: column;
    min-height: 0;
    overflow-y: auto;
  }

  .workspace {
    flex: 1;
    min-height: 0;
    /* The toolbar keeps its own height; the panel row takes the rest, down to
       a floor that keeps both panels readable on a short window. */
    grid-template-rows: auto minmax(12rem, 1fr);
  }

  .unit-browser,
  .roster-panel {
    /* .workspace sets align-items: start, which would leave both panels at
       their content height and defeat the 1fr row. */
    align-self: stretch;
    min-height: 0;
    overflow-y: auto;
    overscroll-behavior: contain;
  }

  /* The headings stay put while their list moves under them, which is the
     point of scrolling the panel rather than the page. */
  .panel-heading {
    position: sticky;
    top: 0;
    z-index: 2;
    background: var(--panel);
  }

}

@media (min-width: 861px) {
  /* WH-116. The composition table was the elongated strip: five short rows,
     each stretched to the full reading column and stacked 180px tall, on a
     screen with room to spare sideways and none to spare downward - the two
     panels were getting 340px of a 900px window. Transposed, each category is
     a tile and the whole thing is one row. The <table> markup is untouched, so
     the phone keeps its columns and the tests keep their cells. */
  .composition-summary table {
    display: block;
    width: auto;
  }

  .composition-summary thead {
    display: none;
  }

  .composition-summary tbody {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
    gap: 0.35rem 0.5rem;
  }

  .composition-summary tr {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    column-gap: 0.35rem;
    padding: 0.3rem 0.5rem;
    border: 1px solid var(--line);
    background: var(--panel);
  }

  /* The name takes the first line; the four values share the second. */
  .composition-summary tr > th {
    flex-basis: 100%;
    padding: 0;
    text-align: left;
  }

  .composition-summary tr > td {
    padding: 0;
    text-align: left;
    color: var(--muted);
    white-space: nowrap;
  }

  /* The headings the tile lost, written as each value's own label so it reads
     as a phrase - "8 units", "530 pts" - rather than a bare number. */
  .composition-summary td[data-composition-count]::after { content: " units"; }
  .composition-summary td[data-composition-points]::after { content: " pts"; }
  .composition-summary td[data-composition-percentage]::before { content: "\B7  "; }
  .composition-summary td[data-composition-allowance]::before { content: "\B7  allowed "; }

  .composition-row-outside {
    border-color: var(--accent-dark);
  }

  /* An empty status line still took its minimum height and margin - a blank
     strip between the controls and the composition table on every desktop
     load. Collapse it until it has something to say. */
  .status:empty {
    min-height: 0;
    margin: 0;
  }

  /* The house-rules banner spanned its own row - 65px for a checkbox and a
     sentence - while the right third of the controls row sat empty beside it.
     On a wide screen it is a fourth column, and the controls are one line. */
  .controls {
    grid-template-columns: minmax(14rem, 22rem) minmax(12rem, 20rem) 10rem minmax(20rem, 34rem);
    align-items: start;
  }

  .house-rules-control {
    grid-column: 4;
    max-width: none;
    margin-top: 1.35rem; /* sits level with the inputs, below their labels */
  }
}

/* Wide but short - a phone on its side, a small laptop with the browser
   chrome open. The header was taking 121px of a 310px viewport. */
@media (min-width: 861px) and (max-height: 639px) {
  .app-header {
    align-items: center;
    padding-top: 0.5rem;
    padding-bottom: 0.5rem;
  }

  .app-header .eyebrow {
    display: none;
  }

  .app-header h1 {
    font-size: 1.25rem;
  }

  .header-actions {
    display: flex;
    flex-wrap: nowrap;
    gap: 0.4rem;
  }

  .header-actions button {
    min-height: 2.2rem;
    padding: 0.3rem 0.7rem;
  }

  .attribution {
    padding-top: 0.5rem;
    padding-bottom: 0.75rem;
    font-size: 0.8rem;
  }
}

@media (max-width: 860px) {
  .saved-army-row {
    grid-template-columns: 1fr;
  }

  .saved-army-actions {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }

  .saved-army-actions button {
    min-width: 0;
    padding-inline: 0.3rem;
    font-size: 0.82rem;
  }

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

  /* At 375px an indent inside an indent leaves the card too narrow to read, so
     the step is dropped and the coloured rail carries the relationship. */
  .roster-attachment-group .roster-card {
    margin-left: 0;
  }

  .roster-attachment-group {
    padding-left: 0.35rem;
  }

  .workspace {
    grid-template-columns: 1fr;
  }

  .workspace-toolbar {
    position: sticky;
    z-index: 10;
    top: 0;
    gap: 0.2rem;
    padding: 0.25rem;
  }

  .workspace-toolbar-line {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: 0 0.75rem;
    align-items: center;
  }

  .workspace-tabs {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  button.workspace-tab {
    min-height: 2.4rem;
    border-color: var(--accent-dark);
    background: transparent;
    color: var(--accent-dark);
    padding: 0.35rem 0.75rem;
  }

  button.workspace-tab + button.workspace-tab {
    border-left: 0;
  }

  button.workspace-tab:hover:not(:disabled) {
    background: #efe3d3;
  }

  button.workspace-tab[aria-selected="true"] {
    background: var(--accent);
    color: white;
  }

  button.workspace-tab[aria-selected="true"]:hover:not(:disabled) {
    background: var(--accent-dark);
  }

  .totals {
    font-size: 0.9rem;
  }

  .add-feedback {
    display: block;
    height: 0;
    overflow: hidden;
    color: var(--muted);
    font-size: 0.8rem;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .add-feedback:not(:empty) {
    height: auto;
    margin-top: 0.35rem;
  }

  .unit-browser,
  .roster-panel {
    grid-column: 1;
    grid-row: auto;
  }

  .workspace-panel-inactive {
    display: none;
  }

  .composition-summary table {
    font-size: 0.72rem;
  }

  .composition-summary th,
  .composition-summary td {
    padding: 0.05rem 0.15rem;
  }

  .composition-summary thead th:nth-child(2) {
    width: 2.4rem;
  }

  .composition-summary thead th:nth-child(3) {
    width: 3.2rem;
  }

  .composition-summary thead th:nth-child(4) {
    width: 3rem;
  }

  .composition-summary thead th:nth-child(5) {
    width: 4.2rem;
  }
}

@media (max-width: 600px) {
  /* WH-079, owner-reported: the equipment, mount and magic-item disclosures
     read better stacked on a phone. .option-kind-sections packs the CLOSED
     kind headers into a compact auto-fit row, which is what keeps a collapsed
     card short (WH-030) - but at 375px that row becomes four ~5.75rem columns
     of cramped tap targets. Stack them here only; the wide layout and the
     already-single-column open state are both untouched. */
  .option-kind-sections {
    grid-template-columns: 1fr;
  }

  .capped-group-heading-meta .group-item-count {
    display: none;
  }

  .general-nomination {
    min-height: 2.75rem;
  }

  .app-header {
    align-items: center;
    gap: 0.6rem;
    padding: 0.55rem 0.75rem;
  }

  .app-header > div:first-child {
    min-width: 0;
  }

  .eyebrow {
    display: none;
  }

  h1 {
    font-size: clamp(0.95rem, 4vw, 1.05rem);
    line-height: 1.1;
  }

  .header-actions {
    flex: 0 1 10rem;
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 0.35rem;
  }

  .header-actions button,
  .unit-row button {
    min-height: 2.75rem;
  }

  .header-actions button {
    min-width: 0;
    padding: 0.3rem 0.35rem;
    font-size: 0.78rem;
  }

  .app-shell {
    padding: 0.55rem 0.75rem;
  }

  .controls {
    grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr) minmax(5.1rem, 0.75fr);
    gap: 0.4rem;
    margin-bottom: 0.55rem;
  }

  .controls label {
    min-width: 0;
    gap: 0.15rem;
    font-size: 0.7rem;
    line-height: 1.1;
  }

  .controls input,
  .controls select {
    min-width: 0;
    min-height: 2.75rem;
    padding: 0.4rem;
    font-size: 1rem;
  }

  .controls .house-rules-switch input {
    min-height: 1rem;
  }

  .status:empty {
    min-height: 0;
    margin-bottom: 0;
  }

  .panel-heading {
    gap: 0.6rem;
    padding: 0.55rem;
  }

  .panel-heading input {
    min-height: 2.75rem;
  }

  .unit-list,
  .roster-list {
    padding: 0.55rem;
  }

  .category-block + .category-block {
    margin-top: 0.7rem;
  }

  .unit-row {
    gap: 0.5rem;
    padding: 0.35rem;
  }

  .unit-row + .unit-row {
    margin-top: 0.35rem;
  }

  .unit-row-details {
    min-width: 0;
  }

  .unit-row .unit-title {
    flex: 0 0 100%;
    line-height: 1.15;
  }

  .unit-row .meta {
    font-size: 0.72rem;
    line-height: 1.2;
  }

  .unit-row .source-status {
    align-self: center;
    font-size: 0.68rem;
  }

  .unit-row > button:not(.unit-row-details) {
    align-self: center;
    padding: 0.35rem 0.65rem;
  }
}

@media (max-width: 340px) {
  .controls {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .controls label:first-child {
    grid-column: 1 / -1;
  }
}

@media print {
  /* Undo the viewport-tall column: a printed page is not a screen, and
     min-height: 100dvh there is a blank first page waiting to happen. */
  body {
    display: block;
    height: auto;
    min-height: 0;
    background: white;
    color: black;
  }

  .app-shell {
    overflow: visible;
  }

  .app-shell {
    display: block;
  }

  .no-print,
  .attribution {
    display: none !important;
  }

  .app-shell {
    padding: 0;
  }

  .status,
  .workspace {
    display: none;
  }

  .print-view {
    display: block;
  }

  .print-view h1 {
    font-size: 20pt;
    border-bottom: 2px solid black;
    padding-bottom: 0.25rem;
  }

  .print-summary,
  .print-unit,
  .print-warning {
    break-inside: avoid;
  }

  /* The owner asked for the nesting on paper too, for ease of reading. Keeping
     a regiment and its characters on one page is the point of the group. */
  .print-attachment-group {
    break-inside: avoid;
  }

  .print-unit-attached {
    padding-left: 1.2rem;
    border-top: 1px dotted black;
  }

  .print-summary {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 0.4rem;
    margin: 0.6rem 0 1rem;
    font-size: 10pt;
  }

  .print-unit {
    border-top: 1px solid black;
    padding: 0.35rem 0;
    font-size: 10pt;
  }

  .print-unit strong {
    font-size: 11pt;
  }

  .print-warning {
    border: 1px solid black;
    padding: 0.25rem;
    margin: 0.25rem 0;
    font-size: 9pt;
  }
}

/* --- WH-101: adding a unit says so where you are looking ---------------- */

/* The count on the Roster tab. It is the only part of the roster visible from
   the Units tab on a phone, so it is what tells you the add landed - and it is
   what the flight ghost flies to. */
.workspace-tab-count {
  display: inline-block;
  margin-left: 0.4rem;
  min-width: 1.35em;
  padding: 0 0.35em;
  border-radius: 999px;
  background: var(--accent);
  color: var(--panel);
  font-size: 0.82em;
  font-variant-numeric: tabular-nums;
  line-height: 1.5;
  text-align: center;
}

/* On the selected tab the accent is already the background, so the badge has
   to invert or it disappears into it. */
.workspace-tab[aria-selected="true"] .workspace-tab-count {
  background: var(--panel);
  color: var(--accent);
}

.workspace-tab-count.is-landing {
  animation: roster-count-land 420ms ease-out;
}

@keyframes roster-count-land {
  0% { transform: scale(1); }
  40% { transform: scale(1.35); }
  100% { transform: scale(1); }
}

/* The acknowledgement on the button that was actually pressed. Both the label
   and the row change, so it reads at a glance and at arm's length. */
.unit-row > button[data-add-unit].is-added {
  background: var(--ok);
  border-color: var(--ok);
}

.unit-row.is-added {
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  transition: background 180ms ease-out;
}

/* The ghost layer sits above the page and takes no space, so nothing it does
   can move the layout underneath it. pointer-events stays off - a ghost must
   never eat a tap meant for the button behind it. */
.flight-layer {
  position: fixed;
  inset: 0;
  z-index: 60;
  pointer-events: none;
  overflow: hidden;
}

.flight-ghost {
  position: fixed;
  max-width: 12rem;
  padding: 0.2rem 0.5rem;
  border-radius: 0.4rem;
  background: var(--accent);
  color: var(--panel);
  font-size: 0.8rem;
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  animation: fly-to-roster 380ms cubic-bezier(0.4, 0, 0.5, 1) forwards;
}

/* The arc: it lifts slightly before travelling, which is what makes it read as
   filing something away rather than sliding a box across the screen. */
@keyframes fly-to-roster {
  0% {
    transform: translate(0, 0) scale(1);
    opacity: 0.95;
  }
  35% {
    transform: translate(calc(var(--flight-x) * 0.3), calc(var(--flight-y) * 0.25 - 1.2rem)) scale(0.92);
    opacity: 0.95;
  }
  100% {
    transform: translate(var(--flight-x), var(--flight-y)) scale(0.35);
    opacity: 0;
  }
}

/* WH-133: section headers that hold their place when open. The card's
   structure - Equipment / Magic Items / Mounts, and the groups inside each -
   is drawn in a second accent so it never competes with the red that marks
   actions and warnings. Solid band for a section, tinted band with a left bar
   for a group inside it; the open state keeps the band and frames what it
   opened, so the heading does not dissolve into its own contents. */
:root {
  --accent-2: #2b5d63;
  --accent-2-dark: #1e454a;
  --accent-2-tint: #e4eeef;
  --accent-2-line: #b5cfd2;
}

/* Unit browser and roster panel: Characters / Regiments / Monsters. */
.category-title {
  background: var(--accent-2);
  color: #fff;
  border-bottom: 0;
  border-radius: 3px;
  padding: 0.4rem 0.6rem;
  font-weight: 700;
  letter-spacing: 0.01em;
}

.category-title .category-summary {
  color: rgba(255, 255, 255, 0.82);
}

.category-title:hover {
  color: #fff;
  background: var(--accent-2-dark);
}

.category-title:active {
  background: var(--accent-2-dark);
}

.category-block[open] > .category-title {
  border-radius: 3px 3px 0 0;
  margin-bottom: 0;
}

.category-block[open] > .category-content {
  border-left: 3px solid var(--accent-2-line);
  padding: 0.45rem 0 0.2rem 0.5rem;
}

/* The roles inside a category: GENERAL, HERO, WIZARD. */
.role-title {
  background: var(--accent-2-tint);
  color: var(--accent-2-dark);
  border-left: 3px solid var(--accent-2);
  border-radius: 0 3px 3px 0;
  font-weight: 700;
  letter-spacing: 0.02em;
  padding: 0.3rem 0.55rem;
}

.role-title .category-summary {
  color: var(--accent-2-dark);
  opacity: 0.75;
}

.role-title:hover {
  color: var(--accent-2-dark);
  background: #d5e4e6;
}

.role-block[open] > .role-title {
  margin-bottom: 0.45rem;
}

.role-block + .role-block {
  margin-top: 0.5rem;
}

/* A unit card in the roster: the name is the header, and a coloured top edge
   separates one card from the next when several are open. */
.roster-card {
  border-top: 4px solid var(--accent-2);
}

.roster-card.error-card {
  border-top-color: var(--bad);
}

.roster-title {
  color: var(--accent-2-dark);
  font-size: 1.1rem;
}

.roster-card.is-minimised .roster-title {
  font-size: 1rem;
}

/* Sections on the card: Equipment / Magic Items / Mounts. */
.option-kind-details {
  border-color: var(--accent-2-line);
  padding: 0;
}

.option-kind-details > summary {
  background: var(--accent-2);
  color: #fff;
  font-size: 0.82rem;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  padding: 0.45rem 0.6rem;
}

.option-kind-details > summary:hover {
  color: #fff;
  background: var(--accent-2-dark);
}

.option-kind-details > summary .option-kind-count {
  color: #fff;
  background: rgba(255, 255, 255, 0.18);
  border-radius: 999px;
  min-width: 1.5rem;
  padding: 0.05rem 0.5rem;
  text-align: center;
  font-weight: 700;
}

.option-kind-details > summary .option-kind-count.is-over-allowance {
  color: var(--warn);
  background: #fff5dc;
}

.option-kind-details[open] {
  border-color: var(--accent-2);
}

.option-kind-details[open] > summary ~ * {
  margin-left: 0.3rem;
  margin-right: 0.3rem;
}

.option-kind-details[open] > summary + * {
  margin-top: 0.35rem;
}

.option-kind-details[open] > :last-child {
  margin-bottom: 0.35rem;
}

/* Groups inside a section: EQUIPMENT - HAND-TO-HAND, MAGIC WEAPONS. */
.option-group {
  border-top: 0;
}

.option-group + .option-group {
  margin-top: 0.35rem;
}

.option-group > summary {
  background: var(--accent-2-tint);
  color: var(--accent-2-dark);
  border-left: 3px solid var(--accent-2);
  border-radius: 0 3px 3px 0;
  padding: 0.5rem 0.55rem;
}

.option-group > summary:hover {
  color: var(--accent-2-dark);
  background: #d5e4e6;
}

.option-group > summary .group-capacity,
.option-group > summary .group-heading-meta {
  color: var(--accent-2-dark);
  opacity: 0.8;
}

.option-group[open] > summary {
  border-bottom: 0;
  border-radius: 0 3px 0 0;
}

.option-group[open] > summary ~ * {
  border-left: 3px solid var(--accent-2-line);
  padding-left: 0.4rem;
}

.option-group[open] {
  padding-bottom: 0.3rem;
}

/* WH-133 follow-up: the roster's Characters band reads "3 units - 246 pts,
   2 shown with their unit", which at phone width ran under the title. The
   title keeps its own width; the summary takes what is left and wraps,
   right-aligned, instead of refusing to. */
.category-title {
  grid-template-columns: auto max-content minmax(0, 1fr);
}

.category-title .category-summary {
  white-space: normal;
  text-align: right;
  line-height: 1.25;
}

/* WH-156: the header, olde worlde. The owner asked for something more epic in
   the second accent. A deep teal band with a faint vignette so it reads as
   cloth rather than flat paint; the title in parchment small caps with a
   little tracking and a shadow to lift it; a gold double rule beneath, the
   way a book's title page is ruled; the eyebrow as a gilt inscription
   between two ornaments. No web font - the display face is the serif every
   device already has, set in small caps, so nothing loads and nothing
   flashes. */
:root {
  --gilt: #c9a961;
  --parchment: #f1e6d2;
}

.app-header {
  color: var(--parchment);
  background:
    radial-gradient(ellipse at 20% 0%, rgba(255, 255, 255, 0.08), transparent 55%),
    radial-gradient(ellipse at 100% 100%, rgba(0, 0, 0, 0.28), transparent 60%),
    linear-gradient(180deg, #2b5d63 0%, #1e454a 100%);
  border-bottom: 0;
  box-shadow:
    inset 0 -1px 0 var(--gilt),
    inset 0 -4px 0 #1e454a,
    inset 0 -5px 0 var(--gilt),
    0 2px 6px rgba(0, 0, 0, 0.25);
  padding-bottom: calc(1.25rem + 5px);
}

.app-header .eyebrow {
  color: var(--gilt);
  font-variant: small-caps;
  text-transform: none;
  font-size: 0.82rem;
  letter-spacing: 0.22em;
}

.app-header .eyebrow::before,
.app-header .eyebrow::after {
  content: "\2756";
  margin: 0 0.5em;
  font-size: 0.7em;
  vertical-align: 0.12em;
  opacity: 0.8;
}

.app-header h1 {
  font-family: "Palatino Linotype", "Book Antiqua", Palatino, Georgia, "Times New Roman", serif;
  font-variant: small-caps;
  font-weight: 700;
  letter-spacing: 0.05em;
  line-height: 1.08;
  color: var(--parchment);
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.35), 0 0 18px rgba(0, 0, 0, 0.25);
}

.app-header button.secondary {
  border-color: var(--parchment);
  color: var(--parchment);
  background: rgba(0, 0, 0, 0.12);
}

.app-header button.secondary:hover:not(:disabled),
.app-header button.secondary:active:not(:disabled) {
  background: var(--parchment);
  color: var(--accent-2-dark);
  border-color: var(--parchment);
}

.app-header button:not(.secondary) {
  border-color: #6e1f17;
  box-shadow: 0 1px 0 rgba(0, 0, 0, 0.3);
}

@media (max-width: 860px) {
  .app-header {
    padding-bottom: calc(0.55rem + 5px);
  }

  /* On a phone the eyebrow goes (it always has); the title carries the gilt
     itself with a hairline above it, so the band is not just teal. */
  .app-header h1 {
    letter-spacing: 0.03em;
  }
}

/* WH-157: the name. "Fantasy War | 4th Edition Army Builder" - the name in
   full weight, the edition lighter after a gilt divider, so the band reads as
   a masthead rather than a sentence. On a phone the two halves stack. */
.app-header h1 {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0 0.45em;
}

.app-header .title-divider {
  color: var(--gilt);
  font-weight: 400;
  opacity: 0.9;
}

.app-header .title-edition {
  font-weight: 400;
  font-size: 0.78em;
  letter-spacing: 0.06em;
  color: var(--parchment);
  opacity: 0.92;
}

@media (max-width: 860px) {
  .app-header .title-divider {
    display: none;
  }

  .app-header .title-name,
  .app-header .title-edition {
    flex-basis: 100%;
  }

  /* Short name, so it can be big where the long one could not. */
  .app-header .title-name {
    font-size: 1.6rem;
    line-height: 1;
  }

  .app-header .title-edition {
    font-size: 0.82rem;
    letter-spacing: 0.1em;
  }
}

/* WH-158: the owner's logo on the masthead - a lion shield over a scroll
   that already carries the name, so the title text is for screen readers
   and the eyebrow stands beside it as an inscription. The 313px PNG is
   124 KB and sharp at twice the largest display size. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

.app-header {
  align-items: center;
}

.masthead {
  display: flex;
  align-items: center;
  gap: 1rem;
  min-width: 0;
}

.masthead-title {
  margin: 0;
  display: block;
  line-height: 0;
}

.masthead-logo {
  display: block;
  width: auto;
  height: clamp(5.5rem, 12vw, 8.5rem);
  filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.45));
}

.app-header .masthead .eyebrow {
  margin: 0;
  max-width: 12rem;
  line-height: 1.5;
}

.app-header .masthead .eyebrow::before,
.app-header .masthead .eyebrow::after {
  content: none;
}

@media (max-width: 860px) {
  .app-header {
    align-items: center;
    padding-top: 0.5rem;
  }

  .masthead-logo {
    height: 5.75rem;
  }
}

/* A phone on its side (WH-123): the header must stay under 70px, so the
   logo drops to the height of the buttons beside it. */
@media (min-width: 861px) and (max-height: 639px) {
  .masthead-logo {
    height: 2.6rem;
  }

  .app-header {
    padding-top: 0.4rem;
    padding-bottom: calc(0.4rem + 5px);
  }
}

/* WH-161: an allied entry says which list it came from, in the second accent
   so it reads as structure (like the section it sits in) rather than a warning. */
.allied-tag {
  display: inline-block;
  align-self: flex-start;
  padding: 0.05rem 0.45rem;
  border: 1px solid var(--accent-2-line);
  border-radius: 3px;
  background: var(--accent-2-tint);
  color: var(--accent-2-dark);
  font-weight: 600;
}

/* WH-162/163: allied lists in the unit browser. A list is a band one shade
   darker than the section that holds it, and inside it the categories and
   roles are drawn exactly as the host's own - same blocks, same memory of
   what was opened. */
.ally-block > .role-title {
  background: var(--accent-2-dark);
  color: #fff;
  border-left-color: var(--gilt);
  text-transform: none;
  font-size: 0.95rem;
  padding: 0.35rem 0.6rem;
}

.ally-block > .role-title .category-summary,
.ally-block > .role-title:hover {
  color: #fff;
}

.ally-block > .role-title:hover {
  background: var(--accent-2);
}

.ally-content {
  padding-left: 0.5rem;
  border-left: 3px solid var(--accent-2-line);
}

.ally-content .category-block + .category-block {
  margin-top: 0.5rem;
}

.ally-unavailable {
  margin: 0.5rem 0 0.2rem 0.1rem;
}
