@import url('https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,300..800&display=swap');

:root {
  --bg: #fafafa;
  --surface: #ffffff;
  --text: #16161d;
  --text-muted: #62626e;
  --border: #e6e6ec;
  --accent-1: #4f46e5;
  --accent-2: #14b8a6;
  --accent-grad: linear-gradient(135deg, var(--accent-1), var(--accent-2));
  --error: #d9376e;
  --success: #0d9488;
  --radius: 14px;
  --shadow: 0 8px 24px rgba(20, 20, 30, 0.06);
  --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;

  /* Message-board chat bubbles (see "Message boards" section below) */
  --chat-bg: #eef1f4;
  --chat-bubble-own: #d9fdd3;
  --chat-bubble-other: #ffffff;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #0f0f14;
    --surface: #17171f;
    --text: #f2f2f5;
    --text-muted: #a3a3ae;
    --border: #2a2a35;
    --shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
    --chat-bg: #0b141a;
    --chat-bubble-own: #005c4b;
    --chat-bubble-other: #202c33;
  }
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font-family: var(--font);
  background: var(--bg);
  color: var(--text);
  line-height: 1.5;
}

.wrap {
  max-width: 1080px;
  margin: 10px auto;
  padding: 0 24px;
}

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

.header-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-top: 18px;
  padding-bottom: 18px;
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-family: "Fraunces", Georgia, "Times New Roman", serif;
  font-size: 1.4rem;
  font-weight: 800;
  letter-spacing: -0.02em;
  color: var(--text);
  text-decoration: none;
  background: var(--accent-grad);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
}

.brand-logo {
  height: 44px;
  width: auto;
  object-fit: contain;
  flex-shrink: 0;
}

.nav {
  display: flex;
  align-items: center;
  gap: 12px;
}

.nav-greeting {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: var(--text-muted);
  font-size: 0.9rem;
  margin-right: 4px;
  text-decoration: none;
}

.nav-greeting:hover { color: var(--text); }

/* Account dropdown (added 2026-10-20) — replaces the separate Log out
   button. Opens on hover for real pointers, and via .is-open (set by
   js/nav-user-menu.js on click/tap) everywhere, so touch screens and the
   Capacitor app work too. The menu sits flush under the button (top:
   100%, with padding-top as the visual gap) so moving the mouse down
   onto it never crosses a dead zone that would close it. */
button.nav-greeting {
  background: none;
  border: 0;
  padding: 4px 0;
  cursor: pointer;
  font-family: inherit;
}
.nav-user { position: relative; }
.nav-user-caret { font-size: 0.75em; opacity: 0.8; }
.nav-user-menu {
  display: none;
  position: absolute;
  right: 0;
  top: 100%;
  padding-top: 6px;
  min-width: 160px;
  z-index: 50;
}
.nav-user.is-open .nav-user-menu { display: block; }
@media (hover: hover) {
  .nav-user:hover .nav-user-menu { display: block; }
}
.nav-user-menu a {
  display: block;
  padding: 10px 14px;
  background: var(--surface);
  color: var(--text);
  text-decoration: none;
  font-size: 0.92rem;
  border-left: 1px solid var(--border);
  border-right: 1px solid var(--border);
}
.nav-user-menu a:first-child {
  border-top: 1px solid var(--border);
  border-radius: var(--radius-sm) var(--radius-sm) 0 0;
}
.nav-user-menu a:last-child {
  border-bottom: 1px solid var(--border);
  border-radius: 0 0 var(--radius-sm) var(--radius-sm);
  box-shadow: 0 10px 24px rgba(20, 20, 30, 0.12);
}
.nav-user-menu a:hover,
.nav-user-menu a:focus-visible { background: var(--border); }

/* Narrow-viewport header — same overflow risk as soberlivingevents.com's
   own stylesheet (see its copy of this comment): a full row of nav
   buttons (Events/Meetings on a sober site/Admin/Reports/greeting/
   Log out) has no wrap/shrink behavior at phone width and can overflow
   past the edge. This file is the shared fallback for 6 of the 8 sites
   (see app/sites.py get_current_site's static_key fallback), three of
   them sober-focused, so they're exposed to the same Meetings-link case. */
@media (max-width: 640px) {
  .header-inner {
    flex-wrap: wrap;
    row-gap: 8px;
    padding-top: 10px;
    padding-bottom: 10px;
  }

  .brand-logo { height: 36px; }

  .nav {
    width: 100%;
    flex-wrap: wrap;
    gap: 8px;
  }

  .nav .btn {
    padding: 6px 14px;
    font-size: 0.85rem;
  }

  .nav-greeting { font-size: 0.82rem; }
}

/* Buttons */
.btn {
  display: inline-block;
  border: 1px solid transparent;
  border-radius: 999px;
  padding: 10px 20px;
  font-size: 0.95rem;
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
  transition: transform 0.15s ease, box-shadow 0.15s ease, opacity 0.15s ease;
}

.btn:hover { transform: translateY(-1px); }

.btn-primary {
  background: var(--accent-grad);
  color: #fff;
  box-shadow: 0 4px 14px rgba(79, 70, 229, 0.35);
}

.btn-ghost {
  background: transparent;
  color: var(--text);
  border-color: var(--border);
}

.btn-danger.btn-ghost { color: var(--error); border-color: var(--error); }

.btn-lg { padding: 14px 28px; font-size: 1.05rem; }
.btn-block { width: 100%; text-align: center; border: none; }

/* Hero */
.hero {
  padding: 96px 0 72px;
  text-align: center;
  background: radial-gradient(ellipse at top, rgba(79, 70, 229, 0.08), transparent 60%);
}

/* "Let us know" suggestion banner — signed-in users only, top of hero,
   full-bleed (a direct child of .hero, ahead of .wrap, so it isn't
   width-constrained). Plain block flow, not flex — flex was splitting
   the sentence's mixed text/<strong>/<button> content into anonymous
   flex items and adding `gap` between all of them (including between
   "there" and "@username"), which is what caused the uneven spacing. */
.hero-announce {
  min-height: 40px;
  padding: 8px 16px;
  margin-bottom: 10px;
  text-align: center;
  background: var(--accent-1);
  color: #fff;
  font-size: 0.9rem;
  line-height: 1.6;
}

.hero-announce-link {
  background: none;
  border: none;
  padding: 0;
  margin: 0;
  color: #fbbf24;
  font: inherit;
  font-weight: 700;
  text-decoration: underline;
  cursor: pointer;
}

.hero-announce-link:hover { opacity: 0.85; }

