/**
 * KOL Components — Shell (application chrome)
 *
 * Chrome for @kolkrabbi/kol-shell — the app shell set (NavRail + AppShell +
 * page scaffolds) lifted from kol-monitor/kol-mirror 2026-08-14. App chrome,
 * not site chrome: kol-framework owns the site shell (SideNav, footer,
 * heroes); this file owns the rail's live width token and the page gutter.
 */

:root {
  /* THE RAIL OWNS THIS VARIABLE (RailFlatGrabOpen, kol-mirror 2026-08-28). It
   * is the rail's LIVE width: `NavRail` sets 48 on mount, writes it per
   * pointermove while the grab is dragged and gsap-tweens it on release, and
   * AppShell's content is offset by the same value — so the page is pushed
   * through the drag. The 48 here is the closed default and the value a
   * consumer's own fixed chrome (`--monitor-rail`, `--fxr-rail`) reads before
   * the rail mounts. 0.80.0–0.87.x had it derive from the sidenav tokens,
   * while the rail WAS a collapsed SideNav; that is reversed. */
  --kol-shell-rail-width: 48px;
  /* Page gutter — PageShell's padding and PageBleed's negative margin.
   * ONE LADDER FOR THE ESTATE (two-page-scaffolds-one-job, kol-client-olina
   * 2026-09-03): this is `--kol-pad-section-x`, the page-content ladder every
   * `.kol-page` already wears (20 · 32 from 768 · 48 from 1024,
   * kol-framework.css). It had been a second ladder — a fixed 48 until
   * 2026-09-01 (ShellPagePadFixedOnMobile, kol-chess: 96px of gutter on a 390
   * phone, Settings ~35pt from the edge while every .kol-page sibling sat
   * ~15pt), then a clamp that reproduced .kol-page's outcome with the technique
   * the framework's own comment rejects; the two agreed only at 48. The clamp
   * stays as the FALLBACK for a consumer that loads kol-theme without
   * kol-framework. PageBleed's negative margin reads the same token, so the
   * pair cannot drift. */
  --kol-shell-page-pad: var(--kol-pad-section-x, clamp(20px, 5vw, 48px));
}

/* ── PageShell ──────────────────────────────────────────────────────────── */

/* THE GEOMETRY IS A CLASS (two-page-scaffolds-one-job, 2026-09-03). It was an
 * inline style object with no class of its own, so a consumer that needed to
 * correct any of it had no selector — only the `style` prop, which is how olina
 * had to hide a double-painted wash. Inline stays for `style` alone. */
.kol-shell-page {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  padding: var(--kol-shell-page-pad);
  /* PageShellScrollbarGutter (kol-monitor 2026-08-28): in `fixed` mode this
   * element IS the scroll container and reserves the gutter; in `scroll` mode
   * the property is inert and the viewport keeps doing what it did. */
  scrollbar-gutter: stable;
  /* THE PAGE'S GROUND, ONE OWNER PER PIXEL. `--kol-shell-page-wash` means WHAT
   * IS LEFT TO PAINT: kol-shell's AppShell paints the primary back and hands
   * the wash down for this to paint (ShellPageWash, 2026-08-27); kol-framework's
   * PageLayout paints the wash on its plane itself and hands down
   * `transparent`, so a PageShell inside it paints nothing over it — before,
   * both painted and `fg-02` rendered as two 0.02 layers (measured on olina's
   * /slide-deck). Outside any frame: primary. */
  background: var(--kol-shell-page-wash, var(--kol-surface-primary));
}
/* `mode="fixed"` — the element is the scroll container; pair with a
 * `flex:1 overflow:auto` body (the settings idiom) */
.kol-shell-page--fixed { min-height: 0; height: 100vh; overflow: hidden; }
/* `width="capped"` — the SITE tier: the framework container ladder, centred.
 * `bleed` (the default, unclassed) fills the window — the app tier. The one
 * real difference between a shell page and a `.kol-page`, and it is a prop
 * now so a site adopting the shipped catalog page does not silently get app
 * geometry. */
