/* ── Floe design system ─────────────────────────────────────────
   Warm editorial palette from the Claude Design "Floe" spec.
   Fonts (Space Grotesk / Manrope) load from Google Fonts when online;
   offline they degrade to system-ui — everything else is self-contained. */
:root {
  --paper:#F7F5EF; --card:#FCFBF8; --line:#E4E0D3; --line-soft:#EDEAE0;
  --ink:#1E2420; --ink-2:#4C4738; --muted:#726D5E; --muted-2:#8B8677;
  --faint:#ADA894; --faint-2:#C7C2B2;
  --green:#2B5940; --green-d:#1E4530; --green-tint:#E1EBE3; --green-bd:#BFDAC7; --green-soft:#C3D9C8;
  --blue:#2E4A66; --blue-tint:#E1E8EF; --blue-bd:#C2D2E0;
  --brown:#8A5A3B; --brown-tint:#F1E5DB; --brown-bd:#E0C7B3;
  --swim:#2E6E7E; --swim-tint:#DEECEE; --swim-bd:#BEDCE1;
  --sand:#7D7869; --sand-tint:#EDEBE3;
  /* semantic surface tokens — the only things dark mode redefines beyond the
     palette above; components read these instead of literal #fff / #F4F8F5. */
  --surface:#fff;          /* raised canvas: day/month cells, inputs, icon chips */
  --today:#F4F8F5;         /* the "today" cell tint */
  --today-ring:rgba(43,89,64,.12);
  --seg-on:#fff;           /* active pill in the segmented control */
  --shadow:rgba(30,36,32,.08);
  --pill-bg:rgba(0,0,0,.05);   /* neutral TSS chip */
  --on-accent:#fff;        /* text/icon sitting on a saturated fill */
  --danger:#B33;           /* out-of-band compliance */
  --donut-track:#E7E3D6;   /* Form donut + day ring backing */
  --logo-belly:#FCFBF8;    /* penguin belly */
  --coach-bg:#F3E3C6; --coach-ink:#7A5A1E; --coach-bd:#E3CB9A;  /* "Coaching" pill (two-user build) */
  --map-line:#2B5940; --map-start:#0ca30c; --map-end:#8A5A3B;  /* Leaflet (JS reads these) */
  --disp:'Space Grotesk', system-ui, sans-serif;
  --sans:'Manrope', system-ui, -apple-system, sans-serif;
  color-scheme:light;
}
/* ── Graphite dark ───────────────────────────────────────────────
   Floe's forest green is already dark, so on a dark ground the accent is
   *lifted* to a muted sage and the pale sport tints become deep tints — same
   brand at night, not an inversion. Set explicitly via data-theme (an inline
   <head> script resolves the stored choice / OS preference before first paint). */
:root[data-theme="dark"] {
  --paper:#17181A; --card:#1E2022; --line:#2E3033; --line-soft:#26282B;
  --ink:#E9E9E5; --ink-2:#C3C2BB; --muted:#95948C; --muted-2:#7E7D75;
  --faint:#66655E; --faint-2:#4B4A45;
  --green:#67987E; --green-d:#83B39A; --green-tint:#1E2C24; --green-bd:#34503F; --green-soft:#3C6350;
  --blue:#7FA3C4; --blue-tint:#1B2733; --blue-bd:#33465A;
  --brown:#C08E68; --brown-tint:#2C2117; --brown-bd:#4A3826;
  --swim:#6BAAB6; --swim-tint:#152A2E; --swim-bd:#2C474C;
  --sand:#A8A292; --sand-tint:#26241C;
  --surface:#24262A; --today:#1C241F; --today-ring:rgba(131,179,154,.16);
  --seg-on:#31343A; --shadow:rgba(0,0,0,.4); --pill-bg:rgba(255,255,255,.07);
  --on-accent:#14150F; --danger:#E08A82; --donut-track:#2E332C; --logo-belly:#E9E9E5;
  --coach-bg:#2E2413; --coach-ink:#E3C892; --coach-bd:#4A3B1E;
  --map-line:#67987E; --map-start:#5FB65F; --map-end:#C08E68;
  color-scheme:dark;
}
* { box-sizing:border-box; }
body { margin:0; background:var(--paper); color:var(--ink); font-family:var(--sans); font-size:14px; }
a { color:var(--green); text-decoration:none; }
a:hover { color:var(--green-d); }
button { font-family:inherit; cursor:pointer; }
/* page-level inputs (builder, settings, login) have inline backgrounds but no colour —
   without this they fall back to dark system text, unreadable on the dark surface. */
input, select, textarea { color:var(--ink); }
.mono { font-variant-numeric:tabular-nums; }

/* nav */
/* #nav wraps .nav and is only as tall as the bar (64px); display:contents dissolves that
   wrapper so the sticky .nav is bound to <body> (the page scroller) and stays pinned for the
   whole scroll instead of unsticking after 64px. z-index:900 clears every page-content layer
   (highest is .map-expand at 500); modal <dialog>s open with showModal(), so they live in the
   top layer and sit above the nav regardless of z-index. */
#nav { display:contents; }
.nav { background:var(--card); border-bottom:1px solid var(--line); display:flex; position:sticky; top:0; z-index:900;
  align-items:center; justify-content:space-between; padding:0 32px; height:64px; }
.nav-left { display:flex; align-items:center; gap:30px; min-width:0; }
.brand { display:flex; align-items:center; gap:9px; }
.brand span { font:700 19px var(--disp); color:var(--ink); letter-spacing:-.01em; }
/* Report-a-bug shortcut: centred to the whole bar, independent of the left/right
   clusters — desktop-width only (hidden below the desktop breakpoint, where the
   cal-view cluster would otherwise reach it). Shows in the installed app too, not just
   a browser tab — the "hidden in standalone" version turned out unwanted in practice. */
.nav-reportbug { position:absolute; left:50%; top:50%; transform:translate(-50%,-50%); white-space:nowrap;
  display:flex; align-items:center; gap:7px; border:none; border-radius:999px; cursor:pointer;
  background:#E8622C; color:#fff; font:700 12.5px var(--sans); letter-spacing:.01em; padding:8px 16px;
  box-shadow:0 2px 8px rgba(232,98,44,.35); transition:filter .12s ease; }
.nav-reportbug:hover { filter:brightness(1.08); }
.nav-reportbug svg { width:15px; height:15px; flex:none; }
@media (max-width:1180px) { .nav-reportbug { display:none; } }
.seg { display:flex; background:var(--line-soft); border-radius:8px; padding:3px; gap:2px; }
.seg a { font:600 12px var(--sans); padding:7px 15px; border-radius:6px; color:var(--muted-2); }
.seg a.on { background:var(--seg-on); color:var(--ink); box-shadow:0 1px 2px var(--shadow); }
.nav a.tab { font:600 13px var(--sans); color:var(--muted); }
.nav a.tab.on { color:var(--green); }
.nav-right { display:flex; align-items:center; gap:18px; }
.nav-right button { background:none; border:none; color:var(--muted); display:flex; align-items:center; gap:6px;
  font:600 12.5px var(--sans); padding:6px 8px; border-radius:6px; }
.nav-right button:hover { background:var(--line-soft); color:var(--ink); }
.avatar { width:29px; height:29px; border-radius:7px; background:var(--green-tint); color:var(--green);
  display:inline-flex; align-items:center; justify-content:center; font:700 12px var(--disp); overflow:hidden; cursor:default; }
/* in-burger dark-mode switch — a small pill that mirrors the nav toggle */
.menu-toggle { justify-content:space-between; gap:12px; }
.menu-sw { margin-left:auto; position:relative; width:34px; height:20px; flex:none; border-radius:999px;
  background:var(--line); transition:background-color .18s ease; }
.menu-sw-thumb { position:absolute; top:3px; left:3px; width:14px; height:14px; border-radius:50%;
  background:var(--surface); box-shadow:0 1px 2px rgba(0,0,0,.25); transition:transform .2s cubic-bezier(.34,1.35,.5,1); }
.menu-sw.on { background:var(--green); }
.menu-sw.on .menu-sw-thumb { transform:translateX(14px); }

/* burger menu — the parked utilities (ATP / Sync / Upload / Backup / Settings) */
.navmenu { position:relative; display:inline-flex; }
.menu-btn { background:none; border:none; color:var(--muted); display:inline-flex; align-items:center;
  padding:6px 7px; border-radius:6px; }
