/* Google Fonts substitutes per the Coder Design System: Lay Grotesk -> Plus
 * Jakarta Sans, FT System Mono -> IBM Plex Mono. @import MUST be first. */
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600&family=IBM+Plex+Mono:wght@400;500&display=swap');

/* Wall of Names — Coder brand styling
 * Tokens + type from the Coder Brand Design System. */

/* ─── Brand fonts ─────────────────────────────────────────────── */
@font-face {
  font-family: "Lay Grotesk";
  font-weight: 400;
  font-display: swap;
  src: url("fonts/LayGrotesk-Regular.woff2") format("woff2");
}
@font-face {
  font-family: "Lay Grotesk";
  font-weight: 500;
  font-display: swap;
  src: url("fonts/LayGrotesk-Medium.woff2") format("woff2");
}
@font-face {
  font-family: "Lay Grotesk";
  font-weight: 600;
  font-display: swap;
  src: url("fonts/LayGrotesk-Semibold.woff2") format("woff2");
}
@font-face {
  font-family: "FT System Mono";
  font-weight: 500;
  font-display: swap;
  src: url("fonts/FTSystemMono-Medium.woff2") format("woff2");
}

:root {
  --coder-ink: rgb(9, 11, 11);
  --coder-ink-soft: rgb(47, 45, 51);
  --coder-white: rgb(255, 255, 255);
  --coder-purple-900: rgb(41, 36, 68);
  --coder-purple-600: rgb(117, 17, 226); /* primary electric purple */
  --coder-purple-400: rgb(188, 124, 255);
  --coder-magenta: rgb(240, 141, 255);
  --coder-cyan: rgb(1, 242, 255);
  --coder-green: rgb(102, 255, 171);
  --coder-blue: rgb(72, 147, 252);
  --coder-coral: rgb(255, 128, 103); /* warm coral — warning/attention */
  --coder-ink-soft-2: rgb(58, 63, 75); /* off dot */
  --muted: rgba(255, 255, 255, 0.7);
  --border: rgba(255, 255, 255, 0.15);
}

* { box-sizing: border-box; }

body {
  margin: 0;
  min-height: 100vh;
  /* Coder brand supergraphic (surface-reflection-dark), full-bleed cover, with a
   * LIGHT brand-tinted scrim on top so the supergraphic reads while keeping the
   * name cards legible. The image is the dominant layer; the gradient only
   * deepens the center slightly. */
  background:
    radial-gradient(1200px 700px at 50% 42%, rgba(9, 11, 11, 0.55), rgba(9, 11, 11, 0.18) 70%, transparent),
    url("assets/bg-surface-reflection-dark.png") center / cover no-repeat,
    var(--coder-ink);
  background-attachment: scroll, scroll, scroll;
  color: var(--coder-white);
  font-family: "Lay Grotesk", "Plus Jakarta Sans", ui-sans-serif, system-ui, sans-serif;
  display: flex;
  flex-direction: column;
}

header {
  display: flex;
  align-items: center;
  gap: 1.2rem;
  padding: 1.5rem clamp(1rem, 3vw, 2.5rem) 1.3rem;
  border-bottom: 1px solid var(--border);
  background: rgba(9, 11, 11, 0.5);
}
header .brand {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  flex: 0 0 auto;
}
header .logo {
  height: 26px;
  width: auto;
  opacity: 0.95;
}
header .wordmark {
  font-size: 1.05rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--coder-white);
  white-space: nowrap;
}
header .cta {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.3rem;
  text-align: center;
}
header .cta-sub {
  font-family: "FT System Mono", "IBM Plex Mono", monospace;
  font-size: 0.8rem;
  letter-spacing: 0.02em;
  color: var(--muted);
  margin-top: 0.1rem;
}
header .cta-sub b { color: var(--coder-cyan); font-weight: 600; }
#prompt-rotator {
  transition: opacity 0.35s ease;
  display: inline-block;
  min-width: 11ch;
  text-align: center;
}
#prompt-rotator.fading { opacity: 0; }
header .cta-step {
  font-size: 1.05rem;
  color: var(--muted);
}
header .cta-step:first-child {
  font-size: 1.25rem;
  font-weight: 600;
  color: var(--coder-white);
}
header .cta a {
  color: var(--coder-cyan);
  text-decoration: none;
  border-bottom: 2px solid var(--coder-cyan);
  text-shadow: 0 0 18px rgba(1, 242, 255, 0.45);
}
header code,
footer code {
  font-family: "FT System Mono", "IBM Plex Mono", ui-monospace, monospace;
  background: rgba(255, 255, 255, 0.08);
  border: 1px solid var(--border);
  padding: 0.1em 0.4em;
  border-radius: 0.4em;
  font-size: 0.85em;
}

