/* LUMA - the day screen (Layer 2)
   Sheet 3 of 9. Source order decides the cascade - see the link tags in luma.html. */

/* ============================================================
   LAYER 2 — DAILY HUB
   ============================================================ */

/* Vertical rhythm for the whole day tab */
.day-stack {
  display: flex;
  flex-direction: column;
  gap: 14px;
  /* Lifts the stack above .day-aura, which paints at z-index 0 behind it. */
  position: relative;
  z-index: 1;
}

/* ---------- The header aura (2026-09-16) ----------
   A soft lime wash bleeding down from the top of the day tab, so the screen
   opens on light rather than on a flat black rectangle. It is the cheap half of
   "premium": depth costs nothing and reads immediately.

   It is ABSOLUTE AGAINST #main-content, not against #tab-day, and that is
   load-bearing. `#tab-day { position: relative }` would beat
   `.tab-view.tab-leaving { position: absolute }` on specificity (an id vs two
   classes) and bring back the bug that rule exists to fix — mid-tab-switch the
   document was BOTH tabs tall and the scrollbar jumped on every tap.
   #main-content is already `position: relative` and has no side padding, so the
   aura reaches the screen edges with no negative margins.

   It lives inside #tab-day, so it is display:none with the tab and never shows
   on the other four. It scrolls away with the page; only the bar is sticky. */
.day-aura {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 380px;
  z-index: 0;
  pointer-events: none;
  background:
    radial-gradient(118% 100% at 14% -10%, rgba(201, 245, 59, 0.11) 0%, rgba(201, 245, 59, 0) 58%),
    radial-gradient(100% 86% at 92% 0%,   rgba(0, 221, 179, 0.06) 0%,  rgba(0, 221, 179, 0) 55%);
}

/* Dropped entirely under reduced transparency: the whole point of the aura is a
   translucent wash, which is the effect that setting exists to opt out of. */
@media (prefers-reduced-transparency: reduce) {
  .day-aura { display: none; }
}

/* Leaves room for the floating evening CTA */
#tab-day.has-evening .day-stack { padding-bottom: 64px; }

/* ---------- 1. Top bar ---------- */

/* ---- The glass bar (2026-09-16) --------------------------------
   The header is a translucent layer the page scrolls UNDER, rather than an
   opaque strip that owns a fixed band of the screen. That is the single most
   recognisable iOS move and it is what the blur is for: you can see that there
   is content beneath it, which is what makes it read as a floating material
   instead of a painted rectangle.

   Three things about how it is built:

   - It is transparent-by-default in effect. At the top of the page the content
     behind it is the page background, so the blur and the tint are invisible
     and the header simply looks like part of the screen. The material only
     appears once something is actually underneath it — which is why the
     bottom edge below needs no JS to switch on.
   - The bottom edge is a GRADIENT, not a 1px divider. A hairline under a
     floating bar draws a hard line across the screen at rest; a short fade
     softens the cut exactly where content passes beneath it.
   - It breaks out of .tab-view's 20px padding with a negative margin and pays
     it back as its own padding, so the material spans the full width while the
     content inside it stays on the app's grid. */
.day-hdr {
  /* A PLAIN WRAPPER. It was sticky, with a glass material and a collapse.

     Giorgi, after living with it: 'whenever we scroll down this top header stays
     always visible, I want it to not be seen like that.' So it scrolls away with
     the page like any other content, which is what it did before.

     Everything that existed only to serve the sticky state is gone with it: the
     tint, the blur, the bottom scroll-edge, the is-stuck collapse (the shrinking
     cat, the vanishing date, the one-line greeting) and initHeaderCollapse()
     with its scroll listener. None of it has a job once the bar does not float.

     What is NOT gone, because none of it was the complaint: .day-aura, the
     edge-lit .mascot-tile and header buttons, and the greeting's tracking.

     The margin is what .day-topbar used to carry before the wrapper existed. */
  margin-bottom: 18px;
}

.day-topbar {
  display: flex;
  align-items: center;
  /* flex-end, not space-between (2026-09-22). space-between only right-aligned
     the buttons because the 56px cat tile was the first item pushing against
     them; with the cat moved to the coach card the row holds the two buttons
     ALONE, and space-between parked them hard against the left edge. */
  justify-content: flex-end;
  padding-bottom: 10px;
  padding-top: calc(env(safe-area-inset-top) + 8px);
  /* 12, not 16: the row carries three columns now (cat, greeting, two
     buttons) and 16 left the greeting under 130px at 375. */
  gap: 12px;
}

/* The other four tabs still use this as a flex child of their own header, so
   the flex properties stay. On the day tab it is now a plain block under the
   row and they simply do not apply. */
.day-topbar-left { min-width: 0; flex: 1 1 auto; }

/* ...and on the day tab it is the title block. The margin is the air between
   the button row and the greeting; without it the two read as one crowded
   stack rather than a header and a title. */
.day-hdr > .day-topbar-left { margin-top: 12px; }

.day-greeting {
  /* `anywhere` was what broke მშვიდობისა across two lines mid-word. It was
     there to survive a 108px column; the greeting has the full width now, so
     the gentler rule is enough — it breaks a word only when a single word
     genuinely cannot fit, which no Georgian greeting here does. */
  overflow-wrap: break-word;
  font-family: var(--font-ka);
  /* Was 22px, which wrapped "შუადღე მშვიდობისა, მაიკო" onto two lines and
     pushed the mascot down. With the countdown badge gone the row has the
     width for one line. */
  font-size: 18px;
  font-weight: 700;
  color: var(--text-1);
  line-height: 1.25;
}

.day-date {
  font-family: var(--font-ka);
  font-size: 13px;
  color: var(--text-2);
  margin-top: 3px;
}

.day-topbar-right {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 0 0 auto;
}

/* Scoped to .day-hdr, which is the DAY tab's header alone — the other four
   tabs use .workout-header / .settings-header and only borrow
   .day-topbar-left. An unscoped .day-greeting rule would shrink every tab
   title. It was scoped to .day-topbar until the greeting moved out of that row
   on 2026-09-16; .day-hdr is the wrapper that still contains both.
   Tracking is size-specific — a bold 18px line reads a touch tight, where the
   10px uppercase .quick-label below needs its positive 0.08em. One global
   letter-spacing would be wrong at one end or the other. */
.day-hdr .day-greeting { font-size: 18px; letter-spacing: -0.01em; }

/* The two header buttons share one look. #notif-btn keeps .btn-icon for its
   flex centring and is overridden here rather than at the source: .btn-icon is
   40px and round and is used all over the app. The descendant selector wins on
   specificity, so source order does not matter. */
.hdr-btn,
.day-topbar-right .btn-icon {
  height: 36px;
  border-radius: var(--radius-sm);
  /* The same edge-lit treatment as the cat tile, one step lighter — these are
     the interactive elements in the bar, and lighter material is what draws the
     eye to them. A translucent fill rather than a solid so they belong to the
     glass they sit on instead of punching a hole through it. */
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.09) 0%, rgba(255, 255, 255, 0.045) 100%);
  border: 1px solid var(--border-2);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.08);
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: transform var(--dur-fast) var(--ease-spring);
}
.day-topbar-right .btn-icon { width: 36px; position: relative; }
.hdr-btn:active,
.day-topbar-right .btn-icon:active { transform: scale(0.92); }

/* Signed out this is a pill led by the log-in glyph; .is-account turns it
   into a round initials badge. renderAccountRow() owns the switch.

   The mark replaced a 7px lime dot, so the padding and gap came in a little to
   pay for the extra width — the pill sits next to the bell and must not crowd
   it. */
/* ALWAYS ROUND (2026-09-22). It was a pill signed out and a round badge signed
   in; Giorgi's reference for this header is a bell and a round profile picture,
   so it is one shape in both states. Signed out it is the lime sign-in glyph
   alone — the word beside it is hidden rather than deleted, because
   renderAccountRow() writes her initial into that same span when she is signed
   in and it is the span the photo will sit behind. */
.hdr-auth {
  /* POSITION: RELATIVE IS LOAD-BEARING. Her picture inside is
     `position: absolute; inset: 0`, and without a positioned ancestor here it
     resolved against the INITIAL CONTAINING BLOCK — so choosing a photo painted
     it across the entire screen behind the day tab, and overflow:hidden below
     could not clip it either, because an absolutely-positioned box escapes the
     clipping of an unpositioned ancestor. The bell beside it has carried
     position:relative all along, for its unread dot; this one never needed it
     until there was something inside it. */
  position: relative;
  /* 36, matching .hdr-btn's own height and the bell beside it. A 40px width on a
     36px-tall button is an oval, not a circle — measured 40x36 before this. */
  width: 36px;
  padding: 0;
  gap: 0;
  border-radius: 50%;
  overflow: hidden;
}
.hdr-auth:not(.is-account) .hdr-auth-text { display: none; }
.hdr-auth-mark {
  width: 16px;
  height: 16px;
  flex: 0 0 auto;
  display: block;
  fill: none;
  stroke: var(--lime);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.hdr-auth-text {
  font-family: var(--font-ka);
  font-size: 12px;
  font-weight: 600;
  color: var(--lime);
  line-height: 1;
  white-space: nowrap;
}
.hdr-auth.is-account {
  width: 36px;
  padding: 0;
  gap: 0;
  border-radius: 50%;
}
.hdr-auth.is-account .hdr-auth-text { font-size: 13px; font-weight: 700; }

/* The unread dot on the bell. The --bg ring is what keeps it reading clear of
   the button's own border rather than merging into it. */
.day-topbar-right .btn-icon.has-nudge::after {
  content: '';
  position: absolute;
  top: -2px;
  right: -2px;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--red);
  border: 2px solid var(--bg);
}

/* ---------- The header, rebuilt on Giorgi's references (2026-09-23) ----------
   "Morning, Budi" big on the left with the date under it, the round picture on the
   right of the SAME line (and the bell beside it). Before this the two buttons sat
   alone in a row of their own and the greeting hung underneath — an empty strip
   across the top of the screen.

   space-between is correct AGAIN: the greeting occupies the left side now, so the
   warning about it parking the buttons against the left edge no longer applies.
   align-items: flex-start keeps the buttons level with the FIRST line of a greeting
   that wraps to two.

   Both buttons are 42px circles. The rule that the picture is as wide as the button
   row is tall still holds — it is simply 42 everywhere now. */
