/* ============================================================================
   Labs _shared.css — CANONICAL TOKENS, LOADED BY EVERY APP (since Jul 2026).
   ----------------------------------------------------------------------------
   Every page includes:   <link rel="stylesheet" href="/_shared.css">
   right before its own <style>. ACTIVE here: the token blocks + the tiny reset.
   Change a token here and it propagates everywhere on the next deploy — but
   note some apps keep deliberate local overrides in their own :root (marked
   "app-specific + overrides"); check those before assuming a token change took.

   COMPONENT RECIPES below sit inside `@media not all { }` — valid CSS that
   never applies, so nothing leaks into apps. They are the copy-paste reference
   for new apps (the scaffolder starter already carries them). Apps own their
   component CSS inline.

   Shared JS lives in /labs-common.js (pull-to-refresh); the theme-boot snippet
   stays inline in each <head> (must run before first paint) — copy it from the
   bottom of this file. Cache note: this file is in the launcher + app service
   worker SHELL lists (root, desk, fitness, briefing, signal-mapper) — bump
   those caches when you change it.
   ========================================================================== */

/* ---- Roboto Flex (self-hosted variable font, latin, wght 100-1000) ----
   34 KB woff2 at /fonts/, preloaded by each page + precached by the SW shells.
   Fallback chain keeps system-ui if it ever fails to load. */
@font-face {
  font-family: 'Roboto Flex';
  font-style: normal;
  font-weight: 100 1000;
  font-display: swap;
  src: url('/fonts/roboto-flex-latin-wght.woff2') format('woff2-variations');
}

/* ---- Material 3 tokens · light (the default) ---- */
:root {
  color-scheme: light;
  --surface: #FFFFFF; --surface-low: #FAF8F2; --surface-tonal: #F1EEE5; --surface-tonal-soft: #F6F3EB;
  --tonal-success: #E5F1EA; --tonal-warn: #F8EBD0; --tonal-danger: #F6DEDE; --tonal-info: #DFE9F4;
  --on-surface: #1A1A18; --on-surface-muted: #5F5E5A; --on-surface-soft: #8E8C85;
  --outline: rgba(0,0,0,0.06);
  --shadow-sm: 0 1px 2px rgba(0,0,0,0.04), 0 1px 1px rgba(0,0,0,0.03);
  --shadow-md: 0 2px 8px rgba(0,0,0,0.07), 0 1px 3px rgba(0,0,0,0.05);
  --shadow-lg: 0 4px 14px rgba(0,0,0,0.10), 0 2px 4px rgba(0,0,0,0.05);
  --radius-sm: 8px; --radius-md: 14px; --radius-lg: 20px; --radius-full: 999px;
  --good: #1F6A4A; --warn: #8B5A0E; --bad: #9B2828; --salt: #1F5FA0; --personal: #8B5A0E;
  --appbar: rgba(250,248,242,.86);
  /* motion — three verbs only (canon §11). Route transitions through these so timings
     stop being copy-pasted; press=tap, lift=hover-card, fade=modals/view-transitions. */
  --motion-press: .12s ease; --motion-lift: .15s ease; --motion-fade: .18s ease;
  /* density scale (canon §12) — default = regular. <html data-density> overrides below.
     Components opt in by using --density-pad / --density-gap for card padding + grid gap. */
  --density-pad: 14px; --density-gap: 10px; --density-font: 13.5px;
}
/* density moods — set by /settings (labs.density) via labs-common.js on <html> */
html[data-density="compact"] { --density-pad: 10px; --density-gap: 6px;  --density-font: 12.5px; }
html[data-density="roomy"]   { --density-pad: 20px; --density-gap: 16px; --density-font: 14.5px; }

