:root { color-scheme: light; }
* { box-sizing: border-box; }
@keyframes fadeIn { from { opacity:0 } to { opacity:1 } }
body { margin:0; font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif; background:var(--bg); color:var(--text); animation:fadeIn .18s ease-out; }
@media(prefers-reduced-motion:reduce){ body{animation:none;} .card{animation:none;} .skeleton{animation:none;} }
.wrap { max-width:920px; margin:0 auto; padding:0 16px 40px; }
.topbar { position:sticky; top:0; z-index:10; background:var(--bg-blur); backdrop-filter:saturate(180%) blur(8px); -webkit-backdrop-filter:saturate(180%) blur(8px); border-bottom:1px solid var(--line); padding:10px 16px; margin-bottom:16px; }
.topbar .inner { max-width:920px; margin:0 auto; }
.title { font-size:16px; font-weight:700; margin:0 0 8px; display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
.phase { display:inline-block; background:var(--accent); color:var(--accent-ink); font-size:10px; font-weight:600; padding:2px 7px; border-radius:999px; }
.nav { display:flex; gap:6px; flex-wrap:wrap; }
.nav a { font-size:13px; font-weight:600; text-decoration:none; color:var(--text); padding:10px 14px; border:1px solid var(--line-strong); border-radius:8px; background:var(--surface); }
.nav a.active { background:var(--text); color:var(--surface); border-color:var(--text); }
h3 { font-size:13px; text-transform:uppercase; letter-spacing:.04em; color:var(--text-3); margin:0 0 10px; }
.card { background:var(--surface); border:1px solid var(--line); border-radius:12px; padding:16px 18px; margin-bottom:16px; box-shadow:0 1px 2px rgba(0,0,0,.03); }
.good{color:var(--ok-fg);} .warn{color:var(--warn-fg);} .bad{color:var(--bad-fg);}
.muted{color:var(--text-3);}
.note { font-size:12px; color:var(--text-3); margin-top:8px; }
.grid { display:grid; grid-template-columns:repeat(auto-fit,minmax(120px,1fr)); gap:12px; }
.metric { background:var(--surface-2); border:1px solid var(--line); border-radius:10px; padding:12px; text-align:center; }
.metric .v { font-size:24px; font-weight:700; line-height:1.1; }
.metric .l { font-size:11px; color:var(--text-3); text-transform:uppercase; letter-spacing:.03em; margin-top:4px; }
.metric .n { font-size:11px; margin-top:3px; }
.two { display:grid; grid-template-columns:1fr 1fr; gap:16px; }
@media(max-width:680px){ .two{grid-template-columns:1fr;} }
canvas { max-height:230px; }
table { width:100%; border-collapse:collapse; font-size:13px; }
th,td { text-align:left; padding:7px 8px; border-bottom:1px solid var(--line); }
th { color:var(--text-3); font-weight:600; font-size:11px; text-transform:uppercase; }
[hidden]{ display:none !important; }

/* Skeleton loading */
@keyframes shimmer { to { background-position:-200% 0; } }
.skel-card { background:var(--surface); border:1px solid var(--line); border-radius:12px; padding:16px 18px; margin-bottom:16px; }
.skel-line { height:14px; margin-bottom:10px; border-radius:4px; }
.skel-line.short { width:40%; }
.skel-line.med { width:65%; }
.skel-chart { height:180px; margin-top:12px; border-radius:8px; }
.skel-grid { display:grid; grid-template-columns:repeat(auto-fit,minmax(120px,1fr)); gap:12px; margin-top:8px; }
.skel-box { height:70px; border-radius:10px; }
.skeleton { background:linear-gradient(90deg,var(--surface-3) 25%,var(--bg) 50%,var(--surface-3) 75%); background-size:200% 100%; animation:shimmer 1.5s infinite; }

/* Card entrance */
@keyframes cardIn { from { opacity:0; transform:translateY(8px); } to { opacity:1; transform:translateY(0); } }
.card { animation: cardIn .25s ease-out both; }

/* ============================================================================
   DESIGN TOKENS + SHARED COMPONENTS  (added 2026-08-11)
   WHY: dark mode is implemented in dock.js as `body.dark` overrides keyed on
   CLASS NAME. That means every new class a page invents is INVISIBLE to dark
   mode until someone remembers to register it — and the Gym/Tasks pages shipped
   ~30 unregistered classes, so they rendered as white cards on a black page.
   Tokens fix it at the root: anything built from var(--x) flips automatically
   and no page ever needs its own colour rules again.
   RULE FOR FUTURE PAGES: never hardcode a hex. Use a token or an existing class.

   2026-08-12: this file's OWN residual hexes are gone too — h3/th/.metric .l, the
   .good/.warn/.bad inks, the two hairline borders, the skeleton shimmer and the
   translucent topbar. Every one had a `body.dark` twin hand-written in dock.js, so
   the light value was the only one that lived here and the dark value could only be
   found by reading a second file. The three -fg tokens and --bg-blur below carry the
   values dock.js already forces, so nothing renders differently in either mode —
   dock.js just stops being load-bearing for the base sheet.
   ============================================================================ */
:root{
  --bg:#f5f6f8; --bg-blur:rgba(245,246,248,.92);
  --surface:#ffffff; --surface-2:#fafbfc; --surface-3:#f0f2f4;
  --line:#e6e8eb; --line-strong:#d7dadd;
  --text:#1c2024; --text-2:#454c54; --text-3:#6e767e;   /* 4.61:1 on --surface (was #727a82, 4.36 — under AA) */
  --accent:#1f6feb; --accent-ink:#ffffff;
  /* standalone status TEXT, not the chip inks: darker than a chip needs, lighter
     than -ink, and the value .good/.warn/.bad have always been */
  --ok-fg:#2da44e; --warn-fg:#b07d00; --bad-fg:#cf222e;
  --ok-bg:#dbf0e2; --ok-ink:#14612f;
  --warn-bg:#fdeccd; --warn-ink:#7a4a00;
  --bad-bg:#fbdcdc; --bad-ink:#8a2b2b;
  --info-bg:#d9e7fb; --info-ink:#134a9c;
  --alt-bg:#e6e0fa; --alt-ink:#4527a0;
  --callout-bg:#fff8e6; --callout-line:#f0dca8; --callout-ink:#5c4300;
}
body.dark{
  --bg:#000000; --bg-blur:rgba(0,0,0,.85);
  --surface:#16181c; --surface-2:#1d1f23; --surface-3:#22262b;
  --line:#2f3336; --line-strong:#3a3f44;
  --text:#e7e9ea; --text-2:#a8b0b8; --text-3:#7d8288;   /* 4.59:1 on --surface (was #71767b, 3.88 — under AA) */
  --accent:#1d9bf0; --accent-ink:#ffffff;
  --ok-fg:#3fb950; --warn-fg:#d9a935; --bad-fg:#f85149;
  --ok-bg:rgba(63,185,80,.15); --ok-ink:#3fb950;
  --warn-bg:rgba(217,169,53,.15); --warn-ink:#d9a935;
  --bad-bg:rgba(248,81,73,.15); --bad-ink:#f85149;
  --info-bg:rgba(29,155,240,.15); --info-ink:#5bb3f5;
  --alt-bg:rgba(139,116,240,.15); --alt-ink:#a992f5;
  --callout-bg:rgba(217,169,53,.10); --callout-line:rgba(217,169,53,.35); --callout-ink:#d9a935;
}

/* --- list row: a card-like item inside a card ------------------------------ */
.rowitem{ background:var(--surface); border:1px solid var(--line-strong); border-radius:13px;
  margin-bottom:12px; overflow:hidden; color:var(--text); }
.rowitem .rowtop{ display:flex; gap:12px; padding:13px; align-items:flex-start; }
.rowitem .rowbody{ flex:1 1 auto; min-width:0; }
.rowitem h4{ margin:0 0 5px; font-size:16px; font-weight:800; color:var(--text); line-height:1.25; }

/* --- chips ---------------------------------------------------------------- */
.chips{ display:flex; gap:5px; flex-wrap:wrap; margin-bottom:6px; }
.chip{ font-size:11px; font-weight:800; letter-spacing:.02em; padding:3px 8px; border-radius:20px;
  background:var(--surface-3); color:var(--text-2); }
.chip.ok{ background:var(--ok-bg); color:var(--ok-ink); }
.chip.warn{ background:var(--warn-bg); color:var(--warn-ink); }
.chip.bad{ background:var(--bad-bg); color:var(--bad-ink); }
.chip.info{ background:var(--info-bg); color:var(--info-ink); }
.chip.alt{ background:var(--alt-bg); color:var(--alt-ink); }

/* --- nav count badge ------------------------------------------------------
   2026-08-14, backlog item 128 (written without the hash on purpose: §7b(d)'s hex
   census greps `#[0-9a-fA-F]{3,6}` and an issue number would land in it as a false
   positive, which is a documented reading of that table). Derek, 2026-08-11: "the red badge count on the nav
   like an iPhone". ONE component, three surfaces — the phone dock's More button,
   the More-sheet row and the desktop rail row all append this same span, because
   dock.js builds all three from one table. TOKENS ONLY (§7b), so dark mode follows
   with no rule in dock.js; dock.js must never learn this class name. */
.navbadge{ display:inline-block; min-width:17px; padding:0 5px; margin-left:6px;
  font-size:10.5px; font-weight:800; line-height:17px; text-align:center;
  border-radius:999px; background:var(--bad-fg); color:var(--accent-ink);
  vertical-align:middle; }
/* On the dock the badge rides the icon instead of sitting in the line box: dock.js
   makes .ic display:block and the anchor 9px/nowrap, so an inline badge would grow
   the bar. Absolute keeps the bar exactly as tall as it was. `position` is NOT
   declared by dock.js's own injected `#dock a` rule, so this adds a property rather
   than fighting one. */
#dock a{ position:relative; }
#dock a .navbadge{ position:absolute; top:0; left:50%; margin-left:5px;
  min-width:15px; padding:0 4px; font-size:9.5px; line-height:15px; }
