/*
 * Chrome-only layout for the four shells (business/referrer/public/admin)
 * and the /styleguide chassis that reuses them. Component-internal styling
 * lives beside its ViewComponent; this file only covers rail/topbar/tabbar/
 * device-frame layout that doesn't map cleanly onto Tailwind utilities.
 *
 * Every value below is either a design token (tokens.css) or one of the
 * shell-specific dimension constants declared in :root here — no raw
 * literals in component rules. The one unavoidable exception is the
 * @media breakpoints: CSS custom properties cannot appear inside a media
 * query condition, so those pixel values are hand-kept in sync with
 * tokens.css §1.11 (--bp-lg: 1024px, --bp-xl: 1280px, --bp-md: 768px).
 */

:root {
  --shell-rail-w: 226px;             /* business left rail, full */
  --shell-rail-w-collapsed: 72px;    /* business left rail, icon-only */
  --shell-topbar-h: 44px;            /* business top bar row */
  --shell-header-h: 56px;            /* referrer header, both variants */
  --shell-tabbar-h: 64px;            /* referrer bottom tab bar */
  --shell-admin-bar-h: 48px;         /* admin dark top bar */
  --shell-avatar-size: 32px;         /* business user-menu avatar */
  --shell-public-col: 420px;         /* public auth column */
  --shell-landing-col: 1120px;       /* public landing (A1) container */
  --shell-device-w: 375px;           /* styleguide referrer device frame */
  --device-frame-h: 720px;           /* styleguide referrer device frame, fixed height */
}

/* ---------- Business rail ---------- */

.business-rail {
  width: var(--shell-rail-w);
  flex-shrink: 0;
  position: sticky;
  top: var(--space-6);
  height: calc(100dvh - (var(--space-6) * 2));
  display: flex;
  flex-direction: column;
  padding: var(--space-6);
  overflow-y: auto;
}

.business-rail__logo {
  margin-bottom: var(--space-6);
}

.business-rail__logo-img {
  height: var(--space-6);
  width: auto;
}

/* Mark-only lockup for the icon-only rail — hidden at full width, swapped in
   below --bp-xl by the rule further down. */
.business-rail__logo-mark {
  display: none;
  height: var(--space-6);
  width: auto;
}

.business-rail__logo a {
  display: inline-flex;
}

