/* ============================================================================
   mambo-setup-shell.css — THE SETUP CONSOLE SHELL (v3.7.0)
   ----------------------------------------------------------------------------
   WHAT THIS FILE IS. inc/setup-render.php v3.7.0 emits, for the first time, the
   demo's own navigation chrome: a persistent rail, a narrow-screen drawer, a
   quick-action row, a space head with a role pill. mambo-setup.css (v3.6.0)
   could not style any of it because none of it existed. This sheet styles ONLY
   that chrome. It is loaded AFTER mambo-setup.css by declaring it a dependency,
   so nothing here needs !important to win source order.

   WHAT THIS FILE IS NOT. It touches no control. Not one .eop-row, .eop-sw,
   .eop-inp, .eop-btn, .eop-chk, binding key, data-eop-val, data-eop-flag,
   nonce or admin-ajax action is referenced anywhere below. The 179 control
   keys and 23 ajax actions are invisible to this stylesheet.

   DESIGN TARGET: _proto/setup-demo.html. Rail 246px sticky, nav item rest /
   hover / current, the two group labels, the Right-now card, the quick-action
   buttons, the uniform hub tile, the space head type scale, the role pill, the
   drawer transition and its scrim — each rule below names the demo line it
   answers.

   HOUSE RULES HONOURED THROUGHOUT
     - every selector starts `body.mb-setup`;
     - colour, radius, shadow, easing and duration come from mambo tokens only.
       A literal appears nowhere; anything the token set does not supply is
       built with color-mix() over a token;
     - authored breakpoints are drawn only from the agreed set
       1120 1020 940 900 860 760 680 620 520. The rail-to-drawer flip is 940
       (246 rail + 34 gap + 660 minimum table);
     - every animation and transition is cancelled under prefers-reduced-motion
       at the foot of the file;
     - no @import, no remote url(), no minmax() carrying a bare px length;
     - never write a star-slash pair inside a comment body. mambo-watch.css did
       once, closed its comment four lines early, and lost the block below it.

   THREE SELECTORS OWNED BY mambo-setup.css ARE DELIBERATELY RE-STATED HERE.
   They are listed at the head of section 1 with the reason, because that file
   asks not to be restated "elsewhere" and a silent override would be a trap
   for whoever edits it next.
   ========================================================================= */


/* ============================================================================
   1. THE PAGE FRAME AND THE CONSOLE HEADER ROW
   ----------------------------------------------------------------------------
   RE-STATED FROM mambo-setup.css, ON PURPOSE, WITH CAUSE:

   (a) `.eop` max-width. mambo-setup.css:178 pins the console at 1240px and
       explains why: "this console has no rail, so 1560 - 246 - 34 ~= 1240".
       That premise expired in v3.7.0 — the console now HAS the rail. Restoring
       the demo's own 1560 gives the main column 1228px, within 12px of the
       measure the space pages have today, so no control row reflows; the rail
       is added beside the content rather than taken out of it.
       `body.mb-setup main.eop` scores (0,2,2) against that rule's (0,2,1);
       setup-render.php always emits the console as <main class="eop">.

   (b) `.eop-hd` and its h1 / .subttl. Shipped, the masthead prints a 24-31px
       <h1>Setup</h1>. The shell now prints the page's real <h1> inside the
       main column — on the hub that is the SAME WORD at a LARGER size, two
       display headings stacked. The demo has no in-page masthead at all; what
       it has in that position is a header ROW: mark, small wordmark, and a
       search button pushed right (setup-demo.html:137-159).
       So the masthead is demoted to exactly that row rather than hidden —
       hiding it would delete two shipped features, because mambo-setup.js
       appends BOTH the command-palette button (:732-746) and the "Saved" pill
       (:940-960) into `.eop-hd`.
   ========================================================================= */
body.mb-setup main.eop {
  max-width: 1560px;
}

/* The header row. Not sticky: the site header (mambo-chrome.css:25) is already
   sticky at z-index 95 and retracts on scroll, so a second sticky bar would
   either double the offset or fight it. */
body.mb-setup .eop .eop-hd {
  align-items: center;
  gap: 11px;
  margin: 0 0 16px;
  padding: 0 0 14px;
  border-bottom: 1px solid var(--line2);
}
body.mb-setup .eop .eop-hd .mk {
  width: 34px;
  height: 34px;
  border-radius: var(--r-md);
}
/* The demo's .brandWm (setup-demo.html:145): 15px, not a display heading. */
body.mb-setup .eop .eop-hd h1 {
  font-size: 15px;
  font-weight: 700;
  letter-spacing: -.022em;
  line-height: 1.1;
}
body.mb-setup .eop .eop-hd .subttl {
  margin: 3px 0 0;
  font-size: 11.5px;
  line-height: 1.35;
  color: var(--muted);
}

/* Two nodes mambo-setup.js creates inside that row which no sheet in the theme
   styles. Both are chrome, both live in the row this file now owns, and an
   unstyled pill beside a styled button is the kind of half-dressed detail the
   screenshot was reporting. The palette OVERLAY (.palette / .palBox) is a
   separate, larger gap and is called out in the handover note, not guessed at
   here. */
body.mb-setup .eop-hd .mb-palbtn .lbl {
  font-size: 13px;
  font-weight: 600;
}
body.mb-setup .eop-hd .mb-palbtn kbd {
  display: inline-flex;
  align-items: center;
  height: 19px;
  padding: 0 6px;
  border: 1px solid var(--line);
  border-radius: var(--r-xs);
  background: var(--card);
  font-family: inherit;
  font-size: 10.5px;
  font-weight: 700;
  color: var(--faint);
}
body.mb-setup .eop-hd .savedPill {
  display: none;
  align-items: center;
  gap: 7px;
  height: 30px;
  padding: 0 12px;
  border-radius: var(--r-pill);
  background: var(--accsoft);
  color: var(--acc);
  font-size: 12.5px;
  font-weight: 600;
  white-space: nowrap;
}
body.mb-setup .eop-hd .savedPill.on {
  display: inline-flex;
  opacity: 1;
  transition: opacity var(--dur-crossfade) var(--ease);
}
/* mambo-setup.js:979 adds .is-fading 260ms before it hides the pill, and under
   reduced motion skips the class entirely and hides at once. */
body.mb-setup .eop-hd .savedPill.on.is-fading {
  opacity: 0;
}
body.mb-setup .eop-hd .savedPill .dot6 {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
}


/* ============================================================================
   2. THE SHELL — .mb-su-shell
   ----------------------------------------------------------------------------
   The demo's .shell (setup-demo.html:161): two columns, rail then main, tops
   aligned. 246px is the rail; the gap tracks the viewport the way the demo's
   container query does, expressed in vw because this is a real page and not
   the prototype's container sandbox.

   `minmax(0,1fr)` carries no px length, so the min(100%, Npx) guard the house
   rules require of a fixed minimum does not apply — the guard exists to stop a
   px minimum from overflowing a narrow parent, and there is no px minimum
   here. The 246px track only ever exists above 940px, where it always fits.
   ========================================================================= */
body.mb-setup .mb-su-shell {
  display: grid;
  grid-template-columns: 246px minmax(0, 1fr);
  gap: clamp(18px, 2.4vw, 34px);
  align-items: start;
  margin-top: 4px;
}
body.mb-setup .mb-su-main {
  min-width: 0;
  padding: 0 0 8px;
}


/* ============================================================================
   3. THE DRAWER TRIGGER — .mb-su-menu
   ----------------------------------------------------------------------------
   The demo's .menuBtn (setup-demo.html:139), which is display:none until the
   rail folds away. Ours carries a label as well as the icon, so it is a pill
   rather than a 36px square.

   DEFAULT IS HIDDEN. The button only means anything once the drawer is the
   navigation, and the open state below lives inside the same 940 query, so
   above 940 the drawer cannot be opened even by a stray class.
   ========================================================================= */
body.mb-setup .mb-su-menu {
  display: none;
  align-items: center;
  gap: 8px;
  height: 36px;
  margin: 0 0 14px;
  padding: 0 13px 0 11px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: transparent;
  color: var(--body);
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  line-height: 1;
  cursor: pointer;
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease);
}
body.mb-setup .mb-su-menu:hover {
  background: var(--soft);
  color: var(--ink);
}
body.mb-setup .mb-su-menu:focus-visible {
  outline: 2px solid var(--acc);
  outline-offset: 2px;
}
body.mb-setup .mb-su-menu .eop-i {
  width: 17px;
  height: 17px;
  stroke-width: 1.9;
}


/* ============================================================================
   4. THE RAIL — .mb-su-rail
   ----------------------------------------------------------------------------
   The demo's .rail (setup-demo.html:162-170). Sticky, scrolling inside itself,
   present in BOTH modes.

   THE STICKY OFFSET IS 88px, NOT THE DEMO'S 62. The demo pins against a 62px
   header drawn inside its own frame. The real page's header is
   var(--header-h) = 68px, sticky at top:0 and retracting on scroll, and 88 is
   already the number mambo-setup.css:425 chose for .eop-sec's
   scroll-margin-top for exactly that reason — 68 plus a 20px breath. One
   number for both, so a jumped-to heading and the rail agree.

   Sticky works here only because mambo-setup.css:187 returned `.eop` to
   overflow:visible. If that ever goes back to hidden, this pins to nothing.
   ========================================================================= */
body.mb-setup .mb-su-rail {
  position: sticky;
  top: 88px;
  align-self: start;
  max-height: calc(100vh - 88px);
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 4px 0 40px;
  scrollbar-width: thin;
}
/* The drawer copy is not sticky and not height-capped — it IS the viewport.
   (0,3,1) against the base rule's (0,2,1), so the order of these two blocks
   cannot matter. Demo: setup-demo.html:352. */
body.mb-setup .mb-su-rail.mb-su-rail--drawer {
  position: static;
  max-height: none;
  overflow-y: visible;
  padding: 18px 12px 32px;
}

/* Group labels — .railGrp / .railGrp.two (setup-demo.html:163-164). */
body.mb-setup .mb-su-grp {
  margin: 0 0 10px;
  padding: 0 10px;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: .15em;
  text-transform: uppercase;
  line-height: 1.4;
  color: var(--muted);
}
body.mb-setup .mb-su-grp--two {
  margin: 22px 0 10px;
}

body.mb-setup .mb-su-nav {
  display: flex;
  flex-direction: column;
  gap: 1px;
}

/* The nav item — .navItem (setup-demo.html:166-170). */
body.mb-setup .mb-su-item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 9px 10px;
  border: 0;
  border-radius: var(--r-md);
  background: transparent;
  color: var(--body);
  font-size: 13.5px;
  font-weight: 600;
  line-height: 1.35;
  text-align: left;
  text-decoration: none;
  transition: background var(--dur-micro) var(--ease),
              color var(--dur-micro) var(--ease);
}
/* :not([aria-current]) is the demo's own guard — the current item must not
   flash the hover wash over its accent tint. */
body.mb-setup .mb-su-item:hover:not([aria-current]) {
  background: var(--soft);
  color: var(--ink);
}
/* Both hooks are matched. setup-render.php emits `.is-on` and
   aria-current="page" together on every current item, and either alone must
   still read as current. */
