/* app-shell.css: the ONE Uliverse app shell (panel R10 rulings 1, 2, 3, 4, 7, 8, 10, 11; 2026-11-04).
   Link order: ../tokens/generated/tokens.css, then assets/app-shell.css, then the page's own <style> (layout only).
   Tokens only: no hex, no raw colour. Pages repeat none of this; they add their own grid and content.
   Markup: <div class="app"><nav class="side">…</nav><div><header class="top"><div class="in">…</div></header><main>…</main></div></div>
   Header glass, the one line every page adds (glass only once content scrolls under it):
   const t = document.querySelector('.top'), u = () => t.classList.toggle('under', scrollY > 4); addEventListener('scroll', u, { passive: true }); u() */

/* Base: box model, Inter, the flat ground (no wash on app screens). */
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
html { scroll-padding-top: 80px; }
/* Skip link: the first tab stop on every app screen goes to the content, past the navigation (WCAG 2.4.1). */
.skip { display: inline-flex; align-items: center; min-height: 48px; position: absolute; left: var(--space-4); top: var(--space-2); z-index: 9; padding: var(--space-2) var(--space-4); border-radius: var(--radius); background: var(--card); box-shadow: var(--shadow-md); color: var(--ink); transform: translateY(-200%); }
.skip:focus { transform: none; }
main:focus { outline: none; } /* main takes focus from the skip link (tabindex="-1"); it is a target, not a control */
body { font-family: "Inter", var(--type-family-body); color: var(--ink); background: var(--ground); font-size: 16px; line-height: 24px; }
img { display: block; }
button { color: inherit; cursor: pointer; }
[hidden] { display: none !important; }

/* Icons: sprite line drawings, 1.75 at every size (the sprite carries non-scaling-stroke). */
.i { width: 20px; height: 20px; flex: none; color: inherit; fill: none; stroke: currentColor; stroke-width: 1.75; stroke-linecap: round; stroke-linejoin: round; }
.i.sm { width: 16px; height: 16px; }
.i.lg { width: 24px; height: 24px; }

/* Screen-reader only text. */
.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }

/* Focus ring: visible on keyboard focus only; a native radio shows it on its label. */
:where(a, button, summary, input, select, textarea, [tabindex]):focus-visible { outline: 2px solid var(--red-aa); outline-offset: 2px; }
.seg label:has(input:focus-visible) { outline: 2px solid var(--red-aa); outline-offset: 2px; }

/* Tokens that never break mid-token: model codes, quantities, dates (write dates with a no-break space). */
.code, .qty, time { white-space: nowrap; }

/* Gutter: one content inset for header, title and lists on every app page. */
.app { --gutter: var(--space-8); --row-inset: var(--space-4); /* one inset for rows, their dividers and any table an app lines up with them */ display: grid; grid-template-columns: 248px minmax(0, 1fr); min-height: 100vh; }

/* Sidebar (768px and up): solid ground plane, icon plus label on every link. */
.side { position: sticky; top: 0; height: 100vh; display: flex; flex-direction: column; gap: var(--space-1); padding: var(--space-4) var(--space-3); background: var(--card); box-shadow: inset -1px 0 0 var(--border); }
.app-name { display: flex; align-items: center; gap: var(--space-2); min-height: 48px; padding: 0 var(--space-3); margin-bottom: var(--space-4); font-weight: var(--type-weight-semibold); font-size: 18px; line-height: 28px; letter-spacing: -0.01em; }
.app-name img { width: auto; height: 20px; } .app-name span { color: var(--ink-2); font-weight: var(--type-weight-medium); }
.side a { position: relative; display: flex; gap: var(--space-3); align-items: center; min-height: 48px; padding: 0 var(--space-3); border-radius: var(--radius); color: var(--ink); text-decoration: none; font-weight: var(--type-weight-medium); font-size: var(--type-role-ui-size); line-height: var(--type-role-ui-line); transition: background-color 120ms var(--ease); }
.side a .i { color: var(--ink-2); }
.count { margin-left: auto; color: var(--ink-2); font-size: 14px; line-height: 20px; font-variant-numeric: tabular-nums; }
.me { margin-top: auto; display: flex; align-items: center; gap: var(--space-3); padding: var(--space-3); font-size: 14px; line-height: 20px; }

