/* One stylesheet, no framework. Sized for a phone held in one hand in a gym: big tap
   targets, no hover-only affordances, and a layout that does not reflow as rows are
   added. */
:root {
  color-scheme: light dark;
  --bg: #fbfbfc;
  --fg: #101418;
  --muted: #6b7280;
  --line: #e4e6ea;
  --card: #fff;
  --ok: #1a7f4b;
  --off: #8a6d1f;
  --bad: #b42318;
  --accent: #1f6feb;
  /* The picker's highlighted row, and the shadow that lifts its list off the form. */
  --pick: #e8f0fe;
  --shadow: rgb(16 20 24 / 18%);
  /* The rest bar at the moment it runs out. See `rest-over`. */
  --flash: #fdf1d8;
}
@media (prefers-color-scheme: dark) {
  :root {
    --bg: #101418;
    --fg: #eef1f5;
    --muted: #9aa3ae;
    --line: #242a31;
    --card: #161b21;
    --ok: #4ade80;
    --off: #fbbf24;
    --bad: #f87171;
    --accent: #58a6ff;
    --pick: #1d2733;
    --shadow: rgb(0 0 0 / 45%);
    --flash: #3a2f12;
  }
}

* { box-sizing: border-box; }

body {
  margin: 0;
  padding: 16px;
  /* No double-tap zoom. `manipulation` is `pan-x pan-y pinch-zoom`: scrolling and a
     deliberate pinch still work, and the double tap that used to zoom is just two
     taps — which is what it is, on a screen whose ✓ gets tapped once a set with a
     wet hand. It also drops the click delay a browser otherwise adds while it waits
     to see whether a second tap is coming.

     The viewport meta tag cannot do this. iOS has ignored `user-scalable=no`,
     `maximum-scale` and `minimum-scale` since iOS 10, deliberately and for
     accessibility, so a page CANNOT take pinch zoom away there — and should not
     want to. `touch-action` is the supported way to turn off one gesture without
     taking the rest, and it is what WebKit's own note about the change points at. */
  touch-action: manipulation;
  max-width: 44rem;
  background: var(--bg);
  color: var(--fg);
  font: 16px/1.5 system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
}

header { display: flex; align-items: baseline; gap: 12px; border-bottom: 1px solid var(--line); padding-bottom: 12px; }
h1 { font-size: 20px; margin: 0; letter-spacing: -0.01em; }
h2 { font-size: 15px; font-weight: 600; margin: 24px 0 8px; color: var(--muted); }

.pill { margin: 0; font-size: 13px; color: var(--muted); }
.pill[data-state="online"] { color: var(--ok); }
.pill[data-state="offline"] { color: var(--off); }

.empty, .muted { color: var(--muted); }

/* Signing in is secondary to logging a set, and is styled to say so: it sits in the
   header beside the connection pill and is not given the accent the primary actions
   carry. It is only ever on screen when sync is actually blocked. */
#sign-in {
  margin-left: auto;
  min-height: 34px;
  padding: 4px 12px;
  font-size: 13px;
  font-weight: 600;
}
#auth-note { margin: 8px 0 0; font-size: 13px; }

/* A second tap, and it should not look like a first one: enrolling happens once per
   device and competing with Sign in would make the rare path look like the usual. */
.linkish {
  border: 0;
  background: none;
  padding: 2px 0;
  min-height: 30px;
  font-size: 13px;
  color: var(--accent);
  text-decoration: underline;
}
#enrol-form { margin: 8px 0 0; max-width: 20rem; }
#auth-note[hidden], #sign-in[hidden], #enrol-form[hidden] { display: none; }

/* Handing somebody a way in: last on the page, and quiet until it is asked for. */
#share { margin: 24px 0 0; padding-top: 16px; border-top: 1px solid var(--line); }
#share-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
#share-note { flex-basis: 100%; margin: 0; }
/* Full width and selectable: a link nobody can see is a link nobody can send. */
#share-link { flex-basis: 100%; font-size: 13px; }
#share[hidden], #share-actions[hidden], #share-note[hidden], #share-link[hidden] { display: none; }
.error { color: var(--bad); margin: 8px 0 0; font-size: 14px; }

/* --- Forms ---------------------------------------------------------------- */

button {
  font: inherit;
  padding: 10px 14px;
  min-height: 44px; /* a thumb, not a mouse */
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--card);
  color: var(--fg);
  cursor: pointer;
}
button[type="submit"], #start { border-color: var(--accent); color: var(--accent); font-weight: 600; }

input, textarea {
  font: inherit;
  width: 100%;
  padding: 10px;
  min-height: 44px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--card);
  color: var(--fg);
}

