/* ============================================================
   9th Designs — App Shell
   ------------------------------------------------------------
   Turns the browser viewport into an application window: a
   unified translucent toolbar with traffic lights, a source-list
   sidebar, a content pane, a status bar, and a command palette.

   It is chrome only. It adds no palette of its own — every
   surface is drawn from tokens the page already defines, and
   each page names its one accent through `--shell-accent`.
   Structure (radius, elevation, motion, type) comes from
   assets/css/9th-standard.css — load that first, this last.
   ============================================================ */

html[data-app-shell] {
  /* ── Chrome metrics ─────────────────────────────────────── */
  --shell-toolbar-h: 52px;   /* same 52px as a standard row      */
  --shell-sidebar-w: 244px;
  --shell-status-h:  28px;

  /* ── Device safe areas ──────────────────────────────────── */
  /* Every page ships `viewport-fit=cover` and a translucent
     status bar, so installed to the home screen the web view
     runs edge to edge — under the clock, under the notch in
     landscape, under the home indicator. The chrome measures
     from these rather than from the raw viewport edge. */
  --shell-safe-top:    env(safe-area-inset-top, 0px);
  --shell-safe-right:  env(safe-area-inset-right, 0px);
  --shell-safe-bottom: env(safe-area-inset-bottom, 0px);
  --shell-safe-left:   env(safe-area-inset-left, 0px);

  /* The toolbar's true height, and the sidebar's true width:
     the metric plus whatever the device reserves beside it.
     Every offset in this file measures from these two. */
  --shell-toolbar-total: calc(var(--shell-toolbar-h) + var(--shell-safe-top));
  --shell-sidebar-total: calc(var(--shell-sidebar-w) + var(--shell-safe-left));

  /* Tracks the visual viewport, so a sheet can stop above the
     on-screen keyboard instead of behind it. JS overwrites this
     with a real pixel height; 100vh is the no-JS resting value
     and works back to browsers without dvh. */
  --shell-vh: 100vh;

  /* ── Light material (default) ───────────────────────────── */
  --shell-surface:   rgba(255, 255, 255, 0.82);
  --shell-sidebar:   rgba(242, 242, 242, 0.76);
  --shell-ink:       #1f1f1f;
  --shell-ink-2:     #6b7280;
  --shell-ink-3:     #9a9aa2;
  --shell-hairline:  rgba(0, 25, 53, 0.08);
  --shell-press:     rgba(0, 25, 53, 0.05);
  --shell-scrim:     rgba(15, 20, 25, 0.28);
  --shell-sheet:     #ffffff;
  --shell-field:     rgba(0, 25, 53, 0.05);

  /* The one restrained chrome accent. Pages re-set this. */
  --shell-accent:    #4b5563;
  --shell-accent-08: rgba(75, 85, 99, 0.08);
  --shell-accent-20: rgba(75, 85, 99, 0.20);

  /* Traffic lights reuse the standard's semantic slots. */
  --shell-close:     #d94f4a;
  --shell-min:       #f59e0b;
  --shell-zoom:      #34c759;
}

/* ── Dark material ────────────────────────────────────────── */
html[data-app-shell][data-app-theme="dark"] {
  --shell-surface:   rgba(12, 12, 16, 0.76);
  --shell-sidebar:   rgba(14, 14, 19, 0.82);
  --shell-ink:       rgba(255, 255, 255, 0.92);
  --shell-ink-2:     rgba(255, 255, 255, 0.52);
  --shell-ink-3:     rgba(255, 255, 255, 0.34);
  --shell-hairline:  rgba(255, 255, 255, 0.09);
  --shell-press:     rgba(255, 255, 255, 0.06);
  --shell-scrim:     rgba(0, 0, 0, 0.52);
  --shell-sheet:     #14141a;
  --shell-field:     rgba(255, 255, 255, 0.07);
}

/* ============================================================
   Window geometry
   ============================================================ */

