/* Public event board on the home page — one event per row, filters beside it.

   Design tokens live here because this sheet is loaded only by index.html.
   They exist mainly so the board stops hardcoding light-theme colours: the tag
   chips, the LIVE badge and every shadow used to be fixed light values, which
   read as lit patches on a dark page. Each one now has a dark counterpart.

   Depth comes from layered shadow over a hairline ring rather than a 1px grey
   border — borders make every surface equally loud, so nothing leads. */
:root {
  --bd-radius: 16px;
  --bd-radius-sm: 10px;
  --bd-accent: #7c3aed;
  --bd-accent-hover: #6d28d9;
  --bd-accent-soft: rgba(124, 58, 237, 0.09);
  --bd-accent-line: rgba(124, 58, 237, 0.20);
  --bd-accent-glow: rgba(124, 58, 237, 0.16);
  --bd-ring: rgba(16, 18, 27, 0.08);
  --bd-ring-strong: rgba(16, 18, 27, 0.14);
  --bd-shadow: 0 1px 2px rgba(16, 18, 27, 0.04), 0 6px 16px -12px rgba(16, 18, 27, 0.24);
  --bd-shadow-lift: 0 2px 4px rgba(16, 18, 27, 0.05), 0 18px 34px -20px rgba(16, 18, 27, 0.35);
  --bd-live: #dc2626;
  --bd-live-ink: #b91c1c;
  --bd-live-soft: #fee2e2;
  --bd-live-line: #fecaca;
  --bd-off: #b45309;
  --bd-off-ink: #92400e;
  --bd-off-soft: #fef3c7;
  --bd-off-line: #fcd34d;
  --bd-inset: rgba(16, 18, 27, 0.05);
}

[data-theme="dark"] {
  --bd-accent: #a78bfa;
  --bd-accent-hover: #c4b5fd;
  --bd-accent-soft: rgba(167, 139, 250, 0.13);
  --bd-accent-line: rgba(167, 139, 250, 0.26);
  --bd-accent-glow: rgba(167, 139, 250, 0.22);
  --bd-ring: rgba(255, 255, 255, 0.07);
  --bd-ring-strong: rgba(255, 255, 255, 0.13);
  --bd-shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 8px 20px -14px rgba(0, 0, 0, 0.7);
  --bd-shadow-lift: 0 2px 6px rgba(0, 0, 0, 0.45), 0 22px 42px -22px rgba(0, 0, 0, 0.85);
  /* Desaturated so a LIVE chip reads as a label, not a warning light. */
  --bd-live: #f87171;
  --bd-live-ink: #fca5a5;
  --bd-live-soft: rgba(248, 113, 113, 0.13);
  --bd-live-line: rgba(248, 113, 113, 0.28);
  --bd-off: #fbbf24;
  --bd-off-ink: #fcd34d;
  --bd-off-soft: rgba(251, 191, 36, 0.14);
  --bd-off-line: rgba(251, 191, 36, 0.34);
  --bd-inset: rgba(0, 0, 0, 0.25);
}

.board-section {
  margin: 0 0 2.25rem;
}