/* "Post your own event" banner on /events/ and /meetings/ — same blue/
   yellow visual language as .hero-announce above, but an ordinary
   in-content block (rounded corners, inside .wrap) rather than a
   full-bleed hero bar, since these pages have no .hero section to sit
   inside of. */
.inline-cta-banner {
  border-radius: var(--radius);
  padding: 12px 20px;
  margin-bottom: 20px;
  text-align: center;
  background: var(--accent-1);
  color: #fff;
  font-size: 0.9rem;
  line-height: 1.6;
}

.inline-cta-banner a {
  color: #fbbf24;
  font-weight: 700;
  text-decoration: underline;
}

.inline-cta-banner a:hover { opacity: 0.85; }

.hero h1 {
  font-size: clamp(2.2rem, 5vw, 3.4rem);
  font-weight: 800;
  letter-spacing: -0.03em;
  margin: 0 0 20px;
}

.hero-sub {
  color: var(--text-muted);
  font-size: 1.15rem;
  max-width: 560px;
  margin: 0 auto 36px;
}

/* Pitch paragraph — its own row spanning the full page width, between
   the hero (picture/copy/ad) and the buttons */
.hero-pitch { text-align: center; }
.hero-pitch p { margin: 0; font-size: 1.1rem; line-height: 1.6; color: var(--text-muted); }

.hero-actions {
  display: flex;
  gap: 14px;
  justify-content: center;
  flex-wrap: wrap;
}

.hero-inner {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 48px;
}

.hero-copy {
  flex: 1 1 480px;
  max-width: 640px;
}

/* AdSense placeholders (landing page only) — visual boxes only, no ad
   script wired up yet; swap the inner markup for the real AdSense <ins>
   unit when ready. */
.ad-placeholder {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  border: 2px dashed var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--text-muted);
  font-size: 0.75rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.ad-placeholder-hero {
  flex: 0 0 300px;
  aspect-ratio: 300 / 250;
}

.ad-section {
  padding: 10px 24px 0;
}

.ad-placeholder-horizontal {
  width: 100%;
  height: 110px;
  margin-bottom: 5px;
}

/* Inserted every Nth card into a .events-grid (see events/index.html,
   meetings/index.html, ADS_EVERY_N_EVENTS) — spans the full grid row
   instead of sitting in a single card-width cell. */
.ad-placeholder-grid {
  grid-column: 1 / -1;
  width: 100%;
  height: 110px;
}

/* Border around every ad slot (2026-10-20) — frames the AdSense unit,
   or the empty space it reserves before consent/fill. */
.ad-placeholder-hero,
.ad-placeholder-horizontal,
.ad-placeholder-grid {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  box-sizing: border-box;
}


@media (max-width: 760px) {
  .hero-inner { flex-direction: column; }
  .ad-placeholder-hero {
    flex-basis: auto;
    width: 100%;
    max-width: 300px;
    margin: 0 auto;
  }
}

/* Feature cards */
.features {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 20px;
  padding: 20px 24px 96px;
}

.feature-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 28px;
  box-shadow: var(--shadow);
}

.feature-card h3 {
  margin: 0 0 10px;
  font-size: 1.1rem;
}

.feature-card p {
  margin: 0;
  color: var(--text-muted);
  font-size: 0.95rem;
}

/* Auth pages */
.auth-section {
  padding: 64px 24px;
  display: flex;
  justify-content: center;
}

.auth-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 40px;
  width: 100%;
  max-width: 420px;
}

.auth-card h1 {
  font-size: 1.6rem;
  margin: 0 0 8px;
}

.auth-sub {
  color: var(--text-muted);
  margin: 0 0 28px;
  font-size: 0.95rem;
}

.auth-card label {
  display: block;
  font-size: 0.85rem;
  font-weight: 600;
  margin: 16px 0 6px;
}

/* Excludes checkbox/radio/file/submit/button types (:not() below) —
   a bare `.auth-card input` outranks a same-element `.btn`/`.btn-primary`
   (this selector has one more type selector at equal class count, so it
   always wins on specificity), which was silently overriding the submit
   button's background/border/padding into looking like a plain input
   box, and doing the same to EventForm's `is_online` checkbox and
   `image` file field on the post/edit-event forms (full-width dark bars
   instead of a normal checkbox/file picker). Found 2026-09-16 while
   styling the profile-edit form and fixed here at the root instead of
   per-page, since every .auth-card-wrapped form shares this rule. */
.auth-card input:not([type="checkbox"]):not([type="radio"]):not([type="file"]):not([type="submit"]):not([type="button"]),
.auth-card select {
  width: 100%;
  padding: 11px 14px;
  border-radius: 10px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--text);
  font-size: 0.95rem;
  font-family: inherit;
}

.auth-card input:not([type="checkbox"]):not([type="radio"]):not([type="file"]):not([type="submit"]):not([type="button"]):focus,
.auth-card select:focus {
  outline: none;
  border-color: var(--accent-1);
  box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.15);
}

/* A field that failed server-side validation — same yellow used
   elsewhere in the app (toggle "on" state, suggestion-box link) rather
   than the pink/red --error color, which stays reserved for the error
   *text* itself so the two don't visually compete. Selector matches the
   base rule above plus a class so it reliably outranks it. */
.auth-card input:not([type="checkbox"]):not([type="radio"]):not([type="file"]):not([type="submit"]):not([type="button"]).field-invalid,
.auth-card select.field-invalid,
.auth-card textarea.field-invalid {
  border-color: #fbbf24;
  box-shadow: 0 0 0 3px rgba(251, 191, 36, 0.2);
}

.auth-card input[type="file"] {
  display: block;
  width: 100%;
  padding: 8px 0;
  font-size: 0.88rem;
  color: var(--text-muted);
}

/* Photo drag-and-drop dropzone (events/new.html, events/edit.html) — the
   real file input stays in the DOM (a <label for="image"> wraps it, so
   clicking anywhere in the box opens the native file dialog with zero
   JS) but is visually hidden; pointer-events:none so drag/drop and click
   are always handled by the label itself, never intercepted by the
   input underneath it. */
.auth-card .photo-dropzone input[type="file"] {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  overflow: hidden;
  pointer-events: none;
}

.photo-dropzone {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 28px 16px;
  border: 2px dashed var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  cursor: pointer;
  text-align: center;
  transition: border-color 0.15s, background 0.15s;
}

.photo-dropzone:hover,
.photo-dropzone.drag-over {
  border-color: var(--accent-1);
  background: var(--bg);
}

.photo-dropzone.field-invalid {
  border-color: #fbbf24;
}

.photo-dropzone-icon { font-size: 1.6rem; }

