/* ============================================================================
   event.clinic app shell. UX Phase 2, E2, owner study 2026-10-02 (D-1471).
   ----------------------------------------------------------------------------
   ONE LABELLED SIDEBAR in place of the icon rail plus panel of 2026-07-26 to
   2026-10-02. Two grid columns: the sidebar and the content. Collapsed, the
   sidebar keeps a short label under every icon (hover tips stay ruled out,
   2026-07-29). Tokens only, no raw hex.
   ============================================================================ */
/* MR-32, W895. Bypass blocks (WCAG 2.4.1): the shell's first tab stop jumps
   to <main>. Absolute, so it takes no cell of the .app grid; off screen until
   focused. */
.skip-link {
  position: absolute; left: var(--sp-2); top: var(--sp-2); z-index: 1000;
  padding: var(--sp-2) var(--sp-3); border-radius: var(--radius);
  background: var(--surface-1); color: var(--accent-700); font-weight: 600;
  box-shadow: var(--shadow-md); transform: translateY(-200%);
}
.skip-link:focus { transform: none; outline: 2px solid var(--accent-600); outline-offset: 2px; }

.app {
  display: grid;
  grid-template-columns: var(--sidebar-w) minmax(0, 1fr);
  height: 100vh;
  overflow: hidden;
}
.app.collapsed { grid-template-columns: var(--sidebar-w-collapsed) minmax(0, 1fr); }

.sidebar {
  background: var(--surface-1);
  border-right: 1px solid var(--line);
  display: flex; flex-direction: column;
  min-width: 0; overflow: hidden;
}
.sb-head {
  height: var(--topbar-h); flex-shrink: 0;
  display: flex; align-items: center; gap: var(--sp-2); padding: 0 var(--sp-3);
  border-bottom: 1px solid var(--line);
}
.sb-head .brand-mark { color: var(--ink-900); }
.sb-brand { font-weight: 700; font-size: var(--text-md); color: var(--ink-900); letter-spacing: -.02em; }
.sb-brand b { color: var(--brand-dot); font-weight: 900; }
.sb-brand .cl { font-weight: 600; opacity: .68; }
.sb-head .brand-mark { display: none; }

.sb-top { display: flex; flex-direction: column; gap: var(--sp-1); padding: var(--sp-2) var(--sp-2) var(--sp-1); flex-shrink: 0; }
.sb-switch {
  display: flex; align-items: center; gap: var(--sp-2); width: 100%; min-height: 44px;
  padding: 6px 10px; border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface-1); color: var(--ink-700); text-align: left; cursor: pointer;
  font-family: inherit;
}
.sb-switch:hover { background: var(--surface-sunken); }
.sb-switch-txt { display: flex; flex-direction: column; min-width: 0; }
.sb-switch-k { font-size: var(--text-min); color: var(--ink-500); }
.sb-switch-txt b {
  font-size: var(--text-sm); font-weight: 620; color: var(--ink-900);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.sb-leave {
  align-self: flex-start; margin-left: 10px; padding: 2px 0; min-height: 24px;
  font-size: var(--text-min); color: var(--accent-600); font-weight: 600; text-decoration: none;
}
.sb-leave:hover { text-decoration: underline; }
.sb-search {
  display: flex; align-items: center; gap: var(--sp-2); width: 100%; min-height: 36px;
  padding: 0 10px; border: 0; border-radius: var(--radius);
  background: var(--surface-sunken); color: var(--ink-500); cursor: pointer;
  font-family: inherit; font-size: var(--text-sm);
}
.sb-search svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; flex-shrink: 0; }
.sb-search-txt { flex: 1; text-align: left; }
.sb-kbd, .cmdbar kbd {
  font-family: inherit; font-size: var(--text-min); color: var(--ink-500);
  border: 1px solid var(--line-strong); border-radius: var(--radius-sm); padding: 0 5px;
  background: var(--surface-1);
}

.sb-nav { flex: 1; overflow-y: auto; padding: var(--sp-1) var(--sp-2) var(--sp-3); }
/* Owner 2026-10-19: a quiet scrollbar. Thin, no track, and the thumb only takes
   colour while the pointer is over the menu, in both widths. */
.sb-nav { scrollbar-width: thin; scrollbar-color: transparent transparent; scrollbar-gutter: stable; }
.sb-nav:hover, .sb-nav:focus-within { scrollbar-color: var(--line-strong) transparent; }
.sb-nav::-webkit-scrollbar { width: 6px; }
.sb-nav::-webkit-scrollbar-track { background: transparent; }
.sb-nav::-webkit-scrollbar-thumb { background: transparent; border-radius: 999px; }
.sb-nav:hover::-webkit-scrollbar-thumb, .sb-nav:focus-within::-webkit-scrollbar-thumb { background: var(--line-strong); }
.sb-group { margin-top: var(--sp-2); }
.sb-divide { margin-top: var(--sp-3); padding-top: var(--sp-3); border-top: 1px solid var(--line); }
.sb-group-h { margin: 0; font-size: var(--text-min); font-weight: 650; color: var(--ink-500); }
.sb-group-h > .sb-group-txt { display: block; padding: 4px 10px; }
.sb-group-btn {
  display: flex; align-items: center; justify-content: space-between; width: 100%;
  min-height: 28px; padding: 4px 10px; border: 0; background: transparent; cursor: pointer;
  font: inherit; color: inherit; border-radius: var(--radius-sm); text-align: left;
}
.sb-group-btn:hover { color: var(--ink-900); background: var(--surface-sunken); }
.sb-group-btn svg { width: 12px; height: 12px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; transition: transform .14s ease; }
.sb-group-btn svg.rot { transform: rotate(90deg); }
.sb-list { list-style: none; margin: 2px 0 0; padding: 0; display: flex; flex-direction: column; gap: 1px; }
.sb-row { display: flex; align-items: center; gap: 2px; }
.sb-row > .nav-item { flex: 1; min-width: 0; }
.sb-row.is-hidden > .nav-item { opacity: .55; }
.sb-edit { display: flex; gap: 2px; flex-shrink: 0; }
.sb-tool {
  width: 28px; height: 28px; display: grid; place-items: center;
  border: 0; border-radius: var(--radius-sm); background: transparent; color: var(--ink-400); cursor: pointer;
}
.sb-tool:hover { background: var(--surface-sunken); color: var(--ink-900); }
.sb-tool svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.sb-tool svg.is-on { fill: var(--accent-600); stroke: var(--accent-600); }
.sb-tool[aria-pressed="true"] { color: var(--accent-600); }

