/**
 * styles.css -- CycleSync component library and layout.
 *
 * Every value here is a var(--token) from tokens.css. If you need a new
 * literal color/size/duration, it belongs in tokens.css, not here -- see that
 * file's header for why.
 *
 * Mobile-first: the whole app is designed as a single-column, bottom-nav
 * phone layout, capped at --container-max even on desktop, rather than a
 * responsive multi-column dashboard that then gets squeezed onto mobile.
 */

*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }

/*
 * GUARANTEE that [hidden] always wins, full stop. Any class below that sets
 * its own `display` (`.btn`, `.lock-screen`, `.card`, ...) has HIGHER
 * specificity than the browser's UA-stylesheet `[hidden] { display: none }`,
 * so an element that is both `class="btn"` and `hidden` renders ANYWAY --
 * exactly what happened with the lock screen showing to every user
 * regardless of whether a PIN was ever set, found by actually loading the
 * page rather than trusting the markup. One global !important rule, right
 * after the reset, is the standard fix -- simpler and more reliable than
 * chasing down a per-class `[hidden]` override for every component that
 * happens to set `display`.
 */
[hidden] { display: none !important; }

html { color-scheme: light dark; }

body {
  font-family: var(--font-body);
  background: var(--bg);
  color: var(--ink);
  line-height: var(--lh-body);
  -webkit-text-size-adjust: 100%;
  min-height: 100vh;
  padding-bottom: calc(var(--nav-height) + var(--sp-4));
}

img, svg { max-width: 100%; display: block; }

/* ==========================================================================
   INTRO SPLASH -- first-launch brand animation, see playIntroSplash() in ui.js
   --------------------------------------------------------------------------
   z-index 400: above even .lock-screen (300), the highest existing layer --
   this is meant to be the very first thing on screen, full stop.
   ========================================================================== */
.intro-splash {
  position: fixed; inset: 0; z-index: 400;
  display: flex; align-items: center; justify-content: center;
  background: var(--bg);
}
/* The GROUP shift (making room for the wordmark) lives here, on the shared
   parent, as its own single transform -- NOT duplicated across the icon and
   wordmark individually. An earlier version put a matching translateX on
   both children separately, but the icon's transform was sharing a
   transition with its own scale reveal (spring-gentle, 943ms) while the
   wordmark's shift ran on a different, shorter duration (400ms) -- two
   independent transitions racing to the same visual offset on different
   clocks, which is visibly WRONG mid-transition (briefly overlapping,
   colliding) even though both eventually land in the right place. A single
   transform on their common parent moves both children in perfect lockstep
   by construction (child positions compose with the parent's transform),
   while each child's OWN transform stays free to animate its own reveal
   (icon's scale, wordmark's small slide-in) on whatever timing suits it,
   with zero risk of the two ever drifting apart mid-flight.
   --intro-shift itself is computed once in JS (playIntroSplash) from the
   wordmark's actual rendered width -- exact for any font-rendering engine,
   rather than a guessed constant that could sit slightly off-center on some
   of them. */
.intro-lockup {
  position: relative; display: flex; align-items: center; justify-content: center;
  transition: transform var(--dur-slow) var(--ease-decelerate);
}
.intro-splash.wordmark-in .intro-lockup { transform: translateX(var(--intro-shift, 0px)); }

.intro-icon {
  /* Gentle, not snappy: at this slower ~3.5s pace (vs. the quicker nav/button
     taps elsewhere), the softer settle with its slight overshoot reads as
     "weighty and premium" rather than "quick UI feedback." Duration must
     match playIntroSplash()'s iconDurMs, or JS advances to the next step
     before this transition actually finishes settling. */
  font-size: 4rem; line-height: 1; opacity: 0; transform: scale(0.85);
  transition: opacity var(--spring-gentle-dur) var(--spring-gentle), transform var(--spring-gentle-dur) var(--spring-gentle);
}
.intro-wordmark {
  position: absolute; left: 100%; margin-left: var(--sp-3); top: 50%;
  font-family: var(--font-display); font-size: 2.25rem; font-weight: 700; color: var(--ink);
  white-space: nowrap; opacity: 0; transform: translate(-8px, -50%);
  transition: opacity var(--dur-slow) var(--ease-decelerate), transform var(--dur-slow) var(--ease-decelerate);
}
.intro-splash.icon-in .intro-icon { opacity: 1; transform: scale(1); }
.intro-splash.wordmark-in .intro-wordmark { opacity: 1; transform: translate(0, -50%); }
.intro-splash.exiting { transition: opacity var(--dur-slow) var(--ease-out); opacity: 0; }
.intro-splash.exiting .intro-lockup { transition: transform var(--dur-slow) var(--ease-out); transform: scale(0.94); }

/* Reduced motion: the spec calls for replacing MOVEMENT with a gentle fade,
   not removing the fade too -- so this component intentionally does NOT
   rely solely on the global duration-token collapse (tokens.css) the way
   every other Phase-1 component does. Transform is dropped from the
   transitioned properties entirely (so scale/translate/shift apply at their
   final value immediately, no motion), while opacity keeps a real,
   non-collapsing duration (--dur-reduced-fade) so branding still visibly,
   gently fades in. */
.intro-splash.reduced .intro-icon,
.intro-splash.reduced .intro-wordmark {
  transition-property: opacity;
  transition-duration: var(--dur-reduced-fade);
  transition-timing-function: var(--ease-out);
}
.intro-splash.reduced .intro-lockup { transition: none; }
.intro-splash.reduced.icon-in .intro-icon { transform: none; }
.intro-splash.reduced.wordmark-in .intro-wordmark { transform: translate(0, -50%); }
.intro-splash.reduced.exiting { transition-duration: var(--dur-reduced-fade); }
.intro-splash.reduced.exiting .intro-lockup { transform: none; }

