/* ===========================================================================
 * The two layout shells (styleguide §4 and §9).
 *
 * Same tokens, different posture. Shells only — no components live here.
 * Every value is a var(); raw numbers belong in tokens.css.
 * =========================================================================== */

.shell {
  display: flex;
  flex-direction: column;
  /* 100vh includes the mobile URL bar; 100dvh does not, which matters under
     viewport-fit=cover. Keep the vh line first as a fallback for browsers
     without dvh support — dvh overrides it where supported. */
  min-height: 100vh;
  min-height: 100dvh;
}

/* ---------------------------------------------------------------------------
 * The top bar is identical on both platforms (§4): 76px tall, with a 1px
 * underline at 12% white. The icon tile sits at (20,20) and the wordmark
 * beside it.
 * ------------------------------------------------------------------------ */
.shell__topbar {
  height: var(--topbar-height);
  flex: 0 0 var(--topbar-height);
  border-bottom: var(--border-width) solid var(--divider);
}

.shell__topbar-inner {
  height: 100%;
  margin: 0 auto;
  padding: 0 var(--space-5);
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

/* The brand: troph-e.app's logo, the gold trophy mark beside the white
   TROPH-E wordmark, as two images so the rail can set its role line under the
   wordmark and a narrow bar can show the mark alone. The mark is the taller of
   the two and hangs from the wordmark's top edge, as the site draws them. */
.brand-lockup {
  display: flex;
  align-items: flex-start;
  gap: var(--brand-gap);
  flex: none;
}

.brand-mark {
  display: block;
  flex: none;
  width: auto;
  height: var(--brand-mark-height);
}

.brand-wordmark {
  display: block;
  flex: none;
  width: auto;
  height: var(--brand-wordmark-height);
}

/* The admin shell's mark is a link home for anyone signed in — the one way a
   platform admin, whose tabs have no Home (D128), gets back to it. It keeps
   the mark and wordmark exactly as drawn: no underline, no colour change, and
   the neutral gold focus ring drawn with outline (D30). */
.brand-link {
  display: flex;
  align-items: center;
  flex: none;
  border-radius: var(--radius-md);
  color: inherit;
  text-decoration: none;
}

.brand-link:focus-visible {
  outline: var(--focus-ring-outline);
  outline-color: var(--focus-colour-neutral);
  outline-offset: var(--focus-ring-offset);
}

.shell__content {
  flex: 1 1 auto;
  width: 100%;
  margin: 0 auto;
}

/* ---------------------------------------------------------------------------
 * The admin top bar, for anyone this layout serves who is not on a rail — a
 * player opening a club, say. Both admin roles are on the rail (D176, D181).
 * ------------------------------------------------------------------------ */
.shell--admin .shell__topbar-inner,
.shell--admin .shell__content {
  max-width: var(--shell-admin-width);
}

.shell--admin .shell__content {
  padding: var(--space-5);
}

/* THE ADMIN TOP BAR STAYS PUT while the page scrolls under it, so the tabs
   are always one tap away on long pages (the Studio, the register, a long
   roster). Sticky rather than an inner scroll box: the document keeps
   scrolling, so Turbo's scroll restoration, anchors and find-in-page all
   behave as before.

   The raised-surface recipe (D29), as the player's tab bar uses it: a tinted
   wash and a blur, never a shadow. Content passing under it is blurred
   rather than hidden, so the bar still reads as part of the lit cabinet. */
.shell--admin .shell__topbar {
  position: sticky;
  top: 0;
  z-index: var(--z-topbar);
}

/* On a layer behind the bar, never on the bar (D194): the bar holds the
   account menu, and an element with a backdrop-filter is the backdrop root
   for whatever opens inside it, so the menu would blur nothing. The page
   header's bar and the tab bar do the same. */
.shell--admin .shell__topbar::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: var(--surface-tabbar);
  backdrop-filter: blur(var(--overlay-blur));
  -webkit-backdrop-filter: blur(var(--overlay-blur));
}

/* On a narrow screen the top bar's account control drops its word, keeping it
   as its accessible name. Scoped to the top bar, so a menu anywhere else on a
   page keeps its words. */