.sb-foot {
  padding: var(--sp-2) var(--sp-3); border-top: 1px solid var(--line);
  flex-shrink: 0; display: flex; flex-direction: column; gap: var(--sp-2);
}
.sb-foot-tools { display: flex; flex-wrap: wrap; gap: var(--sp-1) var(--sp-3); }
.sb-text-btn {
  min-height: 24px; padding: 0; border: 0; background: transparent; cursor: pointer;
  font-family: inherit; font-size: var(--text-min); color: var(--ink-500); font-weight: 600;
}
.sb-text-btn:hover, .sb-text-btn[aria-pressed="true"] { color: var(--accent-700); }

/* W819, MR-16. Help and contact, and the legal pages, under the sidebar tools
   and under the sign-in and workspace cards. Wrapping links, never a menu. */
.legal-links { display: flex; flex-wrap: wrap; gap: var(--sp-1) var(--sp-3); font-size: var(--text-min); }
.legal-links .link { min-height: 24px; display: inline-flex; align-items: center; }
.signin-wrap .legal-links, .page .legal-links { justify-content: center; margin-top: var(--sp-4); }
.app.collapsed .sb-foot .legal-links { display: none; }

/* Owner, 2026-10-08: the sidebar foot is one row. Avatar and "Signed in" on
   the left, the edit tool and the help flyout pushed right. The flyout opens
   upward inside the sidebar; collapsed, the sidebar is too narrow and clips
   (overflow: hidden), so there it is fixed beside the rail instead. */
.panel-foot-user .sb-foot-tools { margin-left: auto; }
.sb-flyout-wrap { position: relative; display: inline-flex; }
.sb-flyout {
  position: absolute; right: 0; bottom: calc(100% + var(--sp-1)); z-index: 30;
  width: max-content; max-width: calc(var(--sidebar-w) - 2 * var(--sp-3));
  display: flex; flex-direction: column; gap: var(--sp-1);
  padding: var(--sp-2) var(--sp-3); background: var(--surface-2);
  border: 1px solid var(--line); border-radius: var(--radius); box-shadow: var(--shadow-md);
}
.sb-flyout .legal-links.sb-flyout-links { flex-direction: column; flex-wrap: nowrap; gap: 0; }
.sb-flyout .sb-text-btn { align-self: flex-start; text-align: left; }
/* Owner, 2026-10-19: the workspace list opens from the name beside the avatar, on the left. */
.sb-flyout.sb-flyout-left { left: 0; right: auto; }
/* The older rule above hides the links in a collapsed foot; in the flyout they are the point. */
.app.collapsed .sb-flyout .legal-links { display: flex; }
.app.collapsed .sb-flyout { position: fixed; left: calc(var(--sidebar-w-collapsed) + var(--sp-1)); right: auto; bottom: var(--sp-2); max-width: 260px; }

/* Focus on every sidebar control, and a ring that is never clipped. */
.sidebar :is(a, button):focus-visible { outline: 2px solid var(--accent-600); outline-offset: -2px; }

.panel-foot-user { display: flex; align-items: center; gap: 10px; }

/* ---------------------------------------------------------------------------
   W284. THE SIGNED-IN AVATAR IS GREY, NOT VIOLET.

   Owner, 2026-09-04: "the purple placeholder for the user photo is very
   disrupting. It would be much better if it were gray."

   ★ THE PURPLE IS NOT OURS. Clerk draws its own placeholder when a person has
   uploaded no photo: initials on a generated violet gradient, chosen from the
   user id, with the colour set INLINE on the element. It is the one thing on
   the shell that does not come from this design system, and it sits in the
   corner of every screen.

   SO THIS OVERRIDES A THIRD PARTY'S MARKUP AND SAYS SO. `cl-avatarBox` is
   Clerk's documented styling hook and is stable across their v5 line, but it
   is still their class and not ours: a major Clerk upgrade could rename it,
   and the failure would be silent -- the violet simply returns. That is the
   trade against the alternative, which is passing an `appearance` prop through
   every UserButton call site and keeping them in step by hand.

   HOW TO CHECK IT STILL WORKS: sign in on app.event.clinic as somebody with no
   profile photo and look at the bottom of the menu. Grey circle, dark initials.

   `!important` IS REQUIRED and is not laziness. Clerk sets the gradient as an
   INLINE style attribute, which no ordinary selector can outrank.
   --------------------------------------------------------------------------- */