main#wall {
  flex: 1;
  /* Not a rigid grid: each name owns a REGION sized by constraints. The regions
     flex-wrap and center; their width is clamped between --card-min and
     --card-max (count-driven density tiers), so a single name is a sane box
     (never full-width) and many names shrink + wrap to fit one screen. The art
     fills its whole region (no fixed canvas, no letterbox). */
  display: flex;
  flex-wrap: wrap;
  align-content: center;
  justify-content: center;
  gap: var(--gap, 1rem);
  padding: 1.5rem clamp(0.8rem, 3vw, 4rem);
  overflow: hidden;
}

.name {
  font-family: "Lay Grotesk", "Plus Jakarta Sans", sans-serif;
  font-size: clamp(1.7rem, 4vw, 3.4rem);
  font-weight: 600;
  letter-spacing: -0.02em;
  text-shadow: 0 0 28px currentColor;
  transition: transform 0.2s ease;
}
.name--enter {
  animation: pop 0.45s cubic-bezier(0.2, 0.9, 0.3, 1.3);
}
.name--highlight {
  animation: highlight-pulse 0.9s ease-out;
}
@keyframes highlight-pulse {
  0%   { box-shadow: 0 0 0 4px rgba(1,242,255,0.9); }
  100% { box-shadow: 0 0 0 4px rgba(1,242,255,0); }
}
.name:hover { transform: scale(1.08) rotate(-1deg); }
.name small {
  display: block;
  font-family: "FT System Mono", "IBM Plex Mono", monospace;
  font-size: 0.32em;
  font-weight: 500;
  color: var(--muted);
  text-shadow: none;
  text-align: center;
  margin-top: 0.2em;
}

@keyframes pop {
  from { opacity: 0; transform: scale(0.6); }
  to   { opacity: 1; transform: scale(1); }
}

footer {
  text-align: center;
  color: var(--muted);
  padding: 1.4rem;
  font-size: 0.9rem;
}
footer .accent { color: var(--coder-purple-400); }



/* ─── Footer link ─────────────────────────────────────────────── */
footer a { color: inherit; text-decoration: none; border-bottom: 1px solid var(--border); }
footer a:hover { color: var(--coder-purple-400); }

/* ─── Empty state ─────────────────────────────────────────────── */
.wall-empty {
  color: var(--muted);
  font-size: 1.1rem;
  text-align: center;
}


/* ─── Activity pill (green) — hover reveals admin label ──────────── */
.activity {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  flex: 0 0 auto;
  font-family: "FT System Mono", "IBM Plex Mono", monospace;
  font-size: 0.85rem;
  color: var(--coder-ink);
  background: var(--coder-green);
  padding: 0.32em 0.9em;
  border-radius: 999px;
  box-shadow: 0 0 24px rgba(102, 255, 171, 0.45);
  cursor: default;
}
.activity .dot {
  width: 0.6em; height: 0.6em;
  border-radius: 50%;
  background: var(--coder-ink);
  animation: blink 1.6s ease-in-out infinite;
}
.activity .activity-admin { display: none; font-weight: 500; }
.activity:hover .activity-label { display: none; }
.activity:hover .activity-admin { display: inline; }
@keyframes blink { 0%,100% { opacity: 1; } 50% { opacity: 0.3; } }