body.mb-setup .mb-su-item[aria-current="page"],
body.mb-setup .mb-su-item.is-on {
  background: var(--accsoft);
  color: var(--acc);
}
body.mb-setup .mb-su-item:focus-visible {
  outline: 2px solid var(--acc);
  outline-offset: -2px;
}
body.mb-setup .mb-su-item-ic {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
body.mb-setup .mb-su-item-ic .eop-i {
  width: 17px;
  height: 17px;
  stroke-width: 1.85;
}
body.mb-setup .mb-su-item-lb {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}


/* ============================================================================
   5. RIGHT NOW — .mb-su-now
   ----------------------------------------------------------------------------
   The demo's .nowCard (setup-demo.html:171-177). Every value in it is real:
   is-live is eos_is_live(), the title is eos_get_current_service(), and the
   count is the same 90-second query GET /eos/v1/live-viewers answers. The card
   therefore has two genuine states and both are styled, rather than the demo's
   single "live" screenshot.
   ========================================================================= */
body.mb-setup .mb-su-now {
  margin: 0 4px;
  padding: 13px 14px;
  border: 1px solid var(--line);
  border-radius: var(--r-12);
  background: var(--stage);
}
body.mb-setup .mb-su-now-h {
  display: flex;
  align-items: center;
  gap: 8px;
}
body.mb-setup .mb-su-now-state {
  font-size: 12.5px;
  font-weight: 700;
  line-height: 1.3;
  color: var(--ink);
}
body.mb-setup .mb-su-now-sub {
  margin: 7px 0 0;
  font-size: 12px;
  line-height: 1.45;
  color: var(--muted);
}
body.mb-setup .mb-su-now-title {
  display: block;
}
body.mb-setup .mb-su-now-count {
  display: block;
  margin-top: 3px;
  font-variant-numeric: tabular-nums;
}
body.mb-setup .mb-su-now-count b {
  font-weight: 700;
  color: var(--body);
}

/* The dot. Live pulses; off air is a flat --faint disc with no animation, the
   demo's .nowDot.off exactly. The ring is mixed from --live rather than
   written as an rgba literal, so it follows the token in both themes. */
@keyframes mbSuNowPulse {
  0%   { box-shadow: 0 0 0 0 color-mix(in srgb, var(--live) 50%, transparent) }
  70%  { box-shadow: 0 0 0 9px color-mix(in srgb, var(--live) 0%, transparent) }
  100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--live) 0%, transparent) }
}
body.mb-setup .mb-su-now-dot {
  flex: 0 0 auto;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--faint);
}
body.mb-setup .mb-su-now.is-live .mb-su-now-dot {
  background: var(--live);
  animation: mbSuNowPulse 2s ease-out infinite;
}

/* The demo's .nowBtn is a <button>; ours is an <a> at /setup/broadcast/, so it
   needs the box a button gets for free. Its LABEL is driven by the override,
   not by `live` — see the renderer's note. Nothing here re-derives either. */
body.mb-setup .mb-su-now-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 32px;
  margin-top: 11px;
  padding: 0 10px;
  border: 1px solid var(--line);
  border-radius: var(--r-8);
  background: var(--card);
  color: var(--ink);
  font-size: 12.5px;
  font-weight: 600;
  line-height: 1;
  text-align: center;
  text-decoration: none;
  transition: background var(--dur) var(--ease),
              border-color var(--dur) var(--ease);
}
body.mb-setup .mb-su-now-btn:hover {
  background: var(--soft);
  border-color: var(--accLight);
}
body.mb-setup .mb-su-now-btn:focus-visible {
  outline: 2px solid var(--acc);
  outline-offset: 2px;
}


/* ============================================================================
   6. MAIN COLUMN — SHARED TYPE
   ----------------------------------------------------------------------------
   .mb-su-h1 is the hub's .hubH1 (setup-demo.html:186) and the space page's
   .spH1 (:202); the size steps down in section mode in section 8. The family
   is restated because event-operations.css:22 puts a heading face on every
   `.eop h1` and the mambo set is single-family.
   ========================================================================= */
body.mb-setup .mb-su-h1 {
  margin: 0;
  font-family: var(--font);
  font-size: clamp(26px, 3vw, 34px);
  font-weight: 700;
  letter-spacing: -.032em;
  line-height: 1.1;
  color: var(--ink);
}
body.mb-setup .mb-su-lede {
  max-width: 60ch;
  margin: 11px 0 0;
  font-size: 15.5px;
  line-height: 1.55;
  color: var(--body);
}


/* ============================================================================
   7. HUB — QUICK ACTIONS, GRID, FOOT
   ----------------------------------------------------------------------------
   .qa / .btnP / .btnS (setup-demo.html:188-192). Three real destinations, each
   an <a> that works with JavaScript off; a capability the viewer lacks removes
   the button entirely, so there is no disabled state to style.
   ========================================================================= */
body.mb-setup .mb-su-qa {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 26px;
}
body.mb-setup .mb-su-qa-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  height: 40px;
  padding: 0 15px;
  border: 1px solid var(--line);
  border-radius: var(--r-ctl);
  background: var(--btn2);
  color: var(--ink);
  font-size: 14px;
  font-weight: 600;
  line-height: 1;
  text-decoration: none;
  white-space: nowrap;
  transition: background var(--dur) var(--ease),
              border-color var(--dur) var(--ease),
              transform var(--dur) var(--ease);
}
body.mb-setup .mb-su-qa-btn:hover {
  background: var(--btn2Hover);
  border-color: var(--accLight);
  transform: translateY(-1px);
}
body.mb-setup .mb-su-qa-btn:active {
  transform: none;
}
body.mb-setup .mb-su-qa-btn:focus-visible {
  outline: 2px solid var(--acc);
  outline-offset: 2px;
}
/* The primary. Its focus ring uses --focus-on-acc, because an --acc ring on an
   --acc fill is invisible (mambo-tokens.css:41). */
body.mb-setup .mb-su-qa-btn.is-pri {
  padding: 0 16px;
  border-color: transparent;
  background: var(--acc);
  color: var(--onacc);
  box-shadow: var(--sc);
}
body.mb-setup .mb-su-qa-btn.is-pri:hover {
  background: var(--accHover);
  border-color: transparent;
}
body.mb-setup .mb-su-qa-btn.is-pri:focus-visible {
  outline-color: var(--focus-on-acc);
  outline-offset: -4px;
}
body.mb-setup .mb-su-qa-ic {
  display: inline-flex;
  align-items: center;
}
body.mb-setup .mb-su-qa-ic .eop-i {
  width: 17px;
  height: 17px;
  stroke-width: 1.9;
}

/* The grid's rhythm. mambo-setup.css:579 gives .eop-cards a 14px top margin
   from when it followed an eyebrow; the eyebrow is gone and .eop-mn is now the
   block that spaces itself — the two .eop-mn offsets, hub and section, are set
   together in section 8 so one edit moves both. */
body.mb-setup .mb-su-main .eop-cards {
  margin-top: 0;
}
/* .foot (setup-demo.html:199). .eop-mn's own 18px gap does most of it. */
body.mb-setup .mb-su-foot {
  margin: 2px 0 0;
}


/* ============================================================================
   8. SECTION MODE — SPACE HEAD AND ROLE PILL
   ----------------------------------------------------------------------------
   .spHead / .spH1 / .spDesc / .rolePill (setup-demo.html:201-204).
   ========================================================================= */
body.mb-setup .mb-su-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 18px;
}
body.mb-setup .mb-su-head-txt {
  min-width: 0;
  flex: 1 1 320px;
}
body.mb-setup .mb-su-head .mb-su-h1 {
  font-size: clamp(24px, 2.7vw, 31px);
  letter-spacing: -.03em;
  line-height: 1.12;
}
body.mb-setup .mb-su-head .mb-su-lede {
  max-width: 58ch;
  margin-top: 9px;
  font-size: 15px;
}

/* THE PILL IS STYLED OFF [data-mb-su-role], NEVER OFF ITS TEXT — the label is
   translated and the state is not. Three real states, because the renderer
   reads three real capabilities: administrator, can-manage, view-only. The
   demo only ever screenshotted the first; the other two are the same shape in
   the tokens their meaning asks for.  */
body.mb-setup .mb-su-role {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  flex: 0 0 auto;
  height: 30px;
  padding: 0 12px;
  border: 1px solid transparent;
  border-radius: var(--r-pill);
  background: var(--tint);
  color: var(--tintink);
  font-size: 12px;
  font-weight: 600;
  line-height: 1;
  white-space: nowrap;
}
body.mb-setup .mb-su-role[data-mb-su-role="manage"] {
  background: var(--accsoft);
  color: var(--acc);
}
body.mb-setup .mb-su-role[data-mb-su-role="view"] {
  background: var(--stage);
  border-color: var(--line);
  color: var(--muted);
}
body.mb-setup .mb-su-role .eop-i {
  width: 13px;
  height: 13px;
  stroke-width: 1.9;
}

/* THE DUPLICATE TITLE. section.eop-sec still opens with an <h3> printing the
   space label, and .mb-su-h1 now prints it 10px above. The h3 was left in the
   PHP deliberately — it lives inside the preserved control container — so it
   is hidden here instead. Do NOT delete it from the renderer; event-operations
   .js:10 observes .eop-sec and the section must keep its shape.
   (0,3,2) against mambo-setup.css:427's (0,2,2). */
body.mb-setup .mb-su-main .eop-sec > h3 {
  display: none;
}
/* THE TWO .eop-mn OFFSETS. Section mode follows the space head at the demo's
   .spBody 20px plus a little, since the jump rail is now the block's first
   visible child; the hub follows the quick actions at the demo's .hubGrid 26px
   (setup-demo.html:193, :208). The hub rule is (0,4,2) and the general one
   (0,3,1), so their order here is not load-bearing. */
body.mb-setup .mb-su-main .eop-mn {
  margin-top: 22px;
}
body.mb-setup .eop[data-eop-space="hub"] .mb-su-main .eop-mn {
  margin-top: 26px;
}


/* ============================================================================
   9. THE HUB TILES ARE UNIFORM
   ----------------------------------------------------------------------------
   THE SCREENSHOT BUG. event-operations.css:549-557 tints each hub card by
   :nth-child — red, green, blue, grey down the grid. mambo-setup.css:846-854
   already re-keyed those nine accents off the card's href, because :nth-child
   mis-assigns every colour after a space the viewer cannot open
   (setup-render.php skips it, and the positions shift).

   But the demo does not want nine colours at all. Its .hubTile
   (setup-demo.html:196) is a SINGLE pair — var(--tint2) on var(--tint2ink) —
   for every tile in the grid, and the card's hover edge is one --accLight. The
   per-space accent is a SPACE-page idea: it belongs on /setup/events/, where
   one accent identifies where you are. On the hub it is nine competing signals
   for nine equal choices.

   So the hub grid is flattened back to the one accent, keyed off [href] so the
   rule scores (0,6,1) — level with mambo-setup.css's nine keyed rules — and
   wins on being later in source. Every card has an href, so every card matches.
   The nine per-space accents on `.eop[data-eop-space=...]` are untouched and
   still colour each space page.

   --as is restated beside --a because it is declared on `.eop` as a formula
   over --a and would otherwise have resolved there, one level up.
   --accsoft is used rather than --tint2: mambo-tokens.css declares the two
   identically in both themes, and --tint2 is one of the five names
   event-operations.css:6 shadows inside `.eop`.
   ========================================================================= */
body.mb-setup .eop[data-eop-space="hub"] .eop-cards > .eop-cardlink[href] {
  --a: var(--acc);
  --as: var(--accsoft);
}
/* The demo's hub card hover is a border tint and a lift, nothing more; the
   accent bar mambo-setup.css draws down the left edge is now one colour for
   every card, which is a texture rather than a signal. It is kept — it costs
   nothing and reads as the same family as the rail's current item. */
body.mb-setup .eop[data-eop-space="hub"] .eop-cards > .eop-cardlink[href]:hover {
  border-color: var(--accLight);
}


/* ============================================================================
   10. THE DRAWER — .mb-su-scrim / .mb-su-drawer
   ----------------------------------------------------------------------------
   BOTH NODES SHIP WITH NO INLINE HIDING. They are plain divs in the DOM on
   every Setup page, in both modes, and the drawer holds a SECOND COPY of the
   rail. Until this block exists the page renders a duplicate navigation list
   in the flow. The closed state below is therefore not decoration — it is the
   only thing keeping the page correct, and it must hold with JavaScript
   broken, which is why nothing here depends on a JS-only class being ABSENT.

   visibility:hidden, not opacity alone. An off-screen drawer that is merely
   translated is still in the tab order: a keyboard user would tab through nine
   invisible space links before reaching the page. visibility removes it and
   is transitioned with a delay so the slide-out is still seen on close.

   z-index sits above the site header (mambo-chrome.css:25 = 95) so the drawer
   is not slid under it, and below the console toast (mambo-setup.css:776 =
   99999) and the chrome palette (mambo-chrome.css:406 = 200).
   ========================================================================= */
