/* LUMA - the widget skin (Layer 9)
   Sheet 4 of 9. Source order decides the cascade - see the link tags in luma.html. */

/* ============================================================
   LAYER 9 — THE WIDGET SKIN
   ============================================================
   Every day-tab card carries data-accent="key". ONE rule maps that to
   --accent / --accent-dim and ONE rule paints the gradient, so a card added
   later either opts in or is deliberately left out — the treatment cannot
   drift. Same discipline as the Layer 8c done fill.

   Deep pitch-black ground with a soft radial bloom in the section's colour,
   over a diagonal surface gradient. */

.card[data-accent="score"]  { --accent: var(--c-score);  --accent-dim: var(--c-score-dim); }
.card[data-accent="cal"]    { --accent: var(--c-cal);    --accent-dim: var(--c-cal-dim); }
.card[data-accent="water"]  { --accent: var(--c-water);  --accent-dim: var(--c-water-dim); }
.card[data-accent="walk"]   { --accent: var(--c-walk);   --accent-dim: var(--c-walk-dim); }
.card[data-accent="work"]   { --accent: var(--c-work);   --accent-dim: var(--c-work-dim); }
.card[data-accent="plank"]  { --accent: var(--c-plank);  --accent-dim: var(--c-plank-dim); }
.card[data-accent="sleep"]  { --accent: var(--c-sleep);  --accent-dim: var(--c-sleep-dim); }
.card[data-accent="weight"] { --accent: var(--c-weight); --accent-dim: var(--c-weight-dim); }
.card[data-accent="coach"]  { --accent: var(--c-coach);  --accent-dim: var(--c-coach-dim); }

/* The steps header glyph takes the teal its ring and its სეირნობა button use,
   not --c-walk's green. Equal specificity to the line above it, so source order
   is the whole of why this wins — do not move it up. */
.card.steps-card { --accent: var(--sc-movement); }

/* 2026-09-15 — FLAT. The two-layer gradient above (accent bloom over a diagonal
   surface ramp) is gone from every card on the day screen, Giorgi's call. The
   --accent / --accent-dim mapping above is untouched and still drives every icon,
   link and ring colour on the card: only the ground changed.

   --hdr-btn is #252B25, the exact border asked for, and has been in :root since
   the day-tab header buttons were built. Do not add a second token for it. */
.card[data-accent] {
  background: var(--surface-flat);
  border: 1px solid var(--hdr-btn);
  border-radius: 18px;
  padding: 18px;
}

/* The hero keeps .card-lg's bigger geometry — it is the anchor of the screen and
   shrinking it to 18/18 would undo the segmented-arc layout. Paint only. */
.card-lg[data-accent] { border-radius: 24px; padding: 20px; }

/* The quote card is the one day-screen card with no data-accent, so the rule
   above misses it and it would sit on the old neutral --surface. */
.quote-card {
  background: var(--surface-flat);
  border: 1px solid var(--hdr-btn);
  border-radius: 18px;
  padding: 18px;
}

/* The card header: accent icon + name, with an optional target on the right. */
.widget-head {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 10px;
}

.widget-head-icon {
  flex: 0 0 auto;
  width: 17px;
  height: 17px;
  fill: none;
  stroke: var(--accent, var(--text-2));
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.widget-head-title {
  font-family: var(--font-ka);
  font-size: 13px;
  font-weight: 600;
  color: var(--text-1);
  flex: 1 1 auto;
  min-width: 0;
}

.widget-head-meta {
  flex: 0 0 auto;
  font-family: var(--font-num);
  font-size: 11px;
  font-weight: 500;
  color: var(--text-3);
}

.widget-head-action {
  flex: 0 0 auto;
  background: none;
  border: 0;
  padding: 2px 0 2px 8px;
  font-family: var(--font-ka);
  font-size: 11px;
  font-weight: 600;
  color: var(--accent, var(--lime));
  cursor: pointer;
}
.widget-head-action:active { opacity: 0.6; }

/* ---------- Story rings ---------- */
/* The label lives INSIDE .story-wrap, which is deliberately full-bleed — so
   this --pad is what puts the text back on the content edge, level with the
   cards and the first circle. It is not a second helping of the stack's own
   padding, which is what it was when the label sat in .day-stack: the text
   landed 20px right of the ring below it.

   Being inside the wrap also takes it out of .day-stack's 14px gap, which was
   stacking with this margin and the strip's 2px to put 24px of air under a
   10px label. 6px matches .settings-section-label. */
.quick-label {
  font-family: var(--font-ka);
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-3);
  padding: 0 var(--pad);
  margin-bottom: 6px;
}

/* The strip runs out past both page gutters, and it has more in it than fits:
   at 369px, 144px of it is off-screen. It used to end in a HARD VERTICAL CUT at
   the right edge — a ring sliced down the middle against the black — which is
   what Giorgi saw and what the phone's own apps never do.

   What they do instead, and what this is: the row keeps running under a soft
   fade at whichever edge still has something behind it. The fade is the signal
   that there is more; a cut is just damage. It appears on the LEFT only once
   she has scrolled, and disappears at each end, so it always means "keep
   going this way" and never lies.

   --fade-l / --fade-r are written by updateStripFade() from the real scroll
   position. At 0px the gradient's two stops sit on top of each other, which is
   a hard edge — i.e. no fade at all — so the ends are honest. */
