/* bears.mtc.llc
   Palette is NOT decorative — Missouri State's brand standards are a LICENCE
   CONDITION (Exhibit A: marks may be used "following all brand guidelines").
   Maroon #5E0009 (PMS 505) must be present; secondary colours may never be used
   alone or more prominently than the primary palette.
   ⛔⛔ NO PURPLE ANYWHERE IN THE APP (Jeremy, 2026-09-11: "make sure the maroons all match …
   there is no more purple in the app"). The MTC logo keeps its own colours; the UI does not
   borrow them. */
:root{
  --maroon:#5E0009; --maroon-lift:#7d1017;
  /* ⭐ FOCUS RINGS ARE MAROON, OFFSET FROM THE CONTROL. The 2026-08-26 rule kept them purple
     because "a maroon ring on a maroon button cannot be seen" — true of a ring drawn ON the
     button, false of one drawn 2px OUTSIDE it: the offset leaves a gap of page colour between
     the two, so the ring reads against the page, not the button. Every button-like focus rule
     below carries outline-offset >= 2px for exactly that reason; inputs are white, so 1px is
     enough there. REVERSED on Jeremy's instruction 2026-09-11 — do not restore the purple.
     ⭐ The view-as banner is CHARCOAL, not maroon: it must read as a STATE ("you are looking
     through someone else's eyes"), distinct from the maroon header it sits under. */
  --focus:#5E0009; --viewas:#2b2425;
  --ink:#181515; --body:#423b3b; --line:#e6e2e2; --bg:#f7f5f5; --card:#fff;
  --ok:#1c7a4a; --warn:#8a5a00; --danger:#a11122;
  --shadow:0 1px 2px rgba(24,21,21,.06),0 8px 24px -12px rgba(24,21,21,.18);
  --radius:14px;
}
*,*::before,*::after{box-sizing:border-box}

/* ⛔⛔ THE HIDDEN ATTRIBUTE MUST ACTUALLY HIDE THINGS. The browser's own rule is
   [hidden]{display:none} at USER-AGENT specificity, so ANY author rule that sets `display`
   beats it — and `.occ{display:grid}` did exactly that. Every schedule filter was setting
   row.hidden = true correctly and every row stayed on screen. Jeremy reported it as "the
   filters do not work"; the JavaScript had been right the whole time.
   ⚠️ AND IT IS WHY MY OWN EARLIER CHECK PASSED: I asserted `r.hidden === true` in the DOM
   instead of looking at the rendered page. The attribute was set. The pixels never changed.
   ⛔ Applies to every element this app hides — .occ, .monthhead, .acrow, .filterbar, dialogs'
   hint rows — so it belongs here once, not on each class. */
[hidden]{display:none !important}
body{margin:0;background:var(--bg);color:var(--body);
  font:16px/1.55 -apple-system,BlinkMacSystemFont,"Segoe UI",Inter,Roboto,Helvetica,Arial,sans-serif;
  -webkit-font-smoothing:antialiased}
h1,h2,h3{color:var(--ink);line-height:1.2;margin:0 0 .4em}
h1{font-size:1.6rem;letter-spacing:-.02em}
/* ⛔ MAROON, NOT MTC PURPLE (2026-08-26). Every link on the site rendered #5f4d73 — 35 of
   them on the Engagement page alone — so a maroon-branded product read as a purple one.
   MO State's brand standards are a LICENCE CONDITION: maroon must be present and secondary
   colours may never dominate, and the most repeated colour on a page is the dominant one. */
a{color:var(--maroon)}