/* ==========================================================================
   GUIDED TOUR -- spotlight overlay, see tour.js (config/geometry) and
   wireTour()/showTourStep() in ui.js (the DOM/interaction layer)
   --------------------------------------------------------------------------
   The cutout is a single element whose box-shadow spreads 9999px in every
   direction -- a well-known, purely-visual way to "dim everything except
   this box" with one element and no clip-path/SVG-mask machinery. It has
   pointer-events:none, same as the glow ring, so neither ever blocks the
   real target element (or, deliberately, the rest of the app) underneath --
   this tour dims attention, it doesn't lock the interface.

   Moving between steps uses a FLIP (First-Last-Invert-Play): top/left/width/
   height are set INSTANTLY to the new target's rect (never transitioned --
   those are exactly the layout-triggering properties the rest of this
   motion system avoids animating), then a transform capturing the delta
   from the OLD rect is applied and immediately animated back to identity,
   which reads as a smooth glide/resize using only `transform`. See
   flipMove() in ui.js.
   ========================================================================== */
<<<<<<< HEAD
.tour-overlay { position: fixed; inset: 0; z-index: 250; pointer-events: none; }
=======
.tour-overlay { position: fixed; inset: 0; z-index: 250; pointer-events: none; }
>>>>>>> f7910b917222c0fbf0810c4c0ab1d10ad30cfd40
.tour-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(20, 17, 15, 0.5);
  pointer-events: none;
}
.tour-hole {
  position: fixed; pointer-events: none; border-radius: var(--r-md);
  box-shadow: 0 0 0 9999px rgba(20, 17, 15, 0.65);
}
.tour-glow {
  position: fixed; pointer-events: none; border-radius: var(--r-md);
  box-shadow: 0 0 0 2px var(--action), 0 0 24px 6px var(--action);
  animation: tour-pulse 2.4s var(--ease-in-out) infinite;
}
@keyframes tour-pulse { 0%, 100% { opacity: 0.35; } 50% { opacity: 0.8; } }
@media (prefers-reduced-motion: reduce) { .tour-glow { animation: none; opacity: 0.6; } }

.tour-skip {
  position: fixed; top: calc(env(safe-area-inset-top, 0) + var(--sp-4)); right: var(--sp-4);
  font: inherit; font-size: var(--text-caption); font-weight: 600; cursor: pointer;
  background: var(--surface-raised); color: var(--ink-secondary);
  border: 1px solid var(--surface-sunken); border-radius: var(--r-full);
  padding: var(--sp-2) var(--sp-4); box-shadow: var(--e2);
}

.tour-tooltip {
  position: fixed; width: min(300px, calc(100vw - var(--sp-6)));
  background: var(--surface-raised); border-radius: var(--r-xl); box-shadow: var(--e3);
  padding: var(--sp-5); opacity: 0;
  transition: opacity var(--dur-medium) var(--ease-decelerate), transform var(--spring-responsive-dur) var(--spring-responsive);
}
.tour-tooltip.visible { opacity: 1; }
.tour-progress { font-size: var(--text-caption); font-weight: 600; color: var(--ink-tertiary); margin-bottom: var(--sp-2); }
.tour-tooltip .text-title { margin-bottom: var(--sp-2); }
.tour-hint { font-size: var(--text-body-sm); color: var(--action); font-weight: 600; margin-top: var(--sp-3); }
.tour-actions { margin-top: var(--sp-4); }
.tour-actions #tour-back:disabled { opacity: 0; pointer-events: none; }
.tour-actions #tour-next:disabled { opacity: 0.4; cursor: not-allowed; }

/* Connector arrow -- a rotated square, matching the tooltip's own surface
   color so it reads as one continuous shape pointing at the target. */
.tour-arrow {
  position: absolute; width: 14px; height: 14px; background: var(--surface-raised);
  transform: rotate(45deg); border-radius: 3px;
}
.tour-tooltip[data-side="top"] .tour-arrow { top: -6px; }
.tour-tooltip[data-side="bottom"] .tour-arrow { bottom: -6px; }

@media (prefers-reduced-motion: reduce) {
  .tour-tooltip { transition: opacity var(--dur-reduced-fade) var(--ease-out); }
}

/* ==========================================================================
   FOCUS / SCREEN-READER UTILITIES
   ========================================================================== */
:focus-visible { outline: 3px solid var(--focus-ring); outline-offset: 2px; border-radius: var(--r-sm); }
:focus:not(:focus-visible) { outline: none; }

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}
.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 200;
  background: var(--ink); color: var(--bg);
  padding: var(--sp-2) var(--sp-4); border-radius: var(--r-md);
}
.skip-link:focus { left: var(--sp-4); top: var(--sp-4); }

/* ==========================================================================
   TYPOGRAPHY SCALE
   ========================================================================== */
.text-display { font-family: var(--font-display); font-size: var(--text-display); line-height: var(--lh-display); letter-spacing: var(--ls-display); font-weight: var(--w-display); }
.text-h1      { font-family: var(--font-display); font-size: var(--text-h1);      line-height: var(--lh-h1);      letter-spacing: var(--ls-h1);      font-weight: var(--w-h1); }
.text-h2      { font-family: var(--font-display); font-size: var(--text-h2);      line-height: var(--lh-h2);      letter-spacing: var(--ls-h2);      font-weight: var(--w-h2); }
.text-title   { font-family: var(--font-body);    font-size: var(--text-title);   line-height: var(--lh-title);   letter-spacing: var(--ls-title);   font-weight: var(--w-title); }
.text-body    { font-family: var(--font-body);    font-size: var(--text-body);    line-height: var(--lh-body);    font-weight: var(--w-body); }
.text-body-sm { font-family: var(--font-body);    font-size: var(--text-body-sm); line-height: var(--lh-body-sm); font-weight: var(--w-body-sm); }
.text-caption { font-family: var(--font-body);    font-size: var(--text-caption); line-height: var(--lh-caption); letter-spacing: var(--ls-caption); font-weight: var(--w-caption); text-transform: uppercase; }

.muted { color: var(--ink-secondary); }
.subtle { color: var(--ink-tertiary); }   /* decorative/large text only -- see tokens.css */

/* ==========================================================================
   APP SHELL
   ========================================================================== */