.board-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  margin-bottom: 0.9rem;
}
.board-head h2 {
  margin: 0;
  font-size: 1.45rem;
  font-weight: 700;
  letter-spacing: -0.02em;
}
.board-count {
  padding: 0.1rem 0.55rem;
  border-radius: 999px;
  background: var(--bd-accent-soft);
  color: var(--bd-accent);
  font-size: 0.78rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* Search sits above everything — the fastest path to one known event. */
.board-search {
  position: relative;
  margin-bottom: 1.1rem;
}
.board-search input {
  width: 100%;
  padding: 0.85rem 2.6rem 0.85rem 2.7rem;
  font: inherit;
  color: var(--text, #1a1a1a);
  background: var(--surface, #fff);
  border: 1px solid var(--bd-ring);
  border-radius: 999px;
  box-shadow: var(--bd-shadow);
  transition: border-color 0.15s, box-shadow 0.15s;
}
.board-search input::placeholder { color: var(--text-muted, #5c5c5c); opacity: 0.75; }
.board-search input:hover { border-color: var(--bd-ring-strong); }
.board-search input:focus {
  outline: none;
  border-color: var(--bd-accent);
  box-shadow: 0 0 0 4px var(--bd-accent-glow);
}
.board-search-icon {
  position: absolute;
  left: 1.05rem;
  top: 50%;
  transform: translateY(-50%);
  opacity: 0.5;
  pointer-events: none;
}
.board-search-clear {
  position: absolute;
  right: 0.55rem;
  top: 50%;
  transform: translateY(-50%);
  border: 0;
  background: none;
  cursor: pointer;
  font-size: 1.1rem;
  line-height: 1;
  padding: 0.3rem 0.45rem;
  border-radius: 50%;
  color: var(--text-muted, #5c5c5c);
}
.board-search-clear:hover { background: var(--border, #e5e7eb); }

.board-layout {
  display: grid;
  grid-template-columns: 220px minmax(0, 1fr);
  gap: 1.75rem;
  align-items: start;
}

/* ── Filters ─────────────────────────────────────────────────────────── */
.board-filters {
  position: sticky;
  top: 1rem;
  background: var(--surface, #fff);
  border: 1px solid var(--bd-ring);
  border-radius: var(--bd-radius);
  box-shadow: var(--bd-shadow);
  padding: 1.1rem 1.15rem 1.2rem;
}
.board-filter-title {
  font-size: 0.72rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-muted, #5c5c5c);
  margin: 0 0 0.75rem;
}
.board-field { margin-bottom: 0.85rem; }
.board-field label {
  display: block;
  font-size: 0.8rem;
  font-weight: 600;
  margin-bottom: 0.3rem;
}
.board-field select {
  width: 100%;
  padding: 0.5rem 0.6rem;
  font: inherit;
  font-size: 0.88rem;
  color: var(--text, #1a1a1a);
  background: var(--surface, #fff);
  border: 1px solid var(--bd-ring-strong);
  border-radius: var(--bd-radius-sm);
  transition: border-color 0.15s, box-shadow 0.15s;
}
.board-field select:focus {
  outline: none;
  border-color: var(--bd-accent);
  box-shadow: 0 0 0 3px var(--bd-accent-glow);
}
.board-reset {
  width: 100%;
  padding: 0.5rem;
  font: inherit;
  font-size: 0.85rem;
  font-weight: 600;
  cursor: pointer;
  color: var(--text-muted, #5c5c5c);
  background: none;
  border: 1px solid var(--bd-ring-strong);
  border-radius: var(--bd-radius-sm);
  transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.board-reset:hover { background: var(--bg, #fafafa); color: var(--text, #1a1a1a); }

/* ── One event per row ───────────────────────────────────────────────── */
.board-list { display: flex; flex-direction: column; gap: 0.75rem; }

.board-row {
  position: relative;
  display: grid;
  grid-template-columns: 140px minmax(0, 1fr) auto;
  gap: 0 1.25rem;
  align-items: center;
  padding: 0.95rem 1.15rem;
  background: var(--surface, #fff);
  border: 1px solid var(--bd-ring);
  border-radius: var(--bd-radius);
  box-shadow: var(--bd-shadow);
  overflow: hidden;
  transition: box-shadow 0.18s ease, transform 0.18s ease, border-color 0.18s ease;
}
.board-row:hover {
  border-color: var(--bd-ring-strong);
  box-shadow: var(--bd-shadow-lift);
  transform: translateY(-2px);
}

/* Status rail. An inset rounded bar rather than border-left: it does not add to
   the box, so a live row lines up with a plain one instead of shifting 4px. */
.board-row::before {
  content: "";
  position: absolute;
  left: 0;
  top: 16px;
  bottom: 16px;
  width: 3px;
  border-radius: 0 3px 3px 0;
  background: transparent;
  transition: background 0.18s ease;
}
.board-row.is-live::before { background: var(--bd-live); }
.board-row.is-today::before { background: var(--bd-accent); }

.board-media {
  width: 140px;
  height: 92px;
  border-radius: 12px;
  object-fit: cover;
  background: var(--bd-inset);
  display: block;
  box-shadow: inset 0 0 0 1px var(--bd-ring);
}
/* Generated date tile shown when an event has no media of its own.
   Colours live here rather than in the JS so the tile follows the theme: on a
   dark page the old hardcoded white card and pale tint read as a lit panel.
   Dark uses lighter ink on a deep tint — same hierarchy, inverted. */
.board-media-ph {
  display: grid;
  place-items: center;
  overflow: hidden;
  background: var(--ph-tint);
  --ph-ink: #64748b;
  --ph-tint: #f4f6f8;
  --ph-card: #ffffff;
  --ph-on-ink: #ffffff;
}
.board-media-ph[data-kind="rsvp"] { --ph-ink: #7c3aed; --ph-tint: #f5f0ff; }
.board-media-ph[data-kind="reg"]  { --ph-ink: #2563eb; --ph-tint: #eff5ff; }

[data-theme="dark"] .board-media-ph {
  --ph-ink: #98a2b3;
  --ph-tint: #1a1b1e;
  --ph-card: #212228;
  /* Ink is light in dark mode, so the month band needs dark text on it. */
  --ph-on-ink: #16161a;
}
[data-theme="dark"] .board-media-ph[data-kind="rsvp"] { --ph-ink: #a78bfa; --ph-tint: #1e1a2b; }
[data-theme="dark"] .board-media-ph[data-kind="reg"]  { --ph-ink: #7aa2f7; --ph-tint: #161d2c; }
.board-media-ph svg {
  width: 100%;
  height: 100%;
  display: block;
  /* SVG text does not inherit the page font automatically in every engine. */
  font-family: 'DM Sans', system-ui, sans-serif;
}

.board-body { min-width: 0; }
.board-title {
  margin: 0 0 0.28rem;
  font-size: 1.12rem;
  font-weight: 680;
  line-height: 1.28;
  letter-spacing: -0.011em;
}
/* Inline text flow rather than flex: with flex, a wrapping meta row stranded
   its separator at the end of the line. As text, the dot stays glued to the
   item it introduces. */
.board-meta {
  margin: 0;
  font-size: 0.855rem;
  line-height: 1.55;
  color: var(--text-muted, #5c5c5c);
}
.board-meta-item { white-space: nowrap; }
.board-meta-item + .board-meta-item::before {
  content: "·";
  margin-right: 0.4rem;
  opacity: 0.42;
}
.board-meta-icon {
  width: 0.92em;
  height: 0.92em;
  margin-right: 0.32em;
  vertical-align: -0.14em;
  opacity: 0.75;
}
.board-desc {
  margin: 0.35rem 0 0;
  font-size: 0.85rem;
  color: var(--text-muted, #5c5c5c);
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
}

.board-tags-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem 0.85rem;
  margin-top: 0.4rem;
  flex-wrap: wrap;
}
.board-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
  margin: 0;
  min-width: 0;
  flex: 1 1 auto;
}
.board-tag {
  padding: 2px 9px;
  border-radius: 999px;
  font-size: 0.72rem;
  font-weight: 600;
  color: var(--bd-accent);
  background: var(--bd-accent-soft);
  border: 1px solid var(--bd-accent-line);
}

.board-live-badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 1px 9px;
  border-radius: 999px;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.03em;
  color: var(--bd-live-ink);
  background: var(--bd-live-soft);
  border: 1px solid var(--bd-live-line);
}
/* A switched-off event, on the staff view of the board. It has to be obvious at a
   glance which rows the public cannot see — the whole reason for showing them
   here is so one does not sit forgotten. */
.board-off-badge {
  display: inline-flex;
  align-items: center;
  padding: 1px 9px;
  border-radius: 999px;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.03em;
  color: var(--bd-off-ink);
  background: var(--bd-off-soft);
  border: 1px solid var(--bd-off-line);
}

.board-row.is-inactive {
  border-color: var(--bd-off-line);
  background: var(--bd-off-soft);
}

/* The accent edge every row already has, recoloured. Together with the badge and
   the Activate button this is enough to pick the row out of a full board —
   hatching the whole card was tried and it fought the media and the body text. */
.board-row.is-inactive::before { background: var(--bd-off); }

/* Dim the media instead: a switched-off event should not look as alive as the
   rest of the board. */
.board-row.is-inactive .board-media,
.board-row.is-inactive .board-media-ph {
  opacity: 0.62;
  filter: saturate(0.7);
}

.board-live-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--bd-live);
  animation: boardPulse 1.8s ease-in-out infinite;
}
@keyframes boardPulse { 0%, 100% { opacity: 1 } 50% { opacity: 0.25 } }
@media (prefers-reduced-motion: reduce) {
  .board-live-dot { animation: none }
  .board-row:hover { transform: none }
}

.board-actions {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  align-items: stretch;
  align-self: stretch;
  justify-content: flex-start;
  min-width: 8.5rem;
}

.board-btn {
  display: inline-block;
  padding: 0.48rem 0.95rem;
  font-size: 0.85rem;
  font-weight: 640;
  text-align: center;
  text-decoration: none;
  border-radius: var(--bd-radius-sm);
  border: 1px solid transparent;
  color: var(--bd-accent);
  background: var(--bd-accent-soft);
  white-space: nowrap;
  cursor: pointer;
  transition: background 0.15s, color 0.15s, box-shadow 0.15s;
}
.board-btn:hover {
  background: var(--bd-accent-line);
  text-decoration: none;
}
.board-btn:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px var(--bd-accent-glow);
}
/* One filled action per row so the eye lands on the thing worth doing. */
.board-btn-primary {
  background: var(--bd-accent);
  color: #fff;
  box-shadow: 0 1px 2px rgba(16, 18, 27, 0.12);
}
.board-btn-primary:hover { background: var(--bd-accent-hover); color: #fff; }
[data-theme="dark"] .board-btn-primary { color: #1b1524; }
[data-theme="dark"] .board-btn-primary:hover { color: #1b1524; }

/* Registration window tag — stands where the Register button would be, so the
   row still reads as "this is the signup slot" while saying it is shut. Sized
   like a button so the action strip keeps its rhythm, but flat and unclickable
   so nobody tries. */
.board-reg-tag {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.42rem 0.9rem;
  border-radius: 999px;
  /* Same amber as the inactive badge: both say "this is switched off", and one
     palette for one meaning. Filled rather than outlined — the tag stands in the
     Register button's slot, and an empty outline there reads as a control that
     failed to load rather than a deliberate state. */
  background: var(--bd-off-soft);
  border: 1px solid var(--bd-off-line);
  color: var(--bd-off-ink);
  font-size: 0.84rem;
  font-weight: 700;
  white-space: nowrap;
}

/* Footer of the detail modal: the way out to the admin edit form. */
.board-detail-foot {
  border-top: 1px solid var(--bd-ring);
  padding-top: 0.9rem;
}
.board-detail-foot .board-btn { width: 100%; }
.board-detail-foot .board-doc-hint { margin: 0.55rem 0 0; }


/* Compact capacity chip — sits at bottom-right; click opens View names (PIN). */
.board-rsvp-progress {
  display: flex;
  flex-direction: column;
  gap: 0.28rem;
  width: 100%;
  margin: auto 0 0;
  padding: 0.4rem 0.6rem;
  border: 1px solid var(--bd-ring);
  border-radius: var(--bd-radius-sm);
  background: var(--bd-inset);
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
  transition: border-color 0.15s, background 0.15s, box-shadow 0.15s;
}
.board-rsvp-progress:hover {
  border-color: var(--bd-accent-line);
  background: var(--bd-accent-soft);
}
.board-rsvp-progress:focus-visible {
  outline: 2px solid var(--event-nav-accent, #7c3aed);
  outline-offset: 2px;
}
.board-rsvp-bar {
  display: block;
  height: 0.32rem;
  border-radius: 999px;
  background: var(--bd-ring-strong);
  overflow: hidden;
}
.board-rsvp-bar > span {
  display: block;
  height: 100%;
  background: linear-gradient(90deg, var(--bd-accent), var(--bd-accent-hover));
  border-radius: inherit;
}
.board-rsvp-count {
  font-size: 0.72rem;
  font-weight: 650;
  line-height: 1.1;
  color: var(--text-muted, #5c5c5c);
  white-space: nowrap;
}

.board-empty {
  padding: 3rem 1.5rem;
  text-align: center;
  font-size: 0.95rem;
  color: var(--text-muted, #5c5c5c);
  background: var(--bd-inset);
  border: 1px solid var(--bd-ring);
  border-radius: var(--bd-radius);
}

.board-day {
  position: sticky;
  top: 0;
  z-index: 2;
  display: flex;
  align-items: center;
  gap: 0.7rem;
  margin: 1.5rem 0 0.6rem;
  padding: 0.35rem 0;
  font-size: 0.74rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--text-muted, #5c5c5c);
  background: var(--bg, #fafafa);
}
/* Hairline running to the right edge, so the label reads as a divider. */
.board-day::after {
  content: "";
  flex: 1 1 auto;
  height: 1px;
  background: var(--bd-ring-strong);
}
.board-day:first-child { margin-top: 0; }

@media (max-width: 860px) {
  .board-layout { grid-template-columns: 1fr; }
  .board-filters { position: static; }
  /* Media above text once the row is too narrow to hold three columns. */
  .board-row { grid-template-columns: 1fr; gap: 0.7rem; }
  .board-media { width: 100%; height: 158px; }
  /* A sticky divider needs the page background to hide rows passing beneath it. */
  .board-day { top: 0; }

  /* Real artwork earns the full-width slot. The generated date tile does not —
     at 158px tall it let one event fill the whole screen. Where the row falls
     back to the placeholder, shrink it to a badge and put the title alongside,
     so a phone shows three or four events instead of one. */
  .board-row:has(.board-media-ph) {
    grid-template-columns: 92px minmax(0, 1fr);
    align-items: start;
    column-gap: 0.85rem;
  }
  .board-row:has(.board-media-ph) .board-media-ph { width: 92px; height: 62px; }
  .board-row:has(.board-media-ph) .board-actions { grid-column: 1 / -1; }
}

@media (max-width: 560px) {
  .board-meta-item { display: block; }
  .board-meta-item + .board-meta-item::before { content: none; }
  .board-actions { flex-direction: row; flex-wrap: wrap; min-width: 0; align-self: stretch; }
  .board-btn { flex: 1 1 auto; }
  .board-rsvp-progress {
    flex: 1 1 6.5rem;
    margin: 0;
    max-width: none;
  }
}

/* ── In session now ───────────────────────────────────────────────────────
   Rooms occupied at this moment, above the event board. One line per room:
   room, who, until when. Hidden entirely whenever nothing is in use, which is
   most of the day — so it must stay cheap to skip and never leave a gap. */
.rooms-now {
  position: relative;
  margin: 0 0 1.5rem;
  padding: 0.95rem 1.15rem 1rem;
  background: var(--surface, #fff);
  border: 1px solid var(--bd-ring);
  border-radius: var(--bd-radius);
  box-shadow: var(--bd-shadow);
  overflow: hidden;
}
.rooms-now::before {
  content: "";
  position: absolute;
  left: 0;
  top: 14px;
  bottom: 14px;
  width: 3px;
  border-radius: 0 3px 3px 0;
  background: var(--bd-accent);
}
.rooms-now[hidden] { display: none !important; }

.rooms-now-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.75rem;
  margin-bottom: 0.5rem;
}
.rooms-now-head h2 {
  margin: 0;
  font-size: 0.78rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted, #5c5c5c);
}
.rooms-now-all {
  font-size: 0.82rem;
  font-weight: 640;
  color: var(--bd-accent);
  text-decoration: none;
  white-space: nowrap;
}
.rooms-now-all:hover { text-decoration: underline; }

.rooms-now-list {
  margin: 0;
  padding: 0;
  list-style: none;
}
.rooms-now-item {
  display: flex;
  align-items: baseline;
  gap: 0.55rem;
  padding: 0.3rem 0;
  font-size: 0.95rem;
  line-height: 1.35;
}
.rooms-now-item + .rooms-now-item {
  border-top: 1px solid var(--bd-ring);
}

/* Filled = in the room now, hollow = doors open, starting within 10 minutes. */
.rooms-now-dot {
  flex: none;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: #16a34a;
  box-shadow: 0 0 0 3px rgba(22, 163, 74, 0.16);
  transform: translateY(-1px);
}
[data-theme="dark"] .rooms-now-dot {
  background: #4ade80;
  box-shadow: 0 0 0 3px rgba(74, 222, 128, 0.15);
}
.rooms-now-dot.is-soon,
[data-theme="dark"] .rooms-now-dot.is-soon {
  background: transparent;
  box-shadow: inset 0 0 0 2px var(--text-muted, #5c5c5c);
  opacity: 0.6;
}

.rooms-now-room {
  font-weight: 700;
  white-space: nowrap;
}
.rooms-now-who {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--text-muted, #5c5c5c);
}
.rooms-now-time {
  flex: none;
  margin-left: auto;
  font-size: 0.85rem;
  font-variant-numeric: tabular-nums;
  color: var(--text-muted, #5c5c5c);
  white-space: nowrap;
}

@media (max-width: 520px) {
  /* Room and time hold the line; the name drops beneath rather than truncating
     to a couple of characters. */
  .rooms-now-item { flex-wrap: wrap; row-gap: 0.1rem; }
  .rooms-now-who {
    flex: 1 0 100%;
    margin-left: 1.1rem;
    font-size: 0.88rem;
  }
}

/* ── Mobile filter collapse ───────────────────────────────────────────────
   On a phone the filter panel used to sit between the search box and the first
   event, so the whole first screen was chrome. Collapsed behind a button, with
   a count so an applied filter is never invisible while hidden. */
.board-filters-toggle {
  display: none;
  width: 100%;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 0.9rem;
  padding: 0.7rem 1rem;
  font: inherit;
  font-weight: 640;
  color: var(--text, #1a1a1a);
  background: var(--surface, #fff);
  border: 1px solid var(--bd-ring);
  border-radius: var(--bd-radius-sm);
  box-shadow: var(--bd-shadow);
  cursor: pointer;
}
.board-filters-count {
  min-width: 1.35rem;
  padding: 0.05rem 0.4rem;
  border-radius: 999px;
  background: var(--bd-accent);
  color: #fff;
  font-size: 0.75rem;
  font-weight: 700;
}
[data-theme="dark"] .board-filters-count { color: #1b1524; }
.board-filters-count[hidden] { display: none; }
.board-filters-chevron {
  margin-left: auto;
  transition: transform 0.15s ease;
}
.board-filters-toggle[aria-expanded="true"] .board-filters-chevron {
  transform: rotate(180deg);
}

@media (max-width: 860px) {
  .board-filters-toggle { display: flex; }
  /* Collapsed by default — the events are why anyone opened the page. */
  .board-filters { display: none; }
  .board-filters.is-open { display: block; }
  /* The button directly above already says "Filters". */
  .board-filters .board-filter-title { display: none; }
}

/* ── Post a document link ─────────────────────────────────────────────────
   Shown in the detail modal when an event names a document but has no link
   behind it yet. Deliberately plain: it is a small utility form inside a
   reading surface, not a call to action competing with the event itself. */
.board-doc-hint {
  margin: 0 0 0.6rem;
  font-size: 0.85rem;
  color: var(--text-muted, #5c5c5c);
}
.board-doc-input {
  width: 100%;
  box-sizing: border-box;
  padding: 0.55rem 0.65rem;
  margin-bottom: 0.5rem;
  font: inherit;
  font-size: 0.92rem;
  color: var(--text, #1a1a1a);
  background: var(--surface, #fff);
  border: 1px solid var(--bd-ring-strong);
  border-radius: var(--bd-radius-sm);
}
.board-doc-input:focus {
  outline: none;
  border-color: var(--bd-accent);
  box-shadow: 0 0 0 3px var(--bd-accent-glow);
}
.board-doc-row {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  flex-wrap: wrap;
}
.board-doc-status {
  font-size: 0.85rem;
  color: var(--text-muted, #5c5c5c);
}
.board-doc-status.is-error { color: #b91c1c; }
[data-theme="dark"] .board-doc-status.is-error { color: #fca5a5; }

/* Signup roster in the detail modal: one wrapping line of numbered names, so a
   long list stays scannable without becoming a column of its own. */
#boardSignupsBody {
  line-height: 1.6;
  word-break: break-word;
}

/* ---------- PIN-gated attendance editor (event detail modal) ---------- */

/* `[hidden]` is only as specific as a class, so the `display: flex` / `grid`
   rules below would otherwise keep the PIN row and the editor on screen while
   the script believes they are hidden — the editor rendered as an empty grid
   before unlocking, and the PIN prompt stayed put after. */
.board-attendee-pin [hidden],
.board-pin-row[hidden],
.board-att[hidden] {
  display: none !important;
}

.board-pin-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  margin-top: 8px;
}

.board-pin-input {
  width: 6.5em;
  letter-spacing: 0.4em;
  text-align: center;
  font: inherit;
  padding: 8px 10px;
  border-radius: 10px;
  border: 1px solid var(--bd-ring-strong);
  background: var(--surface);
  color: var(--text);
  -webkit-text-security: disc;
}

.board-att-input {
  flex: 1 1 12em;
  min-width: 0;
  font: inherit;
  padding: 8px 10px;
  border-radius: 10px;
  border: 1px solid var(--bd-ring-strong);
  background: var(--surface);
  color: var(--text);
}

.board-pin-input:focus,
.board-att-input:focus {
  outline: none;
  border-color: var(--bd-accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--bd-accent) 22%, transparent);
}

.board-pin-status {
  margin: 8px 0 0;
  min-height: 1.2em;
  font-size: 0.86rem;
  color: var(--text-muted);
}

.board-pin-status.is-error { color: var(--bd-live-ink); }

/* Two panels: who is here, and who is still expected. */
.board-att {
  display: grid;
  gap: 12px;
  margin-top: 10px;
}

@media (min-width: 620px) {
  .board-att { grid-template-columns: 1fr 1fr; }
}

.board-att-panel {
  border: 1px solid var(--bd-ring-strong);
  border-radius: 12px;
  padding: 10px 12px 12px;
  background: var(--bd-inset);
  min-width: 0;
}

.board-att-title {
  margin: 0 0 8px;
  font-size: 0.85rem;
  font-weight: 700;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.board-att-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  max-height: 190px;
  overflow-y: auto;
}

.board-att-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border-radius: 999px;
  border: 1px solid var(--bd-ring-strong);
  padding: 5px 10px;
  font: inherit;
  font-size: 0.9rem;
  cursor: pointer;
  background: var(--surface);
  color: var(--text);
  transition: border-color 140ms ease, background 140ms ease;
}

.board-att-chip:hover { border-color: var(--bd-accent); }
.board-att-chip:focus-visible { outline: 2px solid var(--bd-accent); outline-offset: 2px; }

/* Present reads as settled; expected reads as an invitation to tap. */
.board-att-chip--remove {
  background: color-mix(in srgb, var(--bd-accent) 12%, transparent);
  border-color: transparent;
}

.board-att-mark { opacity: 0.6; font-size: 0.95em; line-height: 1; }

/* Serial number on the Here cards — the admin grid's "1. name" look. */
.board-att-order {
  flex: none;
  min-width: 1.5em;
  text-align: right;
  font-size: 0.8em;
  font-variant-numeric: tabular-nums;
  color: var(--text-muted);
}

/* Copy + A–Z sit together at the panel head's right edge. */
.board-att-tools {
  display: flex;
  align-items: center;
  gap: 6px;
}

.board-att-empty {
  margin: 4px 0 0;
  font-size: 0.86rem;
  color: var(--text-muted);
}

/* ---------- attendance: view stage (detail panel) ---------- */

.board-pin-remember {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 10px;
  font-size: 0.86rem;
  color: var(--text-muted);
  cursor: pointer;
}

.board-pin-remember input { width: 16px; height: 16px; accent-color: var(--bd-accent); }

.board-pin-clear {
  display: inline-block;
  margin-top: 8px;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  font-size: 0.84rem;
  color: var(--bd-accent);
  text-decoration: underline;
  cursor: pointer;
}

.board-pin-clear[hidden] { display: none !important; }

/* The one code prompt, opened beneath whichever door was pressed first.
   Boxed so it reads as attached to that button, not as a section of its own. */
.board-pin-prompt {
  margin-top: 10px;
  padding: 10px 12px 12px;
  border-radius: 12px;
  border: 1px dashed var(--bd-ring-strong);
}

.board-pin-prompt .board-doc-hint { margin: 0; }
.board-pin-prompt .board-pin-row { margin-top: 8px; }
.board-pin-prompt[hidden] { display: none !important; }

.board-attendee-view[hidden] { display: none !important; }

.board-attendee-view {
  margin-top: 10px;
  display: grid;
  gap: 10px;
  justify-items: start;
}

.board-attendee-line {
  margin: 0;
  line-height: 1.6;
  overflow-wrap: anywhere;
}

/* ---------- attendance: edit stage (its own modal) ---------- */

/* Bigger than the panels ever were inside the detail modal — this screen has
   nothing else on it, so the lists get the height. */
.board-att--large .board-att-chips { max-height: min(46vh, 420px); }
.board-att--large .board-att-panel { padding: 14px 16px 16px; }

.board-edit-status {
  margin: 12px 0 0;
  min-height: 1.2em;
  font-size: 0.88rem;
  color: var(--text-muted);
}

.board-edit-status.is-error { color: var(--bd-live-ink); }

.board-edit-actions {
  display: flex;
  justify-content: flex-end;
  margin-top: 14px;
}

.board-attendee-view-actions {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}

.board-attendee-view-actions [hidden] { display: none !important; }

/* ---------- attendance editor: sort + drag ---------- */

.board-att-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 8px;
}

.board-att-head .board-att-title { margin: 0; }

.board-att-sort {
  flex: none;
  border: 1px solid var(--bd-ring-strong);
  background: var(--surface);
  color: var(--text-muted);
  border-radius: 8px;
  padding: 3px 8px;
  font: inherit;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  cursor: pointer;
  transition: color 140ms ease, border-color 140ms ease, background 140ms ease;
}

.board-att-sort:hover { color: var(--text); border-color: var(--bd-accent); }
.board-att-sort:focus-visible { outline: 2px solid var(--bd-accent); outline-offset: 2px; }

.board-att-sort.is-active {
  background: var(--bd-accent-soft);
  border-color: var(--bd-accent-line);
  color: var(--bd-accent);
}

/* Drag feedback: the chip fades, and the panel that would receive it lifts. */
.board-att-chip[draggable="true"] { cursor: grab; }
.board-att-chip[draggable="true"]:active { cursor: grabbing; }
.board-att-chip.is-dragging { opacity: 0.4; }

.board-att-panel {
  transition: border-color 140ms ease, background 140ms ease, transform 140ms ease;
}

.board-att-panel.is-drop-target {
  border-color: var(--bd-accent);
  background: var(--bd-accent-soft);
  transform: translateY(-1px);
}

/* While something is in hand, every panel advertises that it can take it. */
.rsvp-modal.is-dragging .board-att-panel:not(.is-drop-target) {
  border-color: var(--bd-ring-strong);
}

@media (prefers-reduced-motion: reduce) {
  .board-att-panel { transition: none; }
  .board-att-panel.is-drop-target { transform: none; }
}

/* ── serving roster (design note §15) ──────────────────────────────────────
   The empty slots are the point: an attendee list answers "who is coming",
   this one answers "what is still unfilled". So an open slot is styled as a
   thing to act on, not as whitespace. */

.board-serving-summary { margin: 0 0 10px; font-size: .86rem; color: var(--text-muted, #605e5c); }
.board-serving-summary.is-open { color: var(--bd-live-ink); font-weight: 600; }

.board-serving-row {
  display: flex; gap: 12px; align-items: baseline; flex-wrap: wrap;
  padding: 7px 0; border-top: 1px solid var(--border, #e1dfdd);
}
.board-serving-row:first-of-type { border-top: 0; }
.board-serving-role {
  flex: 0 0 9.5rem; font-size: .84rem; font-weight: 600;
  display: flex; flex-wrap: wrap; gap: 4px 6px; align-items: baseline;
}
.board-serving-count { font-weight: 400; color: var(--text-muted, #605e5c); }
.board-serving-meta { flex-basis: 100%; font-weight: 400; font-size: .76rem; color: var(--text-muted, #605e5c); }
.board-serving-people { flex: 1 1 12rem; display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
/* A standing instruction for the role, on its own line because these are
   sentences. flex-basis 100% in a wrapping row puts it under both columns
   without disturbing either. */
.board-serving-note {
  flex-basis: 100%; margin-top: 3px;
  font-size: .78rem; line-height: 1.45; color: var(--text-muted, #605e5c);
}

.board-serving-name {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 3px 10px; border-radius: 999px; font-size: .86rem;
  background: var(--bd-accent-soft); color: var(--text, #201f1e);
}
.board-serving-name em { font-style: normal; opacity: .6; font-size: .76rem; }
.board-serving-x {
  border: 0; background: none; cursor: pointer; padding: 0 2px;
  font-size: 1rem; line-height: 1; color: var(--bd-live-ink);
}

/* An unfilled slot reads as a gap while browsing and as a button while editing —
   the same element, so the roster does not reflow when Edit is tapped. */
.board-serving-slot {
  display: inline-flex; align-items: center; padding: 3px 10px;
  border-radius: 999px; font-size: .82rem;
  border: 1px dashed var(--border, #cbc9c7); color: var(--text-muted, #605e5c);
  background: none;
}
button.board-serving-slot { cursor: pointer; border-color: var(--bd-live-ink); color: var(--bd-live-ink); }
/* Adding beyond the planned count is allowed but is not a gap to be filled, so
   it reads in the muted border rather than the open-slot red. */
button.board-serving-slot.is-extra { border-color: var(--border, #cbc9c7); color: var(--text-muted, #605e5c); }
button.board-serving-slot.is-extra:hover { background: var(--bd-inset); }
button.board-serving-slot:hover { background: color-mix(in srgb, var(--bd-live-ink) 8%, transparent); }
.board-serving-row.is-orphan .board-serving-role { color: var(--bd-live-ink); }

.board-serving-picker {
  margin: 10px 0; padding: 10px 12px;
  border: 1px solid var(--border, #e1dfdd); border-radius: 10px;
  background: var(--bd-inset);
}
.board-serving-picker-head { font-size: .84rem; font-weight: 600; margin-bottom: 8px; }
.board-serving-pool { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 10px; }
.board-serving-pick { font-size: .84rem; padding: 4px 12px; }
/* Its own styling, not the PIN field's.
   It used to carry `.board-pin-input`, which is built for a 4-digit code:
   6.5em wide, centred, 0.4em letter-spacing — and `-webkit-text-security: disc`,
   so a typed NAME rendered as dots in every WebKit and Blink browser. Only the
   flex override was applied on top, which also stretched it to dominate the
   panel and pull attention off the pool it is meant to be a fallback for. */
.board-serving-typed {
  flex: 0 1 9rem; min-width: 0;
  font: inherit; font-size: .82rem;
  padding: 4px 10px; border-radius: 8px;
  border: 1px solid var(--border, #cbc9c7);
  /* --bg, not --surface: the same pattern .rsvp-input uses on this page, so the
     field sits slightly recessed against the panel instead of matching it.
     `color: inherit` rather than var(--text) for the same reason it does. */
  background: var(--bg, #fafafa); color: inherit;
}
.board-serving-typed::placeholder { color: var(--text-muted, #605e5c); opacity: .8; }
/* The pool is the primary way in, so the fallback row sits quieter and apart. */
.board-serving-picker .board-pin-row { margin-top: 2px; }
.board-serving-picker .board-pin-row .board-btn { font-size: .82rem; padding: 4px 10px; }

@media (max-width: 520px) {
  .board-serving-role { flex-basis: 100%; }
}
