/* Siege of Baghdad -- the web shell.
 *
 * Light and dark are both first-class: the palette is defined on :root and
 * redefined under prefers-color-scheme, with a data-theme override so the
 * Settings choice wins in either direction.
 *
 * No red/green pair is ever the only thing distinguishing two states -- every
 * such pair also differs in shape, weight or label. */

:root {
  color-scheme: light;
  --ink: #241c14;
  --ink-soft: #5b4c3d;
  --ink-faint: #8a7864;
  --ground: #f4efe4;
  --panel: #fbf8f1;
  --edge: #ded2bd;
  --accent: #8a5a20;
  --accent-soft: #f0e2cb;
  --focus: #1f6feb;
  --shadow: 0 1px 2px rgba(36, 28, 20, .07), 0 6px 20px rgba(36, 28, 20, .06);
  --measure: 34rem;

  /* The page artwork. One plate per page per theme; the pages that have none
   * simply leave --page-art unset. See tools/content/build_site_art.py. */
  --art-home: url("../assets/art/home-hero-light.webp");
  --art-chronicle: url("../assets/art/chronicle-map-light.webp");
  --art-settings: url("../assets/art/settings-city-light.webp");
  --art-niche: url("../assets/art/piece-niche-light.webp");
  --art-play-preview: url("../assets/art/play-match-preview-light.webp");
  /* The Play page's own two colours, from its design notes: antique gold for
   * the frames and rules, deep emerald for the primary action and the rosette
   * that marks a choice. They are NOT a red/green pair and nothing here is told
   * apart by hue alone -- a chosen option also gains a border, a rosette and
   * `aria-pressed`. */
  --gold: #b08f4e;
  --gold-soft: #d9c49a;
  --gold-tint: rgba(176, 143, 78, .10);
  --emerald: #14543a;
  --emerald-ink: #f7f2e6;
  /* The light plates carry far more light than the dark ones, so they are held
   * further back: the artwork is a ground for the text, not a competitor. */
  --art-veil: .72;
  /* The Chronicle's plate is a line drawing, not a painting: at the veil that
   * suits the painted plates it reads as a stain rather than as a map. */
  --art-veil-map: .95;
  --scrim: #f4efe4;
  /* A panel that lets the artwork through, at the coverage the design notes
   * ask for. Over a page with no artwork it reads as the flat panel it is. */
  --panel-sheer: rgba(251, 248, 241, .90);
  /* The factions' own colours, from the design notes: Baghdad deep emerald,
   * the Mongols oxidised copper. Held to an edge, never a fill. */
  /* The Two Armies. A faction is told apart by its heading and its emblem; the
   * tint is an edge and a wash so faint it could be removed without anything
   * becoming ambiguous, which is the test it has to pass. */
  --defenders-wash: rgba(20, 84, 58, .055);
  --mongols-wash: rgba(138, 74, 42, .055);
  --army-art-opacity: .115;
  --army-card-lift: 0 10px 28px rgba(36, 28, 20, .10);
  --defenders-edge: #7d9c86;
  --mongols-edge: #c08a63;
  --portrait-veil: .82;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --ink: #ece3d5;
    --ink-soft: #b6a893;
    --ink-faint: #8b7d6a;
    --ground: #17130f;
    --panel: #201a15;
    --edge: #392f26;
    --accent: #d8a35c;
    --accent-soft: #2c2219;
    --focus: #7cb0ff;
  --shadow: 0 1px 2px rgba(0, 0, 0, .5), 0 6px 20px rgba(0, 0, 0, .4);

    --art-home: url("../assets/art/home-hero-dark.webp");
    --art-chronicle: url("../assets/art/chronicle-map-dark.webp");
    --art-settings: url("../assets/art/settings-city-dark.webp");
    --art-niche: url("../assets/art/piece-niche-dark.webp");
    --art-play-preview: url("../assets/art/play-match-preview-dark.webp");
    --gold: #c9a961;
    --gold-soft: #7a6538;
    --gold-tint: rgba(201, 169, 97, .12);
    --emerald: #1b6b47;
    --emerald-ink: #f7f2e6;
    --art-veil: .9;
    --art-veil-map: 1;
    --scrim: #17130f;
    --panel-sheer: rgba(32, 26, 21, .92);
    --defenders-wash: rgba(75, 126, 91, .055);
    --mongols-wash: rgba(151, 82, 50, .06);
    --army-art-opacity: .09;
    --army-card-lift: 0 12px 30px rgba(0, 0, 0, .32);
    --defenders-edge: #3c5a48;
    --mongols-edge: #7a4a33;
    --portrait-veil: .95;
  }
}