@media (max-width: 959px) {
  .shell--admin .shell__topbar .ui-menu__trigger-text,
  .shell--admin .shell__topbar .shell__account-label {
    position: absolute;
    width: var(--border-width);
    height: var(--border-width);
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
}

/* An anchor (the register's #decisions-waiting, a field scrolled to after an
   error) lands below the pinned bar rather than behind it. */
html:has(.shell--admin) {
  scroll-padding-top: var(--topbar-height);
}

/* ---------------------------------------------------------------------------
 * Player — the 393 column. 20px side padding, single column throughout, and a
 * sticky primary CTA above the home indicator.
 * ------------------------------------------------------------------------ */
.shell--player .shell__topbar-inner,
.shell--player .shell__content {
  max-width: var(--shell-player-width);
}

.shell--player .shell__content {
  padding: var(--space-5);
}

/* The sticky CTA. env(safe-area-inset-bottom) needs viewport-fit=cover on the
   viewport meta tag, which layouts/_head.html.erb sets — without it iOS reports
   0 and the CTA sits under the home indicator. The surface wash and the top
   border keep it legible over scrolling content without a shadow (§6). */
/* `bottom` clears the tab bar rather than sitting at 0, because on the player
   surface both are `position: sticky` on the same edge and would otherwise
   occupy the same strip — the CTA hidden underneath the bar, which reads as the
   CTA having failed to render. The offset is harmless where there is no bar:
   the region only exists when a screen asks for it, and a screen with a CTA and
   no tab bar is one the player is not signed in for. */
.shell__sticky-cta {
  position: sticky;
  bottom: calc(var(--tabbar-height) + env(safe-area-inset-bottom, 0px));
  width: 100%;
  max-width: var(--shell-player-width);
  margin: 0 auto;
  padding: var(--space-3) var(--space-5);
  padding-bottom: calc(var(--space-3) + env(safe-area-inset-bottom, 0px));
  background: var(--surface-1);
  border-top: var(--border-width) solid var(--divider);
  backdrop-filter: blur(var(--blur-overlay));
}

/* In the native app the web tab bar is not rendered (D186): the shell's own
   bar sits below the web view, outside the page. The CTA keeps its offset only
   for a bar that is actually on the page, or it floats a bar's height above
   nothing. */
.hotwire-native .shell__sticky-cta {
  bottom: 0;
}

/* ---------------------------------------------------------------------------
 * The one card primitive the styleguide page needs to demonstrate the wash
 * stack. This is NOT the component library — it is the minimum needed to prove
 * the depth model works. Components come later, per component-list.md §11.
 * ------------------------------------------------------------------------ */
.surface {
  background: var(--surface-1);
  border: var(--border-width) solid var(--border-default);
  border-radius: var(--radius-lg);
  padding: var(--space-5);
}

.shell--player .surface {
  border-radius: var(--radius-md);
}

.surface--nested {
  background: var(--surface-2);
  border-radius: var(--radius-md);
}

/* ---------------------------------------------------------------------------
   The account control — a link to /account, in the ADMIN topbar.

   It was in both topbars until §7's Profile tab landed. The player shell now
   reaches Account Settings from the Profile screen, where §7 nests it, and the
   icon came out of `layouts/player.html.erb` in the same change (D108) — so the
   `.shell--player .shell__account-label { display: none }` rule that used to
   live here came out with it rather than sitting as a selector for something
   that can no longer render.

   Pushed to the trailing edge with `margin-left: auto` rather than a fixed
   position, so it sits after the nav tabs without the layout knowing about it.
--------------------------------------------------------------------------- */
.shell__account {
  margin-left: auto;
  text-decoration: none;
  border-radius: var(--radius-sm);
  color: var(--text-secondary);
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--type-caption-size);
  padding: var(--space-2) var(--space-3);
  transition: color var(--duration-fast) var(--easing-standard),
              background-color var(--duration-fast) var(--easing-standard);
}

.shell__account:hover {
  background: var(--surface-hover-item);
  color: var(--text-primary);
}

/* `outline-color` on its own line, and that IS the fix. This read
   `outline: var(--focus-ring-outline) solid var(--focus-colour-neutral)`, which
   expands to `2px solid rgba(...) solid #F5D673` — an invalid shorthand, so the
   browser drops the whole declaration and the admin topbar's account button had
   no focus ring at all. Nothing errors; the ring is simply not drawn. Every
   other neutral component in this library already spells it this way. */
.shell__account:focus-visible {
  outline: var(--focus-ring-outline);
  outline-offset: var(--focus-ring-offset);
  outline-color: var(--focus-colour-neutral);
}

