/* Jigsaw Publisher — generated-site stylesheet (engine v1)
   Loaded by gallerycode/boot.js. Tokens first; per-site accent arrives via
   --site-accent set in each page's inline critical style. */

:root {
  --paper: #faf7f2;
  --surface: #ffffff;
  --ink: #23201c;
  --dim: #6d675e;
  --line: #e7e1d7;
  --accent: var(--site-accent, #3f6f5f);
  --accent-link: var(--accent);
  --accent-ink: #ffffff;
  --accent-soft: color-mix(in srgb, var(--accent) 12%, var(--paper));
  --radius: 10px;
  --radius-sm: 6px;
  --shadow: 0 1px 2px rgba(35, 32, 28, 0.08), 0 6px 18px rgba(35, 32, 28, 0.07);
  --shadow-lift: 0 2px 4px rgba(35, 32, 28, 0.1), 0 12px 28px rgba(35, 32, 28, 0.12);
  --body-font: system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif;
  --display-font: 'Palatino Linotype', Palatino, Georgia, 'Times New Roman', serif;
  --wrap: 1120px;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --paper: #17181b;
    --surface: #1f2126;
    --ink: #ece9e4;
    --dim: #a09a90;
    --line: #2c2f35;
    --accent-soft: color-mix(in srgb, var(--accent) 22%, var(--paper));
    --accent-link: color-mix(in srgb, var(--accent) 62%, white);
    --shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 6px 18px rgba(0, 0, 0, 0.35);
    --shadow-lift: 0 2px 4px rgba(0, 0, 0, 0.5), 0 12px 28px rgba(0, 0, 0, 0.45);
  }
}
:root[data-theme="dark"] {
  --paper: #17181b;
  --surface: #1f2126;
  --ink: #ece9e4;
  --dim: #a09a90;
  --line: #2c2f35;
  --accent-soft: color-mix(in srgb, var(--accent) 22%, var(--paper));
  --accent-link: color-mix(in srgb, var(--accent) 62%, white);
  --shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 6px 18px rgba(0, 0, 0, 0.35);
  --shadow-lift: 0 2px 4px rgba(0, 0, 0, 0.5), 0 12px 28px rgba(0, 0, 0, 0.45);
}

/* ---------- base ---------- */

* { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font: 1rem/1.6 var(--body-font);
}

img { max-width: 100%; display: block; }

a { color: var(--accent-link); text-underline-offset: 2px; }
a:hover { color: var(--ink); }

button {
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
}

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 3px;
}

.wrap {
  max-width: var(--wrap);
  margin: 0 auto;
  padding: 0 20px;
}

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

/* ---------- header ---------- */

.site-header {
  border-bottom: 1px solid var(--line);
  background: var(--surface);
}

.site-header .wrap {
  display: flex;
  align-items: center;
  gap: 18px;
  min-height: 58px;
  flex-wrap: wrap;
  padding-top: 8px;
  padding-bottom: 8px;
}

.brand {
  font-family: var(--display-font);
  font-size: 1.25rem;
  color: var(--ink);
  text-decoration: none;
  margin-right: auto;
}
.brand:hover { color: var(--accent); }

.site-nav {
  display: flex;
  align-items: center;
  gap: 4px;
  flex-wrap: wrap;
}

.site-nav a {
  color: var(--ink);
  text-decoration: none;
  padding: 8px 12px;
  border-radius: var(--radius-sm);
  font-size: 0.95rem;
}
.site-nav a:hover { background: var(--accent-soft); }

.site-nav a.nav-store, .site-nav a.nav-cards {
  color: var(--accent-link);
  border: 1px solid color-mix(in srgb, var(--accent-link) 45%, var(--line));
}
.site-nav a.nav-store:hover, .site-nav a.nav-cards:hover { background: var(--accent); color: var(--accent-ink); }

/* store-pages dropdown (site setting storeMenu; markup absent when unset).
   Native <details>: opens without JS; page.js adds outside-click/Esc close. */
