/* ==========================================================================
   saved.css — Feature 1: saved NPCs + saved monsters (create_from_rolled_results).
   The "My NPCs" section + the Bestiary "My Monsters" tab share these styles.
   Reuses existing tokens + the .btn / .list-row idioms — no new visual language,
   no new colour tokens, no border-radius.
   ========================================================================== */

/* --- Page wrapper (My NPCs section) ------------------------------------- */
.saved-page {
  /* Full module width to match the Maps/World pages (Ken 2026-06-23). The former
     `max-width: 60rem` capped People/Shops/Taverns ~284px narrower than Maps Home,
     which is uncapped (.maps-home is a bare .module-view spanning #app-content).
     Removing the cap lets the roster/conjure/edit bodies span the same full width
     as Maps; #app-content owns the outer cap + horizontal padding for both. */
  /* Site-wide rhythm (KDL 2026-06-27, Rule 2): the header->content gap is
     --space-md (16px), matching .maps-home, NOT the default .module-view
     --space-lg (24px). One rule for People/Shops/Taverns roster + conjure +
     edit pages so they read identically to Maps Home (breadcrumb 4px -> header
     -> 16px -> content, no bordered band). */
  gap: var(--space-md);
}

/* Site-wide breadcrumb -> header -> content rhythm (KDL 2026-06-27, Rule 2):
   NO bordered band. The header sits a tight --space-sm above the content; the
   header->content gap itself is owned by the wrapper (.saved-page below tightens
   .module-view to --space-md, matching .maps-home). This base rule covers the
   roster header AND the bare conjure/edit sub-headers (no --actions modifier).

   Baseline-row layout (KDL 2026-06-27): the bare conjure/edit sub-views put
   title + tagline directly in .saved-page__head with NO .saved-page__head-text
   wrapper, so without this they stacked (tagline on a 2nd line) and added a full
   tagline line-height of dead band before the content — unlike Maps and the
   roster landing pages. Flex + baseline puts the tagline inline beside the title
   on one row, matching .saved-page__head-text / .module-view__head-left. The
   --actions variant below fully re-declares display/align/justify/gap, so it is
   unaffected (its title block + upper-right Conjure button still sit apart). */
.saved-page__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  column-gap: var(--space-sm);
  row-gap: 0;
  margin-bottom: var(--space-sm);
  padding-bottom: 0;
  border-bottom: none;
}

/* Roster header with an upper-right primary action (Conjure …), DN-3. Title +
   tagline sit left; the button pins right and wraps below on narrow widths.
   Reuses .btn--primary; only the layout is new (no new colour tokens). */
.saved-page__head--actions {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-sm) var(--space-md);
}

/* Title + tagline cluster. Must mirror .module-view__head-left (layout.css):
   flex + baseline so the tagline sits inline to the RIGHT of the title on one
   row, NOT stacked below it. The block default here was adding a full tagline
   line-height (~28px) of dead vertical band between the heading and the roster
   on People/Shops/Taverns, which Maps (using .module-view__head-left) does not
   have. One row = same head->content gap as Maps Home. */
.saved-page__head-text {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  column-gap: var(--space-sm);
  row-gap: 0;
  min-width: 0;
}

/* Upper-right action cluster (#5, 2026-06-27): the Surprise Me + Conjure buttons
   sit inline on one row, pinned right by the head's space-between, wrapping below the
   title on narrow widths like the lone Conjure button did. */
.saved-page__head-actions {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: var(--space-xs, 0.4rem);
  flex-wrap: wrap;
  align-self: center;
}

.saved-page__conjure {
  flex: 0 0 auto;
  align-self: center;
}

/* Back affordance in a conjure sub-view header (in addition to the breadcrumb).
   A subtle secondary button above the title. */
.saved-page__back {
  margin-bottom: var(--space-sm);
}

/* Gender control on the Conjure a Commoner header (Ken #7b, 2026-06-27). Gender is
   set create-then-PATCH (the public POST carries no gender); the select drops onto its
   OWN full-width row below the baseline-aligned title + tagline (flex-basis: 100% in
   the wrapping .saved-page__head row), label + select inline. No new tokens — reuses
   the shared .field idiom and spacing scale. */
.saved-conjure__gender {
  flex: 0 0 100%;
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  margin-top: var(--space-xs);
}
.saved-conjure__gender .field__label {
  margin: 0;
}
.saved-conjure__gender-select {
  width: auto;
  min-width: 12rem;
}

/* People conjure view host (DN-PPL-1): mounts the shared person conjuror
   (generators.js renderPersonConjuror) inline. The conjuror renders its own
   gen-* sheet chrome; this wrapper just frames it as a contained panel so it
   reads as a sub-view, mirroring the framing the prior CTA had. No new tokens. */
.saved-conjure-host {
  padding: var(--space-lg) var(--space-md);
  border: 1px dashed var(--color-border);
  background: rgba(0, 0, 0, 0.18);
}

