/* Paper design system (design-overhaul, locked tokens 2026-08-20):
   ground/ink/secondary/muted palette, hairline dividers, semantic green +
   amber reserved for status use only (never decorative), Geist type with a
   system-ui fallback stack. This file is the single source of truth for
   the token custom properties -- other stylesheets (e.g. auth.css) that
   must keep working on pages which don't load this file yet read the same
   values via `var(--token, <literal>)` fallbacks rather than redeclaring
   `:root`. Geist's Google Fonts <link> stays per-page; this file only
   declares the font-family stack.

   Base only -- no components beyond what T1 needs (buttons, inputs, card,
   hairline divider, nav skeleton). Later tasks extend, they don't fork. */

:root {
  --ground: #FAFAF9;
  --ink: #18181B;
  --secondary: #52525B;
  --muted: #A1A1AA;
  --hairline: #EDEDEB;
  --hairline-2: #E4E4E7;
  --card: #FFFFFF;
  --green: #15803D;
  --green-bg: #ECFDF3;
  --amber: #B45309;
  --amber-bg: #FEF3E2;
  --danger: #B91C1C;
  --radius: 10px;
  --radius-lg: 14px;
  --font-sans: 'Geist', system-ui, -apple-system, "Segoe UI", Roboto, "PingFang SC",
    "Noto Sans SC", "Microsoft YaHei", "Noto Sans Thai", "Leelawadee UI", sans-serif;
  --font-mono: 'Geist Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  /* `light dark` (not a bare `light`) lets native controls (scrollbars,
     form controls) follow the SYSTEM preference by default -- the
     [data-theme] overrides below narrow this to `only light`/`only dark`
     for the two forced states, so an explicit choice always wins over
     system for native chrome too, matching the custom-token behavior. */
  color-scheme: light dark;
}

/* Dark theme tokens (dark-mode batch, 2026-08-20): values lifted from the
   founder-pinned boards (ModernDarkConsole.dc.html / ModernDarkMobile.dc.html)
   -- near-black ground, one elevated dark surface reused for every card/nav/
   input/menu (the boards actually use two adjacent dark tones, #101012 for
   the sidebar and #161618 for cards; collapsed to one #161618 tier here to
   keep the existing single-tier ground/card token model, not a two-tier
   fork -- judgment call, see the decision-record addendum), dark hairlines,
   off-white ink, and green/amber pairs re-picked from the boards' Live/
   Ready and "N possible" pills for AA contrast against the dark ground.
   `--danger` isn't on the boards (no error state shown there) -- picked a
   lighter red (Tailwind red-400) for the same near-black-background AA
   contrast reasoning as the rest of this block.

   Defined TWICE on purpose, in BOTH guards below (identical values): the
   `@media (prefers-color-scheme: dark)` block drives the default "system"
   theme state, `[data-theme="dark"]` drives the explicit toggle override --
   the toggle must win over system in both directions (see account_chip.js),
   so each guard needs its own complete copy rather than one deferring to
   the other. Every value here also exists in the light :root above --
   never define a color ONLY inside one of these guards. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --ground: #0A0A0B;
    --ink: #EDEDED;
    /* Tightening batch (2026-08-20), P1 dark-contrast fix: the boards'
       original #71717A read 4.10:1 on --ground / 3.74:1 on --card, both
       under the 4.5:1 AA body-text floor -- #8A8A93 is the minimal bump
       (computed, not eyeballed) that clears both: 5.78:1 on --ground,
       5.28:1 on --card. Bumped identically in this guard and the
       `[data-theme="dark"]` one below per this file's own "defined TWICE
       on purpose" rule. */
    --secondary: #8A8A93;
    --muted: #52525B;
    --hairline: #26262B;
    --hairline-2: #2E2E33;
    --card: #161618;
    --green: #4ADE80;
    --green-bg: #0F2417;
    --amber: #FBBF24;
    --amber-bg: #2A2113;
    --danger: #F87171;
  }
}

:root[data-theme="light"] {
  color-scheme: only light;
}

:root[data-theme="dark"] {
  color-scheme: only dark;
  --ground: #0A0A0B;
  --ink: #EDEDED;
  --secondary: #8A8A93;
  --muted: #52525B;
  --hairline: #26262B;
  --hairline-2: #2E2E33;
  --card: #161618;
  --green: #4ADE80;
  --green-bg: #0F2417;
  --amber: #FBBF24;
  --amber-bg: #2A2113;
  --danger: #F87171;
}