.photo-dropzone-text {
  font-size: 0.88rem;
  color: var(--text-muted);
}

.auth-card .btn-block { margin-top: 26px; }

.auth-card textarea {
  width: 100%;
  padding: 11px 14px;
  border-radius: 10px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--text);
  font-size: 0.95rem;
  font-family: inherit;
  resize: vertical;
}

.auth-card textarea:focus {
  outline: none;
  border-color: var(--accent-1);
  box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.15);
}

.event-form-card { max-width: 620px; }
.admin-user-edit-card { max-width: 480px; margin: 20px 0 32px; padding: 28px; }

.form-row-split {
  display: grid;
  grid-template-columns: 2fr 1fr;
  gap: 14px;
}

.form-row-split[hidden] { display: none; }

@media (max-width: 480px) {
  .form-row-split { grid-template-columns: 1fr; }
}

/* City/State -> single-field autocomplete (app/static/js/
   city-state-autocomplete.js). js-location-merged is only added once JS
   actually runs, so without it this is just the plain two-column row
   above — see that file's header comment. */
.form-row-split.js-location-merged { grid-template-columns: 1fr; }
.form-row-split.js-location-merged .loc-state-field { display: none; }

.loc-autocomplete-wrap { position: relative; }

.loc-suggestions {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  right: 0;
  z-index: 30;
  margin: 0;
  padding: 4px;
  list-style: none;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  max-height: 260px;
  overflow-y: auto;
}

.loc-suggestions[hidden] { display: none; }

.loc-suggestions li {
  padding: 8px 12px;
  border-radius: 8px;
  cursor: pointer;
  font-size: 0.95rem;
}

.loc-suggestions li.active,
.loc-suggestions li:hover {
  background: var(--accent-1);
  color: #fff;
}

.loc-confirm {
  margin: 6px 0 0;
  font-size: 0.82rem;
  color: var(--success);
}

.loc-confirm[hidden] { display: none; }

.checkbox-label {
  display: flex !important;
  align-items: center;
  gap: 8px;
  font-weight: 400 !important;
}

.field-hint.field-hint-tight { margin-top: 0; }

/* Texting-consent checkbox (_sms_consent.html, added 2026-10-20) — a
   paragraph of required disclosure text, so the box aligns to its first
   line rather than centering on the whole block. */
.checkbox-label.sms-consent {
  align-items: flex-start;
  margin-top: 10px;
  font-size: 0.85rem;
  line-height: 1.45;
}
.checkbox-label.sms-consent input { margin-top: 3px; flex-shrink: 0; }

.field-hint {
  color: var(--text-muted);
  font-size: 0.82rem;
  margin: 4px 0 0;
}

/* Cloudflare Turnstile widget on /register and /login (added 2026-09-28)
   — the widget renders its own iframe UI, this just gives it consistent
   spacing within the form. */
.turnstile-widget {
  margin: 16px 0;
}

.event-form-subhead {
  margin: 32px 0 4px;
  padding-top: 20px;
  border-top: 1px solid var(--border);
  font-size: 1rem;
}

.field-error {
  color: var(--error);
  font-size: 0.82rem;
  margin: 6px 0 0;
}

.auth-switch {
  text-align: center;
  color: var(--text-muted);
  font-size: 0.9rem;
  margin-top: 18px;
}

.auth-switch a { color: var(--accent-1); font-weight: 600; text-decoration: none; }

/* Flash messages */
.flash-stack {
  padding-top: 20px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.flash {
  border-radius: 10px;
  padding: 12px 16px;
  font-size: 0.9rem;
  border: 1px solid var(--border);
}

.flash-success { border-color: var(--success); color: var(--success); }
.flash-error { border-color: var(--error); color: var(--error); }

.auth-resend { margin-top: 10px; }

/* Admin */
.admin-section { padding: 48px 24px 96px; }
.admin-section h1 { margin: 0 0 6px; }

.admin-tabs {
  display: flex;
  gap: 4px;
  margin-bottom: 20px;
  border-bottom: 1px solid var(--border);
}

.admin-tabs a {
  padding: 10px 16px;
  color: var(--text-muted);
  text-decoration: none;
  font-size: 0.9rem;
  font-weight: 600;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
}

.admin-tabs a:hover { color: var(--text); }

.admin-tabs a.admin-tab-active {
  color: var(--accent-1);
  border-bottom-color: var(--accent-1);
}

/* Admin dashboard (added 2026-09-28) — rounded-corner stat tiles across
   the top, same --radius/--shadow language as the rest of this app's
   cards. */
.dashboard-tiles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 16px;
  margin: 20px 0 32px;
}