body.mb-setup .mb-su-scrim {
  position: fixed;
  inset: 0;
  z-index: 96;
  background: var(--scrim);
  backdrop-filter: blur(var(--scrim-blur));
  -webkit-backdrop-filter: blur(var(--scrim-blur));
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity var(--dur-crossfade) var(--ease),
              visibility 0s linear var(--dur-crossfade);
}
body.mb-setup .mb-su-drawer {
  position: fixed;
  inset: 0 auto 0 0;
  z-index: 97;
  width: min(84vw, 286px);
  max-width: 100%;
  background: var(--card);
  border-right: 1px solid var(--line);
  box-shadow: var(--sh);
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  transform: translateX(-100%);
  visibility: hidden;
  transition: transform var(--dur-drawer) var(--ease),
              visibility 0s linear var(--dur-drawer);
}


/* ============================================================================
   11. RESPONSIVE
   ----------------------------------------------------------------------------
   Real media queries, from the authored set only. 940 is the flip: 246 rail +
   34 gap + 660 minimum table — the demo's own arithmetic.

   THE OPEN STATE LIVES INSIDE THE 940 QUERY, DELIBERATELY. Above 940 the rail
   is the navigation and the trigger is display:none, so a drawer that could
   still be opened — by a stale class left behind when the window was widened,
   or by any script adding .is-open — would cover the page with a rail that is
   already visible beside it. Confining the open state to the width that owns
   it makes that impossible without needing a min-width query, which the
   authored breakpoint set does not offer.
   ========================================================================= */
@media (max-width: 1120px) {
  /* A narrower gap on a 13-inch laptop; the rail keeps its 246 so the space
     names never truncate. */
  body.mb-setup .mb-su-shell {
    gap: 22px;
  }
}

@media (max-width: 940px) {
  body.mb-setup .mb-su-shell {
    grid-template-columns: minmax(0, 1fr);
  }
  /* The child combinator is what protects the drawer's copy — it is not in the
     shell, so it is never caught by this. */
  body.mb-setup .mb-su-shell > .mb-su-rail {
    display: none;
  }
  body.mb-setup .mb-su-menu {
    display: inline-flex;
  }
  body.mb-setup .mb-su-scrim.is-open {
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    transition: opacity var(--dur-crossfade) var(--ease),
                visibility 0s linear 0s;
  }
  body.mb-setup .mb-su-drawer.is-open {
    transform: translateX(0);
    visibility: visible;
    transition: transform var(--dur-drawer) var(--ease),
                visibility 0s linear 0s;
  }
}

@media (max-width: 620px) {
  body.mb-setup .eop .eop-hd {
    margin-bottom: 14px;
    padding-bottom: 12px;
  }
  body.mb-setup .mb-su-qa {
    margin-top: 20px;
  }
  body.mb-setup .mb-su-main .eop-mn,
  body.mb-setup .eop[data-eop-space="hub"] .mb-su-main .eop-mn {
    margin-top: 20px;
  }
  body.mb-setup .mb-su-head {
    gap: 12px;
  }
  body.mb-setup .mb-su-lede {
    font-size: 15px;
  }
}

@media (max-width: 520px) {
  /* The demo folds its quick actions to full width on a phone
     (setup-demo.html:681) — three 40px pills wrapping two-and-one reads as a
     mistake at 375. */
  body.mb-setup .mb-su-qa-btn {
    flex: 1 1 100%;
  }
  body.mb-setup .mb-su-drawer {
    width: min(88vw, 286px);
  }
  body.mb-setup .mb-su-role {
    height: 28px;
    padding: 0 10px;
  }
}


/* ============================================================================
   12. REDUCED MOTION
   ----------------------------------------------------------------------------
   Every animation and transition this file introduces is cancelled. Transforms
   are zeroed alongside the transitions wherever the transform was decoration —
   but NOT on the drawer: translateX(-100%) is how the closed drawer stays off
   the page, and clearing it would drop a full-height panel over the content of
   every Setup page for exactly the users least able to deal with it. The
   drawer keeps its geometry and simply loses the slide.

   No !important anywhere: nothing above is overriding an !important rule, and
   the shipped reduced-motion block (event-operations.css:630-638) names no
   selector this file owns.
   ========================================================================= */
@media (prefers-reduced-motion: reduce) {
  body.mb-setup .mb-su-menu,
  body.mb-setup .mb-su-menu:hover,
  body.mb-setup .mb-su-item,
  body.mb-setup .mb-su-item:hover:not([aria-current]),
  body.mb-setup .mb-su-now-btn,
  body.mb-setup .mb-su-now-btn:hover,
  body.mb-setup .mb-su-qa-btn,
  body.mb-setup .mb-su-qa-btn:hover,
  body.mb-setup .mb-su-qa-btn:active {
    transition: none;
    transform: none;
  }
  body.mb-setup .mb-su-now.is-live .mb-su-now-dot {
    animation: none;
  }
  /* The .is-open pair is named as well as the closed pair: the open rules live
     inside the 940 query at (0,3,1) and would otherwise out-specify a plain
     (0,2,1) reset and keep their transition. */
  body.mb-setup .mb-su-scrim,
  body.mb-setup .mb-su-scrim.is-open,
  body.mb-setup .mb-su-drawer,
  body.mb-setup .mb-su-drawer.is-open,
  body.mb-setup .eop-hd .savedPill.on {
    transition: none;
  }
}

/* =========================================================================
   11. THE EVENT / BROADCAST EDITOR                                  v3.7.1

   The shipped editor was a four-step wizard; the design is one scrolling
   column with a section rail beside it. event-form.js stands its wizard down
   when the form carries .mb-su-edform, and mambo-setup.js builds the rail as
   a SIBLING placed immediately before the form.

   The form is deliberately NOT re-parented into a fixed overlay. The date
   picker inside it (.eop-dt-pop) is absolutely positioned, and a fixed or
   transformed ancestor would become its containing block and re-anchor it
   against the viewport instead of its field. The design's look is reached
   here without inheriting that bug.
   ====================================================================== */

body.mb-setup .mb-su-edwrap {
  display: grid;
  grid-template-columns: 226px minmax(0, 1fr);
  gap: clamp(16px, 2vw, 28px);
  align-items: start;
}

body.mb-setup .mb-su-edrail {
  position: sticky;
  top: calc(var(--mb-setup-top, 0px) + 14px);
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 10px;
  border: 1px solid var(--line);
  border-radius: var(--r-card, 14px);
  background: var(--card);
  box-shadow: var(--sc);
  max-height: calc(100vh - var(--mb-setup-top, 0px) - 28px);
  overflow-y: auto;
  overscroll-behavior: contain;
}

