/* exp 1.7 design: paper + pixel plasma, blob panels, pill buttons, pixel type.
   colors come from themes.css (--c-* rgb triplets, --bg, --panel, --line, --plasma-*, --shapes).
   rules: all lowercase, no trailing periods, separators are " | ", status lines start with "! " or "> ". */

@font-face { font-family: "Pixelify Sans"; font-weight: 400; src: url("fonts/pixelify-sans-latin-400-normal.woff2") format("woff2"); font-display: swap; }
@font-face { font-family: "Pixelify Sans"; font-weight: 600; src: url("fonts/pixelify-sans-latin-600-normal.woff2") format("woff2"); font-display: swap; }
@font-face { font-family: "Pixelify Sans"; font-weight: 700; src: url("fonts/pixelify-sans-latin-700-normal.woff2") format("woff2"); font-display: swap; }
@font-face { font-family: "Fredoka"; font-weight: 400; src: url("fonts/fredoka-latin-400-normal.woff2") format("woff2"); font-display: swap; }
@font-face { font-family: "Fredoka"; font-weight: 500; src: url("fonts/fredoka-latin-500-normal.woff2") format("woff2"); font-display: swap; }
@font-face { font-family: "Fredoka"; font-weight: 600; src: url("fonts/fredoka-latin-600-normal.woff2") format("woff2"); font-display: swap; }
@font-face { font-family: "Jersey 10"; font-weight: 400; src: url("fonts/jersey-10-latin-400-normal.woff2") format("woff2"); font-display: swap; }
/* "exp pixel": pixelify sans letters + jersey 10 digits and euro sign (pixelify's 2 / 5 read as z / s, its € like @).
   jersey's figures are drawn smaller: size-adjust 125% puts them at pixelify's letter height. one face per weight
   so no faux bold is synthesized. */
@font-face { font-family: "exp pixel"; font-weight: 400; src: url("fonts/pixelify-sans-latin-400-normal.woff2") format("woff2"); font-display: swap; }
@font-face { font-family: "exp pixel"; font-weight: 600; src: url("fonts/pixelify-sans-latin-600-normal.woff2") format("woff2"); font-display: swap; }
@font-face { font-family: "exp pixel"; font-weight: 700; src: url("fonts/pixelify-sans-latin-700-normal.woff2") format("woff2"); font-display: swap; }
@font-face { font-family: "exp pixel"; font-weight: 400; src: url("fonts/jersey-10-latin-400-normal.woff2") format("woff2"); unicode-range: U+0030-0039, U+20AC; size-adjust: 125%; font-display: swap; }
@font-face { font-family: "exp pixel"; font-weight: 600; src: url("fonts/jersey-10-latin-400-normal.woff2") format("woff2"); unicode-range: U+0030-0039, U+20AC; size-adjust: 125%; font-display: swap; }
@font-face { font-family: "exp pixel"; font-weight: 700; src: url("fonts/jersey-10-latin-400-normal.woff2") format("woff2"); unicode-range: U+0030-0039, U+20AC; size-adjust: 125%; font-display: swap; }

:root {
  /* fallback = water dark (themes.css overrides per [data-theme][data-scheme]) */
  --c-fg: 79 200 255; --c-dim: 41 129 173; --c-mute: 32 100 136; --c-faint: 18 60 82;
  --c-ok: 120 236 214; --c-warn: 246 212 124; --c-err: 255 126 138; --c-cyan: 169 232 255; --c-acc: 169 232 255;
  --bg: #030608; --panel: #071119; --line: #0e2f42;
  --plasma-base: 190; --plasma-range: 50; --plasma-drift: 22; --plasma-sat: .85; --shapes: dot ring;
  --pixel: "exp pixel", "Pixelify Sans", ui-monospace, monospace;
  --body: "Fredoka", system-ui, -apple-system, sans-serif;
  --tiny: "exp pixel", "Pixelify Sans", monospace;  /* small pixel text: pixelify (+ jersey digits) */
  --num: "Jersey 10", "Pixelify Sans", monospace;    /* counters: clear digits (pixelify's 5 reads as s) */
  --radius: 26px 12px 24px 14px / 16px 22px 12px 26px;
  --radius-b: 12px 26px 14px 24px / 22px 16px 26px 12px;
  color-scheme: dark;
}
* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
*, *::before, *::after { --c: var(--c-fg); color: rgb(var(--c)); }
html { background: var(--bg); -webkit-text-size-adjust: 100%; }
/* nothing may widen the page sideways: the tab slide (translateX) and arriving rows are transforms, which count as
   overflow; a wider page makes mobile browsers zoom out and pushed the fixed dock off the bottom (1.20). clip, not
   hidden: no scroll container, sticky keeps working. */
html, body { overflow-x: clip; }
body { margin: 0; min-height: 100%; font-family: var(--body); font-weight: 500; font-size: 16px; line-height: 1.4; text-transform: lowercase; overflow-wrap: anywhere; }
/* every page with the bottom tab bar keeps room for it (forms too: the save button must scroll clear of the bar) */
body:has(> .dock) { padding-bottom: calc(env(safe-area-inset-bottom) + 108px); }   /* tab bar + its xp shadow */
html:has(body[data-tab]), body { touch-action: pan-y pinch-zoom; }
main { max-width: 40rem; margin: 0 auto; padding: calc(env(safe-area-inset-top) + 24px) 16px 24px; position: relative; z-index: 2; }
main.tab-anim { transition: transform .22s ease-out, opacity .22s ease-out; }
[hidden] { display: none !important; }
::selection { background: rgb(var(--c-acc)); color: var(--bg); }
:focus-visible { outline: 2px solid rgb(var(--c-acc)); outline-offset: 2px; }

/* ---------- background layers: paper (pixel plasma + lattice) and particles (fx.js) ---------- */
#bg, #fx { position: fixed; inset: 0; width: 100%; height: 100%; image-rendering: pixelated; }
#bg { z-index: 0; } #fx { z-index: 6; pointer-events: none; }