.panel-foot-user .cl-avatarBox,
.panel-foot-user .cl-userButtonAvatarBox,
.portal-top .cl-avatarBox,
.portal-top .cl-userButtonAvatarBox {
  /* --surface-sunken and NOT --surface-2. Measured in the browser: --surface-2
     computes to white in the light theme, so the circle vanished into the
     panel instead of reading as a placeholder. --surface-sunken is #ededed
     light and #171a20 dark, which is the grey the owner asked for and is
     visible in both. */
  background: var(--surface-sunken) !important;
  background-image: none !important;
  border: 1px solid var(--line-strong);
  /* The initials Clerk draws inherit `currentColor` from the box, so the text
     colour has to move with the background or grey-on-grey is the result. */
  color: var(--ink-700) !important;
}
.panel-foot-user .cl-avatarBox *,
.panel-foot-user .cl-userButtonAvatarBox *,
.portal-top .cl-avatarBox *,
.portal-top .cl-userButtonAvatarBox * {
  background-image: none !important;
  color: var(--ink-700) !important;
}
/* A REAL PHOTO IS LEFT ALONE. Somebody who uploaded a picture should see it,
   and the rules above would otherwise paint a grey square over it. */
.cl-avatarBox img, .cl-userButtonAvatarBox img { background: none !important; }

/* ---------- The collapse chevron, ported from the foundation ----------
   The foundation pins this to the panel footer and keeps it visible in both
   states, which is what makes the collapse reversible: the control that undid
   the panel is still inside the 56px strip that remains.

   margin-left:auto pushes it to the corner of the user row. It cannot simply be
   the footer's last flex child the way the foundation appends it, because
   .panel-foot became a COLUMN on 2026-07-26 for the unit dropdown, so appending
   would give it a full-width row of its own instead of the bottom-right corner.

   Transitions are literal, not var(--t-fast)/var(--ease). Those tokens are the
   foundation's and do not exist in tokens.css; .rail-tip above already uses
   literal .14s ease, so this matches the file it lives in rather than importing
   two new tokens for one button. */
.foot-collapse {
  width: 27px; height: 27px; margin-left: auto;
  border: 0; background: transparent; border-radius: 7px;
  display: grid; place-items: center;
  color: var(--ink-400); flex-shrink: 0; cursor: pointer;
  transition: background .14s ease, color .14s ease, transform .14s ease;
}
.foot-collapse:hover { background: var(--surface-sunken); color: var(--ink-900); }
.foot-collapse:active { transform: scale(.9); }
.foot-collapse:focus-visible {
  outline: none; background: var(--surface-sunken);
  box-shadow: 0 0 0 3px var(--accent-100);
}
.foot-collapse svg {
  width: 16px; height: 16px;
  stroke: currentColor; fill: none; stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round;
  transition: transform .22s cubic-bezier(.4, 0, .2, 1);
}
/* Collapsed, the chevron points the other way: it now offers the way back. */
.foot-collapse.rot svg { transform: rotate(180deg); }

/* ---------- COLLAPSED, 72px ----------
   Owner 2026-10-19: icons only. The two-line captions broke words mid-way
   ("Comp anies") at this width; each row now names itself in a tooltip and to
   a screen reader (title and aria-label in AppShell). Group headings become
   thin rules; the switcher and search keep their marks. */
.app.collapsed .sb-brand,
.app.collapsed .sb-switch-txt,
.app.collapsed .sb-workspace,
.app.collapsed .sb-leave,
.app.collapsed .sb-kbd,
.app.collapsed .sb-edit,
.app.collapsed .sb-foot-tools,
.app.collapsed .sidebar-foot-txt,
.app.collapsed .sb-group-txt { display: none; }
/* Owner 2026-10-19: collapsed, the mark and the chevron share one line, as the
   wordmark and the chevron do when the panel is wide; the pair is centred. */
.app.collapsed .sb-head { justify-content: center; padding: 0; gap: 4px; }
.app.collapsed .foot-collapse { margin-left: 0; }
.app.collapsed .sb-head .brand-mark { display: flex; }
.app.collapsed .sb-top { padding: var(--sp-2) 6px var(--sp-1); }
.app.collapsed .sb-switch { justify-content: center; padding: 6px 0; }
.app.collapsed .sb-search { justify-content: center; padding: 6px 0; min-height: 40px; }
.app.collapsed .sb-search-txt { display: none; }
.app.collapsed .sb-nav { padding: 0 6px var(--sp-3); }
.app.collapsed .sb-group-h { border-top: 1px solid var(--line); margin: var(--sp-1) 8px 0; }
/* Still a 24px target (WCAG 2.5.8): the chevron alone, under a thin rule. */
.app.collapsed .sb-group-btn { min-height: 24px; padding: 0; justify-content: center; }
.app.collapsed .nav-item { justify-content: center; gap: 0; padding: 0; min-height: 40px; }
.app.collapsed .nav-item.active::before { left: -6px; }
.app.collapsed .nav-label { display: none; }
.app.collapsed .sb-foot { padding: var(--sp-2) 0; align-items: center; }
.app.collapsed .panel-foot-user { flex-direction: column; gap: 8px; }

/* D-252. The unit system control, as a QUIET control.

   IT DOES NOT REUSE .input, and that is the point. Owner, 2026-07-26: the
   boxed form field read as too heavy here. A field on a page is asking to be
   filled in; a preference sitting permanently under the navigation is not, and
   borrowing the page control's border, fill and inset shadow made a menu
   footer look like a form. So this is its own class: one row, no chrome until
   you reach for it, and 11px to match .sidebar-foot-txt beside it so the
   footer reads as one line of furniture rather than two competing things. */
