:root {
  --bg: #ffffff;
  --ink: #1c1e21;
  /* Same colour as --ink, as separate R,G,B channels so it can be used
     with rgba() at varying opacity (backdrops, shadows) without repeating
     the literal rgb(28, 30, 33) at every point of use. */
  --ink-rgb: 28, 30, 33;
  --ink-soft: #5b5f66;
  --line: #c7cad0;
  --line-strong: #b9bdc4;
  /* Same colour as --line-strong, split into R,G,B channels - see the
     matching comment on --ink-rgb above. */
  --line-strong-rgb: 185, 189, 196;
  --today-ring: #2f5d8a;
  /* Structural surfaces that were hardcoded hex values before dark mode
     existed - now theme-aware like everything else here. */
  --surface-soft: #f7f7f8;     /* filler calendar cells, row hover */
  --surface-tabs: #f2f2f3;     /* settings tab-strip background */
  --input-border: #e3d29a;     /* editable pace-input border */
  --input-bg: #fdf6df;         /* editable pace-input background (amber tint) */
  /* Rendered height of .range-header - used below to size .month so
     ~3.5 months fit per mobile screen. Named so the relationship is
     obvious if the header's own padding/font-size ever changes. */
  --header-height: 62px;
  /* Roughly the today-pill's own height plus a gap above it, so the page's
     bottom padding (below) can reserve room for it without hardcoding the
     number twice. */
  --today-pill-clearance: 88px;
}

/* Dark mode: automatic only, no in-app toggle - the system's own
   light/dark setting decides. Palette event colours (data.js) are left
   unchanged in both themes since they're a fixed identity per event, not
   a light/dark-mode surface; --today-ring is kept close to its light
   value (just a touch brighter) so it still reads clearly against the
   darker background. */
@media (prefers-color-scheme: dark) {
  :root {
    --bg: #1c1e21;
    --ink: #f2f1ef;
    --ink-rgb: 242, 241, 239;
    --ink-soft: #a9adb5;
    --line: #35383e;
    --line-strong: #464a52;
    --line-strong-rgb: 70, 74, 82;
    --today-ring: #6ea0d6;
    --surface-soft: #232629;
    --surface-tabs: #26292d;
    --input-border: #6b5d33;
    --input-bg: #33301f;
  }
}

* { box-sizing: border-box; }

body {
  margin: 0;
  /* env() falls back to 0 on browsers that don't support it, so this is
     safe everywhere - it only matters on iOS standalone (Add to Home
     Screen), where content would otherwise sit under the notch/home
     indicator without it. Bottom padding also clears the fixed today-pill
     (--today-pill-clearance) on top of the existing 32px/safe-area
     reservation, so the last month's last row isn't hidden behind it. */
  padding-top: env(safe-area-inset-top);
  padding-bottom: calc(32px + var(--today-pill-clearance) + env(safe-area-inset-bottom));
  padding-left: env(safe-area-inset-left);
  padding-right: env(safe-area-inset-right);
  background: var(--bg);
  color: var(--ink);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
}

.range-header {
  padding: 32px 4vw 12px;
  position: relative;
}

.range-header-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.range-header h1 {
  margin: 0;
  font-size: clamp(18px, 2.4vw, 24px);
  font-weight: 600;
  letter-spacing: -0.01em;
}

/* Only ever present when init() had to fall back to the offline
   snapshot - see showOfflineBanner in script.js. Amber, same "needs
   attention" register as the pace-input fields' edit convention, but a
   banner rather than an input since there's nothing to type here. */
.offline-banner {
  margin: 0 4vw 16px;
  padding: 10px 14px;
  border: 1px solid var(--input-border);
  border-radius: 8px;
  background: var(--input-bg);
  color: var(--ink);
  font-size: 13px;
  line-height: 1.4;
}

@media (min-width: 900px) {
  .offline-banner {
    margin-left: 28px;
    margin-right: 28px;
  }
}

.month {
  width: 100%;
  min-height: calc((100vh - var(--header-height)) / 3.5);
  padding: 3vh 4vw;
  border-bottom: 1px solid var(--line);
  display: flex;
  flex-direction: column;
  /* Stops scrollIntoView (used by the today-pill's tap-through) a little
     short of the very top of the screen, rather than landing the month
     name flush against/cut off by the top edge. */
  scroll-margin-top: calc(16px + env(safe-area-inset-top, 0px));
}

