/* ============ Design tokens (shared with Print Cost + Inspiration Gallery) ============
 *
 * Same palette and geometry as the other two apps — this is the house system,
 * not a per-app choice. Change a token here only if it's changing in all three.
 */
:root {
  --teal:      #41b7ab;
  --teal-dark: #369a90; /* accessible teal for text-on-white */
  --slate:     #515a60;
  --bg:        #ffffff;
  --border:    #dddddd;
  --ink:       #222222;
  --black:     #000000;
  --teal-tint: #e8f6f4;
  --error:     #c05252;

  --surface:   #ffffff;
  --text:      var(--slate);
  --heading:   var(--slate);

  --radius: 10px;
  --tap: 48px;
  --maxw: 620px;
  --font: "Avenir Next", Avenir, "Segoe UI", system-ui, -apple-system, sans-serif;

  --safe-top:    env(safe-area-inset-top, 0px);
  --safe-bottom: env(safe-area-inset-bottom, 0px);

  /* Pin to light so iOS doesn't auto-darken inputs and scrollbars for people
     whose phone is in dark mode. */
  color-scheme: light;
}

/* Deliberately light-only, matching the gallery. The dark palette is kept
 * there (commented) if this ever needs to switch back.
 *
 * NOTE FOR ANYONE PORTING MORE CSS FROM THE GALLERY: that app pins
 * html,body { overflow:hidden; touch-action:none } because it's a full-bleed
 * pannable WebGL canvas. Do NOT bring that here. This app is a centred
 * scrolling column like Print Cost, and those two rules would make every
 * screen below the fold unreachable.
 */

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }

body {
  font-family: var(--font);
  font-size: 16px;
  line-height: 1.5;
  color: var(--text);
  background: var(--bg);
  -webkit-text-size-adjust: 100%;
  -webkit-font-smoothing: antialiased;
}

button, input, textarea, select { font: inherit; }
button { cursor: pointer; }

[hidden] { display: none !important; }
.hidden { display: none !important; }
.muted { color: var(--slate); opacity: 0.7; font-weight: 400; }
.hint { font-size: 0.82rem; color: var(--slate); opacity: 0.75; }

.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ============ Layout ============ */

/* The centred column. Bottom padding clears the fixed tab bar plus the
   iPhone home-indicator inset. */
.wrap {
  max-width: var(--maxw);
  margin: 0 auto;
  padding: calc(var(--safe-top) + 18px) 18px
           calc(var(--safe-bottom) + var(--tap) + 34px);
}

/* Signature divider — a 2px teal rule under headings. */
.screen-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  border-bottom: 2px solid var(--teal);
  padding-bottom: 10px;
  margin: 4px 0 20px;
}
h1 { font-size: 1.7rem; font-weight: 500; margin: 0; color: var(--heading); letter-spacing: 0.2px; }
h2 { font-size: 1.1rem; font-weight: 600; margin: 26px 0 10px; color: var(--heading); }
.rule { border: 0; border-top: 2px solid var(--teal); margin: 28px 0 18px; }

.stack { display: grid; gap: 14px; }
.row { display: flex; align-items: center; gap: 10px; }
.row-between { display: flex; align-items: center; justify-content: space-between; gap: 10px; }

/* ============ Bottom tab bar ============ */

.tabbar {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  display: flex;
  background: var(--surface);
  border-top: 1px solid var(--border);
  padding-bottom: var(--safe-bottom);
  z-index: 20;
}
.tabbar a {
  flex: 1;
  min-height: var(--tap);
  display: flex;
  align-items: center;
  justify-content: center;
  text-decoration: none;
  color: var(--slate);
  font-size: 0.9rem;
  font-weight: 600;
  border-top: 2px solid transparent;
  margin-top: -1px;
}
.tabbar a.is-active { color: var(--teal-dark); border-top-color: var(--teal); }

/* ============ Sheet (menu) ============
 *
 * Ported from the Inspiration Gallery, same markup and behaviour: a bottom sheet
 * on a phone, a centred dialog once there's room. Bottom-anchored because that's
 * where your thumb is — a menu that drops from the top corner it was tapped in
 * is a desktop habit. */