.story-wrap {
  margin: 0 calc(-1 * var(--pad));
  -webkit-mask-image: linear-gradient(90deg,
      transparent 0, #000 var(--fade-l, 0px),
      #000 calc(100% - var(--fade-r, 0px)), transparent 100%);
  mask-image: linear-gradient(90deg,
      transparent 0, #000 var(--fade-l, 0px),
      #000 calc(100% - var(--fade-r, 0px)), transparent 100%);
}

.story-strip {
  display: flex;
  gap: 10px;
  padding: 2px var(--pad) 6px;
  overflow-x: auto;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
  -ms-overflow-style: none;
  /* It comes to rest on a whole ring rather than halfway through one.
     `proximity`, not `mandatory`: a mandatory snap fights a short flick and
     makes the row feel sticky. scroll-padding puts the snapped ring on the
     page's own left margin, where every other first item on the screen sits. */
  scroll-snap-type: x proximity;
  scroll-padding-left: var(--pad);
  overscroll-behavior-x: contain;
}
.story-strip::-webkit-scrollbar { display: none; }

.story-item {
  flex: 0 0 auto;
  /* 56 -> 59, with the ring 52 -> 55: Giorgi asked for about 5% more on
     2026-09-16, reading them as too small next to everything else on the tab. */
  width: 59px;
  scroll-snap-align: start;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  transition: transform var(--dur-fast) var(--ease-spring);
}
.story-item:active { transform: scale(0.92); }
.story-item:disabled { cursor: default; opacity: 0.5; }

/* Layer 9 rings, grown twice on Giorgi's call (48 -> 55 -> 64), then taken to
   52 with the 2026-09-15 header redesign. The arc, the track and both stroke
   widths are viewBox units, so only these boxes and the glyph are hard sizes;
   STORY_R / STORY_C stay untouched.

   The dark disc plus a track tinted in the section's own colour is the
   mockup's "coloured border" — it is the ring's EMPTY half, not a decoration,
   so the arc still fills over it exactly as before. */
.story-ring-wrap {
  position: relative;
  width: 55px;
  height: 55px;
  display: block;
  background: var(--surface);
  border-radius: 50%;
}

.story-ring { width: 55px; height: 55px; display: block; }

/* ---- One accent (2026-09-16) ------------------------------------
   Giorgi's call after seeing the screen beside an Apple one: seven fully
   saturated hues across this strip, five more in the score rows below it and
   lime on top of both meant nothing was dominant and the screen read as loud
   rather than expensive. So: ONE full-strength colour.

   - the empty track is a uniform neutral, not the section's own tint
   - the arc that fills it is always lime — earning points is one idea, so it
     gets one colour, and it is the same lime the done fill uses everywhere else
   - the GLYPH keeps its category colour, dimmed, which is what still lets the
     seven be told apart at a glance

   --accent / --accent-dim are still set inline per item by renderStoryRings()
   and still drive the glyph; only the ring itself stopped using them. */
.story-ring-track { stroke: rgba(255, 255, 255, 0.07); }
.story-ring-fill {
  stroke: var(--lime);
  transition: stroke-dasharray var(--dur-slow) var(--ease-out);
}

.story-ring-icon {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}
.story-ring-icon svg {
  width: 21px;
  height: 21px;
  fill: none;
  stroke: var(--accent);
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
  /* Dimmed from 0.75: the glyph is the only place a category's own colour
     survives now, so it has to read as a tint rather than compete with the lime
     arc around it. */
  opacity: 0.5;
}
/* Earned: the glyph goes lime with the arc, so a finished shortcut is one
   colour rather than a lime ring around an orange mark. */
.story-item.is-full .story-ring-icon svg { stroke: var(--lime); opacity: 1; }
/* A finished ring gets a faint disc behind the glyph, so "done" reads from the
   fill rather than only from the ring being closed. */
.story-item.is-full .story-ring-icon::before {
  content: '';
  position: absolute;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: var(--lime);
  opacity: 0.14;
}

.story-label {
  font-family: var(--font-ka);
  font-size: 11px;
  font-weight: 600;
  color: var(--text-3);
  text-align: center;
  line-height: 1.15;
  max-width: 70px;
}
.story-item.is-full .story-label { color: var(--text-2); }

/* 2026-09-08: the dashed .is-rest state is gone. Giorgi's call — ვარჯიში has to
   read like the other six segments in BOTH states, empty when it has earned
   nothing and filled when it has. The rest day still cannot read "complete":
   that is the frac guard in renderStoryRings(), not a style. */

/* The movement card's cardio + burn tiles light in their section accent when
   walking carries them over the line. Deliberately NOT the lime .is-done fill:
   that one means "this earned its points" everywhere else on the tab, and step
   minutes are excluded from scoring on purpose. See isWorkoutDay(). */
.movement-item.is-active .movement-item-icon { stroke: var(--c-work); }
.movement-item.is-active .movement-item-val { color: var(--text-1); }

.movement-item-sub {
  font-family: var(--font-ka);
  font-size: 9px;
  color: var(--text-3);
  margin-top: 3px;
  line-height: 1.2;
  text-align: center;
}

/* Metric pills carry the accent contract without being .card — the paint is
   flatter (they are 1/3-width tiles), but the colour comes from one place. */
.metric-pill[data-accent="cal"]   { --accent: var(--c-cal);   --accent-dim: var(--c-cal-dim); }
.metric-pill[data-accent="water"] { --accent: var(--c-water); --accent-dim: var(--c-water-dim); }
.metric-pill[data-accent="work"]  { --accent: var(--c-work);  --accent-dim: var(--c-work-dim); }

/* Flat too, and on --surface-inset rather than --surface-flat: the pills read as
   tiles sitting on the screen, the same weight as the tiles inside the cards. */
.metric-pill[data-accent] {
  background: var(--surface-inset);
}

/* The one-accent pass reaches the three metric pills too (2026-09-16). They were
   the third full-saturation colour set on this screen after the shortcut rings
   and the score rows — three loud labels with three loud bars, sitting directly
   under everything that had just been calmed down.

   Same rule as the rings: the colour survives as a TINT that tells the three
   apart, and lime is reserved for earned. .metric-pill.is-done already takes the
   shared lime done fill, so a finished pill still reads as finished. */
.metric-pill[data-accent] .metric-pill-label { color: var(--accent); opacity: 0.62; }
.metric-pill[data-accent] .progress-fill { background: var(--accent); opacity: 0.5; }
.metric-pill[data-accent].is-done .metric-pill-label { opacity: 0.9; }
.metric-pill[data-accent].is-done .progress-fill { background: var(--lime); opacity: 0.6; }


/* ---------- Weight history window (Layer 9) ---------- */
.wh-chart-wrap {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 10px 4px 4px;
  margin-top: 12px;
}

.wh-empty {
  font-family: var(--font-ka);
  font-size: 13px;
  color: var(--text-3);
  line-height: 1.6;
  text-align: center;
  padding: 28px 16px;
}

.wh-stats {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 10px;
}

.wh-stat {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 12px;
}

.wh-stat-label {
  font-family: var(--font-ka);
  font-size: 11px;
  color: var(--text-3);
  margin-bottom: 4px;
}

.wh-stat-val {
  font-family: var(--font-num);
  font-size: 18px;
  font-weight: 700;
  color: var(--text-1);
}

.wh-stat-good .wh-stat-val { color: var(--lime); }
.wh-stat-warn .wh-stat-val { color: var(--amber); }

.wh-list-title {
  font-family: var(--font-ka);
  font-size: 12px;
  font-weight: 600;
  color: var(--text-2);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  margin-bottom: 8px;
}

.wh-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 11px 0;
  border-bottom: 1px solid var(--border);
}
.wh-row:last-child { border-bottom: none; }

.wh-row-date {
  flex: 1;
  min-width: 0;
  font-family: var(--font-ka);
  font-size: 13px;
  color: var(--text-2);
  display: flex;
  align-items: center;
  gap: 6px;
}

.wh-row-tag {
  font-family: var(--font-ka);
  font-size: 9px;
  font-weight: 600;
  padding: 2px 6px;
  border-radius: var(--radius-pill);
  background: var(--teal-dim);
  color: var(--teal);
}

.wh-row-val {
  font-family: var(--font-num);
  font-size: 15px;
  font-weight: 700;
  color: var(--text-1);
  font-variant-numeric: tabular-nums;
}

.wh-row-delta {
  font-family: var(--font-num);
  font-size: 12px;
  font-weight: 600;
  color: var(--text-3);
  min-width: 44px;
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.wh-row-delta.is-down { color: var(--lime); }
.wh-row-delta.is-up { color: var(--amber); }


/* ---------- Plank timer dial (Layer 9) ----------
   Redrawn to the phone-stopwatch idiom: a 60-mark dial, m:ss.cc, and two large
   circular buttons. Deliberately NO needle. */
.plank-tick {
  stroke: var(--surface-3);
  stroke-linecap: round;
}
.plank-tick.is-major { stroke: var(--text-3); }
.plank-tick.is-lit { stroke: var(--c-plank); }

.plank-timer-cs {
  font-family: var(--font-num);
  font-size: 0.5em;
  font-weight: 600;
  color: var(--text-2);
  letter-spacing: 0;
  font-variant-numeric: tabular-nums;
}
.plank-timer-cs.is-ghost { visibility: hidden; }

/* The arc keeps Layer 8b's teal -> lime, which MEANS something (lime = the day
   total met the target, the same fraction the score chip reads). The plank
   accent belongs to the dial ticks instead, so the two are not saying the same
   thing in two colours. Do not restyle .plank-ring-fill here — Layer 8b's rule
   is later in the file and wins anyway. */

/* NOTE: .plank-timer-btns is NOT declared here. It used to be, with gap:28px,
   and the Layer 8b copy further down won on source order — so the value in this
   block was a lie for anyone reading it. There is one declaration now. */

/* Sized by the longest label — ten Georgian characters that overflowed an 84px
   circle at 13px, and still overflowed at 96px/12px. Measured, not guessed: the
   longest label renders 76.5px against 82px of content box, and every one of
   the six fits on one line. 2 x 100 + the 20px gap is 220px, exactly the dial
   above, so the two rows line up. */
.plank-round-btn {
  width: 100px;
  height: 100px;
  border-radius: 50%;
  border: 1px solid var(--border-2);
  background: var(--surface-2);
  color: var(--text-1);
  font-family: var(--font-ka);
  font-size: 11.5px;
  font-weight: 600;
  line-height: 1.25;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 0 8px;
  transition: transform var(--dur-fast) var(--ease-spring),
              background-color var(--dur-fast) var(--ease-out);
}
.plank-round-btn:active { transform: scale(0.94); }
.plank-round-btn:disabled { opacity: 0.4; cursor: default; }

.plank-round-btn.is-primary {
  background: var(--lime);
  border-color: var(--lime);
  color: #0A0A0C;
}

.plank-save-btn { margin-top: 20px; }
.plank-save-btn:disabled { opacity: 0.4; }


/* The plank card grew a header, so its icon/text/button row is now nested. */
.plank-card-row {
  display: flex;
  align-items: center;
  gap: 12px;
}

/* ---------- Plank history (Layer 9) ---------- */
.ph-bars {
  display: flex;
  align-items: flex-end;
  gap: 3px;
  height: 110px;
  padding: 0 2px;
}

.ph-bar-col { flex: 1 1 0; height: 100%; min-width: 0; }

.ph-bar-track {
  position: relative;
  height: 100%;
  display: flex;
  align-items: flex-end;
  /* Barely there: an empty day should read as absence, not as a bar of its own.
     30 solid tracks plus 30 target lines rendered as a barcode. */
  background: rgba(255, 255, 255, 0.025);
  border-radius: 3px;
  overflow: hidden;
}

.ph-bar {
  width: 100%;
  background: rgba(167, 139, 250, 0.35);   /* logged but short of target */
  border-radius: 3px;
  transition: height var(--dur-slow) var(--ease-out);
}
.ph-bar.is-done { background: var(--c-plank); }

/* The day's own target rung, so a deload day is not read as a bad day. */
.ph-bar-target {
  position: absolute;
  left: 0;
  right: 0;
  height: 1px;
  background: rgba(255, 255, 255, 0.28);
}

.ph-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 11px 0;
  border-bottom: 1px solid var(--border);
}
.ph-row:last-child { border-bottom: none; }

.ph-row-main { flex: 1; min-width: 0; }

.ph-row-date {
  font-family: var(--font-ka);
  font-size: 13px;
  color: var(--text-1);
}

.ph-row-sets {
  font-family: var(--font-num);
  font-size: 11px;
  color: var(--text-3);
  margin-top: 2px;
}

.ph-row-right { flex: 0 0 auto; text-align: right; }

.ph-row-total {
  font-family: var(--font-num);
  font-size: 15px;
  font-weight: 700;
  color: var(--text-2);
  font-variant-numeric: tabular-nums;
}
.ph-row.is-done .ph-row-total { color: var(--c-plank); }

.ph-row-target {
  font-family: var(--font-ka);
  font-size: 10px;
  color: var(--text-3);
  margin-top: 1px;
}

/* ---------- The "done" fill (Layer 8c) ----------
   Giorgi asked for the plank card's lime fill on everything that can be
   completed on the day tab. One rule list, so a surface added later either
   joins it or is deliberately left out — the treatment cannot drift.

   Every one of these is driven by scoreCats(), so the fill means exactly "this
   earned its points" and can never disagree with the score chips. */
.habit-row.done,
.metric-pill.is-done,
.movement-item.is-done,
.plank-card.done,
.steps-card.is-done,
.weight-card.is-done {
  background: var(--lime-dim);
  border-color: rgba(201, 245, 59, 0.25);
}

/* A habit row has no background or border of its own — only a divider — so the
   fill and the lime ring both need somewhere to sit. Padding, negative margin
   and a transparent 1px border are all reserved HERE rather than on .done:
   adding them when a row is ticked would shift it sideways and grow it by 2px.
   This block sits after the Layer 2 rule and wins on source order. */
.habit-row {
  padding-left: 10px;
  padding-right: 10px;
  margin-left: -10px;
  margin-right: -10px;
  border: 1px solid transparent;
  border-bottom-color: var(--border);
  border-radius: var(--radius-sm);
  transition: background var(--dur-mid) var(--ease-out),
              border-color var(--dur-mid) var(--ease-out);
}

/* Layer 2 dropped the last divider with `border-bottom: none`, which the rule
   above re-establishes. Transparent instead, so every row keeps one height. */
.habit-row:last-child { border-bottom-color: transparent; }

/* The ring closes on all four sides — the outline is the half of this effect
   Giorgi picked out, not the fill on its own. */
.habit-row.done { border-color: rgba(201, 245, 59, 0.25); }

/* ---------- 7b. Plank card + timer (Layer 8b) ----------
   The card mirrors a habit row's anatomy — 36px icon well, text column, action
   on the right — but is a card of its own, because a timed hold is not a tick. */
.plank-card {
  display: block;   /* Layer 9: the row moved into .plank-card-row */
  transition: background var(--dur-mid) var(--ease-out),
              border-color var(--dur-mid) var(--ease-out);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.plank-card:active { transform: scale(0.99); }

.plank-card.done {
  background: var(--lime-dim);
  border-color: rgba(201, 245, 59, 0.25);
}

/* 2026-09-15 — was a 36px grey circle, which read as a little avatar beside the
   name. Now a 44px rounded square on its own purple-tinted ground, matching the
   badge idiom the score breakdown rows use. */
.plank-card-icon {
  width: 44px;
  height: 44px;
  border-radius: 12px;
  background: var(--plank-badge);
  border: 1px solid var(--plank-badge-line);
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
}

/* The header glyph and the badge glyph are the SAME png mask at two sizes, so
   they need two colours stated separately: pink-red up top, purple in the badge.
   :not(.done) rather than source order, so the lime done state still wins
   wherever these rules end up in the sheet. */
.plank-card .widget-head-icon.png-icon { background-color: var(--sc-plank); }
.plank-card:not(.done) .plank-card-icon .habit-icon.png-icon {
  background-color: var(--sc-recovery);
}

/* The badge grew a border when it became a square, so the done state has to
   carry it too — a lime well inside a purple outline read as a mistake. */
.plank-card.done .plank-card-icon {
  background: rgba(201, 245, 59, 0.15);
  border-color: rgba(201, 245, 59, 0.3);
}
.plank-card.done .habit-icon { stroke: var(--lime); }

.plank-card-text { flex: 1; min-width: 0; }

.plank-card-name {
  font-family: var(--font-ka);
  font-size: 16px;
  font-weight: 700;
  color: var(--text-1);
}

.plank-card-detail {
  font-family: var(--font-ka);
  font-size: 12px;
  color: var(--text-3);
  margin-top: 1px;
}

/* The lime pill. It overrides .btn-primary's padding with a fixed height so the
   three activity cards' actions line up; the LABEL is still written by
   renderPlankCard() (დაწყება / გაგრძელება) and must never be hardcoded here. */
.plank-card-btn {
  flex: 0 0 auto;
  height: 40px;
  padding: 0 20px;
  background: var(--sc-nutrition);
  color: var(--surface-flat);
  font-weight: 800;
  border-radius: 99px;
}

/* ---- the full-screen timer ---- */
.plank-timer-sheet {
  width: 100%;
  max-width: 480px;
  margin: 0 auto;
  background: var(--bg);
  min-height: 100dvh;
  max-height: 100dvh;      /* see .inbody-detail-sheet — without it nothing scrolls */
  display: flex;
  flex-direction: column;
  animation: sheetRise var(--dur-slow) var(--ease-out) both;
}

.plank-timer-header {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: calc(env(safe-area-inset-top) + 12px) 16px 16px;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  position: sticky;
  top: 0;
  z-index: 10;
}

.plank-timer-header .btn-icon svg {
  width: 18px;
  height: 18px;
  stroke: var(--text-1);
  fill: none;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.plank-timer-title {
  flex: 1;
  min-width: 0;
  font-family: var(--font-ka);
  font-size: 16px;
  font-weight: 700;
  color: var(--text-1);
}

.plank-timer-scroll {
  flex: 1;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: 24px var(--pad) 40px;
  display: flex;
  flex-direction: column;
  align-items: center;
}

/* .score-ring carries no CSS either — a ring is sized by its width/height
   attributes inside a positioned wrapper, and the readout sits over it. */
/* 220px, NOT 200: the dial svg is rendered at width="220", so a 200px wrapper
   put its centre 10px right of .plank-ring-center's and the readout sat up and
   left of the ring it belongs in. The two boxes must stay equal. */
.plank-ring-wrap {
  position: relative;
  width: 220px;
  height: 220px;
  flex: 0 0 auto;
}

.plank-ring-track { stroke: var(--surface-3); }

.plank-ring-fill {
  stroke: var(--teal);
  stroke-dasharray: 314; /* 2π × 50 */
  stroke-dashoffset: 314;
  transition: stroke-dashoffset var(--dur-mid) var(--ease-out),
              stroke var(--dur-mid) var(--ease-out);
  filter: drop-shadow(0 0 4px rgba(0, 221, 179, 0.4));
}

.plank-ring-fill.hit {
  stroke: var(--lime);
  filter: drop-shadow(0 0 6px var(--lime-glow));
}

@keyframes plankRingPulse {
  0%   { transform: scale(1); }
  50%  { transform: scale(1.06); }
  100% { transform: scale(1); }
}

.plank-ring-wrap.pulse { animation: plankRingPulse 300ms var(--ease-spring); }

.plank-ring-center {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
}

/* The centiseconds are balanced by an invisible twin on the left, so the big
   digits sit on the dial's centre rather than the whole string doing. .num
   already carries tabular-nums, so the ghost is exactly as wide as the live
   value at every reading — no magic offset to drift. */
.plank-timer-num {
  display: flex;
  align-items: baseline;
  justify-content: center;
  font-size: 44px;
  font-weight: 800;
  color: var(--text-1);
  line-height: 1;
}

.plank-timer-target {
  font-family: var(--font-ka);
  font-size: 12px;
  color: var(--text-3);
}

.plank-timer-total {
  font-family: var(--font-ka);
  font-size: 13px;
  color: var(--text-2);
  margin-top: 18px;
}

/* The only declaration of this class — see the note beside .plank-round-btn.
   flex-wrap survives from the three-.btn era; two circles never reach it. */
.plank-timer-btns {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 20px;
  margin-top: 22px;
  width: 100%;
}

.plank-timer-btns .btn { flex: 1 1 30%; min-width: 96px; }

.plank-timer-btns .btn:disabled {
  opacity: 0.4;
  pointer-events: none;
}

.plank-set-list {
  width: 100%;
  margin-top: 24px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.plank-set-row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 0.3em;
  font-family: var(--font-ka);
  font-size: 13px;
  color: var(--text-2);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 9px 14px;
}

/* ---------- 8. Movement summary ---------- */
.movement-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
  margin-top: 12px;
}

/* One rule for the icon/link split: --accent is a single value per card, but the
   header wants red and the ვარჯიში → link wants orange. */
.movement-card .widget-head-icon { stroke: var(--sc-plank); }
.movement-card .widget-head-action { color: var(--sc-weighin); }

.movement-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  background: var(--surface-inset);
  border: 1px solid var(--hdr-btn);
  border-radius: 10px;
  padding: 12px 8px;
  min-width: 0;
}

/* --cell-accent is written inline per cell by renderMovementSummary(), the same
   way scoreBreakdownHtml() passes --row-accent. The .is-done lime rule below is
   more specific and still wins. */
.movement-item-icon {
  width: 18px;
  height: 18px;
  stroke: var(--cell-accent, var(--text-2));
  fill: none;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.movement-item.is-done .movement-item-icon { stroke: var(--lime); }
.movement-item.is-done .movement-item-val { color: var(--lime); }

.movement-item-label {
  font-family: var(--font-ka);
  font-size: 10px;
  color: var(--text-3);
  text-align: center;
}

.movement-item-val {
  font-family: var(--font-num);
  font-size: 14px;
  font-weight: 700;
  color: var(--text-1);
  text-align: center;
  line-height: 1.3;
}

/* Georgian status text rather than a figure */
.movement-item-val.is-text {
  font-family: var(--font-ka);
  font-size: 11px;
  font-weight: 600;
}

/* ---------- 9. Coach ---------- */
/* The lime left border from .card-accent stays — that is the coach's identity,
   not a gradient. Only the diagonal wash goes. */
.coach-card {
  background: var(--surface-flat);
}

.coach-card-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 12px;
}

.coach-msg {
  font-family: var(--font-ka);
  font-size: 15px;
  color: var(--text-1);
  line-height: 1.65;
  font-weight: 400;
  transition: opacity var(--dur-mid) var(--ease-out);
}

/* 2026-09-24 — the coach's one task for today. Lime because it is the one thing
   on the card she is being asked to DO; a quiet tint, not a button, so it reads
   as part of what the cat is saying. */
.coach-task {
  margin-top: 12px;
  padding: 10px 12px 11px;
  border-radius: 12px;
  background: var(--lime-dim);
  border: 1px solid rgba(var(--lime-rgb), 0.28);
}
.coach-task[hidden] { display: none; }
.coach-task-label {
  font-family: var(--font-ka);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--lime);
  margin-bottom: 4px;
}
.coach-task-text {
  font-family: var(--font-ka);
  font-size: 14px;
  line-height: 1.55;
  font-weight: 500;
  color: var(--text-1);
}