:root[data-theme="dark"] {
  color-scheme: dark;
  --ink: #ece3d5;
  --ink-soft: #b6a893;
  --ink-faint: #8b7d6a;
  --ground: #17130f;
  --panel: #201a15;
  --edge: #392f26;
  --accent: #d8a35c;
  --accent-soft: #2c2219;
  --focus: #7cb0ff;
  --shadow: 0 1px 2px rgba(0, 0, 0, .5), 0 6px 20px rgba(0, 0, 0, .4);

  --art-home: url("../assets/art/home-hero-dark.webp");
  --art-chronicle: url("../assets/art/chronicle-map-dark.webp");
  --art-settings: url("../assets/art/settings-city-dark.webp");
  --art-niche: url("../assets/art/piece-niche-dark.webp");
  --art-play-preview: url("../assets/art/play-match-preview-dark.webp");
  --gold: #c9a961;
  --gold-soft: #7a6538;
  --gold-tint: rgba(201, 169, 97, .12);
  --emerald: #1b6b47;
  --emerald-ink: #f7f2e6;
  --art-veil: .9;
  --art-veil-map: 1;
  --scrim: #17130f;
  --panel-sheer: rgba(32, 26, 21, .92);
  --defenders-wash: rgba(75, 126, 91, .055);
  --mongols-wash: rgba(151, 82, 50, .06);
  --army-art-opacity: .09;
  --army-card-lift: 0 12px 30px rgba(0, 0, 0, .32);
  --defenders-edge: #3c5a48;
  --mongols-edge: #7a4a33;
  --portrait-veil: .95;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font: 400 clamp(15px, 0.5vw + 14px, 17px)/1.65 "Iowan Old Style", "Palatino Linotype",
        Palatino, Georgia, "Times New Roman", serif;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

:where(a) { color: var(--accent); text-underline-offset: .18em; }
:where(a):focus-visible,
:where(button):focus-visible { outline: 2px solid var(--focus); outline-offset: 3px; }

.skip {
  position: absolute;
  left: -999px;
}
.skip:focus {
  left: .5rem; top: .5rem; z-index: 10;
  background: var(--panel); padding: .5rem .8rem; border-radius: .4rem;
}

/* ---------- the artwork layer ----------
 *
 * The approved concepts are composites: they show what a finished page looks
 * like, and none of them is a background. What ships is the painting alone,
 * with every word, card and control still in HTML above it -- so the text stays
 * selectable, searchable, translatable and reachable by a screen reader, and
 * the page can be laid out again for a phone without re-cutting the art.
 *
 * Four layers, bottom to top: the plate, a scrim that guarantees the text has
 * something quiet to sit on, the transparent WebGL canvas where there is one,
 * and the HTML. The first two are pseudo-elements on `body` -- they cost no
 * markup, and `pointer-events: none` keeps them from ever taking a click. */

body::before,
body::after {
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
}

body::before {
  z-index: -2;
  background-image: var(--page-art, none);
  background-size: cover;
  background-position: var(--page-art-anchor, center);
  background-repeat: no-repeat;
  opacity: var(--art-veil);
}

/* Text safety, computed rather than painted in: on a wide screen the quiet
 * side is the left, where the plates were composed to be empty; below that
 * the columns stack and the quiet side becomes the top. */
body::after {
  z-index: -1;
  background:
    linear-gradient(to right, var(--scrim) 0%,
      color-mix(in srgb, var(--scrim) 66%, transparent) 30%, transparent 58%),
    linear-gradient(to bottom, transparent 55%,
      color-mix(in srgb, var(--scrim) 62%, transparent) 100%);
}

/* Below this the columns stack and text runs the full width, so the quiet side
 * is the top rather than the left. The breakpoint is 40rem, not the 54rem the
 * layout uses: at 800px the two-column page still has a quiet left, and the
 * full-width scrim there flattened the plate to nothing. */
@media (max-width: 40rem) {
  body::after {
    background: linear-gradient(to bottom,
      color-mix(in srgb, var(--scrim) 82%, transparent) 0%,
      color-mix(in srgb, var(--scrim) 58%, transparent) 50%,
      color-mix(in srgb, var(--scrim) 80%, transparent) 100%);
  }
}

/* Which plate, if any. A page not named here has no artwork and the scrim has
 * nothing to soften, which is why it is keyed off --page-art being set. */
body:not([data-art])::before,
body:not([data-art])::after { display: none; }

body[data-art="home"] {
  --page-art: var(--art-home);
  --page-art-anchor: right center;
}
body[data-art="chronicle"] {
  --page-art: var(--art-chronicle);
  --page-art-anchor: right top;
  --art-veil: var(--art-veil-map);
}
/* And the scrim lets go of it sooner: the chapter list is a column of solid
 * panels that carries its own contrast, so the map does not have to be faded
 * out from under it the way running prose would. */
body[data-art="chronicle"]::after {
  background:
    linear-gradient(to right, var(--scrim) 0%,
      color-mix(in srgb, var(--scrim) 52%, transparent) 26%, transparent 48%),
    linear-gradient(to bottom, transparent 62%,
      color-mix(in srgb, var(--scrim) 55%, transparent) 100%);
}
@media (max-width: 40rem) {
  body[data-art="chronicle"]::after {
    background: linear-gradient(to bottom,
      color-mix(in srgb, var(--scrim) 76%, transparent) 0%,
      color-mix(in srgb, var(--scrim) 52%, transparent) 50%,
      color-mix(in srgb, var(--scrim) 78%, transparent) 100%);
  }
}
body[data-art="settings"] {
  --page-art: var(--art-settings);
  --page-art-anchor: center bottom;
}

/* The artwork runs behind the masthead and the footer rather than starting
 * below them: in the light concepts the page is one continuous sheet of paper
 * with the navigation sitting on it, and an opaque bar cuts the plate in two.
 * In dark the plates are dark along the top anyway, so it still reads as a bar. */
.masthead, .footer { background: var(--panel-sheer); }

/* Panels over artwork let it through at the coverage the design notes set. */
.tile,
.chapter-book,
.roster-item,
.stage { background: var(--panel-sheer); }

/* ---------- masthead ---------- */

.masthead {
  display: flex;
  align-items: center;
  gap: 1rem;
  flex-wrap: wrap;
  padding: .9rem clamp(1rem, 4vw, 3rem);
  border-bottom: 1px solid var(--edge);
  background: var(--panel);
}

.wordmark { text-decoration: none; color: inherit; margin-right: auto; display: block; }
.wordmark-title {
  display: block;
  font-size: 1.18rem;
  letter-spacing: .06em;
  text-transform: uppercase;
}
.wordmark-sub { display: block; font-size: .8rem; color: var(--ink-faint); font-style: italic; }

.nav { display: flex; gap: .3rem; flex-wrap: wrap; }
.nav a {
  text-decoration: none;
  color: var(--ink-soft);
  padding: .42rem .8rem;
  border-radius: .4rem;
  font-size: .95rem;
}
.nav a:hover { background: var(--accent-soft); color: var(--ink); }
/* The current section is marked by weight and a rule, not by colour alone. */
.nav a[aria-current="page"] {
  color: var(--ink);
  font-weight: 600;
  box-shadow: inset 0 -2px 0 var(--accent);
}

.menu-toggle {
  display: none;
  align-items: center;
  gap: .5rem;
  font: inherit;
  font-size: .9rem;
  color: var(--ink);
  background: transparent;
  border: 1px solid var(--edge);
  border-radius: .4rem;
  padding: .4rem .7rem;
  cursor: pointer;
}
.menu-bars, .menu-bars::before, .menu-bars::after {
  display: block; width: 1rem; height: 2px; background: currentColor; position: relative;
}
.menu-bars::before, .menu-bars::after { content: ""; position: absolute; left: 0; }
.menu-bars::before { top: -5px; }
.menu-bars::after { top: 5px; }

@media (max-width: 46rem) {
  .menu-toggle { display: inline-flex; }
  .nav { display: none; width: 100%; flex-direction: column; }
  .nav.open { display: flex; }
  .nav a { padding: .6rem .5rem; }
}

/* ---------- layout ---------- */

.main { flex: 1; padding: clamp(1.4rem, 4vw, 3rem) clamp(1rem, 4vw, 3rem); }
.footer {
  border-top: 1px solid var(--edge);
  padding: 1.2rem clamp(1rem, 4vw, 3rem);
  color: var(--ink-faint);
  font-size: .85rem;
  font-style: italic;
}

/* The footer's own line to the credits: the licences have to be reachable from
 * every page, and the footer is the one thing on all of them. */
.footer-licences { margin: .45rem 0 0; font-style: normal; }

/* Credits and licences: each block ends in the texts and the sources it names. */
.credit-links { list-style: none; padding: 0; margin: .7rem 0 0; }
.credit-links li { margin: .25rem 0; }
.engine-note { color: var(--ink-faint); font-size: .85rem; margin: .6rem 0 0; }

.loading { color: var(--ink-faint); font-style: italic; }

h1, h2, h3 { line-height: 1.22; font-weight: 600; }
h1 { font-size: clamp(1.7rem, 3.2vw, 2.4rem); margin: 0 0 .3em; letter-spacing: .01em; }
h2 { font-size: 1.32rem; margin: 2.1em 0 .5em; }
h3 { font-size: 1.06rem; margin: 1.6em 0 .35em; color: var(--ink-soft); }

.lede { color: var(--ink-soft); font-size: 1.05rem; max-width: var(--measure); }

/* ---------- home ---------- */

.hero { max-width: 46rem; }
.hero h1 { margin-bottom: .2em; }
.hero .year { color: var(--ink-faint); letter-spacing: .3em; font-size: .85rem; }

/* The three sections as equal tiles, each a picture of what is inside above
 * its words. The tiles share a row height; the picture takes whatever the words
 * leave, so a tile with a shorter text simply shows more of its picture. */
/* Three across or one above another, never two and an orphan: a wrapped third
 * tile sat in a row of its own and came out shorter than the other two. */
.tiles {
  display: grid;
  gap: 1rem;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  grid-auto-rows: 1fr;
  margin-top: 2.2rem;
}
@media (max-width: 40rem) { .tiles { grid-template-columns: minmax(0, 1fr); } }
.tile {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  color: inherit;
  text-decoration: none;
  background: var(--panel);
  border: 1px solid var(--edge);
  border-radius: .6rem;
  box-shadow: var(--shadow);
}
.tile:hover { border-color: var(--accent); }
.tile-art {
  position: relative;
  display: block;
  flex: 1 1 auto;
  min-height: clamp(10rem, 20vw, 14rem);
  overflow: hidden;
  border-bottom: 1px solid var(--edge);
}
.tile-art > img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
/* The cover's top holds its title; keep that in the crop. */
.tile-book .tile-art > img { object-position: 50% 0; }

/* The two kings side by side, each on his own army's wash with a rule between.
 * The Caliph is mirrored so the two turn towards each other. */
.tile-kings { position: absolute; inset: 0; display: grid; grid-template-columns: 1fr 1fr; }
.tile-kings img { width: 100%; height: 100%; object-fit: contain; object-position: 50% 100%; }
.tile-kings img:first-child {
  background: var(--defenders-wash);
  border-right: 1px solid var(--edge);
  transform: scaleX(-1);
}
.tile-kings img:last-child { background: var(--mongols-wash); }

.tile-play .tile-art { background: var(--art-play-preview) 50% 62% / cover no-repeat; }

.tile-text { display: block; padding: .85rem 1.1rem 1.05rem; }
.tile-kicker { display: block; font-size: .72rem; letter-spacing: .12em; text-transform: uppercase; color: var(--ink-faint); }
.tile h2 { margin: .2rem 0 .3rem; font-size: 1.15rem; }
.tile p { margin: 0; color: var(--ink-soft); font-size: .92rem; }


/* ---------- chronicle ---------- */

.chronicle-intro {
  display: grid;
  grid-template-columns: clamp(5.5rem, 16vw, 10rem) minmax(0, 1fr);
  gap: clamp(1rem, 3vw, 1.6rem);
  align-items: center;
  max-width: 52rem;
  margin-top: 1rem;
}
.intro-cover img {
  display: block; width: 100%; height: auto;
  border: 1px solid var(--edge); border-radius: .3rem; box-shadow: var(--shadow);
}
.intro-cover:hover img { border-color: var(--accent); }
/* The cover is the way into the book; the words under it say so, inside the
 * same link, so the picture does not have to be guessed at. */
.intro-cover { display: block; text-align: center; }
.intro-cover-label { display: block; margin-top: .45rem; font-size: .9rem; font-weight: 600; }
.chronicle-intro .lede { margin: 0; }

.chapter-books {
  list-style: none; margin: 2.2rem 0 0; padding: 0;
  display: grid; grid-template-columns: minmax(0, 1fr); gap: .8rem;
}
/* min-width 0, or a grid item grows to its widest row of thumbnails and the
 * strip's own scrolling never starts -- the whole page scrolled sideways. */
.chapter-book {
  min-width: 0;
  background: var(--panel);
  border: 1px solid var(--edge);
  border-radius: .5rem;
  padding: .8rem 1rem .9rem;
}
.chapter-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: .2rem 1rem; }
.chapter-head .num { color: var(--ink-faint); font-variant-numeric: tabular-nums; min-width: 1.4rem; }
.chapter-head h2 { margin: 0; font-size: 1.05rem; }
.chapter-head h2 a { color: inherit; text-decoration: none; }
.chapter-head h2 a:hover { text-decoration: underline; }
.chapter-head .meta { color: var(--ink-faint); font-size: .82rem; }
.chapter-head .as-text { margin-left: auto; font-size: .9rem; }