.foot-units {
  display: flex; align-items: center; gap: 6px;
  flex-wrap: wrap; min-width: 0;
}
.foot-units-label {
  font-size: var(--text-xs); color: var(--ink-400); flex-shrink: 0;
}
.foot-units-ctl { position: relative; display: inline-flex; min-width: 0; }
.foot-units-select {
  appearance: none; -webkit-appearance: none;
  background: transparent; border: 1px solid transparent;
  border-radius: var(--radius-sm);
  /* Right padding leaves room for the caret drawn below. */
  padding: 2px 15px 2px 4px;
  font-family: var(--font-ui); font-size: var(--text-xs); font-weight: 600;
  line-height: 1.5; color: var(--ink-700);
  max-width: 100%; text-overflow: ellipsis;
  cursor: pointer;
}
/* The chrome arrives on approach, so the control is discoverable by pointer
   and by keyboard without advertising itself at rest. */
.foot-units-select:hover:not(:disabled) {
  background: var(--surface-sunken); border-color: var(--line);
}
.foot-units-select:focus-visible {
  outline: none; background: var(--surface-sunken);
  border-color: var(--accent-600); box-shadow: 0 0 0 3px var(--accent-100);
}
.foot-units-select:disabled { color: var(--ink-400); cursor: not-allowed; }
/* The caret is DRAWN, not imported, so it takes a token colour instead of a
   hex baked into a data URI. pointer-events:none keeps the click on the
   select underneath. */
.foot-units-ctl::after {
  content: ""; position: absolute; right: 5px; top: 50%; margin-top: -1px;
  width: 0; height: 0;
  border-left: 3.5px solid transparent;
  border-right: 3.5px solid transparent;
  border-top: 4px solid var(--ink-400);
  pointer-events: none;
}
/* flex-basis forces a hint onto its own line under the row. */
.foot-units-hint {
  flex-basis: 100%;
  font-size: var(--text-xs); line-height: 1.35; color: var(--ink-400);
}
.foot-units-err { color: var(--err-600); }

/* ---------- Brand mark, shared by the rail, the portal and the sign-in card ----
   Owner 2026-07-31: use the public site's logo everywhere, and drop the blue
   square behind the letter in the rail.

   WHAT THE PUBLIC LOGO ACTUALLY IS, measured against
   Websites/event-clinic-site/assets/styles.css rather than assumed. It is NOT
   an image file. There is no logo asset on that site at all. It is typography:

     .logo      font-weight 700, letter-spacing -.02em
     .logo .dot color var(--teal), font-weight 900
     .logo .cl  color var(--ink-60), font-weight 600

   So `event` is bold, the full stop is the brand colour and heavier, and
   `clinic` is lighter and dimmer. `.brand-word` below already had that shape,
   which is why this change is small: the structure was right and the COLOUR
   was not.

   --brand-dot IS DECLARED HERE AND NOT IN tokens.local.css, deliberately.
   verify-design-parity.sh fails on ANY custom property in that file, not only
   on ones that restate a generated token, because the local layer exists for
   declarations and not for values. This is a brand constant that comes from
   outside the design foundation entirely, so it lives beside the only rules
   that use it.

   IT IS NOT THE APP'S ACCENT AND MUST NOT BECOME IT. The marketing site calls
   #D62755 `--teal` and this app calls #3150af `--accent-600`; neither is teal and
   they are not the same brand colour. The dot is the one place the public
   crimson appears, because it is the signature of the wordmark. Every action,
   focus ring and status colour in this app stays on --accent-600. */