/* ---------- type ---------- */
h1 { font-family: var(--pixel); font-weight: 700; font-size: 32px; line-height: 1.1; margin: 0; letter-spacing: .01em; text-shadow: 3px 3px 0 rgb(var(--c-acc) / .3); overflow-wrap: anywhere; }
.strip { display: block; width: 100%; height: 9px; margin: 6px 0 10px; image-rendering: pixelated; border-radius: 6px 2px 8px 3px; background: rgb(var(--c-acc) / .35); }
h2 { font-family: var(--tiny); font-weight: 600; font-size: 12px; letter-spacing: .12em; --c: var(--c-dim); margin: 22px 2px 8px; }
h2 > b, .label b { font-weight: 400; }
.head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
.head .ttl { flex: 1; min-width: 0; }
.head .btn { flex: none; margin: 4px 0 0; }
.today-top { display: flex; align-items: flex-start; gap: 10px; margin: 0 0 4px; }
.today-top .grow p { margin: 6px 0; }
/* the face's lower part hangs beside the "# due" heading (left-aligned, short): no empty band below the date lines */
/* the canvas has empty margins (padding for the motion): pulled up under the title strip, and its empty lower part
   tucks behind the heading row; cards paint above it (they are stacking contexts), so taps still reach them */
.face { flex: none; width: 176px; height: 176px; margin: -28px -18px -64px 0; touch-action: manipulation; cursor: pointer; }
.face-spot { cursor: default; }
/* while the "tap and hold" hint shows (new accounts), the face overhangs less, so the first card does not cover it.
   the attribute stays until the next page load: the layout never jumps in the session where the hint goes away. */
.face[data-hint="1"] { margin-bottom: -40px; }
/* everything after the date block (done toast, headings, lists) is one layer above the face: whatever overlaps it
   draws over it and takes the taps there; the face keeps its taps everywhere else */
.today-top ~ * { position: relative; z-index: 1; }
p { margin: 10px 0; }
.dim { --c: var(--c-dim); } .mute { --c: var(--c-mute); } .ok { --c: var(--c-ok); } .warn { --c: var(--c-warn); } .err { --c: var(--c-err); } .late { --c: var(--c-err); } .acc { --c: var(--c-acc); }
.small { font-size: 14px; } .strong { font-weight: 600; }
.lines { line-height: 1.55; }
.tiny { font-family: var(--tiny); font-size: 12px; letter-spacing: .08em; }
a { color: rgb(var(--c)); text-decoration: none; }
.ul { border-bottom: 2px solid rgb(var(--c-acc) / .6); }
code, pre { font-family: var(--pixel); font-size: 16px; }
pre { white-space: pre-wrap; margin: 8px 0; }
.key { font-family: var(--pixel); font-size: 18px; letter-spacing: .04em; --c: var(--c-acc); overflow-wrap: anywhere; text-transform: none; }  /* secrets keep their case */
.notes { white-space: pre-wrap; }
.row { display: flex; flex-wrap: wrap; gap: 8px 10px; align-items: center; }
.grow { flex: 1; min-width: 0; }

/* status lines */
p[role=status], p[role=alert], .toast, .netline { font-family: var(--pixel); font-size: 16px; }
p.err[role=alert] { --c: var(--c-err); } p[role=status] { --c: var(--c-ok); }
p[role=status], p[role=alert], .toast { animation: line-in .28s ease-out backwards; }
p.err[role=alert], .ring { animation: bell .4s ease-in-out; }
.toast { display: flex; align-items: center; gap: 12px; padding: 10px 14px; margin: 10px 0; background: var(--panel); border: 2px solid rgb(var(--c-fg) / .9); border-radius: 14px 6px 14px 6px; box-shadow: 3px 3px 0 rgb(var(--c-ok) / .6); }
.toast span { flex: 1; }
.netline { max-width: 40rem; margin: 8px auto 0; padding: 6px 16px; --c: var(--c-warn); position: relative; z-index: 2; }
.netline.update { --c: var(--c-acc); display: flex; align-items: center; gap: 12px; }

/* ---------- panels: blobs that breathe, with a plasma edge ---------- */
.panel, .items li, .exs > li, .list li, details.sec {
  position: relative; background: var(--panel); border: 2px solid rgb(var(--c-fg) / .85); border-radius: var(--radius);
  box-shadow: 4px 4px 0 rgb(var(--c-acc) / .5); margin: 0 0 12px; overflow: hidden;
}
.items li:nth-child(2n), .exs > li:nth-child(2n), .list li:nth-child(2n), details.sec:nth-child(2n) { border-radius: var(--radius-b); }
@media (prefers-reduced-motion: no-preference) {
  .items li, .exs > li, details.sec { animation: breathe 9s ease-in-out infinite alternate; }
  .items li:nth-child(2n), .exs > li:nth-child(2n), details.sec:nth-child(2n) { animation-delay: -4.5s; }
  .items li:nth-child(n+14) { animation: none; }   /* long lists: only the first screen breathes (300 rows cost frames) */
}
/* breathing = transform only (compositor). animating border-radius repainted every breathing card on every frame,
   at 120 hz on an iphone: a steady battery cost for a barely visible morph. */
@keyframes breathe { to { transform: scale(1.006, .995) rotate(.12deg); } }
.edge { position: absolute; left: 0; top: 0; width: 8px; height: 100%; image-rendering: pixelated; background: rgb(var(--c-acc) / .5); pointer-events: none; }

