/* The story table and its cards (plans/custom-setup-cards.md): a custom
   adventure's six cards laid out on a table (custom_setup/edit), and each
   card opened on a page of its own (custom_setup/card). The owner's mockup in
   the app's parchment layer: Cinzel for names, Lora for words, the shelf's
   card language — an ink border with a gilt rule inside it, chips, a dark
   scrim over a picture.

   Sizes are in px, as the shelf's are (dashboard.css): the root is 16px at
   768px and under and 20px above, and a card's face is laid out to the pixel.

   Kept elsewhere: the parchment, nav, flash and buttons (pages/parchment.css,
   whose lists name .story-page); the stat rows (random_setup.css, likewise);
   the switches, their help bubbles, the wand and the theme pills
   (custom_setup.css); touch sizes and the strip's focus ring (access.css). */

/* ── The page ── */

/* Out of the app's vignette, as every page under the signed-in nav is
   (base.css). It turned the flat sheet and its fields into a gradient,
   darkest in the bottom corners where the phone's pager and Start sit. */

/* Wider than .container under 1400px: three cards a row want about 1040px,
   as the shelf's do. */
@media (max-width: 1399.98px) {
  body:has(.story-table) .container { max-width: 1100px; }
}
.story-page {
  margin: 0 auto;
  padding: 8px 0 64px;
}
.story-table { max-width: 1040px; }
.story-editor { max-width: 920px; }
/* The container's own 0.6rem, made up to a 16px gutter. */
@media (max-width: 768px) {
  .story-page { padding: 0 6px 32px; }
}