* { box-sizing: border-box; }

body {
  background: var(--ground);
  color: var(--ink);
  font-family: var(--font-sans);
  margin: 0;
}

.card {
  background: var(--card);
  border: 1px solid var(--hairline);
  border-radius: var(--radius-lg);
  padding: 1.25rem;
}

hr,
.hairline {
  border: none;
  border-top: 1px solid var(--hairline);
  margin: 0;
}

.btn,
.btn2 {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  padding: .7rem 1.1rem;
  border-radius: var(--radius);
  font: inherit;
  font-size: 1rem;
  font-weight: 600;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
}

.btn {
  background: var(--ink);
  color: var(--ground);
  border: none;
}

.btn:disabled {
  opacity: .55;
  cursor: default;
}

/* Tightening batch (2026-08-20), P2: in dark mode `.btn`'s ink/ground
   pair INVERTS to a light-on-dark pill (--ink is the off-white, --ground
   the near-black) -- opacity alone blends that light fill toward the
   dark page behind it, reading as just another medium-dark surface
   rather than visibly "disabled" next to the vivid enabled pill. A flat
   muted fill (bypassing the opacity blend entirely) keeps the
   distinction unambiguous for BOTH the real `disabled` attribute and
   `aria-disabled="true"` (several buttons in this app set both
   together). Both dark guards, identical values, matching this file's
   own "defined TWICE on purpose" rule. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .btn:disabled,
  :root:not([data-theme="light"]) .btn[aria-disabled="true"] {
    background: var(--hairline-2);
    color: var(--muted);
    opacity: 1;
  }
}

:root[data-theme="dark"] .btn:disabled,
:root[data-theme="dark"] .btn[aria-disabled="true"] {
  background: var(--hairline-2);
  color: var(--muted);
  opacity: 1;
}

.btn2 {
  background: var(--card);
  color: var(--ink);
  border: 1px solid var(--hairline-2);
}

/* Kept in sync by hand with auth.css's identical selector (fix round 1,
   minor 4; pre-merge correction: every page dual-loads THIS file first,
   then auth.css -- auth.css's copy is a value-identical duplicate, not a
   fallback for pages that skip this file. Since auth.css loads second,
   its copy wins the cascade, so BOTH must stay value-identical or they'd
   silently diverge). If you change one, change the other. E2E round 1,
   finding 3: type="url"/type="search" added (future-proof -- the current
   landing field is type="text" per the same round's finding 1, but any
   future url/search field on a Paper page should pick up styling
   automatically rather than reading as unstyled browser chrome). */
/* Tightening batch (2026-08-20), P2 addition: type="date" (console
   Settings' event-date field) previously fell through to unstyled
   browser chrome next to its styled siblings -- added here rather than a
   page-local rule since it's the same shared height/border/radius/font
   treatment, not a one-off. auth.css's kept-in-sync duplicate below gets
   the same addition. */
input[type="email"],
input[type="password"],
input[type="text"],
input[type="url"],
input[type="search"],
input[type="date"] {
  width: 100%;
  padding: .7rem .8rem;
  font: inherit;
  font-size: 1rem;
  border: 1px solid var(--hairline-2);
  border-radius: var(--radius);
  background: var(--card);
  color: var(--ink);
}

/* E2E round 1, finding 3: the native file-picker button otherwise reads as
   unstyled browser chrome next to Paper's inputs/buttons -- styled like
   .btn2 (hairline border, radius, padding), minimal on purpose. */
input[type="file"]::file-selector-button {
  font: inherit;
  font-weight: 600;
  color: var(--ink);
  background: var(--card);
  border: 1px solid var(--hairline-2);
  border-radius: var(--radius);
  padding: .5rem .9rem;
  margin-right: .75rem;
  cursor: pointer;
}

input[type="file"]::file-selector-button:hover {
  background: var(--hairline);
}

/* Top-bar skeleton for later tasks (console/event/index nav rebuilds) --
   just the structural shell, no page-specific content. Superseded on the
   admin-shell pages (console/albums/my-face) by `.shell-nav` below, which
   adds the max-width inner container those pages need; kept here
   unchanged for any page still using the bare skeleton. */
.nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 1rem 1.5rem;
  border-bottom: 1px solid var(--hairline);
}

