/* Griffons Den — members app
   Palette and fonts taken from griffonsden.com; see docs/brand.md.
   Mobile-first: base styles target a ~380px phone, min-width queries widen from there. */

:root {
  --gold:      #d2ac2a;
  --gold-dim:  #a8871d;
  --gold-soft: #e6c95f;      /* headings on very dark ground */
  --red-deep:  #741112;
  --red-dark:  #720001;
  --crimson:   #d60039;
  --black:     #000000;
  --cream:     #faf6f5;
  --white:     #ffffff;
  --teal:      #82c0c7;

  /* v0.8 §3.14: dark scheme matching griffonsden.com. The page ground is near
     black, section bands are dark red, headings and links are gold. Everything
     below is checked against WCAG AA on the ground it actually sits on. */
  /* v0.9 §3.14: the ground is the site's own maroon, taken from the live CSS
     (#720001 on the home sections), with the griffon emblem as a faint fixed
     watermark behind the content, as the site does on its inner pages. */
  /* v1.10 §3.14.10: the base is near-black, the old purerawdnd.com page's #0a0a0a; Admin > Pages >
     Site-wide text (Site colours) sets it, through /site-colours.css. */
  --ground:    #0a0a0a;      /* page background */
  --ground-dk: #0a0a0a;
  --over-top:  #d2ac2a;      /* pulling past the top: the top bar's gold */
  --over-bottom: #0a0a0a;    /* pulling past the bottom */
  --panel:     rgba(0,0,0,.30);   /* cards and tables: the ground, sunk */
  --panel-2:   rgba(0,0,0,.44);   /* inputs, raised rows */
  --band:      #4a0708;
  --topbar:    #d2ac2a;      /* the site's gold top bar */
  --topbar-ink:#741112;      /* its dark red text */
  --ink:       #f2ece6;      /* body text on the maroon — 10.4:1 */
  --muted:     #d8c9bf;      /* secondary text — 7.6:1 */
  --rule:      rgba(210,172,42,.28);
  --sunk:      rgba(0,0,0,.22);

  --display: "Metal Mania", "Papyrus", fantasy;
  --serif:   "Marcellus SC", Georgia, "Times New Roman", serif;
  --body:    "Montserrat", Helvetica, Arial, "Lucida Grande", sans-serif;

  --tap: 44px;                 /* minimum tap target */
  --main-top: 20px;            /* main's top padding; heroes cancel it exactly */
  --pad: 16px;                 /* page gutter on a phone */
  --content: 1080px;           /* the width of the centred content column */
  /* v0.11 §3.14: a hero runs edge to edge, but its words keep the body's own
     side margin. This is that margin measured from the viewport, so it lines up
     with the content column on desktop and falls back to the gutter on a phone. */
  --hero-pad: max(var(--pad), calc(50vw - var(--content) / 2 + var(--pad)));
  --radius: 10px;              /* the site rounds its content rows at 10px */
  /* One spacing scale, the 4px step the site already used, now named (v0.72). */
  --s1: 4px; --s2: 8px; --s3: 12px; --s4: 16px; --s5: 24px; --s6: 32px;
}

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; overflow-x: clip; background: linear-gradient(var(--over-top) 50%, var(--over-bottom) 50%) fixed var(--over-bottom); }

body {
  margin: 0;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  font-family: var(--body);
  font-size: 16px;             /* the site uses 14px; 16px avoids iOS zoom-on-focus */
  line-height: 1.6;
  color: var(--ink);
  background: var(--ground);
  overflow-wrap: break-word;
}

/* The watermark: the griffon-and-sword emblem, large and faint, fixed behind
   the page exactly as the site sets it — cover, centred at the top, no repeat,
   under a dark wash so body text keeps its contrast. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  /* v1.07: nothing here; the watermark is the page's own band (.pageband > .bandbg). */
  display: none;
  pointer-events: none;
}

h1, h2, h3, h4, h5, h6 { line-height: 1.15; margin: 0 0 .5em; }
h1 { font-family: var(--display); font-weight: 400; font-size: 2rem; color: var(--gold); letter-spacing: .01em; }
h2 { font-family: var(--display); font-weight: 400; font-size: 1.5rem; color: var(--gold); }
/* Metal Mania stops being legible at sub-heading sizes — see docs/brand.md */
h3 { font-family: var(--serif); font-size: 1.05rem; color: var(--gold-soft); }
h4 { font-family: var(--serif); font-size: .95rem; color: var(--ink); }

p { margin: 0 0 1em; }

a { color: var(--gold); text-decoration-thickness: 1px; text-underline-offset: 2px; }
a:hover { color: var(--gold-soft); }

small { font-size: .8125rem; }
code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .8125em; background: var(--sunk); color: var(--ink); padding: .1em .35em; border-radius: 4px; }
/* One rule for every image on the site (v0.73): it keeps its own aspect ratio.
   Where both dimensions are fixed (a framed image) it fills the frame and is
   centre-cropped, never scaled unevenly; where a dimension is auto the box takes
   the image's own shape. Set here once, so any new image inherits it and nobody
   has to remember object-fit per element. */
img { max-width: 100%; height: auto; object-fit: cover; object-position: center; }
hr { border: 0; border-top: 1px solid var(--rule); margin: 2rem 0; }

/* ---------- top bar, header and menu (v0.9 §3.14) ------------------------
   Reproduces griffonsden.com's chrome: a full-width gold bar carrying the phone
   number and email in the site's dark red, then a black header with the square
   emblem on the left and a menu button on the right. Unlike the site, the button
   is present at every width and there is no horizontal row of links. */

.topbar {
  background: var(--topbar);
  color: var(--topbar-ink);
  font-size: .875rem;
  padding: 6px var(--pad);
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: 2px 22px;
  text-align: center;
}
.topbar a {
  color: var(--topbar-ink);
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 30px;
}
.topbar a:hover { color: #4a0708; text-decoration: underline; }
.topbar svg { width: 14px; height: 14px; fill: currentColor; flex: none; }

.masthead {
  background: #000;
  padding: 10px var(--pad);
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0 12px;
  position: relative;
}
.wordmark {
  display: flex;
  align-items: center;
  gap: 12px;
  text-decoration: none;
  color: var(--gold);
  font-family: var(--display);
  font-size: 1.25rem;
  line-height: 1;
  min-height: var(--tap);
}
.wordmark img { width: 56px; height: 56px; display: block; }
/* The emblem already carries the name, so the word is for screen readers and
   wide screens only. */
.wordmark span { display: none; }

.menu-toggle {
  background: none;
  border: 0;
  color: var(--gold);
  min-height: var(--tap);
  min-width: var(--tap);
  padding: 0 8px;
  display: inline-flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  gap: 5px;
  cursor: pointer;
  border-radius: 6px;
}
.menu-toggle:hover { background: rgba(210,172,42,.14); }
.menu-toggle span {
  display: block;
  width: 26px;
  height: 2px;
  background: currentColor;
  border-radius: 2px;
}
.menu-toggle .label {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap;
}

/* The panel. Hidden by default; opened by the checkbox that precedes it, so it
   works with no JavaScript at all. */
/* v1.11: fixed to the screen, so opening the menu mid-page never scrolls to the page's top to reach it. */
.menu-state { position: fixed; top: 0; left: 0; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.menu-panel {
  display: none;
  background: #0b0b0b;
  border-top: 1px solid rgba(210,172,42,.35);
  /* It sits inside the header now, so it wraps onto its own line and pulls back
     the header's own padding to stay full width — the same panel as before. */
  width: calc(100% + var(--pad) * 2);
  margin: 10px calc(var(--pad) * -1) -10px;
}
.menu-state:checked ~ .masthead .menu-panel { display: block; }
/* v1.11 §3.14.11: the black menu bar stays at the top of the screen while the page scrolls; the gold top bar
   above it scrolls away. Sticky, not fixed, so it keeps its own place and never covers the page head on load.
   Open on a phone, the panel scrolls within the screen below the bar. Anchors land below the bar:
   public/menu.js keeps --mast-h the bar's height. */
.masthead { position: sticky; top: 0; z-index: 60; }
.menu-state:checked ~ .masthead .menu-panel { max-height: calc(100vh - var(--mast-h, 76px)); overflow-y: auto; }
html { scroll-padding-top: calc(var(--mast-h, 76px) + 8px); }

/* ---------- section headings, the site's pattern -------------------------
   A small gold letter-spaced eyebrow over a white Montserrat headline whose
   last word is bold — "GRIFFONS DEN / WEEKLY **SCHEDULE**". */

.eyebrow {
  font-family: var(--serif);
  font-size: .8125rem;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--gold);
  margin: 0 0 .35em;
}
.headline {
  font-family: var(--body);
  font-weight: 300;
  text-transform: uppercase;
  color: #fff;
  letter-spacing: .01em;
  line-height: 1.05;
  font-size: clamp(1.9rem, 8vw, 3rem);
  margin: 0 0 .6em;
}
.headline b { font-weight: 800; display: block; }

/* The site's divider: a gold rule with a diamond at each end. */
.rule {
  border: 0;
  height: 1px;
  background: linear-gradient(90deg, transparent, rgba(210,172,42,.55) 12%, rgba(210,172,42,.55) 88%, transparent);
  margin: 30px 0;
  position: relative;
  overflow: visible;
}
.rule::before, .rule::after {
  content: "";
  position: absolute;
  top: 50%;
  width: 9px;
  height: 9px;
  background: var(--gold);
  transform: translateY(-50%) rotate(45deg);
}
.rule::before { left: 0; }
.rule::after { right: 0; }

/* ---------- hero and photographs (v0.9 §3.14) ----------------------------
   The hero is a full-bleed image with the title over it — no border, no frame,
   no box. Content sections sit directly on the maroon ground. */

.hero {
  position: relative;
  margin: 0 0 26px;
  padding: 54px var(--hero-pad) 46px;
  background-color: var(--ground-dk);
  background-image: url("/img/den-table-800.jpg");
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  border: 0;
}
.hero::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(12,2,2,.86) 0%, rgba(12,2,2,.76) 45%, rgba(12,2,2,.90) 100%);
}
.hero > * { position: relative; }
/* Over a photograph the eyebrow is small gold text, so it takes the lighter
   gold to stay above AA rather than only AA-large. Measured in docs/brand.md. */
.hero .eyebrow, .pagehead .eyebrow { color: var(--gold-soft); }
/* Only the home hero uses the fantasy display face, and only for its title. */
.hero .hero-title {
  font-family: var(--display);
  font-weight: 400;
  text-transform: none;
  color: var(--gold);
  font-size: clamp(2.1rem, 9vw, 4rem);
  line-height: 1.05;
  margin: 0 0 .35em;
  letter-spacing: .01em;
}
/* No character cap (ruled 2026-09-07): the subtitle sits on one line at desktop width. */
.hero .lede { color: var(--ink); margin-bottom: 0; }

/* A photograph behind a page heading, same treatment, shallower. */
.pagehead {
  position: relative;
  margin: 0 0 26px;
  padding: 40px var(--hero-pad) 34px;
  background-color: var(--ground-dk);
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  border: 0;
}
.pagehead::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(12,2,2,.86) 0%, rgba(12,2,2,.78) 45%, rgba(12,2,2,.90) 100%);
}
.pagehead > * { position: relative; }
.pagehead .lede { color: var(--ink); margin-bottom: 0; }

.band-table { background-image: url("/img/den-table-800.jpg"); }
.band-shelves { background-image: url("/img/den-shelves-800.jpg"); }
.band-room  { background-image: url("/img/den-room-800.jpg"); }

/* v0.11 §3.14: edge to edge at every width, not merely to the edge of the
   centred column. The side margins pull the band out of main to the viewport's
   own edges; --hero-pad puts the words back on the content margin inside it. */
.hero, .pagehead, .bleed {
  margin-left: calc(50% - 50vw);
  margin-right: calc(50% - 50vw);
}
/* …but the words inside a bled band keep the body's own side margin (§ fix 6). */
.bleed > * { padding-left: 0; padding-right: 0; }

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

main {
  max-width: 1080px;
  width: 100%;
  margin: 0 auto;
  padding: var(--main-top) var(--pad) 48px;
  flex: 1 0 auto;
}
/* A full-bleed hero or page head starts flush against the header — no dead
   strip of ground between the two. It pulls back exactly the padding main
   applies, so the two can never drift apart. */
main > .hero:first-child,
main > section:first-child > .hero:first-child,
main > section:first-child > .pagehead:first-child,
main > .pagehead:first-child,
main > .denhero:first-child { margin-top: calc(var(--main-top) * -1); }

section { margin: 0 0 32px; }
/* v1.09 §3.39h: the page's last section keeps no margin below it. Since v1.07 the page default's picture
   box (.bandbg) is main's last child, so section:last-child alone no longer caught the page's section. */
section:last-child, main > section:has(+ .bandbg:last-child) { margin-bottom: 0; }
/* A page that ends in a band runs straight into the footer: no padding of main's below it. */
main:has(> .bgband:nth-last-child(2)),
main:has(> section:nth-last-child(2) > .bgband:last-child),
main:has(> section:nth-last-child(2) > .portal-layout:last-child > .portal-body:last-child > .bgband:last-child) { padding-bottom: 0; }

.narrow, .prose { max-width: 62ch; }
.lede { font-size: 1.05rem; color: var(--muted); }
.fine { font-size: .8125rem; color: var(--muted); }
.opt { font-size: .8125rem; color: var(--muted); font-weight: 400; }
.crumb { font-size: .875rem; margin-bottom: .5em; }
.empty { color: var(--muted); font-style: italic; }

.prose h2 { margin-top: 1.5em; }
.prose ul, .prose ol { padding-left: 1.25em; }
.prose li { margin-bottom: .5em; }

/* ---------- the contract pages ------------------------------------------
   /terms and /gm-agreement are rendered from the owner's Markdown in docs/.
   The document supplies the structure; this only makes it read as a contract:
   numbered sections that are easy to find, and inline code for the
   [PLACEHOLDER]s that are still to be filled in. */

.banner {
  background: var(--gold);
  color: var(--red-deep);
  font-family: var(--serif);
  border-radius: var(--radius);
  padding: 12px 16px;
  margin: 0 0 24px;
}
.contract { max-width: 68ch; }
/* Each numbered section is a landing point, so give it room and the gold of a
   heading — but in the serif, not the display face: seventeen sections of
   Metal Mania reads as a poster rather than a contract. */
.contract h2 {
  font-family: var(--serif);
  font-weight: 400;
  color: var(--gold);
  font-size: 1.3rem;
  letter-spacing: .02em;
  margin: 2em 0 .5em;
}
.contract h3 {
  font-family: var(--serif);
  color: var(--gold-soft);
  font-size: 1.05rem;
  letter-spacing: .02em;
  margin: 1.6em 0 .4em;
}
.contract h2:first-of-type, .contract h3:first-of-type { margin-top: 1em; }
.contract p { margin: 0 0 .9em; }
.contract hr { border: 0; border-top: 1px solid var(--rule); margin: 2em 0; }
/* A placeholder still to be settled: it should look unfinished on the page,
   because it is. */
.contract code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: .9em;
  background: var(--sunk);
  border: 1px dashed var(--rule);
  border-radius: 4px;
  padding: 1px 5px;
  overflow-wrap: anywhere;
}
.contract blockquote {
  margin: 1em 0;
  padding-left: 14px;
  border-left: 3px solid var(--rule);
  color: var(--muted);
}
/* Tables come through the app's own .grid treatment — labelled cards on a
   phone, a real table above 820px — so nothing has to scroll sideways. A table
   with no header row is a list of label/value pairs, not a grid of data, so its
   first column reads as the label instead. */
