/*
 * design_system.css — shared design tokens + the ONE unified button component.
 *
 * Auto-required via `require_tree .` in application.css. Everything clickable
 * uses `.btn` + a variant (`.btn--primary` / `.btn--secondary`) so buttons share
 * the same height, padding, font, and radius regardless of <a> vs <button>.
 * Reuses the palette/type tokens defined in missions.css (--accent, --c-reward,
 * --ink, --ink-light, --font-display, ...).
 */

:root {
  /* Spacing scale — use these for margin/padding/gap, no one-off values. */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.5rem;
  --space-6: 2rem;

  /* Font-size scale — the defined type steps. */
  --fs-sm: 0.9rem;
  --fs-base: 1rem;
  --fs-lg: 1.15rem;
  --fs-xl: 1.4rem;
  --fs-2xl: 1.9rem;

  --font-scribble: "Marker Felt", "Ink Free", "Bradley Hand", cursive;   /* narrower hand-scribbled stack; wire a real webfont later */
}

/* -------------------------------------------------------------------------
 * Unified button component. Same box on both variants (equal border widths,
 * fixed min-height, line-height:1) so every button renders identically sized
 * whether it's an <a> link or a <button> inside a button_to form.
 * ---------------------------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  min-height: 2.75rem;
  padding: var(--space-3) var(--space-5);
  border: 2px solid transparent;
  border-radius: 6px;
  font-family: var(--font-display);
  font-size: var(--fs-base);
  font-weight: 700;
  line-height: 1.05;
  letter-spacing: 0.04em;
  text-align: center;   /* centre wrapped labels too (anchors don't inherit button centring) */
  text-decoration: none;
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}

/* `button_to` wraps its <button> in a <form class="button_to">. That wrapper form
 * becomes the flex/grid item, so the button inside only shrinks to its text while a
 * sibling <a class="btn"> stretches — they come out different sizes when a row
 * stacks (e.g. on phones). Making the wrapper layout-transparent lets the button be
 * a first-class item that sizes exactly like sibling anchors, on every page.
 * (Containers that deliberately re-box the form — e.g. .mission-card__actions — load
 * later and override this.) */
form.button_to { display: contents; }

.btn--primary {
  background: var(--c-reward);
  color: var(--ink-light);
  border-color: var(--c-reward);
}