.nav-menu { position: relative; }
.nav-menu summary {
  list-style: none;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 8px 12px;
  border-radius: var(--radius-sm);
  font-size: 0.95rem;
  color: var(--ink);
}
.nav-menu summary::-webkit-details-marker { display: none; }
.nav-menu summary::after {
  content: "";
  width: 7px;
  height: 7px;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(45deg) translate(-1px, -1px);
  transition: transform 0.15s ease;
}
.nav-menu[open] summary::after { transform: rotate(225deg) translate(-2px, 1px); }
.nav-menu summary:hover { background: var(--accent-soft); }
.nav-menu-list {
  position: absolute;
  right: 0;
  top: calc(100% + 6px);
  z-index: 40;
  margin: 0;
  padding: 6px;
  list-style: none;
  min-width: 210px;
  max-width: min(320px, 90vw);
  /* a 15-link menu on a short phone scrolls inside the panel (spec 2026-09-23) */
  max-height: 70vh;
  overflow-y: auto;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lift);
}
.nav-menu-list a {
  display: block;
  padding: 8px 12px;
  border-radius: var(--radius-sm);
  color: var(--ink);
  text-decoration: none;
  font-size: 0.92rem;
}
.nav-menu-list a:hover { background: var(--accent-soft); }
@media (max-width: 640px) {
  /* a wrapped nav can leave the summary anywhere; pin the open panel to the
     viewport edges so it never overflows off-screen */
  .nav-menu-list { position: fixed; left: 12px; right: 12px; top: auto; max-width: none; }
}

.theme-toggle {
  padding: 8px 10px;
  border-radius: var(--radius-sm);
  color: var(--dim);
  line-height: 0;
}
.theme-toggle:hover { background: var(--accent-soft); color: var(--ink); }
.theme-toggle svg { width: 18px; height: 18px; }

/* ---------- amazon widget zone ---------- */

.widget-zone {
  /* reserved: the amzcode card renders after DOMContentLoaded, so the zone
     must already hold its settled height or everything below shifts (CLS).
     Measured content heights: 233px wide, 301px ~480px, 339px ~400px. */
  min-height: 233px;
  padding: 16px 0 4px;
}
@media (max-width: 540px) { .widget-zone { min-height: 301px; } }
@media (max-width: 440px) { .widget-zone { min-height: 339px; } }
.widget-zone:empty { display: none; }
.widget-zone .amzw { margin: 0 auto; }
.widget-zone--below-grid { padding-top: 28px; }
/* header search bar (owner 2026-09-13): a slim band in the website accent
   right under the header, the compact widget inside; a small reservation
   so the page does not jump when it boots (the full card reserves ~300px) */
.widget-zone--bar { min-height: 0; padding: 8px 0 10px; background: var(--accent); }
/* measured live 2026-09-27 at 1280px: card 61 + 6 margin + disclosure 16 =
   83 (the old 74 left a 9px jump). The compact card's own slot is reserved
   too, so the disclosure under it never moves when amzcode boots. */
.widget-zone--bar .wrap { min-height: 83px; }
.widget-zone--bar [data-amzcode-widget] { min-height: 61px; }
@media (max-width: 760px) {
  .widget-zone--bar .wrap { min-height: 178px; }
  .widget-zone--bar [data-amzcode-widget] { min-height: 156px; }
}
.widget-zone--bar .amzw { margin: 0; }
/* the disclosure beside the links (store pages carry the same line at the top) */
.widget-bar-disclosure { margin: 6px 2px 0; font-size: .78rem; line-height: 1.3; color: var(--ink); }
.widget-zone--bar .widget-bar-disclosure { color: var(--accent-ink); opacity: .88; }

/* ---------- phones (owner 2026-09-20) ----------
   On a 402x874 phone the header (brand row + two nav rows), the search band
   (178px reserved) and a ~130-word intro filled ~90% of the first screen; a
   360px phone wrapped the nav to three rows. Phone-only; desktop unchanged. */
.intro-more { display: none; margin: 6px 0 0; padding: 0; border: 0; background: none; color: var(--accent-link); font: inherit; font-size: .92rem; cursor: pointer; text-decoration: underline; }
/* the slot the published page reserves for that button (owner 2026-09-27,
   PageSpeed CLS): off phones it takes no room at all */