.dashboard-tile {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 18px 20px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.dashboard-tile-label {
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
  font-weight: 600;
}

.dashboard-tile-value {
  font-size: 1.6rem;
  font-weight: 700;
}

.dashboard-tile-sub {
  font-size: 0.82rem;
  color: var(--text-muted);
}

.dashboard-site-list {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 10px;
  margin-top: 4px;
}

.dashboard-site-row { white-space: nowrap; }
.dashboard-site-up { color: limegreen; }
.dashboard-site-down { color: #dc2626; }

.dashboard-section-title { margin: 32px 0 12px; }

.dashboard-mail-layout {
  display: grid;
  grid-template-columns: 320px 1fr;
  gap: 16px;
  margin-top: 16px;
  min-height: 480px;
}

/* The [hidden] attribute alone loses to the unconditional `display: grid`/
   `display: flex` rules above and below — same specificity, later rule in
   the cascade wins, so a plain `el.hidden = true` in JS silently didn't
   hide anything (found via a real "Deleting…" label staying on screen).
   Explicit overrides for every element this page toggles via `hidden`. */
.dashboard-mail-layout[hidden],
.dashboard-mail-reply-buttons [hidden] {
  display: none;
}

.dashboard-mail-list {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow-y: auto;
  max-height: 640px;
}

.dashboard-mail-list-item {
  display: block;
  padding: 12px 16px;
  border-bottom: 1px solid var(--border);
  cursor: pointer;
  text-decoration: none;
  color: var(--text);
}

.dashboard-mail-list-item:hover { background: var(--bg); }
.dashboard-mail-list-item { position: relative; padding-right: 44px; }
.dashboard-mail-list-item-delete { top: 8px; right: 8px; }
.dashboard-mail-list-item-deleting { opacity: 0.5; pointer-events: none; }
.dashboard-mail-list-item.dashboard-mail-list-item-active { background: var(--bg); border-left: 3px solid var(--accent-1); }
.dashboard-mail-list-item-from { font-weight: 600; font-size: 0.85rem; }
.dashboard-mail-list-item-subject { font-size: 0.85rem; margin: 2px 0; }
.dashboard-mail-list-item-date { font-size: 0.75rem; color: var(--text-muted); }

.dashboard-mail-detail {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.dashboard-mail-detail-body {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 20px;
  flex: 1 1 auto;
  overflow-y: auto;
  max-height: 380px;
}

.dashboard-mail-reply {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 16px;
}

.dashboard-mail-reply textarea {
  width: 100%;
  min-height: 100px;
  border-radius: var(--radius);
  border: 1px solid var(--border);
  padding: 10px;
  font-family: inherit;
  resize: vertical;
}

.dashboard-mailbox-groups {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  align-items: start;
  gap: 12px;
}

.dashboard-mailbox-group {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
}

.dashboard-mailbox-domain {
  font-size: 0.82rem;
  color: var(--text-muted);
  margin: 0;
  padding: 8px 14px;
  border-bottom: 1px solid var(--border);
  background: var(--bg);
}

/* Compact single-line rows (added 2026-09-29) — replaces the earlier
   grid-of-boxes layout, which took far more vertical space for 32
   mailboxes than a plain list needed to. Each row is its own bordered
   strip (local part left, count right) rather than a padded card. */
.dashboard-mailbox-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  padding: 6px 14px;
  border: none;
  border-bottom: 1px solid var(--border);
  background: none;
  cursor: pointer;
  font: inherit;
  color: var(--text);
  text-align: left;
}

.dashboard-mailbox-group .dashboard-mailbox-row:last-child { border-bottom: none; }
.dashboard-mailbox-row:hover { background: var(--bg); }
.dashboard-mailbox-row.dashboard-mailbox-row-active { background: var(--bg); box-shadow: inset 3px 0 0 var(--accent-1); }
.dashboard-mailbox-local { font-size: 0.82rem; text-transform: uppercase; color: var(--text-muted); font-weight: 600; }
.dashboard-mailbox-count { font-size: 1.05rem; font-weight: 700; }
.dashboard-mailbox-unseen { font-size: 0.78rem; color: limegreen; font-weight: 600; }
.dashboard-mailbox-error { color: var(--text-muted); }
.dashboard-mail-list-empty { padding: 16px; color: var(--text-muted); font-size: 0.9rem; display: flex; align-items: center; gap: 8px; }
.dashboard-mail-body { white-space: pre-wrap; word-wrap: break-word; font-family: inherit; font-size: 0.9rem; }
.dashboard-mail-headers p { margin: 0; line-height: 1.4; }
.dashboard-mail-headers { margin-bottom: 8px; position: relative; padding-right: 40px; }
.dashboard-mail-delete-icon { position: absolute; top: 0; right: 0; display: inline-flex; padding: 6px; background: none; border: 1px solid transparent; border-radius: 8px; color: var(--text-muted); cursor: pointer; }
.dashboard-mail-delete-icon:hover, .dashboard-mail-delete-icon:focus-visible { color: #dc2626; border-color: var(--border); }
.dashboard-mail-body[hidden], .dashboard-mail-frame[hidden] { display: none; }
.dashboard-mail-view-toggle { margin: -4px 0 4px; font-size: 0.85rem; }
/* Email HTML assumes a white page, so the frame stays white in dark mode. */
.dashboard-mail-frame { display: block; width: 100%; height: 300px; border: 0; border-radius: var(--radius); background: #fff; }

.dashboard-mail-editor { border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); overflow: hidden; }
.dashboard-mail-toolbar { display: flex; flex-wrap: wrap; gap: 2px; padding: 4px; border-bottom: 1px solid var(--border); }
.dashboard-mail-toolbar button { background: none; border: 1px solid transparent; border-radius: 6px; padding: 3px 8px; font: inherit; font-size: 0.85rem; color: inherit; cursor: pointer; }
.dashboard-mail-toolbar button:hover { border-color: var(--border); }
.dashboard-mail-editor-body { min-height: 110px; max-height: 300px; overflow-y: auto; padding: 10px; outline: none; word-wrap: break-word; }
.dashboard-mail-editor-body:empty::before { content: attr(data-placeholder); color: var(--text-muted); }
.dashboard-mail-editor:focus-within { border-color: var(--accent-1); }
.dashboard-mail-reply-buttons { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 10px; }

.dashboard-loading {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 16px;
  color: var(--text-muted);
  font-size: 0.9rem;
}

.dashboard-spinner {
  display: inline-block;
  width: 16px;
  height: 16px;
  border: 2px solid var(--border);
  border-top-color: var(--accent-1);
  border-radius: 50%;
  animation: dashboard-spin 0.7s linear infinite;
}

.dashboard-spinner-sm { width: 12px; height: 12px; border-width: 2px; }

@keyframes dashboard-spin {
  to { transform: rotate(360deg); }
}

.admin-table-wrap {
  margin-top: 24px;
  overflow-x: auto;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
}

.admin-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9rem;
}

.admin-table th, .admin-table td {
  padding: 12px 16px;
  text-align: left;
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}

.admin-table th {
  color: var(--text-muted);
  font-weight: 600;
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.admin-table tr:last-child td { border-bottom: none; }

/* /admin/logs: an entry's message on its own full-width row under the
   entry, wrapping normally instead of being clipped by the nowrap cells. */
.admin-table .log-row-main td { border-bottom: none; padding-bottom: 4px; }
.admin-table .log-row-message td {
  white-space: normal;
  overflow-wrap: anywhere;
  padding-top: 0;
  color: #1d4ed8;
}
@media (prefers-color-scheme: dark) {
  .admin-table .log-row-message td { color: #60a5fa; }
}

.pagination {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 16px;
  margin-top: 16px;
}

.pagination-status {
  font-size: 0.85rem;
  color: var(--text-muted);
}

.status-badge {
  display: inline-block;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 0.78rem;
  font-weight: 600;
  border: 1px solid var(--border);
}

/* Click-to-filter Site/IP/Username/Activity values on /admin/logs —
   plain text at rest (no permanent underline, so a table full of these
   doesn't read as a wall of hyperlinks), a subtle underline only on
   hover/focus for discoverability. */
.log-filter-link {
  color: inherit;
  text-decoration: none;
}

.log-filter-link:hover,
.log-filter-link:focus-visible {
  text-decoration: underline;
}

.status-pending { color: var(--text-muted); }
.status-valid { color: var(--success); border-color: var(--success); }
.status-moderator { color: var(--accent-1); border-color: var(--accent-1); }
.status-admin { color: var(--accent-2); border-color: var(--accent-2); }
.status-banned { color: var(--error); border-color: var(--error); }

/* Event attendance history status (see EventAttendance.status_slug) */
.status-attending { color: var(--accent-1); border-color: var(--accent-1); }
.status-attended { color: var(--success); border-color: var(--success); }
.status-no-show { color: var(--error); border-color: var(--error); }
.status-not-yet-recorded { color: var(--text-muted); }

.admin-muted { color: var(--text-muted); font-size: 0.8rem; }

.user-avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  object-fit: cover;
  flex-shrink: 0;
  background: var(--accent-1);
  color: #fff;
  font-weight: 700;
  overflow: hidden;
}

.user-avatar-sm { width: 28px; height: 28px; font-size: 0.75rem; }
.user-avatar-md { width: 40px; height: 40px; font-size: 0.9rem; }
.user-avatar-lg { width: 96px; height: 96px; font-size: 2rem; }

.profile-current-photo {
  display: flex;
  justify-content: center;
  margin-bottom: 24px;
}

.profile-remove-photo {
  display: flex !important;
  align-items: center;
  gap: 8px;
  font-weight: 400 !important;
  margin-top: 10px !important;
}

/* Compact profile header (2026-09-14) — avatar/name/bio on one line,
   editing tucked behind a <details> disclosure so it doesn't dominate
   the page the way the always-open form used to; emphasis moved to the
   events sections below instead. */
.profile-header-card {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 16px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 20px 24px;
  margin-bottom: 28px;
}

.profile-header-info { flex: 1 1 240px; min-width: 0; }
.profile-header-info h1 { margin: 0; font-size: 1.3rem; }
.profile-header-info .auth-sub { margin: 2px 0 0; }

.profile-edit-disclosure { flex: 0 0 auto; }

.profile-edit-disclosure summary {
  list-style: none;
  cursor: pointer;
}

.profile-edit-disclosure summary::-webkit-details-marker { display: none; }

.profile-edit-disclosure[open] summary { margin-bottom: 16px; }

.profile-edit-form {
  width: 100%;
  max-width: 420px;
}

/* This form lives outside .auth-card (it's tucked in a <details> inside
   .profile-header-card, not an auth page), so none of .auth-card's own
   label/input/select/textarea rules ever reached it — hence "unstyled."
   Mirrors .auth-card's exact treatment. Deliberately scoped by type
   (input[type="text"], not a bare `input`) rather than reusing
   .auth-card's broader `input` selector as-is: this form also has a
   file input (photo) and a checkbox (remove_photo), which should keep
   their native appearance, not the text-field treatment — and a bare
   `.profile-edit-form input` would even outrank the submit button's own
   `.btn-primary` (a higher-specificity element+class selector beats a
   single class), overriding its background/border/padding into looking
   like a plain input box instead of a button. */
.profile-edit-form label {
  display: block;
  font-size: 0.85rem;
  font-weight: 600;
  margin: 16px 0 6px;
}

.profile-edit-form label:first-of-type { margin-top: 0; }

.profile-edit-form textarea,
.profile-edit-form select,
.profile-edit-form input[type="text"] {
  width: 100%;
  padding: 11px 14px;
  border-radius: 10px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--text);
  font-size: 0.95rem;
  font-family: inherit;
}

.profile-edit-form textarea { resize: vertical; }

.profile-edit-form textarea:focus,
.profile-edit-form select:focus,
.profile-edit-form input[type="text"]:focus {
  outline: none;
  border-color: var(--accent-1);
  box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.15);
}

.profile-edit-form input[type="file"] {
  display: block;
  width: 100%;
  padding: 8px 0;
  font-size: 0.88rem;
  color: var(--text-muted);
}

.profile-edit-form .btn-primary { margin-top: 20px; }

@media (max-width: 620px) {
  .profile-edit-disclosure { flex-basis: 100%; }
  .profile-edit-form { max-width: none; }
}

.admin-status-form {
  display: flex;
  gap: 8px;
  align-items: center;
}

.admin-status-form select {
  padding: 6px 10px;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--text);
  font-size: 0.85rem;
}