/* ---- Dark tokens — activated by <html data-scheme="dark"> (theme boot sets it) ---- */
[data-scheme="dark"] {
  color-scheme: dark;
  --surface: #23221E; --surface-low: #161512; --surface-tonal: #2E2C27; --surface-tonal-soft: #292723;
  --tonal-success: #20352A; --tonal-warn: #3B301A; --tonal-danger: #3E2424; --tonal-info: #233241;
  --on-surface: #ECEAE4; --on-surface-muted: #ABA89F; --on-surface-soft: #807D75;
  --outline: rgba(255,255,255,0.08);
  --shadow-sm: 0 1px 2px rgba(0,0,0,0.35), 0 1px 1px rgba(0,0,0,0.25);
  --shadow-md: 0 2px 8px rgba(0,0,0,0.45), 0 1px 3px rgba(0,0,0,0.35);
  --shadow-lg: 0 4px 14px rgba(0,0,0,0.55), 0 2px 4px rgba(0,0,0,0.40);
  --good: #6FBF97; --warn: #D8A557; --bad: #E08A8A; --salt: #7FB1E0; --personal: #D8A557;
  --appbar: rgba(22,21,18,.86);
}

/* ---- cross-document view transitions (Safari 26+/Chrome 126+) ----
   Same-origin page navigations (launcher <-> apps) crossfade instead of flashing.
   Pages that don't link this file (codegen, winner-qr) simply don't participate. */
@view-transition { navigation: auto; }
::view-transition-old(root), ::view-transition-new(root) { animation-duration: .18s; }

/* ---- reset (active) ---- */
* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }


/* ---- app shell v2 (ACTIVE) — appbar + large-title collapse ----
   Markup: <div class="appbar"><div class="appbar-top">
             <a class="back" href="/" aria-label="Labs home"><svg…chevron…></svg></a>
             <h1>Title</h1>
             <div class="actions">…0-2 icon buttons…</div>  (or <span class="appbar-spacer">)
           </div>(optional second row: segs / meta-line)</div>
   Content starts with <h1 class="page-title" data-large-title>Title</h1>.
   labs-common.js adds .scrolled (elevation), .lt-ready (a large title exists) and
   .lt-collapsed (large title scrolled out -> bar title fades in).
   Apps should NOT re-style .appbar locally — extend via .actions contents only. */
.appbar { position: sticky; top: 0; z-index: 20; background: transparent;
  padding: calc(env(safe-area-inset-top) + 8px) 12px 8px;
  transition: background .2s ease, box-shadow .2s ease; }
.appbar.scrolled { background: var(--appbar);
  backdrop-filter: saturate(180%) blur(12px); -webkit-backdrop-filter: saturate(180%) blur(12px);
  box-shadow: 0 1px 0 var(--outline); }
.appbar-top { display: flex; align-items: center; gap: 6px; min-height: 40px; }
.appbar a.back { flex: 0 0 auto; width: 40px; height: 40px; display: flex; align-items: center;
  justify-content: center; border-radius: var(--radius-full); color: var(--on-surface-muted);
  text-decoration: none; transition: background var(--motion-lift), transform var(--motion-press); }
.appbar a.back:active { background: var(--surface-tonal); transform: scale(.92); }
.appbar a.back svg { width: 22px; height: 22px; stroke: currentColor; fill: none;
  stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; display: block; }
.appbar h1 { flex: 1; min-width: 0; font-size: 17px; font-weight: 500; margin: 0;
  text-align: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  transition: opacity .18s ease, transform .18s ease; }
/* hidden-until-collapse is OPT-IN: labs-common.js adds .lt-ready only when a
   [data-large-title] exists — so pages without the script keep a visible title */
.appbar.lt-ready:not(.lt-collapsed) h1 { opacity: 0; transform: translateY(4px); }
.appbar .actions { flex: 0 0 auto; display: flex; gap: 4px; min-width: 40px; justify-content: flex-end; }
.appbar .actions a, .appbar .actions button { width: 40px; height: 40px; display: flex;
  align-items: center; justify-content: center; border-radius: var(--radius-full);
  border: none; background: transparent; color: var(--on-surface-muted); cursor: pointer;
  text-decoration: none; font: inherit; transition: background var(--motion-lift), transform var(--motion-press); }
.appbar .actions a:active, .appbar .actions button:active { background: var(--surface-tonal); transform: scale(.92); }
.appbar .actions svg { width: 22px; height: 22px; stroke: currentColor; fill: none;
  stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; display: block; }