.month-title {
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin-bottom: 10px;
}

.month-title .name {
  font-size: clamp(19px, 2.6vw, 27px);
  font-weight: 700;
  letter-spacing: -0.01em;
}

.month-title .year {
  font-size: clamp(13px, 1.6vw, 16px);
  font-weight: 400;
  color: var(--ink-soft);
}

.grid-wrap {
  flex: 1;
  display: flex;
  flex-direction: column;
}

.weekdays,
.days {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
}

.weekdays div {
  font-size: 10px;
  font-weight: 600;
  color: var(--ink-soft);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  padding: 0 5px 5px;
  border-bottom: 1px solid var(--line-strong);
}

.days {
  border-right: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}

.days .cell {
  border-top: 1px solid var(--line);
  border-left: 1px solid var(--line);
  padding: 5px 6px;
  font-size: 12.5px;
  font-variant-numeric: tabular-nums;
  height: 52px;
  overflow: hidden;
  position: relative;
}

/* The weekday row's own bottom border already closes the top of the
   first week, so the first row of cells doesn't draw its own (avoids
   a doubled-up line right under the weekday labels). */
.days .cell:nth-child(-n+7) {
  border-top: none;
}

.days .cell.empty {
  background: var(--surface-soft);
}

/* Logged-workout marker: a small dot in the corner of any day that has a
   planned session. Hollow ring = planned but not logged yet; filled solid
   = logged, set from the popup's "Mark as complete" button, not by
   clicking the dot itself. Colour is set inline per-cell to match the
   event's palette dot, so it stays consistent with the rest of that
   training block. */
.logged-dot {
  position: absolute;
  right: 4px;
  bottom: 4px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  border: 1.5px solid var(--dot-color, currentColor);
  background: transparent;
  pointer-events: none;
}

.logged-dot.is-logged {
  background: var(--dot-color, currentColor);
}

/* Filler cells lose their grey fill.
   empty-blank also loses its left border, for a filler cell whose left
   neighbour is itself filler (nothing real to visually connect to);
   empty-edge keeps its left border, for a filler cell that sits right
   next to a real date. */
.days .cell.empty-blank,
.days .cell.empty-edge {
  background: transparent;
}

.days .cell.empty-blank {
  border-left: none;
}

/* The grid's own outer bottom/right edges are dropped - the top edge
   (the weekday header's own bottom border) is untouched. Real dates in
   the last column get their right edge back individually, since
   filler cells there shouldn't have one. */
.days.trial-edges {
  border-right: none;
  border-bottom: none;
}

.days.trial-edges .cell:nth-child(7n):not(.empty-blank):not(.empty-edge):not(.empty) {
  border-right: 1px solid var(--line);
}

.days.trial-edges .cell.row-bottom {
  border-bottom: 1px solid var(--line);
}

/* The weekday header divs each draw their own bottom border already -
   the ones sitting above a blank leading filler cell
   don't get one. */
.weekdays div.blank-heading {
  border-bottom: none;
}

/* On mobile, .month draws its own bottom border as the divider between
   stacked months - that line was still showing up under July's last
   row even after .days' own border-bottom was dropped above. */
.month.trial-no-divider {
  border-bottom: none;
}

.days .cell.today {
  box-shadow: inset 0 0 0 1.25px var(--today-ring);
}

.days .cell.today .num {
  font-weight: 700;
  color: var(--today-ring);
}

.days .cell .num {
  display: block;
  line-height: 1;
}


.event-label {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  margin-top: 2px;
  font-size: 10px;
  font-weight: 600;
  line-height: 1.15;
  white-space: normal;
  overflow: hidden;
  text-overflow: ellipsis;
}

@media (max-width: 640px) {
  .event-label { font-size: 8.5px; }
  .days .cell { font-size: 11px; padding: 4px 3px; height: 46px; }
  .weekdays div { font-size: 8.5px; padding: 0 3px 5px; }
}