/* ==========================================================================
   Modern admin shell layout (design polish, 2026-08-20): a full-width nav
   bar + max-width content container + page-header + full-width list-row +
   stat-card grid, shared by console.html/albums.html/my_face.html. Fixes
   the "narrow centered column floating in whitespace" complaint -- the
   content area now runs ~1200px wide like a standard SaaS admin (Linear/
   Stripe register), not a 720px auth-card width stretched onto a
   dashboard. Built entirely from the token set above -- no new colors.
   A few surfaces (the create-event flow, Settings' form cards) stay
   deliberately narrow via `.shell-container-narrow`/`.shell-form` --
   focused single-column flows read better capped, only list/grid views
   need the full 1200px. ========================================== */

.shell-nav {
  width: 100%;
  background: var(--card);
  border-bottom: 1px solid var(--hairline);
}

.shell-nav-inner {
  max-width: 1200px;
  margin: 0 auto;
  padding: 0 24px;
  height: 58px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
}

.shell-nav-left {
  display: flex;
  align-items: center;
  gap: 24px;
  min-width: 0;
}

.shell-nav-logo {
  font-weight: 700;
  font-size: 1.05rem;
  color: var(--ink);
  text-decoration: none;
  white-space: nowrap;
}

.shell-nav-link {
  color: var(--secondary);
  text-decoration: none;
  font-size: .92rem;
  font-weight: 500;
  white-space: nowrap;
}

.shell-nav-link.active {
  color: var(--ink);
  font-weight: 600;
}

.shell-nav-right {
  display: flex;
  align-items: center;
  gap: 1rem;
  flex-shrink: 0;
}

/* Wide content container -- events/albums lists, event detail (Overview +
   Settings tabs live inside this; Settings' own cards cap narrower via
   `.shell-form` below, they don't stretch to 1200px). */
.shell-container {
  width: 100%;
  max-width: 1200px;
  margin: 0 auto;
  padding: 32px 24px 48px;
}

/* Narrow, centered container for focused single-column flows (create
   event, the post-create share view) -- a wide canvas is CORRECT for
   list/detail views but wrong for a short form or a QR card. */
.shell-container-narrow {
  width: 100%;
  max-width: 560px;
  margin: 0 auto;
  padding: 32px 24px 48px;
}

/* Settings-style stacked form cards inside a wide `.shell-container` --
   caps at 640px so a name/description/date form doesn't stretch edge to
   edge on a 1200px canvas. */
.shell-form {
  max-width: 640px;
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
}

/* Page-header: title (+ optional subtitle) left, one primary action right,
   aligned to the title block's bottom edge; stacks under 640px with the
   action full-width. */
.shell-page-header {
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  gap: 1rem;
  flex-wrap: wrap;
  margin-bottom: 1.5rem;
}

.shell-page-header h1 {
  margin: 0 0 .35rem;
}

.shell-page-header p {
  margin: 0;
}

/* Full-width list-row card -- event rows, album rows: content left,
   actions right, spans the full content width (replaces the old
   `.list-item` narrow-column styling). */
.shell-list-row {
  background: var(--card);
  border: 1px solid var(--hairline);
  border-radius: var(--radius);
  padding: 1rem 1.25rem;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 1rem;
  flex-wrap: wrap;
  width: 100%;
}

/* Stat-card grid: 3-up on desktop, 1-up on mobile. */
.shell-stat-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1rem;
}

.shell-stat-card {
  background: var(--card);
  border: 1px solid var(--hairline);
  border-radius: var(--radius-lg);
  padding: 1.25rem 1.4rem;
  display: flex;
  flex-direction: column;
  gap: .35rem;
}

.shell-stat-number {
  font-family: var(--font-mono);
  font-size: 1.9rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  line-height: 1.1;
}

.shell-stat-label {
  color: var(--secondary);
  font-size: .85rem;
}

@media (max-width: 640px) {
  .shell-container,
  .shell-container-narrow {
    padding: 24px 16px 40px;
  }
  .shell-page-header {
    flex-direction: column;
    align-items: stretch;
  }
  .shell-page-header .btn,
  .shell-page-header .btn2 {
    width: 100%;
  }
  .shell-stat-grid {
    grid-template-columns: 1fr;
  }
}

@media (max-width: 480px) {
  .shell-nav-inner {
    gap: .5rem;
    padding: 0 16px;
  }
  .shell-nav-left {
    gap: 12px;
  }
  .shell-nav-right {
    gap: .5rem;
  }
  .shell-nav-logo {
    font-size: .95rem;
  }
  .shell-nav-link {
    font-size: .85rem;
  }
}