/* ─── PR queue panel (admin) ──────────────────────────────────── */
.queue {
  position: fixed;
  bottom: 1.2rem;
  right: 1.2rem;
  width: min(320px, 30vw);
  max-height: 42vh;
  overflow-y: auto;
  background: rgba(21, 24, 33, 0.92);
  border: 1px solid var(--border);
  border-radius: 14px;
  padding: 1rem 1.1rem;
  backdrop-filter: blur(8px);
  box-shadow: 0 8px 40px rgba(0,0,0,0.5);
  z-index: 20;
}
.queue-title {
  font-family: "FT System Mono", "IBM Plex Mono", monospace;
  font-size: 0.9rem;
  font-weight: 500;
  margin-bottom: 0.6rem;
  color: var(--coder-cyan);
}
.queue-title #queue-count {
  background: var(--coder-purple-600);
  color: #fff;
  border-radius: 999px;
  padding: 0 0.5em;
  margin-left: 0.3em;
}
.queue ul { list-style: none; margin: 0; padding: 0; }
.queue li {
  font-size: 0.85rem;
  padding: 0.35em 0;
  border-bottom: 1px solid var(--border);
  display: flex; gap: 0.4em; align-items: baseline;
  animation: pop 0.35s ease;
}
.queue li:last-child { border-bottom: none; }
.queue .pr-user { color: var(--coder-purple-400); font-weight: 600; text-decoration: none; }
.queue a.pr-user:hover { text-decoration: underline; }
.queue .pr-title { flex: 1; color: var(--coder-white); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-decoration: none; }
.queue a.pr-title:hover { text-decoration: underline; }
.queue .pr-num { color: var(--muted); font-family: "FT System Mono", "IBM Plex Mono", monospace; text-decoration: none; }
.queue a.pr-num:hover { color: var(--coder-cyan); }
.queue .queue-empty { color: var(--muted); border: none; }


/* ─── Name regions (each entry is a responsive box; the art fills it) ──────── */
#wall .name {
  /* A flex item that is its own region. Width clamps between the density
     min/max; the box keeps a stable aspect ratio so the art has a predictable
     shape to design against, but it is NOT a fixed pixel canvas — the iframe
     fills 100%/100% and the author's CSS lays out responsively inside it. */
  flex: 1 1 var(--card-min, 240px);
  min-width: var(--card-min, 240px);
  max-width: var(--card-max, 320px);
  aspect-ratio: var(--card-aspect, 4 / 3);
  border-radius: 14px;
  overflow: hidden;
  background: rgba(255,255,255,0.03);
  border: 1px solid var(--border);
  box-shadow: 0 6px 30px rgba(0,0,0,0.3);
  position: relative; /* anchor the hover "fullscreen" button */
}
#wall .name-frame {
  /* The iframe IS the region — fills it completely, no fixed canvas, no
     transform/scale, no letterbox. The author's html/body is told to be
     width:100%;height:100% so their art covers the whole box at any size. */
  width: 100%;
  height: 100%;
  border: 0;
  display: block;
  background: transparent;
}

/* ─── Per-card "fullscreen" button (top-right, on hover) ───────────────────── */
.name-expand {
  position: absolute;
  top: 6px;
  right: 6px;
  z-index: 3;
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  padding: 0;
  border-radius: 8px;
  border: 1px solid rgba(255,255,255,0.18);
  background: rgba(9,11,11,0.55);
  backdrop-filter: blur(4px);
  color: rgba(255,255,255,0.92);
  cursor: pointer;
  opacity: 0;
  transform: translateY(-2px) scale(0.9);
  transition: opacity 0.15s ease, transform 0.15s ease, background 0.15s ease;
}
.name:hover .name-expand,
.name-expand:focus-visible {
  opacity: 1;
  transform: translateY(0) scale(1);
}
.name-expand:hover {
  background: rgba(117,17,226,0.85); /* coder purple */
  border-color: rgba(188,124,255,0.7);
}
/* Don't let the card's hover scale distort the button hit area on touch. */
@media (hover: none) {
  .name-expand { opacity: 1; transform: none; }
}