body.mb-setup .mb-su-edrail-item {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  padding: 8px 10px;
  border: 0;
  border-radius: var(--r-ctl, 10px);
  background: none;
  color: var(--muted);
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
  transition: background var(--dur-micro, .12s) var(--ease),
              color var(--dur-micro, .12s) var(--ease);
}
body.mb-setup .mb-su-edrail-item:hover { background: var(--stage); color: var(--ink); }
body.mb-setup .mb-su-edrail-item.is-on { background: var(--tint); color: var(--tintink); }
body.mb-setup .mb-su-edrail-item:focus-visible {
  outline: 2px solid var(--acc);
  outline-offset: 2px;
}
body.mb-setup .mb-su-edrail-ic {
  display: inline-flex;
  flex: 0 0 auto;
  width: 17px;
  height: 17px;
  color: currentColor;
}
body.mb-setup .mb-su-edrail-ic svg { width: 17px; height: 17px; }
body.mb-setup .mb-su-edrail-lb {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The section heading gains the design's one-line description. The label span
   keeps its old shape exactly; the sub is a new sibling, opted into by class,
   so a heading without one is the element it has always been. */
body.mb-setup .eop-fsec.mb-su-fsec {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  column-gap: 9px;
  row-gap: 2px;
}
body.mb-setup .eop-fsec.mb-su-fsec > .mb-su-fsub {
  grid-column: 2;
  font-size: 12.5px;
  font-weight: 400;
  line-height: 1.45;
  color: var(--muted);
  text-wrap: pretty;
}

@media (max-width: 1020px) {
  body.mb-setup .mb-su-edwrap { grid-template-columns: minmax(0, 1fr); }
  /* The rail becomes a horizontal strip above the form — the design's own
     narrow-screen answer, and the same shape the jump rail already uses. */
  body.mb-setup .mb-su-edrail {
    position: static;
    flex-direction: row;
    gap: 6px;
    max-height: none;
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: thin;
    -webkit-overflow-scrolling: touch;
  }
  body.mb-setup .mb-su-edrail-item { width: auto; flex: 0 0 auto; }
}

@media (prefers-reduced-motion: reduce) {
  body.mb-setup .mb-su-edrail-item { transition: none; }
}

/* =========================================================================
   12. THE EDITOR AS ITS OWN SPACE                                   v3.8.0

   Dennis's correction: Add event gets a full-screen editor, exactly as the
   design has it. v3.7.1 put the design's LANGUAGE on the inline card, which
   was not the ask.

   Two facts make this safe, and they are why nothing is re-parented:

   1. event-operations.js:128 already toggles `.on` on #eop-ev-view-add when
      the Add / All tabs are pressed. That IS the open/close state, so the
      overlay needs no new trigger and no node moved into a new container.

   2. .eop-dt is `position: relative` (event-operations.css:270), so the date
      popover at :281 is anchored to its own field and NOT to a distant
      ancestor. Making an ancestor fixed cannot re-anchor it. The only real
      risk was CLIPPING by the scroll container, answered at the bottom of
      this block.

   The chain above #eop-ev-view-add carries no transform, filter, perspective
   or paint containment, so `position: fixed` resolves against the viewport.
   Any of those added later WOULD break this — that is the thing to protect.
   ====================================================================== */


/* v3.14.0 — THE EDITOR OVERLAY IS NO LONGER KEYED TO ONE ID.
   Every rule in this block used to name `#eop-ev-view-add` literally, which
   meant the full-screen editor was the Events space's alone. Setup -> Sermons
   renders the same editor shape and could not have it without either copying
   this block (two sheets describing one surface) or stealing the id — which is
   impossible, because assets/js/event-operations.js binds the EVENTS console to
   `#eop-ev-form` and would post every sermon save to `eos_setup_event_save`.

   So each selector below now also accepts the marker class `.mb-su-edview`,
   which inc/setup-render.php puts on the sermons editor view and DELIBERATELY
   NOT on the broadcast one — Broadcast keeps its inline editor exactly as it
   is. The events id is left in place untouched, so the Events space computes
   byte-identical styles. This is a generalisation, not a behaviour change. */
body.mb-setup .eop-ev-view#eop-ev-view-add.on,
body.mb-setup .eop-ev-view.mb-su-edview.on {
  position: fixed;
  inset: 0;
  z-index: 120;
  display: grid;
  grid-template-rows: auto minmax(0, 1fr);
  /* v3.14.1 — THE COLUMN HAD TO BE DECLARED.
     With only the rows named, the single implicit column is `auto`, which
     sizes to the MAX-CONTENT of the widest thing inside it. `.mb-su-edact` is
     `flex: 0 0 auto` (set inline by mambo-setup.js) and therefore never
     shrinks, so its intrinsic width propagated all the way up: at 375px the
     header, the section rail and the progress bar all computed 367px inside a
     360px viewport, and the primary Save button's right edge landed at 386 —
     26px off-screen, on a fixed overlay that cannot scroll sideways to reach
     it. Measured on BOTH spaces: Events clipped Save by 7px and Sermons, whose
     footer carries one control more, by 26px. minmax(0, 1fr) caps the track at
     the viewport and is computed identically at every width where the old
     `auto` already resolved to the viewport — which is every width at or above
     431px, verified unchanged. */
  grid-template-columns: minmax(0, 1fr);
  background: var(--pg);
  animation: none;
}

/* The header: title, state, and the REAL controls moved up from .eop-evfoot
   by mambo-setup.js. No new save button is created anywhere — a second Save
   that wrote nothing would be worse than no redesign at all. */
body.mb-setup .mb-su-edhd {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 12px clamp(14px, 2.4vw, 26px);
  border-bottom: 1px solid var(--line);
  background: var(--card);
}
body.mb-setup .mb-su-edx {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 36px;
  height: 36px;
  border: 1px solid var(--line);
  border-radius: var(--r-ctl, 10px);
  background: var(--btn2);
  color: var(--ink);
  cursor: pointer;
  transition: background var(--dur-micro, .12s) var(--ease);
}
body.mb-setup .mb-su-edx:hover { background: var(--stage); }
body.mb-setup .mb-su-edx:focus-visible { outline: 2px solid var(--acc); outline-offset: 2px; }

body.mb-setup .mb-su-edttl { min-width: 0; flex: 1 1 auto; }
body.mb-setup .mb-su-edttl-row {
  display: flex;
  align-items: center;
  gap: 9px;
  min-width: 0;
}
body.mb-setup .mb-su-edttl-row > b {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 15.5px;
  font-weight: 700;
  letter-spacing: -.015em;
  color: var(--ink);
}
body.mb-setup .mb-su-edsave {
  display: block;
  margin-top: 1px;
  font-size: 12px;
  color: var(--muted);
}

/* The progress bar the design puts under the header. Width is set by JS from
   how many sections have a value — never from step position, because there
   are no steps any more. */
body.mb-setup .mb-su-edprog {
  height: 3px;
  background: var(--line2);
  overflow: hidden;
}
body.mb-setup .mb-su-edprog > span {
  display: block;
  height: 100%;
  width: 0;
  background: var(--acc);
  transition: width var(--dur, .24s) var(--ease);
}

/* Body: the rail beside one scrolling column. */
body.mb-setup #eop-ev-view-add.on .mb-su-edbody,
body.mb-setup .mb-su-edview.on .mb-su-edbody {
  display: grid;
  grid-template-columns: 262px minmax(0, 1fr);
  min-height: 0;
}
body.mb-setup #eop-ev-view-add.on .mb-su-edrail,
body.mb-setup .mb-su-edview.on .mb-su-edrail {
  position: static;
  border: 0;
  border-right: 1px solid var(--line);
  border-radius: 0;
  box-shadow: none;
  background: var(--card);
  max-height: none;
  padding: 18px 14px;
  gap: 3px;
  overflow-y: auto;
}
body.mb-setup #eop-ev-view-add.on .mb-su-edscroll,
body.mb-setup .mb-su-edview.on .mb-su-edscroll {
  min-width: 0;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: clamp(16px, 2.4vw, 30px);
  /* Room for .eop-dt-pop (280px tall-ish) to open downward without being
     clipped by this scrollport. The popover is anchored to its field, so all
     it needs is somewhere to land. */
  padding-bottom: 320px;
}
body.mb-setup #eop-ev-view-add.on .eop-evform,
body.mb-setup .mb-su-edview.on .eop-evform {
  max-width: 940px;
  margin-inline: auto;
  border: 0;
  padding: 0;
  background: none;
  box-shadow: none;
}
/* The footer is emptied into the header, so it must not also reserve space. */
body.mb-setup #eop-ev-view-add.on .eop-evfoot.is-mbmoved,
body.mb-setup .mb-su-edview.on .eop-evfoot.is-mbmoved { display: none; }

/* The rail's completion dots, as the design draws them. */
body.mb-setup .mb-su-edrail-dot {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 19px;
  height: 19px;
  border: 1.6px solid var(--line);
  border-radius: 50%;
  color: transparent;
}
body.mb-setup .mb-su-edrail-item.is-done .mb-su-edrail-dot {
  border-color: var(--acc);
  background: var(--acc);
  color: var(--onacc);
}
body.mb-setup .mb-su-edrail-dot svg { width: 11px; height: 11px; }

@media (max-width: 940px) {
  body.mb-setup #eop-ev-view-add.on .mb-su-edbody,
  body.mb-setup .mb-su-edview.on .mb-su-edbody { grid-template-columns: minmax(0, 1fr); grid-template-rows: auto minmax(0, 1fr); }
  body.mb-setup #eop-ev-view-add.on .mb-su-edrail,
  body.mb-setup .mb-su-edview.on .mb-su-edrail {
    flex-direction: row;
    border-right: 0;
    border-bottom: 1px solid var(--line);
    padding: 10px 12px;
    overflow-x: auto;
    overflow-y: hidden;
  }
  body.mb-setup #eop-ev-view-add.on .mb-su-edrail-item,
  body.mb-setup .mb-su-edview.on .mb-su-edrail-item { width: auto; flex: 0 0 auto; }
}
@media (max-width: 620px) {
  body.mb-setup .mb-su-edhd { flex-wrap: wrap; gap: 10px; }
  body.mb-setup .mb-su-edttl { flex: 1 1 100%; order: -1; }
}
/* v3.14.1 — the second half of the same fix. Capping the grid track stops the
   HEADER overflowing, but `.mb-su-edact` itself is still one unbreakable row
   of controls (status segment + Cancel + Save) whose max-content is 372px, so
   on a 360px viewport it stayed 26px over the edge even after wrapping onto
   its own line. Letting that row wrap internally is the only thing that puts
   Save back on screen. Scoped to 430px so that every width at which the row
   already fitted — 480px and up, including the 414px class of phone under the
   grid fix — computes exactly as it shipped. */
@media (max-width: 430px) {
  body.mb-setup .mb-su-edact { flex-wrap: wrap; max-width: 100%; }
}
@media (prefers-reduced-motion: reduce) {
  body.mb-setup .mb-su-edx,
  body.mb-setup .mb-su-edprog > span { transition: none; }
}


/* =========================================================================
   13. ATTENDANCE — THE MAP, THE CHIPS AND THE PEOPLE TABLE          v3.8.0

   Dennis's correction: the demo's Attendance space has FIVE cards and the
   upgrade shipped two. setup-render.php v3.8.0 now emits all five; this
   section dresses the three that were missing — "Where the family gathered",
   "Who was there" and "Export".

   THE DESIGN SOURCE, line by line, is _proto/setup-demo.html:370-473 (the
   attendance block) and :703-711 (its narrow-screen block). Every shape below
   names the demo rule it answers. Nothing is invented: the tiles, the map
   frame, the pan layer, the graticule, the segmented controls, the names
   switch, the heat legend, the zoom cluster, the tooltip, the two chip
   idioms and the people table are all ported.

   FOUR THINGS THE PORT HAD TO CHANGE, AND WHY
   ---------------------------------------------------------------------
   (a) THE --card TRAP. event-operations.css:6-20 declares a LEGACY palette
       ON `.eop` itself, and `--card` is in it: inside the console `--card`
       is #F1EFE9, a warm grey, not the white card surface the demo means.
       mambo-setup.css:1061 already rescues the real one as `--mbc-card` at
       body level. Every surface below therefore reads --mbc-card, and the
       map host RE-DECLARES `--card: var(--mbc-card)` on itself so that the
       ported drawing code — which paints `var(--soft)`, `var(--line)` and
       `color-mix(in srgb, var(--acc) N%, var(--card))` straight onto SVG
       attributes, exactly as attHeatShade() does — resolves to the right
       colour without the JS having to know about the alias. The alias is
       scoped to the map subtree, so no shipped .eop rule elsewhere in the
       card sees a different --card than it does today.

   (b) THE SHADOW LITERALS. The demo writes rgba(24,20,16,.45) and
       rgba(0,0,0,.5) directly. A brown-black shadow is correct on the light
       skin and wrong on the dark one, and a raw colour is not allowed here
       anyway, so each is replaced by the token shadow that already carries
       both skins: --sc for the small lifts, --sh for the map hover, --pop
       for the tooltip.

   (c) THE KNOB'S WHITE. `.atNames i b` is a white dot. --onacc is white on
       the light skin but near-black on the dark one, which is right ON the
       accent and wrong on the grey OFF track. --mbc-card is white on light
       and the card ink on dark: correct in both states, so the knob takes
       that.

   (d) NO PINS, NO CITY. The demo plots one pin per person per CITY because
       its rows are fabricated. inc/attendance.php stores a two-letter
       country and nothing finer — no city, no latitude, no longitude — so
       .atPin, .atRing and the city halo have no port and are deliberately
       absent. The country REGION carries the value instead, which is what
       attDrawWorld()'s COUNTRIES layer already draws.

   HOUSE RULES, unchanged: every selector starts body.mb-setup; colour,
   radius, shadow, easing and duration come from tokens only and anything the
   token set does not supply is mixed over a token; the authored breakpoints
   used here are 620 and 520; every transition and animation introduced below
   is cancelled at the foot of the section; no @import, no remote url(), no
   grid track carrying a bare px length.
   ========================================================================= */


/* ---- 13a. CARD 1 HEAD — title beside the four stat tiles.
   setup-render.php:928 wraps the h4 in .mb-su-geo-head > .mb-su-geo-ttl, so
   `body.mb-setup .eop-card > h4` (mambo-setup.css:1127) no longer reaches it.
   The heading shape is restated here at the depth the markup actually has.
   The card lede is safe: `.eop-card .sub` is a descendant rule. ---------- */
body.mb-setup .mb-su-geo-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 14px;
}
body.mb-setup .mb-su-geo-ttl {
  flex: 1 1 min(100%, 280px);
  min-width: 0;
}
body.mb-setup .mb-su-geo-ttl > h4,
body.mb-setup .mb-su-who-head > h4 {
  display: flex;
  align-items: center;
  gap: 9px;
  flex-wrap: wrap;
  margin: 0 0 4px;
  padding-bottom: 0;
  border-bottom: 0;
  font-size: 15.5px;
  font-weight: 700;
  letter-spacing: -.015em;
  line-height: 1.3;
  color: var(--ink);
}
body.mb-setup .mb-su-geo-ttl > h4 .eop-i,
body.mb-setup .mb-su-who-head > h4 .eop-i {
  width: 17px;
  height: 17px;
  color: var(--muted);
}
/* The lede sits under the title, not under the tiles. */
body.mb-setup .mb-su-geo-ttl > .sub {
  margin: 8px 0 0;
}


/* ---- 13b. THE FOUR STAT TILES — the demo's .atStats / .atTile (:377-388).
   Real buttons, so keyboard and the console's focus ring come free. Two of
   the four are toggles and carry aria-pressed; the two actions do not, which
   is why the pressed look keys on the attribute and not on a class. ------ */
body.mb-setup .mb-su-attiles {
  display: flex;
  flex: 0 1 auto;
  flex-wrap: wrap;
  gap: 9px;
}
body.mb-setup .mb-su-attile {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  min-width: 0;
  padding: 9px 13px;
  border: 1px solid var(--line2);
  border-radius: var(--r-field);
  background: var(--stage);
  font-family: inherit;
  text-align: left;
  cursor: pointer;
  box-shadow: inset 0 -2px 0 transparent;
  transition: transform var(--dur-pop) var(--spring),
              background var(--dur-micro) ease,
              border-color var(--dur-micro) ease,
              box-shadow var(--dur-micro) ease;
}
body.mb-setup .mb-su-attile:hover {
  transform: translateY(-2px);
  background: var(--soft);
}
body.mb-setup .mb-su-attile:active { transform: translateY(0); }
body.mb-setup .mb-su-attile:focus-visible {
  outline: 2px solid var(--acc);
  outline-offset: 2px;
}
body.mb-setup .mb-su-attile[aria-pressed="true"] {
  border-color: var(--acc);
  background: var(--accsoft);
  box-shadow: inset 0 -2px 0 var(--acc);
}
body.mb-setup .mb-su-attile > b {
  font-size: 18px;
  font-weight: 750;
  letter-spacing: -.02em;
  line-height: 1.15;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
body.mb-setup .mb-su-attile > span {
  font-size: 11.5px;
  line-height: 1.3;
  color: var(--muted);
}
body.mb-setup .mb-su-attile[aria-pressed="true"] > span { color: var(--tint2ink); }


/* ---- 13c. THE MAP HOST — the demo's .atMap (:390-391).

   THE ASPECT-RATIO IS NOT DECORATION. The host is empty until the drawing
   code fills it, and everything the drawing code adds is absolutely
   positioned, so without a ratio the box computes to zero height and the
   card shows a hairline where the world should be. It is stated
   unconditionally, for that reason: it is correct while the map is drawn AND
   correct when JavaScript never runs.

   touch-action: pan-y is the demo's own — the map claims the horizontal
   gesture for panning and hands the vertical one back to the page, so a
   thumb-scroll down the console is never swallowed by the map.

   The --card alias is (a) in the section note above.
   ---------------------------------------------------------------------- */
body.mb-setup .mb-su-atmap {
  --card: var(--mbc-card);
  position: relative;
  margin-top: 16px;
  aspect-ratio: 2 / 1;
  border: 1px solid var(--line2);
  border-radius: var(--r-card);
  background: var(--mbc-card);
  overflow: hidden;
  touch-action: pan-y;
  transition: box-shadow var(--dur-crossfade) ease;
}
body.mb-setup .mb-su-atmap:hover {
  box-shadow: inset 0 0 0 1px var(--line), var(--sh);
}
/* The world itself. The drawing code sets these inline as well; stating them
   here means a redraw that forgets one cannot collapse the map. */
body.mb-setup .mb-su-atmap > svg,
body.mb-setup .mb-su-atlayer > svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
}
/* A country region is focusable when someone joined from it. */
body.mb-setup .mb-su-atmap svg path[data-country]:focus-visible {
  outline: 2px solid var(--acc);
  outline-offset: 1px;
}