.sheet {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  background: rgba(0, 0, 0, 0.4);
}

.sheet-panel {
  background: var(--bg);
  width: 100%;
  max-width: var(--maxw);
  border-radius: 16px 16px 0 0;
  display: grid;
  gap: 2px;
  padding: 16px 16px calc(20px + var(--safe-bottom));
}
@media (min-width: 620px) {
  .sheet { align-items: center; }
  .sheet-panel { border-radius: 16px; }
}

.sheet-panel button,
.sheet-panel a {
  min-height: var(--tap);
  display: flex;
  align-items: center;
  padding: 0 16px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  color: var(--heading);
  font-weight: 600;
  text-align: left;
  text-decoration: none;
}
.sheet-panel button:hover,
.sheet-panel a:hover { border-color: var(--teal); color: var(--teal-dark); }
.sheet-cancel {
  justify-content: center;
  color: var(--slate) !important;
  margin-top: 8px;
}

/* Heading row actions. The menu button lives beside whatever a screen passes as
   its own aside, rather than floating over the page. */
.head-actions { display: flex; align-items: center; gap: 8px; flex: none; }

/* ============ Fields ============ */

.field { display: flex; flex-direction: column; gap: 6px; }
.field > span, .label { font-size: 0.95rem; font-weight: 400; color: var(--heading); }

.field input, .field textarea, .field select, .input {
  font: inherit;
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 12px 14px;
  width: 100%;
  appearance: none;
}
.field input, .field select, .input { min-height: var(--tap); padding: 0 14px; }
.field textarea { resize: vertical; line-height: 1.5; }

.field input:focus, .field textarea:focus, .field select:focus, .input:focus {
  outline: 2px solid var(--teal);
  outline-offset: 1px;
  border-color: var(--teal);
}
.field-err { font-size: 0.82rem; color: var(--error); min-height: 1em; }

/* ============ Buttons ============ */

/* These work on <a> as well as <button>. Cross-screen navigation would otherwise
   need a <form method="get"> wrapper just to get a styled control, since an
   anchor arrives underlined, inline, and not centred. */