/* ---------- buttons: pills ---------- */
button, .btn {
  font-family: var(--pixel); font-weight: 600; font-size: 16px; line-height: 1; text-transform: lowercase;
  display: inline-block; padding: 10px 15px 9px; margin: 0 8px 0 0; border: 2px solid rgb(var(--c-fg)); border-radius: 18px 8px 18px 8px;
  background: rgb(var(--c-acc)); --c: var(--panel); color: var(--panel); cursor: pointer; box-shadow: 3px 3px 0 rgb(var(--c-fg));
  transition: transform .34s cubic-bezier(.3, 1.8, .5, 1), box-shadow .2s ease-out; text-decoration: none; white-space: nowrap;   /* release: springs back with a small overshoot */
}
button:nth-child(2n), .btn:nth-child(2n) { border-radius: 8px 18px 8px 18px; }
button:active, .btn:active { transform: translate(2px, 2px) scale(.96); box-shadow: 1px 1px 0 rgb(var(--c-fg)); transition-duration: .05s; }   /* press: quick */
button.strong, .btn.strong, .go { background: rgb(var(--c-ok)); }
button.danger, .danger { background: rgb(var(--c-err)); }
/* small buttons (counter steps, item actions, admin rows): ~35 px tall, still compact but easier to hit (were 31) */
button.small, .btn.small, .minor button, .minor .btn { padding: 9px 12px 8px; font-size: 14px; box-shadow: 2px 2px 0 rgb(var(--c-fg)); }
.minor button, .minor .btn { background: var(--panel); --c: var(--c-fg); color: rgb(var(--c-fg)); border-color: rgb(var(--c-dim)); box-shadow: 2px 2px 0 rgb(var(--c-dim)); }
button.dim, .btn.dim { background: var(--panel); --c: var(--c-dim); color: rgb(var(--c-dim)); border-color: rgb(var(--c-dim)); box-shadow: 2px 2px 0 rgb(var(--c-dim)); }
.actions { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 0; margin: 14px 0; }
.actions form { margin: 0; }
.actions > .dim { --c: var(--c-dim); margin-right: 10px; }
.fold-items { display: contents; }
.fold-items button { background: rgb(var(--c-acc)); }