#set-form { display: flex; flex-wrap: wrap; align-items: end; gap: 8px; margin-top: 12px; }
.field { display: flex; flex-direction: column; gap: 4px; flex: 1 1 10rem; }
.field > span { font-size: 12px; color: var(--muted); }
.field.narrow { flex: 0 0 5.5rem; }
.field.block { display: block; margin-top: 16px; }
.field.block > span { display: block; margin-bottom: 4px; }
/* The exercise field's own label is a <label for>, not a wrapping <label>, so the
   listbox can sit inside the field without every tap on an option focusing the
   input. Same look as the wrapped ones. */
.field > label { font-size: 12px; color: var(--muted); }

/* Adding an exercise is the one thing the workout screen offers besides ticking a
   set off, so its disclosure is drawn as the primary control it is — full width and
   in the accent, not the grey caption the filter's summary had to be rescued from.
   `display: flex` drops the native disclosure triangle in every engine, which is
   what is wanted here: the label already starts with a +. */
#add-exercise > summary {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 44px; /* a thumb, like every other control here */
  margin-top: 16px;
  padding: 10px 14px;
  border: 1px solid var(--accent);
  border-radius: 8px;
  background: var(--card);
  color: var(--accent);
  font-weight: 600;
  cursor: pointer;
  list-style: none; /* Firefox keeps a marker through `display: flex` otherwise */
}
#add-exercise > summary::-webkit-details-marker { display: none; }

/* Open, it is the form's caption and not a control worth drawing: the form below it
   carries its own submit, and two accent buttons a row apart saying "Add exercise"
   sent the tap to the one that CLOSES the form. Shut, it is the primary control
   again — the one thing an empty workout offers. The label changes with it; the two
   spans are what makes that possible without script (Frontend Guidelines #14). */
#add-exercise[open] > summary {
  justify-content: flex-start;
  min-height: 28px;
  margin-bottom: 8px;
  padding: 0;
  border: 0;
  background: none;
  color: var(--muted);
  font-size: 12px;
  font-weight: 400;
}
#add-exercise:not([open]) > summary > .when-open,
#add-exercise[open] > summary > .when-shut { display: none; }

/* --- The exercise picker --------------------------------------------------- */

/* A row of the form rather than a popup over it. Overlaying is the usual answer and
   it is wrong here: at a phone's width Add set wraps to its own line directly under
   the field, so an overlaid list sits on top of the primary action and eats the tap.
   Taking a row reflows the form when the list opens, which on a phone the keyboard
   has already done anyway. */
#exercise-list {
  flex: 1 0 100%;
  margin: 4px 0 0;
  padding: 4px;
  list-style: none;
  /* 40vh, not a row count: the room between the field and the top of the keyboard
     is what actually bounds this, and it differs by phone. */
  max-height: 40vh;
  overflow-y: auto;
  overscroll-behavior: contain;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 10px;
  box-shadow: 0 12px 28px var(--shadow);
}

.picker-option {
  /* 44px: the smallest target a thumb hits reliably, and this is used mid-set with
     one hand. */
  min-height: 44px;
  display: flex;
  align-items: center;
  padding: 8px 12px;
  border-radius: 8px;
  cursor: pointer;
}
.picker-option[aria-selected="true"] { background: var(--pick); }
@media (hover: hover) {
  .picker-option:hover { background: var(--pick); }
}

/* --- Sets ----------------------------------------------------------------- */

#sets, #sessions, #session-sets { list-style: none; margin: 16px 0 0; padding: 0; }

/* One exercise, one card. The name is said once at the top instead of on every row,
   which is what the card buys: five sets of the same thing read as one block a thumb
   works down, and the next exercise is a new block rather than a sixth row that
   happens to say something else.

   Consecutive runs, so coming back to an exercise later is a second card. The gap
   between cards is doing the separating, so the rows inside need only a hairline. */
.group {
  margin: 0 0 16px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--card);
}
.group-exercise {
  margin: 0 0 4px;
  font-size: 15px;
  font-weight: 600;
  color: var(--accent);
}
.group-sets { list-style: none; margin: 0; padding: 0; }
/* Quiet, and full width: it is tapped between every set, so it has to be easy to hit
   without competing with the ✓ on the row above it.

   Quiet is not the same as invisible, and the INSET is what buys the difference: the
   base button rule paints a button `--card`, which is the card's own colour, leaving
   a `--line` hairline as the only thing saying this is a control — 1.2:1 in either
   theme. A step down to the page background plus a `--muted` edge clears 3:1 with no
   accent, which the ✓ on the row above owns. `groups.spec.ts` measures it. */
.group-add {
  width: 100%;
  margin-top: 8px;
  min-height: 40px;
  padding: 6px 10px;
  font-size: 14px;
  border-color: var(--muted);
  background: var(--bg);
  color: var(--fg);
}