.coach-context {
  font-family: var(--font-ka);
  font-size: 11px;
  color: var(--text-3);
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--border);
}

/* ---- the cat leans in and the line becomes a speech bubble (2026-09-22) ----
   It replaced the coach orb, which floated left of the text. Giorgi picked this
   out of the motion study: the advice should come OUT of the cat rather than sit
   in a card that looks like every other card.

   The bubble is full width with the cat under its right-hand corner, not beside
   it. Side by side, a 92px cat leaves the Georgian about 210px and the message
   runs to four lines; the tail is what carries the "she is saying this" and it
   works as well pointing down as sideways. */
.coach-body { position: relative; }

.coach-bubble {
  position: relative;
  background: var(--surface-2);
  border: 1px solid var(--border-2);
  border-radius: 18px;
  padding: 13px 15px;
}
/* The tail. A rotated square with only its two outer edges bordered, so it reads
   as part of the bubble rather than a diamond stuck to it. It sits ABOVE the
   cat's head — 34px in from the right, which is where the cat's face lands in
   the 92px window below. */
.coach-bubble::after {
  content: '';
  position: absolute;
  right: 34px;
  bottom: -7px;
  width: 13px;
  height: 13px;
  background: var(--surface-2);
  border-right: 1px solid var(--border-2);
  border-bottom: 1px solid var(--border-2);
  transform: rotate(45deg);
}