/* Rail: #rail a is a flex row, so the badge takes the trailing edge. Outside the
   1024px query on purpose, same reason as `#rail{display:none}` below — the node
   exists at every width and only the query makes it visible. */
#rail a .navbadge{ margin-left:auto; }
body.rail-min #rail a .navbadge{ margin-left:3px; }

/* --- key/value lines ------------------------------------------------------ */
.kv{ font-size:13.5px; color:var(--text); line-height:1.5; }
.kv .k{ color:var(--text-2); font-weight:700; }
.subtle{ font-size:13px; color:var(--text-2); line-height:1.5; margin-top:6px; }
.subtle.bad{ color:var(--bad-ink); font-weight:600; }

/* --- callout -------------------------------------------------------------- */
.callout{ margin:8px 0 0; padding:8px 10px; background:var(--callout-bg);
  border:1px solid var(--callout-line); border-radius:8px; font-size:13px; color:var(--callout-ink); }
.callout b{ display:block; font-size:11px; text-transform:uppercase; letter-spacing:.04em; margin-bottom:3px; }
.callout ul{ margin:0; padding-left:17px; } .callout li{ margin:2px 0; }

/* --- buttons -------------------------------------------------------------- */
.btn{ font:inherit; font-size:14px; font-weight:700; padding:9px 16px; border-radius:9px;
  border:1.5px solid var(--accent); background:var(--accent); color:var(--accent-ink); cursor:pointer; }