@media (max-width: 700px) {
  /* On phones the Conjure button drops to its own full row under the title so it
     stays reachable without horizontal scroll (DN-3 / AC edge case). */
  .saved-page__conjure {
    align-self: stretch;
    width: 100%;
  }
}

/* --- Bestiary tabs (Bestiary | My Monsters) ----------------------------- */
.bestiary__tabs {
  display: flex;
  gap: var(--space-xs);
  margin-bottom: var(--space-md);
  border-bottom: 1px solid var(--color-border);
}

.bestiary__tab {
  appearance: none;
  background: none;
  border: 1px solid transparent;
  border-bottom: none;
  color: var(--color-text-muted);
  font-family: var(--font-heading-ui);
  font-size: 0.85rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: var(--ls-cinzel-caps);
  padding: 0.5rem 1rem;
  cursor: pointer;
  /* Sit the active tab's bottom edge on the strip rule. */
  margin-bottom: -1px;
  transition: color var(--transition-fast), border-color var(--transition-fast);
}

.bestiary__tab:hover,
.bestiary__tab:focus-visible {
  color: var(--color-accent);
}

.bestiary__tab:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

/* Active tab: accent label + a 2px accent underline that reads as the live tab. */
.bestiary__tab.is-active {
  color: var(--color-accent);
  border-color: var(--color-border);
  border-bottom: 2px solid var(--color-accent);
}

/* --- Roster (shared list of saved entities) ----------------------------- */
.saved-roster {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}

/* Each row reuses the canonical .list-row treatment: surface + Mortar border,
   accent on hover, but laid out as a horizontal name/meta + actions row. The
   WHOLE row is a clickable open affordance (KDL 2026-06-27, Rule 4), matching
   the Maps saved-row: position:relative anchors the Load button's stretched
   ::after overlay so a click anywhere on the row opens the record (the Edit
   detail view; UX revision 2026-06-23 #1 removed the separate read-only View).
   Delete sits above that overlay (.saved-roster__btn--raised). */
.saved-roster__item {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  flex-wrap: wrap;
  padding: var(--space-md);
  background-color: var(--color-surface);
  border: 1px solid var(--color-border);
  border-left: 3px solid var(--color-accent);
  cursor: pointer;
  transition:
    background-color var(--transition-fast),
    border-color var(--transition-fast);
}

.saved-roster__item:hover,
.saved-roster__item:focus-within {
  background-color: var(--color-surface-hover);
  border-color: var(--color-accent);
}

/* Stretch the Load button's hit area over the whole row ("stretched link").
   Lives on the open button only; Delete sits above it via z-index. */
.saved-roster__btn--open::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
}

.saved-roster__main {
  min-width: 0;
  flex: 1 1 16rem;
}

.saved-roster__name {
  font-family: var(--font-heading);
  font-size: var(--text-h4);
  font-weight: 600;
  color: var(--color-heading);
  letter-spacing: 0.03em;
  margin: 0;
  line-height: var(--lh-heading);
  word-break: break-word;
}

.saved-roster__meta {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  color: var(--color-text-muted);
  margin: 0.2rem 0 0;
}

/* NPC kind chip — "Rival Party" vs "Person". A bordered pill; the WORD carries the
   meaning (never color-only). Rival parties get the accent edge to read distinctly
   from a lone person in the same list. */
.saved-chip {
  display: inline-block;
  margin: 0.35rem 0 0;
  padding: 0.12rem 0.5rem;
  font-family: var(--font-mono);
  font-size: 0.66rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  border: 1px solid var(--color-border);
  color: var(--color-text-muted);
}

.saved-chip--rival {
  border-color: var(--color-accent);
  color: var(--color-accent);
}

/* Origin chip (Commoners #3) — "Origin: Tavern: <name> - Proprietor" or "Origin:
   Standalone". The building NAME is a proper noun, so the value is NOT uppercased
   (only the "Origin:" label keeps the chip's mono-caps treatment); it may wrap on a
   narrow card. A sourced origin gets the accent edge to read as "linked to a place". */
.saved-chip--origin {
  text-transform: none;
  letter-spacing: normal;
  max-width: 100%;
  white-space: normal;
}
.saved-chip--origin .saved-chip__label {
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.saved-chip--origin-sourced {
  border-color: var(--color-accent);
  color: var(--color-text);
}

/* A clickable origin chip (forward back-link, Ken #1, 2026-06-26): when the source
   building id is known the chip is a <button> that opens that building. It carries
   the roster's --raised stacking so its click does not also trigger the row's
   stretched Load overlay. Pointer + underline value + a clear focus ring. */
.saved-chip--origin-link {
  cursor: pointer;
  font: inherit;
}
.saved-chip--origin-link:hover {
  border-color: var(--color-accent);
}
.saved-chip--origin-link:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}
.saved-chip--origin-link .saved-chip__label + span {
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 2px;
}

.saved-roster__actions {
  display: flex;
  gap: var(--space-sm);
  flex: 0 0 auto;
  flex-wrap: wrap;
}