/* Custom language dropdown (design directive 2026-08-20: "language selector
   is always top right", ONE shared component replacing every native
   <select data-lang-select> on every page). Progressively enhanced by
   app/static/i18n.js onto any [data-lang-dropdown] marker element -- see
   that file for the ARIA menu-button pattern (aria-haspopup="listbox",
   aria-expanded, Escape/ArrowUp/ArrowDown/Enter, click-outside). Light
   variant below is the default everywhere; .lang-dropdown-glass is a dark/
   translucent BUTTON alternate for later use on dark hero pages -- the
   dropdown MENU itself always stays the light Paper card regardless. */
.lang-dropdown {
  position: relative;
  display: inline-block;
}

.lang-dropdown-btn {
  display: inline-flex;
  align-items: center;
  gap: .35rem;
  min-height: 36px;
  padding: .4rem .7rem;
  background: var(--card);
  color: var(--ink);
  border: 1px solid var(--hairline-2);
  border-radius: 999px;
  font: inherit;
  font-size: .85rem;
  font-weight: 500;
  cursor: pointer;
}

.lang-dropdown-btn:hover {
  background: var(--hairline);
}

.lang-dropdown-chevron {
  color: var(--secondary);
  transition: transform .15s ease;
  flex-shrink: 0;
}

.lang-dropdown-btn[aria-expanded="true"] .lang-dropdown-chevron {
  transform: rotate(180deg);
}

.lang-dropdown-menu {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 20;
  min-width: 140px;
  margin: 0;
  padding: .35rem;
  list-style: none;
  background: var(--card);
  border: 1px solid var(--hairline-2);
  border-radius: var(--radius);
  box-shadow: 0 4px 16px rgba(0, 0, 0, .08);
}

.lang-dropdown-menu[hidden] {
  display: none;
}

.lang-dropdown-option {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .75rem;
  padding: .5rem .6rem;
  border-radius: 6px;
  font-size: .9rem;
  color: var(--ink);
  cursor: pointer;
}

.lang-dropdown-option:hover,
.lang-dropdown-option:focus {
  background: var(--hairline);
  outline: none;
}

.lang-dropdown-check {
  color: var(--ink);
  font-size: .8rem;
  opacity: 0;
}

.lang-dropdown-option[aria-selected="true"] .lang-dropdown-check {
  opacity: 1;
}

/* Dark/glass button variant -- adopted by event.html's photo-hero landing
   screen (design directive amendment 2026-08-20: moved into the same batch,
   not deferred). event.html toggles this class on/off per screen (glass on
   "landing", light everywhere else) via showScreen(). The ::before scrim is
   a self-contained soft dark backdrop behind the pill, independent of
   whatever sits behind it -- guarantees the white label/chevron stay
   readable whether the hero is a busy photo/mosaic OR the plain light-gray
   "no photos yet" ground (--hairline), not just "any poster/mosaic image"
   as originally scoped. */
.lang-dropdown-glass {
  position: relative;
}

.lang-dropdown-glass::before {
  content: "";
  position: absolute;
  inset: -6px;
  border-radius: 999px;
  background: radial-gradient(closest-side, rgba(0, 0, 0, .32), rgba(0, 0, 0, 0));
  pointer-events: none;
}

.lang-dropdown-glass .lang-dropdown-btn {
  position: relative;
  background: rgba(255, 255, 255, .12);
  border-color: rgba(255, 255, 255, .28);
  color: #FFFFFF;
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}

.lang-dropdown-glass .lang-dropdown-btn:hover {
  background: rgba(255, 255, 255, .2);
}

.lang-dropdown-glass .lang-dropdown-chevron {
  color: rgba(255, 255, 255, .85);
}

:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
}

/* Accessible opt-out/misuse footer link (audit P0, guest consent batch):
   shared canonical treatment for the bystander-consent affordance -- the
   old per-page versions were as low as 2.45:1 contrast at 12.8px with a
   ~18px hit area (the least accessible element in the app). Every guest
   page ("event.html", "index.html") uses THIS class rather than a local
   re-declaration, so a future contrast/hit-area fix only has one place to
   land. `padding: .3rem 0` + `display: inline-block` gets the vertical hit
   area to ~24px+ without changing the link's visual size. */
