/* ============================================================
   VOLT design system, dark, electric, alive.
   ============================================================ */
:root {
  --kb: 0px;                 /* keyboard overlap, set from visualViewport */
  --lime: #C6FF00;
  --lime-soft: rgba(198, 255, 0, 0.14);
  --lime-glow: rgba(198, 255, 0, 0.45);
  --bg: #0A0A0C;
  --bg-raise: #101014;
  --card: #16161B;
  --card-2: #1C1C22;
  --line: rgba(255, 255, 255, 0.07);
  --text: #F2F2F0;
  --text-dim: #9B9BA3;
  --text-faint: #5D5D66;
  --danger: #FF5C5C;
  --radius: 18px;
  --radius-sm: 12px;
  /* spring-ish easings */
  --spring: cubic-bezier(.34, 1.56, .64, 1);
  --spring-soft: cubic-bezier(.22, 1.2, .36, 1);
  --ease-out: cubic-bezier(.16, 1, .3, 1);
  /* section undertones, subtle temperature shifts on card surfaces */
  --tint-lifts:     #16181C; --tint-lifts-2:     #1B1E24; --accent-lifts:     #9FB4CC;
  --tint-runs:      #14191A; --tint-runs-2:      #182022; --accent-runs:      #4FD8C6;
  --tint-nutrition: #1A1712; --tint-nutrition-2: #211C15; --accent-nutrition: #FFB454;
  --tint-sleep:     #16151E; --tint-sleep-2:     #1B1A26; --accent-sleep:     #A79BFF;
  --tint-alcohol:   #1A1416; --tint-alcohol-2:   #21181B; --accent-alcohol:   #E06C8A;
  --tint-habits:    #131813; --tint-habits-2:    #171E18; --accent-habits:    #6FCF8E;
  --tint-weight:    #17171A; --tint-weight-2:    #1D1D21; --accent-weight:    #C6FF00;
  --tint-routine:   #17171A; --tint-routine-2:   #1D1D21; --accent-routine:   #C6FF00;
  --tint-mood:      #13181C; --tint-mood-2:      #171E23; --accent-mood:      #7FD4FF;
  --tint-bike:      #1A1610; --tint-bike-2:      #211C14; --accent-bike:      #FFA657;
  --tint-swim:      #101820; --tint-swim-2:      #141F29; --accent-swim:      #5AC8FF;
  --tint-water:     #101A1E; --tint-water-2:     #142127; --accent-water:     #4FD8E8;
  --tint-caffeine:  #191320; --tint-caffeine-2:  #201929; --accent-caffeine:  #DB78E2;
  --tint-screen:    #1A1813; --tint-screen-2:    #211E17; --accent-screen:    #DBCB9F;
  --tint-cycle:     #1A1312; --tint-cycle-2:     #211917; --accent-cycle:     #E8604A;
  --tint-learn:     #17180F; --tint-learn-2:     #1D1F14; --accent-learn:     #BFC841;
  --tint-walk:      #141A12; --tint-walk-2:      #192017; --accent-walk:      #8ED96F;
  --tint-stairs:    #17151A; --tint-stairs-2:    #1D1B21; --accent-stairs:    #9985AD;
  --sat: env(safe-area-inset-top, 0px);
  --sab: env(safe-area-inset-bottom, 0px);
}

* { margin: 0; padding: 0; box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
html { color-scheme: dark; height: 100dvh; overflow: hidden; }
body {
  background: var(--bg);
  color: var(--text);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
  font-size: 16px;
  line-height: 1.45;
  /* The page itself does not scroll, #view does. See .app below.

     Pinned rather than merely `overflow: hidden`, and measured in dvh rather
     than percent, because those two were disagreeing. `height: 100%` resolves
     against the large viewport — the whole screen, including the strip the
     system bars sit over — while .app is 100dvh, the part you can actually
     see. Whenever the two differ the document is taller than the app inside
     it, and one stray drag on a non-scrolling area slides the whole thing up
     and leaves a band of background below the tab bar. Fixed positioning
     removes the document scroller altogether, so there is nothing to drag. */
  position: fixed; inset: 0;
  height: 100dvh; overflow: hidden;
  overscroll-behavior-y: none;
  -webkit-font-smoothing: antialiased;
  /* Every figure in the app is the same width as every other, so a number that
     ticks over does not shove the ones beside it. This is the app's default
     rather than a `.num` class 124 call sites have to remember, because the
     ones that forget are exactly the ones that jitter. The quotes opt back out
. They are running prose set in italic, and prose wants proportional
     figures. */
  font-variant-numeric: tabular-nums;
  user-select: none;
}
.nt-quote, .cat-quote { font-variant-numeric: normal; }
input, textarea, select { user-select: text; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }
[hidden] { display: none !important; }
svg { display: block; }

/* ---------- App shell ---------- */
/* The shell is exactly one screen tall and never scrolls; the view inside it
   does. The bar at the bottom used to be position:fixed, which is a promise
   the browser keeps only while nothing above it has a transform, a filter, or
   a compositing quirk. And on iOS it would sometimes end up painted halfway
   down a long page. As the last flex item of a fixed-height column there is
   nothing left to go wrong: it is where it is because of the layout, not
   because of an offset the compositor has to keep re-deriving. */
.app { display: flex; flex-direction: column; height: 100dvh; overflow: hidden; }
.topbar {
  position: relative; z-index: 40; flex: none;
  display: flex; align-items: center; justify-content: space-between;
  padding: calc(10px + var(--sat)) 18px 8px;
  background: color-mix(in srgb, var(--bg) 86%, transparent);
  backdrop-filter: blur(18px); -webkit-backdrop-filter: blur(18px);
}
.topbar-brand { display: flex; align-items: center; gap: 9px; color: var(--lime); }
.mark-sm { width: 26px; height: 26px; }
.topbar-title {
  font-weight: 800; letter-spacing: 0.14em; font-size: 15px; color: var(--text);
}
.topbar-right { display: flex; align-items: center; gap: 10px; color: var(--text-dim); font-size: 13px; }
.signal {
  position: absolute; left: 0; right: 0; bottom: -6px; width: 100%; height: 12px;
  pointer-events: none; opacity: 0.85;
  filter: drop-shadow(0 0 4px var(--lime-glow));
}

.view {
  flex: 1 1 auto; min-height: 0;          /* min-height:0 or flex refuses to shrink */
  overflow-y: auto; overflow-x: hidden;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-y: contain;
  padding: 14px 16px 24px;                 /* the bar is below it now, not over it */
  max-width: 720px; width: 100%; margin: 0 auto;
}
@media (min-width: 900px) { .view { max-width: 860px; } }

/* view transitions */
.view-enter { animation: view-in .42s var(--spring-soft) both; }
@keyframes view-in { from { opacity: 0; transform: translateY(14px) scale(.985); } to { opacity: 1; transform: none; } }
.view-enter-cal { animation: view-in-cal .45s var(--spring-soft) both; }
@keyframes view-in-cal { from { opacity: 0; transform: translateX(26px); } to { opacity: 1; transform: none; } }
.view-enter-lift { animation: view-in-lift .45s var(--spring) both; }
@keyframes view-in-lift { from { opacity: 0; transform: translateY(26px) scale(.97); } to { opacity: 1; transform: none; } }
.view-enter-run { animation: view-in-run .5s var(--ease-out) both; }
@keyframes view-in-run { from { opacity: 0; transform: translateX(-30px) skewX(-2deg); } to { opacity: 1; transform: none; } }
.view-enter-rise { animation: view-in-rise .5s var(--spring-soft) both; }
@keyframes view-in-rise { from { opacity: 0; transform: translateY(34px); } to { opacity: 1; transform: none; } }

/* ---------- Bottom nav ---------- */
.navbar {
  position: relative; z-index: 40; flex: none;
  display: flex; justify-content: space-around; align-items: flex-end;
  padding: 8px 8px calc(10px + var(--sab));
  background: color-mix(in srgb, var(--bg-raise) 88%, transparent);
  backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px);
  border-top: 1px solid var(--line);
}
.nav-btn {
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  color: var(--text-faint); font-size: 10.5px; font-weight: 600; letter-spacing: .02em;
  padding: 4px 10px; border-radius: 12px;
  transition: color .25s, transform .3s var(--spring);
  flex: 1; max-width: 96px;
}
/* The glyphs are line icons from the shared set now, so they must not be
   force-filled. That is what made Home a solid shape while its neighbours
   were outlines. */
.nav-ic { display: grid; place-items: center; height: 23px; }
.nav-btn svg { width: 23px; height: 23px; }
.nav-log .nav-ic { height: auto; }
.nav-log .nav-ic svg { width: 22px; height: 22px; stroke-width: 2.2; }
.nav-btn:active { transform: scale(.88); }
.nav-btn.active { color: var(--lime); }
.nav-btn.active svg { filter: drop-shadow(0 0 6px var(--lime-glow)); }
.nav-log-ring {
  display: grid; place-items: center;
  width: 44px; height: 44px; margin-top: -22px; border-radius: 50%;
  background: var(--lime); color: #0A0A0C;
  box-shadow: 0 4px 18px var(--lime-glow);
  transition: transform .3s var(--spring), box-shadow .3s;
}
.nav-log-ring svg { width: 22px; height: 22px; }
.nav-log:active .nav-log-ring { transform: scale(.9); }
.nav-log.active .nav-log-ring { box-shadow: 0 4px 26px var(--lime-glow), 0 0 0 5px var(--lime-soft); }

/* ---------- Cards ---------- */
.card {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 16px;
  margin-bottom: 12px;
  position: relative;
  transition: transform .32s var(--spring), box-shadow .3s, border-color .3s;
}
.card.tappable:active { transform: scale(.975); border-color: rgba(198,255,0,.25); }
.card-title {
  font-size: 12px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
  color: var(--text-dim); margin-bottom: 10px;
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
}
.card-title .link { text-transform: none; letter-spacing: 0; }
/* Something is wrong and it is not going to fix itself. Used sparingly, a
   refused backup is currently the only thing that earns it. */