.app { max-width: var(--container-max); margin: 0 auto; min-height: 100vh; position: relative; }

.topbar {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--sp-3); padding: var(--sp-5) var(--sp-4) var(--sp-3);
}
.topbar .brand { display: flex; align-items: center; gap: var(--sp-2); }
.topbar .brand-mark { width: 2rem; height: 2rem; flex: none; }
.topbar h1 { font-family: var(--font-display); font-size: var(--text-h2); font-weight: 700; }
.icon-btn {
  width: var(--touch-min); height: var(--touch-min); display: inline-flex;
  align-items: center; justify-content: center; border-radius: var(--r-full);
  background: transparent; border: none; color: var(--ink-secondary); cursor: pointer;
  font-size: 1.25rem; transition: background var(--dur-fast) var(--ease-standard);
}
.icon-btn:hover { background: var(--surface); }
.icon-btn:active { transform: scale(0.94); }

main.screen { padding: 0 var(--sp-4) var(--sp-8); }
.screen[hidden] { display: none; }

/* Screen enter transition -- fade + tiny rise, opacity-only under reduced motion. */
.screen { animation: screen-in var(--dur-base) var(--ease-standard); }
@keyframes screen-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) {
  .screen { animation: screen-in-reduced var(--dur-fast) linear; }
  @keyframes screen-in-reduced { from { opacity: 0; } to { opacity: 1; } }
}

/* ==========================================================================
   BOTTOM NAVIGATION -- thumb-reachable, one-handed
   ========================================================================== */
.bottom-nav {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 100;
  display: flex; justify-content: center;
}
.bottom-nav-inner {
  position: relative;
  width: 100%; max-width: var(--container-max);
  display: grid; grid-template-columns: repeat(5, 1fr);
  background: var(--surface-raised); box-shadow: var(--e3);
  border-top: 1px solid var(--surface-sunken);
  padding-bottom: env(safe-area-inset-bottom, 0);
}
/* Glides between tabs via translateX(%) alone -- its own width always
   equals one grid column, so `n * 100%` lands it exactly on column n with
   no JS layout measurement (getBoundingClientRect) and no width animation,
   just a single integer written to --nav-index by ui.js on tab change. */
.nav-indicator {
  position: absolute; top: 0; left: 0; height: 3px;
  width: calc(100% / 5);
  display: flex; align-items: center; justify-content: center;
  pointer-events: none;
  transform: translateX(calc(var(--nav-index, 0) * 100%));
  transition: transform var(--spring-responsive-dur) var(--spring-responsive);
}
.nav-indicator::after {
  content: ''; width: 2rem; height: 3px; border-radius: var(--r-full);
  background: var(--action);
}
.nav-btn {
  min-height: var(--nav-height); display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: var(--sp-1);
  background: none; border: none; cursor: pointer; color: var(--ink-tertiary);
  font: inherit; font-size: var(--text-caption); font-weight: 600;
  text-transform: none; letter-spacing: 0;
  transition: color var(--dur-medium) var(--ease-out);
}
.nav-btn .nav-ico {
  font-size: 1.35rem; line-height: 1; transform: scale(0.96);
  transition: transform var(--spring-snappy-dur) var(--spring-snappy);
}
.nav-btn .nav-label { transition: opacity var(--dur-medium) var(--ease-out); }
.nav-btn[aria-current="true"] { color: var(--action); }
.nav-btn[aria-current="true"] .nav-ico { transform: translateY(-1px) scale(1.08); }
.nav-btn:active .nav-ico { transform: scale(0.88); transition-duration: var(--dur-xfast); }

/* ==========================================================================
   CARDS
   ========================================================================== */
.card {
  background: var(--surface-raised); border-radius: var(--r-xl);
  padding: var(--sp-5); box-shadow: var(--e1);
  border: 1px solid var(--surface-sunken);
}
.card + .card { margin-top: var(--sp-4); }
.card-row { display: grid; gap: var(--sp-4); grid-template-columns: 1fr; }
@media (min-width: 480px) { .card-row.cols-2 { grid-template-columns: 1fr 1fr; } }

.card-title {
  font-family: var(--font-display); font-size: var(--text-title); font-weight: 700;
  color: var(--ink); display: flex; align-items: center; gap: var(--sp-2);
  margin-bottom: var(--sp-3);
}

/* Native <details>/<summary> -- keyboard/screen-reader operable disclosure
   with zero JS, styled to match the rest of the card system. */
.help-section { border-top: 1px solid var(--surface-sunken); }
.help-section:first-of-type { border-top: none; }
.help-section summary {
  min-height: var(--touch-min); display: flex; align-items: center;
  font-family: var(--font-display); font-weight: 700; font-size: var(--text-body);
  color: var(--ink); cursor: pointer; list-style: none;
}
.help-section summary::-webkit-details-marker { display: none; }
.help-section summary::before { content: '▸'; color: var(--action); margin-right: var(--sp-2); transition: transform var(--dur-fast) var(--ease-standard); }
.help-section[open] summary::before { transform: rotate(90deg); }
.help-section .onboard-tips { padding-bottom: var(--sp-4); }

/* Card entrance -- float up + fade, staggered by nth-child for a list of cards. */
.card-enter { animation: card-in var(--dur-base) var(--ease-standard) backwards; }
@keyframes card-in { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .card-enter { animation: none; opacity: 1; } }

/* ==========================================================================
   BUTTONS -- default/hover/pressed/focus/disabled/loading states
   ========================================================================== */
