/* ALI Portal — Event Admin UI. Refined institutional/civic; ALI brand palette;
   Spectral (serif display) + Public Sans (civic UI). Ported from the approved
   mockup docs/portal/mockup-portal.html. */

:root{
  --green:#488c36; --green-dk:#386c28; --green-soft:#e7f0e2;
  --navy:#13294b; --navy-2:#1d3a66;
  /* Round-2 #1 contrast: deepen the page BASE (was #f3f2ed) and strengthen the
     BORDER (was #e4e1d7) so white --surface cards/panels/sections read as clearly
     distinct plates on the page instead of near-melting into the background. */
  /* Round-6 contrast (operator 2026-07-09: "greater contrast between sections and
     background"): second deepening step — base #eae8df -> #e2ded0, border #d8d3c6 ->
     #c8c1ad — so white .panel plates read unmistakably as raised cards. */
  --base:#e2ded0; --surface:#ffffff; --ink:#1a1a1a; --muted:#6b6f67;
  --border:#c8c1ad; --accent-soft:#dce7f4;
  --amber:#b9821f; --amber-soft:#f6ecd6; --gray-pill:#ecebe6;
  --serif:'Spectral',Georgia,serif; --ui:'Public Sans',system-ui,sans-serif;
  --shadow:0 1px 2px rgba(19,41,75,.05),0 10px 28px -12px rgba(19,41,75,.24);
  --radius:12px;
}
*{box-sizing:border-box}
body{margin:0;font-family:var(--ui);color:var(--ink);background:var(--base);
  -webkit-font-smoothing:antialiased;}

/* ---------- Accessibility: keyboard focus + skip-link ---------- */
/* A visible focus ring for keyboard users on every interactive element. :focus-visible
   keeps mouse clicks ring-free. Dark-green reads on the light app surfaces; the navy
   sidebar + skip-link override to white below for contrast. */
:focus-visible{outline:2px solid var(--green-dk);outline-offset:2px}
/* White ring where the focused element sits on a dark/green fill (navy sidebar links,
   the skip-link, and buttons that turn green on focus) so the ring stays visible. */