/* Desktop: a single full-width month stretches into oversized tiles
   with wasted space either side, so from here up we show two months
   per row instead, in the same style, capped to a comfortable reading
   width and centred. Everything below 900px (phone and tablet) is
   untouched. */
@media (min-width: 900px) {
  body {
    max-width: 1180px;
    margin: 0 auto;
  }

  .range-header {
    padding: 32px 28px 16px;
  }

  #calendar {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    align-items: stretch;
  }

  /* Without this, the [hidden] attribute's own "display: none" loses to
     the "display: grid" above (same specificity, this rule wins on
     source order) - so switching to day view on desktop left the month
     grid still laid out and scrollable underneath it. */
  #calendar[hidden] {
    display: none;
  }

  .month {
    min-height: 0;
    padding: 36px 28px;
    border-bottom: none;
  }
}

@media print {
  .month { break-inside: avoid; page-break-after: always; }
  /* A modal left open shouldn't appear in a printout. */
  .modal-overlay,
  .event-list-overlay { display: none !important; }
}

.days .cell:not(.empty):not(.empty-blank):not(.empty-edge) {
  cursor: pointer;
}

.days .cell:not(.empty):not(.empty-blank):not(.empty-edge):hover,
.days .cell.selected {
  box-shadow: inset 0 0 0 1.25px var(--line-strong);
}

.days .cell.selected .num {
  font-weight: 700;
  color: var(--line-strong);
}

.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(var(--ink-rgb), 0.4);
  display: flex;
  align-items: center;
  /* "safe" stops a well-known flexbox bug: a centred item taller than its
     container normally clips at the top with no way to scroll up to it,
     because only the overflow on one side is reachable. "safe" tells the
     browser to fall back to start-alignment instead of letting content
     go unreachable - this is what was cutting the top of tall popups
     (e.g. the settings panel with an event row expanded) off on mobile. */
  align-items: safe center;
  justify-content: center;
  padding: 20px;
  overflow-y: auto;
  overscroll-behavior: contain;
  z-index: 1000;
  animation: modal-overlay-in 0.2s ease-out;
}

.modal-overlay[hidden] {
  display: none;
}

.modal-card {
  background: var(--bg);
  border: 1px solid var(--line-strong);
  width: 100%;
  max-width: 380px;
  /* max-height caps the card itself and scrolls its own content once it
     grows past that, rather than growing unbounded and relying on the
     overlay to scroll - so the card shrinks to fit shorter content and
     only becomes internally scrollable once it's genuinely too tall.
     100dvh (dynamic viewport height) rather than 100vh specifically
     because 100vh doesn't account for a mobile browser's address bar,
     which is what caused this to get clipped top/bottom before; 100dvh
     tracks the actual visible area. The vh fallback below only matters
     on the small set of older browsers without dvh support. */
  max-height: calc(100vh - 40px);
  max-height: calc(100dvh - 40px);
  overflow-y: auto;
  overscroll-behavior: contain;
  margin: auto;
  padding: 24px;
  position: relative;
  animation: modal-card-in 0.22s cubic-bezier(0.2, 0.8, 0.3, 1);
}

/* Subtle open animation: backdrop fades in while the card fades and
   scales up slightly from just below full size, rather than appearing
   instantly. Replays on every open without any JS involvement, since the
   "hidden" attribute toggles display:none on and off, and returning from
   display:none re-triggers a fresh animation each time. */
@keyframes modal-overlay-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

@keyframes modal-card-in {
  from { opacity: 0; transform: scale(0.96) translateY(6px); }
  to { opacity: 1; transform: scale(1) translateY(0); }
}

.modal-close {
  position: absolute;
  top: 12px;
  right: 12px;
  width: 28px;
  height: 28px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
  background: transparent;
  color: var(--ink-soft);
  cursor: pointer;
  padding: 0;
}

.modal-close:hover {
  color: var(--ink);
}

.modal-title {
  font-size: 18px;
  font-weight: 700;
  letter-spacing: -0.01em;
  margin: 0 28px 4px 0;
}

.modal-date {
  font-size: 13px;
  color: var(--ink-soft);
  margin: 0 0 16px 0;
}