.intro-more-slot { display: none; }
@media (max-width: 640px) {
  /* header: the brand and the two icon buttons share one row, the links sit
     below as smaller pills. .site-nav becomes display:contents so its
     children join the header's flex row, ordered brand (0), icons (1), a
     full-width zero-height ::before as a forced line break (2), then the
     links and the store menu (3). Flex-grow alone cannot force the break:
     a short first link ("Home") still fitted beside the icons. */
  .site-header .wrap { min-height: 0; padding: 6px 12px; gap: 0 4px; }
  .site-header .wrap::before { content: ""; flex-basis: 100%; height: 0; order: 2; }
  .site-nav { display: contents; }
  .brand { order: 0; flex: 1 1 auto; font-size: 1.05rem; font-weight: 700; margin-right: 0; }
  .site-nav > .theme-toggle { order: 1; padding: 6px 8px; }
  /* the options cog page.js injects beside them: the same height (30px),
     or the brand row grows 6px after first paint (PageSpeed CLS, 2026-09-27) */
  .site-nav > .nav-options { order: 1; padding: 5px 8px; }
  .site-nav:not(:has(.nav-options))::after { order: 1; width: 36px; height: 30px; }
  .site-nav > a, .site-nav > .nav-menu { order: 3; margin-top: 6px; }
  .site-nav a, .nav-menu summary { font-size: 0.85rem; padding: 5px 8px; }
  /* search band: no category dropdown (amzcode.js guards a missing select
     and searches All), a tighter card, store line and disclosure small, and
     the CLS reservation to match */
  .widget-zone--bar .amzw-select { display: none; }
  .widget-zone--bar .amzw { padding: 10px 12px; }
  /* measured live at 412px (2026-09-27): card 107 + 4 margin + disclosure
     30 = 141. The card's own slot is reserved too, so the disclosure paints
     in its final place instead of jumping down when the widget boots. */
  .widget-zone--bar .wrap { min-height: 141px; }
  .widget-zone--bar [data-amzcode-widget] { min-height: 107px; }
  .widget-zone--bar .amzw--compact .amzw-storeline { font-size: .78rem; margin-top: .3rem; }
  .widget-bar-disclosure { font-size: .72rem; margin-top: 4px; }
  /* intro: four lines until Read more. Keyed on .intro-clamp, which page.js
     adds -- so a page without page.js (cards/) is never clamped. */
  .hero { padding-top: 18px; }
  .hero .intro.intro-clamp:not(.is-open) { display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical; overflow: hidden; }
  .intro-more { display: inline-block; }
  /* the button's slot is always 30px tall (24 + its 6px margin) so showing
     or hiding the button moves nothing below it; hidden = not yet decided */
  .intro-more-slot { display: block; min-height: 30px; }
  .intro-more[hidden] { display: none; }
}

/* ---------- advert insert (optional; loads on first scroll) ---------- */

.ad-insert {
  /* reserved once revealed, not before: [hidden] below overrides this with
     display:none pre-scroll (zero footprint), so the min-height only holds
     the floor open against CLS while the fetched content loads in, after
     the visitor's first scroll unhides the zone */
  min-height: 120px;
  margin: 28px 0 8px;
}
.ad-insert[hidden] { display: none !important; }
.ad-insert:empty { display: none; }

/* ---------- owner text blocks above / below the advert (2026-09-24) ---------- */

/* full content width (owner 2026-09-25: the 72ch cap left the text narrower
   than the grid, widget and advert zone above it) */
.page-text { margin: 28px 0 8px; line-height: 1.55; }
.page-text h2, .page-text h3 { margin: 0 0 .5em; line-height: 1.25; }
.page-text p { margin: 0 0 1em; }
.page-text img { max-width: 100%; height: auto; display: block; margin: 12px 0; border-radius: var(--radius); }
.page-text a { color: var(--accent-link); }

/* ---------- feature picture (spec 2026-09-25): one picture, full content width,
   opening the bottom article block ---------- */