.btn.ghost{ background:var(--surface); color:var(--accent); }
.btn.sec{ background:var(--surface); color:var(--text-2); border-color:var(--line-strong); font-weight:700; }
.btn:disabled{ opacity:.6; cursor:default; }
.btnrow{ display:flex; gap:8px; flex-wrap:wrap; }

/* --- forms ---------------------------------------------------------------- */
.fields label{ display:block; font-size:11.5px; font-weight:800; text-transform:uppercase;
  letter-spacing:.04em; color:var(--text-2); margin:12px 0 4px; }
.fields label .opt{ font-weight:600; text-transform:none; letter-spacing:0; color:var(--text-3); }
.fields input[type=text], .fields textarea, .fields select{ width:100%; box-sizing:border-box;
  font:inherit; font-size:15px; padding:10px; border:1.5px solid var(--line-strong); border-radius:9px;
  color:var(--text); background:var(--surface); }
.fields textarea{ min-height:74px; resize:vertical; }
.fields .hint{ font-size:12.5px; color:var(--text-2); margin-top:4px; line-height:1.45; }
.formmsg{ font-size:13.5px; font-weight:700; margin-top:9px; }
.formmsg.ok{ color:var(--ok-ink); } .formmsg.err{ color:var(--bad-ink); }
/* The collapsed wrapper gym-edit.js emits. Moved here from gym.html on 2026-08-13 when
   gym-floor.html began opening the same form: a shared module's markup cannot depend on
   styles that live in one page's <style> block, or the form renders permanently open on
   every other page that uses it. */