/* ---------- forms ---------- */
input, select, textarea {
  font: inherit; font-family: var(--pixel); font-size: 17px; --c: var(--c-fg); color: rgb(var(--c-fg)); background: var(--bg);
  border: 2px solid rgb(var(--c-faint)); border-radius: 10px 16px 10px 16px; padding: 10px 12px; width: 100%; min-height: 44px; -webkit-appearance: none; appearance: none;
}
input:focus, select:focus, textarea:focus { border-color: rgb(var(--c-acc)); outline: none; }
input::placeholder, textarea::placeholder { --c: var(--c-mute); color: rgb(var(--c-mute)); font-family: var(--body); }
input[type=date], input[type=time] { color-scheme: inherit; }
input[type=date]::-webkit-date-and-time-value, input[type=time]::-webkit-date-and-time-value { text-align: left; }
input[type=search]::-webkit-search-cancel-button { -webkit-appearance: none; }
select { background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='8'><path d='M0 0h12L6 8z' fill='%237e8084'/></svg>"); background-repeat: no-repeat; background-position: right 12px center; padding-right: 32px; }
option { background: var(--panel); }
textarea { min-height: 90px; resize: vertical; }
.term { display: grid; gap: 12px; margin: 12px 0; }
.term label { display: grid; grid-template-columns: 6.5em 1fr; align-items: center; gap: 8px; }
.term label > span:first-child { --c: var(--c-dim); font-family: var(--tiny); font-size: 12px; letter-spacing: .08em; }
.term label.top { grid-template-columns: 1fr; }
.term > button { justify-self: start; }
.pair { display: grid; grid-template-columns: 5ch 1fr; gap: 8px; }
.withunit { display: grid; grid-template-columns: 1fr auto; gap: 8px; align-items: end; }
.withunit select { width: auto; min-width: 6ch; }
.ex-form:has(input[name=kind][value=count]:checked) .withunit:not(.inc) { grid-template-columns: 1fr; gap: 0; }
.ex-form:has(input[name=kind][value=time]:checked) .only-count { display: none; }
.ex-form:has(input[name=kind][value=count]:checked) .only-time { display: none; }
.item-form:has(input[name=kind][value=fixed]:checked) .only-interval { display: none; }
.pick { display: inline-flex; align-items: center; gap: 10px; position: relative; }
.pick i { font-style: normal; font-family: var(--pixel); font-weight: 600; font-size: 14px; padding: 9px 12px 8px; border: 2px solid rgb(var(--c-fg));
  border-radius: 18px 8px 18px 8px; background: var(--panel); color: rgb(var(--c-fg)); box-shadow: 2px 2px 0 rgb(var(--c-fg)); }
.hint { --c: var(--c-dim); font-size: 14px; margin: 4px 0 0 calc(6.5em + 8px); }
/* radios as pills */
.seg { display: grid; grid-template-columns: 6.5em auto auto 1fr; gap: 8px; align-items: center; border: 0; padding: 0; margin: 0; }
.seg > span { --c: var(--c-dim); font-family: var(--tiny); font-size: 12px; letter-spacing: .08em; }
.seg label, .term .seg label { position: relative; display: block; grid-template-columns: none; }  /* not the form's label grid */
.seg input, .themes input, .check input, .file input[type=file] { position: absolute; opacity: 0; width: 1px; height: 1px; }
.seg i, .themes i { font-style: normal; font-family: var(--pixel); font-size: 15px; padding: 7px 12px 6px; border: 2px solid rgb(var(--c-faint)); border-radius: 14px 6px 14px 6px; --c: var(--c-dim); display: inline-block; white-space: nowrap; }
.seg input:checked + i, .themes input:checked + i { background: rgb(var(--c-acc)); --c: var(--panel); color: var(--panel); border-color: rgb(var(--c-fg)); }
.seg input:focus-visible + i, .themes input:focus-visible + i { outline: 2px solid rgb(var(--c-acc)); }
.check { display: flex; align-items: center; gap: 10px; }
.check i { font-style: normal; display: inline-block; width: 22px; height: 22px; border: 2px solid rgb(var(--c-faint)); border-radius: 8px 3px 8px 3px; position: relative; }
.check input:checked + i { background: rgb(var(--c-acc)); border-color: rgb(var(--c-fg)); }
.check input:checked + i::before { content: ""; position: absolute; inset: 5px; background: var(--panel); border-radius: 3px 1px 3px 1px; }
.file { display: flex; align-items: center; gap: 10px; }
.file em { font-style: normal; --c: var(--c-dim); }
.search { margin: 10px 0 14px; }

/* ---------- lists: items ---------- */
.items { list-style: none; padding: 0; margin: 0 0 10px; }
.items li { padding: 0; }
.items a { display: grid; grid-template-columns: 62px 1fr; gap: 4px 12px; align-items: center; padding: 14px 16px 14px 20px; }
.items .days { font-family: var(--num); font-weight: 400; font-size: 26px; line-height: 1; text-align: center; --c: var(--c-dim); grid-row: span 2; padding-bottom: 7px; position: relative; }
.items .days::after { content: ""; position: absolute; left: 4px; right: 4px; bottom: 0; height: 4px; border-radius: 2px; background: rgb(var(--c)); opacity: .9; }
.items .late .days { --c: var(--c-err); } .items .soon .days { --c: var(--c-warn); } .items .ok .days { --c: var(--c-acc); }
.items .title { font-family: var(--pixel); font-weight: 600; font-size: 20px; line-height: 1.1; }
.items .late .title { --c: var(--c-err); } .items .soon .title { --c: var(--c-warn); }
.items .inactive .title { --c: var(--c-dim); }
.items .meta { --c: var(--c-dim); font-size: 14px; }
.items .meta .tag, .tag { --c: var(--c-acc); font-family: var(--pixel); }
.pin { --c: var(--c-acc); font-family: var(--pixel); }
.items a:active { background: rgb(var(--c-acc) / .12); }
.items.swipe li { touch-action: pan-y; }
.items.swipe a { position: relative; background: var(--panel); }
.items.swipe li::after { content: "done"; font-family: var(--pixel); font-weight: 700; font-size: 18px; position: absolute; right: 18px; top: 50%; transform: translateY(-50%); --c: var(--c-ok); opacity: 0; transition: opacity .15s; }
.items.swipe li.swiping::after { opacity: .6; } .items.swipe li.armed::after { opacity: 1; animation: pulse .5s ease-in-out infinite alternate; }
@keyframes pulse { to { transform: translateY(-50%) scale(1.15); } }
.tags { display: flex; flex-wrap: wrap; gap: 8px 10px; margin: 8px 0; }
.tagbar a { font-family: var(--pixel); font-size: 15px; --c: var(--c-dim); padding: 4px 8px 3px; border: 2px solid rgb(var(--c-faint)); border-radius: 12px 5px 12px 5px; }
.tagbar a.on { --c: var(--panel); color: var(--panel); background: rgb(var(--c-acc)); border-color: rgb(var(--c-fg)); }
.hist, .list, .daylist { list-style: none; padding: 0; margin: 0; }
.hist li { padding: 6px 0; border-bottom: 2px solid rgb(var(--c-faint) / .5); font-size: 15px; }
.list li { display: flex; gap: 12px; align-items: flex-start; padding: 12px 14px 12px 18px; }
.list li > form { margin-top: 2px; }
.rename form label { grid-template-columns: auto 1fr; }   /* inside a card: the field takes the row */
.rename summary { font-size: 14px; --c: var(--c-dim); padding: 10px 0 8px; cursor: pointer; list-style: none; display: inline-block; }
.rename summary::before { content: "+ "; }
.rename .term { margin: 6px 0; }
.stat { display: grid; grid-template-columns: 7.5em 1fr; gap: 8px 10px; margin: 12px 0; }
.stat dt { --c: var(--c-dim); font-family: var(--tiny); font-size: 12px; letter-spacing: .08em; padding-top: 3px; }
.stat dd { margin: 0; }
.stream > * { animation: line-in .28s ease-out backwards; animation-delay: calc(var(--i, 0) * 30ms); }
li.arrive { animation: row-arrive .42s cubic-bezier(.2, 1.2, .4, 1) backwards; }   /* new after a live answer (quick add, save, undo) */
@keyframes row-arrive { from { transform: translateX(-18px) scale(.97); opacity: 0; } }
@keyframes line-in { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
@keyframes bell { 0%, 100% { transform: none; } 20% { transform: translateX(-8px); } 40% { transform: translateX(8px); } 60% { transform: translateX(-4px); } 80% { transform: translateX(4px); } }

/* ---------- workouts ---------- */
.exs { list-style: none; padding: 0; margin: 0; }
/* compact cards (1.7.4): name + unit / period left, total / goal on one line right, one row of buttons */
.exs > li { padding: 10px 14px 10px 20px; margin-bottom: 10px; }
.ex-head { display: flex; justify-content: space-between; gap: 12px; align-items: center; }
.ex-name { flex: 1; min-width: 0; display: flex; flex-direction: column; line-height: 1.15; }
.ex-name a { font-family: var(--pixel); font-weight: 600; font-size: 20px; overflow-wrap: anywhere; }
.ex-name .dim { font-size: 13px; }
.count { white-space: nowrap; line-height: 1; text-align: right; }
.count b { font-family: var(--num); font-weight: 400; font-size: 32px; display: inline-block; text-shadow: 2px 2px 0 rgb(var(--c-acc) / .35); }
.count .dim { font-family: var(--num); font-size: 20px; }
.ex.met .count b { --c: var(--c-ok); }
.bump { animation: bump .45s cubic-bezier(.2, .9, .3, 1.4); }
@keyframes bump { 0% { transform: scale(1.6) rotate(-6deg); } 100% { transform: none; } }
.goal-hit { animation: flash 1s steps(5); }
@keyframes flash { 0%, 40% { box-shadow: 4px 4px 0 rgb(var(--c-ok)), 0 0 0 8px rgb(var(--c-ok) / .45); } 20%, 60%, 100% { box-shadow: 4px 4px 0 rgb(var(--c-acc) / .5); } }
/* the bar: text fallback (server) replaced by a canvas water sim (workouts.js) */
.pbar { margin: 4px 0 2px; }
.pbar .mark { font-family: var(--num); font-size: 15px; position: relative; height: 15px; line-height: 1; --c: var(--c-dim); }
.pbar .mark b { position: absolute; transform: translateX(-50%); white-space: nowrap; font-weight: 400; transition: left .4s cubic-bezier(.2, .8, .2, 1); left: clamp(2.5ch, var(--x, 0%), calc(100% - 3ch)); }
.pbar .mark b::after { content: ""; display: block; width: 0; height: 0; margin: 0 auto; border: 4px solid transparent; border-top-color: rgb(var(--c-dim)); }
.pbar .meter { display: block; position: relative; height: 24px; border-radius: 16px 6px 16px 6px; background: rgb(var(--c-faint) / .5); border: 2px solid rgb(var(--c-fg)); overflow: hidden; }
.pbar .meter canvas { position: absolute; inset: 0; width: 100%; height: 100%; image-rendering: pixelated; }
.pbar .meter .txt { position: absolute; inset: 0; font-family: var(--pixel); font-size: 14px; line-height: 26px; padding: 0 10px; letter-spacing: .05em; --c: var(--c-dim); }
.pbar .meter.live .txt { display: none; }
.pbar .mark b { max-width: none; }
.pbar .mark b.ok { --c: var(--c-ok); font-weight: 600; }
.others { margin: 2px 0 0; }
.ex-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 0; margin-top: 8px; }
.ex-actions form { margin: 0; }
/* counter buttons are the most tapped controls: ~36 px tall (were 30) */
.ex-actions button { padding: 9px 11px 8px; margin-right: 6px; font-size: 14px; box-shadow: 2px 2px 0 rgb(var(--c-fg)); }
.ex-actions .wo:not(.custom) button:not(.dim), .ex-actions .custom button { font-family: var(--num); font-weight: 400; font-size: 19px; padding: 7px 11px 6px; }
.ex-actions .custom { display: flex; align-items: center; gap: 0; }
.ex-actions .custom input { width: 3.6em; min-height: 34px; padding: 4px 8px; margin-right: 6px; border-radius: 8px 14px 8px 14px; }
.sw-row { display: flex; align-items: center; gap: 10px; margin: 6px 0 0; }
.sw-row button { padding: 6px 12px 5px; font-size: 14px; }
.sw-row button { padding: 9px 12px 8px; }   /* start / reset: ~36 px like the other counter buttons */
.sw { font-family: var(--num); font-weight: 400; font-size: 28px; min-width: 6.5ch; --c: var(--c-dim); font-variant-numeric: tabular-nums; }
.running .sw { --c: var(--c-acc); }
.sw-row button { white-space: nowrap; }
.daylist li { display: grid; grid-template-columns: 7ch 1fr auto; gap: 10px; padding: 4px 0; font-size: 15px; border-bottom: 2px solid rgb(var(--c-faint) / .4); }
.daylist .fill { --c: var(--c-acc); } .daylist li.met b { --c: var(--c-ok); } .daylist li.zero b { --c: var(--c-mute); }
.spark-wrap { overflow-x: auto; } .spark { font-family: ui-monospace, monospace; font-size: 18px; letter-spacing: 1px; --c: var(--c-acc); } .spark .met { --c: var(--c-ok); }
.spark-axis { display: flex; justify-content: space-between; --c: var(--c-dim); font-size: 12px; }
.heat-wrap { overflow-x: auto; max-width: 100%; }
.heat-months, .heat { display: grid; grid-template-columns: repeat(53, 5px); gap: 1px; width: max-content; }
.heat-months { font-family: var(--tiny); font-size: 9px; --c: var(--c-dim); margin-bottom: 2px; }
.heat-months span { white-space: nowrap; overflow: visible; width: 5px; }
.heat { grid-template-rows: repeat(7, 5px); grid-auto-flow: column; }
.heat i { width: 5px; height: 5px; display: block; }
.heat .l0 { background: rgb(var(--c-faint) / .55); } .heat .l1 { background: rgb(var(--c-acc) / .3); } .heat .l2 { background: rgb(var(--c-acc) / .55); } .heat .l3 { background: rgb(var(--c-acc) / .85); }
.heat .l4 { background: rgb(var(--c-acc)); } .heat .l5 { background: rgb(var(--c-ok)); } .heat .lf { background: transparent; }

/* ---------- timeline, activity, help ---------- */
.tl-bars, .tl, .act { list-style: none; padding: 0; margin: 0 0 8px; }
.tl-bars li { white-space: nowrap; padding: 2px 0; font-family: var(--pixel); font-size: 15px; }
.tl-bars b, .stat dd, .act li > span:first-child, .tl li > span:first-child { font-variant-numeric: tabular-nums; }
.tl-bars .fill { --c: var(--c-acc); } .tl-bars .rest { --c: var(--c-faint); } .tl-bars b { font-weight: 600; }
.tl li { display: grid; grid-template-columns: 10ch 1fr; gap: 12px; padding: 6px 0; border-bottom: 2px solid rgb(var(--c-faint) / .4); }
.tl li.proj a { --c: var(--c-dim); }
.act li { display: grid; grid-template-columns: 6ch 1fr; gap: 10px; padding: 6px 0; border-bottom: 2px solid rgb(var(--c-faint) / .4); font-size: 15px; }
.act b { font-weight: 500; --c: var(--c-ok); }
.act-done { --c: var(--c-ok); } .act-created, .act-restored { --c: var(--c-acc); } .act-edited, .act-snoozed, .act-archived { --c: var(--c-dim); }
.kv { display: grid; grid-template-columns: 7.5em 1fr; gap: 4px 10px; margin: 4px 0 14px; font-size: 15px; }
.kv dt { --c: var(--c-dim); } .kv dd { margin: 0; }
.codes { font-family: var(--pixel); font-size: 18px; line-height: 1.8; }
.checks { list-style: none; padding: 0; margin: 0; } .checks li { padding: 6px 0; border-bottom: 2px solid rgb(var(--c-faint) / .4); }
.checks .tag { font-family: var(--tiny); font-size: 12px; margin-right: 8px; } .checks .st-pass .tag { --c: var(--c-ok); } .checks .st-fail .tag { --c: var(--c-err); }
.checks .detail { --c: var(--c-dim); font-size: 14px; }

/* ---------- settings: sections, theme picker ---------- */
details.sec { padding: 0; }
details.sec > summary { list-style: none; cursor: pointer; display: flex; align-items: baseline; gap: 8px; padding: 14px 16px 14px 20px; }
details.sec > summary::-webkit-details-marker { display: none; }
details.sec > summary .h { font-family: var(--pixel); font-weight: 600; font-size: 19px; white-space: nowrap; }
details.sec > summary .st { --c: var(--c-dim); font-size: 14px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
details.sec > summary::after { content: "+"; font-family: var(--pixel); font-size: 20px; margin-left: auto; --c: var(--c-acc); padding-left: 8px; }
details.sec[open] > summary::after { content: "-"; }
details.sec > .sec-body { padding: 0 16px 14px 20px; }
details { margin: 10px 0; }
details > summary { cursor: pointer; }
.themes { display: grid; gap: 4px; margin: 4px 0 12px; border: 0; padding: 0; }
.themes label { display: flex; align-items: center; gap: 12px; cursor: pointer; }
.themes i { min-width: 7ch; text-align: center; }
.swatch { display: inline-flex; align-items: center; gap: 6px; padding: 4px 10px; font-family: var(--pixel); font-size: 14px; background: var(--bg); border: 2px solid var(--line); border-radius: 10px 4px 10px 4px; --c: var(--c-fg); }
.swatch b, .swatch u { font-family: var(--num); font-weight: 400; font-size: 16px; } .swatch b { --c: var(--c-ok); } .swatch em { font-style: normal; --c: var(--c-acc); } .swatch u { text-decoration: none; --c: var(--c-err); }
.more { margin-top: 26px; }

/* face inventory (hold the face): the face glides to the upper middle, accessory rows below, save / cancel */
.face { -webkit-touch-callout: none; user-select: none; -webkit-user-select: none; }
/* no colored backdrop: a fixed, colored element at the top edge makes ios tint the status bar (and it stays tinted
   after closing). instead the page and the dock fade out underneath (opacity only), and the overlay itself is a
   transparent container that starts below the status bar area. */
.face-inv { position: fixed; left: 0; right: 0; top: env(safe-area-inset-top); bottom: 0; z-index: 40; display: flex; flex-direction: column;
  align-items: center; padding: 18px 20px calc(env(safe-area-inset-bottom) + 24px); background: transparent; }
/* only the tiles + buttons fade: the face (inside .inv-face) stays fully visible while it glides in and out */
.face-inv > :not(.inv-face) { opacity: 0; transition: opacity .25s ease-out; }
.face-inv.on > :not(.inv-face) { opacity: 1; }
html.inv-open main, html.inv-open .dock { opacity: .07; pointer-events: none; }
.face-inv { touch-action: none; overscroll-behavior: contain; }   /* no page scroll through it (1.22.1) */
.face-inv .inv-row { touch-action: pan-x; }
.inv-face { width: 172px; height: 172px; margin-bottom: 6px; }   /* five rows below it */
canvas.face.big { width: 172px; height: 172px; margin: 0; }
/* a row never wraps: more items than fit -> it scrolls sideways */
.inv-row { display: flex; gap: 10px; justify-content: safe center; flex-wrap: nowrap; overflow-x: auto; max-width: 100%; margin: 7px 0; padding: 2px 4px 6px; scrollbar-width: none; }
.inv-row::-webkit-scrollbar { display: none; }
.inv-tile { flex: none; width: 58px; height: 58px; padding: 0; margin: 0; display: grid; place-items: center; background: var(--panel);
  border: 2px solid rgb(var(--c-faint)); border-radius: 18px 8px 18px 8px; box-shadow: none; }
.inv-tile:nth-child(2n) { border-radius: 8px 18px 8px 18px; }
.inv-tile[aria-pressed="true"] { border-color: rgb(var(--c-acc)); box-shadow: 3px 3px 0 rgb(var(--c-acc)); }
.inv-tile canvas { width: 40px; height: 40px; image-rendering: pixelated; }
.inv-tile .none { width: 24px; height: 24px; border: 2px dashed rgb(var(--c-dim)); border-radius: 50%; }
.inv-tile.locked { opacity: .35; }
.inv-actions { display: flex; gap: 12px; margin-top: 14px; }
.inv-actions button { margin: 0; }

/* xp strip (1.13): under the tab bar, a level bubble + a slim water bar (fx.Bar slim) */
/* the level bubble: filled accent, number in the panel color, breathing like a bubble */
@keyframes lvl-bob { 0% { transform: translateY(0) scale(1); } 50% { transform: translateY(-1.5px) scale(1.04, .97); } 100% { transform: translateY(.5px) scale(.97, 1.03); } }
.xpmeter { flex: 1; position: relative; height: 10px; border: 2px solid rgb(var(--c-fg)); border-radius: 8px; background: rgb(var(--c-faint) / .5); overflow: hidden; }   /* empty part tinted like the counter bars */
.xpmeter canvas { position: absolute; inset: 0; width: 100%; height: 100%; image-rendering: pixelated; }


/* group profiles: members as their faces (avatars in their own character + outfit) */
.members { list-style: none; padding: 0; margin: 6px 0 18px; display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 12px; }
.member { display: flex; flex-direction: column; align-items: center; gap: 2px; padding: 10px 8px 12px; background: var(--panel);
  border: 2px solid rgb(var(--c-fg)); border-radius: 22px 10px 22px 10px; box-shadow: 4px 4px 0 rgb(var(--c-acc) / .5); text-align: center; }
.member.you { border-color: rgb(var(--c-acc)); }
.ava-box { display: block; width: 116px; height: 116px; background: none; border: 0; padding: 0; }
.ava { width: 116px; height: 116px; display: block; }

/* rotating chores: whose turn; yours in the accent */
.turn { color: rgb(var(--c-dim)); }
.turn.mine { color: rgb(var(--c-acc)); font-weight: 600; }

/* counter streak on the card: accent; at risk (last day of the period, goal not met yet): warn */
.streak { color: rgb(var(--c-acc)); font-weight: 600; }
.streak.risk { color: rgb(var(--c-warn)); }

/* long lists: rows off screen skip layout + paint until they scroll near (1.19: switching to the items panel with 300
   rows laid out every row). the size hint keeps the scrollbar and scroll positions stable. */
.items li { content-visibility: auto; contain-intrinsic-size: auto 92px; }

/* install card checks: plain lines (ok / fail tag, then what) */
.inst-checks { list-style: none; padding: 0; margin: 8px 0; }
.inst-checks li { padding: 3px 0; font-size: 15px; }
.inst-checks b { display: inline-block; min-width: 2.6em; white-space: nowrap; }

/* long-press secrets (1.26): no text selection / callout on the element; it swells slowly while held */
[data-hold] { -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; display: inline-block; transition: transform .2s ease-out; }
[data-hold].holding { transform: scale(1.25); transition: transform .65s cubic-bezier(.3, 0, .2, 1); }
@media (prefers-reduced-motion: reduce) { [data-hold].holding { transform: none; } }

/* the play page (1.26): buttons that do nothing, a water bar, room for the background */
.play-buttons, .play-bar { padding: 16px 16px 18px; }
.play-grid { display: flex; flex-wrap: wrap; gap: 10px; }
.play-grid button { text-transform: none; font-size: 18px; padding: 10px 16px; }   /* (T_T) stays (T_T); big enough to read the faces */
.play-num { font-family: var(--num); font-size: 28px; margin: 0 0 4px; } .play-num .dim { font-size: 18px; }
.play-steps { margin-top: 12px; flex-wrap: wrap; } .play-steps + .play-steps { margin-top: 8px; }
.play-space { height: 85vh; }   /* room for the background (1.26.3: more of it) */

/* backups (2.0): a row per backup, the date left, download + restore right */
.backups li { display: flex; justify-content: space-between; align-items: center; gap: 8px; flex-wrap: wrap; }
.backups li .row { margin: 0; gap: 8px; } .backups li form { margin: 0; }

/* the 2.0 welcome (accounts from 1.x) and the security & privacy page: cards of short paragraphs */
.welcome, .manifesto { padding: 14px 16px 16px; } .welcome h2, .manifesto h2 { font-family: var(--pixel); font-size: 18px; margin: 0 0 6px; }
.welcome p { margin: 0; } .manifesto p { margin: 0 0 8px; } .manifesto p:last-child { margin: 0; }
.welcome-done { margin: 16px 0 40px; }

/* groups (2.0): a card per group; member actions on the right; the admin page marks group admins (★) */
.grp { padding: 14px 16px 16px; } .grp h2 { font-family: var(--pixel); font-size: 18px; margin: 0 0 8px; }
.grp-acts { margin: 6px 0 0; gap: 6px; } .grp-acts form, .grp-leave, form.inline { margin: 0; display: inline-block; }
.grp-manage { margin: 10px 0 8px; } .grp-leave { margin-top: 6px; }
.chip.gadmin button { --c: var(--c-acc); }

/* the admin's restore (2.0): users as checkbox pills, wrapping; hidden when everything is overwritten */
.restore-users { display: flex; flex-wrap: wrap; } .restore-users > span { width: 6.5em; }
.restore:has(input[name=scope][value=all]:checked) .restore-users { display: none; }

/* settings (2.0): pixel waves (fx.js paintWave) between the app block (defaults, theme, sharing, push) and the account
   block (sessions, password, recovery codes, your data) */
.spacer { margin: 20px 4px 18px; }
.spacer canvas.wave { display: block; width: 100%; height: 28px; image-rendering: pixelated; }

/* quick add (items): one line + a live preview of what it will create */
.quick { display: grid; grid-template-columns: 1fr auto; gap: 4px 8px; align-items: center; margin: 4px 0 10px; }
.quick input { min-width: 0; }
.quick button { margin: 0; padding: 8px 14px 7px; font-family: var(--num); font-size: 20px; }
.quick-preview { grid-column: 1 / -1; margin: 0; min-height: 1.3em; }

/* a status line from a live form that would be off-screen: floats at the top for a moment */
.float-note { position: fixed; left: 16px; right: 16px; top: calc(env(safe-area-inset-top) + 10px); z-index: 30; margin: 0 auto; max-width: 38rem;
  padding: 10px 14px; background: var(--panel); border: 2px solid rgb(var(--c-fg)); border-radius: 14px 6px 14px 6px; box-shadow: 3px 3px 0 rgb(var(--c-acc));
  font-family: var(--pixel); font-size: 15px; animation: line-in .25s ease-out; transition: opacity .4s, transform .4s; }
.float-note.err { --c: var(--c-err); } .float-note.out { opacity: 0; transform: translateY(-8px); }

/* admin: group cards, member chips, one-line forms */
.inline-form { display: flex; flex-wrap: nowrap; align-items: center; gap: 8px; margin: 6px 0 12px; }
.inline-form input, .inline-form select { flex: 1; min-width: 0; }
.inline-form button { flex: none; margin: 0; }
.group-card { border: 2px solid rgb(var(--c-faint)); border-radius: 18px 8px 18px 8px; padding: 12px 12px 4px; margin: 0 0 12px; }
.group-head { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
.group-head .tag { font-size: 17px; }
.group-head form { margin-left: auto; }
.group-head button { margin: 0; }
.chips { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 6px; }
.chip { display: inline-flex; align-items: center; gap: 2px; margin: 0; padding: 3px 4px 3px 12px; border: 2px solid rgb(var(--c-acc)); border-radius: 14px 6px 14px 6px; background: rgb(var(--c-acc) / .12); }
.chip span { font-family: var(--pixel); font-weight: 600; font-size: 15px; }
.chip button { background: none; border: 0; box-shadow: none; padding: 2px 8px; margin: 0; font-size: 18px; line-height: 1; --c: var(--c-dim); color: rgb(var(--c-dim)); min-width: 36px; min-height: 36px; }
.chip button:active { transform: none; --c: var(--c-err); color: rgb(var(--c-err)); }

/* ---------- bottom tab bar ---------- */
/* the dock: xp strip above the tab bar, one fixed block at the bottom (spacing follows the real heights) */
.dock { position: fixed; left: 12px; right: 12px; bottom: calc(env(safe-area-inset-bottom) + 7px); z-index: 5; max-width: calc(40rem - 24px); margin: 0 auto;
  display: flex; flex-direction: column; gap: 8px; transition: opacity .35s ease-out; }
.legend { position: relative; z-index: 1; background: rgb(var(--c-fg)); padding: 8px; display: flex; gap: 4px; border-radius: 26px 12px 26px 12px; }
/* the xp meter is the tab bar's shadow (1.22.1): a 6 px offset copy of its shape behind it, the water fills it left to
   right; only the L of it shows (the bottom edge, then up the right side near a level up). empty: the accent, faint */
.legend-wrap { position: relative; }
/* 1.22.5: back to the shifted copy (the bar's shape, 6 px right and down), the classic drop shadow look */
.xpshadow { position: absolute; inset: 0; transform: translate(6px, 6px); border-radius: 26px 12px 26px 12px; overflow: hidden; background: rgb(var(--c-acc) / .3); }
.xpshadow .xpmeter { position: absolute; inset: 0; height: auto; border: 0; border-radius: 0; background: none; }
/* the level (1.22.3): a small pixel bubble floating above the bar's top-left corner (drawn on an 11 x 11 canvas: ring,
   glint, pixel digits), off the bar so the tabs keep their room */
.legend-wrap .lvl { position: absolute; z-index: 2; left: -6px; top: -15px; width: 22px; height: 22px; image-rendering: pixelated;
  animation: lvl-bob 3.2s ease-in-out infinite alternate; pointer-events: none; }
.legend a { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 3px; font-family: var(--pixel); font-weight: 600; font-size: 13px; --c: var(--bg); color: var(--bg); opacity: .55; padding: 7px 2px 5px; border-radius: 16px 8px 16px 8px; }
.legend a canvas { width: 22px; height: 22px; image-rendering: pixelated; background: var(--bg); border-radius: 50% 30% 50% 30%; padding: 4px; }
.legend a.on { opacity: 1; } .legend a.on canvas { background: rgb(var(--c-acc)); box-shadow: 0 0 10px rgb(var(--c-acc)); }
.tabhint { position: fixed; top: 50%; transform: translateY(-50%); z-index: 4; pointer-events: none; opacity: 0; transition: opacity .12s; font-family: var(--pixel); font-size: 16px; --c: var(--c-dim); padding: 8px 12px; background: var(--panel); border: 2px solid rgb(var(--c-faint)); border-radius: 14px 6px 14px 6px; }
.tabhint.on { opacity: 1; } .tabhint.left { left: 14px; } .tabhint.right { right: 14px; } .tabhint.armed { --c: var(--c-acc); border-color: rgb(var(--c-acc)); }
/* pull to refresh: the pixel bubble (canvas drawn by app.js), scales with the pull, pulses when armed */
.ptr { position: fixed; left: 50%; top: calc(env(safe-area-inset-top) + 6px); z-index: 20; width: 48px; height: 48px; margin-left: -24px;
  image-rendering: pixelated; pointer-events: none; opacity: 0; transform: translateY(var(--pull, 0)) scale(var(--grow, .3)); transition: opacity .12s; }
.ptr.on { opacity: 1; }
.ptr.armed { animation: bubble-pulse .7s ease-in-out infinite; }
.ptr.leaving { opacity: 0; transition: opacity .15s ease-out; animation-play-state: paused; }
@keyframes bubble-pulse { 0%, 100% { transform: translateY(var(--pull, 0)) scale(1); } 50% { transform: translateY(var(--pull, 0)) scale(1.18); } }

/* ---------- app-start intro (fx.js): a pixel bubble grows, pops, the page fades in ---------- */
/* the page shows through the bubble as a lens, then expands when it bursts. compositor-only: main gets a transform
   (scale + wobble, will-change), and a "veil" canvas (fx.js: the paper with a hole where the bubble is) covers the
   rest. no clip-path, no filters, no per-frame custom properties (those recalculated the whole page: 10-20 fps).
   the failsafe shows the page after 2.5 s. */
main { transition: opacity .35s ease-out; }
html.intro main { will-change: transform; animation: intro-failsafe 0s 2.5s forwards; }
html.intro .dock, html.intro .netline { opacity: 0; animation: intro-failsafe-o 0s 2.5s forwards; }
html.intro .stream > * { animation-play-state: paused; }
#veil { position: fixed; left: 0; top: 0; z-index: 5; pointer-events: none; image-rendering: pixelated; }
html:not(.intro) #veil { display: none; }
@keyframes intro-failsafe { to { transform: none; } }
@keyframes intro-failsafe-o { to { opacity: 1; } }

@media (prefers-reduced-motion: reduce) {
  li.arrive, .stream > *, .bump, .ring, .goal-hit, .items.swipe li.armed::after, p[role=status], p[role=alert], .toast, .ptr, .legend-wrap .lvl { animation: none !important; }
  main.tab-anim, .pbar .mark b, button, .btn { transition: none; }
}
@media (prefers-contrast: more) { #bg { display: none; } h1, .count b { text-shadow: none; } }

/* page transitions (1.20, view transitions api; without it pages simply swap). only named parts animate: the page
   content crossfades, an item's title glides between its list row and its page; the root (background canvases, the
   dock) does not animate, so the living background never freezes into a snapshot fade. */
/* 1.22.5: the old root snapshot is hidden, not just still: with both shown the default blend (plus-lighter) added them up,
   and the dark tab bar over the light page washed out to nearly white for the whole transition. the new root is live */
::view-transition-old(root) { display: none; }
::view-transition-new(root) { animation: none; mix-blend-mode: normal; }
/* the dock is a part of its own, so it stays above the content: named parts are painted above the root, and the
   crossfading page covered the tab bar (it "disappeared" for the length of the transition). live, no animation */
.dock { view-transition-name: dock; }
::view-transition-old(dock) { display: none; }
::view-transition-new(dock) { animation: none; mix-blend-mode: normal; }
main { view-transition-name: page; }
::view-transition-old(page) { animation: .16s ease-in both vt-out; }
::view-transition-new(page) { animation: .22s ease-out both vt-in; }
::view-transition-group(item-title) { animation-duration: .32s; animation-timing-function: cubic-bezier(.2, .9, .3, 1); }
@keyframes vt-out { to { opacity: 0; } }
@keyframes vt-in { from { opacity: 0; } }
@media (prefers-reduced-motion: reduce) { ::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*) { animation: none !important; } }