.set {
  display: grid;
  grid-template-columns: 3rem 1fr auto auto;
  align-items: center;
  gap: 8px;
  padding: 8px 0;
  border-bottom: 1px solid var(--line);
}
/* The last row of a card sits on the card's own edge; a second line under it reads
   as an empty row. */
.group-sets > .set:last-child { border-bottom: 0; padding-bottom: 0; }
.set-reps, .set-weight { color: var(--muted); font-variant-numeric: tabular-nums; }
.set-edit, .set-remove { min-height: 36px; padding: 4px 10px; font-size: 13px; }
/* Every set starts planned, so this is the ORDINARY row and it must not look like a
   warning: one more column for the tick that turns it into the log, and the numbers
   held back until there is something to report. A completed row is the solid one. */
.set.planned { grid-template-columns: 3rem 1fr auto auto auto; }
.set.planned .set-reps, .set.planned .set-weight { opacity: 0.6; }
.set-done { min-height: 36px; padding: 4px 10px; font-size: 13px; border-color: var(--accent); color: var(--accent); }
.set.editing { display: block; }
.set-edit-form { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; padding: 4px 0; }
.edit-exercise { flex: 1 1 8rem; }
.edit-reps, .edit-weight { flex: 0 0 5rem; }

/* --- History -------------------------------------------------------------- */

.session { padding: 10px 0; border-bottom: 1px solid var(--line); }
/* The whole line is the tap target, so it is a button — stripped back to looking
   like a row, because a list of eight bordered boxes reads as eight decisions. */
.session-open {
  display: block;
  width: 100%;
  text-align: left;
  border: 0;
  border-radius: 0;
  background: none;
  padding: 4px 0;
}
.session time { display: block; font-weight: 600; }
.session-summary { color: var(--muted); font-size: 14px; }
.session-notes { margin: 4px 0 0; color: var(--muted); font-size: 14px; font-style: italic; }

#finish { display: block; margin-top: 16px; }
#finish[hidden] { display: none; }

/* The question Finish asks, taking the button's place. The notes field spans the
   row so the two answers sit under it, Not yet first — the same shape and the same
   thumb rule as the cancel row below. */
#finish-confirm { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 16px; }
#finish-confirm .field { flex-basis: 100%; }
#finish-empty { flex-basis: 100%; margin: 0; font-size: 14px; }
#finish-confirm[hidden] { display: none; }
/* `.field.block` sets `display`, and an author rule beats the user agent's
   `[hidden] { display: none }` at any specificity — so the notes field has to be told
   to honour the attribute the app toggles on it. Without this it is laid out while
   `hidden` is set: on screen for a user, and reported visible by everything that asks. */
#finish-notes[hidden] { display: none; }
#finish-save { border-color: var(--accent); color: var(--accent); font-weight: 600; }
#repeat { display: block; margin-top: 16px; border-color: var(--accent); color: var(--accent); font-weight: 600; }
#repeat[hidden] { display: none; }

/* Cancel is quiet and Finish is not, because one is reached every session and the
   other a few times a year. The confirm row takes Cancel's place rather than opening
   below it: nothing moves under the thumb that just tapped, and Keep it is what lands
   where the finger already is. */
#cancel { display: block; margin-top: 8px; }
#cancel-confirm { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
#cancel-question { flex-basis: 100%; margin: 0; font-size: 14px; }
.danger { border-color: var(--bad); color: var(--bad); font-weight: 600; }
#cancel[hidden], #cancel-confirm[hidden] { display: none; }

/* --- One saved workout ----------------------------------------------------- */
/* Read-only, so the set row loses its two buttons and the grid loses their columns.
   Everything else is deliberately the same as the workout being logged: a set should
   not move on the screen because it happened yesterday. */

#session-back { margin-bottom: 12px; }
#session-heading { font-size: 17px; color: var(--fg); margin: 0 0 2px; }
#session-summary { margin: 0; font-size: 14px; }
.set.readonly { grid-template-columns: 3rem 1fr; }
#session-notes, #session-empty { margin: 16px 0 0; }

/* --- Rest between sets ----------------------------------------------------- */
/* One row, readable at arm's length on a bench: the time is the biggest thing on
   the screen while it is up, because it is read from further away than anything
   else here and often upside down. Tabular figures so the digits do not jitter as
   the seconds change — proportional ones make the whole row twitch once a second. */

/* `display` on an id beats the user agent's `[hidden] { display: none }` — author
   rules win over UA rules at any specificity — so the attribute the app toggles has
   to be honoured explicitly. Without this the bar is laid out while `hidden` is set:
   visible to a user, and reported visible by everything that asks. */
#rest[hidden] { display: none; }

/* A row of #sets, so it inherits that list's `list-style: none` and needs only to
   stand apart from the set above and the one below it — the card it already was,
   with air on both sides instead of only above. */