/* A chapter's pages in one row. A long chapter scrolls sideways inside its own
 * box on a narrow screen rather than widening the page. */
.page-strip {
  position: relative; /* the thumbnails' offsetLeft is measured from the strip */
  display: flex;
  align-items: flex-start;
  gap: .45rem;
  overflow-x: auto;
  margin-top: .7rem;
  padding-bottom: .25rem;
}
.page-strip a {
  flex: 0 0 auto;
  width: 4.6rem;
  line-height: 0;
  text-decoration: none;
  border: 2px solid transparent;
  border-radius: .3rem;
}
.page-strip a:hover, .page-strip a:focus-visible { border-color: var(--accent); }
.page-strip img { display: block; width: 100%; height: auto; border-radius: .2rem; }
.page-strip .n { display: block; text-align: center; font-size: .68rem; line-height: 1.5; color: var(--ink-faint); }

/* ---------- reading a chapter: the pages or the text ---------- */

.reading-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: .6rem 1.2rem;
  margin: .2rem 0 1.5rem;
}
.prose ~ .chapter-steps, .reading-bar:has(+ .prose) { max-width: var(--measure); }

/* Which form is open: filled, bold and ticked, never colour alone. */
.form-switch {
  display: inline-flex;
  border: 1px solid var(--edge);
  border-radius: .45rem;
  overflow: hidden;
  background: var(--panel-sheer);
}
.form-switch a { padding: .38rem .95rem; color: var(--ink-soft); text-decoration: none; font-size: .92rem; }
.form-switch a + a { border-left: 1px solid var(--edge); }
.form-switch a:hover { color: var(--ink); background: var(--accent-soft); }
.form-switch a[aria-current="page"] { color: var(--ink); background: var(--accent-soft); font-weight: 600; }
.form-switch a[aria-current="page"]::before { content: "✓ "; }