.btn-primary, .btn-secondary, .btn-danger {
  min-height: var(--tap);
  border-radius: var(--radius);
  border: 1px solid transparent;
  padding: 0 18px;
  font-weight: 600;
  width: 100%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  text-decoration: none;
}
.btn-primary { background: var(--teal); color: #fff; }
.btn-primary:hover { background: var(--teal-dark); }
.btn-primary:disabled { opacity: 0.45; cursor: not-allowed; }

.btn-secondary, .btn-ghost {
  min-height: var(--tap);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--heading);
  border: 1px solid var(--border);
  padding: 0 18px;
  font-weight: 600;
  width: auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.btn-secondary:hover, .btn-ghost:hover { border-color: var(--teal); color: var(--teal-dark); }
.btn-ghost:disabled { opacity: 0.45; cursor: not-allowed; }

.btn-danger { background: var(--surface); color: var(--error); border-color: var(--error); }
.btn-danger:hover { background: var(--error); color: #fff; }

/* Underlined text action — Print Cost's .tap-text.
 *
 * The 48px minimum is unconditional. It used to be inherited only from
 * .thinrow, so the same class rendered a 24px target everywhere else — half the
 * minimum, on shipped screens, invisible unless measured. A tap target is a
 * property of the control, not of where it happens to be sitting. */
.link-btn, .tap-text {
  background: none; border: 0; padding: 0;
  color: var(--slate); font-weight: 600;
  text-decoration: underline;
  text-decoration-color: var(--border);
  text-decoration-thickness: 2px;
  text-underline-offset: 3px;
  min-height: var(--tap);
  display: inline-flex;
  align-items: center;
}
.link-btn:hover, .tap-text:hover { color: var(--teal-dark); text-decoration-color: var(--teal); }
.tap-text.danger { color: var(--error); }

/* ============ Login ============ */

.login-body { display: grid; place-items: center; min-height: 100vh; padding: 24px; }
.login-card { width: 100%; max-width: 340px; }
.login-title {
  font-size: 1.7rem;
  font-weight: 500;
  letter-spacing: 0.2px;
  color: var(--heading);
  margin: 0 0 10px;
  padding-bottom: 10px;
  border-bottom: 2px solid var(--teal);
}
.login-form { display: grid; gap: 14px; margin-top: 20px; }
.login-form .btn-primary { width: 100%; }
.login-error { margin: 0; color: var(--error); font-size: 0.85rem; }

/* ============ Choice groups (setup screen) ============
 *
 * Radio inputs styled as tappable tiles: time, body part, equipment, format.
 * Markup is a label wrapping a visually-hidden <input type=radio>, so keyboard
 * and screen readers get real radio semantics for free. Never a div soup —
 * :has() and :checked do the highlighting.
 */

.choices {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(84px, 1fr));
  gap: 10px;
}
/* Explicit counts, because auto-fit picks 3 columns at 390px and leaves a set
   of four options sitting 3-then-1. Four short options want .cols-4 (time),
   four longer ones want .cols-2 (body part, equipment). */
.choices.cols-2 { grid-template-columns: repeat(2, 1fr); }
.choices.cols-4 { grid-template-columns: repeat(4, 1fr); }

.choice {
  position: relative;
  min-height: var(--tap);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  text-align: center;
  padding: 10px 8px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--heading);
  font-weight: 600;
  cursor: pointer;
}
.choice input { position: absolute; opacity: 0; pointer-events: none; }
.choice:hover { border-color: var(--teal); }
.choice:has(input:checked) {
  border-color: var(--teal);
  background: var(--teal-tint);
  color: var(--teal-dark);
}
.choice:has(input:focus-visible) { outline: 2px solid var(--teal); outline-offset: 2px; }
.choice:has(input:disabled) { opacity: 0.4; cursor: not-allowed; }
.choice small { font-weight: 400; font-size: 0.75rem; opacity: 0.8; }

/* Checkbox variant for the equipment multi-select. */
.choice.is-check:has(input:checked)::after {
  content: "✓";
  position: absolute;
  top: 4px; right: 7px;
  font-size: 0.8rem;
  color: var(--teal-dark);
}

/* ============ Switch (warmup / cooldown toggle) ============ */

.switch { display: flex; align-items: center; justify-content: space-between; gap: 12px; min-height: var(--tap); }
.switch input { position: absolute; opacity: 0; pointer-events: none; }
.switch .track {
  width: 50px; height: 30px; flex: none;
  border-radius: 15px;
  background: var(--border);
  position: relative;
  transition: background 0.15s ease;
}
.switch .track::after {
  content: "";
  position: absolute; top: 3px; left: 3px;
  width: 24px; height: 24px;
  border-radius: 50%;
  background: #fff;
  transition: transform 0.15s ease;
}
.switch:has(input:checked) .track { background: var(--teal); }
.switch:has(input:checked) .track::after { transform: translateX(20px); }
.switch:has(input:focus-visible) .track { outline: 2px solid var(--teal); outline-offset: 2px; }

/* ============ Cards & lists ============ */

.card {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  padding: 14px;
}
.card + .card { margin-top: 10px; }

.pill {
  display: inline-block;
  padding: 3px 10px;
  border-radius: 999px;
  background: var(--teal-tint);
  color: var(--teal-dark);
  font-size: 0.78rem;
  font-weight: 600;
  white-space: nowrap;
}
.pill.is-plain { background: transparent; border: 1px solid var(--border); color: var(--slate); }

.empty {
  text-align: center;
  padding: 40px 20px;
  color: var(--slate);
  opacity: 0.7;
}

/* Compact list of short values with an inline action — the weight inventory.
 *
 * A .card per row was the first attempt and it read badly: five dumbbells became
 * five chunky boxes where the Remove button outweighed the number it belonged
 * to. One bordered container with hairline dividers keeps a short list scannable
 * while the row itself still holds a full 48px tap target. */