.day-hdr .day-topbar {
  justify-content: space-between;
  /* center, not flex-start (2026-09-24, Giorgi): level with the first line the
     picture floated above the name. Centred on the three-line block, the picture's
     middle lands on the big name, which is the middle line. */
  align-items: center;
  padding-top: calc(env(safe-area-inset-top) + 14px);
  padding-bottom: 0;
  gap: 14px;
}
.day-hdr .day-topbar-left { margin-top: 2px; animation: hdrIn var(--dur-slow) var(--ease-out) both; }
.day-hdr .day-greeting {
  font-size: 24px;
  line-height: 1.2;
  letter-spacing: -0.02em;
  font-weight: 700;
}
.day-hdr .day-date { font-size: 13.5px; margin-top: 5px; }
.day-hdr .dg-hello {
  display: block;
  font-size: 15px;
  font-weight: 500;
  letter-spacing: 0;
  color: var(--text-2);
}
.day-hdr .dg-name {
  display: block;
  margin-top: 2px;
  font-size: 30px;
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.15;
  color: var(--text-1);
}
@keyframes hdrIn {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}
.day-hdr .day-topbar-right { gap: 10px; }
.day-hdr .hdr-btn,
.day-hdr .day-topbar-right .btn-icon {
  width: 42px;
  height: 42px;
  border-radius: 50%;
}
/* The picture carries a thin lime ring with a soft glow — the one lit object in the
   header, because it is HER. Drawn as an outline so it sits outside the circle and
   the photo inside is not shrunk by it. */
.day-hdr .hdr-auth {
  /* 52, the bell stays 42 (2026-09-24, Giorgi): her picture is the main thing in
     the corner and at 42 a photo read as a dot beside the 30px name. Width and
     height together — any mismatch is an oval. */
  /* 56 since 2026-09-24 (52 -> 64 -> 56), Giorgi: 64 was a touch big. The bell
     is 56 too now, so the two match in height. */
  width: 56px;
  height: 56px;
  outline: 1.5px solid rgba(201, 245, 59, 0.55);
  outline-offset: 2px;
  box-shadow: 0 0 18px -4px rgba(201, 245, 59, 0.55), inset 0 1px 0 rgba(255, 255, 255, 0.08);
}
/* The bell (2026-09-24, Giorgi): the picture's height, darker, in the quote card's
   own fill so the two read as one family. Scoped to the bell: .icon-18 is shared. */
.day-hdr .day-topbar-right #notif-btn {
  width: 56px;
  height: 56px;
  background: linear-gradient(160deg, rgba(34, 36, 42, 0.92), rgba(20, 21, 26, 0.92));
}
.day-hdr #notif-btn .icon-18 {
  width: 22px;
  height: 22px;
  stroke: #FFFFFF;
  opacity: 0.85;
}
/* The date line recedes behind the name; the colour stays, only opacity drops.
   .dd-sep wraps S.dateSep in renderTopBar() for 3px more air each side. */
.day-hdr .day-date { opacity: 0.6; }
.day-hdr .dd-sep { margin: 0 3px; }
.day-hdr .hdr-auth.is-account .hdr-auth-text { font-size: 16px; }
.day-hdr .hdr-auth-mark { width: 18px; height: 18px; }

.icon-18 {
  width: 18px;
  height: 18px;
  stroke: var(--text-2);
  fill: none;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* ---------- Past-day read-only state ---------- */
.readonly-bar {
  display: none;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  background: var(--surface-2);
  border: 1px solid var(--border-2);
  border-radius: var(--radius);
  padding: 9px 12px;
  margin-bottom: 14px;
}

#tab-day.day-readonly .readonly-bar { display: flex; }

.readonly-text {
  font-family: var(--font-ka);
  font-size: 12px;
  color: var(--text-2);
}

.readonly-back {
  font-family: var(--font-ka);
  font-size: 12px;
  font-weight: 600;
  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;
}

/* Editing is off while viewing a past day. */
#tab-day.day-readonly .habit-row,
#tab-day.day-readonly .metric-pill,
#tab-day.day-readonly .steps-mode-btn,
#tab-day.day-readonly #weight-log-btn {
  pointer-events: none;
  opacity: 0.55;
}

/* ---------- 2. Score + streak hero ----------
   LAYER 9 restructured this (an explicitly requested redesign, not a refactor —
   rule 4). It was ring-left / everything-else-right, and the five breakdown
   chips wrapped one-per-line at 375px inside a 157px column, 129px tall. Now:
   the five categories are a readable list on the left, the ring and the streak
   own the right, and the progress bar spans the full card underneath both. */
.hero-card {
  display: flex;
  flex-direction: column;
  gap: 14px;
  background: linear-gradient(135deg, var(--surface) 0%, var(--surface-2) 100%);
  border: 1px solid var(--border-2);
  position: relative;
  overflow: hidden;
}

/* Layer 10 — the ring is the hero again. It was [rows | ring] since Layer 9;
   now that the ring carries the percentage itself, the rows no longer need to
   share its line and can take the full width for a bar of their own. This is
   the same explicitly-requested exception to rule 4 the Layer 9 restructure
   was — .hero-left and .hero-right are gone with the markup they styled. */
/* 2026-09-15 — two columns: the arc on the left, the streak and the day's task
   count stacked on the right. The stacked version (ring over streak over rows)
   spent a third of the card's height on one number. */
.hero-top {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 14px;
}

.hero-side {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  /* Centred, not flex-start: the column is ~169px wide beside the 120px ring
     at 375px, and a left-aligned block left the streak hanging off the ring
     with dead space to its right. */
  align-items: center;
  gap: 10px;
}

/* How many of the five sections are finished. Derived from the same
   scoreParts() object the arc and the rows read, so it cannot drift. */
.hero-tasks {
  font-family: var(--font-ka);
  font-size: 13px;
  color: var(--text-2);
  line-height: 1.25;
  text-align: center;
}

.hero-tasks .hero-tasks-n {
  font-family: var(--font-num);
  font-weight: 700;
  color: var(--lime);
  font-variant-numeric: tabular-nums;
}

/* One row per score category. 2026-09-15 — a coloured square badge on the left,
   then label + value on one line with the bar full-width beneath them. The label
   wraps, the value never does. --row-accent / --row-accent-dim are written
   inline by scoreBreakdownHtml() from the --sc-* palette. */
.score-row {
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: var(--font-ka);
  font-size: 12px;
  color: var(--text-2);
  line-height: 1.2;
}

.score-row-badge {
  flex: 0 0 auto;
  width: 34px;
  height: 34px;
  border-radius: 8px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 17px;
  line-height: 1;
  /* 2026-09-16, the one-accent pass: the badge no longer carries the section's
     own tint as a fill. Five tinted squares in a column read as five competing
     labels; a neutral chip with a tinted GLYPH reads as one list. The hairline
     is a real hairline now (white at 6%), which is also what makes the badge
     look like a surface with an edge rather than a flat colour patch. */
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(255, 255, 255, 0.06);
}

/* Earned: lime, matching the shortcut rings and the done fill. */
.score-row-good .score-row-badge {
  background: var(--lime-dim);
  border-color: rgba(201, 245, 59, 0.22);
}
.score-row-good .score-row-icon { stroke: var(--lime); }

.score-row-main {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 5px;
}

.score-row-top {
  display: flex;
  align-items: center;
  gap: 8px;
}

/* Only drawn once the section is full — the tick is the reward, so an empty
   slot held open for it would spend the meaning before she earns it. */