.btn--primary:hover {
  background: color-mix(in srgb, var(--c-reward) 82%, #000);
  border-color: color-mix(in srgb, var(--c-reward) 82%, #000);
}

.btn--secondary {
  background: transparent;
  color: var(--accent);
  border-color: var(--accent);
}

.btn--secondary:hover {
  background: var(--accent);
  color: var(--ink-light);
}

/* Choice-group button: accent-tinted outline that fills on hover. Same box as
 * the base .btn (height/padding/font inherited) so a row of difficulty choices
 * reads as a cohesive set of equal-sized options — used for the modal's
 * difficulty tabs (see .difficulty-tabs in home.css). */
.btn--difficulty {
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  color: var(--accent);
  border-color: var(--accent);
}

.btn--difficulty:hover {
  background: var(--accent);
  color: var(--ink-light);
}

/* -------------------------------------------------------------------------
 * Instant tooltip. Put `.tooltip-host` + `data-tooltip="..."` on any element.
 *
 * Deliberately NOT the native `title=` attribute: browsers wait roughly a second
 * before showing that one, which on a control that looks disabled reads as "nothing
 * happened". This shows on the very first hover frame — the rule below only flips
 * visibility/opacity and sets `transition: none`, so no delay can creep in from a
 * transition on the host element.
 *
 * Positioned BELOW the host on purpose: .modal__dialog (and .modal__body on phones)
 * scrolls with `overflow: auto`, so a bubble above a control at the top of the dialog
 * would be clipped off.
 * ---------------------------------------------------------------------- */
.tooltip-host { position: relative; }

.tooltip-host::after {
  content: attr(data-tooltip);
  position: absolute;
  top: calc(100% + 7px);
  left: 50%;
  transform: translateX(-50%);
  z-index: 5;
  padding: 0.3rem 0.55rem;
  border-radius: 5px;
  background: #1a1410;
  color: #fff;
  font-family: var(--font-display);
  font-size: 0.75rem;
  font-weight: normal;
  letter-spacing: 0.02em;
  line-height: 1.2;
  white-space: nowrap;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;                /* never steals the hover from the host */
  transition: none;
}

/* Little arrow pointing back up at the host. */
.tooltip-host::before {
  content: "";
  position: absolute;
  top: calc(100% + 2px);
  left: 50%;
  transform: translateX(-50%);
  z-index: 5;
  border: 5px solid transparent;
  border-bottom-color: #1a1410;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: none;
}

.tooltip-host:hover::after,
.tooltip-host:hover::before,
.tooltip-host:focus-visible::after,
.tooltip-host:focus-visible::before {
  opacity: 1;
  visibility: visible;
}

/* Runs that must never be split across lines — a number and its icon, an icon and the
 * punctuation after it (see MissionsHelper#mark_sticky_icons). `.tile-icon` is an inline-block,
 * i.e. an atomic inline, and a break opportunity exists on both sides of one; a no-break space
 * next to it does NOT suppress that in Chrome, only nowrap on an ancestor does. */
.nowrap { white-space: nowrap; }

/* Screen-reader-only text: conveys to assistive tech what the tooltip conveys visually. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Reusable modal component (shared/_modal.html.erb, driven by modal_controller).
 * A fixed full-viewport overlay that centers a scrollable dialog; toggled by the
 * `hidden` attribute the controller flips. Reuse for any future pop-up. */
.modal { position: fixed; inset: 0; z-index: 1000; display: flex; align-items: center; justify-content: center; }
.modal[hidden] { display: none; }
.modal__overlay { position: absolute; inset: 0; background: rgba(0,0,0,0.55); }
.modal__dialog { position: relative; z-index: 1; background: var(--page-bg); color: var(--ink); width: min(640px, calc(100% - 2rem)); max-height: calc(100vh - 2rem); overflow: auto; border-radius: 10px; box-shadow: 0 8px 40px rgba(0,0,0,0.35); padding: var(--space-6); }
.modal__close { position: absolute; top: 0.4rem; right: 0.7rem; background: none; border: none; font-size: 1.9rem; line-height: 1; color: var(--ink); cursor: pointer; }
/* The optional forward-CTA label (see `close_label`) shows only on the small-screen
 * full-screen sheet; on desktop the corner control stays the plain ✕. */
.modal__close-label { display: none; }

/* Small / short screens (phones): the centered dialog can grow taller than the
 * visible viewport, pushing its top — and the ✕ — off-screen while body scroll is
 * locked, so the close control becomes unreachable. Turn the modal into a
 * full-screen sheet sized to the *visible* viewport (100dvh, not the toolbar-
 * inflated 100vh) where only the body scrolls and the ✕ stays pinned. These rules
 * are scoped to small/short viewports; the desktop centered dialog is untouched. */
@media (max-width: 700px), (max-height: 500px) {
  /* Plain block (not flex): a flex item keeps min-width:auto and would overflow to
   * its content width, pushing the dialog — and the ✕ — past the screen edge. As a
   * block, width:100% is exactly the viewport. */
  .modal { display: block; height: 100vh; height: 100dvh; }
  .modal__dialog {
    width: 100%;
    max-width: none;
    height: 100%;
    max-height: none;
    border-radius: 0;
    padding: 0;
    overflow: hidden;                              /* the body scrolls, not the dialog */
    display: flex;
    flex-direction: column;
  }
  /* Pinned to the sheet's corner (the dialog no longer scrolls) with a real tap
   * target and a subtle backing so it stays legible over content. */
  .modal__close {
    top: 0.3rem;
    right: 0.4rem;
    z-index: 2;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 2.75rem;
    height: 2.75rem;
    border-radius: 999px;
    background: color-mix(in srgb, var(--page-bg) 85%, transparent);
  }
  .modal__body {
    flex: 1;
    min-height: 0;                                 /* allow the flex child to scroll */
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    padding: 3.4rem var(--space-4) var(--space-5);   /* top padding clears the ✕ / CTA pill */
  }
  /* On the full-screen sheet a bare ✕ reads as "dismiss a page", not "proceed". When
   * the caller supplies a `close_label`, replace the ✕ with a labelled forward CTA
   * ("Go to mission →") in the page accent — same click target, clearer intent. */
  .modal__close--cta {
    width: auto;
    height: auto;
    padding: 0.5rem 0.95rem;
    gap: 0.4rem;
    border-radius: 999px;
    background: var(--accent);
    color: var(--ink-light);
    font-family: var(--font-display);
    font-size: var(--fs-sm);
    letter-spacing: 0.03em;
    text-transform: uppercase;
    line-height: 1;
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.28);
  }
  .modal__close--cta .modal__close-x { display: none; }
  .modal__close--cta .modal__close-label { display: inline-flex; align-items: center; gap: 0.3rem; }
}
/*
 * home.css — minimal, tasteful styling for the landing page (home#index) and
 * the character-setup content shown inside the mission-page modal
 * (setups/_panel, rendered by setups#mission). Reuses the palette tokens and
 * button styling conventions from missions.css (--accent / --c-reward).
 * Auto-required via `require_tree .` in application.css. No markup changes.
 */

body {
  background: var(--page-bg);
  color: var(--ink);
  font-family: var(--font-body);
  line-height: 1.6;
  margin: 0;
}

/* -------------------------------------------------------------------------
 * Landing page — centered, readable single column with a primary button.
 * ---------------------------------------------------------------------- */
.landing {
  max-width: 640px;
  margin: 3rem auto 1.5rem;
  padding: 2rem 2.25rem 2.5rem;
  background: var(--window-bg);
  border-radius: 10px;
  box-shadow: 0 2px 14px rgba(0, 0, 0, 0.12);
}

/* .setup now lives INSIDE the reusable modal dialog (which is the card/frame), so it
 * carries no card styling of its own — just its inner content spacing. */

.landing h1 {
  margin: 0 0 1.25rem;
  font-family: var(--font-display);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  font-size: 2rem;
  line-height: 1.15;
  color: var(--accent);
  text-align: center;
}

.landing p {
  margin: 0 0 1.1rem;
  font-size: 1.02rem;
}

/* Key points under the intro. */
.landing__points {
  margin: 0 0 1.4rem;
  padding-left: 1.25rem;
}

.landing__points li {
  margin: 0.35rem 0;
  font-size: 1.02rem;
}

/* Prominent "One-Shot Rule Changes" panel below the landing card. */
.rule-changes {
  max-width: 640px;
  margin: 0 auto 3rem;
  padding: 1.5rem 2.25rem 1.75rem;
  background: color-mix(in srgb, var(--accent) 6%, var(--window-bg));
  border: 2px solid var(--accent);
  border-radius: 10px;
  box-shadow: 0 2px 14px rgba(0, 0, 0, 0.12);
}

.rule-changes h2 {
  margin: 0 0 0.9rem;
  font-family: var(--font-display);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  font-size: 1.4rem;
  line-height: 1.15;
  color: var(--accent);
  text-align: center;
}

.rule-changes p {
  margin: 0 0 0.9rem;
  font-size: 0.98rem;
  line-height: 1.55;
}

.rule-changes p:last-child {
  margin-bottom: 0;
}

@media (max-width: 700px) {
  .rule-changes {
    margin: 0 var(--space-4) var(--space-4);
    padding: 1.25rem 1.25rem 1.5rem;
  }
}

/* Centered lead-in line just above the "Generate Mission" button (difficulty is
 * chosen later, inside the pop-up). */
.landing .difficulty-picker-prompt {
  margin-bottom: var(--space-3);
  text-align: center;
}

/* -------------------------------------------------------------------------
 * Character setup — the generated party (difficulty, hero cards, items), shown
 * inside the mission-page modal via setups/_panel.
 * ---------------------------------------------------------------------- */

/* Chosen difficulty — prominent display-font banner in the campaign accent. */
.setup__difficulty {
  font-family: var(--font-display);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-size: 1.35rem;
  font-weight: 700;
  color: var(--accent);
  margin: 0 0 var(--space-5);
  text-align: center;
}

/* Heroes — a responsive grid of bordered cards, one column on narrow screens. */
.setup__heroes {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 0.9rem;
  margin: 0 0 1.75rem;
}

.setup__hero {
  position: relative;
  padding: 0.9rem 1rem 1rem;
  background: var(--page-bg);
  border: 1px solid rgba(0, 0, 0, 0.14);
  border-radius: 8px;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
  overflow: hidden;   /* let the portrait bleed to the top/right corners, clipped to the rounded box */
}

/* Top of the box: name + hearts on the left ~two-thirds, character-sheet art filling the
 * right ~third from the top down to the ability row. The art region stretches to the
 * height of the name+hearts block; the img is absolutely placed inside it so its full-res
 * height never drives the layout, and object-fit keeps the figure's aspect. */
.setup__hero .hero__top {
  display: flex;
  align-items: stretch;
  gap: 0.4rem;
}

.setup__hero .hero__main {
  flex: 1 1 auto;
  min-width: 0;
}

.setup__hero .hero__art {
  position: relative;
  flex: 0 0 42%;
  /* Bleed past the box padding on the top + right (to those corners) and a little to the
   * left, so the figure sits larger and closer to centre. The BOTTOM is the one edge to
   * keep clear: a positive bottom margin ends the art above the abilities, leaving a gap
   * below the character's feet. The box's overflow:hidden clips it to the rounded corners. */
  margin: -0.9rem -1rem 0.5rem -0.4rem;
}

.setup__hero .hero__portrait {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;              /* preserve aspect — never stretch in any direction */
  object-position: center bottom;   /* same alignment for every hero: bottom-anchored, centered */
  pointer-events: none;
}

.setup__hero .hero__name {
  margin: 0 0 0.7rem;
  font-family: var(--font-display);
  font-size: var(--fs-lg);
  line-height: 1.2;
  color: var(--accent);
  text-align: center;
  border-bottom: 1px solid rgba(0, 0, 0, 0.1);
  padding-bottom: var(--space-2);
}

/* Stat rows — small labelled lines with the value set off as a chip. */
.setup__hero p {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--space-2);
  margin: 0.35rem 0;
  font-size: var(--fs-sm);
  color: var(--ink);
}

.setup__hero p span {
  min-width: 1.6rem;
  padding: 0.05rem 0.45rem;
  border-radius: 999px;
  background: color-mix(in srgb, var(--accent) 14%, var(--window-bg));
  color: var(--accent);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.95rem;
  text-align: center;
}

/* Hearts shown as red heart tokens instead of a number: up to 4 per row, then wrap. */
.setup__hero .hero__hearts-label {
  margin: 0.35rem 0 0.2rem;
  font-size: var(--fs-sm);
  color: var(--ink);
}

.setup__hero .hero__hearts {
  display: grid;
  grid-template-columns: repeat(4, max-content);
  gap: 0.2rem;
  justify-content: start;
  align-content: start;
  /* Always reserve room for a second row, so the box height is consistent whether a hero
   * has 4-or-fewer hearts (one row) or 5+ (two rows). */
  min-height: 2.9rem;
  margin: 0 0 0.5rem;
}

.setup__hero .heart-token {
  display: block;
  width: 1.35rem;
  height: auto;
}

/* Named abilities, character-sheet style: one column per ability (2 for most, 3 for
 * Dorian's songs). Unlocked slots name the ability and read "unlocked"; locked slots
 * stay empty. "No abilities/songs unlocked" spans the full width when none are open. */
.setup__hero .hero__abilities {
  display: grid;
  grid-template-columns: repeat(2, 1fr);   /* 2 per row: 2-ability heroes fill one row; Dorian's 3 songs wrap 2 + 1 */
  gap: 0.25rem;
  margin: 0.1rem 0 0.35rem;
}

.setup__hero .ability-slot {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.05rem;
  min-height: 1.9rem;
  padding: 0.2rem 0.25rem;
  border-radius: 6px;
}

.setup__hero .ability-slot.is-unlocked {
  background: color-mix(in srgb, var(--accent) 12%, var(--window-bg));
  border: 1px solid var(--accent);
}