/* THE NO-SCRIPT FALLBACK. Server-rendered .eop-meter rows, the same renderer
   the Reports card uses, so the card states its numbers before a single line
   of script has run and states them forever if none ever does. It is laid
   over the host rather than under it, so when the drawing code hides or
   removes it the host keeps its height from the ratio above. */
body.mb-setup .mb-su-atfallback {
  position: absolute;
  inset: 0;
  z-index: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 1px;
  padding: clamp(14px, 3vw, 28px);
  overflow: auto;
  overscroll-behavior: contain;
}
/* ...and the line that lets it actually go away. `display:flex` above is an
   AUTHOR rule, so it outranks the user-agent's `[hidden]{display:none}` no
   matter how the element is hidden. assets/js/mambo-attendance.js:594 hides
   this element with `fallbackEl.hidden = true` (never removing it, so nothing
   the renderer emitted is destroyed) — without this rule that assignment is
   inert and eight meter bars paint over the finished map at z-index 1.
   Any rule that gives an element a display MUST answer [hidden] itself. */
body.mb-setup .mb-su-atfallback[hidden] { display: none; }
body.mb-setup .mb-su-atfallback > .eop-empty {
  max-width: 46ch;
  margin: auto;
}
/* OPTIONAL, and off unless something adds the class: when there is nothing
   to draw at all, a 600px-tall empty panel is worse than none. The renderer
   may put .mb-su-atmap--flat on the host in its zero-row branch and the box
   sizes to the message instead. The default above stays the safe one. */
body.mb-setup .mb-su-atmap.mb-su-atmap--flat { aspect-ratio: auto; }
body.mb-setup .mb-su-atmap--flat .mb-su-atfallback {
  position: static;
  inset: auto;
  overflow: visible;
}


/* ---- 13d. THE PAN LAYER AND ITS GRATICULE — .atLayer / .atGrid / .atEq
   (:392-394). The layer is the only thing that scales; every control below
   is a sibling of it on the host, which is how the demo keeps a 25px button
   from becoming a 75px button at 3x zoom. ------------------------------- */
body.mb-setup .mb-su-atlayer {
  position: absolute;
  inset: 0;
  transform-origin: 0 0;
  will-change: transform;
  transition: transform var(--dur-sheet) var(--ease);
}
body.mb-setup .mb-su-atgrid {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  opacity: .28;
  background:
    repeating-linear-gradient(90deg, transparent 0 8.333%, var(--line2) 8.333% calc(8.333% + 1px)),
    repeating-linear-gradient(transparent 0 16.666%, var(--line2) 16.666% calc(16.666% + 1px));
}
body.mb-setup .mb-su-ateq {
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  height: 1px;
  z-index: 1;
  pointer-events: none;
  opacity: .4;
  background: var(--line);
}


/* ---- 13e. THE CONTROL CLUSTER — .atTop / .atSeg / .atNames (:396-411).
   Reach vs Detail, Outline vs Heat vs Plain, the heat metric picker and the
   names switch. Three segmented looks, distinguished by a modifier rather
   than by three class names, because they are one control. --------------- */
body.mb-setup .mb-su-attop {
  position: absolute;
  left: 12px;
  top: 12px;
  right: 12px;
  z-index: 6;
  display: flex;
  align-items: center;
  gap: 7px;
  flex-wrap: wrap;
}
body.mb-setup .mb-su-atseg {
  display: inline-flex;
  gap: 2px;
  padding: 3px;
  border: 1px solid var(--line);
  border-radius: var(--r-ctl);
  background: var(--mbc-card);
  box-shadow: var(--sc);
}
body.mb-setup .mb-su-atseg button {
  height: 25px;
  padding: 0 11px;
  border: 0;
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--muted);
  font: inherit;
  font-size: 11.5px;
  font-weight: 650;
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--dur) ease, color var(--dur) ease;
}
body.mb-setup .mb-su-atseg button:hover {
  background: var(--soft);
  color: var(--body);
}
body.mb-setup .mb-su-atseg button:focus-visible {
  outline: 2px solid var(--acc);
  outline-offset: 1px;
}
body.mb-setup .mb-su-atseg--acc button[aria-checked="true"] {
  background: var(--acc);
  color: var(--onacc);
}
body.mb-setup .mb-su-atseg--ink button[aria-checked="true"] {
  background: var(--ink);
  color: var(--mbc-card);
}
body.mb-setup .mb-su-atseg--soft button { padding: 0 10px; }
body.mb-setup .mb-su-atseg--soft button[aria-checked="true"] {
  background: var(--accsoft);
  color: var(--acc);
}

/* The names switch. The console's own .eop-sw is 44x26 and would break a
   31px control row, so this is that switch at map scale — the demo's
   reasoning, not a second vocabulary. */
body.mb-setup .mb-su-atnames {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  height: 31px;
  padding: 0 11px 0 9px;
  border: 1px solid var(--line);
  border-radius: var(--r-ctl);
  background: var(--mbc-card);
  color: var(--muted);
  font: inherit;
  font-size: 11.5px;
  font-weight: 650;
  cursor: pointer;
  box-shadow: var(--sc);
  transition: background var(--dur) ease,
              border-color var(--dur) ease,
              color var(--dur) ease;
}
body.mb-setup .mb-su-atnames:hover { background: var(--soft); color: var(--body); }
body.mb-setup .mb-su-atnames:focus-visible {
  outline: 2px solid var(--acc);
  outline-offset: 2px;
}
body.mb-setup .mb-su-atnames[aria-checked="true"] {
  border-color: var(--acc);
  background: var(--accsoft);
  color: var(--acc);
}
body.mb-setup .mb-su-atnames > i {
  position: relative;
  flex: 0 0 auto;
  width: 26px;
  height: 15px;
  border-radius: var(--r-pill);
  background: var(--line);
  transition: background var(--dur) ease;
}
body.mb-setup .mb-su-atnames[aria-checked="true"] > i { background: var(--acc); }
/* The knob takes --btn2 and the halo, which is what body.mb-setup .eop-sw::after
   (mambo-setup.css:1267-1275) already does for the console's own switch and for
   the same reason: on the dark skin --btn2 is #2A2825 and the OFF track is
   --line over the card, two darks a few points apart. The halo mixes over --ink,
   which inside .eop is #F8F8F6 in dark, so the separation that is a drop shadow
   on the light skin becomes a soft rim on the dark one. One switch, one answer. */
body.mb-setup .mb-su-atnames > i > b {
  position: absolute;
  top: 2px;
  left: 2px;
  width: 11px;
  height: 11px;
  border-radius: 50%;
  background: var(--btn2);
  box-shadow: 0 1px 3px color-mix(in srgb, var(--ink) 28%, transparent);
  transition: left var(--dur-enter) var(--spring);
}
body.mb-setup .mb-su-atnames[aria-checked="true"] > i > b { left: 13px; }


/* ---- 13f. THE HEAT LEGEND — .atLeg (:413-419). Five steps, not a ramp, so
   a shade on the map can be matched back to a band. The band swatches carry
   an inline background from the drawing code; that colour mixes over
   var(--card), which the host aliases, so the swatch and the region agree. */
body.mb-setup .mb-su-atleg {
  position: absolute;
  left: 12px;
  bottom: 12px;
  z-index: 6;
  display: flex;
  align-items: center;
  gap: 9px;
  height: 31px;
  padding: 0 12px;
  border: 1px solid var(--line);
  border-radius: var(--r-ctl);
  background: var(--mbc-card);
  box-shadow: var(--sc);
}
body.mb-setup .mb-su-atleg-lb {
  font-size: 11px;
  font-weight: 650;
  color: var(--muted);
  white-space: nowrap;
}
body.mb-setup .mb-su-atleg-lo {
  font-size: 11px;
  color: var(--faint);
  font-variant-numeric: tabular-nums;
}
body.mb-setup .mb-su-atleg-hi {
  font-size: 11px;
  font-weight: 650;
  color: var(--body);
  font-variant-numeric: tabular-nums;
}
body.mb-setup .mb-su-atleg-bands { display: inline-flex; gap: 2px; }
body.mb-setup .mb-su-atleg-bd {
  width: 18px;
  height: 11px;
  border: 1px solid var(--line2);
  border-radius: 3px;
  cursor: pointer;
  transition: transform var(--dur-pop) var(--spring), box-shadow var(--dur-pop) ease;
}
body.mb-setup .mb-su-atleg-bd:hover,
body.mb-setup .mb-su-atleg-bd:focus-visible {
  transform: translateY(-2px) scale(1.12);
  box-shadow: var(--sc);
}
body.mb-setup .mb-su-atleg-bd:focus-visible {
  outline: 2px solid var(--acc);
  outline-offset: 2px;
}


/* ---- 13g. THE ZOOM CLUSTER — .atZoom (:421-424). ---------------------- */
body.mb-setup .mb-su-atzoom {
  position: absolute;
  right: 12px;
  bottom: 12px;
  z-index: 6;
  display: flex;
  align-items: center;
  gap: 6px;
}
body.mb-setup .mb-su-atzoom button {
  width: 30px;
  height: 30px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--mbc-card);
  color: var(--ink);
  cursor: pointer;
  box-shadow: var(--sc);
  transition: background var(--dur) ease, transform var(--dur) var(--spring);
}
body.mb-setup .mb-su-atzoom button:hover {
  background: var(--soft);
  transform: translateY(-1px);
}
body.mb-setup .mb-su-atzoom button:active { transform: translateY(0); }
body.mb-setup .mb-su-atzoom button:focus-visible {
  outline: 2px solid var(--acc);
  outline-offset: 2px;
}
body.mb-setup .mb-su-atzoom button .eop-i { width: 15px; height: 15px; }
body.mb-setup .mb-su-atzoom-rd {
  display: inline-flex;
  align-items: center;
  height: 30px;
  padding: 0 10px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--mbc-card);
  color: var(--muted);
  font-size: 11.5px;
  font-weight: 650;
  font-variant-numeric: tabular-nums;
  box-shadow: var(--sc);
}


/* ---- 13h. THE TOOLTIP AND THE MAP CAPTION — .atTip / .atCap (:428-431).
   The tooltip is a dark chip that follows the pointer; it must be a child of
   the host, which clips at the border radius, so it can never escape the
   card. --pop is the token shadow that replaces the demo's raw black. ---- */
body.mb-setup .mb-su-attip {
  position: absolute;
  z-index: 9;
  display: none;
  padding: 7px 10px;
  border-radius: var(--r-md);
  background: var(--ink);
  color: var(--mbc-card);
  font-size: 11.5px;
  font-weight: 600;
  line-height: 1.35;
  white-space: nowrap;
  pointer-events: none;
  box-shadow: var(--pop);
}
body.mb-setup .mb-su-attip.is-on { display: block; }
body.mb-setup .mb-su-attip b { display: block; font-size: 12.5px; }
body.mb-setup .mb-su-attip span { opacity: .72; }
/* A country label drawn as HTML rather than as SVG text. The triple shadow is
   the demo's own trick for reading over a filled region without a plate. */