/* ─── Single-name modal ("fullscreen" preview) ─────────────────────────────── */
.name-modal[hidden] { display: none !important; }
.name-modal {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: grid;
  place-items: center;
}
.name-modal .nm-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(5,6,8,0.78);
  backdrop-filter: blur(6px);
  opacity: 0;
  transition: opacity 0.2s ease;
}
.name-modal.open .nm-backdrop { opacity: 1; }
.name-modal .nm-dialog {
  position: relative;
  z-index: 1;
  width: min(88vw, 1100px);
  max-height: 90vh;
  display: flex;
  flex-direction: column;
  opacity: 0;
  transform: scale(0.94);
  transition: opacity 0.2s ease, transform 0.2s cubic-bezier(0.2,0.9,0.3,1.25);
}
.name-modal.open .nm-dialog { opacity: 1; transform: scale(1); }
.name-modal .nm-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0 0.25rem 0.6rem;
  color: #fff;
}
.name-modal .nm-title {
  font-family: "Lay Grotesk", "Plus Jakarta Sans", sans-serif;
  font-weight: 600;
  font-size: 1.05rem;
  letter-spacing: -0.01em;
  color: rgba(255,255,255,0.92);
}
.name-modal .nm-links {
  display: flex;
  gap: 8px;
  margin-right: auto;
  margin-left: 0.9rem;
}
.name-modal .nm-link {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 30px;
  padding: 0 11px;
  border-radius: 9px;
  font-family: "FT System Mono", "IBM Plex Mono", monospace;
  font-size: 0.72rem;
  font-weight: 500;
  line-height: 1;
  text-decoration: none;
  color: rgba(255,255,255,0.92);
  background: rgba(255,255,255,0.06);
  border: 1px solid rgba(255,255,255,0.18);
  transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}
.name-modal .nm-link:hover {
  background: rgba(117,17,226,0.85);
  border-color: rgba(188,124,255,0.7);
  color: #fff;
}
.name-modal .nm-link-pr {
  color: var(--coder-cyan);
  border-color: rgba(1,242,255,0.32);
}
.name-modal .nm-link-pr:hover {
  background: rgba(1,242,255,0.16);
  border-color: rgba(1,242,255,0.7);
  color: #fff;
}
.name-modal .nm-close {
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  border-radius: 10px;
  border: 1px solid rgba(255,255,255,0.18);
  background: rgba(255,255,255,0.06);
  color: #fff;
  cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease;
}
.name-modal .nm-close:hover {
  background: rgba(255,255,255,0.14);
  border-color: rgba(255,255,255,0.32);
}
.name-modal .nm-stage {
  /* Big canvas keeping the same card aspect ratio so the art looks like a
     scaled-up version of the wall card, not a reflowed layout. */
  width: 100%;
  aspect-ratio: var(--card-aspect, 4 / 3);
  max-height: 78vh;
  border-radius: 18px;
  overflow: hidden;
  background: rgba(9,11,11,1);
  border: 1px solid var(--border);
  box-shadow: 0 24px 80px rgba(0,0,0,0.6);
}
.name-modal .nm-frame {
  width: 100%;
  height: 100%;
  border: 0;
  display: block;
  background: transparent;
}


/* ─── Density tiers (more names → smaller regions, all wrap onto one screen) ──
   Only the min/max width + gap change with count; aspect ratio stays constant
   so a name designed once looks right whether the wall has 1 or 200 entries. */
body[data-density="cozy"]    #wall { --card-min: 260px; --card-max: 340px; --gap: 1.2rem; }
body[data-density="medium"]  #wall { --card-min: 210px; --card-max: 280px; --gap: 1rem; }
body[data-density="dense"]   #wall { --card-min: 165px; --card-max: 215px; --gap: 0.8rem; }
body[data-density="packed"]  #wall { --card-min: 130px; --card-max: 165px; --gap: 0.6rem; }
body[data-density="huge"]    #wall { --card-min: 100px; --card-max: 128px; --gap: 0.45rem; }


/* ─── Attendee roster (admin) — mirrors the PR queue, bottom-left ──────────── */
.roster {
  position: fixed;
  bottom: 1.2rem;
  left: 1.2rem;
  width: min(300px, 28vw);
  background: rgba(21, 24, 33, 0.92);
  border: 1px solid var(--border);
  border-radius: 14px;
  padding: 1rem 1.1rem;
  backdrop-filter: blur(8px);
  box-shadow: 0 8px 40px rgba(0,0,0,0.5);
  z-index: 20;
}
.roster-title {
  font-family: "FT System Mono", "IBM Plex Mono", monospace;
  font-size: 0.9rem;
  font-weight: 500;
  color: var(--coder-cyan);
  display: flex; align-items: center; gap: 0.4em;
}
.roster-title #roster-count {
  background: var(--coder-purple-600);
  color: #fff;
  border-radius: 999px;
  padding: 0 0.5em;
}
.roster-hint {
  margin-left: auto;
  font-size: 0.7rem;
  color: var(--muted);
  opacity: 0.8;
}
.roster:hover .roster-hint { opacity: 0; }
/* List collapsed by default, revealed on hover; capped height + scroll so 100+
   participants don't overwhelm the screen. */