/* Selection is neutral (ruling 1): container fill, 3px ink leading bar, weight 600, icon in ink. Never the brand tint. */
.side a[aria-current="page"], .row[aria-current="true"], .row[aria-selected="true"] { background: var(--container); }
.side a[aria-current="page"] { font-weight: var(--type-weight-semibold); }
.side a[aria-current="page"] .i, .row[aria-current="true"] .chev, .row[aria-selected="true"] .chev { color: var(--ink); }
.side a[aria-current="page"]::after, .row[aria-current="true"]::after, .row[aria-selected="true"]::after { content: ""; position: absolute; inset-block: var(--space-3); inset-inline-start: 0; width: 3px; border-radius: var(--radius-pill); background: var(--ink); }

/* Header: sticky, clear at rest, Liquid Glass once content scrolls under it (.under), full width with no radius. */
.top { position: sticky; top: 0; z-index: 3; padding-top: env(safe-area-inset-top); transition: background-color 200ms var(--ease), box-shadow 200ms var(--ease); }
.top.under { background: var(--glass); -webkit-backdrop-filter: var(--glass-filter); backdrop-filter: var(--glass-filter); box-shadow: var(--glass-highlight), 0 1px 0 var(--border); }
.top .in { display: flex; align-items: center; gap: var(--space-2); min-height: 64px; padding: var(--space-2) var(--gutter, var(--space-6)); }
.small-title { font-weight: var(--type-weight-semibold); font-size: var(--type-role-ui-size); line-height: var(--type-role-ui-line); visibility: hidden; opacity: 0; transition: opacity 200ms var(--ease), visibility 0s linear 200ms; }
.top.under .small-title { visibility: visible; opacity: 1; transition: opacity 200ms var(--ease); }
.icon-btn { display: inline-grid; place-items: center; width: 48px; height: 48px; margin-left: auto; padding: 0; border: 0; border-radius: var(--radius); background: none; color: var(--ink); text-decoration: none; }
.icon-btn + .icon-btn { margin-left: 0; } /* a group of header buttons sits together on the trailing side */

/* Segmented control from native radios: views, never actions; selected = card plus a 3:1 hairline and weight 600. */
.seg { display: inline-grid; grid-auto-flow: column; grid-auto-columns: 1fr; padding: var(--space-1); border-radius: var(--radius); background: var(--container); }
.seg label { position: relative; display: grid; place-items: center; min-height: 40px; padding: 0 var(--space-5); border-radius: var(--radius); color: var(--ink-2); font-weight: var(--type-weight-medium); font-size: var(--type-role-ui-size); line-height: var(--type-role-ui-line); cursor: pointer; transition: background-color 160ms var(--ease), color 160ms var(--ease); }
.seg input { position: absolute; inset: 0; margin: 0; opacity: 0; cursor: pointer; }
.seg label:has(input:checked) { background: var(--card); color: var(--ink); font-weight: var(--type-weight-semibold); box-shadow: 0 0 0 1px color-mix(in srgb, var(--ink-2) 70%, var(--card)), var(--shadow-sm); }

/* Inset grouped list: raised card, rows lead with a 40px slot, hairlines start after it. */
.group { list-style: none; margin: 0; padding: var(--space-2) 0; background: var(--card); border-radius: var(--radius-panel); box-shadow: var(--shadow-sm); }
.row { position: relative; display: grid; grid-template-columns: 40px minmax(0, 1fr) auto 16px; gap: var(--space-4); align-items: center; min-height: 72px; padding: var(--space-3) var(--row-inset, var(--space-4)); color: inherit; text-decoration: none; border-radius: var(--radius); transition: background-color 120ms var(--ease); }
.group li + li > .row::before { content: ""; position: absolute; top: 0; left: calc(var(--row-inset, var(--space-4)) + var(--space-4) + 40px); right: 0; height: 1px; background: var(--border); }
.group li:has(> .row[aria-current="true"]) > .row::before, .group li:has(> .row[aria-current="true"]) + li > .row::before { opacity: 0; }
.name { display: grid; min-width: 0; }
.name b { font-weight: var(--type-weight-semibold); font-size: 16px; line-height: 24px; overflow-wrap: break-word; }
.meta { display: flex; align-items: center; gap: var(--space-2); color: var(--ink-2); font-size: 14px; line-height: 20px; font-weight: var(--type-weight-regular); }
.chev { color: var(--ink-2); }