.misuse-link {
  color: var(--secondary);
  text-decoration: none;
  font-size: .85rem;
  padding: .3rem 0;
  display: inline-block;
}

.misuse-link:hover {
  text-decoration: underline;
}

/* Visually hidden but still focusable/selectable (unlike `.hidden`'s
   `display: none`) -- for the shared hidden share-link `<input>` the
   console-sidebar batch's Overview/Sharing "Copy link" buttons both read
   from via `.select()` + the `document.execCommand("copy")` fallback,
   which needs a real, selectable element in the layout. Standard
   clip-rect off-screen pattern. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* TODO (tightening batch fix round 1, 2026-08-20, NIT, pre-existing): the
   reviewer noted `#detail-share-link` (the console's `.visually-hidden`
   share-link `<input>`, whose `.value` is a full URL) can still extend its
   scrolling ancestor's scrollWidth -- masked today by that ancestor's own
   `overflow-x: hidden`, so it's not currently visible, but worth root-
   causing (absolutely positioned elements still count toward scrollable
   overflow even when clipped to 1x1) before something removes that
   overflow-x guard. Not fixed blind in this round -- needs a live
   scrollWidth measurement to confirm the real cause before touching it. */

/* Shared loading spinner (console-sidebar batch, 2026-08-20): replaces
   every text-only "Loading…" gate on console.html/albums.html/
   my_face.html. Hairline-ring track, ink arc, ~28px. No dedicated
   `prefers-reduced-motion` override needed -- the global rule above
   already collapses this element's `animation-duration` to ~0 and its
   `animation-iteration-count` to 1, which reads as a static ring (spec:
   "prefers-reduced-motion -> static"). */
.spinner {
  display: inline-block;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  border: 3px solid var(--hairline-2);
  border-top-color: var(--ink);
  animation: spinner-spin .7s linear infinite;
}

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

.shell-loading {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: .85rem;
  padding: 4rem 1rem;
  text-align: center;
}

/* ==========================================================================
   Left-sidebar console shell (console-sidebar batch, 2026-08-20): the
   founder-pinned desktop (>=1024px) layout for the three logged-in app-shell
   pages (console.html/albums.html/my_face.html), replacing the modern-
   admin-shell top-nav ABOVE that breakpoint only -- `.shell-nav` (the
   existing top-nav bar) stays exactly as-is and keeps serving BELOW
   1024px (`.shell-layout`'s children are display:none there instead).
   Board references: ModernConsole.dc.html (sidebar shell + event-detail
   composition) and TopnavConsole.dc.html (stat cards + tabs content
   model) -- every color here is a `var(--token)` from the sheet above, no
   new literal hex values (a dark-mode pass follows this batch). ======= */

/* `.shell-layout` is the ALWAYS-present outer row (sidebar + content) --
   only its `.shell-sidebar` child and `.shell-topbar` (inside
   `.shell-content`) toggle by breakpoint; `.shell-nav`, the existing
   mobile top-nav bar, lives INSIDE `.shell-content` too (above `#app`)
   and is what's actually visible below 1024px, toggling the other way. */
.shell-layout {
  display: flex;
  min-height: 100vh;
  align-items: stretch;
}

.shell-sidebar {
  display: none;
  width: 240px;
  flex-shrink: 0;
  background: var(--card);
  border-right: 1px solid var(--hairline);
  flex-direction: column;
  gap: 22px;
  padding: 20px 14px;
  /* Tightening batch (2026-08-20), amendment 3: sticky at >=1024px (see
     the media guard below) so content scrolls under a fixed sidebar
     instead of the sidebar scrolling away with it. `align-self` isn't
     enough on its own inside `.shell-layout`'s flex row -- `height: 100vh`
     caps it to the viewport and `overflow-y: auto` lets its own nav
     scroll independently if it ever exceeds that (it doesn't today, but
     this keeps a long nav from ever getting clipped). Below `z-index`
     stays under `.chip-menu`'s 40 (auth.css) -- the avatar chip lives in
     the topbar now, not here, but this still shouldn't ever compete with
     it. */
  z-index: 10;
}

@media (min-width: 1024px) {
  .shell-nav {
    display: none;
  }
  .shell-sidebar {
    display: flex;
    position: sticky;
    top: 0;
    height: 100vh;
    overflow-y: auto;
  }
}