.md-table { margin: 1.2em 0; }
.md-table.is-keyvalue td:first-child {
  font-family: var(--serif);
  color: var(--gold-soft);
}
.md-table.is-keyvalue td { display: block; padding: 4px 0; }

/* Stacked by default; side by side once there is room. */
.cols, .split { display: grid; gap: 20px; }
.row { display: grid; gap: 12px; }

/* ---------- hero and pricing -------------------------------------------- */

.schedule-head { margin-bottom: 20px; }
.schedule-head h1 { margin-bottom: .2em; }
.schedule-head .lede { margin-bottom: 0; }



.tables h2, .pricing h2 { font-size: 1.6rem; }

.ladder { list-style: none; margin: 0 0 1em; padding: 0; display: grid; gap: 10px; }
.ladder li {
  background: var(--panel);
  border: 1px solid var(--rule);
  border-left: 4px solid var(--gold);
  border-radius: var(--radius);
  padding: 12px 14px;
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: baseline;
  gap: 0 10px;
}
.ladder .when { font-family: var(--serif); color: var(--gold-soft); }
.ladder .amount { font-size: 1.25rem; font-weight: 700; white-space: nowrap; }
.ladder .amount small { font-size: .8125rem; font-weight: 400; color: var(--muted); margin-left: 4px; }
.ladder .per { grid-column: 1 / -1; font-size: .8125rem; color: var(--muted); }

/* ---------- Game Masters (spec §3.12) ------------------------------------ */

.gm-list { display: grid; gap: 18px; }
.gm-card {
  background: var(--panel);
  border: 1px solid var(--rule);
  border-left: 4px solid var(--gold);
  border-radius: var(--radius);
  padding: 16px;
  display: grid;
  gap: 14px;
}
.gm-card h2 { margin-bottom: .2em; }
.gm-card h3 { margin-top: .8em; }
.gm-inline { margin-top: 20px; }
.gm-systems { color: var(--muted); font-family: var(--serif); font-size: .9rem; margin-bottom: .6em; }
.gm-links a { margin-right: .2em; }

.gm-photo {
  width: 112px;
  height: 112px;
  border-radius: 50%;
  border: 2px solid var(--gold);
  display: block;
}
.gm-photo-empty {
  background: var(--panel-2) url("/griffons-den-logo-240.png") center / 60% no-repeat;
  opacity: .5;
}
.gm-photo-edit { display: grid; gap: 14px; align-items: start; }
/* Admin → Game Masters: a thumbnail in the list; the edit page's two columns. */
.gm-link { color: inherit; text-decoration: underline; text-decoration-color: var(--rule); text-underline-offset: 2px; }
.gm-link:hover { color: var(--gold); }
.products { display: grid; gap: 28px; margin: 24px 0 36px; }
/* v1.15 §3.6.2: with "Coming up" gone the products end /one-shots; the page's last block keeps no margin (§3.39h). */
.products:last-child { margin-bottom: 0; }
/* The picture floats beside the text and the text takes the full width once it passes the
   picture's bottom (ruled 2026-09-16, v0.51); on a phone the picture sits above the text. */
/* The hidden attribute always wins, whatever display a label or row would otherwise get (v0.51). */
[hidden] { display: none !important; }
.product::after { content: ""; display: block; clear: both; }
.product-image { display: block; width: 100%; max-width: 360px; margin: 0 0 16px; border-radius: var(--radius); border: 1px solid var(--rule); }
@media (min-width: 700px) { .product-image { float: left; width: 300px; margin: 4px 28px 12px 0; } }
.private .times { border: 1px solid var(--rule); border-radius: var(--radius); padding: 10px 14px; display: grid; gap: 6px; }
.private .times legend { font-family: var(--serif); color: var(--gold-soft); padding: 0 6px; }
/* Admin → Game Masters: one table per line, its Edit beside it (v0.50). */
.tablelist { margin: 0; }
.grid .tablelist li, .tablelist li { padding: 2px 0; border: 0; white-space: nowrap; }
/* The Rogues Gallery: what a Game Master is available to run (v0.57). */
/* "The site has been updated" (v0.58, §3.23): bottom-left, above a phone's
   thumb reach, never across the middle of what someone is reading. */
/* Admin → Log (v0.59): filters on one line, the change list compact in its cell. */
.revertcell { white-space: nowrap; }
.revertcell .fine { opacity: .65; cursor: help; }
.logfilters { display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-end; margin: 12px 0 18px; }
.logfilters label { margin: 0; }
.grid .changelist, .changelist { margin: 0; }
.grid .changelist li, .changelist li { padding: 0; border: 0; line-height: 1.45; }
/* The log's rows stay one line tall until the detail needs more: the moment and
   the account never wrap, and a long detail wraps inside its own cell. */
.grid td.night { white-space: nowrap; }
.logtable td { vertical-align: top; }
.logtable td[data-label="What changed"] { max-width: 46ch; }
.logpager { justify-content: space-between; }
.agreement-banner { display: flex; gap: 12px; align-items: baseline; justify-content: space-between; flex-wrap: wrap; }
.agreement-banner form { margin: 0; }
.update-notice { position: fixed; left: 16px; bottom: 16px; z-index: 60; display: flex; gap: 10px; align-items: center;
  max-width: calc(100vw - 32px); padding: 10px 14px; border-radius: var(--radius); border: 1px solid var(--gold-soft);
  background: #1b0505; color: var(--cream, #f4ece0); box-shadow: 0 6px 24px rgba(0,0,0,.45); font-size: .9375rem; }
.update-notice button { min-height: 0; padding: 4px 10px; font-size: .875rem; }
.update-notice .update-dismiss { background: none; border: 0; color: var(--muted); text-decoration: underline; padding: 4px 2px; }
@media (max-width: 480px) { .update-notice { left: 12px; right: 12px; bottom: 12px; flex-wrap: wrap; } }
.gm-runs { color: var(--gold-soft); font-size: .9375rem; margin: 6px 0 0; }
.gm-thumb { width: 36px; height: 36px; border-radius: 50%; vertical-align: middle; margin-right: 8px; }
.gm-admin { align-items: start; }
@media (min-width: 900px) { .gm-admin { grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr); } }
.gm-link-row { align-items: end; }

@media (min-width: 600px) {
  .gm-card { grid-template-columns: 112px minmax(0, 1fr); align-items: start; }
  .gm-photo-edit { grid-template-columns: 112px minmax(0, 1fr); }
  .gm-link-row { grid-template-columns: 1fr 2fr; }
  /* The campaign masthead composes at tablet width and up: hero beside the
     heading, centred against it (v0.72). Below 600px they stack. */
  .campmast.has-hero { display: grid; grid-template-columns: 190px minmax(0, 1fr); gap: var(--s5); align-items: center; }
  .campmast.has-hero .campaign-hero { margin: 0; }
}

/* ---------- seasons and one-shots ---------------------------------------- */

.offers { list-style: none; margin: 0 0 1em; padding: 0; display: grid; gap: 14px; }
.offers li {
  background: var(--panel);
  border: 1px solid var(--rule);
  border-left: 4px solid var(--teal);
  border-radius: var(--radius);
  padding: 14px 16px;
}
.offers h3 { margin-bottom: .25em; }
.offers .meta { font-size: .875rem; color: var(--muted); margin-bottom: .5em; }
.offers .offer-price { font-weight: 700; margin-bottom: .75em; }
.offers .offer-price small { font-weight: 400; color: var(--muted); font-family: var(--serif); margin-left: .4em; }

/* A pending season (§3.5): it is advertised and takes a waiting list, but has
   not reached the number it needs to start. A soft white-blue glow marks it out
   from the confirmed cards. Static by rule — box-shadow only, no animation, so
   it also costs nothing to reduced-motion readers. */
.offers li.is-pending {
  border-left-color: #cfe6ff;
  box-shadow: 0 0 0 1px rgba(207, 230, 255, .55), 0 0 18px 4px rgba(207, 230, 255, .30);
}
.offers li.is-pending .pending-note {
  font-family: var(--serif);
  font-size: .9rem;
  color: #dfecff;
  margin: -.4em 0 .75em;
}

/* ---------- buttons and forms ------------------------------------------- */