/* On to the neighbouring chapter, in the form being read. Each step says what
 * it is before it says where it goes. */
.chapter-steps {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: .8rem;
  margin-top: 2.5rem;
  padding-top: 1.2rem;
  border-top: 1px solid var(--edge);
}
.book .chapter-steps { margin-top: 1.4rem; }
.step {
  display: block;
  padding: .65rem .9rem;
  color: inherit;
  text-decoration: none;
  background: var(--panel-sheer);
  border: 1px solid var(--edge);
  border-radius: .5rem;
}
.step:hover { border-color: var(--accent); }
.step-next { grid-column: 2; text-align: end; }
.step-label { display: block; font-size: .8rem; color: var(--ink-faint); }
.step-title { display: block; font-weight: 600; }

.prose { max-width: var(--measure); }
.prose p { margin: 0 0 1.05em; }
/* Logical sides, so an Arabic quotation's rule stands on its right, where the
   line begins. */
.prose blockquote {
  margin: 1.6em 0;
  padding-inline-start: 1.1rem;
  border-inline-start: 3px solid var(--accent);
  color: var(--ink-soft);
  font-style: italic;
}
/* Arabic has no italic, and the book's Latin faces carry no Arabic, so it takes
   a system Naskh face a size up. On the element that carries the lang only, or a
   quotation's paragraph would grow twice. */
.prose [lang="ar"] {
  font-family: "Noto Naskh Arabic", "Traditional Arabic", "Arabic Typesetting",
               "Geeza Pro", "Segoe UI", Tahoma, serif;
  font-style: normal;
  font-size: 1.2em;
  line-height: 1.8;
}
.prose .source-link {
  margin-top: -.6em;
  font-size: .9rem;
  overflow-wrap: anywhere;
}

/* A table from the manuscript -- one, chapter 9's reading list. It scrolls in
 * its own box rather than widening the page, and it keeps a minimum width so a
 * phone scrolls it instead of setting three columns one word to a line. The
 * header is told apart by weight, case and a rule, not by colour alone. */
.prose .table-scroll { overflow-x: auto; margin: 1.4em 0 1.9em; }
.prose-table {
  width: 100%;
  min-width: 30rem;
  border-collapse: collapse;
  font-size: .93rem;
  line-height: 1.45;
}
.prose-table th,
.prose-table td {
  padding: .6em .75em;
  text-align: start;
  vertical-align: top;
  border-bottom: 1px solid var(--edge);
  white-space: pre-line;
}
.prose-table thead th {
  border-bottom: 2px solid var(--accent);
  color: var(--ink-soft);
  font-size: .78rem;
  font-weight: 600;
  letter-spacing: .05em;
  text-transform: uppercase;
}
.prose-table tbody th { font-weight: 600; }
.prose-table tbody tr:last-child > * { border-bottom: 0; }

.chapter-nav {
  display: flex;
  gap: 1rem;
  justify-content: space-between;
  margin-top: 2.5rem;
  padding-top: 1.2rem;
  border-top: 1px solid var(--edge);
  font-size: .95rem;
}

/* ---------- book ---------- */

/* One column: the page, its paging, the open chapter's pages, the chapters. */
.book { max-width: 46rem; }
.book > .page-strip { margin-top: 1.2rem; }

.page-frame {
  background: var(--panel);
  border: 1px solid var(--edge);
  border-radius: .5rem;
  padding: .7rem;
  box-shadow: var(--shadow);
  display: grid;
  place-items: center;
  overflow: hidden;
}
/* The page and its blurred thumbnail share one cell; the page covers it. */
.page-frame > img { grid-area: 1 / 1; }
.page-frame .page-under { filter: blur(6px); }
/* A filter makes its own stacking context, which paints above plain siblings
 * whatever their order -- the loaded page stayed hidden under the blur. */