/* Dorian's 3rd song lands alone on row 2 — span the row and center it (same width
 * as the top two slots) rather than leaving it left-aligned. */
.setup__hero .ability-slot:nth-child(3):last-child {
  grid-column: 1 / -1;
  justify-self: center;
  width: calc(50% - 0.125rem);
}

.setup__hero .ability-slot__name {
  font-family: var(--font-display);
  font-size: 0.68rem;
  line-height: 1.05;
  color: var(--accent);
}

.setup__hero .ability-slot__tag {
  font-size: 0.52rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-muted);
}

.setup__hero .hero__abilities--none {
  display: block;   /* full-width caption, not a grid cell */
  margin: 0.1rem 0 0.35rem;
  font-size: var(--fs-sm);
  font-style: italic;
  color: var(--ink-muted);
  text-align: center;
}

/* Fresh Cut Grass has no hearts — he gets stress tokens instead. Placeholder art: a
 * sun emoji on a bright-blue square (swap for real token art when available). */
.setup__hero .stress-token {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.35rem;
  height: 1.35rem;
  background: #1FA2FF;
  border-radius: 4px;
  font-size: 0.9rem;
  line-height: 1;
}

/* Starting items — a clean, readable list. */
.setup__items {
  margin: 0 0 var(--space-5);
}

.setup__items h2 {
  margin: 0 0 0.6rem;
  font-family: var(--font-display);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-size: var(--fs-lg);
  color: var(--accent);
  border-bottom: 2px solid var(--accent);
  padding-bottom: 0.3rem;
}

.setup__items ul {
  margin: 0;
  padding-left: 1.3rem;
}

.setup__items li {
  margin: 0.3rem 0;
  font-size: var(--fs-base);
}

/* Reminder — set apart as an italic, accent-bordered callout. */
.setup__reminder {
  margin: 0 0 1.75rem;
  padding: 0.8rem 1rem;
  background: color-mix(in srgb, var(--accent) 8%, var(--window-bg));
  border-left: 4px solid var(--accent);
  border-radius: 0 6px 6px 0;
  font-style: italic;
  font-size: 0.95rem;
  line-height: 1.5;
}