body.mb-setup .mb-su-atcap {
  position: absolute;
  z-index: 3;
  transform-origin: 50% 0;
  pointer-events: none;
  white-space: nowrap;
  font-size: 10.5px;
  font-weight: 650;
  color: var(--body);
  text-shadow: 0 0 3px var(--mbc-card), 0 0 3px var(--mbc-card), 0 0 3px var(--mbc-card);
}


/* ---- 13i. THE CHIP ROWS — the demo's .atChips / .atChip and its quieter
   twin .atCRow / .atCLbl / .atCChip (:433-445).

   The markup gives all three rows ONE class and tells them apart by
   data-mb-su-atrow, so the two idioms are selected on that attribute:
     country   — the full-size chip, accent when pressed. It is the primary
                 filter and the one the "countries" stat tile jumps to.
     continent — the small chip, ink when pressed. A coarser cut above the
                 country row, exactly as the demo stacks them.
     device    — the small chip, accent-tint when pressed, so a device filter
                 is never mistaken for a place filter at a glance.
   ---------------------------------------------------------------------- */
body.mb-setup .mb-su-atchips {
  margin-top: 12px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
body.mb-setup .mb-su-atrow {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 7px;
}
/* Only the rows AFTER the first carry the divider, so the group opens flush
   against the map exactly as the demo's first .atChips row does. */
body.mb-setup .mb-su-atrow + .mb-su-atrow {
  margin-top: 4px;
  padding-top: 11px;
  border-top: 1px solid var(--line2);
}
body.mb-setup .mb-su-atlbl {
  margin-right: 2px;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--faint);
}
body.mb-setup .mb-su-atchip {
  max-width: 100%;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  font: inherit;
  cursor: pointer;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  transition: background var(--dur) ease,
              border-color var(--dur) ease,
              color var(--dur) ease,
              transform var(--dur-micro) ease;
}
body.mb-setup .mb-su-atchip:active { transform: scale(.96); }
body.mb-setup .mb-su-atchip:focus-visible {
  outline: 2px solid var(--acc);
  outline-offset: 2px;
}
body.mb-setup .mb-su-atchip > b {
  margin-left: 7px;
  font-weight: 750;
  font-variant-numeric: tabular-nums;
  opacity: .72;
}

/* the country row — .atChip */
body.mb-setup [data-mb-su-atrow="country"] .mb-su-atchip {
  height: 32px;
  padding: 0 13px;
  background: var(--mbc-card);
  color: var(--body);
  font-size: 12.5px;
  font-weight: 600;
}
body.mb-setup [data-mb-su-atrow="country"] .mb-su-atchip:hover { background: var(--stage); }
body.mb-setup [data-mb-su-atrow="country"] .mb-su-atchip[aria-pressed="true"] {
  border-color: var(--acc);
  background: var(--accsoft);
  color: var(--acc);
}
/* A country with no outline on the ported COUNTRIES layer still filters the
   table and the chips — it simply cannot light up on the map. The dotted edge
   says so without a second line of copy. */
body.mb-setup [data-mb-su-atrow="country"] .mb-su-atchip[data-mb-su-shape="0"] {
  border-style: dashed;
}

/* the continent and device rows — .atCChip */
body.mb-setup [data-mb-su-atrow="continent"] .mb-su-atchip,
body.mb-setup [data-mb-su-atrow="device"] .mb-su-atchip {
  height: 28px;
  padding: 0 11px;
  border-color: var(--line2);
  background: var(--stage);
  color: var(--muted);
  font-size: 12px;
  font-weight: 600;
}
body.mb-setup [data-mb-su-atrow="continent"] .mb-su-atchip > b,
body.mb-setup [data-mb-su-atrow="device"] .mb-su-atchip > b { margin-left: 6px; }
body.mb-setup [data-mb-su-atrow="continent"] .mb-su-atchip:hover,
body.mb-setup [data-mb-su-atrow="device"] .mb-su-atchip:hover {
  background: var(--soft);
  color: var(--body);
}
body.mb-setup [data-mb-su-atrow="continent"] .mb-su-atchip[aria-pressed="true"] {
  border-color: var(--ink);
  background: var(--ink);
  color: var(--mbc-card);
}
body.mb-setup [data-mb-su-atrow="device"] .mb-su-atchip[aria-pressed="true"] {
  border-color: var(--acc);
  background: var(--accsoft);
  color: var(--acc);
}


/* ---- 13j. CARD 2 HEAD — heading, search box, export button on one line.
   The demo's .atHead / .atSrch (:447-450). The search field is an .eop-inp,
   so it already has the console's height, radius, surface and focus ring;
   all it needs here is a place in the row. event-operations.css:119 sets
   `width:100%` on .eop-inp at (0,1,0) and this rule is (0,2,1), so the basis
   below is what sizes it. ----------------------------------------------- */
body.mb-setup .mb-su-who-head {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 12px;
}
body.mb-setup .mb-su-who-head > h4 {
  flex: 1 1 auto;
  min-width: 0;
  margin-bottom: 0;
}
body.mb-setup .mb-su-atsrch {
  flex: 1 1 200px;
  width: auto;
  min-width: 0;
  max-width: 100%;
}
body.mb-setup .mb-su-who-head > .eop-btn { flex: 0 0 auto; }


/* ---- 13k. THE PEOPLE TABLE — the demo's .atTblWrap and table.at
   (:451-465).

   TWO CORRECTIONS THE SHIPPED SHEET FORCES, both of them real bugs if left:

   1. `.eop-tbl th:not(:first-child)` (mambo-setup.css:3620) right-aligns
      EVERY column after the first, because the Reports table it was written
      for is a name and then five numbers. This table is not that shape:
      Where, Device and Last seen are words and must read from the left. The
      rule below puts them back and then hands the right edge to .r alone.

   2. `.r` is a demo-only class. Nothing in the console styles it, so without
      the rule below Services and Stayed would sit left with the words —
      which is the same bug seen from the other side.

   SIDEWAYS SCROLL. .eop-evwrap already carries `overflow:auto`
   (event-operations.css:148); .mb-su-atwrap adds the frame, the sticky-header
   scrollport and the scroll shadows on top of it. The shadows are the
   background-attachment pair rather than the demo's ::after: an absolutely
   positioned pseudo-element inside a scroll container scrolls away with the
   content, so the demo's fade is only correct at scrollLeft 0. The local and
   scroll pair below is painted by the box itself, appears only when there is
   something to scroll to, and needs no script.
   ---------------------------------------------------------------------- */
body.mb-setup .mb-su-atwrap {
  position: relative;
  margin-top: 14px;
  max-height: 420px;
  border: 1px solid var(--line2);
  border-radius: var(--r-12);
  overscroll-behavior-x: contain;
  background:
    linear-gradient(to right, var(--mbc-card) 40%, transparent) left center / 30px 100% no-repeat local,
    linear-gradient(to left,  var(--mbc-card) 40%, transparent) right center / 30px 100% no-repeat local,
    radial-gradient(farthest-side at 0 50%, color-mix(in srgb, var(--ink) 16%, transparent), transparent) left center / 13px 100% no-repeat scroll,
    radial-gradient(farthest-side at 100% 50%, color-mix(in srgb, var(--ink) 16%, transparent), transparent) right center / 13px 100% no-repeat scroll;
}
/* Overrides mambo-setup.css:3586 — 520px is a six-column table's floor only
   when none of the columns carries an avatar. */