.editwrap{ display:none; padding:0 13px 14px; border-top:1px solid var(--line); margin-top:2px; }
.editwrap.open{ display:block; }

/* --- misc ----------------------------------------------------------------- */
.thumb{ flex:0 0 auto; width:96px; height:96px; border-radius:9px; object-fit:cover;
  background:var(--surface-3); border:1px solid var(--line-strong); cursor:zoom-in; }
.thumb.none{ display:flex; align-items:center; justify-content:center; font-size:11px; font-weight:700;
  color:var(--bad-ink); background:var(--bad-bg); border-color:var(--callout-line); text-align:center; padding:6px; cursor:default; }
.sectionhead{ margin:20px 0 10px; }
.sectionhead h3{ margin:0; font-size:17px; text-transform:none; letter-spacing:0; font-weight:800; color:var(--text); }
.sectionhead .why{ font-size:13px; color:var(--text-2); line-height:1.5; margin-top:4px; }
.pillbar{ display:flex; gap:7px; flex-wrap:wrap; margin-top:10px; }
/* 34px (2026-08-17). Went to 44px for touch comfort and Derek called them too big — 13 of
   them is a lot of furniture above the list. 34px still clears the WCAG 2.5.8 floor (24x24)
   with room, and reads as a chip rather than a button. */
.pillbar button{ font:inherit; font-size:13px; font-weight:700; padding:7px 13px; border-radius:99px;
  min-height:34px; border:1.5px solid var(--line-strong); background:var(--surface);
  color:var(--text); cursor:pointer; }
.pillbar button:focus-visible{ outline:2px solid var(--accent); outline-offset:2px; }
.pillbar button.on{ background:var(--text); border-color:var(--text); color:var(--surface); }
.dashadd{ width:100%; font:inherit; font-size:14px; font-weight:700; padding:12px; border-radius:11px;
  border:2px dashed var(--line-strong); background:var(--surface-2); color:var(--accent);
  cursor:pointer; margin-bottom:16px; }

/* --- dark-mode specificity guard -------------------------------------------
   dock.js injects a BLANKET `body.dark button{background:#1d9bf0}` at runtime,
   and because it is injected after this stylesheet it beats any single-class
   button rule here (it turned the filter pills solid blue). These rules carry
   two classes so they win, and they are ADDITIVE — the blanket still applies to
   every unclassed button on the older pages, so nothing existing changes.
   --------------------------------------------------------------------------- */
body.dark .btn{ background:var(--accent); color:var(--accent-ink); border-color:var(--accent); }
body.dark .btn.ghost{ background:var(--surface); color:var(--accent); }
body.dark .btn.sec{ background:var(--surface); color:var(--text-2); border-color:var(--line-strong); }
body.dark .pillbar button{ background:var(--surface); color:var(--text); border-color:var(--line-strong); }
body.dark .pillbar button.on{ background:var(--text); color:var(--surface); border-color:var(--text); }
body.dark .dashadd{ background:var(--surface-2); color:var(--accent); border-color:var(--line-strong); }
body.dark .task .box{ background:var(--surface); }
body.dark .task .box.on{ background:var(--ok-ink); }
body.dark .task .box.pending{ background:var(--warn-ink); }
body.dark .thumb.none{ background:var(--bad-bg); color:var(--bad-ink); }

/* ============================================================================
   DESKTOP SHELL — left rail + wide layout (2026-08-13, Derek's widescreen ask)
   ----------------------------------------------------------------------------
   THE PROBLEM IT FIXES: every page was pinned to `.wrap{max-width:920px}` and the
   twelve-page `.nav` pill row wrapped to two lines, so a 1920px monitor showed
   ~48% glass with the top ~120px spent on navigation the dock already provides.
   The 920 was never a desktop decision — it is the right measure for PROSE, and
   it stays exactly that. Charts, tables and streak grids simply stop inheriting it.

   NOTHING BELOW 1024px CHANGES. The phone shell (dock, More sheet, hidden top nav)
   is untouched; every rule here is inside a min-width query and scoped to
   `body.has-rail`, a class dock.js only adds on a wide viewport. Colours are TOKENS
   (DATA-MAP §7b) so dark mode follows for free, and every selector carries an id or
   two classes so dock.js's injected `body.dark button{...}` blanket cannot win.
   ============================================================================ */