.page-frame > img:not(.page-under) { position: relative; z-index: 1; }
/* The book has two page shapes; the size comes from book.json so the frame
 * reserves the right box and the layout does not jump when a page loads. */
.page-frame img { width: 100%; height: auto; border-radius: .25rem; display: block; }

.page-bar { display: flex; align-items: center; gap: .8rem; margin-top: .9rem; }
.page-bar button {
  font: inherit;
  background: var(--panel);
  color: var(--ink);
  border: 1px solid var(--edge);
  border-radius: .4rem;
  padding: .4rem .9rem;
  cursor: pointer;
}
.page-bar button:hover:not(:disabled) { border-color: var(--accent); }
.page-bar button:disabled { opacity: .45; cursor: default; }
.page-where { color: var(--ink-faint); font-size: .88rem; font-variant-numeric: tabular-nums; }

/* The open page in its chapter's strip: a ring plus a bold number. */
.page-strip a[aria-current="page"] { border-color: var(--accent); }
.page-strip a[aria-current="page"] .n { color: var(--ink); font-weight: 700; }

/* ---------- settings ---------- */

.settings { max-width: var(--measure); display: grid; gap: 1.4rem; margin-top: 1.5rem; }
.setting {
  background: var(--panel);
  border: 1px solid var(--edge);
  border-radius: .5rem;
  padding: 1rem 1.1rem;
}
.setting h2 { margin: 0 0 .2rem; font-size: 1rem; }
.setting p { margin: 0 0 .7rem; color: var(--ink-soft); font-size: .9rem; }
.choices { display: flex; gap: .4rem; flex-wrap: wrap; }
.choices button {
  font: inherit; font-size: .9rem;
  background: transparent; color: var(--ink);
  border: 1px solid var(--edge); border-radius: .4rem;
  padding: .35rem .85rem; cursor: pointer;
}
.choices button[aria-pressed="true"] {
  border-color: var(--accent);
  background: var(--accent-soft);
  font-weight: 600;
}

@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
}

/* ---------- the two armies ---------- */

/* Each army is a chapter of the collection rather than a row of cards under a
 * heading: its own panel, its own emblem, and a faint drawing at the foot of it
 * to gather the empty space the grid leaves.
 *
 * **The faction tint is never the difference.** The heading names the army in
 * words and the emblem says it again in a shape; the colour is an edge and a
 * wash at five per cent, and the page would still be unambiguous with both of
 * them taken out. */
.army-section {
  --army-edge: var(--edge);
  --army-wash: transparent;
  position: relative;
  isolation: isolate;
  overflow: hidden;
  margin: 2rem 0 2.6rem;
  padding: clamp(1rem, 2vw, 1.5rem);
  border: 1px solid color-mix(in srgb, var(--army-edge) 64%, var(--edge));
  border-radius: .75rem;
  background:
    linear-gradient(135deg, var(--army-wash), transparent 46%),
    color-mix(in srgb, var(--panel) 94%, transparent);
}

.army-section[data-faction="defenders"] {
  --army-edge: var(--defenders-edge);
  --army-wash: var(--defenders-wash);
  --army-emblem: url("../assets/art/armies/faction-emblem-baghdad.svg");
  --army-art: url("../assets/art/armies/armies-marginalia-baghdad.webp");
}

.army-section[data-faction="mongols"] {
  --army-edge: var(--mongols-edge);
  --army-wash: var(--mongols-wash);
  --army-emblem: url("../assets/art/armies/faction-emblem-mongol.svg");
  --army-art: url("../assets/art/armies/armies-marginalia-mongol.webp");
}

.army-heading {
  position: relative;
  z-index: 2;
  display: grid;
  grid-template-columns: 3.25rem minmax(0, 1fr);
  align-items: center;
  gap: .9rem;
  margin-bottom: 1rem;
  padding-bottom: .75rem;
  border-bottom: 1px solid color-mix(in srgb, var(--army-edge) 70%, transparent);
}

.army-heading h2 { margin: 0; }

.army-kicker {
  margin: 0 0 .08rem;
  color: var(--ink-faint);
  font-size: .68rem;
  letter-spacing: .18em;
  text-transform: uppercase;
}

/* The emblem is a MASK rather than an image, so the one file is the right
 * colour in either theme -- the same reason the portraits are rendered on
 * nothing and faded with a mask. */
.army-emblem {
  width: 3.1rem;
  aspect-ratio: 1;
  background: var(--army-edge);
  -webkit-mask: var(--army-emblem) center / contain no-repeat;
  mask: var(--army-emblem) center / contain no-repeat;
}

/* The drawing at the foot of the section: a band running the full width of the
 * panel, under the cards and inside the border.
 *
 * **It is in the flow, not behind the cards.** Absolutely positioned it had
 * nowhere to be -- a full grid left it a forty-pixel strip to show through, and
 * at three quarters opacity it still could not be seen. A marginal drawing runs
 * along the foot of the page; it does not hide under what is written above it.
 *
 * The 6:1 is not a guess about these two drawings. They measure 7.04 and 4.28,
 * and `build_site_art.py` cuts each one out of its canvas and pads it back to
 * this one shape with clear space -- so the page can lay a band out against a
 * ratio it knows, and neither picture is stretched to reach it. Change the
 * ratio there and change it here.
 *
 * Its job is to gather the space the cards leave, not to play a scene: a tenth
 * of full strength, and never above 14%. */
.army-marginalia {
  width: 100%;
  aspect-ratio: 6 / 1;
  margin-top: clamp(.8rem, 1.6vw, 1.3rem);
  background: var(--army-art) center / contain no-repeat;
  opacity: var(--army-art-opacity);
  pointer-events: none;
  user-select: none;
}

.army-heading,
.roster { position: relative; z-index: 1; }