.menu-btn:hover, .menu-btn[aria-expanded="true"] { background:var(--line-soft); color:var(--ink); }
.menu-btn.on { color:var(--green); }
/* calendar-icon shortcut in the bar (sized like the burger) → the calendar (#/week) */
.navcal { display:inline-flex; align-items:center; color:var(--muted); padding:6px 7px; border-radius:6px; }
.navcal:hover { background:var(--line-soft); color:var(--ink); }
.navcal.on { color:var(--green); }
.menu { position:absolute; top:calc(100% + 8px); left:0; z-index:60; min-width:190px;
  background:var(--card); border:1px solid var(--line); border-radius:10px;
  box-shadow:0 10px 30px var(--shadow); padding:6px; display:flex; flex-direction:column; }
.menu[hidden] { display:none; }
.menu-item { display:flex; align-items:center; width:100%; text-align:left; background:none; border:none;
  padding:9px 12px; border-radius:7px; font:600 13px var(--sans); color:var(--ink); }
.menu-item:hover, .menu-item:focus-visible { background:var(--line-soft); color:var(--ink); outline:none; }
.menu-item.on { color:var(--green); }
/* Same treatment as the desktop nav's centred Report-a-bug pill (.nav-reportbug) —
   orange box, pill shape, bug icon — so the burger-menu entry (the only place this
   lives on mobile, where the nav pill has no room) reads as the same feature. */