:root{ --rail-w:216px; --rail-w-min:64px; }
/* Hidden by default, OUTSIDE the query on purpose: dock.js only builds the rail on a wide
   viewport, but a desktop window dragged narrow keeps the node — without this it would
   fall back into the flow as a bare stack of links at the bottom of the page. */
#rail{ display:none; }

@media (min-width:1024px){
  body.has-rail{ padding-left:var(--rail-w); }
  body.has-rail.rail-min{ padding-left:var(--rail-w-min); }
  /* the rail IS the navigation up here — the pill row and the phone dock both go */
  body.has-rail .topbar .nav{ display:none; }
  body.has-rail #dock, body.has-rail #moreSheet, body.has-rail #moreShade{ display:none !important; }
  /* 1600 was measured, not guessed: at 1920 with the rail out it fills the glass and
     still leaves a margin; the old 920 left 48% of the monitor empty. */
  body.has-rail .wrap, body.has-rail .topbar .inner{ max-width:1600px; }

  #rail{ position:fixed; top:0; left:0; bottom:0; width:var(--rail-w); z-index:20;
    background:var(--surface); border-right:1px solid var(--line);
    display:flex; flex-direction:column; padding:10px 8px; overflow-y:auto; }
  body.rail-min #rail{ width:var(--rail-w-min); }
  #rail .railhead{ display:flex; align-items:center; justify-content:space-between; gap:8px;
    padding:6px 8px 12px; margin-bottom:4px; border-bottom:1px solid var(--line); }
  #rail .brand{ font-size:14px; font-weight:800; letter-spacing:.01em; color:var(--text); white-space:nowrap; }
  #railToggle{ font:inherit; font-size:14px; line-height:1; cursor:pointer; padding:6px 8px;
    border:1px solid var(--line-strong); border-radius:8px; background:var(--surface); color:var(--text-3); }
  #railToggle:hover{ color:var(--text); }
  #rail a{ display:flex; align-items:center; gap:10px; padding:9px 10px; margin-bottom:2px;
    font-size:13.5px; font-weight:600; text-decoration:none; color:var(--text-2);
    border-radius:8px; white-space:nowrap; }
  #rail a:hover{ background:var(--surface-3); color:var(--text); }
  #rail a.on{ background:var(--text); color:var(--surface); }
  #rail a .ic{ flex:0 0 22px; text-align:center; font-size:15px; }
  #rail .railfoot{ margin-top:auto; padding-top:10px; border-top:1px solid var(--line); }
  /* collapsed: icons only. The label is hidden, never removed, so it stays in the
     accessibility tree and one class flips the whole rail back. */
  body.rail-min #rail .lb, body.rail-min #rail .brand{ display:none; }
  body.rail-min #rail .railhead{ justify-content:center; }
  body.rail-min #rail a{ justify-content:center; padding:10px 0; }
}

/* Two columns — coaching prose left at a readable measure, data cards right.
   Held back to 1500px on purpose: below that the data column would be narrower
   than the charts want, and one wide column beats two cramped ones. */
@media (min-width:1500px){
  body.has-rail .colwrap{ display:grid; grid-template-columns:minmax(520px,640px) minmax(0,1fr);
    gap:24px; align-items:start; }
  /* The Desk is long (report card, commitments, question, thread). Sticky + its own
     scroll is what makes "both visible without scrolling" actually true — otherwise
     the data column just runs off the bottom of a very tall coaching column. */
  body.has-rail .col-coach{ position:sticky; top:84px; max-height:calc(100vh - 104px);
    overflow-y:auto; padding-right:4px; }
  body.has-rail .col-coach::-webkit-scrollbar{ width:8px; }
  body.has-rail .col-coach::-webkit-scrollbar-thumb{ background:var(--line-strong); border-radius:8px; }
  body.has-rail .col-data{ min-width:0; }
  /* the stat grid finally has room to breathe */
  body.has-rail .col-data .grid{ grid-template-columns:repeat(auto-fit,minmax(150px,1fr)); }
}