.roster ul {
  list-style: none; margin: 0; padding: 0;
  max-height: 0;
  overflow-y: auto;
  opacity: 0;
  transition: max-height 0.25s ease, opacity 0.2s ease, margin-top 0.25s ease;
}
.roster:hover ul {
  max-height: min(60vh, 28rem);
  opacity: 1;
  margin-top: 0.6rem;
}
.roster ul::-webkit-scrollbar { width: 6px; }
.roster ul::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }
.roster li.roster-row {
  font-size: 0.85rem;
  padding: 0.35em 0;
  border-bottom: 1px solid var(--border);
  display: flex; gap: 0.5em; align-items: center;
}
.roster li.roster-row:last-child { border-bottom: none; }
.roster .r-dot {
  width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto;
  background: var(--muted);
}
.roster .r-dot.on {
  background: var(--coder-green);
  box-shadow: 0 0 8px var(--coder-green);
}
.roster .r-dot.off { background: var(--coder-ink-soft-2); }
.roster .r-user {
  flex: 1; color: var(--coder-white); font-weight: 600;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.roster .r-badge {
  font-family: "FT System Mono", "IBM Plex Mono", monospace;
  font-size: 0.7rem;
  padding: 0.05em 0.5em;
  border-radius: 999px;
  flex: 0 0 auto;
}
.roster .r-badge.ok { color: var(--coder-green); border: 1px solid rgba(102,255,171,0.4); }
.roster .r-badge.need {
  color: var(--coder-coral); border: 1px solid rgba(255,128,103,0.5);
  background: rgba(255,128,103,0.08); cursor: help;
}
.roster .roster-empty { color: var(--muted); border: none; font-size: 0.85rem; }


/* ─── PR queue status badges ──────────────────────────────────────────────── */
.queue .pr-status {
  font-family: "FT System Mono", "IBM Plex Mono", monospace;
  font-size: 0.68rem;
  padding: 0.05em 0.5em;
  border-radius: 999px;
  flex: 0 0 auto;
  white-space: nowrap;
  border: 1px solid transparent;
}
.queue .pr-status.st-reviewing          { color: var(--coder-cyan); border-color: rgba(1,242,255,0.4); }
.queue .pr-status.st-re-review          { color: var(--coder-cyan); border-color: rgba(1,242,255,0.4); }
.queue .pr-status.st-changes-requested  { color: var(--coder-coral); border-color: rgba(255,128,103,0.5); background: rgba(255,128,103,0.08); }
.queue .pr-status.st-conflicts          { color: var(--coder-coral); border-color: rgba(255,128,103,0.5); background: rgba(255,128,103,0.08); }
.queue .pr-status.st-approved           { color: var(--coder-green); border-color: rgba(102,255,171,0.4); }
.queue .pr-status.st-queued             { color: var(--muted); border-color: var(--border); }
/* let the title shrink so the badge fits */
.queue .pr-title { min-width: 0; }


/* ─── Status filter bar (Hiring / Open to work / …) ───────────────────────── */
.filterbar {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  justify-content: center;
  padding: 0.3rem 1rem 0.7rem;
}
.filter-chip {
  font-family: "FT System Mono", "IBM Plex Mono", monospace;
  font-size: 0.72rem;
  letter-spacing: 0.02em;
  color: var(--muted);
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--border);
  border-radius: 9999px;
  padding: 0.3em 0.85em;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 0.45em;
  transition: background 0.15s, border-color 0.15s, color 0.15s;
}
.filter-chip:hover { color: var(--coder-white); border-color: rgba(255, 255, 255, 0.3); }
.filter-chip.empty { opacity: 0.45; }
.filter-chip.empty:hover { opacity: 0.7; }
.filter-chip .fc-count {
  font-size: 0.72rem;
  color: var(--coder-white);
  font-weight: 600;
}
.filter-chip .fc-dash { color: var(--muted); }
/* Active chip: brand accent. Each status gets its own brand color when active. */
.filter-chip.on { color: var(--coder-ink); border-color: transparent; }
.filter-chip.on .fc-count, .filter-chip.on .fc-dash { color: var(--coder-ink); }
.filter-chip[data-key="all"].on       { background: var(--coder-white); }
.filter-chip[data-key="hiring"].on     { background: var(--coder-cyan); }
.filter-chip[data-key="seeking"].on    { background: var(--coder-green); }
.filter-chip[data-key="freelance"].on  { background: var(--coder-coral); }
.filter-chip[data-key="learning"].on   { background: var(--coder-purple-400); }