.modal-empty-note {
  font-size: 13px;
  color: var(--ink-soft);
  margin: 0;
}

/* Also reused, unchanged, as the wrapper around a set of discipline
   blocks - same flex/gap rhythm as the outer field list. */
.modal-fields {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

/* Divider between multiple events landing on the same day (rare -
   most days have at most one). */
.modal-event-divider {
  border-top: 1px solid var(--line-strong);
  margin: 20px 0 0;
}

.modal-discipline {
  padding-top: 16px;
  border-top: 1px solid var(--line);
}

.modal-discipline:first-child {
  padding-top: 0;
  border-top: none;
}

.modal-discipline-name {
  font-size: 13px;
  font-weight: 700;
  letter-spacing: -0.01em;
  margin-bottom: 8px;
}

.modal-discipline-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
}

.modal-field-label {
  display: block;
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--ink-soft);
  margin-bottom: 3px;
}

.modal-field-value {
  font-size: 14px;
}

.modal-field-value.is-empty {
  color: var(--ink-soft);
  font-style: italic;
}

.modal-field-value a {
  color: var(--today-ring);
}

.modal-complete-btn {
  align-self: flex-start;
  margin-top: 4px;
  padding: 8px 14px;
  border: 1px solid var(--line-strong);
  background: transparent;
  color: var(--ink);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
}

.modal-complete-btn:hover {
  box-shadow: inset 0 0 0 1px var(--line-strong);
}

.modal-complete-btn.is-logged {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--bg);
}

.modal-complete-btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/* Event list panel: centred overlay matching .modal-overlay/.modal-card's
   layout exactly (same dimmed backdrop, flex-centred card, border/padding/
   close-button language), just with a slightly wider max-width to fit
   each row's date/countdown column. */
.event-list-overlay {
  position: fixed;
  inset: 0;
  background: rgba(var(--ink-rgb), 0.4);
  display: flex;
  align-items: center;
  /* See the matching comment on .modal-overlay - same fix, same reason
     (this is the panel that was clipping top/bottom when an event row
     was expanded). */
  align-items: safe center;
  justify-content: center;
  padding: 20px;
  overflow-y: auto;
  overscroll-behavior: contain;
  z-index: 1000;
  animation: modal-overlay-in 0.2s ease-out;
}

.event-list-overlay[hidden] {
  display: none;
}

.event-list-panel {
  background: var(--bg);
  border: 1px solid var(--line-strong);
  width: 100%;
  max-width: 420px;
  animation: modal-card-in 0.22s cubic-bezier(0.2, 0.8, 0.3, 1);
  /* Capped and internally scrollable, same as .modal-card - see the
     comment there for why 100dvh specifically. Shrinks to fit when every
     row is collapsed, and only scrolls internally once expanding a row
     pushes it past the cap. */
  max-height: calc(100vh - 40px);
  max-height: calc(100dvh - 40px);
  overflow-y: auto;
  overscroll-behavior: contain;
  margin: auto;
  padding: 24px;
  position: relative;
}

.event-list-panel-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 14px;
}

/* Segmented tab control (Events / Paces) replacing the old static panel
   title - the header icon's own meaning still primarily reads as "show
   me events", so this always opens on the Events tab. */
.settings-tabs {
  display: flex;
  gap: 4px;
  padding: 3px;
  background: var(--surface-tabs);
  border-radius: 8px;
}

.settings-tab {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px 12px;
  border: none;
  border-radius: 6px;
  background: transparent;
  color: var(--ink-soft);
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
}

.settings-tab.is-active {
  background: var(--bg);
  color: var(--ink);
  box-shadow: 0 1px 2px rgba(var(--ink-rgb), 0.12);
}

.event-list-close {
  flex: none;
  width: 28px;
  height: 28px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
  background: transparent;
  color: var(--ink-soft);
  cursor: pointer;
  padding: 0;
}

.event-list-close:hover {
  color: var(--ink);
}

.event-list-row {
  border-top: 1px solid var(--line);
}

.event-list-row:first-child {
  border-top: none;
}

.event-list-row-head {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 12px 0;
  border: none;
  background: transparent;
  cursor: pointer;
  text-align: left;
  font: inherit;
  color: inherit;
}