:root { --brand-dot: #D62755; }

/* NO BACKGROUND PLATE, NO FIXED BOX. The rail is 56px wide, so the wordmark
   does not fit and the rail gets the MARK: the letter and the dot that follows
   it, which is the wordmark cropped rather than a different logo. */
.brand-mark {
  display: flex; align-items: baseline; gap: 1px;
  font-weight: 700; font-size: 19px; letter-spacing: -.02em;
  color: var(--sidebar-fg); flex-shrink: 0; line-height: 1;
}
.brand-mark::after {
  content: "."; color: var(--brand-dot); font-weight: 900;
}

.brand-word { font-weight: 700; font-size: 15px; color: #fff; letter-spacing: -.02em; }
/* The public site's weight 900 on the stop, not the 650 this used to inherit. */
.brand-word b { color: var(--brand-dot); font-weight: 900; }
/* `clinic` is LIGHTER AND DIMMER than `event` on the public site, which is
   half of what makes the wordmark read as one word with a hinge in it. The
   app rendered both halves identically until now. Dimmed with opacity rather
   than the site's literal --ink-60, because this wordmark sits on a DARK
   sidebar and on the sign-in card, where a fixed mid-grey would sink. */
.brand-word .cl { font-weight: 600; opacity: .68; }

/* ---------- Panel nav items ----------
   These moved from the DARK sidebar to the LIGHT panel on 2026-07-26, so the
   colours inverted with them. Ported from the foundation's .pnav: ink on
   surface, a sunken hover, and a teal-050 wash with a teal-700 label for the
   active item rather than the dark sidebar's solid fill. Keeping the old dark
   values here would have left white text on a white panel. */
.nav-item {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 10px; border-radius: var(--radius);
  color: var(--ink-700); font-size: var(--text-sm); font-weight: 520;
  cursor: pointer; text-decoration: none; position: relative;
}
.nav-item:hover { background: var(--surface-sunken); }
/* The module mark, ported from the foundation's .pico. Icons arrived with the
   collapse chevron (2026-07-26): at 56px the label is gone and the mark is the
   only thing left to aim at. Geometry lives in ModuleIcons.tsx. */
.nav-icon {
  width: 20px; height: 20px; display: grid; place-items: center;
  flex-shrink: 0; color: var(--ink-400);
  transition: color .14s ease;
}
.nav-icon svg {
  width: 17px; height: 17px;
  stroke: currentColor; fill: none; stroke-width: 1.7;
  stroke-linecap: round; stroke-linejoin: round;
}
.nav-item:hover .nav-icon { color: var(--ink-700); }
.nav-item.active .nav-icon { color: var(--accent-600); }
/* Ellipsis rather than wrap, so a long module name cannot push the row to two
   lines and break the rhythm of the list. */
.nav-label {
  flex: 1; min-width: 0;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* The collapsed label. Present in the DOM at all times and revealed only by the
   collapsed rule below, so an expanded row does not pop a tooltip repeating the
   word already beside the pointer. */
.nav-fly { display: none; }
.nav-item.active {
  background: var(--accent-050); color: var(--accent-700); font-weight: 620;
}
.nav-item.active::before {
  content: ""; position: absolute; left: -8px; top: 8px; bottom: 8px; width: 3px;
  border-radius: 0 3px 3px 0; background: var(--accent-600);
}

.sidebar-foot-txt { font-size: var(--text-xs); color: var(--ink-400); }

/* ---------- Main ---------- */
.main { display: flex; flex-direction: column; overflow: hidden; min-width: 0; }
.topbar {
  height: var(--topbar-h); flex-shrink: 0; background: var(--surface-1);
  border-bottom: 1px solid var(--line);
  display: flex; align-items: center; gap: var(--sp-3); padding: 0 var(--sp-4);
}
.topbar-title { font-size: var(--text-md); font-weight: 620; color: var(--ink-900); }
.topbar-spacer { flex: 1; }

.content { flex: 1; overflow-y: auto; scroll-behavior: smooth; }
.page { max-width: var(--maxw); margin: 0 auto; padding: var(--sp-5) var(--sp-6) 64px; }
/* Owner 2026-10-19: the trial banner lines up with the page under it, never edge to edge. */
.shell-banner { max-width: var(--maxw); margin: 0 auto; padding: var(--sp-5) var(--sp-6) 0; }
.page-title { font-size: var(--text-xl); font-weight: 650; letter-spacing: -.02em; color: var(--ink-900); }
.page-intro { color: var(--ink-500); margin-top: var(--sp-2); }

/* ---------- An unbuilt block, and a child of one. D-621, D-623 ----------

   NOT A LINK AND STYLED SO THAT IS OBVIOUS BEFORE ANYBODY CLICKS. The entry
   exists to say the event HAS this part and the product does not yet. It
   dims, loses its pointer, and carries the words "not built" beside the
   label, because a dimmed row alone reads as "you lack permission" and that
   is a different and much worse message. */
.nav-item.is-unbuilt {
  cursor: default;
  color: var(--sidebar-fg-dim);
  opacity: .62;
}
.nav-item.is-unbuilt:hover { background: none; color: var(--sidebar-fg-dim); }
.nav-unbuilt {
  margin-left: auto; flex-shrink: 0;
  font-size: var(--text-xs);
  color: var(--sidebar-fg-dim);
}
/* The panel collapses to the rail's width, and at that size the label and this
   tag are both hidden; the fly-out carries "not built yet" instead. */
.collapsed .nav-unbuilt { display: none; }

/* A CHILD SITS UNDER ITS PARENT BY ORDER, NOT BY AN INDENT. D-623 amended
   2026-09-05 on the owner's report: "nincs egy vonalban a többi szöveggel".
   
   The rule here was `.nav-item.is-child .nav-label { padding-left: var(--sp-4) }`
   -- the LABEL indented and the icon left where it was, because at 56px the
   label is display:none and indenting the icon would have shifted the collapsed
   rail. The intent was right and the result was not: every icon in a column with
   one caption pushed out of line reads as a mistake, not as nesting. Nobody sees
   "belongs to Program"; they see a row that failed to line up.
   
   So the indent is gone and the class with it. Hierarchy is carried by ORDER --
   a child renders directly beneath its parent inside the same group -- which is
   how every other group in this rail already reads. */

/* ---------- Supplier portal, the reduced external shell ---------- */
.portal { min-height: 100vh; display: flex; flex-direction: column; background: var(--surface-0); }
.portal-top {
  height: var(--topbar-h); flex-shrink: 0; background: var(--surface-1);
  border-bottom: 1px solid var(--line);
  display: flex; align-items: center; gap: var(--sp-3); padding: 0 var(--sp-5);
}
.portal-top .brand-word { color: var(--ink-900); }
.portal-top .brand-word b { color: var(--accent-600); }
.portal-tag {
  font-size: var(--text-xs);
  color: var(--ink-400); font-weight: 600;
}
.portal-main { flex: 1; overflow-y: auto; }

/* W277. THE MICROSITE'S ONE-LINE HEADER, and what happens when the line is
   too short. The supplier portal's header is a fixed 56px; the mini menu put
   a name, four tabs, help, the language switch and the account into it with
   flex-wrap, so on a phone the second line wrapped INTO the fixed height and
   the switch, the help and the way out were clipped off the screen. Measured
   2026-09-03 at 390px: only the name and three tabs were visible.
   Here the header grows, the name and the controls share the first line, and
   the tabs take a full-width second line that scrolls sideways. */
.micro-top { height: auto; min-height: var(--topbar-h); flex-wrap: wrap; gap: var(--sp-3) var(--sp-4); padding: var(--sp-2) var(--sp-5); }
.micro-top .micro-tabs { margin: 0; border: 0; flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; }
.micro-top .micro-tabs::-webkit-scrollbar { display: none; }
.micro-top .micro-tab { white-space: nowrap; }
@media (max-width: 720px) {
  .micro-top { padding: var(--sp-2) var(--sp-4); }
  .micro-top .micro-tabs { order: 10; flex-basis: 100%; border-top: 1px solid var(--line); margin-top: calc(-1 * var(--sp-1)); }
  .micro-top .topbar-spacer { display: none; }
  .micro-top .cb-name { flex: 1 1 auto; min-width: 0; }
}
.portal-page { max-width: 760px; margin: 0 auto; padding: var(--sp-6) var(--sp-5) 64px; }
.portal-card {
  margin-top: var(--sp-4); padding: var(--sp-4); border: 1px solid var(--line);
  border-radius: var(--radius-lg); background: var(--surface-1);
}
.portal-card-head { display: flex; align-items: center; gap: var(--sp-3); }

/* ---------- Signed-out landing ---------- */
.signin-wrap { min-height: 100vh; display: grid; place-items: center; padding: var(--sp-6); }
.signin-card {
  background: var(--surface-1); border: 1px solid var(--line); border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md); padding: var(--sp-8); max-width: 400px; width: 100%; text-align: center;
}
.signin-card .brand-word { color: var(--ink-900); font-size: var(--text-xl); }
.signin-card .brand-word b { color: var(--accent-600); }
.signin-actions { display: flex; gap: var(--sp-2); justify-content: center; margin-top: var(--sp-5); }

/* ===========================================================================
   W22.1, D-313. THE EVENT CONTEXT BAR AND LEVEL 3.
   Ported from the design foundation (`design/event-clinic-foundation-v12.html`,
   `.context-bar` and `.tabs` at line 390 onward). Token names are mapped to
   this app's own scale, so both themes follow automatically: every colour below
   is a variable that already has a [data-theme="dark"] value in tokens.css.
   =========================================================================== */

/* ---------- The event context bar ---------- */
.context-bar {
  flex-shrink: 0; display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap;
  padding: 9px var(--sp-4); background: var(--surface-1);
  border-bottom: 1px solid var(--line);
  font-size: var(--text-sm); color: var(--ink-500);
}
.context-bar .cb { display: flex; align-items: center; gap: 6px; min-width: 0; }
.context-bar .cb b { color: var(--ink-900); font-weight: 620; }
.context-bar .cb svg {
  width: 15px; height: 15px; stroke: currentColor; fill: none; stroke-width: 1.7;
  stroke-linecap: round; stroke-linejoin: round; color: var(--ink-400); flex-shrink: 0;
}
.context-divider { width: 1px; height: 14px; background: var(--line-strong); flex-shrink: 0; }
.cb-spacer { flex: 1; }
.cb-leave {
  color: var(--accent-600); font-weight: 600; font-size: var(--text-xs);
  text-decoration: none; padding: 3px 9px; border-radius: var(--radius-sm);
  border: 1px solid var(--line-strong); white-space: nowrap;
  transition: background .13s ease, color .13s ease, border-color .13s ease;
}
.cb-leave:hover { background: var(--accent-050); color: var(--accent-700); border-color: var(--accent-500); }
.cb-leave:focus-visible { outline: 2px solid var(--accent-600); outline-offset: 2px; }

/* ---------- LEVEL 3 AS PAGES, the family row. Owner 2026-09-30: third-level
   items sit on top of the page, never in the panel. A segmented row, so it
   reads as a different control from the ?tab= bar that may sit beneath it. */
.family-wrap {
  flex-shrink: 0; background: var(--surface-1); border-bottom: 1px solid var(--line);
  padding: var(--sp-3) var(--sp-4);
  overflow-x: auto;
}
.family-seg { min-width: max-content; }
.family-seg .seg-btn { text-decoration: none; white-space: nowrap; display: inline-flex; align-items: center; }

/* ---------- LEVEL 3, tabs with sliding ink ---------- */
.tabs-wrap {
  flex-shrink: 0; background: var(--surface-1); border-bottom: 1px solid var(--line);
  padding: 0 var(--sp-4);
  /* The bar must scroll on its own rather than widen the page. Six tabs plus a
     More menu fits a laptop and does not fit a narrow window. */
  overflow-x: auto;
}
.tabs {
  display: flex; align-items: flex-end; gap: 1px; position: relative;
  min-width: max-content;
}
.tab {
  padding: 9px 13px; font-size: var(--text-sm); font-weight: 550; color: var(--ink-500);
  border: 0; background: transparent; white-space: nowrap; cursor: pointer;
  border-radius: var(--radius) var(--radius) 0 0;
  display: inline-flex; align-items: center; gap: 6px; position: relative;
  transition: color .13s ease, background .13s ease;
}
.tab:hover { color: var(--ink-900); background: var(--surface-sunken); }
.tab.on { color: var(--accent-700); font-weight: 640; }
.tab:focus-visible { outline: 2px solid var(--accent-600); outline-offset: -2px; }
.tab .tn {
  font-size: var(--text-min); font-weight: 700; padding: 0 6px; border-radius: 9px;
  background: var(--surface-sunken); color: var(--ink-400);
  font-variant-numeric: tabular-nums; transition: background .13s ease, color .13s ease;
}
.tab.on .tn { background: var(--accent-100); color: var(--accent-700); }
.tab-chev { width: 12px; height: 12px; stroke: currentColor; fill: none; stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round; }
.tab-ink {
  position: absolute; bottom: 0; left: 0; height: 2px; width: 0; border-radius: 2px 2px 0 0;
  background: var(--accent-600); pointer-events: none;
  transition: transform .26s cubic-bezier(.22, .61, .36, 1), width .26s cubic-bezier(.22, .61, .36, 1);
}
/* THE INK IS DECORATION AND MUST NOT BE THE ONLY SIGNAL. .tab.on already
   changes colour and weight, so a person who cannot see the underline still
   knows which tab is open, and reduced motion simply stops it moving. */
@media (prefers-reduced-motion: reduce) { .tab-ink { transition: none; } }

.tab-more-wrap { position: relative; }
.menu.tab-menu {
  /* Fixed, placed by Tabs.tsx from the More button: inside the scrolling
     `.tabs-wrap` an absolute menu is clipped to the bar's 35px. */
  position: fixed; top: 0; left: 0; min-width: 200px; z-index: 40;
  background: var(--surface-1); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow-md); padding: 4px; display: none;
}
.menu.tab-menu.show { display: block; }
.menu.tab-menu .menu-item {
  display: block; width: 100%; text-align: left; border: 0; background: transparent;
  padding: 7px 10px; border-radius: var(--radius-sm); cursor: pointer;
  font-size: var(--text-sm); color: var(--ink-700);
}
.menu.tab-menu .menu-item:hover { background: var(--surface-sunken); color: var(--ink-900); }
.menu.tab-menu .menu-item.on { color: var(--accent-700); font-weight: 620; }

/* ============================================================================
   NARROW SCREENS. 2026-08-03.

   THE SHELL HAD NO WIDTH MEDIA QUERY AT ALL. `.app` reserved rail 56px plus
   panel 230px at every viewport, so on a 360px screen the content column was
   MEASURED at 74px: every heading broke one word per line and no screen in the
   app was usable. It was not one screen's bug, it was the frame's.

   THE FIX REUSES THE COLLAPSE THE FOUNDATION ALREADY DESIGNED rather than
   inventing a mobile layout. `.app.collapsed` narrows the panel to the rail's
   width, which is exactly why the panel items were given icons in the first
   place. At 360px that returns 248px to the content instead of 74.

   THE PANEL IS NARROWED, NOT HIDDEN, and that is deliberate. The panel is how
   you move between the surfaces of one group, so hiding it would remove the
   only route to most of the app on the device where a user is least able to go
   hunting for another one. Collapsed, the icons still navigate.

   THE COLLAPSE TOGGLE IS HIDDEN HERE BECAUSE IT WOULD HAVE NOTHING TO DO.
   Both states resolve to the same columns below this width, so the chevron
   would be a control that silently does nothing, which is worse than no
   control. It returns the moment there is room for it to mean something.
   ========================================================================= */
@media (max-width: 900px) {
  /* The COLUMNS are not set here. `AppShell` adds the `collapsed` class below
     this same breakpoint, which brings all fifteen of the collapsed rules with
     it rather than only the grid. Setting the columns here as well would be a
     second source of truth for one behaviour, and the two would drift.

     What is left here is the two things the class cannot express: a control
     that has no job at this width, and padding that is a tenth of the column. */
  .foot-collapse { display: none; }
  .page { padding: var(--sp-4) var(--sp-4) 48px; }
  .shell-banner { padding: var(--sp-4) var(--sp-4) 0; }
}

/* ============================================================================
   PHONES. W715, D-1333, 2026-09-30.

   At 360px the rule above still left two 56px strips beside the page, so every
   page measured 248px wide, on the dashboard, the event list and the crowd
   report alike. Below 640px the rail and the panel leave the grid and slide in
   together from the topbar's menu button. The panel shows its labels there,
   because `AppShell` drops `collapsed` at this width: a collapsed panel names
   its icons only on hover, and a touch screen has none.

   `visibility: hidden` while closed keeps the off-screen links out of the tab
   order. The drawer sits under the content drawer's 99 and 100.
   ========================================================================= */
.topbar-menu, .nav-scrim { display: none; }
@media (max-width: 640px) {
  .app, .app.collapsed { grid-template-columns: minmax(0, 1fr); }
  .sidebar {
    position: fixed; top: 0; bottom: 0; left: 0; z-index: 97;
    width: min(var(--sidebar-w), calc(100vw - var(--sp-8)));
    transform: translateX(-100%); visibility: hidden;
    transition: transform .2s ease, visibility .2s;
  }
  /* Opening does not transition visibility, so the drawer can take focus at once.
     Closing does, so it stays visible while it slides away. */
  .app.nav-open .sidebar { transform: none; visibility: visible; transition: transform .2s ease; }
  .nav-scrim { display: block; position: fixed; inset: 0; z-index: 96; border: 0; padding: 0; cursor: default; background: rgba(28, 33, 31, .28); }
  .topbar { padding-left: var(--sp-2); gap: var(--sp-2); }
  .topbar-menu {
    display: inline-grid; place-items: center; width: 40px; height: 40px;
    border: 0; border-radius: var(--radius); background: transparent; color: var(--ink-700); cursor: pointer;
  }
  .topbar-menu:hover { background: var(--surface-sunken); }
  .topbar-menu:focus-visible { outline: 2px solid var(--accent-600); outline-offset: 1px; }
  .topbar-menu svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; }
}
@media (max-width: 640px) and (prefers-reduced-motion: reduce) {
  .sidebar { transition: none; }
}