.menu-item-reportbug { background:#E8622C; color:#fff; border-radius:999px; gap:8px; margin:3px 0; }
.menu-item-reportbug svg { width:15px; height:15px; flex:none; }
.menu-item-reportbug:hover, .menu-item-reportbug:focus-visible { background:#d5551f; color:#fff; }
.menu-sep { height:1px; background:var(--line); margin:5px 8px; }

/* ── workout card ⋮ menu + paste mode (#54 resurfaced) ──────
   The menu is appended to <body> and fixed-positioned rather than absolute: on phones
   .day-col sets overflow:hidden, so a menu rendered inside the column would be clipped.
   Below 640px it drops to a bottom sheet, same as the tour popover. */
/* The ⋮ is absolutely positioned so it costs the title no width. A week column is only
   ~130px wide: giving the button its own place in the .top flex row squeezed long titles
   until they broke mid-word ("En du ran ce rid e"). Out of flow, the card is laid out
   exactly as it was before the menu existed.
   On desktop it fades in on hover — the columns are too cramped to carry a permanent
   button, and right-click opens the same menu anyway. Touch has no hover, so below 760px
   it's always visible; there the week is an accordion and cards are full-width. */
.act { position:relative; }
.act .kebab {
  position:absolute; top:4px; right:3px; width:22px; height:22px; padding:0; border:none;
  background:none; border-radius:6px; color:var(--muted-2); cursor:pointer; font:700 15px/1 var(--sans);
  display:inline-flex; align-items:center; justify-content:center; }
@media (hover:hover) and (min-width:761px) {
  .act .kebab { opacity:0; transition:opacity .12s ease; }
  .act:hover .kebab, .act .kebab:focus-visible, .act .kebab[aria-expanded="true"] { opacity:1; }
  /* a revealed button sits over the title's top-right corner — give it a ground so a long
     title stays legible underneath */
  .act:hover .kebab { background:var(--pill-bg); }
}
.act .kebab:hover, .act .kebab:focus-visible, .act .kebab[aria-expanded="true"],
.pcard .kebab:hover, .pcard .kebab:focus-visible, .pcard .kebab[aria-expanded="true"] {
  background:var(--line-soft); color:var(--ink); outline:none; }
/* day view: the header row already carries pair/View/edit/push, so the ⋮ sits in flow at
   the end of it rather than overlapping them in the corner. */
.pcard .kebab { position:static; flex:none; margin-left:2px; }
.mchip[data-id] { cursor:pointer; }
.mchip[data-id]:hover { outline:1.5px solid var(--green-bd); outline-offset:-1px; }
.card-menu { position:fixed; top:0; left:0; z-index:100001; min-width:210px; }
/* a done-state row (e.g. "On Garmin ✓") — present so the card can answer "did this get
   there?", but not a button; it must not invite a click or look broken */
.menu-item:disabled { color:var(--muted-2); cursor:default; }
.menu-item:disabled:hover { background:none; color:var(--muted-2); }
.menu-item-danger { color:var(--brown); }
.menu-item-danger:hover, .menu-item-danger:focus-visible { background:var(--line-soft); color:var(--brown); }

/* paste mode: the calendar becomes a day picker. Cards go pointer-events:none so a tap
   anywhere in a day lands on the column — the day is the target, not the workout. */
.paste-bar { position:fixed; z-index:100002; left:50%; transform:translateX(-50%); bottom:18px;
  display:flex; align-items:center; gap:12px; max-width:calc(100vw - 24px);
  padding:9px 10px 9px 16px; border-radius:999px; background:var(--card);
  border:1px solid var(--line); box-shadow:0 12px 34px var(--shadow); }
.paste-bar[hidden] { display:none; }
.paste-what { font:600 13px var(--sans); color:var(--ink); min-width:0; overflow:hidden;
  text-overflow:ellipsis; white-space:nowrap; }
.paste-hint { font:500 12px var(--sans); color:var(--muted-2); flex:none; }
.paste-cancel { flex:none; border:1px solid var(--line); background:var(--surface); color:var(--ink);
  border-radius:999px; padding:6px 14px; font:600 12px var(--sans); cursor:pointer; }
.paste-cancel:hover { background:var(--line-soft); }
body.pasting .day-col, body.pasting .mcell { outline:2px dashed var(--green-bd); outline-offset:-2px; cursor:copy; }
body.pasting .day-col:hover, body.pasting .mcell:hover { outline-style:solid; outline-color:var(--green); background:var(--today); }
body.pasting .act, body.pasting .ditem, body.pasting .mchip { pointer-events:none; }

@media (max-width:640px) {
  .card-menu { left:12px !important; right:12px !important; bottom:14px !important; top:auto !important;
    width:auto; transform:none !important; }
  .card-menu .menu-item { padding:12px 14px; }        /* comfortable tap targets in the sheet */
  .paste-bar { left:12px; right:12px; transform:none; max-width:none; }
}

/* ── notifications: bell + unread badge + inbox popover + settings matrix (issue #7) ── */
.notifwrap { position:relative; display:inline-flex; }
.nav-right button.notifbell { position:relative; padding:6px 7px; color:var(--muted); }
.nav-right button.notifbell:hover, .nav-right button.notifbell[aria-expanded="true"] { background:var(--line-soft); color:var(--ink); }
.notif-badge { position:absolute; top:-1px; right:-1px; box-sizing:border-box; min-width:16px; height:16px; padding:0 4px;
  background:var(--brown); color:var(--on-accent); border-radius:999px; font:700 9.5px var(--sans); line-height:16px;
  text-align:center; pointer-events:none; box-shadow:0 0 0 2px var(--card); }
.notif-badge[hidden] { display:none; }
.notif-pop { left:auto; right:0; min-width:322px; max-width:360px; padding:0; overflow:hidden; }
.notif-head { display:flex; align-items:center; justify-content:space-between; gap:10px; padding:11px 14px;
  border-bottom:1px solid var(--line-soft); font:700 10.5px var(--sans); letter-spacing:.06em; text-transform:uppercase; color:var(--muted-2); }
.notif-head button { background:none; border:none; padding:0; cursor:pointer; color:var(--green);
  font:600 11px var(--sans); letter-spacing:0; text-transform:none; }
.notif-head button:hover { text-decoration:underline; }
.notif-list { max-height:min(60vh,430px); overflow-y:auto; }
.notif-item { display:flex; gap:10px; align-items:flex-start; padding:11px 14px; border-top:1px solid var(--line-soft); color:var(--ink); }
.notif-item:first-child { border-top:none; }
.notif-item:hover, .notif-item.unread:hover { background:var(--line-soft); }
.notif-item.unread { background:var(--green-tint); }
.notif-dot { width:7px; height:7px; border-radius:50%; margin-top:5px; flex:none; background:transparent; }
.notif-item.unread .notif-dot { background:var(--green); }
.notif-body { min-width:0; display:flex; flex-direction:column; gap:2px; }
.notif-text { font:500 13px var(--sans); line-height:1.42; color:var(--ink); }
.notif-item.unread .notif-text { font-weight:600; }
.notif-time { font:500 11px var(--sans); color:var(--faint); }
.notif-empty { padding:26px 14px; text-align:center; font:500 12.5px var(--sans); color:var(--faint); }
/* mute-everything switch (#113) — sits under the list so it never competes with the
   notifications themselves for attention */
.notif-foot { border-top:1px solid var(--line-soft); }
.notif-mute { display:flex; align-items:center; justify-content:space-between; gap:10px; width:100%;
  padding:10px 14px; background:none; border:none; cursor:pointer; text-align:left;
  font:500 12px var(--sans); color:var(--muted-2); }
.notif-mute:hover { background:var(--line-soft); }
.notif-mute[aria-checked="true"] { color:var(--ink-2); font-weight:600; }

/* Settings › Notifications matrix */
.notif-matrix { display:flex; flex-direction:column; margin-top:6px; }
.notif-mrow { display:grid; grid-template-columns:1fr 74px 74px 74px; align-items:center; gap:10px; padding:13px 0; border-top:1px solid var(--line-soft); }
.notif-mrow.notif-mhead { border-top:none; padding:0 0 4px; font:700 10px var(--sans); letter-spacing:.05em; text-transform:uppercase; color:var(--muted-2); }
.notif-mrow > span:not(.notif-mlabel) { justify-self:center; display:inline-flex; }
.notif-mhead span:not(:first-child) { justify-self:center; }
.notif-mlabel { display:flex; flex-direction:column; gap:2px; min-width:0; }
.notif-mlabel b { font:600 13px var(--sans); color:var(--ink); }
.notif-mlabel em { font:500 11.5px var(--sans); font-style:normal; color:var(--muted-2); }

/* the in-app on/off switch (the email column reuses it, disabled) */
.nswitch { position:relative; display:inline-block; width:38px; height:22px; flex:none; }
.nswitch input { position:absolute; inset:0; width:100%; height:100%; margin:0; opacity:0; cursor:pointer; }
.nswitch input:disabled { cursor:default; }
.nswitch .track { position:absolute; inset:0; background:var(--line); border-radius:999px; transition:background-color .18s ease; }
.nswitch .track::before { content:""; position:absolute; top:3px; left:3px; width:16px; height:16px; border-radius:50%;
  background:var(--surface); box-shadow:0 1px 2px rgba(0,0,0,.25); transition:transform .2s cubic-bezier(.34,1.35,.5,1); }
.nswitch input:checked + .track { background:var(--green); }
.nswitch input:checked + .track::before { transform:translateX(16px); }
.nswitch input:disabled + .track { opacity:.45; }
.nswitch input:focus-visible + .track { box-shadow:0 0 0 3px var(--green-bd); }

/* page shell */
.page { max-width:1800px; margin:0 auto; padding:28px 32px 56px; }
.page.narrow { max-width:1400px; }
/* in-page back link on the record route (issue #46) — modest, not a button; lives in
   the page body, so the nav shows no back-item next to the Waddle tab. */
.record-back { display:flex; width:fit-content; align-items:center; gap:5px; margin:14px 16px 8px;
  font:600 13px var(--sans); color:var(--muted); }
.record-back:hover { color:var(--green); }
.rule { border-top:2px solid var(--ink); margin-top:16px; }
.rule-soft { border-top:1px solid var(--line); margin-top:2px; margin-bottom:22px; }
.eyebrow { font:700 10px var(--sans); letter-spacing:.22em; color:var(--green); }
.h1 { font:600 28px var(--disp); color:var(--ink); letter-spacing:-.01em; }
.pagehead { display:flex; align-items:flex-end; justify-content:space-between; gap:20px; flex-wrap:wrap; }
.navbtn { display:inline-flex; width:26px; height:26px; border:1px solid var(--line); border-radius:6px;
  align-items:center; justify-content:center; color:var(--muted); background:var(--card); }
.navbtn:hover { border-color:var(--faint); }
.kpis { display:flex; align-items:center; gap:20px; }
.kpi { text-align:right; }
.kpi .l { font:700 9.5px var(--sans); letter-spacing:.18em; color:var(--muted-2); }
.kpi .v { font:600 19px var(--disp); color:var(--ink); margin-top:2px; }
.formpill { display:flex; align-items:center; gap:9px; border-radius:7px; padding:8px 16px;
  font:700 12px var(--disp); letter-spacing:.04em; }
.formpill i { width:7px; height:7px; border-radius:50%; }

.card { background:var(--card); border:1px solid var(--line); border-radius:8px; }
.card-h { font:700 10px var(--sans); letter-spacing:.16em; color:var(--muted-2); }
.btn { display:inline-flex; align-items:center; gap:8px; border:1px solid var(--line); background:var(--card);
  color:var(--ink); font:600 12.5px var(--sans); padding:9px 15px; border-radius:7px; }
.btn:hover { border-color:var(--faint); }
.btn.primary { background:var(--green); color:var(--on-accent); border-color:transparent; }
.btn.primary:hover { background:var(--green-d); color:var(--on-accent); }

/* week view */
.week-row { display:flex; gap:12px; align-items:stretch; }
.week-days { flex:1; min-width:0; display:flex; gap:12px; }
.day-col { flex:1; min-width:0; background:var(--surface); border:1px solid var(--line); border-radius:8px;
  padding:12px 11px; display:flex; flex-direction:column; gap:8px; min-height:360px; color:inherit; }
.day-col.today { background:var(--today); border-color:var(--green-bd); box-shadow:0 0 0 1px var(--today-ring); }
.day-col .dow { font:700 10px var(--sans); letter-spacing:.16em; color:var(--muted-2); }
.day-col .dnum { font:600 21px var(--disp); }
/* week day-column wrappers: transparent on desktop (display:contents keeps the column
   layout identical); on phones they let each day fold to a header row — see the phone block. */
.dc-mhead { display:none; }
.dc-top, .dc-body { display:contents; }
.act { border-radius:6px; padding:9px 10px; }
.act.done { border:1px solid; }
.act.plan { border:1px dashed var(--line); background:var(--card); }
.act .top { display:flex; align-items:center; gap:8px; }
.act .ico { width:23px; height:23px; border-radius:7px; display:inline-flex; align-items:center; justify-content:center; flex:none; }
/* overflow-wrap so a long unbreakable name ("Walpertskirchen Cycling", straight
   from Garmin) breaks instead of spilling out past the card border — min-width:0
   only lets the box shrink, it can't split the word. break-word rather than
   anywhere: anywhere also drops min-content to ~1 character, which lets flex
   crush the title into a sliver even when there's room. */
.act .tt { font:600 12px var(--sans); flex:1; min-width:0; overflow-wrap:break-word; }
.act .mid { margin-top:8px; display:flex; align-items:baseline; gap:8px; }
.act .dur { font:600 16px var(--disp); }
.act .tsspill { font:700 10px var(--sans); padding:3px 8px; border-radius:6px; background:var(--pill-bg); color:var(--muted); letter-spacing:.03em; }
.act .sub { margin-top:4px; font:500 11px var(--sans); color:var(--muted-2); }
.act .planbadge { margin-top:5px; font:700 8.5px var(--sans); letter-spacing:.16em; color:var(--faint); }
.act .planline { margin-top:6px; padding-top:6px; border-top:1px dashed var(--line); font:500 10px var(--sans); color:var(--muted-2); }
.act .comp { margin-top:6px; font:700 9px var(--sans); letter-spacing:.06em; border-radius:5px; padding:2px 7px; display:inline-block; color:var(--on-accent); }
.dayfoot { margin-top:auto; display:flex; align-items:center; gap:8px; padding-top:6px; }
.reststate { margin-top:28px; display:flex; flex-direction:column; align-items:center; gap:8px; color:var(--muted-2); }

/* day-items: notes + availability — a day-scoped layer, visually distinct from workouts
   (no sport tint; a document glyph for notes, a status dot for availability) */
.ditems { display:flex; flex-direction:column; gap:5px; }
.ditem { display:flex; align-items:center; gap:6px; border-radius:6px; padding:5px 8px;
  font:600 11px var(--sans); min-width:0; }
.ditem.note { background:var(--surface); border:1px dashed var(--line); color:var(--muted); }
.ditem.avail { background:var(--card); border:1px solid var(--line); color:var(--muted-2); }
.ditem svg, .ditem .dot { flex:none; }
.ditem .dot { width:7px; height:7px; border-radius:50%; }
.ditem .lbl { min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.ditems.mini { gap:3px; margin-top:2px; }
.ditems.mini .ditem { padding:2px 5px; gap:4px; border-radius:4px; font-size:10px; }
.ditems.mini .ditem svg { width:9px; height:9px; }
.ditems.mini .ditem .dot { width:5px; height:5px; }

/* summary sidebars */
.side { width:300px; flex:none; background:var(--card); border:1px solid var(--line); border-radius:8px;
  padding:18px; display:flex; flex-direction:column; gap:15px; }
.side.wide { width:256px; }
@media (max-width:1180px) {
  .week-row { flex-direction:column; }
  .week-days { overflow-x:auto; padding-bottom:6px; }
  .week-days .day-col { flex:1 0 128px; }
  .side { width:auto; }
}
.ledger .lr { display:flex; align-items:baseline; gap:6px; padding:7px 0; border-top:1px solid var(--line-soft);
  font:500 11.5px var(--sans); color:var(--muted); }
.ledger .lr .dots { flex:1; border-bottom:1px dotted var(--line); }
.ledger .lr .val { font:600 12.5px var(--disp); color:var(--ink); }
.ledger .lr .val .tot { color:var(--faint); font-weight:500; }
.bysport .bar { display:flex; height:7px; border-radius:3px; overflow:hidden; gap:2px; }
.bysport .legend { display:flex; flex-wrap:wrap; gap:10px; margin-top:9px; }
.bysport .legend span { display:inline-flex; align-items:center; gap:5px; font:600 10px var(--sans); color:var(--muted); }
.bysport .legend i { width:7px; height:7px; border-radius:50%; }
.flag { border-radius:5px; padding:2px 8px; color:var(--on-accent); font:700 9px var(--sans); letter-spacing:.04em; display:inline-block; margin-top:4px; }

/* month grid */
.month-grid { display:grid; grid-template-columns:repeat(7,1fr); gap:8px; }
.dow-row { display:grid; grid-template-columns:repeat(7,1fr); gap:8px; margin-bottom:8px; }
.dow-row div { font:700 10px var(--sans); letter-spacing:.16em; color:var(--muted-2); padding:0 2px; }
.mcell { background:var(--surface); border:1px solid var(--line); border-radius:8px; padding:9px 8px; display:flex;
  flex-direction:column; gap:5px; min-height:112px; min-width:0; overflow:hidden; color:inherit; }
.mcell.today { background:var(--today); border-color:var(--green-bd); box-shadow:0 0 0 1px var(--today-ring); }
.mcell.out { opacity:.4; }
.mcell .top { display:flex; align-items:center; justify-content:space-between; }
.mcell .n { font:600 13px var(--disp); }
.mchip { display:flex; align-items:center; gap:5px; border-radius:4px; padding:3px 6px; min-width:0; overflow:hidden; }
.mchip i { width:6px; height:6px; border-radius:50%; flex:none; }
.mchip .t { font:600 10.5px var(--sans); flex:1; min-width:0; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.mcell .daytss { margin-top:auto; text-align:right; font:700 9px var(--sans); color:var(--faint); }

/* detail pages */
.wellstrip { display:flex; gap:22px; padding:16px 18px; }
.wellstrip .w { flex:1; display:flex; flex-direction:column; gap:2px; }
.wellstrip .w + .w { border-left:1px solid var(--line); padding-left:22px; }
.wellstrip .l { font:700 9px var(--sans); letter-spacing:.14em; color:var(--muted-2); }
.wellstrip .v { font:600 17px var(--disp); }
.wellstrip .v small { font-size:11px; color:var(--faint); font-weight:500; }
.actbig { background:var(--green-tint); border:1px solid var(--green-bd); border-radius:8px; padding:16px 18px;
  display:flex; flex-direction:column; gap:10px; color:inherit; }
.actbig .row { display:flex; align-items:center; gap:10px; }
.actbig .stats { display:flex; gap:24px; padding-left:40px; flex-wrap:wrap; }
.actbig .stats .v { font:600 15px var(--disp); }
.actbig .stats .l { font:500 10.5px var(--sans); color:var(--muted-2); }
.metricgrid { display:grid; grid-template-columns:repeat(4,1fr); row-gap:18px; padding:18px 20px; }
.metricgrid .m { display:flex; flex-direction:column; gap:2px; }
.metricgrid .m:not(:nth-child(4n+1)) { border-left:1px solid var(--line); padding-left:22px; }
.metricgrid .l { font:700 9px var(--sans); letter-spacing:.14em; color:var(--muted-2); }
.metricgrid .v { font:600 18px var(--disp); }
.metricgrid .v small { font-size:11px; color:var(--faint); font-weight:500; }
.zbar-row { display:flex; align-items:center; gap:10px; }
.zbar-row .zl { width:104px; flex:none; font:600 11px var(--sans); color:var(--ink-2); }
.zbar-row .zt { flex:1; height:14px; background:var(--line-soft); border-radius:3px; overflow:hidden; }
.zbar-row .zf { display:block; height:14px; border-radius:3px; }
/* map */
.map-wrap { position:relative; }
.map-box { border-radius:8px; overflow:hidden; }
.map-expand { position:absolute; top:8px; right:8px; z-index:500; background:var(--card); border:1px solid var(--line);
  border-radius:6px; padding:5px 9px; font:600 11px var(--sans); color:var(--ink); cursor:pointer; }
.leaflet-container { font-family:var(--sans); background:var(--line-soft); }
.floe-livedot-wrap { background:none; border:none; }
.floe-livedot { width:12px; height:12px; border-radius:50%; background:var(--green); border:2.5px solid var(--surface);
  box-sizing:border-box; box-shadow:0 1px 3px rgba(0,0,0,.35); transform-origin:center;
  animation:floe-breathe 2.6s ease-in-out infinite; }
@keyframes floe-breathe { 0%,100% { transform:scale(1); } 50% { transform:scale(1.12); } }
@media (prefers-reduced-motion:reduce) { .floe-livedot { animation:none; } }
.zbar-row .zv { width:52px; flex:none; text-align:right; font:600 11.5px var(--disp); }
.donut-wrap { display:flex; align-items:center; gap:16px; }
.sportmini { display:none; }   /* phone-only sport-split ring beside the Form ring; desktop uses the BY SPORT bar */

/* ATP */
.atp-table { display:grid; }
.atp-head, .atp-row { display:grid; grid-template-columns:1.1fr .7fr 1.2fr 1fr .7fr .8fr .8fr; align-items:center; }
.atp-head { padding:12px 20px; border-bottom:1px solid var(--ink); }
.atp-head span { font:700 9.5px var(--sans); letter-spacing:.1em; color:var(--muted-2); }
.atp-row { padding:11px 20px; border-bottom:1px solid var(--line-soft); }
.atp-row.cur { background:var(--today); }
.atp-row .r { font:600 12.5px var(--sans); color:var(--ink); }
.pill { font:700 11px var(--sans); letter-spacing:.03em; border-radius:6px; padding:5px 11px; display:inline-block; }
.atp-target { border-bottom:1px dashed var(--line); cursor:pointer; padding-bottom:1px; }
.atp-target:hover { border-color:var(--green); color:var(--green); }
.atp-target input { width:64px; text-align:right; font:inherit; color:inherit; background:var(--surface);
  border:1px solid var(--green-bd); border-radius:5px; padding:2px 6px; }
.atp-phase.pill { cursor:pointer; }
.atp-phase select { font:700 11px var(--sans); border:1px solid var(--line); border-radius:6px; padding:4px 6px;
  background:var(--surface); color:var(--ink); }
.atp-race-row { display:flex; align-items:center; justify-content:space-between; gap:10px; margin-top:8px; }
.atp-race-row span { font:500 13px var(--sans); color:var(--ink); flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.atp-race-row select { width:auto; }
.atp-season-head { display:flex; align-items:center; gap:10px; flex-wrap:wrap; }
.atp-season-head .btn { padding:5px 11px; font-size:12px; }
.tnum { font:600 13px var(--disp); text-align:right; }

/* dashboard (#/dashboard) */
.dash-head { display:flex; align-items:flex-end; justify-content:space-between; gap:16px; flex-wrap:wrap; }
.pseg { display:flex; background:var(--line-soft); border-radius:8px; padding:3px; gap:2px; flex-wrap:wrap; }
.pseg button { font:600 12px var(--sans); padding:7px 14px; border-radius:6px; color:var(--muted-2); background:none; border:none; white-space:nowrap; }
.pseg button:hover { color:var(--ink); }
.pseg button.on { background:var(--seg-on); color:var(--ink); box-shadow:0 1px 2px var(--shadow); }
.dash-grid { display:grid; grid-template-columns:repeat(12,1fr); gap:16px; align-items:start; }
.dash-grid > .card { padding:18px 20px; display:flex; flex-direction:column; gap:14px; min-width:0; }
.dc-5 { grid-column:span 5; } .dc-6 { grid-column:span 6; } .dc-7 { grid-column:span 7; } .dc-12 { grid-column:1 / -1; }
.dl-totals { display:flex; gap:28px; flex-wrap:wrap; }
.dl-totals .t .v { font:600 24px var(--disp); color:var(--ink); }
.dl-totals .t .v small { font-size:12px; color:var(--faint); font-weight:500; }
.dl-totals .t .l { font:700 9px var(--sans); letter-spacing:.14em; color:var(--muted-2); margin-top:2px; }
.dl-legend { display:flex; flex-wrap:wrap; gap:12px; }
.dl-legend span { display:inline-flex; align-items:center; gap:6px; font:600 11px var(--sans); color:var(--muted); }
.dl-legend i { width:9px; height:9px; border-radius:3px; flex:none; }
.dash-tablewrap { overflow-x:auto; }
.dash-tbl { min-width:440px; }
.dash-tbl .tot-row td { border-top:1px solid var(--ink); font-weight:700; color:var(--ink); }
@media (max-width:1080px) { .dc-5, .dc-6, .dc-7 { grid-column:1 / -1; } }

/* dialogs / toast / overlay */
dialog { border:1px solid var(--line); border-radius:12px; background:var(--card); color:var(--ink);
  padding:20px 22px; min-width:340px; max-height:88vh; overflow-y:auto; }
dialog::backdrop { background:rgba(30,36,32,.35); }
dialog h3 { margin:0 0 12px; font:600 16px var(--disp); }
dialog label { display:block; margin:9px 0 3px; color:var(--muted); font-size:12px; }
dialog input, dialog select, dialog textarea { width:100%; font:inherit; color:var(--ink); background:var(--surface);
  border:1px solid var(--line); border-radius:7px; padding:8px 10px; }
dialog textarea { resize:vertical; }
dialog label.chk { display:flex; align-items:center; gap:7px; margin-top:12px; color:var(--ink); font-size:13px; }
dialog label.chk input { width:auto; }
.actions { display:flex; justify-content:flex-end; gap:8px; margin-top:16px; align-items:center; }
.dlg-link { font-size:12px; color:var(--green); margin-right:auto; }
/* plan chooser: Workout / Note / Availability (issue #11) */
.choose { display:flex; flex-direction:column; gap:8px; }
.choose-opt { display:flex; align-items:center; gap:12px; width:100%; text-align:left; cursor:pointer;
  border:1px solid var(--line); background:var(--surface); border-radius:9px; padding:11px 13px; }
.choose-opt[hidden] { display:none; }   /* class display:flex would otherwise beat the [hidden] UA rule (#54 paste) */
.choose-opt:hover { border-color:var(--faint); background:var(--card); }
.choose-opt .ico { width:34px; height:34px; border-radius:8px; background:var(--card); color:var(--green);
  display:inline-flex; align-items:center; justify-content:center; flex:none; }
.choose-opt .txt { display:flex; flex-direction:column; min-width:0; gap:1px; }
.choose-opt .t { font:600 14px var(--disp); color:var(--ink); }
.choose-opt .d { font:400 12px var(--sans); color:var(--muted-2); }
#save-status { font-size:13px; color:var(--muted); max-width:400px; line-height:1.5; }
#save-status ul { margin:6px 0; padding-left:18px; }

.wk-row { display:flex; gap:10px; align-items:flex-start; padding:10px 0; border-bottom:1px solid var(--line-soft); }
.wk-row:last-child { border-bottom:none; }
.wk-row .ico { margin-top:2px; }
.wk-main { flex:1; min-width:0; }
.wk-title { font:600 13.5px var(--sans); }
.wk-metrics { color:var(--muted); font-size:12.5px; margin-top:2px; }
.wk-notes { color:var(--muted-2); font-size:12px; white-space:pre-wrap; margin-top:4px; max-height:110px; overflow-y:auto; line-height:1.5; }
.wk-del { color:var(--faint); background:none; border:none; padding:2px 6px; }
.wk-del:hover { color:var(--brown); }
.badge { font:700 9px var(--sans); border-radius:5px; padding:2px 7px; color:var(--on-accent); letter-spacing:.04em; }
.tag { font:700 10px var(--sans); background:var(--green-tint); color:var(--green); border:1px solid var(--green-bd);
  border-radius:6px; padding:4px 9px; letter-spacing:.05em; }

#stream-tip, .stream-tip { position:absolute; display:none; pointer-events:none; z-index:6; background:var(--ink); color:var(--paper);
  border-radius:8px; padding:7px 10px; font-size:12px; box-shadow:0 8px 22px rgba(0,0,0,.22); }
.streams-wrap { position:relative; }

#toast { position:fixed; bottom:20px; left:50%; transform:translateX(-50%); background:var(--ink); color:var(--paper);
  padding:10px 18px; border-radius:9px; font-size:13px; display:none; z-index:20; max-width:80vw; white-space:pre-line;
  box-shadow:0 8px 26px rgba(0,0,0,.25); }
#drop-overlay { position:fixed; inset:0; display:none; z-index:50; background:rgba(43,89,64,.10);
  border:3px dashed var(--green); border-radius:14px; align-items:center; justify-content:center;
  font:700 22px var(--disp); color:var(--green); pointer-events:none; }
.empty { border:1.5px dashed var(--line); border-radius:8px; padding:22px; text-align:center; color:var(--faint); }
.pcard { transition:box-shadow .1s, transform .1s; }
.pcard.drop-ok { box-shadow:0 0 0 2px var(--green); transform:scale(1.01); }
/* #45: week-view drag-to-reschedule drop target. Extra specificity (.week-days …) so it
   beats both .day-col.today and .day-col:hover regardless of source order. */
.week-days .day-col.drop-ok { box-shadow:0 0 0 2px var(--green); border-color:var(--green-bd); background:var(--green-tint); }
.unpair:hover { color:var(--brown) !important; }

/* plan vs done */
.pvd { display:flex; gap:20px; flex-wrap:wrap; }
.pvd-stat { flex:1; min-width:120px; }
.pvd-stat .l { font:700 9px var(--sans); letter-spacing:.14em; color:var(--muted-2); }
.pvd-stat .v { font:600 18px var(--disp); margin-top:2px; }
.pvd-stat .v small { font-size:11px; color:var(--faint); font-weight:500; }
.adh-bar { display:flex; height:20px; border-radius:5px; overflow:hidden; margin-top:6px; }
.adh-bar div { display:flex; align-items:center; justify-content:center; font:700 10px var(--sans); color:var(--on-accent); }
.iv-table { width:100%; border-collapse:collapse; }
.iv-table th { font:700 9.5px var(--sans); letter-spacing:.08em; color:var(--muted-2); text-align:right; padding:6px 8px; border-bottom:1px solid var(--ink); }
.iv-table th:first-child { text-align:left; }
.iv-table td { font:500 12.5px var(--disp); text-align:right; padding:7px 8px; border-bottom:1px solid var(--line-soft); }
.iv-table td:first-child { text-align:left; font-family:var(--sans); font-weight:600; }
.hit-dot { display:inline-block; width:8px; height:8px; border-radius:50%; margin-left:6px; }

/* ── layouts lifted out of app.js inline styles ──────────────────
   These used to be inline `style="…"`, which no media query can override.
   Desktop rendering is unchanged; the phone block at the bottom re-lays them. */
/* Login + Waddle splash always render in the light palette, even under dark mode. */
.force-light {
  --paper:#F7F5EF; --card:#FCFBF8; --line:#E4E0D3; --line-soft:#EDEAE0;
  --ink:#1E2420; --ink-2:#4C4738; --muted:#726D5E; --muted-2:#8B8677;
  --faint:#ADA894; --faint-2:#C7C2B2;
  --green:#2B5940; --green-d:#1E4530; --green-tint:#E1EBE3; --green-bd:#BFDAC7; --green-soft:#C3D9C8;
  --blue:#2E4A66; --blue-tint:#E1E8EF; --blue-bd:#C2D2E0;
  --brown:#8A5A3B; --brown-tint:#F1E5DB; --brown-bd:#E0C7B3;
  --swim:#2E6E7E; --swim-tint:#DEECEE; --swim-bd:#BEDCE1;
  --sand:#7D7869; --sand-tint:#EDEBE3;
  --surface:#fff; --today:#F4F8F5; --today-ring:rgba(43,89,64,.12);
  --seg-on:#fff; --shadow:rgba(30,36,32,.08); --pill-bg:rgba(0,0,0,.05);
  --on-accent:#fff; --danger:#B33; --donut-track:#E7E3D6; --logo-belly:#FCFBF8;
  --coach-bg:#F3E3C6; --coach-ink:#7A5A1E; --coach-bd:#E3CB9A;
  --map-line:#2B5940; --map-start:#0ca30c; --map-end:#8A5A3B;
  color-scheme:light;
}
.login { min-height:100vh; display:flex; background:var(--paper); color:var(--ink); }
.login-brand { flex:0 0 42%; background:var(--green); color:#fff; padding:44px;
  display:flex; flex-direction:column; align-items:center; justify-content:center; text-align:center; }
.login-word { font:700 52px var(--disp); letter-spacing:-.02em; margin-top:16px; }
.login-tag { font:600 20px/1.4 var(--disp); margin-top:32px; }
.login-blurb { font:400 14px/1.6 var(--sans); color:rgba(255,255,255,.78); margin-top:12px; max-width:320px; }
.login-pane { flex:1; display:flex; align-items:center; justify-content:center; padding:40px; }
.login-form { width:360px; display:flex; flex-direction:column; gap:22px; }

/* ── Waddle (indoor-ride recording) ─────────────────────── */
.waddle-splash { min-height:calc(100vh - 78px); display:flex; flex-direction:column;
  align-items:center; justify-content:center; text-align:center; padding:40px;
  background:var(--green); color:#fff; animation:floe-rise .3s ease both; }
/* .waddle-mark is the brand mark itself — used both as the animated Waddle-splash
   wordmark and (without `wm-animated`, see bikePenguinMark() in app.js) as a still
   logo elsewhere, e.g. the ride-chooser header. `color` defaults to --ink so it
   reads on an ordinary page. .waddle-splash sets its own color:#fff, but a rule
   targeting .waddle-mark directly always wins over an inherited ancestor value —
   so the splash needs its own higher-specificity override here too (issue #98),
   not just the inherited color from its container. */
.waddle-mark { height:auto; overflow:visible; color:var(--ink); }
.waddle-splash .waddle-mark { color:#fff; }
.waddle-mark .wm-wheel { transform-box:fill-box; transform-origin:center; }
@media (prefers-reduced-motion: no-preference) {
  .waddle-mark.wm-animated .wm-wheel { animation:wm-roll 1.04s linear infinite; }
  .waddle-mark.wm-animated .wm-whoosh line { animation:wm-whoosh .98s ease-in infinite; }
  .waddle-mark.wm-animated .wm-whoosh line:nth-child(2) { animation-delay:.32s; }
  .waddle-mark.wm-animated .wm-whoosh line:nth-child(3) { animation-delay:.60s; }
}
@keyframes wm-roll { to { transform:rotate(360deg); } }
@keyframes wm-whoosh {
  0%   { opacity:0; transform:translateX(7px); }
  28%  { opacity:.55; }
  100% { opacity:0; transform:translateX(-26px); }
}
.waddle-word { font:700 68px var(--disp); letter-spacing:-.02em; color:#fff; margin-top:22px; }
.waddle-lead { font:600 21px var(--disp); color:#fff; margin-top:14px; }
.waddle-tag { font:500 18px/1.6 var(--sans); color:rgba(255,255,255,.85); margin-top:8px; max-width:540px; }
.waddle-cta { display:inline-block; margin-top:34px; background:#F7F5EF; color:var(--green);
  font:700 14px var(--disp); letter-spacing:.01em; padding:13px 28px; border-radius:10px;
  box-shadow:0 4px 14px rgba(0,0,0,.18); transition:transform .15s ease, background .15s ease; }
.waddle-cta:hover { background:#fff; color:var(--green-d); transform:translateY(-1px); }
.waddle-choose { display:flex; flex-direction:column; gap:16px; padding-top:34px; }
.waddle-choose-h { display:flex; align-items:center; gap:10px; font:600 26px var(--disp); color:var(--ink); letter-spacing:-.01em; }
.waddle-choose-h .waddle-mark { flex:none; }
.waddle-hint { font:500 12.5px var(--sans); color:var(--brown); background:var(--brown-tint);
  border:1px solid var(--brown-bd); border-radius:8px; padding:10px 13px; }
.wchoices { display:grid; grid-template-columns:1fr 1fr; gap:14px; }
@media (max-width:640px){ .wchoices { grid-template-columns:1fr; } }
.wchoice { display:flex; flex-direction:column; gap:6px; padding:20px; border:1px solid var(--line);
  border-radius:12px; background:var(--surface); color:inherit; transition:border-color .15s, box-shadow .15s, transform .15s; }
a.wchoice:hover { border-color:var(--green-bd); box-shadow:0 4px 16px var(--shadow); transform:translateY(-2px); }
.wchoice.off { opacity:.72; border-style:dashed; }
.wchoice-k { font:700 10px var(--sans); letter-spacing:.16em; text-transform:uppercase; color:var(--green); }
.wchoice-t { font:600 18px var(--disp); color:var(--ink); }
.wchoice-d { font:400 13px/1.5 var(--sans); color:var(--muted-2); }
.wtag { font:700 9px var(--sans); letter-spacing:.06em; color:var(--green); border:1px solid var(--green-bd);
  border-radius:4px; padding:1px 5px; vertical-align:2px; }
.waddle-soon { font:500 12.5px var(--sans); color:var(--faint); margin-top:6px; }
.wlib-h { font:700 10px var(--sans); letter-spacing:.16em; text-transform:uppercase; color:var(--muted-2); margin-top:10px; }
.wlib-list { display:flex; flex-direction:column; gap:10px; }
.wlib-item { display:flex; flex-direction:column; gap:3px; padding:14px 16px; border:1px solid var(--line);
  border-radius:10px; background:var(--surface); color:inherit; transition:border-color .15s, box-shadow .15s, transform .15s; }
.wlib-item:hover { border-color:var(--green-bd); box-shadow:0 4px 16px var(--shadow); transform:translateY(-1px); }
.wlib-t { font:600 15px var(--disp); color:var(--ink); }
.wlib-d { font:400 12.5px/1.5 var(--sans); color:var(--muted-2); }

/* month: the 1120px floor keeps 7 legible columns beside the sidebar on desktop */
.month-row { display:flex; gap:20px; align-items:flex-start; min-width:1120px; }

/* Tablets: 1120px still forces a horizontal pan / zoom-out at 820px, and the
   settings sidebar squeezes the form. Relax both before the phone rules. */
@media (max-width:1180px) {
  .month-row { flex-direction:column; align-items:stretch; min-width:0; }
}
@media (max-width:900px) {
  .set-shell { flex-direction:column; gap:14px; align-items:stretch; }
  .set-nav { width:auto; position:static; flex-direction:row; flex-wrap:wrap; gap:4px; }
}

/* day view: wellness + workout cards beside the Form panel */
.day-row { display:flex; gap:20px; align-items:flex-start; }
.day-main { flex:1; min-width:0; display:flex; flex-direction:column; gap:14px; }
.dcard-title { font:700 14px var(--sans); color:var(--ink); flex:1; min-width:0;
  overflow-wrap:break-word; }
/* the Tomorrow list in the sidebar takes raw Garmin titles too */
.side { overflow-wrap:break-word; }

/* settings: sticky section nav + content */
.set-shell { display:flex; gap:32px; align-items:flex-start; }
/* top:92px = the 64px sticky nav + the original 28px gap, so this section nav clears the header */
.set-nav { width:190px; flex:none; position:sticky; top:92px; display:flex; flex-direction:column; gap:2px; }
.set-nav a { font:600 13px var(--sans); color:var(--muted); padding:9px 12px; border-radius:6px; text-transform:capitalize; }
.set-nav a:hover { color:var(--ink); }
.set-nav a.active { color:var(--ink); background:var(--line-soft); }
.set-main { flex:1; min-width:0; display:flex; flex-direction:column; gap:20px; }

.fgrid { display:grid; gap:16px; }
.fgrid.tight { gap:12px; }
.fgrid-2 { grid-template-columns:1fr 1fr; }
.fgrid-3 { grid-template-columns:1fr 1fr 1fr; }

/* charts: height comes from --ch so the phone block can free the aspect ratio.
   (`height` inline would outrank the stylesheet.) */
.chart-svg { width:100%; height:var(--ch); display:block; }

/* ── motion ───────────────────────────────────────────────────────
   Gentle, convention-y motion: views rise + fade in on a screen change,
   controls give a press/hover response, dialogs pop over a fading backdrop,
   and colours cross-fade while the theme flips. All disabled under
   prefers-reduced-motion at the very bottom of this section. */
@keyframes floe-rise { from { opacity:0; transform:translateY(8px); } to { opacity:1; transform:none; } }
.page { animation:floe-rise .28s cubic-bezier(.22,.61,.36,1) both; }
.login { animation:floe-rise .3s ease both; }

/* toggling Day→Week→Month slides the new view in from the side it advances toward */
@keyframes floe-slide-l { from { opacity:0; transform:translateX(24px); } to { opacity:1; transform:none; } }
@keyframes floe-slide-r { from { opacity:0; transform:translateX(-24px); } to { opacity:1; transform:none; } }
#view[data-anim="left"]  .page { animation:floe-slide-l .3s cubic-bezier(.22,.61,.36,1) both; }
#view[data-anim="right"] .page { animation:floe-slide-r .3s cubic-bezier(.22,.61,.36,1) both; }

/* press / hover feedback on anything interactive */
.btn, .navbtn, .seg a, .nav a.tab, .nav-right button, .menu-btn, .menu-item, .pill, .tag, .avatar, .choose-opt {
  transition:background-color .18s ease, border-color .18s ease, color .18s ease,
             box-shadow .18s ease, transform .1s ease; }
.btn:active, .navbtn:active, .nav-right button:active, .choose-opt:active { transform:translateY(1px) scale(.98); }
.btn.primary:hover { box-shadow:0 3px 12px var(--shadow); }

/* calendar cells lift a touch on hover */
.day-col, .mcell { transition:transform .16s ease, box-shadow .16s ease,
             background-color .2s ease, border-color .2s ease; }
.day-col:hover, .mcell:hover { transform:translateY(-2px); box-shadow:0 6px 16px var(--shadow); }

/* dialogs pop in over a fading backdrop */
@keyframes floe-pop { from { opacity:0; transform:translateY(10px) scale(.98); } to { opacity:1; transform:none; } }
@keyframes floe-fade { from { opacity:0; } to { opacity:1; } }
dialog[open] { animation:floe-pop .2s cubic-bezier(.2,.8,.3,1) both; }
dialog[open]::backdrop { animation:floe-fade .2s ease both; }

/* smooth colour cross-fade while the theme flips (class added by JS for ~450ms) */
:root.theme-anim, :root.theme-anim * {
  transition:background-color .35s ease, border-color .35s ease, color .35s ease,
             fill .35s ease, stroke .35s ease, box-shadow .35s ease !important; }

@media (prefers-reduced-motion:reduce) {
  .page, .login, dialog[open], dialog[open]::backdrop { animation:none !important; }
  .day-col, .mcell, .btn, .navbtn, .nav-right button, .seg a, .nav a.tab { transition:none !important; }
  .day-col:hover, .mcell:hover { transform:none !important; box-shadow:none !important; }
  :root.theme-anim, :root.theme-anim * { transition:none !important; }
  #view[data-anim] .page { animation:none !important; }
}

/* workout-builder step rows (moved off inline styles so phones can restyle them) */
.brow { display:flex; align-items:center; gap:8px; flex-wrap:wrap; padding:10px 12px;
  border:1px solid var(--line); border-left:3px solid var(--line); border-radius:8px; background:var(--card); }
.brow-kind { font:700 10px var(--sans); letter-spacing:.08em; width:74px; }

/* ── phones ──────────────────────────────────────────────────────
   Without this the app renders at 900–1150px and the browser zooms the whole
   page out to fit, which is why everything was unreadably small. */
@media (max-width:760px) {
  /* nav → one clean row on phones (owner-specified). display:contents dissolves
     .nav-left/.nav-right so `order` can lay everything out on a single line:
        penguin · burger ····· [ Day | Week | Month ] ····· bell · avatar
     The segment is centred by giving BOTH it and the bell a margin-left:auto — flex
     splits the free space equally, so the segment lands in the middle and bell+avatar
     sit flush right. Dashboard and the dark-mode toggle move into the burger. */
  .nav { flex-wrap:nowrap; height:auto; min-height:56px; padding:8px 12px; gap:8px; align-items:center; }
  .nav-left, .nav-right { display:contents; }
  .brand { order:1; }
  .navmenu { order:2; }
  .nav a.tab { order:3; }                      /* Back on detail routes (Dashboard/Waddle hidden below) */
  .seg { order:4; margin-left:auto; }          /* centred by this auto-margin + the bell's */
  .coachpill { order:5; }
  .nav select { order:5; max-width:128px !important; }        /* athlete switcher, if present */
  .notifwrap { order:5; margin-left:auto; }    /* bell — flush right */
  .nav-right .avatar { order:6; width:34px; height:34px; }
  /* Dashboard + Waddle tabs collapse into the burger on phones; Waddle is computer-only (needs
     desktop Web Bluetooth). Hiding by viewport is robust by design: a desktop window is always
     wider than 760px, so both always show there — no user-agent sniffing that could misfire and
     hide them on a real computer. (Dark mode now lives in the burger on every viewport.) */
  .nav a.tab[href="#/dashboard"], .nav a.tab[href="#/waddle"], .navcal { display:none; }
  /* tap targets */
  .nav a.tab, .menu-btn { padding:8px 8px; min-height:40px; }
  .nav-right button { padding:8px 8px; min-height:40px; font-size:12px; }
  .seg a { padding:9px 12px; }
  .notif-pop { min-width:0; width:min(322px, calc(100vw - 24px)); }
  .brand svg { width:26px; height:26px; }

  .page, .page.narrow { padding:16px 14px 40px; max-width:100%; }
  .h1 { font-size:22px; }
  .pagehead { gap:12px; }
  .kpis { gap:14px; flex-wrap:wrap; }

  /* login: a fixed 360px form beside a 42% brand panel pushed the submit button
     off-screen — you could not sign in at all on a phone. */
  .login { flex-direction:column; min-height:100dvh; }
  .login-brand { flex:none; padding:30px 20px; }
  .login-brand svg { width:64px; height:64px; }
  .login-word { font-size:34px; margin-top:10px; }
  .login-tag { font-size:16px; margin-top:14px; }
  .login-blurb { display:none; }
  .login-pane { padding:24px 16px; align-items:flex-start; }
  .login-form { width:100%; max-width:400px; }

  /* week: seven columns can't work at 390px — stack the days, and FOLD each one to a
     header (date · TSS ring · chevron) so the whole week fits on a glance instead of a
     long scroll. The chevron toggles .expanded to reveal that day's workouts in place. */
  .week-row { flex-direction:column; align-items:stretch; }
  .week-days { flex-direction:column; overflow-x:visible; gap:8px; }
  .week-days .day-col { flex:none; min-height:0; padding:0; gap:0; overflow:hidden; }
  .day-col:hover { transform:none; box-shadow:none; }   /* no lift on tap */
  .dc-top, .dayfoot { display:none; }                    /* desktop top + footer ring hidden */
  .dc-mhead { display:flex; align-items:center; gap:10px; padding:13px 14px; }
  .dc-mdate { font:600 15px var(--disp); color:var(--ink); white-space:nowrap; }
  .day-col.today .dc-mdate { color:var(--green); }
  .dc-mtoday { font:700 8px var(--sans); letter-spacing:.14em; color:var(--green); margin-left:5px; }
  .dc-mcount { flex:1; min-width:0; text-align:center; font:600 11px var(--sans); color:var(--muted-2);
    overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  .dc-tssring { position:relative; display:inline-flex; margin-left:auto; flex:none; }  /* ring + arrow → right */
  .dc-tssnum { position:absolute; inset:0; display:flex; align-items:center; justify-content:center;
    font:700 10px var(--disp); color:var(--muted-2); }
  .dc-arrow { display:inline-flex; align-items:center; color:var(--muted-2); padding:6px 2px; transition:transform .2s ease; }
  .day-col.expanded .dc-arrow { transform:rotate(180deg); }
  .dc-body { display:none; padding:2px 14px 14px; }
  .day-col.expanded .dc-body { display:block; }
  /* order:-1 floats the summary (Form donut, ledger, by-sport) above the folded days —
     the at-a-glance numbers come first. */
  .side, .side.wide { width:auto; align-self:stretch; order:-1; }

  /* month: drop the desktop floor and compact the cells to a dot-calendar,
     so all 7 days fit rather than 3. */
  .month-row { flex-direction:column; min-width:0; align-items:stretch; }
  .month-grid, .dow-row { gap:4px; }
  .dow-row div { font-size:8px; letter-spacing:.06em; padding:0; text-align:center; }
  .mcell { min-height:62px; padding:5px 4px; gap:3px; border-radius:6px; }
  .mcell .n { font-size:11px; }
  .mcell .top { gap:3px; }
  /* the "TODAY" badge doesn't fit a ~50px cell — the green number + ring already
     say it. :not(.n) so we never blank the day number on ordinary cells. */
  .mcell .top span:not(.n) { display:none; }
  .mchip { padding:2px; justify-content:center; gap:0; }
  .mchip .t { display:none; }             /* label can't fit ~50px — keep the sport dot */
  .mchip i { width:7px; height:7px; }
  .mcell .daytss { font-size:8px; }

  /* settings: sidebar + content side by side pushed the form off-screen */
  .set-shell { flex-direction:column; gap:14px; }
  .set-nav { width:auto; position:static; flex-direction:row; flex-wrap:wrap; gap:4px; }
  .set-main { width:100%; }
  .fgrid-2, .fgrid-3 { grid-template-columns:1fr; }

  /* charts: viewBox + fixed px height letterboxed the graphic into a thin strip
     with dead space above and below. height:auto keeps the aspect ratio honest. */
  .chart-svg { height:auto; }

  /* builder step rows: give the kind label its own line so the controls below it
     stop wrapping raggedly around a 74px label and orphaning the @ / – / % marks. */
  .brow { gap:6px 8px; padding:12px; }
  .brow-kind { width:100%; }

  /* day: the Form panel beside the cards squeezed the main column so narrow that
     workout titles were clipped by it — stack instead. */
  .day-row { flex-direction:column; align-items:stretch; }
  .day-main { width:100%; }
  /* let edit/status/details drop below the title rather than share its line —
     otherwise they take ~200px and a Garmin title breaks across three lines. */
  .pcard > div:first-child { flex-wrap:wrap; }
  .dcard-title { flex:1 1 auto; }

  /* detail pages */
  .metricgrid { grid-template-columns:1fr 1fr; padding:14px; column-gap:12px; }
  .metricgrid .m:not(:nth-child(4n+1)) { border-left:none; padding-left:0; }
  .metricgrid .m:not(:nth-child(2n+1)) { border-left:1px solid var(--line); padding-left:14px; }
  .wellstrip { flex-wrap:wrap; gap:12px; padding:14px; }
  .wellstrip .w { flex:1 1 40%; }
  .wellstrip .w + .w { border-left:none; padding-left:0; }
  .actbig .stats { padding-left:0; gap:14px; }
  .zbar-row .zl { width:64px; font-size:10px; }
  .zbar-row .zv { width:44px; }
  /* summary (Task C): fill the dead space beside the Form ring with a sport-split ring,
     and hide the horizontal BY SPORT bar — uses the gap AND shortens the card. */
  .donut-wrap { flex-wrap:wrap; gap:12px; }
  .sportmini { display:flex; flex-direction:column; align-items:center; gap:7px; margin-left:auto; flex:none; }
  .sportmini .legend { display:flex; flex-direction:column; gap:3px; }
  .sportmini .legend span { display:inline-flex; align-items:center; gap:5px; font:600 10px var(--sans); color:var(--muted); white-space:nowrap; }
  .sportmini .legend i { width:7px; height:7px; border-radius:50%; flex:none; }
  .bysport { display:none; }

  /* page headers (Task D): stack + centre the Day/Week/Month header instead of leaving it
     left-stuck with dead space on the right. */
  .pagehead.ph-center { flex-direction:column; align-items:center; text-align:center; gap:14px; }
  .pagehead.ph-center .kpis { justify-content:center; flex-wrap:wrap; }
  .pagehead.ph-center .kpi { text-align:center; }

  /* ATP: a 6-column grid at 390px — give it its own scroll rather than
     widening the page and zooming everything out. */
  .atp-wrap { overflow-x:auto; }
  .atp-head, .atp-row { min-width:680px; padding-left:12px; padding-right:12px; }

  /* dialogs: min-width:340px + padding overflowed a 360px phone */
  dialog { min-width:0; width:calc(100vw - 24px); max-width:420px; padding:16px; }
  .actions { flex-wrap:wrap; }
  /* iOS zooms the page in when a focused input is under 16px */
  dialog input, dialog select, dialog textarea, .login-form input { font-size:16px; }
}

/* ── #100 first-run coach-marks ─────────────────────────────
   A guided tour that anchors each popup beside its real control and pulses a ring on it.
   The ring's big box-shadow spotlights the target (dims everything else); the scrim sits
   under it purely to swallow clicks so nothing outside the tour fires. */
#tour[hidden] { display:none; }
.tour-scrim { position:fixed; inset:0; z-index:99997; background:transparent; }
.tour-scrim.dim { background:rgba(18,22,18,.5); }
/* No transition on the box geometry: the ring is repositioned on every scroll tick to
   track its target, so a left/top/width/height transition would lag the scroll (and is
   layout-thrashy). It snaps to each target instead; the pulse below carries the motion. */
.tour-ring { position:fixed; z-index:99998; border-radius:12px; border:2.5px solid var(--green);
  box-shadow:0 0 0 9999px rgba(18,22,18,.5); }
.tour-ring.pulse { animation:tour-pulse 1.6s ease-in-out infinite; }
@keyframes tour-pulse {
  0%,100% { box-shadow:0 0 0 9999px rgba(18,22,18,.5), 0 0 0 0 rgba(94,150,110,.6); }
  50%     { box-shadow:0 0 0 9999px rgba(18,22,18,.5), 0 0 0 11px rgba(94,150,110,0); }
}
.tour-pop { position:fixed; z-index:100000; width:320px; max-width:calc(100vw - 28px);
  background:var(--surface); color:var(--ink); border:1px solid var(--line); border-radius:14px;
  box-shadow:0 18px 50px rgba(0,0,0,.30); padding:18px 20px; }
@media (max-width:640px) {
  /* small screens: popup drops to a bottom sheet; the ring still marks the target */
  .tour-pop { left:12px !important; right:12px !important; bottom:14px !important; top:auto !important;
    width:auto; transform:none !important; }
}
@media (prefers-reduced-motion:reduce) { .tour-ring.pulse { animation:none; } }

/* ── workout builder: drag-to-reorder sections ───────────── */
.bstep { display:flex; align-items:stretch; gap:8px; }
.bstep-reorder { flex:none; display:flex; flex-direction:column; align-items:center;
  justify-content:center; gap:1px; }
.bgrip { cursor:grab; color:var(--faint); font-size:14px; line-height:1; user-select:none; }
.bgrip:active { cursor:grabbing; }
.bstep-body { flex:1; min-width:0; }
.bmove { border:none; background:none; cursor:pointer; color:var(--faint); font-size:9px;
  line-height:1; padding:1px 3px; }
.bmove:hover:not(:disabled) { color:var(--green); }
.bmove:disabled { opacity:.28; cursor:default; }
.bstep.bdragging { opacity:.4; }
.bstep.bdrop > .bstep-body { outline:2px dashed var(--green); outline-offset:3px; border-radius:10px; }

/* ── #49 PWA: install banner + offline indicator ───────────── */
#install-banner { position:fixed; left:12px; right:12px; bottom:14px; z-index:9000;
  display:flex; align-items:center; gap:12px; background:var(--surface);
  border:1px solid var(--line); border-radius:12px; box-shadow:0 12px 34px rgba(0,0,0,.18);
  padding:12px 14px; font:500 13px/1.45 var(--sans); color:var(--ink); }
#install-banner span { flex:1; min-width:0; }
#install-banner #install-x { flex:none; border:none; background:none; color:var(--faint);
  font-size:15px; cursor:pointer; padding:4px 6px; }
@media (min-width:641px) { #install-banner { left:auto; right:18px; max-width:400px; } }
#offline-chip { position:fixed; left:50%; transform:translateX(-50%); bottom:14px; z-index:9001;
  background:var(--brown-tint); border:1px solid var(--brown-bd); color:var(--brown);
  border-radius:999px; padding:7px 14px; font:600 12px var(--sans);
  box-shadow:0 6px 18px rgba(0,0,0,.12); white-space:nowrap; }

/* ── Pull-to-refresh (touch only) ──────────────────────────────
   Installed PWAs run in standalone mode, which drops the browser chrome's own
   pull-to-refresh gesture — this is a hard reload (fresh HTML + fresh stamped
   assets), the touch equivalent of a desktop hard refresh, for whenever a deploy
   landed while the app was already open. */
#ptr { position:fixed; top:0; left:50%; z-index:9002; width:38px; height:38px;
  margin-left:-19px; margin-top:-38px; border-radius:50%; background:var(--surface);
  border:1px solid var(--line); box-shadow:0 6px 16px rgba(0,0,0,.14);
  display:flex; align-items:center; justify-content:center; color:var(--muted);
  transition:transform .18s ease, opacity .18s ease; opacity:0; pointer-events:none; }
#ptr svg { width:18px; height:18px; transition:transform .1s linear; }
#ptr.ptr-ready svg { color:var(--green); }
#ptr.ptr-spin svg { animation:ptr-spin .7s linear infinite; }
@keyframes ptr-spin { to { transform:rotate(360deg); } }
@media (prefers-reduced-motion:reduce) { #ptr, #ptr svg { transition:none; } #ptr.ptr-spin svg { animation:none; } }