/* Four and three for Baghdad's seven, three and three for the Mongols' six.
 * `auto-fill` put five on a wide screen and left two stranded underneath, which
 * reads as a grid that ran out rather than as a company drawn up. */
.roster {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: .75rem;
  margin: 0;
}

.army-section[data-faction="mongols"] .roster {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

@media (max-width: 72rem) {
  .roster,
  .army-section[data-faction="mongols"] .roster {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 38rem) {
  .roster,
  .army-section[data-faction="mongols"] .roster {
    grid-template-columns: 1fr;
  }
  .army-section { padding: 1rem; }
  .army-heading { grid-template-columns: 2.6rem minmax(0, 1fr); }
  .army-emblem { width: 2.6rem; }
  .army-marginalia { opacity: .07; }
}

/* A card is the figure and its name side by side: the portrait sits on the
 * right and fades out under the words, which is what the visual notes ask for
 * and why the portraits are rendered on nothing. The fade is a MASK rather than
 * a painted gradient, so one image works over either theme's card colour. */
.roster-item {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  display: grid;
  gap: .15rem;
  align-content: start;
  /* Taller than it was: at four columns the longest names take three lines, and
   * the answer is height rather than a squeezed portrait. */
  min-height: 9.25rem;
  transition: transform 160ms ease, box-shadow 160ms ease,
              border-color 160ms ease, background-color 160ms ease;
  text-decoration: none;
  color: inherit;
  background: var(--panel);
  border: 1px solid var(--edge);
  border-radius: .5rem;
  padding: .8rem .9rem;
}
.roster-item > span { position: relative; z-index: 1; max-width: 63%; }
/* The whole card is the link, so the whole card answers -- and it answers to
 * the keyboard as well as the mouse. The outline itself is the global
 * `:focus-visible` rule's, untouched. */
.roster-item:is(:hover, :focus-visible) {
  transform: translateY(-2px);
  border-color: var(--accent);
  box-shadow: var(--army-card-lift);
}

/* The portrait brightens and does NOT move.
 *
 * The design asked for a small nudge and scale with it, and it cost four of the
 * seven Baghdad portraits: animating `transform` promotes the image to its own
 * compositing layer, and a MASKED layer then failed to paint at all. Three
 * cards showed a figure and the rest showed none -- by position, not by
 * content, which is what gave it away. `opacity` alone is safe, and the card
 * under it still lifts, since a card carries no mask. */
.roster-item:is(:hover, :focus-visible) .roster-portrait { opacity: 1; }

.roster-portrait {
  position: absolute;
  z-index: 0;
  right: -.6rem;
  bottom: 0;
  height: 104%;
  width: auto;
  pointer-events: none;
  object-fit: contain;
  object-position: right bottom;
  opacity: var(--portrait-veil, .92);
  -webkit-mask-image: linear-gradient(to right, transparent 0%, #000 46%);
  mask-image: linear-gradient(to right, transparent 0%, #000 46%);
  transition: opacity 160ms ease;
}

/* The two armies also differ at the card edge -- Baghdad emerald, the Mongols
 * oxidised copper. Never by that colour alone: the section heading above and
 * the piece code on the card say which is which in words. */
.roster-item[data-faction="defenders"] { border-color: var(--defenders-edge); }
.roster-item[data-faction="mongols"] { border-color: var(--mongols-edge); }
.roster-item[data-faction="defenders"]:hover,
.roster-item[data-faction="mongols"]:hover { border-color: var(--accent); }
.roster-code {
  font-size: .74rem; letter-spacing: .14em; color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
}
.roster-name { font-weight: 600; }
.roster-role { font-size: .82rem; color: var(--ink-soft); font-style: italic; }

.piece { display: grid; gap: 1.6rem; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); align-items: start; }
@media (max-width: 54rem) { .piece { grid-template-columns: 1fr; } }

.stage {
  background: var(--panel);
  border: 1px solid var(--edge);
  border-radius: .6rem;
  padding: .6rem;
  box-shadow: var(--shadow);
  position: sticky;
  top: 1rem;
}
/* The figure stands in a lit niche. The canvas asks for an alpha context and
 * clears to zero, so the plate behind it shows through around the model --
 * which is the whole reason the niche was drawn empty. */
.piece-canvas {
  display: block;
  width: 100%;
  aspect-ratio: 3 / 4;
  border-radius: .4rem;
  cursor: grab;
  touch-action: none;
  background: var(--art-niche) center / cover no-repeat, var(--ground);
}
.piece-canvas:active { cursor: grabbing; }
.stage-hint { margin: .55rem .2rem 0; font-size: .78rem; color: var(--ink-faint); }
.stage-failed { margin: 2rem 1rem; color: var(--ink-soft); }
.piece-prose h2 { margin-top: 1.6em; }

/* ---------------------------------------------------------- the Play screen
 *
 * A council of war on a sheet of manuscript: the choices down the left, and on
 * the right a framed preview that says back what has been chosen. From the
 * page's own design notes.
 *
 * Two colours belong to this screen and to nothing else -- antique gold for the
 * frames and rules, deep emerald for the primary action and for the rosette
 * that marks a choice. **No state is told apart by colour alone**: a chosen
 * option gains a gold border, a tinted face, a rosette and `aria-pressed`, so
 * the green is the last of four signals rather than the only one.
 *
 * Everything here is scoped to `.play`. The Settings page shares `.settings`,
 * `.setting` and `.choices` with it and is NOT part of this design. */

.play {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.12fr);
  gap: clamp(1.6rem, 4vw, 3.2rem);
  align-items: start;
}

/* The preview follows the choices and the action in the source, so on a narrow
 * screen it falls below them without anything being reordered. */
@media (max-width: 62rem) {
  .play { grid-template-columns: 1fr; }
}

.play h1 { margin: 0 0 .3em; }
.play .lede { margin-bottom: 1.8rem; }

/* A framed panel: a gold rule with a dot pinned at each corner, which is the
 * design's one piece of ornament. The dots are four gradients on a single inset
 * pseudo-element rather than four elements, so nothing is added to the markup
 * for them. */
.framed {
  position: relative;
  border: 1px solid var(--gold);
  background-color: var(--panel-sheer);
  padding: clamp(1.1rem, 2.4vw, 1.7rem);
}

.framed::before {
  content: "";
  position: absolute;
  inset: 6px;
  pointer-events: none;
  background-repeat: no-repeat;
  background-image:
    radial-gradient(circle at 0 0, var(--gold) 0 1.7px, transparent 2px),
    radial-gradient(circle at 100% 0, var(--gold) 0 1.7px, transparent 2px),
    radial-gradient(circle at 0 100%, var(--gold) 0 1.7px, transparent 2px),
    radial-gradient(circle at 100% 100%, var(--gold) 0 1.7px, transparent 2px);
}

/* --- the choices ---------------------------------------------------------- */

/* `.settings` and `.setting` come from the Settings page, where each question
 * is its own little panel. Inside the frame here they are not panels: the frame
 * is the panel, and a panel inside a panel over artwork stops reading as
 * either. */
.play .settings {
  display: block;
  max-width: none;
  margin: 0;
}

.play .setting {
  background: none;
  border: 0;
  border-radius: 0;
  padding: 0;
  box-shadow: none;
}

/* A rule with a diamond on it between one question and the next. */
.play .settings > * + * {
  position: relative;
  margin-top: 1.5rem;
  padding-top: 1.5rem;
}

.play .settings > * + *::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 1px;
  background: linear-gradient(90deg, transparent,
      var(--gold-soft) 10%, var(--gold-soft) 90%, transparent);
}

/* The rosette before a question: a disc with a diamond set in it. Two
 * pseudo-elements, because one cannot be both a circle and a rotated square. */
.play .setting h2 {
  position: relative;
  padding-left: 1.9rem;
  font-size: 1.05rem;
  margin: 0 0 .7rem;
}

.play .setting h2::before {
  content: "";
  position: absolute;
  left: 0;
  top: 50%;
  width: 1.15rem;
  height: 1.15rem;
  transform: translateY(-50%);
  border-radius: 50%;
  background: var(--emerald);
  border: 1px solid var(--gold);
}

.play .setting h2::after {
  content: "";
  position: absolute;
  left: .36rem;
  top: 50%;
  width: .43rem;
  height: .43rem;
  transform: translateY(-50%) rotate(45deg);
  border: 1px solid var(--gold-soft);
}

/* Against the machine, the two questions that only matter then stand side by
 * side, so the page still fits without scrolling once they appear. */
/* **`display` beats the `hidden` attribute**, and this pair is hidden for most
 * of the page's life: without this it laid itself out as a grid in two-player
 * mode and asked which army a game between two people at one screen is played
 * with. */
.play .setting-pair[hidden] { display: none; }

.play .setting-pair {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1.4rem 1.6rem;
}

.play .choices { gap: .5rem; }

.play .choices button {
  position: relative;
  /* Sized to what it says, not stretched to the row: the design has "A bare
   * board" narrower than "Outside the walls", and three equal thirds would read
   * as a segmented control rather than as three answers. */
  flex: 0 1 auto;
  min-width: 0;
  text-align: left;
  padding: .62rem .8rem;
  border: 1px solid var(--gold-soft);
  border-radius: 0;
  background: var(--panel);
  color: var(--ink);
}

.play .choices button:hover { border-color: var(--gold); }

.play .choices button[aria-pressed="true"] {
  border-color: var(--gold);
  background: var(--gold-tint);
  color: var(--ink);
  padding-left: 2.1rem;
  font-weight: 600;
}

.play .choices button[aria-pressed="true"]::before {
  content: "";
  position: absolute;
  left: .62rem;
  top: 50%;
  width: 1.05rem;
  height: 1.05rem;
  transform: translateY(-50%);
  border-radius: 50%;
  background: var(--emerald);
  border: 1px solid var(--gold);
}

.play .choices button[aria-pressed="true"]::after {
  content: "";
  position: absolute;
  left: .95rem;
  top: 50%;
  width: .39rem;
  height: .39rem;
  transform: translateY(-50%) rotate(45deg);
  border: 1px solid var(--gold-soft);
}

/* --- the one thing that ends the choosing --------------------------------- */

.begin-row { margin: 1.6rem 0 0; }

/* An <a>, because the choices go into the address -- so it has to be told it is
 * a control, or it reads as a sentence with a link in it. */
.begin {
  display: inline-flex;
  align-items: center;
  gap: 1rem;
  padding: .85rem 1.5rem;
  background: var(--emerald);
  color: var(--emerald-ink);
  border: 1px solid var(--gold);
  box-shadow: inset 0 0 0 1px rgba(247, 242, 230, .16), var(--shadow);
  font: inherit;
  font-weight: 700;
  font-size: 1.06rem;
  text-decoration: none;
}

.begin:hover { filter: brightness(1.15); }
.begin-arrow { font-size: 1.1em; line-height: 1; }

/* --- the preview ---------------------------------------------------------- */

.preview { align-self: start; }

.preview-title {
  display: flex;
  align-items: center;
  gap: 1.1rem;
  margin: 0 0 1.15rem;
  font-size: .8rem;
  font-weight: 600;
  letter-spacing: .24em;
  text-transform: uppercase;
}

.preview-title::before,
.preview-title::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--gold-soft);
}