/* ---------- THE BREADCRUMB, E3 ----------
   The location cue in the topbar. The last item is the page and not a link. */
.crumbs { min-width: 0; overflow: hidden; }
.crumbs ol { display: flex; align-items: center; gap: 6px; list-style: none; margin: 0; padding: 0; white-space: nowrap; }
.crumbs li { display: flex; align-items: center; gap: 6px; min-width: 0; font-size: var(--text-sm); color: var(--ink-500); }
.crumbs li + li::before { content: "›"; color: var(--ink-400); }
.crumbs a { color: var(--ink-500); text-decoration: none; overflow: hidden; text-overflow: ellipsis; display: inline-flex; align-items: center; min-height: 24px; }
.crumbs a:hover { color: var(--accent-700); text-decoration: underline; }
.crumbs a:focus-visible { outline: 2px solid var(--accent-600); outline-offset: 2px; }
.crumbs [aria-current="page"] { color: var(--ink-900); font-weight: 620; overflow: hidden; text-overflow: ellipsis; }
.crumb-event { display: inline-flex; align-items: center; gap: 6px; }
.crumb-event img { max-height: 20px; width: auto; }
@media (max-width: 640px) {
  .crumbs li:not(:last-child) { display: none; }
  .crumbs li:last-child::before { display: none; }
}