.kol-shell-page--capped {
  max-width: var(--kol-container-max, none);
  margin-inline: auto;
  width: 100%;
  /* the site tier's VERTICAL rung too: `.kol-page` pads `64px` block, and a
   * capped page beside twenty `.kol-page` siblings sat 16px higher on the 48
   * gutter (kol-client-olina 2026-09-03, on the link). Verbatim to `.kol-page`;
   * the app tier (bleed) keeps its 48 — every monitor / mirror / fxr page. */
  padding-block: 64px;
}
/* PageBleed — cancels the gutter for a full-width embed (monitor's rack) */
.kol-shell-page-bleed { margin-inline: calc(var(--kol-shell-page-pad) * -1); }

/* ── CatalogPage ────────────────────────────────────────────────────────── */

/* THE CATALOG GRID — a ceiling with a floor: up to 6 tracks, none narrower
 * than `CatalogPage minColumn` (160), gap 24. The count is `--kol-catalog-n`,
 * MEASURED by CatalogPage (a ResizeObserver on this element) and published on
 * the page, where `.kol-filters-first` reads the same number — so the filter
 * row's first group is one track of THIS grid at every width. The grid used to
 * be an inline `auto-fill` that resolved to the same tracks but told nobody
 * how many (CatalogFilterFirstGroupTrackCount, 2026-09-03). 0.137.0 computed
 * the count in CSS with a length ÷ length inside `round()`, which Firefox
 * rejects — one column, huge card — see kol-components-organisms.css. */
.kol-catalog-grid {
  display: grid;
  gap: 24px;
  grid-template-columns: repeat(var(--kol-catalog-n, 6), minmax(0, 1fr));
}

/* ── NavRail ────────────────────────────────────────────────────────────── */

/* The flat rail's own stacking. Geometry (the fixed box, the padding, the gap)
 * rides the element as utilities — it is one div and every child is a direct
 * child of it, which is the shape the ruling asked for; only the z-tier needs a
 * rule, because it must join the DS ladder rather than a consumer's raw number
 * (monitor's z-70 rail sat above its own z-50 overlays, a live stacking bug).
 * Above page content, BELOW overlay (50) and modal (100). */
.kol-shell-rail {
  z-index: var(--kol-z-sticky);
}

/* The 2026-08-12 active-state ruling, carried natively so consumers delete
 * their overrides: ink full-strength in EVERY state (the nav variant's oq-64
 * rest is site-nav quiet, wrong on an icon rail — the rail also sets oq-96
 * inline per rung); hover = the oq-04 wash from the variant; active route =
 * that wash HELD ON. Never `selected`/`pressed` — navigation is location, not
 * a toggled tool. Scoped to the rail: the global
 * `.kol-btn-nav[aria-current="page"]` brightness-only rule stays the site-nav law. */
.kol-shell-rail .kol-btn-nav {
  color: var(--kol-oq-96);
}
.kol-shell-rail .kol-btn-nav[aria-current="page"] {
  background-color: var(--kol-oq-04);
  color: var(--kol-oq-96);
}

/* ── GridCard preview fits ──────────────────────────────────────────────── */

/* Ported from monitor's local components.css (.gridcard-preview) — ship with
 * the card so both consumers delete the local copy. */
.kol-shell-card-preview img {
  object-position: top left;
}
.kol-shell-card-preview--cover img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.kol-shell-card-preview--natural img {
  max-width: none;
  transform: scale(0.5);
  transform-origin: top left;
}
.kol-shell-card-preview--compact img {
  max-width: none;
  transform: scale(0.3);
  transform-origin: top left;
}

/* THE APP TIER'S SELECTION IS NEUTRAL (ShellHomeSystem, 2026-08-27): kol-framework
 * paints ::selection brand-yellow, which is a SITE's; an app is not a site.
 * AppShell wraps its tree in `.kol-app-shell`. */
.kol-app-shell ::selection {
  background-color: var(--kol-fg-24);
  color: inherit;
}