.event-list-dot {
  flex: none;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  border: 1.5px solid var(--dot-color, currentColor);
  background: transparent;
}

/* Future events: hollow ring, same convention as an unlogged workout dot.
   Current/in-progress: filled solid, same convention as a logged workout
   dot - this event is "in play" right now. Past events: filled but muted,
   plus soft-coloured row text, reusing the existing past/faded language
   used nowhere else yet but consistent with how completed state reads
   elsewhere (solid = done/active vs hollow = not yet). */
.event-list-row.is-current .event-list-dot,
.event-list-row.is-past .event-list-dot {
  background: var(--dot-color, currentColor);
}

.event-list-row.is-past {
  opacity: 0.6;
}

.event-list-row-name {
  flex: 1;
  font-size: 14px;
  font-weight: 600;
}

.event-list-row-when {
  flex: none;
  font-size: 12px;
  color: var(--ink-soft);
  text-align: right;
}

.event-list-row-chevron {
  flex: none;
  width: 14px;
  height: 14px;
  color: var(--ink-soft);
  transition: transform 0.15s ease;
}

.event-list-row-head[aria-expanded="true"] .event-list-row-chevron {
  transform: rotate(90deg);
}

.event-list-row-body {
  padding: 0 0 16px;
}

.event-list-row-body[hidden] {
  display: none;
}

/* Paces tab: benchmark inputs (Swim CSS pace, Cycling LTHR, Run threshold
   pace) plus three auto-calculated zone tables below them. Not wired into
   WORKOUTS/the day popup - the calculator and its storage stand alone for
   now. */
.paces-benchmarks {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding-bottom: 20px;
  margin-bottom: 20px;
  border-bottom: 1px solid var(--line);
}

.paces-input-field {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 12px;
  font-weight: 600;
  color: var(--ink-soft);
}

/* Soft amber tint marks these as the user-editable inputs everything
   else on the tab is calculated from - same "yellow cells are yours to
   edit" convention as a typical training-plan spreadsheet. */
.paces-input-field input {
  padding: 9px 10px;
  border: 1px solid var(--input-border);
  border-radius: 6px;
  background: var(--input-bg);
  color: var(--ink);
  font: inherit;
  font-size: 14px;
  font-weight: 400;
}

.paces-input-field input:focus {
  outline: 2px solid var(--today-ring);
  outline-offset: 1px;
}

.paces-save-btn {
  align-self: flex-start;
  margin-top: 4px;
  padding: 8px 14px;
  border: 1px solid var(--line-strong);
  background: transparent;
  color: var(--ink);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
}

.paces-save-btn:hover {
  box-shadow: inset 0 0 0 1px var(--line-strong);
}

.paces-save-btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.paces-save-status {
  margin: 4px 0 0;
  font-size: 12px;
  color: var(--ink-soft);
  min-height: 1em;
}

.paces-zone-tables {
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.paces-zone-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
}

.paces-zone-table caption {
  text-align: left;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: -0.01em;
  margin-bottom: 8px;
}

.paces-zone-unit {
  font-weight: 400;
  color: var(--ink-soft);
}

.paces-zone-table th {
  text-align: left;
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--ink-soft);
  padding: 0 0 6px;
  border-bottom: 1px solid var(--line-strong);
}

.paces-zone-table td {
  padding: 7px 0;
  border-bottom: 1px solid var(--line);
}

.paces-zone-table tbody tr:last-child td {
  border-bottom: none;
}

.paces-zone-value {
  font-variant-numeric: tabular-nums;
  font-weight: 600;
}

.paces-zone-value.is-empty {
  font-weight: 400;
  font-style: italic;
  color: var(--ink-soft);
}

/* The floating "today" button's own styling (glass surface, sizing,
   position) now lives in liquid-glass-toolbar.css as the centre pill of
   the #today-toolbar three-piece toolbar (see index.html). Only the
   label's text-overflow behaviour is app-specific, since the label's
   content (today's workout/rest-day summary) can vary in length. */