.appbar .appbar-spacer { flex: 0 0 auto; width: 40px; }
.page-title { font-size: 26px; font-weight: 500; letter-spacing: -0.01em; margin: 2px 0 2px; }
/* Wide screens (desktop browser): the in-content large title reads as dead space there —
   show the classic in-bar title instead. Phones keep the large-title collapse pattern. */
@media (min-width: 700px) {
  .page-title[data-large-title] { display: none; }
  .page-title[data-large-title] + .page-sub { display: none; }
  .appbar.lt-ready:not(.lt-collapsed) h1 { opacity: 1; transform: none; }
}
.page-sub { font-size: 13px; color: var(--on-surface-muted); margin: 0 0 14px; }
:focus-visible { outline: 2px solid var(--salt); outline-offset: 2px; }


/* ---- appbar v2.3 — scroll-LINKED collapsing bar (.appbar.lt), bar tracks its text ----
   One custom property --lt (0 = expanded, 1 = collapsed) drives everything:
   bar padding, title size, title weight (Roboto Flex variable axis — slight
   optical thickening as it shrinks), and the blur+hairline layer's opacity.
   Driven natively by CSS scroll-driven animation over the first 64px of scroll
   (Safari 26+/Chrome 115+); labs-common.js provides an identical rAF fallback
   for older engines. Desktop (≥700px) pins collapsed. */
@property --lt { syntax: "<number>"; inherits: true; initial-value: 0; }
@keyframes lt-collapse { to { --lt: 1; } }
.appbar.lt { background: transparent; box-shadow: none;
  padding: calc(env(safe-area-inset-top) + 6px + 8px * (1 - var(--lt, 0))) 16px
           calc(6px + 8px * (1 - var(--lt, 0))); }
@supports (animation-timeline: scroll()) {
  .appbar.lt { animation: lt-collapse linear both; animation-timeline: scroll();
    animation-range: 0px 64px; }
}
.appbar.lt::before { content: ""; position: absolute; inset: 0; z-index: -1;
  background: var(--appbar);
  backdrop-filter: saturate(180%) blur(12px); -webkit-backdrop-filter: saturate(180%) blur(12px);
  box-shadow: 0 1px 0 var(--outline); opacity: var(--lt, 0); }
.appbar.lt.scrolled { background: transparent; box-shadow: none;
  backdrop-filter: none; -webkit-backdrop-filter: none; }
.appbar.lt h1 { opacity: 1; transform: none; text-align: left;
  font-size: calc(17px + 9px * (1 - var(--lt, 0)));
  font-variation-settings: "wght" calc(500 + 60 * var(--lt, 0));
  letter-spacing: calc(-0.01em * (1 - var(--lt, 0))); }
/* the whole ROW tracks the text: row floor 40->32px, controls 40->32px,
   icons 22->18px — collapsed bar ≈ 44px + safe-area (iOS compact-bar height) */
.appbar.lt .appbar-top { min-height: calc(40px - 8px * var(--lt, 0)); }
.appbar.lt a.back,
.appbar.lt .appbar-top > button,
.appbar.lt .actions a, .appbar.lt .actions button {
  width: calc(40px - 8px * var(--lt, 0));
  height: calc(40px - 8px * var(--lt, 0));
  font-size: calc(17px - 3px * var(--lt, 0)); }
.appbar.lt .appbar-top svg {
  width: calc(22px - 4px * var(--lt, 0));
  height: calc(22px - 4px * var(--lt, 0)); }
.appbar.lt .appbar-spacer { display: none; }
@media (min-width: 700px) { .appbar.lt { --lt: 1; animation: none; } }

/* ---- Desktop bar refinements (2026-07-13, pencil notes #58/#60) ----
   #58: the action cluster (reload etc.) sat 8px off the screen edge on
   desktop; the bar row gets a touch more headroom there. Phones keep the
   safe-area spacing and are untouched.
   #60: a .meta-line joins the bar row RIGHT OF THE TITLE on desktop (no
   second row). Pages without a meta-line keep the classic layout. */