/* The same trailing position for the multi-role account MENU (D144), which
   replaces the link rather than sitting beside it. Its panel opens from the
   trailing edge so it never runs off the 960 column. */
.shell__account-menu {
  margin-left: auto;
}

/* ---------------------------------------------------------------------------
 * The platform admin's rail shell (D176).
 *
 * A rail of grouped destinations pinned to the window's left edge, and a
 * content column beside it capped at --page-max-width and left-aligned, never
 * centred. There is no separate top bar: the page's own header is pinned as
 * the bar, so the thing that follows the reader down a long page is the page's
 * title and its actions, not the brand.
 * ------------------------------------------------------------------------ */
.shell--rail {
  display: grid;
  grid-template-columns: var(--sidebar-width) minmax(0, 1fr);
  align-items: start;
  --sticky-offset: calc(var(--topbar-height-rail) + var(--space-5));
}

html:has(.shell--rail) {
  scroll-padding-top: var(--topbar-height-rail);
}

.shell__rail {
  position: sticky;
  top: 0;
  height: 100vh;
  height: 100dvh;
  display: flex;
  flex-direction: column;
  background: var(--surface-sidebar);
  border-right: var(--border-width) solid var(--divider);
}

.shell__rail-brand {
  flex: none;
  height: var(--topbar-height-rail);
  padding: 0 var(--space-5);
  border-bottom: var(--border-width) solid var(--divider);
  color: inherit;
  text-decoration: none;
}

.shell__rail-brand:focus-visible {
  outline: var(--focus-ring-outline);
  outline-color: var(--focus-colour-neutral);
  outline-offset: calc(-1 * var(--focus-ring-offset) - var(--border-width));
}

/* Mark, then wordmark, with the role line under the wordmark alone: a grid,
   so the wrapper can dissolve and its two lines take the second column. */
.shell__rail-brand {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-content: center;
  align-items: start;
  column-gap: var(--brand-gap);
  row-gap: var(--space-1);
}

.shell__rail-wordmark { display: contents; }
.shell__rail-brand .brand-mark { grid-row: 1 / span 2; }
.shell__rail-brand .brand-wordmark,
.shell__rail-brand .shell__rail-role { grid-column: 2; }

/* The club's name, under the wordmark (D181). Long names truncate; the full
   name is the element's title. */