/* `window` pages sit inside the frame: toolbar above, source
   list beside. `overlay` pages (the homepage) keep their own
   full-bleed first screen and let the chrome arrive on scroll. */
html[data-app-shell="window"] body {
  padding-top: var(--shell-toolbar-total);
}

/* Landscape on a notched phone puts the inset on one side only.
   `window` pages are text on a plain surface, so the whole body
   moves in. `overlay` pages are not included: their first screen
   is a full-bleed background that should still reach the glass,
   so they inset their own content instead. */
html[data-app-shell="window"] body {
  padding-right: var(--shell-safe-right);
  padding-left: var(--shell-safe-left);
}

@media (min-width: 1024px) {
  html[data-app-shell="window"] body {
    padding-left: var(--shell-sidebar-total);
  }
  html[data-app-shell="window"].shell-sidebar-collapsed body {
    padding-left: var(--shell-safe-left);
  }
}

/* The shell owns navigation, so the template's own rail and its
   hamburger step aside rather than duplicating it. */
html[data-app-shell="window"] #header,
html[data-app-shell="window"] .mobile-nav-toggle,
html[data-app-shell="window"] .back-bar {
  display: none !important;
}

html[data-app-shell="window"] #main {
  margin-left: 0 !important;
}

/* Gigs pages already reserve 52px for their own bar; that space
   now belongs to the toolbar, so the padding must not double. */
html[data-app-shell="window"] .page-wrap {
  padding-top: 0;
}

html[data-app-shell] body {
  overscroll-behavior-y: none;
}

/* Anchor targets clear the toolbar instead of hiding under it. */
html[data-app-shell] [id] {
  scroll-margin-top: calc(var(--shell-toolbar-total) + 12px);
}

/* Chrome is chrome: never selectable, never a text cursor. */
#shell-root {
  -webkit-user-select: none;
  user-select: none;
  cursor: default;
  -webkit-tap-highlight-color: transparent;
  font-family: FixelText, -apple-system, BlinkMacSystemFont, "Segoe UI",
               system-ui, sans-serif;
}

/* The chrome is injected into pages whose resets we do not
   control, and its metrics assume padding sits inside the box.
   Stated here so a page without a reset measures the same. */
#shell-root,
#shell-root *,
#shell-root *::before,
#shell-root *::after {
  box-sizing: border-box;
}

#shell-root button {
  font: inherit;
  color: inherit;
  background: none;
  border: none;
  cursor: default;
  padding: 0;
}

/* A visible ring for keyboard users, invisible to the mouse. */
#shell-root :focus-visible {
  outline: 2px solid var(--shell-accent);
  outline-offset: 2px;
  border-radius: 6px;
}

/* ============================================================
   Unified toolbar
   ============================================================ */

.shell-titlebar {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: var(--shell-toolbar-total);
  z-index: 2147483000;
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  /* The status-bar inset is padding, not margin, so the glass
     runs behind the clock while the controls sit clear of it. */
  padding: var(--shell-safe-top)
           calc(12px + var(--shell-safe-right))
           0
           calc(12px + var(--shell-safe-left));
  background: var(--shell-surface);
  backdrop-filter: blur(24px) saturate(180%);
  -webkit-backdrop-filter: blur(24px) saturate(180%);
  color: var(--shell-ink);
  transition: opacity .28s var(--ease-spring),
              transform .28s var(--ease-spring),
              background-color .2s ease;
}

/* The 0.5px closing hairline every glass surface gets. */
.shell-titlebar::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 0.5px;
  background: var(--shell-hairline);
}

/* Overlay mode: the first screen is untouched, the toolbar
   materialises only once the page has scrolled past it. */
html[data-app-shell="overlay"] .shell-titlebar,
html[data-app-shell="overlay"] .shell-status {
  opacity: 0;
  pointer-events: none;
  transform: translateY(-6px);
}

html[data-app-shell="overlay"].shell-past-fold .shell-titlebar,
html[data-app-shell="overlay"].shell-past-fold .shell-status {
  opacity: 1;
  pointer-events: auto;
  transform: none;
}