/* The device choice sits on the family row's line, at its right. */
.family-wrap { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2) var(--sp-4); }
.device-seg { margin-left: auto; min-width: max-content; }
.device-seg .seg-btn { text-decoration: none; white-space: nowrap; display: inline-flex; align-items: center; }

/* ---------- THE COMMAND BAR, E11 ---------- */
.cmdbar {
  width: min(640px, calc(100vw - var(--sp-8))); max-height: min(560px, calc(100vh - 120px));
  margin: 12vh auto auto; padding: 0;
  border: 1px solid var(--line); border-radius: var(--radius-lg);
  background: var(--surface-1); color: var(--ink-900); box-shadow: var(--shadow-md);
}
.cmdbar::backdrop { background: rgba(28, 33, 31, .32); }
.cmdbar-box { display: flex; flex-direction: column; max-height: inherit; }
.cmdbar-input {
  border: 0; border-bottom: 1px solid var(--line); padding: 14px 16px;
  font: inherit; font-size: var(--text-md); background: transparent; color: var(--ink-900);
}
.cmdbar-input:focus-visible { outline: 2px solid var(--accent-600); outline-offset: -2px; }
.cmdbar-list { list-style: none; margin: 0; padding: var(--sp-1); overflow-y: auto; }
.cmdbar-group { padding: 8px 10px 4px; font-size: var(--text-min); font-weight: 650; color: var(--ink-500); }
.cmdbar-item {
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3);
  min-height: 36px; padding: 6px 10px; border-radius: var(--radius-sm); cursor: pointer; font-size: var(--text-sm);
}
.cmdbar-item.on { background: var(--accent-050); color: var(--accent-700); }
.cmdbar-sub { color: var(--ink-500); font-size: var(--text-min); }
.cmdbar-empty { padding: 12px 16px; color: var(--ink-500); font-size: var(--text-sm); margin: 0; }
.cmdbar-foot { margin: 0; padding: 8px 16px; border-top: 1px solid var(--line); color: var(--ink-500); font-size: var(--text-min); }