.thinlist {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
}
.thinlist > * + * { border-top: 1px solid var(--border); }

.thinrow {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  min-height: var(--tap);
  padding: 0 14px;
}
.thinrow .val {
  font-weight: 600;
  color: var(--heading);
  font-variant-numeric: tabular-nums;
}
/* The destructive action stays quiet until you reach for it: a red button per
   row turns a list of weights into a wall of alarm. */
.thinrow form { display: flex; }
.thinrow .tap-text {
  min-height: var(--tap);
  padding: 0 0 0 12px;
  font-size: 0.9rem;
}

/* Move list — the preview screen and the history detail view. */
.move-list { display: grid; gap: 8px; margin: 0; padding: 0; list-style: none; }
.move-row {
  display: grid;
  grid-template-columns: 52px 1fr auto;
  align-items: center;
  gap: 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 8px 12px 8px 8px;
}
.move-row img {
  width: 52px; height: 52px;
  object-fit: cover;
  border-radius: 6px;
  background: var(--teal-tint);
}
.move-row .move-name { font-weight: 600; color: var(--heading); }
.move-row .move-meta { font-size: 0.8rem; color: var(--slate); opacity: 0.75; }
.move-row .move-time { font-variant-numeric: tabular-nums; font-weight: 600; color: var(--slate); }
.move-row.is-rest { border-style: dashed; opacity: 0.65; }
/* A "Round 2" divider between groups of moves. display:block resets the
   3-column grid it inherits from .move-row — as a grid item the label lands in
   the 52px thumbnail column and wraps one word per line. */
.move-row.is-round-break {
  display: block;
  border: 0; padding: 6px 0 0;
  font-size: 0.8rem; font-weight: 600;
  letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--slate); opacity: 0.6;
}

/* ============ Live session ============
 *
 * Deliberately not inside .wrap: this screen is fixed to the viewport so the
 * countdown can't scroll out of view mid-set, and nothing here reflows when
 * the move name is long.
 */

.session {
  position: fixed;
  inset: 0;
  display: grid;
  grid-template-rows: auto 1fr auto;
  padding: calc(var(--safe-top) + 12px) 16px calc(var(--safe-bottom) + 16px);
  background: var(--bg);
}

/* Top strip: total elapsed and overall progress. */
.session-top { display: grid; gap: 8px; }
.session-elapsed {
  display: flex; align-items: baseline; justify-content: space-between;
  font-size: 0.85rem; color: var(--slate);
  font-variant-numeric: tabular-nums;
}
/* Driven by setting --progress (0-1) on .progress, so the JS sets a custom
   property rather than writing style.width — CLAUDE.md rules out inline style
   for anything structural, and a percentage width is exactly that. */
.progress { height: 4px; border-radius: 2px; background: var(--border); overflow: hidden; }
.progress > i {
  display: block;
  height: 100%;
  background: var(--teal);
  width: calc(var(--progress, 0) * 100%);
}

/* Middle: the current move.
 *
 * Exactly three children, and the row template says so: a heading group, the
 * move group, then .move-steps taking the remaining space as the only thing
 * allowed to scroll. Six loose elements here would land in implicit rows and
 * push the controls off the bottom of the screen, so they're grouped rather
 * than listed. overflow:hidden is the backstop — a long move name should
 * truncate the bullets, never the Pause button. */
.session-main {
  display: grid;
  grid-template-rows: auto auto 1fr;
  align-content: start;
  gap: 12px;
  padding: 14px 0;
  min-height: 0;
  overflow: hidden;
}

.session-head { min-height: 0; }
.session-move {
  display: grid;
  grid-template-rows: auto auto 1fr;
  gap: 8px;
  min-height: 0;
}

.move-title { font-size: 1.5rem; font-weight: 600; color: var(--heading); margin: 0; line-height: 1.2; }