.warn-card { border-color: rgba(255,92,92,.28); background: linear-gradient(160deg, #1F1517, #16161B); }
.warn-card b { color: var(--danger); }
.mono { font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: 11px; line-height: 1.5; }

/* section tints */
.tint-lifts     { background: linear-gradient(160deg, var(--tint-lifts-2), var(--tint-lifts)); }
.tint-runs      { background: linear-gradient(160deg, var(--tint-runs-2), var(--tint-runs)); }
.tint-nutrition { background: linear-gradient(160deg, var(--tint-nutrition-2), var(--tint-nutrition)); }
.tint-sleep     { background: linear-gradient(160deg, var(--tint-sleep-2), var(--tint-sleep)); }
.tint-alcohol   { background: linear-gradient(160deg, var(--tint-alcohol-2), var(--tint-alcohol)); }
.tint-caffeine  { background: linear-gradient(160deg, var(--tint-caffeine-2), var(--tint-caffeine)); }
.tint-screen    { background: linear-gradient(160deg, var(--tint-screen-2), var(--tint-screen)); }
.tint-cycle     { background: linear-gradient(160deg, var(--tint-cycle-2), var(--tint-cycle)); }
.tint-learn     { background: linear-gradient(160deg, var(--tint-learn-2), var(--tint-learn)); }
.tint-walk      { background: linear-gradient(160deg, var(--tint-walk-2), var(--tint-walk)); }
.tint-stairs    { background: linear-gradient(160deg, var(--tint-stairs-2), var(--tint-stairs)); }
.tint-habits    { background: linear-gradient(160deg, var(--tint-habits-2), var(--tint-habits)); }
.tint-mood      { background: linear-gradient(160deg, var(--tint-mood-2), var(--tint-mood)); }
.tint-bike      { background: linear-gradient(160deg, var(--tint-bike-2), var(--tint-bike)); }
.tint-swim      { background: linear-gradient(160deg, var(--tint-swim-2), var(--tint-swim)); }
.tint-weight    { background: linear-gradient(160deg, var(--tint-weight-2), var(--tint-weight)); }
.tint-routine   { background: linear-gradient(160deg, var(--tint-routine-2), var(--tint-routine)); }
.tint-water     { background: linear-gradient(160deg, var(--tint-water-2), var(--tint-water)); }

/* hero surface, frosted, for earned moments only */
.hero-surface {
  background: linear-gradient(150deg, rgba(255,255,255,.13), rgba(255,255,255,.05));
  border: 1px solid rgba(255,255,255,.18);
  backdrop-filter: blur(22px); -webkit-backdrop-filter: blur(22px);
  border-radius: 22px;
  box-shadow: 0 12px 40px rgba(0,0,0,.5), inset 0 1px 0 rgba(255,255,255,.16);
}

/* ---------- Type helpers ---------- */
.h1 { font-size: 26px; font-weight: 800; letter-spacing: -.02em; }
.h2 { font-size: 19px; font-weight: 750; letter-spacing: -.01em; }
.dim { color: var(--text-dim); }
.faint { color: var(--text-faint); }
.small { font-size: 13px; }
.tiny { font-size: 11.5px; }
.lime { color: var(--lime); }
.num { font-variant-numeric: tabular-nums; }
.link { color: var(--lime); font-weight: 650; font-size: 13.5px; }
.center { text-align: center; }
.row { display: flex; align-items: center; gap: 10px; }
.row-between { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.wrap { flex-wrap: wrap; }
.grow { flex: 1; min-width: 0; }
.mt4{margin-top:4px}.mt8{margin-top:8px}.mt12{margin-top:12px}.mt16{margin-top:16px}.mt24{margin-top:24px}
.mb8{margin-bottom:8px}.mb12{margin-bottom:12px}.mb18{margin-bottom:18px}

/* ---------- Buttons ---------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  padding: 12px 18px; border-radius: 14px;
  background: var(--card-2); border: 1px solid var(--line);
  font-weight: 700; font-size: 15px;
  transition: transform .28s var(--spring), box-shadow .25s, background .25s;
}
.btn:active { transform: scale(.94); }
.btn-primary {
  background: var(--lime); color: #0A0A0C; border: 0;
  box-shadow: 0 3px 16px rgba(198,255,0,.28);
}
.btn-primary:active { box-shadow: 0 2px 24px var(--lime-glow); }
.btn-ghost { background: transparent; border: 1px solid var(--line); color: var(--text-dim); }
.btn-danger { color: var(--danger); border-color: rgba(255,92,92,.3); background: rgba(255,92,92,.08); }
.btn-sm { padding: 8px 13px; font-size: 13.5px; border-radius: 11px; }
.btn-block { width: 100%; }
.btn-icon { padding: 9px; border-radius: 12px; }
.btn-icon svg { width: 18px; height: 18px; }

/* chips */
.chip {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 6px 12px; border-radius: 999px;
  background: var(--card-2); border: 1px solid var(--line);
  font-size: 13px; font-weight: 600; color: var(--text-dim);
  transition: transform .25s var(--spring), background .2s, color .2s, border-color .2s;
}
.chip:active { transform: scale(.92); }
.chip.on { background: var(--lime-soft); color: var(--lime); border-color: rgba(198,255,0,.35); }
.chip-row { display: flex; flex-wrap: wrap; gap: 7px; }
/* Filters above a long list. Wrapping 13 muscles and 7 kinds of kit costs five
   lines of a phone screen, which is five lines the results do not get, so these
   stay on one line each and scroll sideways. */
.chip-scroll { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none;
  -webkit-overflow-scrolling: touch; padding-bottom: 2px; }
.chip-scroll::-webkit-scrollbar { display: none; }
.chip-scroll > .chip { flex: none; white-space: nowrap; }

/* ---------- Forms ---------- */
.field { margin-bottom: 13px; }
.field label { display: block; font-size: 12px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--text-dim); margin-bottom: 6px; }
.input, textarea.input, select.input {
  width: 100%; padding: 13px 14px;
  background: var(--bg-raise); border: 1.5px solid var(--line); border-radius: 13px;
  color: var(--text); font-size: 16px;
  transition: border-color .25s, box-shadow .25s;
  appearance: none;
}
.input:focus { outline: none; border-color: var(--lime); box-shadow: 0 0 0 3px var(--lime-soft), 0 0 14px rgba(198,255,0,.12); }

/* A number outside what the field can hold. Marked as you type rather than on
   Save, so the answer arrives while you are still looking at the field. The
   focus ring stays lime: the field is wrong, not dangerous, and turning the
   whole form red over a mistyped digit is a scolding. */
.input.bad { border-color: var(--accent-alcohol); background: rgba(224,108,138,.06); }
.input.bad:focus { box-shadow: 0 0 0 3px rgba(224,108,138,.18); }
.field-err {
  margin: 5px 2px 0; color: var(--accent-alcohol); font-weight: 600; line-height: 1.35;
  animation: err-in .22s var(--spring) both;
}
@keyframes err-in { from { opacity: 0; transform: translateY(-3px); } }
textarea.input { min-height: 74px; resize: vertical; }
.input-row { display: flex; gap: 9px; }
.input-row .field { flex: 1; margin-bottom: 0; }
.seg { display: flex; background: var(--bg-raise); border: 1px solid var(--line); border-radius: 13px; padding: 3px; gap: 3px; }
.seg button {
  flex: 1; padding: 9px 6px; border-radius: 10px; font-size: 13.5px; font-weight: 650; color: var(--text-dim);
  transition: background .22s, color .22s, transform .25s var(--spring);
}
.seg button:active { transform: scale(.95); }
/* Ten options in a row that was drawn for four. The padding goes, not the
   height. A 1-to-10 scale is still a thumb target, so it keeps its full
   vertical hit area and only gives up the space either side of the digits. */
.seg-tight { gap: 2px; padding: 3px 2px; }
.seg-tight button { padding: 10px 0; font-size: 13px; letter-spacing: -.02em; }
.seg button.on { background: var(--lime); color: #0A0A0C; }

/* stepper (fast numeric entry) */
.stepper { display: flex; align-items: center; gap: 8px; }
.stepper .input { text-align: center; font-weight: 800; font-size: 19px; }
.stepper button {
  width: 44px; height: 44px; border-radius: 12px; flex: none;
  background: var(--card-2); border: 1px solid var(--line);
  font-size: 20px; font-weight: 800; color: var(--lime);
  transition: transform .25s var(--spring);
}
.stepper button:active { transform: scale(.86); }

/* ---------- Bottom sheet ---------- */
.sheet-layer { position: fixed; inset: 0; z-index: 80; }
.sheet-backdrop { position: absolute; inset: 0; background: rgba(0,0,0,.42); opacity: 0; transition: opacity .3s; }
.sheet-layer.open .sheet-backdrop { opacity: 1; }
.sheet {
  position: absolute; left: 0; right: 0; bottom: 0;
  max-height: 88dvh; overflow-y: auto;
  /* Scroll anchoring keeps what you were looking at in place when content
     changes above it, which is right for a page and wrong for a sheet whose
     entire contents are replaced: it would put the old offset back. */
  overflow-anchor: none;
  background: var(--bg-raise);
  border-radius: 24px 24px 0 0;
  border-top: 1px solid rgba(255,255,255,.1);
  padding: 8px 18px calc(22px + var(--sab));
  transform: translateY(102%);
  transition: transform .42s var(--spring-soft);
  max-width: 720px; margin: 0 auto;
}
.sheet-layer.open .sheet { transform: translateY(0); }
/* A sheet you type into must not change shape while you type. Height is fixed
   rather than driven by the content, and the sheet sits above the keyboard
   instead of behind it, so the search box stays exactly where you left it and
   the results scroll under it. */
.sheet.sheet-tall {
  bottom: var(--kb, 0px);
  height: min(88dvh, calc(100dvh - var(--kb, 0px) - 20px));
  display: flex; flex-direction: column; overflow: hidden;
  padding-bottom: 10px;
  transition: transform .42s var(--spring-soft), height .2s ease, bottom .2s ease;
}
.sheet.sheet-tall > .sheet-grip { flex: none; }
.sheet.sheet-tall > .sheet-body {
  flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column;
  /* The fallback that stops `tall` being a trap. A tall sheet hides its own
     overflow and hands scrolling to a .sheet-scroll child; one written without
     that child could not scroll at all, and simply ran off the bottom of the
     screen. With a scroll region present the body fits exactly and this does
     nothing; without one, the body scrolls. */
  overflow-y: auto; -webkit-overflow-scrolling: touch;
}
/* Everything except the scroll region is fixed. Flex items shrink by default,
   so the grip and the search box were each giving up a fraction of a pixel as
   the list changed size. Which is a search box that drifts while you type. */
.sheet.sheet-tall > .sheet-body > :not(.sheet-scroll) { flex: none; }
.sheet-scroll { flex: 1 1 auto; min-height: 0; overflow-y: auto; -webkit-overflow-scrolling: touch; }
.sheet-grip { width: 40px; height: 4.5px; border-radius: 3px; background: rgba(255,255,255,.18); margin: 6px auto 14px; }
.sheet-title { font-size: 19px; font-weight: 800; margin-bottom: 14px; display:flex; align-items:center; justify-content: space-between; }
/* A title that opts into .row is an icon followed by a name, in nine sheets
   across the app. space-between pushed the two to opposite ends of the screen
   with a hand's width of nothing between them, which read as a layout bug
   because it was one. One of them had already worked around it locally. */
.sheet-title.row { justify-content: flex-start; }

/* ---------- Toast ---------- */
.toast-layer { position: fixed; left: 0; right: 0; bottom: calc(92px + var(--sab)); z-index: 90; display: flex; flex-direction: column; align-items: center; gap: 8px; pointer-events: none; }
.toast {
  background: var(--card-2); border: 1px solid rgba(198,255,0,.25);
  color: var(--text); font-size: 14px; font-weight: 650;
  padding: 11px 18px; border-radius: 999px;
  box-shadow: 0 6px 24px rgba(0,0,0,.5), 0 0 18px rgba(198,255,0,.08);
  animation: toast-in .45s var(--spring) both;
  display: flex; gap: 8px; align-items: center;
}
.toast.out { animation: toast-out .3s ease both; }
@keyframes toast-in { from { opacity: 0; transform: translateY(16px) scale(.9); } to { opacity: 1; transform: none; } }
@keyframes toast-out { to { opacity: 0; transform: translateY(10px) scale(.94); } }

/* ---------- Gate (splash + login) ---------- */
.gate {
  position: fixed; inset: 0; z-index: 100;
  background: radial-gradient(120% 90% at 50% 0%, #101014 0%, #0A0A0C 60%);
  display: grid; place-items: center;
  transition: opacity .5s ease, transform .6s var(--ease-out);
}
.gate.leaving { opacity: 0; transform: scale(1.06); pointer-events: none; }
/* A boot sequence replayed on demand, over the app, to preview one. */
.splash-demo { z-index: 130; cursor: pointer; }

/* ---------- Preview bar ---------- */
/* Sits above the nav and outlives the view you started from, so you can wander
   off and look at the thing you are trying on and still find the way back. */
.preview-bar {
  position: fixed; left: 10px; right: 10px; z-index: 95;
  bottom: calc(84px + var(--sab));
  display: flex; align-items: center; gap: 8px;
  padding: 10px 12px; border-radius: 16px;
  background: color-mix(in srgb, var(--bg-raise) 92%, transparent);
  /* the accent, not a hardcoded lime: you may well be previewing an accent */
  border: 1px solid color-mix(in srgb, var(--lime) 32%, transparent);
  box-shadow: 0 10px 30px rgba(0,0,0,.55), 0 0 22px var(--lime-soft);
  backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px);
  max-width: 700px; margin: 0 auto;
  transform: translateY(18px); opacity: 0;
  transition: transform .3s var(--spring), opacity .25s ease;
}
.preview-bar.in { transform: none; opacity: 1; }
.pv-lab { font-size: 13.5px; font-weight: 750; }
.preview-bar .btn { flex: none; }
.gate-inner { width: min(340px, 88vw); text-align: center; position: relative; }
/* An SVG clips to its own viewport by default, so a glow on anything inside it
   stops dead at the edge of the box. Which is how a soft halo became a hard
   square sitting behind the mark. */
.mark { overflow: visible; }
.mark-lg { width: 108px; height: 108px; margin: 0 auto 10px; color: var(--lime); filter: drop-shadow(0 0 7px var(--lime-glow)); }
/* The dash only exists where something is going to draw it. Setting it
   globally hid the topbar mark, and every boot that drops or fades the bolt in
   rather than drawing it, rendered nothing at all.
   The numbers are 100 because the path carries pathLength="100". They used to
   be 200 against a path 104 units long, so the offset spent the first 48% of
   the animation travelling from 200 down to 104 with nothing on screen, and
   then drew the whole bolt in the back half, at the fastest part of an
   ease-out curve. That is why it appeared to fill up all at once. */
.mark-path { stroke-dasharray: none; stroke-dashoffset: 0; }
.mark-draw .mark-path {
  stroke-dasharray: 100; stroke-dashoffset: 100;
  animation: mark-draw 1.8s cubic-bezier(.5,.02,.22,1) forwards;
}
@keyframes mark-draw { to { stroke-dashoffset: 0; } }
.mark-idle .mark-path { animation: mark-pulse 3.4s ease-in-out 2.1s infinite; }
/* One `animation` declaration wins outright, so a mark that both draws itself
   and then breathes has to say so in a single rule. It said it in two, and the
   pulse replaced the draw. */
.mark-draw.mark-idle .mark-path {
  animation: mark-draw 1.8s cubic-bezier(.5,.02,.22,1) forwards,
             mark-pulse 3.4s ease-in-out 2.1s infinite;
}
@keyframes mark-pulse {
  0%, 100% { filter: none; opacity: 1; }
  50% { filter: drop-shadow(0 0 6px var(--lime-glow)); opacity: .82; }
}
.wordmark {
  font-size: 42px; font-weight: 900; letter-spacing: .34em; margin-left: .34em;
  color: var(--text);
  opacity: 0; animation: word-in .7s var(--ease-out) 1.15s forwards;
}
@keyframes word-in {
  0% { opacity: 0; transform: translateY(12px); letter-spacing: .5em; }
  60% { text-shadow: 0 0 24px var(--lime-glow); }
  100% { opacity: 1; transform: none; text-shadow: 0 0 0 transparent; }
}
.gate-tag { color: var(--text-faint); font-size: 12.5px; letter-spacing: .22em; text-transform: uppercase; margin-top: 6px; opacity: 0; animation: fade-in .6s ease 1.65s forwards; }
@keyframes fade-in { to { opacity: 1; } }
.gate-form { margin-top: 34px; opacity: 0; animation: fade-in .5s ease 1.9s forwards; }
.gate-form .input { text-align: center; background: rgba(255,255,255,.05); }
.gate-form.shake { animation: gate-shake .5s var(--ease-out); }
@keyframes gate-shake {
  0%,100% { transform: translateX(0); }
  20% { transform: translateX(-10px); } 40% { transform: translateX(9px); }
  60% { transform: translateX(-6px); } 80% { transform: translateX(4px); }
}
.gate-err { color: var(--danger); font-size: 13px; margin-top: 10px; min-height: 18px; }

/* ---------- Rings, charts, viz ---------- */
.chart-wrap { position: relative; }
.chart-wrap svg { width: 100%; height: auto; }
.glow-num { filter: drop-shadow(0 0 10px var(--lime-glow)); }
.ring-label { position: absolute; inset: 0; display: grid; place-items: center; text-align: center; pointer-events: none; }
.ring-label .big { font-size: 30px; font-weight: 850; line-height: 1; }
.ring-anim circle.fg { transition: stroke-dashoffset 1s var(--ease-out); }

/* heatmap grid (GitHub-style) */
.heat { display: grid; grid-auto-flow: column; grid-template-rows: repeat(7, 1fr); gap: 3px; }
.heat i {
  width: 11px; height: 11px; border-radius: 3px; background: rgba(255,255,255,.06);
  transition: transform .2s var(--spring);
}
.heat i:active { transform: scale(1.5); }
.heat i.l1 { background: color-mix(in srgb, var(--heat-color, var(--lime)) 30%, #14141a); }
.heat i.l2 { background: color-mix(in srgb, var(--heat-color, var(--lime)) 55%, #14141a); }
.heat i.l3 { background: var(--heat-color, var(--lime)); box-shadow: 0 0 5px color-mix(in srgb, var(--heat-color, var(--lime)) 60%, transparent); }
.heat i.miss { background: rgba(255,92,92,.28); }

/* streak flame / signal chip */
.streak-chip {
  display: inline-flex; align-items: center; gap: 6px;
  font-weight: 800; font-size: 14px; color: var(--lime);
  background: var(--lime-soft); border: 1px solid rgba(198,255,0,.25);
  padding: 6px 12px; border-radius: 999px;
}
.streak-chip svg { width: 15px; height: 10px; }

/* odometer */
.odo { display: inline-flex; overflow: hidden; font-variant-numeric: tabular-nums; font-weight: 850; }
.odo .digit { display: inline-block; }
.odo-roll { animation: odo-roll .1s linear; }

/* ---------- Calendar ---------- */
.cal-head { display: grid; grid-template-columns: repeat(7, 1fr); text-align: center; font-size: 11px; font-weight: 700; color: var(--text-faint); text-transform: uppercase; letter-spacing: .06em; margin-bottom: 6px; }
.cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; }
.cal-day {
  aspect-ratio: 1 / 1.15; border-radius: 13px; position: relative;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
  background: var(--card); border: 1px solid transparent;
  font-size: 14px; font-weight: 650; color: var(--text-dim);
  transition: transform .28s var(--spring), border-color .25s, background .25s;
}
.cal-day:active { transform: scale(.88); }
.cal-day.other { opacity: .28; }
.cal-day.today { border-color: var(--lime); color: var(--lime); box-shadow: 0 0 10px rgba(198,255,0,.12); }
.cal-day.paused { background: repeating-linear-gradient(135deg, var(--card), var(--card) 5px, #1b1b22 5px, #1b1b22 8px); }
.cal-dots { display: flex; gap: 3px; height: 5px; }
.cal-dots i { width: 5px; height: 5px; border-radius: 50%; }
.cal-day .goal-flag { position: absolute; top: 4px; right: 5px; font-size: 9px; }

/* dot colors per category */
.dot-lift{background:var(--accent-lifts)} .dot-run{background:var(--accent-runs)}
.dot-meal{background:var(--accent-nutrition)} .dot-sleep{background:var(--accent-sleep)}
.dot-alcohol{background:var(--accent-alcohol)} .dot-habit{background:var(--accent-habits)}
.dot-caffeine{background:var(--accent-caffeine)}
.dot-cycle{background:var(--accent-cycle)}
.dot-water{background:var(--accent-water)} .dot-screen{background:var(--accent-screen)}
.dot-learn{background:var(--accent-learn)}
.dot-weight{background:var(--lime)} .dot-task{background:#fff} .dot-note{background:var(--text-faint)}
.dot-mood{background:var(--accent-mood)}

/* ---------- Checklists ---------- */
.check-item {
  display: flex; align-items: center; gap: 12px; padding: 11px 4px;
  border-bottom: 1px solid var(--line); transition: opacity .3s;
}
.check-item:last-child { border-bottom: 0; }
.check-box {
  width: 26px; height: 26px; border-radius: 9px; flex: none;
  border: 2px solid var(--text-faint); display: grid; place-items: center;
  transition: all .3s var(--spring); color: transparent;
}
.check-box svg { width: 15px; height: 15px; }
.check-item.done .check-box,
/* A checklist ticks things off; a settings list turns things on. Same control,
   two meanings, and only the first one earns a line through the label. */
.check-box.on { background: var(--lime); border-color: var(--lime); color: #0A0A0C; box-shadow: 0 0 10px rgba(198,255,0,.3); transform: scale(1.05); }
label.check-item { cursor: pointer; }
label.check-item:active .check-box { transform: scale(.92); }
.check-item.done .check-label { color: var(--text-faint); text-decoration: line-through; }
.check-label { flex: 1; font-weight: 600; font-size: 15px; }
.check-sub { font-size: 12px; color: var(--text-faint); }

/* A fold for the fields most people never fill. Shut, the meal form is a name
   and four boxes, which is the whole reason it is quick. */
.more-fields > summary {
  display: flex; align-items: center; gap: 6px; cursor: pointer; list-style: none;
  padding: 7px 2px; font-size: 12.5px; font-weight: 700; color: var(--text-dim);
  transition: color .2s;
}
.more-fields > summary::-webkit-details-marker { display: none; }
.more-fields > summary:active { color: var(--lime); }
.more-fields[open] > summary { color: var(--text); }
.more-fields[open] > summary .ic { transform: rotate(45deg); transition: transform .25s var(--spring); }

/* the diet split: one bar, three shares, sized by what they are */
.diet-split { display: flex; gap: 3px; margin: 2px 0 12px; }
.ds-part {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  min-width: 0; padding: 7px 2px; border-radius: 9px; overflow: hidden;
  background: color-mix(in srgb, var(--c) 16%, transparent);
  border: 1px solid color-mix(in srgb, var(--c) 34%, transparent);
}
.ds-part b { font-size: 15px; font-weight: 800; color: var(--c); line-height: 1.1; }
.ds-part i {
  font-style: normal; font-size: 10px; font-weight: 600; color: var(--text-faint);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%;
}

/* nutrient picker: the name, its week, and what it averages */
.nut-row.on { border-color: color-mix(in srgb, var(--lime) 40%, transparent); }
.nut-dot { width: 10px; height: 10px; border-radius: 50%; flex: none; }
.nut-spark { display: flex; align-items: flex-end; gap: 2px; height: 22px; width: 54px; flex: none; }
.nut-spark i { flex: 1; border-radius: 1.5px; opacity: .75; min-height: 2px; }

/* water: a number you tap up and down, and how full the day is */
.wt-now { text-align: center; padding: 6px 0 2px; }
.wt-big { display: flex; align-items: baseline; justify-content: center; gap: 5px; }
.wt-big b { font-size: 42px; font-weight: 800; line-height: 1; color: var(--accent-water); }
.wt-track {
  height: 9px; border-radius: 99px; margin: 12px 0 0; overflow: hidden;
  background: rgba(255,255,255,.07);
}
.wt-track i {
  display: block; height: 100%; border-radius: 99px;
  background: linear-gradient(90deg, color-mix(in srgb, var(--accent-water) 55%, transparent), var(--accent-water));
  transition: width .4s var(--spring);
}
.wt-track i.full { box-shadow: 0 0 12px color-mix(in srgb, var(--accent-water) 55%, transparent); }

/* A card sharing its row. The row spaces the pair; a card that also spaces
   itself leaves a gap under one of them and not the other. */
.pair-card { margin-bottom: 0; height: 100%; }
.row > .grow > .pair-card { margin-top: 0; }

/* The button that belongs to a coloured card takes that card's colour. A lime
   primary inside the water card reads as the app's button parked on it. */
.btn-water {
  background: color-mix(in srgb, var(--accent-water) 18%, transparent);
  border-color: color-mix(in srgb, var(--accent-water) 45%, transparent);
  color: var(--accent-water); font-weight: 750;
}
.btn-water:active { background: color-mix(in srgb, var(--accent-water) 28%, transparent); }

/* the profile gate: which of you is this */
.gate-others { display: flex; flex-wrap: wrap; gap: 7px; justify-content: center; margin-top: 12px; }
.gate-other {
  padding: 6px 13px; border-radius: 999px; font-size: 12.5px; font-weight: 650;
  color: var(--text-dim); background: rgba(255,255,255,.05); border: 1px solid var(--line);
  transition: transform .2s var(--spring), color .2s, border-color .2s;
}
.gate-other:active { transform: scale(.94); color: var(--lime); border-color: var(--lime); }
.gate-note { margin-top: 10px; color: var(--text-faint); text-align: center; }
.list-item.pf-on { border-color: color-mix(in srgb, var(--lime) 34%, transparent); }

/* arranging the Home screen, on the Home screen */
.arr-hint { border-color: color-mix(in srgb, var(--lime) 30%, transparent); }
.arr-cell {
  position: relative; border-radius: 20px; padding: 2px;
  border: 1.5px dashed color-mix(in srgb, var(--lime) 35%, transparent);
  margin-bottom: 12px; touch-action: none; user-select: none;
  transition: transform .2s var(--spring), border-color .2s, opacity .25s;
}
.arr-cell.is-hidden { opacity: .38; border-style: dotted; }
.arr-cell.is-hidden .arr-eye { color: var(--text-faint); }
.arr-cell.arr-drag {
  border-color: var(--lime); border-style: solid;
  transform: scale(1.02); box-shadow: 0 14px 40px rgba(0,0,0,.5);
  z-index: 3;
}
.arr-bar {
  display: flex; align-items: center; gap: 7px; padding: 3px 9px 4px;
  color: var(--text-dim);
}
.arr-bar b { text-transform: uppercase; letter-spacing: .07em; font-size: 10.5px; }
.arr-grip { color: var(--lime); display: inline-flex; }
.arr-eye {
  width: 26px; height: 26px; flex: none; border-radius: 8px; display: grid; place-items: center;
  color: var(--lime); background: rgba(255,255,255,.05); border: 1px solid var(--line);
}
.arr-eye:active { transform: scale(.9); }
/* The card inside is being moved, not used. Nothing in it should look tappable
   or animate under the finger. */
.arr-body { pointer-events: none; }
.arr-body > .card { margin-bottom: 0; }
/* Fixed rather than sticky. Sticky resolves against the scroll container, and
   this one is the last child of a full-height view, so it pinned itself over
   the middle of the page instead of the bottom of the screen. */
.arr-done {
  position: fixed; left: 14px; right: 14px; bottom: calc(var(--sab) + 74px); z-index: 40;
}
.arr-done .btn { box-shadow: 0 10px 30px rgba(0,0,0,.55); }
/* Room for it, so the last card is not permanently underneath. */
.view.arranging { padding-bottom: 96px; }

/* reorderable list of home cards */
.ord-list { display: flex; flex-direction: column; gap: 7px; }
.ord-row {
  display: flex; align-items: center; gap: 9px; padding: 9px 10px;
  border-radius: 13px; background: rgba(255,255,255,.04); border: 1px solid var(--line);
  touch-action: none; transition: opacity .25s, border-color .25s, transform .18s var(--spring);
}
.ord-row.dragging { border-color: var(--lime); background: rgba(198,255,0,.08); transform: scale(1.02); }
.ord-row.hidden { opacity: .42; }
.ord-row.hidden .ord-eye { color: var(--text-faint); }
.ord-row.hidden b { text-decoration: line-through; }
/* A card whose tracker is off cannot be placed anywhere useful, so it says so
   rather than pretending the choice will stick. */
.ord-row.unavailable { opacity: .38; }
.ord-row.unavailable [data-grip], .ord-row.unavailable [data-eye] { pointer-events: none; opacity: .4; }
.ord-grip { color: var(--text-faint); cursor: grab; padding: 4px 2px; }
.ord-grip:active { cursor: grabbing; }
.ord-w { flex: none; width: 26px; text-align: right; }
.ord-btn {
  width: 28px; height: 28px; flex: none; border-radius: 9px; display: grid; place-items: center;
  color: var(--text-dim); background: rgba(255,255,255,.05); border: 1px solid var(--line);
  transition: transform .2s var(--spring), color .2s;
}
.ord-btn:active { transform: scale(.9); color: var(--lime); }
.ord-btn[data-up] .ic { transform: rotate(-90deg); }
.ord-btn[data-down] .ic { transform: rotate(90deg); }
.ord-eye { color: var(--lime); }
.check-item.locked { opacity: .72; }
.check-item.locked .check-box { border-color: var(--line); }

/* ---------- Celebration ---------- */
.celebrate-layer {
  position: fixed; inset: 0; z-index: 95; display: grid; place-items: center;
  background: rgba(5,5,7,.72); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  animation: fade-in .3s ease both;
}
.celebrate-card {
  width: min(340px, 86vw); padding: 30px 24px; text-align: center;
  animation: celebrate-pop .65s var(--spring) both;
}
@keyframes celebrate-pop { from { opacity: 0; transform: scale(.6) translateY(30px); } to { opacity: 1; transform: none; } }
/* the seal, an unlock assembling itself around the icon */
.seal { position: relative; width: 108px; height: 108px; margin: 0 auto 16px; }
.seal-svg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible;
  fill: none; stroke: var(--lime); stroke-linecap: round; stroke-linejoin: round; }
.seal-ret {
  stroke-width: 2; opacity: .85;
  transform-origin: 60px 60px;
  animation: seal-ret .85s cubic-bezier(.16,.9,.24,1) both;
}
.seal-ret.b { animation-name: seal-ret-b; }
@keyframes seal-ret {
  0% { opacity: 0; transform: rotate(-150deg) scale(1.35); }
  55% { opacity: .9; }
  100% { opacity: .35; transform: rotate(0) scale(1); }
}
@keyframes seal-ret-b {
  0% { opacity: 0; transform: rotate(150deg) scale(1.35); }
  55% { opacity: .9; }
  100% { opacity: .35; transform: rotate(0) scale(1); }
}
.seal-hex {
  stroke-width: 2.6; stroke-dasharray: 300; stroke-dashoffset: 300;
  filter: drop-shadow(0 0 7px var(--lime-glow));
  transform-origin: 60px 60px;
  animation: seal-hex .8s cubic-bezier(.2,.8,.25,1) .16s both;
}
@keyframes seal-hex {
  0% { stroke-dashoffset: 300; transform: scale(1.5) rotate(-25deg); opacity: 0; }
  40% { opacity: 1; }
  75% { transform: scale(.93) rotate(0); }
  100% { stroke-dashoffset: 0; transform: scale(1) rotate(0); opacity: 1; }
}
.seal-tick {
  stroke-width: 2.4; opacity: 0; transform-box: view-box; transform-origin: 60px 60px;
  animation: seal-tick .45s var(--ease-out) both;
  animation-delay: calc(.62s + var(--i) * .045s);
}
@keyframes seal-tick {
  0% { opacity: 0; transform: rotate(calc(var(--i) * 60deg)) scale(1.5); }
  60% { opacity: 1; }
  100% { opacity: .55; transform: rotate(calc(var(--i) * 60deg)) scale(1); }
}
.seal-core {
  position: absolute; inset: 30px; border-radius: 16px; display: grid; place-items: center;
  color: #0A0A0C;
  background: radial-gradient(circle, var(--lime) 0%, var(--lime) 55%, transparent 78%);
  animation: seal-core .7s cubic-bezier(.2,.9,.25,1) .34s both;
}
@keyframes seal-core {
  0% { transform: scale(.3); opacity: 0; filter: blur(6px); }
  60% { transform: scale(1.16); opacity: 1; filter: blur(0); }
  100% { transform: scale(1); }
}

/* full-screen shockwave + border trace + rising motes behind the card */
.fx-seal { position: fixed; inset: 0; z-index: 96; pointer-events: none; overflow: hidden; }
.fx-seal .seal-wave {
  position: absolute; left: 50%; top: 50%; width: 120px; height: 120px; margin: -60px 0 0 -60px;
  border-radius: 50%; border: 2px solid var(--lime); opacity: 0;
  animation: seal-wave 1.15s cubic-bezier(.15,.75,.3,1) both;
}
.fx-seal .w2 { animation-delay: .13s; }
.fx-seal .w3 { animation-delay: .26s; border-width: 1px; }
@keyframes seal-wave {
  0% { transform: scale(.2); opacity: 0; }
  18% { opacity: .55; }
  100% { transform: scale(9); opacity: 0; }
}
.fx-seal .seal-edge {
  position: absolute; inset: 6px; border-radius: 26px;
  border: 1.5px solid var(--lime);
  clip-path: inset(0 0 0 0 round 26px);
  opacity: 0; animation: seal-edge 1.2s var(--ease-out) .2s both;
  box-shadow: 0 0 20px var(--lime-glow), inset 0 0 26px color-mix(in srgb, var(--lime) 10%, transparent);
}
@keyframes seal-edge {
  0% { opacity: 0; transform: scale(1.03); }
  25% { opacity: .75; transform: scale(1); }
  100% { opacity: 0; }
}
.fx-seal .seal-mote {
  position: absolute; left: var(--x); bottom: -10px; width: 4px; height: 4px; border-radius: 50%;
  background: var(--lime); box-shadow: 0 0 8px var(--lime);
  opacity: 0; animation: seal-mote 1.5s var(--ease-out) both; animation-delay: var(--d);
}
@keyframes seal-mote {
  0% { opacity: 0; transform: translateY(0) scale(var(--s)); }
  25% { opacity: .9; }
  100% { opacity: 0; transform: translateY(-70vh) scale(calc(var(--s) * .4)); }
}
@media (prefers-reduced-motion: reduce) {
  .seal-ret, .seal-hex, .seal-tick, .seal-core { animation-duration: .01ms !important; }
  .seal-hex { stroke-dashoffset: 0; }
}

/* ---------- Trophy case ---------- */

/* ---------- Misc ---------- */
.divider { height: 1px; background: var(--line); margin: 14px 0; }
.pill-list { display: flex; flex-direction: column; gap: 8px; }
.list-item {
  display: flex; align-items: center; gap: 12px; padding: 12px 14px;
  background: var(--card); border: 1px solid var(--line); border-radius: 14px;
  transition: transform .28s var(--spring);
}
.list-item:active { transform: scale(.97); }
.badge-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--lime); box-shadow: 0 0 6px var(--lime-glow); }
.photo-thumb { width: 52px; height: 52px; border-radius: 10px; object-fit: cover; border: 1px solid var(--line); }
.photo-strip { display: flex; gap: 8px; overflow-x: auto; padding: 4px 0; }
.photo-strip img { width: 84px; height: 84px; border-radius: 12px; object-fit: cover; flex: none; }
.empty {
  text-align: center; color: var(--text-faint); padding: 26px 14px; font-size: 14px;
}
.pr-flash { animation: pr-flash 1.6s var(--ease-out); }
@keyframes pr-flash { 0% { box-shadow: 0 0 0 0 var(--lime-glow); } 100% { box-shadow: 0 0 0 26px transparent; } }
/* A pause is a fact about today, not a headline. It lives in the plan card's
   header as a chip: small, quiet, and countable down when it has an end. */
.ct-right { display: flex; align-items: center; gap: 8px; text-transform: none; letter-spacing: 0; }
.pause-chip {
  display: inline-flex; align-items: center; gap: 3px;
  font-size: 10.5px; font-weight: 600; letter-spacing: 0; text-transform: none;
  color: rgba(167,155,255,.62); background: rgba(167,155,255,.07);
  border: 1px solid rgba(167,155,255,.15); border-radius: 999px;
  padding: 1px 7px 1px 5px; line-height: 1.5;
}
.pause-chip svg { opacity: .65; }
/* The date you set has come round. Still not loud. But this one is worth
   noticing, because it is the way out rather than the thing you're waiting on. */
.pause-chip.due {
  color: rgba(198,255,0,.75); background: rgba(198,255,0,.08); border-color: rgba(198,255,0,.22);
}
.pz-lead {
  display: flex; gap: 10px; align-items: center; font-size: 14px;
  background: rgba(167,155,255,.10); border: 1px solid rgba(167,155,255,.24);
  border-radius: var(--radius); padding: 12px 14px; margin-bottom: 12px;
}
.pz-lead.due { background: rgba(198,255,0,.09); border-color: rgba(198,255,0,.28); color: var(--lime); }
.pz-lead.due .tiny { color: var(--text-dim); }
.tag { font-size: 12px; color: var(--accent-runs); font-weight: 650; }
.note-block { background: var(--bg-raise); border-left: 3px solid var(--lime); border-radius: 8px; padding: 9px 12px; font-size: 13.5px; color: var(--text-dim); font-style: italic; }
.summary-out { white-space: pre-wrap; font-size: 14px; line-height: 1.65; user-select: text; }
/* ---------- the glance strip ----------
   Every category page opens with one: the numbers you came for, above the
   first chart. It replaced two half-width cards that laid themselves out
   independently, so a title wrapping to two lines dropped its number a line
   below its neighbour's. Subgrid puts all the values on one row and all the
   labels on the next, whatever the labels do. */
.strip-card { padding: 14px 12px; }
.stat-strip {
  display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
  grid-template-rows: auto auto; gap: 3px 6px; align-items: end;
}
.ss-cell {
  display: grid; grid-template-rows: subgrid; grid-row: span 2;
  text-align: center; min-width: 0;
}
.ss-val { font-size: 27px; font-weight: 850; line-height: 1.05; letter-spacing: -.02em; }
.ss-lab {
  font-size: 9.5px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  color: var(--text-faint); line-height: 1.25;
}
/* Subgrid landed in Safari 16; before that the two rows fall back to auto and
   the cells simply align at the bottom, which is what align-items:end is for. */

/* ---------- muscle grid ---------- */
/* Four across, not three. Twelve muscles in three columns is four rows and
   most of a screen for what is meant to be a glance. */
.muscle-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 6px; }
.muscle-cell {
  position: relative; overflow: hidden;
  border-radius: 11px; padding: 8px 7px; text-align: center; width: 100%;
  background: var(--bg-raise); border: 1px solid var(--line);
  font-size: 10.5px; font-weight: 650; color: var(--text-faint);
  transition: transform .25s var(--spring);
}
.muscle-cell:active { transform: scale(.94); }
.muscle-cell.on {
  color: #fff; border-color: rgba(159,180,204,.32);
  background: color-mix(in srgb, var(--accent-lifts) var(--fill, 20%), var(--bg-raise));
}
.mc-name { display: block; text-transform: capitalize; line-height: 1.2; }
.mc-n { display: block; font-size: 14.5px; font-weight: 800; margin-top: 1px; }
/* Indirect work rides behind the direct number rather than being added into it
, four sets of curls and four sets of rows are not the same four sets. */
.mc-ind { font-size: 10.5px; font-weight: 700; font-style: normal; opacity: .55; margin-left: 2px; }

/* the exercise the progression chart is showing, as a button rather than
   twenty chips wrapped over six rows */
.pickbtn {
  display: inline-flex; align-items: center; gap: 3px;
  padding: 4px 8px 4px 11px; border-radius: 999px;
  background: var(--card-2); border: 1px solid var(--line);
  font-size: 12.5px; font-weight: 700; color: var(--text); text-transform: none; letter-spacing: 0;
  max-width: 60%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.pickbtn:active { transform: scale(.94); }

/* what you did last time, at the top where you can reach it */
.last-card { width: 100%; text-align: left; }
.last-card:not(.row) { display: block; }
.last-exs { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 10px; }
.chip-static { padding: 3px 9px; font-size: 11.5px; font-weight: 600; pointer-events: none; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

/* ---------- icons & chart chrome (v2 polish) ---------- */
.ic { display: inline-block; vertical-align: middle; flex: none; }
/* A direction marker or a tick sits inside a line of numbers, so it rides the
   text baseline rather than the middle of the line box, and it does not push
   the line taller than the digits beside it. */
.ic-dir { vertical-align: -.11em; margin-right: .5px; }

/* ---------- info tooltips ----------
   A mark beside the thing it defines, and nothing on the page until it is
   asked. It replaced ninety-odd paragraphs of standing caption text, most
   describing what the screen already showed, the rest definitions that are
   read once and then re-read forever. */
.info {
  display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px; padding: 0; flex: none;
  color: var(--text-faint); vertical-align: -.22em;
  transition: color .2s, transform .25s var(--spring);
}
.info:active { transform: scale(.84); color: var(--text-dim); }
/* it lives in headings that are uppercase and tracked; it is neither. The
   heading is a space-between row, so without the auto margin the mark drifts
   into the middle of it instead of sitting against the words it belongs to. */
.card-title .info, .sheet-title .info { text-transform: none; letter-spacing: 0; margin-left: 3px; }
.card-title .info { margin-right: auto; }
/* The heading is a flex row: title, mark, trailing meta. Left to wrap freely
   the meta broke across two lines and the title broke around the mark, which
   is what "The shape of your week · last 12 weeks" did. The meta holds one
   line and gives up width first; the title gets what is left. */
.card-title > .tiny { white-space: nowrap; flex: 0 1 auto; min-width: 0; }
.card-title:has(.info) { flex-wrap: nowrap; }
.tipbox {
  position: fixed; z-index: 90; max-width: min(268px, calc(100vw - 20px));
  background: #1C1C23; border: 1px solid rgba(255,255,255,.13); border-radius: 12px;
  padding: 9px 12px; box-shadow: 0 14px 34px rgba(0,0,0,.62);
  font-size: 12.5px; line-height: 1.45; font-weight: 500; color: var(--text-dim);
  letter-spacing: 0; text-transform: none; text-align: left;
  animation: tip-in .18s var(--ease-out) both;
}
.tipbox::after {
  content: ''; position: absolute; left: var(--arrow, 50%); margin-left: -5px;
  border: 5px solid transparent; border-top-color: #1C1C23;
  bottom: -10px;
}
.tipbox.below::after { bottom: auto; top: -10px; border-top-color: transparent; border-bottom-color: #1C1C23; }
@keyframes tip-in { from { opacity: 0; transform: translateY(3px); } }

/* A heatmap's colours mean something, and a swatch says it in a fifth of the
   words a sentence needs. */
.legend {
  display: flex; align-items: center; gap: 5px; flex-wrap: wrap; margin-top: 9px;
  font-size: 10.5px; font-weight: 650; letter-spacing: .03em; color: var(--text-faint);
}
.legend i { width: 9px; height: 9px; border-radius: 2.5px; display: inline-block; }
.legend i + i, .legend i:not(:first-child) { margin-left: 8px; }
.lg-on   { background: var(--lime); }
.lg-half { background: color-mix(in srgb, var(--lime) 40%, #14141a); }
.lg-miss { background: rgba(255,92,92,.55); }
.lg-off  { background: rgba(255,255,255,.08); }
.icb {
  display: grid; place-items: center; width: 38px; height: 38px; flex: none;
  border-radius: 12px; background: rgba(255,255,255,.055);
  border: 1px solid rgba(255,255,255,.06);
  position: relative;
}
/* Logged today. It sits inside the badge's own corner so it needs no ring to
   separate it from the card behind, and it takes the card's accent from
   currentColor, no new colour, no legend, no word. */
.icb.did::after {
  content: ''; position: absolute; top: 5px; right: 5px;
  width: 3px; height: 3px; border-radius: 50%;
  background: currentColor;
}
.icb .ic { width: 20px; height: 20px; }
.icb-sm { width: 30px; height: 30px; border-radius: 10px; }
.icb-sm .ic { width: 16px; height: 16px; }
.chart-legend { display: flex; gap: 16px; margin-top: 8px; font-size: 11.5px; color: var(--text-dim); }
.chart-legend span { display: inline-flex; align-items: center; gap: 6px; }
.chart-legend i { width: 14px; height: 3px; border-radius: 2px; display: inline-block; }
.donut-leg { display: inline-flex; align-items: center; gap: 7px; font-size: 12.5px; }
.donut-leg i { width: 9px; height: 9px; border-radius: 3px; display: inline-block; }
.donut-leg b { margin-left: 2px; }
.chart-tip {
  /* No centring transform here: toast-in ends on `transform: none` and an
     animation overrides a plain declaration, so it never applied. The left
     edge is positioned directly instead, in bindChartTips. */
  position: absolute; z-index: 5; pointer-events: none;
  background: #1E1E25; border: 1px solid var(--lime); border-radius: 9px;
  padding: 5px 10px; font-size: 12px; font-weight: 650; white-space: nowrap;
  box-shadow: 0 6px 18px rgba(0,0,0,.55);
  animation: toast-in .25s var(--spring) both;
}
.heat i { width: 12.5px; height: 12.5px; border-radius: 3.5px; }
.seg-labels button { font-size: 12px; }
.mood-dot { width: 12px; height: 12px; border-radius: 50%; display: block; margin: 0 auto 4px; background: currentColor; }

/* ============================================================
   FX layer, the living circuit
   ============================================================ */
#atmo { position: fixed; inset: 0; z-index: 0; width: 100vw; height: 100dvh; pointer-events: none; }
.app { position: relative; z-index: 1; }

/* sparks */
/* A particle travelling this fast is a streak, not a dot, and a 6px dot inside
   a 22px glow is a blob. Same rule as the mark and the charts: the glow is a
   fraction of the thing it lights. The element is a hairline stretched along
   its own heading, longest at the moment it is moving fastest. */
.fx-spark {
  position: fixed; z-index: 120; width: 3px; height: 2px; border-radius: 2px;
  margin: -1px 0 0 -1.5px; pointer-events: none;
  box-shadow: 0 0 5px var(--lime-glow);
  animation: fx-spark var(--t, .62s) cubic-bezier(.08,.72,.24,1) forwards;
}
@keyframes fx-spark {
  0%   { opacity: 1; transform: translate(0,0) rotate(var(--a)) scaleX(1); }
  35%  { opacity: 1; transform: translate(var(--mx), var(--my)) rotate(var(--a)) scaleX(4); }
  100% { opacity: 0; transform: translate(var(--dx), var(--dy)) rotate(var(--a)) scaleX(1.2); }
}
/* The deposit gets a tail for the same reason. It is drawn behind the head and
   pointed back down the path, so the thing reads as moving rather than as a dot
   that happens to be in a different place each frame. */
.fx-deposit {
  position: fixed; z-index: 120; width: 7px; height: 7px; border-radius: 50%;
  margin: -3.5px 0 0 -3.5px;
  background: var(--lime); pointer-events: none;
  box-shadow: 0 0 7px var(--lime-glow);
}
.fx-deposit::before {
  content: ''; position: absolute; right: 50%; top: 50%;
  width: 26px; height: 2px; margin-top: -1px; border-radius: 2px;
  transform-origin: 100% 50%; transform: rotate(var(--a, 0deg));
  background: linear-gradient(270deg, var(--lime), transparent);
  opacity: .75;
}

/* PR lightning strike */
.fx-strike { position: fixed; inset: 0; z-index: 130; pointer-events: none; }
.fx-strike svg { width: 100%; height: 100%; }
.fx-strike path {
  stroke-dasharray: 100; stroke-dashoffset: 100;
  animation: fx-bolt .28s cubic-bezier(.7,0,1,1) .05s forwards;
}
@keyframes fx-bolt { to { stroke-dashoffset: 0; } }
.fx-strike .fx-flash {
  position: absolute; inset: 0; background: #EFFFD9; opacity: 0;
  animation: fx-flash .5s ease-out .3s;
}
@keyframes fx-flash { 0% { opacity: 0; } 12% { opacity: .5; } 100% { opacity: 0; } }
.fx-strike.out { animation: fx-fade-out .24s ease forwards; }
@keyframes fx-fade-out { to { opacity: 0; } }

/* staggered cascade on view entry */
.view.cascade > * {
  animation: cascade-in 1.15s var(--spring-soft) both;
  animation-delay: calc(var(--i, 0) * 120ms);
}
@keyframes cascade-in {
  from { opacity: 0; transform: translateY(26px) scale(.97); }
  to { opacity: 1; transform: none; }
}
.view.cascade.flavor-lift > .card { animation-name: cascade-slam; }
@keyframes cascade-slam {
  0% { opacity: 0; transform: translateY(-16px) scale(1.015); }
  62% { opacity: 1; transform: translateY(2px) scale(.998); }
  80% { transform: translateY(-1px); }
  100% { opacity: 1; transform: none; }
}
.view.cascade.flavor-run > .card { animation-name: cascade-streak; }
@keyframes cascade-streak {
  0% { opacity: 0; transform: translateX(-52px) skewX(-3deg); filter: blur(3px); }
  70% { filter: blur(0); }
  100% { opacity: 1; transform: none; filter: none; }
}
.view.cascade.flavor-sleep > .card { animation-duration: 1.7s; animation-name: cascade-drift; }
@keyframes cascade-drift {
  from { opacity: 0; transform: translateY(10px); filter: blur(3px); }
  to { opacity: 1; transform: none; filter: none; }
}

/* app recedes behind sheets (modal depth)
   The navbar deliberately lives OUTSIDE #app. A transformed or filtered
   ancestor becomes the containing block for position:fixed, so while this
   recede was running the bar stopped being screen-fixed and anchored itself to
   the bottom of the whole document, halfway down the page, and for 450ms
   after the sheet closed as well. Nothing that is fixed may go inside #app. */
#app { transition: transform .45s var(--spring-soft), filter .45s ease; }
body.sheet-open #app {
  transform: scale(.92) translateY(-10px);
  border-radius: 22px; overflow: hidden;
  filter: brightness(.8);
}
/* It still recedes with everything else. A filter on the element itself is
   fine; it is only ancestors that redefine where fixed means. */
/* The bar is inside #app again, so it recedes with everything else for free.
   dimming it a second time here would just make it twice as dark. */

/* chart draw-ins */
[data-chart] path[data-draw] {
  stroke-dasharray: var(--len, 600); stroke-dashoffset: var(--len, 600);
  animation: chart-draw 1s cubic-bezier(.3,.6,.3,1) .15s forwards;
}
@keyframes chart-draw { to { stroke-dashoffset: 0; } }
[data-chart] .bar-grow {
  transform-box: fill-box; transform-origin: 50% 100%;
  animation: bar-grow .6s var(--spring-soft) both;
  animation-delay: calc(var(--i, 0) * 40ms + .1s);
}
@keyframes bar-grow { from { transform: scaleY(0); } to { transform: scaleY(1); } }
.heat i { animation: heat-pop .4s var(--spring) both; animation-delay: calc(var(--i, 0) * 5ms); }
@keyframes heat-pop { from { opacity: 0; transform: scale(.4); } to { opacity: 1; transform: scale(1); } }

/* today breathes */
.cal-day.today { animation: today-breathe 3.2s ease-in-out infinite; }
@keyframes today-breathe {
  0%, 100% { box-shadow: 0 0 8px rgba(198,255,0,.1); }
  50% { box-shadow: 0 0 16px rgba(198,255,0,.28); }
}

/* PR shimmer */
:is(.rec-card.pr, .rec-row)::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(115deg, transparent 30%, rgba(255,255,255,.09) 46%, rgba(198,255,0,.06) 50%, transparent 62%);
  background-size: 260% 100%;
  animation: shimmer 4.6s ease-in-out infinite;
}
@keyframes shimmer { 0%, 55% { background-position: 120% 0; } 100% { background-position: -140% 0; } }

/* checkbox charge */
.check-item.done .check-box { animation: check-charge .45s var(--spring); }
@keyframes check-charge {
  0% { box-shadow: 0 0 0 0 rgba(198,255,0,.5); transform: scale(.8); }
  60% { transform: scale(1.18); }
  100% { box-shadow: 0 0 10px rgba(198,255,0,.3); transform: scale(1.05); }
}

/* splash: wordmark ignites letter by letter */
.wordmark { display: inline-flex; }
.wordmark .wl { opacity: 0; animation: letter-ignite .55s steps(1) both; animation-delay: calc(.85s + var(--i) * .12s); }
@keyframes letter-ignite {
  0% { opacity: 0; }
  20% { opacity: 1; text-shadow: 0 0 22px var(--lime-glow), 0 0 4px var(--lime); }
  32% { opacity: .35; }
  44% { opacity: 1; text-shadow: 0 0 26px var(--lime-glow); }
  58% { opacity: .7; }
  70%, 100% { opacity: 1; text-shadow: none; }
}
.gate.leaving { animation: gate-discharge .6s var(--ease-out) both; }
@keyframes gate-discharge {
  0% { opacity: 1; transform: scale(1); filter: brightness(1); }
  25% { filter: brightness(1.8); }
  100% { opacity: 0; transform: scale(1.08); filter: brightness(1); }
}

/* FLIP-ish category morph clone */
.fx-morph {
  position: fixed; z-index: 60; pointer-events: none;
  border-radius: var(--radius); border: 1px solid rgba(198,255,0,.3);
  background: var(--card-2); opacity: .95;
  transition: all .32s cubic-bezier(.3,.7,.3,1);
}

/* empty states pulse faintly instead of sitting dead */
.empty { animation: empty-pulse 3s ease-in-out infinite; }
@keyframes empty-pulse { 0%, 100% { opacity: .55; } 50% { opacity: .95; } }

@media (prefers-reduced-motion: reduce) {
  .rec-card.pr::after, .rec-row::after, .cal-day.today, .empty, .wordmark .wl,
  .cc-node.live { animation: none !important; opacity: 1 !important; }
  .view.cascade > * { animation: none !important; }
}


/* streak stat strip, reads as a stat, not a button */
/* The one line under the greeting. No box and no glow on purpose: it sits
   between the headline and the plan, and anything with a border there competes
   with both. A hairline above and below is enough to make it its own thing. */
.headline {
  display: flex; align-items: center; gap: 10px;
  width: 100%; text-align: left; color: inherit; font: inherit;
  margin: 14px 0 2px; padding: 11px 2px;
  background: none; border: 0;
  border-top: 1px solid rgba(255,255,255,.07);
  border-bottom: 1px solid rgba(255,255,255,.07);
  font-size: 13.5px; line-height: 1.35;
}
.headline .grow { min-width: 0; }
.hl-ic { display: flex; flex: none; color: var(--lime); }
.hl-num {
  font-size: 17px; font-weight: 850; color: var(--lime);
  font-variant-numeric: tabular-nums; letter-spacing: -.01em;
}
.hl-when { color: var(--text-faint); }
.hl-go { flex: none; color: var(--text-faint); font-size: 15px; }
.headline:active { background: rgba(255,255,255,.03); }

/* tappable heatmap cells (Alcohol page) */
.heat i[data-date] { cursor: pointer; }
.heat i[data-date]:active { transform: scale(1.6); }
.heat i.fut { opacity: .25; }

/* full-screen photo viewer */
.photo-lightbox {
  position: fixed; inset: 0; z-index: 140; display: grid; place-items: center;
  background: rgba(4,4,6,.9); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  animation: fade-in .22s ease both; padding: 24px;
}
.photo-lightbox img {
  max-width: 96vw; max-height: 86dvh; border-radius: 14px;
  box-shadow: 0 20px 60px rgba(0,0,0,.6); animation: celebrate-pop .4s var(--spring) both;
}
.photo-lightbox.out { animation: fade-out .22s ease forwards; }
.photo-lightbox .pl-close {
  position: absolute; top: calc(14px + var(--sat)); right: 16px;
  width: 40px; height: 40px; border-radius: 50%; font-size: 18px;
  background: rgba(255,255,255,.12); color: #fff; border: 1px solid rgba(255,255,255,.2);
}
.photo-strip img, .photo-thumb, .rec-card img { cursor: pointer; }

/* ============================================================
   Progression, rank, tracks, themes
   ============================================================ */
.rank-card { padding: 18px; }
.rank-top { display: flex; align-items: flex-end; justify-content: space-between; gap: 12px; }
.rank-label { font-size: 10.5px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase; color: var(--text-dim); }
.rank-title { font-size: 25px; font-weight: 850; letter-spacing: -.02em; line-height: 1.1; margin-top: 2px; }
.rank-num { font-size: 34px; font-weight: 850; color: var(--lime); line-height: 1; filter: drop-shadow(0 0 10px var(--lime-glow)); }
.rank-bar { height: 7px; border-radius: 5px; background: rgba(255,255,255,.09); overflow: hidden; margin-top: 12px; }
.rank-bar i {
  display: block; height: 100%; border-radius: 5px;
  background: linear-gradient(90deg, color-mix(in srgb, var(--lime) 45%, transparent), var(--lime));
  box-shadow: 0 0 12px var(--lime-glow);
  transition: width 1.1s var(--ease-out);
}

/* track rows */
.track-row {
  display: flex; align-items: center; gap: 12px; width: 100%; text-align: left;
  padding: 12px 14px; background: var(--card); border: 1px solid var(--line);
  border-radius: 15px; transition: transform .28s var(--spring), border-color .25s;
}
.track-row:active { transform: scale(.975); border-color: rgba(255,255,255,.16); }
.tk-ic { display: grid; place-items: center; width: 36px; height: 36px; flex: none;
  border-radius: 11px; background: rgba(255,255,255,.05); border: 1px solid rgba(255,255,255,.06); }
.tk-top { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.tk-tier { font-size: 11px; font-weight: 800; letter-spacing: .08em; text-transform: uppercase; }
.tk-bar { height: 5px; border-radius: 4px; background: rgba(255,255,255,.08); overflow: hidden; margin: 6px 0 5px; }
.tk-bar i { display: block; height: 100%; border-radius: 4px; background: var(--lime);
  box-shadow: 0 0 8px var(--lime-glow); transition: width 1s var(--ease-out); }
.tk-sub { color: var(--text-dim); }
.tk-pips { display: inline-flex; align-items: flex-end; gap: 2.5px; flex: none; }
.tk-pips i { width: 4px; border-radius: 1.5px; background: rgba(255,255,255,.14); display: block; }
.tk-pips i:nth-child(1) { height: 6px; }  .tk-pips i:nth-child(2) { height: 8px; }
.tk-pips i:nth-child(3) { height: 10px; } .tk-pips i:nth-child(4) { height: 12px; }
.tk-pips i:nth-child(5) { height: 14px; }
/* what the next rung pays, on the row itself */
/* When the sub-line is too long it wraps, which is fine; what is not fine is
   it breaking between "unlocks" and the thing it unlocks. This keeps the clause
   together so it moves to the second line whole. */
.tk-pay { color: var(--text-dim); white-space: nowrap; }
.tk-pay b { color: var(--lime); font-weight: 700; }

/* The prize on a rung. Deliberately not a card: it sits inside one, and a
   second bordered box inside a bordered box reads as a nested thing rather
   than as a label on the line above it. */
.rw-row { display: flex; flex-wrap: wrap; gap: 6px; }
.rw-chip {
  display: inline-flex; align-items: baseline; gap: 5px; font-size: 11px;
  padding: 3px 9px; border-radius: 999px;
  background: color-mix(in srgb, var(--lime) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--lime) 26%, transparent);
}
.rw-chip b { color: var(--lime); font-weight: 700; }
.rw-chip.dim-chip {
  background: rgba(255,255,255,.045); border-color: rgba(255,255,255,.09);
}
.rw-chip.dim-chip b { color: var(--text-dim); }

/* The ladder of titles. Rungs you have are ticked and quiet; the one you are on
   is lit; the rest are what is left to do. */
.rank-list { display: flex; flex-direction: column; gap: 2px; }
.rank-rung {
  display: flex; align-items: center; gap: 10px; padding: 9px 12px;
  border-radius: 11px; border: 1px solid transparent; color: var(--text-dim);
}
.rank-rung .rr-mark { display: grid; place-items: center; width: 16px; flex: none; }
.rank-rung .rr-n {
  flex: none; width: 24px; text-align: right; font-variant-numeric: tabular-nums;
  font-size: 12px; font-weight: 700; color: var(--text-dim);
}
.rank-rung.current .rr-n, .rank-rung.on .rr-n { color: var(--lime); }
.rank-rung.done { color: var(--text-dim); }
.rank-rung.done .rr-mark { color: var(--lime); }
.rank-rung.done b { color: var(--text); }
.rank-rung.on {
  background: color-mix(in srgb, var(--lime) 10%, transparent);
  border-color: color-mix(in srgb, var(--lime) 30%, transparent);
}
.rank-rung.on b { color: var(--lime); }
/* the rank card is a button now, and should feel like one */
.rank-card { cursor: pointer; transition: transform .28s var(--spring); }
.rank-card:active { transform: scale(.99); }

/* How much of the tracks in front of you is done. A progress bar, and it looks
   like one — the rank line below it is a status and looks different on purpose. */
.done-line {
  display: flex; align-items: center; gap: 12px; padding: 10px 13px;
  border-radius: 13px; color: var(--text-dim);
  background: rgba(255,255,255,.028); border: 1px solid var(--line);
}
.done-line b { color: var(--lime); }
.done-bar {
  flex: none; width: 76px; height: 5px; border-radius: 4px;
  background: rgba(255,255,255,.09); overflow: hidden;
}
.done-bar i {
  display: block; height: 100%; border-radius: 4px; background: var(--lime);
  box-shadow: 0 0 7px var(--lime-glow); transition: width 1s var(--ease-out);
}

/* Rank, under the ladders it is the sum of. */
.rank-line {
  display: flex; align-items: center; gap: 10px; padding: 10px 13px;
  border-radius: 13px; color: var(--text-dim);
  background: rgba(255,255,255,.028); border: 1px dashed var(--line);
}
.rank-line .insig { color: var(--lime); flex: none; }
.rank-line b { color: var(--text); }

/* ============================================================
   Equippable kit, surfaces, ring styles, frames, boot sequences
   ============================================================ */

/* --- surfaces: the material every card is cut from ---
   THE .card, .track-row, .list-item, .rec-card, .feat-cell, .unlock-cell,
    .nu-row, .nt-row, .rb-row, .to-row, .wip-card, .cal-sum, .yr-cell LIST. Every card-like surface in the app belongs in it, and each
   surface below repeats it verbatim inside :is(). A new card class that is not
   added here is a card the Surface unlock does nothing to, which is
   how five of them ended up outside it. surface-frame-test.mjs fails if any
   panel class stops responding, so the omission cannot happen again.

   These match on the attribute alone rather than on :root, so the Trophy Case
   can hang data-surface on a 34px disc and get the genuine article inside it
   instead of a hand-drawn impression of it. Same for the frames below. */
[data-surface="carbon"] {
  --card: #131316; --card-2: #17171B; --line: rgba(255,255,255,.09);
  --radius: 14px; --radius-sm: 10px;
}
[data-surface="carbon"] :is(.card, .track-row, .list-item, .rec-card, .feat-cell, .unlock-cell,
    .nu-row, .nt-row, .rb-row, .to-row, .wip-card, .cal-sum, .yr-cell) {
  background-image:
    repeating-linear-gradient(45deg, rgba(255,255,255,.022) 0 2px, transparent 2px 4px),
    repeating-linear-gradient(-45deg, rgba(0,0,0,.25) 0 2px, transparent 2px 4px);
}
[data-surface="frost"] {
  --card: rgba(30,32,38,.55); --card-2: rgba(38,40,48,.55);
  --line: rgba(255,255,255,.16); --radius: 22px;
}
[data-surface="frost"] :is(.card, .track-row, .list-item, .rec-card, .feat-cell, .unlock-cell,
    .nu-row, .nt-row, .rb-row, .to-row, .wip-card, .cal-sum, .yr-cell, .sheet) {
  backdrop-filter: blur(16px) saturate(1.2); -webkit-backdrop-filter: blur(16px) saturate(1.2);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.10);
}
[data-surface="blueprint"] {
  --card: #0E1319; --card-2: #111820; --line: color-mix(in srgb, var(--lime) 26%, transparent);
  --radius: 6px; --radius-sm: 4px;
}
[data-surface="blueprint"] :is(.card, .track-row, .list-item, .rec-card, .feat-cell, .unlock-cell,
    .nu-row, .nt-row, .rb-row, .to-row, .wip-card, .cal-sum, .yr-cell) {
  background-image:
    linear-gradient(color-mix(in srgb, var(--lime) 7%, transparent) 1px, transparent 1px),
    linear-gradient(90deg, color-mix(in srgb, var(--lime) 7%, transparent) 1px, transparent 1px);
  background-size: 18px 18px, 18px 18px;
}
[data-surface="brutal"] {
  --card: #0F0F11; --card-2: #141416; --line: rgba(255,255,255,.22);
  --radius: 0px; --radius-sm: 0px;
}
[data-surface="brutal"] :is(.card, .track-row, .list-item, .rec-card, .feat-cell, .unlock-cell,
    .nu-row, .nt-row, .rb-row, .to-row, .wip-card, .cal-sum, .yr-cell, .btn, .chip) {
  border-radius: 0 !important; border-width: 1.5px; box-shadow: none !important;
  background-image: none;
}
[data-surface="brutal"] .h1, [data-surface="brutal"] .h2 { letter-spacing: -.03em; }

[data-surface="linen"] {
  --card: #1A1917; --card-2: #201F1C; --line: rgba(255,240,220,.10); --radius: 16px;
}
[data-surface="linen"] :is(.card, .track-row, .list-item, .rec-card, .feat-cell, .unlock-cell,
    .nu-row, .nt-row, .rb-row, .to-row, .wip-card, .cal-sum, .yr-cell) {
  background-image:
    repeating-linear-gradient(0deg, rgba(255,245,225,.028) 0 1px, transparent 1px 3px),
    repeating-linear-gradient(90deg, rgba(255,245,225,.022) 0 1px, transparent 1px 3px);
  box-shadow: inset 0 1px 0 rgba(255,240,215,.05);
}

/* --- score ring styles --- */
.ring-anim .fg2 { opacity: .55; }
.ring-reactor .reactor-ticks { animation: reactor-spin 26s linear infinite; }
@keyframes reactor-spin { to { transform: rotate(-360deg); } }
.ring-segment .seg-tick { opacity: 0; animation: seg-in .5s var(--ease-out) both;
  animation-delay: calc(var(--i) * 22ms); }
@keyframes seg-in { from { opacity: 0; } to { opacity: 1; } }
.ring-liquid .liquid-well { fill: rgba(255,255,255,.04); }
.ring-liquid .liquid-wave { animation: liquid-slide 5.5s linear infinite; }
.ring-liquid .liquid-wave.slow { animation-duration: 8.5s; animation-direction: reverse; }
@keyframes liquid-slide { to { transform: translateX(var(--w, 120px)); } }
/* Lattice fills cell by cell, in the order the cells are laid out, so the
   honeycomb reads as filling rather than appearing. */
.ring-lattice .lat-cell { opacity: 0; animation: lat-in .42s var(--ease-out) both;
  animation-delay: calc(var(--i) * 26ms); }
.ring-lattice .lat-cell:not(.on) { animation-delay: 0s; opacity: 1; }
@keyframes lat-in { from { opacity: 0; transform: scale(.7); } to { opacity: 1; transform: none; } }
/* Three arcs, each starting a beat after the last. */
.ring-macro .macro-arc { animation: macro-in .9s var(--ease-out) both;
  animation-delay: calc(var(--i) * 150ms); transform-origin: center; }
@keyframes macro-in { from { opacity: 0; } to { opacity: 1; } }
/* The broken line down the middle of the road, running past. */
.ring-odometer .odo-line { animation: odo-run 2.8s linear infinite; }
@keyframes odo-run { to { stroke-dashoffset: -40; } }

.ring-trace .fg-path { transition: stroke-dashoffset 1.4s cubic-bezier(.16,1,.3,1); }
.ring-plateRing .fg2 { opacity: .45; }
.ring-balance .balance-beam { transform: rotate(var(--tilt, 0deg)); }

/* --- record frames ---
   THE RECORD LIST. Records is three views: the book, the feed, and the goal
   cards. A frame that only knows about .rec-card frames the goals and nothing
   else, which is what "Record Frame · PR and goal cards" became when
   PRs stopped being cards. All three, everywhere below. */
[data-frame="etched"] :is(.rec-card, .rb-row, .rec-row) {
  border-width: 2px; border-style: double;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.05), inset 0 0 26px rgba(0,0,0,.4);
}
[data-frame="chrome"] :is(.rec-card, .rb-row, .rec-row) {
  border-color: transparent;
  background-image: linear-gradient(155deg, var(--card-2), var(--card)),
    linear-gradient(140deg, #6F7681, #E9EEF5 28%, #767D88 52%, #F2F5F9 74%, #6F7681);
  background-origin: border-box; background-clip: padding-box, border-box;
  border-width: 1.5px;
}
[data-frame="holo"] :is(.rec-card, .rb-row, .rec-row) {
  border-color: transparent;
  background-image: linear-gradient(155deg, var(--card-2), var(--card)),
    conic-gradient(from 210deg, #FF5CD8, #FFC93C, #39FF6A, #45E0FF, #C77DFF, #FF5CD8);
  background-origin: border-box; background-clip: padding-box, border-box;
  border-width: 1.8px;
}
[data-frame="holo"] :is(.rec-card, .rb-row, .rec-row)::before {
  content: ''; position: absolute; inset: 0; pointer-events: none; border-radius: inherit;
  background: linear-gradient(115deg, transparent 34%, rgba(255,255,255,.14) 48%, transparent 60%);
  background-size: 240% 100%; animation: shimmer 5.2s ease-in-out infinite;
}
[data-frame="relic"] :is(.rec-card, .rb-row, .rec-row) {
  border-color: rgba(198,166,100,.45);
  box-shadow: inset 0 0 30px rgba(160,120,50,.12);
  background-image: radial-gradient(120% 90% at 20% 0%, rgba(198,166,100,.09), transparent 55%);
}
[data-frame="relic"] :is(.rec-tag, .rb-val) { color: #D9BE7E; border-color: rgba(198,166,100,.4); }
[data-frame="ledger"] :is(.rec-card, .rb-row, .rec-row) {
  border-color: rgba(255,245,225,.18);
  background-image: repeating-linear-gradient(0deg,
    transparent 0 17px, rgba(255,245,225,.055) 17px 18px);
}
[data-frame="ledger"] :is(.rec-card, .rb-row, .rec-row)::before {
  content: ''; position: absolute; left: 26px; top: 0; bottom: 0; width: 1px;
  background: color-mix(in srgb, var(--lime) 30%, transparent); pointer-events: none;
}
/* Tile did not exist. It had a preview chip and a blurb and a requirement and
   a place in the grid, and equipping it changed nothing whatsoever, the frame
   itself was never written, and the hand-drawn chip was convincing enough that
   nobody noticed for as long as the chip was hand-drawn. */
[data-frame="tile"] :is(.rec-card, .rb-row, .rec-row) {
  border-color: color-mix(in srgb, var(--lime) 34%, transparent);
  border-top-width: 3px;
  background-image:
    linear-gradient(rgba(255,255,255,.05) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255,255,255,.05) 1px, transparent 1px);
  background-size: 22px 22px, 22px 22px;
}
[data-frame="tile"] :is(.rec-card, .rb-row, .rec-row)::before {
  /* the waterline */
  content: ''; position: absolute; left: 0; right: 0; top: 9px; height: 1.5px;
  pointer-events: none; border-radius: inherit;
  background: linear-gradient(90deg, transparent,
    color-mix(in srgb, var(--lime) 55%, transparent) 30%,
    color-mix(in srgb, var(--lime) 55%, transparent) 70%, transparent);
}

/* ---- six more surfaces and six more frames, one per track ----
   Only five track rungs were still free, so the rest hang off milestone rungs,
   which are tracker-scoped and keep the split even: every one of the twelve
   tracks now hands over exactly one surface or one frame. */

[data-surface="graphite"] { --card: #16171A; --card-2: #1B1C20; --line: rgba(255,255,255,.10); --radius: 12px; }
[data-surface="graphite"] :is(.card, .track-row, .list-item, .rec-card, .feat-cell, .unlock-cell,
    .nu-row, .nt-row, .rb-row, .to-row, .wip-card, .cal-sum, .yr-cell) { background-image: repeating-linear-gradient(90deg, rgba(255,255,255,.018) 0 1px, transparent 1px 3px);
  box-shadow: inset 0 -1px 0 rgba(255,255,255,.05); }
.kit-chip[data-surface="graphite"] { background: repeating-linear-gradient(90deg, #24262B 0 2px, #17181C 2px 4px); }

[data-surface="parchment"] { --card: #1E1B16; --card-2: #241F19; --line: rgba(226,198,150,.20); --radius: 6px; }
[data-surface="parchment"] :is(.card, .track-row, .list-item, .rec-card, .feat-cell, .unlock-cell,
    .nu-row, .nt-row, .rb-row, .to-row, .wip-card, .cal-sum, .yr-cell) { background-image: radial-gradient(120% 90% at 20% 0%, rgba(226,198,150,.07), transparent 60%);
  box-shadow: inset 0 0 30px rgba(0,0,0,.35); }
.kit-chip[data-surface="parchment"] { background: linear-gradient(150deg, #2C2620, #17140F); }

[data-surface="mesh"] { --card: #131519; --card-2: #171A1F; --line: rgba(255,255,255,.13); --radius: 16px; }
[data-surface="mesh"] :is(.card, .track-row, .list-item, .rec-card, .feat-cell, .unlock-cell,
    .nu-row, .nt-row, .rb-row, .to-row, .wip-card, .cal-sum, .yr-cell) { background-image:
    linear-gradient(45deg, rgba(255,255,255,.035) 25%, transparent 25% 75%, rgba(255,255,255,.035) 75%),
    linear-gradient(45deg, rgba(255,255,255,.035) 25%, transparent 25% 75%, rgba(255,255,255,.035) 75%);
  background-size: 8px 8px; background-position: 0 0, 4px 4px; }
.kit-chip[data-surface="mesh"] { background: repeating-conic-gradient(#232730 0 25%, #14161A 0 50%) 0 0/8px 8px; }

[data-surface="lacquer"] { --card: #121216; --card-2: #17171C; --line: rgba(255,255,255,.08); --radius: 20px; }
[data-surface="lacquer"] :is(.card, .track-row, .list-item, .rec-card, .feat-cell, .unlock-cell,
    .nu-row, .nt-row, .rb-row, .to-row, .wip-card, .cal-sum, .yr-cell) { background-image: linear-gradient(165deg, rgba(255,255,255,.10), transparent 42%);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.14), 0 10px 24px rgba(0,0,0,.4); }
.kit-chip[data-surface="lacquer"] { background: linear-gradient(165deg, #33343E, #101014); }

[data-surface="terrazzo"] { --card: #17181C; --card-2: #1C1D22; --line: rgba(255,255,255,.11); --radius: 18px; }
[data-surface="terrazzo"] :is(.card, .track-row, .list-item, .rec-card, .feat-cell, .unlock-cell,
    .nu-row, .nt-row, .rb-row, .to-row, .wip-card, .cal-sum, .yr-cell) { background-image:
    radial-gradient(circle at 18% 30%, rgba(255,255,255,.07) 0 3px, transparent 4px),
    radial-gradient(circle at 62% 68%, rgba(255,255,255,.05) 0 2px, transparent 3px),
    radial-gradient(circle at 86% 22%, rgba(255,255,255,.06) 0 2.5px, transparent 3.5px); }
.kit-chip[data-surface="terrazzo"] { background: radial-gradient(circle at 30% 35%, #33353D 0 4px, transparent 5px), #16171B; }

[data-surface="tarmac"] { --card: #141416; --card-2: #191919; --line: rgba(255,255,255,.09); --radius: 10px; }
[data-surface="tarmac"] :is(.card, .track-row, .list-item, .rec-card, .feat-cell, .unlock-cell,
    .nu-row, .nt-row, .rb-row, .to-row, .wip-card, .cal-sum, .yr-cell) { background-image: radial-gradient(circle at 50% 50%, rgba(255,255,255,.05) 0.5px, transparent 0.6px);
  background-size: 4px 4px; }
.kit-chip[data-surface="tarmac"] { background: radial-gradient(circle at 50% 50%, #2B2B2E 0.6px, #141416 0.7px) 0 0/4px 4px; }

/* ---- the hidden three ---- */
/* Volcanic glass: near black, one hard specular streak across the top left,
   and a sharp corner. Nothing soft about it. */
[data-surface="obsidian"] { --card: #0C0C10; --card-2: #101014; --line: rgba(255,255,255,.10); --radius: 4px; }
[data-surface="obsidian"] :is(.card, .track-row, .list-item, .rec-card, .feat-cell, .unlock-cell,
    .nu-row, .nt-row, .rb-row, .to-row, .wip-card, .cal-sum, .yr-cell) { background-image:
    linear-gradient(118deg, rgba(255,255,255,.16) 0 6%, rgba(255,255,255,.02) 12%, transparent 26%),
    linear-gradient(200deg, rgba(120,140,190,.07), transparent 55%);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.09), inset 0 -14px 26px rgba(0,0,0,.55); }
.kit-chip[data-surface="obsidian"] { background: linear-gradient(118deg, #4A4E5E 0 8%, #0B0B0F 26%); }

/* Metal left outside: warm patina blotched unevenly, and a rough edge. */
[data-surface="oxide"] { --card: #191411; --card-2: #201814; --line: rgba(196,116,68,.24); --radius: 8px; }
[data-surface="oxide"] :is(.card, .track-row, .list-item, .rec-card, .feat-cell, .unlock-cell,
    .nu-row, .nt-row, .rb-row, .to-row, .wip-card, .cal-sum, .yr-cell) { background-image:
    radial-gradient(58% 44% at 22% 18%, rgba(196,116,68,.16), transparent 70%),
    radial-gradient(38% 52% at 78% 72%, rgba(150,78,44,.14), transparent 68%),
    radial-gradient(circle at 50% 50%, rgba(0,0,0,.30) .5px, transparent .6px);
  background-size: auto, auto, 3px 3px;
  box-shadow: inset 0 0 26px rgba(0,0,0,.42); }
.kit-chip[data-surface="oxide"] { background: radial-gradient(circle at 32% 30%, #7A4526, #1A1310 72%); }

/* Deep matte with the light only along the edge, the way pile catches it. */
[data-surface="velvet"] { --card: #141019; --card-2: #191423; --line: rgba(178,150,220,.18); --radius: 22px; }
[data-surface="velvet"] :is(.card, .track-row, .list-item, .rec-card, .feat-cell, .unlock-cell,
    .nu-row, .nt-row, .rb-row, .to-row, .wip-card, .cal-sum, .yr-cell) { background-image:
    radial-gradient(120% 130% at 50% 130%, rgba(150,116,210,.15), transparent 62%);
  box-shadow: inset 0 0 0 1px rgba(178,150,220,.10), inset 0 22px 34px rgba(0,0,0,.5); }
.kit-chip[data-surface="velvet"] { background: radial-gradient(120% 130% at 50% 130%, #4B3A75, #120E18 70%); }


[data-frame="deckle"] :is(.rec-card, .rb-row, .rec-row) { border-style: dashed; border-width: 2px;
  border-color: color-mix(in srgb, var(--lime) 30%, transparent);
  border-radius: 4px; }

[data-frame="cradle"] :is(.rec-card, .rb-row, .rec-row) { border: 0;
  box-shadow: inset 0 3px 0 color-mix(in srgb, var(--lime) 45%, transparent),
              inset 0 -3px 0 color-mix(in srgb, var(--lime) 45%, transparent); }

[data-frame="bezel"] :is(.rec-card, .rb-row, .rec-row) { border-width: 3px; border-color: rgba(255,255,255,.07);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--lime) 40%, transparent),
              0 4px 14px rgba(0,0,0,.45); }

[data-frame="corners"] :is(.rec-card, .rb-row, .rec-row) { border-color: transparent; }
[data-frame="corners"] :is(.rec-card, .rb-row, .rec-row)::before {
  content: ''; position: absolute; inset: 3px; pointer-events: none;
  border: 2px solid color-mix(in srgb, var(--lime) 55%, transparent);
  border-radius: 6px;
  clip-path: polygon(0 0, 30% 0, 30% 2px, 2px 2px, 2px 30%, 0 30%,
    0 70%, 2px 70%, 2px calc(100% - 2px), 30% calc(100% - 2px), 30% 100%, 0 100%,
    100% 100%, 70% 100%, 70% calc(100% - 2px), calc(100% - 2px) calc(100% - 2px),
    calc(100% - 2px) 70%, 100% 70%, 100% 30%, calc(100% - 2px) 30%,
    calc(100% - 2px) 2px, 70% 2px, 70% 0, 100% 0);
}

[data-frame="spine"] :is(.rec-card, .rb-row, .rec-row) { border-color: rgba(255,255,255,.08); border-left-width: 5px;
  border-left-color: color-mix(in srgb, var(--lime) 65%, transparent);
  border-radius: 3px 14px 14px 3px; }

[data-frame="halftone"] :is(.rec-card, .rb-row, .rec-row) { border-color: color-mix(in srgb, var(--lime) 25%, transparent);
  background-image: radial-gradient(circle at 50% 50%, rgba(255,255,255,.07) 1px, transparent 1.2px);
  background-size: 7px 7px; }

/* ---- the hidden three ---- */
/* Bolted down: a heavy plate with a fastener sunk at each corner. */
[data-frame="rivet"] :is(.rec-card, .rb-row, .rec-row) { border-width: 2px;
  border-color: rgba(255,255,255,.10); border-radius: 5px;
  background-image:
    radial-gradient(circle at 9px 9px, color-mix(in srgb, var(--lime) 60%, transparent) 0 2.4px, rgba(0,0,0,.5) 2.4px 3.4px, transparent 3.6px),
    radial-gradient(circle at calc(100% - 9px) 9px, color-mix(in srgb, var(--lime) 60%, transparent) 0 2.4px, rgba(0,0,0,.5) 2.4px 3.4px, transparent 3.6px),
    radial-gradient(circle at 9px calc(100% - 9px), color-mix(in srgb, var(--lime) 60%, transparent) 0 2.4px, rgba(0,0,0,.5) 2.4px 3.4px, transparent 3.6px),
    radial-gradient(circle at calc(100% - 9px) calc(100% - 9px), color-mix(in srgb, var(--lime) 60%, transparent) 0 2.4px, rgba(0,0,0,.5) 2.4px 3.4px, transparent 3.6px);
  background-repeat: no-repeat; }

/* Off a reel: sprocket holes punched down both edges. */
[data-frame="filmstrip"] :is(.rec-card, .rb-row, .rec-row) { border-color: transparent; border-radius: 3px;
  border-left: 11px solid rgba(255,255,255,.07); border-right: 11px solid rgba(255,255,255,.07);
  background-image:
    linear-gradient(rgba(0,0,0,.55) 0 0), linear-gradient(rgba(0,0,0,.55) 0 0);
  background-repeat: repeat-y, repeat-y;
  background-size: 5px 12px, 5px 12px;
  background-position: -8px 4px, calc(100% + 8px) 4px;
  background-origin: border-box; background-clip: border-box; }

/* A bent tube switched on: thin, bright, and glowing on both sides of itself. */
[data-frame="neon"] :is(.rec-card, .rb-row, .rec-row) { border-width: 1.5px; border-radius: 13px;
  border-color: color-mix(in srgb, var(--lime) 85%, transparent);
  box-shadow: 0 0 9px color-mix(in srgb, var(--lime) 45%, transparent),
              inset 0 0 9px color-mix(in srgb, var(--lime) 22%, transparent); }

/* --- rank insignia --- */
.insig { color: var(--lime); filter: drop-shadow(0 0 5px var(--lime-glow)); flex: none; }

/* --- the feature index --- */
.fx-group { border-bottom: 1px solid var(--line); }
.fx-group:last-child { border-bottom: 0; }
.fx-head {
  display: flex; align-items: center; gap: 11px; width: 100%; text-align: left;
  padding: 12px 2px; transition: opacity .2s;
}
.fx-head:active { opacity: .6; }
.fx-chev { display: flex; color: var(--text-faint); transition: transform .28s var(--spring); }
.fx-group.open .fx-chev { transform: rotate(90deg); }
.fx-body { padding: 0 2px 10px 45px; }
.fx-row { padding: 9px 0; border-top: 1px solid var(--line); }
.fx-row:first-child { border-top: 0; }
.fx-name { font-size: 14.5px; font-weight: 750; }
.fx-what { font-size: 13px; line-height: 1.45; color: var(--text-dim); margin-top: 2px; }
.fx-where {
  font-size: 11px; font-weight: 650; letter-spacing: .02em; color: var(--text-faint);
  margin-top: 5px;
}
.fx-where .link { font-size: 11px; font-weight: 750; }

/* --- the stage in an unlock's sheet ---
   Real UI with the unlock on it, at the size you will actually meet it. The
   sheet used to show the 34px grid tile, so a Record Frame was a disc with an
   edge and the only way to see one was to leave and go to Records. */
.kit-stage {
  position: relative; margin-top: 14px; padding: 14px;
  border-radius: 16px; background: var(--bg); border: 1px solid var(--line);
  overflow: hidden;
}
.kit-stage.center { display: grid; place-items: center; }
.kit-stage .chart-wrap { margin: 0 auto !important; }
/* the signal is a line under a bar, so stage it as one */
.kit-stage.stage-thread { padding: 0; height: 46px; display: grid; place-items: center;
  background: linear-gradient(180deg, #101014, var(--bg)); }
.kit-stage.stage-thread svg { width: 100%; height: 14px; color: var(--lime); }
/* a boot is a full screen, so the stage is a window onto one. Centred by the
   absolute positioning the still already has: a grid row sizes itself to the
   item, so a 300px child in a 132px box starts at the top and hangs out of the
   bottom rather than sitting in the middle of it. */
.kit-stage.stage-boot { height: 132px; position: relative; }
.kit-stage.stage-boot .boot-still {
  width: 300px; height: 300px;
  transform: translate(-50%, -50%) scale(.42); transform-origin: center;
}
.kit-stage.stage-boot .boot-still .gate-inner { width: 300px; }
.kit-stage.stage-boot .boot-still .wordmark { display: block; font-size: 34px; }
.kit-stage.stage-boot .boot-still .gate-tag { display: block; }
.kit-stage.stage-atmo { display: grid; place-items: center; text-align: center; }
.kit-stage.stage-atmo .atmo-chip { width: 78px; height: 78px; border-radius: 18px; }

/* --- kit preview chips in the Unlocks grid ---
   A tile does not draw an impression of what the unlock does. It wears it.
   Surface and Record Frame set the same data attribute the app sets on <html>,
   so the little card inside is cut from the real material by the real rules.
   Score Ring calls the dial renderer. Signal runs one frame of the real
   waveform function. Boot stages the real launch sequence and holds it in the
   middle. None of it is per-item. Which is the entire point. Three unlocks
   have now shipped with a blank tile because the art was a hand-written class
   somebody had to remember to add, and there is nothing left to remember. A
   tile that shows nothing now means the unlock itself does nothing, which is a
   bug worth seeing. */
.kit-chip {
  width: 34px; height: 34px; border-radius: 50%; display: grid; place-items: center;
  overflow: hidden; flex: none; position: relative;
  background: #0B0B0E; border: 1px solid rgba(255,255,255,.09);
}
.locked .kit-chip { opacity: .5; }

/* the material itself, sized to the disc's inscribed square rather than bled
   past its edge. Half of what a surface or a frame IS lives in its border and
   its corner radius, and Brutal (flat, hard-edged, zero radius) bled to the
   edge was an empty circle. Laid out by the chip's own grid rather than
   absolutely positioned: place-items:center shrink-wraps an inset abspos child
   and then centres the result, which put every sample down and to the right. */
/* 23px: the square inscribed in a 34px circle is 24, so this clears the crop
   with a hair to spare and the whole border survives. */
.chip-card { position: relative; width: 23px; height: 23px; margin: 0; }
/* A frame never changes the card's radius, so a big one here says nothing and
   swallows the border, which is the only thing a frame does say. */
.kit-chip[data-frame] .chip-card { border-radius: 6px; }
/* Frost is see-through by design, so give it something of its own to be in
   front of, against the chip's flat ground it reads as nothing at all. */
.kit-chip[data-surface="frost"] { background:
  linear-gradient(150deg, #3A3F52, #14161C); }

/* the real dial. No numbers at 34px. The shape is the whole message. */
.chip-ring { position: absolute; inset: 2px; }
.chip-ring .chart-wrap { margin: 0 !important; width: 100% !important; }
.chip-ring .ring-label { display: none; }

/* one frame of the real waveform, cropped to the middle of the line */
.chip-thread { width: 30px; height: 30px; color: var(--lime); overflow: visible; }

/* the real launch sequence, held part-way through. Everything is staged at
   full size and scaled down as one piece, so the scenery keeps its proportions
   against the mark instead of being redrawn at chip scale. */
.boot-chip { color: var(--lime); }
.boot-chip::before {
  content: ''; position: absolute; inset: 0;
  background: radial-gradient(circle, color-mix(in srgb, var(--lime) 20%, transparent), transparent 72%);
}
.boot-still {
  position: absolute; top: 50%; left: 50%; width: 150px; height: 150px;
  transform: translate(-50%, -50%) scale(.235); transform-origin: center;
  display: grid; place-items: center; pointer-events: none;
}
.boot-still .gate-inner { width: 150px; }
.boot-still .mark-lg { margin-bottom: 0; }
/* The disc crops hard and a 34px circle is narrow at the bottom, so anything
   the sequence stages away from the mark gets pulled in toward it. Terminal's
   log sits 118px down a 108px mark, which at this scale is outside the circle
   entirely. */
.boot-still .boot-lines { top: 104px; }
/* Held part-way in by holdSequence() in splash.js, not by CSS: a fixed delay
   here would land at the peak of a 0.5s animation and past the end of a 2.3s
   one, and these sequences run both. */
/* See the note there.
   the wordmark and tagline are the same in every boot, so they say nothing
   here and only cost legibility */
.boot-still .wordmark, .boot-still .gate-tag { display: none; }

/* --- career poster --- */
.poster-cta {
  display: flex; align-items: center; gap: 12px; width: 100%; text-align: left;
  padding: 13px 15px; margin-bottom: 12px; border-radius: 16px;
  background: linear-gradient(120deg, var(--lime-soft), transparent 62%), var(--card);
  border: 1px solid color-mix(in srgb, var(--lime) 30%, transparent);
  transition: transform .28s var(--spring);
}
.poster-cta:active { transform: scale(.975); }
.poster-cta.locked { opacity: .55; border-color: var(--line); background: var(--card); }
.pc-mark { display: grid; place-items: center; width: 38px; height: 38px; flex: none;
  border-radius: 12px; background: rgba(255,255,255,.05); border: 1px solid rgba(255,255,255,.07); }
/* ------------------------------------------------------------
   The Career Poster. The one thing built to leave the app
   ------------------------------------------------------------ */
/* Every colour on the card comes from these four tokens, so a finish is five
   lines and a layout never has to know which one is on. */
.poster {
  --pa: #C6FF00; --pbg1: #1B1B21; --pbg2: #0D0D10;
  --pink: #EDEDE8; --pdim: #8A8A93; --pline: rgba(255,255,255,.09);
  position: relative; overflow: hidden; border-radius: 18px; padding: 20px 18px 16px;
  background: radial-gradient(130% 80% at 100% 0%, color-mix(in srgb, var(--pa) 9%, transparent), transparent 58%),
              linear-gradient(160deg, var(--pbg1), var(--pbg2));
  border: 1px solid color-mix(in srgb, var(--pa) 24%, transparent);
  color: var(--pink);
}
.poster[data-skin="mono"] {
  --pa: #FFFFFF; --pbg1: #17171A; --pbg2: #08080A; --pink: #F2F2F0; --pdim: #85858C;
}
.poster[data-skin="blueprint"] {
  --pa: #58C6F5; --pbg1: #10203A; --pbg2: #060D1A; --pink: #E6F1FA; --pdim: #7C96B0;
  --pline: rgba(120,180,230,.16);
}
.poster[data-skin="ember"] {
  --pa: #FF7A33; --pbg1: #2A1810; --pbg2: #120906; --pink: #F6E7DD; --pdim: #A98876;
  --pline: rgba(255,150,90,.14);
}
/* The light one. Reads as printed rather than screenshotted, and is the only
   finish that survives being pasted into a document. */
.poster[data-skin="paper"] {
  --pa: #14140F; --pbg1: #F3F0E6; --pbg2: #E4E0D2; --pink: #14140F; --pdim: #6E6A5C;
  --pline: rgba(0,0,0,.14);
  border-color: rgba(0,0,0,.16);
}
.poster .po-num, .poster .po-hero { color: var(--pa); }
.poster[data-skin="paper"] .po-num,
.poster[data-skin="paper"] .po-hero { color: #14140F; }

.po-head { display: flex; align-items: flex-start; gap: 11px; margin-bottom: 18px; }
.po-name { font-size: 22px; font-weight: 850; letter-spacing: -.02em; line-height: 1.1; }
.po-since { font-size: 11px; color: var(--pdim); margin-top: 3px; letter-spacing: .01em; }
.po-brand { font-size: 11px; font-weight: 900; letter-spacing: .3em; color: var(--pdim); flex: none; }
.po-lab { font-size: 9.5px; font-weight: 800; letter-spacing: .12em; text-transform: uppercase;
  color: var(--pdim); }
.po-faint { color: var(--pdim); }

/* You choose how many stats go on, so the grid has to look deliberate at any
   count. Flex with a centred last row rather than a fixed three columns: five
   tiles land 3 + 2 centred instead of 3 + 2 shoved left. */
.po-grid, .po-his, .po-mini-row {
  display: flex; flex-wrap: wrap; justify-content: center; gap: 16px 8px; text-align: center;
}
/* The basis has to clear the widest gap any of these three use (10px), or a
   full row overflows by a pixel and wraps the last item onto its own line. */
.po-grid > *, .po-his > *, .po-mini-row > * { flex: 0 0 calc(33.333% - 8px); min-width: 0; }
.po-grid[data-n="1"] > *, .po-his[data-n="1"] > *, .po-mini-row[data-n="1"] > * { flex-basis: 100%; }
.po-grid[data-n="2"] > *, .po-grid[data-n="4"] > *,
.po-his[data-n="2"] > *, .po-mini-row[data-n="2"] > * { flex-basis: calc(50% - 7px); }
.po-num { font-size: 21px; font-weight: 850; line-height: 1.05;
  filter: drop-shadow(0 0 8px color-mix(in srgb, var(--pa) 34%, transparent)); }
.po-grid .po-lab { margin-top: 3px; }
.poster[data-skin="paper"] .po-num { filter: none; }

/* Career moments: a weight, a distance, a month. Legible to anybody. */
.po-his { gap: 10px; margin-top: 18px; padding-top: 14px; border-top: 1px solid var(--pline);
  text-align: left; }
.po-his[data-n="1"] { text-align: center; }
.po-hi-v { font-size: 17px; font-weight: 850; margin-top: 4px; color: var(--pink); line-height: 1.1; }
.po-hi-u { font-size: 10px; font-weight: 700; color: var(--pdim); }
.po-hi-s { font-size: 10px; color: var(--pdim); margin-top: 2px; line-height: 1.3; }

.po-prs { margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--pline); }
.po-pr { display: flex; align-items: center; gap: 9px; padding: 4px 0; }
.po-dot { width: 5px; height: 5px; border-radius: 50%; background: var(--pa); flex: none;
  box-shadow: 0 0 7px color-mix(in srgb, var(--pa) 55%, transparent); }
.po-foot { display: flex; justify-content: space-between; gap: 10px; margin-top: 16px;
  padding-top: 12px; border-top: 1px solid var(--pline);
  font-size: 10px; color: var(--pdim); letter-spacing: .04em; }

/* ---- Ticket ---- */
.poster[data-layout="ticket"] { padding: 0 0 14px; }
.po-tk-top { position: relative; padding: 20px 18px 16px; }
.po-tk-brand { position: absolute; top: 20px; right: 18px; }
.po-tk-top .po-name { margin-top: 6px; }
/* the tear, with the notches punched out of the card's own edges */
.po-perf { display: flex; align-items: center; gap: 0; }
.po-perf i { width: 13px; height: 13px; border-radius: 50%; flex: none;
  background: var(--bg); box-shadow: inset 0 0 0 1px var(--pline); }
.po-perf i:first-child { margin-left: -7px; }
.po-perf i:last-child { margin-right: -7px; }
.po-perf span { flex: 1; height: 1px;
  background: repeating-linear-gradient(90deg, var(--pline) 0 5px, transparent 5px 10px); }
.po-rows { padding: 14px 18px 6px; }
.po-row2 { display: flex; align-items: baseline; gap: 8px; padding: 6px 0; }
.po-r-l { font-size: 11.5px; color: var(--pdim); flex: none; }
.po-r-dots { flex: 1; height: 1px; align-self: center;
  background: repeating-linear-gradient(90deg, var(--pline) 0 2px, transparent 2px 6px); }
.po-r-v { font-size: 16px; font-weight: 850; color: var(--pa); flex: none; }
.poster[data-skin="paper"] .po-r-v { color: #14140F; }
.po-stubs { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; padding: 14px 18px 4px; }
.po-bars { display: flex; align-items: flex-end; gap: 2px; height: 26px; padding: 8px 18px 0; }
.po-bars i { display: block; height: 100%; background: var(--pink); }

/* ---- Minimal ---- */
.poster[data-layout="minimal"] { padding: 30px 20px 22px; text-align: center; }
.po-hero { font-size: 68px; font-weight: 900; line-height: .95; letter-spacing: -.04em;
  margin: 10px 0 2px;
  filter: drop-shadow(0 0 22px color-mix(in srgb, var(--pa) 30%, transparent)); }
.poster[data-skin="paper"] .po-hero { filter: none; }
.po-hero-l { font-size: 12px; font-weight: 800; letter-spacing: .22em; text-transform: uppercase;
  color: var(--pdim); }
.po-mini-row { gap: 8px; margin-top: 26px; padding-top: 18px; border-top: 1px solid var(--pline); }
.po-mini-row .po-num { font-size: 16px; }
.po-mini-foot { display: flex; justify-content: space-between; align-items: baseline;
  margin-top: 22px; padding-top: 14px; border-top: 1px solid var(--pline);
  font-size: 12px; font-weight: 750; }

/* ---- the picker ---- */
.po-picker { display: flex; flex-direction: column; gap: 8px; margin-top: 14px; }
.po-picker .chip-row { flex-wrap: wrap; }
.po-swatch { position: relative; padding-left: 24px; }
.po-swatch::before {
  content: ''; position: absolute; left: 9px; top: 50%; transform: translateY(-50%);
  width: 10px; height: 10px; border-radius: 3px; border: 1px solid rgba(255,255,255,.18);
}
.po-sw-volt::before      { background: linear-gradient(135deg, #C6FF00, #12120F); }
.po-sw-mono::before      { background: linear-gradient(135deg, #FFFFFF, #101012); }
.po-sw-blueprint::before { background: linear-gradient(135deg, #58C6F5, #0B1526); }
.po-sw-ember::before     { background: linear-gradient(135deg, #FF7A33, #180C07); }
.po-sw-paper::before     { background: linear-gradient(135deg, #F3F0E6, #CFCABA); }
.po-pick-head { display: flex; align-items: baseline; justify-content: space-between;
  gap: 10px; margin-top: 6px; }
.po-pick-t { font-size: 10px; font-weight: 800; letter-spacing: .12em;
  text-transform: uppercase; color: var(--text-dim); }
/* The number is the whole idea: the layouts take from the top of this list, so
   what you put first is what a small layout keeps. */
.po-pick i { font-style: normal; font-weight: 900; font-size: 9.5px; margin-right: 5px;
  color: #0A0A0C; background: var(--lime); border-radius: 4px; padding: 1px 4px; }
.po-pick.on.over { opacity: .5; }
.po-pick.on.over i { display: none; }

/* --- boot sequences --- */
/* Each one has its own character on the way in and then gets out of the way.
   Scenery that stays is what made these read as clip art: a wheel parked behind
   the mark, crosshairs through the middle of it, a horizon line across the logo
   for good. Everything below that is not the mark ends at opacity 0. */

/* strike. It lands, throws shards, and settles */
.boot-strike .strike-flash {
  opacity: 0; animation: strike-flash .5s ease-out both;
  filter: drop-shadow(0 0 3px var(--lime));
}
@keyframes strike-flash { 0% { opacity: .95; } 22% { opacity: .5; } 100% { opacity: 0; } }
.boot-strike .mark-path { animation: strike-settle .9s var(--spring) both; }
@keyframes strike-settle {
  0% { transform: translateY(-24px) scale(1.4); opacity: 0; }
  38% { transform: translateY(2px) scale(.96); opacity: 1; }
  100% { transform: none; }
}
.boot-strike .shard {
  transform-origin: 32px 32px; opacity: 0;
  animation: shard-out .8s var(--ease-out) both; animation-delay: calc(.16s + var(--d));
}
@keyframes shard-out {
  0%   { opacity: 0; transform: rotate(var(--a)) scaleY(.15); }
  28%  { opacity: .7; }
  100% { opacity: 0; transform: rotate(var(--a)) translateY(-14px) scaleY(1.4); }
}

/* scope, four brackets close on it, hold, and release */
.boot-scope .scope-br {
  opacity: 0;
  animation: scope-br 1.5s cubic-bezier(.16,.9,.24,1) both;
  animation-delay: calc(var(--i) * 55ms);
}
@keyframes scope-br {
  0%   { opacity: 0; transform: translate(calc(var(--dx) * -2.2), calc(var(--dy) * -2.2)); }
  30%  { opacity: .85; transform: translate(var(--dx), var(--dy)); }
  62%  { opacity: .85; transform: none; }
  100% { opacity: 0; transform: none; }
}
.boot-scope .scope-ring {
  transform-origin: 32px 32px; opacity: 0;
  animation: scope-ring 1.6s cubic-bezier(.16,.9,.24,1) both;
}
@keyframes scope-ring {
  0%   { opacity: 0; transform: rotate(-120deg) scale(1.35); }
  40%  { opacity: .5; }
  70%  { opacity: .5; transform: rotate(0) scale(1); }
  100% { opacity: 0; transform: rotate(0) scale(1); }
}
.boot-scope .mark-path { animation: scope-mark .8s var(--spring) .3s both; }
@keyframes scope-mark { from { opacity: 0; transform: scale(.55); } to { opacity: 1; transform: none; } }

/* terminal, the log types itself, then clears, leaving the mark */
.boot-terminal .mark-path { animation: scope-mark .7s var(--spring) both; }
.boot-lines {
  position: absolute; left: 50%; top: 118px; transform: translateX(-50%);
  display: flex; flex-direction: column; align-items: flex-start; gap: 4px;
  animation: lines-out .5s ease 1.45s both;      /* gone before the wordmark lands */
  pointer-events: none;
}
@keyframes lines-out { from { opacity: 1; } to { opacity: 0; } }
.boot-line {
  font-size: 10px; letter-spacing: .1em; color: var(--lime); opacity: 0;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  white-space: nowrap;
  animation: boot-line .3s steps(2) both; animation-delay: calc(.3s + var(--i) * .3s);
}
@keyframes boot-line { from { opacity: 0; } to { opacity: .7; } }

/* sunrise, light comes up behind it and then goes */
.sun-glow {
  /* The light has to come up behind the MARK, not behind the wordmark. The
     gradient's hot edge is its bottom, so the bottom sits on the horizon line
     the mark is standing on, about 88px down a 108px mark. */
  position: absolute; left: 50%; top: -18px; width: 210px; height: 106px;
  transform: translateX(-50%); pointer-events: none;
  background: radial-gradient(60% 70% at 50% 100%,
    color-mix(in srgb, var(--lime) 42%, transparent) 0%,
    color-mix(in srgb, var(--lime) 12%, transparent) 42%, transparent 72%);
  opacity: 0;
  animation: sun-rise 2.3s cubic-bezier(.2,.75,.3,1) both;
}
@keyframes sun-rise {
  0%   { opacity: 0; transform: translateX(-50%) translateY(34px); }
  38%  { opacity: .95; }
  64%  { opacity: .8; transform: translateX(-50%) translateY(0); }
  100% { opacity: 0; transform: translateX(-50%) translateY(-6px); }
}
.boot-sunrise .horizon {
  stroke-dasharray: 100; stroke-dashoffset: 100;
  animation: horizon-in 2.3s cubic-bezier(.2,.8,.3,1) both;
}
@keyframes horizon-in {
  0%   { stroke-dashoffset: 100; opacity: 0; }
  34%  { opacity: .55; }
  66%  { stroke-dashoffset: 0; opacity: .55; }
  100% { stroke-dashoffset: 0; opacity: 0; }
}
.boot-sunrise .mark-path { animation: scan-mark 1s var(--ease-out) .55s both; }

/* spin. It spins up, blurs past, and is gone by the time it stops */
.boot-spin .spin-wheel {
  animation: wheel-spin 1.9s cubic-bezier(.1,.7,.25,1) both;
  transform-origin: 32px 32px;
}
@keyframes wheel-spin {
  0%   { opacity: 0; transform: rotate(0deg) scale(.78); }
  22%  { opacity: 1; }
  70%  { opacity: .85; transform: rotate(690deg) scale(1); }
  100% { opacity: 0; transform: rotate(760deg) scale(1.06); }
}
.boot-spin .mark-path { animation: scan-mark .9s var(--ease-out) .5s both; }

/* scan, a trace sweeps past and resolves into the shape */
.boot-scan .scan-trace {
  stroke-dasharray: 100; stroke-dashoffset: 100;
  animation: scan-draw 1.9s cubic-bezier(.3,0,.5,1) both;
}
@keyframes scan-draw {
  0%   { stroke-dashoffset: 100; opacity: .75; }
  58%  { stroke-dashoffset: 0; opacity: .75; }
  100% { stroke-dashoffset: 0; opacity: 0; }
}
.boot-scan .scan-bar { animation: scan-sweep 1.5s cubic-bezier(.4,0,.6,1) both; }
@keyframes scan-sweep {
  0%   { opacity: .55; transform: translateX(0); }
  85%  { opacity: .35; }
  100% { opacity: 0; transform: translateX(64px); }
}
.boot-scan .mark-path { animation: scan-mark 1s var(--ease-out) .6s both; }
@keyframes scan-mark { from { opacity: 0; filter: blur(4px); } to { opacity: 1; filter: none; } }

/* ---- the hidden two ---- */
/* eclipse. A dark disc slides off and takes a rim of light with it as it goes */
.boot-eclipse .ecl-disc { animation: ecl-slide 1.7s cubic-bezier(.5,0,.3,1) both; }
@keyframes ecl-slide {
  0%   { transform: translateX(-2px); }
  100% { transform: translateX(78px); }
}
.boot-eclipse .ecl-corona { animation: ecl-rim 1.7s ease-out both; }
@keyframes ecl-rim {
  0%   { opacity: 0; transform: scale(.94); }
  40%  { opacity: .5; }
  100% { opacity: 0; transform: scale(1.1); }
}
.boot-eclipse .mark-path { animation: scan-mark .8s var(--ease-out) .5s both; }

/* develop. The grain burns off and the mark comes up through it */
.boot-develop .dev-grain { animation: dev-fade 1.8s ease-out both; }
@keyframes dev-fade {
  0%   { opacity: .85; }
  55%  { opacity: .35; }
  100% { opacity: 0; }
}
.boot-develop .dev-mark { animation: dev-print 1.8s cubic-bezier(.3,.7,.3,1) both; }
@keyframes dev-print {
  0%   { opacity: 0; filter: blur(5px); }
  45%  { opacity: .45; filter: blur(2px); }
  100% { opacity: 1; filter: none; }
}

@media (prefers-reduced-motion: reduce) {
  .reactor-ticks, .liquid-wave, .seg-tick, .boot-line,
  .lat-cell, .macro-arc, .odo-line { animation: none !important; opacity: 1 !important; }
  /* Scenery is skipped outright rather than frozen mid-entrance: held still,
     a wheel or a reticle is exactly the parked furniture this set removed. */
  .boot-strike .shard, .boot-strike .strike-flash, .boot-scan .scan-bar,
  .boot-scan .scan-trace, .boot-spin .spin-wheel, .boot-scope .scope-br,
  .boot-scope .scope-ring, .boot-sunrise .horizon, .sun-glow, .boot-lines,
  .boot-eclipse .ecl-disc, .boot-eclipse .ecl-corona, .boot-develop .dev-grain {
    animation: none !important; opacity: 0 !important;
  }
}

/* Category grid. An odd count used to leave one card stranded on its own row,
   so the last one spans instead. */
/* minmax(0,·) rather than 1fr: a rigid 104px mark inside an auto-minimum track
   widens the column, and two widened columns push the grid past the screen. */
.cat-grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 10px; }
.cat-grid > * { min-width: 0; }
.cat-grid .row-between { min-width: 0; }
/* One box for every card mark, whatever form it takes, so nine different
   glyphs still line the grid up. Colour comes from the card's own ink, that
   is what the hollow states and threshold lines are drawn in. */
.glyph {
  width: 104px; max-width: 100%; height: 30px; flex: 0 1 auto; min-width: 0;
  color: var(--text-faint); overflow: visible;
}
.glyph-arc { width: 32px; height: 32px; flex: none; }
.cat-grid > *:last-child:nth-child(odd) { grid-column: 1 / -1; }

/* collapsible month sections, keeps a year of history to one screen */
.mo-group { border-top: 1px solid var(--line); }
.mo-group:first-of-type { border-top: 0; }
.mo-head {
  display: flex; align-items: center; gap: 9px; width: 100%; text-align: left;
  padding: 12px 2px; transition: opacity .2s;
}
.mo-head:active { opacity: .6; }
.mo-chev { display: grid; place-items: center; color: var(--text-faint); flex: none;
  transition: transform .28s var(--ease-out); }
.mo-group.open .mo-chev { transform: rotate(90deg); color: var(--lime); }
.mo-name { font-size: 13.5px; font-weight: 750; flex: none; }
.mo-sum { color: var(--text-faint); text-align: right; flex: 1; }
.mo-body { display: none; padding-bottom: 6px; }
.mo-group.open .mo-body { display: block; animation: mo-open .3s var(--ease-out) both; }
@keyframes mo-open { from { opacity: 0; transform: translateY(-5px); } to { opacity: 1; transform: none; } }

/* mood history rows */
.mood-row {
  display: flex; align-items: center; gap: 10px; width: 100%; text-align: left;
  padding: 9px 10px; border-radius: 11px; margin-bottom: 3px;
  background: rgba(255,255,255,.03); border: 1px solid transparent;
  transition: transform .2s var(--spring), border-color .2s;
}
.mood-row:active { transform: scale(.98); border-color: var(--line); }
.mood-dot { width: 9px; height: 9px; border-radius: 50%; flex: none; }

/* meals grouped by day */
.meal-day { border-top: 1px solid var(--line); padding: 11px 0 4px; }
.meal-day:first-of-type { border-top: 0; padding-top: 4px; }
.md-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.md-toggle {
  display: flex; align-items: baseline; gap: 8px; min-width: 0; text-align: left;
  padding: 4px 0; transition: opacity .2s;
}
.md-toggle:active { opacity: .6; }
.md-chev {
  align-self: center; flex: none; color: var(--text-faint); display: inline-flex;
  transition: transform .28s var(--spring);
}
.meal-day.open .md-chev { transform: rotate(90deg); color: var(--lime); }
.md-n { flex: none; align-self: center; margin-left: auto; }
.md-date { font-size: 12.5px; font-weight: 750; flex: none; }
.md-tot { font-size: 12px; color: var(--text-dim); display: flex; align-items: baseline; gap: 2px; min-width: 0; }
.md-tot .faint { margin-right: 6px; font-size: 10.5px; }
.md-tot b { color: var(--text); }
/* Add to a day you're already looking at. Faint until touched, the history is
   there to be read, and a row of bright + signs down the page would read as a
   to-do list of days you failed to log. */
.md-add {
  flex: none; display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; border-radius: 999px; align-self: center;
  background: rgba(255,255,255,.05); border: 1px solid var(--line);
  color: var(--text-dim); opacity: .6;
  transition: transform .2s var(--spring), opacity .2s, color .2s;
}
.md-add:active { transform: scale(.88); opacity: 1; color: var(--lime); }
/* Shut by default. A month of six meals a day is two hundred rows between you
   and the day you came for, and the totals above are what you scan anyway. */
.md-meals { display: none; flex-direction: column; gap: 2px; padding-top: 7px; }
.meal-day.open .md-meals { display: flex; animation: md-in .26s var(--spring) both; }
@keyframes md-in { from { opacity: 0; transform: translateY(-4px); } }
.md-meal {
  display: flex; align-items: center; gap: 9px; width: 100%; text-align: left;
  padding: 7px 10px; border-radius: 10px; font-size: 13px;
  background: rgba(255,255,255,.03); border: 1px solid transparent;
  transition: transform .2s var(--spring), border-color .2s;
}
.md-meal:active { transform: scale(.98); border-color: var(--line); }

.list-item.in-session { opacity: .45; }

/* exercise name doubles as the swap control */
.ex-name {
  display: inline-flex; align-items: center; gap: 6px; text-align: left;
  padding: 2px 6px 2px 0; border-radius: 8px; transition: opacity .2s;
}
.ex-name:active { opacity: .55; }
.ex-name .ic { opacity: .5; }

/* "did I do it today" rows */
.today-row {
  display: flex; align-items: center; gap: 12px; width: 100%; text-align: left;
  padding: 11px 13px; border-radius: 14px;
  background: rgba(255,255,255,.04); border: 1px solid var(--line);
  transition: transform .26s var(--spring), border-color .25s;
}
.today-row:active { transform: scale(.975); }
.tr-mark {
  display: grid; place-items: center; width: 34px; height: 34px; flex: none;
  border-radius: 11px; color: var(--text-faint);
  background: rgba(255,255,255,.05); border: 1px solid rgba(255,255,255,.07);
}
.tr-mark.part { color: var(--accent-habits); border-color: rgba(111,207,142,.3); }
.tr-mark.on {
  color: var(--accent-habits); background: rgba(111,207,142,.14);
  border-color: rgba(111,207,142,.4); box-shadow: 0 0 14px rgba(111,207,142,.25);
}
.tr-mark.on.mood {
  color: var(--accent-mood); background: color-mix(in srgb, var(--accent-mood) 14%, transparent);
  border-color: color-mix(in srgb, var(--accent-mood) 40%, transparent);
  box-shadow: 0 0 14px color-mix(in srgb, var(--accent-mood) 25%, transparent);
}

/* mood distribution */
.mood-bar-row { display: flex; align-items: center; gap: 10px; margin-bottom: 7px; }
.mood-bar-lab { width: 42px; flex: none; color: var(--text-dim); }
.mood-bar { flex: 1; height: 8px; border-radius: 5px; background: rgba(255,255,255,.06); overflow: hidden; }
.mood-bar i { display: block; height: 100%; border-radius: 5px; transition: width .9s var(--ease-out); }

/* milestone ladders, compact grid so the list scales */
.ms-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.ms-cell {
  display: flex; flex-direction: column; gap: 6px; text-align: left;
  padding: 11px 12px 10px; border-radius: 14px;
  background: var(--card); border: 1px solid var(--line);
  transition: transform .28s var(--spring), border-color .25s;
}
.ms-cell:active { transform: scale(.96); }
.ms-head { display: flex; align-items: center; gap: 7px; }
.ms-ic { display: grid; place-items: center; width: 22px; height: 22px; flex: none;
  border-radius: 7px; background: rgba(255,255,255,.05); color: var(--text-dim); }
.ms-cell.hard .ms-ic { background: var(--lime-soft); color: var(--lime); }
.ms-name { font-size: 12px; font-weight: 750; line-height: 1.15; flex: 1; min-width: 0; }
.ms-count { font-size: 11px; font-weight: 800; color: var(--text-dim); flex: none; }
.ms-sub { color: var(--text-dim); line-height: 1.25; }
.ms-cell.maxed { border-color: color-mix(in srgb, var(--lime) 32%, transparent); }
.ms-cell.maxed .ms-count { color: var(--lime); }

/* milestone ladders */
.ms-now { display: flex; align-items: flex-end; justify-content: space-between;
  gap: 12px; margin: 14px 0 10px; }
.ms-step { font-size: 10px; font-weight: 800; letter-spacing: .1em; text-transform: uppercase;
  color: var(--text-faint); margin-right: 5px; }
.ms-row .tk-ic { color: var(--text-dim); }
.ms-row .tk-tier { color: var(--text-dim); font-size: 12px; letter-spacing: 0; }
.ms-row.hard .tk-ic { color: var(--lime); background: var(--lime-soft);
  border-color: color-mix(in srgb, var(--lime) 22%, transparent); }
.ms-row.maxed { border-color: color-mix(in srgb, var(--lime) 32%, transparent); }
.ms-row.maxed .tk-tier { color: var(--lime); }

/* long-game feats: same shape as a track row, dimmer until earned */
.long-row .tk-ic { color: var(--text-faint); }
.long-row:not(.done) { opacity: .72; }
.long-row .tk-tier { color: var(--text-faint); }
.long-row.done { border-color: color-mix(in srgb, var(--lime) 32%, transparent); }
.long-row.done .tk-ic { color: var(--lime); background: var(--lime-soft);
  border-color: color-mix(in srgb, var(--lime) 28%, transparent); box-shadow: 0 0 14px var(--lime-glow); }
.long-row.done .tk-tier { color: var(--lime); }

/* tier ladder in the track sheet */
.tier-row { display: flex; gap: 12px; padding: 11px 2px; border-bottom: 1px solid var(--line); }
.tier-row:last-child { border-bottom: 0; }
.tier-row.done { opacity: 1; }
.tier-row:not(.done):not(.active) { opacity: .5; }
.tier-dot { width: 13px; height: 13px; border-radius: 50%; flex: none; margin-top: 4px;
  border: 2px solid var(--text-faint); background: transparent; }
.tier-row.active .tier-dot { border-color: var(--lime); box-shadow: 0 0 8px var(--lime-glow); }

/* earned identity, worn beside Quick add */
.qa-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin: 16px 2px 8px; }
.callsign {
  display: inline-flex; align-items: center; gap: 5px; flex: none;
  font-size: 10.5px; font-weight: 800; letter-spacing: .14em; text-transform: uppercase;
  color: var(--lime); padding: 4px 9px 4px 7px; border-radius: 999px; text-decoration: none;
  background: var(--lime-soft); border: 1px solid color-mix(in srgb, var(--lime) 24%, transparent);
  transition: transform .28s var(--spring);
}
.callsign:active { transform: scale(.94); }
.callsign .cs-sep { opacity: .45; }
.callsign .ic { filter: drop-shadow(0 0 5px var(--lime-glow)); }

.rank-foot { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; margin-top: 9px; }
.seg.tabs { padding: 3px; gap: 2px; }
.seg.tabs button {
  flex: 1; min-width: 0; padding: 8px 2px;
  font-size: 11.5px; letter-spacing: -.01em; white-space: nowrap;
}

/* ------------------------------------------------------------
   Unlock grid, accents, atmospheres, callsigns share one cell
   ------------------------------------------------------------ */
.unlock-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 9px; }
.unlock-cell {
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  padding: 13px 7px 11px; border-radius: 15px; background: var(--card);
  border: 1px solid var(--line); text-align: center;
  transition: transform .28s var(--spring), border-color .25s;
}
.unlock-cell:active { transform: scale(.94); }
.unlock-cell.equipped { border-color: var(--lime); box-shadow: 0 0 0 1px var(--lime-soft), inset 0 0 22px rgba(255,255,255,.04); }
.unlock-cell.locked { opacity: .45; }
/* A hidden unlock. Brighter than a plain locked tile rather than dimmer: it is
   not a door you failed to open, it is a thing you do not know about yet, and
   it should be the tile your eye goes to. */
.unlock-cell.veiled { opacity: .8; border-style: dashed; border-color: rgba(255,255,255,.18); }
.uc-veil {
  width: 34px; height: 34px; border-radius: 50%;
  display: grid; place-items: center;
  font-size: 17px; font-weight: 850; color: var(--text-dim);
  background: repeating-linear-gradient(45deg, rgba(255,255,255,.05) 0 3px, transparent 3px 6px), var(--bg-raise);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.09);
}
.kit-art-lg .uc-veil { width: 54px; height: 54px; font-size: 26px; }
.uc-art { position: relative; display: grid; place-items: center; }
.uc-check, .uc-lock {
  position: absolute; right: -4px; bottom: -3px; width: 17px; height: 17px;
  border-radius: 50%; display: grid; place-items: center;
  border: 2px solid var(--card);
}
.uc-check { background: var(--lime); color: #0A0A0C; }
.uc-lock { background: var(--bg-raise); color: var(--text-faint); }
.uc-name { font-size: 12.5px; font-weight: 750; line-height: 1.15; }
.uc-req { color: var(--text-faint); line-height: 1.3; }
.unlock-cell.equipped .uc-req { color: var(--text-dim); }
.uc-on {
  margin-top: 1px; padding: 2px 7px; border-radius: 999px;
  font-weight: 800; letter-spacing: .08em; text-transform: uppercase; font-size: 9px;
  color: var(--lime); background: var(--lime-soft);
  border: 1px solid color-mix(in srgb, var(--lime) 26%, transparent);
}

/* the detail sheet behind every unlockable */
.kit-sheet-head { display: flex; align-items: center; gap: 14px; }
.kit-art-lg { display: grid; place-items: center; width: 62px; height: 62px; flex: none;
  border-radius: 18px; background: rgba(255,255,255,.04); border: 1px solid rgba(255,255,255,.07); }
.kit-art-lg.locked { opacity: .45; }
.kit-art-lg > * { transform: scale(1.45); }
.kit-why {
  margin-top: 14px; padding: 13px 14px; border-radius: 15px;
  background: var(--bg-raise); border: 1px solid var(--line);
}
.kit-why-lab { font-size: 10px; font-weight: 800; letter-spacing: .12em;
  text-transform: uppercase; color: var(--text-faint); margin-bottom: 8px; }
.kit-why-line { display: flex; align-items: center; gap: 10px; font-size: 14px; font-weight: 650; }

.th-swatch, .atmo-chip, .cs-chip {
  width: 34px; height: 34px; border-radius: 50%; display: grid; place-items: center;
  overflow: hidden; flex: none;
}
.th-swatch { background: var(--c); color: #0A0A0C; box-shadow: 0 0 14px color-mix(in srgb, var(--c) 55%, transparent); }
.locked .th-swatch { background: rgba(255,255,255,.1); box-shadow: none; }
.th-swatch.prism {
  background: conic-gradient(from 0deg, #FF5CD8, #FFC93C, #39FF6A, #45E0FF, #C77DFF, #FF5CD8);
  animation: prism-spin 6s linear infinite;
}
@keyframes prism-spin { to { transform: rotate(360deg); } }
.cs-chip { background: var(--lime-soft); color: var(--lime); border: 1px solid rgba(255,255,255,.08); }
.locked .cs-chip { background: rgba(255,255,255,.06); color: var(--text-faint); }

/* live little previews of what each atmosphere does */
.atmo-chip { position: relative; background: #0B0B0E; border: 1px solid rgba(255,255,255,.09); }
.atmo-chip::before, .atmo-chip::after { content: ''; position: absolute; inset: 0; }
.atmo-fields::before {
  background:
    radial-gradient(circle at 30% 32%, var(--lime) 0%, transparent 42%),
    radial-gradient(circle at 68% 66%, var(--lime) 0%, transparent 38%);
  opacity: .6; animation: atmo-drift 7s ease-in-out infinite alternate;
}
.atmo-ember::before {
  background:
    radial-gradient(circle at 50% 108%, var(--lime) 0%, transparent 58%);
  opacity: .75;
}
/* Streaks, not dots. Wildly different lengths, leaning rather than upright,
   thinning toward the top, flickering the way the canvas ones do. Nothing here
   is the same size as anything else, which is the whole character of it. */
.atmo-ember::after {
  background:
    radial-gradient(ellipse 5% 22% at 28% 68%, var(--lime) 0 62%, transparent 63%),
    radial-gradient(ellipse 11% 11% at 62% 54%, var(--lime) 0 62%, transparent 63%),
    radial-gradient(ellipse 4% 17% at 44% 36%, var(--lime) 0 62%, transparent 63%),
    radial-gradient(ellipse 3.5% 9% at 74% 26%, var(--lime) 0 62%, transparent 63%),
    radial-gradient(ellipse 6% 6% at 18% 42%, var(--lime) 0 62%, transparent 63%),
    radial-gradient(ellipse 3% 13% at 86% 44%, var(--lime) 0 62%, transparent 63%),
    radial-gradient(ellipse 2.5% 5% at 36% 16%, var(--lime) 0 62%, transparent 63%);
  transform: skewX(-9deg);
  animation: atmo-rise 3.6s linear infinite, atmo-flick .42s steps(2) infinite;
}
@keyframes atmo-flick { from { opacity: 1; } to { opacity: .72; } }
.atmo-deep::after {
  background:
    radial-gradient(circle at 24% 30%, #fff 0 7%, transparent 8%),
    radial-gradient(circle at 66% 22%, #fff 0 5%, transparent 6%),
    radial-gradient(circle at 40% 62%, #fff 0 6%, transparent 7%),
    radial-gradient(circle at 76% 70%, #fff 0 5%, transparent 6%),
    radial-gradient(circle at 56% 84%, #fff 0 4%, transparent 5%);
  opacity: .8; animation: atmo-twinkle 4s ease-in-out infinite;
}
.atmo-aurora::before {
  background: linear-gradient(100deg, transparent 6%, var(--lime) 22%, transparent 36%,
    transparent 52%, var(--lime) 68%, transparent 84%);
  opacity: .55; animation: atmo-sway 5s ease-in-out infinite alternate;
}
.atmo-grid::before {
  background:
    repeating-linear-gradient(to bottom, transparent 0 5px, color-mix(in srgb, var(--lime) 55%, transparent) 5px 6px),
    linear-gradient(to bottom, transparent 40%, color-mix(in srgb, var(--lime) 22%, transparent));
  opacity: .55;
}
.atmo-storm::before {
  background: radial-gradient(circle at 40% 60%, rgba(255,255,255,.24) 0%, transparent 55%),
              radial-gradient(circle at 70% 44%, rgba(255,255,255,.16) 0%, transparent 50%);
}
.atmo-storm::after {
  background: radial-gradient(circle at 62% 30%, var(--lime) 0%, transparent 45%);
  animation: atmo-flash 4.5s steps(1) infinite;
}
.atmo-rain::before {
  background: repeating-linear-gradient(74deg, transparent 0 4px,
    color-mix(in srgb, var(--lime) 70%, transparent) 4px 5px, transparent 5px 9px);
  opacity: .5; animation: atmo-fall 1.6s linear infinite;
}
.atmo-dunes::before {
  background:
    radial-gradient(120% 60% at 50% 118%, var(--lime) 0%, transparent 60%),
    radial-gradient(120% 55% at 30% 92%, var(--lime) 0%, transparent 55%);
  opacity: .45; animation: atmo-swell 6s ease-in-out infinite alternate;
}
.atmo-pulse::before {
  background: radial-gradient(circle at 50% 50%, transparent 18%,
    color-mix(in srgb, var(--lime) 65%, transparent) 20%, transparent 24%,
    transparent 42%, color-mix(in srgb, var(--lime) 40%, transparent) 44%, transparent 48%);
  animation: atmo-beat 1.4s cubic-bezier(.2,.7,.3,1) infinite;
}
.atmo-drift::after {
  background:
    radial-gradient(circle at 38% 44%, var(--lime) 0 10%, transparent 11%),
    radial-gradient(circle at 55% 38%, var(--lime) 0 8%, transparent 9%),
    radial-gradient(circle at 50% 58%, var(--lime) 0 8%, transparent 9%),
    radial-gradient(circle at 66% 56%, var(--lime) 0 6%, transparent 7%);
  opacity: .8; animation: atmo-flock 5s ease-in-out infinite alternate;
}
.atmo-hearth::before {
  background: radial-gradient(circle at 50% 108%, var(--lime) 0%, transparent 62%);
  opacity: .9; animation: atmo-bank 4s ease-in-out infinite alternate;
}
.atmo-hearth::after {
  background: radial-gradient(circle at 30% 90%, var(--lime) 0 9%, transparent 10%),
              radial-gradient(circle at 62% 92%, var(--lime) 0 7%, transparent 8%);
  animation: atmo-coal 2.4s ease-in-out infinite alternate;
}
/* Caustics: the crossing net of light on the bottom of a pool. */
.atmo-causal::before {
  background:
    repeating-linear-gradient(28deg, transparent 0 5px,
      color-mix(in srgb, var(--lime) 55%, transparent) 5px 6px, transparent 6px 11px),
    repeating-linear-gradient(-28deg, transparent 0 5px,
      color-mix(in srgb, var(--lime) 55%, transparent) 5px 6px, transparent 6px 11px);
  opacity: .55; animation: atmo-caustic 3.4s ease-in-out infinite alternate;
}
@keyframes atmo-caustic {
  from { transform: translate(-2px, 0) scale(1); }
  to   { transform: translate(2px, 1px) scale(1.08); }
}
/* Pressure: one broad band, drifting up and down over a long period. */
.atmo-pressure::before {
  background: linear-gradient(to bottom, transparent 22%,
    color-mix(in srgb, var(--lime) 62%, transparent) 48%, transparent 74%);
  opacity: .75; animation: atmo-baro 5s ease-in-out infinite alternate;
}
@keyframes atmo-baro {
  from { transform: translateY(-22%); }
  to   { transform: translateY(22%); }
}

/* ---- the hidden three ---- */
/* Constellation: points, and the lines somebody decided joined them up. */
.atmo-constellation::before {
  background:
    radial-gradient(circle at 22% 28%, var(--lime) 0 1.6px, transparent 2.2px),
    radial-gradient(circle at 58% 18%, var(--lime) 0 1.2px, transparent 1.8px),
    radial-gradient(circle at 78% 46%, var(--lime) 0 1.6px, transparent 2.2px),
    radial-gradient(circle at 40% 62%, var(--lime) 0 1.2px, transparent 1.8px),
    radial-gradient(circle at 68% 80%, var(--lime) 0 1.4px, transparent 2px),
    linear-gradient(29deg, transparent calc(50% - .4px),
      color-mix(in srgb, var(--lime) 40%, transparent) 50%, transparent calc(50% + .4px)),
    linear-gradient(-52deg, transparent calc(50% - .4px),
      color-mix(in srgb, var(--lime) 30%, transparent) 50%, transparent calc(50% + .4px));
  opacity: .8; animation: atmo-turn 14s linear infinite;
}
@keyframes atmo-turn { to { transform: rotate(360deg); } }

/* Kelp: standing threads, swaying out of step with each other. */
.atmo-kelp::before {
  background: repeating-linear-gradient(90deg, transparent 0 4px,
    color-mix(in srgb, var(--lime) 60%, transparent) 4px 5.4px, transparent 5.4px 9px);
  opacity: .6; animation: atmo-sway 3.2s ease-in-out infinite alternate;
}
@keyframes atmo-sway {
  from { transform: skewX(-7deg) translateX(-2px); }
  to   { transform: skewX(7deg) translateX(2px); }
}

/* Bloom: one swell out of the middle, then a long wait. */
.atmo-bloom::before {
  background: radial-gradient(circle at 50% 52%, var(--lime) 0%, transparent 58%);
  opacity: 0; animation: atmo-bloom 3.6s ease-out infinite;
}
@keyframes atmo-bloom {
  0%   { opacity: 0; transform: scale(.2); }
  38%  { opacity: .8; }
  62%  { opacity: 0; transform: scale(1.25); }
  100% { opacity: 0; transform: scale(1.25); }
}

.atmo-slumber::before {
  background: radial-gradient(circle at 50% 62%, var(--lime) 0%, transparent 55%);
  opacity: .55; animation: atmo-breathe 4.6s ease-in-out infinite alternate;
}
.atmo-headwind::before {
  background: repeating-linear-gradient(115deg, var(--lime) 0 1.5px, transparent 1.5px 7px);
  opacity: .45; animation: atmo-rush 1.5s linear infinite;
}
.atmo-lanes::before {
  background: repeating-linear-gradient(0deg, var(--lime) 0 1px, transparent 1px 6px);
  opacity: .4; animation: atmo-swell 3.4s ease-in-out infinite alternate;
}
.atmo-lanes::after {
  background: radial-gradient(circle at 40% 45%, var(--lime) 0 18%, transparent 46%);
  opacity: .4; animation: atmo-breathe 4s ease-in-out infinite alternate;
}
@keyframes atmo-rush { to { transform: translate(-7px, 3.3px); } }
@keyframes atmo-bank { to { transform: translateY(-2px) scale(1.08); } }
@keyframes atmo-coal { 0% { opacity: .4; } 100% { opacity: 1; } }
@keyframes atmo-breathe { to { transform: translateY(-3px) scale(1.14); opacity: .85; } }
@keyframes atmo-fall { to { transform: translate(-9px, 9px); } }
@keyframes atmo-swell { to { transform: translateY(-3px) scaleX(1.1); } }
@keyframes atmo-beat { 0% { transform: scale(.55); opacity: .9; } 70%, 100% { transform: scale(1.25); opacity: 0; } }
@keyframes atmo-flock { to { transform: translate(4px, -3px) rotate(8deg); } }
@keyframes atmo-drift { to { transform: translate(3px, -3px) scale(1.12); } }
@keyframes atmo-rise { from { transform: translateY(9px); opacity: 0; } 40% { opacity: .85; } to { transform: translateY(-10px); opacity: 0; } }
@keyframes atmo-twinkle { 0%, 100% { opacity: .35; } 50% { opacity: .95; } }
@keyframes atmo-sway { to { transform: translateX(5px) skewX(-6deg); } }
@keyframes atmo-flash { 0%, 88% { opacity: 0; } 90%, 92% { opacity: .9; } 94% { opacity: .2; } 96% { opacity: .7; } 97%, 100% { opacity: 0; } }

/* ------------------------------------------------------------
   Feats
   ------------------------------------------------------------ */
.feat-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 9px; }
.feat-cell {
  display: flex; flex-direction: column; gap: 4px; padding: 13px;
  border-radius: 15px; background: var(--card); border: 1px solid var(--line);
  transition: transform .28s var(--spring);
  position: relative; overflow: hidden;
}
.feat-cell:active { transform: scale(.96); }
.ft-ic {
  width: 30px; height: 30px; border-radius: 10px; display: grid; place-items: center;
  background: rgba(255,255,255,.05); border: 1px solid rgba(255,255,255,.06);
  color: var(--text-faint); margin-bottom: 3px;
}
.feat-cell.got { border-color: rgba(198,255,0,.3); border-color: color-mix(in srgb, var(--lime) 32%, transparent); }
.feat-cell.got .ft-ic {
  background: var(--lime-soft); color: var(--lime);
  border-color: color-mix(in srgb, var(--lime) 30%, transparent);
  box-shadow: 0 0 14px var(--lime-glow);
}
.feat-cell.got::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(120% 90% at 100% 0%, var(--lime-soft), transparent 62%);
}
.feat-cell:not(.got) { opacity: .62; }
.feat-cell.secret .ft-name { letter-spacing: .06em; }
.ft-name { font-size: 13.5px; font-weight: 780; line-height: 1.2; }
.ft-desc { color: var(--text-dim); line-height: 1.3; }
.ft-when { color: var(--text-faint); margin-top: auto; padding-top: 6px;
  font-weight: 700; letter-spacing: .02em; }
.feat-cell.got .ft-when { color: color-mix(in srgb, var(--lime) 62%, var(--text-faint)); }

/* ------------------------------------------------------------
   Records. PRs and completed goals
   ------------------------------------------------------------ */
.rec-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.rec-card {
  position: relative; overflow: hidden; display: flex; flex-direction: column; gap: 3px;
  padding: 12px 13px 11px; min-height: 116px; border-radius: 16px;
  background: linear-gradient(155deg, var(--card-2), var(--card));
  border: 1px solid var(--line);
  transition: transform .3s var(--spring);
}
.rec-card:active { transform: scale(.96); }
/* A PR is lime-edged wherever it appears. It stopped being a card in the
   Records rebuild and took its own look with it, .rec-row is the row it
   became, and every .rec-row is a PR. */
:is(.rec-card.pr, .rec-row) { border-color: color-mix(in srgb, var(--lime) 34%, transparent);
  box-shadow: inset 0 0 24px color-mix(in srgb, var(--lime) 7%, transparent); }
.rec-card > img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; opacity: .26; }
.rec-card > :not(img) { position: relative; }
.rec-head { display: flex; align-items: center; gap: 7px; margin-bottom: 5px; }
.rec-ic {
  width: 26px; height: 26px; border-radius: 9px; flex: none; display: grid; place-items: center;
  background: var(--lime-soft); color: var(--lime);
  border: 1px solid color-mix(in srgb, var(--lime) 26%, transparent);
}
.rec-tag {
  font-size: 9.5px; font-weight: 800; letter-spacing: .12em; text-transform: uppercase;
  color: var(--text-faint); padding: 2px 6px; border-radius: 6px;
  background: rgba(255,255,255,.05); border: 1px solid rgba(255,255,255,.07);
}
:is(.rec-card.pr, .rec-row) .nu-tag { color: var(--lime); border-color: color-mix(in srgb, var(--lime) 26%, transparent); }
.rec-card.goal .rec-ic { background: rgba(255,255,255,.06); color: var(--text); border-color: rgba(255,255,255,.09); }
.rec-label { font-weight: 780; font-size: 13.5px; line-height: 1.25; }
.rec-sub { font-size: 11.5px; color: var(--text-dim); line-height: 1.3; }
.rec-date { color: var(--text-faint); margin-top: auto; padding-top: 5px; }
.rec-card { width: 100%; text-align: left; }

/* ------------------------------------------------------------
   New. The inbox for things you have earned but not looked at
   ------------------------------------------------------------ */
/* One mark, everywhere: a tile you have not opened yet carries a lime dot, so
   finding what changed never depends on remembering which tab it lived in. */
.nu-dot {
  position: absolute; top: 7px; right: 7px; width: 7px; height: 7px; border-radius: 50%;
  background: var(--lime); box-shadow: 0 0 0 2px var(--card), 0 0 9px var(--lime-glow);
  pointer-events: none; z-index: 2;
}
.ms-cell, .unlock-cell, .track-row { position: relative; }
/* `.rec-card > :not(img)` forces its children relative to sit over the photo,
   and that beats the base rule. So this one has to re-declare position. */
.rec-card .nu-dot { position: absolute; top: 9px; right: 9px; }
/* A wide row already spends its top-right corner on a tier label, so it gets a
   rail down the leading edge instead of a dot in the corner. */
.track-row .nu-dot {
  top: 10px; bottom: 10px; left: -1px; right: auto; width: 3px; height: auto;
  border-radius: 0 3px 3px 0; box-shadow: 0 0 10px var(--lime-glow);
}
/* ------------------------------------------------------------
   Insights, scoreboard, week wheel, reflection
   ------------------------------------------------------------ */
.sb-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 2px 14px; }
.sb-tile { padding: 9px 0; border-bottom: 1px solid var(--line); }
.sb-grid > .sb-tile:nth-last-child(-n+2) { border-bottom: 0; }
.sb-val { font-size: 21px; font-weight: 800; line-height: 1.1; }
.sb-lab { font-size: 11px; color: var(--text-dim); margin-top: 1px; }
.sb-delta { font-size: 10.5px; font-weight: 700; margin-top: 3px; color: var(--text-faint); }
.sb-delta.up   { color: var(--lime); }
.sb-delta.down { color: var(--accent-alcohol); }
.sb-delta.flat { color: var(--text-dim); }

/* ---------- Body weight: the average, and the weeks behind it ---------- */
.wk-d.up { color: var(--lime); }
.wk-d.down { color: var(--accent-alcohol); }
.wk-d.flat { color: var(--text-dim); }
.wk-d { font-weight: 700; }

.wk-list { border-top: 1px solid var(--line); }
.wk-row {
  display: grid; grid-template-columns: 1fr auto 54px 26px; align-items: center;
  column-gap: 9px; row-gap: 5px; padding: 7px 0 8px;
  border-bottom: 1px solid var(--line); font-size: 13px;
}
.wk-list > .wk-row:last-child { border-bottom: 0; }
.wk-name { color: var(--text-dim); font-weight: 650; }
.wk-avg { font-weight: 800; font-size: 14.5px; }
.wk-delta-s { font-size: 11.5px; text-align: right; }
.wk-n { text-align: right; }
/* The dot says where the week sits between the lightest and heaviest shown.
   Numbers a pound apart are hard to rank by reading; a position is not. */
.wk-track {
  grid-column: 1 / -1; position: relative; height: 3px; border-radius: 2px;
  background: rgba(255,255,255,.06);
}
.wk-track i {
  position: absolute; top: 50%; width: 7px; height: 7px; border-radius: 50%;
  background: var(--lime); transform: translate(-50%, -50%); box-shadow: 0 0 8px var(--lime-glow);
}

.rose-wrap { display: flex; justify-content: center; padding: 4px 0 2px; }
.rose { width: min(210px, 76%); height: auto; color: var(--text-faint); }
.sh-legend { display: flex; justify-content: space-between; gap: 4px; margin-top: 6px; }
.sh-day { display: flex; flex-direction: column; align-items: center; gap: 2px; flex: 1; min-width: 0; }
.sh-day b { font-size: 11px; font-weight: 800; color: var(--text-faint); }
.sh-day i { font-size: 10px; font-style: normal; color: var(--text-faint); font-variant-numeric: tabular-nums; }
.sh-day.solid b, .sh-day.solid i { color: var(--lime); }
.sh-day.thin b, .sh-day.thin i { color: var(--accent-alcohol); opacity: .8; }

/* The strip of weeks behind you. The dots carry how each one landed; the row
   of hit targets under them is what you actually tap. */
.rf-strip { display: flex; gap: 3px; padding: 2px 0 4px; }
.rf-week {
  flex: 1; min-width: 0; height: 32px; display: grid; place-items: center;
  border-radius: 8px; transition: background .2s;
}
.rf-week:active { background: rgba(255,255,255,.08); }
.rf-week.now { background: rgba(255,255,255,.045); }
.rf-week i {
  display: block; width: 11px; height: 11px; border-radius: 50%;
  border: 1.5px solid var(--line);
}
.rf-week.has i {
  border-color: transparent; background: var(--rc);
  box-shadow: 0 0 8px color-mix(in srgb, var(--rc) 60%, transparent);
}
.rf-last { display: flex; align-items: center; gap: 9px; margin-top: 10px; }
.rf-chip {
  flex: none; font-size: 10px; font-weight: 800; letter-spacing: .06em; text-transform: uppercase;
  color: var(--rc); background: color-mix(in srgb, var(--rc) 13%, transparent);
  border: 1px solid color-mix(in srgb, var(--rc) 32%, transparent);
  border-radius: 999px; padding: 2px 8px;
}
/* the reflection sheet */
.rf-facts { display: flex; flex-direction: column; gap: 7px; margin-bottom: 16px; }
.rf-fact {
  display: flex; gap: 9px; align-items: flex-start; font-size: 13px; line-height: 1.4;
  color: var(--text-dim); background: var(--bg-raise); border: 1px solid var(--line);
  border-radius: 11px; padding: 9px 12px;
}
.rf-fact svg { flex: none; margin-top: 1px; color: var(--text-faint); }
.rf-fact.good svg { color: var(--lime); }
.rf-scale { display: flex; gap: 6px; }
.rf-scale .rf-dot {
  flex: 1; min-width: 0; display: flex; flex-direction: column; align-items: center; gap: 5px;
  padding: 9px 2px 7px; border-radius: 12px; background: var(--card-2);
  border: 1px solid var(--line); transition: border-color .2s, background .2s;
}
.rf-dot i { width: 13px; height: 13px; border-radius: 50%; background: var(--rc); opacity: .35;
  transition: opacity .2s, box-shadow .2s; }
.rf-dot span { color: var(--text-faint); font-weight: 700; letter-spacing: -.01em; }
.rf-dot.on { border-color: var(--rc); background: color-mix(in srgb, var(--rc) 11%, transparent); }
.rf-dot.on i { opacity: 1; box-shadow: 0 0 10px var(--rc); }
.rf-dot.on span { color: var(--rc); }
/* the Home nudge */
.rf-nudge {
  display: flex; align-items: center; gap: 11px; width: 100%; text-align: left;
  padding: 12px 14px; margin-bottom: 12px;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
}
.rf-nudge:active { transform: scale(.985); }
.rf-nudge .icb { flex: none; }

/* ------------------------------------------------------------
   The circuit, one conductor per habit
   ------------------------------------------------------------ */
.circuit { display: flex; flex-direction: column; gap: 15px; }
.cc-lane {
  display: flex; flex-direction: column; gap: 6px; width: 100%; text-align: left;
  padding: 2px 0; transition: opacity .25s;
}
.cc-lane:active { opacity: .6; }
.cc-head { display: flex; align-items: baseline; gap: 8px; }
.cc-streak {
  flex: none; font-size: 12px; font-weight: 800; font-variant-numeric: tabular-nums;
  color: var(--text-faint); padding: 1px 8px; border-radius: 999px;
  background: rgba(255,255,255,.04); border: 1px solid var(--line);
}
.cc-streak.live {
  color: var(--cc); background: color-mix(in srgb, var(--cc) 12%, transparent);
  border-color: color-mix(in srgb, var(--cc) 34%, transparent);
}
/* The unpowered wire: always full width, so a broken habit still reads as a
   length of conductor with the current missing rather than as absent data. */
.cc-rail {
  position: relative; display: block; height: 7px; border-radius: 4px;
  background: rgba(255,255,255,.055);
  /* week ticks, one notch every seven days of the ten-week window */
  background-image: repeating-linear-gradient(90deg,
    transparent 0 calc(10% - 1px), rgba(255,255,255,.075) calc(10% - 1px) 10%);
}
/* Before the habit existed there is no wire at all. That is not a run of
   failures and must never be shaded like one. */
.cc-dead {
  position: absolute; left: 0; top: 50%; height: 1px; transform: translateY(-50%);
  border-radius: 1px;
  background: repeating-linear-gradient(90deg,
    rgba(255,255,255,.11) 0 2px, transparent 2px 5px);
}
/* Current. One element per unbroken run, so forty days is one rod with two
   rounded ends and the gaps between runs are physical breaks. */
.cc-run {
  position: absolute; top: 0; bottom: 0; border-radius: 4px;
  background: linear-gradient(90deg,
    color-mix(in srgb, var(--cc) 68%, transparent), var(--cc));
  box-shadow: 0 0 7px color-mix(in srgb, var(--cc) 45%, transparent);
}
/* The terminal. Lit while the current still reaches today. */
.cc-node {
  position: absolute; right: -4px; top: 50%; width: 9px; height: 9px;
  transform: translateY(-50%); border-radius: 50%;
  background: var(--bg-raise); border: 1.5px solid rgba(255,255,255,.14);
}
.cc-node.live {
  background: var(--cc); border-color: var(--cc);
  box-shadow: 0 0 10px color-mix(in srgb, var(--cc) 75%, transparent);
  animation: cc-pulse 2.6s ease-in-out infinite;
}
@keyframes cc-pulse {
  0%, 100% { box-shadow: 0 0 8px color-mix(in srgb, var(--cc) 55%, transparent); }
  50%      { box-shadow: 0 0 15px color-mix(in srgb, var(--cc) 95%, transparent); }
}
.cc-meta { color: var(--text-faint); letter-spacing: .01em; }
.cc-fresh {
  color: var(--text-faint); line-height: 1.5; margin-top: 14px; padding-top: 11px;
  border-top: 1px solid var(--line);
}
.cc-fresh b { color: var(--text-dim); }
.cc-axis {
  display: flex; justify-content: space-between; color: var(--text-faint);
  padding-top: 2px; border-top: 1px solid var(--line); margin-top: 3px;
}
/* the habit sheet */
.hb-now { display: flex; align-items: flex-end; justify-content: space-between;
  gap: 12px; margin: 14px 0 10px; }
.hb-tell {
  display: flex; gap: 9px; align-items: flex-start; font-size: 13px; line-height: 1.45;
  color: var(--text-dim); background: var(--bg-raise);
  border: 1px solid var(--line); border-radius: var(--radius); padding: 11px 13px;
}
.hb-tell svg { flex: none; margin-top: 2px; color: var(--accent-habits); }

/* The day's plan, above what actually got logged. States read the same here as
   in the week strip and the week review, one vocabulary for one idea. */
.pl-tag {
  flex: none; font-weight: 700; letter-spacing: .02em; color: var(--text-faint);
}
.plan-item.ok .pl-tag   { color: var(--lime); }
.plan-item.miss .pl-tag { color: var(--accent-alcohol); }   /* same red as the week strip */
.plan-item.off          { opacity: .55; }

.nu-head {
  display: flex; align-items: center; gap: 10px; padding: 12px 14px; margin-bottom: 4px;
  background: var(--card); border: 1px solid var(--line); border-radius: 15px;
}
.nu-row {
  position: relative; display: flex; align-items: center; gap: 11px;
  width: 100%; text-align: left; padding: 11px 13px;
  background: var(--card); border: 1px solid var(--line); border-radius: 14px;
  transition: transform .28s var(--spring), opacity .3s;
}
.nu-row:active { transform: scale(.975); }
.nu-row.read { opacity: .38; }        /* it goes quiet the moment you open it */
.nu-meta { display: flex; flex-direction: column; align-items: flex-end; gap: 3px; flex: none; }
/* Every row carries one of these, so a lime pill on each would rebuild the
   jumble one line further down. The lime belongs to the count. */
.nu-tag {
  padding: 2px 7px; border-radius: 999px; font-size: 9px; font-weight: 800;
  letter-spacing: .07em; text-transform: uppercase; white-space: nowrap;
  color: var(--text-faint); background: rgba(255,255,255,.045);
  border: 1px solid var(--line);
}
.tab-count {
  display: inline-grid; place-items: center; min-width: 15px; height: 15px; padding: 0 4px;
  margin-left: 6px; border-radius: 999px; font-size: 9.5px; font-weight: 800; font-style: normal;
  background: var(--lime); color: #0A0A0C; vertical-align: 1px;
}
.seg.tabs button.on .tab-count { background: #0A0A0C; color: var(--lime); }

@media (prefers-reduced-motion: reduce) {
  .th-swatch.prism, .atmo-chip::before, .atmo-chip::after { animation: none; }
}


/* --- cardio: the sport switch and the ride / swim breakdown cards --- */
.sport-seg button { display: flex; align-items: center; justify-content: center; gap: 5px; }
.sport-seg svg { flex: none; }
.mix-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px 10px; }
.mix-grid > div {
  display: flex; flex-direction: column; gap: 2px;
  padding: 9px 11px; border-radius: 11px;
  background: rgba(255,255,255,.035); border: 1px solid rgba(255,255,255,.05);
}
.mix-grid b { font-size: 20px; font-weight: 800; line-height: 1.1; }
/* a sport-specific block inside the log sheet, swapped by the Where control */
.sport-only[hidden] { display: none; }


/* --- indoor ride effort verdict, live under the power fields --- */
.effort-note {
  display: flex; align-items: flex-start; gap: 7px;
  margin-top: 10px; padding: 8px 10px; border-radius: 10px;
  font-size: 12px; line-height: 1.4;
  border: 1px solid transparent;
}
.effort-note.hide { display: none; }
.effort-note svg { flex: none; margin-top: 1px; }
.effort-note.ok {
  color: var(--lime); background: var(--lime-soft);
  border-color: color-mix(in srgb, var(--lime) 30%, transparent);
}
.effort-note.soft {
  color: var(--text-dim); background: rgba(255,255,255,.04);
  border-color: rgba(255,255,255,.08);
}


/* --- strength table: one row per lift, in estimated max --- */
.str-list { display: flex; flex-direction: column; gap: 2px; }
.str-row {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 2px; border-bottom: 1px solid rgba(255,255,255,.045);
}
.str-row:last-child { border-bottom: 0; }
.str-row .grow { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.str-row b.small { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.str-num { display: flex; align-items: baseline; gap: 2px; }
.str-num b { font-size: 17px; font-weight: 800; }
.str-delta {
  min-width: 46px; text-align: right; font-size: 11.5px; font-weight: 700;
  color: var(--text-dim);
}
.str-delta.up { color: var(--lime); }
.str-delta.down { color: var(--accent-alcohol); }
.str-delta.best {
  color: var(--lime); text-transform: uppercase; letter-spacing: .08em; font-size: 10px;
}


/* --- daily score breakdown --- */
.score-head { display: flex; align-items: flex-start; gap: 14px; margin-bottom: 14px; }
.score-big { font-size: 44px; font-weight: 800; line-height: 1; color: var(--lime); flex: none; }
.score-den { font-size: 15px; font-weight: 700; color: var(--text-dim); margin-left: 2px; }
.score-list { display: flex; flex-direction: column; gap: 6px; }
.score-row {
  display: flex; align-items: center; gap: 11px; width: 100%; text-align: left;
  padding: 10px 12px; border-radius: 13px;
  background: rgba(255,255,255,.035); border: 1px solid rgba(255,255,255,.05);
  color: inherit; font: inherit;
}
.score-row .grow { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.score-row .sr-ic { display: flex; flex: none; color: var(--text-dim); }
.score-row.full .sr-ic { color: var(--lime); }
.score-row.none { opacity: .78; }
.sr-bar { display: block; height: 3px; border-radius: 2px; background: rgba(255,255,255,.08); margin-top: 3px; }
.sr-bar i { display: block; height: 100%; border-radius: 2px; background: var(--lime); }
.score-row.none .sr-bar i { background: var(--text-dim); }
.sr-pts { flex: none; display: flex; align-items: baseline; gap: 1px; }
.sr-pts b { font-size: 15px; font-weight: 800; }
.score-row.full .sr-pts b { color: var(--lime); }
.score-next {
  margin-top: 14px; padding: 12px 14px; border-radius: 13px;
  background: var(--lime-soft); border: 1px solid color-mix(in srgb, var(--lime) 22%, transparent);
}
.score-next .small { line-height: 1.6; }


/* --- training load --- */
.load-top { display: flex; align-items: center; gap: 14px; margin-bottom: 4px; }
.load-ratio {
  flex: none; display: flex; flex-direction: column; align-items: center; justify-content: center;
  width: 74px; height: 74px; border-radius: 18px;
  background: rgba(255,255,255,.04); border: 1.5px solid rgba(255,255,255,.09);
}
.load-ratio .num { font-size: 25px; font-weight: 800; line-height: 1; }
.load-ratio .tiny { color: var(--text-dim); margin-top: 3px; letter-spacing: .06em; text-transform: uppercase; font-size: 9.5px; }
.load-ratio.ok    { color: var(--lime); border-color: color-mix(in srgb, var(--lime) 32%, transparent); background: var(--lime-soft); }
.load-ratio.watch { color: var(--accent-nutrition); border-color: rgba(255,180,84,.36); background: rgba(255,180,84,.09); }
.load-ratio.warn  { color: var(--accent-alcohol); border-color: rgba(224,108,138,.45); background: rgba(224,108,138,.11); }
.load-ratio.low   { color: var(--accent-sleep); border-color: rgba(167,155,255,.36); background: rgba(167,155,255,.09); }
.load-chart { margin-top: 12px; }
.load-kinds { display: flex; flex-direction: column; gap: 7px; }
.load-kind { display: flex; align-items: center; gap: 9px; }
.load-kind .lk-ic { display: flex; color: var(--text-dim); }
.lk-ratio { min-width: 38px; text-align: right; font-weight: 800; font-size: 13px; color: var(--text-dim); }
.lk-ratio.ok { color: var(--lime); }
.lk-ratio.watch { color: var(--accent-nutrition); }
.lk-ratio.warn { color: var(--accent-alcohol); }
.lk-ratio.low { color: var(--accent-sleep); }


/* --- the week strip, inside the plan card --- */
.week-strip {
  display: block; width: 100%; text-align: left; margin: 0 0 12px;
  padding: 10px 12px; border-radius: 13px; color: inherit; font: inherit;
  background: rgba(255,255,255,.03); border: 1px solid rgba(255,255,255,.05);
}
.ws-head { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 8px; }
.ws-days { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; }
.ws-day {
  display: flex; flex-direction: column; align-items: center; gap: 5px;
  padding: 5px 0 6px; border-radius: 9px;
}
.ws-day.today { background: var(--lime-soft); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--lime) 26%, transparent); }
.ws-wd { font-size: 10.5px; font-weight: 700; color: var(--text-dim); letter-spacing: .04em; }
.ws-day.today .ws-wd { color: var(--lime); }
.ws-marks { display: flex; gap: 3px; align-items: center; min-height: 9px; }
.ws-mark { width: 8px; height: 8px; border-radius: 50%; display: block; }
.ws-mark.done   { background: var(--lime); }
.ws-mark.today  { background: transparent; box-shadow: inset 0 0 0 2px var(--lime); }
.ws-mark.ahead  { background: transparent; box-shadow: inset 0 0 0 1.5px rgba(255,255,255,.28); }
.ws-mark.missed { background: var(--accent-alcohol); opacity: .8; }
/* Paused is the quietest state of all. It is the app telling you it is not
   asking. It must never read louder than a session still to come. */
.ws-mark.paused { background: transparent; box-shadow: inset 0 0 0 1.5px var(--accent-sleep); opacity: .3; }
.ws-mark.moved  { background: var(--accent-nutrition); opacity: .75; }
.ws-mark.extra  { background: rgba(255,255,255,.34); }
.ws-mark.rest   { width: 4px; height: 4px; background: rgba(255,255,255,.16); }

/* --- the week sheet --- */
.wk-date { flex: none; display: flex; flex-direction: column; align-items: center; width: 26px; }
.list-item.wk-today { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--lime) 28%, transparent); }
.wk-item { display: flex; align-items: center; gap: 7px; padding: 1px 0; }
.wk-item + .wk-item { margin-top: 4px; }
.wk-item svg { flex: none; color: var(--text-dim); }
.wk-item .tiny { margin-left: auto; color: var(--text-dim); }
.wk-item.done svg, .wk-item.done .tiny { color: var(--lime); }
.wk-item.missed .tiny { color: var(--accent-alcohol); }
.wk-item.paused .tiny { color: var(--accent-sleep); }
.wk-item.moved .tiny { color: var(--accent-nutrition); }
.wk-item.today .tiny { color: var(--lime); }


/* --- calendar: what is still to come, and what was asked for and missed --- */
.cal-dots i.planned { background: transparent !important; box-shadow: inset 0 0 0 1.5px currentColor; opacity: .45; }
.cal-miss {
  position: absolute; top: 4px; right: 4px;
  width: 5px; height: 5px; border-radius: 50%;
  background: var(--accent-alcohol); opacity: .55;
}
.cal-day { position: relative; }


/* ------------------------------------------------------------
   A workout that is still going
   The sheet is a view onto a draft, not the draft itself, so both
   ends say so: a live strip inside the form, and a way back to it
   from Home.
   ------------------------------------------------------------ */
.lf-live {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 12px; margin-bottom: 12px;
  border-radius: 13px;
  background: color-mix(in srgb, var(--lime) 7%, transparent);
  border: 1px solid color-mix(in srgb, var(--lime) 22%, transparent);
}
.lf-live b { color: var(--lime); }
/* Discard is the destructive option here. It does not get to be the brightest
   thing in the strip. */
.lf-live .link { flex: none; color: var(--text-dim); font-weight: 650; }
.lf-dot {
  flex: none; width: 8px; height: 8px; border-radius: 50%;
  background: var(--lime);
  animation: lf-pulse 2.4s ease-in-out infinite;
}
@keyframes lf-pulse {
  0%, 100% { opacity: 1; box-shadow: 0 0 0 0 color-mix(in srgb, var(--lime) 50%, transparent); }
  55%      { opacity: .5; box-shadow: 0 0 0 6px transparent; }
}
@media (prefers-reduced-motion: reduce) { .lf-dot { animation: none; } }

.wip-card {
  display: flex; align-items: center; gap: 11px; width: 100%; text-align: left;
  padding: 12px 14px; margin-bottom: 12px;
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--lime) 8%, var(--card));
  border: 1px solid color-mix(in srgb, var(--lime) 30%, transparent);
}
.wip-card:active { transform: scale(.985); }
.wip-card .icb { flex: none; color: var(--lime); }
.wip-card b { color: var(--lime); }
.wip-go { flex: none; font-size: 13px; font-weight: 750; color: var(--lime); }


/* ------------------------------------------------------------
   The record book
   A standings table, not a feed: one line per lift, the number
   that stands right now on the right, and the whole climb behind
   it a tap away.
   ------------------------------------------------------------ */
.rb-sum { display: flex; justify-content: space-around; gap: 8px; text-align: center; }
.rb-list { display: flex; flex-direction: column; gap: 8px; }
.rb-row {
  display: flex; align-items: center; gap: 11px;
  width: 100%; text-align: left; padding: 11px 13px;
  background: var(--card); border: 1px solid var(--line); border-radius: 14px;
  transition: transform .28s var(--spring);
}
.rb-row:active { transform: scale(.975); }
.rb-row .grow { min-width: 0; }
.rb-row b { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rb-val {
  flex: none; font-variant-numeric: tabular-nums; font-weight: 800;
  font-size: 15px; letter-spacing: -.02em; color: var(--lime);
}

.rb-row, .rec-row { position: relative; }

/* the climb */
.rb-chain { display: flex; flex-direction: column; gap: 2px; }
.rb-link {
  display: flex; align-items: baseline; gap: 9px;
  padding: 6px 8px; border-radius: 9px;
  border-left: 2px solid var(--line); margin-left: 3px;
}
.rb-link-v {
  flex: none; min-width: 88px;
  font-variant-numeric: tabular-nums; font-weight: 750; font-size: 13.5px;
  color: var(--text-dim);
}
.rb-link.now {
  border-left-color: var(--lime);
  background: color-mix(in srgb, var(--lime) 7%, transparent);
}
.rb-link.now .rb-link-v { color: var(--lime); font-weight: 850; font-size: 15px; }


/* ------------------------------------------------------------
   Time off, the log of every break
   ------------------------------------------------------------ */
.to-row {
  display: flex; align-items: center; gap: 11px;
  width: 100%; text-align: left; padding: 11px 13px;
  background: var(--card); border: 1px solid var(--line); border-radius: 14px;
  transition: transform .28s var(--spring);
}
.to-row:active { transform: scale(.975); }
.to-row .grow { min-width: 0; }
.to-row b { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.to-row.open { border-color: color-mix(in srgb, var(--lime) 30%, transparent); }
.to-days {
  flex: none; font-variant-numeric: tabular-nums; font-weight: 800;
  font-size: 15px; letter-spacing: -.02em; color: var(--text-dim);
}
.to-row.open .to-days { color: var(--lime); }


/* ------------------------------------------------------------
   Calendar: what the month was, and the year around it
   The page gains one row and loses one, the seven-item legend
   that used to explain six dots now lives in the month sheet.
   ------------------------------------------------------------ */
.cal-title {
  display: flex; align-items: baseline; gap: 7px;
  font-size: 21px; font-weight: 800; letter-spacing: -.02em; color: var(--text);
  padding: 2px 6px; border-radius: 10px;
}
.cal-title:active { transform: scale(.96); }

.cal-sum {
  display: flex; align-items: center; gap: 4px; width: 100%;
  padding: 10px 12px; margin-bottom: 12px;
  background: var(--card); border: 1px solid var(--line); border-radius: 14px;
}
.cal-sum:active { transform: scale(.985); }
.cal-stat { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; text-align: left; }
.cal-stat-v { display: flex; align-items: baseline; gap: 5px; }
.cal-stat b {
  font-size: 19px; font-weight: 850; letter-spacing: -.03em; color: var(--text);
  font-variant-numeric: tabular-nums;
}
.cal-stat .tiny { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cal-sum-go { flex: none; font-size: 15px; }
.cal-dlt { font-size: 10.5px; font-weight: 800; color: var(--text-faint); font-variant-numeric: tabular-nums; }
.cal-dlt.up { color: var(--lime); }
.cal-dlt.down { color: var(--accent-alcohol); }

/* the grid, shaded by the day's score */
.cal-day.sc0 { background: var(--card); }
.cal-day.sc1 { background: color-mix(in srgb, var(--lime) 8%, var(--card)); }
.cal-day.sc2 { background: color-mix(in srgb, var(--lime) 17%, var(--card)); }
.cal-day.sc3 { background: color-mix(in srgb, var(--lime) 30%, var(--card)); }
.cal-day.sc4 { background: color-mix(in srgb, var(--lime) 46%, var(--card)); color: var(--text); }
/* A fully paused day is not a day you scored badly, so the stripe wins over
   the shading. Scoped pauses never get this class, see calendar.js. */
.cal-day.paused { background: repeating-linear-gradient(135deg, var(--card), var(--card) 5px, #1b1b22 5px, #1b1b22 8px); }

/* the month sheet */
.ms-row { display: flex; align-items: baseline; gap: 8px; padding: 9px 0; border-bottom: 1px solid var(--line); }
.ms-row:last-child { border-bottom: 0; }
.ms-val { font-variant-numeric: tabular-nums; font-weight: 800; }
.cal-key { display: flex; flex-direction: column; gap: 8px; font-size: 12.5px; color: var(--text-dim); }
.cal-key span { display: flex; align-items: center; gap: 8px; }
.cal-key i { flex: none; width: 8px; height: 8px; border-radius: 50%; }
.cal-key .key-sc { width: 15px; height: 15px; border-radius: 5px; background: color-mix(in srgb, var(--lime) 46%, var(--card)); }
.cal-key .key-paused { width: 15px; height: 15px; border-radius: 5px;
  background: repeating-linear-gradient(135deg, var(--card), var(--card) 4px, #26262e 4px, #26262e 7px); }

/* the year */
.yr-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
.yr-cell {
  display: flex; flex-direction: column; align-items: flex-start; gap: 6px;
  padding: 11px 12px; border-radius: 14px;
  background: var(--card); border: 1px solid transparent;
  transition: transform .28s var(--spring);
}
.yr-cell:active { transform: scale(.95); }
.yr-cell.sc1 { background: color-mix(in srgb, var(--lime) 8%, var(--card)); }
.yr-cell.sc2 { background: color-mix(in srgb, var(--lime) 17%, var(--card)); }
.yr-cell.sc3 { background: color-mix(in srgb, var(--lime) 30%, var(--card)); }
.yr-cell.sc4 { background: color-mix(in srgb, var(--lime) 46%, var(--card)); }
.yr-cell.fut { opacity: .32; }
.yr-cell.now { border-color: var(--lime); }
.yr-bar { display: block; width: 100%; height: 3px; border-radius: 2px; background: rgba(255,255,255,.09); }
.yr-bar i { display: block; height: 100%; border-radius: 2px; background: var(--lime); opacity: .8; }

/* the way out of the preset row, once there are more than a handful */
.chip.chip-more { border-style: dashed; color: var(--text-dim); }
/* Meal headings inside the food picker, and the rule that separates what you
   saved from what you merely ate this week. */
.mp-head { margin: 14px 2px 8px; }
.mp-split {
  display: flex; align-items: baseline; gap: 8px;
  margin: 22px 2px 2px; padding-top: 14px;
  border-top: 1px solid var(--line);
  font-size: 12px; font-weight: 800; letter-spacing: .1em; text-transform: uppercase;
  color: var(--text-dim);
}
.mp-split + .mp-head { margin-top: 10px; }
/* A preset called "Cinnamon Toast ice cream 3 serving" is a whole row on its
   own; the names are shortened in logsheet.js. This only stops a long one
   from ever taking a whole line to itself. */
#mf-presets .chip { max-width: 62%; white-space: nowrap; }

/* The day in a sheet title, which is also the control for changing it. Sized
   to sit on the title's baseline rather than push it around, and it goes lime
   the moment it stops saying Today so a backfill is never a silent one. */
.chip-day {
  padding: 3px 10px; font-size: 12px; font-weight: 650;
  vertical-align: 2px; margin-left: 4px;
}
.chip-day::after { content: '⌄'; font-size: 11px; opacity: .55; margin-left: 1px; }
.day-row { gap: 7px; align-items: center; }
.day-row .input { flex: 1; padding: 9px 11px; font-size: 14px; }


/* ------------------------------------------------------------
   Notes
   Notes were write-only: the one way back was a search box that
   needed you to remember the words. These are the three ways in
, the timeline, a tag, a search, plus the one that matters
   most, an old note arriving without being asked for.
   ------------------------------------------------------------ */
.nt-old {
  border-color: color-mix(in srgb, var(--lime) 26%, transparent);
  background: color-mix(in srgb, var(--lime) 6%, var(--card));
  text-align: left; width: 100%; display: block;
}
.nt-quote { font-size: 15.5px; line-height: 1.5; color: var(--text); font-style: italic; }

.nt-list { display: flex; flex-direction: column; gap: 8px; }
.nt-row {
  display: flex; align-items: flex-start; gap: 11px;
  width: 100%; text-align: left; padding: 11px 13px;
  background: var(--card); border: 1px solid var(--line); border-radius: 14px;
  transition: transform .28s var(--spring);
}
.nt-row:active { transform: scale(.985); }
.nt-row .grow { min-width: 0; }
.nt-day {
  flex: none; display: flex; flex-direction: column; align-items: center;
  width: 30px; padding-top: 1px;
}
.nt-day b { font-size: 17px; font-weight: 850; letter-spacing: -.03em; color: var(--text); font-variant-numeric: tabular-nums; }
.nt-meta { display: flex; align-items: center; gap: 5px; }
.nt-meta svg { flex: none; }
.nt-text {
  font-size: 14px; line-height: 1.45; color: var(--text); margin-top: 3px;
  /* three lines is a glance; the sheet is where you read the whole thing */
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}
.nt-tags { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 6px; }
.nt-tags i {
  font-style: normal; font-size: 11px; font-weight: 700; color: var(--lime);
  background: color-mix(in srgb, var(--lime) 10%, transparent);
  padding: 1px 7px; border-radius: 999px;
}
.nt-thumb { flex: none; width: 44px; height: 44px; border-radius: 10px; object-fit: cover; }
.nt-full { width: 100%; border-radius: 14px; margin-bottom: 12px; }

/* The Notes card carries words where the others carry a number. It sits in the
   same slot and clamps to two lines, so the card is the same height as the one
   beside it rather than stretching its whole row. */
.cat-quote {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
  font-style: italic; line-height: 1.35;
}
.cat-when { font-style: normal; color: var(--lime); font-weight: 750; margin-right: 3px; }

/* The running total on the caffeine sheet, which changes under your thumb as
   you tap servings in, so it reads as the answer rather than a heading. */
.cf-total { text-align: center; padding: 14px 0 4px; }
.cf-total .h1 { color: var(--accent-caffeine); }
.cf-total .tiny { margin-top: 4px; min-height: 15px; }

/* The star row on a learning item. Empty stars stay visible so the control
   reads as a rating you have not given rather than a rating of nothing. */
.stars { display: inline-flex; gap: 3px; }
.stars button { background: none; border: 0; padding: 2px; line-height: 0; color: var(--text-dim); }
.stars button.on { color: var(--accent-learn); }