/* THE WINDOW AND THE CLIP ARE A MEASURED PAIR — see the retired .mascot-tile
   note in 08-motion.css. Within its box the drawn cat covers only ~68% of the
   width and ~41% of the height, so the clip has to be about 1.28x the window or
   the cat paints too small to read; what gets cropped is the art's own padding,
   never the cat. 92 x 1.28 = 118.

   The size lives on the BOX. A CSS transform on the svg silently loses: lottie
   writes an inline transform on every SVG it builds and that beats any rule. */
.coach-cat {
  --cat-zoom: 118px;
  --cat-shift: 0px;
  width: 92px;
  height: 92px;
  margin: 6px 0 -4px auto;     /* right-aligned under the tail */
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
}

.btn-icon-sm {
  width: 32px;
  height: 32px;
}

.btn-icon-sm svg {
  width: 16px;
  height: 16px;
  stroke: var(--text-2);
  fill: none;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* ---------- 10. Quote ---------- */
.quote-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 14px;
  gap: 10px;
}

.quote-text {
  font-family: var(--font-num);
  font-size: 15px;
  color: var(--text-1);
  line-height: 1.7;
  font-style: italic;
  font-weight: 300;
  transition: opacity 300ms var(--ease-out);
}

/* The Georgian translation must not be italic Inter. */
.quote-text.is-ka {
  font-family: var(--font-ka);
  font-style: normal;
  font-weight: 400;
  font-size: 14px;
}