.score-row-check {
  flex: 0 0 auto;
  display: none;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  align-items: center;
  justify-content: center;
  font-size: 10px;
  font-weight: 800;
  line-height: 1;
  color: var(--bg-0, #0B0B0F);
  /* Lime, not the section colour: the tick means "this earned its points", which
     is one idea across the whole day tab and therefore one colour. */
  background: var(--lime);
}

.score-row-good .score-row-check { display: flex; }

/* A finished bar goes lime — one accent, same as the rings — and steps back in
   opacity so the unfinished ones still read first. The old `saturate(0.6)` went
   with it: it existed to dull a fully saturated section colour, and dulling lime
   only makes it look muddy. */
.score-row-good .score-row-bar-fill { background: var(--lime); opacity: 0.55; }

.score-row-icon {
  flex: 0 0 auto;
  width: 14px;
  height: 14px;
  fill: none;
  stroke: var(--row-accent, var(--text-3));
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
  opacity: 0.85;
}

.score-row-label { flex: 1 1 auto; min-width: 0; }

.score-row-val {
  flex: 0 0 auto;
  font-family: var(--font-num);
  font-size: 12px;
  font-weight: 600;
  color: var(--text-3);
  font-variant-numeric: tabular-nums;
}

.score-row-good .score-row-val { color: var(--lime); }
.score-row-good .score-row-label { color: var(--text-1); }
/* Half credit — steps over 70% of target, or 6h of sleep. Amber so "close but
   not full" is legible at a glance rather than reading as a plain miss. */
.score-row-part .score-row-val { color: var(--amber); }

/* The blurred lime blob in the hero's top-right corner went with the card
   gradients on 2026-09-15 — "no gradients anywhere" includes a 60px blur that
   only existed to keep the corner from reading flat. It reads flat now on
   purpose, like every other card on the screen. */

/* ---- SWITCH TO A CENTRED RING ----
   Giorgi kept the arc on the left but wants to see it before committing. Three
   properties move it to the centred, larger layout and nothing else has to
   change (the viewBox is unitless, so the geometry just scales):
     1. this width/height   148px -> 180px
     2. .hero-top           flex-direction: row -> column
     3. .hero-side          align-items: flex-start -> center
   plus width/height on the <svg> in the markup. */
.hero-score-wrap {
  position: relative;
  flex: 0 0 auto;
  width: 148px;
  height: 148px;
}

/* ONE ring, 2026-09-15 (evening). The whole history in four lines, so nobody
   flips it a fifth time without knowing what each attempt was for:
     Layer 10    — five segments.
     2026-09-08  — back to one lime arc; five identical stubs read as broken.
     2026-09-15  — segments again, a colour each, to fix that legibility problem.
     this        — one solid ring again, for WEIGHT. Not a legibility fix: the
                   segments read fine, they just read like a children's game.
   Nothing was lost dropping the colours — the five rows below carry a coloured
   badge and a coloured bar each, and the დღეს: n / 5 line counts the finished
   ones. The ring's job is the one number.

   12 units of stroke on r=49 inside a 0 0 120 120 box: outer edge 55, so it
   still fits, and at 148px it renders ~14.8px thick against the old ~9px.

   THE SIZES HERE ARE MEASURED, NOT CHOSEN. "100%" is the widest thing that
   ever sits inside the ring, and the number does not sit at the centre line —
   the label is under it — so what it has to clear is the CHORD at its own
   height, not the diameter. At 136px/r47 a 40px figure overran that chord by
   23px. 148px + r49 + a 36px figure clears it by 5px. Changing any one of the
   three means re-measuring all of them. */
/* THE CLASS NAMES MATTER AND THEY WERE WRONG (fixed 2026-09-22).

   buildScoreRing() has generated .sc-seg-track / .sc-seg-fill since the ring became
   segmented, and NEITHER had a rule anywhere in css/. The rules that were here were
   .sc-track / .sc-fill, left behind by the single-arc version, matching nothing in
   the document. Two things followed, and both are what Giorgi photographed:

     - the track got no stroke at all, so the unfilled ring was invisible and the
       coloured pieces appeared to float in the dark
     - the fill got no transition, so the dasharray written 100ms after build SNAPPED
       into place instead of growing

   If the ring is ever rebuilt again, check these names against the JS first. */
/* ---- THE GLASS RING (2026-09-23) ----
   Giorgi: "it feels so flat … even when it's empty, it should have something."
   References for the feel only — a frosted glass pill with colour drifting inside,
   and a dark ball with light sliding round its inner edge. Shape, size, the five
   colours and the one clockwise sweep (rule 5d) are all unchanged.

   Layers, back to front:
     .ring-glass      the hole inside the ring: dark glass with an inner shadow
     svg              the glass tube (.sc-seg-track), the glowing arcs, then the
                      hairlines, the reflection and the head ON TOP of the colour
     .score-center    the glassy number

   The mist and the edge light that lived inside .ring-glass were removed on
   2026-09-24: they had become the no-WebGL stand-in for the bubble, and Giorgi
   does not want a stand-in at all. No motion preference — rule 5b. */
.hero-score-wrap { --ring-p: 0; isolation: isolate; }

/* Positioned, so document order puts it ABOVE .ring-glass (an absolutely
   positioned sibling otherwise paints over an in-flow svg). overflow: visible so
   the arcs' glow is not shaved off at the svg's own box. */
.score-ring { position: relative; display: block; overflow: visible; }

/* The hole: r=43 of the 0 0 120 120 box → inset (60-43)/120 = 14.17%. */
.ring-glass {
  position: absolute;
  inset: 14.17%;
  border-radius: 50%;
  overflow: hidden;
  background:
    radial-gradient(circle at 50% 22%, rgba(255, 255, 255, 0.07), rgba(255, 255, 255, 0) 62%),
    rgba(0, 0, 0, 0.22);
  box-shadow: inset 0 0 18px rgba(0, 0, 0, 0.55),
              inset 0 1px 0 rgba(255, 255, 255, 0.06);
}

/* The empty tube: a faint see-through band rather than a flat grey. It must keep
   a real stroke — tests/ring-sweep.js asserts it, because an invisible track is
   exactly the failure that shipped on 2026-09-22. */
.sc-seg-track { stroke: rgba(255, 255, 255, 0.055); }

/* The arcs glow in their own colour (buildScoreRing() sets `color` beside the
   stroke). This reverses the 2026-09-15 "no bloom" call, on Giorgi's request for
   the glass look — the glow is what makes it light rather than paint.

   --ease-out, NOT --ease-spring. The spring curve overshoots (1.56) and a ring that
   springs past its own value and comes back is reporting a score she did not have. */
.sc-seg-fill {
  filter: drop-shadow(0 0 2.5px currentColor);
  transition: stroke-dasharray var(--dur-ring-fill) var(--ease-out),
              opacity var(--dur-mid) var(--ease-out);
}

.sc-glass-edge, .sc-glass-spec { pointer-events: none; }

/* Drawn at twelve o'clock; updateScoreRing() rotates it to the end of the sweep
   about the ring's centre (view-box units — 60,60 is the middle of 0 0 120 120). */
.sc-head {
  fill: var(--text-1);
  color: var(--lime);
  opacity: 0;
  pointer-events: none;
  transform-box: view-box;
  transform-origin: 60px 60px;
  filter: drop-shadow(0 0 2px currentColor) drop-shadow(0 0 5px currentColor);
  transition: transform var(--dur-ring-fill) var(--ease-out),
              opacity var(--dur-mid) var(--ease-out);
}

/* The glassy number: same Inter digits, lit from above like the reference's.
   background-clip is safe here — the count-up only ever writes textContent. */
.hero-score-wrap .score-num,
.hero-score-wrap .score-pct-sign {
  background: linear-gradient(180deg,
    var(--text-1) 0%, var(--text-1) 38%, #B9BEC6 64%, var(--text-1) 100%);
  -webkit-background-clip: text;
          background-clip: text;
  color: transparent;
  filter: drop-shadow(0 2px 6px rgba(0, 0, 0, 0.5));
}
.hero-score-wrap .score-pct-sign { opacity: 0.75; }
.hero-score-wrap .score-label { color: var(--text-2); opacity: 0.8; }

.score-center {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

/* The count-up writes textContent on #score-num, which would wipe any child,
   so the % is a sibling rather than nested. */
.score-value {
  display: flex;
  align-items: baseline;
  gap: 1px;
}

/* 700 rather than 800: at this size 800 reads as shouting. The negative tracking
   is what stops a big tabular figure looking loose. */
.score-num {
  font-size: 36px;
  font-weight: 700;
  color: var(--text-1);
  line-height: 1;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}

.score-pct-sign {
  font-size: 15px;
  font-weight: 600;
  color: var(--text-2);
  line-height: 1;
}

/* Georgian now, so it takes the Georgian face — rule 3. text-transform is gone:
   Georgian has no case, so it only ever did nothing here. */
.score-label {
  font-size: 10px;
  font-weight: 600;
  color: var(--text-3);
  font-family: var(--font-ka);
  letter-spacing: 0.06em;
  margin-top: 7px;
}

/* .hero-score-title went with the Layer 10 hero: the card's title is inside
   the ring now, as .score-label under the percentage. */
/* 2026-09-15: the streak used to sit on one small line under the ring — flame,
   number and a two-line label all at footnote size — and read as an afterthought
   rather than the thing to chase. It now stacks: flame + count on a line of
   their own, label underneath. The ring is untouched; the day % is still its
   own reading. */
/* The flame, the count and the label sit on one line beside the arc now. */
.hero-streak {
  display: flex;
  /* Column, not row: at 375px the side of the hero is ~169px, and with the
     label beside the number 'დღე ზედიზედ' broke across two lines mid-phrase.
     Under it, it reads on one and the whole block centres properly. */
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
}

.streak-row {
  display: flex;
  align-items: center;
  gap: 8px;
}

/* Was the 🔥 emoji at 26px. Replaced 2026-09-16 with the app's own hand-drawn
   flame: it was the last full-colour emoji left on the day tab once the score
   rows took SVG glyphs, and one emoji beside a 36px number is exactly the tell
   the rest of that pass removed. The amber is kept — this is the one warm mark
   on a lime screen and it is what makes a streak feel like a streak. */
.streak-fire {
  display: inline-flex;
  width: 26px;
  height: 26px;
  line-height: 1;
  animation: firePulse 2s ease-in-out infinite;
  filter: drop-shadow(0 2px 10px rgba(255, 138, 43, 0.45));
}
/* Filled, not stroked, and that is the whole point. The first attempt reused
   ICONS.flame — the CALORIES glyph — as a 1.7px outline, and Giorgi read it
   immediately as "that is the calories icon, not fire". An outline of a flame is
   a leaf; fire is a solid shape with a hot core, so this is a silhouette with a
   second lighter flame inside it and a base-to-tip gradient. */
.streak-fire svg {
  width: 100%;
  height: 100%;
  overflow: visible;
}
.streak-fire .fire-outer { fill: url(#fireGrad); }
/* The hot centre. Pale, slightly transparent, and it breathes on its own offset
   from the outer flame's pulse — which is what stops the mark reading as a
   sticker and starts it reading as something burning. */
.streak-fire .fire-core {
  fill: var(--fire-tip);
  opacity: 0.75;
  /* Without these the scaleY below is measured from the SVG's own origin and the
     core flies off the top of the box. Same pairing the splash mark's ring used. */
  transform-box: fill-box;
  transform-origin: center bottom;
  animation: fireCore 2s ease-in-out infinite 0.35s;
}

@keyframes fireCore {
  0%, 100% { opacity: 0.55; transform: scaleY(0.94); }
  50%      { opacity: 0.9;  transform: scaleY(1.06); }
}

@keyframes firePulse {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.15); }
}

/* inline-block so the count flip in bumpStreak() can actually translate it —
   transforms are ignored on a plain inline span. */
/* inline-block so the count flip in bumpStreak() can actually translate it —
   transforms are ignored on a plain inline span. */
.streak-num {
  display: inline-block;
  font-size: 36px;
  font-weight: 800;
  color: var(--lime);
  line-height: 1;
  letter-spacing: -0.02em;
  text-shadow: 0 0 20px rgba(201, 245, 59, 0.25);
}

.streak-label {
  font-family: var(--font-ka);
  font-size: 13px;
  color: var(--text-2);
  /* No max-width. The old 64px cap is what broke "დღე ზედიზედ" onto two lines
     beside the number; the side column has room for it on one. */
  line-height: 1.2;
  text-align: center;
}

/* The linear bar under the hero is back (2026-09-08) — same day total as the
   ring, asked for as a second, flatter reading of it. It reuses .progress-bar /
   .progress-fill-lime and carries no label: the number is already in the middle
   of the ring, and a label would be the third printing of one figure. */
.score-breakdown {
  display: flex;
  flex-direction: column;
  gap: 7px;
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px solid var(--border);
}

/* Layer 10 — each row carries its own track now that it has the full width.
   The bar is per-section (val/max), NOT a second copy of the day total: the
   ring already shows that, and two bars saying different things one above the
   other was the confusion the old linear bar created. */
.score-row-bar {
  flex: 1 1 auto;
  height: 8px;
  border-radius: var(--radius-pill);
  /* Not --surface-3 (#242430). That token is blue-leaning, and five empty bars
     of it stacked inside the hero card — which carries a warm lime-green tint —
     read as five purple lines. A plain white wash takes the card's own colour
     instead of fighting it. */
  background: rgba(255, 255, 255, 0.05);
  overflow: hidden;
}

/* display:block is load-bearing, not tidiness. This is a <span>, and an inline
   box ignores width and height outright — the row bars had silently never
   filled on any day since Layer 10, on any score. Found 2026-09-15. */
.score-row-bar-fill {
  display: block;
  height: 100%;
  width: 0;
  border-radius: inherit;
  /* Part-done keeps the section's own colour, dimmed — like the ring glyphs,
     this is the tint that still tells the five rows apart. Full-done goes lime
     in the rule further down. */
  background: var(--row-accent, var(--text-3));
  opacity: 0.45;
  transition: width var(--dur-slow) var(--ease-out);
}

.score-pill {
  font-family: var(--font-ka);
  font-size: 10px;
  font-weight: 600;
  padding: 3px 8px;
  border-radius: var(--radius-pill);
  background: var(--surface-3);
  color: var(--text-2);
  white-space: nowrap;
}

.score-pill-good { background: var(--lime-dim); color: var(--lime); }

/* Half credit — steps over 70% of target, or 6h of sleep. Amber so "close but
   not full" is legible at a glance rather than reading as a plain miss. */
.score-pill-part { background: var(--amber-dim); color: var(--amber); }

/* ---------- 3. Calendar strip ---------- */
.cal-wrap {
  margin: 0 calc(-1 * var(--pad));
  margin-top: 4px;
}

.cal-strip {
  display: flex;
  gap: 8px;
  padding: 4px var(--pad) 8px;
  overflow-x: auto;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}

.cal-strip::-webkit-scrollbar { display: none; }

/* Layer 2 fix, scoped to the day tab because .cal-strip is shared with the
   workout-history strip: overflow-x:auto makes the block axis clip too, so the
   selected day's glow and the press transform were being shaved off. */
#cal-strip {
  padding: 10px var(--pad) 12px;   /* was 4px/8px; inline stays 20px */
  gap: 4px;                        /* Layer 9: 40px cells, tighter pitch */
  min-height: 86px;                /* floor so the score arc can never be cut */
  scroll-padding-inline: var(--pad);
  -ms-overflow-style: none;        /* scrollbar-width + ::-webkit- cover the rest */
  /* 14 days never fit, so once scrolled a day is always half-cut at the edge.
     Fade the gutters instead of hard-cutting: at scrollLeft 0 the fade covers
     only the padding, so the first day still reads flush and unclipped. */
  -webkit-mask-image: linear-gradient(to right, transparent 0, #000 var(--pad),
                      #000 calc(100% - var(--pad)), transparent 100%);
  mask-image: linear-gradient(to right, transparent 0, #000 var(--pad),
              #000 calc(100% - var(--pad)), transparent 100%);
}

/* Layer 9: the strip was 52px cards each with their own surface and border —
   fourteen small panels competing with the cards above and below it. Now the
   cells are transparent and narrow, and only the selected day carries a filled
   pill, so the row reads as a date scale rather than as more furniture. */
.cal-day {
  flex: 0 0 auto;
  width: 40px;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 6px 0 6px;
  border-radius: var(--radius-pill);
  background: transparent;
  border: 1px solid transparent;
  cursor: pointer;
  transition: background-color var(--dur-mid) var(--ease-out),
              border-color var(--dur-mid) var(--ease-out),
              transform var(--dur-fast) var(--ease-spring);
  position: relative;
}

.cal-day:active { transform: scale(0.94); }

/* Today, unselected: an outline only. */
.cal-day.today { border-color: var(--border-2); }

/* The open day: a filled lime pill with dark type, as in the reference. */
.cal-day.selected {
  background: var(--lime);
  border-color: var(--lime);
}
.cal-day.selected .cal-day-dom { color: #0A0A0C; }
.cal-day.selected .cal-day-dow { color: rgba(10, 10, 12, 0.7); }
.cal-day.selected .cal-arc-track { stroke: rgba(10, 10, 12, 0.25); }
.cal-day.selected .cal-arc-fill  { stroke: #0A0A0C; }

.cal-day.future { opacity: 0.45; }

/* Past / today / future hierarchy — past days recede. */
.cal-day.past { opacity: 0.4; }
/* A tapped past day is the one open in the day view — keep it legible. */
.cal-day.past.selected { opacity: 1; }

.cal-day-arc {
  width: 28px;
  height: 14px;
  margin-top: 6px;
}

.cal-day-spacer { height: 14px; margin-top: 6px; }

.cal-arc-track { stroke: var(--surface-3); opacity: 0.7; }
.cal-arc-fill { stroke: var(--lime); transition: stroke-dasharray 400ms var(--ease-out); }

.cal-day-dow {
  font-family: var(--font-ka);
  font-size: 10px;
  font-weight: 600;
  color: var(--text-3);
  letter-spacing: 0.04em;
}

.cal-day.today .cal-day-dow { color: var(--lime); }

.cal-day-dom {
  font-family: var(--font-num);
  font-size: 15px;
  font-weight: 700;
  color: var(--text-1);
  margin-top: 2px;
  line-height: 1;
}

.cal-day.streak-member::after {
  content: '';
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  height: 2px;
  background: var(--lime);
  opacity: 0.4;
}

.cal-day.streak-start::after { left: 50%; }
.cal-day.streak-end::after   { right: 50%; }

/* ---------- 4. Weight widget ---------- */
.weight-card-top {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 12px;
}

.weight-value-row {
  display: flex;
  align-items: baseline;
  gap: 4px;
  margin-top: 4px;
  flex-wrap: wrap;
}

.weight-num {
  font-size: 36px;
  font-weight: 800;
  color: var(--text-1);
  line-height: 1;
}

.weight-unit {
  font-family: var(--font-ka);
  font-size: 14px;
  color: var(--text-2);
}

.weight-delta {
  font-size: 13px;
  font-weight: 600;
  padding: 2px 7px;
  border-radius: var(--radius-pill);
  margin-left: 4px;
}

.weight-delta.up   { color: var(--red);  background: var(--red-dim); }
.weight-delta.down { color: var(--teal); background: var(--teal-dim); }
.weight-delta.same { color: var(--text-2); background: var(--surface-3); }

.weight-avg {
  font-family: var(--font-ka);
  font-size: 11px;
  color: var(--text-3);
  margin-top: 4px;
}

.weight-right {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 8px;
  flex: 0 0 auto;
}

.weight-streak-badge {
  display: flex;
  align-items: center;
  gap: 4px;
  font-family: var(--font-ka);
  font-size: 12px;
  color: var(--text-2);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  padding: 4px 10px;
}

.weight-streak-badge .icon-14 {
  width: 14px;
  height: 14px;
  stroke: var(--text-3);
  fill: none;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.weight-form {
  margin-top: 14px;
  padding-top: 14px;
  border-top: 1px solid var(--border);
  animation: fadeIn 200ms var(--ease-out) both;
}

.weight-form.hidden { display: none; }

.weight-form-inner {
  display: flex;
  align-items: center;
  gap: 8px;
  background: var(--surface-2);
  border: 1px solid var(--border-2);
  border-radius: var(--radius);
  padding: 12px 16px;
}

.weight-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;
  width: 100%;
}

.weight-input::placeholder { color: var(--text-3); }

.weight-input-unit {
  font-family: var(--font-ka);
  font-size: 16px;
  color: var(--text-2);
}

.weight-form-btns {
  display: flex;
  gap: 8px;
  margin-top: 10px;
}

@keyframes fadeIn {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* ---------- 5. Metric pills ---------- */
.metric-pills {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
}

.metric-pill {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 12px;
  cursor: pointer;
  transition: border-color var(--dur-mid) var(--ease-out),
              transform var(--dur-fast) var(--ease-spring);
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
}

.metric-pill:active { transform: scale(0.96); }
.metric-pill:hover  { border-color: var(--border-2); }

/* The glyph is a SIBLING of the [data-s] label, never a child: applyStrings()
   assigns textContent and would wipe it at boot. Paths mirror ICONS.protein /
   .flame / .bottle so the pills speak the same language as the story rings
   directly above them. */
.metric-pill-head {
  display: flex;
  align-items: center;
  gap: 5px;
  min-width: 0;
}

.metric-pill-icon {
  flex: 0 0 auto;
  width: 13px;
  height: 13px;
  fill: none;
  stroke: var(--accent, var(--text-2));
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
  opacity: 0.85;
}

.metric-pill-label {
  font-family: var(--font-ka);
  font-size: 11px;
  font-weight: 600;
  color: var(--text-2);
  letter-spacing: 0.06em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.metric-pill-value {
  display: flex;
  align-items: baseline;
  gap: 2px;
  margin-top: 2px;
  min-width: 0;
  flex-wrap: wrap;
}

.metric-pill-value .num {
  font-size: clamp(15px, 4.6vw, 18px);
  font-weight: 700;
  color: var(--text-1);
}

.metric-pill-value .metric-pill-max {
  font-size: 12px;
  font-weight: 600;
  color: var(--text-3);
}

.metric-pill-sep {
  font-family: var(--font-num);
  font-size: 12px;
  color: var(--text-3);
  margin: 0 1px;
}

.metric-pill-unit {
  font-family: var(--font-ka);
  font-size: 11px;
  color: var(--text-2);
  margin-left: 1px;
}

.metric-pill-bar {
  height: 4px;
  margin-top: 6px;
}

/* ---------- 6. Habits ---------- */
.habits-list {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin-top: 4px;
}

.habit-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 13px 0;
  border-bottom: 1px solid var(--border);
  cursor: pointer;
  transition: opacity var(--dur-fast) var(--ease-out);
  -webkit-tap-highlight-color: transparent;
}

.habit-row:last-child { border-bottom: none; }
.habit-row:active { opacity: 0.7; }

.habit-icon-wrap {
  width: 36px;
  height: 36px;
  border-radius: 10px;
  background: var(--surface-2);
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  transition: background var(--dur-mid) var(--ease-out);
}

.habit-row.done .habit-icon-wrap { background: var(--lime-dim); }

.habit-icon {
  width: 18px;
  height: 18px;
  stroke: var(--text-2);
  fill: none;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
  transition: stroke var(--dur-mid) var(--ease-out);
}

.habit-row.done .habit-icon { stroke: var(--lime); }

/* ---------- The masked PNG glyph (2026-09-07) ----------
   svg('plank', …) returns one of these instead of an <svg>, so the bitmap still
   takes its colour from whatever rule owns the site. Each site therefore needs a
   background-color twin of its old `stroke`; the width/height it already sets
   apply to a span unchanged, so nothing moves.

   mask-size is the single number that decides how big the glyph reads. The
   artwork is a 2.13:1 solid silhouette that touches both edges of its canvas,
   where the old stroke drawing filled ~78% of the box; at `contain` its floor
   line would run a third wider than every stroke icon beside it in the score
   list and the story strip. 86% is the compromise — a filled shape carries more
   weight than a 1.5px stroke, so it holds the row at a smaller footprint. */
.png-icon {
  display: block;
  -webkit-mask: var(--plank-png) center / 86% auto no-repeat;
          mask: var(--plank-png) center / 86% auto no-repeat;
}

/* 1. the plank card header (17px) — accent purple */
.widget-head-icon.png-icon { background-color: var(--accent, var(--text-2)); }

/* 2. the plank card row (18px) — grey, lime once the hold is logged */
.habit-icon.png-icon {
  background-color: var(--text-2);
  transition: background-color var(--dur-mid) var(--ease-out);
}
.plank-card.done .habit-icon.png-icon { background-color: var(--lime); }

/* 3. the score breakdown row (14px) — per-row accent */
/* Same masked-glyph exception as the story ring: `stroke` cannot reach it, so
   the earned state has to be spelled out for background-color too. */
.score-row-icon.png-icon { background-color: var(--row-accent, var(--text-3)); }
.score-row-good .score-row-icon.png-icon { background-color: var(--lime); }
/* Inside the 34px badge the glyph IS the content, not a 14px marker beside
   text — so it takes the size the emoji had rather than .score-row-icon's. */
/* Inside the 34px badge the glyph IS the content, not a 14px marker beside text.
   0.62 rather than 1: it is a tint until the row is earned, matching the
   shortcut-ring glyphs above it. */
.score-row-badge .score-row-icon { width: 19px; height: 19px; opacity: 0.62; }
.score-row-good .score-row-badge .score-row-icon { opacity: 1; }

/* 4. the day-tab story ring (21px). The rules below are svg-typed selectors and
   would leave a span unsized, so the size lives here too. */
.story-ring-icon .png-icon {
  width: 18px;
  height: 18px;
  background-color: var(--accent);
  /* Matches the stroke glyphs' 0.5 — the plank is a MASK painted with
     background-color rather than a stroked path, so none of the `stroke` rules
     in the one-accent pass reach it and it needs saying twice. */
  opacity: 0.5;
}
.story-item.is-full .story-ring-icon .png-icon {
  background-color: var(--lime);
  opacity: 1;
}

.habit-text {
  flex: 1;
  min-width: 0;
}

.habit-name {
  font-family: var(--font-ka);
  font-size: 15px;
  font-weight: 500;
  color: var(--text-1);
  transition: color var(--dur-mid) var(--ease-out);
}

.habit-row.done .habit-name {
  color: var(--text-2);
  text-decoration: line-through;
  text-decoration-color: var(--text-3);
}

.habit-detail {
  font-family: var(--font-ka);
  font-size: 12px;
  color: var(--text-3);
  margin-top: 1px;
}

.habit-check {
  width: 26px;
  height: 26px;
  border-radius: 50%;
  border: 2px solid var(--border-2);
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  transition: background var(--dur-mid) var(--ease-spring),
              border-color var(--dur-mid) var(--ease-spring),
              box-shadow var(--dur-mid) var(--ease-spring);
}

.habit-row.done .habit-check {
  background: var(--lime);
  border-color: var(--lime);
  box-shadow: 0 0 8px var(--lime-glow);
}

.habit-check svg {
  opacity: 0;
  transform: scale(0.5);
  transition: opacity var(--dur-mid) var(--ease-spring),
              transform var(--dur-mid) var(--ease-spring);
}

.habit-row.done .habit-check svg {
  opacity: 1;
  transform: scale(1);
}

@keyframes habitFlash {
  0%   { background: var(--lime-dim); }
  100% { background: transparent; }
}

/* toggleHabit() puts .flash on a .day-row (js/04-day.js), and this rule named
   .habit-row — the quick-fill panel's class, deleted on 2026-09-21. So ticking a row
   painted nothing. Both selectors are listed rather than renamed, because habitFlash
   is still the right animation and .habit-row may yet exist somewhere. */
.habit-row.flash,
.day-row.flash { animation: habitFlash 400ms var(--ease-out); }

/* ---------- 7. Steps ---------- */
.steps-top {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
}

.steps-value-row {
  display: flex;
  align-items: baseline;
  gap: 4px;
  margin-top: 4px;
}

.steps-num {
  font-size: 42px;
  font-weight: 800;
  color: var(--text-bright);
  line-height: 1;
}

.steps-sep {
  font-family: var(--font-num);
  color: var(--text-3);
  font-size: 16px;
}

.steps-target {
  font-size: 16px;
  font-weight: 600;
  color: var(--text-2);
}

.steps-pct {
  font-family: var(--font-num);
  font-size: 12px;
  color: var(--sc-nutrition);
  margin-top: 4px;
  font-weight: 600;
}

.steps-ring-wrap {
  position: relative;
  width: 64px;
  height: 64px;
  flex: 0 0 auto;
}

.steps-ring-track { stroke: var(--surface-3); }

.steps-ring-fill {
  stroke: var(--sc-movement);
  stroke-dasharray: 251; /* 2π × 40 */
  stroke-dashoffset: 251;
  transition: stroke-dashoffset 700ms var(--ease-spring),
              stroke var(--dur-mid) var(--ease-out);
  filter: drop-shadow(0 0 4px rgba(0, 221, 179, 0.4));
}

.steps-ring-fill.goal-hit {
  stroke: var(--lime);
  filter: drop-shadow(0 0 4px rgba(201, 245, 59, 0.5));
}

.steps-ring-icon {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}

.steps-ring-icon {
  font-size: 20px;
  line-height: 1;
}

.steps-modes {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
  margin-top: 14px;
}

/* 2026-09-15 — icon ON TOP of the label in a 3-across grid, not beside it. The
   three labels are different lengths (საღამოს სეირნობა wraps to two lines) and
   side-by-side made the three buttons visibly unequal. */
.steps-mode-btn {
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  gap: 6px;
  font-family: var(--font-ka);
  font-size: 11px;
  font-weight: 500;
  color: var(--text-2);
  background: var(--surface-inset);
  border: 1px solid var(--hdr-btn);
  border-radius: 10px;
  padding: 10px 6px;
  cursor: pointer;
  transition: transform var(--dur-fast) var(--ease-spring),
              background var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out);
  white-space: normal;
  line-height: 1.25;
  text-align: center;
}

/* 2026-09-15 — the three glyphs were --text-3 hairlines on --surface-2 and were
   effectively invisible. They now get the .story-item treatment from the
   shortcuts row: the icon stroked in its own --accent at full opacity, sitting
   on a faint disc of the same colour. Same idea, no ring — these are buttons in
   a row, not progress. --accent is set per button by renderStepModes(). */
.steps-mode-ico {
  flex: 0 0 auto;
  position: relative;
  width: 24px;
  height: 24px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.steps-mode-ico::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: var(--accent, var(--text-3));
  opacity: 0.14;
}

.steps-mode-btn svg {
  position: relative;
  width: 14px;
  height: 14px;
  stroke: var(--accent, var(--text-3));
  fill: none;
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
  flex: 0 0 auto;
}

/* Pressing one lights its own colour, the way a story ring fills. */
.steps-mode-btn:active .steps-mode-ico::before { opacity: 0.26; }

.steps-mode-btn:active {
  transform: scale(0.96);
  background: var(--surface-3);
  border-color: var(--border-2);
}


/* ============================================================
   The InBody cycle row (2026-09-18)
   ============================================================
   One thin row above the shortcut strip, on screen every day, changing
   character as the test approaches. It replaces the amber banner that appeared
   for a single morning and vanished.

   --stage is the ONLY thing that differs between the five states; everything
   below reads it, so a stage is one rule rather than five copies of the row. */
.ib-row {
  display: flex;
  align-items: center;
  gap: 11px;
  width: 100%;
  padding: 9px 12px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-family: var(--font-ka);
  text-align: left;
  cursor: pointer;
  transition: background var(--dur-mid) var(--ease-out),
              border-color var(--dur-mid) var(--ease-out),
              box-shadow var(--dur-mid) var(--ease-out);
}
.ib-row:active { transform: scale(0.99); }

.ib-row-ring { position: relative; width: 30px; height: 30px; flex: 0 0 auto; }
.ib-row-ring svg { width: 100%; height: 100%; display: block; }
.ib-row-track { stroke: var(--surface-3); }
.ib-row-fill  { stroke: var(--stage); }
.ib-row-over  { stroke: var(--red); }

.ib-row-n {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 10px;
  font-weight: 700;
  color: var(--text-1);
}

.ib-row-mid { min-width: 0; display: block; }
.ib-row-kicker {
  display: block;
  font-family: var(--font-num);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--stage);
  opacity: 0.9;
}
.ib-row-date {
  display: block;
  font-size: 13px;
  font-weight: 600;
  color: var(--text-1);
  margin-top: 1px;
}

.ib-row-right {
  margin-left: auto;
  text-align: right;
  font-size: 11px;
  line-height: 1.35;
  color: var(--text-2);
}
.ib-row-right .num { font-weight: 700; color: var(--stage); }

/* ---- 1. sleeping — present, asking for nothing ----
   Lifted a notch from the first preview on Giorgi's call: at the preview's
   value the ring all but disappeared against the card on a dark screen, and the
   whole point of this stage is that the date is still readable. */
.ib-row.ib-sleeping { --stage: var(--ib-sleeping); }
.ib-row.ib-sleeping .ib-row-kicker { color: var(--text-3); }

/* ---- 2. waking — the same row, with its colour ---- */
.ib-row.ib-waking {
  --stage: var(--teal);
  border-color: rgba(0, 221, 179, 0.18);
}

/* ---- 3. charging — the last week before the test ---- */
.ib-row.ib-charging {
  --stage: var(--lime);
  border-color: rgba(201, 245, 59, 0.28);
  background: linear-gradient(180deg, var(--lime-dim), transparent 70%), var(--surface);
  animation: ibRowPulse var(--ib-pulse) var(--ease-in-out) infinite;
}
@keyframes ibRowPulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(201, 245, 59, 0); }
  50%      { box-shadow: 0 0 18px 0 rgba(201, 245, 59, 0.15); }
}

/* ---- 4. ready — and it STAYS until she uploads ---- */
.ib-row.ib-ready {
  --stage: var(--amber);
  border-color: rgba(232, 134, 58, 0.40);
  background: linear-gradient(180deg, var(--amber-dim), transparent 70%), var(--surface);
}

/* ---- 5. overdue — the ring overfills, and the app says so ---- */
.ib-row.ib-overdue {
  --stage: var(--amber);
  border-color: rgba(224, 85, 85, 0.38);
  background: linear-gradient(180deg, rgba(224, 85, 85, 0.10), transparent 70%), var(--surface);
}
.ib-row.ib-overdue .ib-row-right .num { color: var(--red); }

/* ============================================================
   THE DAY ROWS (2026-09-21)

   Eight thick rows replacing six cards. The row IS the bar: 58px tall with the
   section's colour sweeping in from the left, so progress is the first thing she
   sees rather than a 3px line under the text.

   THE FILL IS NEON, AND THAT IS THREE THINGS, NOT ONE. A high alpha alone reads as
   a tinted wash — the first mockup ran the colour at 19%→40% over the dark row and
   the only bright thing on it was the leading edge. Neon needs: real luminance in
   the body, a glow BLEEDING PAST the fill's edge, and the edge itself staying the
   brightest point so the bar still reads as moving.

   Check it at 25% / 60% / 100%. A neon tuned at full disappears at a quarter, and
   the white number sits ON the fill at every one of those.
   ============================================================ */
/* NO BOX (2026-09-22). Giorgi: "all these habits are inside one big segment... they
   should be separated from each other." The element stays — day-rows.js measures it,
   day-wiring.js kills it, and animateCards() gives it the staggered entry — but it
   paints nothing, so each row reads as its own object on the page.

   The selector repeats .card and the attribute: .card[data-accent] in 04-widgets.css
   is later AND more specific. Its 18px is also why the 14px padding that used to be
   on .day-rows-card never applied to anything.

   overflow: visible is deliberate. .card clips, and each row's fill carries a 22px
   accent glow that was being shaved off at the card's edge. */
.card.day-rows-card[data-accent] {
  background: none;
  border-color: transparent;
  border-radius: 0;
  padding: 0;
  overflow: visible;
}
#day-rows { display: flex; flex-direction: column; gap: 10px; }

.day-row {
  position: relative;
  display: flex;
  align-items: center;
  gap: 10px;
  /* 78, was 68, was 58. 2026-09-23, Giorgi: "each bar should be a little bit bigger,
     right now it's too small for the fingers". The row was never the problem as a
     whole — the TICK was 30px, well under the ~44px a fingertip needs. Everything
     inside grew with it so the proportions hold. */
  min-height: 78px;
  padding: 0 10px 0 10px;
  border-radius: 20px;
  background: var(--surface-inset);
  border: 1px solid transparent;
  overflow: hidden;
  transition: border-color var(--dur-mid) var(--ease-out),
              background var(--dur-mid) var(--ease-out);
}

/* The fill sits BEHIND the content as an absolute sibling, not as a flex item — a
   fill in the flow is an 81%-wide box that shoves the emoji and the label into the
   remaining fifth, one character per line. */
.day-row-fill {
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 0;
  border-radius: 20px 0 0 20px;   /* must track .day-row's radius */
  pointer-events: none;
  background: linear-gradient(90deg,
              color-mix(in srgb, var(--accent) 26%, transparent),
              color-mix(in srgb, var(--accent) 58%, transparent));
  box-shadow: 0 0 22px -2px var(--accent);
  transition: width var(--dur-slow) var(--ease-spring);
}
/* The wavefront. 3px of the pure colour with a glow behind it, so the bar has a head
   rather than just stopping — that is what makes a part-filled row read as moving. */
.day-row-fill::after {
  content: '';
  position: absolute;
  right: 0; top: 0; bottom: 0;
  width: 3px;
  background: var(--accent);
  box-shadow: 0 0 14px 2px var(--accent);
}
/* A zero-width fill still paints its edge and its glow, which put a coloured smudge
   down the left of every untouched row. */
.day-row.is-empty .day-row-fill::after,
.day-row.is-done  .day-row-fill::after { opacity: 0; }

/* The whole row except the tick is one button, so the tap target is the row. */
.day-row-hit {
  position: relative;
  z-index: 1;
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 0;
  background: none;
  border: 0;
  text-align: left;
  cursor: pointer;
  font-family: inherit;
  color: inherit;
}

.day-row-well {
  width: 46px; height: 46px;
  flex: 0 0 auto;
  border-radius: 14px;
  display: grid;
  place-items: center;
  background: color-mix(in srgb, var(--accent) 17%, transparent);
  transition: background var(--dur-mid) var(--ease-out);
}
/* 26px on a 64px source is ~2.5x on a 3x phone — the published ceiling holds. */
.day-row-emoji { width: 26px; height: 26px; display: block; }
/* The plank has no emoji — it wears Giorgi's own drawing, masked so it can take the
   accent. A mask is painted with background-color, so none of the colour rules that
   reach an <img> reach this one. */
.day-row-emoji.png-icon {
  background-color: var(--accent);
  -webkit-mask: var(--plank-png) center / contain no-repeat;
          mask: var(--plank-png) center / contain no-repeat;
}

.day-row-text { flex: 1; min-width: 0; }
.day-row-name {
  display: block;
  font-size: 15.5px;
  font-weight: 500;
  color: var(--text-1);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* White, not --text-3: the number sits ON the fill, and --text-3 is tuned for the
   dark row behind it. */
.day-row-meta {
  display: block;
  margin-top: 3px;
  font-size: 12.5px;
  color: rgba(255, 255, 255, 0.66);
  white-space: nowrap;
}
.day-row.is-empty .day-row-meta { color: var(--text-3); }

/* 40px drawn, 48px to the finger: the ::before below extends the hit area without
   making the circle look heavier. */
.day-row-tick {
  position: relative;
  z-index: 1;
  width: 40px; height: 40px;
  flex: 0 0 auto;
  padding: 0;
  border-radius: 50%;
  border: 1.7px solid rgba(255, 255, 255, 0.17);
  background: transparent;
  display: grid;
  place-items: center;
  cursor: pointer;
  transition: background var(--dur-mid) var(--ease-spring),
              border-color var(--dur-mid) var(--ease-out),
              transform var(--dur-mid) var(--ease-spring);
}
.day-row-tick::before {
  content: '';
  position: absolute;
  inset: -4px;
  border-radius: 50%;
}
.day-row-tick svg {
  width: 18px; height: 18px;
  fill: none;
  stroke: #0A0A0C;
  stroke-width: 3.4;
  stroke-linecap: round;
  stroke-linejoin: round;
  opacity: 0;
  transform: scale(0.5);
  transition: opacity var(--dur-fast), transform var(--dur-mid) var(--ease-spring);
}
.day-row-tick:active { transform: scale(0.88); }
.day-row-chev {
  position: relative;
  z-index: 1;
  width: 40px;
  flex: 0 0 auto;
  text-align: center;
  font-family: var(--font-num);
  font-size: 17px;
  color: var(--text-3);
}

/* Done. The row keeps its OWN colour rather than going lime, because an emoji cannot
   go lime — it carries its own colours and ignores everything the card does. So the
   earned signal lives in the tick, the full fill and the lime rim. */
.day-row.is-done { border-color: rgba(201, 245, 59, 0.26); }
.day-row.is-done .day-row-tick { background: var(--lime); border-color: var(--lime); }
.day-row.is-done .day-row-tick svg { opacity: 1; transform: scale(1); }
.day-row.is-done .day-row-name { color: #fff; }
.day-row.is-done .day-row-meta { color: rgba(255, 255, 255, 0.82); }
.day-row.is-done .day-row-well { background: rgba(255, 255, 255, 0.14); }

/* A rest day. Training is worth 0 points, so the row states that and steps out of the
   way — no fill it can never earn, no tick it can never tick. */
.day-row.is-rest .day-row-well { background: rgba(255, 255, 255, 0.05); }
.day-row.is-rest .day-row-emoji { opacity: 0.45; }
.day-row.is-rest .day-row-name { color: var(--text-2); }

.day-row.is-readonly .day-row-hit,
.day-row.is-readonly .day-row-tick { pointer-events: none; }
.day-row.is-readonly .day-row-tick { opacity: 0.45; }

/* ---- the day's line (2026-09-21) ----
   The quote, moved up from a card near the bottom and slimmed to one line. It is quiet
   on purpose: she reads it once and moves on, and it must not compete with the box
   under it. The whole line is the EN/KA toggle — tapping it swaps the language, which
   is what the separate button used to do. */
.quote-line {
  display: block;
  width: 100%;
  padding: 2px 2px 14px;
  background: none;
  border: 0;
  text-align: left;
  cursor: pointer;
  font-family: inherit;
  color: inherit;
}
.quote-line .quote-text {
  display: block;
  font-family: var(--font-num);
  font-size: 13px;
  line-height: 1.55;
  color: var(--text-2);
}
.quote-line .quote-text.is-ka { font-family: var(--font-ka); font-size: 12.5px; }
.quote-line .quote-author {
  display: block;
  margin-top: 3px;
  font-family: var(--font-num);
  font-size: 11.5px;
  color: var(--text-3);
}

/* ---- the quote gets its own box again (2026-09-23) ----
   Giorgi's reference is an "AI Recommendation" card: a small glowing badge and a
   kicker on one line, the text under it, on frosted glass. The rules above stay as
   the base; .quote-box is what turns the bare line into the card.

   The border is a GRADIENT, drawn with the two-background trick (padding-box fill
   over a border-box gradient) because border-image cannot take a radius. It runs
   lime → teal → nothing, so the card has a lit edge top-left and fades out, the way
   a real glass edge catches one light. */
.quote-box {
  position: relative;
  overflow: hidden;
  padding: 14px 16px 15px;
  border-radius: 20px;
  border: 1px solid transparent;
  background:
    linear-gradient(160deg, rgba(34, 36, 42, 0.92), rgba(20, 21, 26, 0.92)) padding-box,
    linear-gradient(135deg, rgba(201, 245, 59, 0.55), rgba(0, 221, 179, 0.28) 45%,
                    rgba(255, 255, 255, 0.06) 80%) border-box;
  box-shadow: 0 10px 30px -14px rgba(201, 245, 59, 0.28),
              inset 0 1px 0 rgba(255, 255, 255, 0.05);
  -webkit-backdrop-filter: blur(14px);
          backdrop-filter: blur(14px);
  animation: quoteIn var(--dur-slow) var(--ease-out) both;
  transition: transform var(--dur-fast) var(--ease-spring);
}
.quote-box:active { transform: scale(0.985); }
/* A slow lime glow in the top-left corner, behind everything. It drifts a few px so
   the card feels lit rather than printed. */
.quote-box::before {
  content: '';
  position: absolute;
  width: 170px; height: 170px;
  left: -60px; top: -90px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(201, 245, 59, 0.20), rgba(201, 245, 59, 0) 70%);
  pointer-events: none;
  animation: quoteGlow 7s var(--ease-in-out) infinite alternate;
}
@keyframes quoteIn {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: translateY(0); }
}
@keyframes quoteGlow {
  from { transform: translate(0, 0) scale(1); }
  to   { transform: translate(26px, 10px) scale(1.15); }
}

.quote-kicker {
  position: relative;
  display: flex;
  align-items: center;
  gap: 9px;
  margin-bottom: 9px;
}
.quote-badge {
  width: 26px; height: 26px;
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  border-radius: 9px;
  background: linear-gradient(145deg, rgba(201, 245, 59, 0.26), rgba(0, 221, 179, 0.16));
  box-shadow: 0 0 14px -2px rgba(201, 245, 59, 0.45);
}
.quote-badge svg { width: 16px; height: 16px; fill: var(--lime); overflow: visible; }
.quote-badge .qb-small {
  fill: var(--teal);
  transform-origin: 19px 18.5px;
  animation: quoteTwinkle 2.6s var(--ease-in-out) infinite;
}
@keyframes quoteTwinkle {
  0%, 100% { opacity: 0.35; transform: scale(0.7); }
  50%      { opacity: 1;    transform: scale(1.1); }
}
.quote-kicker-text {
  font-family: var(--font-ka);
  font-size: 13px;
  font-weight: 600;
  color: var(--text-1);
}
.quote-box .quote-text {
  position: relative;
  font-size: 14.5px;
  line-height: 1.55;
  font-style: italic;
  color: rgba(242, 242, 245, 0.86);
}
.quote-box .quote-text.is-ka { font-style: normal; font-size: 14px; }
.quote-box .quote-author { position: relative; margin-top: 7px; }

/* ---------- Where the fortnight sits (2026-09-22) ----------
   It is a BARE .cal-wrap in .day-stack, above the hero card — no card of its own and
   no wrapper. It spent 2026-09-21 inside the hero card and about an hour in a card of
   its own; Giorgi sent both back. The only thing 2026-09-22 changed is the order: the
   days come before the ring instead of after it.

   So there is deliberately NO rule here. .cal-wrap's `margin: 0 calc(-1 * var(--pad))`
   takes it to the screen edges and #cal-strip carries its own inset. If you find
   yourself adding .cal-card back, read this first. */

/* ---------- The plank timer, inline in its row (2026-09-22) ----------
   plankInlineHtml() has emitted .plank-inline and five .pi-* classes since the timer
   moved into the row, and NOT ONE of them had a rule. The panel rendered as raw text
   over the page with two white pills: `.btn` sets `border: none` and a pill radius but
   NO background, so an unstyled .btn falls through to the browser's default ButtonFace.
   That is what Giorgi photographed.

   It is a sibling of .day-row, not a child — the row is `overflow: hidden` and would
   clip it — so it picks up #day-rows' 10px gap for free and needs no margin. */
.plank-inline {
  background: var(--plank-badge);
  border: 1px solid var(--plank-badge-line);
  border-radius: 17px;
  padding: 16px 14px 14px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  animation: fadeIn var(--dur-mid) var(--ease-out);
}

/* One numSpan, one line. The centiseconds are a separate span for WEIGHT only and
   carry no gap — the same rule the full-screen timer's clock follows, because a
   flex gap between the digits and the hundredths reads as a broken number. */
.pi-clock {
  font-family: var(--font-num);
  font-variant-numeric: tabular-nums;   /* or the whole clock shifts every tick */
  line-height: 1;
  color: var(--text-bright);
}
.pi-main { font-size: 38px; font-weight: 700; letter-spacing: -0.02em; }
.pi-cs   { font-size: 20px; font-weight: 600; color: var(--c-plank); }

.pi-total {
  font-family: var(--font-ka);
  font-size: 12px;
  color: var(--text-2);
}
.pi-total .num { font-family: var(--font-num); font-weight: 600; color: var(--text-1); }

.pi-btns { display: flex; align-items: center; gap: 8px; }

/* Start/resume is the primary action and takes the lime; save is the quiet one.
   Both repeat .btn so they cannot lose to it on source order. */
.btn.pi-go {
  background: var(--c-plank);
  color: #140D22;
  padding: 10px 22px;
}
.btn.pi-go:active { box-shadow: 0 0 18px rgba(167, 139, 250, 0.35); }

.btn.pi-save {
  background: transparent;
  color: var(--text-1);
  border: 1px solid var(--border-2);
  padding: 9px 20px;
}
.btn.pi-save:active { background: var(--surface-2); }

/* The door to the full-screen timer. A glyph, not a word, so it needs no string. */
.pi-full {
  width: 36px;
  height: 36px;
  flex: 0 0 36px;
  border-radius: 50%;
  background: transparent;
  border: 1px solid var(--border-2);
  color: var(--text-2);
  font-size: 15px;
  line-height: 1;
  cursor: pointer;
  display: grid;
  place-items: center;
  transition: transform var(--dur-fast) var(--ease-spring),
              background var(--dur-fast) var(--ease-out);
  -webkit-tap-highlight-color: transparent;
}
.pi-full:active { transform: scale(0.9); background: var(--surface-2); }

/* ---------- Swipe a row to finish it (2026-09-22) ----------
   The CONTENT slides, the fill does not. The fill is the progress bar and sliding it
   with the label would read as the bar itself moving. Only .day-row-hit carries the
   transform, driven by one custom property that wireRowSwipe() writes in px.

   The tick is NOT translated, deliberately: .day-row-tick:active already owns
   `transform: scale(0.88)` and the two would fight. It steps aside instead, which
   also stops two ticks being on screen at once mid-gesture. */
.day-row-hit {
  transform: translateX(var(--swipe, 0px));
  transition: transform 320ms var(--ease-spring);
}
/* While the finger is down there is no transition — the row has to track it exactly.
   Releasing removes .is-swiping, and the spring above carries it home. */
.day-row.is-swiping .day-row-hit { transition: none; }
.day-row.is-swiping .day-row-tick,
.day-row.is-swiping .day-row-chev { opacity: 0; }

/* The pad sits in the space the content vacates, so its SIDE follows the direction:
   an unfinished row slides right and uncovers the left; a finished one slides left
   and uncovers the right. Both come off .is-done, which is also what wireRowSwipe()
   reads for the direction — one source, so they cannot disagree. */
.day-row-pad {
  position: absolute;
  top: 50%;
  left: 12px;
  width: 40px;
  height: 40px;
  border-radius: 13px;
  display: grid;
  place-items: center;
  background: var(--lime);
  opacity: 0;
  transform: translateY(-50%) scale(0.78);
  pointer-events: none;
  transition: opacity var(--dur-fast) var(--ease-out),
              transform var(--dur-mid) var(--ease-spring);
}
.day-row.is-done .day-row-pad,
.day-row.is-clearable .day-row-pad {
  left: auto;
  right: 12px;
  /* Undoing is not an achievement, so it does not get the lime. */
  background: var(--surface-2);
  border: 1px solid var(--border-2);
}
.day-row.is-swiping .day-row-pad { opacity: 1; transform: translateY(-50%) scale(1); }

.day-row-pad svg {
  width: 21px;
  height: 21px;
  fill: none;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.pad-tick { stroke: #0A0A0C; stroke-width: 2.9; }
.pad-undo { stroke: var(--text-1); stroke-width: 2.1; }
/* One pad, two glyphs, swapped by the same class that picks the side. */
.day-row-pad .pad-undo { display: none; }
.day-row.is-done .day-row-pad .pad-tick,
.day-row.is-clearable .day-row-pad .pad-tick { display: none; }
.day-row.is-done .day-row-pad .pad-undo,
.day-row.is-clearable .day-row-pad .pad-undo { display: block; }

/* ---------- The reward (2026-09-22) ----------
   rewardDiff() has added .just-done to a finished row for 900ms since the eight-row
   screen shipped, and the class had NO RULE — so the one moment the whole screen is
   built around painted nothing at all. The chime and the vibration were firing into
   silence on iOS, which has no Vibration API: the motion is the entire reward there.

   The row swells by 2.5% and no more. It is 68px tall and full width; anything
   larger reads as the layout breaking rather than as a cheer. */
@keyframes rowJustDone {
  0%   { transform: scale(1); }
  28%  { transform: scale(1.025); }
  100% { transform: scale(1); }
}
@keyframes rowJustDoneGlow {
  0%   { box-shadow: 0 0 0 0 var(--accent); }
  30%  { box-shadow: 0 0 22px 2px var(--accent); }
  100% { box-shadow: 0 0 0 0 rgba(0, 0, 0, 0); }
}
.day-row.just-done {
  animation: rowJustDone 620ms var(--ease-spring),
             rowJustDoneGlow 900ms var(--ease-out);
}
/* The tick lands last and overshoots — it is the thing she was aiming at. */
@keyframes tickLand {
  0%   { transform: scale(0.6); }
  55%  { transform: scale(1.22); }
  100% { transform: scale(1); }
}
.day-row.just-done .day-row-tick { animation: tickLand 520ms var(--ease-spring) 60ms; }

/* The particles. Fixed, not absolute: burstFromRow() positions the host from a
   viewport rect, and #day-rows-card is no longer a painted box to sit inside. */
.row-burst {
  position: fixed;
  width: 0;
  height: 0;
  z-index: 170;          /* over the rows, under the sheets (150/151) is not enough
                            — a burst can fire while the plank panel is open */
  pointer-events: none;
}
.row-burst i {
  position: absolute;
  left: 0;
  top: 0;
  width: 7px;
  height: 7px;
  border-radius: 2px;
  opacity: 0;
}

/* ============================================================
   HABITS OF HER OWN + PER-ROW STREAKS (2026-09-23)
   Her rows are .day-row with .is-custom and an inline --accent, so every existing
   rule — the fill, the neon edge, the tick, the swipe pad, the reward — applies to
   them unchanged. Only what is new is styled here.
   ============================================================ */

/* The "⚡ 3 days" line under a row's detail. Amber, not lime: lime means earned
   today; a streak is a different fact and must not read as a second tick. */
.row-streak {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  margin-left: 8px;
  padding: 1px 7px 1px 5px;
  border-radius: 999px;
  background: rgba(232, 134, 58, 0.14);
  font-family: var(--font-ka);
  font-size: 11px;
  font-weight: 600;
  color: #FFB36B;
  vertical-align: 1px;
}
.row-streak svg { width: 11px; height: 11px; fill: #FFB36B; }
.row-streak .num { font-family: var(--font-num); }

/* The quiet door at the end of the list, for when she scrolls to the bottom. */
.add-habit-row {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  min-height: 64px;
  border-radius: 20px;
  border: 1.5px dashed rgba(255, 255, 255, 0.14);
  background: transparent;
  font-family: var(--font-ka);
  font-size: 14px;
  font-weight: 500;
  color: var(--text-2);
  cursor: pointer;
  transition: transform var(--dur-fast) var(--ease-spring),
              border-color var(--dur-mid) var(--ease-out);
}
.add-habit-row:active { transform: scale(0.98); border-color: rgba(201, 245, 59, 0.5); }
.add-habit-plus {
  width: 28px; height: 28px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: rgba(201, 245, 59, 0.14);
  color: var(--lime);
  font-family: var(--font-num);
  font-size: 18px;
  font-weight: 600;
  line-height: 1;
}

/* The floating plus, Giorgi's reference. Inside the 480px column on a desktop,
   above the tab bar on a phone. */
.add-habit-fab {
  position: fixed;
  z-index: 45;
  right: max(18px, calc((100vw - 480px) / 2 + 18px));
  bottom: calc(env(safe-area-inset-bottom) + 86px);
  width: 58px; height: 58px;
  border-radius: 50%;
  border: 0;
  display: grid;
  place-items: center;
  background: radial-gradient(circle at 35% 30%, #E4FF7A, var(--lime) 55%, #A9D11E);
  box-shadow: 0 10px 26px -6px rgba(201, 245, 59, 0.6), 0 2px 6px rgba(0, 0, 0, 0.4),
              inset 0 1px 0 rgba(255, 255, 255, 0.5);
  cursor: pointer;
  animation: fabIn 600ms var(--ease-spring) 400ms both;
  transition: transform var(--dur-fast) var(--ease-spring);
}
.add-habit-fab svg { width: 26px; height: 26px; fill: none; stroke: #0A0A0C; stroke-width: 2.6;
  stroke-linecap: round; transition: transform var(--dur-mid) var(--ease-spring); }
.add-habit-fab:active { transform: scale(0.9); }
.add-habit-fab:active svg { transform: rotate(90deg); }
@keyframes fabIn {
  from { transform: scale(0) rotate(-90deg); }
  to   { transform: scale(1) rotate(0); }
}
html[data-tab]:not([data-tab="day"]) .add-habit-fab { display: none; }
/* The last row must be reachable past the button. */
#tab-day .day-stack { padding-bottom: 72px; }

/* ---- the creator sheet ---- */
.ch-icons {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 8px;
}
.ch-icon {
  aspect-ratio: 1;
  border-radius: 14px;
  border: 1.5px solid transparent;
  background: var(--surface-2);
  display: grid;
  place-items: center;
  cursor: pointer;
  transition: transform var(--dur-fast) var(--ease-spring),
              border-color var(--dur-mid) var(--ease-out),
              background var(--dur-mid) var(--ease-out);
}
.ch-icon img { width: 28px; height: 28px; }
.ch-icon:active { transform: scale(0.9); }
.ch-icon.is-on {
  border-color: var(--lime);
  background: rgba(201, 245, 59, 0.12);
  box-shadow: 0 0 14px -4px rgba(201, 245, 59, 0.7);
  animation: chPick 360ms var(--ease-spring);
}
@keyframes chPick {
  0% { transform: scale(0.85); }
  60% { transform: scale(1.08); }
  100% { transform: scale(1); }
}
/* 2026-09-24 — repeats .sheet-notes on purpose. .sheet-notes' min-height: 76px is in
   04-widgets.css, LATER than this file, so a bare .ch-name lost and the three
   fields stood 76px tall (Giorgi: "shrink them a little"). */
.sheet-notes.ch-name, .sheet-notes.ch-goal, .sheet-notes.ch-unit {
  min-height: 0;
  height: 44px;
  padding: 10px 14px;
  resize: none;
}
.ch-seg {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 4px;
  padding: 4px;
  border-radius: 14px;
  background: var(--surface-2);
}
.ch-seg button {
  min-height: 42px;
  border: 0;
  border-radius: 11px;
  background: transparent;
  font-family: var(--font-ka);
  font-size: 13.5px;
  color: var(--text-2);
  cursor: pointer;
  transition: background var(--dur-mid) var(--ease-out), color var(--dur-mid) var(--ease-out);
}
.ch-seg button.is-on { background: var(--surface-3); color: var(--text-1); font-weight: 600; }
.ch-count { display: grid; grid-template-columns: 96px 1fr; gap: 8px; margin-top: 10px; }
.ch-count[hidden] { display: none; }
.ch-delete { color: var(--red); margin-top: 14px; }

/* ---------- The score card, redesigned (2026-09-24) ----------
   Giorgi: the card "doesn't have some kind of clear visual sense of the main
   thing". References: Samantha's music app (one bold card with a clear hero and
   a round play button) and a glass bubble with a prism streak through it. His
   four calls: the bubble big and centred; rainbow, from his photo; it wakes up as
   she scores; the card in the quote card's family — lit edge, frosted fill, glow.

   Layers, back to front:
     .hero-card::before   a slow colour cloud behind the bubble (orange/blue/violet)
     .ring-glass          the dark hole, the bubble's ground (no stand-in bubble any more)
     .score-orb           the living bubble (js/14-score-orb.js), rebuilt after a lost context
     svg                  the ring — thinner and further out now, r=51 stroke 7
     .score-center        the number
     .hero-side           two glass chips: the streak and the day's tasks
     .hero-more           the round lime arrow, top right (#score-more-btn)

   Every override here repeats .card AND the attribute: .card[data-accent] and
   .card-lg[data-accent] in 04-widgets.css are later AND more specific, and a bare
   .hero-card rule silently loses to them (the #cal-card trap). */
.card.hero-card[data-accent] {
  gap: 16px;
  padding: 22px 16px 18px;
  border-radius: 26px;
  border: 1px solid transparent;
  background:
    linear-gradient(170deg, rgba(28, 29, 36, 0.94), rgba(15, 16, 21, 0.96)) padding-box,
    linear-gradient(140deg, rgba(var(--hg-orange), 0.55), rgba(var(--hg-blue), 0.32) 42%,
                    rgba(var(--hg-violet), 0.20) 68%, rgba(255, 255, 255, 0.05) 88%) border-box;
  box-shadow: 0 16px 40px -20px rgba(var(--hg-violet), 0.55),
              inset 0 1px 0 rgba(255, 255, 255, 0.06);
}

/* The colour cloud. Three soft spots in one box, turned slowly — turning a
   lop-sided gradient reads as the colours drifting round the bubble, and it is
   a single transform-only loop. It brightens as the day fills (--ring-p is set on
   the card as well as the ring by updateScoreRing()). Every stop fades to zero
   alpha of its OWN colour — `transparent` is transparent black and greys it. */
.card.hero-card[data-accent]::before {
  content: '';
  position: absolute;
  left: 50%;
  top: -34px;
  width: 300px;
  height: 300px;
  margin-left: -150px;
  border-radius: 50%;
  pointer-events: none;
  background:
    radial-gradient(circle at 30% 34%, rgba(var(--hg-orange), 0.26), rgba(var(--hg-orange), 0) 46%),
    radial-gradient(circle at 70% 44%, rgba(var(--hg-blue), 0.26), rgba(var(--hg-blue), 0) 48%),
    radial-gradient(circle at 48% 74%, rgba(var(--hg-violet), 0.28), rgba(var(--hg-violet), 0) 50%);
  opacity: calc(0.55 + 0.45 * var(--ring-p, 0));
  transition: opacity var(--dur-ring-fill) var(--ease-out);
  animation: heroGlow var(--dur-hero-glow) linear infinite;
  will-change: transform;
}
@keyframes heroGlow { to { transform: rotate(360deg); } }

.card.hero-card[data-accent] .hero-top {
  position: relative;
  z-index: 1;
  flex-direction: column;
  gap: 16px;
}

/* 148 → 200. The viewBox is unitless, so the ring's geometry just scales; the
   radii themselves live in RING_R / RING_W (js/04-day.js). */
.card.hero-card[data-accent] .hero-score-wrap { width: 200px; height: 200px; }

/* The hole moved with the ring: r = RING_R − RING_W/2 = 47.5 of the 0 0 120 120
   box → inset (60 − 47.5)/120 = 10.4167%. */
.card.hero-card[data-accent] .ring-glass { inset: 10.4167%; }

/* A canvas is a replaced element: inset alone leaves it at its 300x150 default,
   so the box is spelled out. */
.score-orb {
  position: absolute;
  left: 10.4167%;
  top: 10.4167%;
  width: 79.1667%;
  height: 79.1667%;
  border-radius: 50%;
  display: block;
  pointer-events: none;
  /* Hidden until its first real frame (js/14 adds .is-live) — see size(). */
  opacity: 0;
  transition: opacity var(--dur-mid) var(--ease-out);
}
.score-orb.is-live { opacity: 1; }

/* The first time the card is really on screen (not behind the splash) the bubble
   swells in while the ring and the number replay from empty. Its own box, so it
   fights no other transform. --ease-spring is right here: this is an entrance,
   not a reading — unlike the arcs, which must never overshoot the score. */
.hero-score-wrap.is-entering { animation: heroOrbIn var(--dur-slow) var(--ease-spring) both; }
@keyframes heroOrbIn {
  from { transform: scale(0.88); }
  to   { transform: scale(1); }
}

/* The head is a <g> now — the dot plus its comet tail, turned as one. The tail is
   WHITE light running along the arc: in the arc's own colour it vanished into it. */
.sc-head-tail { stroke: var(--text-1); stroke-linecap: butt; }
.sc-head-dot { fill: var(--text-1); }

/* The number, re-measured for the bigger hole (see the chord note on
   .hero-score-wrap): 100% at 46px clears the chord at its own height at 200px. */
.card.hero-card[data-accent] .score-num { font-size: 46px; }
.card.hero-card[data-accent] .score-pct-sign { font-size: 18px; }
.card.hero-card[data-accent] .score-label { font-size: 11px; margin-top: 6px; }

/* ---- the two tiles ----
   Two equal glass tiles side by side, not two pills. As pills on one line they
   measured 153 + 171 against 301px at 375 and wrapped onto two rows; shrinking the
   Georgian to fit was the wrong trade. Each tile is half the width and lets its
   own text wrap — the number leads, the words sit under it. Equal min-height, so
   they line up while .hero-side keeps align-items: center (header-redesign.js). */
.card.hero-card[data-accent] .hero-side {
  flex: 0 0 auto;
  width: 100%;
  flex-direction: row;
  justify-content: center;
  align-items: center;
  gap: 8px;
}
.card.hero-card[data-accent] .hero-streak,
.card.hero-card[data-accent] .hero-tasks {
  flex: 1 1 0;
  min-width: 0;
  /* stretch the tiles to one height — the tasks tile runs to three lines */
  align-self: stretch;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  min-height: 62px;
  padding: 8px 10px;
  border-radius: 18px;
  text-align: center;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(255, 255, 255, 0.09);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05);
  -webkit-backdrop-filter: blur(10px);
          backdrop-filter: blur(10px);
}
.card.hero-card[data-accent] .streak-row { gap: 6px; }
.card.hero-card[data-accent] .streak-fire { width: 20px; height: 20px; }
.card.hero-card[data-accent] .streak-num { font-size: 22px; }
.card.hero-card[data-accent] .streak-label { font-size: 12px; }
/* Its three pieces (S.tasksToday, the count, S.tasksWord) are flex items here, so
   the count gets a line of its own and leads, like the streak's number beside it. */
.card.hero-card[data-accent] .hero-tasks { font-size: 12px; line-height: 1.3; gap: 1px; }
.card.hero-card[data-accent] .hero-tasks .hero-tasks-n { font-size: 17px; }

/* ---- the door to the history: a round lime arrow ---- */
.hero-more {
  position: absolute;
  top: 14px;
  right: 14px;
  z-index: 2;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  border: 0;
  padding: 0;
  display: grid;
  place-items: center;
  background: var(--lime);
  color: #0A0A0C;
  box-shadow: 0 4px 16px var(--lime-glow);
  cursor: pointer;
  transition: transform var(--dur-fast) var(--ease-spring),
              box-shadow var(--dur-fast) var(--ease-out);
}
.hero-more:active { transform: scale(0.92); box-shadow: 0 0 20px var(--lime-glow-2); }
.hero-more svg {
  width: 17px;
  height: 17px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2.4;
  stroke-linecap: round;
  stroke-linejoin: round;
}
/* Read by screen readers and by score-history-and-inbody.js, never drawn. */
.hero-more-label {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}