.event-type-toolbar {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  margin-top: 20px;
}

.event-type-add-form {
  display: flex;
  gap: 8px;
  align-items: center;
}

.event-type-add-form input[type="text"],
.event-type-filter-input {
  padding: 6px 10px;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--text);
  font-size: 0.85rem;
  width: 240px;
}

.btn-sm { padding: 6px 14px; font-size: 0.85rem; }

.settings-card {
  margin-top: 24px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 32px;
  max-width: 520px;
}

.settings-row { margin-bottom: 22px; }

.settings-row label {
  display: block;
  font-weight: 600;
  font-size: 0.92rem;
  margin-bottom: 4px;
}

.settings-desc {
  margin: 0 0 8px;
  color: var(--text-muted);
  font-size: 0.82rem;
}

.settings-row input {
  width: 100%;
  padding: 10px 14px;
  border-radius: 10px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--text);
  font-size: 0.95rem;
}

.settings-row input:focus {
  outline: none;
  border-color: var(--accent-1);
  box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.15);
}

/* Events */
.events-section { padding: 0 24px 40px; }

.events-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: 16px;
}

.events-header h1 { margin: 0; }

.event-filter {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 24px;
  flex-wrap: wrap;
}

.event-filter label {
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--text-muted);
}

.event-filter select,
.event-filter input[type="text"],
.event-filter input[type="date"] {
  padding: 8px 12px;
  border-radius: 10px;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text);
  font-size: 0.9rem;
  font-family: inherit;
}

.event-filter input[type="text"] {
  width: 140px;
}

.filter-match-count {
  font-size: 0.85rem;
  color: var(--text-muted);
}

/* Calendar-icon date filter on /events/ and /meetings/ — the icon
   button reveals + opens the native date input (see the page's own
   <script>); this just keeps the two from touching once it's visible. */
.date-filter {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}

/* Online-events include/exclude toggle on /events/ — a real checkbox
   (keyboard/screen-reader operable) visually hidden and styled as a
   slide switch via the adjacent-sibling track/thumb spans. */
.toggle-switch {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--text-muted);
}

.toggle-switch input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

.toggle-track {
  flex: none;
  width: 36px;
  height: 20px;
  border-radius: 999px;
  background: var(--border);
  transition: background 0.15s ease;
}

.toggle-thumb {
  display: block;
  width: 16px;
  height: 16px;
  margin: 2px;
  border-radius: 50%;
  background: var(--surface);
  box-shadow: 0 1px 3px rgba(20, 20, 30, 0.3);
  transition: transform 0.15s ease;
}