.btn {
  font: inherit; font-size: var(--text-body-sm); font-weight: 600; cursor: pointer;
  min-height: var(--touch-min); padding: 0 var(--sp-4);
  display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-2);
  border-radius: var(--r-md); border: 1.5px solid transparent;
  background: var(--action); color: #fff;
  transition: background var(--dur-fast) var(--ease-standard), transform var(--spring-snappy-dur) var(--spring-snappy), opacity var(--dur-fast) var(--ease-standard);
}
.btn:hover { background: var(--action-hover); }
.btn:active { transform: scale(0.97); transition-duration: var(--dur-instant); }
.btn:disabled { opacity: 0.45; cursor: not-allowed; transform: none; }
.btn.is-loading { color: transparent; position: relative; }
.btn.is-loading::after {
  content: ''; position: absolute; width: 1.1rem; height: 1.1rem;
  border: 2px solid rgba(255,255,255,0.4); border-top-color: #fff; border-radius: 50%;
  animation: spin 700ms linear infinite;
}
@media (prefers-reduced-motion: reduce) { .btn.is-loading::after { animation-duration: 1400ms; } }
@keyframes spin { to { transform: rotate(360deg); } }

.btn-secondary { background: var(--surface-raised); color: var(--action); border-color: var(--action); }
.btn-secondary:hover { background: var(--rose); color: var(--ink); border-color: var(--rose); }
.btn-ghost { background: none; color: var(--ink-secondary); border-color: var(--surface-sunken); }
.btn-ghost:hover { background: var(--surface); }
.btn-danger { background: var(--surface-raised); color: var(--error-strong); border-color: var(--error-strong); }
.btn-danger:hover { background: var(--error); color: #fff; }
.btn-sm { min-height: 2.25rem; padding: 0 var(--sp-3); font-size: var(--text-caption); text-transform: none; letter-spacing: 0; }
.btn-block { width: 100%; }
.btn-row { display: flex; gap: var(--sp-2); flex-wrap: wrap; margin-top: var(--sp-4); }
.btn-row-tight { margin-top: 0; }
.mt-lg { margin-top: var(--sp-6); }

/* FAB -- quick-log entry point, always reachable in the lower half of the screen. */
.fab {
  position: fixed; right: var(--sp-4);
  bottom: calc(var(--nav-height) + env(safe-area-inset-bottom, 0) + var(--sp-4));
  width: 3.75rem; height: 3.75rem; border-radius: var(--r-full);
  background: var(--action); color: #fff; border: none; box-shadow: var(--e3);
  font-size: 1.6rem; display: flex; align-items: center; justify-content: center;
  cursor: pointer; z-index: 90; transition: transform var(--dur-fast) var(--ease-standard);
}
.fab:active { transform: scale(0.93); }

/* ==========================================================================
   INPUTS
   ========================================================================== */
.field { margin-bottom: var(--sp-4); }
.field > label, .field > .field-label {
  display: block; font-weight: 600; font-size: var(--text-body-sm);
  margin-bottom: var(--sp-2); color: var(--ink);
}
.field .field-hint { font-size: var(--text-caption); text-transform: none; letter-spacing: 0; color: var(--ink-secondary); margin-top: var(--sp-1); }

.input, .field input[type="date"], .field input[type="number"], .field input[type="text"],
.field input[type="time"], .field select, .field textarea {
  font: inherit; font-size: var(--text-body); width: 100%;
  min-height: var(--touch-min); padding: var(--sp-2) var(--sp-3);
  border: 1.5px solid var(--surface-sunken); border-radius: var(--r-md);
  background: var(--surface-raised); color: var(--ink);
  transition: border-color var(--dur-fast) var(--ease-standard);
}
.input:hover, .field input:hover, .field select:hover { border-color: var(--ink-tertiary); }
.input:focus, .field input:focus, .field select:focus, .field textarea:focus { border-color: var(--action); }
.input:disabled, .field input:disabled { opacity: 0.55; cursor: not-allowed; }
.field.has-error .input, .field.has-error input { border-color: var(--error-strong); }
.field textarea { resize: vertical; min-height: 5rem; }
.field-row { display: flex; gap: var(--sp-4); flex-wrap: wrap; }
.field-row .field { flex: 1 1 9rem; margin-bottom: 0; }
.hint { font-size: var(--text-caption); text-transform: none; letter-spacing: 0; color: var(--ink-secondary); margin-top: var(--sp-1); }

/* Search */
.search { position: relative; }
.search input { padding-left: var(--sp-8); }
.search::before {
  content: '🔍'; position: absolute; left: var(--sp-3); top: 50%; transform: translateY(-50%);
  font-size: 0.9rem; opacity: 0.6; pointer-events: none;
}

/* Switch (toggle) */
.switch { display: inline-flex; align-items: center; gap: var(--sp-3); cursor: pointer; }
.switch input[type="checkbox"] { position: absolute; opacity: 0; width: var(--touch-min); height: 1.75rem; margin: 0; cursor: pointer; }
.switch-track {
  width: 2.75rem; height: 1.6rem; border-radius: var(--r-full); background: var(--surface-sunken);
  position: relative; flex: none; transition: background var(--dur-fast) var(--ease-standard);
  border: 1.5px solid var(--surface-sunken);
}
.switch-track::after {
  content: ''; position: absolute; top: 1px; left: 1px; width: 1.3rem; height: 1.3rem;
  border-radius: 50%; background: #fff; box-shadow: var(--e1);
  transition: transform var(--dur-fast) var(--ease-standard);
}
.switch input:checked + .switch-track { background: var(--action); border-color: var(--action); }
.switch input:checked + .switch-track::after { transform: translateX(1.15rem); }
.switch input:focus-visible + .switch-track { outline: 3px solid var(--focus-ring); outline-offset: 2px; }
.switch-label { font-size: var(--text-body-sm); color: var(--ink); }

/* Slider (symptom intensity, 1-10) */
.slider-field { margin-bottom: var(--sp-5); }
.slider-head { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: var(--sp-2); }
.slider-val { font-family: var(--font-display); font-weight: 700; color: var(--action); min-width: 1.6em; text-align: right; }
input[type="range"].slider {
  -webkit-appearance: none; appearance: none; width: 100%; height: var(--touch-min);
  background: transparent; cursor: pointer; margin: 0;
}
input[type="range"].slider::-webkit-slider-runnable-track {
  height: 6px; border-radius: var(--r-full); background: var(--surface-sunken);
}
input[type="range"].slider::-webkit-slider-thumb {
  -webkit-appearance: none; width: 1.6rem; height: 1.6rem; margin-top: -12px;
  border-radius: 50%; background: var(--action); border: 3px solid var(--surface-raised);
  box-shadow: var(--e2); cursor: pointer;
}
input[type="range"].slider::-moz-range-track { height: 6px; border-radius: var(--r-full); background: var(--surface-sunken); }
input[type="range"].slider::-moz-range-thumb {
  width: 1.6rem; height: 1.6rem; border-radius: 50%; background: var(--action);
  border: 3px solid var(--surface-raised); box-shadow: var(--e2); cursor: pointer;
}
.slider-scale { display: flex; justify-content: space-between; font-size: var(--text-caption); text-transform: none; color: var(--ink-tertiary); margin-top: var(--sp-1); }

/* Errors -- role="alert", never colour-only (icon + text) */
.errors {
  background: color-mix(in srgb, var(--error) 18%, var(--surface-raised));
  border: 1.5px solid var(--error-strong); color: var(--error-strong);
  border-radius: var(--r-md); padding: var(--sp-3) var(--sp-4);
  margin-bottom: var(--sp-4); font-size: var(--text-body-sm);
}
.errors ul { margin: 0; padding-left: 1.2rem; }
.errors:empty { display: none; }

/* ==========================================================================
   CHIPS / TAGS / BADGES
   ========================================================================== */
.chip-row { display: flex; gap: var(--sp-2); flex-wrap: wrap; }
.chip {
  font: inherit; font-size: var(--text-body-sm); cursor: pointer;
  min-height: var(--touch-min); padding: 0 var(--sp-3); display: inline-flex; align-items: center; gap: var(--sp-1);
  background: var(--surface); color: var(--ink-secondary); border: 1.5px solid transparent;
  border-radius: var(--r-full); transition: background var(--dur-fast) var(--ease-standard), border-color var(--dur-fast) var(--ease-standard);
}
.chip:hover { background: var(--surface-sunken); }
.chip[aria-pressed="true"], .chip.active {
  background: var(--rose); border-color: var(--rose-strong); color: var(--ink); font-weight: 600;
}
.chip .chip-remove { margin-left: var(--sp-1); opacity: 0.7; }

.badge {
  display: inline-flex; align-items: center; gap: 0.3em; font-size: var(--text-caption);
  font-weight: 700; padding: 0.15em var(--sp-2); border-radius: var(--r-full);
  background: var(--surface); color: var(--ink-secondary);
}
.badge-estimate { background: var(--lavender); color: var(--lavender-strong); }
.badge-success { background: var(--success); color: #fff; }
.badge-warning { background: var(--warning); color: #fff; }

.confidence { font-size: var(--text-body-sm); color: var(--ink-secondary); }
.confidence strong { color: var(--ink); }

/* ==========================================================================
   PROGRESS RING -- cycle-day visualisation on Home
   ========================================================================== */
.progress-ring { position: relative; width: 9rem; height: 9rem; margin: 0 auto; }
.progress-ring svg { transform: rotate(-90deg); }
.progress-ring .ring-track { fill: none; stroke: var(--surface-sunken); stroke-width: 10; }
.progress-ring .ring-value {
  fill: none; stroke: var(--phase-menstrual-strong); stroke-width: 10; stroke-linecap: round;
  transition: stroke-dashoffset var(--dur-slow) var(--ease-standard), stroke var(--dur-slow) var(--ease-standard);
}
.progress-ring .ring-center {
  position: absolute; inset: 0; display: flex; flex-direction: column;
  align-items: center; justify-content: center; text-align: center;
}
.progress-ring .ring-day { font-family: var(--font-display); font-size: 1.9rem; font-weight: 700; line-height: 1; }
.progress-ring .ring-phase { font-size: var(--text-caption); color: var(--ink-secondary); margin-top: var(--sp-1); }

/* ==========================================================================
   TOOLTIP
   ========================================================================== */
.tooltip { position: relative; display: inline-flex; }
.tooltip .tooltip-bubble {
  position: absolute; bottom: calc(100% + var(--sp-2)); left: 50%; transform: translateX(-50%) translateY(4px);
  background: var(--ink); color: var(--bg); font-size: var(--text-caption); text-transform: none;
  padding: var(--sp-2) var(--sp-3); border-radius: var(--r-md); white-space: nowrap;
  box-shadow: var(--e2); opacity: 0; pointer-events: none; z-index: 60;
  transition: opacity var(--dur-fast) var(--ease-standard), transform var(--dur-fast) var(--ease-standard);
}
.tooltip:hover .tooltip-bubble, .tooltip:focus-within .tooltip-bubble { opacity: 1; transform: translateX(-50%) translateY(0); }

/* ==========================================================================
   BOTTOM SHEET -- floating log bar / quick actions over content
   ========================================================================== */
.sheet-overlay {
  position: fixed; inset: 0; background: rgba(26, 22, 19, 0.4); z-index: 150;
  opacity: 0; transition: opacity var(--dur-base) var(--ease-standard);
}
.sheet-overlay.is-open { opacity: 1; }
.sheet-overlay[hidden] { display: none; }
.sheet {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 151;
  max-width: var(--container-max); margin: 0 auto;
  background: var(--surface-raised); border-radius: var(--r-2xl) var(--r-2xl) 0 0;
  box-shadow: var(--e3); padding: var(--sp-3) var(--sp-5) calc(var(--sp-6) + env(safe-area-inset-bottom, 0));
  transform: translateY(100%); transition: transform var(--dur-base) var(--ease-standard);
  max-height: 85vh; overflow-y: auto;
}
.sheet-overlay.is-open .sheet { transform: translateY(0); }
.sheet-handle { width: 2.5rem; height: 4px; border-radius: var(--r-full); background: var(--surface-sunken); margin: 0 auto var(--sp-4); }
@media (prefers-reduced-motion: reduce) {
  .sheet, .sheet-overlay { transition-duration: 0.001ms; }
}

/* ==========================================================================
   DIALOG (native <dialog>, styled)
   ========================================================================== */
dialog.modal {
  /* The universal `* { margin: 0 }` reset above also zeroes a native
     <dialog>'s UA-default `margin: auto`, which is what centers it within
     its `position: fixed; inset: 0` box -- so centering has to be
     re-asserted explicitly here rather than relied on as a browser default. */
  margin: auto;
  border: none; border-radius: var(--r-xl); padding: var(--sp-5);
  box-shadow: var(--e3); max-width: min(24rem, 90vw); color: var(--ink); background: var(--surface-raised);
}
dialog.modal::backdrop { background: rgba(26, 22, 19, 0.45); }
dialog.modal[open] { animation: dialog-in var(--dur-base) var(--ease-standard); display: flex; flex-direction: column; }
@keyframes dialog-in { from { opacity: 0; transform: scale(0.96); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { dialog.modal[open] { animation: none; } }

/* ==========================================================================
   INSTALL PROMPT -- Android/desktop popup card + iOS bottom instruction tip
   ========================================================================== */
.install-modal { text-align: center; max-width: min(22rem, 88vw); }
.install-modal-icon { display: flex; justify-content: center; margin-bottom: var(--sp-4); }
.install-modal-icon img { border-radius: var(--r-lg); box-shadow: var(--e2); }
.install-modal-body { color: var(--ink-secondary); line-height: var(--lh-body); margin: var(--sp-3) 0 var(--sp-5); }
.install-modal-actions { flex-direction: column; }

.ios-install-tip {
  position: fixed; left: var(--sp-4); right: var(--sp-4);
  bottom: calc(var(--nav-height) + env(safe-area-inset-bottom, 0) + var(--sp-4));
  max-width: calc(var(--container-max) - var(--sp-8)); margin: 0 auto;
  background: var(--surface-raised); border-radius: var(--r-xl); box-shadow: var(--e3);
  padding: var(--sp-5); z-index: 140;
  animation: ios-tip-in var(--dur-base) var(--ease-standard);
}
.ios-install-tip[hidden] { display: none; }
.ios-install-tip-close { position: absolute; top: var(--sp-2); right: var(--sp-2); }
.ios-share-ico { display: inline-block; }
@keyframes ios-tip-in { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .ios-install-tip { animation: none; } }

/* ==========================================================================
   TOAST / SNACKBAR
   ========================================================================== */
.toast {
  position: fixed; left: 50%; bottom: calc(var(--nav-height) + var(--sp-4)); transform: translateX(-50%);
  background: var(--ink); color: var(--bg); padding: var(--sp-3) var(--sp-5);
  border-radius: var(--r-full); box-shadow: var(--e3); font-size: var(--text-body-sm); z-index: 120;
  display: flex; align-items: center; gap: var(--sp-4); max-width: 90vw;
  animation: toast-in var(--dur-base) var(--ease-standard);
}
@keyframes toast-in { from { opacity: 0; transform: translateX(-50%) translateY(8px); } to { opacity: 1; transform: translateX(-50%) translateY(0); } }
.toast[hidden] { display: none; }
.toast-action {
  font: inherit; font-weight: 700; cursor: pointer; flex: none;
  background: none; border: none; color: var(--rose); text-decoration: underline; padding: 0;
}
.toast-action:hover { color: #fff; }

/* ==========================================================================
   TABS (in-page segmented control, e.g. filters within a screen)
   ========================================================================== */
.tabs { display: flex; gap: var(--sp-1); background: var(--surface); border-radius: var(--r-full); padding: 3px; }
.tab {
  flex: 1; font: inherit; font-weight: 600; font-size: var(--text-body-sm); cursor: pointer;
  min-height: 2.5rem; background: none; border: none; border-radius: var(--r-full);
  color: var(--ink-secondary); transition: background var(--dur-fast) var(--ease-standard), color var(--dur-fast) var(--ease-standard);
}
.tab[aria-selected="true"] { background: var(--surface-raised); color: var(--ink); box-shadow: var(--e1); }

/* ==========================================================================
   CALENDAR
   ========================================================================== */
.cal-head { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-2); margin-bottom: var(--sp-4); }
.cal-title { font-family: var(--font-display); font-size: var(--text-title); font-weight: 700; }
.cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; touch-action: pan-y; }
/* Month-change slide -- see changeMonth() in ui.js. overflow:hidden crops
   the two grids (old sliding out, new sliding in) while they briefly
   overlap; a fixed viewport height isn't needed since both grids share the
   same row count for any given month and the container just sizes to the
   one currently in normal flow. */
.cal-grid-viewport { position: relative; overflow: hidden; }
.cal-grid-viewport .cal-grid { transition: transform var(--spring-responsive-dur) var(--spring-responsive); }
.cal-grid-enter { position: absolute; top: 0; left: 0; width: 100%; }
.cal-grid-exit-fwd { transform: translateX(-100%); }
.cal-grid-exit-back { transform: translateX(100%); }
.cal-grid-enter-fwd { transform: translateX(100%); }
.cal-grid-enter-back { transform: translateX(-100%); }

.cal-weekday { text-align: center; font-size: var(--text-caption); color: var(--ink-tertiary); padding-bottom: var(--sp-1); }
.cal-day {
  font: inherit; font-size: var(--text-body-sm); cursor: pointer; position: relative;
  aspect-ratio: 1; min-height: var(--touch-min);
  display: flex; align-items: center; justify-content: center;
  border: 1.5px solid transparent; border-radius: var(--r-md);
  background: var(--surface); color: var(--ink-secondary);
  transition: border-color var(--dur-medium) var(--ease-out), background-color var(--dur-medium) var(--ease-out),
              color var(--dur-medium) var(--ease-out), outline-color var(--dur-medium) var(--ease-out),
              transform var(--spring-snappy-dur) var(--spring-snappy);
}
.cal-day:hover { border-color: var(--sage); }
.cal-day:active { transform: scale(0.9); transition-duration: var(--dur-instant); }
.cal-day.out { opacity: 0.35; }
.cal-day.today { border-color: var(--ink); font-weight: 700; }
.cal-day.selected { outline: 2.5px solid var(--focus-ring); outline-offset: 1px; transform: scale(1.05); }

.cal-day.period { background: var(--phase-menstrual); color: var(--ink); font-weight: 700; }
.cal-day.period-start { border-color: var(--phase-menstrual-strong); }
.cal-day.predicted { background: color-mix(in srgb, var(--phase-menstrual) 45%, var(--surface)); color: var(--ink); border-style: dashed; border-color: var(--phase-menstrual-strong); }
.cal-day.predicted-range { background: color-mix(in srgb, var(--phase-menstrual) 20%, var(--surface)); border-style: dotted; border-color: var(--phase-menstrual); }
.cal-day.fertile { background: var(--phase-fertile); color: var(--ink); border-style: dashed; border-color: var(--phase-fertile-strong); }
.cal-day.ovulation { background: var(--phase-fertile-strong); color: #fff; font-weight: 700; border-style: dashed; }

.cal-day .logdot { position: absolute; bottom: 4px; width: 5px; height: 5px; border-radius: 50%; background: var(--phase-follicular-strong); }
.cal-day.period .logdot, .cal-day.ovulation .logdot { background: rgba(255,255,255,0.85); }

.legend { display: flex; flex-wrap: wrap; gap: var(--sp-2) var(--sp-4); margin-top: var(--sp-4); font-size: var(--text-body-sm); color: var(--ink-secondary); }
.legend span { display: inline-flex; align-items: center; gap: var(--sp-1); }
.legend i { width: 14px; height: 14px; border-radius: var(--r-sm); border: 1.5px solid transparent; flex: none; }
.legend .k-period { background: var(--phase-menstrual); }
.legend .k-predicted { background: color-mix(in srgb, var(--phase-menstrual) 45%, var(--surface)); border-color: var(--phase-menstrual-strong); border-style: dashed; }
.legend .k-fertile { background: var(--phase-fertile); border-color: var(--phase-fertile-strong); border-style: dashed; }
.legend .k-ovulation { background: var(--phase-fertile-strong); }
.legend .k-log { background: var(--phase-follicular-strong); border-radius: 50%; }

/* ==========================================================================
   TIMELINE / HISTORY / JOURNAL CARDS
   ========================================================================== */
.timeline-list { list-style: none; }
.timeline-item {
  display: flex; align-items: flex-start; gap: var(--sp-4);
  padding: var(--sp-4) 0; border-bottom: 1px solid var(--surface-sunken);
}
.timeline-item:last-child { border-bottom: none; }
.timeline-main { flex: 1 1 12rem; min-width: 0; }
.timeline-date { font-weight: 700; color: var(--ink); }
.timeline-meta { font-size: var(--text-body-sm); color: var(--ink-secondary); }
.timeline-actions { display: flex; gap: var(--sp-2); flex: none; }

.journal-entry { padding: var(--sp-4); }
.journal-entry .je-title { font-family: var(--font-display); font-weight: 700; font-size: var(--text-title); }
.journal-entry .je-body { margin: var(--sp-2) 0; white-space: pre-wrap; }
.journal-entry .je-body b, .journal-entry .je-body strong { font-weight: 700; }
.journal-entry .je-body i, .journal-entry .je-body em { font-style: italic; }
.journal-entry .je-photos { display: flex; gap: var(--sp-2); flex-wrap: wrap; margin-top: var(--sp-2); }
.journal-entry .je-photos img { width: 4.5rem; height: 4.5rem; object-fit: cover; border-radius: var(--r-md); }
.journal-entry audio { width: 100%; margin-top: var(--sp-2); }

.rich-toolbar { display: flex; gap: var(--sp-1); margin-bottom: var(--sp-2); }
.rich-toolbar button { width: 2.25rem; height: 2.25rem; border-radius: var(--r-sm); border: 1.5px solid var(--surface-sunken); background: var(--surface-raised); cursor: pointer; font-weight: 700; }
.rich-toolbar button:hover { background: var(--surface); }
.rich-editor {
  min-height: 8rem; border: 1.5px solid var(--surface-sunken); border-radius: var(--r-md);
  padding: var(--sp-3); background: var(--surface-raised); font: inherit; line-height: var(--lh-body);
}
.rich-editor:focus { border-color: var(--action); outline: none; }
.rich-editor[data-placeholder]:empty::before { content: attr(data-placeholder); color: var(--ink-tertiary); }

/* ==========================================================================
   STATS / INSIGHTS
   ========================================================================== */
.stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(7.5rem, 1fr)); gap: var(--sp-3); }
.stat { background: var(--surface); border-radius: var(--r-md); padding: var(--sp-3); text-align: center; }
.stat .val { font-family: var(--font-display); font-size: 1.5rem; font-weight: 700; color: var(--ink); display: block; }
.stat .lbl { font-size: var(--text-caption); color: var(--ink-secondary); }

.insight-card { display: flex; gap: var(--sp-3); align-items: flex-start; }
.insight-card .insight-ico { font-size: 1.5rem; flex: none; }
.insight-card p { margin: 0; }
.insight-confidence { display: block; margin-top: var(--sp-1); }

/* Inline SVG chart container -- draws in on load via stroke-dashoffset (set inline per chart) */
.chart { width: 100%; height: auto; }
.chart .chart-line { fill: none; stroke-width: 2.5; stroke-linecap: round; stroke-linejoin: round; }
.chart .chart-line.animate {
  animation: chart-draw var(--dur-slow) var(--ease-standard) forwards;
}
@keyframes chart-draw { from { stroke-dashoffset: var(--chart-len, 1000); } to { stroke-dashoffset: 0; } }
@media (prefers-reduced-motion: reduce) { .chart .chart-line.animate { animation: none; stroke-dashoffset: 0; } }
.chart .chart-dot { fill: var(--action); }
.chart .chart-axis { stroke: var(--surface-sunken); stroke-width: 1; }
.chart .chart-label { font-size: 0.55rem; fill: var(--ink-tertiary); }

/* ==========================================================================
   EMPTY STATES + SKELETONS
   ========================================================================== */
.empty-state { text-align: center; padding: var(--sp-8) var(--sp-4); color: var(--ink-secondary); }
.empty-state .emoji { font-size: 2.25rem; display: block; margin-bottom: var(--sp-3); }
.empty-state .empty-title { font-family: var(--font-display); font-weight: 700; color: var(--ink); margin-bottom: var(--sp-1); }

.skeleton {
  background: linear-gradient(100deg, var(--surface) 30%, var(--surface-sunken) 50%, var(--surface) 70%);
  background-size: 200% 100%; border-radius: var(--r-md);
  animation: skeleton-shimmer 1.4s ease-in-out infinite;
}
@keyframes skeleton-shimmer { from { background-position: 200% 0; } to { background-position: -200% 0; } }
@media (prefers-reduced-motion: reduce) { .skeleton { animation: none; background: var(--surface-sunken); } }
.skeleton-text { height: 0.9em; margin-bottom: var(--sp-2); }
.skeleton-card { height: 6rem; border-radius: var(--r-xl); }

/* ==========================================================================
   AVATAR
   ========================================================================== */
.avatar {
  width: 2.75rem; height: 2.75rem; border-radius: 50%; flex: none;
  background: var(--rose); color: var(--ink); display: flex; align-items: center; justify-content: center;
  font-family: var(--font-display); font-weight: 700;
}

/* ==========================================================================
   DISCLAIMER
   ========================================================================== */
.disclaimer {
  background: color-mix(in srgb, var(--warning) 15%, var(--surface-raised));
  border: 1.5px solid var(--warning-strong); color: var(--warning-strong);
  border-radius: var(--r-md); padding: var(--sp-4); font-size: var(--text-body-sm);
}
.estimate {
  display: inline-flex; align-items: center; font-size: var(--text-caption); font-weight: 700;
  background: var(--lavender); color: var(--lavender-strong);
  padding: 0.1em var(--sp-2); border-radius: var(--r-full); vertical-align: middle; margin-left: var(--sp-1);
}

/* Big headline stat, e.g. Home's "3 days until" */
.big-stat { font-family: var(--font-display); font-size: 2.1rem; font-weight: 700; line-height: 1.1; color: var(--ink); }
.big-stat small { display: block; font-family: var(--font-body); font-size: var(--text-body-sm); font-weight: 400; color: var(--ink-secondary); }

/* Privacy badge (topbar) */
.privacy-badge {
  display: inline-flex; align-items: center; gap: var(--sp-1);
  background: var(--surface); color: var(--ink-secondary);
  padding: var(--sp-1) var(--sp-3); border-radius: var(--r-full); font-size: var(--text-caption);
}

/* ==========================================================================
   ONBOARDING
   ========================================================================== */
.onboard-screen {
  min-height: 100vh; display: flex; flex-direction: column; justify-content: center;
  padding: var(--sp-8) var(--sp-5); max-width: var(--container-max); margin: 0 auto;
}
.onboard-screen[hidden] { display: none; }

/* The wizard's content is a single innerHTML swap on load (see
   renderSetupWizard()), so a CSS transition (which needs a persisting
   element to interpolate FROM) can't drive this -- an animation, which
   plays automatically on insertion, can. Staggered via animation-delay per
   element type, "both" fill-mode holds the from-state during the delay and
   the to-state after finishing. */
@keyframes onboard-reveal { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: translateY(0); } }
#wizard-body > .onboard-icon,
#wizard-body > .text-h1,
#wizard-body > .text-body,
#wizard-body > .field,
#wizard-body > #wizard-continue {
  animation: onboard-reveal var(--dur-slow) var(--ease-decelerate) both;
}
#wizard-body > .onboard-icon { animation-delay: 0ms; }
#wizard-body > .text-h1 { animation-delay: 60ms; }
#wizard-body > .text-body { animation-delay: 120ms; }
#wizard-body > .field { animation-delay: 180ms; }
#wizard-body > #wizard-continue { animation-delay: 240ms; }

.onboard-icon { font-size: 3rem; text-align: center; margin-bottom: var(--sp-4); }
.onboard-tips { list-style: none; margin-top: var(--sp-4); display: flex; flex-direction: column; gap: var(--sp-3); }
.onboard-tips li {
  position: relative; padding-left: var(--sp-5); color: var(--ink-secondary);
  font-size: var(--text-body); line-height: var(--lh-body);
}
.onboard-tips li::before { content: '•'; position: absolute; left: var(--sp-1); color: var(--action); font-weight: 700; }

/* ==========================================================================
   LOCK SCREEN
   ========================================================================== */
.lock-screen {
  position: fixed; inset: 0; z-index: 300; background: var(--bg);
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--sp-5);
  padding: var(--sp-5);
}
.lock-screen[hidden] { display: none; }
.pin-dots { display: flex; gap: var(--sp-3); }
.pin-dots .pd { width: 1rem; height: 1rem; border-radius: 50%; border: 2px solid var(--ink-tertiary); }
.pin-dots .pd.filled { background: var(--action); border-color: var(--action); }
.pin-pad { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--sp-3); width: 100%; max-width: 16rem; }
.pin-pad button {
  min-height: 3.75rem; border-radius: var(--r-full); border: 1.5px solid var(--surface-sunken);
  background: var(--surface-raised); font-family: var(--font-display); font-size: 1.4rem; cursor: pointer;
}
.pin-pad button:active { background: var(--surface); }

/* ==========================================================================
   RESPONSIVE / PRINT
   ========================================================================== */
@media (max-width: 380px) {
  .stat-grid { grid-template-columns: repeat(2, 1fr); }
  .cal-day { font-size: var(--text-caption); }
}

@media print {
  .bottom-nav, .fab, .btn, .btn-row, .cal-head button, .toast { display: none; }
  body { background: #fff; padding-bottom: 0; }
  .card { box-shadow: none; border-color: #ccc; }
}