.today-pill-label {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Tap-through highlight: a brief "look here" pulse on today's cell,
   distinct from the permanent .today box-shadow ring so the two don't
   read as the same state. Entirely inset (no outer glow spilling past
   the cell's own border) so it reads as a highlight inside the tile
   rather than a halo around it. Fades out on its own via the animation
   rather than needing a second class removal round-trip. */
@keyframes today-pill-pulse {
  0% { box-shadow: inset 0 0 0 2px var(--today-ring), inset 0 0 16px rgba(47, 93, 138, 0.28); }
  100% { box-shadow: inset 0 0 0 2px var(--today-ring), inset 0 0 16px rgba(47, 93, 138, 0); }
}

.days .cell.pulse {
  animation: today-pill-pulse 1.4s ease-out;
}

/* Anyone with reduced-motion enabled at the OS level gets these
   near-instant instead of animated - covers the modal/event-list open
   animation, the today-cell pulse, and the event-list chevron rotation.
   The today-pill's scroll-to-month behaviour is also switched from smooth
   to instant in script.js under the same media query. */
@media (prefers-reduced-motion: reduce) {
  .modal-overlay,
  .event-list-overlay,
  .modal-card,
  .event-list-panel,
  .days .cell.pulse,
  .event-list-row-chevron {
    animation-duration: 0.001s !important;
    transition-duration: 0.001s !important;
  }
}

/* Day view: a flat list alternative to the month grid, toggled by the
   liquid-glass toolbar's left button (see #view-toggle in script.js).
   Same left/right edges as .range-header/.month (4vw), so switching
   views doesn't shift the content edges the user's eye has already
   settled on. */
#day-view {
  padding: 8px 4vw calc(32px + var(--today-pill-clearance) + env(safe-area-inset-bottom));
}

#day-view[hidden] {
  display: none;
}

.day-view-month-heading {
  padding: 22px 2px 8px;
  font-size: clamp(15px, 1.9vw, 18px);
  font-weight: 700;
  letter-spacing: -0.01em;
}

.day-view-month-heading:first-child {
  padding-top: 6px;
}

.day-row {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 9px 8px;
  margin: 0 -8px;
  border-top: 1px solid var(--line);
  border-radius: 6px;
  cursor: pointer;
  scroll-margin-top: calc(16px + env(safe-area-inset-top, 0px));
}

/* Heavier rule + a clearly larger gap at the start of each week
   (Monday), so the list reads in 7-day chunks rather than one
   continuous run of rows - suppressed on the 1st of a month since the
   month heading above already provides that break. The gap has to be
   well past the 9px row padding to actually read as a break rather
   than looking like a rendering glitch. */
.day-row.week-start {
  margin-top: 24px;
  border-top: 1px solid var(--line-strong);
}

.day-row:hover {
  background: var(--surface-soft);
}

.day-row:focus-visible {
  outline: 2px solid var(--today-ring);
  outline-offset: -2px;
}

/* today's ring sits on the row's own border-box (now with real
   horizontal padding to sit inside), rather than clipping straight
   against the text with almost no margin either side. Border-top is
   hidden on this row and its next sibling so the ring's own top/bottom
   edges read as the row's only lines, not doubled up with the list's
   plain dividers. */
.day-row.today {
  box-shadow: inset 0 0 0 1.5px var(--today-ring);
  border-top-color: transparent;
}

.day-row.today + .day-row {
  border-top-color: transparent;
}

.day-row-date {
  flex: 0 0 56px;
  display: flex;
  flex-direction: column;
  line-height: 1.2;
}

.day-row-weekday {
  font-size: 10.5px;
  font-weight: 600;
  color: var(--ink-soft);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.day-row-num {
  font-size: 16px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

/* Same convention as the month view's .logged-dot: an outline circle for
   an unlogged workout, filled once logged. Events (no logged state at
   all) get a permanently filled dot, matching their solid colour swatch
   on the month view's cell background. */
.day-row-dot {
  flex: none;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  border: 1.5px solid var(--dot-color, currentColor);
  background: transparent;
}

.day-row-dot.is-filled {
  background: var(--dot-color, currentColor);
}

.day-row-label {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 13.5px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.day-row-label-empty {
  color: var(--ink-soft);
}

@media (min-width: 900px) {
  #day-view {
    padding-left: 28px;
    padding-right: 28px;
  }
}