.business-rail__nav {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.business-rail__item {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  height: var(--control-h);
  padding-inline: var(--space-3);
  border-radius: var(--radius-lg);
  color: var(--color-ink-muted);
  font-size: var(--text-base);
  line-height: var(--text-base--line-height);
  font-weight: 500;
  transition: background-color var(--duration-fast) var(--ease-out),
              color var(--duration-fast) var(--ease-out);
}

.business-rail__item:hover {
  background: var(--color-surface-inset);
}

.business-rail__item.is-active {
  background: var(--color-accent-strong);
  color: var(--color-on-accent);
}

.business-rail__icon {
  flex-shrink: 0;
}

/* Not-yet-built surfaces (11 Aug 2026 decision) — de-emphasised, no hover
   affordance, since these are non-interactive spans, not links. */
.business-rail__item--disabled {
  opacity: 0.5;
}

.business-rail__item--disabled:hover {
  background: none;
}

/* Icon-only rail below --bp-xl (1280px) */
@media (max-width: 1279px) {
  .business-rail {
    width: var(--shell-rail-w-collapsed);
    padding-inline: var(--space-2);
  }

  .business-rail__label {
    display: none;
  }

  .business-rail__item {
    justify-content: center;
    padding-inline: 0;
  }

  /* The "Soon" badge is a text label like .business-rail__label — hide it
     alongside that label rather than let it crowd the icon-only rail. */
  .business-rail__item--disabled .ui-badge {
    display: none;
  }

  .business-rail__logo-img {
    display: none;
  }

  .business-rail__logo-mark {
    display: block;
  }

  .business-rail__logo {
    display: flex;
    justify-content: center;
  }
}

/* Off-canvas rail below --bp-lg (1024px) */
@media (max-width: 1023px) {
  .rail-drawer {
    position: fixed;
    inset: 0;
    z-index: var(--z-drawer);
    pointer-events: none;
  }

  .rail-scrim {
    position: absolute;
    inset: 0;
    background: var(--color-overlay);
    opacity: 0;
    transition: opacity var(--duration-base) var(--ease-out);
    pointer-events: none;
  }

  .business-rail {
    position: fixed;
    top: var(--space-4);
    left: var(--space-4);
    bottom: var(--space-4);
    height: auto;
    width: var(--shell-rail-w);
    transform: translateX(calc(-100% - var(--space-4)));
    transition: transform var(--duration-slow) var(--ease-out);
    pointer-events: auto;
  }

  .business-rail__label {
    display: block;
  }

  .business-rail__item {
    justify-content: flex-start;
    padding-inline: var(--space-3);
  }

  .business-rail__logo-img {
    display: block;
  }

  .business-rail__logo-mark {
    display: none;
  }

  .business-rail__logo {
    display: block;
  }

  .rail-drawer.is-open .rail-scrim {
    opacity: 1;
    pointer-events: auto;
  }

  .rail-drawer.is-open .business-rail {
    transform: translateX(0);
  }
}

@media (min-width: 1024px) {
  .business-rail-toggle {
    display: none;
  }
}

/* ---------- Business top bar ---------- */

.business-topbar {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-height: var(--shell-topbar-h);
}

.business-topbar__search {
  flex: 1;
  max-width: var(--bp-sm);
}

/* The search pill stops growing at --bp-sm, so the bell and user menu have
   to be pushed to the end of the row explicitly — otherwise they sit
   directly against the pill, adrift in the middle of the bar. */
.business-topbar__actions {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-inline-start: auto;
}

.business-topbar__avatar {
  width: var(--shell-avatar-size);
  height: var(--shell-avatar-size);
  border-radius: var(--radius-round);
  object-fit: cover;
  background: var(--color-surface-inset);
  flex-shrink: 0;
}

/* ---------- Referrer shell ---------- */

.referrer-shell {
  width: 100%;
  max-width: var(--bp-sm);
  margin-inline: auto;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  position: relative;
}

/* Native-gated override: inside the Hotwire Native shell there's no web
   header/tab bar competing for viewport, and the native chrome already
   owns the full screen, so forcing a full 100dvh column here just makes
   the content phantom-scroll by nothing. Additive next to the base rule
   above rather than edited in place, so the web behaviour is untouched. */
.turbo-native .referrer-shell {
  min-height: auto;
}

/*
 * Above --bp-md (768px) the shell centres as a deliberate glass "device" —
 * below that it is plain full-bleed mobile content, so the glass recipe is
 * applied here (scoped to the breakpoint) rather than via the shared
 * .surface-glass class, which would also paint it at mobile widths.
 */
@media (min-width: 768px) {
  .referrer-shell {
    min-height: auto;
    margin-block: var(--space-12);
    border-radius: var(--radius-lg);
    overflow: hidden;
    background: var(--color-surface);
    backdrop-filter: blur(var(--glass-blur));
    -webkit-backdrop-filter: blur(var(--glass-blur));
    border: var(--border-glass);
    box-shadow: var(--shadow-md);
  }

  .perf-lite .referrer-shell {
    background: var(--color-surface-solid);
  }
}

@media (min-width: 768px) {
  @supports not (backdrop-filter: blur(1px)) {
    .referrer-shell {
      background: var(--color-surface-solid);
    }
  }
}

.referrer-header {
  height: var(--shell-header-h);
  padding-inline: var(--space-4);
  padding-top: env(safe-area-inset-top);
  display: flex;
  align-items: center;
  gap: var(--space-3);
  position: relative;
}

.referrer-header--back {
  justify-content: center;
}

.referrer-header.is-scrolled {
  position: sticky;
  top: 0;
  z-index: var(--z-sticky);
  box-shadow: var(--shadow-sm);
}

.referrer-header__back {
  position: absolute;
  left: var(--space-4);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--control-h);
  height: var(--control-h);
  border-radius: var(--radius-round);
  color: var(--color-ink);
  flex-shrink: 0;
}