/* The big countdown. tabular-nums so the digits don't jitter every tick.
 *
 * Sized down from its original clamp(4rem, 22vw, 7rem): at 7rem the countdown
 * plus a full-width control pair left almost nothing for the instructions, which
 * is the text you actually need mid-set. The controls now sit beside it in
 * .session-timerbar instead of along the bottom. */
.timer-big {
  font-size: clamp(2.75rem, 14vw, 4rem);
  font-weight: 500;
  line-height: 1;
  color: var(--teal-dark);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
}

/* Countdown on the left, pause and stop as icons on the right. */
.session-timerbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
.session-timerbar .timer-big { flex: 1 1 auto; min-width: 0; }
.session-iconbar { display: flex; gap: 8px; flex: none; }

.icon-btn {
  width: var(--tap);
  height: var(--tap);
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--heading);
  padding: 0;
}
.icon-btn:hover { border-color: var(--teal); color: var(--teal-dark); }
.icon-btn.is-danger { color: var(--error); border-color: var(--border); }
.icon-btn.is-danger:hover { border-color: var(--error); background: var(--error); color: #fff; }
.icon-btn svg {
  width: 22px; height: 22px;
  fill: none; stroke: currentColor; stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round;
}
/* Filled glyphs (the pause bars, the stop square) want no stroke. */
.icon-btn svg.is-solid { fill: currentColor; stroke: none; }
.timer-label {
  font-size: 0.8rem; font-weight: 700;
  letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--slate); opacity: 0.7;
}

/* Calm countdown: anything that isn't a work interval.
 *
 * Split from .is-rest deliberately. .is-rest came to mean two things — "read
 * calmer" and "there is no current move, so hide the picture and enlarge the
 * text" — and warmup and cooldown want only the first. They are moves you
 * perform, so the picture is the point. One class could not say both. */
.session.is-calm .timer-big,
.session.is-rest .timer-big { color: var(--slate); }
.session.is-paused .timer-big { opacity: 0.4; }

/* Rest is reading time.
 *
 * During work you know what you're doing and the picture is the useful thing;
 * during rest you want to read what's coming next. So rest hides the image and
 * gives the whole middle of the screen to the next move's instructions, set
 * larger. This is what makes the directions legible without a redesign — the
 * space was always there, just spent on a photo at the wrong moment. */
.session.is-rest .move-figure { display: none; }
.session.is-rest .move-steps { font-size: 1rem; gap: 8px; }
.session.is-rest .move-title { font-size: 1.25rem; opacity: 1; }

/* "Next up" label above the coming move's name during rest. */
.nextup-label {
  font-size: 0.75rem; font-weight: 700;
  letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--teal-dark);
  margin: 0 0 2px;
}

/* Weights needed, listed on the preview screen before you start. */
.kit-list { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 4px; }
.kit-none { color: var(--slate); opacity: 0.75; font-size: 0.9rem; }

/* Post-workout rating. One row per move, three choices, thumb-sized. */
.rate-row {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: 10px;
  padding: 10px 0;
}
.rate-row + .rate-row { border-top: 1px solid var(--border); }
.rate-row .rate-name { font-weight: 600; color: var(--heading); }
.rate-choices { display: flex; gap: 6px; flex: none; }
.rate-choices label {
  position: relative;
  min-height: var(--tap);
  min-width: var(--tap);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--slate);
  cursor: pointer;
  text-align: center;
}
.rate-choices input { position: absolute; opacity: 0; pointer-events: none; }
.rate-choices label:has(input:checked) {
  border-color: var(--teal);
  background: var(--teal-tint);
  color: var(--teal-dark);
}
/* "Never again" is destructive — it removes the move from every future workout,
   so it reads red once chosen rather than teal like the others. */
.rate-choices label.is-never:has(input:checked) {
  border-color: var(--error);
  background: var(--surface);
  color: var(--error);
}
.rate-choices label:has(input:focus-visible) { outline: 2px solid var(--teal); outline-offset: 2px; }