@media (min-width: 700px) {
  .appbar { padding-top: 16px; }
  .appbar:has(> .meta-line) { display: grid;
    grid-template-columns: auto auto minmax(0, 1fr) auto;
    align-items: center; column-gap: 12px; }
  .appbar:has(> .meta-line) .appbar-top { display: contents; }
  .appbar:has(> .meta-line) h1 { text-align: left; flex: none; }
  .appbar:has(> .meta-line) > .meta-line { order: 3; margin-top: 0;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .appbar:has(> .meta-line) .actions,
  .appbar:has(> .meta-line) .appbar-spacer,
  .appbar:has(> .meta-line) .appbar-top > button { order: 4; }
}

/* ---- COMPONENT RECIPES — reference only (never applied; copy inner rules) ---- */
@media not all {
/* ---- base ---- */
* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
html, body { margin: 0; }
body {
  font-family: "Roboto Flex","Google Sans","Roboto",-apple-system,BlinkMacSystemFont,system-ui,sans-serif;
  background: var(--surface-low); color: var(--on-surface); font-size: 15px; line-height: 1.5;
  -webkit-font-smoothing: antialiased; padding-bottom: env(safe-area-inset-bottom);
}

/* ---- sticky app bar (with "‹ Labs" back link) ---- */
.appbar { position: sticky; top: 0; z-index: 20; background: var(--appbar);
  backdrop-filter: saturate(180%) blur(12px); -webkit-backdrop-filter: saturate(180%) blur(12px);
  border-bottom: 1px solid var(--outline); padding: calc(env(safe-area-inset-top) + 10px) 16px 10px; }
.appbar-top { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.appbar a.back { text-decoration: none; color: var(--on-surface-muted); font-size: 14px; }
.appbar h1 { font-size: 20px; font-weight: 500; margin: 0; }
.meta-line { font-size: 12px; color: var(--on-surface-soft); margin-top: 4px; font-variant-numeric: tabular-nums; }

/* ---- layout + cards ---- */
.wrap { max-width: 640px; margin: 0 auto; padding: 16px 16px 48px; }
.section { margin-bottom: 22px; }
.section-eyebrow { font-size: 11px; text-transform: uppercase; letter-spacing: .7px;
  color: var(--on-surface-muted); margin-bottom: 6px; }
.section-title { font-size: 17px; font-weight: 500; margin: 0 0 12px; }
.card { background: var(--surface); border-radius: var(--radius-md); padding: 16px 18px; box-shadow: var(--shadow-sm); }
.stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; }
.stat-card { background: var(--surface-tonal-soft); border-radius: var(--radius-md); padding: 14px 16px; }

/* ---- segmented control ---- */
.seg { display: inline-flex; gap: 3px; background: var(--surface-tonal); border-radius: var(--radius-full); padding: 3px; }
.seg button { font: inherit; font-size: 12.5px; font-weight: 500; border: none; background: transparent;
  color: var(--on-surface-muted); padding: 6px 12px; border-radius: var(--radius-full); cursor: pointer; }
.seg button.active { background: var(--surface); color: var(--on-surface); box-shadow: var(--shadow-sm); }

/* ---- pills / stale flag ---- */
.pill { display: inline-block; font-size: 11px; padding: 3px 10px; border-radius: var(--radius-full);
  background: var(--surface-tonal); color: var(--on-surface-muted); font-weight: 500; }
.stale { color: var(--bad); font-weight: 500; }
}

/* ============================================================================
   THEME BOOT — paste this <script> into <head> (after the theme-color meta):

   <script>
   (function(){
     var mq = matchMedia('(prefers-color-scheme: dark)');
     function apply(){
       var t = 'system'; try { t = localStorage.getItem('labs.theme') || 'system'; } catch(e){}
       var dark = t === 'dark' || (t === 'system' && mq.matches);
       document.documentElement.setAttribute('data-scheme', dark ? 'dark' : 'light');
       var m = document.querySelector('meta[name="theme-color"]');
       if (m) m.setAttribute('content', dark ? '#161512' : '#FAF8F2');
     }
     apply();
     mq.addEventListener('change', apply);
     window.addEventListener('storage', apply);
     window.addEventListener('pageshow', apply);
     window.__labsTheme = apply;
   })();
   </script>
   ========================================================================== */