/* Once the toolbar takes over, the page's own floating hamburger
   steps aside rather than sitting on top of it. Above the fold it
   is untouched. */
html[data-app-shell="overlay"].shell-past-fold .nav-toggle {
  opacity: 0;
  pointer-events: none;
  transition: opacity .2s ease;
}

/* ── Traffic lights ───────────────────────────────────────── */
.shell-lights {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 8px 0 6px;
  flex-shrink: 0;
}

.shell-light {
  width: 12px;
  height: 12px;
  border-radius: var(--r-pill);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  position: relative;
  transition: filter .15s ease, transform .08s ease, opacity .15s ease;
}

.shell-light::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: var(--r-pill);
  box-shadow: inset 0 0 0 0.5px rgba(0, 25, 53, 0.14);
}

.shell-light--close { background: var(--shell-close); }
.shell-light--min   { background: var(--shell-min); }
.shell-light--zoom  { background: var(--shell-zoom); }

.shell-light svg {
  width: 8px;
  height: 8px;
  opacity: 0;
  transition: opacity .12s ease;
  position: relative;
  z-index: 1;
  color: rgba(0, 0, 0, 0.55);
}

@media (hover: hover) and (pointer: fine) {
  .shell-lights:hover .shell-light svg { opacity: 1; }
}

/* No hover to reveal them, so the glyphs stay on — otherwise a
   finger is aiming at three unlabelled dots. */
@media (hover: none) {
  .shell-light svg { opacity: 1; }
}

.shell-light:active { transform: scale(.92); filter: brightness(.88); }

/* ── Toolbar buttons ──────────────────────────────────────── */
.shell-iconbtn {
  width: 30px;
  height: 30px;
  border-radius: var(--r-thumb);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--shell-ink-2);
  flex-shrink: 0;
  transition: background-color .12s ease, color .12s ease,
              transform .08s ease, opacity .15s ease;
}

.shell-iconbtn svg { width: 16px; height: 16px; }
@media (hover: hover) and (pointer: fine) {
  .shell-iconbtn:hover { background: var(--shell-press); color: var(--shell-ink); }
}
.shell-iconbtn:active { transform: scale(.92); opacity: .92; }
.shell-iconbtn[disabled] { opacity: .3; pointer-events: none; }
.shell-iconbtn.is-active { color: var(--shell-accent); background: var(--shell-accent-08); }

.shell-navgroup {
  display: flex;
  align-items: center;
  gap: 2px;
  flex-shrink: 0;
}

/* ── Centred window title ─────────────────────────────────── */
.shell-title {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  max-width: min(46vw, 460px);
  pointer-events: none;
  opacity: 0;
  transition: opacity .2s ease;
}

/* Native behaviour: the title arrives in the toolbar only once
   the page's own heading has scrolled away. */
html.shell-scrolled .shell-title,
html[data-app-shell="overlay"].shell-past-fold .shell-title { opacity: 1; }