.shell__rail-role {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.shell__rail-role,
.shell__identity-role {
  font-size: var(--type-caption-size);
  line-height: var(--type-caption-leading);
  color: var(--text-tertiary);
}

/* The club's block under the brand, on a club admin's rail only (D181): the
   crest, the club's name and the role. THE ONE CUE that a page is a club's and
   not the console is this block's gold wash and hairline, from the gold the
   chrome already uses for a selected tile (D27). Gold stays the only chromatic
   colour; the club's own colours are inside the crest (D32). */
.shell__rail-club {
  flex: none;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin: var(--space-3) var(--space-3) 0;
  padding: var(--space-2) var(--space-3);
  border: var(--border-width) solid var(--gold-halo);
  border-radius: var(--radius-md);
  background: var(--gold-wash-hover);
  color: inherit;
  text-decoration: none;
  transition: background-color var(--duration-fast) var(--easing-standard);
}

.shell__rail-club:hover { background: var(--gold-wash-active); }
.shell__rail-club:focus-visible {
  outline: var(--focus-ring-outline);
  outline-offset: var(--focus-ring-offset);
  outline-color: var(--focus-colour-neutral);
}

.shell__rail-club-text {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

/* Long names truncate; the full name is the link's title. */
.shell__rail-club-name {
  color: var(--text-primary);
  font-weight: var(--font-weight-semibold);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.shell__rail-club-role {
  font-size: var(--type-eyebrow-size);
  line-height: var(--type-eyebrow-leading);
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  font-weight: var(--font-weight-semibold);
  color: var(--gold-300);
}

.shell__rail-nav {
  flex: 1 1 auto;
  overflow-y: auto;
  padding: var(--space-4) var(--space-3);
}

.shell__rail-foot {
  flex: none;
  padding: var(--space-3);
  border-top: var(--border-width) solid var(--divider);
}

.shell__identity .ui-menu { left: 0; right: 0; width: auto; }

.shell__rail-feedback {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-1);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  color: var(--text-tertiary);
  font-size: var(--type-caption-size);
  line-height: var(--type-caption-leading);
  text-decoration: none;
}

.shell__rail-feedback:hover { color: var(--text-primary); background: var(--surface-hover-item); }
.shell__rail-feedback:focus-visible {
  outline: var(--focus-ring-outline);
  outline-offset: var(--focus-ring-offset);
  outline-color: var(--focus-colour-neutral);
}

.shell__identity-tile {
  flex: none;
  width: var(--identity-tile);
  height: var(--identity-tile);
  border-radius: var(--radius-pill);
  background: var(--surface-3);
  color: var(--text-primary);
  display: grid;
  place-items: center;
  font-size: var(--type-caption-size);
  font-weight: var(--font-weight-semibold);
}

.shell__identity-text { display: flex; flex-direction: column; min-width: 0; flex: 1 1 auto; }

.shell__identity-name {
  color: var(--text-primary);
  font-weight: var(--font-weight-semibold);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* `clip`, not `hidden`: the pinned bar's wash reaches past the content column
   to the window's right edge, and clipping it here keeps the page from
   scrolling sideways without making this a scroll container, which would
   unpin the bar. */
.shell__main { min-width: 0; overflow-x: clip; }

.shell--admin.shell--rail .shell__content {
  container: admin-content / inline-size;
  max-width: calc(var(--page-max-width) + 2 * var(--page-gutter));
  margin: 0;
  padding: 0 var(--page-gutter) var(--space-10);
}

/* THE PAGE HEADER IS THE BAR. Its own box steps aside (`display: contents`)
   so the bar row can pin to the top of the whole page while the subtitle stays
   in the flow beneath it. The wash, the blur and the rule are drawn by a
   pseudo-element that runs from the column's edge to the window's, so the bar
   spans the content area however wide the window is. */
.shell--rail .ui-page-header { display: contents; }

/* 64px when the title and the actions share a line. A page with more actions
   than fit (a draft version has five) wraps them inside the bar rather than
   squeezing the title to nothing or pushing a button off the screen. */
.shell--rail .ui-page-header__bar {
  display: flex;
  align-items: center;
  gap: var(--space-2) var(--space-4);
  position: sticky;
  top: 0;
  z-index: var(--z-topbar);
  flex-wrap: wrap;
  min-height: var(--topbar-height-rail);
  margin: 0 calc(-1 * var(--page-gutter)) var(--space-6);
  padding: var(--space-3) var(--page-gutter);
}

.shell--rail .ui-page-header__bar .ui-page-header__heading {
  flex: 1 1 var(--rail-heading-min);
}

.shell--rail .ui-page-header__bar .ui-page-header__action {
  flex: 0 1 auto;
  margin-left: auto;
}

.shell--rail .ui-page-header__bar::before {
  content: "";
  position: absolute;
  inset: 0 -100vw 0 0;
  z-index: -1;
  background: var(--surface-tabbar);
  backdrop-filter: blur(var(--overlay-blur));
  -webkit-backdrop-filter: blur(var(--overlay-blur));
  border-bottom: var(--border-width) solid var(--divider);
}

/* Trail and title on one line: "Library › Trophy artwork". */
.shell--rail .ui-page-header__heading {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-width: 0;
}

.shell--rail .ui-page-header__trail ol { margin: 0; flex-wrap: nowrap; }

.shell--rail .ui-page-header__crumb {
  font-size: var(--type-body-size);
  line-height: var(--type-body-leading);
  font-weight: var(--font-weight-semibold);
  color: var(--text-tertiary);
  white-space: nowrap;
}

.shell--rail .ui-page-header__title {
  font-size: var(--type-h2-size);
  line-height: var(--type-h2-leading);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.shell--rail .ui-page-header__subtitle {
  margin: calc(-1 * var(--space-2)) 0 var(--space-6);
  max-width: var(--prompt-prose-measure);
}

/* A laptop role, so this is a fallback rather than a design target: below
   1024 the rail keeps its icons and counts and drops its words, which stay as
   each link's title. */
@media (max-width: 1023px) {
  .shell--rail { grid-template-columns: var(--sidebar-width-compact) minmax(0, 1fr); }
  .shell--rail .shell__rail-wordmark,
  .shell--rail .ui-sidenav__label,
  .shell--rail .shell__identity-text,
  .shell--rail .shell__identity .ui-menu__trigger .icon:last-child,
  .shell--rail .shell__rail-feedback span { display: none; }
  /* Hidden from the eye only: the label stays each link's accessible name. */
  .shell--rail .ui-sidenav__text {
    position: absolute;
    width: var(--border-width);
    height: var(--border-width);
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
  /* The children stay reachable as icon-less dots under their parent. */
  .shell--rail .ui-sidenav__child { padding: 0; justify-content: center; font-size: 0; }
  .shell--rail .ui-sidenav__child::before {
    content: "";
    width: var(--rail-dot-size);
    height: var(--rail-dot-size);
    margin: 0;
    border-radius: var(--radius-pill);
    background: var(--text-tertiary);
  }
  .shell--rail .ui-sidenav__child.is-current::before { background: var(--gold-100); margin: 0; }
  .shell--rail .shell__rail-brand { justify-content: center; padding: 0; grid-template-columns: auto; }
  /* The club's block keeps its crest alone; the name stays its title. */
  .shell--rail .shell__rail-club { justify-content: center; padding: var(--space-2) 0; }
  .shell--rail .shell__rail-club-text { display: none; }
  .shell--rail .ui-sidenav__item { justify-content: center; position: relative; }
  .shell--rail .ui-sidenav__count { position: absolute; top: 0; right: 0; }
  .shell--rail .shell__identity .ui-menu__trigger,
  .shell--rail .shell__rail-feedback { justify-content: center; }
  .shell--rail .shell__identity .ui-menu { right: auto; width: var(--menu-width); }
}

/* A record's rail (D178): wider on the rail shell, and pinned under the bar
   while the record beside it scrolls. */
.shell--rail { --page-rail-width: var(--page-rail-width-detail); }

@media (min-width: 900px) {
  /* Scrolls itself when taller than the window, as the workbench's side does,
     so its last facts are never trapped under a long main column. */
  .shell--rail .ui-page__rail {
    position: sticky;
    top: var(--sticky-offset);
    max-block-size: calc(100dvh - var(--sticky-offset) - var(--space-5));
    overflow-y: auto;
    scrollbar-width: thin;
  }
}

/* ---------------------------------------------------------------------------
 * The club admin on a phone (D181).
 *
 * Below 768 the rail would take a fifth of the screen, so it steps aside for
 * a bottom bar — the player's tab bar (D82) with a More menu. The page header
 * stays the pinned bar at the top, as on the rail, so the title and its
 * actions still follow the reader down the page. Above 768 the bar is never
 * drawn and the rail is the only navigation.
 * ------------------------------------------------------------------------ */
.shell__tabbar { display: none; }

@media (max-width: 767px) {
  /* A column again, stretched: the rail's `align-items: start` would shrink
     the page to the width of its widest word. */
  .shell--club {
    display: flex;
    align-items: stretch;
    --page-gutter: var(--space-5);
  }
  .shell--club .shell__rail { display: none; }
  .shell--club .shell__main { flex: 1 1 auto; }

  .shell--club .shell__tabbar {
    display: flex;
    position: sticky;
    bottom: 0;
    z-index: var(--z-topbar);
  }

  /* The title gives up its line to the actions before it truncates to "C…". */
  .shell--club .ui-page-header__bar .ui-page-header__heading { flex-basis: auto; }

  /* A toast rises above the bar rather than covering it. */
  .shell--club .ui-toast-region {
    bottom: calc(var(--tabbar-height) + var(--space-3) + env(safe-area-inset-bottom, 0px));
  }
}

/* More: a menu whose trigger is drawn as a tab, and lights up gold while the
   page is one of the destinations it holds. */
.shell__tabbar-more { flex: 1; display: flex; }

.shell__tabbar-more .ui-menu__trigger {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-1);
  min-height: var(--control-height-mobile);
  padding: 0;
  border: 0;
  background: none;
  color: var(--text-muted);
}

.shell__tabbar-more .ui-menu__trigger:hover { color: var(--text-primary); background: none; }
.shell__tabbar-more.is-current .ui-menu__trigger { color: var(--gold-100); }
.shell__tabbar-more.is-current .ui-tabbar__label { font-weight: var(--font-weight-semibold); }

.shell__tabbar-count {
  margin-left: auto;
  color: var(--text-tertiary);
  font-size: var(--type-caption-size);
}