#rest {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin: 10px 0;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--card);
}

#rest-remaining {
  margin: 0;
  flex: 1 1 5rem;
  font-size: 28px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
}
#rest[data-state="resting"] #rest-remaining { color: var(--accent); }
/* Overdue, not wrong: the colour says "you are still sitting here", which is the
   entire nudge. It is the warning amber the offline pill uses, not the error red. */
#rest[data-state="done"] #rest-remaining { color: var(--off); }
#rest[data-state="off"] #rest-remaining { font-size: 15px; font-weight: 500; color: var(--muted); }

/* The rest is over, said to the eye.
   On a phone on silent this is the ONLY alert there is: `navigator.vibrate` is on no
   version of Safari, and iOS is sent no notification at all (docs/rest-timers.md). So
   it is the whole card rather than the digits, and it is three pulses — one per pip —
   rather than a colour that was already amber a second ago and says nothing new. */
@keyframes rest-over {
  from { background: var(--flash); border-color: var(--off); }
  to { background: var(--card); border-color: var(--line); }
}
#rest[data-state="done"] { animation: rest-over 0.6s ease-out 3; }
/* A full-width card pulsing on a phone is exactly what the setting is about. The
   moment still has to be visible, so it is said once, in colour, and left there. */
@media (prefers-reduced-motion: reduce) {
  #rest[data-state="done"] { animation: none; border-color: var(--off); }
}

.rest-nudge { min-height: 40px; padding: 6px 10px; font-size: 14px; font-variant-numeric: tabular-nums; }
#rest-skip { min-height: 40px; padding: 6px 12px; font-size: 14px; }
/* An offer, not a control: it sits below the three that are used between every set,
   and it is gone for good once the browser has answered either way. */
#rest-notify {
  flex: 0 0 auto;
  min-height: 30px;
  padding: 2px 10px;
  font-size: 12px;
  color: var(--muted);
}
#rest-notify[hidden] { display: none; }

/* The duration for THIS exercise, on its own line: it is set a few times a month,
   and it must not compete with the three controls above that are touched between
   every set. */
#rest-setting {
  flex: 1 0 100%;
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  color: var(--muted);
}
#rest-exercise { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#rest-duration {
  font: inherit;
  min-height: 34px;
  padding: 2px 6px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--card);
  color: var(--fg);
}

/* --- The exercise filter --------------------------------------------------- */
/* Chips, not a multi-select: a select needs a long press and a scroll on a phone,
   and the whole point is one thumb between sets. They wrap rather than scroll
   sideways, so nothing is hidden off the edge. */

#filter { margin-top: 12px; }

/* The summary is drawn as a button, because it is one. Small grey text with no
   border read as a caption over the chips and people did not try tapping it. It
   also needs its own arrow: `display: flex` drops the native disclosure marker in
   every engine, so without this there was nothing at all to say it opens. */
#filter > summary {
  font-size: 13px;
  font-weight: 600;
  color: var(--fg);
  cursor: pointer;
  min-height: 44px; /* a thumb, like every other control here */
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 12px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--card);
  list-style: none; /* Firefox keeps a marker through `display: flex` otherwise */
}
#filter > summary::-webkit-details-marker { display: none; }

/* Drawn, not typed: a glyph would depend on a font the phone may render as a box. */
#filter > summary::before {
  content: "";
  width: 7px;
  height: 7px;
  border-right: 2px solid var(--muted);
  border-bottom: 2px solid var(--muted);
  transform: rotate(-45deg);
  transition: transform 120ms ease;
}
#filter[open] > summary::before { transform: rotate(45deg); }
@media (prefers-reduced-motion: reduce) {
  #filter > summary::before { transition: none; }
}

#filter[open] > summary { margin-bottom: 8px; }
#filter-count { margin-left: auto; } /* the count sits at the far edge, out of the way */

.facet { border: 0; margin: 0 0 6px; padding: 0; display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.facet legend { float: left; } /* a legend is not a flex item; float keeps it on the row */
.facet > legend + * { clear: none; }

.facet legend, #filter-count {
  font-size: 12px;
  color: var(--muted);
}
.facet legend { width: 5.5rem; padding: 0; }

.tag {
  min-height: 34px;
  padding: 4px 10px;
  font-size: 13px;
  border-radius: 999px;
}
/* The pressed state has to read at a glance in bad light, so it is a fill and not a
   border tint. */
.tag[aria-pressed="true"] {
  border-color: var(--accent);
  background: var(--accent);
  color: #fff;
  font-weight: 600;
}

#filter-count { font-weight: 400; }
#filter-clear { min-height: 30px; padding: 2px 10px; font-size: 12px; margin-top: 2px; }