/* Owner 2026-10-19: the collapsed rail's hover label. Small, quiet, beside the
   icon, a soft fade in; no arrow, no delay games. Not shown when expanded. */
.rail-tip {
  position: fixed; z-index: 120; transform: translateY(-50%);
  padding: 5px 10px; border-radius: var(--radius-sm);
  background: var(--ink-900); color: var(--surface-1);
  font-size: var(--text-min); font-weight: 560; line-height: 1.3; letter-spacing: .005em;
  white-space: nowrap; max-width: 260px; overflow: hidden; text-overflow: ellipsis;
  box-shadow: var(--shadow-sm); pointer-events: none;
  animation: rail-tip-in .12s ease-out;
}
@keyframes rail-tip-in { from { opacity: 0; transform: translate(-3px, -50%); } to { opacity: 1; transform: translate(0, -50%); } }
@media (prefers-reduced-motion: reduce) { .rail-tip { animation: none; } }

/* 2026-10-20 design audit. AN IN-PAGE TAB BAR HAS NO SCROLLER. The shell's bar
   sits in `.tabs-wrap`, which scrolls; three screens (SpaceDetail,
   VenueDetailScreen, TeamAvailabilityScreen) render <Tabs> straight into the
   page, where `min-width: max-content` widened the whole content column: the
   space view measured 747px inside a 390px phone. A scroller here would clip
   the More menu, so on a phone the bar WRAPS instead, and each open tab carries
   its own underline because one sliding ink cannot follow a second line. */
@media (max-width: 640px) {
  :not(.tabs-wrap) > .tabs { min-width: 0; flex-wrap: wrap; }
  :not(.tabs-wrap) > .tabs > .tab-ink { display: none; }
  :not(.tabs-wrap) > .tabs .tab.on { box-shadow: inset 0 -2px 0 var(--accent-600); }
}

/* Screen tour G11: reserve the scrollbar's gutter on both edges, so the icons in the collapsed rail stay centred. */
.sb-nav { scrollbar-gutter: stable both-edges; }