.feature-picture { margin: 28px 0 8px; position: relative; }
/* its action row, injected by page.js (owner 2026-10-06, replacing the
   corner share icon of 2026-09-25): Play this jigsaw, Print, Share -- the
   lightbox's actions for the same picture, always visible under it */
.feature-actions { display: flex; flex-wrap: wrap; justify-content: center; gap: 10px; margin-top: 12px; }
.feature-actions button.btn { font: inherit; cursor: pointer; color: inherit; }
.feature-actions button.btn-primary { color: var(--accent-ink); }
.feature-picture picture, .feature-picture img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--radius);
  box-shadow: var(--shadow);
}
/* centred under the picture, above the action row (owner 2026-10-06) */
.feature-picture figcaption { margin: 10px 0 0; color: var(--dim); font-size: 0.95rem; line-height: 1.4; text-align: center; }

/* ---------- hero ---------- */

.hero {
  padding: 40px 0 8px;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 16px 40px;
  align-items: center;
}

.hero h1 {
  font-family: var(--display-font);
  font-weight: 400;
  font-size: clamp(1.9rem, 4.5vw, 2.9rem);
  line-height: 1.15;
  margin: 0 0 10px;
  letter-spacing: 0.01em;
}

.hero .subhead {
  font-size: 1.15rem;
  color: var(--dim);
  margin: 0 0 14px;
  max-width: 56ch;
}

.hero .intro {
  margin: 0;
  max-width: 62ch;
}

.hero .subhead:empty, .hero .intro:empty { display: none; }

.hero-actions { margin: 14px 0 0; display: flex; flex-wrap: wrap; gap: 10px; }
.hero-actions .btn, .actions .btn { text-decoration: none; }
/* the hero's "Play a random jigsaw" is a <button>: give it the link's type and pointer */
.hero-actions button.btn { font: inherit; cursor: pointer; color: inherit; }
.hero-actions button.btn-primary { color: var(--accent-ink); }

.hero-piece {
  width: 210px;
  height: 210px;
  background-size: cover;
  background-position: center;
  clip-path: url(#jp-piece);
  transform: rotate(6deg);
  filter: drop-shadow(0 8px 14px rgba(35, 32, 28, 0.25));
}

@media (max-width: 760px) {
  .hero { grid-template-columns: 1fr; padding-top: 28px; }
  .hero-piece { display: none; }
}

/* ---------- actions row + saved strip ---------- */

.actions {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  padding: 18px 0 6px;
}

.btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 9px 16px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: var(--surface);
  font-size: 0.95rem;
  box-shadow: var(--shadow);
}
.btn:hover { border-color: var(--accent); color: var(--accent-link); }

.btn-primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
}
.btn-primary:hover { filter: brightness(1.08); color: var(--accent-ink); }

.btn-link { background: none; border: 0; color: var(--accent-link); text-decoration: underline; padding: 8px 4px; cursor: pointer; font: inherit; }

/* Amazon links in the two article blocks (owner 2026-10-05, see
   docs/how-to-add-amazon-to-articles.md): a pill <button class="btn"> that
   reads like the page's other buttons, and a bare <button class="btn-image">
   around an <img> banner. .btn-link above is the text-link form. */
.page-text button.btn { font: inherit; cursor: pointer; color: inherit; }
.page-text button.btn-primary { color: var(--accent-ink); }
.page-text .btn-image { display: block; border: 0; padding: 0; background: none; cursor: pointer; }

.saved-strip {
  padding: 10px 0 2px;
}
.saved-strip[hidden] { display: none; }

.saved-strip h2 {
  font-family: var(--display-font);
  font-weight: 400;
  font-size: 1.2rem;
  margin: 0 0 10px;
}

.saved-list {
  display: flex;
  gap: 12px;
  overflow-x: auto;
  padding-bottom: 8px;
  scrollbar-width: thin;
}