/* Lime green "on" state (2026-09-14, explicit request) — deliberately not
   var(--accent-1): a fixed on/off color independent of each site's own
   accent, same reasoning a browser's own toggle switches don't reskin per
   site either. */
.toggle-switch input:checked + .toggle-track { background: limegreen; }
.toggle-switch input:checked + .toggle-track .toggle-thumb { transform: translateX(16px); }
.toggle-switch input:focus-visible + .toggle-track { outline: 2px solid var(--accent-1); outline-offset: 2px; }

.events-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 20px;
}

.event-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 24px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  text-decoration: none;
  color: inherit;
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}

.event-card:hover {
  transform: translateY(-2px);
  box-shadow: 0 12px 28px rgba(20, 20, 30, 0.1);
}

.event-card-image {
  width: calc(100% + 48px);
  margin: -24px -24px 0;
  max-width: none;
  height: 160px;
  object-fit: cover;
  border-radius: var(--radius) var(--radius) 0 0;
}

.event-card-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}

.event-date {
  font-size: 0.78rem;
  font-weight: 700;
  color: var(--accent-1);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

/* A recurring event's card date/detail-page date shows the computed
   *next* occurrence (see Event.next_occurrence()) — this small note next
   to it just names the pattern (e.g. "↻ Weekly"), muted/non-uppercase so
   it doesn't compete with the date itself. */
.recurrence-note {
  color: var(--text-muted);
  font-weight: 400;
  text-transform: none;
  letter-spacing: normal;
}

/* "Matched: <address>" line on the event detail page — shown only when
   Location resolved to something more precise than city-center (see
   Event.matched_address), so a visitor can confirm what the map pin
   actually points at. */
.matched-address-note {
  display: block;
  color: var(--text-muted);
  font-size: 0.82rem;
  margin-top: 2px;
}

.category-badge {
  display: inline-block;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--accent-2);
  border: 1px solid var(--accent-2);
  white-space: nowrap;
}

.event-detail-image {
  display: block;
  width: 100%;
  max-height: 360px;
  object-fit: cover;
  border-radius: var(--radius);
  margin-bottom: 20px;
}

.event-card h3 { margin: 0; font-size: 1.15rem; }

.event-location {
  color: var(--text-muted);
  font-size: 0.9rem;
  margin: 0;
}

.event-desc {
  color: var(--text-muted);
  font-size: 0.9rem;
  margin: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
}

.event-meta-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-top: auto;
  padding-top: 10px;
  font-size: 0.85rem;
  color: var(--text-muted);
}

.attending-badge {
  display: inline-block;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--success);
  border: 1px solid var(--success);
}

.cancelled-badge {
  display: inline-block;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--error);
  border: 1px solid var(--error);
  margin-left: 8px;
}

.sober-badge {
  display: inline-block;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--success);
  border: 1px solid var(--success);
  white-space: nowrap;
}

.event-cancelled-notice {
  margin: 12px 0;
}

.empty-state {
  text-align: center;
  padding: 64px 24px;
  color: var(--text-muted);
}

.empty-state .btn { margin-top: 16px; }

.event-detail-wrap {
  width: 100%;
  max-width: 640px;
  margin: 0 auto;
}

.event-detail-card {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 40px;
  width: 100%;
}

/* Floats over the top-left corner of the card (over the hero image
   when there is one) rather than sitting inline before the title. */
.event-detail-back-btn {
  position: absolute;
  top: 10px;
  left: 10px;
  z-index: 5;
}

/* Title row: a back-to-events icon button beside the (possibly
   multi-line) title on the left, category/cancelled badges and the
   host-status banner stacked on the right — divided from the "Hosted
   by" line below by a border, same treatment every section heading
   below gets (see .event-detail-card .event-detail-section-title). */
.event-detail-header-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding-bottom: 16px;
  margin-bottom: 16px;
  border-bottom: 1px solid var(--border);
}

.event-detail-header-left {
  display: flex;
  align-items: center;
  min-width: 0;
}

.event-detail-header-left h1 { margin: 0; font-size: 1.8rem; }

.event-detail-header-right {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 8px;
  flex-shrink: 0;
}

.event-detail-meta-left {
  display: flex;
  align-items: center;
  gap: 8px;
}

.event-detail-header-right .event-host-banner { margin: 0; }

.event-detail-host {
  display: flex;
  align-items: center;
  gap: 8px;
  color: var(--text-muted);
  font-size: 0.9rem;
  margin: 0 0 10px;
}

/* Host-only row of icon buttons (Edit / Cancel / Discussion board) that
   used to be plain text links inline with .event-detail-host above —
   pulled onto their own row so they read as actions, not more prose. */
.event-host-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0 0 20px;
}

.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--text);
  font-size: 1.05rem;
  line-height: 1;
  text-decoration: none;
  cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease, transform 0.15s ease;
}

.icon-btn:hover { background: var(--surface); transform: translateY(-1px); }

.icon-btn-danger:hover { border-color: var(--error); }

.event-detail-strip {
  display: flex;
  flex-wrap: wrap;
  gap: 20px;
  margin-bottom: 20px;
}

.event-detail-strip-item {
  display: flex;
  align-items: flex-start;
  gap: 10px;
}

.event-detail-strip-icon { font-size: 1.3rem; line-height: 1.4; }

.event-detail-strip-item div { display: flex; flex-direction: column; }
.event-detail-strip-item strong { font-size: 0.95rem; }
.event-detail-strip-item span { color: var(--text-muted); font-size: 0.88rem; }

.event-map {
  width: 100%;
  height: 300px;
  margin-bottom: 20px;
  border-radius: var(--radius);
  border: 1px solid var(--border);
  overflow: hidden;
}

.event-map iframe { width: 100%; height: 100%; border: 0; display: block; }

.event-description {
  white-space: pre-wrap;
  margin: 0 0 28px;
  line-height: 1.6;
}

.event-attend-row {
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
  margin-bottom: 28px;
}

.event-attendees-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: -14px 0 28px;
}

.attendee-avatar-link {
  display: inline-block;
  line-height: 0;
  border-radius: 999px;
  transition: transform 0.15s ease;
}

.attendee-avatar-link:hover { transform: translateY(-2px); }

/* Shown on an event's own page only to its host/co-hosts (see
   app/events.py::detail()'s is_host/is_co_host) — identifies the
   enhanced management view, distinct from the plain visitor view. */
.event-host-banner {
  display: inline-block;
  background: var(--accent-1);
  color: #fff;
  font-weight: 600;
  font-size: 0.85rem;
  padding: 6px 14px;
  border-radius: 999px;
  margin: 0 0 14px;
}