.referrer-header__title {
  font-size: var(--text-base);
  line-height: var(--text-base--line-height);
  font-weight: 600;
  color: var(--color-ink);
  text-align: center;
  /* NV-04: truncates to one line with an ellipsis rather than wrapping
     under the absolutely-positioned back button / action slot. Reserves
     the same width on both sides regardless of whether the action slot
     is actually present, so the title never re-centres when it appears. */
  max-width: calc(100% - ((var(--control-h) + var(--space-4)) * 2));
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.referrer-header__action {
  position: absolute;
  right: var(--space-4);
}

/* No side padding at phone widths: every referrer page already wraps itself
   in p-4, and this plus the layout's own gutter stacked three 16px gutters —
   48px a side, a 306px column on a 402px phone, which read as the whole UI
   being zoomed out. The page's p-4 is the one phone gutter. Inside the
   ≥768px device frame the extra inset is kept, where it reads as the frame's
   margin rather than lost width. */
.referrer-content {
  flex: 1;
  padding-top: var(--space-4);
  padding-bottom: calc(var(--shell-tabbar-h) + var(--space-4) + env(safe-area-inset-bottom));
}

/* Below 768px the referrer shell has no surface of its own (outside the
   styleguide's device frame), so pages sit on the bare canvas and
   inset-tinted fields vanish into it. Fields here get
   the solid fill; cards inside reset it (forms.css). */
@media (max-width: 767px) {
  .referrer-content {
    --fm-control-bg: var(--color-surface-solid);
  }
}

@media (min-width: 768px) {
  .referrer-content {
    padding-inline: var(--space-4);
  }
}

/* Native-gated override: the bottom padding above reserves space for the
   web .referrer-tabbar, which the native shell never renders — left in
   place it's a dead gap sitting above the native tab bar. Additive next to
   the base rule, not an edit to it, so the web layout is untouched. */
.turbo-native .referrer-content {
  padding-bottom: var(--space-4);
}

.referrer-tabbar {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: var(--z-nav);
  height: calc(var(--shell-tabbar-h) + env(safe-area-inset-bottom));
  padding-bottom: env(safe-area-inset-bottom);
  display: flex;
  border-top: var(--border-glass);
}

@media (min-width: 768px) {
  .referrer-tabbar {
    position: absolute;
  }
}

.referrer-tabbar__item {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-1);
  min-height: var(--tap-min);
  color: var(--color-ink-muted);
}

.referrer-tabbar__item.is-active {
  color: var(--color-accent-strong);
}

.referrer-tabbar__label {
  font-size: var(--text-2xs);
  line-height: var(--text-2xs--line-height);
}

/* Not-yet-built surfaces (11 Aug 2026 decision). The badge stacks UNDER the
   label rather than beside it: four tabs share 375px, so a label and a pill
   side by side overflow their tab and collide with the neighbouring labels.
   Verified live at 375px — an inline row is visibly broken there. */
.referrer-tabbar__label-row {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  max-width: 100%;
  min-width: 0;
}

.referrer-tabbar__item--disabled {
  opacity: 0.5;
}

/*
 * ---------- Native-only ----------
 * Audit 4.9: iOS shows its "Copy"/"Save Image" callout on a long-press of a
 * link or image, which reads as a broken context menu inside a native app
 * that has no page to view source of. Scoped to links and images only —
 * inputs and textareas are deliberately excluded, or text selection in form
 * fields would break with them. Inert on the web, where that callout is
 * expected browser behaviour.
 */

.turbo-native a,
.turbo-native img {
  -webkit-touch-callout: none;
}

/* ---------- Public shell ---------- */

.public-shell {
  width: 100%;
  max-width: var(--shell-public-col);
  margin-inline: auto;
}

/* ---------- Admin shell ---------- */

.admin-topbar {
  height: var(--shell-admin-bar-h);
  background: var(--color-ink);
  display: flex;
  align-items: center;
  gap: var(--space-6);
  padding-inline: var(--space-6);
  /* Nothing may spill out of the bar. Its children are light-on-dark, so a
     child pushed past the bar's edge lands on the light page background and
     becomes unreadable — which is exactly what a wider nav did at narrow
     viewports, and what the axe colour-contrast job caught. */
  overflow: hidden;
}