.saved-item {
  flex: 0 0 auto;
  width: 168px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  text-decoration: none;
  color: var(--ink);
  box-shadow: var(--shadow);
}
.saved-item img { width: 100%; aspect-ratio: 3 / 2; object-fit: cover; }
.saved-item .saved-meta {
  display: block;
  padding: 8px 10px 10px;
  font-size: 0.85rem;
}
.saved-item .saved-title {
  display: block;
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.saved-item .saved-state { color: var(--dim); }
/* Carry on coloring (owner 2026-11-08): where the pictures live */
.saved-strip .saved-note { margin: -6px 0 10px; font-size: 0.85rem; color: var(--dim); }
.saved-item:hover { border-color: var(--accent); }

/* game-options gear in the header nav, beside the theme toggle (owner
   placement ruling 2026-09-08). Pages published from 2026-10-25 carry it;
   page.js injects it on older pages, so those reserve its slot (40x36,
   the button's padding plus its 20px icon): a button added after first
   paint widened the nav and, where the row had no slack (PageSpeed's
   desktop run, Linux font widths), the share icon hopped to a new row and
   the page scored CLS 0.59. Browsers without :has() drop the rule and
   behave as before. */
.site-nav:not(:has(.nav-options))::after { content: ""; width: 40px; height: 36px; }
.nav-options {
  padding: 8px 10px;
  border-radius: var(--radius-sm);
  color: var(--dim);
  line-height: 0;
  cursor: pointer;
  background: none;
  border: 0;
}
.nav-options svg { width: 20px; height: 20px; }
.nav-options:hover { color: var(--ink); background: var(--accent-soft); }

/* remove button added per card by page.js (owner feature 2026-09-07) */
.saved-item { position: relative; }
.saved-remove {
  position: absolute;
  top: 6px;
  right: 6px;
  width: 26px;
  height: 26px;
  padding: 0;
  font: 700 1rem/1 var(--body-font);
  cursor: pointer;
  color: var(--dim);
  background: color-mix(in srgb, var(--surface) 82%, transparent);
  border: 1px solid var(--line);
  border-radius: 50%;
}
.saved-remove:hover { color: #c4483f; border-color: #c4483f; background: var(--surface); }

/* ---------- card grid ---------- */

.gallery-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(235px, 1fr));
  gap: 20px;
  padding: 22px 0 8px;
  list-style: none;
  margin: 0;
}

.card {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  box-shadow: var(--shadow);
}
.card:hover { box-shadow: var(--shadow-lift); border-color: color-mix(in srgb, var(--accent) 40%, var(--line)); }

.card-play {
  display: block;
  width: 100%;
  text-align: left;
}
/* spec 2026-09-13 §3: a cards folder's tile is an <a>, not a <button> -- the
   global `button {...}` reset (border/background/padding/font/cursor) does
   not apply to it, but an <a> has none of those by default either; only the
   link color + underline need overriding to match the button variant. */
a.card-play.card-make { text-decoration: none; color: inherit; }
a.card-play.card-make:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.card-pic {
  position: relative;
  display: block;
}
.card-pic img { width: 100%; height: auto; }
.card-pic::after {
  /* box-lid inner frame */
  content: "";
  position: absolute;
  inset: 8px;
  border: 1px solid rgba(255, 255, 255, 0.55);
  border-radius: 4px;
  pointer-events: none;
}

.card-play .play-hint {
  position: absolute;
  left: 50%;
  top: 50%; /* owner 2026-11-08: dead centre on the picture (42% looked high) */
  transform: translate(-50%, -50%);
  background: var(--accent);
  color: var(--accent-ink);
  border-radius: 999px;
  padding: 8px 18px;
  font-size: 0.95rem;
  /* left: 50% leaves the pill only half the tile to shrink into, so a
     two-word label wrapped ("Color this / picture"); keep it on one line */
  white-space: nowrap;
  opacity: 0;
  transition: opacity 0.15s ease;
  pointer-events: none;
}
.card-play:hover .play-hint,
.card-play:focus-visible .play-hint { opacity: 1; }

.card-body {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 11px 14px 12px;
}

.card-title {
  font-weight: 600;
  font-size: 0.98rem;
  flex: 1;
  min-width: 0;
}