.shell-sidebar-brand {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 6px;
  font-weight: 700;
  font-size: 1.05rem;
  color: var(--ink);
  text-decoration: none;
  white-space: nowrap;
}

.shell-sidebar-mark {
  width: 22px;
  height: 22px;
  border-radius: 6px;
  background: var(--ink);
  color: var(--ground);
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.shell-sidebar-nav {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.shell-sidebar-link {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: .55rem .6rem;
  border-radius: 8px;
  font-size: .875rem;
  font-weight: 500;
  color: var(--secondary);
  text-decoration: none;
  background: none;
  border: none;
  font-family: inherit;
  cursor: pointer;
  text-align: left;
  width: 100%;
}

.shell-sidebar-link svg {
  flex-shrink: 0;
}

.shell-sidebar-link:hover {
  background: var(--hairline);
}

.shell-sidebar-link.active {
  color: var(--ink);
  font-weight: 600;
  background: var(--hairline);
}

/* `.shell-sidebar-bottom`, its chip-width rule, the upward-opening
   `.chip-menu` variant, and `.chip-label` (the side-by-side avatar+email
   layout) are ALL GONE (tightening batch amendment 1, 2026-08-20) -- the
   account chip moved from the sidebar's bottom to the topbar's right end
   (`#fm-account-chip-topbar` below), avatar-only, using the one ordinary
   downward-opening `.chip-menu` (auth.css) every other mount already
   uses. The sidebar keeps wordmark + nav only. */

.shell-content {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}

.shell-topbar {
  display: none;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  height: 58px;
  flex-shrink: 0;
  padding: 0 28px;
  background: var(--card);
  border-bottom: 1px solid var(--hairline);
}

@media (min-width: 1024px) {
  .shell-topbar {
    display: flex;
    /* Tightening batch (2026-08-20), amendment 3: sticky at the top of
       the content column so it stays put while the tab/list content
       scrolls under it -- `z-index: 10` clears ordinary page content but
       stays below `.chip-menu`'s 40 (auth.css), so the avatar dropdown
       (now mounted inside this bar) is never clipped by it. The solid
       `--card` background + existing bottom hairline already give it a
       clean edge in both themes -- nothing scrolls-under-transparent. */
    position: sticky;
    top: 0;
    z-index: 10;
  }
}

.shell-breadcrumb {
  display: flex;
  align-items: center;
  gap: .45rem;
  min-width: 0;
  font-size: .82rem;
  color: var(--muted);
  white-space: nowrap;
}

/* The breadcrumb's first ("Events") segment is a real `<button>` -- the
   back-affordance the boards imply by never drawing a separate "Back"
   control (console-sidebar batch). `all: unset` drops button chrome
   while keeping every inherited property (color, font) exactly as the
   surrounding `.shell-breadcrumb` text -- no new literal color, `cursor`
   is the only non-inherited property that needs restating. */
.shell-breadcrumb button {
  all: unset;
  cursor: pointer;
}

.shell-breadcrumb button:hover {
  color: var(--ink);
}

.shell-breadcrumb .shell-breadcrumb-current {
  color: var(--ink);
  font-weight: 500;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* --- event-detail composition (console-sidebar batch) -------------------
   Header (name + Live pill + meta line + "Open guest page"), the four
   tabs (Overview/Albums/Sharing/Settings), the access-code card (QR +
   code + actions, reused verbatim on both Overview and Sharing), and the
   Albums-tab full source rows. Shares `.shell-stat-grid`/`.shell-stat-
   card`/`.shell-stat-number`/`.shell-stat-label` (modern-admin-shell,
   above) for the stat cards -- no new stat-card rules needed here. */

.shell-detail-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  margin-bottom: 1.75rem;
}

.shell-detail-title-row {
  display: flex;
  align-items: center;
  gap: .65rem;
  flex-wrap: wrap;
}

.shell-detail-title-row h1 {
  margin: 0;
  font-size: 1.5rem;
}

.shell-pill {
  display: inline-flex;
  align-items: center;
  gap: .35rem;
  font-size: .72rem;
  font-weight: 600;
  padding: .2rem .55rem;
  border-radius: 999px;
}

.shell-pill.live {
  color: var(--green);
  background: var(--green-bg);
}

.shell-pill.live::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--green);
}

.shell-pill.ready {
  color: var(--green);
  background: var(--green-bg);
}

.shell-pill.pending {
  color: var(--amber);
  background: var(--amber-bg);
}