/* What has been chosen, in a list that is read as well as looked at: it follows
 * the choices live, which is why it carries `aria-live` rather than being
 * redrawn silently under the painting. */
.preview-lines {
  list-style: none;
  margin: 0 0 1.3rem;
  padding: 0;
  display: grid;
  gap: .6rem;
}

.preview-lines li {
  display: flex;
  align-items: center;
  gap: .85rem;
  font-size: 1.04rem;
}

.rosette {
  position: relative;
  flex: none;
  width: 1.5rem;
  height: 1.5rem;
  border: 1px solid var(--gold);
  border-radius: 50%;
}

.rosette::after {
  content: "";
  position: absolute;
  inset: 0;
  margin: auto;
  width: .5rem;
  height: .5rem;
  transform: rotate(45deg);
  border: 1px solid var(--gold);
}

.preview-lines .is-lead .rosette {
  background: var(--emerald);
  border-color: var(--emerald);
}

.preview-lines .is-lead .rosette::after { border-color: var(--gold-soft); }

/* The painting. It carries no words and nothing in it is the only way to learn
 * anything, so it is decoration and says so -- and it never takes a click. */
.preview-plate {
  aspect-ratio: 1672 / 941;
  background: var(--art-play-preview) center / cover no-repeat;
  border: 1px solid var(--gold-soft);
  pointer-events: none;
}