.side a:focus-visible,.skip-link:focus-visible,.btn:focus-visible{outline-color:#fff}
/* Skip-to-content: off-screen until focused (first focusable element on the page),
   then slides into the top-left corner. */
.skip-link{position:fixed;top:8px;left:8px;z-index:1000;background:var(--navy);color:#fff;
  padding:10px 16px;border-radius:8px;font-weight:600;text-decoration:none;
  transform:translateY(-200%);transition:transform .15s}
.skip-link:focus{transform:translateY(0)}
/* Visually-hidden utility: content stays in the a11y tree (table captions,
   icon-button names, "(required)" hints) but is not painted. */
.sr-only{position:absolute!important;width:1px;height:1px;padding:0;margin:-1px;
  overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}

.app{display:grid;grid-template-columns:248px 1fr;min-height:100vh}

/* ---------- Sidebar ---------- */
/* COMPONENT: Sticky full-height sidebar (position:sticky; top:0). Rendered once
   by the shared portal chrome, never per-view. Pairs with the sticky .topbar. */
.side{background:var(--navy);color:#dfe6f1;display:flex;flex-direction:column;
  position:sticky;top:0;align-self:start;height:100vh;overflow:hidden}
.side::after{content:"";position:absolute;inset:0;pointer-events:none;
  background:
    radial-gradient(120% 60% at 100% 0,rgba(72,140,54,.18),transparent 60%),
    repeating-linear-gradient(115deg,rgba(255,255,255,.025) 0 2px,transparent 2px 26px);
}
.brand{padding:22px 22px 18px;display:flex;align-items:center;gap:11px;
  border-bottom:1px solid rgba(255,255,255,.08);position:relative;z-index:1}
.brand .mark{width:34px;height:34px;border-radius:9px;flex:0 0 auto;
  background:linear-gradient(150deg,var(--green),var(--green-dk));
  display:grid;place-items:center;font-family:var(--serif);font-weight:700;
  color:#fff;font-size:18px;box-shadow:0 2px 8px rgba(0,0,0,.25)}
.brand b{font-family:var(--serif);font-weight:600;font-size:18px;color:#fff;
  letter-spacing:.2px;line-height:1}
.brand small{display:block;font-size:10.5px;letter-spacing:1.6px;
  text-transform:uppercase;color:#9fb0c9;margin-top:3px}
/* The sidebar is a 100vh flex column (.brand · nav · .foot) with overflow:hidden,
   so the nav itself must scroll when the link list is taller than the viewport —
   otherwise tall navs (many access groups, or a short laptop screen) clip the
   lower links + the Sign-out footer with no way to reach them. flex:1 + min-height:0
   lets nav shrink below its content and scroll; .brand and .foot stay pinned. */
nav{padding:14px 12px;position:relative;z-index:1;flex:1 1 auto;min-height:0;overflow-y:auto;scrollbar-width:thin}
/* Round-2 #6 left-nav weighting: GROUP labels are the dominant emphasis —
   larger + bolder + brighter than the individual links below them (which are
   toned down in `.nav a`). Inverts the prior relationship where the links out-
   weighed their section headings. */
.nav-label{font-size:12px;font-weight:700;letter-spacing:1.2px;text-transform:uppercase;
  color:#aebfd6;padding:11px 12px 6px}
/* Collapsible nav groups (operator 2026-07-02: start minimized). <summary> reuses
   the .nav-label look; the caret flips on [open]. */
.nav-group>summary.nav-label{cursor:pointer;list-style:none;display:flex;align-items:center;user-select:none}
.nav-group>summary.nav-label::-webkit-details-marker{display:none}
.nav-group>summary.nav-label::after{content:'\25B8';margin-left:auto;font-size:11px;opacity:.7}
.nav-group[open]>summary.nav-label::after{content:'\25BE'}
.nav-group>summary.nav-label:hover{color:#c8d5e8}
.nav-group>summary.nav-label:focus-visible{outline:2px solid var(--green);outline-offset:-2px;border-radius:6px}
/* Round-2 #6: individual links are the SECONDARY tier — smaller + lighter weight
   + slightly dimmer than their group heading above. Active/hover states below
   still brighten to #fff so the current page stays obvious. */
.nav a{display:flex;align-items:center;gap:11px;padding:9px 12px;margin:2px 0;
  border-radius:9px;color:#b6c2d5;text-decoration:none;font-size:13px;
  font-weight:400;transition:background .15s,color .15s}
.nav a svg{width:18px;height:18px;opacity:.85}
.nav a:hover{background:rgba(255,255,255,.06);color:#fff}
.nav a.active{background:linear-gradient(90deg,rgba(72,140,54,.28),rgba(72,140,54,.06));
  color:#fff;box-shadow:inset 3px 0 0 var(--green)}
/* QA2 #2: flat TOP-LEVEL links (Dashboard, About the Portal — no group header
   above them) read all-caps, echoing the uppercase .nav-label group headers.
   CSS transform only — the label strings still feed <title>/<h1>/breadcrumbs. */
.nav > a.nav-top{text-transform:uppercase;letter-spacing:.6px}
/* QA3 #2 + #3: the two flat top-level links are deliberately UNEQUAL in weight.
   Dashboard is the portal's home and the operator wants it to pull the eye
   ("make DASHBOARD stand out, slightly larger and slightly different color"),
   while How To Use The Portal is a reference link that should sit quietly at the
   bottom ("make it smaller, same color change"). Both take the SAME brand tint
   -- the green-leaning parchment already used by the sidebar's sign-out link --
   so they read as one pair of chrome links distinct from the gray-blue section
   links between them; only the SIZE separates them. Both class names are NEW, so
   nothing that existed before this rule changes appearance (the #305 lesson:
   portal.css loads on every portal page, so a promotion must not restyle a
   surface that never opted in). The same markup renders in the mobile off-canvas
   drawer, so the treatment is identical on phone and desktop. */
.nav a.nav-dashboard{font-size:15px;font-weight:600;color:#cfe0c4}
.nav a.nav-help{font-size:12px;color:#cfe0c4}
/* Carry-over from #454: this comment was muddled, and the muddle pointed the wrong
   way -- it read as if the OVERRIDE below were the rule with equal specificity, which
   invites a later reader to delete it as redundant. It is not redundant. Two different
   rules are involved:
     .nav a.nav-dashboard        (0,2,1)  TIES `.nav a.active` (0,2,1) and beats it
                                          ONLY by falling later in this file -- which
                                          is the entire reason an override is needed.
     .nav a.nav-dashboard.active (0,3,1)  wins outright, so the override itself does
                                          NOT depend on source order.
   Measured, not reasoned: in headless Chromium against this stylesheet, deleting the
   line below makes an active Dashboard link compute to rgb(207,224,196) instead of
   rgb(255,255,255). Same shape as `.nav a.nav-sub.active` further down. */
.nav a.nav-dashboard.active,.nav a.nav-help.active{color:#fff}
/* Access-group SUB-ITEM tier (QA R1 #2/#5/#6/#10 — "sub-items inconsistent
   color/indent; sections not real containers"). Every direct link UNDER a group
   header (People · Task Forces · Events · Communications · Insights ·
   Administration · My Account) carries `.nav-sub`: ONE uniform indent + muted
   parchment tone + a vertical nest-rail — the SAME sub-item tier the two-level
   subtree children (.nav-child / .nav-sublabel, in portal-shell.php's shell-polish
   block) already use, so the whole nav reads as one hierarchy (group header above,
   contained children indented below) instead of a mix of flush gray-blue links and
   indented parchment children. The dominant cool/bold `.nav-label` stays the group
   header; Dashboard (a flat, header-less group) keeps the top-level `.nav a` look.
   Mobile: the identical markup renders in the off-canvas drawer, so the indent +
   tint apply there too (36px indent leaves ample room in the 272px drawer). */
.nav a.nav-sub{padding-left:36px;color:#d5c69d;position:relative}
/* Operator 2026-08-03: the vertical nest-rail is REMOVED. It implied containment
   -- it made Users & Access read as a sub-heading of the item above it rather than
   a peer -- which is the opposite of what the QA2 #3 flattening was for. The indent
   alone carries the tier; the rail was adding hierarchy the data does not have. */
.nav a.nav-sub:hover{color:#fff}
/* Active sub-item keeps its indent + nest-rail, but the green active wash + white
   text (from `.nav a.active` above) must win the colour — explicit, since
   `.nav a.nav-sub` has equal specificity and falls later in the cascade. */
.nav a.nav-sub.active{color:#fff}
.side .foot{margin-top:auto;padding:16px 18px;border-top:1px solid rgba(255,255,255,.08);
  font-size:12px;color:#8ea1bd;position:relative;z-index:1}
.side .foot .signout{display:inline-block;margin:6px 0 4px;color:#cfe0c4;font-weight:600;
  text-decoration:none}
.side .foot .signout:hover{color:#fff;text-decoration:underline}
.side .foot .foot-ver{margin-top:4px;color:#8fa0bd;font-size:11px}
.side .foot .foot-ver .foot-ver-link{color:#fff;text-decoration:underline}
.side .foot .foot-ver .foot-ver-link:hover{color:#fff;text-decoration:underline}
/* Staging version badge (v0.14.29S): click to reveal base + additions. The pop
   is position:FIXED (QA1 #1) — the sidebar is overflow:hidden, so an absolute
   pop anchored inside .foot gets clipped by the sidebar edge / overlapped by the
   main column. Fixed coords anchor it above the footer at the viewport's
   bottom-left, independent of the sidebar's overflow. z-index 30: above the main
   column's sticky tiers (topbar 5/6, thead 3) and the mobile drawer/overlay
   (20/19), below modals + skip-link (1000). */
.foot-stage{display:inline-block;position:relative}
.foot-stage>summary{cursor:pointer;list-style:none;color:#fff;text-decoration:underline;user-select:none}
.foot-stage>summary::-webkit-details-marker{display:none}
.foot-stage__pop{position:fixed;left:12px;bottom:56px;z-index:30;width:min(340px,calc(100vw - 24px));max-height:60vh;overflow-y:auto;
  background:var(--surface);color:var(--ink);border:1px solid var(--border);border-radius:10px;box-shadow:var(--shadow);
  padding:10px 14px;font-size:12px;line-height:1.5;text-align:left}
/* .side is position:sticky, which ALWAYS forms a stacking context — its
   children (the pop included, fixed or not: stacking follows DOM ancestry,
   not the containing block) cannot out-stack the main column from inside it.
   So the lift goes on .side ITSELF, only while the pop is open; harmless
   since .side and .main are separate grid columns that never overlap.
   (No :has support ⇒ rule drops; the pop still escapes the sidebar clipping,
   the only loss is the rare short-viewport sticky-tier overlap.) */
.side:has(.foot-stage[open]){z-index:30}
.foot-stage__pop b{color:var(--navy)}
.foot-stage__pop ul{margin:6px 0 0;padding-left:18px}
.foot-stage__pop li{margin:3px 0}

/* ---------- Main ---------- */
.main{display:flex;flex-direction:column;min-width:0}
/* COMPONENT: Sticky context header (position:sticky; top:0; z-index:5; translucent
   blur fill). Holds the page title + context switcher. Known limitation: .topbar
   and the sticky `thead th` (below) both pin to top:0 with no offset coordination,
   so on a scrolled list the sticky thead can slide under the topbar — a future fix
   would set thead `top` to the topbar height (not changed in Phase 0). */
.topbar{display:flex;align-items:center;gap:18px;padding:16px 30px;
  background:rgba(255,255,255,.72);backdrop-filter:blur(6px);
  border-bottom:1px solid var(--border);position:sticky;top:0;z-index:5}
.topbar h1{font-family:var(--serif);font-weight:600;font-size:23px;margin:0;
  color:var(--navy)}
/* Topbar access-group breadcrumb ("EVENTS › Events") — the muted group prefix
   orients the user to where the current section sits in the grouped nav. */
.topbar-group{font-family:var(--sans,inherit);font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:.6px;color:var(--muted);vertical-align:middle}
.topbar-sep{color:var(--muted);font-weight:400}
.evt-picker{margin-left:6px;display:flex;align-items:center;gap:8px;
  background:var(--surface);border:1px solid var(--border);border-radius:999px;
  padding:7px 14px;font-size:13px;font-weight:600;color:var(--navy);cursor:pointer}
.evt-picker .dot{width:7px;height:7px;border-radius:50%;background:var(--green)}
.evt-picker svg{width:14px;height:14px;color:var(--muted)}
.spacer{flex:1}
.me{display:flex;align-items:center;gap:10px;font-size:13px;color:var(--muted)}
.me .av{width:32px;height:32px;border-radius:50%;background:var(--accent-soft);
  color:var(--navy);display:grid;place-items:center;font-weight:700;font-size:13px}

/* Content fills the available width (no fixed cap) so wide data tables use the
   whole browser window instead of being boxed at 1180px (which forced a desktop
   horizontal scrollbar on wide tables like the event Invitees list). The grid
   .app (248px nav | 1fr) + flex .main (min-width:0) keep the left nav fixed and
   let this column flex to fill; the ≤880px block resets max-width:100% for mobile. */
.content{padding:30px;max-width:none;width:100%}
.eyebrow{font-size:11px;letter-spacing:1.6px;text-transform:uppercase;
  color:var(--green-dk);font-weight:700}
.lede{color:var(--muted);font-size:14px;margin:4px 0 22px}

/* Responsive form/layout grids (collapse to 1 column on small screens — see media queries) */
.fld-grid{display:grid;grid-template-columns:1fr 1fr;gap:14px}
.col2{display:grid;grid-template-columns:1fr 1fr;gap:20px}

/* List pagination (per-page selector + prev/next) */
.pager{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;padding:12px 16px;border-top:1px solid var(--border);font-size:13px;color:var(--muted)}
.pager select{padding:4px 6px;border:1px solid var(--border);border-radius:6px;font:inherit;margin:0 2px}
.pager-nav{display:flex;align-items:center;gap:10px}
.pager-btn{color:var(--navy);font-weight:600;text-decoration:none;padding:4px 9px;border:1px solid var(--border);border-radius:6px}
.pager-btn:hover{background:var(--surface)}
.pager-btn.off{color:var(--muted);opacity:.5;pointer-events:none}
/* Top instance of the pager (rows-per-page at the head of a list, #9/B1): the border
   reads downward toward the table instead of up. */
.pager-top{border-top:0;border-bottom:1px solid var(--border)}

/* Stat cards.
   Fluid grid: auto-fit + minmax sizes cards to the container — ~4-up on a wide
   desktop, dropping to 3/2-up as the viewport narrows, and 2 compact cards per row
   on a phone (never one giant full-width card). minmax(160px,1fr) preserves the
   desktop feel (cards never exceed an even share, never narrower than 160px); the
   media-query overrides below lower the floor + tighten gaps on small screens. */
.stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:16px;margin-bottom:28px}
.card{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);
  padding:18px 18px 16px;box-shadow:var(--shadow);position:relative;overflow:hidden}
.card::before{content:"";position:absolute;left:0;top:0;bottom:0;width:4px;background:var(--green)}
.card.navy::before{background:var(--navy)}
.card.amber::before{background:var(--amber)}
.card.gray::before{background:#b9bcb3}
a.card{text-decoration:none;color:inherit;display:block;transition:box-shadow .15s,transform .15s}
a.card:hover{box-shadow:0 6px 20px -8px rgba(19,41,75,.30);transform:translateY(-1px)}
.card .k{font-size:12px;letter-spacing:.3px;text-transform:uppercase;
  color:var(--muted);font-weight:600;display:flex;align-items:center;gap:7px}
.card .v{font-family:var(--serif);font-weight:600;font-size:40px;line-height:1.05;
  color:var(--navy);margin-top:8px}
.card .sub{font-size:12px;color:var(--muted);margin-top:5px}
.card .sub b{color:var(--green-dk)}

/* Panel */
.panel{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);
  box-shadow:var(--shadow);overflow:hidden}
.panel-head{display:flex;align-items:center;gap:14px;padding:16px 20px;
  border-bottom:1px solid var(--border);flex-wrap:wrap}
.panel-head h2{font-family:var(--serif);font-weight:600;font-size:18px;margin:0;color:var(--navy)}
.count-chip{font-size:12px;font-weight:600;color:var(--green-dk);background:var(--green-soft);
  border-radius:999px;padding:3px 10px}
.search{margin-left:auto;display:flex;align-items:center;gap:8px;background:var(--base);
  border:1px solid var(--border);border-radius:9px;padding:8px 12px;min-width:240px}
.search svg{width:16px;height:16px;color:var(--muted)}
.search input{border:0;background:transparent;font:inherit;font-size:14px;width:100%;color:var(--ink)}
.filters{display:flex;gap:8px;padding:12px 20px;flex-wrap:wrap;border-bottom:1px solid var(--border)}
.chip{font-size:12.5px;font-weight:600;border:1px solid var(--border);background:var(--surface);
  color:var(--muted);border-radius:999px;padding:5px 12px;cursor:pointer;transition:.15s}
.chip:hover{border-color:var(--green);color:var(--green-dk)}
.chip.on{background:var(--navy);border-color:var(--navy);color:#fff}

table{width:100%;border-collapse:collapse}
/* COMPONENT: .table-scroll — wide-table handling. Wrapping a wide <table> in a
   <div class="table-scroll"> keeps the
   table a real <table> (NOT display:block) so position:sticky thead keeps working
   — sticky positions against this overflow:auto scroll container. overflow-x:auto
   stays as a FALLBACK for genuinely narrow screens, but the table now fills 100%
   and its cells WRAP (see .table-scroll>table td below) instead of forcing a
   desktop horizontal scrollbar — so wide tables (e.g. the event Invitees list)
   use the full window width rather than side-scrolling. */
.table-scroll{overflow-x:auto;-webkit-overflow-scrolling:touch;max-width:100%}
.table-scroll>table{width:100%}
/* Long cell content (URLs, emails, the read-only RSVP-link field) wraps instead
   of stretching the table past the viewport. overflow-wrap:anywhere lets the
   browser break inside an unbroken string only when needed; readonly link inputs
   in a cell shrink to fit (width:100%;max-width:100%) so the row stays in-bounds. */
.table-scroll>table td{overflow-wrap:anywhere;word-break:break-word}
.table-scroll>table td input[readonly],.table-scroll>table td input.linkfld{
  width:100%;max-width:100%;min-width:0}
/* Sticky headers: stay visible while scrolling long lists. Solid background +
   shadow stand in for the border (borders don't travel with sticky cells). */
thead th{text-align:left;font-size:11px;letter-spacing:.8px;text-transform:uppercase;
  color:var(--muted);font-weight:700;padding:12px 20px;border-bottom:1px solid var(--border);
  position:sticky;top:0;background:var(--surface);z-index:3;
  box-shadow:0 1px 0 var(--border)}
tbody td{padding:14px 20px;border-bottom:1px solid #f0eee7;font-size:14px;vertical-align:middle}
tbody tr{transition:background .12s}
tbody tr:hover{background:#faf9f4}
tbody tr:last-child td{border-bottom:0}
.person{font-weight:600;color:var(--ink)}
.person small{display:block;font-weight:400;color:var(--muted);font-size:12.5px;margin-top:1px}
.tags{display:flex;gap:5px;flex-wrap:wrap}
.tag{font-size:11px;font-weight:600;padding:2px 8px;border-radius:6px;
  background:var(--accent-soft);color:var(--navy)}
.tag.press{background:#efe3f3;color:#6a2c83}
.tag.gov{background:var(--green-soft);color:var(--green-dk)}
.pill{font-size:12px;font-weight:700;padding:4px 11px;border-radius:999px;display:inline-flex;align-items:center;gap:6px}
.pill::before{content:"";width:6px;height:6px;border-radius:50%}
.pill.ok{background:var(--green-soft);color:var(--green-dk)} .pill.ok::before{background:var(--green)}
.pill.pend{background:var(--amber-soft);color:#7a5200} .pill.pend::before{background:var(--amber)}
.pill.no{background:var(--gray-pill);color:#5f6259} .pill.no::before{background:#9a9d93}
.num{font-variant-numeric:tabular-nums;font-weight:600;color:var(--navy)}
.rowact{color:var(--green-dk);font-weight:600;font-size:13px;text-decoration:none}
.btn{display:inline-block;background:var(--green-dk);color:#fff;border:0;border-radius:8px;padding:9px 16px;font:inherit;font-weight:600;cursor:pointer;text-decoration:none}
.btn:hover,.btn:focus{background:var(--navy);color:#fff}
.btn-sm{padding:7px 13px;font-size:13px}
.btn-ghost{background:var(--surface);color:var(--green-dk);border:1px solid var(--border)}
.btn-ghost:hover{background:var(--green-soft);color:var(--green-dk)}
.rowlink{color:var(--ink);text-decoration:none}
.rowlink:hover{color:var(--green-dk);text-decoration:underline}
tbody tr:hover{cursor:default}
.guesttag{font-size:10.5px;font-weight:700;letter-spacing:.4px;color:var(--muted);
  text-transform:uppercase;background:var(--base);border:1px solid var(--border);
  border-radius:5px;padding:1px 5px;margin-left:6px}

/* ---------- Task 1: clickable contact rows ---------- */
tr.contact-row:hover{background:#f0ede6 !important;cursor:pointer}

/* ---------- Task 2: event detail header ---------- */
.event-live-card{display:inline-flex;align-items:center;gap:14px;
  background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);
  padding:12px 18px;margin-bottom:18px;box-shadow:var(--shadow)}
.event-live-card__label{font-size:11px;letter-spacing:1.2px;text-transform:uppercase;
  color:var(--muted);font-weight:700}
.event-live-card__code{font-family:var(--serif);font-weight:600;font-size:15px;
  color:var(--navy);background:var(--accent-soft);border-radius:6px;padding:3px 10px}
.event-live-card__link{color:var(--green-dk);font-weight:700;font-size:13px;
  text-decoration:none;padding:6px 12px;border:1px solid var(--green);border-radius:7px;
  transition:background .15s}
.event-live-card__link:hover{background:var(--green-soft)}

.event-meta-stack{margin-bottom:10px}
.event-meta-line{font-size:13px;color:var(--muted);line-height:1.9}
.event-meta-line.eyebrow{font-size:11px;letter-spacing:1.6px;text-transform:uppercase;
  color:var(--green-dk);font-weight:700;line-height:1.9}

/* ---------- Task 3: events list card rows ---------- */
.event-list-row{display:flex;align-items:flex-start;gap:18px;
  padding:18px 20px;border-bottom:1px solid #f0eee7}
.event-list-row:last-child{border-bottom:0}
/* QA3 #15 — archive shading. The list runs event_date DESC (upcoming first, oldest
   last), so everything below the divider is over and should read as an archive at a
   glance. Tinting the row BACKGROUND (not the text) is deliberate: dimming the copy
   would drop past events below the contrast floor the round-2/round-6 work raised
   the whole palette to meet. #ebe8e0 sits between --surface and --base, so an
   archive row reads as recessed against the white plate without losing legibility
   (--ink #1a1a1a on #ebe8e0 = 14.4:1, still AAA). The date badge is desaturated to
   match; its white-on-navy text is untouched.
   NOTE: the bare token `is-past` is NOT unique — .event-chip.is-past (line ~407) is a
   pre-existing countdown pill in the same list. Every rule here is therefore compound
   with .event-list-row, so the chip is untouched and no other surface can pick these up. */
.event-list-row.is-past{background:#ebe8e0}
.event-list-row.is-past .event-date-badge{background:#4a5568}
.event-list-row.is-past .event-date-badge:hover{background:var(--navy-2)}
/* The one-time upcoming→past boundary rule. It carried no styling at all before QA3
   #15 (a bare browser <hr>), which read as an accidental line rather than a labelled
   boundary; now it is an explicit divider matching the panel's own border colour. */
.events-past-divider{border:0;border-top:2px solid var(--border);margin:0}
.event-date-badge{display:flex;flex-direction:column;align-items:center;
  flex:0 0 72px;background:var(--navy);color:#fff;border-radius:10px;
  padding:10px 8px 8px;text-decoration:none;transition:background .15s;
  box-shadow:var(--shadow)}
.event-date-badge:hover{background:var(--navy-2)}
.event-date-badge__month{font-size:10px;letter-spacing:1.4px;text-transform:uppercase;
  opacity:.75;font-weight:700}
.event-date-badge__day{font-family:var(--serif);font-size:28px;font-weight:600;
  line-height:1.1}
.event-date-badge__code{font-size:9px;letter-spacing:.8px;text-transform:uppercase;
  opacity:.6;margin-top:4px;font-weight:700}
.event-date-badge__view{font-size:10px;font-weight:700;margin-top:6px;
  background:rgba(255,255,255,.15);border-radius:4px;padding:2px 6px}
.event-list-row__body{flex:1;min-width:0}
.event-list-row__title{font-family:var(--serif);font-size:16px;font-weight:600;
  color:var(--navy);margin-bottom:6px;display:flex;align-items:center;gap:10px}
.event-active-badge{font-size:10.5px;font-weight:700;letter-spacing:.5px;
  text-transform:uppercase;background:var(--green-soft);color:var(--green-dk);
  border-radius:5px;padding:2px 7px}
.event-list-row__stats{display:flex;gap:16px;font-size:13px;color:var(--muted);
  margin-bottom:6px}
.event-list-row__stats strong{color:var(--navy)}
/* Guest breakdown riding on the "confirmed" (total headcount) stat — lighter
   parenthetical so the headline figure stays dominant. */
.event-list-row__stat-note{font-size:12px;opacity:.85}
/* Second meta line: location · check-in time · invited+response-rate.
   Items are dot-separated via ::before so any subset reads cleanly. */
.event-list-row__meta{display:flex;flex-wrap:wrap;align-items:center;
  font-size:12.5px;color:var(--muted);line-height:1.5;margin-bottom:6px}
.event-list-row__meta>span{display:inline-flex;align-items:center}
.event-list-row__meta>span+span::before{content:"·";margin:0 8px;opacity:.5}
.event-list-row__meta strong{color:var(--navy);font-weight:600}
/* Chip row: small at-a-glance badges (countdown · seats left · open rate). */
.event-list-row__chips{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:6px}
.event-chip{font-size:11px;font-weight:700;letter-spacing:.2px;
  background:var(--gray-pill);color:var(--navy);border-radius:999px;
  padding:2px 9px;white-space:nowrap}
.event-chip.is-soon{background:var(--green-soft);color:var(--green-dk)}
.event-chip.is-past{background:var(--gray-pill);color:var(--muted)}
.event-chip.is-full{background:var(--amber-soft);color:var(--amber)}
.event-chip.is-email{background:var(--accent-soft);color:var(--navy)}
.event-list-row__desc{font-size:13px;color:var(--muted);line-height:1.6;
  margin-top:4px}
.event-list-row__open{align-self:center;white-space:nowrap;flex:0 0 auto}

/* ---------- Event tools: collapsed disclosures render as compact buttons
   sharing one row (they expand to full width when opened) ---------- */
.event-tools{display:flex;flex-wrap:wrap;gap:10px;align-items:flex-start;margin-bottom:20px}
.event-tools>details{flex:0 1 auto;margin:0}
.event-tools>details[open]{flex-basis:100%}
.event-tools>details:not([open])>summary{padding:9px 16px;font-size:14px;white-space:nowrap}

/* ==========================================================================
   RESPONSIVE — mobile-first overrides
   ========================================================================== */

/* ---- Hamburger toggle button (hidden on desktop) ---- */
.nav-toggle{
  display:none;                    /* shown via media query below */
  align-items:center;justify-content:center;
  width:40px;height:40px;
  background:transparent;border:1px solid rgba(19,41,75,.15);
  border-radius:8px;cursor:pointer;padding:0;
  flex:0 0 auto;color:var(--navy);
}
.nav-toggle svg{width:22px;height:22px;display:block}

/* ---- Overlay (behind drawer on mobile, hidden on desktop) ---- */
.nav-overlay{
  display:none;
  position:fixed;inset:0;
  background:rgba(0,0,0,.45);
  z-index:19;
}
.app.nav-open .nav-overlay{display:block}

/* ==========================================================================
   ≤ 1024 px — tablets (incl. iPad landscape / split view). Apply the touch
   essentials here — BEFORE the full single-column collapse at 880px — so large
   tablets aren't stranded on the dense desktop sizing: 16px form text (stops iOS
   focus-zoom) and 44px minimum tap targets, including the bare-text per-row
   action links that the global .btn rule does NOT cover.
   ========================================================================== */
@media (max-width:1024px){
  input:not([type=checkbox]):not([type=radio]),select,textarea{font-size:16px}
  .btn{min-height:44px;display:inline-flex;align-items:center;justify-content:center}
  .btn-sm{min-height:44px}
  /* Per-row action links (Edit / Confirm / Resend …) are bare text by default —
     give them a real 44px tappable box + spacing from their neighbours. */
  .rowact{display:inline-flex;align-items:center;min-height:44px;padding:0 6px}
  td .rowact+.rowact,td .rowact+.btn-sm,td .btn-sm+.rowact,td .btn-sm+.btn-sm{margin-left:6px}
}

/* ==========================================================================
   ≤ 880 px — tablet / large phone — collapse to single column
   ========================================================================== */
@media (max-width:880px){

  /* Layout: single column; sidebar becomes off-canvas drawer */
  .app{
    grid-template-columns:1fr;
    position:relative;
  }

  /* Sidebar drawer */
  .side{
    position:fixed;top:0;left:0;bottom:0;
    width:272px;
    transform:translateX(-100%);
    transition:transform .22s ease;
    z-index:20;
    overflow-y:auto;
    -webkit-overflow-scrolling:touch;
  }
  .app.nav-open .side{transform:translateX(0)}

  /* The drawer's transform makes .side the containing block for the fixed
     staging pop (QA1 #1), so its coords turn drawer-relative here — pin it to
     the 272px drawer's width instead of the viewport's. */
  .foot-stage__pop{left:8px;right:8px;width:auto}

  /* Main area fills full width */
  .main{width:100%;min-width:0}

  /* Topbar: show hamburger, tighten padding */
  .nav-toggle{display:flex}
  .topbar{
    padding:12px 16px;
    gap:12px;
  }
  .topbar h1{font-size:19px}

  /* Content: tighten padding */
  .content{padding:16px;max-width:100%}

  /* Overflow hardening: nothing may push the page wider than the screen. */
  .main,.content{overflow-x:hidden}
  .content img{max-width:100%;height:auto}
  .content code{word-break:break-all}
  /* Inline min-widths (260px selects etc.) lose on phones — !important beats inline styles. */
  .content .fld,.content select,.content input[type=text],.content input[type=email],.content input[type=password],.content input[type=search]{min-width:0 !important;max-width:100%}
  .content form{max-width:100%}
  .rte,.rte-toolbar{max-width:100%}

  /* Stats cards: stay fluid but drop the floor so phones pack 2 compact cards per
     row (140px fits two on a ~360px phone) instead of one giant full-width card. */
  .stats{grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:12px;margin-bottom:20px}

  /* Side-by-side panel layouts stack on tablet/phone */
  .col2{grid-template-columns:1fr;gap:16px}

  /* Tables scroll horizontally (display:block fallback for UNWRAPPED panel
     tables — narrow sub-tables that have no .table-scroll wrapper). */
  .panel table{
    display:block;
    overflow-x:auto;
    -webkit-overflow-scrolling:touch;
    white-space:nowrap;
    width:100%;
  }
  /* Tables inside a .table-scroll wrapper keep the wrapper as their scroll
     context at every width and stay real <table>s (not display:block) so
     position:sticky thead keeps working on phones too. Undo the block fallback
     above for them; the base .table-scroll rule already handles the overflow.

     FIXED FLOOR, not max-content (QA2 #26). `min-width:max-content` means "as wide
     as the content would be if it never wrapped", so it forbade wrapping outright
     and directly contradicted the base .table-scroll rule above, whose own comment
     says cells WRAP rather than force a wide table. Measured on the 8-column
     invitees table in headless Chromium: forced to 2085px inside a 766px wrapper --
     63% off-screen at 768px, 81% at 390px, with Name/Organization ballooning to
     489px/379px because nothing was allowed to wrap. A fixed floor keeps the
     intended horizontal-scroll affordance with readable columns instead: at 1000px
     the invitees table gives Name 133px / Org 171px and 234px of scroll at 768px.

     THREE MEASURED TRAPS before changing this number:
       - Below ~800px a floor stops binding at all -- that table's own min-content is
         799px -- and the layout silently reverts to wrap-and-fit. A 720px floor
         measured byte-identical to deleting the declaration.
       - `min(1000px,max-content)` is INVALID CSS: intrinsic keywords are not allowed
         inside min()/max()/clamp(), so the whole declaration is dropped and this rule
         falls back to its previous value. It measured exactly as broken as the
         original. Do not "improve" this into a min() that silently no-ops.
       - This selector covers EVERY .table-scroll table. All 7 in the plugin are wide
         data tables (6-20 columns), so a flat floor is safe today; a future NARROW
         table in a .table-scroll wrapper would gain scroll it does not need and
         should get its own rule rather than lowering this one. */
  .table-scroll>table{display:table;overflow-x:visible;white-space:normal;width:100%;min-width:1000px}
  /* Allow certain cells to wrap */
  .panel table .person,.panel table td.wrap{white-space:normal}

  /* Event list: badge shrinks a bit, body wraps */
  .event-list-row{gap:12px;padding:14px 16px;flex-wrap:wrap}
  .event-list-row__open{width:100%;margin-top:4px}
  .event-live-card{flex-wrap:wrap;gap:10px}
  /* At-a-glance extras: keep the stat + meta lines wrapping with snug gaps so
     they read as tidy rows (not a column of one item each) on a phone. */
  .event-list-row__stats{gap:4px 12px;flex-wrap:wrap}
  .event-list-row__meta{font-size:12px}
  .event-list-row__meta>span+span::before{margin:0 6px}

  /* Panels: full-width search box; tighter head + table cells so panels stop
     hoarding whitespace on small screens. Bottom margins (often inline 20–28px)
     are pulled in via !important to beat the inline style. */
  .search{min-width:0;width:100%}
  .panel-head{gap:10px;padding:14px 16px}
  .panel{margin-bottom:16px !important}
  thead th{padding:10px 14px}
  tbody td{padding:11px 14px}
  /* Disclosure section spacing (dashboard .dash-sec, .add-inv etc.) */
  details.panel{margin-bottom:16px !important}

  /* Forms: full-width fields, min tap target, no iOS zoom */
  input,select,textarea{
    width:100%;
    min-height:44px;
    font-size:16px !important;  /* prevents iOS auto-zoom */
    box-sizing:border-box;
  }
  /* Buttons keep their natural width (inline-flex, not flex) — no full-width green slabs. */
  .btn{min-height:44px;display:inline-flex;width:auto;align-items:center;justify-content:center}
  /* Checkboxes/radios are exempt from the full-width 44px input sizing above. */
  input[type=checkbox],input[type=radio]{width:17px !important;min-height:0;height:17px;flex:0 0 auto}
  /* Dashboard stat cards: tighter type so labels/subtext fit the 2-up grid. */
  .card{padding:14px 12px 12px}
  .card .k{font-size:11px;letter-spacing:.2px}
  .card .v{font-size:30px}
  .card .sub{font-size:11.5px;overflow-wrap:break-word}

  /* Chips / filter row */
  .filters{padding:10px 16px}

  /* Me / avatar: drop label text on small screens */
  .me .me-name{display:none}

  /* Event meta stack */
  .event-meta-line{font-size:13px}
}

/* ==========================================================================
   ≤ 640 px — phones — two-column form field pairs collapse to one column
   ========================================================================== */
@media (max-width:640px){
  .fld-grid{grid-template-columns:1fr}
  /* Email/phone repeater rows: stack instead of cramming the radio + value input
     + label input + remove button onto one non-wrapping line (which pushed the
     remove button off-screen on a phone). */
  .pl-row{flex-wrap:wrap}
}

/* ==========================================================================
   ≤ 520 px — small phone — 2-up dense stats, further compression
   ========================================================================== */
@media (max-width:520px){

  /* Keep 2 compact cards per row even on the smallest phones (128px floor fits two
     on a ~320px screen). Previously this forced 1 column, producing the giant
     full-width stacked cards the operator flagged — ~one stat per screen-height. */
  .stats{grid-template-columns:repeat(auto-fit,minmax(128px,1fr));gap:10px}

  .topbar h1{font-size:17px}
  .content{padding:12px}

  /* Card: trim padding + shrink the oversized stat number so two read comfortably. */
  .card{padding:12px 10px 10px}
  .card .v{font-size:24px;margin-top:5px}

  /* Event date badge: a touch smaller */
  .event-date-badge{flex:0 0 60px;padding:8px 6px 6px}
  .event-date-badge__day{font-size:24px}

  /* Buttons: full width where appropriate */
  .panel-head .btn,.panel-head .btn-ghost{width:auto}

  /* Evt-picker: truncate long titles */
  .evt-picker{max-width:180px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}
}

/* ==========================================================================
   DESIGN-SYSTEM COMPONENTS (Portal redesign — Phase 0)
   Tokens + primitives centralized here from duplicated per-view inline <style>
   blocks so there is ONE source of truth (template-friendly: everything keys off
   the existing CSS variables, so a white-label rebrand is a token swap).
   Each rule is byte-identical to the inline copies it replaces — SO on the views
   that defined it inline, rendering is unchanged. BUT portal.css is the only
   stylesheet and loads on every portal page, so promoting these to global ALSO
   styles pages that used these classes WITHOUT an inline copy (e.g. settings,
   backup, the admin sub-tools, taxonomy-tag) and were therefore UNSTYLED before.
   That is a deliberate consistency improvement (verified: every newly-styled
   element is the intended component — a .fld input, a .notice-ok/.notice-err
   banner, a .grp fieldset, a .ck checkbox label — no collisions), but it IS a
   visual change on ~30 pages and wants an operator staging spot-check.
   SCOPE: every token below is wrapped in :where(#ali-app) so it applies ONLY
   inside the portal shell. portal.css also loads on STANDALONE pages (the public
   pay page, the login page) that have no #ali-app — without the scope, the global
   `form label` shrank the public pay-page labels 14px -> 13px (second-pass review
   finding). :where() contributes ZERO specificity, so inside the portal every
   rule keeps exactly the winner it had as an inline copy.
   INTENTIONALLY left LOCAL (drifted — would change rendering): view-admin's
   compact .fld/.ck, the AMBER .notice-err in view-taskforces / -calendar /
   -taskforces-member, and the whole .btn family (its own reconciliation pass).
   ========================================================================== */

/* --- Form field tokens ---------------------------------------------------- */
/* Text/select/textarea field. Centralized from byte-identical inline copies in
   view-events / -contacts / -organizations / -member.
   (view-admin keeps a COMPACT .fld variant locally — do not fold it in here; it
   pins width:auto so the global width:100% can't leak into its row layouts.
   The scoped .prf .fld in view-news / -gallery / -press is a separate follow-up.) */
:where(#ali-app) .fld{width:100%;padding:8px 10px;border:1px solid var(--border);border-radius:7px;font:inherit;margin-top:4px;background:var(--surface)}
:where(#ali-app) .fld[disabled],:where(#ali-app) .fld[readonly]{background:#f2f2ef;color:var(--muted)}
:where(#ali-app) form label{font-size:13px;font-weight:600;color:var(--navy)}

/* --- Fieldset group + inline checkbox label ------------------------------- */
/* Centralized from byte-identical inline copies in view-contacts / -organizations.
   (view-admin keeps a tighter .ck variant locally.) */
:where(#ali-app) .grp{border:1px solid var(--border);border-radius:9px;margin:14px 0 0;padding:10px 14px}
:where(#ali-app) .grp legend{font-size:12px;text-transform:uppercase;letter-spacing:.6px;color:var(--muted);font-weight:700;padding:0 6px}
:where(#ali-app) .ck{font-weight:500 !important;margin-right:14px;display:inline-flex;align-items:center;gap:6px}

/* --- Notice / banner tokens ----------------------------------------------- */
/* .notice-ok: success banner — centralized from byte-identical inline copies in
   view-events / -contacts / -organizations / -member / -admin / -taskforces /
   -calendar. (view-press / -news / -gallery still carry their own identical inline
   copy — folding them in is a follow-up.)
   .notice-err: the RED error banner (view-contacts / -organizations / -admin). The
   AMBER .notice-err in view-taskforces / -calendar / -taskforces-member is a
   DIFFERENT palette and stays local. NOTE: the legacy `.err`-named banner in
   view-press / -news / -gallery is deliberately NOT aliased here — a global `.err`
   would bleed onto the syslog `.lvl.err` severity pill; unifying `.err` →
   `.notice-err` is a separate markup follow-up. */
:where(#ali-app) .notice-ok{background:var(--green-soft);color:var(--green-dk);border:1px solid var(--green);border-radius:8px;padding:10px 14px;margin-bottom:16px;font-weight:600}
:where(#ali-app) .notice-err{background:#fdecec;border:1px solid #c0392b;color:#962d22;border-radius:8px;padding:10px 14px;margin-bottom:16px;font-weight:600}

/* --- COMPONENT: Tab strip (Reports & Admin sub-nav) ----------------------- */
/* Underline tab navigation. The active tab carries BOTH class="on" (visual: navy
   text + green underline) AND aria-current="page" (a11y), emitted only for the
   current tab. Promoted from byte-identical inline copies in view-reports
   (.rep-tabs) and view-admin (.admin-tabs); both legacy names kept so the markup
   is unchanged. */
.rep-tabs,.admin-tabs{display:flex;gap:4px;border-bottom:1px solid var(--border);margin-bottom:20px;flex-wrap:wrap}
.rep-tabs a,.admin-tabs a{padding:9px 16px;font-weight:600;font-size:14px;color:var(--muted);text-decoration:none;border-bottom:2px solid transparent;margin-bottom:-1px}
.rep-tabs a:hover,.admin-tabs a:hover{color:var(--navy)}
.rep-tabs a.on,.admin-tabs a.on{color:var(--navy);border-bottom-color:var(--green)}

/* ==========================================================================
   ROUND-2 #9 — tile text fit/wrap + visible, styled scrollbars
   ==========================================================================
   Two guarantees: (1) tile/card text ALWAYS wraps and never overflows the tile
   (.card is overflow:hidden, so an un-wrapped long token would be clipped and
   lost); (2) where a tile IS height-constrained and must scroll, the scrollbar
   is clearly visible + branded (not the near-invisible overlay bar) so it's
   obvious the region scrolls. Mobile-first: min-width:0 lets flex/grid children
   shrink so long strings break instead of forcing horizontal overflow. */

/* (1) Wrap long content inside tiles/cards. min-width:0 lets these flex/grid
   items shrink below their content; overflow-wrap breaks unbroken strings
   (long emails, URLs, metric labels) only when needed. */
.card,.event-live-card,.event-list-row,.event-list-row__body{min-width:0}
.card .k,.card .v,.card .sub,
.event-list-row__title,.event-list-row__desc,.event-list-row__meta,
.event-live-card__code{overflow-wrap:anywhere;word-break:break-word;min-width:0}

/* (2) A tile that must scroll opts in with .tile-scroll; the existing light-
   surface scroll containers (wide-table wrapper, staging popover) get the same
   clearly-visible treatment. Kept OFF the navy sidebar nav (its own thin bar) so
   the light track never clashes with the dark fill. */
.tile-scroll{overflow:auto;max-width:100%}
.tile-scroll,.table-scroll,.foot-stage__pop{
  scrollbar-width:thin;scrollbar-color:var(--green) var(--border)}
.tile-scroll::-webkit-scrollbar,
.table-scroll::-webkit-scrollbar,
.foot-stage__pop::-webkit-scrollbar{width:11px;height:11px}
.tile-scroll::-webkit-scrollbar-track,
.table-scroll::-webkit-scrollbar-track,
.foot-stage__pop::-webkit-scrollbar-track{background:var(--border);border-radius:8px}
.tile-scroll::-webkit-scrollbar-thumb,
.table-scroll::-webkit-scrollbar-thumb,
.foot-stage__pop::-webkit-scrollbar-thumb{
  background:var(--green);border-radius:8px;border:2px solid var(--border)}
.tile-scroll::-webkit-scrollbar-thumb:hover,
.table-scroll::-webkit-scrollbar-thumb:hover,
.foot-stage__pop::-webkit-scrollbar-thumb:hover{background:var(--green-dk)}

/* ==========================================================================
   COMPONENT: records-list (QA1 #11/#17) — shared per-user cards|list toggle
   + pinned favorites for record lists (Members' Directory, Organizations).
   Rendered by includes/records-list.php.
   ========================================================================== */
/* Toggle row: sits under the panel head, styled like the .filters chip row. */
.rl-toggle{display:flex;gap:8px;align-items:center;flex-wrap:wrap;padding:12px 20px;border-bottom:1px solid var(--border)}
.rl-toggle-lbl{font-size:12px;text-transform:uppercase;letter-spacing:.6px;color:var(--muted);font-weight:700}
/* Pin star: quiet by default, amber when pinned. */
.rl-pin-form{display:inline;margin:0}
.rl-pin{background:transparent;border:0;cursor:pointer;font:inherit;font-size:17px;line-height:1;color:var(--muted);padding:4px 7px;border-radius:6px}
.rl-pin:hover,.rl-pin:focus-visible{color:var(--amber);background:var(--amber-soft)}
.rl-pin.on{color:var(--amber)}
.rl-pin-cell{width:34px;text-align:center}
/* Card wrapper: hosts the pin star over a clickable card without nesting a
   form inside the card's anchor (invalid HTML). */
.rl-card-wrap{position:relative}
.rl-card-wrap>.dir-card,.rl-card-wrap>.rl-card{height:100%;box-sizing:border-box;padding-right:42px}
.rl-card-wrap>.rl-pin-form{position:absolute;top:10px;right:10px;z-index:1}
/* Generic records card + grid (Organizations cards view; mirrors the
   Directory's inline .dir-grid/.dir-card geometry). */
.rl-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:14px;padding:14px 20px}
.rl-card{display:block;border:1px solid var(--border);border-radius:10px;padding:14px 16px;background:var(--surface)}
.rl-card .nm{font-weight:700;color:var(--navy);font-size:15px;margin:0 0 4px}
.rl-card .nm a{color:inherit;text-decoration:none}
.rl-card .nm a:hover,.rl-card .nm a:focus-visible{text-decoration:underline}
.rl-card .meta{color:var(--muted);font-size:13px;line-height:1.55;margin-top:6px;display:grid;gap:3px}
.rl-card .tags{display:flex;gap:4px;flex-wrap:wrap}