.card-state {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 0.8rem;
  color: var(--dim);
}
.card-state:empty { display: none; }
.card-state .piece-ico { width: 14px; height: 14px; }
.card-state.is-done { color: var(--accent-link); font-weight: 600; }
.card-state.is-live::before {
  content: "";
  width: 8px;
  height: 8px;
  border-radius: 50%;
  border: 2px solid var(--accent);
}

.card-view {
  position: absolute;
  top: 10px;
  right: 10px;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: rgba(20, 18, 16, 0.45);
  color: #fff;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  transition: opacity 0.15s ease;
}
.card:hover .card-view,
.card-view:focus-visible { opacity: 1; }
.card-view svg { width: 16px; height: 16px; }
/* owner item 12: the share control page.js injects beside the magnifier */
.card-share { right: 50px; }
/* spec 2026-09-13 §3: the card/puzzle tool control page.js injects beside share */
.card-tool { right: 90px; }
.card-tool-puzzle svg { width: 15px; height: 15px; }
/* no hover on touch devices: the magnifier must be visible to be findable
   (owner question 2026-09-08 -- at opacity 0 it was an invisible tap trap
   in the picture's corner); also a roomier tap target */
@media (hover: none), (pointer: coarse) {
  .card-view { opacity: 1; width: 38px; height: 38px; }
  .card-view svg { width: 18px; height: 18px; }
  .card-tool-puzzle svg { width: 17px; height: 17px; }
  .card-share { right: 54px; }
  .card-tool { right: 98px; }
}

.card--own .card-play {
  min-height: 200px;
  height: 100%;
  border: 2px dashed var(--line);
  border-radius: var(--radius);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  color: var(--dim);
  text-align: center;
  padding: 20px;
}
.card--own { border: 0; background: none; box-shadow: none; }
/* the mid-grid article block (owner 2026-10-14): a full-width row of the
   grid after the first eight cards, no card chrome, the block's own spacing */
.card--text { grid-column: 1 / -1; border: 0; background: none; box-shadow: none; }
/* owner 2026-11-08: the block is a .card only for the grid; it must not
   inherit the picture card's clipping (an SSI banner's dropdown menus were
   cut off at the block's edge) or its hover shadow (a grey box appeared
   around the whole banner on hover) */
.card--text, .card--text:hover { overflow: visible; box-shadow: none; }
.card--text .page-text { margin: 6px 0; }
.card--own .card-play:hover { border-color: var(--accent); color: var(--accent-link); }
.card--own .piece-ico { width: 34px; height: 34px; }

/* ---------- lightbox ---------- */

.lightbox {
  position: fixed;
  inset: 0;
  z-index: 900;
  background: rgba(12, 11, 10, 0.88);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 20px;
}
.lightbox[hidden] { display: none; }

.lightbox img {
  max-width: min(94vw, 1400px);
  max-height: 78vh;
  width: auto;
  height: auto;
  border-radius: 6px;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.6);
}

.lightbox .lb-caption {
  color: #e8e4dd;
  padding: 14px 0 0;
  font-size: 0.95rem;
}

/* four actions since round 2 (Play / Print / Share / Make a card): on a phone the row wraps whole buttons instead of breaking their labels */
.lightbox .lb-bar {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 10px;
  padding-top: 14px;
}

.lightbox .lb-btn {
  color: #e8e4dd;
  border: 1px solid rgba(255, 255, 255, 0.35);
  border-radius: 999px;
  padding: 8px 16px;
  font-size: 0.9rem;
}
.lightbox .lb-btn:hover { background: rgba(255, 255, 255, 0.12); }

.lightbox .lb-close {
  position: absolute;
  top: 16px;
  right: 16px;
  width: 42px;
  height: 42px;
  border-radius: 50%;
  color: #fff;
  background: rgba(255, 255, 255, 0.12);
  font-size: 1.3rem;
  line-height: 1;
}

.lightbox .lb-nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 46px;
  height: 46px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.12);
  color: #fff;
  font-size: 1.4rem;
}
.lightbox .lb-prev { left: 14px; }
.lightbox .lb-next { right: 14px; }

/* ---------- landing page extras ---------- */