.move-figure { min-height: 0; display: grid; place-items: center; }
.move-figure img {
  max-width: 100%;
  max-height: 34vh;
  object-fit: contain;
  border-radius: var(--radius);
  background: var(--teal-tint);
}

.move-steps {
  margin: 0; padding-left: 1.1em;
  font-size: 0.92rem; color: var(--text);
  display: grid; gap: 4px;
  /* Without this the rows stretch to fill the 1fr track they sit in, so three
     short bullets end up spread down the screen with a paragraph of air between
     them — very obvious on the rest screen, where the whole point is reading. */
  align-content: start;
  overflow-y: auto;
  min-height: 0;
}

.move-weights { font-size: 0.85rem; color: var(--slate); }

/* Up-next line + controls. No skip button anywhere: out of scope by design. */
.session-foot { display: grid; gap: 10px; }
.session-next { font-size: 0.85rem; color: var(--slate); opacity: 0.8; text-align: center; }
.session-controls { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.session-controls button { width: 100%; }

/* Full-screen finish state.
 *
 * overflow-y is not optional here. This centres its content, and once the
 * rating list arrived the inner column could exceed the viewport — a
 * 15-minute session measured 1120px against 844px, putting the Done button
 * 244px below the fold with no way to scroll to it. A finish screen you
 * cannot leave is the worst possible dead end, since the workout is already
 * logged and the only escape is reloading.
 *
 * align-items:start rather than centre once it overflows: with place-items
 * centre, content taller than the viewport is clipped equally at BOTH ends,
 * so the top becomes unreachable too. */
.session-done {
  position: fixed;
  inset: 0;
  display: grid;
  justify-items: center;
  align-items: safe center;
  padding: 24px;
  background: var(--bg);
  overflow-y: auto;
  overscroll-behavior: contain;
}
.session-done .inner {
  text-align: center;
  max-width: 340px;
  width: 100%;
  padding-bottom: calc(var(--safe-bottom) + 8px);
}

/* ============ Weekly coverage (dashboard) ============ */

.week-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 10px;
}
.week-cell {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 12px 8px;
  text-align: center;
}
.week-cell .part {
  font-size: 0.78rem; font-weight: 700;
  letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--slate); opacity: 0.75;
}
.week-cell .count { font-size: 1.6rem; font-weight: 500; color: var(--border); line-height: 1.3; }
.week-cell.is-hit { border-color: var(--teal); background: var(--teal-tint); }
.week-cell.is-hit .part { color: var(--teal-dark); opacity: 1; }
.week-cell.is-hit .count { color: var(--teal-dark); }

.goal-line { display: flex; align-items: baseline; justify-content: space-between; margin: 4px 0 12px; }
.goal-line .big { font-size: 2rem; font-weight: 500; color: var(--heading); line-height: 1; }

/* ============ Calendar ============ */

.cal-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
.cal-head .month { font-weight: 600; color: var(--heading); }
.cal-nav { display: flex; gap: 6px; }
.cal-nav a {
  min-width: 40px; min-height: 40px;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--border); border-radius: var(--radius);
  text-decoration: none; color: var(--slate); font-weight: 600;
}
.cal-nav a:hover { border-color: var(--teal); color: var(--teal-dark); }

.cal { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; }
.cal .dow {
  text-align: center; font-size: 0.7rem; font-weight: 700;
  letter-spacing: 0.04em; text-transform: uppercase;
  color: var(--slate); opacity: 0.6; padding-bottom: 4px;
}
.cal-day {
  aspect-ratio: 1;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 3px;
  border: 1px solid transparent;
  border-radius: 8px;
  font-size: 0.85rem;
  color: var(--slate);
  text-decoration: none;
}
.cal-day.is-empty { visibility: hidden; }
.cal-day.is-today { border-color: var(--border); font-weight: 700; }
.cal-day.has-workout { background: var(--teal-tint); color: var(--teal-dark); font-weight: 600; }
.cal-day .dot { width: 4px; height: 4px; border-radius: 50%; background: var(--teal); }
.cal-day.has-workout:hover { border-color: var(--teal); }