/* Host/co-host-only replacement for .event-attendees-row above — a
   vertical list (name + co-host status + promote/demote action) instead
   of a row of bare avatars, since a manager needs to act on each
   participant individually rather than just see who's coming. */
.event-participant-list { margin: -6px 0 28px; }

.event-participant-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 0;
  border-bottom: 1px solid var(--border);
}

.event-participant-row:last-child { border-bottom: none; }

/* Same "make the <form> transparent to the flex row" trick as
   .board-message-actions form — lets a POST form's own <button> sit
   inline with everything else instead of forcing a line break. */
.event-participant-row form { display: contents; }

.event-participant-link {
  display: flex;
  align-items: center;
  gap: 10px;
  color: var(--text);
  text-decoration: none;
  min-width: 0;
}

.event-participant-link:hover { text-decoration: underline; }

.event-detail-section-title {
  font-size: 1.1rem;
  margin: 0 0 14px;
}

/* Scoped to the event detail card specifically — this class is shared
   with admin/profile pages (Login activity, Your events, ...) that
   don't want this divider, and with the "More ... events" heading
   below the card, which the sample layout this mirrors doesn't cover
   either. */
.event-detail-card .event-detail-section-title {
  padding-bottom: 10px;
  border-bottom: 1px solid var(--border);
}

.event-full-note {
  color: var(--error);
  font-weight: 600;
  font-size: 0.9rem;
}

.event-specifics {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 16px 20px;
  margin-bottom: 28px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.event-specifics-row {
  display: flex;
  gap: 8px;
  font-size: 0.9rem;
}

.event-specifics-row strong { min-width: 130px; flex-shrink: 0; }
.event-specifics-row span { color: var(--text-muted); white-space: pre-wrap; }

.event-related { margin-top: 40px; }

.event-related-grid { grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }

.event-detail-footer-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 12px;
}

.event-detail-footer-actions .auth-switch { margin: 0; }

.event-detail-footer-buttons {
  display: flex;
  align-items: center;
  gap: 10px;
}

.report-dialog {
  border: 1px solid white;
  border-radius: var(--radius);
  box-shadow: 0 20px 50px rgba(20, 20, 30, 0.25);
  padding: 32px;
  width: 100%;
  max-width: 440px;
  background: var(--surface);
  color: var(--text);
}

.report-dialog::backdrop { background: rgba(20, 20, 30, 0.5); }

.report-dialog h2 { margin: 0 0 4px; font-size: 1.3rem; }

.report-dialog label {
  display: block;
  font-size: 0.85rem;
  font-weight: 600;
  margin: 16px 0 6px;
}

.report-dialog select,
.report-dialog textarea {
  width: 100%;
  padding: 11px 14px;
  border-radius: 10px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--text);
  font-size: 0.95rem;
  font-family: inherit;
}

.report-dialog-actions {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  margin-top: 20px;
}

/* Message boards (event boards + the per-site public board) — styled as
   a WhatsApp-style chat: a scrollable bubble thread over a tinted
   "wallpaper" background, own messages in a green bubble on the right,
   everyone else's in a white/surface bubble on the left with the
   sender's avatar+name, and a pill-shaped composer bar pinned to the
   bottom of the card. */
.board-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 0;
  width: 100%;
  max-width: 720px;
  margin: 0 auto;
  overflow: hidden;
}

.board-card-header { padding: 8px 28px 16px; margin: 0 0 0; }

.board-card-header h1 { margin: 0 0; font-size: 1.5rem; }

/* Board-scoped override, not a global .auth-sub change — this class is
   shared across ~20 other pages (login, register, admin, events,
   profile, …) that still want their normal subtitle spacing. */
.board-card-header .auth-sub { margin: 0 0 0; }

/* Same specificity as the override above so source order (this comes
   after) lets it win for margin-top specifically, keeping the
   subheading's small pull-up intact under the tightened .auth-sub. */
.board-card-header .board-subheading { margin-top: -4px; }

.board-messages {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin: 0;
  padding: 20px;
  max-height: 60vh;
  overflow-y: auto;
  background: var(--chat-bg);
}

.board-message {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin: 6px 0;
}

.board-message-own { justify-content: flex-end; }
.board-message-other { justify-content: flex-start; }

/* Aligned to the top of the row (not the bottom) so the avatar sits
   next to the sender's name/bubble even once a Delete/Report action
   row underneath makes the column taller. */
.board-message-avatar { flex-shrink: 0; margin-top: 18px; }

.board-message-col {
  display: flex;
  flex-direction: column;
  max-width: 72%;
  min-width: 0;
}

.board-message-own .board-message-col { align-items: flex-end; }
.board-message-other .board-message-col { align-items: flex-start; }

.board-message-author {
  font-size: 0.76rem;
  font-weight: 700;
  color: var(--accent-1);
  margin: 0 0 2px 12px;
}

/* The bubble itself — a flattened corner on the side that "points" at
   the sender's avatar/edge mimics a chat-bubble tail without needing a
   separate pseudo-element triangle. */
.chat-bubble {
  padding: 8px 12px;
  border-radius: 16px;
  box-shadow: 0 1px 1px rgba(20, 20, 30, 0.08);
  min-width: 0;
}

.board-message-other .chat-bubble {
  background: var(--chat-bubble-other);
  border-bottom-left-radius: 4px;
}

.board-message-own .chat-bubble {
  background: var(--chat-bubble-own);
  border-bottom-right-radius: 4px;
}

.board-message-body {
  margin: 0;
  white-space: pre-wrap;
  overflow-wrap: break-word;
  font-size: 0.95rem;
  line-height: 1.4;
}

.board-message-time {
  display: block;
  text-align: right;
  color: var(--text-muted);
  font-size: 0.7rem;
  margin-top: 2px;
}

/* A form-submit button styled to read as a plain text link — used
   elsewhere (e.g. event co-host promote/demote) for a POST action that
   should look like a hyperlink rather than a button. */
.link-btn {
  background: none;
  border: none;
  padding: 0;
  margin: 0;
  font: inherit;
  font-size: 0.8rem;
  color: var(--accent-1);
  text-decoration: underline;
  cursor: pointer;
}

.link-btn-danger { color: var(--error); }

/* Per-message "⋯" menu — the wrap gives the trigger/dropdown a
   positioning anchor without affecting the bubble's own sizing. The
   trigger stays invisible until the message is hovered/focused or its
   menu is open, so the thread doesn't show a row of controls at rest. */
.chat-bubble-wrap { position: relative; }