.filter-row {
  padding: 16px 0 0;
}
.filter-row input {
  width: min(420px, 100%);
  padding: 10px 16px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--ink);
  font: inherit;
}
.filter-row input::placeholder { color: var(--dim); }

.card--gallery-link {
  display: block;
  text-decoration: none;
  color: inherit;
}
/* owner 2026-10-11: the title runs the card's full width with the count
   under it (storybook titles run long); the related grid stacked already */
.card--gallery-link .card-body { flex-direction: column; align-items: flex-start; gap: 2px; }
.card--gallery-link .card-meta {
  color: var(--dim);
  font-size: 0.85rem;
}

/* Related galleries (spec 2026-09-25): up to seven small landing-style
   cards linking to other gallery pages, the last block above the footer.
   Seven across in the 1080px wrap (140px min + 14px gap = 7 columns), two
   on a phone (the wrap is ~305px at 360px, so the minimum must leave room
   for two plus the gap). Keep the grid in step with pages.RELATED_SIZES
   and pages.RELATED_MAX if it changes. */
.related-galleries {
  margin: 36px 0 8px;
  padding-top: 24px;
  border-top: 1px solid var(--line);
}
.related-galleries h2 {
  font-family: var(--display-font);
  font-weight: 400;
  font-size: 1.5rem;
  line-height: 1.2;
  margin: 0 0 14px;
}
.related-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: 14px;
  list-style: none;
  margin: 0;
  padding: 0;
}
.related-grid .card-body { padding: 8px 10px 9px; flex-direction: column; align-items: flex-start; gap: 2px; }
.related-grid .card-title { font-size: 0.92rem; line-height: 1.25; }
.related-grid .card-pic::after { inset: 6px; }

/* Landing pagination (owner 2026-09-17): centred under the grid with its
   own rule, the forward link the accent pill so it reads as the next step
   rather than a footnote. */
.pagination {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 14px 24px;
  margin-top: 30px;
  padding: 26px 0 10px;
  border-top: 1px solid var(--line);
}
.pagination .page-note { color: var(--dim); font-size: 0.95rem; }
.pagination .btn-pager {
  padding: 12px 24px;
  font-size: 1.05rem;
  font-weight: 600;
  text-decoration: none;
  transition: transform .15s ease, box-shadow .15s ease;
}
.pagination .btn-pager:hover { transform: translateY(-1px); }
.pagination .btn-primary { box-shadow: 0 8px 22px rgba(0, 0, 0, .18); }
.pagination .btn-primary:hover { box-shadow: 0 10px 26px rgba(0, 0, 0, .24); }

.no-matches {
  color: var(--dim);
  padding: 30px 0;
}
.no-matches[hidden] { display: none; }

/* ---------- footer ---------- */

.site-footer {
  margin-top: 48px;
  border-top: 1px solid var(--line);
  background: var(--surface);
  color: var(--dim);
  font-size: 0.9rem;
}
.site-footer .wrap {
  padding-top: 22px;
  padding-bottom: 26px;
  display: flex;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}
.site-footer a { color: var(--dim); }
.site-footer a:hover { color: var(--accent-link); }
.site-footer .foot-links { display: flex; gap: 16px; flex-wrap: wrap; }
.site-footer .disclosure { flex-basis: 100%; font-size: 0.85rem; }

/* ---------- stub game notice (replaced by the real engine bundle) ---------- */

.jp-stub {
  position: fixed;
  inset: 0;
  z-index: 1000;
  background: rgba(12, 11, 10, 0.7);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
}
.jp-stub .jp-stub-panel {
  background: var(--surface);
  color: var(--ink);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lift);
  max-width: 380px;
  padding: 26px 28px;
  text-align: center;
}
.jp-stub h2 { font-family: var(--display-font); font-weight: 400; margin: 0 0 8px; }
.jp-stub p { margin: 0 0 16px; color: var(--dim); }

/* ---------- print ---------- */

@media print {
  .site-header, .site-footer, .widget-zone, .ad-insert, .actions, .saved-strip,
  .card-view, .filter-row, .pagination, .related-galleries { display: none !important; }
}