/* The way back, top left of both pages. base.css makes every link teal. */
.story-table-top,
.story-editor-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  min-height: 44px;
}
.story-back {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 44px;
  font-family: 'Cinzel', serif;
  font-size: 12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: #65523a;
  text-decoration: none;
}
@media (hover: hover) {
  .story-back:hover { color: #2a1d0e; }
}

/* ── The table's head ── */
.story-table-header {
  position: relative;
  margin: 0 0 30px;
  padding-bottom: 24px;
  text-align: center;
}
/* The gilt rule under the lede, as under every setup page's heading. */
.story-table-header::after {
  content: '';
  position: absolute;
  bottom: 0;
  left: 15%;
  right: 15%;
  height: 1px;
  background: linear-gradient(to right, transparent 0%, #a0895e 20%, #8a7354 50%, #a0895e 80%, transparent 100%);
  opacity: 0.6;
}
.story-table-top { margin-bottom: 6px; }
.story-eyebrow {
  margin: 0;
  font-family: 'Cinzel', serif;
  font-size: 11px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: #65523a;
}
.story-table-title {
  margin: 8px 0 10px;
  font-family: 'Cinzel', serif;
  font-size: clamp(24px, 4vw, 34px);
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: 0.06em;
  color: #3a2a14;
  text-wrap: balance;
  overflow-wrap: anywhere;
}
/* "Untitled adventure": fainter, still 3:1 or more as large type. */
.story-table-title.is-empty { color: #7a6548; }
.story-table-lede {
  max-width: 52ch;
  margin: 0 auto;
  font-family: 'Lora', Georgia, serif;
  font-size: 15px;
  line-height: 1.6;
  color: #5a4a30;
}

/* ── The deck ──
   Three cards a row on a desktop, two on a phone, where the faces go
   compact (below). */
.story-deck {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 24px;
}
@media (max-width: 760px) {
  .story-deck { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; }
}
@media (max-width: 559.98px) {
  .story-deck { gap: 12px; }
}

/* ── A card on the table ──
   A link covering the whole card. Its own stacking context, so nothing on one
   is drawn over the next; the overflow is cut here, which leaves the card's
   own focus ring alone (an outline is never clipped by its own box). */
.story-card {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-width: 0;
  aspect-ratio: 3 / 4;
  padding: 18px 18px 16px;
  color: #5a4a30;
  text-decoration: none;
}
/* The face's paint, which random setup's World and Hero cards share
   (pages/random_cards.css, .seed-card), and so do the Premium page's plans
   (pages/premium.css, .plan-card), without the rest of a story card. */
:is(.story-card, .seed-card, .plan-card) {
  background: radial-gradient(ellipse at 50% 30%, #f6eedd 0%, #eadcc0 70%, #ddcaa6 100%);
  border: 1px solid rgba(58, 42, 20, 0.55);
  box-shadow: 0 1px 0 rgba(255, 245, 220, 0.6), 0 6px 18px rgba(40, 25, 10, 0.18);
}
:is(.story-card, .seed-card, .plan-card)::before {
  content: '';
  position: absolute;
  inset: 7px;
  border: 1px solid rgba(58, 42, 20, 0.22);
  pointer-events: none;
}
.story-card::before { z-index: 3; }
@media (hover: hover) {
  .story-card:hover { box-shadow: 0 1px 0 rgba(255, 245, 220, 0.6), 0 12px 26px rgba(40, 25, 10, 0.26); }
}
/* Some of what it needs is written and some is not: the red chip, and the
   card's edge picks it up. */
.story-card.is-unfinished { border-color: rgba(144, 48, 48, 0.7); }

/* Above the words' scrim (z-index 2, later in the page), whose fade reaches
   up over the picture on the hero's card. */
.story-card-top {
  position: relative;
  z-index: 3;
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: 6px 8px;
}
/* The name breaks between its words before the chip gives up its place
   beside it: the chip drops to a line of its own only when the longest word
   and the chip do not fit side by side (a flex basis of 0, never narrower
   than that word). break-word rather than anywhere, which would let that
   word count as a letter wide. */
.story-card-name {
  flex: 1 1 0;
  min-width: min-content;
  font-family: 'Cinzel', serif;
  font-size: 12px;
  font-weight: 700;
  line-height: 1.4;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: #3a2a14;
  overflow-wrap: break-word;
}

/* The chip: the card's state. Done in green, Unfinished in the failure red,
   To write on plain paper; the optional cards' quiet outline. */
.story-chip {
  flex: none;
  max-width: 100%;
  padding: 4px 8px;
  border: 1px solid rgba(58, 42, 20, 0.45);
  background: rgba(239, 227, 205, 0.92);
  font-family: 'Cinzel', serif;
  font-size: 10px;
  font-weight: 600;
  line-height: 1.4;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: #2a1d0e;
  white-space: nowrap;
}
.story-chip--done { background: #4f6f58; border-color: #33503c; color: #eef2e6; }
.story-chip--unfinished { background: var(--color-failure, #903030); border-color: #5e1c1c; color: #f6e7df; }
.story-chip--optional,
.story-chip--filled { background: transparent; border-color: rgba(58, 42, 20, 0.3); color: #65523a; }

/* Each card's mark, faint behind the words, at one height on every card —
   Settings is never blank, and a blank card's mark in the middle left a new
   table's bottom row uneven. */
.story-card-glyph {
  position: absolute;
  top: 16%;
  left: 50%;
  z-index: 0;
  width: 38%;
  transform: translateX(-50%);
  opacity: 0.16;
  color: #2a1d0e;
  pointer-events: none;
}
.story-card-glyph svg { display: block; width: 100%; height: auto; }

/* What is on the card, at its foot. */
.story-card-body {
  position: relative;
  z-index: 2;
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-height: 0;
  margin-top: auto;
}
/* Clamped, and never squeezed below the clamp: a clamped box's automatic
   minimum height is 0, so a face with more words than room shrank these
   and cut a line through its letters. They keep whole lines, and the face
   is laid out to leave them the room (the hero's scrim below). */
.story-card-title {
  flex-shrink: 0;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  overflow: hidden;
  overflow-wrap: anywhere;
  font-family: 'Cinzel', serif;
  font-size: 19px;
  font-weight: 700;
  line-height: 1.2;
  color: #2a1d0e;
  text-wrap: balance;
}
.story-card-title:empty { display: none; }
.story-card-text {
  flex-shrink: 0;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 4;
  overflow: hidden;
  overflow-wrap: anywhere;
  font-family: 'Lora', Georgia, serif;
  font-size: 13.5px;
  line-height: 1.5;
  color: #5a4a30;
}
.story-card-text:empty { display: none; }
.story-card-list {
  display: flex;
  flex-direction: column;
  gap: 5px;
  font-family: 'Lora', Georgia, serif;
  font-size: 13.5px;
  line-height: 1.4;
  color: #3a2a14;
}
/* One line each, cut with an ellipsis: a long item name must not push the
   rest off the card. */
.story-card-item {
  display: block;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.story-card--inventory .story-card-item::before {
  content: '◆';
  content: '◆' / '';
  margin-right: 8px;
  font-size: 0.8em;
  color: #8a7354;
}
/* A person's initial in a roundel. */
.story-card-initial {
  display: inline-grid;
  place-items: center;
  width: 18px;
  height: 18px;
  margin-right: 8px;
  border: 1px solid #65523a;
  border-radius: 50%;
  font-family: 'Cinzel', serif;
  font-size: 10px;
  font-weight: 700;
  line-height: 1;
  vertical-align: 1px;
  color: #3a2a14;
}
.story-card-mark {
  display: inline-block;
  width: 1.1em;
  margin-right: 6px;
  text-align: center;
  color: #4f6f58;
}
.story-card-switch.is-off { color: #65523a; }
.story-card-switch.is-off .story-card-mark { color: #8a7354; }

.story-card-foot {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 4px 10px;
  padding-top: 8px;
  border-top: 1px solid rgba(160, 137, 94, 0.45);
  font-family: 'Cinzel', serif;
  font-size: 10.5px;
  line-height: 1.4;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: #65523a;
}
.story-card-open {
  margin-left: auto;
  white-space: nowrap;
  color: #3a2a14;
}
/* "06:40 · 1 object": a gap alone read as one token. */
.story-card-foot-item + .story-card-foot-item::before {
  content: '·';
  content: '·' / '';
  margin-right: 10px;
}

/* The hero's card once a portrait is drawn: the painting, full bleed, and the
   words on a dark scrim, as on the shelf. The scrim is dark from the top of
   the words down, so the faintest words keep their contrast over a light
   painting; its fade reaches up over the picture from a pseudo-element
   rather than from padding, which took 60px (44px on a phone) from the words
   and cut a long name's description short. */
.story-card--art { background: #2b2014; color: #efe3cd; }
.story-card--art::before { border-color: rgba(232, 214, 176, 0.35); }
.story-card-img {
  position: absolute;
  inset: 0;
  z-index: 0;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 12%;
}
.story-card--art .story-card-body {
  margin: auto -18px -16px;
  padding: 12px 18px 16px;
  background: linear-gradient(to top, rgba(20, 13, 5, 0.94), rgba(20, 13, 5, 0.8));
}
.story-card--art .story-card-body::before {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: 100%;
  height: 60px;
  background: linear-gradient(to top, rgba(20, 13, 5, 0.8), transparent);
  pointer-events: none;
}
/* A plate as wide as its word, not the row the name otherwise grows into. */
.story-card--art .story-card-name { flex: 0 1 auto; padding: 3px 7px; background: rgba(20, 13, 5, 0.6); color: #f6ecd8; }
.story-card--art .story-card-title { color: #f6ecd8; }
.story-card--art .story-card-text { color: #e3d6bc; }
.story-card--art .story-card-foot { border-top-color: rgba(232, 214, 176, 0.3); color: #d9cbb0; }
.story-card--art .story-card-open { color: #f1e6cf; }

/* A phone: two cards a row, about 175px wide, so the faces go compact —
   smaller words, shorter clamps, no "Open", at most four rows of a list. */
@media (max-width: 559.98px) {
  .story-card { gap: 6px; padding: 12px 12px 11px; }
  .story-card::before { inset: 5px; }
  .story-card-name { font-size: 10.5px; letter-spacing: 0.12em; }
  /* Small enough to sit beside "Starting point" on a 175px card. */
  .story-chip { padding: 2px 4px; font-size: 8.5px; letter-spacing: 0.06em; }
  .story-card-glyph { top: 20%; width: 34%; }
  .story-card-title { font-size: 15px; -webkit-line-clamp: 2; }
  /* With nothing under it, a name has the room for a third line. */
  .story-card-title:has(+ .story-card-text:empty) { -webkit-line-clamp: 3; }
  .story-card-text,
  .story-card-list { font-size: 12px; }
  .story-card-text { -webkit-line-clamp: 3; }
  .story-card-list { gap: 3px; }
  .story-card:is(.story-card--inventory, .story-card--characters) .story-card-item:nth-child(n+5) { display: none; }
  .story-card-foot { padding-top: 6px; font-size: 9.5px; letter-spacing: 0.1em; }
  .story-card-foot.is-bare,
  .story-card-open { display: none; }
  .story-card--art .story-card-body { margin: auto -12px -11px; padding: 8px 12px 11px; }
  .story-card--art .story-card-body::before { height: 44px; }
}

/* ── Start, under the table ── */
.story-start {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  margin-top: 34px;
  text-align: center;
}
.story-start-missing,
.story-start-drawing {
  max-width: 60ch;
  margin: 0;
  font-family: 'Lora', Georgia, serif;
  font-size: 14px;
  line-height: 1.5;
}
.story-start-missing { color: #903030; }
.story-start-drawing { font-style: italic; color: #5a4a30; }
.story-start-form { margin: 0; }
/* The seal: the one thing the table is for. Weighted to outrank
   parchment.css's .btn--primary. */
body .btn.story-start-button {
  min-height: 48px;
  padding: 12px 30px;
  font-size: 14px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.story-table-foot {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  margin-top: 10px;
  text-align: center;
}
.story-start-note {
  max-width: 46ch;
  margin: 0;
  font-family: 'Lora', Georgia, serif;
  font-size: 13px;
  line-height: 1.5;
  color: #65523a;
}
.story-discard-form { margin: 0; }
.story-discard {
  min-height: 44px;
  padding: 0 6px;
  border: 0;
  background: none;
  cursor: pointer;
  font-family: 'Lora', Georgia, serif;
  font-size: 13px;
  color: #65523a;
  text-decoration: underline;
  text-underline-offset: 3px;
}
@media (hover: hover) {
  .story-discard:hover { color: #7a3a2a; }
}

/* On a phone Start stays in reach at the foot of the screen, over the cards
   as they scroll by, and lands in its place under them. */
@media (max-width: 559.98px) {
  .story-start {
    position: sticky;
    bottom: 0;
    z-index: 3;
    gap: 6px;
    margin: 24px calc(-0.6rem - 6px) 0;
    padding: 18px calc(0.6rem + 6px) calc(12px + env(safe-area-inset-bottom));
  }
  .story-start-missing,
  .story-start-drawing { font-size: 13px; }
  .story-start-form,
  body .btn.story-start-button { width: 100%; }
  /* A field or card that takes focus stops above the bar, not under it: a
     new blueprint's three-card "Still needed" makes the bar 143px tall. */
  html:has(.story-start) { scroll-padding-bottom: 160px; }
}

/* ── An open card ── */
.story-editor-header { margin-bottom: 18px; }

/* "Saved" / "Saving…" / "Not saved — check your connection", with a dot. */
.story-save-status {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
  font-family: 'Lora', Georgia, serif;
  font-size: 13px;
  font-style: italic;
  line-height: 1.3;
  text-align: right;
  color: #65523a;
}
.story-save-status::before {
  content: '';
  flex: none;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: #4f6f58;
}
.story-save-status[data-state="saving"]::before { background: #a0895e; }
.story-save-status[data-state="failed"] { font-style: normal; color: #903030; }
.story-save-status[data-state="failed"]::before { background: #903030; }

/* The strip: every card, this one underlined. Sideways-scrolling on a phone,
   its scrollbar hidden — the cut-off name at the edge says there is more. */
/* Positioned so a link's offsetLeft is measured from the strip, which
   story_strip_controller.js#centreCurrent reads; with a little scroll padding
   a link brought into view on focus keeps its ring clear of the edge. */
.story-strip {
  position: relative;
  display: flex;
  gap: 4px;
  margin-top: 10px;
  overflow-x: auto;
  scroll-padding-inline: 12px;
  scrollbar-width: none;
  border-bottom: 1px solid rgba(160, 137, 94, 0.45);
}
.story-strip::-webkit-scrollbar { display: none; }
.story-strip-link {
  position: relative;
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  min-height: 44px;
  padding: 0 12px;
  font-family: 'Cinzel', serif;
  font-size: 12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  white-space: nowrap;
  color: #65523a;
  text-decoration: none;
}
@media (hover: hover) {
  .story-strip-link:hover { color: #2a1d0e; }
}
.story-strip-link[aria-current="page"] { font-weight: 700; color: #2a1d0e; }
/* Each name as wide as its bold self, so the open card's bold label does not
   push the others along from card to card: a hidden bold copy (data-text)
   in the same grid column sets the width. */
.story-strip-name { display: inline-grid; }
.story-strip-name::after {
  content: attr(data-text);
  content: attr(data-text) / '';
  height: 0;
  overflow: hidden;
  visibility: hidden;
  font-weight: 700;
}
.story-strip-link[aria-current="page"]::after {
  content: '';
  position: absolute;
  left: 10px;
  right: 10px;
  bottom: 0;
  height: 2px;
  background: #3a2a14;
}
/* The dot is the card's state (data-state, kept up to date by each save):
   green when done or filled, red when unfinished, an empty ring otherwise. */
.story-strip-dot {
  flex: none;
  width: 8px;
  height: 8px;
  border: 1px solid #65523a;
  border-radius: 50%;
}
.story-strip-link:is([data-state="done"], [data-state="filled"]) .story-strip-dot { background: #4f6f58; border-color: #4f6f58; }
.story-strip-link[data-state="unfinished"] .story-strip-dot { background: #903030; border-color: #903030; }
@media (forced-colors: active) {
  .story-strip-link:is([data-state="done"], [data-state="filled"], [data-state="unfinished"]) .story-strip-dot { background: CanvasText; }
}

/* The sheet: the card itself, a page of lighter paper in a ruled frame. */
.story-sheet {
  position: relative;
  padding: 26px 28px 28px;
  background: #f3ead8;
  border: 1px solid rgba(58, 42, 20, 0.45);
  box-shadow: 0 1px 0 rgba(255, 245, 220, 0.7), 0 10px 30px rgba(40, 25, 10, 0.16);
}
.story-sheet::before {
  content: '';
  position: absolute;
  inset: 7px;
  border: 1px solid rgba(58, 42, 20, 0.16);
  pointer-events: none;
}
@media (max-width: 559.98px) {
  .story-sheet { padding: 20px 16px 22px; }
  .story-sheet::before { inset: 5px; }
}
.story-sheet-head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px 16px;
  margin-bottom: 20px;
  padding-bottom: 16px;
  border-bottom: 1px solid rgba(160, 137, 94, 0.45);
}
.story-sheet-heading { flex: 1 1 280px; min-width: 0; }
.story-sheet-title {
  margin: 0;
  font-family: 'Cinzel', serif;
  font-size: 26px;
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: 0.04em;
  color: #2a1d0e;
  overflow-wrap: anywhere;
}
.story-sheet-hint {
  max-width: 60ch;
  margin: 6px 0 0;
  font-family: 'Lora', Georgia, serif;
  font-size: 14px;
  line-height: 1.55;
  color: #65523a;
}
@media (max-width: 559.98px) {
  .story-sheet-title { font-size: 21px; }
}
/* The wand, at the size of the sheet's head rather than a field's, and a
   thumb's height on any screen: this outweighs access.css's touch rule. */
body .story-sheet-head .btn.btn-wand {
  min-height: 44px;
  padding: 9px 14px;
  font-size: 12px;
}
.story-card-alert .flash-alert { margin: 0 0 18px; }

/* The Save button a page without script shows under the fields
   (custom_setup/card, in <noscript>). */
.story-nojs-save {
  display: flex;
  justify-content: flex-end;
  margin-top: 22px;
}

/* While the wand writes, the fields it is about to replace are frozen — all
   of them, the selects, pills and slips too. */
form.is-generating .story-sheet-fields {
  opacity: 0.55;
  pointer-events: none;
}

/* ── Fields ── */
.story-sheet-fields {
  display: flex;
  flex-direction: column;
  gap: 18px;
}
.story-field {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
}
.story-field-row {
  display: flex;
  flex-wrap: wrap;
  gap: 18px 14px;
}
.story-field-row > .story-field { flex: 1 1 200px; }
.story-field-row > :is(.story-field--hero-sex, .story-field--start-time) { flex: 0 1 170px; }
.story-label {
  font-family: 'Cinzel', serif;
  font-size: 12px;
  font-weight: 600;
  line-height: 1.4;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: #3a2a14;
}
/* Line height 1: at the label's 1.4 the 13px tag made its label taller than
   one without, and the Hour field sat lower than Place beside it. */
.story-optional {
  line-height: 1;
  font-family: 'Lora', Georgia, serif;
  font-size: 13px;
  font-weight: 400;
  font-style: italic;
  letter-spacing: 0;
  text-transform: none;
  color: #65523a;
}
.story-field-hint {
  margin: 0;
  font-family: 'Lora', Georgia, serif;
  font-size: 13px;
  line-height: 1.5;
  color: #65523a;
}
/* 16px: under that a phone's browser zooms the page into the field. Random
   setup's seeds sit on its table, not on a sheet. */
:is(.story-editor, .random-table) :is(.details-input, select) {
  width: 100%;
  min-height: 44px;
  margin: 0;
  padding: 10px 12px;
  background: #fbf6ea;
  border: 1px solid #b59a70;
  border-radius: 0;
  font-family: 'Lora', Georgia, serif;
  font-size: 16px;
  font-style: normal;
  line-height: 1.4;
  color: #2a1d0e;
}
.story-editor textarea.details-input { resize: vertical; line-height: 1.55; }
/* The other fields' height (16px × 1.4, the padding and the border): Chrome
   pads its time field's inner parts and drew it 2px taller than Place. */
.story-editor input[type="time"] { height: calc(1.4em + 22px); }
:is(.story-editor, .random-table) .details-input::placeholder { color: #7a6548; font-style: italic; opacity: 1; }
:is(.story-editor, .random-table) :is(.details-input, select):focus { border-color: #5a4a30; }
:is(.story-editor, .random-table) .char-counter {
  align-self: flex-end;
  margin: 0;
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  color: #7a6548;
  opacity: 1;
}
.story-field--language select { max-width: 320px; }
.story-field--theme .theme-pills { margin: 0; }

/* ── Hero: the portrait beside the words ── */
.story-hero {
  display: grid;
  grid-template-columns: 250px minmax(0, 1fr);
  gap: 26px;
  align-items: start;
}
.story-hero-fields {
  display: flex;
  flex-direction: column;
  gap: 18px;
  min-width: 0;
}
@media (max-width: 700px) {
  .story-hero { grid-template-columns: minmax(0, 1fr); gap: 22px; }
}

.hero-portrait-panel {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
@media (max-width: 700px) {
  .hero-portrait-panel { width: 100%; max-width: 230px; margin: 0 auto; }
}
/* 3:4, like the cards; the painting is 2:3 and the crop keeps the head. With
   no picture, the shelf's parchment plate and the hero's initial. */
.hero-portrait-frame {
  position: relative;
  overflow: hidden;
  display: grid;
  place-items: center;
  aspect-ratio: 3 / 4;
  border: 1px solid rgba(58, 42, 20, 0.55);
  background:
    repeating-linear-gradient(45deg, rgba(90, 60, 30, 0.05) 0 2px, transparent 2px 9px),
    radial-gradient(ellipse at 50% 38%, #e9dcc2 0%, #d8c6a2 60%, #c4ae86 100%);
}
.hero-portrait-frame::before {
  content: '';
  position: absolute;
  inset: 6px;
  z-index: 2;
  border: 1px solid rgba(232, 214, 176, 0.45);
  pointer-events: none;
}
.hero-portrait-frame .lightbox-trigger {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  padding: 0;
  border: 0;
  background: none;
  cursor: zoom-in;
}
/* Inside the frame, whose overflow would cut off a ring drawn outside. */
.hero-portrait-frame .lightbox-trigger:focus-visible { outline-offset: -4px; }
.hero-portrait-img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 12%;
}
/* Drawn from a description the hero no longer has: faded, until redrawn. */
.hero-portrait-panel.is-stale .hero-portrait-img { filter: grayscale(0.5) sepia(0.3); }
.hero-portrait-initial {
  display: grid;
  place-items: center;
  width: 100px;
  height: 100px;
  border: 2px solid #3a2a14;
  outline: 1px solid #3a2a14;
  outline-offset: 5px;
  background: rgba(239, 227, 205, 0.6);
  font-family: 'Cinzel', serif;
  font-size: 64px;
  font-weight: 700;
  line-height: 1;
  color: #3a2a14;
}
/* Being drawn: the initial in dashes, or the old picture dimmed while a new
   one is drawn over it, and a sheen across the frame (the motion is under
   Motion below). The sheen lets clicks through to the old picture's
   lightbox. Without motion it rests over the frame as a pale wash. */
.hero-portrait-panel[data-state="drawing"] .hero-portrait-initial { border-style: dashed; outline-style: dashed; color: #7a6548; }
.hero-portrait-panel[data-state="drawing"] .hero-portrait-img { filter: grayscale(0.6) opacity(0.7); }
.hero-portrait-panel[data-state="drawing"] .hero-portrait-frame::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 1;
  background: rgba(255, 248, 230, 0.25);
  pointer-events: none;
}
.hero-portrait-status {
  margin: 0;
  font-family: 'Lora', Georgia, serif;
  font-size: 13px;
  line-height: 1.5;
  text-align: center;
  color: #5a4a30;
}
.hero-portrait-status:empty { display: none; }
.hero-portrait-status.is-warn { color: #903030; }
.hero-portrait-status a,
.hero-portrait-allowance a {
  color: #3a2a14;
  text-decoration: underline;
  text-underline-offset: 2px;
}
.hero-portrait-panel .hero-portrait-draw { width: 100%; }
.hero-portrait-allowance {
  margin: 0;
  font-family: 'Lora', Georgia, serif;
  font-size: 12.5px;
  line-height: 1.5;
  font-variant-numeric: tabular-nums;
  text-align: center;
  color: #65523a;
}

/* The stat allocator in a ruled box, a row to a stat. */
.story-editor .hero-stats {
  margin: 0;
  padding: 10px 14px;
  background: #f8f1e2;
  border: 1px solid rgba(160, 137, 94, 0.7);
}
.story-editor .stats-label { font-size: 12px; font-weight: 600; letter-spacing: 0.12em; color: #3a2a14; }
.story-editor .stats-points { font-size: 13px; color: #5a4a30; }
.story-editor .stat-row { min-height: 44px; }
/* The stat's name as a word, not the game sidebar's small capitals
   (game/layout.css), which the setup pages otherwise inherit. */
body .story-editor .stat-row .stat-label { font-size: 15px; letter-spacing: 0; text-transform: none; opacity: 1; }
.story-editor .stat-row + .stat-row { border-top: 1px solid rgba(160, 137, 94, 0.3); }
.story-editor .stat-btn {
  width: 36px;
  height: 36px;
  background: #fbf6ea;
  border-color: #b59a70;
  border-radius: 0;
  font-size: 18px;
}

/* ── Slips: the rows of a list card ──
   Each item, person or object is a small slip of its own, a 44px × at its
   side. */
.story-slips {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.story-slips-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.story-slips-list:not(:has(.story-slip)) { display: none; }
.story-slip {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 44px;
  align-items: start;
  gap: 8px;
  padding: 12px;
  background: #f8f1e2;
  border: 1px solid rgba(160, 137, 94, 0.7);
}
.story-slip-fields {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 8px;
}
.story-slip--inventory .story-slip-fields { grid-template-columns: minmax(0, 1fr) 100px; }
.story-slip--objects .story-slip-fields { grid-template-columns: minmax(0, 2fr) minmax(0, 1.2fr); }
.story-slip-description { grid-column: 1 / -1; }
/* A phone: the name beside its ×, the full width of the row, and everything
   under it the slip's whole width — the ×'s column no longer leaves an
   empty strip beside the description, and the count goes under the name
   (about 180px left beside it, too little for "Hooked lighting pole"). The
   fields' wrapper steps aside (display: contents) so they sit in the slip's
   own grid; it has no role for a screen reader to lose. */
@media (max-width: 559.98px) {
  .story-slip { gap: 6px; padding: 8px; }
  .story-slip-fields { display: contents; }
  .story-slip-name { grid-column: 1; grid-row: 1; }
  .story-slip-remove { grid-column: 2; grid-row: 1; }
  .story-slip :is(.story-slip-quantity, .story-slip-status, .story-slip-description) { grid-column: 1 / -1; }
  .story-slip .story-slip-quantity { justify-self: start; width: 110px; }
}
.story-slip-remove {
  width: 44px;
  height: 44px;
  padding: 0;
  border: 1px solid transparent;
  background: none;
  cursor: pointer;
  font-size: 22px;
  line-height: 1;
  color: #65523a;
}
@media (hover: hover) {
  .story-slip-remove:hover { border-color: #a0895e; color: #903030; }
}
.story-slips-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 8px 12px;
}
.story-slips-count {
  font-family: 'Lora', Georgia, serif;
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  color: #7a6548;
}

/* ── Settings: the switches as switches ──
   shared/_setup_toggles' checkboxes, drawn as the mockup's sliders in a
   ruled list. Still checkboxes to a screen reader and to a form without
   script; a forced-colours palette gets the plain box back. */
.story-switches { display: flex; flex-direction: column; }
.story-switches .setup-toggle {
  flex-wrap: wrap;
  margin: 0;
  padding: 12px 0;
  border-top: 1px solid rgba(160, 137, 94, 0.35);
}
.story-switches .setup-toggle:first-child { border-top: 0; }
/* The label shrinks and wraps before the "?" beside it would drop to a line
   of its own (a German label is long). */
.story-switches .setup-toggle .setup-toggle-label { flex: 0 1 auto; min-width: 0; gap: 12px; font-size: 15px; color: #2a1d0e; }
.story-switches .setup-toggle-note { flex-basis: 100%; padding-left: 56px; font-size: 13px; opacity: 1; color: #65523a; }
.story-switches .setup-choice { margin: 0; padding: 12px 0; border-top: 1px solid rgba(160, 137, 94, 0.35); column-gap: 20px; }
.story-switches .setup-choice .setup-choice-label,
.story-switches .setup-choice .setup-choice-option { font-size: 15px; color: #2a1d0e; }
.story-switches .setup-choice-option input[type="radio"] { width: 20px; height: 20px; accent-color: #3a2a14; }
.story-switches .setup-toggle-label input[type="checkbox"] {
  appearance: none;
  position: relative;
  flex: none;
  width: 44px;
  height: 26px;
  margin: 0;
  border: 1px solid #5a4a30;
  border-radius: 13px;
  background: #d9c9a8;
  cursor: pointer;
}
.story-switches .setup-toggle-label input[type="checkbox"]::after {
  content: '';
  position: absolute;
  top: 3px;
  left: 3px;
  width: 18px;
  height: 18px;
  border: 1px solid #5a4a30;
  border-radius: 50%;
  background: #fbf6ea;
}
.story-switches .setup-toggle-label input[type="checkbox"]:checked { background: #3a2a14; border-color: #3a2a14; }
.story-switches .setup-toggle-label input[type="checkbox"]:checked::after { left: 21px; border-color: #3a2a14; }
@media (forced-colors: active) {
  .story-switches .setup-toggle-label input[type="checkbox"] { appearance: auto; width: auto; height: auto; }
  .story-switches .setup-toggle-label input[type="checkbox"]::after { content: none; }
}

/* ── The pager ── */
.story-pager {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-top: 22px;
}
.story-pager-side {
  display: flex;
  flex: 1 1 0;
  min-width: 0;
}
.story-pager-side--next { justify-content: flex-end; }
.story-pager .btn { text-align: center; }
/* The space between an arrow and its card's name, which a touch screen's
   flex buttons (access.css, a.btn) drop, as .exit-label's gap does there. */
.story-pager-link { gap: 0.4em; }
/* On a phone it stays in reach at the foot of the screen (data-sticky-foot,
   so a help bubble near it opens upward), its buttons smaller — 12px, the
   side links in full ink, still legible in the corner of the screen. */
@media (max-width: 559.98px) {
  .story-pager {
    position: sticky;
    bottom: 0;
    z-index: 3;
    margin: 18px calc(-0.6rem - 6px) 0;
    padding: 14px calc(0.6rem + 6px) calc(10px + env(safe-area-inset-bottom));
  }
  body .story-pager .btn { padding: 8px 10px; font-size: 12px; letter-spacing: 0.06em; }
  body .story-pager .story-pager-link { color: #3a2a14; }
  html:has(.story-pager) { scroll-padding-bottom: 90px; }
}

/* The phone's two bars, Start on the table and the pager on a card: a band
   of paper while they float over what scrolls under them — solid under the
   words, faded above them, so nothing shows through "Still needed" — and
   none once they come to rest at the end of the page, where a lighter band
   ended in a hard line above the darker page. The band is a pseudo-element
   because a scroll-state query can style the container's own pseudo-
   elements but not the container; a browser without the query keeps it.
   Random setup's Generate and its Settings sheet's Done are the same bars
   (pages/random_cards.css). */
@media (max-width: 559.98px) {
  :is(.story-start, .story-pager, .random-generate, .random-settings-pager) { container-type: scroll-state; }
  :is(.story-start, .story-pager, .random-generate, .random-settings-pager)::before {
    content: '';
    position: absolute;
    inset: 0;
    z-index: -1;
    background: linear-gradient(to top, #e2d3b8 calc(100% - 14px), rgba(226, 211, 184, 0));
    pointer-events: none;
  }
  @supports (container-type: scroll-state) {
    :is(.story-start, .story-pager, .random-generate, .random-settings-pager)::before { opacity: 0; }
    @container scroll-state(stuck: bottom) {
      :is(.story-start, .story-pager, .random-generate, .random-settings-pager)::before { opacity: 1; }
    }
  }
  /* Forced colours drop the gradient, and the band with it. */
  @media (forced-colors: active) {
    :is(.story-start, .story-pager, .random-generate, .random-settings-pager)::before { background: Canvas; }
  }
}

/* ── Motion ──
   All of it waits for a reader who has not asked for less. */
@media (prefers-reduced-motion: no-preference) {
  .story-card { transition: transform 0.25s ease, box-shadow 0.25s ease; }
  .story-save-status[data-state="saving"]::before { animation: story-pulse 1s ease-in-out infinite; }
  .hero-portrait-panel[data-state="drawing"] .hero-portrait-frame::after {
    background: linear-gradient(100deg, transparent 30%, rgba(255, 248, 230, 0.55) 50%, transparent 70%);
    transform: translateX(-100%);
    animation: story-sheen 1.8s linear infinite;
  }
}
@media (hover: hover) and (prefers-reduced-motion: no-preference) {
  .story-card:hover { transform: translateY(-3px); }
}
@keyframes story-pulse { 50% { opacity: 0.3; } }
@keyframes story-sheen { to { transform: translateX(100%); } }