/* ── THE RAIL AS A DRAWER ────────────────────────────────────────────────────
 * `AppShell touch="drawer"` below `drawerBelow` (ShellRailNoDrawerOnMobile,
 * kol-chess 2026-08-31). At 390 the 48px rail is 12.3% of the viewport spent on
 * chrome in the one place there is none to spare — a chess board went from
 * 302px to 350px the moment it folded. `railToggleKey` could not help: it is a
 * KEY, and a phone has no keyboard.
 *
 * JS owns the breakpoint and stamps `data-rail-drawer` on the shell root; CSS
 * only paints. No media query here — the fold width is a prop, so a query would
 * be a second source of truth for it. And no `!important`: the rail reads its
 * own `--kol-shell-drawer-width` in drawer mode instead of the live rail token,
 * so nothing here is fighting an inline style. */
/* FROM THE LEFT (ShellDrawerSideCorrection, kol-chess 2026-09-01). 0.34.0 read
 * "hamburger menu location" as a ruling on the panel and mirrored the whole of
 * drawer mode; the ask was the trigger's corner. The panel slides in from the
 * left as it did through 0.33.0 — NavRail pins left-0 in both modes, off-canvas
 * is -100% — and the trigger holds top-right instead. */
[data-rail-drawer] .kol-shell-rail {
  transform: translateX(-100%);
  transition: transform 220ms cubic-bezier(0.4, 0, 0.2, 1);
}
[data-rail-drawer="open"] .kol-shell-rail {
  transform: translateX(0);
}
@media (prefers-reduced-motion: reduce) {
  [data-rail-drawer] .kol-shell-rail { transition: none; }
}

/* Above the rail, so it stays tappable with the drawer open. */
/* TOP-RIGHT, both states (ShellDrawerOnRight, held by ShellDrawerSideCorrection).
 * This is the whole fix: the trigger used to translate by the drawer width and
 * ride the panel's trailing edge, landing the open X mid-screen at 390. Pinned
 * to the far corner it never meets the left-hand panel, so the 2026-08-31 travel
 * rule stays retired — one control, one position, glyph swaps in place. */
.kol-shell-drawer-trigger {
  position: fixed;
  top: 12px;
  right: 12px;
  z-index: calc(var(--kol-z-sticky) + 2);
  width: 32px;
  height: 32px;
}

.kol-shell-drawer-scrim {
  position: fixed;
  inset: 0;
  z-index: calc(var(--kol-z-sticky) - 1);
  background: color-mix(in srgb, var(--kol-color-ab-black) 48%, transparent);
}

/* ── THE TAP OPENER IS THE LINE (ShellRailCollapsedWithTapOpen, kol-mirror
 * 2026-09-01; corrected by RailGrabTapIsALine, kol-monitor 2026-09-02 — user:
 * "it was a thicker line" · "who decided it should be a chevron?"). `touch=
 * "shell"` keeps the collapsed 48px rail visible, and its only opener is the
 * grab strip: 8px on the line, a pill that wakes on pointer PROXIMITY. A thumb
 * never hovers, so 0.38.0 put a 24px disc with a chevron on the line — the DS
 * agent's shape, not the ruling. The ruling is the strip ITSELF, thicker: under
 * a coarse pointer the strip widens to a thumb-sized hit and its pill sits at
 * rest, twice as thick — the same affordance a fine pointer drags, no glyph.
 * Fine pointers keep the ruled grab (RailFlatGrabOpen, OneGrabGestureBothRails)
 * and never see this; the strip already toggles on a press with no travel.
 * Geometry lives here, the pill's motion stays in kol-animation.css. */
.kol-rail-grab {
  appearance: none;
  background: transparent;
  border: 0;
  padding: 0;
  color: inherit;
}
@media (pointer: coarse) {
  .kol-rail-grab {
    width: 24px;
    right: -12px;
    cursor: pointer;
  }
  /* no proximity on touch — the pill is simply THERE, and thicker */
  .kol-rail-grab::before {
    opacity: 1;
    width: 0.25rem;
    transition: none;
  }
}