/* The nav is the only child that may shrink: it absorbs the overflow and
   scrolls, so the wordmark and the right-hand label always stay on the bar. */
.admin-topbar > nav {
  min-width: 0;
  overflow-x: auto;
  scrollbar-width: none;
}

.admin-topbar > nav::-webkit-scrollbar {
  display: none;
}

.admin-topbar > a,
.admin-topbar > .ml-auto {
  flex-shrink: 0;
}

.admin-topbar__link {
  font-size: var(--text-sm);
  line-height: var(--text-sm--line-height);
  color: var(--color-surface-solid);
  opacity: 0.7;
  padding-bottom: var(--space-1);
  border-bottom: 2px solid transparent;
  transition: opacity var(--duration-fast) var(--ease-out),
              border-color var(--duration-fast) var(--ease-out);
}

.admin-topbar__link:hover {
  opacity: 1;
}

/* Named state, not just brighter text — a 2px accent underline. */
.admin-topbar__link.is-active {
  opacity: 1;
  border-bottom: var(--border-selected);
}

/* Not-yet-built admin sections (11 Aug 2026 decision) — a non-interactive
   span, so no hover state; further de-emphasised below the already-muted
   default link opacity. */
.admin-topbar__link--disabled {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  opacity: 0.45;
}

/*
 * ---------- Styleguide device frame ----------
 * The /styleguide referrer preview (?shell=referrer) always renders as a
 * 375px phone frame, regardless of viewport — unlike the production
 * referrer shell, whose glass "device" treatment only kicks in above
 * --bp-md. This overrides .referrer-shell unconditionally.
 */

.referrer-shell.styleguide-device-frame {
  max-width: var(--shell-device-w);
  height: var(--device-frame-h);
  max-height: var(--device-frame-h);
  margin-block: var(--space-12);
  border-radius: var(--radius-lg);
  overflow-y: auto;
  background: var(--color-surface);
  backdrop-filter: blur(var(--glass-blur));
  -webkit-backdrop-filter: blur(var(--glass-blur));
  border: var(--border-glass);
  box-shadow: var(--shadow-md);
}

.perf-lite .referrer-shell.styleguide-device-frame {
  background: var(--color-surface-solid);
}

/* The frame is phone-sized but sits in a desktop viewport, so the ≥768px
   content inset would apply inside it — show the phone's single gutter. */
.referrer-shell.styleguide-device-frame .referrer-content {
  padding-inline: 0;
}

/*
 * The frame itself scrolls internally (the styleguide page's content is far
 * taller than one phone screen) while the tab bar stays pinned to the
 * frame's own bottom edge, not the real document's — `position: absolute`
 * from the base referrer-tabbar rule would otherwise sink to the bottom of
 * the full scrollable content instead of staying visible. `sticky` keeps it
 * pinned against this element's own scrolling box (its nearest scrolling
 * ancestor) as that box scrolls.
 */
.referrer-shell.styleguide-device-frame .referrer-tabbar {
  position: sticky;
}

/*
 * Mirrors page: comparisons must show the WHOLE screen, like the Figma
 * export beside them — a fixed-height scrolling frame would crop the
 * sticky CTA, activity feed and anything else below the first fold.
 */
.referrer-shell.styleguide-device-frame--full {
  height: auto;
  max-height: none;
  overflow-y: visible;
}

/*
 * NV-03 styleguide exhibit (_nv03_tabbar.html.erb) — the base
 * .referrer-tabbar rule is `position: fixed` below --bp-md and only
 * switches to `absolute` (relative to the exhibit's 375px frame) above it,
 * so the exhibit only rendered its absolute-in-frame presentation
 * correctly at one particular viewport width. `.is-exhibit` on the frame
 * forces `absolute` unconditionally, so the tabbar stays contained in the
 * frame regardless of the actual browser viewport width.
 */
.is-exhibit .referrer-tabbar {
  position: absolute;
}