/* Thumbnail tile: product at about 80% of 40px; data-initials paint only when the image fails (Chrome, Firefox). */
.thumb { width: 40px; height: 40px; padding: 2px; border-radius: var(--radius); background: var(--container); object-fit: contain; }
@media (min-width: 768px) { .row[aria-current="true"] .thumb, .row[aria-selected="true"] .thumb { background: var(--card); } } /* the tile stays visible on a selected row; phone lists show no selection */
img[data-initials] { position: relative; }
img[data-initials]::before { content: ""; position: absolute; inset: 0; border-radius: inherit; background: var(--container); }
img[data-initials]::after { content: attr(data-initials); position: absolute; inset: 0; display: grid; place-items: center; border-radius: inherit; background: var(--container); color: var(--ink-2); font-size: 14px; line-height: 20px; font-weight: var(--type-weight-semibold); }

/* Monogram: warm initials for people, never a silhouette; .sm sits in a meta line. */
.monogram { display: inline-grid; place-items: center; width: 32px; height: 32px; border-radius: 50%; background: color-mix(in srgb, var(--amber) 16%, var(--card)); color: var(--ink); font-size: 14px; line-height: 20px; font-weight: var(--type-weight-semibold); flex: none; }
.monogram.sm { width: 24px; height: 24px; font-size: 11px; line-height: 16px; letter-spacing: 0.02em; } /* initials, not text: aria-hidden beside the name */

/* Status chips for exceptions only (Ready is the default, no chip): icon plus word, icons on the -text tokens for 3:1. */
.chip { display: inline-flex; align-items: center; gap: var(--space-1); justify-self: start; padding: var(--space-1) var(--space-2); border-radius: var(--radius-pill); background: var(--container); color: var(--ink); font-size: 14px; line-height: 20px; font-weight: var(--type-weight-medium); white-space: nowrap; }
.chip .i { color: var(--ink-2); }
.chip.up { background: color-mix(in srgb, var(--up) 12%, var(--card)); }
.chip.up .i { color: var(--color-semantic-up-text); }
.chip.amber { background: color-mix(in srgb, var(--amber) 16%, var(--card)); }
.chip.amber .i { color: var(--color-semantic-amber-text); }
.chip.info { background: color-mix(in srgb, var(--info) 10%, var(--card)); }
.chip.info .i { color: var(--color-semantic-info-text); }

/* Buttons: one filled primary per view, the rest outlined; 48px targets. */
.btn { display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2); min-height: 48px; padding: 0 var(--space-4); border: 1px solid var(--border); border-radius: var(--radius); background: var(--card); font-weight: var(--type-weight-semibold); font-size: var(--type-role-ui-size); line-height: var(--type-role-ui-line); transition: transform 120ms var(--ease), box-shadow 160ms var(--ease), background-color 160ms var(--ease); }
a.btn { color: inherit; text-decoration: none; } /* a link styled as a button never turns link blue */
.btn.primary { border-color: var(--ink); background: var(--ink); color: var(--card); }

/* Toast with Undo: role="status", the page keeps it 8s and pauses on hover or focus; sits above the phone tab bar. */
.toast { position: fixed; left: 50%; bottom: var(--space-8); z-index: 8; display: flex; align-items: center; gap: var(--space-3); max-width: calc(100vw - var(--space-8)); padding: var(--space-1) var(--space-1) var(--space-1) var(--space-5); translate: -50% 0; border-radius: var(--radius-pill); background: var(--card); box-shadow: var(--shadow-lg); font-weight: var(--type-weight-medium); font-size: var(--type-role-ui-size); line-height: var(--type-role-ui-line); }
.toast { width: max-content; } /* one line when it fits; the max-width above wraps a long one to two */
/* The toast sits above sheets and dialogs (z 6 and 7), so Undo is always visible and tappable after a verdict in a sheet. */
.toast > .i { color: var(--color-semantic-up-text); }
.toast [data-undo] { display: inline-flex; align-items: center; gap: var(--space-1); min-height: 48px; padding: 0 var(--space-4); border: 0; border-radius: var(--radius-pill); background: var(--container); font-weight: var(--type-weight-semibold); }
.toast:not([hidden]) { animation: shell-toast 200ms var(--ease); }
@keyframes shell-toast { from { opacity: 0; transform: translateY(8px); } }