.quote-author {
  font-family: var(--font-num);
  font-size: 12px;
  color: var(--text-3);
  margin-top: 10px;
}

.quote-translate-btn {
  font-family: var(--font-ka);
  font-size: 12px;
  font-weight: 500;
  color: var(--lime);
  background: var(--lime-dim);
  border: 1px solid rgba(201, 245, 59, 0.2);
  border-radius: var(--radius-pill);
  padding: 5px 12px;
  cursor: pointer;
  white-space: nowrap;
  transition: transform var(--dur-fast) var(--ease-spring);
}

.quote-translate-btn:active { transform: scale(0.96); }

/* ---------- 11. Evening CTA ---------- */
.evening-cta {
  position: fixed;
  bottom: calc(72px + env(safe-area-inset-bottom) + 12px);
  left: 50%;
  transform: translateX(-50%);
  width: calc(100% - 40px);
  max-width: 440px;
  z-index: 40;
  animation: slideUp 400ms var(--ease-spring) both;
}

.evening-cta .btn svg {
  width: 16px;
  height: 16px;
  stroke: #0A0A0C;
  fill: none;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}

@keyframes slideUp {
  from { opacity: 0; transform: translateX(-50%) translateY(20px); }
  to   { opacity: 1; transform: translateX(-50%) translateY(0); }
}