.message-menu-trigger {
  position: absolute;
  top: 4px;
  right: 4px;
  width: 22px;
  height: 22px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.16);
  color: #fff;
  font-size: 1rem;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.12s ease, background 0.12s ease;
}

.board-message:hover .message-menu-trigger,
.message-menu-trigger:focus-visible,
.message-menu-trigger[aria-expanded="true"] {
  opacity: 1;
}

.message-menu-trigger:hover { background: rgba(0, 0, 0, 0.32); }

/* position: fixed (not absolute-anchored to the bubble) because
   .board-messages scrolls — top/left are set by JS from the trigger's
   real screen position each time it opens, so the menu always lands
   fully in the viewport instead of being clipped by that overflow. */
.message-menu-dropdown {
  position: fixed;
  z-index: 30;
  min-width: 150px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 10px;
  box-shadow: 0 8px 24px rgba(20, 20, 30, 0.2);
  padding: 6px;
  display: flex;
  flex-direction: column;
}

/* display: flex above has the same specificity as the UA stylesheet's
   [hidden] { display: none }, and author styles win ties — without
   this override every dropdown would render open by default. */
.message-menu-dropdown[hidden] { display: none; }

.message-menu-dropdown form { display: contents; }

.message-menu-item {
  display: block;
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  padding: 8px 10px;
  border-radius: 6px;
  font: inherit;
  font-size: 0.85rem;
  color: var(--text);
  text-decoration: none;
  cursor: pointer;
}

.message-menu-item:hover { background: var(--bg); }

.message-menu-item-danger { color: var(--error); }

.message-menu-item-static { color: var(--text-muted); cursor: default; }
.message-menu-item-static:hover { background: none; }

.board-empty-state { padding: 40px 20px; background: var(--chat-bg); }

.board-post-form {
  display: flex;
  align-items: flex-end;
  gap: 10px;
  padding: 14px 20px;
  border-top: 1px solid var(--border);
  background: var(--surface);
}

.board-post-field { flex: 1; min-width: 0; }

.board-post-form textarea {
  /* A <textarea> defaults to display: inline-block, which reserves a
     few px of descender space below it inside its wrapping div — that
     gap was why the Post button (bottom-aligned to the wrapping div)
     sat visibly lower than the textarea's own bottom edge. */
  display: block;
  width: 100%;
  padding: 11px 18px;
  border-radius: 20px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--text);
  font-size: 0.95rem;
  font-family: inherit;
  resize: vertical;
  max-height: 200px;
}

.board-post-form textarea:focus {
  outline: none;
  border-color: var(--accent-1);
  box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.15);
}

.board-post-send {
  border-radius: 999px;
  flex-shrink: 0;
}

/* Footer */
.site-footer {
  border-top: 1px solid var(--border);
  padding: 28px 0;
  color: var(--text-muted);
  font-size: 0.85rem;
  text-align: center;
}

.footer-legal-links {
  margin: 6px 0 0;
}

.footer-socials {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 22px;
  margin: 0 0 14px;
}

.footer-socials svg {
  width: 34px;
  height: 34px;
}

/* Extra space between the Instagram/TikTok group and the X group. */
.footer-socials-handle + a {
  margin-left: 14px;
}

.footer-socials a {
  display: inline-flex;
  color: inherit;
  transition: color 0.15s;
}

.footer-socials a:hover,
.footer-socials a:focus-visible {
  color: var(--accent-1);
}

.footer-build-info {
  margin: 6px 0 0;
  font-size: 0.75rem;
  opacity: 0.7;
}

/* Privacy Policy / Terms of Service */
.legal-page {
  padding: 48px 24px 96px;
  display: flex;
  justify-content: center;
}

.legal-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 40px;
  width: 100%;
  max-width: 720px;
}

.legal-card h1 { margin: 0 0 4px; }

.legal-updated {
  color: var(--text-muted);
  font-size: 0.85rem;
  margin: 0 0 28px;
}

.legal-card h2 {
  font-size: 1.15rem;
  margin: 28px 0 10px;
}

.legal-card p, .legal-card li {
  color: var(--text-muted);
  line-height: 1.6;
}

.legal-card ul { padding-left: 20px; }

/* Cookie consent banner — shown once on first load, see cookie-consent.js */
.cookie-consent {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 100;
  background: var(--surface);
  border-top: 1px solid var(--border);
  box-shadow: 0 -8px 24px rgba(20, 20, 30, 0.08);
}

.cookie-consent-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  flex-wrap: wrap;
  padding: 16px 24px;
}

.cookie-consent-inner p {
  margin: 0;
  color: var(--text-muted);
  font-size: 0.9rem;
}

.cookie-consent-actions {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  flex-shrink: 0;
}

/* Own-profile tabs + attending calendar (added 2026-10-20). The tab bar
   reuses .admin-tabs' styling; .profile-tabs only adds spacing. */
.profile-tabs { margin-top: 28px; }

.profile-calendar-head {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 12px;
}
.profile-calendar-title { margin: 0 8px; font-size: 1.15rem; min-width: 10ch; text-align: center; }

.profile-calendar-wrap {
  overflow-x: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
}

.profile-calendar {
  width: 100%;
  min-width: 560px;
  border-collapse: collapse;
  table-layout: fixed;
}
.profile-calendar th {
  padding: 8px 6px;
  font-size: 0.75rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
  border-bottom: 1px solid var(--border);
}
.profile-calendar td {
  height: 96px;
  padding: 6px;
  vertical-align: top;
  border-right: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
}
.profile-calendar td:last-child { border-right: none; }
.profile-calendar tr:last-child td { border-bottom: none; }
.profile-calendar td.cal-other-month { background: color-mix(in srgb, var(--text) 4%, transparent); }
.profile-calendar td.cal-other-month .cal-day-number { color: var(--text-muted); opacity: 0.6; }

.cal-day-number {
  display: inline-block;
  min-width: 24px;
  padding: 1px 4px;
  font-size: 0.8rem;
  font-weight: 600;
  text-align: center;
  color: var(--text);
  border-radius: 999px;
}
.cal-today .cal-day-number { background: var(--accent-1); color: #fff; }

.cal-event {
  display: block;
  margin-top: 4px;
  padding: 2px 6px;
  font-size: 0.75rem;
  line-height: 1.3;
  color: var(--text);
  text-decoration: none;
  background: color-mix(in srgb, var(--accent-1) 14%, transparent);
  border-left: 3px solid var(--accent-1);
  border-radius: 4px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cal-event:hover { text-decoration: underline; }
.cal-event-time { font-weight: 600; }