/* Compact action buttons within a row. ONE compact in-row size shared with the
   Maps reference .maps-saved-row__btn (KDL 2026-06-27, Rule 3) so roster rows
   and map rows have identically sized actions. */
.saved-roster__btn {
  padding: 0.25rem 0.7rem;
  font-size: 0.72rem;
}

/* Delete rides ABOVE the Load button's stretched ::after overlay (Rule 4) so a
   click on it does not also trigger the row-open. Load itself IS the overlay
   source and needs no raised z-index. Mirrors .maps-saved-row__btn--delete. */
.saved-roster__btn--raised {
  position: relative;
  z-index: 2;
}

.saved-roster__loading {
  margin: 0;
  font-style: italic;
}

/* --- Empty state -------------------------------------------------------- */
.saved-roster__empty {
  padding: var(--space-lg) var(--space-md);
  border: 1px dashed var(--color-border);
  background: rgba(0, 0, 0, 0.18);
}

.saved-roster__empty-title {
  font-family: var(--font-heading-ui);
  font-size: 1rem;
  letter-spacing: var(--ls-cinzel-caps);
  text-transform: uppercase;
  color: var(--color-text);
  margin: 0 0 var(--space-xs);
}

.saved-roster__empty-sub {
  margin: 0;
  font-style: italic;
}

/* --- Commoners CARD GRID (Ken 2026-06-27) ------------------------------- */
/* The Commoners roster renders as a CARD grid in the Adventurer/Buildings idiom
   (.char-card-grid / .char-card from components.css). These rules only adjust the
   commoner-specific bits the shared card chrome doesn't cover: the chip row inside
   the card body (the .char-card__meta slot holds TWO inline chips — the PERSON/Rival
   type chip + the INERT origin label — so it must wrap), and the portrait staying a
   person CIRCLE (the shared .char-card__portrait is already circular; we keep it).
   Nothing here touches the shared .char-card rules, so the Adventurer/Buildings
   grids are unaffected. */
.cmnr-card__chips {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.35rem;
  /* The shared .char-card__meta forces a UI-caps font; the chips carry their own
     mono treatment (.saved-chip), so let them own their type. */
  letter-spacing: normal;
  text-transform: none;
}

/* The .saved-chip family defaults to a top margin (it sat under a row name); inside
   the flex chip row that extra margin throws the baseline off, so zero it here. */
.cmnr-card__chips .saved-chip {
  margin-top: 0;
}

/* Ancestry · Gender line beneath the name (Ken #3, 2026-06-27): "Human · Female".
   The shared .char-card__meta forces a UI-caps font; this line reads as plain prose,
   so drop the caps/tracking and quiet it a touch under the name. */
.cmnr-card__ancestry-gender {
  letter-spacing: normal;
  text-transform: none;
  color: var(--color-text-muted);
}

/* --- Snapshot viewer (inside the shared info modal body) ---------------- */
.saved-view__notes {
  font-family: var(--font-body);
  font-size: 1rem;
  line-height: var(--lh-body);
  color: var(--color-text);
  font-style: italic;
  margin: 0 0 var(--space-md);
  padding-bottom: var(--space-md);
  border-bottom: 1px solid var(--color-border);
  white-space: pre-wrap;
  word-break: break-word;
}

/* Rolled-detail definition list — label/value pairs from the npc snapshot. */
.saved-view__list {
  margin: 0;
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: var(--space-xs) var(--space-md);
}

.saved-view__term {
  font-family: var(--font-heading-ui);
  font-size: var(--text-sm);
  letter-spacing: var(--ls-cinzel-caps);
  text-transform: uppercase;
  color: var(--color-text-muted);
  margin: 0;
}

.saved-view__def {
  font-family: var(--font-body);
  font-size: 1rem;
  color: var(--color-text);
  line-height: var(--lh-body);
  margin: 0;
  word-break: break-word;
}

/* --- Edit fields -------------------------------------------------------- */
.saved-edit__field + .saved-edit__field {
  margin-top: var(--space-md);
}

/* The creature stat block shown above Name + Notes when an owner edits a saved
   monster (batch-12 #4): same renderer as the Bestiary / admin read-only view.
   A divider separates the read-only card from the editable fields beneath it. */
.saved-edit__statblock {
  margin-bottom: var(--space-md);
  padding-bottom: var(--space-md);
  border-bottom: 1px solid var(--color-border);
}

/* Inline rename/notes editor in the edit-in-conjure view (UX revision #2,
   2026-06-23). A bordered panel that frames the name + notes fields and the
   "Save name & notes" action, separating them from the rolled details below. */
.saved-edit__inline {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
  padding: var(--space-md);
  margin-bottom: var(--space-lg);
  border: 1px solid var(--color-border);
}

@media (max-width: 520px) {
  .saved-roster__item {
    flex-direction: column;
    align-items: stretch;
  }
  .saved-roster__actions {
    justify-content: flex-start;
  }
  .saved-view__list {
    grid-template-columns: 1fr;
    gap: 0.1rem var(--space-md);
  }
  .saved-view__term {
    margin-top: var(--space-sm);
  }
}