/* ---- chrome ---- */
.topbar{display:flex;align-items:center;gap:1.5rem;padding:.85rem max(1.5rem,env(safe-area-inset-right)) .85rem max(1.5rem,env(safe-area-inset-left));
  background:var(--maroon);color:#fff;box-shadow:var(--shadow);flex-wrap:wrap}
.topbar .brand{display:flex;align-items:center}
.topbar img{display:block;border-radius:6px;background:#fff;padding:3px}
.nav{display:flex;gap:1.25rem;margin-right:auto;flex-wrap:wrap}
.navtoggle{display:none}
.nav a{color:#f3e6e7;text-decoration:none;font-weight:550;padding:.25rem 0;
  border-bottom:2px solid transparent}
.nav a:hover{color:#fff;border-bottom-color:rgba(255,255,255,.55)}
.who{display:flex;align-items:center;gap:.7rem;font-size:.9rem;color:#f0dfe0}
.who .name{font-weight:600;color:#fff}
.who .out{color:#f0dfe0;text-decoration:underline}
/* ⛔⛔ .pill WAS ONLY EVER STYLED FOR THE HEADER BAR — white translucent on maroon — and then
   got used on eight light-background places across five pages (setup, users, partners,
   partner, library), where a white fill and a white border on a white card is very nearly
   invisible. Measured 2026-08-26 by rendering, not by reading.
   ⭐ The LIGHT style is now the default, because that is where most of them live, and the
   translucent one is scoped to the bar it was written for. */
.pill{background:#f5f3f3;border:1px solid var(--line);color:var(--body);
  border-radius:999px;padding:.1rem .55rem;font-size:.75rem;letter-spacing:.02em;
  display:inline-block;vertical-align:1px}
.topbar .pill,.who .pill{background:rgba(255,255,255,.16);border-color:rgba(255,255,255,.3);
  color:#f0dfe0}
/* an outlined variant, for "this is a different KIND of thing" without inventing a colour */
.pill.out{background:transparent;border-style:dashed;border-color:#d6c3c6;color:var(--maroon)}

/* ⛔ 244 — max(), not a bare env(): on every phone without a notch the inset is 0, so a raw
   env() would DELETE the gutter rather than protect it. The pair with viewport-fit=cover in
   views/header.php is what makes these mean anything; neither half works alone. */
.wrap{max-width:1080px;margin:0 auto;
  padding:2rem max(1.25rem,env(safe-area-inset-right)) 4rem max(1.25rem,env(safe-area-inset-left))}
.foot{max-width:1080px;margin:0 auto;padding:0 1.25rem 3rem;color:#6d6566;font-size:.83rem}
/* ⭐ ITEM 130 — A TABLE PAGE MAY USE THE WIDTH THAT IS ALREADY THERE.
   Measured 2026-09-03 on the live site: `main` capped at 1080px inside a 1718px window, so
   638px of screen sat EMPTY while the People table crushed a two-word name onto two lines and
   a phone number onto two. ⛔ NOT RAISED GLOBALLY: 1080px is a deliberate reading measure for
   prose, and widening every page would make the long copy blocks on Setup and the guest pages
   worse. This is opt-in, per page, on the pages that hold a wide table. */
.wrap.wide{max-width:1400px}

/* ---- cards ---- */
.panel{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);
  padding:1.5rem;box-shadow:var(--shadow)}
.panel.narrow{max-width:34rem}

.sport-head{display:flex;align-items:baseline;gap:.75rem;margin:2.2rem 0 .9rem}
.sport-head:first-of-type{margin-top:.5rem}
.sport-head h2{font-size:1.15rem;margin:0}
.sport-head .meta{color:#6d6566;font-size:.87rem}

.games{display:grid;gap:.7rem}
.game{display:grid;grid-template-columns:auto 3rem 1fr auto;align-items:center;gap:1rem;
  background:var(--card);border:1px solid var(--line);border-left:4px solid var(--line);
  border-radius:var(--radius);padding:.9rem 1.15rem;text-decoration:none;color:inherit;
  box-shadow:var(--shadow);transition:transform .12s ease,border-color .12s ease}
.game:hover{transform:translateY(-1px);border-left-color:var(--maroon)}
.game.away{opacity:.62}
.game.away:hover{transform:none;border-left-color:var(--line)}

.date{text-align:center;min-width:3.4rem}
.date .d{font-size:1.35rem;font-weight:700;color:var(--ink);line-height:1}
.date .m{font-size:.7rem;text-transform:uppercase;letter-spacing:.09em;color:#6d6566}
.logo{width:3rem;height:3rem;object-fit:contain}
.opp{min-width:0}
.opp .name{font-weight:650;color:var(--ink);font-size:1.02rem}
.opp .sub{font-size:.85rem;color:#6d6566;display:flex;gap:.5rem;flex-wrap:wrap}
.tag{font-size:.72rem;text-transform:uppercase;letter-spacing:.06em;font-weight:700;
  padding:.1rem .45rem;border-radius:4px;border:1px solid}
.tag.home{color:var(--maroon);border-color:#e6c9cc;background:#fbf1f2}
.tag.away{color:#73696a;border-color:var(--line);background:#f5f3f3}
.tag.tv{color:var(--maroon);border-color:#e9dde0;background:#fbf6f7}

/* Counts carry a LABEL as well as a colour — MO State's guidelines ask that
   colour never be the sole carrier of meaning. */
.count{text-align:right;min-width:6.5rem}
.count .n{font-size:1.05rem;font-weight:700;color:var(--ink)}
.count .l{font-size:.74rem;text-transform:uppercase;letter-spacing:.06em;color:#6d6566}
.count.full .n{color:var(--danger)}
.count.none .n{color:#aca1a2}

/* ───────────────────────────────────── OCCASIONS: one list, games and events ──
   ⛔ Sport is NEVER colour-coded. At eight sports the colours turn to mud, and MO State's
   brand standards are a licence condition — maroon must be present and secondary colours may
   never dominate. Sport is a small text tag; ONE accent for everything.
   ⛔ An event is set apart by an OUTLINE, not a hue, for the same reason. */
.sub{margin-top:-.3rem;color:#6d6566}

.filterbar{display:flex;flex-wrap:wrap;gap:.45rem;align-items:center;
  margin:.2rem 0 1.3rem;padding:.55rem;background:var(--card);
  border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow)}
.filterbar .fspacer{flex:1 1 auto}
.chip{font:inherit;font-size:.86rem;font-weight:600;cursor:pointer;
  padding:.34rem .78rem;border-radius:999px;border:1px solid var(--line);
  background:#f5f3f3;color:var(--body);display:inline-flex;align-items:center;gap:.4rem}
.chip:hover{border-color:#d3cccd}
.chip.on{background:var(--maroon);border-color:var(--maroon);color:#fff}
/* the Events chip: outlined rather than filled, so it reads as a different KIND of thing
   without being a different colour */
.chip.out{background:transparent;border-style:dashed;border-color:#cec7c7}
/* ⚠️ MAROON-LIFT, NOT PURPLE (2026-08-26). An ACTIVE Events chip was the last large block of
   purple on the site and it sat right beside four maroon sport chips. It still needs to differ
   from them — the lifted maroon does that, and the chip also says "Events" on it, so the colour
   was never carrying the meaning alone. */
.chip.out.on{background:var(--maroon-lift);border-color:var(--maroon-lift);border-style:solid;color:#fff}
.chip.sm{font-size:.78rem;padding:.28rem .62rem}
.chip .cn{font-size:.74rem;opacity:.72;font-variant-numeric:tabular-nums}
.chip.on .cn{opacity:.85}

.monthhead{font-size:.76rem;font-weight:700;letter-spacing:.13em;color:#908687;
  margin:1.6rem 0 .6rem;padding-bottom:.35rem;border-bottom:1px solid var(--line)}
.monthhead:first-child{margin-top:.2rem}

.occasions{display:grid;gap:.7rem}
/* the row skeleton is the SAME for both kinds — only the tag and the pill differ */
.occ{display:grid;grid-template-columns:auto 3rem 1fr auto;align-items:center;gap:1rem;
  background:var(--card);border:1px solid var(--line);border-left:4px solid var(--line);
  border-radius:var(--radius);padding:.9rem 1.15rem;text-decoration:none;color:inherit;
  box-shadow:var(--shadow);transition:transform .12s ease,border-color .12s ease}
a.occ:hover{transform:translateY(-1px);border-left-color:var(--maroon)}
.occ.is-event{border-left-style:dashed;border-left-color:#cec7c7}
a.occ.is-event:hover{border-left-color:var(--maroon-lift)}
.occ.is-away{opacity:.62}
/* ⭐ THE EVENT TAG STAYS DISTINCT WITHOUT BEING PURPLE (2026-08-26).
   ⛔ The distinction itself is not optional — the occasion model puts events and games in ONE
   list, and the tag is the only thing telling them apart. But it does not have to be a second
   HUE: this is the device the schedule chips already use and already argue for ("set apart by
   outline rather than by colour"), so an EVENT now reads as a DASHED MAROON outline against a
   solid one. Same family, still unmistakable, and MO State's palette is not diluted. */
.tag.evt{color:var(--maroon);border-color:var(--maroon);border-style:dashed;background:transparent}
.count.away .l{color:#aca1a2}

.btn{display:inline-block;background:var(--maroon);color:#fff;text-decoration:none;
  padding:.6rem 1.1rem;border-radius:9px;font-weight:600;border:0;cursor:pointer}
.btn:hover{background:var(--maroon-lift)}
.btn.ghost{background:transparent;color:var(--maroon);border:1px solid #e0c4c7}

.notice{border-left:4px solid var(--warn);background:#fff9ec;border-radius:8px;
  padding:.9rem 1.1rem;margin-bottom:1.5rem;color:#5c4a1f;font-size:.92rem}
.notice strong{color:#4a3a14}
/* ⛔ THIS RULE WAS MISSING AND game.php HAS BEEN ASKING FOR IT SINCE IT SHIPPED. The hard-cap
   notice — "no more passes can be issued for this game" — rendered in the same amber as an
   informational note, so the one state that STOPS a staffer looked exactly like the one that
   does not. A class that matches nothing fails silently; that is the whole trap. */
.notice.bad{border-left-color:var(--danger);background:#fdf3f4;color:#5c1f27}
.notice.bad strong{color:var(--danger)}
.ghtags{margin:0 0 .45rem;display:flex;gap:.4rem;flex-wrap:wrap}

@media (max-width:640px){
  .game,.occ{grid-template-columns:auto 2.4rem 1fr;row-gap:.5rem}
  .count{grid-column:1/-1;text-align:left}
  .logo{width:2.4rem;height:2.4rem}
  /* ⚠️ chips WRAP rather than scroll — a horizontal scroller hides which filters exist,
     which is the one thing chips are for. */
  .filterbar{padding:.5rem}
  .filterbar .fspacer{flex-basis:100%}
}

/* ---- game detail ---- */
.crumb{margin:0 0 1rem}.crumb a{text-decoration:none;color:#6d6566;font-size:.9rem}
.crumb a:hover{color:var(--maroon)}
.gamehead{display:flex;align-items:center;gap:1.25rem;background:var(--card);
  border:1px solid var(--line);border-left:4px solid var(--maroon);border-radius:var(--radius);
  padding:1.25rem 1.5rem;box-shadow:var(--shadow);margin-bottom:1.5rem;flex-wrap:wrap}
.ghlogo{width:4.5rem;height:4.5rem;object-fit:contain}
.ghmeta{flex:1;min-width:14rem}.ghmeta h1{margin:0 0 .2rem}
.ghsub{margin:0;color:#6d6566;font-size:.92rem}
.ghcount{text-align:right;min-width:8rem}
.ghcount .n{font-size:2rem;font-weight:700;color:var(--ink);line-height:1}
.ghcount .n span{font-size:1.1rem;font-weight:600;color:#aca1a2}
.ghcount .l{font-size:.8rem;text-transform:uppercase;letter-spacing:.06em;color:#6d6566;margin-top:.2rem}
.ghcount.full .n,.ghcount.over .n{color:var(--danger)}
/* An away game has no allotment to be full or over — it is simply not ours. Muted, not
   alarming: nothing is wrong, there is just nothing here. PLAN.md D55b. */
.ghcount.away .n{color:#aca1a2;font-weight:600}
.ghcount.away .l{color:#6d6566}
.ghcount.over .l{color:var(--danger);font-weight:600}

.panelhead{display:flex;align-items:center;justify-content:space-between;gap:1rem;
  margin-bottom:1rem;flex-wrap:wrap}
.panelhead h2{margin:0;font-size:1.1rem}
.empty{color:#6d6566;margin:.5rem 0 0}

/* ⭐ 251b — THE ROSTER'S SCROLL FRAME. The largest roster in the system is 126 rows (event 464,
   measured 2026-09-12), which pushes everything below Attending off the bottom of a very long
   page. A frame keeps the page navigable and the list complete.
   ⛔⛔ THE CAP IS LIFTED IN PRINT, AND THAT IS THE LOAD-BEARING HALF. This table IS the will-call
      sheet MTC owes Learfield — §5 rule 1: "the will-call sheet prints every guest in full, the
      gate needs the real name or that person is turned away." A max-height that survived into
      print would truncate the sheet SILENTLY, which is the exact class of failure this repo keeps
      recording. So the print rule is written in the same change, not left to be noticed.
   ⛔ ON THE WRAPPER, NOT ON .roster — :965 already gives .roster display:block;overflow-x:auto at
      narrow widths, and a vertical cap on that element would fight the mobile rule.
   ⭐ The header stays put: position:sticky on th, so scrolling 126 rows never loses the columns. */
.rosterframe{max-height:min(62svh,44rem);overflow-y:auto;overflow-x:auto;
  -webkit-overflow-scrolling:touch;border:1px solid var(--line);border-radius:10px;
  background:var(--card)}
/* ⛔⛔ TWO NESTED HORIZONTAL SCROLLERS — I WROTE THE WARNING AND THEN SHIPPED THE BUG.
   My own comment above says "it is a wrapper, NOT overflow on .roster itself — :965 already sets
   .roster{display:block;overflow-x:auto} at narrow widths". Then I gave the wrapper
   overflow-x:auto too, so at phone width the TABLE scrolls sideways INSIDE a frame that also
   scrolls sideways. MEASURED in the page: .rosterframe overflowX auto, .roster display block /
   overflowX auto, and tableScrollsItself = true — two scrollbars fighting over one gesture,
   which is what Jeremy would have found the moment he opened the invite list on a phone.
   ⭐ THE FRAME OWNS BOTH AXES; THE TABLE OWNS NEITHER. One scroller, both directions, and the
     sticky header keeps working because the frame is the scroll container. */
@media (max-width:640px){
  .rosterframe .roster{display:table;overflow-x:visible;min-width:38rem}
  /* min-width is what makes the FRAME scroll horizontally instead of the table: the table is
     wider than the phone, the frame clips it, and one gesture moves it. */
}
.rosterframe .roster th{position:sticky;top:0;z-index:1;background:#faf8f8}
.rosterframe .roster{font-size:.92rem}
@media print{
  /* ⛔ NEVER CAP THE WILL-CALL SHEET */
  .rosterframe{max-height:none;overflow:visible;border:0;border-radius:0;background:none}
  .rosterframe .roster th{position:static}
}
.roster{width:100%;border-collapse:collapse;font-size:.94rem}
.roster th{text-align:left;font-size:.74rem;text-transform:uppercase;letter-spacing:.07em;
  color:#6d6566;font-weight:700;padding:.5rem .6rem;border-bottom:1px solid var(--line)}
.roster td{padding:.75rem .6rem;border-bottom:1px solid var(--line);vertical-align:top}
.roster tr:last-child td{border-bottom:0}
.roster .num{text-align:right}
.gname{font-weight:600;color:var(--ink)}
.gsub{font-size:.83rem;color:#6d6566;margin-top:.1rem}
.muted{color:#7a7172}
.is-private{background:#fafafa}
.pill.dark{background:var(--focus);color:#fff;border:0;margin-left:.4rem;vertical-align:1px}
.actions{text-align:right;white-space:nowrap}
.lnk{background:none;border:0;color:var(--maroon);cursor:pointer;font:inherit;
  font-size:.87rem;padding:.2rem .4rem;border-radius:5px}
/* Confirmation ON THE ROW after a sign-in link is sent — a toast at the top of a long
   People table is read by nobody. Reverts after a few seconds so it cannot be mistaken for
   a permanent state. PLAN.md item 124. */
.lnk.ok{color:#1f7a3d;font-weight:600}
.lnk:hover{background:#f3f2f2}
.lnk.danger{color:var(--danger)}.lnk.danger:hover{background:#fdf0f1}
.lnk:disabled{opacity:.5;cursor:default}

/* ---- dialog ---- */
/* ⚠️ margin:auto and max-height ADDED 2026-08-26 and they belong on the BASE rule, not on one
   dialog. Without margin:auto a modal is not reliably centred; without max-height a tall one
   (the Access modal, the add-guest dialog with its consent text) runs off the bottom of the
   viewport with no way to reach its buttons — measured 565px tall in an 824px window sitting
   at y=394. The dialog scrolls inside itself instead. */
dialog{border:0;border-radius:var(--radius);padding:0;max-width:32rem;width:calc(100% - 2rem);
  margin:auto;max-height:calc(100vh - 3rem);max-height:calc(100svh - 3rem);overflow:auto;
  box-shadow:0 24px 64px -16px rgba(24,21,21,.45)}
dialog::backdrop{background:rgba(24,21,21,.45)}
dialog form{padding:1.5rem}
dialog h2{margin:0 0 1rem;font-size:1.15rem}
dialog label{display:block;font-size:.82rem;font-weight:600;color:var(--ink);margin-bottom:.85rem}
dialog input{display:block;width:100%;margin-top:.25rem;padding:.55rem .7rem;font:inherit;
  border:1px solid #dbd5d6;border-radius:8px;background:#fff;color:var(--ink)}
dialog input:focus{outline:2px solid var(--focus);outline-offset:1px;border-color:var(--focus)}
.row2{display:grid;grid-template-columns:1fr 1fr;gap:.75rem}
dialog menu{display:flex;gap:.6rem;justify-content:flex-end;margin:1.25rem 0 0;padding:0}
.hint{font-size:.83rem;color:#6d6566;margin:.1rem 0 0}
.hint.warn{color:var(--warn);font-weight:600}
.err{color:var(--danger);font-size:.88rem;margin:.6rem 0 0;font-weight:600}

.sugg{border:1px solid var(--line);border-radius:9px;max-height:15rem;overflow:auto;
  margin:-.6rem 0 .85rem;background:#fff;box-shadow:var(--shadow)}
.suggrow{display:block;width:100%;text-align:left;background:none;border:0;cursor:pointer;
  padding:.55rem .75rem;font:inherit;border-bottom:1px solid var(--line)}
.suggrow:last-child{border-bottom:0}
.suggrow:hover,.suggrow:focus{background:#f8f7f7;outline:none}
.sname{display:block;font-weight:600;color:var(--ink);font-size:.92rem}
.smeta{display:block;font-size:.79rem;color:#6d6566;margin-top:.05rem}
@media (max-width:640px){.row2{grid-template-columns:1fr}}

/* ---- blast ---- */
.headbtns{display:flex;gap:.6rem;flex-wrap:wrap}
dialog select{display:block;width:100%;margin-top:.25rem;padding:.55rem .7rem;font:inherit;
  border:1px solid #dbd5d6;border-radius:8px;background:#fff;color:var(--ink)}
dialog textarea{display:block;width:100%;margin-top:.25rem;padding:.55rem .7rem;font:inherit;
  border:1px solid #dbd5d6;border-radius:8px;background:#fff;color:var(--ink);resize:vertical}
dialog textarea:focus,dialog select:focus{outline:2px solid var(--focus);outline-offset:1px}
#confirmDlg{max-width:44rem}
.recips{max-height:8rem;overflow:auto;border:1px solid var(--line);border-radius:9px;
  padding:.5rem .7rem;margin:.5rem 0 .9rem;background:#fafafa}
.recip{font-size:.86rem;color:#423b3b;padding:.15rem 0}
.subjline{font-size:.9rem;margin:.2rem 0 .6rem;color:#423b3b}
#cPreview{width:100%;height:22rem;border:1px solid var(--line);border-radius:10px;background:#fff}

/* ---- suite & prime ---- */
.entitle{display:grid;grid-template-columns:repeat(auto-fit,minmax(15rem,1fr));gap:.8rem;margin:1.2rem 0 1.6rem}
/* ⚠️ The event cards' top rule was MTC purple; maroon-lift keeps them distinct from the plain
   panels around them without introducing a second hue. */
.ecard{background:var(--card);border:1px solid var(--line);border-top:3px solid var(--maroon-lift);
  border-radius:var(--radius);padding:.95rem 1.1rem;box-shadow:var(--shadow)}
.ecard .ek{font-size:.74rem;text-transform:uppercase;letter-spacing:.07em;color:#6d6566;font-weight:700}
.ecard .ev{font-size:1.5rem;font-weight:700;color:var(--ink);margin:.15rem 0}
.ecard .ev span{font-size:.85rem;font-weight:600;color:#aca1a2}
.ecard .ee{font-size:.79rem;color:#6d6566;line-height:1.4}

/* ---- gameday settings ---- */
.gsettings{margin-top:1.25rem}
.gsettings label{display:block;font-size:.82rem;font-weight:600;color:var(--ink);margin-bottom:.9rem}
.gsettings textarea,.gsettings input,.gsettings select{display:block;width:100%;margin-top:.25rem;
  padding:.55rem .7rem;font:inherit;border:1px solid #dbd5d6;border-radius:8px;background:#fff;
  color:var(--ink);resize:vertical}
.gsettings textarea:focus,.gsettings input:focus,.gsettings select:focus{
  outline:2px solid var(--focus);outline-offset:1px;border-color:var(--focus)}
.hint.ok{color:var(--ok);font-weight:600}

/* ================= dashboard ================= */
.hero{position:relative;overflow:hidden;border-radius:var(--radius);margin-bottom:1.25rem;
  background:linear-gradient(135deg,#5E0009 0%,#7d1017 55%,#2a0004 160%);
  color:#fff;padding:2.1rem 2rem;box-shadow:var(--shadow)}
.heroInner{position:relative;z-index:2;max-width:38rem}
.hero .eyebrow{margin:0 0 .35rem;font-size:.74rem;letter-spacing:.14em;text-transform:uppercase;
  color:rgba(255,255,255,.72);font-weight:700}
.hero h1{margin:0 0 .4rem;font-size:2rem;color:#fff;letter-spacing:-.02em}
.hero .lede{margin:0;color:rgba(255,255,255,.86);font-size:1rem;line-height:1.5;max-width:34rem}
.heroMark{position:absolute;right:-1.5rem;top:50%;transform:translateY(-50%);
  height:150%;opacity:.10;z-index:1;pointer-events:none}

.nextgame{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);
  box-shadow:var(--shadow);overflow:hidden;margin-bottom:1.25rem}
.ngLabel{display:flex;align-items:center;gap:.5rem;padding:.7rem 1.4rem;background:#fbf1f2;
  border-bottom:1px solid #f0dfe1;font-size:.76rem;font-weight:700;letter-spacing:.07em;
  text-transform:uppercase;color:var(--maroon)}
.ngLabel .dot{width:7px;height:7px;border-radius:50%;background:var(--maroon);
  box-shadow:0 0 0 3px rgba(94,0,9,.16)}
.ngBody{display:flex;align-items:center;gap:1.4rem;padding:1.4rem;flex-wrap:wrap}
.ngLogo{width:5rem;height:5rem;object-fit:contain;flex:none}
.ngMeta{flex:1;min-width:15rem}
.ngMeta h2{margin:0 0 .25rem;font-size:1.45rem;letter-spacing:-.01em}
.ngMeta p{margin:0;color:#6d6566;font-size:.92rem}
.ngVenue{margin-top:.15rem !important;font-size:.86rem !important}
.ngNums{text-align:right;min-width:9rem}
.ngBig{font-size:2.3rem;font-weight:700;color:var(--ink);line-height:1}
.ngBig span{font-size:1.1rem;color:#aca1a2;font-weight:600}
.ngSub{font-size:.8rem;color:#6d6566;margin:.15rem 0 .7rem}
.bar{height:5px;background:#f2eff0}
.bar span{display:block;height:100%;background:linear-gradient(90deg,var(--maroon),#7d1017)}

.statrow{display:grid;grid-template-columns:repeat(auto-fit,minmax(11rem,1fr));gap:.8rem;margin-bottom:1.5rem}
.stat{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);
  padding:1rem 1.15rem;box-shadow:var(--shadow)}
.stat .sk{font-size:.72rem;text-transform:uppercase;letter-spacing:.08em;color:#6d6566;font-weight:700}
.stat .sv{font-size:1.7rem;font-weight:700;color:var(--ink);line-height:1.15;margin:.1rem 0}
.stat .sv span{font-size:.9rem;color:#aca1a2;font-weight:600}
.stat .se{font-size:.79rem;color:#6d6566}

.cols{display:grid;grid-template-columns:1fr 1fr;gap:1rem;align-items:start}
.uplist,.actlist{list-style:none;margin:0;padding:0}
.uplist li{border-bottom:1px solid var(--line)}
.uplist li:last-child{border-bottom:0}
.uplist a{display:flex;align-items:center;gap:.8rem;padding:.65rem .1rem;text-decoration:none;color:inherit}
.uplist a:hover .un{color:var(--maroon)}
.uplist .ud{font-size:.78rem;font-weight:700;color:#6d6566;min-width:3.2rem;letter-spacing:.02em}
.uplist img{width:1.9rem;height:1.9rem;object-fit:contain}
.uplist .un{flex:1;font-weight:600;color:var(--ink);font-size:.95rem}
.uplist .uc{font-size:.85rem;font-weight:700;color:var(--ink)}
.uplist .uc.zero{color:#cac2c3}

/* ⚠️ .uplist a was the ONLY styled child; the away rows are a <span class="nolink"> because
   they have nowhere to go, and without this they would collapse to unaligned text. */
.uplist .nolink{display:flex;align-items:center;gap:.8rem;padding:.65rem .1rem;
  color:inherit;opacity:.6}
/* a small dot stands in for the opponent crest an event does not have, so every row's
   columns line up whether or not there is a logo */
.uplist .udot{width:1.9rem;height:1.9rem;display:inline-flex;flex:0 0 1.9rem}
.uplist .udot::after{content:"";margin:auto;width:.5rem;height:.5rem;border-radius:50%;
  background:#dad5d5}
.uplist .udot.evt::after{background:transparent;border:2px solid var(--maroon-lift);
  width:.62rem;height:.62rem}

/* THE PARTNER FILTER (82, made a dropdown 2026-08-26).
   ⛔⛔ IT USED TO BE A ROW OF CHIPS AND THAT DID NOT SCALE. Jeremy: "if we end up with 50
      partners that is not manageable." The chips on the SCHEDULE stay chips — that is a closed
      set of four. An organisation list grows forever, so it gets a dropdown with a search. */
.orgbar{margin-bottom:.9rem;position:relative;z-index:30}
.orgbar .orglabel{font-size:.82rem;color:#6d6566;font-weight:600;margin-right:.2rem}
.orgnote{margin:-.4rem 0 1.1rem;font-size:.86rem;color:#6d6566}
.orgnote strong{color:var(--maroon)}
.orgnote a{color:#6d6566;text-decoration:underline;text-underline-offset:2px}
.orgnote a:hover{color:var(--maroon)}

.orgpick{position:relative}
/* ⛔ list-style:none AND ::-webkit-details-marker — Safari uses the pseudo-element and ignores
   the property, so dropping either leaves a stray disclosure triangle on one browser only. */
.orgpick > summary{list-style:none;cursor:pointer}
.orgpick > summary::-webkit-details-marker{display:none}
.orgpick-btn{display:inline-flex;align-items:center;gap:.5rem;
  font:inherit;font-size:.86rem;font-weight:600;color:var(--ink);
  background:#fff;border:1px solid var(--line);border-radius:999px;
  padding:.34rem .75rem .34rem .85rem;min-width:12rem;justify-content:space-between}
.orgpick-btn:hover{border-color:#d3cccd}
.orgpick-btn:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
.orgpick[open] > .orgpick-btn{border-color:var(--maroon);box-shadow:0 0 0 3px rgba(94,0,9,.09)}
.orgpick-caret{flex:none;color:#6d6566;transition:transform .12s ease}
.orgpick[open] .orgpick-caret{transform:rotate(180deg)}

.orgpick-panel{position:absolute;top:calc(100% + .4rem);left:0;z-index:40;
  min-width:17rem;max-width:min(24rem,92vw);
  background:var(--card);border:1px solid var(--line);border-radius:.7rem;
  box-shadow:var(--shadow);padding:.35rem}
.orgpick-search{width:100%;font:inherit;font-size:.86rem;
  padding:.42rem .6rem;margin-bottom:.3rem;
  border:1px solid var(--line);border-radius:.45rem;background:#fbfbfb}
.orgpick-search:focus{outline:none;border-color:var(--focus);background:#fff}
/* ⚠️ The list SCROLLS. At 50 partners an unbounded panel runs off the bottom of the screen and
   the last partners are unreachable — which is the same failure the chips had, moved. */
.orgpick-list{list-style:none;margin:0;padding:0;max-height:17rem;overflow-y:auto}
.orgpick-opt{display:flex;align-items:center;gap:.5rem;
  padding:.42rem .5rem;border-radius:.45rem;text-decoration:none;
  color:var(--body);font-size:.88rem}
.orgpick-opt:hover,.orgpick-opt:focus-visible{background:#f5f3f3;color:var(--ink);outline:none}
.orgpick-opt.on{background:var(--maroon);color:#fff;font-weight:600}
.orgpick-opt.on .orgpick-kind{color:rgba(255,255,255,.8)}
.orgpick-tick{flex:none;width:1rem;text-align:center;font-weight:700}
.orgpick-name{flex:1 1 auto}
.orgpick-off{color:#a19798;font-weight:400}
.orgpick-kind{flex:none;font-size:.72rem;color:#958a8b;font-variant-numeric:tabular-nums}
.orgpick-none{margin:.3rem .5rem .45rem;font-size:.84rem;color:#6d6566}

@media (max-width:560px){
  .orgpick,.orgpick-btn{width:100%}
  .orgpick-panel{left:0;right:0;max-width:none}
}

/* UNDO AT THE GATE (item 94). ⚠️ The button has to be reachable with a thumb, in the dark,
   with a queue behind you — and it must not be so prominent that it gets pressed by accident
   while somebody is aiming a camera. */
/* ⚠️ .scanrow IS A BLOCK ON WHITE, NOT A FLEX ROW ON MAROON. I wrote this rule for a dark
   flex row first — `margin-left:auto` does nothing in a block, and a white-on-transparent
   button is invisible on a white card. Positioned instead, so the existing stacked
   name/detail layout is left exactly as it was. */
.scanrow{position:relative}
/* ⛔⛔ 192b — TWO CONTROLS CANNOT SHARE ONE ABSOLUTE CORNER. `.scanundo` was
   position:absolute at top/right, and a wrong-day row that is ALSO checked in carries TWO of
   them (Undo, and Admit anyway) — so the second painted exactly on top of the first and the
   Undo was invisible while sitting in the DOM. Jeremy photographed it twice; my assertions
   checked the source and the API JSON and were green through both.
   ⭐ THE ACTIONS NOW LIVE IN A FLEX CONTAINER BELOW THE TEXT, which is also the right shape for
      a phone at a gate: two pills side by side at the top-right would squeeze the guest's name
      into a column on a 390px screen. */
/* ⭐⭐ 192c — THE DOOR PICKER IS A GATE CONTROL AND HAD NO STYLING AT ALL.
   Measured on the live page: a bare 19px-tall native select — and it is REQUIRED, so a person
   at a door must hit it before they can admit anybody. It is now a full-width 44px control at
   EVERY size, not just on phones: nobody works a gate on a mouse, and a bigger target costs a
   desktop nothing. ⛔ Its own block, below the question, so the label never squeezes it. */
.gate-door{display:block;margin:.8rem 0 .2rem;font-weight:600}
.gate-door select{display:block;width:100%;margin-top:.35rem;font:inherit;font-size:1rem;
  min-height:44px;padding:.5rem .6rem;border:1px solid var(--line);border-radius:.5rem;
  background:#fff;color:inherit}
/* ⭐ And every control inside the gate panel is thumb-sized — UNCONDITIONALLY, not behind a
   media query. The gate is the one screen in this product used one-handed, outdoors, with a
   queue waiting, and it is ALWAYS a phone.
   ⛔ It was written as `@media (pointer:coarse)` first, and that is exactly the kind of rule I
      cannot verify from this desktop browser: it measured 36px here and I would have been
      guessing that it became 44px on his handset. A size I can measure beats a size I have to
      reason about. */
.gate .btn{min-height:44px}
.scanacts{display:flex;gap:.4rem;justify-content:flex-end;flex-wrap:wrap;margin-top:.5rem}
.scanacts:empty{display:none;margin:0}
/* ⭐⭐ 192c — A GATE IS WORKED ON A PHONE, WITH ONE THUMB, IN A QUEUE. Jeremy, 2026-09-09:
   "this need to be completely reactive and work with all phone sizes."
   ⛔ The desktop pill is ~24px tall. Apple's own guidance is a 44px minimum target and this is
      the control that admits somebody or undoes admitting them — a mis-tap here is a person
      wrongly let in or wrongly turned away.
   ⭐ On a COARSE POINTER or a narrow screen the actions become full-height, share the row two-up
      and WRAP rather than overflow, so there is no width at which they collide or leave the card.
   ⚠️ `pointer:coarse` catches a big phone in landscape that is wider than the breakpoint; the
      max-width catches a narrow desktop window. Either alone misses a real case. */
@media (pointer: coarse), (max-width: 30rem){
  .scanacts{gap:.5rem}
  .scanundo{min-height:44px;padding:.6rem 1rem;font-size:.9rem;flex:1 1 8rem}
}
.scanundo{font:inherit;font-size:.78rem;
  font-weight:600;padding:.24rem .7rem;border-radius:999px;cursor:pointer;
  border:1px solid var(--line);background:#fff;color:#6e6e73}
.scanundo:hover{border-color:var(--maroon);color:var(--maroon)}
.scanundo:disabled{opacity:.5;cursor:default}
/* ⛔ STRUCK THROUGH, NOT DELETED. The row stays so the log still shows what happened. */
.scanrow.undone{border-left-color:#ccc}
.scanrow.undone strong,.scanrow.undone span:not(.scanundone){text-decoration:line-through;opacity:.55}
.scanundone{position:absolute;top:.7rem;right:.7rem;font-size:.72rem;text-transform:uppercase;
  letter-spacing:.06em;color:#958a8b}
/* the name must not run under the button */
/* ⭐ 192b — no reserved gutter needed now the actions are not overlaying the text. */
.scanrow strong,.scanrow span{padding-right:0}

/* the branding library, split by brand owner (item 90). The category is a SUB-head inside a
   brand section, so it must read as smaller than the brand without disappearing. */
.libcat{font-size:.82rem;text-transform:uppercase;letter-spacing:.06em;
  color:#6d6566;margin:1rem 0 .3rem;font-weight:700}
.libcat:first-of-type{margin-top:.3rem}

/* the Access modal (81) — role and override must READ differently, not just compute
   differently, or a deliberate exception looks like a normal setting */
.acperms{display:grid;gap:.15rem;margin:.6rem 0 .9rem}
.acrow{display:flex;align-items:center;justify-content:space-between;gap:1rem;
  padding:.55rem .6rem;border-radius:9px}
.acrow.is-override{background:#fbf1f2;box-shadow:inset 3px 0 0 var(--maroon)}
.aclabel{font-weight:600;color:var(--ink);font-size:.94rem}
.acsub{font-weight:500;font-size:.76rem;color:#908687;margin-top:.1rem}
.acrow.is-override .acsub{color:var(--maroon)}
.acseg{display:flex;flex:0 0 auto;border:1px solid var(--line);border-radius:8px;overflow:hidden}
.acseg button{font:inherit;font-size:.8rem;padding:.3rem .6rem;border:0;cursor:pointer;
  background:#fff;color:var(--body);border-left:1px solid var(--line)}
.acseg button:first-child{border-left:0}
.acseg button.on{background:var(--maroon);color:#fff}
@media (max-width:640px){ .acrow{flex-wrap:wrap;gap:.4rem} }

/* two wallet buttons side by side — they wrap on a phone rather than overflow, and a
   single button is unaffected (a one-item flex row lays out as it always did) */
.walletbtns{display:flex;gap:.5rem;flex-wrap:wrap;justify-content:center;align-items:center}

/* month bands inside the Engagement table — the same grouping the schedule uses, so the
   two screens read the same way. ⛔ A month band with nothing under it is hidden by
   schedule.js, exactly as on the schedule. */
.occtable .monthrow td{background:#f5f3f3;font-size:.72rem;font-weight:700;letter-spacing:.13em;
  color:#908687;padding:.4rem .6rem}
.occtable .occ .tag{margin-right:.35rem}

/* ⛔⛔ A TABLE ROW IS NOT A CARD. `.occ` is the SCHEDULE's card skeleton — `display:grid` with
   four columns — and the Engagement "By occasion" table reuses that class on its <tr> so both
   screens can share ONE filter (`.chip[data-f]` + rows carrying `data-f`). Sharing the class
   dragged the card layout in with it: seven <td>s poured into a four-column grid, wrapping onto
   a second line, each cell an arbitrary width, none of them lining up with the <th> above.
   Measured 2026-08-28: the row computed `display:grid`, width 524px inside a 990px table, cells
   179/48/172/35/179/48/172 against headers 524/71/60/97/70/93/75. Jeremy: "this is not
   diplaying right presently 'By occasion' it in the engagement".
   ⭐ THE CLASS IS KEPT ON PURPOSE — it is what makes the shared filter work, and renaming it
   would fork the one filter into two.
   ⛔ AND THIS IS ONLY SAFE BECAUSE OF D23: `[hidden]{display:none !important}` now sits above
   every author rule, so re-declaring `display` here can no longer stop a filter from hiding a
   row. Before that fix this exact shape of rule was the bug. */
.occtable tr.occ{display:table-row;background:none;border:0;border-radius:0;padding:0;
  box-shadow:none;transform:none}
.occtable tr.occ>td{display:table-cell;vertical-align:middle}
.occtable tr.occ:hover{transform:none}

/* ───────────────────────────────── YOUR BENEFITS — a list, never a tile grid ──
   ⛔ One row per benefit HELD. A grid built for eight sports shows a partner who holds one
   thing a single tile and seven blanks; a list of one row looks intentional. */
.benefits{margin-bottom:1.5rem}
.benefits .meta{color:#6d6566;font-size:.87rem}
.benlist{list-style:none;margin:0;padding:0}
.benlist li{padding:.55rem 0;border-bottom:1px solid var(--line)}
.benlist li:last-child{border-bottom:0}
.benlist a{display:flex;align-items:center;gap:.9rem;text-decoration:none;color:inherit}
.benlist a:hover .bl{color:var(--maroon)}
.benlist .bl{flex:0 0 auto;min-width:11rem;font-weight:650;color:var(--ink);font-size:.95rem}
.benlist .bbar{flex:1;height:.5rem;border-radius:999px;background:#eeebeb;overflow:hidden;
  min-width:4rem}
.benlist .bbar>span{display:block;height:100%;background:var(--maroon);border-radius:999px}
.benlist .bn{font-weight:700;color:var(--ink);font-size:.95rem;font-variant-numeric:tabular-nums}
.benlist .bn span{font-weight:600;color:#aca1a2}
.benlist .bsub{font-size:.78rem;color:#908687;margin-top:.1rem}
@media (max-width:640px){
  .benlist a{flex-wrap:wrap;gap:.5rem}
  .benlist .bl{flex:1 1 100%;min-width:0}
}

.actlist li{padding:.6rem 0;border-bottom:1px solid var(--line)}
.actlist li:last-child{border-bottom:0}
.actlist .an{font-weight:600;color:var(--ink);font-size:.95rem;display:flex;
  justify-content:space-between;gap:.75rem}
.actlist .ap{font-weight:700;color:#6d6566;font-size:.82rem;white-space:nowrap}
.actlist .am{font-size:.82rem;color:#6d6566;margin-top:.1rem}

@media (max-width:820px){
  .cols{grid-template-columns:1fr}
  .hero{padding:1.6rem 1.35rem}
  .hero h1{font-size:1.6rem}
  .ngNums{text-align:left;min-width:0}
}

/* ================= MO State watermark =================
   The "Mo State" script sitting faintly behind every page.

   ⚠️ NOT a CSS mask. The first attempt masked on the PNG's alpha channel — but
      this artwork is a script with a HEAVY OUTLINE, so its alpha is one solid
      blob and the silhouette swallowed the letterforms completely. It rendered
      as an amorphous smudge, not a wordmark.
      The asset is now the artwork with its WHITE FILL REMOVED (fuzz 28%), so the
      maroon outline carries the shape, and it is drawn as a normal background
      image at low opacity. Outline art needs more opacity than a solid would.
   ⛔ It must never compete with text: fixed, non-interactive, behind everything,
      and excluded from print (see print.css). */
body::before{
  content:"";
  position:fixed;
  right:3vw;
  bottom:4vh;
  width:min(40vw, 26rem);
  aspect-ratio:1200 / 935;   /* must match the artwork or `contain` letterboxes it */
  background:url(/assets/mostate-script.png) no-repeat center/contain;
  opacity:.11;
  pointer-events:none;
  z-index:0;
}
/* Content must sit above the watermark. The topbar paints its own ground. */
/* ⛔⛔ THE CLASSIC z-index TRAP, and it is why the mobile menu rendered BEHIND the page.
   .topbar and .wrap both sat at z-index:1 and .wrap comes LATER in the DOM, so at equal
   z-index the content won. Worse: giving .topbar a z-index makes it a STACKING CONTEXT, so
   the menu's own z-index:40 was sealed inside it and only ever competed with its siblings in
   the header — a child can never escape its parent's stacking context, however large its
   number. The header has to outrank the content, and the fix belongs HERE, not on .nav.
   ⚠️ 70 puts it above the Terra launcher (60) and its panel (59) too, so an open menu covers
   them instead of being punched through by a floating circle. */
.wrap,.foot{position:relative;z-index:1}
.topbar{position:relative;z-index:70}
.hero{z-index:1}

@media (max-width:640px){
  body::before{width:70vw;right:2vw;bottom:2vh;opacity:.09}
}
/* ⛔ A STRAY `}` SAT HERE. CSS parsing made it swallow the NEXT rule, so
   .gsettings .lblhint never applied at any width — a rule that was simply never in effect
   and could not be seen to fail. Removed 2026-08-25. */
/* ⛔ F2 — SCOPED TO .gsettings, SO EVERY OTHER .lblhint RENDERED INLINE. partner.php's
   add-person dialog reads "Email They sign in with a link sent here." as one unbroken
   string, inconsistent with its own Name/Mobile/Role siblings, on the FIRST screen an
   admin uses to onboard a partner. MEASURED in the browser, not read off the source.
   ⭐ The class was always generic; only the selector was not. Unscoped now, with
   .gsettings kept as a no-op ancestor for anything relying on specificity. */
.lblhint{display:block;font-weight:400;color:#6d6566;font-size:.82rem;margin:.15rem 0 .1rem}
dialog menu.split{justify-content:space-between;align-items:center}
dialog menu.split span{display:flex;gap:.6rem}
.ck{display:flex;gap:.55rem;align-items:flex-start;padding:.4rem 0;font-weight:400;
  font-size:.9rem;color:var(--ink);cursor:pointer;margin:0}
.ck input{margin-top:.25rem;flex:none}

/* ⚠️ The generic `dialog input {display:block; width:100%}` rule above is meant for
   text fields, but it also caught these CHECKBOXES — rendering each one as a
   full-width block on its own line, floating above the name it belongs to. Reset
   them explicitly rather than loosening the generic rule, which text inputs rely on. */
.ck input[type="checkbox"]{display:inline-block;width:auto;padding:0;margin:.3rem 0 0;
  flex:none;accent-color:var(--maroon)}
.ck{align-items:flex-start}
.ck:hover{background:#f5f3f3;border-radius:6px}
.ck span{flex:1;min-width:0}

/* ── Terra ─────────────────────────────────────────────────────────────────
   A circular launcher in the lower right that opens a chat window. It replaced an
   inline panel that lived on two screens only; a launcher that follows you is
   reachable from wherever you actually got stuck.
   ⛔ The ICON came from Disbursal Flow. The UI did not — this is Bears' own. */
.terra-launch{
  position:fixed;right:1.25rem;bottom:1.25rem;z-index:60;
  width:3.5rem;height:3.5rem;border-radius:50%;
  display:flex;align-items:center;justify-content:center;
  background:#fffdfa;border:2px solid #5E0009;cursor:pointer;padding:0;
  /* Maroon must appear — it is a licence condition, not a preference. It is the ring.
     ⚠️ Bears is LIGHT-THEME ONLY (no data-theme, no prefers-color-scheme), so the
     light-background artwork terra-d.png is the only variant that ships. terra-n.png (the
     dark-background one) stays in assets/ as source and is deliberately NOT deployed —
     shipping an asset nothing references is weight on every page load for nobody. */
  box-shadow:0 6px 18px rgba(0,0,0,.22);
  transition:transform .15s ease, box-shadow .15s ease;
}
.terra-launch:hover{transform:translateY(-2px);box-shadow:0 9px 24px rgba(0,0,0,.28)}
/* ⚠️ .wrap's bottom padding clears the launcher only at the END of the document. Mid-scroll
   the circle still covers the right ~76px, which is exactly where .actions is right-aligned
   in every table. Tucked in and made slightly smaller on a phone. */
@media (max-width:40rem){
  .terra-launch{right:.6rem;bottom:.6rem;width:3rem;height:3rem}
  .terra-launch img{width:2.1rem;height:2.1rem}
}
.terra-launch:focus-visible{outline:3px solid var(--focus);outline-offset:3px}
.terra-launch img{display:block;width:2.5rem;height:2.5rem;object-fit:contain}
/* ⚠️ The eye is MAROON as of 2026-08-25 (Jeremy asked; it was grey). Recolouring it was not
   a hue swap: the artwork's ALPHA is solid across the whole eye and its detail lives in the
   LUMINANCE — dark ink on light fill. Flooding RGB while keeping alpha produced a solid
   maroon blob, the same failure the MO State watermark hit here. The mask is
   alpha × (1 − luminance)^0.5, so a dark line becomes opaque maroon and the light fill
   becomes transparent. Source of truth: assets/terra-maroon.png. */
.terra-launch.is-open{transform:scale(.92);opacity:.9}

.terra-panel{
  position:fixed;right:1.25rem;bottom:5.4rem;z-index:59;
  width:23rem;max-width:calc(100vw - 2.5rem);
  height:29rem;max-height:calc(100vh - 8rem);max-height:calc(100svh - 8rem);
  background:#fff;border-radius:.9rem;overflow:hidden;
  box-shadow:0 16px 44px rgba(0,0,0,.26);
  display:none;flex-direction:column;
}
.terra-panel.is-open{display:flex}

.terra-head{display:flex;align-items:center;gap:.6rem;padding:.7rem .8rem;
  background:#5E0009;color:#fff;flex:0 0 auto}
.terra-head-mark{background:#fffdfa;border-radius:50%;padding:.18rem;flex:0 0 auto}
.terra-head-txt{display:flex;flex-direction:column;line-height:1.2;flex:1 1 auto;min-width:0}
.terra-head-txt strong{font-size:.98rem}
.terra-head-txt span{font-size:.72rem;opacity:.82}
/* ⚠️ The attribution sits UNDER the capability line and must not compete with it — quieter
   and smaller, so "gameday, parking, rosters" is still the first thing read. */
.terra-head-txt .terra-powered{font-size:.64rem;opacity:.62;letter-spacing:.01em}
.terra-x{background:none;border:0;color:#fff;font-size:1.5rem;line-height:1;cursor:pointer;
  padding:0 .2rem;opacity:.85}
.terra-x:hover{opacity:1}

.terra-log{flex:1 1 auto;overflow-y:auto;padding:.8rem;background:#faf9f9}
.terra-msg{padding:.5rem .7rem;border-radius:.75rem;margin-bottom:.5rem;line-height:1.45;
  font-size:.92rem;max-width:88%}
.terra-msg .who{display:block;font-size:.66rem;letter-spacing:.06em;text-transform:uppercase;
  opacity:.7;margin-bottom:.15rem}
.terra-msg.you{background:rgba(94,0,9,.09);margin-left:auto;border-bottom-right-radius:.2rem}
.terra-msg.terra{background:#fff;border:1px solid rgba(0,0,0,.07);border-bottom-left-radius:.2rem}
.terra-msg.pending{opacity:.6;font-style:italic}
.terra-msg.err{background:rgba(180,40,40,.10);border-color:rgba(180,40,40,.2)}

.terra-ask{display:flex;gap:.4rem;padding:.6rem;background:#fff;border-top:1px solid rgba(0,0,0,.08);
  flex:0 0 auto}
.terra-ask input{flex:1;min-width:0;padding:.5rem .65rem;border:1px solid rgba(0,0,0,.18);
  border-radius:1.2rem;font:inherit;font-size:.92rem}
.terra-ask input:focus{outline:2px solid var(--focus);outline-offset:0;border-color:transparent}
.terra-send{background:#5E0009;color:#fff;border:0;border-radius:1.2rem;padding:.5rem .95rem;
  font:inherit;font-size:.9rem;font-weight:600;cursor:pointer}
.terra-send:disabled{opacity:.55;cursor:default}

/* On a phone the panel is the screen, not a floating card. */
@media (max-width:34rem){
  .terra-panel{right:.6rem;left:.6rem;bottom:4.8rem;width:auto;height:auto;top:3.5rem}
}
/* The will-call sheet is a printed document handed to a gate. Terra is not part of it. */
@media print{ .terra-launch,.terra-panel{display:none !important} }

/* ── The gate ──────────────────────────────────────────────────────────────
   Read at arm's length, outdoors, in a queue, probably in the dark. Big type,
   one decision per screen, and colour that survives glare. */
.gate .gate-banner{padding:.9rem 1rem;border-radius:.6rem;margin:0 0 1rem;font-size:1.15rem}
.gate .gate-banner strong{display:block;font-size:1.5rem;line-height:1.2}
.gate .gate-banner span{display:block;font-size:.9rem;opacity:.9;margin-top:.2rem}
.gate .gate-banner .gate-note{font-weight:600;opacity:1;margin-top:.45rem}
.gate-banner.good{background:#1f6b3a;color:#fff}
.gate-banner.warn{background:#8a5a00;color:#fff}
.gate-banner.bad {background:#8a1f1f;color:#fff}
.gate-guest{padding:.4rem 0 1rem}
.gate-name{font-size:1.7rem;font-weight:700;line-height:1.15}
.gate-party{font-size:1.15rem;font-weight:600;color:#5E0009;margin:.2rem 0 .5rem}
.gate-sub{font-size:.92rem;opacity:.75;margin-top:.15rem}
.gate-admit{font-size:1.15rem;padding:.85rem 1.6rem;width:100%}

/* ── 264 · THE BIG MARK AFTER A SCAN ────────────────────────────────────────
   Jeremy: "a transparent model basically to show for a few seconds and then auto dismiss or could be
   quickly dismissed by touching the screen." ⭐ Sized off the SHORTER side of the screen so a phone
   held either way and a tablet all get a mark most of the screen wide. The scan page shows through.
   ⛔ Fixed colours, not theme tokens: this is read at arm's length outdoors, in both themes alike. */
.gatemark{position:fixed;inset:0;z-index:2000;display:flex;flex-direction:column;align-items:center;
  justify-content:center;gap:clamp(.6rem,2.5vmin,1.4rem);padding:max(16px,4vmin);
  background:rgba(24,18,18,.30);-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);
  text-align:center;cursor:pointer;touch-action:none;animation:gm-in .14s ease-out}
.gatemark .gm-img{display:block;width:min(72vw,58vh,620px);height:auto;max-height:58vh;object-fit:contain;
  filter:drop-shadow(0 8px 22px rgba(0,0,0,.35))}
.gatemark .gm-card{position:relative;overflow:hidden;background:rgba(255,255,255,.95);color:#1d1515;
  border-radius:1rem;padding:.75rem 1.2rem .9rem;max-width:min(92vw,40rem);box-shadow:0 10px 30px rgba(0,0,0,.25)}
.gatemark .gm-title{font-size:clamp(1.45rem,6vw,2.3rem);font-weight:800;line-height:1.1}
.gatemark .gm-text{font-size:clamp(1rem,4.2vw,1.35rem);line-height:1.3;margin-top:.3rem}
.gatemark .gm-tap{font-size:.8rem;opacity:.6;margin-top:.45rem}
.gatemark .gm-bar{position:absolute;left:0;bottom:0;height:4px;width:100%;transform-origin:left;
  background:#5E0009;animation:gm-bar var(--gm-ms,2500ms) linear forwards}
.gatemark.gm-check .gm-title{color:#1f7a2e}
.gatemark.gm-x .gm-title{color:#b3121b}
.gatemark.gm-caution .gm-title{color:#8a5a00}
@media (orientation:landscape) and (max-height:520px){
  .gatemark{flex-direction:row}
  .gatemark .gm-img{width:min(44vw,78vh);max-height:78vh}
  .gatemark .gm-card{max-width:48vw;text-align:left}
}
/* 264 · the "At the gate" rule panel */
.grule{border:1px solid rgba(0,0,0,.1);border-radius:.8rem;padding:1rem;margin:.8rem 0}
.grule.is-off{opacity:.6}
.grule-when{margin:.9rem 0 .6rem;font-size:1.02rem}
.grule-row{display:flex;gap:1rem;align-items:flex-start;padding:.7rem 0;border-top:1px solid rgba(0,0,0,.07)}
.grule-thumb{flex:0 0 auto;width:6.5rem;height:6.5rem;border:1px dashed rgba(0,0,0,.25);border-radius:.6rem;
  background:repeating-conic-gradient(#f1eded 0 25%,#fff 0 50%) 50%/16px 16px;display:flex;flex-direction:column;
  align-items:center;justify-content:center;gap:.2rem;cursor:pointer;padding:.35rem;font:inherit}
.grule-thumb img{max-width:100%;max-height:4.6rem;object-fit:contain}
.grule-thumb span{font-size:.75rem;color:#5E0009;font-weight:600}
.grule-body{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;gap:.45rem}
.grule-body input[type=text]{width:100%}
.grule-lbl{font-weight:700}
.grule-stats,.grule-foot,.grule-in{font-size:.9rem;margin:.6rem 0 0}
.grule-cands{margin-top:.7rem}
/* ⭐ a picker and its button on ONE baseline — the label's own bottom margin had lifted the button (Jeremy, pixels) */
.grule-add{display:flex;flex-wrap:wrap;align-items:flex-end;gap:.75rem;margin:.4rem 0 .2rem}
.gsettings .grule-add label{flex:1 1 18rem;margin:0}
.grule-add select,.grule-add .btn{height:2.75rem;box-sizing:border-box}
.grule-add .btn{flex:0 0 auto;display:inline-flex;align-items:center}
.gsettings .grule-add select:disabled{opacity:1;color:var(--ink);background:#f7f4f4;cursor:default}
.grule-in-marks{display:flex;align-items:center;gap:.75rem;margin-top:.6rem;font-size:.9rem}
.grule-mini{flex:0 0 auto;width:3.6rem;height:3.6rem;border:1px solid rgba(0,0,0,.1);border-radius:.5rem;display:flex;
  align-items:center;justify-content:center;background:repeating-conic-gradient(#f1eded 0 25%,#fff 0 50%) 50%/12px 12px}
.grule-mini img{max-width:88%;max-height:88%;object-fit:contain}
/* ⭐ "Issued together" used a bare native select — 19px tall, no border (Jeremy, pixels) */
#sec-dual .js-dual-pick{height:2.6rem;min-width:16rem;max-width:100%;padding:0 .7rem;font:inherit;
  border:1px solid #dbd5d6;border-radius:8px;background:#fff;color:var(--ink)}
.grule-dlg{max-width:min(40rem,94vw);padding:1.2rem 1.4rem;border:0;border-radius:1rem;box-shadow:0 18px 50px rgba(0,0,0,.3)}
.grule-dlg h3{margin:0 0 .3rem}
.grule-dlg .dlgfoot{margin-top:.8rem;display:flex;justify-content:flex-end}
.grule-opts{list-style:none;padding:0;margin:.6rem 0;display:grid;grid-template-columns:repeat(auto-fill,minmax(8rem,1fr));gap:.6rem}
.grule-opt{width:100%;border:1px solid rgba(0,0,0,.15);border-radius:.6rem;padding:.5rem;cursor:pointer;font:inherit;
  background:repeating-conic-gradient(#f1eded 0 25%,#fff 0 50%) 50%/16px 16px;display:flex;flex-direction:column;align-items:center;gap:.3rem}
.grule-opt img{height:4.5rem;max-width:100%;object-fit:contain}
.grule-opt span{font-size:.8rem;background:#fff;padding:0 .3rem;border-radius:.3rem}
.grule-up{display:block;margin:.4rem 0}
@media (max-width:520px){.grule-row{flex-direction:column}.grule-thumb{width:100%;height:7rem}}
/* ⛔ NO OPACITY IN THE ENTRANCE — measured 2026-09-13: screenshots caught the mark half-faded; a phone that
   throttles animation would show a see-through check in sunlight. It pops at full strength; only its size eases. */
@keyframes gm-in{from{transform:scale(.94)}to{transform:none}}
@keyframes gm-bar{from{transform:scaleX(1)}to{transform:scaleX(0)}}
@media (prefers-reduced-motion:reduce){.gatemark{animation:none}.gatemark .gm-bar{animation:none;display:none}}
.ticket-card{text-align:center}
.ticket-qr{margin:1rem auto;max-width:260px}
.ticket-qr svg{width:100%;height:auto;display:block}
@media print{ .ticket-card .muted:last-of-type,.nav,.who,.foot{display:none} }
.ticket-one{margin:1.25rem 0;padding-bottom:1rem;border-bottom:1px solid rgba(0,0,0,.08)}
.ticket-one:last-of-type{border-bottom:0}
.ticket-seat{font-weight:600;font-size:.9rem;color:#5E0009;letter-spacing:.03em}
@media print{ .ticket-one{page-break-inside:avoid} }

/* ── The ticket ────────────────────────────────────────────────────────────
   A guest prints this or shows it on a phone, so it has to survive both.
   ⛔ EVERY SPONSOR MARK HERE IS DARK INK ON TRANSPARENT — measured, not assumed
      (lockup rgb(120,45,52), Bud Light rgb(4,49,106), Endzone rgb(11,19,73)).
      They MUST sit on a light background. The first cut put Bud Light on a navy
      footer and it came out invisible — a logo that renders is not a logo that
      reads, and this is the second time that lesson has been paid for here.
   ⛔ Maroon #5E0009 is a LICENCE CONDITION, so it leads as the matchup band and
      the seat pill rather than as a backdrop that swallows the artwork. */
.tk-wrap{max-width:23rem;margin:0 auto}
.tk{background:#fff;border-radius:.85rem;overflow:hidden;margin:0 0 1.5rem;
  box-shadow:0 6px 20px rgba(0,0,0,.14);border:1px solid rgba(0,0,0,.08)}
.tk-top{background:#fffdfa;padding:.8rem 1rem;display:flex;align-items:center;
  justify-content:space-between;gap:.6rem;border-bottom:1px solid rgba(0,0,0,.06)}
.tk-lockup{height:52px;width:auto;display:block}
.tk-seat{color:#fff;background:#5E0009;font-size:.74rem;font-weight:700;letter-spacing:.06em;
  border-radius:1rem;padding:.22rem .7rem;white-space:nowrap}
.tk-band{background:#5E0009;color:#fff;text-align:center;padding:.5rem 1rem;
  font-size:1.05rem;font-weight:700;letter-spacing:.02em}
.tk-body{padding:1rem 1rem .9rem;text-align:center}
.tk-name{font-size:1.35rem;font-weight:700;line-height:1.2}
.tk-when{font-size:.88rem;opacity:.78;margin-top:.25rem}
.tk-where{font-size:.82rem;opacity:.6}
/* Directions on the pass itself — this is the screen a guest has open in the car.
   Item 128. */
.tk-map{margin-top:.35rem;font-size:.82rem}
.tk-map a{color:inherit;opacity:.75;text-decoration:underline}
.tk-qr{margin:.9rem auto .35rem;max-width:230px}
.tk-qr svg{width:100%;height:auto;display:block}
/* ⛔ "Admits one" was 11.8px at opacity .55 — 3.10:1 on white, failing AA. It is also the
   line that stops a door waving a whole party through on ONE code, which makes it the worst
   possible place to save a few pixels of contrast. */
.tk-admits{font-size:.8rem;letter-spacing:.1em;text-transform:uppercase;opacity:.8}
.tk-in{margin-top:.5rem;font-size:.8rem;font-weight:600;color:#1f6b3a}
.tk-foot{background:#f4f1ec;padding:.7rem 1rem .6rem;display:flex;align-items:center;
  justify-content:center;gap:.7rem;flex-wrap:wrap;border-top:1px solid rgba(0,0,0,.06)}
.tk-bud{height:26px;width:auto}
.tk-endzone{height:13px;width:auto}
.tk-presents{color:rgba(0,0,0,.55);font-size:.64rem;letter-spacing:.04em;
  flex-basis:100%;text-align:center;margin-top:.15rem}
.tk-note{font-size:.8rem;text-align:center}
@media print{
  .nav,.who,.foot,.noprint,.terra-launch,.terra-panel{display:none !important}
  .tk{page-break-inside:avoid;box-shadow:none;border:1px solid #ccc}
  .tk-wrap{max-width:none}
}
.tk-holder{margin-top:.55rem;font-size:.85rem;background:rgba(94,0,9,.06);border-radius:.4rem;
  padding:.35rem .55rem}
.tk-holder span{display:block;font-size:.75rem;opacity:.65}
.tk-hist{margin-top:.5rem;font-size:.76rem;opacity:.7;text-align:left}
.tk-hist span{opacity:.7}
.tk-transfer{margin-top:.85rem;width:100%;background:#fff;color:#5E0009;
  border:2px solid #5E0009;font-weight:600;padding:.6rem 1rem;border-radius:.5rem;
  cursor:pointer;font-size:.95rem}
.tk-transfer:hover{background:#5E0009;color:#fff}
.tk-transfer:focus-visible{outline:3px solid var(--focus);outline-offset:2px}
#tfDlg{border:0;border-radius:.7rem;padding:1.2rem;max-width:22rem;width:92vw;
  box-shadow:0 14px 40px rgba(0,0,0,.3)}
#tfDlg::backdrop{background:rgba(0,0,0,.35)}
#tfDlg label{font-size:.82rem;font-weight:600;display:block;margin-bottom:.25rem}
.tf-err{color:#8a1f1f;font-size:.84rem;margin:.5rem 0 0;min-height:1rem}
.bn-panel{max-width:23rem;margin:0 auto 1.5rem}
.bn-qr{width:110px}
.bn-qr svg{width:100%;height:auto;display:block}
#bnDlg{border:0;border-radius:.7rem;padding:1.2rem;max-width:22rem;width:92vw;
  box-shadow:0 14px 40px rgba(0,0,0,.3)}
#bnDlg::backdrop{background:rgba(0,0,0,.35)}
#bnDlg label{font-size:.82rem;font-weight:600;display:block;margin-bottom:.25rem}
@media print{ .bn-panel .actions,#bnDlg{display:none} }
.lib-ext{font-size:.7rem;opacity:.6;font-weight:400;letter-spacing:.03em}
#libLinkDlg{border:0;border-radius:.7rem;padding:1.2rem;max-width:24rem;width:92vw;
  box-shadow:0 14px 40px rgba(0,0,0,.3)}
#libLinkDlg::backdrop{background:rgba(0,0,0,.35)}
#libLinkDlg label{font-size:.82rem;font-weight:600;display:block;margin-bottom:.25rem}

/* ── Messages ──────────────────────────────────────────────────────────────
   Same visual language as Terra's window (Jeremy: "the chat will look the same
   basically") — but people talking to people. */
.ch-wrap{display:grid;grid-template-columns:16rem 1fr;gap:1rem;align-items:start}
.ch-list{background:#fff;border-radius:.7rem;overflow:hidden;max-height:32rem;overflow-y:auto;
  border:1px solid rgba(0,0,0,.07)}
.ch-item{display:block;width:100%;text-align:left;background:none;border:0;cursor:pointer;
  padding:.65rem .8rem;border-bottom:1px solid rgba(0,0,0,.06);font:inherit}
.ch-item:hover{background:rgba(94,0,9,.04)}
.ch-item.is-on{background:rgba(94,0,9,.09)}
.ch-item-name{font-weight:600;font-size:.92rem;display:flex;justify-content:space-between;gap:.4rem}
.ch-item-prev{font-size:.78rem;opacity:.6;margin-top:.1rem;overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap}
.ch-badge{background:#5E0009;color:#fff;border-radius:1rem;font-size:.68rem;padding:.05rem .42rem;
  font-weight:700;flex:0 0 auto}
.ch-main{background:#fff;border-radius:.7rem;overflow:hidden;display:flex;flex-direction:column;
  height:32rem;border:1px solid rgba(0,0,0,.07)}
.ch-empty{margin:auto;opacity:.55;font-size:.92rem}
.ch-head{display:flex;justify-content:space-between;align-items:center;gap:.6rem;
  padding:.7rem .9rem;background:#5E0009;color:#fff;flex:0 0 auto}
.ch-head .ch-sub{display:block;font-size:.72rem;opacity:.8}
.ch-head .lnk{color:#fff;opacity:.85}
.ch-log{flex:1 1 auto;overflow-y:auto;padding:.8rem;background:#faf9f9}
.ch-msg{position:relative;padding:.5rem .7rem;border-radius:.75rem;margin-bottom:.5rem;
  line-height:1.45;font-size:.92rem;max-width:76%;white-space:pre-wrap;word-break:break-word}
.ch-msg .who{display:block;font-size:.66rem;letter-spacing:.06em;text-transform:uppercase;
  opacity:.6;margin-bottom:.15rem}
.ch-msg.mine{background:rgba(94,0,9,.09);margin-left:auto;border-bottom-right-radius:.2rem}
.ch-msg.theirs{background:#fff;border:1px solid rgba(0,0,0,.07);border-bottom-left-radius:.2rem}
.ch-msg.gone{opacity:.45;font-style:italic}
.ch-del{position:absolute;top:.2rem;right:.3rem;background:none;border:0;cursor:pointer;
  font-size:1rem;line-height:1;opacity:0;color:#8a1f1f}
.ch-msg:hover .ch-del{opacity:.7}
.ch-send{display:flex;gap:.4rem;padding:.6rem;border-top:1px solid rgba(0,0,0,.08);flex:0 0 auto}
.ch-send input{flex:1;min-width:0;padding:.5rem .7rem;border:1px solid rgba(0,0,0,.18);
  border-radius:1.2rem;font:inherit;font-size:.92rem}
#chDirectDlg,#chGroupDlg{border:0;border-radius:.7rem;padding:1.2rem;max-width:24rem;width:92vw;
  box-shadow:0 14px 40px rgba(0,0,0,.3)}
#chDirectDlg::backdrop,#chGroupDlg::backdrop{background:rgba(0,0,0,.35)}
#chDirectDlg label,#chGroupDlg label{font-size:.82rem;font-weight:600;display:block;margin-bottom:.25rem}
@media (max-width:44rem){
  .ch-wrap{grid-template-columns:1fr}
  .ch-main{height:26rem}
  /* ⚠️ .ch-list kept max-height:32rem through this query — only .ch-main was shrunk — so on a
     phone a 512px thread list stacked ABOVE the messages and pushed the conversation off
     screen. The list is a chooser; the conversation is the point. */
  .ch-list{max-height:14rem}
}
/* Library thumbnails. ⛔ A checkerboard behind them on purpose — most of these marks have
   transparent backgrounds, and on plain white a white logo is indistinguishable from a
   missing one. That exact confusion has already cost time on this project twice. */
.lib-thumb{width:58px;height:58px;padding:3px;border:1px solid rgba(0,0,0,.12);border-radius:.4rem;
  cursor:pointer;display:block;
  background-color:#fff;
  background-image:linear-gradient(45deg,#eee 25%,transparent 25%,transparent 75%,#eee 75%),
                   linear-gradient(45deg,#eee 25%,transparent 25%,transparent 75%,#eee 75%);
  background-size:12px 12px;background-position:0 0,6px 6px}
.lib-thumb:hover{border-color:#5E0009}
.lib-thumb img{width:100%;height:100%;object-fit:contain;display:block}
.lib-noimg{display:flex;align-items:center;justify-content:center;width:58px;height:58px;
  border:1px dashed rgba(0,0,0,.18);border-radius:.4rem;font-size:.7rem;font-weight:700;
  opacity:.5;letter-spacing:.03em}
.lib-prev{border:0;border-radius:.7rem;padding:0;max-width:min(90vw,44rem);
  box-shadow:0 18px 50px rgba(0,0,0,.35)}
.lib-prev::backdrop{background:rgba(0,0,0,.55)}
.lib-prev-head{display:flex;justify-content:space-between;align-items:center;gap:1rem;
  padding:.7rem 1rem;background:#5E0009;color:#fff}
.lib-prev-x{background:none;border:0;color:#fff;font-size:1.6rem;line-height:1;cursor:pointer;
  padding:0 .2rem}
.lib-prev-body{padding:1.2rem;min-height:14rem;display:flex;align-items:center;justify-content:center;
  background-color:#fff;
  background-image:linear-gradient(45deg,#eee 25%,transparent 25%,transparent 75%,#eee 75%),
                   linear-gradient(45deg,#eee 25%,transparent 25%,transparent 75%,#eee 75%);
  background-size:20px 20px;background-position:0 0,10px 10px}
.lib-prev-body img{max-width:100%;max-height:64vh;display:block}

/* ── Section jump bar ──────────────────────────────────────────────────────
   Sticks under the header so it is reachable from anywhere down a long page.
   ⛔ scroll-margin-top on the targets is not optional: without it an anchor
      jump parks the heading UNDERNEATH the sticky bar and the section looks
      like it starts halfway down. */
.secnav{position:sticky;top:0;z-index:20;display:flex;gap:.35rem;flex-wrap:wrap;
  padding:.55rem 0 .6rem;margin:0 0 .9rem;background:var(--bg,#f7f5f5);
  border-bottom:1px solid rgba(0,0,0,.08)}
.secnav a{display:inline-flex;align-items:center;gap:.35rem;text-decoration:none;
  font-size:.85rem;font-weight:600;color:#5E0009;background:#fff;
  border:1px solid rgba(94,0,9,.22);border-radius:1.2rem;padding:.28rem .75rem;
  white-space:nowrap}
.secnav a:hover{background:rgba(94,0,9,.07)}
.secnav a.is-here{background:#5E0009;color:#fff;border-color:#5E0009}
.secnav-n{font-size:.72rem;opacity:.65;font-weight:700}
.secnav a.is-here .secnav-n{opacity:.85}
[id].panel,[id].sport-block,section[id]{scroll-margin-top:4.2rem}
@media print{ .secnav{display:none} }

/* ── Charts ────────────────────────────────────────────────────────────────
   ⛔ Text wears TEXT tokens, never the series colour — a maroon bar carries the
      identity, the numbers beside it stay in ordinary ink. Grid and axis stay
      recessive so they never out-weigh the data they support. */
/* ⛔ minmax(26rem,1fr) SETS A HARD FLOOR OF 416px — measured on the live page, it was one of
   two things forcing horizontal scroll at 390px. min() lets the track shrink below its
   preferred size on a phone instead of pushing the page sideways. */
.bcards{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(26rem,100%),1fr));
  gap:1rem;margin:0 0 1rem}
.bcard-wide{margin:0 0 1.2rem}
.bcard{background:#fff;border:1px solid rgba(0,0,0,.07);border-radius:.7rem;padding:1rem 1.1rem}
.bcard h2{margin:0;font-size:1rem}
.bcard-sub{margin:.15rem 0 .7rem;font-size:.82rem;opacity:.62}
.bcard-note{margin:.6rem 0 0;font-size:.74rem;opacity:.6;line-height:1.4}
.bchart{display:block;overflow:visible}
.bchart .bc-label{font:600 12px/1 -apple-system,Segoe UI,Roboto,Arial,sans-serif;fill:#1d1d1f}
.bchart .bc-sub{font:400 11px/1 -apple-system,Segoe UI,Roboto,Arial,sans-serif;fill:#8f8a8a}
.bchart .bc-val{font:600 12px/1 -apple-system,Segoe UI,Roboto,Arial,sans-serif;fill:#3a3a3c}
.bchart .bc-over{font:600 11px/1 -apple-system,Segoe UI,Roboto,Arial,sans-serif;fill:#5E0009}
.bchart .bc-peak{font:700 11px/1 -apple-system,Segoe UI,Roboto,Arial,sans-serif;fill:#5E0009}
.bchart .bc-axis{stroke:rgba(0,0,0,.14);stroke-width:1}
@media print{ .bcards{grid-template-columns:1fr 1fr} .bcard{break-inside:avoid} }

/* ── Small screens ─────────────────────────────────────────────────────────
   ⛔⛔ THE MEASUREMENT THAT DROVE THIS: at a simulated 390px the Engagement page
      overflowed by 217px and the offender was table.roster at 562px. A wide table
      does not shrink — it pushes the whole PAGE sideways, and then every other
      element on it is off-screen too. This app is used on a phone at a stadium.
   ⭐ The table scrolls INSIDE ITS OWN BOX rather than the page scrolling. */
/* ⚠️ SCOPED TO 40rem, NOT 48rem. At 641-768px the roster already fits; display:block drops
   width:100% so the table shrink-wrapped and gained a scrollbar on a tablet with room to
   spare. A fix that fires where there was no problem creates one. */
@media (max-width: 40rem) {
  .roster{display:block;overflow-x:auto;-webkit-overflow-scrolling:touch;
    /* a hint that there is more to the right, rather than a silent cut */
    background:linear-gradient(90deg,#fff 30%,rgba(255,255,255,0)),
               linear-gradient(90deg,rgba(255,255,255,0),#fff 70%) 100% 0,
               radial-gradient(farthest-side at 0 50%,rgba(0,0,0,.14),transparent),
               radial-gradient(farthest-side at 100% 50%,rgba(0,0,0,.14),transparent) 100% 0;
    background-repeat:no-repeat;background-size:36px 100%,36px 100%,14px 100%,14px 100%;
    background-attachment:local,local,scroll,scroll}
  .roster th,.roster td{white-space:nowrap}
  /* ⛔⛔ REGRESSION I INTRODUCED WITH THE LINE ABOVE, caught in review the same hour.
     nowrap INHERITS into child elements, and .gsub carries free text — a library
     description is maxlength 500, so one long one became a single unbreakable ~3000px
     line and the Branding table scrolled sideways almost without end. The nowrap is
     meant to stop CELLS wrapping into a tower; it was never meant to reach prose. */
  .roster .gsub{white-space:normal}

  /* ⛔ ~28px tap targets on an app used ONE-HANDED AT A STADIUM. .lnk is every action in
     every table — Edit, Remove access, Ticket, Make private, Preview, Download. 44px is
     the iOS/WCAG 2.5.5 minimum and this was well under it. */
  .lnk{min-height:44px;padding:.55rem .5rem;display:inline-flex;align-items:center}
  /* In-table dropdowns render at the UA default ~13.3px beside 15px cells; font:inherit
     elsewhere is scoped to dialogs only. */
  .roster select{font:inherit;font-size:.85rem}

  /* The header nav is seven items; let it scroll sideways rather than wrap into
     three ragged lines that push the content down the screen. */
  /* ⛔⛔ THIS RULE WAS INERT UNTIL flex-wrap WAS ADDED. .nav is display:flex with
     flex-wrap:wrap set at the top of this file, and white-space cannot override that — the
     seven links kept wrapping to three rows and the topbar took 281px, 42% of a 390px
     screen. ⚠️ AND NO OVERFLOW MEASUREMENT CAN CATCH IT: a nav that WRAPS overflows by
     zero, so the page measured clean while the bug was fully present. Two independent
     reviewers found this; the measurement found nothing. */
  /* ⛔ SCROLLING THE NAV WAS THE WRONG FIX. It truncated the list mid-word with no hint that
     more existed — it read as a broken header rather than a scrollable one. Seven items need
     a menu, not a treadmill. */
  .navtoggle{display:flex;flex-direction:column;justify-content:center;gap:5px;
    width:44px;height:44px;padding:0 10px;background:none;border:0;cursor:pointer;
    margin-left:auto;order:3}
  .navtoggle span{display:block;height:2px;background:#f3e6e7;border-radius:2px}
  .navtoggle[aria-expanded="true"] span:nth-child(1){transform:translateY(7px) rotate(45deg)}
  .navtoggle[aria-expanded="true"] span:nth-child(2){opacity:0}
  .navtoggle[aria-expanded="true"] span:nth-child(3){transform:translateY(-7px) rotate(-45deg)}
  .navtoggle span{transition:transform .16s ease,opacity .16s ease}

  .nav{display:none;position:absolute;top:100%;left:0;right:0;z-index:1;
    flex-direction:column;gap:0;background:#4a0007;padding:.35rem 0;
    box-shadow:0 10px 26px rgba(0,0,0,.35)}
  .nav.is-open{display:flex}
  .nav a{display:flex;align-items:center;min-height:48px;padding:.2rem 1.1rem;
    border-bottom:1px solid rgba(255,255,255,.08)}
  .nav a:last-child{border-bottom:0}
  /* ⛔⛔ ITEM 194 — EVERY PAGE OVERFLOWED BY 48px AT 390px, AND THE GATE IS USED ON A PHONE.
     Measured 2026-09-09 while shooting the help guide: document.scrollWidth 438 against a 390
     viewport on /t.php, /scan.php, /, /schedule.php and /event.php — the whole site.
     ⭐ THE MECHANISM: .topbar is flex-wrap:nowrap here with gap 1.5rem and 1.5rem side padding,
        and NONE of its children could shrink. A flex item's default min-width is `auto`, which
        means "never smaller than my content" — so a long organisation name and "Dana Whitfield ·
        Admin · Sign out" simply pushed the hamburger 48px off-screen.
     ⚠️ AND THE COMMENT DIRECTLY ABOVE THIS ONE ALREADY WARNED WHY IT WAS MISSED: "a nav that
        WRAPS overflows by zero, so the page measured clean while the bug was fully present."
        This is the mirror of that. When flex-wrap:nowrap was introduced to fix the wrapping nav,
        the overflow became REAL and measurable — and nobody re-measured after the fix.
     ⛔ min-width:0 IS THE FIX, NOT A SMALLER FONT. Shrinking type to make things fit hides the
        constraint instead of resolving it, and the next longer name brings it straight back. */
  .topbar{position:relative;flex-wrap:nowrap;gap:.6rem;padding-left:1rem;padding-right:1rem}
  .topbar .brand{min-width:0;flex:0 1 auto}
  .brandname{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .who{margin-left:0;min-width:0;gap:.45rem;flex:0 1 auto}
  /* The person's name yields first — it is the one thing on this bar the reader already knows. */
  .who .name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}
  /* ⛔ Sign out and the hamburger NEVER shrink: one is the way out and the other is the way to
     everything else. A header that fits by hiding its own exits is not fixed. */
  .who .out{flex:none;white-space:nowrap}
  .navtoggle{flex:none}
  .secnav a{min-height:44px}
  .nav::-webkit-scrollbar{display:none}

  /* ⚠️ The fixed Terra launcher sits over the bottom-right of the page. On a phone that is
     exactly where the last row of a table ends up, so the page gets room to clear it. */
  .wrap{padding-bottom:5rem}

  /* Charts: a 980-wide viewBox on a 350px screen renders 11px labels at 4px. Let the wide
     season chart scroll inside its card at its real size instead of dissolving. */
  /* ⚠️ 620px against a 980 viewBox is still scale 0.63 — the season chart's date labels
     landed at 7px. At natural size they are legible and the card already scrolls. */
  .bcard-wide .bchart{min-width:980px}
  .bcard-wide{overflow-x:auto}
  /* The two half-width charts were scaling a 460 viewBox into a 313px box: 0.68, so 12px
     labels rendered at 8.2px. A partner reads these. Same treatment. */
  .bcards .bcard{overflow-x:auto}
  .bcards .bchart{min-width:520px}
  /* The sticky jump bar is ~127px tall once it wraps, but the anchor offset was 67px, so a
     jump left the heading behind the bar. */
  [id].panel,[id].sport-block,section[id]{scroll-margin-top:9rem}
  /* The footer sits AFTER .wrap, so its padding did not clear the fixed launcher. */
  .foot{padding-bottom:4.5rem}

  .panelhead{flex-wrap:wrap;gap:.5rem}
  h1{font-size:1.4rem}
}

/* ⚠️ The SVG height attribute capped the scale at 1, so on a tablet the half-width charts
   sat at natural size inside 115px of dead gutter each side. Let the aspect ratio drive it. */
.bchart{height:auto}
/* The gate's "next" control — the most-pressed thing on the page once a queue forms. */
.gate-next{display:block;width:100%;text-align:center;font-size:1.05rem;padding:.8rem 1rem;
  text-decoration:none}
/* Call / Text actions. ⚠️ Kept off the printed will-call sheet — a link is not a link on paper. */
.contact-acts{display:inline-flex;gap:.15rem;margin-left:.15rem;vertical-align:middle}
.contact-acts .lnk{font-size:.8rem;padding:.15rem .35rem}
/* ⭐ ITEM 130 — the icon buttons carry their own size; do not let the old text padding
   squash them back into a 16px target. */
.contact-acts .ico-btn{padding:0}
.numline{display:flex;align-items:center;gap:.15rem;flex-wrap:wrap}
@media (max-width:40rem){ .contact-acts .lnk{min-height:44px;padding:.5rem .45rem}
  .contact-acts .ico-btn{min-height:44px;min-width:44px} }
@media print{ .contact-acts{display:none} }

/* ── SMS consent, on the add/edit dialogs ─────────────────────────────────
   ⛔ The disclosure is LEGIBLE, not fine print. A staff member reads it aloud to a guest,
      so 11px grey would defeat the whole purpose — and a carrier reviewer sees this exact
      block as the opt-in evidence in our registration. */
.consent{margin:.9rem 0 .2rem;padding:.75rem .85rem;border:1px solid rgba(94,0,9,.25);
         border-left:3px solid #5E0009;border-radius:.45rem;background:rgba(94,0,9,.035)}
.consent-tick{display:flex;gap:.55rem;align-items:flex-start;font-weight:600;cursor:pointer;
              font-size:.92rem;line-height:1.35}
.consent-tick input{margin:.15rem 0 0;flex:0 0 auto;width:1rem;height:1rem;cursor:pointer}
.consent-text{margin:.55rem 0 0;font-size:.8rem;line-height:1.5;color:#423b3b}
.consent-note{margin:.4rem 0 0;font-size:.78rem;color:#6e6e73;font-style:italic}
@media (max-width:40rem){ .consent-text{font-size:.78rem} }

/* Engagement org filter */
.orgfilter{display:flex;align-items:center;gap:.6rem;margin:0 0 1rem;font-size:.9rem}
.orgfilter label{color:#6e6e73}
.orgfilter select{padding:.4rem .5rem;border:1px solid rgba(0,0,0,.2);border-radius:.4rem;font:inherit}

/* ── VIP wordmark on the pass ─────────────────────────────────────────────
   ⛔ On the WHITE band between the lockup and the maroon matchup bar. The mark is dark
      maroon ink, so it needs a light ground — the same rule that made Bud Light invisible
      on navy. Never move it onto the maroon band. */
/* ⭐ VIP is CENTRED IN the lockup row, not stacked under it. Absolute-centred so the seat
   pill on the right cannot shove it off-centre — a flex `justify-content` would centre it
   between its siblings, which is a different (and visibly wrong) position. */
.tk-top{position:relative}
.tk-top .tk-vip{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);
                width:26%;max-width:112px;height:auto}
@media print{ .tk-top .tk-vip{width:24%} }

/* ── Organisations screen ─────────────────────────────────────────────────
   ⛔ The bulk row is the PRIMARY control: an allotment is almost always "N per game", and the
      per-game grid (30 games x every org) is the exception, so it lives behind a disclosure. */
.subhead{font-size:.82rem;letter-spacing:.06em;text-transform:uppercase;color:#6e6e73;
         margin:1.3rem 0 .5rem;font-weight:700}
.sportrow{display:flex;align-items:flex-end;gap:.9rem;flex-wrap:wrap;padding:.6rem 0;
          border-bottom:1px solid rgba(0,0,0,.07)}
.sportrow .sportname{flex:1 1 12rem;font-weight:600}
.sportrow .sportname .muted{display:block;font-weight:400;font-size:.8rem}
.sportrow label{display:flex;flex-direction:column;gap:.2rem;font-size:.75rem;color:#6e6e73}
.sportrow input{padding:.35rem .45rem;border:1px solid rgba(0,0,0,.2);border-radius:.35rem;font:inherit}
.pergame{margin:.4rem 0 1rem}
.pergame summary{cursor:pointer;font-size:.85rem;color:#5E0009;padding:.35rem 0}
.pergame table{margin-top:.4rem}
@media (max-width:40rem){ .sportrow{gap:.6rem} .sportrow .sportname{flex-basis:100%} }

/* ── Organisation finder ──────────────────────────────────────────────────
   ⭐ A search over the real rows, not a <select>: a native dropdown would hide the People /
      Passes / Used counts, and those counts are the reason for the page. */
.orgfind{display:flex;align-items:center;gap:.7rem;margin:0 0 .8rem}
.orgfind input{flex:1 1 22rem;max-width:28rem;padding:.55rem .7rem;font:inherit;
               border:1px solid rgba(0,0,0,.2);border-radius:.45rem}
.orgfind input:focus{outline:2px solid #5E0009;outline-offset:1px;border-color:#5E0009}
.orgfind .muted{font-size:.85rem}
#orgList tr.is-hit{background:rgba(94,0,9,.05)}
#orgList tr.is-hit .gname{text-decoration:underline}

/* ── Organisation logos ───────────────────────────────────────────────────
   ⛔⛔ EVERY LOGO WE HOLD IS DARK INK ON TRANSPARENCY — Learfield's is literally black and its
      file is named "OnWhite". They must sit on a LIGHT ground. Never place one on the maroon
      bar: there is no contrast there, and this is the third time this trap has been hit
      (Bud Light on navy, the Terra eye, now these). */
.orgname{display:inline-flex;align-items:center;gap:.6rem}
.orglogo{width:34px;height:34px;object-fit:contain;flex:0 0 34px;
         background:#fff;border-radius:.3rem;padding:2px}
/* A fixed placeholder keeps every name in the column starting at the same x. */
.orglogo.placeholder{background:rgba(0,0,0,.04);border:1px dashed rgba(0,0,0,.12)}
.orgtitle{display:flex;align-items:center;gap:.7rem}
.orglogo.lg{width:44px;height:44px;flex-basis:44px}
.orglogo.preview{width:auto;height:64px;max-width:220px;padding:.4rem}

/* Setup — event seat allocation, one small box per organisation */
.allocrow{display:flex;flex-wrap:wrap;gap:.5rem;margin-top:.35rem}
.allocrow label{display:flex;flex-direction:column;gap:.15rem;font-size:.7rem;color:#6e6e73}
.allocrow input{padding:.3rem .35rem;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;font:inherit}

/* ── THE GUEST PAGE — the only surface a client ever sees ─────────────────
   ⛔ Its own scope (body.guest): none of the staff chrome, no nav, no watermark. A guest must
      never see a control they cannot use or a number that is not theirs. */
body.guest{background:#f6f5f5;margin:0}
.gwrap{max-width:34rem;margin:0 auto;padding:1.2rem 1rem 2.5rem}
.ghead{text-align:center;padding:.6rem 0 1.1rem}
.ghero{text-align:center;padding:0 .4rem 1.2rem}
.geyebrow{margin:0 0 .4rem;font-size:.74rem;letter-spacing:.11em;text-transform:uppercase;color:#8c8586}
.ghero h1{margin:0 0 .5rem;font-size:1.65rem;line-height:1.2;color:#181515;letter-spacing:-.01em}
.gwhen{margin:0;font-size:1rem;color:#423b3b}
.gvenue{margin:.2rem 0 0;font-size:.9rem;color:#6e6e73}
/* ⚠️ The NOTE is not decoration: for the arena it explains that we are sending them to a
   car park rather than the building, because the building's own street is closed. A link
   whose destination looks wrong is ignored unless it says why. Item 128. */
.gmap{margin:.45rem 0 0;font-size:.9rem}
.gmapnote{display:block;margin-top:.2rem;font-size:.8rem;color:#8a8a8f;line-height:1.45}
/* ⭐ The SOURCE is named on the page. A bare temperature on a guest's pass invites the
   question 'says who' — and past seven days there is no forecast at all, so the block is
   simply absent rather than showing a guess. Item 129. */
.gwx{margin:.5rem 0 0;font-size:.95rem;color:#423b3b}
.gwxsrc{display:block;margin-top:.15rem;font-size:.75rem;color:#8a8a8f}
.tk-wx{margin-top:.25rem;font-size:.8rem;opacity:.7}
.gname{margin:.7rem 0 0;font-size:.92rem;color:#423b3b}
.guest .panel{background:#fff;border-radius:.7rem;padding:1.1rem 1.2rem;margin:0 0 1rem;
              box-shadow:0 1px 3px rgba(19,16,16,.06)}
.guest .panel h2{margin:0 0 .5rem;font-size:1rem;color:#181515}
.gnote{border-left:4px solid #5E0009}
.gdetails{font-size:.93rem;line-height:1.65;color:#423b3b}
.gsmall{text-align:center;font-size:.85rem;margin:.2rem 0 1.2rem}
.guest .tk{margin:0 auto 1.1rem;max-width:22rem}
.gfoot{text-align:center;font-size:.78rem;color:#8c8586;padding-top:1rem}
@media (max-width:26rem){ .ghero h1{font-size:1.4rem} }

/* ── THE GATE SCANNER ─────────────────────────────────────────────────────
   ⛔ Designed for one hand, outdoors, at night, with a queue waiting: big tally, big video,
      big log rows, and colour that survives glare. */
.scanwrap{max-width:34rem;margin:0 auto;padding-bottom:env(safe-area-inset-bottom)}
.scantally{display:flex;gap:.6rem;margin:.6rem 0 1rem}
.scantally div{flex:1;background:#fff;border-radius:.6rem;padding:.7rem .5rem;text-align:center;
               box-shadow:0 1px 3px rgba(19,16,16,.06)}
.scantally strong{display:block;font-size:1.8rem;line-height:1;color:#181515}
.scantally span{font-size:.72rem;letter-spacing:.05em;text-transform:uppercase;color:#8c8586}
/* ⛔⛔ 242 — `max-width:100%` IS LOAD-BEARING, NOT TIDINESS. `aspect-ratio` resolves the OTHER
   axis from whichever one is constrained, so a min-height SETS THE WIDTH: in gate mode
   `min-height:22rem` (352px) × 4/3 made this box **469px wide on a 440px phone**, 29px past the
   screen, scrolling the whole gate page sideways. Measured at 440px (iPhone 17 Pro Max) on
   2026-09-12 — Jeremy: "in gate mode on a phone the camera square is wider than the screen".
   ⭐ The cap is here rather than only in the kiosk rule so that NO future min-height can push
     this box off-screen again — the same defect class as D92's <select>, which also sized itself
     from its content and overflowed a phone on this exact screen. */
.scanstage{position:relative;background:#181515;border-radius:.7rem;overflow:hidden;
           aspect-ratio:4/3;display:none;max-width:100%}
.scanstage.is-on{display:block}
.scanwrap .btn{min-height:44px}
.scanstage video{width:100%;height:100%;object-fit:cover;display:block}
/* a target, so the person knows where to point rather than guessing */
.scanframe{position:absolute;inset:18% 22%;border:3px solid rgba(255,255,255,.85);
           border-radius:.5rem;box-shadow:0 0 0 100vmax rgba(0,0,0,.35)}
.scanhint{text-align:center;color:#6e6e73;font-size:.9rem;margin:.7rem 0}
.gatefind{margin:1rem 0 .2rem}
/* 268 · Find by name — every control ≥44px, like the rest of the gate (192c) */
.gatefind-pill{display:inline-flex;align-items:center;gap:.45rem;min-height:44px;padding:.55rem 1.1rem;border-radius:999px;
  border:1.5px solid #5E0009;background:#fff;color:#5E0009;font:inherit;font-weight:700;cursor:pointer}
.gatefind-pill[aria-expanded="true"]{background:#5E0009;color:#fff}
.gatefind-panel{margin-top:.6rem}
.gatefind-panel input{width:100%;min-height:48px;padding:.6rem .8rem;font:inherit;font-size:1.05rem;border:1px solid rgba(0,0,0,.25);
  border-radius:.55rem;box-sizing:border-box}
.gatefind-hint{font-size:.8rem;color:#6e6e73;margin:.35rem 0 .5rem}
.gatefind-list{list-style:none;margin:0;padding:0}
.gatefind-row{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:.5rem .8rem;background:#fff;
  border-radius:.55rem;padding:.65rem .8rem;margin:0 0 .45rem;box-shadow:0 1px 2px rgba(19,16,16,.06)}
.gatefind-who{display:flex;flex-direction:column;min-width:0;flex:1 1 12rem}
.gatefind-who strong{font-size:1.08rem;color:#181515}
.gatefind-who span{font-size:.82rem;color:#6e6e73}
.gatefind-who .gatefind-count{color:#181515;font-weight:600}
.gatefind-acts{display:flex;gap:.45rem;flex-wrap:wrap}
.gatefind-admit{min-height:44px;padding:.5rem 1rem}
.gatefind-full{font-weight:700;color:#2e7d32;min-height:44px;display:inline-flex;align-items:center}
.gatefind-empty{font-size:.9rem;color:#6e6e73;padding:.4rem 0}
.scanmanual{margin:1rem 0}
/* ⛔ 244 — 192c's rule reached the door picker and the undo button and MISSED these two.
   Gloved or cold hands at a dark gate: every control, unconditionally.
   ⛔⛔ REACH 44px WITH PADDING, NEVER `display:flex`. A <summary> is `display:list-item`, and
      ANY other display value silently removes the disclosure triangle — so the control stops
      looking like it opens. Caught by screenshotting the fix and comparing it with the shot
      taken before it: the numbers were right (44px) and the affordance was gone. */
.scanmanual summary{cursor:pointer;color:#5E0009;font-size:.9rem;padding:.75rem 0;min-height:44px}
.scanmanual input{width:100%;padding:.6rem;margin:.5rem 0;font:inherit;min-height:44px;
                  border:1px solid rgba(0,0,0,.2);border-radius:.45rem}
.scanlog{list-style:none;margin:.4rem 0 2rem;padding:0}
.scanrow{background:#fff;border-radius:.5rem;padding:.7rem .8rem;margin:0 0 .45rem;
         border-left:5px solid #ccc;box-shadow:0 1px 2px rgba(19,16,16,.05)}
.scanrow strong{display:block;font-size:1.05rem;color:#181515}
.scanrow span{font-size:.85rem;color:#6e6e73}
.scanrow.good{border-left-color:#2e7d32}
.scanrow.warn{border-left-color:#b26a00}
.scanrow.bad {border-left-color:#b3261e}


/* ═══ ITEM 130 · DENSITY ═══════════════════════════════════════════════════════════════════
   Jeremy, 2026-09-03: "things are crowded again in the UI … maybe the call text and edit words
   are icons in those screens, also instead of displaying it all in the grid some basic info is
   in the grid and you click the name to expand a people modal."
   Measured before: People row 9 controls / 91-114px tall; game roster row 8 controls with the
   action strip taking 405px of 1080 — 37.5% of the page. */

/* An icon control: a real hit target, not a 16px glyph you have to aim at. ⛔ 2.1rem is above
   the ~44px-at-default-zoom finger target these screens are used with at a gate. */
.ico-btn{display:inline-flex;align-items:center;justify-content:center;
  width:2.1rem;height:2.1rem;padding:0;border-radius:6px;vertical-align:middle}
.ico-btn .ico{display:block}
.ico-btn:hover{background:#f3f2f2}
/* ⚠️ The icons are the ONLY label these controls have, so the focus ring must stay obvious.
   Focus rings are purple here DELIBERATELY (the site is maroon) — do not "fix" the colour. */
.ico-btn:focus-visible{outline:2px solid var(--focus);outline-offset:2px}

/* The name is a button that opens the person dialog. It must still READ as the name — the
   affordance is the hover, not a permanent link colour shouting on every row. */
button.gname{background:none;border:0;padding:.1rem .25rem;margin-left:-.25rem;
  font:inherit;font-weight:600;color:var(--ink);cursor:pointer;border-radius:5px;text-align:left}
button.gname:hover{background:#f3f2f2;text-decoration:underline}
button.gname:focus-visible{outline:2px solid var(--focus);outline-offset:1px}

/* ⭐ ITEM 133 — an override is visible ON THE ROW. `+gate` is granted, `−gate` is taken away;
   the direction matters because bears_user_perms works in both directions. */
.pill.perm{margin-left:.3rem;font-variant-numeric:tabular-nums}
.pill.perm.on{background:#eef6f0;border-color:#bfe0cb;color:#1f7a3d}
.pill.perm.off{background:#fdf0f1;border-color:#f0c9cd;color:var(--danger)}
.urole{font-size:.87rem}
/* Give the columns that wrapped enough room to stop wrapping. */
.roster .umail{font-size:.87rem;word-break:break-word}
.roster .umob{white-space:nowrap}

/* The person dialog. ⚠️ Wider than the base dialog because it holds two selects side by side;
   the base max-width of 32rem put Type and Role on top of each other. */
#personDlg{max-width:34rem}
#personDlg .pbody{padding:0}
#personDlg h2{margin-bottom:.15rem}
.pacts{display:flex;flex-wrap:wrap;gap:.5rem;margin-top:1rem}
.pacts .btn{flex:0 0 auto}
@media (max-width:34rem){ .pacts{flex-direction:column} .pacts .btn{width:100%} }


/* ⭐ ITEM 130 · THE ROSTER ROW MENU.
   ⛔⛔ THE EXPLICIT [hidden] RULE IS LOAD-BEARING, NOT BELT-AND-BRACES. On 2026-08-26 `hidden`
      was set on all 35 away rows of the schedule and every one STAYED ON SCREEN, because
      `.occ{display:grid}` beat the user-agent's `[hidden]{display:none}`. The attribute was
      set; the pixels never changed, and it was reported as working. Never trust the UA default
      on an element this file also gives a display to. */
.rowmenu{position:absolute;right:0;top:calc(100% - .2rem);z-index:20;
  background:var(--card);border:1px solid var(--line);border-radius:8px;
  box-shadow:0 8px 24px rgba(28,24,24,.16);padding:.3rem;min-width:11rem;text-align:left}
.rowmenu[hidden]{display:none}
.rowmenu .lnk{display:block;width:100%;text-align:left;padding:.4rem .55rem}
/* ⛔ The cell must be a positioning context or the menu anchors to the page and lands
   somewhere else entirely on a scrolled table. */
.roster td.actions{position:relative}
.js-more{font-size:1rem;line-height:1;letter-spacing:.06em}

/* ⭐ ITEM 131 · THE DISTRIBUTION DIALOG */
/* The allotment headline is a button when it can be acted on. It must still READ as the
   number — the affordance is the hover and the "Distribute to staff" line beneath it. */
.ghcount .n.as-link{background:none;border:0;padding:0;font:inherit;color:inherit;cursor:pointer}
.ghcount .n.as-link:hover{text-decoration:underline}
.ghcount .n.as-link:focus-visible{outline:2px solid var(--focus);outline-offset:3px}
#allotDlg{max-width:32rem}
.alsum{display:flex;gap:1.2rem;flex-wrap:wrap;padding:.6rem .7rem;margin:.2rem 0 .8rem;
  background:#f8f7f7;border:1px solid var(--line);border-radius:8px;font-size:.9rem}
.alsum strong{font-size:1.05rem}
/* ⛔ NEGATIVE IS A REAL STATE, NOT AN ERROR — an org may distribute more than it holds, exactly
   as it may invite more guests than its allotment. It is coloured because it is worth noticing,
   never because it is forbidden. */
.alsum .over{color:var(--danger);font-weight:600}
.alrows{display:flex;flex-direction:column;gap:.1rem;max-height:22rem;overflow-y:auto}
.alrow{display:flex;align-items:center;gap:.8rem;padding:.45rem .1rem;
  border-bottom:1px solid var(--line)}
.alrow:last-child{border-bottom:0}
.alwho{flex:1 1 auto;font-weight:600;min-width:0}
.alwho .gsub{font-weight:400}
.alwho .gsub.over{color:var(--danger)}
.alnum{width:5rem;flex:0 0 auto;text-align:right}

/* ⭐ the organisation chooser — the first step when the page is not already filtered */
.alorg{display:flex;align-items:center;justify-content:space-between;gap:1rem;width:100%;
  padding:.7rem .6rem;background:none;border:0;border-bottom:1px solid var(--line);
  font:inherit;text-align:left;cursor:pointer;border-radius:6px}
.alorg:last-child{border-bottom:0}
.alorg:hover{background:#f3f2f2}
.alorg:focus-visible{outline:2px solid var(--focus);outline-offset:-2px}
.alorg .alwho{display:block;font-weight:600}
/* ⚠️ .gsub is a <span> inside a <span> here, so it does NOT inherit the block display it gets
   in a table cell — without this the row reads "Meridian Title Company50 held" with no break. */
.alorg .gsub{display:block;font-weight:400}
.alpick{color:var(--maroon);font-size:.87rem;white-space:nowrap}

/* ⭐ ITEM 140 · the distribution switch, the spread button and the shortfall warning */
/* ⚠️ GRID, NOT FLEX. As a flex row with space-between, inside a 512px dialog, the label's
   inner span collapsed to 61px — "Cap each person at their number" wrapped ONE WORD PER LINE
   and stood 237px tall — and the Spread button rendered ON TOP of the hint. Two cells on row
   one, the hint spanning both on row two: nothing negotiates for space. */
.alswitch{display:grid;grid-template-columns:1fr auto;align-items:center;
  column-gap:.75rem;row-gap:.3rem;
  padding:.6rem .7rem;margin:0 0 .6rem;background:#f8f7f7;border:1px solid var(--line);
  border-radius:8px}
.alflag{display:flex;align-items:center;gap:.5rem;cursor:pointer;font-size:.9rem;min-width:0}
.alflag input{flex:0 0 auto;width:1rem;height:1rem;margin:0}
.alflag strong{min-width:0}
/* ⛔ SPANS BOTH COLUMNS. As a third flex child it was squeezed into the leftover width and
   collided with the button. */
.alhint{grid-column:1 / -1;margin:0}
@media (max-width:26rem){ .alswitch{grid-template-columns:1fr} #alSpread{width:100%} }
/* ⛔ A WARNING, NOT AN ERROR — an under-allocation is a state to notice, never a refusal. */
.alwarn{margin:0 0 .7rem;padding:.5rem .7rem;border-radius:8px;font-size:.87rem;
  background:#fff8e6;border:1px solid #f0dfae;color:#6b5313}
.alovr{display:inline-block;margin-top:.15rem;font-size:.78rem;padding:.1rem .4rem}
.alovr.on{color:#1f7a3d;font-weight:600;background:#eef6f0}

/* ⭐ ITEM 132 · the schedule's own-event control and dialog */
.pagehead{display:flex;align-items:flex-start;justify-content:space-between;gap:1rem;flex-wrap:wrap}
.pagehead h1{margin-bottom:.2rem}
.pagehead .btn{flex:0 0 auto;margin-top:.35rem}
/* ⚠️ Same grid lesson as the cap switch: a checkbox beside a two-line label is a flex row that
   loses, so the label gets its own column and is allowed to wrap normally. */
.evflag{display:grid;grid-template-columns:auto 1fr;gap:.5rem;align-items:start;
  padding:.6rem .7rem;margin:.6rem 0 0;background:#f8f7f7;border:1px solid var(--line);
  border-radius:8px}
.evflag input{margin:.2rem 0 0}
.evflag .hint{display:block;margin:.15rem 0 0}

/* ⭐ ITEM 137 · a partner-owned occasion wears its own marks (Q25: never Missouri State's) */
/* ⛔ A partner mark is an arbitrary upload, not one of our four measured lockups, so it is
   CONSTRAINED rather than trusted — a 4000px logo would otherwise blow the card apart. */
.tk-lockup{max-height:34px;width:auto;object-fit:contain}
/* When there is no mark, the organisation's NAME carries the identity. */
.tk-lockup-text{font-weight:700;font-size:.95rem;color:var(--ink);letter-spacing:.01em}
.tk-foot-strip{padding:0;background:none;border:0}
/* ⛔⛔ `object-fit: cover` GUILLOTINED THE PARTNER'S LOGO. Measured 2026-09-10 on Wil Fischer's
   Bud Light strip: the asset is 384x215 (aspect 1.79 — a MARK), the box is 350x74 (aspect 4.73).
   `cover` scales to FILL, so it rendered the image at 350x196 and cropped the middle 74px, taking
   the top and bottom off the wordmark. Jeremy spotted it on his own phone: "look at the budlight
   logo on the screen."
   ⭐ `cover` is for a photographic banner. A BRAND MARK must never be cropped — a partner's logo
     is a licence condition, not decoration. `contain` fits the whole mark inside the box and
     letterboxes, which is correct for both a mark AND a true wide strip (that fills exactly).
   ⚠️ Found by LOOKING. The markup was byte-identical to what shipped before, the CSS was present
     and "working", and no assertion on text could ever see it. */
.tk-strip{display:block;width:100%;max-height:74px;object-fit:contain;
          padding:.45rem 1rem;box-sizing:border-box;background:#f4f1ec}
/* ⭐ 252e — a BAND-shaped image (4:1 or wider) is already a finished strip, so it fills the foot
   edge to edge at its own height; padding and the 74px cap are for a MARK sitting inside a strip. */
.tk-strip.is-fill{padding:0;max-height:none;height:auto;object-fit:fill}

/* ⚠️ .gsettings makes every input full width — right for a textarea, absurd for a colour
   swatch, which rendered as a 700px bar. */
/* ⛔ THE SWATCH IS THE ONLY CLICK TARGET. It used to sit inside a <label>, which makes the
   whole label — including the sentence explaining it — open the picker. `cursor:default` on the
   row says so too: a pointer over plain text promises something that no longer happens. */
.colorrow{display:flex;align-items:center;gap:.6rem;flex-wrap:wrap;cursor:default}
.collabel{font-weight:600;font-size:.9rem}
.colorrow .hint{flex:1 1 14rem;margin:0}
#evbrand input[type="color"]{width:4.5rem;height:2.2rem;padding:.15rem;cursor:pointer;
  flex:0 0 auto;border:1px solid var(--line);border-radius:6px;background:none}
#evbrand .row2 select{width:100%}

/* ⭐ 141g · THE BRANDING SIZE GUIDE — a disclosure, because the panel it sits in was crowded.
   ⛔ MATCHES .scanmanual / .pergame RATHER THAN INVENTING A THIRD DISCLOSURE LOOK: same maroon
      summary, same pointer, same small type. A screen that grows a new affordance style per
      panel is how "crowded" starts. */
.sizeguide{margin:.2rem 0 .9rem}
.sizeguide summary{cursor:pointer;color:#5E0009;font-size:.9rem}
/* ⛔ The paragraphs inside carry .hint, which already sets colour and size; this only tightens
   the spacing so an opened guide does not push the colour row off the fold. */
.sizeguide p{margin:.5rem 0 0}

/* ⭐ ITEM 169 — a partner with no mark on file gets their NAME in the chrome, never MTC's
   lockup. Sized to sit on the same 38px baseline as the logo image it replaces. */
.brand .brandname{display:inline-block;line-height:38px;color:#fff;font-weight:700;
  font-size:1.02rem;letter-spacing:.01em;white-space:nowrap;max-width:46vw;
  overflow:hidden;text-overflow:ellipsis}

/* ============================================================================
   ITEM 139 — GATE MODE. The kiosk presentation of the topbar.
   ⛔⛔ PRESENTATION ONLY. Nothing here locks anything; a gate-mode session holds exactly the
      permissions it held a second before (PLAN.md §7 Q37, Jeremy's decision).
   ============================================================================ */

/* The bar keeps its maroon, so nobody has to wonder whether they are in the right product —
   it just stops carrying seven links they cannot use while holding a camera up to a phone. */
.topbar-gate{justify-content:space-between;padding:.7rem 1rem;flex-wrap:nowrap;gap:.75rem}

/* ⛔ THE OCCASION IS THE BIGGEST THING ON THE SCREEN. A scanner pointed at the wrong door
   refuses every real guest in the queue, and the person holding it is looking at a face, not
   at a heading — so on the rare occasion they DO look, it has to be unmissable.
   ⚠️ min-width:0 is load-bearing: without it a flex child refuses to shrink and the long
   opponent names push the "Scanning as" strip off the right edge of a 768px iPad in portrait. */
.gatepin{min-width:0;display:flex;flex-direction:column;gap:.1rem}
.gatepin-occ{display:block;font-size:1.45rem;line-height:1.1;font-weight:700;color:#fff;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.gatepin-sub{font-size:.82rem;letter-spacing:.04em;text-transform:uppercase;color:#f0dfe0;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

/* ⭐⭐ THE HONESTY AFFORDANCE AND THE EXIT ARE ONE CONTROL. Under "one login all night" every
   admission on this device is recorded against this name, and this line is the only thing that
   tells whoever is now holding it whose name that is. Putting the way out behind it means the
   name gets read at exactly the moment somebody thinks about taking over.
   ⛔ It is NOT a confirmation and must never become one — Jeremy asked for no friction. */
.gateout{position:relative;flex:0 0 auto}
.gateout>summary{list-style:none;cursor:pointer;text-align:right;line-height:1.15;
  padding:.3rem .1rem;border-radius:.4rem}
.gateout>summary::-webkit-details-marker{display:none}
.gateout>summary:focus-visible{outline:2px solid #fff;outline-offset:2px}
.gateout-lab{display:block;font-size:.66rem;letter-spacing:.08em;text-transform:uppercase;
  color:#e2c7ca}
.gateout-who{display:block;font-weight:650;color:#fff;font-size:.95rem;
  max-width:40vw;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* ⚠️ z-index 30, ABOVE the camera stage. A child cannot escape its parent's stacking context,
   so this sheet is anchored inside .topbar — which creates none — rather than inside the scan
   panel, which does. That mistake is written up in CLAUDE.md and cost a session once already. */
.gateout-sheet{position:absolute;right:0;top:calc(100% + .5rem);z-index:30;width:16rem;
  background:#fff;border:1px solid var(--line);border-radius:var(--radius);
  box-shadow:0 12px 32px rgba(24,21,21,.28);padding:.9rem}
.gateout-sheet p{margin:0 0 .7rem;font-size:.83rem;color:var(--body);line-height:1.4}
.gateout-sheet .btn{width:100%}

/* ⭐ ON A PHONE the name is what gives way, never the occasion — the door you are working
   matters more on screen than a name the holder already knows. */
@media (max-width:34rem){
  .gatepin-occ{font-size:1.2rem}
  .gateout-who{max-width:32vw}
  .gateout-sheet{width:14rem}
}

/* ⛔⛔ THE EXIT HAD NO AFFORDANCE AND I ONLY SAW IT IN A SCREENSHOT.
   "SCANNING AS / Jeremy Burcham" rendered as two lines of plain text in the corner of a maroon
   bar. It IS the way out of gate mode — and it looked exactly like a status label, so nobody
   would ever have pressed it. 47 assertions passed on it: every one asked whether the STRING
   was present, and a string cannot tell you it looks unpressable.
   ⭐ A chevron and a hairline are the whole fix — enough to read as a control, not so much that
      it competes with the occasion, which is the thing that must dominate. */
.gateout>summary{display:flex;align-items:center;gap:.45rem;justify-content:flex-end;
  border:1px solid rgba(255,255,255,.34);background:rgba(255,255,255,.08);padding:.32rem .6rem}
.gateout>summary::after{content:"";flex:0 0 auto;width:.42rem;height:.42rem;margin-top:-.2rem;
  border-right:2px solid #f0dfe0;border-bottom:2px solid #f0dfe0;transform:rotate(45deg);
  transition:transform .12s ease}
.gateout[open]>summary::after{transform:rotate(-135deg);margin-top:.14rem}
.gateout>summary:hover{background:rgba(255,255,255,.16)}

/* ⛔⛔ AND THE CAMERA DID NOT DOMINATE — the one thing the device exists to do was a small
   button two-thirds of the way up a mostly empty 1024px screen. On a kiosk the job gets the
   screen. ⚠️ It cannot AUTO-start: getUserMedia needs a user gesture, so the button is the
   mechanism and it simply has to be findable from arm's length. */
.scanwrap.is-kiosk{max-width:44rem}
.scanwrap.is-kiosk .panelhead{border:0;padding-bottom:0;margin-bottom:.4rem}
.scanwrap.is-kiosk .scantally{margin:.4rem 0 .9rem}
.scanwrap.is-kiosk .scanstage{min-height:22rem}
/* ⛔⛔ 242 — AND ON A PHONE THAT MINIMUM MUST GO, not just be capped. `aspect-ratio` takes its
   width from a constrained height, so 22rem × 4/3 = 469px on a 440px screen: the camera box hung
   29px off the side and took the page with it. `max-width:100%` (see .scanstage) stops the
   overflow, but on its own it leaves a 440×352 box — a broken 5:4 that crops more of the guest's
   code than the design intends. Dropping the minimum lets the box be a true 4:3 the width of the
   phone. ⚠️ 34rem is the same breakpoint the kiosk's own max-width logic already implies; a
   kiosk screen is far wider and keeps the 22rem floor.
   ⭐ Gate mode is USED ON A PHONE AT A DARK DOOR — this is the one screen where "it looks fine on
     my laptop" is worth nothing. Measured at 440px and 390px before and after. */
@media (max-width:34rem){ .scanwrap.is-kiosk .scanstage{min-height:0} }
.scanwrap.is-kiosk #scanStart{font-size:1.25rem;padding:1rem 2.4rem;width:100%;max-width:22rem}
.scanwrap.is-kiosk .scanhint{font-size:1rem}
/* ⭐ The typed-code fallback stays a quiet <details> — it is the thing you reach for when the
   lens will not focus, not something that should compete with the camera. */
.scanwrap.is-kiosk .scanmanual{margin-top:1.1rem}

/* ⛔⛔ D92 — THE OCCASION PICKER OVERFLOWED THE PAGE ON A PHONE, AND IT IS PRE-EXISTING.
   Measured at 390px on BOTH the kiosk and the ordinary gate page: the <select> sized itself to
   its longest option ("Men's Basketball · vs Sam Houston · 6 Mar 2027") and came out 398px
   wide, scrolling the whole page sideways by 28px. Live since the picker shipped 2026-08-28,
   on the one screen CLAUDE.md says is "used on a phone at a dark gate".
   ⭐ FOUND ONLY BY MEASURING THE RENDERED PAGE — and note the inverse of the standing warning:
      "an absence of overflow is not evidence", but a PRESENCE of it is a defect, every time.
   ⚠️ min-width:0 on the form is the load-bearing half: .panelhead is a flex container and a
      flex item will not shrink below its content without it, so max-width alone does nothing. */
.panelhead form{min-width:0;max-width:100%}
#gateGame{max-width:100%;min-width:0;
  /* ⭐ 2026-09-11 — the one control a gate worker MUST set, sized for a thumb at a dark door
     (192c's ≥44px rule, which the /t.php door picker got and this one did not) */
  min-height:44px;padding:.4rem .6rem;font:inherit;font-size:.95rem;color:var(--ink);
  background:#fff;border:1px solid var(--line);border-radius:8px}

/* ⭐ 235 — a store buyer's other dates, on their guest page (g.php #gdates) */
.gdates h2{margin:0 0 .3rem}
.gdatelist{list-style:none;margin:.8rem 0 0;padding:0}
.gdatelist li{display:flex;justify-content:space-between;align-items:center;gap:.75rem;
  padding:.7rem .2rem;border-top:1px solid var(--line);min-height:44px}
.gdatelist li:first-child{border-top:0}
.gdatelist a{font-weight:600;color:var(--maroon)}
.gdatelist li.here{background:#fbf1f2;border-radius:8px;padding-left:.6rem;padding-right:.6rem}
.gdatelist .gdn{font-size:.85rem;color:#8c8586;white-space:nowrap}

/* ⚠️ THE EXIT WAS A 30px TAP TARGET. Apple's own minimum is 44, and this is pressed by somebody
   holding an iPad in one hand at a gate. Measured with getBoundingClientRect, not eyeballed. */
.gateout>summary{min-height:44px}
.gateout-id{display:block;min-width:0;text-align:right}

/* ══ ITEM 174 · VIEW AS — the read-only lens ══════════════════════════════════
   ⭐ The BAR is the safety feature. A super admin who forgets whose face they are wearing and
      starts working is worse than the confusion this fixes, so it sits in the document flow on
      every page (never a corner toast that scrolls away) and carries its own way out.
   ⚠️ It is MTC purple, deliberately NOT maroon: maroon is the product's own chrome, and a
      warning drawn in the house colour reads as part of the furniture. This must not. */
.pill-btn{font:inherit;cursor:pointer;line-height:inherit}
.who .pill-btn:hover{background:rgba(255,255,255,.3)}
.who .pill-btn:focus-visible{outline:2px solid #fff;outline-offset:2px}

.viewas-bar{background:var(--viewas);color:#fff;display:flex;flex-wrap:wrap;gap:.75rem;
  align-items:center;justify-content:space-between;padding:.55rem 1.25rem;
  font-size:.9rem;position:sticky;top:0;z-index:40}
.viewas-bar strong{color:#fff}
.viewas-ro{display:inline-block;margin-left:.5rem;padding:.05rem .5rem;border-radius:999px;
  background:rgba(255,255,255,.18);font-size:.78rem}
.viewas-bar .btn.ghost{background:#fff;color:var(--viewas);border-color:#fff;flex:none}

.viewas-dlg{border:0;border-radius:14px;padding:1.4rem;max-width:32rem;width:calc(100% - 2rem)}
.viewas-dlg::backdrop{background:rgba(24,21,21,.45)}
.viewas-dlg input[type=search]{width:100%;padding:.5rem .7rem;margin:.2rem 0 .8rem;
  border:1px solid var(--line);border-radius:8px;font:inherit}
.viewas-list{max-height:22rem;overflow-y:auto;display:flex;flex-direction:column;gap:.25rem}
.viewas-row{display:flex;flex-direction:column;align-items:flex-start;gap:.1rem;width:100%;
  text-align:left;background:#fff;border:1px solid var(--line);border-radius:9px;
  padding:.5rem .7rem;cursor:pointer;font:inherit;color:var(--body)}
.viewas-row:hover{border-color:var(--focus);background:#fafafa}
.viewas-row:focus-visible{outline:2px solid var(--focus);outline-offset:1px}
.viewas-nm{font-weight:600;color:var(--ink)}
.viewas-meta{font-size:.8rem;color:#736a6a}

/* ══ ITEM 181 · WHO IS ACTUALLY IN ════════════════════════════════════════════
   ⛔ GREEN = everybody in, AMBER = a party part-way through. Never red: nobody has done anything
      wrong, and red at a gate means "stop this person". */
.arr{display:inline-block;margin-left:.45rem;padding:.08rem .5rem;border-radius:999px;
  font-size:.74rem;font-weight:600;letter-spacing:.01em;vertical-align:1px;white-space:nowrap}
.arr-all{background:#e3f4e6;color:#1f5c2b;border:1px solid #bfe3c7}
.arr-part{background:#fdf1dc;color:#7a4d06;border:1px solid #f0d9ab}
.arrfilter{display:flex;gap:.4rem;align-items:center;margin:0 0 .7rem}
.arrfilter .chip{font:inherit;font-size:.82rem;cursor:pointer;background:#fff;
  border:1px solid var(--line);border-radius:999px;padding:.2rem .7rem;color:var(--body)}
.arrfilter .chip:hover{border-color:var(--maroon)}
.arrfilter .chip.on{background:var(--maroon);border-color:var(--maroon);color:#fff}
.arrfilter .chip:focus-visible{outline:2px solid var(--maroon);outline-offset:2px}
.arrcount{font-size:.8rem}

/* ══ ITEM 183 · CSV IMPORT PREVIEW ═══════════════════════════════════════════ */
#csvDlg{border:0;border-radius:14px;padding:1.4rem;max-width:36rem;width:calc(100% - 2rem)}
#csvDlg::backdrop{background:rgba(24,21,21,.45)}
#csvPreview{max-height:20rem;overflow-y:auto}
#csvPreview table{margin-top:.4rem}
/* ⚠️ A skipped row is INFORMATION, not an alarm — nothing has gone wrong and nothing was lost.
   A tinted left edge reads as "look here" without the page turning into a wall of red. */
.csvbad td{background:#fdf6f6}
.csvbad tr td:first-child{border-left:3px solid #c98b8b}

/* ── ITEM 136 · THE ROOM ─────────────────────────────────────────────────────
   A partner's "suite" is a named room their occasions group under (Jeremy, 2026-09-09) —
   a LABEL, never a seat pool and never a page. §UI's review test is that the schedule stays
   one date-ordered list, so the room is a tag on the row and a filter, nothing more.
   ⚠️ Deliberately quiet. The venue already printed here in plain text; this only marks it as
   the thing the filter groups on, and a loud treatment would compete with the pill, which is
   the row's load-bearing element. */
.occ .sub .room { opacity: .85 }
.roomlink { margin-top: .35rem }
.roomlink a { color: inherit; text-decoration: underline; text-underline-offset: 2px }
.roomlink a:hover { text-decoration-thickness: 2px }

/* ── ITEM 193d · HELP ────────────────────────────────────────────────────────
   ⚠️ The `?` sits in .who, which item 194 just taught to shrink at phone width. It is `flex:none`
   for the same reason Sign out is: a header that fits by hiding the way to the instructions has
   not fixed anything. */
.helpq{display:inline-flex;align-items:center;justify-content:center;width:26px;height:26px;
  border-radius:50%;border:1px solid rgba(255,255,255,.45);color:#f0dfe0;font-weight:700;
  font-size:.85rem;text-decoration:none;flex:none;line-height:1}
.helpq:hover{background:rgba(255,255,255,.16);color:#fff}
.helplist{list-style:none;margin:0;padding:0}
.helplist li{padding:.6rem 0;border-bottom:1px solid var(--line)}
.helplist li:last-child{border-bottom:0}
.helplist a{font-weight:600;text-decoration:none}
.helpdesc{display:block;color:var(--muted);font-size:.9rem;margin-top:.15rem}
.helpbody{max-width:46rem}
.helpbody h2{margin:1.6rem 0 .5rem}
.helpbody h2:first-child{margin-top:0}
.helpbody h3{margin:1.2rem 0 .4rem;font-size:1.02rem}
.helpbody p{margin:.5rem 0;line-height:1.55}
.helpbody ul{margin:.4rem 0 .8rem 1.1rem}
.helpbody li{margin:.25rem 0;line-height:1.5}
.helpbody blockquote{margin:.9rem 0;padding:.7rem .9rem;border-left:3px solid var(--maroon);
  background:rgba(94,0,9,.05);border-radius:0 6px 6px 0}
/* ⛔ A SCREENSHOT IS DECORATION, NOT CONTENT — the prose never depends on it. It is bordered so a
   reader can tell where the picture ends and the page resumes. */
.help-shot{display:block;width:100%;height:auto;border:1px solid var(--line);border-radius:8px;
  margin:.9rem 0 .3rem}
.help-cap{display:block;color:var(--muted);font-size:.86rem;margin-bottom:1rem}
.helpnav{display:flex;justify-content:space-between;gap:1rem;margin:1rem 0 2rem;flex-wrap:wrap}

/* ── ITEM 193g · HELP SEARCH ─────────────────────────────────────────────────
   ⭐ A plain GET form, so it works with JavaScript off — the same rule the organisation filter
   follows: a control that decides what you are looking at must never silently do nothing. */
.helpsearch{display:flex;gap:.5rem;align-items:center;flex-wrap:wrap;margin:0 0 1rem}
.helpsearch input[type=search]{flex:1 1 22rem;min-width:0;min-height:44px;padding:.5rem .75rem;
  border:1px solid var(--line);border-radius:8px;font:inherit;background:#fff}
.helpsearch input[type=search]:focus{outline:2px solid var(--focus);outline-offset:1px}
.helpsearch .btn{min-height:44px}
.helpclear{white-space:nowrap}

/* ⭐ ITEM 199b — the row-level send-sign-in-link icon on the People page.
   ⛔ 34px, NOT the 44px the GATE controls use. Item 192c raised every gate control to 44px
      unconditionally because that screen is worked one-handed at a door with a queue behind
      you. This is an admin table on a desk; matching 44px here would push the row height back
      up and undo item 130, which existed to bring 9 controls per row down to a readable grid.
      Different screen, different rule — the 192c minimum is about the GATE, not about touch
      targets everywhere.
   ⚠️ vertical-align: middle, because it sits inline after the address text. */
.iconbtn{display:inline-flex;align-items:center;justify-content:center;
  width:34px;height:34px;margin-left:.35rem;vertical-align:middle;
  border:1px solid var(--line);border-radius:8px;background:transparent;
  color:var(--muted);cursor:pointer;padding:0;flex:none;
  transition:color .12s,border-color .12s,background .12s}
.iconbtn:hover{color:var(--ink);border-color:var(--ink);background:var(--tint)}
.iconbtn:disabled{opacity:.45;cursor:default}
/* ⭐ ON SUCCESS THE SHARED HANDLER SHOWS "Link sent ✓" HERE TOO — the confirmation belongs next
   to the person it is about, and a green tick with no words is not an answer. The button
   therefore has to GROW out of its 34px square for six seconds, which is what width:auto does;
   the handler restores the SVG from innerHTML afterwards. */
.iconbtn.ok{color:#1a7f37;border-color:#1a7f37;width:auto;padding:0 .5rem;font-size:.78rem}

/* ── ITEM 209 · "give this seat to someone else" ─────────────────────────────────────────
   ⛔ Gate/guest controls are >=44px UNCONDITIONALLY. 192c: a required control shipped as a
      19px unstyled <select> and a `@media (pointer: coarse)` rule cannot be evaluated from
      this harness, so the size is not made conditional on guessing the device. */
.giveseat            { max-width: 340px; margin: .25rem auto 1.75rem; text-align: center; }
.giveseat .btn       { min-height: 44px; }
.giveseat-form       { text-align: left; background: #fff; border: 1px solid #e3e1e1;
                       border-radius: 10px; padding: 1rem; margin-top: .6rem; }
.giveseat-form label { display: block; font-size: .88rem; margin: 0 0 .7rem; color: #3a3a3a; }
.giveseat-form input { display: block; width: 100%; box-sizing: border-box; min-height: 44px;
                       margin-top: .25rem; padding: .5rem .6rem; font-size: 1rem;
                       border: 1px solid #cac6c7; border-radius: 6px; }
.giveseat-note       { font-size: .85rem; color: #5a5a5a; margin: 0 0 .9rem; }
.giveseat-actions    { display: flex; gap: .5rem; margin: .25rem 0 0; }
.giveseat-actions .btn { flex: 1 1 auto; }
.giveseat-msg        { font-size: .88rem; margin: .6rem 0 0; min-height: 1.2em; color: #5E0009; }
.giveseat-done       { font-size: .92rem; color: #2f5d33; background: #eef6ef;
                       border: 1px solid #cfe3d1; border-radius: 8px; padding: .75rem .9rem; }
@media print { .giveseat { display: none !important; } }


/* ══════════ 250/251 · THE PASS PREVIEW — one strip, one stage, one ledger ══════════════════
   ⭐ Jeremy, 2026-09-12: "we need to make sure when designing a pass concept it is clear and
      concise in the view and we can simply click expand review and publish new views and it
      clear what we are allowing and disallowing to the user controlling the event and the user
      that can brand the event item."
   ⛔ A FRESH NAMESPACE (.wlp-) ON PURPOSE. The web card inside the stage is the REAL partial
      (views/pass_card.php) wearing its own .tk-* rules; nothing here may RESTYLE .tk-* — a
      preview that renders differently from the thing it previews is worse than none. The
      hover-outline rules below TARGET .tk-* without changing any of their properties.
   ⛔⛔ WHAT WAS THROWN AWAY, AND WHY, so nobody restores it:
      · THE PHONE FRAMES (.wlp-phone). ~1.1rem of chrome a column, and they forced
        min-height:20rem so every card had dead space under it. The web card is never in a
        handset frame anywhere a guest sees it, so the frame made the TRUEST surface look the
        least true. The old comment here already conceded they were decoration.
      · align-items:stretch + margin-top:auto on the QR. Stretching to the tallest sibling and
        pinning the QR to the bottom drew a void no wallet card has.
      · THE FLAGS DRAWN ON THE ARTWORK (.wlp-flag/.wlp-note/.wlp-apple-strip-pending). They
        collided with the Bud Light logo TWICE — I "fixed" it once and the screenshot showed it
        still colliding, because .wlp-g-hero was never a flex container so flex-direction did
        nothing there. ⭐ A warning drawn INSIDE a card makes the reviewer judge the warning.
        Every word survives as a .wlp-gap line UNDER the surface it is about.
   ⭐ ONE ACCENT VARIABLE so allowd can re-theme this block per tenant without touching the
      rest of the file: .wlp{--accent:var(--maroon)}. */
.wlp{--accent:var(--maroon);margin:1.25rem 0 .25rem;padding-top:1rem;border-top:1px solid var(--line)}

/* ── the collapsed strip: four specimens, a summary, a disclosure ───────────────────────── */
.wlp-bar{display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:.9rem;
  width:100%;padding:.55rem .7rem;font:inherit;text-align:left;cursor:pointer;color:inherit;
  background:#faf8f8;border:1px solid var(--line);border-radius:10px}
.wlp-bar:hover{border-color:#d3cccd}
.wlp-bar:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
.wlp-bar[aria-expanded="true"]{border-radius:10px 10px 0 0}
.wlp-sum{min-width:0;display:flex;flex-direction:column;gap:.1rem}
.wlp-sum strong{color:var(--ink);font-size:.95rem}
.wlp-sum span{font-size:.8rem;color:#6d6566;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.wlp-cta{font-size:.86rem;font-weight:600;color:var(--accent);white-space:nowrap}
.wlp-cta::after{content:" \25BE"}
.wlp-bar[aria-expanded="true"] .wlp-cta::after{content:" \25B4"}
.wlp-body{border:1px solid var(--line);border-top:0;border-radius:0 0 10px 10px;
  padding:1rem .9rem .6rem;background:var(--card)}

/* ── THE FOUR SPECIMENS. The BORDER is the lock state: solid = locked, dashed = open.
   ⭐ That grammar is the house's own — .chip.out, .tag.evt, .pill.out and .occ.is-event all
      already mean "set apart by outline rather than by a second colour", which is what keeps
      MO State's palette undiluted (a licence condition, not a taste). NO PADLOCK GLYPHS: the
      state is carried by the word, the border and the ground, so none of them is the sole
      carrier — the same rule the maroon/contrast note at :121 states for colour. */
.wlp-els{display:flex;gap:.35rem}
.wlp-el{display:grid;place-items:center;width:28px;height:28px;border-radius:6px;overflow:hidden;
/* ⛔⛔ A CSS-WIDE KEYWORD CANNOT BE ONE COMPONENT OF A SHORTHAND — FIXED 2026-09-12.
   `font:600 .78rem/1 inherit` is an INVALID declaration: `inherit` is only legal as the WHOLE
   value (`font:inherit`), never alongside other components. A browser that cannot parse a
   declaration DISCARDS THE ENTIRE THING, so every rule written this way silently lost its weight,
   size AND line-height and rendered at the 16px body size instead of ~12px.
   ⚠️ `var(--f-body,inherit)` was the same bug wearing a disguise: `--f-body` is defined NOWHERE in
      this file, so the fallback substituted the bare keyword straight back into the shorthand.
   ⭐ FOUND BY A DESIGN REVIEW READING THE CSS, NOT BY ANY CHECK WE OWN — nothing errors, nothing
     logs, and the panel just looks wrong in a way that reads as bad design rather than a defect.
   ⛔ `font:inherit` ON ITS OWN IS PERFECTLY VALID and appears ~34 times in this file, usually
      followed by longhand font-size/font-weight. Those are CORRECT. Do not "fix" them. */
  background:var(--bg-el,#fff);color:var(--fg-el,var(--ink));font-weight:700;font-size:.72rem;line-height:1;
  font-style:normal;border:2px solid var(--accent);flex:none}
.wlp-el.is-open{border-style:dashed;border-color:#b8a9ab}
.wlp-el img{max-width:20px;max-height:20px;object-fit:contain;display:block}
.wlp-el.is-colour{background:var(--bg-el,#5E0009)}
/* the TEXT specimen renders "Aa" in the font colour ON the ground — so the pair previews
   CONTRAST, not just hue. That is the one thing Apple leaves to us. */
.wlp-el.is-text{background:var(--bg-el,#5E0009);color:var(--fg-el,#fff)}
.wlp-el.is-empty::after{content:"\2014";color:#aca1a2;font-weight:500}

/* ── the stage: natural height, TOP aligned, equal width, no frames ─────────────────────── */
.wlp-surfaces{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:1rem;align-items:start}
.wlp-surface{margin:0;min-width:0}
.wlp-surface figcaption{font-weight:700;font-size:.7rem;line-height:1.3;letter-spacing:.06em;
  text-transform:uppercase;color:#6d6566;margin:0 0 .4rem}
.wlp-surface figcaption span{font-weight:500;letter-spacing:0;text-transform:none;margin-left:.35rem}
.wlp-screen{border-radius:12px;overflow:hidden;border:1px solid var(--line);background:#fff}
.wlp-screen-web{background:#f5f4f3;padding:.6rem}
.wlp-screen-web .tk-wrap{max-width:100%}
.wlp-screen-web .tk{margin:0}
/* ⛔ KEEP. The QR is 230px by its own rule — right on a real card, wrong in a preview column,
   and SCOPED so the pass a guest actually scans is untouched. */
.wlp-screen-web .tk-qr{max-width:62%;margin-left:auto;margin-right:auto}
/* the honesty line, UNDER the card it is about — never drawn on the artwork */
.wlp-gap{margin:.4rem 0 0;font-weight:600;font-size:.74rem;line-height:1.35;color:var(--warn)}
.wlp-gap code{font-size:.95em}

/* ── Apple, in the Figure 4-4 order ─────────────────────────────────────────────────────────
   ⛔⛔ THE STRIP IS A BACKGROUND AND THE PRIMARY FIELDS ARE DRAWN ON TOP OF IT. Measured from
      Apple's Wallet Developer Guide 2026-09-12; this preview previously drew the strip as its
      own band ABOVE the primaries, which is simply not the card. The slot is 375x98pt.
   ⛔ A pass may not carry both a strip and a background image, and backgroundColor is IGNORED
      when a background image is present — we never send one, so there is no background lock. */
.wlp-apple{background:var(--bg-a,#5E0009);color:var(--fg-a,#fff);padding:.7rem .75rem .9rem;
  display:flex;flex-direction:column;gap:.5rem}
.wlp-apple em{display:block;font-style:normal;font-size:.6rem;letter-spacing:.08em;
  text-transform:uppercase;color:var(--lab-a,rgba(255,255,255,.75))}
.wlp-apple-top{display:flex;align-items:center;gap:.5rem}
.wlp-apple-logo{max-height:32px;max-width:100px;width:auto;height:auto;object-fit:contain}
.wlp-apple-logotext{font-weight:600;font-size:.82rem;line-height:1.2}
.wlp-apple-header{margin-left:auto;text-align:right;font-size:.7rem}
.wlp-apple-header b{font-size:.85rem}
/* ⛔⛔ I WROTE object-fit:cover HERE AND THIS FILE ALREADY DOCUMENTED WHY THAT IS WRONG — see
   :1375, measured 2026-09-10 ON THIS EXACT ASSET: "cover GUILLOTINED THE PARTNER'S LOGO … the
   asset is 384x215 (aspect 1.79 — a MARK), the box is 350x74 (aspect 4.73). cover scales to FILL
   so it cropped the middle." I reintroduced it in the Apple mock; the banner blew up to ~115px
   tall with the primary fields printed across the word BUD LIGHT. Jeremy saw it in one look.
   ⛔⛔ AND THE ENDZONE BEING WORSE IS THE DIAGNOSIS, NOT A SEPARATE BUG: endzone-word.png is
      1966x200 = 9.83:1 going into Apple's 3.83:1 slot, against bud-light.png at 1.79:1. ⭐ THE
      ASSETS ARE NOT STRIP-SHAPED — one is a square-ish mark, the other is ten times wider than
      tall — so NOTHING that fills that slot can be honest. It letterboxes, exactly as .tk-strip
      does on the web card, on the same #f4f1ec ground.
   ⚠️ PRODUCT CONSEQUENCE, not a style note: item 250 sends this to Apple's footer.png, and it
      will look wrong on real phones until purpose-made ~3.8:1 assets exist. The mock is the first
      thing that made that visible, which is the whole argument for having built it. */
.wlp-apple-strip{margin:0 -.75rem;background:#f4f1ec;overflow:hidden}
.wlp-apple-strip img{display:block;width:100%;max-height:46px;object-fit:contain;
  padding:.4rem .75rem;box-sizing:border-box}
.wlp-apple-strip.is-bare{background:none;margin:0}
/* ⛔ THE PRIMARIES SIT BELOW THE BAND, NOT ON IT. Figure 4-4 layers them, and that is correct for
   a purpose-made 375x98 strip; over a LETTERBOXED logo it is unreadable, and Apple specifies no
   contrast rule to lean on (measured from their own guide). The caption under the card says so
   rather than the layout pretending otherwise. */
.wlp-apple-primary{padding:.1rem 0 0}
.wlp-apple-primary strong{font-size:1.05rem;line-height:1.2}
.wlp-apple-grid{display:grid;grid-template-columns:1fr 1fr;gap:.4rem .6rem;font-size:.78rem}
.wlp-apple-grid b{font-size:.78rem}

/* ── Google — aspect-agnostic, because BOTH layouts are live at once ────────────────────────
   ⚠️⚠️ A full-screen component-based redesign began rolling out 2026-08-18 and A GUEST CAN
      TOGGLE BETWEEN THE TWO with the clock icon on the card. So there is no single correct
      Android card to draw. ⭐ The mock draws one and says the other exists, and the caption
      lets the reviewer flip — approving against a silently chosen layout is the "approved
      against a fiction" risk, and showing the second fiction is what closes it.
   ⭐ ONE pair of variables decides geometry and order. Emit them from one PHP constant beside
      gwallet.php's heroImage builder so the mock and the builder cannot disagree. */
.wlp-google{background:var(--bg-g,#5E0009);color:var(--fg-g,#fff);padding:.7rem .75rem .9rem;
  display:flex;flex-direction:column;gap:.55rem}
.wlp-g-top{display:flex;align-items:center;gap:.5rem;order:0}
/* ⛔ A CIRCLE MASK WITH A 15% SAFE MARGIN, NOT object-fit:cover. Google's own rule is a 660x660
   1:1 image inset 15% so the mark is not cut off; `cover` CROPS it, which flatters a mark that
   loses its edges on the real card. Measured the same day as the maroon-lockup crop. */
/* ⛔ 15% AS PADDING ON AN overflow:hidden GRID BOX RENDERED AN EMPTY WHITE DISC. Google's rule
   is a 660x660 image with a 15% safe margin so the circle mask cannot clip the mark; expressing
   that as padding on the masked box left the image sized to a content box the white ground then
   covered. The margin belongs to the IMAGE, not the disc. */
.wlp-g-logo{width:34px;height:34px;border-radius:50%;background:#fff;overflow:hidden;
  display:grid;place-items:center;flex:none}
.wlp-g-logo img{width:70%;height:70%;object-fit:contain;display:block}
.wlp-g-letter{width:34px;height:34px;border-radius:50%;background:rgba(255,255,255,.22);
  display:grid;place-items:center;font-weight:700;flex:none}
.wlp-g-iss{font-size:.8rem;font-weight:600}
/* a wide logo (1280x400, 16:5) REPLACES the logo+issuer row on Android */
.wlp-g-wide{width:100%;aspect-ratio:16/5;object-fit:contain;background:#fff;border-radius:6px;order:0}
.wlp-g-name{margin:0;font-size:.95rem;line-height:1.25;order:1}
.wlp-g-rows{display:flex;flex-direction:column;gap:.3rem;font-size:.8rem;order:1}
.wlp-g-rows b{font-size:.8rem}
.wlp-g-rows em{display:block;font-style:normal;font-size:.58rem;letter-spacing:.07em;
  text-transform:uppercase;opacity:.8}
.wlp-g-hero{aspect-ratio:var(--hero-ar,3/1);border-radius:.45rem;overflow:hidden;
  background:rgba(255,255,255,.92);order:var(--hero-pos,2)}
.wlp-g-hero img{width:100%;height:100%;object-fit:cover;display:block}
.wlp-google[data-glay="new"]{--hero-ar:1032/812;--hero-pos:1}
.wlp-google[data-glay="old"]{--hero-ar:3/1;--hero-pos:2}
.wlp-gtog{display:inline-flex;gap:.2rem;margin-left:.5rem;vertical-align:middle}
.wlp-gtog button{font-weight:600;font-size:.68rem;line-height:1;letter-spacing:0;text-transform:none;
  padding:.3rem .55rem;border-radius:999px;cursor:pointer;border:1px solid var(--line);
  background:#f5f3f3;color:var(--body);min-height:28px}
.wlp-gtog [aria-pressed="true"]{background:var(--accent);border-color:var(--accent);color:#fff}

/* ── shared ─────────────────────────────────────────────────────────────────────────────── */
/* ⛔⛔ THE ENDZONE BAND HAD NO RULE AT ALL, AND THAT IS WHY IT LOOKED WRONG.
   `.wlp-g-band` is emitted by pass_preview.php:330 and was defined NOWHERE in this stylesheet —
   it lived in the block I replaced wholesale and I did not carry it across. So Apple's footer
   rendered as two bare <img> straight onto the maroon card: endzone-word.png is rgb(11,19,73),
   which against #5E0009 is a contrast ratio of 1.23:1 (the AA floor is 4.5). Jeremy read it off
   the screen immediately — "endzone is the wrong color should be white".
   ⭐⭐ AND MY FIRST ATTEMPT TO FIX IT EDITED A RULE THAT DID NOT EXIST: the anchored replace
      asserted 1 occurrence, found 0, and wrote nothing — which is D55a in reverse (a selector
      that matches nothing fails silently) and exactly why every scripted edit here carries an
      assertion. Without it the deploy would have reported success and changed nothing.
   ⭐ THE WEB CARD WAS ALWAYS RIGHT: .tk-foot (:796) draws the same two marks on #f4f1ec, which
     is why the identical assets read perfectly there — 13.9:1. One pair of images, two surfaces,
     and only one of them supplied the background they were drawn for. The ground is the fix. */
.wlp-g-band{display:flex;align-items:center;justify-content:center;gap:.7rem;flex-wrap:wrap;
  background:#f4f1ec;border-radius:.4rem;padding:.45rem .6rem}
.wlp-g-band img{max-height:20px;width:auto;display:block}
/* ⛔ LEFT-JUSTIFIED BECAUSE OF `margin:0`. A 9.5rem max-width box inside a flex COLUMN stretches
   to the cross axis by default, and capping the width without an auto margin pins it to the start
   edge — so both wallet QRs sat hard left while every real wallet card centres the code. I wrote
   margin:0 when I removed the old margin-top:auto (which had pinned the QR to the bottom and drew
   a void above it); removing one bad margin and replacing it with another is how this survived.
   ⭐ Jeremy: "the QR sections of the Wallet passes are left justified i think they should be
     center by design". */
.wlp-qr{background:#fff;border-radius:.5rem;padding:.45rem;max-width:9.5rem;
  margin:0 auto;order:3;align-self:center}
.wlp-qr svg{width:100%;height:auto;display:block}
.wlp-meta{margin:.4rem 0 0;font-size:.64rem;opacity:.75;font-variant-numeric:tabular-nums;order:4}
.wlp-raw{display:block;font:500 .55rem/1.3 var(--f-mono,monospace);opacity:.6;margin-top:.1rem}
.wlp-err{margin:.6rem;font-size:.78rem;color:#b3261e}

/* one surface at a time on demand; below 60rem "All three" is not offered */
.wlp-surfaces[data-show="web"]    .wlp-surface:not([data-s="web"]),
.wlp-surfaces[data-show="apple"]  .wlp-surface:not([data-s="apple"]),
.wlp-surfaces[data-show="google"] .wlp-surface:not([data-s="google"]),
.wlp-surfaces[data-show="email"]  .wlp-surface:not([data-s="email"]){display:none}
/* ⭐ 252e — the email figure is only ever shown by its own tab */
.wlp-surfaces[data-show="all"] .wlp-surface[data-s="email"]{display:none}
.wlp-surfaces:not([data-show="all"]){grid-template-columns:minmax(0,23rem);justify-content:center}
.wlp-seg{display:flex;gap:.3rem;flex-wrap:wrap;justify-content:center;margin:.9rem 0 1rem}
.wlp-seg button{font-weight:600;font-size:.8rem;line-height:1;padding:.45rem .8rem;border-radius:999px;cursor:pointer;
  border:1px solid var(--line);background:#f5f3f3;color:var(--body);min-height:32px}
.wlp-seg [aria-selected="true"]{background:var(--accent);border-color:var(--accent);color:#fff}

/* ── THE LEDGER: one cell per lockable element, and the cell IS that element's control ─────
   ⭐⭐ THE POINT OF THE WHOLE REDESIGN. Mark, Banner, the size guide and Color used to be four
      blocks ABOVE a preview of the same four things, so you could change an input without ever
      seeing what it did — which is exactly how the branding was got wrong three times in one
      afternoon on 2026-09-12. Lock + specimen + control are now one object. */
.wlp-ledger{display:grid;grid-template-columns:1fr 1fr;gap:.6rem}
.wlp-cell{display:grid;grid-template-columns:auto minmax(0,1fr) auto;
  grid-template-areas:"el t lock" "ctl ctl ctl";align-items:center;gap:.35rem .65rem;
  padding:.6rem .7rem;border-radius:10px;border:1px solid var(--line);background:#fff}
.wlp-cell.is-locked{background:#fbf1f2;border-color:#e6c9cc}
.wlp-cell.is-open{border-style:dashed;border-color:#cec7c7}
.wlp-cell .wlp-el{grid-area:el;width:36px;height:36px}
.wlp-cell-t{grid-area:t;min-width:0;line-height:1.25}
.wlp-cell-t b{display:block;color:var(--ink);font-size:.9rem}
.wlp-cell-t small{display:block;font-size:.76rem;color:#6d6566;white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis}

/* the owner's switch — a knob and a WORD */
.wlp-lock{grid-area:lock;display:inline-flex;align-items:center;gap:.4rem;font-weight:600;font-size:.78rem;line-height:1;
  padding:.3rem .55rem .3rem .3rem;border-radius:999px;cursor:pointer;color:var(--accent);
  border:1px solid #e0c4c7;background:#fff;min-height:32px}
.wlp-lock-k{width:26px;height:16px;border-radius:8px;background:#d9cfc6;position:relative;
  transition:background .12s;flex:none}
.wlp-lock-k::after{content:"";position:absolute;top:2px;left:2px;width:12px;height:12px;
  border-radius:50%;background:#fff;transition:transform .12s}
.wlp-lock[aria-checked="true"] .wlp-lock-k{background:var(--accent)}
.wlp-lock[aria-checked="true"] .wlp-lock-k::after{transform:translateX(10px)}
.wlp-lock:focus-visible{outline:2px solid var(--focus);outline-offset:2px}

/* ⛔⛔ THE PARTNER'S HALF, AND IT IS D83/D87/D90 APPLIED: hiding a control is only HALF the fix.
   Where the owner gets a switch, the partner gets WHO HOLDS THE LOCK and a way to ask them, so
   a locked cell never dead-ends. ⚠️ Three of six partner orgs have NO users at all, so the
   person degrades to the organisation — the fallback event.php:266 already uses. */
.wlp-held{grid-area:lock;font-size:.76rem;color:var(--body);text-align:right;line-height:1.3;
  max-width:12rem}
.wlp-held b{color:var(--accent);display:block}
.wlp-held a{color:var(--accent);text-underline-offset:2px}
.wlp-held.is-yours{color:var(--ok);font-weight:600}

.wlp-ctl{grid-area:ctl;display:flex;align-items:center;gap:.5rem;font-size:.78rem;
  color:#6d6566;margin-top:.15rem}
.wlp-ctl select,.wlp-ctl input[type="color"]{font:inherit;font-size:.86rem;flex:1 1 auto;
  min-width:0;padding:.35rem .5rem;border:1px solid #dbd5d6;border-radius:8px;background:#fff}
.wlp-ctl input[type="color"]{flex:0 0 2.6rem;height:2rem;padding:.1rem}
/* belt and braces ONLY — CSS is not a permission. The server does not render the control, and
   api/pevent.php refuses the write (§5: the screen and the server use ONE predicate). */
.wlp[data-role="partner"] .wlp-cell.is-locked .wlp-ctl{display:none}

/* ── hover a cell → outline that element on all three surfaces ──────────────────────────────
   ⭐ The artwork is never marked up: two data attributes on the root, and the rules TARGET the
      existing classes without changing any property. Nothing is drawn when nobody is asking,
      so what gets judged is the design and not the annotation. */
.wlp[data-hl="logo"] :is(.tk-lockup,.wlp-apple-logo,.wlp-g-logo,.wlp-g-wide),
.wlp[data-hl="strip"] :is(.tk-foot,.wlp-apple-strip,.wlp-g-hero),
.wlp[data-hl="colour"] :is(.tk-band,.wlp-apple,.wlp-google),
.wlp[data-hl="text"] :is(.tk-band,.wlp-apple-primary,.wlp-g-name)
  {outline:3px solid var(--accent);outline-offset:3px;border-radius:4px}
.wlp[data-hl-open="1"] :is(.tk-lockup,.wlp-apple-logo,.wlp-g-logo,.wlp-g-wide,.tk-foot,
  .wlp-apple-strip,.wlp-g-hero,.tk-band,.wlp-apple,.wlp-google,.wlp-apple-primary,.wlp-g-name)
  {outline-style:dashed}
.wlp[data-hl] .wlp-screen{overflow:visible}

/* ── the status line. ⛔ NOT A PUBLISH BUTTON, AND THAT IS DELIBERATE: brand.js records
   save-on-change as a decision — "a Save button would add a state where the screen and the pass
   disagree, and this panel's whole job is to show what the pass will look like". There is no
   draft, so "Publish" would name a transaction that already happened. These four states
   describe what the save path actually does. */
.wlp-act{display:flex;align-items:center;gap:.6rem;flex-wrap:wrap;margin:1rem -.9rem -.6rem;
  padding:.7rem .9rem max(.7rem,env(safe-area-inset-bottom));background:#faf8f8;
  border-top:1px solid var(--line);border-radius:0 0 10px 10px}
.wlp-state{flex:1 1 12rem;display:inline-flex;align-items:center;gap:.45rem;font-size:.84rem;
  color:#6d6566}
.wlp-state i{width:.55rem;height:.55rem;border-radius:50%;background:var(--ok);flex:none}
.wlp-act[data-state="busy"] .wlp-state i{background:var(--warn);animation:wlp-pulse 1s ease-in-out infinite}
.wlp-act[data-state="done"] .wlp-state{color:var(--ok);font-weight:600}
.wlp-act[data-state="failed"] .wlp-state{color:var(--danger);font-weight:600}
.wlp-act[data-state="failed"] .wlp-state i{background:var(--danger)}
@keyframes wlp-pulse{50%{opacity:.35}}

@media (max-width:60rem){
  .wlp-seg [data-s="all"]{display:none}
  .wlp-surfaces[data-show="all"]{grid-template-columns:minmax(0,23rem);justify-content:center}
  .wlp-surfaces[data-show="all"] .wlp-surface:not([data-s="web"]){display:none}
}
@media (max-width:30rem){
  .wlp-ledger{grid-template-columns:1fr}
  .wlp-cell{grid-template-areas:"el t" "lock lock" "ctl ctl";
    grid-template-columns:auto minmax(0,1fr)}
  .wlp-held,.wlp-lock{justify-self:start;text-align:left;max-width:none}
  .wlp-bar{gap:.5rem}
}

/* ⭐ ITEM 257 — the roster's type-to-find box. Sits in .headbtns beside "+ Add guest", NOT in the
   .arrfilter bar: that bar is only drawn once somebody has arrived, and the search has to exist on
   an evening when nobody has. Borrowed from .orgfind input (bears.css:1168) rather than invented,
   so it matches the search box people already use on the partners screen. */
.rosterfind{flex:0 1 14rem;min-width:9rem;padding:.45rem .6rem;font:inherit;font-size:.86rem;
  border:1px solid var(--line);border-radius:8px;background:#fff;color:var(--ink)}
.rosterfind::placeholder{color:#a19798}
.rosterfind:focus{outline:2px solid var(--maroon);outline-offset:1px;border-color:var(--maroon)}
/* ⛔ 44px MINIMUM ON A TOUCH TARGET AT THE GATE — item 192c: the door picker shipped as a 19px
   unstyled control and was unusable on a phone in a queue. This is the same audience. */
@media (max-width:640px){
  .rosterfind{flex:1 1 100%;min-height:44px;font-size:16px}
}

/* ═══════════════════════════════════════════════════════════════════════════════════════════
   THE PASS DESIGNER — ITEM 252. Prefix .bp- (brand panel).

   ⭐⭐ THE ONE IDEA: every candidate in a picker is drawn INSIDE THE FRAME THE SLOT WILL
      ACTUALLY OCCUPY — 160x50 on the card's cream, the same 160x50 on Apple's own maroon
      ground, 375x98 with the guest name drawn over it, the 19:1 footer sliver, Google's circle
      with its 15% inset, the 1032x812 hero crop. A wrong-shape asset therefore LOOKS WRONG
      BEFORE ANYBODY CHOOSES IT, which is the exact class of mistake that happened three times
      on 2026-09-12 and was each time found hours later by a person holding a phone.
      ⛔ `logo` and `applelogo` are DELIBERATELY TWO SHAPES at the same aspect ratio: the web
         logo sits on cream, Apple composites its logo straight onto the pass background. A
         navy mark that reads fine on cream vanishes on maroon, so one shared frame would hide
         the failure this panel exists to expose.

   ⛔ WHAT WAS REMOVED FROM THE DESIGN BEFORE IT LANDED HERE, and why:
      · `.chip` / `.chip:hover` / `.chip.sm` / `.chip:focus-visible` — .chip ALREADY has three
        rules in this file and is used by four other screens. Appending the designer's version
        would have silently restyled every chip in the app, including the schedule filter bar.
        ⚠️ My own collision check missed it because it only tested `.bp-*` names — a probe that
           could not see the region containing the answer.
      · `.mock` / `.evbrand-rule` — scaffolding that exists only in the standalone mock.
      · the `mast` / `feat` / `foot` frames — those are the EMAIL group, and `img_email_*` does
        not exist yet (measured: 0 such columns). Shipping them would be a screen claiming a
        behaviour the code cannot perform. They arrive with 252b.

   ⭐ CUSTOM PROPERTIES: --bg-a, --sw-bg, --sw-fg (and --em-* with 252b) are set INLINE per
      instance by the view, exactly as pass_preview.php:304 already sets --bg-a/--fg-a. They are
      absent from :root ON PURPOSE. --hl-style is self-contained: used as var(--hl-style,solid)
      and flipped to dashed by .bp[data-hl-open="1"] below.
   ═══════════════════════════════════════════════════════════════════════════════════════════ */
   NEW — the designer. Prefix .bp- (brand panel). Everything below ships.
   ═══════════════════════════════════════════════════════════════════════════════════════════ */
.bp{margin:1.25rem 0 .25rem;padding-top:1rem;border-top:1px solid var(--line)}

/* 1 · the collapsed bar's 3x3 lock grid — three rows = Web / Apple / Google, three cells = the
   slots in reading order. Filled = locked, dashed = open, faint = empty. The WORDS beside it
   carry the count, so colour is never the sole carrier. */
/* ⚠️ 252e — ONE ROW PER SURFACE, NOT A 3x3 GRID. Apple has five spots now (icon and thumbnail were
   added), so a fixed three-column grid would have wrapped Apple's last two cells onto Google's row
   and every row below would have read as the wrong surface. */
.bp-grid{display:flex;flex-direction:column;gap:3px;flex:none}
.bp-gr{display:flex;gap:3px}
.bp-grid i{display:block;width:11px;height:11px;box-sizing:border-box;border-radius:2px;border:1.5px solid var(--maroon);background:var(--maroon)}
.bp-grid i.is-open{background:transparent;border-style:dashed;border-color:#b8a9ab}
.bp-grid i.is-empty{background:transparent;border-color:#e6e2e2}

/* 2 · surface tabs carry their own lock count */
/* each tab carries its three cells in slot order (logo · band · footer), so reading DOWN the tab
   strip shows one role across every surface — the by-role view without a by-role layout */
.bp-cells{display:inline-grid;grid-auto-flow:column;grid-auto-columns:8px;gap:2px;vertical-align:middle}
.bp-cells i{display:block;height:8px;border-radius:1.5px;border:1.5px solid currentColor;background:currentColor}
.bp-cells i.is-open{background:transparent;border-style:dashed;opacity:.7}
.bp-cells i.is-empty{opacity:.35}
.bp-tabs button .bp-n{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
.bp-tabs button{position:relative}
.bp-also{display:flex;align-items:center;gap:.35rem;flex-wrap:wrap;flex:1 1 100%;margin-top:.35rem}
.bp-also > span{font-size:.76rem;color:#6d6566;margin-right:.1rem}

/* 3 · the stage: specimen + rows. Two columns from 60rem; stacked below it. */
.bp-stage{display:grid;gap:1.1rem;align-items:start}
@media (min-width:60rem){.bp-stage{grid-template-columns:minmax(0,23rem) minmax(0,1fr)}}
.bp-surface{margin:0;min-width:0}
.bp-surface figcaption{font-weight:700;font-size:.7rem;line-height:1.3;letter-spacing:.06em;text-transform:uppercase;color:#6d6566;margin:0 0 .4rem;display:flex;align-items:center;flex-wrap:wrap;gap:.2rem .35rem}
.bp-surface figcaption span{font-weight:500;letter-spacing:0;text-transform:none}
.bp-surface[hidden]{display:none}

/* ⭐ CLICKABLE ARTWORK. The elements that ARE a slot get a pointer and a soft hover outline —
   the artwork is not marked up; the rules target the real classes exactly as data-hl does. */
.bp [data-hot]{cursor:pointer}
.bp [data-hot]:hover{outline:2px solid var(--maroon);outline-offset:3px;border-radius:4px}
.bp[data-hl="web.logo"]   .tk-lockup,
.bp[data-hl="web.band"]   .tk-foot,
.bp[data-hl="web.wordmark"] .tk-vip,
.bp[data-hl="web.colour"] .tk-band,
.bp[data-hl="apple.logo"] .wlp-apple-logo,
.bp[data-hl="apple.strip"] .wlp-apple-strip,
.bp[data-hl="apple.footer"] .wlp-apple-footer,
.bp[data-hl="apple.colour"] .wlp-apple,
.bp[data-hl="google.logo"] .wlp-g-logo,
.bp[data-hl="google.hero"] .wlp-g-hero,
.bp[data-hl="google.wide"] .wlp-g-wide,
.bp[data-hl="google.colour"] .wlp-google{outline:3px var(--hl-style,solid) var(--maroon);outline-offset:3px;border-radius:4px}
.bp[data-hl-open="1"]{--hl-style:dashed}
.bp[data-hl] .wlp-screen{overflow:visible}

/* 4 · THE ROWS — one shape for an image slot and for the colours. Thumb · name+state · lock. */
.bp-rows{display:flex;flex-direction:column;gap:.5rem;min-width:0}
.bp-row{border:1px solid var(--line);border-radius:10px;background:#fff;min-width:0}
.bp-row.is-locked{background:#fbf1f2;border-color:#e6c9cc}
.bp-row.is-open{border-style:dashed;border-color:#cec7c7}
.bp-row.is-editing{border-style:solid;border-color:var(--maroon);box-shadow:0 0 0 3px rgba(94,0,9,.09)}
.bp-head{display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:.7rem;padding:.55rem .7rem;width:100%;font:inherit;text-align:left;color:inherit;background:none;border:0;border-radius:10px;cursor:pointer}
.bp-head:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
div.bp-head{cursor:default}
.bp-thumb{position:relative;width:84px;height:34px;flex:none;border-radius:6px;overflow:hidden;display:block;
  background-color:#fff;border:1px solid rgba(0,0,0,.12);
  background-image:linear-gradient(45deg,#eee 25%,transparent 25%,transparent 75%,#eee 75%),linear-gradient(45deg,#eee 25%,transparent 25%,transparent 75%,#eee 75%);
  background-size:10px 10px;background-position:0 0,5px 5px}
.bp-thumb img{position:absolute;inset:0;width:100%;height:100%;object-fit:contain;display:block;padding:3px}
.bp-thumb.is-circle{width:34px;height:34px;border-radius:50%;background-image:none}
.bp-thumb.is-circle img{inset:15%;width:70%;height:70%;padding:0}
.bp-thumb.is-empty::after{content:"\2014";color:#aca1a2;font-weight:600;position:absolute;inset:0;display:grid;place-items:center}
.bp-thumb.is-swatch{display:grid;place-items:center;background-image:none;background:var(--sw-bg,#5E0009);color:var(--sw-fg,#fff);font-weight:700;font-size:.85rem;line-height:1}
.bp-t{min-width:0;line-height:1.25}
.bp-t b{display:block;color:var(--ink);font-size:.9rem}
.bp-t small{display:block;font-size:.76rem;color:#6d6566;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.bp-t small .bp-size{color:#a19798}
.bp-row.is-editing .bp-t small{white-space:normal}
.bp-state{justify-self:end}

/* 5 · THE PICKER — the row opened. Every candidate is drawn INSIDE the slot's own frame. */
.bp-pick{padding:.2rem .7rem .8rem;border-top:1px dashed var(--line);margin-top:0}
.bp-pick-h{display:flex;align-items:baseline;justify-content:space-between;gap:.6rem;flex-wrap:wrap;margin:.55rem 0 .5rem}
.bp-pick-h b{font-size:.82rem;color:var(--ink)}
.bp-pick-h span{font-size:.76rem;color:#6d6566}
.bp-opts{display:grid;gap:.5rem;list-style:none;margin:0;padding:0}
.bp-opts[data-shape="logo"],.bp-opts[data-shape="wordmark"]{grid-template-columns:repeat(auto-fill,minmax(112px,1fr))}
.bp-opts[data-shape="circle"]{grid-template-columns:repeat(auto-fill,minmax(88px,1fr))}
.bp-opts[data-shape="band"],.bp-opts[data-shape="strip"],.bp-opts[data-shape="wide"]{grid-template-columns:repeat(auto-fill,minmax(180px,1fr))}
.bp-opts[data-shape="footer"]{grid-template-columns:1fr}
.bp-opts[data-shape="hero"]{grid-template-columns:repeat(auto-fill,minmax(150px,1fr))}
.bp-opt{display:flex;flex-direction:column;gap:.3rem;padding:.35rem;border:1.5px solid var(--line);border-radius:9px;background:#fff;cursor:pointer;font:inherit;text-align:left;color:inherit;width:100%}
.bp-opt:hover{border-color:#d3cccd}
.bp-opt:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
.bp-opt[aria-checked="true"]{border-color:var(--maroon);box-shadow:0 0 0 2px rgba(94,0,9,.12)}
.bp-opt[aria-checked="true"] .bp-opt-n::before{content:"\2713  ";color:var(--maroon);font-weight:700}
.bp-opt-n{font-size:.74rem;color:var(--body);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.bp-opt-n small{color:#a19798}
/* the frame: the slot's own aspect and ground */
.bp-frame{position:relative;width:100%;overflow:hidden;border-radius:5px;display:grid;place-items:center;
  background-color:#fff;
  background-image:linear-gradient(45deg,#eee 25%,transparent 25%,transparent 75%,#eee 75%),linear-gradient(45deg,#eee 25%,transparent 25%,transparent 75%,#eee 75%);
  background-size:10px 10px;background-position:0 0,5px 5px}
.bp-frame img{position:absolute;inset:0;width:100%;height:100%;object-fit:contain;display:block}
.bp-frame[data-shape="logo"]{aspect-ratio:160/50;background:#fffdfa;background-image:none}
.bp-frame[data-shape="applelogo"]{aspect-ratio:160/50;background:var(--bg-a,#5E0009);background-image:none}
.bp-frame[data-shape="wordmark"]{aspect-ratio:3/1;background:#fffdfa;background-image:none}
.bp-frame[data-shape="band"]{aspect-ratio:5/1;background:#f4f1ec;background-image:none}
.bp-frame[data-shape="band"] img{padding:.15rem .5rem}
.bp-frame[data-shape="strip"]{aspect-ratio:375/98;background:#f4f1ec;background-image:none}
.bp-frame[data-shape="strip"] img{padding:.2rem .4rem .2rem 5.2rem;object-position:right center}
.bp-frame[data-shape="strip"] .bp-over{position:absolute;left:.4rem;bottom:.25rem;color:#181515;line-height:1.1}
.bp-frame[data-shape="strip"] .bp-over em{display:block;font-style:normal;font-size:.5rem;letter-spacing:.08em;text-transform:uppercase;opacity:.65}
.bp-frame[data-shape="strip"] .bp-over strong{font-size:.85rem}
.bp-frame[data-shape="footer"]{aspect-ratio:286/15;min-height:18px;background:#f4f1ec;background-image:none}
.bp-frame[data-shape="footer"] img{padding:1px 0}
.bp-frame[data-shape="circle"]{aspect-ratio:1;border-radius:50%;width:64px;justify-self:center;background:#fff;background-image:none}
.bp-frame[data-shape="circle"] img{inset:15%;width:70%;height:70%}
.bp-frame[data-shape="hero"]{aspect-ratio:1032/812;background:#fff;background-image:none}
.bp-frame[data-shape="hero"] img{width:100%;height:100%;object-fit:cover}
.bp-frame[data-shape="wide"]{aspect-ratio:16/5;background:#fff;background-image:none}
/* ⭐ THE EMAIL FRAMES — restored with db/045. They were deliberately withheld when the .bp-
   block first landed, because img_email_* did not exist and a picker row for a column that is
   not there is exactly the thing this repo's top rule forbids. The columns exist now.
   ⛔ The masthead frame paints --em-bg because the email header is a COLOURED band and a mark is
      judged against that ground, not against white — mtc-horizontal.png is dark ink and would
      look fine on white while being invisible on maroon. */
.bp-frame[data-shape="mast"]{aspect-ratio:4/1;background:var(--em-bg,#5E0009)}
.bp-frame[data-shape="mast"] img{padding:.3rem 25%}
.bp-frame[data-shape="feat"]{aspect-ratio:5/1;background:#f8f7f7}
.bp-frame[data-shape="feat"] img{padding:.35rem 1rem}
.bp-frame[data-shape="foot"]{aspect-ratio:4/1;background:#fafafa}
.bp-frame[data-shape="foot"] img{padding:.35rem 30%}
.bp-frame[data-shape="none"]{background:#faf8f8;background-image:none;border:1px dashed #cec7c7;color:#aca1a2;font-size:.78rem;font-weight:600}
.bp-frame.is-bad::after{content:attr(data-why);position:absolute;right:.3rem;top:.25rem;font-weight:700;font-size:.58rem;line-height:1;letter-spacing:.06em;text-transform:uppercase;color:#fff;background:var(--warn);padding:.15rem .3rem;border-radius:3px}
.bp-pick-foot{display:flex;align-items:center;justify-content:space-between;gap:.6rem;flex-wrap:wrap;margin:.7rem 0 0;font-size:.78rem;color:#6d6566}
.bp-pick-foot a{font-weight:600}

/* 5b · the email specimen. In production this is the real template rendered with the preview
   ticket into an <iframe srcdoc> — INCLUDED, never copied — exactly as pass_card.php is. */
.bp-email{font-size:.8rem;line-height:1.45;color:#423b3b;background:#fff}
.bp-em-mast{background:var(--em-bg,#5E0009);color:var(--em-fg,#fff);padding:14px 16px;display:flex;justify-content:center;min-height:56px}
.bp-em-mastimg{height:40px;width:auto;max-width:70%;object-fit:contain;display:block}
.bp-em-body{padding:.9rem 1rem .6rem;display:flex;flex-direction:column;gap:.55rem;align-items:flex-start}
.bp-em-body p{margin:0}
.bp-em-h{font-size:1rem;font-weight:700;color:var(--ink)}
.bp-em-box{align-self:stretch;background:#fafafa;border:1px solid #e6e2e2;border-radius:8px;padding:.5rem .65rem;display:flex;flex-direction:column;gap:.1rem}
.bp-em-box b{color:var(--ink)}  .bp-em-box span{font-size:.74rem;color:#6d6566}
.bp-em-cta{display:inline-block;background:var(--em-bg,#5E0009);color:var(--em-fg,#fff);font-weight:600;padding:.45rem .9rem;border-radius:8px}
.bp-em-feature{align-self:stretch;background:#f8f7f7;border-radius:8px;padding:.5rem .8rem;display:flex;justify-content:center;min-height:36px}
.bp-em-featimg{height:26px;width:auto;max-width:100%;object-fit:contain;display:block}
.bp-em-foot{background:#fafafa;border-top:1px solid #e6e2e2;padding:.7rem 1rem;font-size:.7rem;color:#6d6566;display:flex;align-items:center;gap:.6rem;flex-wrap:wrap}
.bp-em-footimg{height:22px;width:auto;max-width:40%;object-fit:contain;display:block}
.bp-thumb.is-words{display:grid;place-items:center;background-image:none;background:#f5f3f3;color:#6d6566;font-weight:700;font-size:1.1rem}
.bp-row.is-fixed{border-color:var(--line);background:#fcfbfb}
.bp-words{margin:0;display:flex;flex-direction:column;gap:.35rem}
.bp-words div{display:grid;grid-template-columns:5.5rem minmax(0,1fr);gap:.5rem;font-size:.8rem;padding:.3rem 0;border-bottom:1px dashed var(--line)}
.bp-words div:last-child{border-bottom:0}
.bp-words dt{font-weight:600;color:var(--ink)}  .bp-words dd{margin:0;color:var(--body)}

/* 6 · colours, opened: two swatch lines of the same shape, each with its own lock */
.bp-colours{display:flex;flex-direction:column;gap:.45rem;margin:.55rem 0 0}
.bp-col{display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:.6rem;padding:.35rem .4rem;border-radius:8px;background:#faf8f8}
.bp-col input[type="color"]{width:3rem;height:2rem;padding:.1rem;border:1px solid var(--line);border-radius:6px;background:none;cursor:pointer}
.bp-col input[type="color"]:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
.bp-col input[type="color"]:disabled{cursor:default;opacity:.7}
.bp-col b{display:block;font-size:.84rem;color:var(--ink)}
.bp-col small{display:block;font-size:.74rem;color:#6d6566;font-variant-numeric:tabular-nums}
.bp-contrast{font-weight:600}
.bp-contrast.is-low{color:var(--danger)}

/* 7 · role: the server renders ONE of these; the mock renders both and hides one. */
.bp[data-role="owner"] .bp-partner,.bp[data-role="partner"] .bp-owner{display:none}
.bp[data-role="partner"] .bp-row.is-locked .bp-head{cursor:default}

@media (max-width:30rem){
  .bp-head{grid-template-columns:auto minmax(0,1fr);grid-template-areas:"th t" "st st"}
  .bp-thumb{grid-area:th}.bp-t{grid-area:t}
  .bp-state{grid-area:st;justify-self:start;padding-left:calc(84px + .7rem)}
  .bp-state .wlp-held{text-align:left;max-width:none}
  .bp-thumb.is-circle + .bp-t + .bp-state{padding-left:calc(34px + .7rem)}
  .wlp-bar{gap:.4rem .6rem;grid-template-columns:auto minmax(0,1fr)}
  .wlp-sum strong{white-space:normal;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
  .wlp-cta{grid-column:2;justify-self:end}
}

/* mock-only chrome */

/* ══ 252e · the specimen spots the renderers gained ══════════════════════════════════════════ */
.wlp-apple-thumb{float:right;width:4.2rem;height:4.2rem;margin:-.2rem 0 .2rem .5rem}
.wlp-apple-thumb img{width:100%;height:100%;object-fit:contain;display:block}
.wlp-apple-foot{clear:both;margin:.5rem 0 .2rem}
.wlp-apple-foot img{display:block;width:100%;max-height:1.1rem;object-fit:contain}
.wlp-g-wide{margin:-.2rem 0 .3rem}
.wlp-g-wide img{display:block;width:100%;max-height:3.4rem;object-fit:contain;object-position:left center}
.wlp-email{background:#fff;border-radius:.6rem;overflow:hidden;border:1px solid var(--line);color:#423b3b;font-size:.8rem}
.wlp-e-mast{display:flex;justify-content:center;align-items:center;padding:.8rem;min-height:2.6rem}
.wlp-e-mast img{display:block;max-height:2.2rem;max-width:70%;object-fit:contain}
.wlp-e-body{padding:.8rem .9rem 1rem}
.wlp-e-body > b{display:block;text-align:center;color:#181515;font-size:.95rem;margin-bottom:.4rem}
.wlp-e-body p{margin:0 0 .6rem}
.wlp-e-body img{max-width:60% !important;margin:.4rem auto .8rem !important}
.wlp-e-sign{border-top:1px solid var(--line);padding-top:.5rem}
.bp-frame[data-shape="icon"],.bp-frame[data-shape="thumb"]{aspect-ratio:1;width:64px;justify-self:center;background:var(--bg-a,#5E0009);background-image:none}
.bp-opts[data-shape="icon"],.bp-opts[data-shape="thumb"]{grid-template-columns:repeat(auto-fill,minmax(88px,1fr))}

/* ══ 252d · the wording editor inside the Words row ════════════════════════════════════════ */
.bp-copy{display:flex;flex-direction:column;gap:.3rem;margin:0 0 .85rem}
.bp-copy label{display:flex;flex-direction:column;gap:.1rem;font-size:.85rem;color:var(--ink)}
.bp-copy label small{font-size:.74rem;color:#6d6566;font-weight:400}
.bp-copy textarea{width:100%;box-sizing:border-box;min-height:4.2rem;padding:.55rem .65rem;border:1px solid var(--line);
  border-radius:.45rem;font-family:inherit;font-size:.9rem;line-height:1.45;color:var(--body);background:#fff;resize:vertical}
.bp-copy textarea:focus{outline:2px solid var(--maroon);outline-offset:2px}
.bp-copy textarea[readonly]{background:#f7f5f5;color:#6e6566}
.bp-copy-reset{align-self:flex-start;font-size:.78rem}
.bp-copy-vars{font-size:.76rem;color:#6d6566;margin:.1rem 0 .6rem;line-height:1.6}
.bp-copy-vars code{font-size:.74rem;background:#f5f3f3;border-radius:3px;padding:0 .25rem;color:var(--ink)}
@media (max-width:480px){ .bp-copy textarea{font-size:16px} }
.bp-col-held{font-size:.8rem;color:#6d6566;margin:.2rem 0 .5rem}

/* ══ 260 · the Arrived list sits in the same frame as Attending, shorter ═══════════════════ */
.rosterframe.is-arrivals{max-height:min(40svh,22rem)}
.rosterframe.is-arrivals .roster td{padding-top:.45rem;padding-bottom:.45rem}
.rosterframe.is-arrivals .arr-when{white-space:nowrap}
.rosterframe.is-arrivals .arr-when .gsub{display:inline;margin-left:.35rem}
@media (max-width:640px){
  .rosterframe.is-arrivals .roster{min-width:0}
  .rosterframe.is-arrivals .arr-when{white-space:normal}
  .rosterframe.is-arrivals .arr-when .gsub{display:block;margin-left:0}
}

/* ══ 261 · the Attending toolbar on ONE line, easier to see ═══════════════════════════════════
   Jeremy, 2026-09-13: Will-call sheet, Email this group, + Add guest, Import CSV and Find a name
   "should only take up one line and be visibly easier to see". Measured before: Import CSV wrapped to
   a second row, and the controls were three different heights (46 / 46 / 36px) and two font sizes —
   a <button> does not inherit the page font, an <a class="btn"> does.
   ⭐ One height, one font, no wrapping on a desktop; the search grows into the space that is left and
   carries a magnifier so it reads as a search at a glance. Phones still wrap (a 44px search row). */
.headbtns.rostertools{flex-wrap:nowrap;align-items:center;gap:.5rem;flex:1 1 auto;justify-content:flex-end;min-width:0}
.rostertools .btn{display:inline-flex;align-items:center;height:2.6rem;padding:0 1rem;box-sizing:border-box;
  font:inherit;font-size:.93rem;font-weight:600;line-height:1;white-space:nowrap}
.rostertools .rosterfind{flex:1 1 15rem;min-width:11rem;max-width:22rem;height:2.6rem;box-sizing:border-box;
  font-size:.95rem;border:1.5px solid #cdbfc1;border-radius:9px;padding:0 .7rem 0 2.1rem;
  background:#fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='none' stroke='%235E0009' stroke-width='2' stroke-linecap='round'%3E%3Ccircle cx='7' cy='7' r='5'/%3E%3Cpath d='m11 11 3.5 3.5'/%3E%3C/svg%3E") no-repeat .7rem center}
.rostertools .rosterfind::placeholder{color:#8c8283}
.rostertools .arrcount:empty{display:none}
.rostertools .arrcount{white-space:nowrap;font-size:.85rem}
@media (max-width:900px){
  .headbtns.rostertools{flex-wrap:wrap;justify-content:flex-start}
  .rostertools .rosterfind{max-width:none}
}
@media (max-width:640px){
  .rostertools .rosterfind{flex:1 1 100%;height:44px;font-size:16px}
}
/* 259 — Setup: which sport's pass the designer below is showing */
.designpick{display:flex;flex-wrap:wrap;align-items:center;gap:.5rem .75rem;margin:1.2rem 0 .6rem;scroll-margin-top:6.5rem}
.designpick-lbl{font-weight:700;color:var(--ink,#1d1718)}
.designpick-chips{display:flex;flex-wrap:wrap;gap:.4rem}
.designpick a.chip{text-decoration:none;min-height:36px}
.designpick a.chip:focus-visible{outline:2px solid var(--maroon);outline-offset:2px}
.designpick-note{color:var(--muted,#6f6667);font-size:.86rem}
/* 135 — Guests this season: numbers never wrap; a date leads its occasion on one line; the table scrolls sideways
   inside its frame on a narrow screen instead of squeezing every column */
#guestTable{min-width:52rem}
#guestTable td.num,#guestTable th.num{white-space:nowrap}
#guestTable td.gh-when{white-space:nowrap}
#guestTable .gh-d{font-variant-numeric:tabular-nums;color:var(--muted,#6f6667);margin-right:.25rem}
#sec-guests .panelhead{flex-wrap:wrap;gap:.6rem}
/* 277 — Branding: one card per brand (logo · name · asset count) → click for that brand's assets */
.brandgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(13.5rem,1fr));gap:1rem;margin:.4rem 0 1.4rem}
.brandcard{display:flex;flex-direction:column;align-items:center;gap:.55rem;padding:1.2rem 1rem 1rem;
  background:var(--card);border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow);
  text-decoration:none;color:var(--ink);text-align:center;transition:border-color .15s,box-shadow .15s,transform .15s}
.brandcard:hover{border-color:var(--maroon);box-shadow:0 6px 18px rgba(94,0,9,.12);transform:translateY(-2px)}
.brandcard:focus-visible{outline:2px solid var(--maroon);outline-offset:2px}
.brandcard-logo{display:flex;align-items:center;justify-content:center;width:100%;height:6.5rem;border-radius:10px;
  background:#f5f3f3 repeating-conic-gradient(#ece8e8 0 25%,#f7f5f5 0 50%) 0 0/16px 16px}
.brandcard-logo img{max-width:85%;max-height:5.6rem;object-fit:contain}
.brandcard-logo.is-general img{max-height:4.2rem}
.brandcard-initial{font-size:2.4rem;font-weight:700;color:var(--maroon)}
.brandcard-name{font-weight:700;font-size:.98rem;line-height:1.25}
.brandcard-count{font-size:.84rem;color:var(--muted,#6f6667)}
.brandhead{display:flex;align-items:center;gap:1.1rem}
.brandhead .brandcard-logo{width:9rem;flex:none;height:6rem}
.brand-back{color:inherit;text-decoration:none;opacity:.7}
.brand-back:hover{opacity:1;text-decoration:underline}
@media (max-width:540px){ .brandhead{flex-direction:column;align-items:flex-start} .brandgrid{grid-template-columns:repeat(2,minmax(0,1fr))} }
/* 278 — Engagement: two views (tabs), headline tiles that link to their detail, and the By-occasion view */
.eg-top{display:flex;align-items:center;justify-content:space-between;gap:.8rem;flex-wrap:wrap;margin:-.2rem 0 1rem}
.eg-tabs{display:inline-flex;background:#efeaea;border-radius:999px;padding:.25rem;gap:.2rem}
.eg-tab{padding:.5rem 1.1rem;border-radius:999px;text-decoration:none;color:var(--body);font-weight:600;font-size:.92rem}
.eg-tab:hover{color:var(--maroon)}
.eg-tab.on{background:var(--maroon);color:#fff}
.eg-tab:focus-visible{outline:2px solid var(--maroon);outline-offset:2px}
.eg-tiles,.ov-tiles{display:grid;grid-template-columns:repeat(auto-fit,minmax(11rem,1fr));gap:.8rem;margin:0 0 1.2rem}
.eg-tile,.ov-tile{display:flex;flex-direction:column;gap:.25rem;padding:1rem 1.1rem;background:var(--card);border:1px solid var(--line);
  border-radius:var(--radius);box-shadow:var(--shadow);text-decoration:none;color:var(--ink)}
a.eg-tile:hover{border-color:var(--maroon);transform:translateY(-1px)}
.eg-n,.ov-n{font-size:1.8rem;font-weight:700;line-height:1.1;color:var(--maroon);font-variant-numeric:tabular-nums}
.eg-n small,.ov-n small{font-size:.95rem;color:var(--muted,#6f6667);font-weight:600}
.eg-l,.ov-l{font-size:.84rem;color:var(--body)}
.ov-tiles{margin:.2rem 0 0}
.ov-pick .ov-form{display:flex;align-items:center;gap:.7rem;flex-wrap:wrap}
.ov-lbl{font-weight:700}
.ov-pick select{font:inherit;font-size:.95rem;padding:.55rem .7rem;border:1.5px solid #cdbfc1;border-radius:9px;min-width:min(24rem,100%);background:#fff}
.ov-note{margin:.5rem 0 0;font-size:.84rem}
.eg-open{font-size:.78rem;margin-left:.35rem;opacity:.75}
/* 281–283 — Engagement rebuilt from the Fable 5.1 review: a hero with a subject, sentence tiles, bars inside rows, detail on
   demand. One maroon ramp; came = maroon, did-not = track grey (never red or green). Checked at 400px in Chrome. */
.eg-tiles,.ov-tiles{grid-template-columns:repeat(auto-fit,minmax(9rem,1fr))}
.eg-s{font-size:.78rem;color:var(--muted,#6f6667);line-height:1.35}
.eg-n-date{font-size:1.45rem}
.eg-hero{display:grid;grid-template-columns:auto 1fr auto;gap:1.2rem;align-items:start;background:var(--card);border:1px solid var(--line);
  border-left:5px solid var(--maroon);border-radius:var(--radius);box-shadow:var(--shadow);padding:1.2rem 1.3rem;margin:0 0 1.1rem}
.eg-hero-logo{width:5.5rem;height:5.5rem;object-fit:contain;border-radius:10px;background:#f5f3f3;padding:.4rem}
.eg-hero-body{grid-column:2;min-width:0}
.eg-hero:not(:has(.eg-hero-logo)) .eg-hero-body{grid-column:1 / 3}
.eg-hero-k{font-size:.74rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--maroon)}
.eg-hero h2{margin:.15rem 0 .35rem;font-size:1.45rem;line-height:1.2}
.eg-hero-s{margin:.2rem 0 .6rem;font-size:1rem;color:var(--body);line-height:1.45}
.eg-hero-s strong{color:var(--ink)}
.eg-hero-meta{margin:.55rem 0 0;font-size:.84rem;color:var(--muted,#6f6667)}
.eg-hero-links{display:flex;flex-wrap:wrap;gap:.4rem 1.2rem;margin:.7rem 0 0;font-size:.9rem}
.eg-hero-links a{color:var(--maroon);font-weight:600}
.eg-names{margin:.7rem 0 0}
.eg-names summary,.eg-more>summary,.eg-rowmore summary,.eg-diag summary{cursor:pointer;font-weight:600;color:var(--maroon);min-height:44px;
  display:flex;align-items:center;list-style:none}
.eg-names summary::-webkit-details-marker,.eg-more>summary::-webkit-details-marker,.eg-rowmore summary::-webkit-details-marker,.eg-diag summary::-webkit-details-marker{display:none}
.eg-names summary::before,.eg-more>summary::before,.eg-rowmore summary::before,.eg-diag summary::before{content:'';width:.45rem;height:.45rem;
  border-right:2px solid currentColor;border-bottom:2px solid currentColor;transform:rotate(-45deg);margin-right:.55rem;transition:transform .15s}
.eg-names[open] summary::before,.eg-more[open]>summary::before,.eg-rowmore[open] summary::before,.eg-diag[open] summary::before{transform:rotate(45deg)}
.eg-names summary:focus-visible,.eg-more>summary:focus-visible,.eg-rowmore summary:focus-visible,.eg-diag summary:focus-visible{outline:2px solid var(--maroon);outline-offset:2px;border-radius:6px}
.eg-names-cols{display:grid;grid-template-columns:1fr;gap:.6rem}
.eg-names-cols h3{margin:.2rem 0 .3rem;font-size:.86rem}
.eg-names-cols ul{margin:0;padding-left:1.1rem;font-size:.9rem;columns:11rem;column-gap:1.4rem}
.eg-next{border-left:1px solid var(--line);padding-left:1.2rem;min-width:13rem;max-width:17rem}
.eg-next-t{font-weight:700;margin:.15rem 0 .2rem}
.eg-next p{margin:0 0 .6rem;font-size:.88rem;color:var(--body)}
.eg-more{padding:.2rem 1.1rem .9rem}
.eg-more>summary{font-size:.98rem}
.eg-barcell{min-width:10rem}
.eg-bar{display:block;height:10px;border-radius:5px;background:#edeaea;overflow:hidden;margin:.15rem 0 .25rem}
.eg-bar>span{display:block;height:100%;border-radius:5px;min-width:2px}
.eg-barn{font-size:.84rem;font-variant-numeric:tabular-nums;white-space:nowrap}
.eg-guestchips{margin:.1rem 0 .7rem}
.eg-occtable td{vertical-align:top}
.eg-occcell{min-width:14rem}
.eg-rowmore summary{min-height:32px;font-size:.8rem;font-weight:600}
.eg-rowmore ul{margin:.1rem 0 .3rem;padding-left:1rem;font-size:.82rem;color:var(--body);white-space:normal}
.eg-lead{margin:.1rem 0 1rem;font-size:.98rem;line-height:1.5}
.eg-reachgrid{display:grid;grid-template-columns:repeat(auto-fit,minmax(17rem,1fr));gap:1rem 1.6rem;align-items:start}
.eg-mini td,.eg-mini th{padding-top:.35rem;padding-bottom:.35rem}
.eg-diag{margin:1rem 0 0;border-top:1px solid var(--line);padding-top:.3rem}
.eg-multiples{display:grid;grid-template-columns:repeat(auto-fill,minmax(12.5rem,1fr));gap:.8rem}
.eg-mult{border:1px solid var(--line);border-radius:10px;padding:.7rem .8rem;background:#fff;overflow:hidden}
.eg-mult-t{font-weight:700;font-size:.9rem;line-height:1.25}
.eg-mult .eg-dots{margin:.45rem 0 .2rem;max-width:100%;height:auto}
.eg-mult-n{font-size:.82rem;font-weight:600}
.eg-dots{display:block;max-width:100%;height:auto}
.eg-pdfhead{margin:-.4rem 0 1rem;font-size:.95rem}
.ov-step{display:flex;flex-wrap:wrap;align-items:center;gap:.5rem;margin:.7rem 0 0}
.ov-step .btn,.ov-step .chip{min-height:44px;display:inline-flex;align-items:center;text-decoration:none}
.eg-arr{margin:.2rem 0 .3rem}
@media (max-width:48rem){
  .eg-hero{grid-template-columns:auto 1fr}
  .eg-next{grid-column:1 / -1;border-left:0;border-top:1px solid var(--line);padding:1rem 0 0;max-width:none}
}
@media (max-width:40rem){
  .eg-hero{grid-template-columns:1fr;padding:1rem}
  .eg-hero-logo{width:3.6rem;height:3.6rem}
  .eg-hero-body,.eg-hero:not(:has(.eg-hero-logo)) .eg-hero-body{grid-column:1}
  .eg-hero h2{font-size:1.25rem}
  .eg-occcell{min-width:11rem}
  .eg-barcell{min-width:8rem}
  .eg-names-cols ul{columns:1}
}
/* ⭐ 282 (review #16) — ON A PHONE, NO TABLE WIDER THAN THE SCREEN: each row becomes a card, the first cell its title and
   every other cell a labelled pair (data-l). Seen in Chrome at 400px 2026-09-14: the occasion table showed only its first
   column and every number sat off-screen. */
.eg-reach-row{display:grid;grid-template-columns:7.5rem 1fr auto;align-items:center;gap:.6rem;margin:.35rem 0;font-size:.86rem}
.eg-reach-l{font-weight:600}
.eg-reach-row .eg-bar{height:12px;margin:0}
.eg-reach-n{font-variant-numeric:tabular-nums;white-space:nowrap;font-size:.84rem}
@media (max-width:40rem){
  .roster.eg-stack{display:block;background:none;overflow:visible;min-width:0}
  #guestTable.eg-stack{min-width:0}
  .eg-stack thead{display:none}
  .eg-stack tbody,.eg-stack tfoot{display:block}
  .eg-stack tr,.occtable.eg-stack tr.occ{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:.35rem .8rem;
    padding:.75rem .1rem;border-bottom:1px solid var(--line)}
  .eg-stack tr>td,.occtable.eg-stack tr.occ>td{display:block;padding:0;border:0;text-align:left;white-space:normal}
  .eg-stack tr>td:first-child{grid-column:1 / -1}
  .eg-stack tr.monthrow{display:block;padding:0;border:0}
  .eg-stack tr.monthrow>td{display:block;padding:.4rem .6rem}
  .eg-stack td[data-l]::before{content:attr(data-l);display:block;font-size:.66rem;font-weight:700;letter-spacing:.06em;
    text-transform:uppercase;color:var(--muted,#6f6667);margin-bottom:.1rem}
  .eg-stack td.eg-barcell{grid-column:span 2;min-width:0}
  .eg-stack .gsub{white-space:normal}
  .roster.eg-stack td.num{text-align:left}
  #guestTable.eg-stack td.gh-when,#guestTable.eg-stack td.num{white-space:normal}
  #guestTable.eg-stack td:not(.num){grid-column:1 / -1}
  #guestTable.eg-stack td.gh-when[data-l]::before{display:inline;margin-right:.4rem}
  .eg-stack td[data-l="Invited by"]{font-size:.84rem}
  .rosterframe:has(> .eg-stack){max-height:none;overflow:visible;border:0}
  .eg-mini th,.eg-mini td{padding-left:.3rem;padding-right:.3rem;font-size:.84rem}
  .eg-mini td .muted{display:block;font-size:.72rem}
  .eg-reach-row{grid-template-columns:6.5rem 1fr auto}
}
@media print{
  /* wkhtmltopdf (the PDF) is an old WebKit: no grid, no columns — so print as blocks and inline-blocks */
  .eg-hero{display:block;padding:.6rem .8rem}
  .eg-next,.eg-hero-logo{display:none}
  .eg-tiles{display:block}
  .eg-tile{display:inline-block;width:23%;margin:0 1% .6rem 0;vertical-align:top;box-shadow:none}
  .eg-reachgrid,.eg-multiples{display:block}
  .eg-mult{display:inline-block;width:31%;margin:0 1% .6rem 0;vertical-align:top}
  .eg-namespara{font-size:.8rem;line-height:1.45;margin:.3rem 0}
  /* …and no CSS variables: spell the colours out, or links print blue */
  .eg-tile,.eg-tile .eg-l,.eg-tile .eg-s{color:#423b3b;text-decoration:none}
  .eg-n{color:#5E0009}
  .eg-hero{border-left:5px solid #5E0009;border-top:1px solid #e6e2e2;border-right:1px solid #e6e2e2;border-bottom:1px solid #e6e2e2}
  .eg-hero-k{color:#5E0009}
  .eg-names summary::before,.eg-more>summary::before,.eg-diag summary::before{display:none}
  .eg-hero,.eg-mult{break-inside:avoid;page-break-inside:avoid}
  .eg-names summary,.eg-more>summary,.eg-rowmore summary,.eg-diag summary{color:#000}
  .eg-rowmore summary{display:none}
}
/* 282 — the arrivals chart scales down to ~55% on a phone; its labels are drawn larger so they land near 9px there */
.eg-arr .bc-sub{font-size:15px}
.eg-arr .bc-peak,.eg-arr .bc-over{font-size:15px}
.eg-trend{max-width:640px}

/* 286 — a withdrawn date's page lists the order's live dates as full-width buttons (44px targets). */
.gorder { list-style: none; margin: 1rem 0 0; padding: 0; display: grid; gap: .6rem; }
.gorder .btn { display: flex; align-items: center; min-height: 44px; width: 100%; box-sizing: border-box; justify-content: center; text-align: center; }
/* 285 D2 — another organisation's event seats, read-only for an MTC admin. */
.allocro{display:flex;flex-direction:column;gap:.15rem;font-size:.7rem;color:#6e6e73;padding:.1rem .2rem}
.allocro strong{font-size:.9rem;color:var(--ink)}
/* 285 D5 — the "No limit" switch beside a seat number */
.nolimit{display:inline-flex;align-items:center;gap:.3rem;font-size:.78rem;font-weight:600;color:#423b3b;margin-left:.45rem;white-space:nowrap;cursor:pointer;min-height:32px}
.nolimit input{width:1rem;height:1rem;margin:0}
td.num .nolimit{margin-left:.35rem}

/* 285 D3 — the organisation page's tabs: Agreement · Seats · People */
.orgtabs{display:flex;gap:.25rem;margin:1rem 0 1.1rem;border-bottom:1px solid rgba(94,0,9,.18);overflow-x:auto}
.orgtab{appearance:none;background:none;border:0;border-bottom:3px solid transparent;margin-bottom:-1px;
  padding:.7rem 1.1rem;min-height:44px;font:inherit;font-weight:600;font-size:.95rem;color:#6e6e73;cursor:pointer;white-space:nowrap}
.orgtab:hover{color:var(--ink)}
.orgtab[aria-selected="true"]{color:#5E0009;border-bottom-color:#5E0009}
.orgtab:focus-visible{outline:2px solid #5E0009;outline-offset:2px}

/* 285 D4 — Setup's events, one row per series; each date behind a disclosure */
.evseries tr.evseries-dates td{padding-top:0;border-top:0}
.evseries tr.evseries-dates summary{font-size:.8rem;color:#6e6e73;cursor:pointer;min-height:32px;display:flex;align-items:center}
.evdates{list-style:none;margin:.35rem 0 .6rem;padding:0;display:grid;gap:.25rem}
.evdates li{display:flex;flex-wrap:wrap;align-items:center;gap:.5rem;font-size:.88rem}
.evdates li .lnk.danger{margin-left:auto}
@media (max-width:40rem){ .evseries tr.evseries-dates td{display:block} }

/* 285 E — quiet saves: a status line or an inline question next to the control, never alert()/confirm() */
.quiet{font-size:.8rem;line-height:1.35;min-height:0}
.quiet:empty{display:none}
.quiet.ok{color:#2e7d32}
.quiet.err{color:#b3261e;font-weight:600}
.quiet.info{color:#6e6e73}
.quiet.ask{display:flex;flex-wrap:wrap;align-items:center;gap:.5rem;margin:.45rem 0;padding:.5rem .65rem;
  border:1px solid rgba(94,0,9,.25);border-radius:.5rem;background:#fbf7f7;color:var(--ink);flex-basis:100%}
.quiet.ask .btn{min-height:36px;padding:.3rem .8rem}
.sportrow > .quiet{flex-basis:100%}

/* 285 E — one input language on the organisation page: every seat and pass box matches the series row's */
.orgpane table input[type=number],.orgpane table input[type=text]{padding:.35rem .45rem;border:1px solid rgba(0,0,0,.2);
  border-radius:.35rem;font:inherit;min-height:36px;box-sizing:border-box}
.orgpane table input[type=text]{width:100%}
.quiet.ask .quiet-in{flex:1 1 12rem;min-height:36px;padding:.35rem .5rem;border:1px solid rgba(0,0,0,.2);border-radius:.35rem;font:inherit}

/* ══ 285 UI SWEEP (2026-09-14) — found by measuring every screen at 1280 and 400px ══ */
/* F7 grey text read 3.9–4.2:1 on white; small text needs 4.5. Warm grey, darker. */
.gsub,.hint{color:#6d6566}
.roster th{color:#6d6566}
/* F5 native inputs at 21px */
.qh input[type=number],.qh select,#logoFile{min-height:36px;padding:.3rem .45rem;border:1px solid rgba(0,0,0,.2);border-radius:.35rem;font:inherit;box-sizing:border-box}
#logoFile{padding:.35rem;background:#fff}
/* F6 targets: a person's name opens their dialog; link buttons are real buttons */
.roster button.gname.js-person{min-height:36px;padding:.2rem 0;text-align:left}
.lnk{min-height:32px}
@media (max-width:40rem){ .lnk,.roster button.gname.js-person{min-height:44px} }
/* F1 People as cards: the Sign-in and Mobile cells span the card so their icons are never under the next cell */
@media (max-width:40rem){
  .eg-stack td.umail,.eg-stack td.umob{grid-column:1 / -1}
  .eg-stack td.umob .contact-acts{margin-left:.35rem}
  #doorList.eg-stack td{grid-column:auto}
  #doorList.eg-stack tr{grid-template-columns:1fr auto}
  #evaccess .eg-stack td:last-child:empty{display:none}
}
/* 285 sweep — the design picker's summary line was cut off with an ellipsis on a phone; let it wrap there */
@media (max-width:40rem){ .wlp-sum span{white-space:normal} }


/* ════════════════════════════ 287 — THE DESIGN SYSTEM, FROM ENGAGEMENT AND SCHEDULE ════════════════════════════
   Fable 5.1 pixel review (docs/partners-setup-people/REVIEW-FABLE-PIXELS-2026-09-14.md). Values are the ones the
   good reference screens already use; these rules make the event/game page, Partners, the organisation page, Setup
   and People speak the same language. */

/* C — one header bar at desktop: nowrap, the user block right, the name behind an avatar menu */
.topbar{flex-wrap:nowrap;gap:1.25rem;min-height:64px;padding-top:.5rem;padding-bottom:.5rem}
.nav{flex-wrap:nowrap;gap:1.1rem}
.nav a{font-size:.95rem;white-space:nowrap}
.who{flex:none;gap:.55rem}
.usermenu{position:relative}
.usermenu summary{list-style:none;cursor:pointer;display:flex;align-items:center}
.usermenu summary::-webkit-details-marker{display:none}
.usermenu .avatar{display:inline-flex;align-items:center;justify-content:center;width:34px;height:34px;border-radius:50%;
  background:rgba(255,255,255,.18);border:1px solid rgba(255,255,255,.35);color:#fff;font-size:.8rem;font-weight:700;letter-spacing:.02em}
.usermenu summary:focus-visible .avatar{outline:2px solid #fff;outline-offset:2px}
.usermenu-panel{position:absolute;right:0;top:calc(100% + 10px);z-index:80;min-width:15rem;background:#fff;color:var(--ink);
  border:1px solid var(--line);border-radius:12px;box-shadow:0 18px 40px -12px rgba(24,21,21,.35);padding:.9rem 1rem;
  display:flex;flex-direction:column;gap:.15rem}
.usermenu-panel .name{color:var(--ink);font-weight:650;font-size:.95rem}
.usermenu-sub{color:#6d6566;font-size:.8rem;margin-bottom:.55rem;word-break:break-all}
.who .usermenu-panel .out{color:var(--maroon);text-decoration:none;font-weight:600;padding:.45rem 0 .1rem;border-top:1px solid var(--line)}
/* below 72rem the ten links go behind the menu button (the phone presentation, reused) */
@media (max-width:72rem){
  .topbar{position:relative}
  .navtoggle{display:flex;flex-direction:column;justify-content:center;gap:5px;width:44px;height:44px;padding:0 10px;
    background:none;border:0;cursor:pointer;margin-left:auto;order:3;flex:none}
  .navtoggle span{display:block;height:2px;background:#f3e6e7;border-radius:2px;transition:transform .16s ease,opacity .16s ease}
  .navtoggle[aria-expanded="true"] span:nth-child(1){transform:translateY(7px) rotate(45deg)}
  .navtoggle[aria-expanded="true"] span:nth-child(2){opacity:0}
  .navtoggle[aria-expanded="true"] span:nth-child(3){transform:translateY(-7px) rotate(-45deg)}
  .nav{display:none;position:absolute;top:100%;left:0;right:0;z-index:75;flex-direction:column;gap:0;background:#4a0007;
    padding:.35rem 0;box-shadow:0 10px 26px rgba(0,0,0,.35)}
  .nav.is-open{display:flex}
  .nav a{display:flex;align-items:center;min-height:48px;padding:.2rem 1.25rem;border-bottom:1px solid rgba(255,255,255,.08)}
  .who{margin-left:auto}
}

/* B.2 — cards: 24px between them, never touching */
.panel + .panel,.panel + .orgpane,.orgpane + .orgpane,section.orgpane > .panel + .panel{margin-top:1.5rem}
.cols > .panel + .panel{margin-top:0}
.panelhead h2{font-size:1.125rem;font-weight:650}

/* B.4 — table headers on a pale band; cells top-aligned, 14px 12px */
.roster th{background:#f7f5f5;font-size:.6875rem;font-weight:650;letter-spacing:.06em;color:#6d6566;
  padding:.6rem .75rem;border-bottom:1px solid #e8e2e2;vertical-align:bottom}
.roster td{padding:.85rem .75rem;vertical-align:top}
.roster td.num,.roster th.num{font-variant-numeric:tabular-nums}
.roster.fixed{table-layout:fixed}
.roster.fixed td,.roster.fixed th{overflow-wrap:anywhere}

/* B.6 — inputs 40px, regular weight (D21: every value rendered bold because inputs inherited the label's 600) */
.panel input[type=text],.panel input[type=number],.panel input[type=email],.panel input[type=tel],.panel input[type=date],
.panel input[type=search],.panel input[type=url],.panel select,.panel textarea{
  min-height:40px;border:1px solid #d9d2d2;border-radius:8px;padding:.45rem .75rem;font:inherit;font-size:.9375rem;
  font-weight:400;color:#1a1a1a;background:#fff;box-sizing:border-box}
.panel textarea{min-height:120px;line-height:1.5}
.panel input:focus,.panel select:focus,.panel textarea:focus{outline:2px solid var(--focus);outline-offset:1px;border-color:var(--focus)}
.gsettings textarea,.gsettings input,.gsettings select{font-weight:400}
.btn{min-height:40px;display:inline-flex;align-items:center;justify-content:center;gap:.4rem;border-radius:8px;
  padding:0 1.1rem;font-size:.9375rem;line-height:1.2}
.btn.ghost{border-color:rgba(94,0,9,.35);background:#fff}
.btn.danger{background:#fff;color:var(--danger);border:1px solid rgba(161,17,34,.35)}

/* B.7 — permission chips are outline maroon, never green (D14) */
.pill.perm.on{background:transparent;border-color:#d9cfd0;color:var(--maroon)}
.pill.perm.off{background:transparent;border-color:#e8c9cc;color:var(--danger);text-decoration:line-through}

/* D23 — one notice: warm grey, 3px maroon rule */
.notice{background:#f3efef;border-left:3px solid var(--maroon);color:var(--body)}
.notice strong{color:var(--ink)}

/* D30 — one back link: 14px maroon, flush with the content edge */
.crumb{margin:0 0 1rem}.crumb a,a.crumb{color:var(--maroon);text-decoration:none;font-size:.875rem;font-weight:500}
.crumb a:hover,a.crumb:hover{text-decoration:underline}

/* B.9 — an empty card still has its action */
.panel .empty{padding:1rem 0 .25rem}

/* ⭐⭐ 287 — "Who may invite": a fixed-column grid, one line per organisation (see event.php) */
.accgrid{margin-top:1rem;border:1px solid #ece7e7;border-radius:10px;overflow:hidden}
.accrow{display:grid;grid-template-columns:minmax(0,1fr) 15rem 5.5rem 7.5rem;column-gap:1rem;align-items:center;
  padding:.65rem 1rem;border-top:1px solid #ece7e7;min-height:3.75rem}
.accrow:first-child{border-top:0}
.acchead{background:#f7f5f5;min-height:0;padding-top:.55rem;padding-bottom:.55rem;font-size:.6875rem;font-weight:650;
  letter-spacing:.06em;text-transform:uppercase;color:#6d6566}
.accname{font-weight:600;color:var(--ink);min-width:0;overflow-wrap:anywhere}
.accseats{display:flex;align-items:center;gap:.75rem;min-width:0}
.accseats input[type=number]{width:6rem;flex:none;text-align:right;font-variant-numeric:tabular-nums}
.accseats input[type=number]:disabled{background:#f7f5f5;color:#6d6566}
.accseats strong{font-variant-numeric:tabular-nums}
.accused{text-align:right;font-variant-numeric:tabular-nums;color:var(--ink)}
.accact{text-align:right}
.accact .btn{min-height:36px;padding:0 .9rem;font-size:.875rem}
.btn.ghost.danger-text{color:var(--danger);border-color:rgba(161,17,34,.3)}
.nolimit{display:inline-flex;align-items:center;gap:.45rem;margin:0;font-size:.875rem;font-weight:500;color:var(--body);
  white-space:nowrap;cursor:pointer;min-height:40px}
.nolimit input[type=checkbox]{width:1.05rem;height:1.05rem;margin:0;accent-color:var(--maroon);flex:none}
.accadd{display:grid;grid-template-columns:minmax(0,1fr) auto;column-gap:.75rem;align-items:end;margin-top:1.25rem}
.accadd label{grid-column:1 / -1;font-size:.8125rem;font-weight:600;color:var(--ink);margin-bottom:.35rem}
.accadd select{width:100%}
.accadd .hint{grid-column:1 / -1;margin-top:.45rem}
@media (max-width:45rem){
  .accrow{grid-template-columns:minmax(0,1fr) auto;row-gap:.5rem;padding:.8rem .9rem}
  .acchead{display:none}
  .accname{grid-column:1 / -1}
  .accseats{grid-column:1 / 2}
  .accused{grid-column:2 / 3;align-self:center}
  .accused::before{content:"Used ";color:#6d6566;font-size:.75rem;text-transform:uppercase;letter-spacing:.06em;margin-right:.3rem}
  .accact{grid-column:1 / -1;text-align:left}
  .accact:empty{display:none}
  .accact .btn{width:100%;min-height:44px}
  .accadd{grid-template-columns:1fr}
  .accadd .btn{width:100%;min-height:44px;margin-top:.5rem}
}
.accrow > .quiet{grid-column:1 / -1}
.acchead span.num{text-align:right}
.accseats strong{display:inline-block;min-width:6rem;text-align:right;padding-right:.75rem}
@media (max-width:45rem){
  .accact{grid-column:2 / 3;text-align:right}
  .accseats{grid-column:1 / -1}
  .accused{grid-column:1 / 2;text-align:left}
  .accact .btn{width:auto;min-height:40px}
}

/* ════ 287 — event page ════ */
/* D19 · the switch rows (This occasion is on / Issue a scannable VIP pass) are rows inside their card, not inset boxes */
.evflag{background:none;border:0;border-top:1px solid #ece7e7;border-radius:0;padding:1rem 0 0;margin:1.25rem 0 0;
  grid-template-columns:1.25rem 1fr;column-gap:.75rem}
.evflag input[type=checkbox]{width:1.1rem;height:1.1rem;accent-color:var(--maroon);margin:.15rem 0 0}
.evflag strong{font-size:.9375rem;color:var(--ink)}
.passcard .wlp{margin:.25rem 0 0;padding-top:0;border-top:0}
/* D26 · no card inside a card at the gate; D27 · mark tiles a fixed 72px, warm grey, "Change" beneath the mark */
.grule{border:0;border-top:1px solid #ece7e7;border-radius:0;padding:1rem 0 0;margin:1rem 0 0}
.grule-thumb{width:4.5rem;height:4.5rem;background:#f3efef;border:1px solid #e8e2e2;border-radius:10px;
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.2rem;padding:.35rem;background-image:none}
.grule-thumb img{max-width:100%;max-height:2.6rem}
.grule-thumb span{font-size:.6875rem}
.grule-lbl{font-weight:650;color:var(--ink);font-size:.9375rem}
.gsettings label.ck,.grule-auto{font-weight:500;font-size:.875rem;display:flex;gap:.5rem;align-items:flex-start}
.grule-auto input[type=checkbox]{width:1rem;height:1rem;margin:.15rem 0 0;accent-color:var(--maroon);flex:none;min-height:0}
.grule-stats{color:var(--body)}
.grule-foot{display:flex;gap:.5rem;flex-wrap:wrap}
.grule-stats .lnk,.grule-foot .lnk{border:1px solid rgba(94,0,9,.3);border-radius:8px;min-height:36px;padding:0 .8rem;
  display:inline-flex;align-items:center;margin:.5rem .5rem 0 0;font-weight:600}
@media (max-width:520px){ .grule-row{flex-direction:row} .grule-thumb{width:4.5rem;height:4.5rem} }
/* D8 · a primary roster is never a clipped inner scroll: the whole list, header sticky to the page */
/* ⛔⛔ REVERSED 2026-09-15 — Jeremy: "the attending section of an event was expanded again, it was not supposed to be, it needs to
   stay in a sized window or frame because it is too long". The pixel review's D8 ("no inner max-height on a primary table") was
   applied over HIS design; the frame is the design. It keeps its original height (min(62svh,44rem)) and now carries a bottom fade so
   a row cut at the frame edge reads as "scroll for more", which was the review's actual complaint. Do NOT remove the frame again. */
.rosterframe{max-height:min(62svh,44rem);overflow-y:auto;position:relative}
.rosterframe::after{content:"";position:sticky;display:block;bottom:0;height:1.5rem;margin-top:-1.5rem;pointer-events:none;
  background:linear-gradient(to bottom,rgba(255,255,255,0),#fff)}
/* ⛔ NEVER CAP THE WILL-CALL SHEET — the rule above comes after the print block at the top of the file and would win on order. */
@media print{.rosterframe{max-height:none;overflow:visible}.rosterframe::after{display:none}}

/* ════ 287 — organisation page ════ */
/* cards separated by a script tag still get their gap (".panel + .panel" does not match across a <script>) */
.wrap > .panel,.wrap > section > .panel,.orgpane > .panel,.wrap > .gamehead{margin-bottom:1.5rem}
.wrap > .panel:last-child,.orgpane > .panel:last-child{margin-bottom:0}
/* D22 · header block */
.orghead{display:grid;grid-template-columns:minmax(0,1fr) auto;column-gap:1rem;align-items:center;margin:0 0 .25rem}
.orghead .orgtitle{margin:0;display:flex;align-items:center;gap:.75rem;font-size:1.75rem}
.orghead .orglogo.lg{width:3rem;height:3rem;object-fit:contain;background:#fff;border:1px solid var(--line);border-radius:10px;padding:.2rem}
.orgsub{grid-column:1 / 2;margin:.2rem 0 0;color:#6d6566;font-size:.95rem}
.orghead-act{grid-row:1 / span 2;grid-column:2}
@media (max-width:40rem){ .orghead{grid-template-columns:1fr} .orghead-act{grid-row:auto;grid-column:1;justify-self:start;margin-top:.6rem} .orghead .orgtitle{font-size:1.4rem} }
/* B.5 · every series row on one grid: name · input · input · button (the button's label no longer moves the inputs) */
.sportrow{display:grid;grid-template-columns:minmax(0,1fr) 6.5rem 6.5rem 11.5rem;column-gap:1rem;align-items:end;
  padding:1rem 0 .35rem;border-top:1px solid #ece7e7}
.sportrow:first-of-type{border-top:0}
.sportrow .sportname{font-weight:650;color:var(--ink);align-self:center}
.sportrow .sportname .muted{display:block;font-weight:400;font-size:.8125rem;color:#6d6566;margin-top:.1rem}
.sportrow label{display:flex;flex-direction:column;gap:.3rem;font-size:.75rem;font-weight:600;color:#6d6566;margin:0}
.sportrow input[type=number]{width:100%;text-align:right;font-variant-numeric:tabular-nums}
.sportrow label.nolimit{flex-direction:row;font-size:.875rem;font-weight:500;color:var(--body);min-height:40px;align-self:end}
.sportrow .btn{width:100%;padding:0 .5rem;white-space:nowrap}
.pergame{margin:.1rem 0 .6rem}
.pergame summary{min-height:36px;display:flex;align-items:center;font-weight:500}
@media (max-width:40rem){
  .sportrow{grid-template-columns:1fr 1fr;row-gap:.6rem}
  .sportrow .sportname{grid-column:1 / -1}
  .sportrow .btn{grid-column:1 / -1;min-height:44px}
}
/* D11 · agreement: no grey "unset" rows, inputs fill their fixed column, actions right with Save rightmost */
.agree tr.is-private td{background:none}
.agree tr.is-private .gname{color:#6d6566}
.agree td input[type=number]{width:100%;text-align:right}
.agree td.actions{display:table-cell}
.agree td.actions .btn{min-height:36px;padding:0 .9rem;margin-left:.4rem}
/* D33 · logo card */
.logoform{display:flex;align-items:center;gap:1rem;flex-wrap:wrap;margin-top:1rem}
.logotile{width:5rem;height:5rem;border:1px solid var(--line);border-radius:12px;background:#fff;display:flex;align-items:center;justify-content:center;overflow:hidden}
.logotile img{max-width:88%;max-height:88%;object-fit:contain}
.logotile span{font-size:.75rem;color:#6d6566}
#logoFile{min-height:40px;border:1px dashed #d9d2d2;border-radius:8px;padding:.35rem .5rem;background:#faf8f8;font-size:.875rem;max-width:100%}
#logoFile::file-selector-button{border:1px solid rgba(94,0,9,.35);background:#fff;color:var(--maroon);border-radius:6px;padding:.3rem .7rem;margin-right:.6rem;font:inherit;font-weight:600;cursor:pointer}
.logoform .err{flex-basis:100%}
.orgtabs{scrollbar-width:none;overflow-y:hidden}
.orgtabs::-webkit-scrollbar{display:none}
.agree tr.is-private{background:none}

/* ════ 287 — Partners list and People ════ */
.orglist tbody tr[data-href]{cursor:pointer}
.orglist tbody tr[data-href]:hover td{background:#faf8f8}
.orgcell{display:grid;grid-template-columns:34px minmax(0,1fr);column-gap:.75rem;align-items:center}
.orgcell .orglogo{grid-row:1 / span 2}
.orgcell .gname{text-decoration:none;color:var(--ink);font-weight:650}
.orgcell .gname:hover{text-decoration:underline}
.orgcell .gsub,.orgcell .pill{grid-column:2}
.orgcell .gsub{margin-top:0}
.peopletbl td{vertical-align:top}
.roster button.gname.js-person{min-height:0;padding:0;line-height:1.35;font-size:.9375rem;text-align:left;background:none;border:0;cursor:pointer;color:var(--ink);font-weight:650}
.roster button.gname.js-person:hover{text-decoration:underline;color:var(--maroon)}
.peopletbl td .gsub{margin-top:.15rem}
.peopletbl td.umail{display:table-cell}
.peopletbl td.umail .iconbtn{vertical-align:middle;margin-left:.35rem}
.peopletbl td.umob{white-space:normal}
@media (max-width:40rem){ .roster button.gname.js-person{min-height:44px} }
.peopletbl button.gname.js-person{display:block}
.peopletbl td:first-child > .gsub{margin-left:0;padding-left:0}
.peopletbl td.umob{white-space:nowrap}
.peopletbl td.umob .contact-acts{display:inline-flex;margin-left:.35rem}

/* ════ 287 — Setup ════ */
#sec-sports .roster.eg-stack{table-layout:fixed}
#sec-sports td.actions .btn{min-height:36px;padding:0 .9rem;font-size:.875rem}
#sec-sports td.actions .gsub{margin-top:.4rem}
.evseries td .gname{text-decoration:none;color:var(--ink);font-weight:650}
.evseries a.gname:hover{text-decoration:underline;color:var(--maroon)}
.evseries td.actions .btn{min-height:36px;padding:0 .9rem;font-size:.875rem}
.evdatesbox summary{cursor:pointer;list-style:none;display:inline-flex;align-items:baseline;gap:.25rem;color:var(--ink)}
.evdatesbox summary::-webkit-details-marker{display:none}
.evdatesbox summary::before{content:"\25B8";color:var(--maroon);font-size:.8rem;margin-right:.25rem}
.evdatesbox[open] summary::before{content:"\25BE"}
.evdatesbox .evdates{margin:.5rem 0 0}
.evdates li{display:grid;grid-template-columns:5.5rem minmax(0,1fr) auto;align-items:center;gap:.5rem;font-size:.875rem;padding:.2rem 0}
.evdates li .lnk{margin:0;min-height:30px}
.qh td{vertical-align:middle}
.qh input[type=number]{width:100%}
.qh select{width:100%}
.qh .qh-act{text-align:right;white-space:nowrap}
.qh .qh-act .btn{min-height:36px;padding:0 .9rem;font-size:.875rem}
.qh-foot{margin-top:.75rem}


/* ════ 287 D9/D10 — phone: rosters and the new fixed tables become cards whose values never share a baseline with a wrapped label ════ */
@media (max-width:40rem){
  .rosterframe:has(> .rstack){overflow-x:hidden;overflow-y:auto;max-height:70svh}   /* still a sized frame on a phone (Jeremy 2026-09-15) */
  .roster.rstack{display:block;min-width:0}
  .roster.rstack thead{display:none}
  .roster.rstack tbody{display:block}
  .roster.rstack tr{display:grid;grid-template-columns:minmax(0,1fr) auto;column-gap:.75rem;row-gap:.35rem;padding:.8rem .1rem;border-bottom:1px solid var(--line)}
  .roster.rstack td{display:block;padding:0;border:0;text-align:left;min-width:0}
  .roster.rstack td:nth-child(1){grid-column:1;grid-row:1}
  .roster.rstack td:nth-child(5){grid-column:2;grid-row:1;align-self:start}
  .roster.rstack td:nth-child(2){grid-column:1 / -1;overflow-wrap:anywhere}
  .roster.rstack td:nth-child(3),.roster.rstack td:nth-child(4){grid-column:auto}
  .roster.rstack td:nth-child(3)::before,.roster.rstack td:nth-child(4)::before{display:block;font-size:.66rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:#6d6566;margin-bottom:.1rem}
  .roster.rs-event td:nth-child(3)::before{content:"Seats"}
  .roster.rs-game td:nth-child(3)::before{content:"VIP passes"}
  .roster.rstack td:nth-child(4)::before{content:"Invited by"}
  .roster.rstack td .pill{white-space:normal;max-width:100%}
  /* the new fixed tables: two columns; long text and actions take the whole width */
  .roster.fixed.eg-stack tr{grid-template-columns:repeat(2,minmax(0,1fr))}
  .roster.fixed.eg-stack td[data-l="Terms"],.roster.fixed.eg-stack td[data-l="Who may invite"],
  .roster.fixed.eg-stack td[data-l="Pass look"],.roster.fixed.eg-stack td[data-l="Sign-in"],
  .roster.fixed.eg-stack td[data-l="Dates"],.roster.fixed.eg-stack td.actions{grid-column:1 / -1}
  .roster.fixed.eg-stack td.actions{text-align:left}
  .roster.fixed.eg-stack td[data-l]::before{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  #sec-sports .roster.eg-stack tr{grid-template-columns:repeat(3,minmax(0,1fr))}
  #sec-sports .roster.eg-stack td.sportbrand,#sec-sports .roster.eg-stack td.actions{grid-column:1 / -1;text-align:left}
  #sec-sports .roster.eg-stack td.actions .btn{min-height:44px}
}
/* a column header never breaks mid-word (Chrome's font set "INVITES" as "INVITE / S" in a 7% column) */
.roster.fixed th{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;overflow-wrap:normal}
/* 287 — the "Saved" tick of a save-as-you-edit card */
.savedtick{display:inline-flex;align-items:center;gap:.3rem;color:var(--ok);font-weight:600;margin-top:.5rem}
.savedtick::before{content:"\2713"}

/* ════ 287 — People rows, from Chrome pixels 2026-09-15 ════
   The name button carried a .7rem top margin and a -.25rem left pull from older button.gname rules, so the name sat ~6px below
   every other cell and 4px left of its own column header and sub-line. The send-link icon sat inline after the address, so it moved
   with the length of each email, and its 40px box made the rows 83px tall. Now: the name starts on the row's top line at the
   column edge; the icon is pinned to the column's right edge, out of the line box. */
.peopletbl button.gname.js-person{margin:0}
.peopletbl td.umail{position:relative;padding-right:3.25rem;overflow-wrap:anywhere}
.peopletbl td.umail .iconbtn{position:absolute;right:.5rem;top:.45rem;margin:0}
.peopletbl td{line-height:1.4}
.peopletbl td.umob .contact-acts{vertical-align:top;line-height:1}
.peopletbl td.umob .contact-acts .ico-btn{min-height:0;padding-block:0}
/* 287 — on a phone the sticky jump bar is ONE row that scrolls sideways. Wrapped, People's five organisations took three rows
   (~150px) of a sticky bar over the list it jumps through (Chrome, 400px frame, 2026-09-15). */
@media (max-width:40rem){
  .secnav{flex-wrap:nowrap;overflow-x:auto;scrollbar-width:none;-webkit-overflow-scrolling:touch}
  .secnav::-webkit-scrollbar{display:none}
  .secnav a{flex:0 0 auto;white-space:nowrap}
}
/* 287 D29 — ONE TAB IDIOM. Engagement's view switch was a grey pill with a filled maroon choice; the organisation page's tabs are
   an underline. Both switch views of one page, so both are now the underline (spec B.8): 15px/600, maroon 3px underline on the
   choice, one rule under the row. The PDF button stays on the right of that row. */
.eg-top{align-items:flex-end;border-bottom:1px solid rgba(94,0,9,.18);padding-bottom:.45rem;margin-bottom:1.1rem}
.eg-tabs{display:flex;background:none;border-radius:0;padding:0;gap:.25rem;margin-bottom:calc(-.45rem - 1px)}
.eg-tab{border-radius:0;padding:.7rem 1.1rem;min-height:44px;box-sizing:border-box;display:inline-flex;align-items:center;
  font-size:.95rem;font-weight:600;color:#6e6e73;border-bottom:3px solid transparent;white-space:nowrap}
.eg-tab:hover{color:var(--ink)}
.eg-tab.on{background:none;color:#5E0009;border-bottom-color:#5E0009}
.eg-top > :not(.eg-tabs){margin-bottom:.1rem}
@media (max-width:40rem){
  /* the PDF button wraps below: the rule belongs to the tabs, not to the wrapped row */
  .eg-top{border-bottom:0;padding-bottom:0}
  .eg-tabs{width:100%;border-bottom:1px solid rgba(94,0,9,.18);margin-bottom:.35rem}
  .eg-tab{margin-bottom:-1px}
}

/* ⭐ Jeremy 2026-09-15: filtered to Wil Fischer on Marshall the count read "79 / 150" in body-size text — "the 79 should be large and
   bold". The count is a BUTTON when it opens Distribute, and `.ghcount .n.as-link{font:inherit}` (above) reset its 2rem/700 to the
   body font. The button keeps the headline's size and weight; the "/ 150" keeps its smaller grey. */
.ghcount .n.as-link{font-size:2rem;font-weight:700;line-height:1;font-family:inherit}
.ghcount .n.as-link span{font-size:1.1rem;font-weight:600;color:#aca1a2}
.ghcount.full .n.as-link,.ghcount.over .n.as-link{color:var(--danger)}

/* ⛔ 2026-09-15 — the Apple thumbnail is a 3:2 box on the real pass (90×60pt, picture centred, never cropped); the preview shows the
   same box, not a square. */
.wlp-apple-thumb{width:4.2rem;height:3.2rem;padding:0 .45rem;box-sizing:border-box}   /* 2nd fix: Wallet's box ~1.3:1, picture shrunk inside it */
.wlp-apple-note{margin:.2rem 0 .3rem;font-size:.72rem;line-height:1.3}

/* 289 D14 — the super admin's "see every brand" switch under the organisation header */
.orgflag{display:flex;gap:.5rem;align-items:flex-start;margin:.35rem 0 0;font-size:.9rem}
.orgflag input{margin-top:.2rem}

/* Jeremy 2026-09-15 — every image spot says exactly what file to send */
.bp-upload{display:block;margin-top:.2rem;color:var(--body);line-height:1.35}
.bp-pick-h .bp-upload{margin-top:.3rem;font-size:.82rem}
/* the Upload line wraps (it is the instruction a person acts on — never cut with an ellipsis) and its label stays inline */
.bp-t small.bp-upload{white-space:normal;overflow:visible;text-overflow:clip;color:var(--body);margin-top:.15rem}
.bp-t .bp-upload b{display:inline;font-size:inherit}
.bp-pick-h .bp-upload b{display:inline}

/* 289 D15 — in place of Add guest when a person holds nothing */
.askfor{align-self:center;font-size:.9rem;color:var(--body);max-width:22rem;line-height:1.3}
/* P18 · "Apply to all 17 upcoming" is wider than the old 9.5rem column; a muted note sits where the button would */
.sportrow > .muted{align-self:center;font-size:.875rem}
/* H3 · the gate-mode ? meets the gate's 44px touch rule */
.topbar-gate .helpq-gate{width:44px;height:44px;flex:0 0 44px;font-size:1.1rem;align-self:center}
/* 290 · help in place: a panel on the right of the screen (a sheet from the bottom on a phone) */
.helpdrawer{margin:0 0 0 auto;padding:0;border:0;width:min(34rem,100vw);max-width:100vw;height:100dvh;max-height:100dvh;
  background:var(--bg,#fbf9f9);color:var(--ink,#1d1d1f);box-shadow:-12px 0 40px rgba(0,0,0,.18);display:none;flex-direction:column}
.helpdrawer[open]{display:flex}
.helpdrawer::backdrop{background:rgba(20,10,12,.35)}
.helpdrawer-head{display:flex;align-items:center;gap:.75rem;padding:.9rem 1rem .9rem 1.25rem;border-bottom:1px solid var(--line,#e6e0e0);background:#fff}
.helpdrawer-head h2{margin:0;font-size:1.1rem;flex:1 1 auto;min-width:0}
.helpdrawer-full{font-size:.875rem;white-space:nowrap}
.helpdrawer-x{width:44px;height:44px;border:0;border-radius:.5rem;background:transparent;font-size:1.6rem;line-height:1;color:inherit;cursor:pointer}
.helpdrawer-x:hover{background:rgba(94,0,9,.08)}
.helpdrawer-body{overflow-y:auto;padding:1rem 1.25rem 2rem;flex:1 1 auto;max-width:none}
.helpdrawer-body img.help-shot{max-width:100%;height:auto}
.helpdrawer-here{outline:2px solid #5E0009;outline-offset:4px;border-radius:.25rem;transition:outline-color .6s}
/* the small ? beside a control */
a.helpinline{display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;margin-left:.35rem;border-radius:50%;
  border:1px solid rgba(94,0,9,.35);color:#5E0009;font-size:.75rem;font-weight:700;text-decoration:none;vertical-align:middle;line-height:1}
a.helpinline:hover{background:rgba(94,0,9,.08)}
@media (pointer:coarse){ a.helpinline{width:32px;height:32px;font-size:.9rem} }
@media (max-width:40rem){
  .helpdrawer{width:100vw;height:88dvh;max-height:88dvh;margin:auto 0 0;border-radius:1rem 1rem 0 0;box-shadow:0 -12px 40px rgba(0,0,0,.2)}
  .helpdrawer-full{display:none}
}
.helpdrawer-body h2,.helpdrawer-body h3{scroll-margin-top:.75rem}
/* 291 · What's new */
.usermenu-panel a.whatsnew{display:block;padding:.45rem 0;text-decoration:none;color:inherit}
.usermenu-panel a.whatsnew:hover{text-decoration:underline}
.changelog h3{margin-top:2rem;padding-top:1.25rem;border-top:1px solid var(--line,#e6e0e0);font-size:1.35rem}
.changelog h3:first-child{margin-top:0;padding-top:0;border-top:0}
.changelog h4{margin:1.2rem 0 .35rem;font-size:1rem;color:#5E0009}
.changelog ul{margin:.2rem 0 .6rem}
.changelog li{margin:.2rem 0}
/* 292 · the sandbox strip */
.sandboxbar{background:repeating-linear-gradient(45deg,#1d1d1f 0 14px,#3a3a3c 14px 28px);color:#ffd60a;text-align:center;font-weight:700;font-size:.85rem;padding:.35rem 1rem;letter-spacing:.02em}
/* 289c · a picture shows at its own size up to the column (phone composites are 350–1050px wide and must not be stretched) */
.helpbody img.help-shot{max-width:100%;height:auto;margin-left:auto;margin-right:auto}