.button, button, input[type="submit"] {
  font-family: var(--serif);
  font-size: 1rem;
  letter-spacing: .04em;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--tap);
  padding: 10px 20px;
  background: var(--red-deep);
  color: var(--gold);
  border: 2px solid var(--gold);
  border-radius: var(--radius);
  text-decoration: none;
  cursor: pointer;
  text-align: center;
}
.button:hover, button:hover, input[type="submit"]:hover { background: #8d1516; color: var(--gold-soft); }
.button:focus-visible, button:focus-visible, a:focus-visible, input:focus-visible,
select:focus-visible, textarea:focus-visible, summary:focus-visible {
  outline: 3px solid var(--teal);
  outline-offset: 2px;
}

button.primary, .button.primary { font-size: 1.05rem; width: 100%; }
/* v0.84: the Table forum button — the primary filled treatment, full column width,
   a taller hit area, the most prominent element between the header and the GM card. */
.table-forum-btn { display: block; width: 100%; text-align: center; padding: 16px 20px; margin: 4px 0 20px; font-size: 1.1rem; }
.table-forum-btn .unread, .unread { display: inline-block; min-width: 1.4em; padding: 0 .45em; margin-left: .4em; border-radius: 999px;
  background: var(--gold, #d2ac2a); color: var(--crimson, #7a1f2b); font-size: .8em; font-weight: 700; line-height: 1.5; text-align: center; vertical-align: middle; }
.push-banner { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }
.push-banner span { flex: 1 1 16rem; }
button.small, .button.small { min-height: var(--tap); padding: 6px 16px; font-size: .875rem; }
/* Forum post controls (v0.91): one wrapping row of labelled buttons; the editor's
   Save and Cancel side by side, full width on a phone; text areas grow, no inner scroll. */
.post-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 8px 0 4px; }
.post-actions form.inline { display: inline-flex; align-items: center; gap: 6px; margin: 0; }
.posteditor { margin: 8px 0 4px; }
.editor-actions { display: flex; gap: 8px; margin-top: 8px; }
.editor-actions .button { flex: 0 0 auto; }
textarea.autogrow { resize: vertical; overflow-y: hidden; min-height: calc(1.5em * 4 + 1rem); }

/* Forum built out (§3.25, v0.92): every post is a bordered card; a card's header
   toggles it; read posts open collapsed to one line, unread and the latest stay open. */
.forumtools { display: flex; gap: 14px; margin: 4px 0 10px; }
.forumfeed { display: grid; gap: 12px; margin: 12px 0 20px; }
.post { border: 1px solid var(--rule); border-radius: var(--radius); background: var(--panel); padding: 10px 14px; }
.post.post-unread { border-left: 3px solid var(--gold); }
.post-head { display: flex; width: 100%; align-items: baseline; gap: 8px; background: transparent; border: 0; color: inherit; font: inherit; text-align: left; padding: 0; margin: 0; min-height: 0; cursor: pointer; }
.post-head:hover, .post-head:focus { background: transparent; color: inherit; }
.post-head .post-meta { flex: 0 0 auto; }
.post-meta strong { font-weight: 600; }
.post-meta small { color: var(--muted); font-size: .8125rem; margin-left: 6px; }
.post-snippet { display: none; flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--muted); font-size: .875rem; }
.post.is-collapsed .post-content { display: none; }
.post.is-collapsed .post-snippet { display: block; }
.post-body { margin: 8px 0; }
.tombstone { color: var(--muted); font-style: italic; margin: 6px 0; }

/* The full-width "New posts" divider above the first unread post. */
.newposts { display: flex; align-items: center; gap: 10px; margin: 4px 0; color: var(--crimson); font-family: var(--serif); font-size: .75rem; text-transform: uppercase; letter-spacing: .08em; }
.newposts::before, .newposts::after { content: ""; flex: 1 1 auto; border-top: 1px solid var(--crimson); opacity: .55; }

.postbox-title { font-family: var(--serif); font-size: 1.15rem; margin: 18px 0 4px; }

/* Phone-only floating jump-to-composer button, revealed by the script. */
.jumpnew { position: fixed; left: 50%; transform: translateX(-50%); bottom: 16px; z-index: 30; box-shadow: 0 4px 14px rgba(0,0,0,.4); }

/* Character profiles (§3.27, v0.93). */
.character-head { display: flex; gap: 16px; align-items: flex-start; flex-wrap: wrap; margin-bottom: 12px; }
.character-portrait { width: 160px; height: 160px; border-radius: var(--radius); border: 1px solid var(--rule); flex: 0 0 auto; }
.trade-lists { display: flex; flex-wrap: wrap; gap: 20px; }
.trade-list { flex: 1 1 240px; min-width: 0; }
.trade-row { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; margin-bottom: 6px; }
.trade-row input[name="item"] { flex: 1 1 140px; }
.trade-row input[name="note"] { flex: 2 1 180px; }
.character-actions { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 18px; }
.party-list { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 12px; }
.party-member { display: flex; flex-direction: column; align-items: center; width: 96px; text-align: center; }
.party-member a { display: flex; flex-direction: column; align-items: center; text-decoration: none; }
.party-portrait { width: 64px; height: 64px; border-radius: var(--radius); border: 1px solid var(--rule); }
.party-portrait-empty { background: var(--sunk); display: block; }
.party-name { font-weight: 600; font-size: .875rem; margin-top: 4px; }
.party-player { color: var(--muted); font-size: .8125rem; }
/* The character badge beside a post author in the forum. */
.post-character { display: inline-flex; align-items: center; gap: 4px; text-decoration: none; margin-right: 6px; }
.post-character-portrait { width: 24px; height: 24px; border-radius: 5px; vertical-align: middle; }
.post-character-name { font-size: .8125rem; color: var(--gold-soft); }
@media (max-width: 520px) { .editor-actions { flex-wrap: nowrap; } .editor-actions .button { flex: 1 1 0; } }
button.danger, .button.danger { background: var(--crimson); border-color: var(--crimson); color: var(--white); }
button.danger:hover { background: #a80030; border-color: #a80030; }

/* Text-only buttons that sit inside prose or table cells. */
button.link {
  background: none;
  border: 0;
  color: var(--gold);
  padding: 0 10px;
  font-family: var(--body);
  font-size: .9rem;
  text-decoration: underline;
  min-height: var(--tap);
}
main button.link { color: var(--gold); font-family: var(--serif); }
main button.link:hover { color: var(--gold-soft); background: none; }

.stack { display: grid; gap: 14px; align-content: start; }
.stack h2, .stack h3, .stack h4 { margin-bottom: 0; }

label {
  display: block;
  font-family: var(--serif);
  font-size: .9rem;
  color: var(--gold-soft);
}
label.check {
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: var(--body);
  color: var(--ink);
  min-height: var(--tap);
}
label.check input { width: 22px; height: 22px; margin: 0; flex: none; }

input, select, textarea {
  font-family: var(--body);
  font-size: 16px;             /* keeps iOS from zooming the viewport on focus */
  width: 100%;
  min-height: var(--tap);
  margin-top: 4px;
  padding: 10px 12px;
  color: var(--ink);
  background: var(--panel-2);
  border: 1px solid var(--rule);
  border-radius: 8px;
}
input::placeholder, textarea::placeholder { color: #8d8177; }
/* The browser's own date/time pickers default to a dark glyph on dark. */
input[type="date"], input[type="time"] { color-scheme: dark; }
textarea { min-height: 96px; line-height: 1.5; }
input[type="checkbox"] { width: auto; min-height: 0; }

.inline { display: inline; }
.inline-form { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 14px; }
.inline-form input { margin-top: 0; flex: 1 1 8rem; min-width: 0; }
.inline-form button { flex: none; }
.narrow-form { max-width: 34rem; }

details {
  background: var(--panel);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding: 0 14px;
  margin-bottom: 14px;
}
details[open] { padding-bottom: 14px; }
summary {
  font-family: var(--serif);
  color: var(--gold-soft);
  cursor: pointer;
  padding: 12px 0;
  min-height: var(--tap);
  display: flex;
  align-items: center;
}
details .stack { margin-top: 4px; }

/* ---------- landing band (spec §3.13) ------------------------------------
   One slim band under the menu, above the tables. Mobile stacks it; from 600px
   the sign-in button sits at the right. */

.memberband {
  background: rgba(0,0,0,.34);
  border-bottom: 1px solid rgba(210,172,42,.3);
  padding: 10px var(--pad);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 16px;
}
.memberband p {
  margin: 0;
  color: var(--ink);
  font-family: var(--serif);
  font-size: .95rem;
}
.memberband .button { flex: none; }

@media (min-width: 600px) {
  .memberband { justify-content: space-between; padding: 10px var(--pad); }
}

/* ---------- notices ----------------------------------------------------- */

.ribbon, .flash {
  padding: 12px var(--pad);
  font-size: .9rem;
  text-align: center;
}
.ribbon { background: var(--gold); color: var(--red-deep); font-family: var(--serif); }
.flash { border-radius: var(--radius); text-align: left; margin: 0 var(--pad) 16px; }
main .flash { margin-left: 0; margin-right: 0; }
.flash-ok    { background: #e7f3e9; color: #1d4427; border: 1px solid #b9d9c1; }
.flash-error { background: #fdeaef; color: #7a0322; border: 1px solid #f3bfcd; }
.flash-warn  { background: #fdf3dd; color: #6b4c05; border: 1px solid #ecd9a2; }

/* ---------- tables: cards on a phone, a real grid on a wide screen ------- */

/* On a phone the rows are cards, so the table stops doing table layout
   entirely — otherwise a long unbreakable cell value (an email address) sets a
   min-content width the card layout cannot shrink below. */
.grid { width: 100%; border-collapse: collapse; margin-bottom: 1em; display: block; }
.grid tbody { display: block; }

/* Phone layout. Each row becomes a card; each cell shows its column name from
   the data-label attribute in the template. Nothing is shrunk to fit. */
.grid thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.grid tr {
  display: block;
  background: var(--panel);
  border: 1px solid var(--rule);
  border-left: 4px solid var(--gold);
  border-radius: var(--radius);
  padding: 10px 14px;
  margin-bottom: 12px;
}
.grid td {
  display: grid;
  grid-template-columns: minmax(0, 7.5rem) minmax(0, 1fr);
  gap: 10px;
  align-items: baseline;
  padding: 5px 0;
  border: 0;
  min-width: 0;
  overflow-wrap: anywhere;
}
.grid td::before {
  content: attr(data-label);
  font-family: var(--serif);
  font-size: .8125rem;
  color: var(--muted);
}
/* Cells with no column name (action buttons) span the card. */
.grid td[data-label=""] { display: block; padding-top: 10px; }
.grid td[data-label=""]::before { content: none; }
.grid td:empty { display: none; }

.grid td.night { font-family: var(--serif); color: var(--gold-soft); }
/* The time slot is the identifier of a table (ruled 2026-09-06); the adventure
   name is secondary text under it wherever a table is named. */
.adventure { display: block; color: var(--muted); font-size: .875rem; font-weight: 400; margin-top: 2px; }
p.adventure { margin: -.4em 0 .8em; font-size: 1.05rem; }
h1 + p.adventure { font-family: var(--serif); color: var(--gold-soft); }
.grid td.slot small.adventure { grid-column: 2; }
/* The slot title never wraps (ruled 2026-09-12): other columns compress to make
   room. On a phone the card's label column narrows for this cell so the whole
   slot fits beside "Time slot". */
.grid td.slot, .grid td.slot a { white-space: nowrap; }
.grid td.slot small.adventure { white-space: normal; }
.grid td.slot { grid-template-columns: minmax(0, 4.5rem) minmax(0, 1fr); }
.slot-title { white-space: nowrap; display: inline-block; max-width: 100%; min-width: 0; vertical-align: bottom; }
/* A slot in a flex or grid track must be allowed to be narrower than its text, or the track grows instead. */
.split > *, .campaign-head, .ev-body { min-width: 0; }
.ev-body a { display: flex; }
/* A card cell that carries slot titles narrows its label column like td.slot does. */
.grid td:has(.slot-title) { grid-template-columns: minmax(0, 4.5rem) minmax(0, 1fr); }
/* A waitlist summary line wraps between its slot, its adventure and the Edit control. */
.manage > summary { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; }
.grid.sortable td.slot { overflow: visible; text-overflow: clip; }
/* An email address is one unbreakable word; on a real table it must be allowed
   to break so the slot column and the Actions column keep their room. */
.grid td[data-label="Email"] { overflow-wrap: anywhere; word-break: break-word; }
.tally .adventure { font-size: .8125rem; }

/* Admin → a campaign's own page (board 50; v0.32). Members is a panel that is
   always open, directly under the campaign's details — the list is the point
   of the page. */
.campaign-admin .dates { color: var(--gold-soft); font-family: var(--serif); }
.campaign-admin .members { margin: 18px 0 24px; background: var(--panel); border: 1px solid var(--rule); border-radius: var(--radius); }
.campaign-admin .members > h2 { margin: 0; padding: 12px 14px; font-family: var(--serif); font-size: 1.05rem; color: var(--gold); border-bottom: 1px solid var(--rule); }
.campaign-admin .members .grid { margin: 0; }
.campaign-admin .members .grid td small { display: block; color: var(--muted); }
.campaign-admin .members .flash { margin: 12px 14px; }
.campaign-admin .addmember .flash { margin: 0 0 12px; max-width: 40rem; }
/* Add Member sits below the list, a compact block of its own. */
.campaign-admin .addmember { margin: 0 0 28px; }
.campaign-admin .addmember .inline-form { display: grid; gap: 8px; max-width: 40rem; }
.campaign-admin .addmember .inline-form select { max-width: 100%; }
.campaign-admin .addmember .fine { max-width: 40rem; }
@media (min-width: 820px) {
  .campaign-admin .addmember .inline-form { grid-template-columns: minmax(16rem, 1fr) max-content; align-items: end; }
}

/* Back control at the top left of every page below a list (ruled 2026-09-09). */
.back { margin: 0 0 10px; font-family: var(--serif); }
.back a { color: var(--gold-soft); text-decoration: none; }
.back a:hover { color: var(--gold); text-decoration: underline; }

/* Admin → People: Revoke and Delete on each row; a revoked person is marked. */
td.peopleactions form.inline, td.peopleactions a.button { display: inline-block; vertical-align: middle; margin: 2px 6px 2px 0; }
td.peopleactions .warn { display: block; max-width: 22rem; color: var(--gold-soft); }
.grid tbody tr.is-revoked td:first-child { box-shadow: inset 3px 0 0 var(--rule); }
.grid tbody tr.is-revoked td { color: var(--muted); }

/* Admin → People: the account's capacity forms sit on one row. */
.seatrow.capacity { grid-template-columns: 1fr; gap: 8px; }
.seatrow.capacity .inline-form { display: flex; gap: 8px; align-items: end; flex-wrap: wrap; }
@media (min-width: 820px) { .seatrow.capacity { grid-template-columns: 1fr 1fr; } }
.account .pill { margin-left: 8px; }

/* Join / Move / Drop in the Members drawer: each button opens its dropdown in
   place (a <details>), so it works with no JavaScript. */
.seataction { display: inline-block; vertical-align: top; margin: 4px 8px 4px 0; }
.seataction > summary { list-style: none; display: inline-flex; }
.seataction > summary::-webkit-details-marker { display: none; }
.seataction[open] > summary { background: var(--red-deep); color: var(--gold); }
.seataction .inline-form { margin: 8px 0 4px; display: grid; gap: 8px; }
.seataction .inline-form select { max-width: 100%; }
.seataction > .fine { margin: 0 0 6px; max-width: 32rem; }
td.seatactions form.inline { display: inline-block; vertical-align: top; margin: 4px 0; }
@media (min-width: 820px) {
  .seataction .inline-form { grid-template-columns: minmax(16rem, 1fr) max-content; align-items: end; }
}

/* Admin → Overview: sortable, resizable Seats table (public/sortable.js). The
   header links sort on their own with JavaScript off; the handles are a
   desktop affordance and are hidden where the grid is a stack of cards. */
.grid.sortable thead th { position: relative; padding-right: 18px; }
.grid.sortable thead th a { color: inherit; text-decoration: none; }
.grid.sortable thead th a:hover { color: var(--gold); }
.grid.sortable thead th[aria-sort="ascending"] a,
.grid.sortable thead th[aria-sort="descending"] a { color: var(--gold); }
.grid.sortable .sortmark { font-size: .7em; }
.grid.sortable .resizer { display: none; }
.grid.sortable td small.adventure { display: none; }
@media (min-width: 820px) {
  .grid.sortable .resizer {
    display: block; position: absolute; top: 0; right: 0; width: 10px; height: 100%;
    cursor: col-resize; user-select: none; touch-action: none;
  }
  .grid.sortable .resizer:hover, .grid.sortable.is-resizing .resizer { background: linear-gradient(to right, transparent 4px, var(--gold) 4px, var(--gold) 6px, transparent 6px); }
  .grid.sortable td, .grid.sortable th { overflow: hidden; text-overflow: ellipsis; }
}
.ev .adventure, .mev .adventure { font-size: .75rem; }
summary .adventure { display: inline; margin-left: 6px; }
.grid td.name a { font-weight: 600; display: inline-flex; align-items: center; min-height: var(--tap); }
/* A <small> inside a cell is its own grid item, so keep it in the value column
   under the value rather than letting it fall under the label. */
/* nowrap suits a short session time; longer notes must wrap or they set the
   card's minimum width and push the page sideways. */
.grid td small { grid-column: 2; display: block; color: var(--muted); }
.grid td.night small { white-space: nowrap; }

.grid tr.is-full { border-left-color: var(--rule); }
.grid tr.is-full td.seats { color: var(--muted); }
.grid tr.is-today { border-left-color: var(--crimson); }
.grid tr.status-delivered { border-left-color: var(--teal); }
.grid tr.status-flex, .grid tr.status-cancelled_credit { border-left-color: var(--crimson); }
.grid tr.status-closure, .grid tr.status-ended, .grid tr.status-cancelled { border-left-color: var(--rule); }
.grid tr.status-past_due { border-left-color: var(--crimson); }
.grid tr.status-paused { border-left-color: var(--muted); }

.grid td.empty { display: block; }
.grid td.empty::before { content: none; }

/* A totals row (the GM pay run) is part of the same stack on a phone and a
   footer row on a wide screen; it reads as a summary either way. */
.grid tfoot { display: block; }
.grid tfoot tr.total { border-left-color: var(--gold-soft); background: var(--panel-2); }

/* Admin -> People: adding a person is one compact block above the list, so the
   list and its seat drawers keep the whole content width. It used to sit in a
   right-hand column, which took a third of the page for a four-field form and
   left that third empty below it. */
.addperson, .compact-form {
  display: grid;
  gap: 10px;
  margin: 0 0 18px;
  padding: 14px;
  background: var(--panel);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
}
.addperson label, .compact-form label { display: grid; gap: 4px; }
.addperson > .fine, .compact-form > .fine { margin: 0; }
.addperson button, .compact-form button { justify-self: start; }
@media (min-width: 820px) {
  .addperson {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr) minmax(0, 9rem) max-content;
    align-items: end;
    column-gap: 14px;
  }
  /* Board 49: the same compact row for any short "Add …" form that used to sit
     beside a list — closures, goodwill credits. Fields share the row; the
     button keeps its width. */
  .compact-form { grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); align-items: end; column-gap: 14px; }
  .compact-form button { grid-column: auto; }
  .addperson > .fine, .compact-form > .fine { grid-column: 1 / -1; }
  .addperson button, .compact-form button { min-width: max-content; white-space: nowrap; }
}

/* An anchored member row lands a little below the top of the viewport rather
   than flush against it, so the row above gives it context. */
tr[id^="m"], .seatrow[id^="s"] { scroll-margin-top: 24px; }

/* Admin -> People: one drawer per member, one row per seat. Each row is its own
   form so a member with four seats saves them one at a time. */
.seatdrawer > summary { font-size: .9375rem; }
.seatrow {
  display: grid;
  gap: 8px;
  padding: 12px 0;
  border-top: 1px solid var(--rule);
}
.seatrow > p { margin: 0; }
/* The account line above the seat rows: ordinary wrapping text, never a grid.
   Under v0.22–v0.32 it carried .seatrow, and with five inline runs (held and
   billed differing) the long run landed in the max-content column, could not
   wrap, and pushed the table — and the Actions column — off the page. */
.accountline { margin: 0; padding: 12px 0 10px; }
.accountline .button { margin-left: 4px; }
.seatrow label { display: grid; gap: 4px; }
.seatrow button { justify-self: start; }
@media (min-width: 820px) {
  /* One line per seat: what it is, who sits in it, which table, and Save. */
  .seatrow {
    grid-template-columns: minmax(11rem, auto) minmax(9rem, 1fr) minmax(14rem, 1.6fr) max-content;
    align-items: end;
    column-gap: 16px;
  }
  .seatrow > p { align-self: center; margin: 0; }
  /* The cards' min-width:0 (which stops a select widening a phone card) would
     otherwise let the Save button collapse to nothing beside the flexible
     tracks. */
  .seatrow button { min-width: max-content; white-space: nowrap; }
}

/* The single-table picker: its summary is the site's button with a caret, so it
   reads as a dropdown rather than as a line of text with a stray triangle. */
/* No surrounding frame: the button is the control, and a box around it reads as
   a second, empty one. */
.pickone {
  margin: 0 0 1.2em;
  background: none;
  border: 0;
  border-radius: 0;
  padding: 0;
}
.pickone[open] { padding-bottom: 0; }
.pickone > summary {
  font-family: var(--serif);
  font-size: 1rem;
  letter-spacing: .04em;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  min-height: var(--tap);
  padding: 10px 20px;
  background: var(--red-deep);
  color: var(--gold);
  border: 2px solid var(--gold);
  border-radius: var(--radius);
  cursor: pointer;
  list-style: none;                 /* no default disclosure triangle */
}
.pickone > summary::-webkit-details-marker { display: none; }
.pickone > summary::after {
  content: "";
  width: .5em; height: .5em;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: translateY(-25%) rotate(45deg);
  transition: transform .15s ease;
}
.pickone[open] > summary::after { transform: translateY(15%) rotate(225deg); }
.pickone > summary:hover { background: #8d1516; color: var(--gold-soft); }
.pickone > ul { margin-top: .8em; }

/* A feed address to copy: its own box, wrapping anywhere, so it can be selected
   in one gesture rather than picked out of a sentence. */
.copybox {
  background: var(--panel-2);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding: 10px 12px;
  margin: 0 0 1em;
  overflow-wrap: anywhere;
}
.copybox code { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: .9375rem; }
.howto li { margin-bottom: .5em; }

/* Label text that only a screen reader needs. */
.visually-hidden {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap;
}

/* A row that carries a nested list rather than a field/value pair — the seats
   held by one member — is not laid out as a labelled cell: the fixed label
   column left too little for a date input and pushed the page wider than the
   viewport. */
/* On a phone the whole .grid is a stack of cards, so this cell is a block like
   every other. On a real table it must stay a table-cell: a block box inside a
   table row is not a cell, so it loses its colspan and collapses to the width
   of the first column — which is what squeezed the seat drawer into the Name
   column at desktop widths. The table-cell rule is in the 820px block below. */
tr.seatlist td { display: block; }
tr.seatlist td::before { content: none; }

/* A form control in a card cell must not set the cell's minimum width — a
   <select> carries an intrinsic one that pushed the page 3px wide at 320. */
.grid td select, .grid td input, .grid td button { max-width: 100%; min-width: 0; }

/* ---------- lists ------------------------------------------------------- */

.plain { list-style: none; margin: 0 0 1em; padding: 0; }
.plain li { padding: 8px 0; border-bottom: 1px solid var(--rule); }
.plain li:last-child { border-bottom: 0; }
.plain.small { font-size: .875rem; }

.dates { list-style: none; margin: 0 0 1em; padding: 0; }
.dates li {
  padding: 8px 12px;
  border-left: 3px solid var(--gold);
  background: var(--panel);
  border-radius: 0 6px 6px 0;
  margin-bottom: 6px;
}
.dates li.status-closure { border-left-color: var(--rule); color: var(--muted); }
.dates li.status-flex,
.dates li.status-cancelled_credit { border-left-color: var(--crimson); color: var(--muted); text-decoration: line-through; }
.dates li.status-rescheduled { border-left-color: var(--teal); }

.roster { list-style: none; margin: 0; padding: 0; }
.roster li { border-bottom: 1px solid var(--rule); }
.roster li:last-child { border-bottom: 0; }

/* ---------- cards, seats, tallies --------------------------------------- */

.seat, .join {
  background: var(--panel);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding: 16px var(--pad);
  margin-bottom: 20px;
}
.seat > header { border-bottom: 1px solid var(--rule); margin-bottom: 14px; padding-bottom: 10px; }
.seat > header h2 { margin-bottom: .2em; }
.seat > header p { margin-bottom: .5em; color: var(--muted); font-size: .9rem; }
.seat.status-past_due { border-left: 4px solid var(--crimson); }
.seat.status-paused { border-left: 4px solid var(--muted); }
.seat.status-active { border-left: 4px solid var(--gold); }

.pill {
  display: inline-block;
  font-family: var(--serif);
  font-size: .75rem;
  letter-spacing: .06em;
  text-transform: uppercase;
  padding: 4px 12px;
  border-radius: 999px;
  background: var(--sunk);
  color: var(--gold-soft);
  border: 1px solid var(--rule);
}
.status-past_due .pill { background: #fdeaef; color: #7a0322; border-color: #f3bfcd; }
.status-active .pill { background: #f6efd8; color: #6b4c05; border-color: #ecd9a2; }

.big { font-size: 1.6rem; font-weight: 700; margin-bottom: .2em; }
.big small { font-size: .875rem; font-weight: 400; color: var(--muted); }
.price { font-size: 1.35rem; font-weight: 700; }
.price small { display: block; font-size: .8125rem; font-weight: 400; color: var(--muted); line-height: 1.5; }
.recap { background: var(--sunk); border-radius: 8px; padding: 12px 14px; font-size: .9375rem; }

.tallies { display: grid; gap: 12px; margin-bottom: 24px; }
.projected-head { font-size: 1.1rem; margin: 0 0 8px; }
.tallies.projected { margin-bottom: 8px; }
.tally {
  background: var(--panel);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding: 12px 14px;
  display: grid;
  gap: 2px;
}
.tally strong { font-size: 1.5rem; font-family: var(--serif); color: var(--gold); line-height: 1.2; }
.tally span { font-size: .8125rem; color: var(--muted); }

/* GM desk cards (ruled 2026-09-07): each is a link into the table's session
   list. Their own grid, wide enough for a slot title on one or two lines at
   desktop width, and a heading a size down from the tallies'. */
.desk-cards { display: grid; gap: 12px; margin-bottom: 24px; }
.desk-card {
  display: grid; gap: 3px; padding: 14px 16px; text-decoration: none; color: var(--muted);
  background: var(--panel); border: 1px solid var(--rule); border-left: 4px solid var(--gold); border-radius: var(--radius);
}
.desk-card:hover { background: var(--sunk); }
/* The admin's Edit control (v0.42): a small button beside a campaign's name;
   on a desk card it sits in the corner, outside the card's own link. */
/* Admin → Game Masters (v0.55): the Edit beside each slot is a plain link, the slot's own size and face, bright blue, no box. */
.release { border: 1px solid var(--gold-soft); border-radius: var(--radius); padding: 12px 18px; margin: 16px 0; }
.signbox { border: 1px solid var(--rule); border-radius: var(--radius); padding: 16px 20px; margin-top: 20px; }
.admin-edit-link { color: #4da3ff; font: inherit; text-decoration: underline; margin-left: 8px; }
.admin-edit-link:hover { color: #8cc4ff; }
.admin-edit { vertical-align: middle; margin-left: 6px; font-size: .75rem; padding: 2px 8px; min-height: 0; }
.desk-card-wrap { position: relative; }
.desk-card-wrap > .admin-edit { position: absolute; top: 10px; right: 10px; margin: 0; z-index: 1; }
.ev-body .admin-edit, .grid td .admin-edit { display: inline-block; }
/* The grid row's button and the admin's Edit share one line (ruled 2026-09-15); the text columns give way. */
.grid td.rowactions { white-space: nowrap; }
.desk-card strong { font-family: var(--serif); font-size: 1.2rem; line-height: 1.25; color: var(--gold); }
.desk-card span { font-size: .8125rem; }
.desk-card .adventure { color: var(--ink); font-size: .9375rem; }
.desk-card .go { margin-top: 6px; color: var(--gold-soft); font-family: var(--serif); }
@media (min-width: 820px) { .desk-cards { grid-template-columns: repeat(auto-fill, minmax(380px, 1fr)); } }

.weeknav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 16px;
}
.weeknav h2 { font-size: 1.05rem; font-family: var(--serif); margin: 0; text-align: center; }
.weeknav a { min-height: var(--tap); display: inline-flex; align-items: center; font-size: .9rem; }

/* ---------- admin sub-navigation ---------------------------------------- */

/* The admin tabs wrap rather than scroll sideways: with eight of them a hidden
   scroll hides half the sections on a phone, and a scroll container was also
   pushing the page's own scroll width past the viewport. */
.subnav {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 20px;
  padding-bottom: 4px;
}
.subnav a {
  flex: 0 1 auto;
  font-family: var(--serif);
  font-size: .875rem;
  text-decoration: none;
  color: var(--gold);
  background: var(--panel);
  border: 1px solid var(--rule);
  border-radius: 999px;
  padding: 0 14px;
  min-height: var(--tap);
  display: inline-flex;
  align-items: center;
  white-space: nowrap;
}
.subnav a.current { background: var(--red-deep); color: var(--gold); border-color: var(--red-deep); }

.onramp {
  background: var(--sunk);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding: 14px 16px;
  margin-top: 18px;
}
.onramp h2 { font-size: 1.15rem; }
.onramp details { background: var(--panel); }

/* ---------- schedule ----------------------------------------------------- */

.viewswitch { display: flex; gap: 6px; margin-bottom: 16px; }
.viewswitch a {
  flex: 1;
  font-family: var(--serif);
  font-size: .9rem;
  text-align: center;
  text-decoration: none;
  color: var(--gold);
  background: var(--panel);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  min-height: var(--tap);
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.viewswitch a.current { background: var(--red-deep); color: var(--gold); border-color: var(--red-deep); }


.days { display: grid; gap: 12px; }
.day {
  background: var(--panel);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding: 10px 14px;
  margin: 0;
}
.day.is-today { border-left: 4px solid var(--crimson); }
.day.is-closed { background: var(--sunk); }
.day h3 { font-size: .9rem; margin-bottom: .4em; color: var(--gold-soft); }

.evs { list-style: none; margin: 0; padding: 0; }
.ev { display: grid; grid-template-columns: 5.5rem minmax(0, 1fr); gap: 8px; padding: 7px 0; border-top: 1px solid var(--rule); }
/* On a phone the time column gives way so the slot title fits on its one line. */
@media (max-width: 599px) { .ev { grid-template-columns: 4.25rem minmax(0, 1fr); } }
.ev:first-child { border-top: 0; }
.ev-time { font-family: var(--serif); font-size: .8125rem; color: var(--muted); }
.ev-body a { font-weight: 600; display: inline-flex; align-items: center; min-height: 30px; }
.ev-body small { display: block; font-size: .75rem; color: var(--muted); }
.ev.is-mine { border-left: 4px solid var(--gold); padding-left: 8px; margin-left: -12px; }
.ev.is-off .ev-body > a { text-decoration: line-through; color: var(--muted); }

/* List view: each entry is a campaign card echoing the Tables page (§3.13.1, v0.92).
   The day section keeps today's crimson edge and the shaded closure day; the card
   carries the gold "your table" edge, the strike-through, the room chip. */
/* v1.13 §3.13.1a: the Schedule's list view is the Current Campaigns (/tables) table, one per day. On a desktop the
   day tables share their column widths, so every day lines up; on a phone they are the /tables cards. */
.schedtables { margin: 0; }
.schedtables .grid { margin: 0; }
.schedtables .grid tr.is-mine td:first-child { box-shadow: inset 4px 0 0 var(--gold); }
.schedtables .grid tr.is-off td.slot a { text-decoration: line-through; color: var(--muted); }
.schedtables .grid td.slot, .schedtables .grid td.slot a, .schedtables .grid td.rowactions { white-space: normal; }
@media (min-width: 820px) {
  .schedtables .grid { table-layout: fixed; width: 100%; }
  .schedtables .grid th:nth-child(1) { width: 72px; }
  .schedtables .grid th:nth-child(2) { width: 34%; }
  .schedtables .grid th:nth-child(3) { width: 13%; }
  .schedtables .grid th:nth-child(4) { width: 15%; }
  .schedtables .grid th:nth-child(5) { width: 13%; }
}
.tag {
  display: inline-block;
  font-family: var(--serif);
  font-style: normal;
  font-size: .7rem;
  text-transform: uppercase;
  letter-spacing: .06em;
  padding: 2px 8px;
  border-radius: 999px;
  margin-left: 6px;
  white-space: nowrap;
}
.tag-off { background: #fdeaef; color: #7a0322; }
.tag-closed { background: var(--sunk); color: var(--muted); }
.tag-mine { background: #f6efd8; color: #6b4c05; }
.tag-alt { background: #e8f2f3; color: #245c63; }
.tag-pending { background: #e6f0ff; color: #274a7a; }
.tag-room { background: var(--sunk); color: var(--gold-soft); border: 1px solid var(--rule); }

/* Room badge inside a month-grid cell: short, so the title still fits. */
.mroom {
  font-family: var(--serif);
  font-size: .62rem;
  background: var(--sunk);
  border: 1px solid var(--rule);
  border-radius: 3px;
  padding: 0 3px;
  margin-right: 3px;
  color: var(--gold-soft);
}

/* Month grid: scrolls sideways on a phone rather than crushing seven columns. */
.monthgrid { overflow-x: auto; -webkit-overflow-scrolling: touch; margin-bottom: 1em; }
.monthhead, .monthrow { display: grid; grid-template-columns: repeat(7, minmax(92px, 1fr)); gap: 4px; min-width: 644px; }
.monthhead { margin-bottom: 4px; }
.monthhead span { font-family: var(--serif); font-size: .75rem; color: var(--muted); text-align: center; }
.monthrow { margin-bottom: 4px; }
.mcell {
  background: var(--panel);
  border: 1px solid var(--rule);
  border-radius: 6px;
  padding: 4px;
  min-height: 74px;
}
.mcell.is-blank { background: transparent; border-color: transparent; }
.mcell.is-closed { background: var(--sunk); }
.mcell.is-today { border-color: var(--crimson); border-width: 2px; }
.mday { font-family: var(--serif); font-size: .75rem; color: var(--muted); display: block; }
.mcell.is-blank { background: transparent; border-color: transparent; }
.mev {
  display: block;
  font-size: .7rem;
  line-height: 1.3;
  padding: 2px 4px;
  margin-top: 2px;
  border-radius: 4px;
  background: var(--panel-2);
  color: var(--ink);
  text-decoration: none;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.mev.is-mine { background: #f6efd8; border-left: 3px solid var(--gold); }
.mev.is-off { text-decoration: line-through; color: var(--muted); }

.subscribe { margin-top: 32px; border-top: 1px solid var(--rule); padding-top: 20px; }
.subscribe code { word-break: break-all; }

@media (min-width: 820px) {
  .viewswitch { justify-content: flex-start; }
  .viewswitch a { flex: 0 0 auto; padding: 0 22px; }
  .monthhead, .monthrow { min-width: 0; }
}

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

.colophon {
  background: rgba(0,0,0,.34);
  color: rgba(255, 255, 255, .85);
  padding: 28px var(--pad) 32px;
  font-size: .875rem;
  display: grid;
  gap: 18px;
  border-top: 1px solid rgba(210,172,42,.3);
}
.colophon .inner { max-width: 1080px; margin: 0 auto; width: 100%; display: grid; gap: 18px; }
.colophon h2 {
  font-family: var(--serif);
  font-size: .8125rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--gold);
  margin-bottom: .6em;
}
.colophon a { color: var(--gold); }
.colophon a:hover { color: var(--white); }
.colophon p { margin-bottom: .4em; }
.colophon a { min-height: var(--tap); display: inline-flex; align-items: center; }
.colophon .legal { border-top: 1px solid rgba(210, 172, 42, .3); padding-top: 16px; }
.colophon .legal a { margin-right: .35em; min-height: 0; display: inline; }
.colophon .parking { color: rgba(255, 255, 255, .7); }

/* ---------- 600px: two-up cards, side-by-side form rows ----------------- */

@media (min-width: 600px) {
  :root { --pad: 24px; }

  .masthead { padding: 12px var(--pad); }

  h1 { font-size: 2.4rem; }

  .tallies { grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); }
  .offers { grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }
  .ladder { grid-template-columns: repeat(3, 1fr); }
  .ladder li { grid-template-columns: 1fr; }
  /* minmax(0,…) so a track can shrink below a date input's intrinsic width
     instead of pushing the form wider than the page. */
  .row { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .colophon .inner { grid-template-columns: 1fr 1fr; }
  .colophon .legal { grid-column: 1 / -1; }
  button.primary, .button.primary { width: auto; justify-self: start; }
}

/* ---------- 820px: real tables, two-column pages ------------------------ */

@media (min-width: 820px) {
  .hero, .band-table { background-image: url("/img/den-table.jpg"); }
  .band-shelves { background-image: url("/img/den-shelves.jpg"); }
  .band-room  { background-image: url("/img/den-room.jpg"); }
  .hero { padding: 84px var(--hero-pad) 72px; }
  .pagehead { padding: 58px var(--hero-pad) 48px; }

  .grid { display: table; }
  .grid tbody { display: table-row-group; }
  .grid thead { position: static; width: auto; height: auto; clip: auto; }
  .grid thead th {
    text-align: left;
    font-family: var(--serif);
    font-size: .8125rem;
    letter-spacing: .04em;
    color: var(--muted);
    font-weight: 400;
    padding: 8px 12px;
    border-bottom: 2px solid var(--gold);
    white-space: nowrap;
  }
  .grid tr {
    display: table-row;
    background: var(--panel);
    border: 0;
    border-radius: 0;
    padding: 0;
    margin: 0;
  }
  .grid tbody tr { border-bottom: 1px solid var(--rule); }
  .grid tbody tr:hover { background: var(--sunk); }
  .grid td {
    display: table-cell;
    padding: 12px;
    vertical-align: middle;
    border-bottom: 1px solid var(--rule);
    overflow-wrap: normal;
  }
  .grid td.name a { display: inline; min-height: 0; }
  .grid td::before { content: none; }
  .grid td[data-label=""] { display: table-cell; padding: 12px; }
  .grid td:empty { display: table-cell; }
  .grid td.night small { display: block; margin-left: 0; }
  .grid tfoot { display: table-footer-group; }
  /* colspan only works on a real cell (see tr.seatlist above). */
  tr.seatlist td { display: table-cell; }

  /* The role select was squeezed to about four characters by its column, so
     "member" read as "mel". Only above 820px: on a phone the cards are one
     column wide and a minimum here would push the page sideways. */
  .grid td[data-label="Role"] select { min-width: 9ch; }

  .md-table.is-keyvalue td { display: table-cell; padding: 10px 12px; }
  .md-table.is-keyvalue td:first-child { width: 34%; }
  /* The card accent stripe becomes a left border on the first cell. */
  .grid tbody tr.is-full td:first-child { box-shadow: inset 3px 0 0 var(--rule); }
  .grid tbody tr.is-today td:first-child,
  .grid tbody tr.status-flex td:first-child,
  .grid tbody tr.status-past_due td:first-child,
  .grid tbody tr.status-cancelled_credit td:first-child { box-shadow: inset 3px 0 0 var(--crimson); }
  .grid tbody tr.status-delivered td:first-child { box-shadow: inset 3px 0 0 var(--teal); }

  .split { grid-template-columns: 1.4fr 1fr; align-items: start; gap: 32px; }
  .cols { grid-template-columns: 1fr 1fr; }
  /* .cols.wide is gone (board 49): no admin page puts a form beside a list. */
  .seat .cols { gap: 24px; }
  .colophon .inner { grid-template-columns: 2fr 1fr 1fr; }
}

@media (min-width: 1000px) {
  .seat details .cols { grid-template-columns: repeat(3, 1fr); }
}

/* Respect a reduced-motion preference (nothing here animates, but hover
   transitions may be added later). */
@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; animation: none !important; }
}

/* Campaign images (v0.71; laid out v0.72). Square where they show; a non-square
   source is centre-cropped by object-fit, never distorted. A campaign without
   one renders nothing here, so there is no gap and no placeholder box. All
   spacing from the --s scale. */

/* The tile is its own column in the listing, so no text wraps around it and
   every row's slot text starts at the same left edge. */
.campaign-tile { width: 56px; height: 56px; max-width: none; border-radius: var(--radius); display: block; }
.grid td.pic { width: 56px; min-width: 56px; }
.grid td.pic a { display: block; line-height: 0; }
/* Phone (cards): the tile sits in a left gutter reserved on the card; content
   flows to its right. A card with no image reserves no gutter. Scoped to the
   card width so it never touches the real table below 820px. */
/* The Tables listing card on a phone (v0.74): no visible column labels, the
   values standing alone. The row is a grid — the image, the time slot and the
   adventure name composed together on the top row, everything else full width
   at the left edge beneath, the buttons at the foot. The column names stay for
   an inline .linelabel at the start of each meta line (below), hidden on the desktop table.
   Scoped to .tables .grid and under 820px so the real table above is untouched. */
@media (max-width: 819px) {
  .tables .grid tr {
    display: grid;
    grid-template-columns: 56px minmax(0, 1fr);
    column-gap: var(--s4);
    row-gap: var(--s2);
    padding: var(--s3) var(--s4);
  }
  .tables .grid tr:not(.has-pic) { grid-template-columns: minmax(0, 1fr); }
  /* Each cell is a plain block: no inner label/value grid, no visible label. */
  .tables .grid td { display: block; grid-column: 1 / -1; padding: 0; }
  .tables .grid td::before { content: none; }
  .tables .grid td:empty { display: none; }
  /* Row 1: the tile in its gutter, the slot and adventure beside it. */
  .tables .grid td.pic { grid-column: 1; grid-row: 1; align-self: start; }
  .tables .grid tr.has-pic td.slot { grid-column: 2; grid-row: 1; }
  .tables .grid td.slot a { display: inline; min-height: 0; white-space: normal; }
  .tables .grid td.slot { align-self: center; }
  /* The buttons sit at the foot with a little air above them. */
  .tables .grid td.rowactions { padding-top: var(--s3); }
}
/* Column names kept for a screen reader only, off the desktop table where the
   real <thead> supplies them. */
/* The inline label at the start of each phone meta line (v0.75): ordinary text,
   the site's muted colour its only distinction, no heading styling. It is real
   text so a screen reader reads it once; on the desktop table it is hidden and
   the real <thead> supplies the heading, so it is never read twice. */
.linelabel { color: var(--muted); }
@media (min-width: 820px) { .grid .linelabel { display: none; } }
/* The pending pill and its count are one unit and never break apart. */
.seatstate { display: inline-flex; align-items: baseline; gap: var(--s2); flex-wrap: wrap; }
.seatstate .count { white-space: nowrap; }

/* The schedule tile: a small square before the entry, cleared so the tags below wrap normally. */
.campaign-tile.sched { width: 32px; height: 32px; border-radius: var(--s2); float: left; margin: 2px var(--s2) 2px 0; }

/* The campaign page masthead: hero and heading as one composition. */
.campaign-hero { display: block; width: 100%; max-width: 190px; aspect-ratio: 1 / 1; border-radius: var(--radius); }
.campmast { margin: 0 0 var(--s5); }
.campmast-title > h1 { margin-top: 0; }
/* Phone: hero on top, heading beneath, both to the same left edge. */
.campmast.has-hero .campaign-hero { margin: 0 0 var(--s4); }

/* The upload field on the campaign form. */
.campaign-thumb { border-radius: var(--s2); display: block; margin: 0 0 var(--s2); }
.imgfield { border: 1px solid var(--rule); border-radius: var(--s2); padding: var(--s3) var(--s4); }
.imgfield legend { font-size: .8125rem; color: var(--muted); padding: 0 var(--s2); }

/* The square crop tool (v0.76, public/imagecrop.js). A modal over the form; no
   library, canvas only. Spacing from the --s scale. */
.cropdlg { border: 1px solid var(--rule); border-radius: var(--radius); background: var(--panel-2); color: var(--ink); padding: 0; max-width: min(92vw, 640px); }
.cropdlg::backdrop { background: rgba(0,0,0,.6); }
.crop-wrap { padding: var(--s5); }
.crop-title { margin: 0 0 var(--s4); font-size: 1.1rem; }
.crop-body { display: flex; flex-wrap: wrap; gap: var(--s5); align-items: flex-start; }
.crop-stage { flex: 1 1 auto; min-width: 0; }
.crop-canvas { display: block; max-width: 100%; height: auto; touch-action: none; border-radius: var(--s2); cursor: move; }
.crop-side { flex: 0 0 auto; }
.crop-cap, .crop-chosen-cap { color: var(--muted); font-size: .8125rem; margin: 0 0 var(--s2); }
.crop-preview, .crop-chosen { display: block; border-radius: var(--radius); border: 1px solid var(--rule); }
.crop-help { margin: var(--s4) 0 0; }
.crop-actions { display: flex; flex-wrap: wrap; gap: var(--s3); align-items: center; margin-top: var(--s4); }
.crop-chosen { margin: var(--s3) 0 var(--s1); }

/* The campaign-group portal (§3.25, v0.77). Same spacing scale. */
.portal-links { margin: 0 0 var(--s5); display: flex; flex-wrap: wrap; gap: var(--s2); }
.portal-layout { display: grid; gap: var(--s5); }
.portal-contents { align-self: start; }
/* v1.09 §3.39h: the layout's last block keeps no margin below it (on /rules, More rules' band margin). */
.portal-layout > :last-child { margin-bottom: 0; }
.portal-contents ul { list-style: none; margin: 0; padding: 0; }
.portal-contents li { margin: 0 0 var(--s1); }
.portal-contents li.lvl-3 { padding-left: var(--s4); font-size: .9375rem; }
.portal-contents a { color: var(--gold-soft); text-decoration: none; }
.portal-contents a:hover { color: var(--gold); text-decoration: underline; }
.portal-body :is(h2, h3, h4) { scroll-margin-top: var(--s5); }
.portal-body img { display: block; max-width: 100%; border-radius: var(--radius); margin: var(--s4) 0; }
.logfeed { display: grid; gap: var(--s6); }
.logentry { border-top: 1px solid var(--rule); padding-top: var(--s4); }
.logdate { margin: 0 0 var(--s2); }
.logtitle { display: block; color: var(--muted); font-size: .9375rem; font-weight: 400; margin-top: var(--s1); }
.logimgs { display: flex; flex-wrap: wrap; gap: var(--s3); margin-top: var(--s3); }
.logimg { display: block; flex: 0 0 auto; }
.logimg img { display: block; max-height: 320px; width: auto; border-radius: var(--s2); border: 1px solid var(--rule); }
@media (min-width: 820px) {
  .portal-layout { grid-template-columns: minmax(0, 3fr) minmax(0, 1fr); }
  /* v1.03: every block beside the contents takes column 1, each on its own row;
     pinning them all to row 1 drew two text blocks on top of each other on /rules. */
  /* v1.13 §3.14.11a: the column sticks just below the sticky menu bar (public/menu.js keeps --mast-h its
     height), not under it; a list taller than the screen below the bar scrolls within itself. */
  .portal-contents { grid-column: 2; grid-row: 1 / span 20; position: sticky; top: calc(var(--mast-h, 76px) + var(--s4));
    max-height: calc(100vh - var(--mast-h, 76px) - var(--s4) * 2); overflow-y: auto; overscroll-behavior: contain; }
  .portal-layout > :not(.portal-contents) { grid-column: 1; }
  /* v1.09 §3.39h: the contents' twenty rows are mostly empty; with a row gap each empty row still added
     24px, 456px of nothing below the last block. The rows have no gap; the blocks in column 1 are spaced
     by a margin instead. */
  .portal-layout { row-gap: 0; }
  .portal-layout > :not(.portal-contents) ~ :not(.portal-contents) { margin-top: var(--s5); }
}

/* ---------- Admin → Bookkeeping (§4.1, v0.97) ---------------------------
   The page in order: a two-row toolbar, Revenue by month full width with a
   bold total, the explanation as a collapsed plain line, then one tabbed
   table at a time. No side columns (§3.14.9); the --s spacing scale only. */

/* Row two of the toolbar: the report and its two buttons, wrapping on a phone.
   The label and select stay one unit so the row never breaks between them. */
.bk-exports { align-items: end; }
.bk-exports label { display: grid; gap: var(--s1); min-width: 0; flex: 1 1 100%; max-width: 24rem; }
/* On a phone the report takes its own line and the two buttons pair on the next,
   so the toolbar is two rows; from 600px the three sit on one. */
@media (min-width: 600px) { .bk-exports label { flex: 0 1 24rem; } }
.bk-exports select { margin-top: 0; width: 100%; }
.bk-exports button { flex: none; }
/* The year-end pack ignores the range and says so beside the dropdown. With
   JavaScript off it is always shown, because it is always true. */
.bk-rangenote { flex: 1 1 100%; margin: 0; }
/* One size for every export control; the year-end pack alone is the filled
   primary treatment, switched on by public/bookkeeping.js when it is chosen. */
.bk-exports .button.is-primary, .bk-exports button.is-primary {
  background: var(--gold);
  color: var(--topbar-ink);
  border-color: var(--gold);
}
.bk-exports .button.is-primary:hover, .bk-exports button.is-primary:hover {
  background: var(--gold-soft);
  color: var(--topbar-ink);
}

/* The explanation: a disclosure, but a plain text line. The global details{}
   frame and the browser's own marker would otherwise draw two empty boxes
   around it, which is the lesson of v0.91. */
details.plainnote {
  background: none;
  border: 0;
  border-radius: 0;
  padding: 0;
  margin: 0 0 var(--s5);
}
details.plainnote[open] { padding-bottom: 0; }
details.plainnote > summary {
  font-family: var(--body);
  font-size: .8125rem;
  color: var(--muted);
  display: inline-flex;
  align-items: center;
  gap: var(--s1);
  min-height: 0;
  padding: var(--s2) 0;
  list-style: none;
  text-decoration: underline;
  text-underline-offset: 2px;
}
details.plainnote > summary::-webkit-details-marker { display: none; }
details.plainnote > summary::before { content: "\25B8"; text-decoration: none; }
details.plainnote[open] > summary::before { content: "\25BE"; }
details.plainnote p { margin: var(--s2) 0 0; }

/* The tabs: ordinary links, so they work with no JavaScript and a ?tab= address
   opens on the right one. They scroll sideways inside their own strip rather
   than taking the page with them. */
.bk-tabs {
  display: flex;
  gap: var(--s2);
  margin: 0 0 var(--s4);
  padding-bottom: var(--s1);
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  border-bottom: 1px solid var(--rule);
}
.bk-tabs a {
  flex: 0 0 auto;
  white-space: nowrap;
  font-family: var(--serif);
  font-size: .9375rem;
  color: var(--muted);
  text-decoration: none;
  padding: var(--s2) var(--s3);
  min-height: var(--tap);
  display: inline-flex;
  align-items: center;
  border-bottom: 3px solid transparent;
  margin-bottom: -5px;
}
.bk-tabs a:hover { color: var(--gold-soft); }
.bk-tabs a.current { color: var(--gold); border-bottom-color: var(--gold); }

/* A tab's header row: its name, and its own two downloads at the right. */
.bk-panel-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s2) var(--s4);
  margin: 0 0 var(--s3);
}
.bk-panel-head h2 { margin: 0; }
.bk-dl { display: flex; gap: var(--s2); margin: 0; flex: 0 0 auto; }

/* A wide table (the ledger is ten columns) scrolls inside its own box, never
   sideways across the page. */
.tablewrap { max-width: 100%; overflow-x: auto; }
.tablewrap .grid { margin-bottom: var(--s4); }
/* A long free-text cell would otherwise set a min-content width that pushes the
   ledger's money columns out of the content column and into that inner scroll.
   Bounded here, the ledger fits the page at desktop width and still scrolls if
   some row is longer than any bound. */
@media (min-width: 820px) {
  .cellclip { display: inline-block; max-width: 22rem; overflow-wrap: anywhere; }
  .cellclip.narrow { max-width: 11rem; }
}

/* The range's total row, in bold at every width; on a phone it is a card like
   any other row, so weight alone marks it. */
.bk-money tfoot tr.total td { font-weight: 700; }
/* It names the whole range, which is longer than a month, so it wraps rather
   than setting a width the table cannot come below (td.night is nowrap). */
.bk-money tfoot tr.total td.night { white-space: normal; }
/* The pending marker: small, muted, and explained by its title attribute. */
.pendingmark { font-size: .8125rem; color: var(--muted); cursor: help; white-space: nowrap; }

@media (min-width: 820px) {
  /* Money right, month and time slot left; figures line up digit under digit. */
  .bk-money th.money, .bk-money td.money { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
  .bk-money tfoot tr.total td { border-top: 2px solid var(--gold); }
}

/* ---------- The public site (v0.99, website merge round 2; §3.28) --------
   Every page is a vertical run of bands on the maroon ground, one idea per
   band, divided by the site's diamond-ended rule. Spacing from the --s scale;
   body copy in a single reading column (.prose, 62ch). */

/* A page head over one of the old site's photographs. The picture is an <img>
   beneath the same dark wash the photo bands use, because the CSP allows no
   inline style; it fills the band and is centre-cropped, never distorted. */
.pagehead.has-photo { overflow: hidden; min-height: 240px; display: flex; flex-direction: column; justify-content: flex-end; }
.pagehead-photo { position: absolute; inset: 0; width: 100%; height: 100%; max-width: none; z-index: 0; }
.pagehead.has-photo::before { z-index: 1; }
.pagehead.has-photo > :not(.pagehead-photo) { z-index: 2; }
@media (min-width: 820px) { .pagehead.has-photo { min-height: 320px; } }

.band { margin: 0 0 var(--s5); }
.band > .prose, .band.prose { max-width: 62ch; }
.band .headline { font-size: clamp(1.6rem, 6vw, 2.4rem); }
.pagecontents { font-family: var(--serif); font-size: .9375rem; color: var(--muted); margin: calc(var(--s5) * -1) 0 var(--s5); line-height: 2; }
.pagecontents a { color: var(--gold-soft); text-decoration: none; white-space: nowrap; }
.pagecontents a:hover { color: var(--gold); text-decoration: underline; }

/* Lists of benefits and features: two-column card grids on desktop, one on a
   phone, each item a short bold lead and, where the old site had one, one
   sentence (merge spec §4 rule 4). */
.cardgrid { list-style: none; margin: 0 0 var(--s5); padding: 0; display: grid; gap: var(--s3); counter-reset: card; }
.cardgrid li, .cardgrid .card { background: var(--panel); border: 1px solid var(--rule); border-left: 4px solid var(--gold); border-radius: var(--radius); padding: var(--s3) var(--s4); display: grid; gap: var(--s1); }
.cardgrid li strong { font-family: var(--serif); color: var(--gold-soft); font-size: 1.02rem; }
.cardgrid li span { font-size: .9375rem; color: var(--ink); }
ol.cardgrid li strong::before { counter-increment: card; content: counter(card) ". "; color: var(--gold); }
.cardgrid.tight { gap: var(--s2); }
.cardgrid.tight li { padding: var(--s2) var(--s3); }
.cardgrid.wide .card h3 { margin: 0 0 var(--s2); font-family: var(--serif); color: var(--gold); font-size: 1.1rem; }
.cardgrid.wide .card p { margin: 0 0 var(--s2); }
@media (min-width: 600px) { .cardgrid { grid-template-columns: repeat(2, minmax(0, 1fr)); } .cardgrid.wide { grid-template-columns: 1fr; } }
@media (min-width: 900px) { .cardgrid.wide { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* Galleries: square, centre-cropped tiles, two across on a phone, four on
   desktop (merge spec §4 rule 3); each opens the full image. */
.gallery { list-style: none; margin: 0 0 var(--s5); padding: 0; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s2); }
.gallery li { margin: 0; position: relative; }
.gallery a { display: block; line-height: 0; border-radius: var(--radius); overflow: hidden; }
.gallery img { width: 100%; aspect-ratio: 1 / 1; height: auto; max-width: 100%; border-radius: var(--radius); display: block; }
.gallery.small { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.gallery.small form { position: absolute; bottom: var(--s1); left: var(--s1); }
@media (min-width: 820px) { .gallery { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--s3); } .twocol .gallery { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* Two columns from 820px: prose beside a picture or a small gallery. */
.twocol { display: grid; gap: var(--s5); align-items: start; }
/* v1.11 §3.14.12: side by side, the photo sits vertically centred beside the text (stacked on phones, unchanged). */
@media (min-width: 820px) { .twocol { align-items: center; } }
.twocol > * { min-width: 0; }
.bandphoto { width: 100%; height: auto; border-radius: var(--radius); }
@media (min-width: 820px) { .twocol { grid-template-columns: minmax(0, 3fr) minmax(0, 2fr); } }

/* Testimonials as quote cards: the quote, then the family's names and characters
   in the gold eyebrow style; long ones keep their full text (rule 5). */
.quotes { display: grid; gap: var(--s4); }
.quotecard { margin: 0; background: var(--panel); border: 1px solid var(--rule); border-radius: var(--radius); padding: var(--s4) var(--s5); }
.quotecard p { margin: 0 0 var(--s3); }
.quotecard p:last-of-type { margin-bottom: 0; }
.quotename { font-family: var(--serif); color: var(--gold); font-size: 1.1rem; margin: 0 0 var(--s2) !important; }
.quotecard footer.eyebrow { margin: var(--s3) 0 0; font-size: .75rem; }
.quotecard.flavour { margin: var(--s5) 0 0; max-width: 62ch; }
/* v1.03: a family's character portrait, beside its first card (the old page's 200px square). */
.quotecard.has-portrait { display: flow-root; }
.quoteportrait { float: left; width: 200px; height: 200px; border-radius: var(--radius); border: 1px solid var(--rule); margin: 0 var(--s4) var(--s3) 0; }
@media (max-width: 600px) { .quoteportrait { float: none; display: block; width: 100%; max-width: 280px; height: auto; aspect-ratio: 1; margin: 0 auto var(--s3); } }
@media (min-width: 900px) { .quotes { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
/* v1.10 §3.12b: Family's testimonials, one card per family, full width, one after another. */
@media (min-width: 900px) { .quotes.families { grid-template-columns: minmax(0, 1fr); } }
.familyquote { margin: 0 0 var(--s4); padding: 0; border: 0; }
.familycard > footer.eyebrow { clear: both; }

.refs li { margin-bottom: var(--s3); font-size: .9375rem; }
.ext { font-size: .75em; margin-left: .2em; }
.cta .actions { display: flex; flex-wrap: wrap; gap: var(--s2); align-items: center; }
.cta .actions .button.primary { width: auto; }
.actions { display: flex; flex-wrap: wrap; gap: var(--s2); }
.motto { font-family: var(--serif); color: var(--gold-soft); font-size: 1.1rem; margin: var(--s4) 0 0; }

/* The Trading Post catalogue: filter chips, then item cards. */
.chips { display: flex; flex-wrap: wrap; gap: var(--s2); margin: 0 0 var(--s4); }
.chips a { font-family: var(--serif); font-size: .875rem; text-decoration: none; color: var(--gold); background: var(--panel); border: 1px solid var(--rule); border-radius: 999px; padding: 6px 14px; min-height: 36px; display: inline-flex; align-items: center; }
.chips a.current { background: var(--red-deep); color: var(--gold); border-color: var(--gold); }
.catalogue { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s3); grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }
.catalogue .item { background: var(--panel); border: 1px solid var(--rule); border-radius: var(--radius); overflow: hidden; display: grid; grid-template-rows: auto 1fr; }
.item-pic { display: block; line-height: 0; }
.item-pic img, .item-pic .nopic { width: 100%; aspect-ratio: 1 / 1; height: auto; display: block; background: var(--sunk); }
.item-body { padding: var(--s2) var(--s3) var(--s3); display: grid; gap: var(--s1); }
.item-body h3 { margin: 0; font-size: 1rem; line-height: 1.25; }
.item-body h3 a { text-decoration: none; }
.item-body .fine { margin: 0; }
.credit { margin: 0; font-size: .875rem; color: var(--muted); }
.credit strong { color: var(--gold); }
@media (min-width: 600px) { .catalogue { grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); } }

/* Forms on the public pages: a choice of radios, and the Saturday list. */
fieldset.choice { border: 1px solid var(--rule); border-radius: var(--radius); padding: var(--s3) var(--s4); display: grid; gap: var(--s2); }
fieldset.choice legend { font-family: var(--serif); color: var(--gold-soft); padding: 0 var(--s2); }
fieldset.choice label.check { align-items: flex-start; }
fieldset.nights label.check small { margin-left: var(--s2); }

/* A video embed keeps 16:9 inside the column; the CSP allows the two hosts. */
.video { position: relative; aspect-ratio: 16 / 9; width: 100%; max-width: 100%; margin: 0 0 var(--s4); border-radius: var(--radius); overflow: hidden; background: var(--sunk); }
.video iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }

/* News. */
.newslist { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s4); }
.newslist li { background: var(--panel); border: 1px solid var(--rule); border-left: 4px solid var(--gold); border-radius: var(--radius); padding: var(--s4) var(--s5); }
.newslist h2 { margin: 0 0 var(--s2); font-size: 1.3rem; }
.newslist h2 a { text-decoration: none; }
.newslist p { margin: 0; }
.posthero { width: 100%; height: auto; border-radius: var(--radius); margin: 0 0 var(--s4); }
.postnav { display: flex; flex-wrap: wrap; gap: var(--s2) var(--s5); margin-top: var(--s5); font-family: var(--serif); }

/* The home page's teaser bands: picture and words, alternating sides on desktop. */
.teasers { display: grid; gap: var(--s5); }
.teaser { display: grid; gap: var(--s4); align-items: center; }
.teaser img { width: 100%; height: auto; aspect-ratio: 4 / 3; border-radius: var(--radius); }
.teaser .headline { font-size: clamp(1.5rem, 5vw, 2rem); margin-bottom: var(--s3); }
@media (min-width: 820px) {
  .teaser { grid-template-columns: minmax(0, 2fr) minmax(0, 3fr); }
  .teaser:nth-child(even) img { order: 2; }
}

/* The Family tag on a tile, the page and the schedule card. */
.tag-family { background: #e8f2e6; color: #1f5a2a; }
.compass { text-align: center; margin: var(--s6) 0 0; }
.compass img { max-width: 240px; height: auto; }

/* The footer's map, the same on every page. */
.colophon .map { grid-column: 1 / -1; }
.colophon .map iframe { width: 100%; height: 220px; border: 0; border-radius: var(--radius); display: block; }
.colophon p small { color: rgba(255,255,255,.6); }

/* ---------- v1.00: the old landing page's hero and gallery, and the lightbox --------
   The hero is the Divi section rebuilt: the dragon art edge to edge under the
   same dark wash the heroes use, the gold griffon centred over it, the title in
   the display face, the line beneath. The art is an <img> (no inline style). */
.denhero { position: relative; overflow: hidden; margin-top: 0; margin-bottom: var(--s6); padding: 48px var(--hero-pad) 40px; background: var(--ground-dk); text-align: center; min-height: 360px; display: grid; align-items: center; }
.denhero-art { position: absolute; inset: 0; width: 100%; height: 100%; max-width: none; object-position: center; z-index: 0; }
.denhero::before { content: ""; position: absolute; inset: 0; background: linear-gradient(180deg, rgba(0,0,0,.40) 0%, rgba(0,0,0,.72) 100%); z-index: 1; }
.denhero-body { position: relative; z-index: 2; display: grid; justify-items: center; gap: var(--s3); }
.denhero-logo { width: min(46vw, 260px); height: auto; filter: drop-shadow(0 6px 18px rgba(0,0,0,.6)); }
.denhero-title { font-family: var(--display); font-weight: 400; color: var(--gold); font-size: clamp(2.2rem, 9vw, 4.2rem); line-height: 1.05; margin: 0; letter-spacing: .02em; text-transform: uppercase; text-shadow: 0 2px 12px rgba(0,0,0,.7); }
.denhero .lede { color: var(--ink); margin: 0; font-size: clamp(1rem, 2.6vw, 1.25rem); }
/* The large photograph above the tiles keeps its own shape, then the eight tiles. */
.gallery.large { grid-template-columns: minmax(0, 1fr); margin-bottom: var(--s2); }
.gallery.large img { aspect-ratio: 4 / 3; }
@media (min-width: 820px) { .gallery.large { grid-template-columns: minmax(0, 1fr); } }
/* The lightbox: one image over a dark ground, its caption, arrows and a close. */
.lightbox { border: 0; padding: 0; background: transparent; max-width: 100vw; max-height: 100vh; width: 100vw; height: 100vh; margin: 0; color: var(--ink); }
.lightbox::backdrop { background: rgba(0,0,0,.92); }
.lightbox figure { margin: 0; width: 100%; height: 100%; display: grid; grid-template-rows: minmax(0, 1fr) auto; align-items: center; justify-items: center; padding: var(--s6) var(--s5); box-sizing: border-box; }
.lightbox img { max-width: 100%; max-height: 100%; width: auto; height: auto; border-radius: var(--radius); }
.lightbox figcaption { padding: var(--s3) 0 0; font-family: var(--serif); color: var(--muted); text-align: center; }
.lightbox button { position: absolute; background: rgba(0,0,0,.55); border: 1px solid var(--rule); color: var(--gold); font-size: 1.8rem; line-height: 1; width: var(--tap); height: var(--tap); min-height: 0; padding: 0; border-radius: 50%; display: grid; place-items: center; }
.lightbox button:hover { background: rgba(0,0,0,.8); color: var(--gold-soft); }
.lightbox-close { top: var(--s3); right: var(--s3); }
.lightbox-prev { left: var(--s2); top: 50%; transform: translateY(-50%); }
.lightbox-next { right: var(--s2); top: 50%; transform: translateY(-50%); }
.lightbox.is-single .lightbox-prev, .lightbox.is-single .lightbox-next, .lightbox.is-single .lightbox-count { display: none; }
.lightbox-count { position: absolute; left: var(--s3); top: var(--s3); font-size: .8125rem; color: var(--muted); font-family: var(--serif); padding: 6px 10px; background: rgba(0,0,0,.55); border-radius: 999px; }
/* Admin -> Boardgame Club: the host picker sits inline in its row. */
.hostform { display: flex; gap: var(--s2); align-items: center; flex-wrap: wrap; }
.hostform select { width: auto; min-width: 12em; max-width: 100%; }

/* ---------- v1.00 §3.32: the admin's in-place editor ---------------------- */
/* v1.08 §3.32b: the Edit strip, plain, at the very bottom of the page below the footer; it scrolls with the page. */
.edbar { display: flex; flex-wrap: wrap; gap: var(--s2); align-items: center; padding: var(--s3) var(--hero-pad, var(--s3)); background: #000; border-top: 1px solid var(--rule); }
.edbar .button, .edbar button { width: auto; margin: 0; }
.edbar-note { color: var(--muted); flex-basis: 100%; }
/* v1.10 §3.32c: what a save stored, said for a few seconds once the page is back out of edit mode. */
.ed-toast { position: fixed; top: var(--s3); left: 50%; transform: translateX(-50%); z-index: 80; max-width: calc(100vw - 2 * var(--s3)); padding: var(--s2) var(--s4); background: #000; color: var(--ink); border: 1px solid var(--gold); border-radius: var(--radius); box-shadow: 0 6px 24px rgba(0,0,0,.5); transition: opacity .8s; }
.ed-toast.is-gone { opacity: 0; }
/* v1.11 §3.14.13: the back-to-top arrow, bottom right, once the page is scrolled; out of sight over text. */
.totop { position: fixed; right: var(--s3); bottom: var(--s3); z-index: 55; width: 48px; height: 48px; margin: 0; padding: 0; display: grid; place-items: center;
  border-radius: 50%; border: 1px solid var(--gold); background: rgba(0,0,0,.78); color: var(--gold); box-shadow: 0 4px 16px rgba(0,0,0,.5);
  opacity: 0; pointer-events: none; transition: opacity .25s; cursor: pointer; }
.totop svg { width: 26px; height: 26px; fill: currentColor; }
.totop.is-shown { opacity: 1; pointer-events: auto; }
.totop.is-shown.is-over-text { opacity: 0; pointer-events: none; }
.totop:hover, .totop:focus-visible { background: #000; color: var(--gold-soft); }
@media (prefers-reduced-motion: reduce) { .totop { transition: none; } }
/* v1.10: Site-wide text's formatted text field and its colour pickers. */
.richfield { display: grid; gap: var(--s1); }
.ed-richfield { min-height: 4em; padding: var(--s2) var(--s3); background: var(--panel-2); border: 1px solid var(--rule); border-radius: var(--radius); }
.ed-richfield > :last-child { margin-bottom: 0; }
/* v1.12 §3.32e: every multi-line box is a formatted-text field (public/richfield.js). */
.ed-richfield { max-width: 100%; overflow-wrap: anywhere; }
.ed-richfield:focus { outline: 2px solid var(--gold); outline-offset: 2px; }
.ed-richfield:empty::before { content: attr(data-placeholder); color: var(--muted); }
.ed-richfield.is-readonly { opacity: .85; }
.richfield-note { margin: var(--s1) 0 0; }
.colourfield { display: flex; gap: var(--s2); align-items: center; }
.colourfield input[type=color] { width: 3.2em; height: 2.2em; padding: 2px; flex: 0 0 auto; }
/* v1.10 §3.32d: the text toolbar, over the page while a text block is being edited. */
.ed-rtb { position: fixed; top: var(--s2); left: 50%; transform: translateX(-50%); z-index: 75; display: flex; flex-wrap: wrap; gap: 4px; max-width: calc(100vw - 2 * var(--s2)); padding: 6px; background: #000; border: 1px solid var(--gold); border-radius: var(--radius); box-shadow: 0 6px 24px rgba(0,0,0,.5); }
.ed-rtb button { width: auto; margin: 0; min-width: 2.4em; }
body.is-editing .ed-rich:focus { outline-style: solid; }
.edbar-note.is-error { color: #ffb4c0; }
body.is-editing [data-ed] { outline: 2px dashed rgba(210,172,42,.7); outline-offset: 3px; border-radius: 3px; min-height: 1em; cursor: text; }
body.is-editing [data-ed]:focus-within, body.is-editing [data-ed]:hover { outline-color: var(--gold-soft); outline-style: solid; }
body.is-editing [data-ed-kind="image"], body.is-editing [data-ed-kind="gallery"] a, body.is-editing [data-ed-kind="link"] { cursor: pointer; }
body.is-editing [data-ed-part] { outline: 1px dotted rgba(210,172,42,.6); padding: 0 2px; }
.ed-textarea { width: 100%; min-height: 96px; font: inherit; font-size: .95rem; line-height: 1.45; color: var(--ink); background: var(--panel-2); border: 1px solid var(--rule); border-radius: var(--s2); padding: var(--s2) var(--s3); box-sizing: border-box; resize: vertical; }
.ed-textarea.ed-rich { font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: .875rem; }
.ed-listhost { display: block !important; grid-column: 1 / -1; padding: 0 !important; border: 0 !important; background: none !important; }
.ed-listhost .fine { margin: var(--s1) 0 0; }
.gallery .ed-add { display: grid; place-items: center; aspect-ratio: 1 / 1; border: 2px dashed var(--rule); border-radius: var(--radius); }
.ed-addbtn { width: auto; min-height: var(--tap); }
.eddlg { width: min(92vw, 560px); }
.eddlg label { display: grid; gap: var(--s1); }
.eddlg input[type="text"] { width: 100%; box-sizing: border-box; }
.ed-preview { max-width: 100%; max-height: 240px; width: auto; height: auto; border-radius: var(--radius); border: 1px solid var(--rule); display: block; margin: 0 0 var(--s3); }
.ed-preview[src=""] { display: none; }
.fine-lines { list-style: none; margin: 0 0 var(--s2); padding: 0; }
.fine-lines li { font-size: .8125rem; color: var(--muted); margin: 0; }
.inline-rich p { display: inline; margin: 0; }
.chips.plain li { font-family: var(--serif); font-size: .875rem; color: var(--gold); background: var(--panel); border: 1px solid var(--rule); border-radius: 999px; padding: 6px 14px; min-height: 36px; display: inline-flex; align-items: center; }
.colophon .address p { margin: 0; }

/* ---------- The menu (rebuilt v1.01, ruled 2026-09-22; §3.14) -------------
   Five headings, each opening its items. Below 900px the menu button opens a
   full-width panel and each heading is a collapsible section; from 900px the
   headings are a row on the right of the black header, each opening its list
   below it on hover or click. public/menu.js adds html.js and runs the keyboard;
   with no JavaScript every section in the panel is open, and on a desktop a
   list opens on hover or when focus is inside it. */
.mainnav .navtop, .mainnav .navlist { list-style: none; margin: 0; padding: 0; }
.mainnav .navtop { padding: 6px 0 10px; }
.mainnav .navhead, .mainnav .navitem {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s2);
  width: 100%; min-height: var(--tap); padding: 10px var(--pad); margin: 0;
  color: var(--gold); font-family: var(--serif); font-size: 1rem; letter-spacing: .04em;
  text-decoration: none; text-align: left; background: none; border: 0; border-radius: 0;
  border-bottom: 1px solid rgba(210,172,42,.14); cursor: pointer; line-height: 1.2;
}
.mainnav .navhead { font-family: var(--display); font-size: 1.2rem; }
.mainnav .navitem { padding-left: calc(var(--pad) + 16px); font-size: .98rem; }
.mainnav .navhead:hover, .mainnav .navitem:hover { background: rgba(210,172,42,.12); color: var(--gold-soft); }
.mainnav .navitem.current { color: #fff; background: rgba(210,172,42,.2); }
.mainnav .navhead.current { color: #fff; }
.mainnav form { margin: 0; }
.navcaret { width: 8px; height: 8px; border-right: 2px solid currentColor; border-bottom: 2px solid currentColor; transform: rotate(45deg); margin-top: -4px; flex: 0 0 auto; }
.navhead[aria-expanded="true"] .navcaret { transform: rotate(-135deg); margin-top: 4px; }
.navsolo .navcaret { display: none; }
/* In the panel: sections collapse once the script runs; without it all are open. */
.js .mainnav .navlist { display: none; }
.js .mainnav .navgroup.is-open .navlist { display: block; }

@media (min-width: 900px) {
  .masthead { flex-wrap: nowrap; }
  .menu-toggle { display: none; }
  .menu-panel { display: block; width: auto; margin: 0 0 0 auto; background: none; border-top: 0; }
  .mainnav .navtop { display: flex; align-items: center; justify-content: flex-end; flex-wrap: wrap; gap: 4px; padding: 0; }
  .mainnav .navgroup { position: relative; }
  .mainnav .navhead { width: auto; min-height: 40px; padding: 6px 12px; border-bottom: 2px solid transparent; border-radius: 4px; white-space: nowrap; font-size: 1.25rem; gap: 8px; }
  .mainnav .navhead:hover { background: none; color: var(--gold-soft); }
  .mainnav .navhead.current { border-bottom-color: var(--gold); }
  /* The list drops below its heading, over the page. */
  .mainnav .navlist, .js .mainnav .navlist { display: none; position: absolute; top: 100%; left: 0; z-index: 50; min-width: 13rem; padding: 6px 0;
    background: #0b0b0b; border: 1px solid rgba(210,172,42,.35); border-radius: var(--radius); box-shadow: 0 10px 28px rgba(0,0,0,.55); }
  /* From the third heading on, a list opens leftward from the heading's right edge, so it never leaves the page. */
  .mainnav .navgroup:nth-child(n+3) .navlist { left: auto; right: 0; }
  .mainnav .navgroup.is-open .navlist, html:not(.js) .mainnav .navgroup:hover .navlist, html:not(.js) .mainnav .navgroup:focus-within .navlist { display: block; }
  .mainnav .navitem { padding: 10px 16px; white-space: nowrap; border-bottom: 0; }
}
/* v1.02: the one hero's button, over the art. */
.denhero-cta { margin: var(--s2) 0 0; }
.denhero-cta .button.primary { width: auto; padding-left: 28px; padding-right: 28px; }
/* Admin → Rooms (v1.02): each day, the two rooms side by side from 600px. */
.roomdays { display: grid; gap: var(--s4); }
.roomday { margin: 0; background: var(--panel); border: 1px solid var(--rule); border-radius: var(--radius); padding: var(--s3) var(--s4); }
.roomday h3 { margin: 0 0 var(--s2); }
.roomcols { display: grid; gap: var(--s3); }
.roomcol h4 { margin: 0 0 var(--s1); font-family: var(--serif); color: var(--gold-soft); }
.roomtaken { margin-bottom: var(--s1); }
.roomfree { margin: 0; }
@media (min-width: 600px) { .roomcols { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
/* The participants badge (v1.02, §3.35): one per game, colour-coded, text always present. */
.tag-badge { font-style: normal; white-space: nowrap; }
.badge-adult { background: #2b2b2b; color: #f2ece6; border: 1px solid rgba(210,172,42,.45); }
.badge-family { background: #e8f2e6; color: #1f5a2a; }
.badge-kids { background: #e5eef9; color: #1d3f73; }
.badge-inclusive-kids { background: #f3e8f8; color: #5a2370; }
.mbadge { display: block; font-size: .6875rem; line-height: 1.2; padding: 0 4px; border-radius: 3px; width: max-content; max-width: 100%; overflow: hidden; }

/* v1.02: an item with a sub-list (Events → Past Events): the link, its own
   disclosure button beside it, and the sub-list indented below, open by
   default without JavaScript. */
.mainnav .navsub { display: flex; flex-wrap: wrap; align-items: stretch; }
.mainnav .navsub > .navitem { flex: 1 1 auto; width: auto; }
.mainnav .navsubtoggle { flex: 0 0 auto; width: var(--tap); min-height: var(--tap); padding: 0; margin: 0; display: grid; place-items: center; background: none; border: 0; border-bottom: 1px solid rgba(210,172,42,.14); color: var(--gold); border-radius: 0; }
.mainnav .navsubtoggle .navcaret { transform: rotate(-45deg); margin: 0; }
.mainnav .navsubtoggle[aria-expanded="true"] .navcaret { transform: rotate(45deg); margin-top: -4px; }
.mainnav .navsublist { list-style: none; margin: 0; padding: 0; flex: 1 0 100%; }
.mainnav .navsublist .navitem { padding-left: calc(var(--pad) + 32px); font-size: .92rem; }
.js .mainnav .navsublist { display: none; }
.js .mainnav .navsub.is-open > .navsublist { display: block; }
@media (min-width: 900px) {
  .mainnav .navsubtoggle { border-bottom: 0; min-height: 0; }
  .mainnav .navsublist .navitem { padding-left: 32px; }
}

/* v1.02: Past Events as an accordion, each event a collapsed drawer. */
.drawers { display: grid; gap: var(--s3); }
.drawer { background: var(--panel); border: 1px solid var(--rule); border-radius: var(--radius); }
.drawer > summary { cursor: pointer; list-style: none; display: flex; flex-wrap: wrap; justify-content: space-between; align-items: baseline; gap: var(--s2); padding: var(--s3) var(--s4); min-height: var(--tap); font-family: var(--serif); color: var(--gold); font-size: 1.1rem; }
.drawer > summary::-webkit-details-marker { display: none; }
.drawer > summary::after { content: ""; width: 8px; height: 8px; border-right: 2px solid currentColor; border-bottom: 2px solid currentColor; transform: rotate(45deg); margin-left: auto; align-self: center; }
.drawer[open] > summary::after { transform: rotate(-135deg); }
.drawer-date { color: var(--muted); font-size: .875rem; }
.drawer-body { padding: 0 var(--s4) var(--s4); }
.drawer-body .band { margin-bottom: var(--s4); }
.pastgame-hero { display: block; width: 100%; height: auto; border-radius: var(--radius); margin-bottom: var(--s3); }
.pastgame-events { margin: 0 0 var(--s3); padding-left: 1.2em; }
.pastgame-pics { display: grid; gap: var(--s3); max-width: 720px; }

/* v1.03 §3.38: the one-time newsletter question, over the dashboard. */
dialog.nlask { position: fixed; inset: auto var(--s4) var(--s4) auto; max-width: min(92vw, 420px); z-index: 70; margin: 0; background: var(--panel-2); color: var(--ink); border: 1px solid var(--gold); border-radius: var(--radius); padding: var(--s4) var(--s5); box-shadow: 0 10px 30px rgba(0,0,0,.6); }
dialog.nlask h2 { margin: 0 0 var(--s2); font-size: 1.2rem; }
dialog.nlask .actions { display: flex; gap: var(--s2); }
.newsletter-toggle p { display: flex; flex-wrap: wrap; gap: var(--s2); align-items: center; }
/* The home page's mailing list band. */
.mailinglist form { display: flex; flex-wrap: wrap; gap: var(--s2); align-items: end; }
.mailinglist label { flex: 1 1 14rem; }
.mailinglist button { width: auto; }
.tag-owner { background: var(--gold); color: #1a0d00; font-style: normal; vertical-align: middle; font-size: .75rem; }

/* ---- v1.05 §3.39a: the media library ------------------------------------------ */
.dropzone { border: 2px dashed var(--rule); border-radius: var(--radius); padding: var(--s4); margin: 0 0 var(--s4); text-align: center; }
.dropzone.is-over { border-color: var(--gold); background: rgba(249, 202, 42, .06); }
.dropzone label.button { display: inline-block; width: auto; }
.dropzone-list { list-style: none; margin: var(--s2) 0 0; padding: 0; text-align: left; }
.dropzone-list .is-error, .ed-picker .is-error { color: #ffb4c0; }
.mediafilters { display: flex; flex-wrap: wrap; gap: var(--s3); align-items: end; margin: var(--s4) 0 var(--s2); }
.mediafilters label { flex: 1 1 160px; margin: 0; }
.mediafilters .check { flex: 0 0 auto; }
.mediafilters button { width: auto; }
.mediaviews .current { color: var(--ink); text-decoration: none; font-weight: 600; }
.bulkbar { display: flex; flex-wrap: wrap; gap: var(--s2); align-items: center; }
.bulkbar button { width: auto; margin: 0; }
.mediagrid { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: var(--s3); }
.mediagrid li { position: relative; background: var(--panel); border: 1px solid var(--rule); border-radius: var(--radius); }
.mediagrid li.is-unattached { border-style: dashed; }
.mediapick { position: absolute; top: 4px; left: 4px; z-index: 1; margin: 0; background: rgba(0,0,0,.6); border-radius: 4px; padding: 2px 4px; }
.mediatile { display: block; text-decoration: none; color: var(--ink); }
.mediatile img { display: block; width: 100%; height: auto; aspect-ratio: 1; border-radius: var(--radius) var(--radius) 0 0; }
.mediakind { display: grid; place-items: center; aspect-ratio: 1; font-family: var(--serif); color: var(--gold); font-size: 1.2rem; }
.mediatitle { display: block; padding: var(--s1) var(--s2); font-size: .8rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mediaitem-grid { display: grid; gap: var(--s4); }
@media (min-width: 900px) { .mediaitem-grid { grid-template-columns: minmax(0, 3fr) minmax(0, 2fr); } }
.mediaitem-preview img, .mediaitem-preview video { display: block; max-width: 100%; height: auto; border-radius: var(--radius); }
.mediaitem-preview audio { width: 100%; }
.facts { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: var(--s1) var(--s3); margin: 0 0 var(--s3); }
.facts dt { color: var(--muted); }
.facts dd { margin: 0; overflow-wrap: anywhere; }
.copylink { display: flex; gap: var(--s2); }
.copylink input { flex: 1 1 auto; min-width: 0; }
.copylink button { width: auto; margin: 0; }
.mediausage { padding-left: 1.2em; }
.mediausage small { color: var(--muted); }
.imgedit-tools { display: flex; flex-wrap: wrap; gap: var(--s2); align-items: end; margin-bottom: var(--s3); }
.imgedit-tools label { margin: 0; flex: 0 1 180px; }
.imgedit-tools button { width: auto; margin: 0; }
.imgedit-stage { position: relative; display: inline-block; max-width: 100%; overflow: hidden; touch-action: none; user-select: none; cursor: crosshair; }
.imgedit-stage canvas { display: block; max-width: 100%; height: auto; }
.imgedit-box { position: absolute; border: 2px solid var(--gold); box-shadow: 0 0 0 9999px rgba(0,0,0,.55); pointer-events: none; }
#imgedit-note.is-error { color: #ffb4c0; }
/* The page editor's library picker. */
.cropdlg.ed-picker { width: min(900px, 92vw); max-width: min(900px, 92vw); }
.ed-pickgrid { list-style: none; margin: var(--s3) 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(110px, 1fr)); gap: var(--s2); max-height: 55vh; overflow: auto; }
.ed-pick { display: block; width: 100%; padding: 0; margin: 0; background: var(--panel); border: 2px solid var(--rule); border-radius: var(--radius); color: var(--ink); text-align: left; }
.ed-pick.is-picked { border-color: var(--gold); }
.ed-pick img { display: block; width: 100%; height: auto; aspect-ratio: 1; }
.ed-pick span { display: block; padding: 2px 4px; font-size: .75rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ed-bgthumb { width: 96px; height: 64px; vertical-align: middle; margin-right: var(--s2); border-radius: 4px; }
.gallery li.is-dragging { opacity: .4; }
.gallery li.is-dropping { outline: 2px solid var(--gold); }
.video.is-file video { display: block; width: 100%; height: auto; }
.ed-videobtn { width: auto; margin-top: var(--s2); }

/* ---- v1.08 §3.39g: section bands, drawn as the old Divi sites drew them --------------------------------
   docs/legacy-site/parallax-audit.md. Each section with a background is its own full-width band with hard
   edges: its picture sits in .bandbg, a box exactly as tall as the band that hides whatever leaves it
   (Divi's .et_parallax_bg_wrap); public/parallax.js widens that box to the screen's sides when the band sits
   in a narrower column. The picture is .bandimg (Divi's .et_parallax_bg): cover, 50% 0% unless the focal
   point moves it, no repeat. The overlay is ::after (Divi's .et_parallax_gradient), black gradients.
     .m-css   Divi's CSS parallax: the picture and its overlay are fixed to the screen.
     .m-true  Divi's True Parallax: public/parallax.js sets the picture's height and moves it.
     .m-none  a plain background that scrolls with its band.
   On a phone or tablet (html.divi-mobile, Divi's user-agent test) and under prefers-reduced-motion the
   fixed pictures scroll with their band and True Parallax never starts, as Divi's fallback did. */
.bgband { position: relative; isolation: isolate; }
/* v1.09: a section band holds its last line's margin inside it, so nothing of the page shows below its edge. */
section.bgband { display: flow-root; }
/* v1.10 §3.39k: bands sit flush against each other: no margin between a band and the band after it. */
.bgband:has(+ .bgband) { margin-bottom: 0; }
.bgband + .bgband { margin-top: 0; }
.bandbg { position: absolute; inset: 0; z-index: -1; overflow: hidden; pointer-events: none; background: var(--ground); }
.bandimg { position: absolute; left: 0; right: 0; top: 0; bottom: 0; background-repeat: no-repeat; background-size: cover; background-position: 50% 0%; }
.bandbg::after { content: ""; position: absolute; inset: 0; }
.m-css > .bandimg, .m-css::after { background-attachment: fixed; }
.m-true > .bandimg { top: auto; height: 100%; }
.divi-mobile .m-css > .bandimg, .divi-mobile .m-css::after { background-attachment: scroll; }
@media (prefers-reduced-motion: reduce) { .m-css > .bandimg, .m-css::after { background-attachment: scroll; } }
.ov-0::after { background-image: linear-gradient(rgba(0,0,0,0) 0%, rgba(0,0,0,0) 100%); }
.ov-5::after { background-image: linear-gradient(rgba(0,0,0,0.05) 0%, rgba(0,0,0,0.05) 100%); }
.ov-10::after { background-image: linear-gradient(rgba(0,0,0,0.1) 0%, rgba(0,0,0,0.1) 100%); }
.ov-15::after { background-image: linear-gradient(rgba(0,0,0,0.15) 0%, rgba(0,0,0,0.15) 100%); }
.ov-20::after { background-image: linear-gradient(rgba(0,0,0,0.2) 0%, rgba(0,0,0,0.2) 100%); }
.ov-25::after { background-image: linear-gradient(rgba(0,0,0,0.25) 0%, rgba(0,0,0,0.25) 100%); }
.ov-30::after { background-image: linear-gradient(rgba(0,0,0,0.3) 0%, rgba(0,0,0,0.3) 100%); }
.ov-35::after { background-image: linear-gradient(rgba(0,0,0,0.35) 0%, rgba(0,0,0,0.35) 100%); }
.ov-40::after { background-image: linear-gradient(rgba(0,0,0,0.4) 0%, rgba(0,0,0,0.4) 100%); }
.ov-45::after { background-image: linear-gradient(rgba(0,0,0,0.45) 0%, rgba(0,0,0,0.45) 100%); }
.ov-50::after { background-image: linear-gradient(rgba(0,0,0,0.5) 0%, rgba(0,0,0,0.5) 100%); }
.ov-55::after { background-image: linear-gradient(rgba(0,0,0,0.55) 0%, rgba(0,0,0,0.55) 100%); }
.ov-60::after { background-image: linear-gradient(rgba(0,0,0,0.6) 0%, rgba(0,0,0,0.6) 100%); }
.ov-65::after { background-image: linear-gradient(rgba(0,0,0,0.65) 0%, rgba(0,0,0,0.65) 100%); }
.ov-66::after { background-image: linear-gradient(rgba(0,0,0,0.66) 0%, rgba(0,0,0,0.66) 100%); }
.ov-70::after { background-image: linear-gradient(rgba(0,0,0,0.7) 0%, rgba(0,0,0,0.7) 100%); }
.ov-75::after { background-image: linear-gradient(rgba(0,0,0,0.75) 0%, rgba(0,0,0,0.75) 100%); }
.ov-80::after { background-image: linear-gradient(rgba(0,0,0,0.8) 0%, rgba(0,0,0,0.8) 100%); }
.ov-85::after { background-image: linear-gradient(rgba(0,0,0,0.85) 0%, rgba(0,0,0,0.85) 100%); }
.ov-90::after { background-image: linear-gradient(rgba(0,0,0,0.9) 0%, rgba(0,0,0,0.9) 100%); }
.ov-95::after { background-image: linear-gradient(rgba(0,0,0,0.95) 0%, rgba(0,0,0,0.95) 100%); }
.ov-gd-hero::after { background-image: linear-gradient(rgba(0,0,0,0.4) 0%, rgba(0,0,0,0.72) 100%); }
.ov-gd-footer::after { background-image: linear-gradient(rgba(0,0,0,0.9) 1%, rgba(0,0,0,0.26) 100%); }
.ov-prd-rules::after { background-image: linear-gradient(#000000 0%, rgba(0,0,0,0.5) 5%, rgba(0,0,0,0.25) 100%); }
.ov-prd-vending::after { background-image: linear-gradient(#000000 0%, rgba(0,0,0,0.5) 5%, rgba(0,0,0,0.5) 100%); }
.ov-prd-footer::after { background-image: linear-gradient(rgba(0,0,0,0.35) 0%, rgba(0,0,0,0.9) 100%); }
.ov-prd-head::after { background-image: linear-gradient(rgba(0,0,0,0.35) 85%, #000000 100%); }
.ov-head-wash::after { background-image: linear-gradient(180deg, rgba(12,2,2,0.86) 0%, rgba(12,2,2,0.78) 45%, rgba(12,2,2,0.9) 100%); }
.ov-prd-savebig::after { background-image: linear-gradient(rgba(10,10,10,0.5) 0%, rgba(10,10,10,0.33) 53%, rgba(10,10,10,0.79) 85%, #0a0a0a 100%); }
.bandmh-1 { min-height: 200px; }
.bandmh-2 { min-height: 300px; }
.bandmh-3 { min-height: 400px; }
.bandmh-4 { min-height: 500px; }
.bandmh-5 { min-height: 600px; }
.bandmh-6 { min-height: 700px; }
.bandmh-7 { min-height: 800px; }
.bandmh-8 { min-height: 900px; }
.pageband > .bandbg { background: transparent; }
/* v1.09 §3.39i: Plain (no picture): the site's own ground, with no picture, no watermark and no overlay. */
.bandbg.is-plain, .pageband > .bandbg.is-plain { background: var(--ground); }
.bandbg.is-plain::after, .bandbg.is-plain > .bandimg { display: none; }
/* v1.10 §3.39j: every page head is a band; its picture and wash are its band's (.bandbg), never its own. */
.pagehead.bgband { background-color: transparent; background-image: none; }
.pagehead.bgband::before { display: none; }
.pagehead.is-tall { min-height: 240px; display: flex; flex-direction: column; justify-content: flex-end; }
@media (min-width: 820px) { .pagehead.is-tall { min-height: 320px; } }
.denhero.bgband { background: transparent; }
.denhero.bgband::before { display: none; }
/* A page head band has no picture of its own and no box: its band's picture shows through. */
.pagehead.on-layer { background: transparent; min-height: 240px; display: flex; flex-direction: column; justify-content: flex-end; }
.pagehead.on-layer::before { display: none; }
@media (min-width: 820px) { .pagehead.on-layer { min-height: 320px; } }
.mdband { padding: 50px 0; margin: 0; }
@media (min-width: 981px) { .mdband { padding: 4vw 0; } }
/* The contents list beside a banded article stays above the bands' pictures. */
.portal-contents { z-index: 1; }

/* ---- v1.05 §3.14.8: Admin > Menu ---------------------------------------------- */
.menutree .menulevel { list-style: none; margin: 0; padding: 0; }
.menutree .menulevel .menulevel { margin-left: var(--s5); }
.menuitem { margin: var(--s1) 0; }
.menurow { display: flex; flex-wrap: wrap; gap: var(--s2); align-items: center; padding: var(--s2); background: var(--panel); border: 1px solid var(--rule); border-radius: var(--radius); }
.menurow .grip { cursor: grab; color: var(--muted); }
.menukind { font-size: .75rem; color: var(--muted); min-width: 4.5em; }
.menurow .m-label { flex: 1 1 160px; min-width: 0; margin: 0; }
.menurow .m-href { flex: 2 1 200px; min-width: 0; margin: 0; }
.menuacts { display: flex; gap: 4px; flex-wrap: wrap; }
.menuacts button { width: auto; margin: 0; min-width: 2.2em; }
.menudrop { font-size: .75rem; color: var(--muted); padding: 2px var(--s2); border: 1px dashed transparent; border-radius: var(--radius); }
.menuitem.is-dragging { opacity: .4; }
.menurow.drop-before { box-shadow: 0 -3px 0 var(--gold); }
.menurow.drop-after { box-shadow: 0 3px 0 var(--gold); }
.menudrop.drop-into { border-color: var(--gold); color: var(--gold); }
.menuactions button { width: auto; }
#menu-note.is-error { color: #ffb4c0; }
.menupreviews { display: flex; flex-wrap: wrap; gap: var(--s4); margin: var(--s4) 0; }
.menupreviews figure { margin: 0; }
.menupreviews figcaption { font-size: .8rem; color: var(--muted); margin-bottom: var(--s1); }
.menupreview-desk { width: 640px; max-width: 100%; height: 420px; overflow: hidden; border: 1px solid var(--rule); border-radius: var(--radius); }
.menupreview-desk iframe { width: 1280px; height: 840px; border: 0; transform: scale(.5); transform-origin: 0 0; }
.menupreview-phone { width: 266px; height: 490px; overflow: hidden; border: 1px solid var(--rule); border-radius: var(--radius); }
.menupreview-phone iframe { width: 380px; height: 700px; border: 0; transform: scale(.7); transform-origin: 0 0; }
body.is-editing .ed-menulabel { outline: 1px dashed var(--gold); outline-offset: 2px; cursor: text; }
/* v1.06 §3.39d: the page editor's Backgrounds panel: beside the page, not over it, so the page's own
   layer is the desktop preview while the controls are open. */
.cropdlg.ed-bgpanel { position: fixed; inset: var(--s3) var(--s3) auto auto; margin: 0; width: min(380px, calc(100vw - 2 * var(--s3))); max-height: calc(100vh - 2 * var(--s3)); overflow: auto; z-index: 70; background: #170505; box-shadow: 0 8px 28px rgba(0,0,0,.6); }
.ed-bgbox { border: 1px solid var(--rule); border-radius: var(--radius); padding: var(--s2) var(--s3); }
.ed-bgbox summary { cursor: pointer; font-family: var(--serif); color: var(--gold); }
.ed-bgrow button { width: auto; }
.ed-focal { position: relative; cursor: crosshair; line-height: 0; }
.ed-focal img { display: block; width: 100%; height: auto; border-radius: 4px; }
.ed-focaldot { position: absolute; width: 14px; height: 14px; margin: -7px 0 0 -7px; border: 2px solid #fff; border-radius: 50%; box-shadow: 0 0 0 2px rgba(0,0,0,.6); pointer-events: none; }
/* v1.14 §3.39l: the dot is dragged (touch included) and its place typed as two numbers; a section following the
   page default shows the default's point, dimmed, and cannot move it. */
.ed-focal { touch-action: none; user-select: none; }
.ed-focal.is-following { cursor: default; opacity: .6; }
.ed-focalnums { display: flex; flex-wrap: wrap; gap: var(--s2) var(--s4); align-items: baseline; margin: 0; }
.ed-focalnums label { display: inline-flex; align-items: baseline; gap: var(--s1); }
.ed-focalnum { width: 5em; }
.ed-phone { width: 228px; height: 420px; overflow: hidden; border: 1px solid var(--rule); border-radius: var(--radius); }
.ed-phone iframe { width: 380px; height: 700px; border: 0; transform: scale(.6); transform-origin: 0 0; }

/* ---- v1.16 §3.18.2, §3.40: the sign-in code, Get the app, the install banner ---------------------------- */
.codebox { font-size: 1.5rem; letter-spacing: .3em; max-width: 9em; font-variant-numeric: tabular-nums; }
.installhint { font-size: .9375rem; color: var(--muted); }
.in-app .installhint, .in-app .installbanner { display: none; }
.installbanner { display: flex; flex-wrap: wrap; gap: var(--s2) var(--s4); align-items: center; justify-content: space-between;
  padding: var(--s3) var(--s4); margin: 0 0 var(--s5); border: 1px solid var(--gold); border-radius: var(--radius); }
.installbanner[hidden] { display: none; }
.installbanner p { margin: 0; }
.appinstall .installsteps li { margin: 0 0 var(--s2); }
.appinstall .benefits li { margin: 0 0 var(--s1); }
.claimbanner { padding: var(--s4); margin: 0 0 var(--s5); border: 2px solid var(--gold); border-radius: var(--radius); }
.claimbanner p { margin: 0 0 var(--s3); }
.level { font-variant-numeric: tabular-nums; }