@media (max-width: 700px) {
  .landing {
    margin: var(--space-4);
    padding: 1.5rem 1.25rem 1.75rem;
  }

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

/* -------------------------------------------------------------------------
 * Difficulty tabs inside the modal — a centered row of .btn--difficulty tabs
 * (difficulty_controller flips is-active + panel[hidden]) over one panel per tier.
 * The "Show character setup" toggle that reopens the modal sits above the
 * prologue in the mission article.
 * ---------------------------------------------------------------------- */
.difficulty-tabs { display: flex; flex-wrap: wrap; gap: var(--space-2); justify-content: center; margin-bottom: var(--space-4); }
/* Each tab carries its OWN difficulty hue via --tab-color, so it shows, hovers and
 * fills in that colour (a legend) — never the page's current accent. This overrides the
 * accent-based .btn--difficulty base (equal specificity, home.css loads after it). The
 * active tab and hover both fill solid. */
.difficulty-tabs__btn[data-difficulty="easy"]    { --tab-color: #AB3B79; }
.difficulty-tabs__btn[data-difficulty="medium"]  { --tab-color: #6A821E; }
.difficulty-tabs__btn[data-difficulty="hard"]    { --tab-color: #2898AD; }
.difficulty-tabs__btn[data-difficulty="extreme"] { --tab-color: #3B3B3B; }
/* Extreme is now live in every environment (all 12 missions have authored variants). The
 * previous env-gate that hid it in production has been removed. */
.difficulty-tabs__btn {
  background: color-mix(in srgb, var(--tab-color) 12%, transparent);
  color: var(--tab-color);
  border-color: var(--tab-color);
}
.difficulty-tabs__btn:hover,
.difficulty-tabs__btn.is-active {
  background: var(--tab-color);
  border-color: var(--tab-color);
  color: var(--ink-light);
}
/* A tier that isn't released yet (Setup::COMING_SOON): greyed out, inert, and it keeps that
 * look on hover — the live tabs fill solid on hover, which would otherwise make a disabled
 * tab look clickable. The tooltip comes from .tooltip-host in design_system.css. */
.difficulty-tabs__btn.is-coming-soon,
.difficulty-tabs__btn.is-coming-soon:hover {
  --tab-color: #9A9A9A;
  background: color-mix(in srgb, #9A9A9A 12%, transparent);
  border-color: #B4B4B4;
  color: #8A8A8A;
  cursor: not-allowed;
}

.setup-panel[hidden] { display: none; }
.mission__setup-toggle { margin-bottom: var(--space-4); align-self: flex-start; }
/*
 * language.css — the language switcher pinned in the top-right corner of every page.
 *
 * Sits above page content but BELOW the setup modal (z-index 1000 in design_system.css), so
 * an open character-setup pop-up covers it instead of the flags floating over the overlay.
 * The pill has its own translucent dark backing because it has to stay legible over both the
 * light parchment (mission page) and the plain page background (landing, picker).
 */
.lang {
  position: fixed;
  top: var(--space-3);
  right: var(--space-3);
  z-index: 900;
  display: flex;
  gap: 2px;
  padding: 3px;
  border-radius: 999px;
  background: rgba(20, 16, 12, 0.72);
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.35);
  backdrop-filter: blur(3px);
}

/* button_to wraps each button in its own <form>; strip its layout so the pill stays tight. */
.lang__form { margin: 0; display: block; line-height: 0; }

.lang__btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0;
  padding: 0.3rem 0.55rem;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: rgba(255, 255, 255, 0.78);
  font-family: var(--font-display);
  font-size: 0.78rem;
  letter-spacing: 0.04em;
  line-height: 1;
  cursor: pointer;
  transition: background-color 120ms ease, color 120ms ease;
}

.lang__btn:hover { background: rgba(255, 255, 255, 0.14); color: #fff; }

.lang__btn:focus-visible {
  outline: 2px solid #fff;
  outline-offset: 1px;
}

/* The active language reads as selected rather than clickable. */
.lang__btn.is-active {
  background: rgba(255, 255, 255, 0.92);
  color: #1a1410;
  cursor: default;
}

.lang__flag { display: inline-flex; line-height: 0; }

.flag {
  display: block;
  width: 20px;
  height: auto;
  border-radius: 2px;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.35);
}

/* Phones: the flags alone carry the meaning, so drop the codes and tighten the pill. */
@media (max-width: 34rem) {
  .lang { top: var(--space-2); right: var(--space-2); }
  .lang__code { display: none; }
  .lang__btn { padding: 0.3rem 0.4rem; }
}
/*
 * missions.css — visual replica of a CLC mission page (Milestone 1).
 *
 * All color lives here as CSS custom properties so re-tuning the palette (or
 * adding a campaign) is a one-file change. Campaign-independent window tokens
 * sit on :root; the campaign accent is themeable per campaign via .theme-*.
 * No frameworks, no textures/backgrounds yet — plain CSS only.
 */

:root {
  /* Page + surfaces */
  --page-bg: #F1E9DC;          /* warm cream / parchment */
  --paper-warm: #E7D3A9;       /* warm tan blended into the parchment art (multiply) */
  --window-bg: #FBF6EC;        /* slightly lighter cream for window bodies */
  --ink: #2A241C;              /* body text */
  --ink-light: #FDF7EC;        /* light text on colored bars */
  --ink-muted: #6E6456;        /* muted body text on light surfaces (captions, hints) */

  /* Fonts */
  --font-display: "cinder", Georgia, "Times New Roman", serif;
  --font-body: "lft-etica", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --font-condensed: "lft-etica-condensed", "lft-etica", system-ui, sans-serif;

  /* Rule-box colours: title / field / icon (campaign-independent, exact book
   * hexes). Ordered win → time → question → special, then reward + next. */
  --c-win: #D4A000;            /* gold title-bar */
  --win-field: #FDF2DE;        /* gold text-field */
  --win-icon: #E7AE1E;         /* gold icon */
  --c-time: #A9323F;           /* red title/icon */
  --time-field: #F0DDD9;       /* red text-field */
  --c-question: #009BDA;       /* cyan title/icon */
  --q-field: #E1EFFA;          /* cyan text-field */
  --c-special: rgba(0, 0, 0, 0.8);       /* dark slate title */
  --special-field: rgba(0, 0, 0, 0.15);  /* slate text-field */
  --c-reward: #557A38;         /* green */
  --c-next: #D9C7A1;           /* tan / parchment — Next Locations region removed; token retained for the styles test / future reuse */
  --box-edge: rgba(60, 40, 20, 0.55);  /* neutral dark-brown edge for the coloured rule boxes */

  /* Campaign accent — default; set per-theme below (.theme-*). */
  --accent: #A62A6E;
}

/* -------------------------------------------------------------------------
 * Campaign accent themes. Adding a campaign = one block here + one seed row.
 * ---------------------------------------------------------------------- */
.theme-tal_dorei  { --accent-light: #AB3B79; --accent-dark: #78054F; --accent: var(--accent-light); }  /* magenta / purple-pink */
.theme-wildemount { --accent-light: #6A821E; --accent-dark: #354700; --accent: var(--accent-light); }  /* forest green */
.theme-marquet    { --accent-light: #2898AD; --accent-dark: #005772; --accent: var(--accent-light); }  /* Marquet teal */

/* Difficulty-driven accent themes (setups#mission). The chosen difficulty tints the whole
 * page — top bar (var(--accent)), PROLOGUE/EPILOGUE headers (var(--accent-dark)) and the
 * mission-number badge. Same hues as the campaign themes; extreme goes dark grey / black. */
.theme-diff-easy    { --accent-light: #AB3B79; --accent-dark: #78054F; --accent: var(--accent-light); }
.theme-diff-medium  { --accent-light: #6A821E; --accent-dark: #354700; --accent: var(--accent-light); }
.theme-diff-hard    { --accent-light: #2898AD; --accent-dark: #005772; --accent: var(--accent-light); }
.theme-diff-extreme { --accent-light: #3B3B3B; --accent-dark: #111111; --accent: var(--accent-light); }

/* Difficulty text variants: an authored extreme override renders BOTH a normal and an
   extreme copy; only the active tier's copy shows. Outside .mission-play (missions#show)
   there is no theme-diff-* wrapper, so the normal copy shows and the extreme copy hides. */
.diff-variant--extreme { display: none; }
.theme-diff-extreme .diff-variant--normal { display: none; }
.theme-diff-extreme .diff-variant--extreme { display: block; }

/* Extreme alteration mark (~text~): a hand-scribbled, UPPERCASE span flagging a value or
   sentence changed for Extreme. Black ink, slightly enlarged, on a faint black highlight
   (the background sits behind any inline icons in the mark too). Weight is NOT forced here —
   bold comes from the surrounding **…** markup, so bolded bits (e.g. "sisters", the number)
   stay bold while marked prose stays regular weight. */
.extreme-mark {
  --mark-scale: 1.1;                /* enlarge the marked TEXT only; icons are counter-scaled below */
  --mark-pad-x: 0.55em;             /* horizontal padding; also the left-edge pull-back below */
  font-family: var(--font-scribble);
  color: var(--ink);
  text-transform: uppercase;
  font-size: calc(1em * var(--mark-scale));
  /* Dark highlight with slightly UNEVEN/JAGGED edges — a torn-rectangle SVG (like the epilogue
     separation wave) stretched behind the text. It's a background (painted per line-fragment, so
     wrapped marks each get their own patch) and nothing clips the element, so the text stays
     crisp and line-height/spacing are untouched. Kept a touch smaller than the old box-shadow
     bleed so stacked lines don't overlap. */
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 26' preserveAspectRatio='none'%3E%3Cpath d='M2 4 15 1 27 4 39 1 51 4 63 1 75 4 87 1 98 3.5 98 22.5 87 25 75 22 63 25 51 22 39 25 27 22 15 25 2 23 2 4Z' fill='black' fill-opacity='0.14'/%3E%3C/svg%3E") center / 100% 100% no-repeat;
  padding: 0.27em var(--mark-pad-x);
}
/* When a mark OPENS a left-aligned block (a whole special-rules segment, or a narrative
   paragraph), its left padding would otherwise indent the first line so it no longer lines up
   with the plain text above. Pull it left by exactly that padding: the text starts flush at the
   block's left edge while the highlight just bleeds into the surrounding padding. Scoped to
   left-aligned contexts + :first-child only, so centred boxes (win/time/?) and mid-line marks
   are unaffected. */
.window--special .rule-seg > .extreme-mark:first-child,
.prologue p > .extreme-mark:first-child,
.epilogue p > .extreme-mark:first-child {
  margin-left: calc(-1 * var(--mark-pad-x));
}
/* Inline icons inside the mark keep their normal size — undo the text enlargement so a
   [[skull]]/[[damage]] in the mark matches an icon in the surrounding copy. */
.extreme-mark .tile-icon,
.extreme-mark .coin-multi { font-size: calc(1em / var(--mark-scale)); }
.extreme-mark .coin-multi .tile-icon { font-size: 1em; }   /* wrapper already counter-scaled; don't double it */

/* -------------------------------------------------------------------------
 * Page container
 * ---------------------------------------------------------------------- */
.mission {
  position: relative;
  max-width: 1100px;
  margin: var(--space-5) auto;
  padding: 0;
  /* Light parchment page art (BG-light) is set inline per page over this neutral base.
   * No veil needed — BG-light already IS the light page tone. */
  background-color: var(--page-bg);
  background-image: none;               /* BG-light art set inline per page */
  background-size: cover;
  background-position: top center;
  background-repeat: no-repeat;
  color: var(--ink);
  font-family: var(--font-body);
  line-height: 1.5;
  border-radius: 0;          /* square book-page corners */
  overflow: hidden;
  box-shadow: 0 2px 14px rgba(0, 0, 0, 0.18);
}

/* -------------------------------------------------------------------------
 * Header band — full-width accent bar with badge, title, location.
 * ---------------------------------------------------------------------- */
/* FIXED-height accent band. Sized to comfortably fit the stacked campaign label +
 * title (h1) + location, vertically centred, with the cluster never exceeding the bar.
 * The band does NOT clip (overflow visible) so the taller mission-number badge can bleed
 * above and below it; the badge's own bleed off the left is fine (the .mission box clips
 * it). */
.mission__header {
  position: relative;
  display: flex;
  align-items: center;
  gap: 1.5rem;
  height: 6.5rem;                  /* exact: bar bottom meets the badge's torn RIGHT edge */
  background: var(--accent);
  color: var(--ink-light);
  padding: 0 2rem 0 0;             /* zero left pad → badge sits FLUSH at the page's left edge */
  overflow: visible;
}

/* The stacked campaign-label / title / location column, left-aligned to the badge. */
.mission__heading {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  text-align: left;
  min-width: 0;
}

.mission__campaign-label {
  font-family: var(--font-condensed);
  text-transform: uppercase;
  letter-spacing: 0.14em;
  font-size: 0.8rem;
  opacity: 0.92;
}

/* Mission-number badge: the dark fringed banner art rendered at its natural torn aspect
 * (sized by height) with the mission code laid over it. The art is TALLER than the fixed
 * bar, anchored to the TOP-LEFT and bleeding off the top (clipped by .mission) and left,
 * and overhanging the BOTTOM of the bar (the band is overflow:visible). Its shorter torn
 * right edge sits about at the bar's height, so the fuller body overhangs top and bottom. */
.mission__header .badge {
  position: relative;
  display: inline-flex;
  flex: none;
  align-self: flex-start;
  align-items: center;
  justify-content: center;
  margin: -0.9rem 0 0 0;         /* flush to the page's left edge (no inset); bleeds off the top */
}

.badge__art {
  display: block;
  height: 8.6rem;                /* > the 6rem bar, so the body overhangs top + bottom */
  width: auto;
}

/* Extreme has no black badge asset — render any coloured badge as a solid black
 * silhouette (alpha preserved). */
.badge__art.is-black { filter: brightness(0); }

.badge__code {
  position: absolute;
  inset: 0.9rem 1.7rem 1.2rem 0.2rem;   /* box spans the full 6.5rem bar → code vertically centred in the bar */
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--ink-light);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 2.8rem;
  line-height: 1;
  letter-spacing: 0.02em;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
}

.mission__header h1 {
  margin: 0.15rem 0 0.12rem;
  font-family: var(--font-display);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  font-size: 2.05rem;
  line-height: 1.05;
}

/* Location sits in the plain body face at a small size (not the letterspaced display
 * small-caps), matching the book's quiet sub-title under the mission name. */
.mission__location {
  font-family: var(--font-body);
  text-transform: none;
  letter-spacing: normal;
  font-size: 0.85rem;
  opacity: 0.95;
}

/* -------------------------------------------------------------------------
 * Two-column body: narrative (left, wider) + rule windows (right, narrower).
 * ---------------------------------------------------------------------- */
.mission__body {
  display: grid;
  grid-template-columns: 2fr 1fr;
  gap: var(--space-5);
  padding: 1.75rem 2rem 0;   /* no bottom padding: the epilogue dark region reaches the page's bottom edge */
}

/* Left narrative column: a flex column tall enough to SEPARATE the two blocks — PROLOGUE
 * at the top on the light parchment, EPILOGUE pushed to the very bottom onto its own dark
 * torn-edged region. min-height guarantees a clear gap between them even when the copy is
 * short. */
.mission__left {
  min-width: 0;
  position: relative;
  display: flex;
  flex-direction: column;
}

/* EPILOGUE region: a DARK-parchment block in the bottom-left (up to ~two-thirds width — the
 * left column's natural width, so it never reaches the right-hand rule boxes). margin-top:auto
 * pins it to the column bottom, separated from the prologue; negative left/bottom margins bleed
 * it off the page's left + bottom edges (.mission clips to a square). The BG-dark parchment art
 * is set inline. The wavy TOP edge is a clip-path traced pixel-for-pixel from the book's
 * separation-line image (via a decode script) — x in %, y in PX so the wave is a FIXED height
 * while the region scales/moves horizontally (dynamically movable). Left/right/bottom are
 * straight (they align with the page + column edges). */
.mission__epilogue {
  margin: var(--space-4) 0 0 calc(-1 * var(--space-5) - 2rem);  /* sit just below the prologue; bleed off the left edge */
  flex: 1 1 auto;                     /* grow to fill the left column down to the page's bottom edge */
  padding: 3.4rem 1.75rem 2.6rem calc(var(--space-5) + 2rem);
  position: relative;
  z-index: 1;
  background-color: #8a6f48;   /* dark fallback if the BG-dark art fails to load */
  background-size: cover;
  background-position: bottom left;
  background-repeat: no-repeat;
  clip-path: polygon(0% 100%, 0% 7.5px, 0.00% 7.5px, 0.64% 8.8px, 1.29% 9.9px, 1.93% 10.4px, 2.57% 10.7px, 3.22% 10.7px, 3.86% 10.8px, 4.50% 10.8px, 5.15% 11.4px, 5.79% 11.4px, 6.43% 11.0px, 7.08% 11.4px, 7.72% 12.0px, 8.36% 11.3px, 9.01% 10.1px, 9.65% 9.4px, 10.29% 9.4px, 10.94% 8.8px, 11.58% 8.1px, 12.22% 6.9px, 12.87% 6.3px, 13.51% 6.0px, 14.15% 6.1px, 14.80% 5.7px, 15.44% 4.6px, 16.08% 3.8px, 16.73% 3.6px, 17.37% 3.8px, 18.01% 3.4px, 18.66% 2.7px, 19.30% 3.2px, 19.94% 3.8px, 20.59% 4.5px, 21.23% 4.6px, 21.87% 4.6px, 22.52% 4.2px, 23.16% 4.5px, 23.80% 5.1px, 24.45% 5.7px, 25.09% 6.2px, 25.73% 5.7px, 26.38% 5.2px, 27.02% 5.1px, 27.66% 4.2px, 28.31% 3.2px, 28.95% 2.7px, 29.59% 3.0px, 30.24% 3.6px, 30.88% 4.7px, 31.52% 5.2px, 32.17% 5.2px, 32.81% 5.2px, 33.45% 5.2px, 34.10% 5.4px, 34.74% 5.5px, 35.38% 5.4px, 36.03% 5.9px, 36.67% 6.6px, 37.31% 6.7px, 37.96% 6.2px, 38.60% 5.7px, 39.24% 5.1px, 39.89% 4.9px, 40.53% 4.2px, 41.17% 3.2px, 41.82% 3.1px, 42.46% 3.9px, 43.10% 5.2px, 43.75% 5.7px, 44.39% 5.5px, 45.03% 5.1px, 45.68% 5.4px, 46.32% 5.2px, 46.96% 5.1px, 47.61% 4.9px, 48.25% 4.2px, 48.89% 4.3px, 49.54% 4.9px, 50.18% 4.9px, 50.82% 4.0px, 51.47% 3.0px, 52.11% 3.2px, 52.75% 3.9px, 53.40% 5.3px, 54.04% 6.3px, 54.68% 7.2px, 55.33% 7.3px, 55.97% 6.4px, 56.61% 6.3px, 57.26% 6.2px, 57.90% 6.3px, 58.54% 6.3px, 59.19% 6.3px, 59.83% 6.3px, 60.47% 6.0px, 61.12% 5.2px, 61.76% 3.2px, 62.40% 1.1px, 63.05% 0.2px, 63.69% 0.1px, 64.33% 0.7px, 64.98% 1.1px, 65.62% 2.0px, 66.26% 1.6px, 66.91% 1.1px, 67.55% 1.2px, 68.19% 1.9px, 68.84% 2.4px, 69.48% 3.1px, 70.12% 4.5px, 70.77% 5.4px, 71.41% 5.6px, 72.05% 5.1px, 72.70% 4.3px, 73.34% 3.6px, 73.98% 2.8px, 74.63% 3.3px, 75.27% 4.0px, 75.91% 3.8px, 76.56% 3.0px, 77.20% 3.3px, 77.84% 3.2px, 78.49% 2.5px, 79.13% 1.8px, 79.77% 1.9px, 80.42% 3.2px, 81.06% 4.9px, 81.70% 5.6px, 82.35% 5.4px, 82.99% 5.5px, 83.63% 6.8px, 84.28% 8.2px, 84.92% 8.4px, 85.56% 7.1px, 86.21% 6.3px, 86.85% 6.4px, 87.49% 7.5px, 88.14% 8.3px, 88.78% 8.3px, 89.42% 7.4px, 90.07% 7.5px, 90.71% 7.8px, 91.35% 7.8px, 92.00% 6.3px, 92.64% 5.3px, 93.28% 5.5px, 93.93% 6.0px, 94.57% 6.2px, 95.21% 6.1px, 95.86% 6.0px, 96.50% 6.0px, calc(100% - 17.0px) 6.0px, calc(100% - 15.1px) 6.2px, calc(100% - 13.2px) 6.7px, calc(100% - 11.5px) 7.5px, calc(100% - 9.9px) 8.6px, calc(100% - 8.6px) 9.9px, calc(100% - 7.5px) 11.5px, calc(100% - 6.7px) 13.2px, calc(100% - 6.2px) 15.1px, calc(100% - 6.0px) 17.0px, calc(100% - 6.0px) 9.00%, calc(100% - 6.4px) 10.14%, calc(100% - 7.2px) 11.28%, calc(100% - 6.9px) 12.41%, calc(100% - 6.2px) 13.55%, calc(100% - 6.2px) 14.69%, calc(100% - 4.8px) 15.82%, calc(100% - 3.6px) 16.96%, calc(100% - 3.9px) 18.10%, calc(100% - 2.7px) 19.24%, calc(100% - 3.6px) 20.38%, calc(100% - 4.6px) 21.51%, calc(100% - 4.6px) 22.65%, calc(100% - 4.3px) 23.79%, calc(100% - 5.4px) 24.93%, calc(100% - 6.1px) 26.06%, calc(100% - 5.3px) 27.20%, calc(100% - 4.8px) 28.34%, calc(100% - 3.0px) 29.48%, calc(100% - 2.9px) 30.61%, calc(100% - 4.3px) 31.75%, calc(100% - 5.2px) 32.89%, calc(100% - 5.1px) 34.03%, calc(100% - 5.4px) 35.16%, calc(100% - 5.4px) 36.30%, calc(100% - 6.0px) 37.44%, calc(100% - 6.7px) 38.58%, calc(100% - 5.9px) 39.71%, calc(100% - 5.1px) 40.85%, calc(100% - 4.2px) 41.99%, calc(100% - 3.0px) 43.12%, calc(100% - 4.5px) 44.26%, calc(100% - 5.6px) 45.40%, calc(100% - 5.2px) 46.54%, calc(100% - 5.2px) 47.67%, calc(100% - 5.1px) 48.81%, calc(100% - 4.2px) 49.95%, calc(100% - 4.8px) 51.09%, calc(100% - 4.6px) 52.23%, calc(100% - 3.0px) 53.36%, calc(100% - 3.8px) 54.50%, calc(100% - 5.7px) 55.64%, calc(100% - 7.3px) 56.78%, calc(100% - 6.5px) 57.91%, calc(100% - 6.4px) 59.05%, calc(100% - 6.4px) 60.19%, calc(100% - 6.3px) 61.32%, calc(100% - 6.2px) 62.46%, calc(100% - 4.5px) 63.60%, calc(100% - 1.0px) 64.74%, calc(100% - 0.0px) 65.88%, calc(100% - 1.0px) 67.01%, calc(100% - 2.0px) 68.15%, calc(100% - 1.2px) 69.29%, calc(100% - 1.5px) 70.43%, calc(100% - 2.6px) 71.56%, calc(100% - 4.5px) 72.70%, calc(100% - 5.7px) 73.84%, calc(100% - 4.8px) 74.97%, calc(100% - 3.5px) 76.11%, calc(100% - 3.2px) 77.25%, calc(100% - 3.9px) 78.39%, calc(100% - 2.9px) 79.53%, calc(100% - 3.2px) 80.66%, calc(100% - 1.9px) 81.80%, calc(100% - 2.3px) 82.94%, calc(100% - 5.0px) 84.08%, calc(100% - 5.6px) 85.21%, calc(100% - 5.9px) 86.35%, calc(100% - 8.2px) 87.49%, calc(100% - 7.2px) 88.62%, calc(100% - 6.2px) 89.76%, calc(100% - 7.8px) 90.90%, calc(100% - 8.3px) 92.04%, calc(100% - 7.3px) 93.18%, calc(100% - 7.9px) 94.31%, calc(100% - 6.2px) 95.45%, calc(100% - 5.4px) 96.59%, calc(100% - 6.3px) 97.72%, calc(100% - 6.7px) 98.86%, calc(100% - 5.2px) 100.00%);
}

/* Epilogue text matches the book (same as the prologue): dark campaign-accent heading + dark
 * body ink. The BG-dark region is only a medium tan, so the dark text reads cleanly on it. */
.mission__epilogue .epilogue h2 { color: var(--accent-dark); }
.mission__epilogue .epilogue p  { color: var(--ink); }

/* -------------------------------------------------------------------------
 * Reward widget (EXTREME-only, sits under the epilogue in the left column). The per-campaign
 * ornate filigree FRAME (magenta for C1) is the box background (stretched 100% 100%): a banner
 * plaque in the top-left holds "REWARD!", the white body below holds the congrats message.
 * Width-capped + centred so the portrait-ish frame art isn't stretched wide in the 2fr column.
 * ---------------------------------------------------------------------- */
.mission__reward {
  margin: var(--space-4) auto 0;   /* centre within the dark epilogue region */
  width: 100%;
  max-width: 260px;                /* smaller box */
}
/* Keep the frame art's true 606:750 aspect so the ornate frame is never squished. */
.reward {
  position: relative;
  aspect-ratio: 606 / 750;
}
/* Frame art desaturated to MONOCHROME (grayscale) — keeps the ornate filigree detail but drops
   the campaign colour to suit the extreme neutral palette (there is no greyscale frame asset). */
.reward__frame {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  filter: grayscale(1);
}
/* "REWARD!" over the frame's top-left plaque (~top-left 64% width, top ~13% height). */
.reward__header {
  position: absolute;
  z-index: 1;
  top: 5.5%;
  left: 4%;
  width: 64%;
  height: 13%;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--ink-light);
  font-family: var(--font-display);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  font-weight: 700;
  font-size: 1.15rem;
  line-height: 1;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.45);
}
/* Message centred in the frame's light body region (inset below the plaque, inside the ornate
   border); dark ink so it reads on the pale body. */
.reward__body {
  position: absolute;
  inset: 20% 15% 13%;   /* top below the plaque; sides/bottom inside the frame border */
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
}
.reward__message { margin: 0; color: var(--ink); font-size: 0.95rem; line-height: 1.4; }

/* Spoiler cover — a parchment patch filling the frame's pale interior (clear of the ornate
   border + the magenta plaque). Click to reveal: it "burns away" via an animated grid-of-holes
   mask (see @keyframes reward-burn) plus a charring filter shift. */
@property --burn-r {
  syntax: "<length>";
  inherits: false;
  initial-value: 0px;
}
.reward__cover {
  position: absolute;
  /* Position + size as pure percentages of the reward box, so it scales at ANY screen size
     (percentages track the box; the old mm offsets did not). These bake in the hand-tuned
     layout exactly as it was at the 260px reference: centred in the frame's white interior,
     enlarged, then nudged left 2px / down 1px. */
  inset: 17.99% 5.91% 4.73% 8.67%;
  z-index: 2;
  margin: 0;
  border: 0;
  padding: 0.4rem 0.6rem;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  background-color: #e7dabb;         /* parchment fallback under the art */
  background-size: cover;
  background-position: center;
  color: var(--ink);
  font-family: var(--font-condensed);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-weight: 700;
  font-size: 0.95rem;
  line-height: 1.2;
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.12), 0 1px 4px rgba(0, 0, 0, 0.25);
  border-radius: 0 10px 10px 10px;   /* round every corner except the top-left */
  /* Burn mask: a handful of holes at IRREGULAR positions that grow from nothing. Intersect
     compositing makes the mask transparent wherever ANY hole reaches, so scattered circles
     spread, overlap and clear the whole patch — reads like real burning, not a grid. The soft
     edge scales with the radius (× 1.6), so at --burn-r 0 every hole collapses to zero and the
     parchment is FULLY intact (no pre-visible see-through). Several holes are centred OUTSIDE
     the patch (negative / >100%) so it also burns inward from the edges. */
  --burn-r: 0px;
  --hole: #0000 0, #0000 var(--burn-r), #000 calc(var(--burn-r) * 1.6);   /* one hole's stops, reused */
  -webkit-mask-image:
    radial-gradient(circle at -6% 30%,  var(--hole)),
    radial-gradient(circle at 106% 58%, var(--hole)),
    radial-gradient(circle at 44% -6%,  var(--hole)),
    radial-gradient(circle at 60% 106%, var(--hole)),
    radial-gradient(circle at 24% 74%,  var(--hole)),
    radial-gradient(circle at 76% 30%,  var(--hole)),
    radial-gradient(circle at 50% 52%,  var(--hole));
  mask-image:
    radial-gradient(circle at -6% 30%,  var(--hole)),
    radial-gradient(circle at 106% 58%, var(--hole)),
    radial-gradient(circle at 44% -6%,  var(--hole)),
    radial-gradient(circle at 60% 106%, var(--hole)),
    radial-gradient(circle at 24% 74%,  var(--hole)),
    radial-gradient(circle at 76% 30%,  var(--hole)),
    radial-gradient(circle at 50% 52%,  var(--hole));
  -webkit-mask-composite: source-in;   /* Safari legacy keyword for intersect */
          mask-composite: intersect;   /* transparent where ANY hole reaches */
}
.reward__cover.is-burning {
  pointer-events: none;
  animation: reward-burn 1.25s ease-in forwards;
}
.reward__cover.is-burned { display: none; }
@keyframes reward-burn {
  0%   { --burn-r: 0px;   filter: none; }
  60%  { filter: sepia(0.6) brightness(0.85) contrast(1.1); }
  100% { --burn-r: 260px; filter: sepia(0.9) brightness(0.5) contrast(1.2); }
}
@media (prefers-reduced-motion: reduce) {
  .reward__cover.is-burning { animation: none; opacity: 0; }
}

.mission__right {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 0;   /* rule windows stack FLUSH (no gap), matching the book */
  /* Breathing room below the last rule box so a long right column never touches the
   * page's bottom edge. This lives on the RIGHT column only: the left column still
   * stretches to the grid height and its epilogue (flex:1) fills the dark region all
   * the way down, so the dark area keeps reaching the bottom edge. */
  padding-bottom: 2.5rem;
}

/* Narrative blocks */
.prologue h2,
.epilogue h2 {
  font-family: var(--font-display);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-size: 2.1rem;
  line-height: 1.05;
  color: var(--accent-dark);
  padding-bottom: 0;
  margin: 0 0 var(--space-3);
}

.prologue p,
.epilogue p { margin: 0 0 0.9rem; }

/* -------------------------------------------------------------------------
 * Boxed regions — shared chrome for windows and reward.
 * Region-specific border/background colors live in each section below.
 * ---------------------------------------------------------------------- */
.window {
  overflow: hidden;
}

/* Art-backed bars — the window title bars carry inline campaign art that stretches
 * edge-to-edge over a solid fallback colour (declared per-element, so a box keeps its
 * colour if the art fails to load). Shared here: no tiling, centered, full-bleed.
 * (The mission-number badge and the reward frame are separate <img>/box-background art.) */
.window__header {
  background-repeat: no-repeat;
  background-position: center;
  background-size: 100% 100%;
}

/* -------------------------------------------------------------------------
 * Windows — rounded boxes with a colored header bar and a cream body.
 * ---------------------------------------------------------------------- */
/* Flush, square, borderless rule boxes matching the book: no gap (they touch — set on
 * .mission__right), no border-radius, and no visible border. Only the bar-art header
 * colours and field background colours distinguish them. */
.window {
  border: none;
  border-radius: 0;
  background: var(--window-bg);
}

/* Title bar: the fringed campaign bar art (set inline) stretches across via the
 * shared art-backed-bars rule; the per-window flat colour below is the fallback.
 * Text is centered cream condensed. */
.window__header {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 2rem;
  padding: 0.4rem 0.9rem;
  font-family: var(--font-condensed);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  font-weight: 700;
  font-size: 1rem;
  color: var(--ink-light);
  text-align: center;
}

/* Boss-mission skull to the left of the WIN CONDITION title — white on the gold bar. */
.window__header .window__skull {
  height: 1.25em;
  margin-right: 0.4rem;
  filter: brightness(0) invert(1);
}

.window__body {
  padding: 0.8rem 0.95rem;
  font-size: 0.95rem;
}

/* Win Condition / Time / "?" bodies are centred (like the book); Special Rules stays
 * left-aligned for its longer prose. */
.window--win .window__body,
.window--time .window__body,
.window--question .window__body {
  text-align: center;
}

/* Blank-line-separated segments (e.g. FIRST / THEN, or the X / O branches of a "?"
 * box) render as blocks, spaced apart like paragraphs. Single \n stays a tight <br>. */
.window__body .rule-seg { display: block; }
.window__body .rule-seg + .rule-seg { margin-top: 0.75em; }
/* The win box's FIRST / THEN rows sit a little tighter than the other segment gaps. */
.window--win .window__body .rule-seg + .rule-seg { margin-top: 0.4em; }

/* Per-window header-bar fallback colours (behind the fringed art). */
.window--win .window__header      { background-color: var(--c-win); }
.window--time .window__header     { background-color: var(--c-time); }
.window--question .window__header { background-color: var(--c-question); }
.window--special .window__header  { background-color: var(--c-special); }

/* Per-window text-field (body) colors — exact book fields. */
.window--win .window__body      { background: var(--win-field); }
.window--time .window__body     { background: var(--time-field); }
.window--question .window__body { background: var(--q-field); }
.window--special .window__body  { background: var(--special-field); }

/* -------------------------------------------------------------------------
 * Mission nav — action bar under the mission article (Next / Home).
 * A centered horizontal bar, separated from the article by a top border, that
 * wraps on narrow screens. Mirrors the button conventions used elsewhere.
 * (The character setup is reached via the in-page modal toggle, not the nav.)
 * ---------------------------------------------------------------------- */
.mission-nav {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--space-4);
  max-width: 1100px;
  margin: 0 auto var(--space-6);
  padding: var(--space-5) var(--space-6) 0;
  border-top: 1px solid rgba(0, 0, 0, 0.12);
}

/* button_to wraps buttons in a <form>; drop its default margin so they line up
 * with the sibling links in the flex bar. The buttons themselves are the shared
 * .btn / .btn--primary / .btn--secondary components. */
.mission-nav form {
  margin: 0;
}

/* -------------------------------------------------------------------------
 * Tile icons — the real EN-34 token art rendered inline in rules text/bars.
 * ---------------------------------------------------------------------- */
.tile-icon {
  display: inline-block;
  height: 1.7em;                 /* taller than the text, like the book's inline tokens */
  width: auto;
  vertical-align: -0.5em;        /* sit the taller token centred on the text line */
  margin: 0 0.18em;
}

/* Multi-coin token: the blank coin disc (icones-L-2) with the value drawn inside, matched
 * to the dark bold "1" baked into the single-coin art (icones-L-1). The wrapper carries the
 * inline alignment/margin so the disc + number sit exactly like a normal tile icon. */
.coin-multi {
  position: relative;
  display: inline-block;
  vertical-align: -0.5em;
  margin: 0 0.18em;
}
.coin-multi > .tile-icon {
  display: block;
  vertical-align: top;
  margin: 0;
}
.coin-multi__num {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  transform: translateY(0.03em);  /* midway between baseline-aligned and disc-centred */
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.05em;
  line-height: 1;
  color: #3f3f3f;
}

/* FIRST / THEN are WIDE text-label tiles (the word "FIRST"/"THEN"), not glyphs — at the
 * tall glyph height their lettering reads larger than the body copy. Shrink them so the
 * label sits about at the body font size. */
.tile-icon.icon--first,
.tile-icon.icon--then {
  height: 1.25em;
  vertical-align: -0.2em;
}

/* Larger token for the "?" question title bar. Flat like the book — no drop-shadow. */
.tile-icon--lg {
  height: 1.85em;
  margin: 0;
}

/* Header flag tokens (boss skull / "!" hard / padlock) sit under the location. */
/* Flag tokens (boss skull / "!" hard / padlock) sit inline right after the location, and
 * render WHITE on the coloured header bar (the source art is black -> brightness(0) invert). */
.mission__header .icon {
  height: 1.5em;
  margin: 0 0 0 0.4rem;
  vertical-align: -0.35em;
  filter: brightness(0) invert(1);
}

/* -------------------------------------------------------------------------
 * Narrow screens: stack the two columns.
 * ---------------------------------------------------------------------- */
@media (max-width: 700px) {
  .mission { margin: var(--space-2); }

  .mission__header h1 { font-size: 1.7rem; }

  /* Stack vertically in the order: (setup toggle), PROLOGUE, the rule windows, then
   * EPILOGUE last. The epilogue is nested inside .mission__left next to the prologue,
   * so `display:contents` lifts both blocks into the body's own flow, and `order`
   * drops the epilogue below the .mission__right rule stack. (Desktop keeps the grid.) */
  .mission__body {
    display: flex;
    flex-direction: column;
    padding: 1.25rem 1.1rem 0;   /* no bottom padding — the epilogue reaches the article's bottom edge */
  }
  .mission__left { display: contents; }
  .mission__right { order: 0; padding-bottom: 0; }
  .mission__epilogue {
    order: 1;                                /* after the rule windows */
    flex: 0 0 auto;
    /* Bleed the dark parchment to the article's left/right + bottom edges: negative
     * side margins cancel the body's side padding (the article itself has padding:0
     * and clips overflow), the body drops its bottom padding, and this is the last
     * item — so the region reaches the outer container's edges. Inner padding keeps
     * the text inset. */
    margin: 0 -1.1rem 0;
    padding: 2.6rem 1.6rem 2.4rem;
  }

  .mission-nav {
    margin: 0 var(--space-2) var(--space-4);
    padding: 1.25rem 1.1rem 0;
    /* Stack the two actions to equal full width so they read as an aligned pair
     * (the button_to form is display:contents, so its button stretches too). */
    flex-direction: column;
    align-items: stretch;
  }
}
/*
 * picker.css — landing action row + hint, the mission index (grouped cards), and
 * the hero picker (checkbox roster). Reuses the shared design tokens
 * (--space-*, --fs-*, --accent, --c-*, --page-bg, --window-bg, --ink, fonts) and
 * the .btn component. Auto-required via `require_tree .`.
 */

/* -------------------------------------------------------------------------
 * Landing — row of actions + a small muted explanatory note.
 * ---------------------------------------------------------------------- */
.landing__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  justify-content: center;
  margin-bottom: var(--space-4);
}

.landing__hint {
  max-width: 44ch;
  margin: 0 auto;
  font-size: var(--fs-sm);
  color: var(--ink-muted);
  text-align: center;
  line-height: 1.5;
}

@media (max-width: 700px) {
  .landing__actions { flex-direction: column; }
}

/* -------------------------------------------------------------------------
 * Mission index — centered container, campaign groups with accent headings,
 * responsive grid of bordered mission cards.
 * ---------------------------------------------------------------------- */
.mission-index {
  max-width: 960px;
  margin: 3rem auto;
  padding: 0 var(--space-5);
}

.mission-index__head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  margin-bottom: var(--space-6);
}

.mission-index__head h1 {
  margin: 0;
  font-family: var(--font-display);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  font-size: var(--fs-2xl);
  color: var(--accent);
}

.mission-index__campaign {
  margin-bottom: var(--space-6);
}

.mission-index__campaign-title {
  margin: 0 0 var(--space-4);
  padding-bottom: var(--space-2);
  font-family: var(--font-display);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  font-size: var(--fs-xl);
  color: var(--accent);
  border-bottom: 2px solid var(--accent);
}

.mission-cards {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: var(--space-4);
}

/* Card layout: id chip + info on the top row, the two actions span a full-width
 * row underneath — so the title never gets squeezed by the buttons. */
.mission-card {
  display: grid;
  grid-template-columns: auto 1fr;
  grid-template-areas:
    "id      info"
    "actions actions";
  gap: var(--space-3);
  align-items: start;
  padding: var(--space-4);
  background: var(--window-bg);
  border: 1px solid rgba(0, 0, 0, 0.14);
  border-radius: 10px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}

.mission-card__id {
  grid-area: id;
  align-self: start;
  min-width: 2.6rem;
  padding: var(--space-2) var(--space-3);
  background: var(--accent);
  color: var(--ink-light);
  font-family: var(--font-display);
  font-size: var(--fs-lg);
  font-weight: 700;
  text-align: center;
  border-radius: 6px;
}

.mission-card__info {
  grid-area: info;
  min-width: 0;
}

.mission-card__title {
  margin: 0 0 var(--space-1);
  font-family: var(--font-display);
  font-size: var(--fs-lg);
  line-height: 1.15;
  color: var(--ink);
}

.mission-card__location {
  margin: 0 0 var(--space-2);
  font-size: var(--fs-sm);
  color: var(--ink-muted);
}

.mission-card__tags {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
}

.mission-card__actions {
  grid-area: actions;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-1);
}

/* Both actions share the full-width row as equal-size buttons. `button_to`
 * wraps its button in a <form>, so stretch the form too. */
.mission-card__actions > * {
  flex: 1 1 8rem;
}

.mission-card__actions form {
  display: flex;
}

.mission-card__actions .btn {
  width: 100%;
}

/* Small pill chips for mission flags. */
.tag {
  display: inline-block;
  padding: 0.1rem var(--space-2);
  border-radius: 999px;
  font-family: var(--font-display);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ink-light);
}

.tag--boss    { background: var(--c-time); }
.tag--hard    { background: var(--c-question); }
.tag--advanced { background: var(--c-special); }

@media (max-width: 700px) {
  .mission-index { margin: var(--space-4) auto; }
}

/* -------------------------------------------------------------------------
 * Hero picker — centered container, checkbox roster as a grid of cards.
 * ---------------------------------------------------------------------- */
.hero-picker {
  max-width: 720px;
  margin: 3rem auto;
  padding: 0 var(--space-5);
}

.hero-picker__head {
  margin-bottom: var(--space-5);
}

.hero-picker__head h1 {
  margin: 0 0 var(--space-2);
  font-family: var(--font-display);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  font-size: var(--fs-2xl);
  color: var(--accent);
}

.hero-picker__mission {
  margin: 0 0 var(--space-3);
  font-size: var(--fs-base);
  color: var(--ink);
}

.hero-picker__mission span {
  color: var(--ink-muted);
}

.hero-picker__hint {
  margin: 0 0 var(--space-4);
  font-size: var(--fs-sm);
  color: var(--ink-muted);
}

/* Fixed-size hero cards, one per hero, in that hero's own colour (`--accent` set inline) —
 * like the setup boxes but with just the name + portrait (hearts/abilities come later). */
.hero-picker__list {
  list-style: none;
  margin: 0 0 var(--space-5);
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: var(--space-3);
}

.pick-hero {
  margin: 0;
}

.pick-hero__label {
  position: relative;
  display: block;
  cursor: pointer;
}

/* Visually-hidden checkbox — still focusable; the card is the visible control. */
.pick-hero__check {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  margin: 0;
}

.pick-hero__card {
  position: relative;
  display: block;
  height: 12.5rem;               /* fixed size, same for every hero */
  overflow: hidden;
  background: var(--window-bg);
  border: 2px solid rgba(0, 0, 0, 0.16);
  border-radius: 10px;
  transition: border-color 0.12s ease, box-shadow 0.12s ease;
}

.pick-hero__name {
  position: absolute;
  inset: 0 0 auto 0;
  z-index: 2;
  padding: 0.6rem 0.5rem;
  text-align: center;
  font-family: var(--font-display);
  font-size: var(--fs-base);
  line-height: 1.1;
  color: var(--accent);
}

/* The portrait sits in a padded region BELOW the name (not behind it), so it never fades. */
.pick-hero__art {
  position: absolute;
  inset: 2.5rem 0.7rem 0.7rem;
  z-index: 1;
}

.pick-hero__portrait {
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: center bottom;   /* bottom-anchored, centred — same as the setup art */
}

/* Selection tick (hero colour), shown only when checked. */
.pick-hero__tick {
  position: absolute;
  top: 0.35rem;
  right: 0.45rem;
  z-index: 3;
  display: none;
  width: 1.3rem;
  height: 1.3rem;
  align-items: center;
  justify-content: center;
  border-radius: 999px;
  background: var(--accent);
  color: var(--ink-light);
  font-size: 0.85rem;
  line-height: 1;
}

.pick-hero__check:checked ~ .pick-hero__card {
  border-color: var(--accent);
  /* outer ring + shadow (no inset ring, which the name background used to sit in front of) */
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 35%, transparent), 0 3px 12px rgba(0, 0, 0, 0.16);
}
.pick-hero__check:checked ~ .pick-hero__card .pick-hero__tick {
  display: flex;
}
.pick-hero__check:focus-visible ~ .pick-hero__card {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.pick-hero__check:disabled ~ .pick-hero__card {
  opacity: 0.4;
  cursor: not-allowed;
}

@media (max-width: 700px) {
  .hero-picker { margin: var(--space-4) auto; }
}
/*
 * This is a manifest file that'll be compiled into application.css, which will include all the files
 * listed below.
 *
 * Any CSS (and SCSS, if configured) file within this directory, lib/assets/stylesheets, or any plugin's
 * vendor/assets/stylesheets directory can be referenced here using a relative path.
 *
 * You're free to add application-wide styles to this file and they'll appear at the bottom of the
 * compiled file so the styles you add here take precedence over styles defined in any other CSS
 * files in this directory. Styles in this file should be added after the last require_* statement.
 * It is generally better to create a new file per style scope.
 *


 */