.shell-title__text {
  font-size: 13px;
  font-weight: 600;
  letter-spacing: -.005em;
  color: var(--shell-ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.shell-title__sub {
  font-size: 13px;
  color: var(--shell-ink-3);
  white-space: nowrap;
}

/* ── Trailing cluster + search field ──────────────────────── */
.shell-actions {
  display: flex;
  align-items: center;
  gap: var(--sp-1);
  margin-left: auto;
  flex-shrink: 0;
}

/* The page's one primary verb, in the toolbar's trailing cluster. */
.shell-action {
  display: inline-flex;
  align-items: center;
  height: 30px;
  padding: 0 12px;
  margin-right: var(--sp-1);
  border-radius: var(--r-thumb);
  background: var(--shell-accent-08);
  box-shadow: inset 0 0 0 1px var(--shell-accent-20);
  color: var(--shell-accent);
  font-size: var(--fs-micro);
  font-weight: 700;
  letter-spacing: var(--tr-micro);
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
  transition: background-color .12s ease, transform .08s ease, opacity .15s ease;
}

@media (hover: hover) and (pointer: fine) {
  .shell-action:hover { background: var(--shell-accent-20); text-decoration: none; }
}
.shell-action:active { transform: scale(.98); opacity: .92; }
.shell-action[hidden] { display: none; }

@media (max-width: 700px) {
  .shell-action { display: none; }
}

.shell-search {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  height: 30px;
  padding: 0 8px 0 10px;
  border-radius: var(--r-thumb);
  background: var(--shell-field);
  color: var(--shell-ink-3);
  font-size: 13px;
  transition: background-color .12s ease, color .12s ease, transform .08s ease;
}

.shell-search svg { width: 13px; height: 13px; }
@media (hover: hover) and (pointer: fine) {
  .shell-search:hover { color: var(--shell-ink-2); }
}
.shell-search:active { transform: scale(.98); opacity: .92; }
.shell-search__label { min-width: 62px; text-align: left; }

.shell-kbd {
  display: inline-flex;
  align-items: center;
  gap: 1px;
  padding: 2px 5px;
  border-radius: 5px;
  background: var(--shell-press);
  color: var(--shell-ink-3);
  font-size: var(--fs-micro);
  font-weight: 600;
  line-height: 1;
}

@media (max-width: 700px) {
  .shell-search__label,
  .shell-search .shell-kbd { display: none; }
  .shell-search { width: 30px; justify-content: center; padding: 0; }
  .shell-title { max-width: 40vw; }
}

/* Phone toolbars keep Back and drop Forward, as Safari does. */
@media (max-width: 480px) {
  .shell-navgroup [data-shell-nav="forward"] { display: none; }
  /* Three controls and a centred title on a 320px toolbar: the
     title takes what is genuinely free and ellipsises the rest. */
  .shell-title { max-width: 34vw; }
}

/* ── Touch targets ────────────────────────────────────────────
   A finger needs ~44px. The toolbar's 52px height supplies the
   vertical run; these widen the controls to match and space the
   traffic lights so their hit areas cannot overlap. */
@media (pointer: coarse) {
  .shell-iconbtn { width: 40px; height: 40px; }
  .shell-iconbtn svg { width: 18px; height: 18px; }
  .shell-search { height: 40px; }
  .shell-action { height: 38px; padding: 0 14px; }

  .shell-lights { gap: 14px; padding: 0 10px 0 6px; }
  .shell-light { width: 14px; height: 14px; }
  .shell-light svg { width: 9px; height: 9px; }

  /* An invisible target the height of the toolbar and exactly as
     wide as the pitch between two lights — full-height to press,
     never wide enough to steal its neighbour's tap. */
  .shell-light::before {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    width: 28px;
    height: 44px;
    transform: translate(-50%, -50%);
  }
}

/* The collapsed search button follows the same 40px target. */
@media (pointer: coarse) and (max-width: 700px) {
  .shell-search { width: 40px; }
}

/* ── Navigation progress ──────────────────────────────────── */
.shell-progress {
  position: absolute;
  left: 0;
  bottom: 0;
  height: 2px;
  width: 0;
  background: var(--shell-accent);
  opacity: 0;
  border-radius: 0 var(--r-pill) var(--r-pill) 0;
  transition: width .2s ease, opacity .2s ease;
  z-index: 1;
}

html.shell-loading .shell-progress { opacity: 1; }

/* ============================================================
   Source-list sidebar
   ============================================================ */

.shell-sidebar {
  position: fixed;
  top: var(--shell-toolbar-total);
  bottom: 0;
  left: 0;
  width: var(--shell-sidebar-total);
  z-index: 2147482900;
  background: var(--shell-sidebar);
  backdrop-filter: blur(24px) saturate(180%);
  -webkit-backdrop-filter: blur(24px) saturate(180%);
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: thin;
  scrollbar-color: var(--shell-hairline) transparent;
  padding: var(--sp-2)
           10px
           calc(var(--sp-4) + var(--shell-safe-bottom))
           calc(10px + var(--shell-safe-left));
  transition: transform .32s var(--ease-spring), opacity .2s ease, visibility .32s;
}

/* While a finger is on the drawer it tracks the finger exactly;
   the spring belongs to the release, not the drag. */
html.shell-dragging .shell-sidebar,
html.shell-dragging .shell-scrim {
  transition: none;
}

.shell-sidebar::-webkit-scrollbar { width: 8px; }
.shell-sidebar::-webkit-scrollbar-thumb {
  background: var(--shell-hairline);
  border-radius: var(--r-pill);
  border: 2px solid transparent;
  background-clip: padding-box;
}

/* The vertical hairline that separates list from content. */
.shell-sidebar::after {
  content: "";
  position: fixed;
  top: var(--shell-toolbar-total);
  bottom: 0;
  left: var(--shell-sidebar-total);
  width: 0.5px;
  background: var(--shell-hairline);
  pointer-events: none;
}

.shell-sidebar__caption {
  font-size: var(--fs-caption);
  font-weight: var(--fw-caption);
  letter-spacing: var(--tr-caption);
  text-transform: uppercase;
  color: var(--shell-ink-3);
  padding: 14px 8px 6px;
}

.shell-sidebar__group + .shell-sidebar__group .shell-sidebar__caption {
  padding-top: 18px;
}

.shell-sidebar__list { list-style: none; margin: 0; padding: 0; }

/* Desktop density: 32px rows, 13px labels — the compact rhythm a
   Mac source list uses. Touch bumps this back to a 44px target. */
.shell-srcrow {
  display: flex;
  align-items: center;
  gap: 9px;
  min-height: 32px;
  padding: 5px 8px;
  border-radius: var(--r-thumb);
  color: var(--shell-ink);
  font-size: 13px;
  font-weight: 500;
  line-height: 1.25;
  text-decoration: none;
  transition: var(--t-row), color .12s ease;
}

@media (hover: hover) and (pointer: fine) {
  .shell-srcrow:hover { background: var(--shell-press); text-decoration: none; }
}
.shell-srcrow:active { background: var(--shell-press); transform: scale(.99); }

/* Selection: the object hue at 8% fill with the hue as the label,
   exactly the 08/20 reuse the standard calls for. */
.shell-srcrow.is-current {
  background: var(--shell-accent-08);
  box-shadow: inset 0 0 0 1px var(--shell-accent-20);
  color: var(--shell-accent);
  font-weight: 600;
}

.shell-srcrow__icon {
  width: 20px;
  height: 20px;
  border-radius: 6px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  color: var(--shell-ink-2);
  font-size: 12px;
}

.shell-srcrow.is-current .shell-srcrow__icon { color: var(--shell-accent); }
.shell-srcrow__icon svg { width: 15px; height: 15px; }

.shell-srcrow__label {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.shell-srcrow__badge {
  margin-left: auto;
  font-size: var(--fs-micro);
  font-weight: 700;
  letter-spacing: var(--tr-micro);
  text-transform: uppercase;
  color: var(--shell-ink-3);
}

/* Nested children (a role under Gigs, a policy under Policies). */
.shell-sidebar__list--child { margin-left: 20px; }
.shell-sidebar__list--child .shell-srcrow { min-height: 28px; font-size: 12.5px; }

/* ── Off-canvas below 1024px: the list becomes a drawer ───── */
@media (max-width: 1023px) {
  .shell-sidebar {
    /* 320px of usable list, plus the inset it has to sit clear of,
       and never more than most of the screen. */
    width: min(calc(320px + var(--shell-safe-left)), 86vw);
    border-top-right-radius: var(--r-sheet);
    border-bottom-right-radius: var(--r-sheet);
    box-shadow: 8px 0 28px rgba(15, 20, 25, 0.16);
    transform: translateX(-102%);
    visibility: hidden;
    padding-top: var(--sp-2);
  }
  .shell-sidebar::after { display: none; }
  html.shell-sidebar-open .shell-sidebar { transform: none; visibility: visible; }

  /* A drawer mid-drag is visible at whatever offset the finger
     put it, before either end state has been committed. */
  html.shell-dragging .shell-sidebar { visibility: visible; }
}

/* Touch targets return to the standard 44px row wherever the
   pointer is a finger — including an iPad in landscape, which is
   exactly 1024px wide and so gets the fixed column, not the drawer. */
@media (max-width: 1023px), (pointer: coarse) {
  .shell-srcrow { min-height: 44px; font-size: 15px; padding: 8px 10px; gap: 12px; }
  .shell-srcrow__icon { width: 26px; height: 26px; border-radius: 7px; }
  .shell-srcrow__icon svg { width: 17px; height: 17px; }
  .shell-sidebar__list--child .shell-srcrow { min-height: 40px; font-size: 14px; }
}

@media (min-width: 1024px) {
  html[data-app-shell="window"].shell-sidebar-collapsed .shell-sidebar {
    transform: translateX(-102%);
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
  }
}

/* Overlay pages hand their whole viewport to the page, so the
   source list is a drawer at every width — never a fixed column. */
html[data-app-shell="overlay"] .shell-sidebar {
  transform: translateX(-102%);
  visibility: hidden;
  border-top-right-radius: var(--r-sheet);
  border-bottom-right-radius: var(--r-sheet);
  box-shadow: 8px 0 28px rgba(15, 20, 25, 0.16);
}

html[data-app-shell="overlay"] .shell-sidebar::after { display: none; }

html[data-app-shell="overlay"].shell-sidebar-open .shell-sidebar {
  transform: none;
  visibility: visible;
}

html[data-app-shell="overlay"].shell-sidebar-open .shell-scrim {
  opacity: 1;
  pointer-events: auto;
}

/* ── Scrim behind the drawer and the palette ──────────────── */
.shell-scrim {
  position: fixed;
  inset: 0;
  z-index: 2147482800;
  background: var(--shell-scrim);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity .28s var(--ease-spring), visibility .28s;
}

html.shell-sidebar-open .shell-scrim {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
}

@media (min-width: 1024px) {
  html.shell-sidebar-open .shell-scrim { opacity: 0; pointer-events: none; }
}

/* ============================================================
   Status bar
   ============================================================ */

.shell-status {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  height: calc(var(--shell-status-h) + var(--shell-safe-bottom));
  padding: 0
           calc(12px + var(--shell-safe-right))
           var(--shell-safe-bottom)
           calc(12px + var(--shell-safe-left));
  z-index: 2147482950;
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  background: var(--shell-surface);
  backdrop-filter: blur(24px) saturate(180%);
  -webkit-backdrop-filter: blur(24px) saturate(180%);
  font-size: var(--fs-micro);
  color: var(--shell-ink-3);
  transition: opacity .28s var(--ease-spring), transform .28s var(--ease-spring);
}

.shell-status::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: 0.5px;
  background: var(--shell-hairline);
}

.shell-status__path {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.shell-status__sep { opacity: .5; }
.shell-status__hint { margin-left: auto; display: flex; align-items: center; gap: 6px; }

@media (min-width: 1024px) {
  html[data-app-shell="window"]:not(.shell-sidebar-collapsed) .shell-status {
    left: var(--shell-sidebar-total);
  }
}

/* The status bar is a desktop affordance. A phone has no room for
   a permanent breadcrumb, and its two hints name keys a phone does
   not have — so it goes, and the space goes back to the page. */
@media (max-width: 767px) {
  .shell-status { display: none; }
}

/* A landscape phone is wide enough to match the desktop rules and
   far too short to spend 28px on chrome. */
@media (max-height: 460px) and (orientation: landscape) {
  html[data-app-shell] { --shell-toolbar-h: 44px; }
  .shell-status { display: none; }
}

/* Content must never end up under the status bar — or, where there
   is no status bar, under the home indicator. Higher specificity
   than the page's own `body` rule on purpose. */
html[data-app-shell] body {
  padding-bottom: var(--shell-safe-bottom);
}

@media (min-width: 768px) {
  html[data-app-shell] body {
    padding-bottom: calc(var(--shell-status-h) + var(--shell-safe-bottom) + 8px);
  }
}

@media (max-height: 460px) and (orientation: landscape) {
  html[data-app-shell] body {
    padding-bottom: var(--shell-safe-bottom);
  }
}

/* ============================================================
   Command palette
   ============================================================ */

.shell-palette {
  position: fixed;
  inset: 0;
  z-index: 2147483100;
  display: flex;
  justify-content: center;
  align-items: flex-start;
  padding: max(12vh, 64px) var(--gutter) var(--gutter);
  background: var(--shell-scrim);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity .2s ease, visibility .2s;
}

html.shell-palette-open .shell-palette {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
}

.shell-palette__sheet {
  width: 100%;
  max-width: 560px;
  max-height: min(62vh, 520px);
  display: flex;
  flex-direction: column;
  background: var(--shell-sheet);
  border-radius: var(--r-sheet);
  box-shadow: 0 24px 60px rgba(15, 20, 25, 0.28),
              0 0 0 0.5px var(--shell-hairline);
  overflow: hidden;
  transform: scale(.96) translateY(-8px);
  opacity: 0;
  transition: transform .32s var(--ease-spring), opacity .2s ease;
}

html.shell-palette-open .shell-palette__sheet {
  transform: none;
  opacity: 1;
}

.shell-palette__field {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: 14px var(--gutter);
  flex-shrink: 0;
}

.shell-palette__field svg {
  width: 17px;
  height: 17px;
  color: var(--shell-ink-3);
  flex-shrink: 0;
}

/* 16px so iOS never zooms the field on focus. */
.shell-palette__input {
  flex: 1;
  min-width: 0;
  border: none;
  background: none;
  outline: none;
  font: inherit;
  font-size: var(--fs-body);
  color: var(--shell-ink);
  -webkit-user-select: text;
  user-select: text;
}

.shell-palette__input::placeholder { color: var(--shell-ink-3); }

.shell-palette__results {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 0 var(--sp-2) var(--sp-2);
  border-top: 0.5px solid var(--shell-hairline);
  list-style: none;
  margin: 0;
}

.shell-palette__caption {
  font-size: var(--fs-caption);
  font-weight: var(--fw-caption);
  letter-spacing: var(--tr-caption);
  text-transform: uppercase;
  color: var(--shell-ink-3);
  padding: 14px 10px 6px;
}

.shell-paletterow {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  min-height: 44px;
  padding: 7px 10px;
  border-radius: var(--r-tile);
  color: var(--shell-ink);
  text-decoration: none;
  cursor: default;
  transition: var(--t-row);
}

.shell-paletterow.is-active { background: var(--shell-accent-08); }
.shell-paletterow.is-active .shell-paletterow__title { color: var(--shell-accent); }

.shell-paletterow__icon {
  width: 28px;
  height: 28px;
  border-radius: var(--r-tile);
  background: var(--shell-field);
  color: var(--shell-ink-2);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

.shell-paletterow__icon svg { width: 15px; height: 15px; }
.shell-paletterow__body { display: block; min-width: 0; flex: 1; }

.shell-paletterow__title {
  display: block;
  font-size: 14px;
  font-weight: 600;
  line-height: 1.25;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.shell-paletterow__sub {
  display: block;
  font-size: 12px;
  color: var(--shell-ink-3);
  margin-top: 1px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.shell-paletterow__hint {
  font-size: var(--fs-micro);
  color: var(--shell-ink-3);
  flex-shrink: 0;
}

.shell-palette__empty {
  text-align: center;
  padding: 28px 16px 24px;
  color: var(--shell-ink-3);
  font-size: var(--fs-page-sub);
}

.shell-palette__foot {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: 8px 14px calc(8px + env(safe-area-inset-bottom));
  border-top: 0.5px solid var(--shell-hairline);
  font-size: var(--fs-micro);
  color: var(--shell-ink-3);
  flex-shrink: 0;
  flex-wrap: wrap;
}

.shell-palette__foot span {
  display: inline-flex;
  align-items: center;
  gap: 5px;
}

/* ── The palette on a phone ───────────────────────────────────
   Focusing the field raises the keyboard, which shrinks the
   visual viewport but not the layout viewport — so a sheet sized
   in vh keeps its full height and hides its results behind the
   keyboard. --shell-vh is the visual viewport, kept current by
   app-shell.js, so the sheet ends where the keyboard begins. */
@media (max-width: 640px) {
  .shell-palette {
    padding: calc(var(--shell-toolbar-total) + var(--sp-2))
             calc(var(--sp-3) + var(--shell-safe-right))
             calc(var(--sp-3) + var(--shell-safe-bottom))
             calc(var(--sp-3) + var(--shell-safe-left));
  }

  .shell-palette__sheet {
    max-height: calc(var(--shell-vh)
                     - var(--shell-toolbar-total)
                     - var(--shell-safe-bottom)
                     - var(--sp-6));
  }

  /* A phone has no ⌘, no ↑↓ and no ⏎ — the footer is naming keys
     that are not on the device. The row hints go with it. */
  .shell-palette__foot { display: none; }
  .shell-paletterow__hint { display: none; }

  /* Rows are the only thing being aimed at here, so they get the
     full standard row height rather than the compact 44px. */
  .shell-paletterow { min-height: var(--row-h); }
  .shell-palette__results { padding-bottom: var(--sp-3); }
}

/* ============================================================
   Page transitions
   ============================================================ */

/* Cross-document/soft transition for browsers with the View
   Transition API; the class below is the fallback everywhere else. */
html.shell-swapping body > *:not(#shell-root) {
  opacity: 0;
  transform: translateY(6px);
  transition: opacity .16s ease, transform .16s ease;
}

@view-transition { navigation: auto; }

::view-transition-old(root) { animation: shell-fade-out .16s ease both; }
::view-transition-new(root) { animation: shell-fade-in .26s var(--ease-spring) both; }

@keyframes shell-fade-out { to { opacity: 0; } }
@keyframes shell-fade-in {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  html.shell-swapping body > *:not(#shell-root) {
    opacity: 1;
    transform: none;
  }
  ::view-transition-old(root),
  ::view-transition-new(root) { animation: none; }
}

/* ============================================================
   Native hardening
   ============================================================ */

html[data-app-shell] ::selection {
  background: var(--shell-accent-20);
}

/* ── Scroll lock behind a drawer or sheet ─────────────────────
   `overflow: hidden` alone does not hold iOS: touch-scrolling the
   scrim scrolls the page underneath it. Pinning the body is what
   actually stops it; app-shell.js supplies the offset so the page
   does not jump to the top and lose its place. */
html.shell-locked,
html.shell-locked body {
  overflow: hidden;
}

/* left and right rather than a width, so the body's own safe-area
   padding is not added on top of a 100% and pushed into overflow. */
html.shell-locked body {
  position: fixed;
  top: var(--shell-locked-y, 0px);
  left: 0;
  right: 0;
}

/* Reserving the scrollbar keeps pages of different heights from
   nudging sideways as the router swaps them. Window pages only —
   on an overlay page it would shift the first screen. */
html[data-app-shell="window"] {
  scrollbar-gutter: stable;
}

/* Print: the chrome is screen furniture only. */
@media print {
  #shell-root { display: none !important; }
  html[data-app-shell="window"] body { padding: 0 !important; }
}