/* ---------- Bottom sheet (metric log · steps log · evening check-in) ---------- */
#sheet-backdrop {
  position: fixed;
  inset: 0;
  z-index: 150;
  background: rgba(6, 6, 8, 0.6);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  opacity: 0;
  transition: opacity var(--dur-mid) var(--ease-out);
}

#sheet-backdrop.open { opacity: 1; }
#sheet-backdrop.gone { display: none; }

#sheet {
  position: fixed;
  left: 50%;
  /* --kb is the height of the on-screen keyboard, set by the visualViewport
     watcher near INIT. iOS anchors position:fixed to the LAYOUT viewport, which
     does NOT shrink when the keyboard opens — so at bottom:0 this sheet, its
     field and its save button all sat underneath it. Measured on a 390x844
     phone: the field at y679-713 and the save button at y784-824, against a
     number pad covering everything from y553 down. She typed blind and had no
     visible way to save.
     It is the bottom property, not the transform, on purpose: the open/close spring lives
     in the transform and must not fight the keyboard tracking. */
  bottom: var(--kb, 0px);
  width: 100%;
  max-width: 480px;
  z-index: 151;
  transform: translateX(-50%) translateY(100%);
  background: var(--surface-2);
  border: 1px solid var(--border-2);
  border-bottom: none;
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  padding: 10px var(--pad) calc(var(--pad) + env(safe-area-inset-bottom));
  transition: transform var(--dur-slow) var(--ease-spring);
}

#sheet.open { transform: translateX(-50%) translateY(0); }
#sheet.gone { display: none; }

.sheet-handle {
  width: 36px;
  height: 4px;
  border-radius: var(--radius-pill);
  background: var(--surface-3);
  margin: 0 auto 16px;
}

.sheet-title {
  font-family: var(--font-ka);
  font-size: 17px;
  font-weight: 600;
  color: var(--text-1);
  margin-bottom: 14px;
}

.sheet-field {
  display: flex;
  align-items: center;
  gap: 8px;
  background: var(--surface);
  border: 1px solid var(--border-2);
  border-radius: var(--radius);
  padding: 12px 16px;
}

.sheet-input {
  flex: 1;
  min-width: 0;
  font-family: var(--font-num);
  font-size: 28px;
  font-weight: 700;
  color: var(--text-1);
  background: none;
  border: none;
  outline: none;
}

.sheet-input::placeholder { color: var(--text-3); }

.sheet-unit {
  font-family: var(--font-ka);
  font-size: 16px;
  color: var(--text-2);
  flex: 0 0 auto;
}

.sheet-label {
  font-family: var(--font-ka);
  font-size: 12px;
  font-weight: 600;
  color: var(--text-2);
  letter-spacing: 0.06em;
  margin: 16px 0 8px;
}

.sheet-label:first-of-type { margin-top: 0; }

.sheet-btns {
  display: flex;
  gap: 8px;
  margin-top: 16px;
}

.sheet-btns .btn { flex: 1; }

.sheet-quick {
  display: flex;
  gap: 8px;
  margin-top: 10px;
  flex-wrap: wrap;
}

.sheet-quick-btn {
  font-family: var(--font-num);
  font-size: 13px;
  font-weight: 600;
  color: var(--text-2);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  padding: 7px 14px;
  cursor: pointer;
  transition: transform var(--dur-fast) var(--ease-spring),
              border-color var(--dur-fast) var(--ease-out);
}

.sheet-quick-btn:active { transform: scale(0.94); border-color: var(--border-2); }

/* Mood slider (2026-09-23) — replaced the three mood buttons. JS writes --mood (the
   blended colour), --mood-pos (0–1), --brow-o and --cheek-o; everything below reads
   them, so the face, the glow, the thumb ring and the word can never disagree. */
.mood-dial {
  /* JS writes --mood-live; the unset state overrides --mood itself, which an
     inline --mood would have beaten. */
  --mood: var(--mood-live, var(--mood-mid));
  --mood-pos: 0.5;
  --thumb: 34px;
  position: relative;
  padding: 18px 16px 12px;
  border-radius: var(--radius-lg);
  background:
    radial-gradient(120% 90% at 50% 18%,
      color-mix(in srgb, var(--mood) 24%, transparent), transparent 70%),
    var(--surface);
  border: 1px solid color-mix(in srgb, var(--mood) 28%, var(--border));
  overflow: hidden;
}

.mood-face-wrap {
  width: 116px;
  height: 116px;
  margin: 0 auto;
  animation: moodFloat var(--mood-float) var(--ease-in-out) infinite;
}
.mood-face {
  width: 100%;
  height: 100%;
  overflow: visible;
  transition: transform var(--dur-mid) var(--ease-spring);
}
.mood-dial.is-dragging .mood-face { transform: scale(1.07); }