/* Press response everywhere; hover only where a pointer can hover. */
.btn:active, .icon-btn:active, .toast [data-undo]:active { transform: scale(.97); }
.row:active { background: var(--container); }
.side a:active { background: color-mix(in srgb, var(--ink) 6%, var(--container)); }
@media (hover: hover) and (pointer: fine) {
  .btn:hover { box-shadow: var(--shadow-md); }
  .row:hover:not([aria-current="true"]):not([aria-selected="true"]), .side a:hover:not([aria-current="page"]) { background: color-mix(in srgb, var(--container) 60%, var(--card)); }
}

/* Switch: a real <button role="switch" aria-checked>, 44 by 24 drawn inside a 48px target; on is ink (red stays for alerts), off holds 3:1 on the card. */
.switch { position: relative; width: 44px; height: 24px; padding: 12px 2px; box-sizing: content-box; background-clip: content-box; border: 0; border-radius: var(--radius-pill); background-color: var(--border); transition: background-color 140ms var(--ease); }
.switch[aria-checked="true"] { background-color: var(--ink); } /* on is ink like the primary button; red stays for alerts */
.switch::after { content: ""; position: absolute; top: 14px; left: 4px; width: 20px; height: 20px; border-radius: 50%; background: var(--card); box-shadow: var(--shadow-sm); transition: transform 140ms var(--ease); }
.switch[aria-checked="true"]::after { transform: translateX(20px); }
.switch[aria-checked="false"]::after { box-shadow: 0 0 0 1px color-mix(in srgb, var(--ink-2) 70%, var(--card)), var(--shadow-sm); } /* off still reads at 3:1 on the card (E3), the segmented control's hairline */
.switch:active { transform: scale(.97); }

/* Shown only on phone (e.g. Settings moves from the tab bar to a header button). */
.phone-only { display: none; }

/* Phone: the sidebar becomes the floating Liquid Glass capsule tab bar; content scrolls under it. */
@media (max-width: 767px) {
  html { scroll-padding-bottom: calc(var(--space-24) + 72px + env(safe-area-inset-bottom)); } /* focus never hides under the tab bar or the toast above it (WCAG 2.4.11) */
  .app { --gutter: var(--space-4); --row-inset: var(--space-3); grid-template-columns: minmax(0, 1fr); padding-bottom: calc(var(--space-24) + env(safe-area-inset-bottom)); }
  .side { position: fixed; inset: auto var(--space-4) max(var(--space-4), env(safe-area-inset-bottom)); z-index: 4; height: auto; flex-direction: row; gap: 0; padding: var(--space-1); border-radius: calc(var(--radius-pill) + var(--space-1)); background: var(--glass); box-shadow: var(--glass-highlight), var(--shadow-md); -webkit-backdrop-filter: var(--glass-filter); backdrop-filter: var(--glass-filter); }
  .app-name, .me, .count, .side .desk-only { display: none; } /* four tabs at most on phone: five 14px labels do not fit 343px */
  .phone-only { display: inline-grid; }
  .side a { flex: 1 1 0; min-width: 0; flex-direction: column; justify-content: center; gap: 0; min-height: 48px; padding: 2px 0; border-radius: var(--radius-pill); font-size: 14px; line-height: 20px; letter-spacing: -0.01em; }
  .side a .i { width: 24px; height: 24px; }
  .side a[aria-current="page"] { background: color-mix(in srgb, var(--ink) 8%, transparent); }
  .side a[aria-current="page"]::after { content: none; }
  .top .in { min-height: 56px; padding-block: var(--space-1); }
  .seg { display: grid; }
  .seg label { min-height: 48px; padding: 0 var(--space-2); }
  .row { gap: var(--space-1) var(--space-3); padding-inline: var(--row-inset, var(--space-3)); }
  .group li + li > .row::before { left: calc(var(--row-inset, var(--space-3)) + var(--space-3) + 40px); }
  .toast { bottom: calc(var(--space-24) + env(safe-area-inset-bottom)); } /* just above the tab bar, in thumb reach of Undo; the scroll padding keeps the control just used clear of it (PE panel 2026-11-05) */
}

/* Reduced motion: end states, no travel. Glass goes solid through the tokens under reduced transparency or more contrast. */
@media (prefers-reduced-motion: reduce) {
  .toast:not([hidden]) { animation: none; }
  .top, .small-title, .side a, .row, .btn, .seg label, .switch, .switch::after { transition: none; }
}