/* Cards hidden by the active filter fade out and collapse (calm, no jump). */
#wall .name.filtered-out {
  display: none;
}


/* ─── Clickable cards (when the person shared a contact link) ──────────────── */
#wall .name.has-link {
  cursor: pointer;
}
/* Let clicks fall through the iframe to the parent card's click handler. */
#wall .name.has-link .name-frame {
  pointer-events: none;
}
#wall .name.has-link:hover {
  outline: 1px solid rgba(1, 242, 255, 0.45);
  box-shadow: 0 0 0 1px rgba(1, 242, 255, 0.25), 0 8px 30px rgba(1, 242, 255, 0.12);
}


/* ───────────────────────────────────────────────────────────────────────────
   MOBILE  (≤640px)
   The default layout targets a projector (one fixed screen, no scroll, cards
   shrink by count). On phones we instead let the wall SCROLL with comfortably
   sized cards, stack the header, and hide the admin-only corner panels.
   ─────────────────────────────────────────────────────────────────────────── */
@media (max-width: 640px) {
  html, body { height: auto; min-height: 100%; overflow-y: auto; }
  header {
    flex-direction: column;
    align-items: center;
    gap: 0.85rem;
    padding: 1.1rem 1.1rem 1rem;
    text-align: center;
  }
  header .brand { gap: 0.5rem; }
  header .logo { height: 22px; }
  header .wordmark { font-size: 1.05rem; }
  header .cta { gap: 0.5rem; }
  header .cta-step { font-size: 0.95rem; line-height: 1.45; }
  header .cta-step:first-child { font-size: 1.05rem; line-height: 1.4; }
  header .cta-sub { font-size: 0.74rem; line-height: 1.5; margin-top: 0.15rem; }
  /* The activity pill (admin display) is irrelevant on a phone. */
  header .activity { display: none !important; }

  /* Wall: scrollable column of readable cards, NOT a fit-one-screen grid. */
  main#wall {
    align-content: flex-start;
    overflow: visible;        /* let the whole page scroll naturally on a phone */
    flex: 0 0 auto;
    gap: 0.9rem;
    padding: 1rem 0.9rem 2rem;
  }
  /* Ignore the count-driven density tiers on mobile — keep cards a comfortable,
     readable size (two-up on most phones, one-up on very narrow). */
  body[data-density] #wall {
    --card-min: 150px;
    --card-max: 230px;
    --gap: 0.9rem;
  }
  #wall .name {
    flex: 1 1 150px;
    min-width: 150px;
    max-width: none;
  }

  /* Admin-only corner panels overlap content on a phone — hide them. */
  .roster, .queue { display: none !important; }

  /* Filter bar: wrap nicely, comfy tap targets. */
  .filterbar { gap: 0.4rem; padding: 0.4rem 0.8rem 0.8rem; }
  .filter-chip { font-size: 0.78rem; padding: 0.4em 0.9em; }

  footer { font-size: 0.8rem; padding: 0.8rem 1rem; text-align: center; }
  /* No hover-scale on touch (it sticks after a tap). */
  .name:hover { transform: none; }
  #wall .name.has-link:hover { box-shadow: 0 6px 30px rgba(0,0,0,0.3); outline: none; }
}

/* Very narrow phones: single column for legibility. */
@media (max-width: 380px) {
  #wall .name { flex-basis: 100%; min-width: 0; }
}