.mf-glow  { fill: var(--mood); opacity: 0.35; filter: blur(10px); }
.mf-head  { fill: var(--mood); }
.mf-shine { fill: #fff; opacity: 0.18; }
.mf-cheek { fill: #FF8FB1; opacity: calc(var(--cheek-o, 0) * 0.7); }
.mf-eye   { fill: #1A1410; }
.mf-glint { fill: #fff; }
.mf-eyes  {
  transform-box: fill-box;
  transform-origin: center;
  animation: moodBlink var(--mood-blink) var(--ease-in-out) infinite;
}
.mf-brow  {
  stroke: #1A1410;
  stroke-width: 5;
  stroke-linecap: round;
  fill: none;
  opacity: var(--brow-o, 1);
}
.mf-mouth {
  fill: #3A1A12;
  stroke: #1A1410;
  stroke-width: 4.5;
  stroke-linejoin: round;
  stroke-linecap: round;
}

/* Untouched: a grey face at the centre, and the thumb nudging to say "drag me". */
.mood-dial.is-unset { --mood: var(--text-3); border-color: var(--border); }
.mood-dial.is-unset .mood-fill { opacity: 0; }
.mood-dial.is-unset .mood-thumb { animation: moodNudge var(--mood-nudge) var(--ease-in-out) infinite; }

.mood-word {
  margin: 4px 0 14px;
  text-align: center;
  font-family: var(--font-ka);
  font-size: 20px;
  font-weight: 700;
  color: var(--mood);
  min-height: 28px;
}
.mood-word.pop { animation: moodPop var(--dur-slow) var(--ease-spring); }

.mood-track {
  position: relative;
  height: 44px;
  border-radius: var(--radius-pill);
  background: var(--surface-3);
  touch-action: none;
  cursor: pointer;
  outline: none;
}
.mood-track:focus-visible { box-shadow: 0 0 0 2px var(--border-2); }

/* The fill is the WHOLE gradient, clipped to the thumb — so the colours sit still on
   the track and the thumb reveals them, rather than the gradient squashing as it
   grows. */
.mood-fill {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: linear-gradient(90deg, var(--mood-low), var(--mood-mid) 50%, var(--mood-high));
  clip-path: inset(0 calc((1 - var(--mood-pos)) * (100% - 44px)) 0 0 round var(--radius-pill));
  box-shadow: 0 0 18px -4px var(--mood);
  transition: opacity var(--dur-mid) var(--ease-out);
}

.mood-thumb {
  position: absolute;
  top: 5px;
  left: calc(5px + var(--mood-pos) * (100% - 44px));
  width: var(--thumb);
  height: var(--thumb);
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 0 0 3px var(--mood), 0 4px 14px rgba(0, 0, 0, 0.35);
  transition: transform var(--dur-mid) var(--ease-spring);
  pointer-events: none;
}
.mood-dial.is-dragging .mood-thumb { transform: scale(1.14); }

.mood-ends {
  display: flex;
  justify-content: space-between;
  margin-top: 8px;
  padding: 0 4px;
  font-family: var(--font-ka);
  font-size: 12px;
  color: var(--text-3);
}

@keyframes moodFloat { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-4px); } }
@keyframes moodBlink { 0%, 94%, 100% { transform: scaleY(1); } 97% { transform: scaleY(0.1); } }
@keyframes moodPop   { 0% { transform: scale(0.8); opacity: 0.4; } 100% { transform: scale(1); opacity: 1; } }
@keyframes moodNudge { 0%, 100% { transform: translateX(0); } 30% { transform: translateX(-10px); } 60% { transform: translateX(10px); } }

.sheet-notes {
  width: 100%;
  min-height: 76px;
  resize: none;
  font-family: var(--font-ka);
  font-size: 14px;
  color: var(--text-1);
  background: var(--surface);
  border: 1px solid var(--border-2);
  border-radius: var(--radius);
  padding: 12px 14px;
  outline: none;
}

.sheet-notes::placeholder { color: var(--text-3); }

.sheet-stepper {
  display: flex;
  align-items: center;
  justify-content: space-between;
  background: var(--surface);
  border: 1px solid var(--border-2);
  border-radius: var(--radius);
  padding: 10px 14px;
}

.stepper-btn {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text-1);
  font-family: var(--font-num);
  font-size: 20px;
  font-weight: 600;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: transform var(--dur-fast) var(--ease-spring);
}

.stepper-btn:active { transform: scale(0.9); }

.stepper-value {
  font-family: var(--font-num);
  font-size: 26px;
  font-weight: 700;
  color: var(--text-1);
}

.stepper-unit {
  font-family: var(--font-ka);
  font-size: 14px;
  color: var(--text-2);
  margin-left: 4px;
}

/* ---------- The sleep sky (2026-09-24) ----------
   Replaced the −/+ stepper on the sleep sheet. JS writes --sky (0 sunset … 1 night),
   --sky-pos (the thumb, 0–1) and the two blended colours --sky-1 / --sky-2 on the
   card; every layer below reads them, so the sun, the moon, the stars and the ground
   can never disagree. Sunset at 3h and under, full night from 8h. */
.sleep-sky {
  --sky: 0.5;
  --sky-pos: 0.5;
  --sky-1: var(--sky-dusk-1);
  --sky-2: var(--sky-dusk-2);
  position: relative;
  height: 196px;
  border-radius: var(--radius-lg);
  overflow: hidden;
  isolation: isolate;
  background:
    /* the last light on the horizon, gone by night */
    radial-gradient(90% 60% at 50% 112%,
      rgba(255, 214, 150, calc((1 - var(--sky)) * 0.55)), rgba(255, 214, 150, 0) 70%),
    linear-gradient(168deg, var(--sky-1), var(--sky-2));
  box-shadow: 0 10px 30px -12px var(--sky-2), inset 0 1px 0 rgba(255, 255, 255, 0.14);
  transition: filter var(--dur-mid) var(--ease-out);
}
.sleep-sky > * { position: absolute; }

.ss-stars { inset: 0; opacity: clamp(0, calc((var(--sky) - 0.55) * 2.4), 1); }
.ss-stars i {
  position: absolute;
  width: var(--s);
  height: var(--s);
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 0 6px rgba(255, 255, 255, 0.8);
  animation: ssTwinkle var(--sky-twinkle) var(--ease-in-out) var(--d) infinite;
}

/* The sun sinks as the night gets longer: half on the horizon at a short night,
   under it by ~5.5h. */
.ss-sun {
  left: 50%;
  bottom: -46px;
  width: 124px;
  height: 124px;
  margin-left: -62px;
  border-radius: 50%;
  background: linear-gradient(145deg, #E8E35C, #A6E063);
  box-shadow: 0 0 40px rgba(232, 227, 92, 0.45);
  transform: translateY(calc(var(--sky) * 190px));
  opacity: clamp(0, calc(1 - var(--sky) * 1.5), 1);
}

/* The moon rises as the sun goes: peach at dusk, white and glowing at night. */
.ss-moon {
  left: 50%;
  top: 20px;
  width: 70px;
  height: 70px;
  margin-left: -35px;
  overflow: visible;
  fill: color-mix(in srgb, #FFFFFF calc(var(--sky) * 100%), #FFC49A);
  transform: translateY(calc((1 - var(--sky)) * 120px));
  opacity: clamp(0, calc((var(--sky) - 0.12) * 2.2), 1);
  filter: drop-shadow(0 0 calc(4px + var(--sky) * 14px) rgba(255, 255, 255, calc(var(--sky) * 0.75)));
  animation: ssGlow var(--sky-glow) var(--ease-in-out) infinite;
}

.ss-read {
  top: 14px;
  left: 18px;
  display: flex;
  align-items: baseline;
  gap: 5px;
  color: #fff;
  text-shadow: 0 2px 12px rgba(0, 0, 0, 0.25);
  pointer-events: none;
}
.ss-val {
  font-family: var(--font-num);
  font-size: 40px;
  font-weight: 800;
  letter-spacing: -0.03em;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
.ss-unit {
  font-family: var(--font-ka);
  font-size: 15px;
  font-weight: 600;
  opacity: 0.85;
}

/* The track is 40px tall for the finger; the bar drawn in it is 12. */
.ss-track {
  left: 14px;
  right: 14px;
  bottom: 12px;
  height: 40px;
  touch-action: none;
  cursor: pointer;
  outline: none;
}
.ss-track::before {
  content: '';
  position: absolute;
  left: 8px;
  right: 8px;
  top: 14px;
  height: 12px;
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.92);
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.18);
}
.ss-track:focus-visible::before { box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.5); }
.ss-thumb {
  position: absolute;
  top: 5px;
  left: calc(5px + var(--sky-pos) * (100% - 40px));
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: #fff;
  box-shadow: inset 0 0 0 8px #fff, inset 0 0 0 30px var(--sky-1), 0 3px 12px rgba(0, 0, 0, 0.3);
  transition: transform var(--dur-mid) var(--ease-spring);
  pointer-events: none;
}
.sleep-sky.is-dragging .ss-thumb { transform: scale(1.15); }

/* Not answered yet: a muted evening, and the thumb nudging to say "drag me". */
.sleep-sky.is-unset { filter: saturate(0.35) brightness(0.8); }
.sleep-sky.is-unset .ss-thumb { animation: moodNudge var(--mood-nudge) var(--ease-in-out) infinite; }

@keyframes ssTwinkle { 0%, 100% { opacity: 1; } 50% { opacity: 0.3; } }
@keyframes ssGlow    { 0%, 100% { scale: 1; } 50% { scale: 1.04; } }

/* 2026-09-23 — the way back to "not logged" on the sleep sheet. A quiet text link,
   not a button: clearing is the rare action and must not compete with the stepper. */
.sleep-clear {
  display: block;
  margin: 8px auto 0;
  padding: 8px 14px;
  background: none;
  border: 0;
  font-family: var(--font-ka);
  font-size: 13px;
  color: var(--text-2);
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}
.sleep-clear[hidden] { display: none; }

/* ---------- Celebration ---------- */
#celebration {
  position: fixed;
  inset: 0;
  z-index: 180;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--dur-mid) var(--ease-out);
}