/* Narrow enough that two answers no longer sit beside each other. */
@media (max-width: 42.5rem) {
  .play .choices { flex-direction: column; align-items: stretch; }
  .play .setting-pair { grid-template-columns: 1fr; }
  .begin { width: 100%; justify-content: center; }
}

/* The board is a document in an iframe, so nothing sizes it from the inside --
 * the holder has to, and it has to fit a 16:9 board (the game exports 1280x720
 * and keeps its aspect) into whatever the shell leaves, with neither dimension
 * running over. A board taller than the window is played by scrolling, which is
 * no way to play; a board that keeps its width and loses its height is played
 * inside two black bars.
 *
 * The height is MEASURED, not guessed. `body` is already a column filling the
 * viewport and `.main` already takes what the masthead and footer leave, so
 * making that main a column too hands the holder the rest. A constant for "how
 * tall the shell's chrome is" was the first attempt: 13rem, and 97px out at
 * 1280x720 alone. It cannot be right at two window sizes at once -- the
 * masthead wraps and main's padding is a clamp, so the real figure ran from
 * 254px on a phone to 305px on a desktop.
 *
 * Then the holder is a size CONTAINER and the frame takes the smaller of the
 * two limits: the width there is, and the width the ratio allows for the height
 * there is. This is the only formulation of the four tried that holds the ratio
 * in BOTH directions -- a max-width or a max-height clamps its own axis and
 * leaves the other where it was, so `height:100%` came out at 1.34 on a tall
 * window and `width:100%` at 4.14 on a short one, both from the same rule.
 *
 * Two things it rests on, each of which silently gives 0 or a stretched box:
 * the holder's flex basis must be `0` and not `0%` (a percentage against a
 * parent of indefinite height resolves back to the content, and then `100cqh`
 * is 0), and the frame's height must be said to be `auto`, or it is stretched
 * by a `height` that the ratio then has nothing left to set. */
.main:has(.board-holder) { display: flex; flex-direction: column; }

.board-holder {
  flex: 1 1 0;
  min-height: 16rem;
  container-type: size;
  display: flex;
  align-items: center;
  justify-content: center;
}

.board-frame {
  display: block;
  width: min(100cqw, calc(100cqh * 16 / 9));
  height: auto;
  aspect-ratio: 16 / 9;
  box-sizing: border-box;
  border: 1px solid var(--edge);
  border-radius: .4rem;
  background: var(--panel);
}

/* The way back out of a game, and the way to fill the screen with it. Under the
 * board and with it. */
.board-back {
  flex: none;
  margin: .9rem 0 0;
  font-size: .9rem;
  text-align: center;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 1.4rem;
}

.board-full {
  font: inherit;
  font-size: .88rem;
  padding: .32rem .7rem;
  border: 1px solid var(--edge);
  border-radius: .25rem;
  background: var(--panel);
  color: var(--ink-soft);
  cursor: pointer;
}

.board-full:hover { border-color: var(--accent); color: var(--ink); }

/* Full screen, the holder is the screen. It is already a size container and
 * already centres the frame, so the frame's own `min(100cqw, 100cqh * 16 / 9)`
 * keeps the board 16:9 and puts the leftover room in black beside it -- which
 * is what a monitor of any other shape needs. */
.board-holder:fullscreen {
  background: #000;
  padding: 0;
}

.board-holder:-webkit-full-screen {
  background: #000;
  padding: 0;
}

/* Filling the page, where the browser has no full screen to give (iPhone): the
 * holder laid over the whole viewport, masthead and footer too. It stays a size
 * container, so the frame is still 16:9 with black beside it; the safe-area
 * padding keeps the board out of the notch and the home bar, and a padded size
 * container measures its content box, so the ratio is taken inside it. */
.board-holder { position: relative; }

.board-holder.is-filling {
  position: fixed;
  inset: 0;
  z-index: 1000;
  min-height: 0;
  box-sizing: border-box;
  padding: env(safe-area-inset-top) env(safe-area-inset-right)
           env(safe-area-inset-bottom) env(safe-area-inset-left);
  background: #000;
}

.board-holder.is-filling .board-frame {
  border: 0;
  border-radius: 0;
}

/* Nothing behind it may scroll while it covers the page. And the page itself goes
 * black: the site's viewport meta has no `viewport-fit=cover` (no other page is
 * laid out for a notch), so in landscape Safari keeps the page clear of the notch
 * and paints the strip beside it in the page's own colour -- which has to be the
 * board's black, not the parchment. The safe-area padding above is then 0, and
 * would only start to count if the site ever opted into the notch. */
html:has(.board-holder.is-filling),
html:has(.board-holder.is-filling) body {
  overflow: hidden;
  background: #000;
}

.board-unfill {
  position: absolute;
  top: calc(env(safe-area-inset-top) + .5rem);
  right: calc(env(safe-area-inset-right) + .5rem);
  z-index: 1;
  font: inherit;
  font-size: .85rem;
  padding: .35rem .8rem;
  border: 1px solid rgba(255, 255, 255, .4);
  border-radius: .25rem;
  background: rgba(0, 0, 0, .6);
  color: #f3eee4;
  cursor: pointer;
}

/* It gains a display above, so it has to keep the hidden attribute's meaning. */
.board-unfill[hidden] { display: none; }