body.mb-setup .mb-su-attbl { min-width: 640px; }
/* Correction 1. */
body.mb-setup .mb-su-attbl th:not(:first-child),
body.mb-setup .mb-su-attbl td:not(:first-child) {
  text-align: left;
  font-variant-numeric: normal;
}
/* Correction 2. */
body.mb-setup .mb-su-attbl th.r,
body.mb-setup .mb-su-attbl td.r {
  text-align: right;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
/* The header is sticky against .mb-su-atwrap; --stage is opaque in both skins
   so the rows pass under it cleanly. */
body.mb-setup .mb-su-attbl thead th { background: var(--stage); }
body.mb-setup .mb-su-attbl tbody tr[hidden] { display: none; }

/* The name cell — avatar, name, address. */
body.mb-setup .mb-su-atwho {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}
body.mb-setup .mb-su-atav {
  flex: 0 0 auto;
  width: 27px;
  height: 27px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--tint);
  color: var(--tintink);
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: .01em;
}
body.mb-setup .mb-su-atnm {
  display: flex;
  flex-direction: column;
  min-width: 0;
  gap: 1px;
}
body.mb-setup .mb-su-atnm > b {
  font-size: 13px;
  font-weight: 650;
  line-height: 1.3;
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
body.mb-setup .mb-su-atnm > small {
  font-size: 11.5px;
  line-height: 1.3;
  color: var(--muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* The empty result of a search. No display is set on it, so the `hidden`
   attribute the renderer ships with keeps working untouched. */
body.mb-setup .mb-su-atnone {
  margin: 0;
  padding: 34px 16px;
  text-align: center;
  font-size: 13.5px;
  color: var(--muted);
}


/* ---- 13l. CARD 5 — EXPORT. The scope select and the three format links are
   an .eop-vrow and an .eop-qa, both of which the console already dresses; the
   only thing missing is a floor under the select so a long service title does
   not squeeze it to a chevron. ------------------------------------------ */
body.mb-setup .mb-su-atexport select.eop-inp {
  min-width: min(100%, 240px);
}


/* ---- 13m. RESPONSIVE. The demo does this at a 559px container query
   (setup-demo.html:703-711); 620 is the authored breakpoint that lands in the
   same place once the rail has already gone at 940. --------------------- */
@media (max-width: 620px) {
  /* The four tiles go 2-up and take the whole width, under the title. */
  body.mb-setup .mb-su-attiles {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 9px;
    width: 100%;
    flex: 1 1 100%;
  }
  /* 2:1 at 375 is a 170px-tall world. 4/3 is the demo's own answer. */
  body.mb-setup .mb-su-atmap { aspect-ratio: 4 / 3; }
  /* THE CONTROL STRIP STOPS WRAPPING, and this is the one place the port
     leaves the demo. In heat style the strip carries four controls; wrapped,
     they stack three deep and eat 70px off the top of a 238px map, and the
     legend and the zoom cluster take another 83px off the bottom — 73px of
     visible world is not a map. One 32px row that scrolls sideways keeps
     every control reachable and gives the world back ~65px. The strip is its
     own scroll container, so the map's pan gesture is unaffected. */
  body.mb-setup .mb-su-attop {
    gap: 5px;
    flex-wrap: nowrap;
    overflow-x: auto;
    overflow-y: hidden;
    padding-bottom: 2px;
    scrollbar-width: none;
    overscroll-behavior-x: contain;
  }
  body.mb-setup .mb-su-attop::-webkit-scrollbar { height: 0; }
  body.mb-setup .mb-su-attop > * { flex: 0 0 auto; }
  body.mb-setup .mb-su-atseg button { padding: 0 8px; }
  body.mb-setup .mb-su-atzoom-rd { display: none; }
  /* Legend and zoom share the bottom row rather than stacking: 88px is the
     two 30px buttons, their gap and the 12px margin. */
  body.mb-setup .mb-su-atleg {
    right: 88px;
    justify-content: space-between;
    gap: 7px;
    padding: 0 10px;
  }
  body.mb-setup .mb-su-atleg-lb {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  /* The heading takes its own line so the search box has a usable width. */
  body.mb-setup .mb-su-who-head > h4 { flex: 1 1 100%; }
  body.mb-setup .mb-su-atsrch { flex: 1 1 auto; }
}

@media (max-width: 520px) {
  body.mb-setup .mb-su-atmap { margin-top: 13px; }
  /* The band swatches are the legend's only load-bearing part at this width;
     the low end of the range is already the first band's own colour. */
  body.mb-setup .mb-su-atleg-lo { display: none; }
  body.mb-setup .mb-su-who-head > .eop-btn { flex: 1 1 100%; }
  body.mb-setup .mb-su-atwrap { max-height: 60vh; }
}


/* ---- 13n. REDUCED MOTION. Every transition and animation this section
   introduces, plus the one the drawing code sets inline on the world (the
   demo carries the same rule, and for the same reason: the map must still
   DRAW, only the motion goes). ------------------------------------------ */
@media (prefers-reduced-motion: reduce) {
  body.mb-setup .mb-su-attile,
  body.mb-setup .mb-su-attile:hover,
  body.mb-setup .mb-su-attile:active,
  body.mb-setup .mb-su-atmap,
  body.mb-setup .mb-su-atlayer,
  body.mb-setup .mb-su-atseg button,
  body.mb-setup .mb-su-atnames,
  body.mb-setup .mb-su-atnames > i,
  body.mb-setup .mb-su-atnames > i > b,
  body.mb-setup .mb-su-atleg-bd,
  body.mb-setup .mb-su-atleg-bd:hover,
  body.mb-setup .mb-su-atleg-bd:focus-visible,
  body.mb-setup .mb-su-atzoom button,
  body.mb-setup .mb-su-atzoom button:hover,
  body.mb-setup .mb-su-atchip,
  body.mb-setup .mb-su-atchip:active {
    transition: none;
    transform: none;
  }
  /* The world and its regions carry their animation and their transition
     INLINE from the drawing code, so these two need the reach the demo's own
     line has. Nothing else in this section uses !important. */
  body.mb-setup .mb-su-atmap svg,
  body.mb-setup .mb-su-atmap svg path { animation: none !important; }
  body.mb-setup .mb-su-atmap svg path[data-country] { transition: none !important; }
}

/* =========================================================================
   14. THE EDITOR OVERLAY vs THE WORDPRESS ADMIN BAR                 v3.13.1

   Dennis reported the Save / Publish / Draft / Close buttons missing from the
   top of both the sermon and event editors. They were not missing — they were
   UNDERNEATH #wpadminbar.

   The admin bar is `position: fixed; top: 0` with `z-index: 99999`. The editor
   overlay is `inset: 0; z-index: 120`. 120 loses to 99999 by five orders of
   magnitude, so the bar painted straight over the header — and the header is
   where mambo-setup.js had just MOVED the only Save and Cancel controls, after
   hiding the footer they came from. Net effect for a logged-in operator: an
   editor with no way to save.

   WordPress already tells us: it adds `.admin-bar` to <body> and shifts <html>
   by 32px (46px below 783px, where the bar is taller and wraps). The overlay
   has to start below it rather than under it.
   ====================================================================== */

body.mb-setup.admin-bar .eop-ev-view#eop-ev-view-add.on,
body.mb-setup.admin-bar .eop-ev-view.mb-su-edview.on {
  top: 32px;
}

@media screen and (max-width: 782px) {
  body.mb-setup.admin-bar .eop-ev-view#eop-ev-view-add.on,
  body.mb-setup.admin-bar .eop-ev-view.mb-su-edview.on {
    top: 46px;
  }
}

/* The overlay must actually FILL what is left, or the grid's rows size to
   content and the body row collapses. `inset: 0` alone did not survive the
   `top` override, because `inset` sets all four and a later `top` replaces
   only one — height then comes from `bottom: 0`, which is still in force. This
   states it outright so neither depends on the other. */
body.mb-setup .eop-ev-view#eop-ev-view-add.on,
body.mb-setup .eop-ev-view.mb-su-edview.on {
  height: auto;
  max-height: 100vh;
}
body.mb-setup.admin-bar .eop-ev-view#eop-ev-view-add.on,
body.mb-setup.admin-bar .eop-ev-view.mb-su-edview.on {
  max-height: calc(100vh - 32px);
}
@media screen and (max-width: 782px) {
  body.mb-setup.admin-bar .eop-ev-view#eop-ev-view-add.on,
  body.mb-setup.admin-bar .eop-ev-view.mb-su-edview.on {
    max-height: calc(100vh - 46px);
  }
}

/* =========================================================================
   15. THE ACTION BAR IN BOTH PLACES                                 v3.14.0

   Dennis asked for Save / Draft / Cancel at the top AND on the sidebar. That
   also removes the dependency that caused v3.13.x: the controls no longer live
   wherever the overlay happened to put them.

   The real bar is STICKY, so it pins whether the overlay activated or not. The
   rail block is a MIRROR whose buttons forward to the real ones by .click() —
   there is still exactly one wired Save on the page.
   ====================================================================== */

/* The real bar, pinned. --mb-setup-top is published by mambo-setup.js and is
   already the site header's height; the admin bar is added here because a
   sticky element resolves against the viewport, not the overlay. */
body.mb-setup .mb-su-edstick {
  position: sticky;
  top: var(--mb-setup-top, 0px);
  z-index: 8;
  display: flex;
  align-items: center;
  gap: 9px;
  flex-wrap: wrap;
  padding: 10px 12px;
  margin: 0 0 14px;
  border: 1px solid var(--line);
  border-radius: var(--r-ctl, 10px);
  background: var(--card);
  box-shadow: var(--sc);
}
body.mb-setup.admin-bar .mb-su-edstick { top: calc(var(--mb-setup-top, 0px) + 32px); }
@media screen and (max-width: 782px) {
  body.mb-setup.admin-bar .mb-su-edstick { top: calc(var(--mb-setup-top, 0px) + 46px); }
}
/* Inside the overlay the header already owns row 1, so nothing needs pinning. */
body.mb-setup #eop-ev-view-add.on .mb-su-edstick,
body.mb-setup .mb-su-edview.on .mb-su-edstick {
  position: static;
  margin: 0;
  border: 0;
  padding: 0;
  background: none;
  box-shadow: none;
}

/* The rail mirror. */
body.mb-setup .mb-su-railact {
  display: flex;
  flex-direction: column;
  gap: 7px;
  margin-top: 14px;
  padding-top: 13px;
  border-top: 1px solid var(--line2);
}
body.mb-setup .mb-su-railseg {
  display: flex;
  gap: 3px;
  padding: 3px;
  border-radius: var(--r-ctl, 10px);
  background: var(--stage);
}
body.mb-setup .mb-su-railstatus {
  flex: 1 1 0;
  min-width: 0;
  height: 30px;
  border: 0;
  border-radius: var(--r-sm, 7px);
  background: none;
  color: var(--muted);
  font: inherit;
  font-size: 12.5px;
  font-weight: 600;
  cursor: pointer;
  transition: background var(--dur-micro, .12s) var(--ease), color var(--dur-micro, .12s) var(--ease);
}
body.mb-setup .mb-su-railstatus[aria-pressed="true"] {
  background: var(--card);
  color: var(--ink);
  box-shadow: var(--sc);
}
body.mb-setup .mb-su-railbtn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  width: 100%;
  min-height: 38px;
  padding: 0 13px;
  border: 1px solid var(--line);
  border-radius: var(--r-ctl, 10px);
  background: var(--btn2);
  color: var(--ink);
  font: inherit;
  font-size: 13.5px;
  font-weight: 600;
  text-align: center;
  cursor: pointer;
  transition: background var(--dur-micro, .12s) var(--ease);
}
body.mb-setup .mb-su-railbtn:hover { background: var(--stage); }
body.mb-setup .mb-su-railbtn.is-pri {
  border-color: transparent;
  background: var(--acc);
  color: var(--onacc);
}
body.mb-setup .mb-su-railbtn.is-pri:hover { background: var(--accHover, var(--acc)); }
body.mb-setup .mb-su-railstatus:focus-visible,
body.mb-setup .mb-su-railbtn:focus-visible { outline: 2px solid var(--acc); outline-offset: 2px; }

/* Below 940 the rail flips to a horizontal strip directly under the sticky bar,
   which would put two identical sets of Draft / Save / Cancel within 60px of each
   other. The mirror exists because on a tall desktop rail the top bar can be
   scrolled far away; at this width it never is. So the mirror stands down and the
   sticky bar is the single set. Verified by measuring both at 375 and 1440. */
@media (max-width: 940px) {
  body.mb-setup .mb-su-railact { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  body.mb-setup .mb-su-railstatus,
  body.mb-setup .mb-su-railbtn { transition: none; }
}

/* =========================================================================
   16. THE INLINE FALLBACK IS A REAL LAYOUT                          v3.14.0

   Every rule that made the editor two columns was scoped to `.on`, i.e. to the
   overlay being active. On Dennis's site it is not, so the editor fell back to
   a stacked block: the section rail full width, the form beneath it. Functional
   and ugly, and it is what he has been looking at.

   The two-column shape does not need the overlay. Only the FIXED POSITIONING
   does. So the grid is stated unconditionally and the overlay rules above stay
   as the additional layer they should always have been.
   ====================================================================== */

body.mb-setup .mb-su-edbody {
  display: grid;
  grid-template-columns: 262px minmax(0, 1fr);
  gap: clamp(16px, 2vw, 28px);
  align-items: start;
  min-width: 0;
}
body.mb-setup .mb-su-edbody > .mb-su-edrail {
  position: sticky;
  top: calc(var(--mb-setup-top, 0px) + 14px);
  max-height: calc(100vh - var(--mb-setup-top, 0px) - 28px);
  overflow-y: auto;
  overscroll-behavior: contain;
}
body.mb-setup.admin-bar .mb-su-edbody > .mb-su-edrail {
  top: calc(var(--mb-setup-top, 0px) + 46px);
  max-height: calc(100vh - var(--mb-setup-top, 0px) - 78px);
}
body.mb-setup .mb-su-edbody > .mb-su-edscroll { min-width: 0; }

@media (max-width: 940px) {
  body.mb-setup .mb-su-edbody { grid-template-columns: minmax(0, 1fr); }
  body.mb-setup .mb-su-edbody > .mb-su-edrail {
    position: static;
    max-height: none;
    flex-direction: row;
    overflow-x: auto;
    overflow-y: hidden;
  }
}

/* ---- the explicit exit, v3.14.1 ----------------------------------------
   Close is not Cancel: it activates the list tab, the same path module 7's X
   uses. It sits in the pinned bar AND in the rail mirror, because the header
   that used to be the only way out is not guaranteed to render. */
body.mb-setup .mb-su-edclose {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 34px;
  height: 34px;
  margin-right: 3px;
  border: 1px solid var(--line);
  border-radius: var(--r-ctl, 10px);
  background: var(--btn2);
  color: var(--ink);
  cursor: pointer;
  transition: background var(--dur-micro, .12s) var(--ease);
}
body.mb-setup .mb-su-edclose:hover { background: var(--stage); }
body.mb-setup .mb-su-edclose svg { width: 16px; height: 16px; }
body.mb-setup .mb-su-edclose:focus-visible { outline: 2px solid var(--acc); outline-offset: 2px; }
body.mb-setup .mb-su-railbtn.is-close { color: var(--muted); }
body.mb-setup .mb-su-railbtn.is-close:hover { color: var(--ink); }
@media (prefers-reduced-motion: reduce) {
  body.mb-setup .mb-su-edclose { transition: none; }
}

/* =========================================================================
   17. THE EDITOR TAKES THE SCREEN                                   v3.15.0

   Dennis: the header and footer must not show or interfere on the event and
   sermon creation space.

   mambo-setup.js puts `mb-su-editing` on <body> while an editor view is open.
   Both chrome roots are hidden by their KEPT legacy classes (.eos-nav and
   .eos-footer), which is the same handle smart-tv.css:13 already uses — so
   nothing another sheet binds to is renamed or removed, and closing the editor
   restores everything by dropping one class.

   The WordPress admin bar stays. It is not the site's chrome, an operator often
   needs it mid-edit, and the overlay already offsets for it.
   ====================================================================== */

body.mb-setup.mb-su-editing .eos-nav,
body.mb-setup.mb-su-editing .mb-nav,
body.mb-setup.mb-su-editing .eos-footer,
body.mb-setup.mb-su-editing .mb-footer {
  display: none;
}

/* With the sticky header gone there is no offset left to reserve, so the value
   mambo-setup.js publishes must not keep pushing the editor down. The pinned
   bar and the rail both read this token. */
body.mb-setup.mb-su-editing #eop {
  --mb-setup-top: 0px;
}

/* The console's own masthead, breadcrumb and space head are page furniture too:
   inside the editor they repeat what the editor header already says. */
body.mb-setup.mb-su-editing .eop-hd,
body.mb-setup.mb-su-editing .eop-perm,
body.mb-setup.mb-su-editing .eop-subnav,
body.mb-setup.mb-su-editing .mb-su-head,
body.mb-setup.mb-su-editing .eop-evtabs {
  display: none;
}

/* The rail is the editor's own section rail while editing; the space rail beside
   it would be a second, unrelated navigation. */
body.mb-setup.mb-su-editing .mb-su-shell > .mb-su-rail,
body.mb-setup.mb-su-editing .mb-su-drawer,
body.mb-setup.mb-su-editing .mb-su-menu {
  display: none;
}
body.mb-setup.mb-su-editing .mb-su-shell {
  grid-template-columns: minmax(0, 1fr);
}

/* Nothing above may leave the page unscrollable if the editor is closed by a
   route change rather than the tab — the class is dropped on pagehide, and this
   is the belt: no rule here sets overflow on <body> or <html>. */

/* =============================================================================
   18. THE EDITOR WORKSPACE — ARRANGEMENT AND RAIL          v3.21.0

   Dennis: "they are not scrolling as they should. the side bars are not working
   too (a click on a feature should jump you to the designated area to edit).
   upgrade the sidebar and the responsiveness. buttons on the sidebar should be
   properly sized and positioned well."

   The rail's dead click was a JS fault, fixed in mambo-setup.js: it called
   window.scrollTo, but since v3.8.0 the editor is `position: fixed; inset: 0`
   and the thing that scrolls is `.mb-su-edscroll`. The window had nothing to
   scroll, so every rail click moved nothing. This section is the layout half.
   ========================================================================== */

/* ---- 18.1 THE DEAD ZONE BETWEEN THE RAIL AND THE FORM --------------------
   `.eop-evform` is capped at 940px and centred inside a `minmax(0, 1fr)`
   column. On a wide screen the column is enormous, so the form floated to the
   middle of it while the rail stayed hard against the left edge — leaving a
   ~400px gap that reads as broken rather than roomy.

   The fix is to bound the PAIR and centre them together, so the rail sits
   beside its form at every width instead of being abandoned at the edge. */
body.mb-setup .mb-su-edview.on .mb-su-edbody {
  width: 100%;
  max-width: 1320px;      /* 262 rail + 28 gap + 940 form + breathing room */
  margin-inline: auto;
}

/* ---- 18.2 THE RAIL'S LABELS ---------------------------------------------
   `.mb-su-edrail-lb` was nowrap + ellipsis, so "Live features for this
   service" truncated to "Live features for this serv…". A navigation label
   that cannot be read is not navigation. Two lines, then clamp — the longest
   real label fits in two and the row height stays predictable. */
body.mb-setup .mb-su-edrail-item {
  min-height: 38px;
  align-items: flex-start;
  padding-block: 9px;
  text-align: left;
}

body.mb-setup .mb-su-edrail-ic {
  margin-top: 1px;        /* optical: align the glyph to the first line, not the box */
}

body.mb-setup .mb-su-edrail-lb {
  white-space: normal;
  overflow: hidden;
  text-overflow: clip;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  line-height: 1.32;
}

/* The dot keeps its own line-height so a two-line label does not drag it out
   of alignment with the first line of text. */
body.mb-setup .mb-su-edrail-dot {
  margin-top: 1px;
}

/* ---- 18.3 THE RAIL AS A COLUMN THAT SCROLLS ITSELF -----------------------
   Inside the overlay the rail is a grid column of a fixed-height parent. Its
   own overflow has to be owned here or a long form's rail is simply cut off at
   the bottom with no way to reach the last sections. */
body.mb-setup .mb-su-edview.on .mb-su-edrail {
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 12px 10px 24px;
  scrollbar-width: thin;
}

/* ---- 18.4 RESPONSIVENESS -------------------------------------------------
   Below the two-column breakpoint the rail becomes a horizontal strip. Its
   items were built for a vertical column, so they need the opposite treatment:
   one line, no clamp, and no growing. */
@media (max-width: 940px) {
  body.mb-setup .mb-su-edview.on .mb-su-edrail {
    padding: 10px 12px;
    gap: 6px;
  }
  body.mb-setup .mb-su-edrail-item {
    width: auto;
    flex: 0 0 auto;
    align-items: center;
    min-height: 34px;
  }
  body.mb-setup .mb-su-edrail-lb {
    -webkit-line-clamp: 1;
    line-clamp: 1;
    white-space: nowrap;
    text-overflow: ellipsis;
  }
}

/* ---- 18.5 THE ACTION COLUMN ---------------------------------------------
   Save / Cancel / Close sat at three different widths because each sized to
   its own label. They are a stack of equal-weight decisions and should read as
   one control group. */
body.mb-setup .mb-su-railact {
  display: flex;
  flex-direction: column;
  gap: 8px;
  width: 100%;
}

body.mb-setup .mb-su-railact > button,
body.mb-setup .mb-su-railact > .mb-su-railbtn {
  width: 100%;
  min-height: 40px;
  justify-content: center;
}

/* =============================================================================
   19. THE EDITOR MUST ACTUALLY SCROLL                          v3.24.0

   Dennis: "they are not scrolling as they should."

   THE BUG, measured in a harness that lets mambo-setup.js build the overlay
   itself rather than hand-writing its scaffolding.

   § 12 declares the open view as
       display: grid; grid-template-rows: auto minmax(0, 1fr);
   — TWO rows. The view has THREE children:

       .mb-su-edhd     the header            67px
       .mb-su-edprog   the progress hairline  3px
       .mb-su-edbody   the workspace         833px

   So row 1 takes the header, the `minmax(0, 1fr)` row is spent on the 3px
   progress bar (and collapses to 0px), and `.mb-su-edbody` falls into an
   IMPLICIT row, which is `auto` — content-sized, unconstrained. Its
   `.mb-su-edscroll` child therefore has nothing to scroll against: measured at
   1440x900 the scroller was 2674px tall with scrollHeight === clientHeight.
   Nothing scrolled, and the rail had nowhere to scroll to.

   FIXED AS FLEX, NOT BY ADDING A THIRD ROW. `grid-template-rows: auto auto
   minmax(0,1fr)` would work today and break the next time a child is added or
   removed — and .mb-su-edprog is built at runtime, so it is not always present.
   A flex column does not care how many header elements precede the body: the
   body takes the remaining space whatever the count. The view has only ever had
   one column, so nothing is lost in the change.
   ========================================================================== */

/* THE EVENT VIEW IS TARGETED BY ID, THE SERMON VIEW BY CLASS. Only the sermon
   view carries `mb-su-edview` (setup-render.php:2368); the event editor is
   `#eop-ev-view-add` and has no such class. § 12's own rule lists both for that
   reason, and this must match it selector for selector or it silently applies to
   sermons only — which is exactly what my first attempt did. The ID is not mine
   and is not new; it is the shape § 12 already established for this element. */
body.mb-setup .eop-ev-view#eop-ev-view-add.on,
body.mb-setup .eop-ev-view.mb-su-edview.on {
  display: flex;
  flex-direction: column;
}

/* The header and the hairline keep their own height; the body takes the rest.
   `min-height: 0` is the part that lets a flex child actually shrink below its
   content — without it the body would still grow and the scroller would still
   have nothing to scroll. */
body.mb-setup .eop-ev-view#eop-ev-view-add.on > .mb-su-edhd,
body.mb-setup .eop-ev-view#eop-ev-view-add.on > .mb-su-edstick,
body.mb-setup .eop-ev-view#eop-ev-view-add.on > .mb-su-edprog,
body.mb-setup .eop-ev-view.mb-su-edview.on > .mb-su-edhd,
body.mb-setup .eop-ev-view.mb-su-edview.on > .mb-su-edstick,
body.mb-setup .eop-ev-view.mb-su-edview.on > .mb-su-edprog {
  flex: 0 0 auto;
}

body.mb-setup .eop-ev-view#eop-ev-view-add.on > .mb-su-edbody,
body.mb-setup .eop-ev-view.mb-su-edview.on > .mb-su-edbody {
  flex: 1 1 auto;
  min-height: 0;
}

/* ---- 19.2 ...AND THE ROW INSIDE IT MUST BE BOUNDED TOO -------------------
   Making the view a flex column bounded `.mb-su-edbody` at the viewport (830px
   measured) but did NOT make anything scroll, because the break is one level
   further down. `.mb-su-edbody` is a grid, and § 12 gives it columns only:

       grid-template-columns: 262px minmax(0, 1fr);   (line 1106)
       align-items: start;                            (line 2300)

   With no `grid-template-rows`, the children land in an IMPLICIT row, which is
   `auto` — content-sized, never clamped by the container's own height. And
   `align-items: start` tells the items not to fill the row even when there is
   one. So `.mb-su-edscroll` sized itself to all 2724px of form and simply
   overflowed its 830px parent: measured scrollHeight === clientHeight === 2724,
   i.e. `overflow-y: auto` had nothing to scroll. That is the user's report.

   `align-items: start` is right for the INLINE editor (short rail, tall form,
   rail sticks to the top), so it is left alone; only the open overlay is
   corrected, and via `align-self` on the children rather than by flipping the
   parent's `align-items`, so nothing else inherits the change.

   The rail is safe to stretch: § 12 line 1118 already gives it `max-height:
   none` and `overflow-y: auto` in the open state, so a full-height rail scrolls
   on its own — which is what § 18 wanted when it added rail overflow. */
body.mb-setup .eop-ev-view#eop-ev-view-add.on > .mb-su-edbody,
body.mb-setup .eop-ev-view.mb-su-edview.on > .mb-su-edbody {
  grid-template-rows: minmax(0, 1fr);
}

body.mb-setup .eop-ev-view#eop-ev-view-add.on > .mb-su-edbody > .mb-su-edrail,
body.mb-setup .eop-ev-view#eop-ev-view-add.on > .mb-su-edbody > .mb-su-edscroll,
body.mb-setup .eop-ev-view.mb-su-edview.on > .mb-su-edbody > .mb-su-edrail,
body.mb-setup .eop-ev-view.mb-su-edview.on > .mb-su-edbody > .mb-su-edscroll {
  align-self: stretch;
  min-height: 0;
}

/* Below 940 the rail is a horizontal strip ABOVE the form, so the grid needs
   two rows again — § 12 line 1168 says so. This block only exists because the
   rule above carries an ID and would otherwise outrank it inside this range and
   collapse the strip into the form's row. 940 is in the authored breakpoint set;
   a `min-width: 941px` guard on the rule above would not have been. */
@media (max-width: 940px) {
  body.mb-setup .eop-ev-view#eop-ev-view-add.on > .mb-su-edbody,
  body.mb-setup .eop-ev-view.mb-su-edview.on > .mb-su-edbody {
    grid-template-rows: auto minmax(0, 1fr);
  }
}

/* ---- 19.3 § 18.5 UN-HID THE ACTION MIRROR ON SMALL SCREENS --------------
   Regression of my own making, caught by measuring the tablet strip. § 16 hides
   the rail's Save/Cancel mirror below 940 (line 2275) because the sticky top bar
   already carries that set and two copies land within 60px of each other. § 18.5
   then re-declared

       body.mb-setup .mb-su-railact { display: flex; ... }

   at IDENTICAL specificity and LATER in the file, which beats a media-query rule
   — media queries add no specificity. So the mirror came back at every width.

   On the desktop rail that is invisible (it is meant to be there). On the ≤ 940
   horizontal strip it is a 70×152px vertical stack of buttons sitting among the
   section chips: measured at 768px it forced the strip to 196px tall and, since
   the strip's flex line stretches, dragged all five chips from their natural 41px
   to 166px. That is the "buttons on the sidebar should be properly sized and
   positioned well" report, at tablet and phone.

   Re-stating the hide after § 18.5 is the whole fix. */
@media (max-width: 940px) {
  body.mb-setup .mb-su-railact { display: none; }
}

/* Belt for the strip itself: a chip should never be taller than its own content
   just because a sibling is tall. § 18.4 already centres and floors them at 34px;
   this stops the flex line from stretching them past that if anything else is
   ever added to the rail. */
@media (max-width: 940px) {
  body.mb-setup .mb-su-edview.on .mb-su-edrail { align-items: center; }
}