#celebration.open { opacity: 1; }
#celebration.gone { display: none; }

.cel-card {
  padding: 22px 28px;
  border-radius: var(--radius-lg);
  text-align: center;
  max-width: 300px;
  transform: scale(0.82);
  opacity: 0;
  transition: transform var(--dur-slow) var(--ease-spring),
              opacity var(--dur-mid) var(--ease-out);
}

#celebration.open .cel-card { transform: scale(1); opacity: 1; }

.cel-text {
  font-family: var(--font-ka);
  font-size: 16px;
  font-weight: 600;
  color: var(--text-1);
  line-height: 1.5;
}

/* ---------- The finish moment (Layer 10) ----------
   Putki's #celebrate, rebuilt in LUMA's palette: drifting blobs behind a
   vignette, the mascot above the line, one button below. It exists because
   finishing a workout used to produce two stacked toasts that were gone
   before she had read either. */
#finish-overlay {
  position: fixed;
  inset: 0;
  z-index: 190;              /* over #celebration (180) — only one can be right */
  overflow: hidden;
  background: var(--bg);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 24px calc(var(--pad) + env(safe-area-inset-left))
           calc(28px + env(safe-area-inset-bottom))
           calc(var(--pad) + env(safe-area-inset-right));
  opacity: 0;
  transition: opacity var(--dur-mid) var(--ease-out);
}
#finish-overlay.open { opacity: 1; }
#finish-overlay.gone { display: none; }

.fin-blobs { position: absolute; inset: -10%; pointer-events: none; }

.fin-blob {
  position: absolute;
  left: var(--fx);
  top: var(--fy);
  width: var(--fs);
  height: var(--fs);
  border-radius: 50%;
  background: var(--fc);
  opacity: 0.22;
  filter: blur(50px);
  animation: finDrift var(--fdur) var(--ease-in-out) var(--fdl) infinite alternate;
}

@keyframes finDrift {
  from { transform: translate(0, 0); }
  to   { transform: translate(var(--fdx), var(--fdy)); }
}

.fin-vignette {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: radial-gradient(120% 80% at 50% 40%,
              rgba(10,10,12,0) 0%, rgba(10,10,12,0.55) 62%, rgba(10,10,12,0.9) 100%);
}

.fin-content {
  position: relative;
  text-align: center;
  max-width: 340px;
  transform: scale(0.86) translateY(10px);
  opacity: 0;
  transition: transform var(--dur-slow) var(--ease-spring),
              opacity var(--dur-mid) var(--ease-out);
}
#finish-overlay.open .fin-content { transform: scale(1) translateY(0); opacity: 1; }
/* 2026-09-23 — the kicker and the phrase arrive from opposite edges (CargoX).
   The rules match only while .open is set, so the entry replays on every finish.
   #finish-overlay clips overflow, so the travel cannot widen the page. */
#finish-overlay.open .fin-label { animation: slideInL var(--dur-slide) var(--ease-out) 180ms both; }
#finish-overlay.open .fin-msg   { animation: slideInR var(--dur-slide) var(--ease-out)
                                  calc(180ms + var(--slide-stagger)) both; }

/* The clip is 288x320 — taller than wide, so height leads and width follows. */
.fin-mascot {
  width: 180px;
  height: 200px;
  margin: 0 auto 4px;
}

.fin-label {
  font-family: var(--font-ka);
  font-size: 11px;
  font-weight: 600;
  color: var(--lime);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  margin-bottom: 10px;
}

.fin-msg {
  font-family: var(--font-ka);
  font-size: 19px;
  font-weight: 600;
  color: var(--text-1);
  line-height: 1.45;
}

.fin-stat {
  margin-top: 12px;
  font-size: 13px;
  color: var(--text-2);
}

.fin-edit { margin-top: 8px; }

.fin-foot {
  position: relative;
  width: 100%;
  max-width: 340px;
  margin-top: 26px;
  opacity: 0;
  transform: translateY(8px);
  transition: opacity var(--dur-mid) var(--ease-out) 120ms,
              transform var(--dur-mid) var(--ease-out) 120ms;
}
#finish-overlay.open .fin-foot { opacity: 1; transform: translateY(0); }