/* Neutral (non-amber) "not ready yet" variant -- console.html carries a
   pre-existing BINDING rule (design-overhaul T3, `test_console_page_has_
   zero_amber`) that amber never appears on that page at all ("no
   'found'-style status color on the host side"); its album-row status
   pill uses this instead of `.shell-pill.pending` for every non-ready
   state. Kept as a distinct modifier (not a replacement for `.pending`
   above) since a future page without that constraint may still want the
   amber treatment. */
.shell-pill.neutral {
  color: var(--secondary);
  background: var(--hairline);
}

.shell-detail-meta {
  margin: .3rem 0 0;
  font-size: .82rem;
  /* Tightening batch (2026-08-20), P1: informational meta text moved off
     --muted (a decorative-only token, not AA-safe as body-copy color in
     dark) onto --secondary, alongside .reveal-event-line and
     .shell-album-row-secondary. */
  color: var(--secondary);
}

.shell-tabs {
  display: flex;
  gap: 1.5rem;
  border-bottom: 1px solid var(--hairline);
  margin-bottom: 1.5rem;
}

.shell-tab-btn {
  background: none;
  border: none;
  font: inherit;
  font-size: .875rem;
  font-weight: 500;
  color: var(--secondary);
  padding: 0 0 .65rem;
  margin-bottom: -1px;
  border-bottom: 2px solid transparent;
  cursor: pointer;
}

.shell-tab-btn.active {
  color: var(--ink);
  font-weight: 600;
  border-bottom-color: var(--ink);
}

.shell-tab-panel {
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
}

.shell-access-card {
  background: var(--card);
  border: 1px solid var(--hairline);
  border-radius: var(--radius-lg);
  padding: 1.25rem;
  display: flex;
  align-items: center;
  gap: 1.25rem;
  flex-wrap: wrap;
}

.shell-access-qr {
  width: 84px;
  height: 84px;
  border-radius: var(--radius);
  border: 1px solid var(--hairline);
  flex-shrink: 0;
  display: block;
  object-fit: contain;
}

.shell-access-code {
  display: flex;
  flex-direction: column;
  gap: .2rem;
}

.shell-access-code-label {
  font-size: .75rem;
  font-weight: 500;
  color: var(--muted);
}

.shell-access-code-value {
  font-family: var(--font-mono);
  font-size: 1.15rem;
  font-weight: 500;
  letter-spacing: .06em;
}

.shell-access-actions {
  display: flex;
  gap: .6rem;
  flex-wrap: wrap;
  margin-left: auto;
}

.shell-summary-card {
  background: var(--card);
  border: 1px solid var(--hairline);
  border-radius: var(--radius-lg);
  display: flex;
  flex-direction: column;
}

.shell-summary-card-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 1rem 1.25rem;
  border-bottom: 1px solid var(--hairline);
}

.shell-summary-card-header h3 {
  margin: 0;
  font-size: .875rem;
}

.shell-album-row {
  display: flex;
  align-items: center;
  gap: .9rem;
  padding: .9rem 1.25rem;
  border-bottom: 1px solid var(--hairline);
}

.shell-album-row:last-child {
  border-bottom: none;
}

.shell-album-thumb {
  width: 40px;
  height: 40px;
  border-radius: 8px;
  background: var(--hairline);
  border: 1px solid var(--hairline-2);
  flex-shrink: 0;
}

.shell-album-row-body {
  display: flex;
  flex-direction: column;
  gap: .1rem;
  min-width: 0;
  flex: 1;
}

.shell-album-row-name {
  font-size: .875rem;
  font-weight: 500;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.shell-album-row-secondary {
  font-size: .78rem;
  /* Tightening batch (2026-08-20), P1: see .shell-detail-meta above. */
  color: var(--secondary);
}

.shell-album-row-stats {
  display: flex;
  align-items: center;
  gap: .9rem;
  flex-shrink: 0;
}

.shell-album-row-count {
  font-family: var(--font-mono);
  font-size: .8rem;
  color: var(--secondary);
}

@media (max-width: 1023px) {
  .shell-detail-header {
    flex-direction: column;
    align-items: stretch;
  }
  .shell-access-card {
    flex-direction: column;
    align-items: flex-start;
  }
  .shell-access-actions {
    margin-left: 0;
    width: 100%;
  }
  .shell-access-actions .btn,
  .shell-access-actions .btn2 {
    flex: 1;
  }
}
