/* ----------------------------------------------------------------- reset */
* { margin: 0; padding: 0; box-sizing: border-box; }
:root {
  /* Zinc-100 base, Zinc-900 ink, Zinc-200 dividers, see design spec. */
  --bg-0: #f2f2f1;
  --bg-1: #e7e6e2;
  --ink: #18181b;
  --ink-dim: #71717a;
  --glass: rgba(250, 250, 250, 0.75);
  --glass-brd: rgba(228, 228, 231, 0.9);
  --accent: #0f8fb3;
  --emphasis: #f50404; /* highlighted text: stats, links, labels, numbers */
  --stretch: #0f8fb3;
  --strengthen: #d9713c;
  --flag: #e5342b;
  --capture: #e5484d;
  --capture-hi: #f0605f;
  --dark-bg: #0a0a0b;
  --dark-brd: rgba(255, 255, 255, 0.1);
  --dark-ink: #f4f4f5;
  --dark-ink-dim: #a1a1aa;
  --font: "Google Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, Roboto, Helvetica, Arial, sans-serif;
}

html, body {
  height: 100%;
  width: 100%;
  overflow: hidden;
  color: var(--ink);
  font-family: var(--font);
  -webkit-font-smoothing: antialiased;
  user-select: none;
  /* No double-tap zoom anywhere, so WebKit never waits to see whether a
     second tap follows. (The half-second hold on the very first tap after
     launch is a different thing: see src/lib/fastTap.js.) Elements that set
     their own touch-action (the model, the charts) keep theirs. */
  touch-action: manipulation;
}

/* The page gradient lives on <html> alone so it paints on the root canvas:
   that lets a z-index:-1 layer (the onboarding dot grid) sit above the
   gradient but below everything else. Giving <body> the same background
   would paint it over that layer. */
html { background: linear-gradient(165deg, var(--bg-0), var(--bg-1)); }

#scene {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  touch-action: none;
  /* Held invisible until the model has loaded, then faded up, the first
     frame of the figure should arrive, not pop. */
  opacity: 0;
  transition: opacity 1.4s ease;
}
#scene.is-ready { opacity: 1; }

/* Onboarding's dot grid: pinned under the model canvas and everything else,
   above the body gradient, so the page colour is unchanged and nothing on
   it is clickable. */
.dots-layer {
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  animation: dotsIn 0.8s ease both;
}
@keyframes dotsIn { from { opacity: 0; } to { opacity: 1; } }
.dots { display: block; width: 100%; height: 100%; }

/* Every internal scroll region in the app (the muscle index, the exercise
   panel, the console's card list, a flipped card's trend charts, the
   heatmap's day list, a parsed-workout review) keeps scrolling, this only
   hides the browser's own scrollbar chrome. A thin bar floating over the
   glass panels read as stray debug UI, not part of the design. */
.indexpanel, .panel__list, .sheet__scroll, .trends, .review {
  scrollbar-width: none;      /* Firefox */
  -ms-overflow-style: none;   /* legacy Edge */
}
.indexpanel::-webkit-scrollbar,
.panel__list::-webkit-scrollbar,
.sheet__scroll::-webkit-scrollbar,
.trends::-webkit-scrollbar,
.review::-webkit-scrollbar {
  display: none;
}
/* Desktop: the sheet is a fixed right-hand column, so the model is confined
   to the left of it. Under the mobile breakpoint the split turns horizontal
   instead, the model takes the top half (see the mobile block). */
@media (min-width: 621px) {
  body.is-console #scene { width: calc(100% - min(460px, 42vw)); }
}

/* --------------------------------------------------------------- loader */
.loader {
  position: fixed;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 18px;
  z-index: 40;
  color: var(--ink-dim);
  letter-spacing: 0.02em;
  transition: opacity 0.6s ease;
}
.loader.is-hidden { opacity: 0; pointer-events: none; }
/* The turning logo (the spheres animation); the message line under it stays
   empty unless the model fails to load. */
.loader__logo { display: block; width: 120px; height: 120px; }
.loader__msg { margin: 0; font-size: 14px; color: var(--stage-ink); text-align: center; }
.loader__msg:empty { display: none; }

/* ----------------------------------------------------------- browse index */
.indextoggle {
  position: fixed;
  top: 68px; left: 24px;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 9px 15px;
  border-radius: 12px;
  background: var(--dark-bg);
  border: 1px solid var(--dark-brd);
  box-shadow: 0 10px 30px rgba(24, 24, 27, 0.05);
  color: var(--dark-ink);
  font: 500 14px var(--font);
  cursor: pointer;
  z-index: 26;
  transition: background 0.2s ease;
}
.indextoggle:hover { background: #1a1a1c; }
.indextoggle[aria-expanded="true"] { box-shadow: inset 0 0 0 1px rgba(15, 143, 179, 0.3); }
.indextoggle__ico { font-size: 15px; }

.indexpanel {
  position: fixed;
  top: 118px; left: 24px;
  width: 250px;
  max-height: calc(100vh - 180px);
  overflow-y: auto;
  padding: 8px;
  border-radius: 18px;
  background: var(--glass);
  border: 1px solid var(--glass-brd);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  box-shadow: 0 20px 45px rgba(24, 24, 27, 0.08), inset 0 1px 0 rgba(255, 255, 255, 0.6);
  z-index: 24;
  opacity: 0;
  transform: translateX(-16px);
  pointer-events: none;
  transition: opacity 0.35s ease, transform 0.35s ease;
}
.indexpanel.is-open { opacity: 1; transform: none; pointer-events: auto; }

.idx__search {
  position: sticky;
  top: -8px;
  margin: -8px -8px 6px;
  padding: 8px 8px 6px;
  background: var(--glass);
  z-index: 1;
}
.idx__search input {
  width: 100%;
  padding: 8px 12px;
  border-radius: 10px;
  border: 1px solid var(--glass-brd);
  background: rgba(24, 24, 27, 0.04);
  color: var(--ink);
  font: 400 13.5px var(--font);
  outline: none;
  transition: background 0.15s ease, box-shadow 0.15s ease;
}
.idx__search input::placeholder { color: var(--ink-dim); }
.idx__search input:focus {
  background: #fff;
  box-shadow: inset 0 0 0 1.5px var(--accent);
}

.idx__region.is-hidden { display: none; }
.idx__item.is-hidden { display: none; }
.idx__region { margin-bottom: 6px; }
.idx__title {
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--ink-dim);
  padding: 12px 10px 6px;
}
.idx__item {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border: 0;
  background: transparent;
  border-radius: 10px;
  cursor: pointer;
  text-align: left;
  transition: background 0.15s ease;
}
.idx__item:hover { background: rgba(24, 24, 27, 0.04); }
.idx__item.is-active { background: rgba(24, 24, 27, 0.06); box-shadow: inset 0 0 0 1px var(--glass-brd); }
.idx__item.is-away { opacity: 0.38; }
.idx__item.is-away:hover { background: transparent; }
.idx__dot { flex: 0 0 auto; width: 10px; height: 10px; border-radius: 50%; box-shadow: 0 0 8px currentColor; }
.idx__meta { display: flex; flex-direction: column; min-width: 0; }
.idx__name { font-size: 14px; font-weight: 500; color: var(--ink); }
.idx__mus { font-size: 11px; color: var(--ink-dim); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* -------------------------------------------------------------- tooltip */
.tooltip {
  position: fixed;
  z-index: 30;
  padding: 7px 12px;
  border-radius: 10px;
  background: rgba(250, 250, 250, 0.96);
  border: 1px solid var(--glass-brd);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  color: var(--ink);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.01em;
  pointer-events: none;
  transform: translate(-50%, calc(-100% - 14px));
  white-space: nowrap;
  box-shadow: 0 10px 30px rgba(24, 24, 27, 0.12);
}
.tooltip small { display: block; font-weight: 400; color: var(--ink-dim); font-size: 11px; margin-top: 1px; }

/* ----------------------------------------------------------------- hint */
.hint {
  position: fixed;
  bottom: 22px; left: 50%;
  transform: translateX(-50%);
  padding: 9px 18px;
  border-radius: 999px;
  background: var(--glass);
  border: 1px solid var(--glass-brd);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  box-shadow: 0 10px 26px rgba(24, 24, 27, 0.06);
  color: var(--ink-dim);
  font-size: 13px;
  z-index: 20;
  transition: opacity 0.4s ease, transform 0.4s ease;
}
.hint b { color: var(--ink); font-weight: 600; }
.hint.is-hidden,
body.is-onboarding .hint { opacity: 0; transform: translateX(-50%) translateY(12px); pointer-events: none; }

/* ----------------------------------------------------------- onboarding */
/* The check-up's composer still uses the old intake's pane. */
.onb__pane { gap: 14px; }
.onb__pane .btn--primary { align-self: stretch; }

/* The first-run flow (src/components/OnboardingFlow.jsx). Each screen is a
   full-screen layer that slides; the back button and the bottom dock sit
   above them and stay put. The 3D model loads underneath for the app that
   follows, but isn't shown until then. */
body.is-onboarding #scene,
body.is-onboarding .loader {
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.35s ease;
}
.flow {
  position: fixed;
  inset: 0;
  z-index: 36;
  overflow: hidden;
  --flow-dock: 150px; /* kept clear under every screen for the dock */
}
.flow svg {
  width: 20px; height: 20px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.flow__page {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  padding: calc(env(safe-area-inset-top) + 76px) 24px calc(env(safe-area-inset-bottom) + var(--flow-dock));
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: none;
}
.flow__page::-webkit-scrollbar { display: none; }
.flow__body {
  width: min(100%, 440px);
  margin-inline: auto;
  display: flex;
  flex-direction: column;
  gap: 14px;
}
/* Hero screens: the words sit a little above the middle. */
.flow__body--center {
  flex: 1 0 auto;
  justify-content: center;
  align-items: center;
  text-align: center;
  padding-bottom: 8vh;
}
.flow__title {
  font-size: clamp(30px, 8.4vw, 40px);
  font-weight: 500;
  line-height: 1.08;
  letter-spacing: -0.03em;
  color: var(--ink);
  text-wrap: balance;
}
.flow__title--center,
.flow__title--center + .flow__sub { text-align: center; }
.flow__sub {
  font-size: 15px;
  line-height: 1.5;
  color: var(--ink-dim);
  text-wrap: pretty;
}
.flow__stat {
  margin-top: 18px;
  font-size: clamp(64px, 20vw, 88px);
  font-weight: 500;
  line-height: 1;
  letter-spacing: -0.045em;
  color: var(--emphasis);
}
.flow__stat + .flow__sub { margin-top: -4px; }
/* A number too long for the full size (570,000,000), smaller, so it stays
   on one line even on the narrowest phones; its unit keeps the usual size. */
.flow__stat--long { font-size: clamp(40px, 12.5vw, 60px); }
.flow__stat--long .flow__stat-unit { font-size: clamp(19px, 6vw, 26px); margin-top: 0.3em; }
/* The rolling number (RollingNumber): a window one digit tall over each
   column's strip. Tabular figures keep every column the width of one digit,
   so the number doesn't jitter sideways as digits pass. */
.roll { display: inline-flex; font-variant-numeric: tabular-nums; }
.roll__col { display: inline-block; height: 1em; overflow: hidden; }
.roll__strip { display: flex; flex-direction: column; }
.roll__strip > span { height: 1em; line-height: 1; }
/* The two consent boxes under the account form, and the small print on the
   intro screen. Links sit in the stage's light ink, underlined. */
.flow__consents { display: flex; flex-direction: column; gap: 12px; margin-top: 4px; }
.flow__consent { display: flex; align-items: flex-start; gap: 12px; font-size: 13px; line-height: 1.45; color: var(--stage-ink); cursor: pointer; }
.flow__consent input { flex: none; width: 22px; height: 22px; margin: 0; accent-color: #fff; }
.flow__consent a, .flow__fine a { color: #fff; text-underline-offset: 2px; }
.flow__fine { margin-top: 18px; max-width: 28em; font-size: 12.5px; line-height: 1.5; color: var(--stage-ink); text-align: center; text-wrap: pretty; }
/* The unit sits on its own line under the number. */
.flow__stat-unit {
  display: block;
  max-width: 16em;
  margin-inline: auto;
  line-height: 1.15;
  text-wrap: balance;
  margin-top: 0.08em;
  font-size: 0.3em;
  letter-spacing: -0.01em;
}
.flow__cite {
  max-width: 34ch;
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--ink-dim);
  text-wrap: pretty;
}
/* A source at the foot of the screen, just above the dock. */
.flow__cite--foot {
  flex: none;
  margin: 12px auto 0;
  text-align: center;
}

.flow__back {
  position: absolute;
  top: calc(env(safe-area-inset-top) + 16px);
  left: 16px;
  z-index: 2;
  width: 40px; height: 40px;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: 50%;
  background: rgba(24, 24, 27, 0.06);
  color: var(--ink);
  cursor: pointer;
}

/* Check your email: one box per digit (--code-length, set from the
   component) drawn over one transparent input. */
.flow__code {
  position: relative;
  display: grid;
  grid-template-columns: repeat(var(--code-length, 6), 1fr);
  gap: 6px;
  margin-top: 8px;
}
.flow__code-input {
  position: absolute;
  inset: 0;
  width: 100%;
  opacity: 0;
  font-size: 16px; /* stops iOS zooming in on focus */
  caret-color: transparent;
}
.flow__code-box {
  aspect-ratio: 5 / 6;
  display: grid;
  place-items: center;
  border: 1px solid var(--glass-brd);
  border-radius: 12px;
  background: rgba(255, 255, 255, 0.7);
  font-size: 24px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.flow__code:focus-within .flow__code-box.is-next { border-color: var(--emphasis); box-shadow: 0 0 0 3px rgba(245, 4, 4, 0.12); }
.flow__code-box.is-wrong { border-color: rgba(245, 4, 4, 0.5); }
.flow__code-msg { text-align: center; }
.flow__link:disabled { color: var(--ink-dim); cursor: default; }

/* How far through onboarding: level with the back button, centred. */
.flow__progress {
  position: absolute;
  top: calc(env(safe-area-inset-top) + 32px);
  left: 50%;
  z-index: 2;
  width: 108px;
  height: 8px;
  margin-left: -54px;
  border-radius: 999px;
  background: rgba(24, 24, 27, 0.08);
  overflow: hidden;
}
.flow__progress-fill { display: block; height: 100%; border-radius: inherit; background: var(--emphasis); }

/* The dock: the one bottom button every screen shares, plus a line under
   it on the screens that have a second choice. Where content scrolls under
   it (--fade), that content fades out into the page colour. */
.flow__dock {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  padding: 28px 24px calc(env(safe-area-inset-bottom) + 28px);
  pointer-events: none;
}
.flow__dock--fade { background: linear-gradient(to bottom, rgba(231, 230, 226, 0), var(--bg-1) 28px); }
.flow__dock > * { pointer-events: auto; }
.btn--primary.flow__cta {
  align-self: center;
  width: min(100%, 440px);
  padding: 17px 22px;
  font-size: 16px;
}
.flow__link {
  appearance: none;
  border: 0;
  background: none;
  padding: 6px 10px;
  font: 500 14px var(--font);
  color: var(--emphasis);
  cursor: pointer;
}
.flow__body > .flow__link { align-self: center; margin-top: 10px; }

/* Create an account. 16px inputs so iOS doesn't zoom in on focus. */
.flow__fields { display: flex; flex-direction: column; gap: 14px; margin-top: 10px; }
.flow__row { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.flow .modal__input { font-size: 16px; }

/* Where does it hurt?, the diagram takes whatever height is left between
   the title and the buttons. Its images are stacked exactly, so a tap's
   position on the figure is a position on every layer. */
.flow__body--where { flex: 1 1 auto; min-height: 0; }
.backmap {
  flex: 1 1 0;
  min-height: 180px;
  display: flex;
  justify-content: center;
  padding: 8px 0;
}
.backmap__figure {
  position: relative;
  height: 100%;
  aspect-ratio: 900 / 1289; /* back.json has the real size; this holds the space until it loads */
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.backmap__figure img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  -webkit-user-drag: none;
}
.backmap__combo { opacity: 0; transition: opacity 0.3s ease; }
.backmap__combo.is-on { opacity: 1; }
.flow__choices { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
.flow__choice,
.flow__chip {
  appearance: none;
  padding: 12px 10px;
  border: 1px solid var(--glass-brd);
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.7);
  color: var(--ink);
  font: 500 14px var(--font);
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease, transform 0.15s ease;
}
.flow__choice:active,
.flow__chip:active { transform: scale(0.97); }
.flow__choice.is-on,
.flow__chip.is-on { background: var(--ink); border-color: var(--ink); color: var(--dark-ink); }

/* What do you want to achieve? */
.flow__goals { display: flex; flex-direction: column; gap: 10px; margin-top: 8px; }
.flow__goal {
  appearance: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 17px 16px 17px 22px;
  border: 1px solid var(--glass-brd);
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.7);
  color: var(--ink);
  font: 500 16px var(--font);
  text-align: left;
  cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease, transform 0.15s ease;
}
.flow__goal:active { transform: scale(0.98); }
.flow__goal.is-on { background: #fff; border-color: rgba(245, 4, 4, 0.35); }
.flow__goal-check {
  flex: none;
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: rgba(24, 24, 27, 0.05);
  color: transparent;
  transition: background 0.15s ease, color 0.15s ease;
}
.flow__goal.is-on .flow__goal-check { background: var(--emphasis); color: #fff; }
.flow__goal-check svg { width: 15px; height: 15px; stroke-width: 2.6; }

/* Let's make your battle plan */
.flow__lede { margin-top: 10px; font-size: 17px; line-height: 1.4; color: var(--ink-dim); text-wrap: pretty; }
.flow__features { list-style: none; display: flex; flex-direction: column; gap: 26px; margin-top: 18px; }
.flow__features li { display: flex; align-items: center; gap: 18px; }
.flow__feature-icon {
  flex: none;
  width: 60px; height: 60px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: rgba(245, 4, 4, 0.1);
  color: var(--emphasis);
}
.flow__feature-icon svg { width: 30px; height: 30px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.flow__feature-name { font-size: 18px; font-weight: 600; letter-spacing: -0.01em; }
.flow__feature-text { margin-top: 4px; font-size: 15px; line-height: 1.45; color: var(--ink-dim); text-wrap: pretty; }

/* How your trial works */
.flow__toggle {
  align-self: center;
  display: inline-flex;
  gap: 2px;
  padding: 4px;
  border-radius: 999px;
  background: rgba(24, 24, 27, 0.06);
}
.flow__toggle-item {
  appearance: none;
  border: 0;
  background: none;
  padding: 8px 18px;
  border-radius: 999px;
  font: 500 14px var(--font);
  color: var(--ink);
  cursor: pointer;
  transition: background 0.2s ease, color 0.2s ease;
}
.flow__toggle-item.is-on { background: var(--ink); color: var(--dark-ink); }
/* A vertical track through the three icons that fades out below the last. */
.timeline {
  position: relative;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 24px;
  margin-top: 16px;
}
.timeline::before {
  content: '';
  position: absolute;
  left: 11px; top: 18px; bottom: -36px;
  width: 14px;
  border-radius: 7px;
  background: linear-gradient(to bottom, rgba(245, 4, 4, 0.16) 70%, rgba(245, 4, 4, 0));
}
.timeline__step { position: relative; display: grid; grid-template-columns: 36px 1fr; gap: 16px; }
.timeline__node {
  width: 36px; height: 36px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--emphasis);
  color: #fff;
}
.timeline__node svg { width: 18px; height: 18px; }
.timeline__when { padding-top: 7px; font-size: 17px; font-weight: 600; line-height: 1.3; letter-spacing: -0.01em; }
.timeline__what { margin-top: 4px; font-size: 15px; line-height: 1.5; color: var(--ink-dim); }
.timeline__what b { font-weight: 600; color: var(--ink); }

/* Your trial started */
.flow__badge {
  width: 64px; height: 64px;
  display: grid;
  place-items: center;
  margin-bottom: 6px;
  border-radius: 50%;
  background: rgba(245, 4, 4, 0.1);
  color: var(--emphasis);
}
.flow__badge svg { width: 28px; height: 28px; }

/* What makes it hurt? */
.flow__chips { display: flex; flex-wrap: wrap; gap: 8px; margin: 6px 0 8px; }
.flow__chip { padding: 10px 14px; }
.flow__other { min-height: 84px; resize: none; }

/* Anatomizing: three dots rising in turn, like a wave. */
.flow__wave { display: inline-flex; align-items: center; gap: 8px; height: 28px; margin-top: 4px; }
.flow__wave i {
  width: 10px; height: 10px;
  border-radius: 50%;
  background: var(--ink);
  animation: flowWave 1.1s ease-in-out infinite;
}
.flow__wave i:nth-child(2) { animation-delay: 0.15s; }
.flow__wave i:nth-child(3) { animation-delay: 0.3s; }
@keyframes flowWave {
  0%, 60%, 100% { transform: translateY(0); opacity: 0.35; }
  30% { transform: translateY(-9px); opacity: 1; }
}

/* Got it, the whole screen is the button. */
.flow__tap { position: absolute; inset: 0; width: auto; cursor: pointer; outline: none; }
.flow__hint {
  position: absolute;
  left: 0; right: 0;
  bottom: calc(env(safe-area-inset-bottom) + 48px);
  font-size: 14px;
  color: var(--ink-dim);
  animation: flowBreathe 2.4s ease-in-out infinite;
}
@keyframes flowBreathe { 0%, 100% { opacity: 0.45; } 50% { opacity: 1; } }

@media (prefers-reduced-motion: reduce) {
  .flow__wave i, .flow__hint { animation: none; opacity: 1; }
}

/* Account popup over the model. Same glass as the sheet, so it reads as part
   of the app rather than a browser dialog. */
.modal {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: grid;
  place-items: center;
  padding: 20px;
  background: rgba(231, 230, 226, 0.55);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
}
.modal__card {
  position: relative;
  width: min(100%, 400px);
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 26px 24px 22px;
  border-radius: 22px;
  background: rgba(250, 250, 250, 0.92);
  border: 1px solid var(--glass-brd);
  box-shadow: 0 24px 60px rgba(24, 24, 27, 0.18);
}
.modal__card .sheet__sub { margin-top: -6px; }
.modal__close {
  position: absolute;
  top: 12px; right: 12px;
  width: 32px; height: 32px;
  border: 0;
  border-radius: 50%;
  background: rgba(24, 24, 27, 0.06);
  color: var(--ink-dim);
  font: 400 20px/1 var(--font);
  cursor: pointer;
}
.modal__close:hover { background: rgba(24, 24, 27, 0.1); }
.modal__close:disabled { opacity: 0.4; cursor: default; }
.modal__field { display: flex; flex-direction: column; gap: 6px; }
.modal__label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-dim);
}
.modal__input {
  appearance: none;
  width: 100%;
  padding: 12px 14px;
  border-radius: 12px;
  border: 1px solid var(--glass-brd);
  background: rgba(255, 255, 255, 0.7);
  font: 400 15px var(--font);
  color: var(--ink);
  user-select: text;
}
.modal__input:focus { outline: none; border-color: rgba(24, 24, 27, 0.28); }
.modal__input::placeholder { color: #b6b6bd; }
.modal__notice { font-size: 12.5px; line-height: 1.5; color: var(--ink-dim); }

.modal__switch { font-size: 13px; color: var(--ink-dim); text-align: center; }
.modal__link {
  appearance: none;
  border: 0;
  background: none;
  padding: 0;
  color: var(--ink);
  font: inherit;
  text-decoration: underline;
  cursor: pointer;
}

/* ---------------------------------------------------------------- panel */
.panel {
  position: fixed;
  top: 118px;
  left: 24px;
  width: min(390px, calc(100vw - 48px));
  max-height: calc(100vh - 150px);
  display: flex;
  flex-direction: column;
  border-radius: 22px;
  padding: 0;
  z-index: 35;

  /* "Surface Layer": oklab(0.98 0 0 / 0.75) ≈ rgba(250,250,250,.75), 16px blur */
  background: var(--glass);
  border: 1px solid var(--glass-brd);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  box-shadow:
    0 24px 60px rgba(24, 24, 27, 0.12),
    inset 0 1px 0 rgba(255, 255, 255, 0.7);

  opacity: 0;
  transform: translateX(-30px) scale(0.98);
  pointer-events: none;
  transition: opacity 0.45s cubic-bezier(0.22, 1, 0.36, 1),
              transform 0.45s cubic-bezier(0.22, 1, 0.36, 1);
  overflow: hidden;
}
.panel.is-open {
  opacity: 1;
  transform: translateX(0) scale(1);
  pointer-events: auto;
}
/* subtle top sheen */
.panel::before {
  content: "";
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 40%;
  background: linear-gradient(180deg, rgba(255,255,255,0.5), transparent);
  pointer-events: none;
}

.panel__close {
  position: absolute;
  top: 14px; right: 14px;
  width: 32px; height: 32px;
  border-radius: 50%;
  border: 1px solid var(--glass-brd);
  background: rgba(24, 24, 27, 0.04);
  color: var(--ink);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  z-index: 2;
  transition: background 0.2s ease;
}
.panel__close:hover { background: rgba(24, 24, 27, 0.08); }

/* vertical rhythm: 32px between major blocks (head / list / disclaimer) */
.panel__head {
  padding: 32px 28px 24px;
  border-bottom: 1px solid var(--glass-brd);
}
.panel__region {
  display: inline-block;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--emphasis);
  margin-bottom: 10px;
}
.panel__name {
  font-size: 26px;
  font-weight: 400;
  letter-spacing: -0.01em;
  line-height: 1.15;
}
.panel__muscles {
  margin-top: 8px;
  font-size: 13px;
  color: var(--ink-dim);
  font-weight: 400;
}
.panel__blurb {
  margin-top: 16px;
  font-size: 15px;
  line-height: 1.55;
  color: #3f3f46;
}

.panel__isolate {
  margin-top: 14px;
  padding: 7px 14px;
  border-radius: 999px;
  border: 1px solid var(--glass-brd);
  background: rgba(24, 24, 27, 0.04);
  color: var(--ink);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.02em;
  cursor: pointer;
  transition: background 0.2s ease, border-color 0.2s ease, color 0.2s ease;
}
.panel__isolate:hover { background: rgba(24, 24, 27, 0.09); }
.panel__isolate.is-active {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}

.panel__list {
  padding: 12px 16px 8px;
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* exercise card */
.ex {
  border-radius: 14px;
  padding: 4px 4px;
  margin: 10px 6px;
  transition: background 0.2s ease;
}
.ex__btn {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 12px;
  text-align: left;
  background: transparent;
  border: 0;
  color: var(--ink);
  padding: 12px 12px;
  border-radius: 12px;
  cursor: pointer;
}
.ex__btn:hover { background: rgba(24, 24, 27, 0.035); }
.ex__tag {
  flex: 0 0 auto;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  padding: 4px 8px;
  border-radius: 7px;
}
.ex__tag--stretch { background: rgba(245, 4, 4, 0.1); color: var(--emphasis); }
.ex__tag--strengthen { background: rgba(217, 113, 60, 0.14); color: var(--strengthen); }
.ex__meta { flex: 1 1 auto; min-width: 0; }
.ex__name { font-size: 15px; font-weight: 500; }
.ex__time { font-size: 12px; color: var(--ink-dim); margin-top: 2px; }
.ex__chev {
  flex: 0 0 auto;
  color: var(--ink-dim);
  transition: transform 0.3s ease;
  font-size: 13px;
}
.ex.is-open .ex__chev { transform: rotate(90deg); }

.ex__body {
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.4s cubic-bezier(0.22, 1, 0.36, 1);
}
.ex.is-open .ex__body { max-height: 460px; }
.ex__inner { padding: 2px 14px 16px; }
.ex__why {
  font-size: 13px;
  line-height: 1.55;
  color: #52525b;
  font-style: italic;
  margin-bottom: 12px;
  padding-left: 12px;
  border-left: 2px solid rgba(15, 143, 179, 0.35);
}
.ex__steps { list-style: none; counter-reset: step; }
.ex__steps li {
  position: relative;
  padding: 6px 0 6px 30px;
  font-size: 13.5px;
  line-height: 1.55;
  color: #3f3f46;
  counter-increment: step;
}
.ex__steps li::before {
  content: counter(step);
  position: absolute;
  left: 0; top: 5px;
  width: 20px; height: 20px;
  border-radius: 50%;
  background: rgba(24, 24, 27, 0.05);
  color: var(--emphasis);
  font-size: 11px;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
}

.panel__disclaimer {
  padding: 20px 28px 24px;
  font-size: 11px;
  line-height: 1.5;
  color: #a1a1aa;
  border-top: 1px solid var(--glass-brd);
}

/* ------------------------------------------------------------------ sheet */
/* The console is the whole app now, so this is the app's primary surface.
   Desktop: a fixed right-hand column beside the model. Mobile: a bottom
   sheet that is always 100vh tall and slid down by half via a transform
   (framer-motion owns that `y`, see ProgressSheet), so expanding to full
   screen is a GPU transform rather than an animated height. */
.sheet {
  position: fixed;
  top: 0; right: 0; bottom: 0;
  width: min(460px, 42vw);
  z-index: 35;
  display: flex;
  flex-direction: column;
  background: var(--glass);
  border-left: 1px solid var(--glass-brd);
  backdrop-filter: blur(18px);
  -webkit-backdrop-filter: blur(18px);
}

.sheet__pane {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding: 22px 20px 20px;
}
/* Top-aligned, not vertically centred: the intro is the first thing a new
   user reads, so it starts where their eye already is. */
.sheet__pane--center {
  justify-content: flex-start;
  /* Centers the intro block as a whole on a wide/full-width sheet (the
     mobile breakpoint expands .sheet to 100% viewport width), so extra
     space is split evenly left/right instead of collecting only on the
     right while the text hugs the left edge. .sheet__intro's own max-width
     keeps it from stretching to fill under this. */
  align-items: center;
  gap: 18px;
  padding-bottom: 40px;
  /* Outer gutter for narrow sheets, where the intro block fills edge-to-edge
     minus this, same role the old fixed padding played. */
  padding-left: 32px;
  padding-right: 32px;
}
.sheet__intro {
  width: 100%;
  max-width: 420px;
  /* The block centers; the heading and lede inside it stay left-aligned
     against the block's own edge, which is the actual request, not
     centered text, a centered column of left-aligned text. */
  text-align: left;
  /* Same 18px the pane's own flex gap used to provide when the heading and
     lede were its direct children, wrapping them in this container moved
     them off that flex row, so the gap has to be restored here instead. */
  display: flex;
  flex-direction: column;
  gap: 32px;
}

.sheet__head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  flex: 0 0 auto;
}
.sheet__title {
  font-size: 19px;
  font-weight: 500;
  letter-spacing: -0.015em;
  line-height: 1.25;
}
.sheet__sub {
  margin-top: 5px;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--ink-dim);
}
.sheet__display {
  font-size: clamp(28px, 6.6vw, 38px);
  font-weight: 500;
  line-height: 1.1;
  letter-spacing: -0.03em;
}
.sheet__lede {
  font-size: 14.5px;
  line-height: 1.55;
  color: var(--ink-dim);
  max-width: 42ch;
}

/* The only scroll region in the app, the card list. */
.sheet__scroll {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding-bottom: 8px;
}

/* ------------------------------------------------------------------ buttons */
.btn {
  appearance: none;
  border: 0;
  border-radius: 999px;
  font: 500 14px var(--font);
  cursor: pointer;
  transition: transform 0.15s ease, opacity 0.15s ease, background 0.15s ease;
}
.btn:active { transform: scale(0.985); }
.btn--primary {
  flex: 0 0 auto;
  align-self: stretch;
  padding: 15px 22px;
  background: var(--ink);
  color: var(--dark-ink);
}
.btn--primary:disabled { opacity: 0.4; cursor: default; }
.sheet__pane--center .btn--primary { align-self: flex-start; }
.btn--ghost {
  flex: 0 0 auto;
  padding: 8px 14px;
  font-size: 13px;
  background: rgba(24, 24, 27, 0.06);
  color: var(--ink);
}
.btn--ghost:hover { background: rgba(24, 24, 27, 0.1); }
.btn--back {
  align-self: flex-start;
  padding: 8px 14px 8px 10px;
  font-size: 13px;
  background: rgba(24, 24, 27, 0.06);
  color: var(--ink);
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.composer__input {
  flex: 1 1 auto;
  min-height: 150px;
  resize: none;
  border-radius: 16px;
  border: 1px solid var(--glass-brd);
  background: rgba(255, 255, 255, 0.6);
  padding: 14px 16px;
  font: 400 14px/1.65 var(--font);
  color: var(--ink);
  user-select: text;
}
.composer__input:focus { outline: none; border-color: rgba(24, 24, 27, 0.28); }
.composer__input::placeholder { color: #b6b6bd; }
.composer__error { font-size: 12.5px; line-height: 1.5; color: var(--flag); }

/* ------------------------------------------------------------- score cards */
/* Dark cards on a light app: the scores are the one thing on screen that
   should stop you, and inverting them out of the surface they sit on is the
   cheapest way to say so without adding chrome. */
.scorecard,
.selected {
  --accent: #a1a1aa;
  --wash: rgba(40, 40, 46, 0.5);
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 14px;
  width: 100%;
  text-align: left;
  padding: 16px 18px 18px;
  border-radius: 24px;
  border: 1px solid rgba(255, 255, 255, 0.07);
  background:
    radial-gradient(120% 90% at 0% 0%, var(--wash) 0%, transparent 62%),
    linear-gradient(155deg, #17171a 0%, #0c0c0e 100%);
  color: var(--dark-ink);
  box-shadow: 0 10px 30px rgba(24, 24, 27, 0.12);
}
.scorecard {
  appearance: none;
  font: inherit;
  cursor: pointer;
  transition: transform 0.18s ease, box-shadow 0.18s ease;
}
.scorecard:hover { transform: translateY(-1px); box-shadow: 0 14px 34px rgba(24, 24, 27, 0.18); }
.scorecard:active { transform: scale(0.995); }

.scorecard__head,
.selected__head {
  display: flex;
  align-items: center;
  gap: 12px;
}
/* Portrait, not a circle: the badge holds a whole standing figure, so it
   needs the height to render one legibly, and enough padding that the figure
   isn't pressed against the edges. */
.scorecard__badge {
  flex: 0 0 auto;
  width: 56px; height: 88px;
  display: grid;
  place-items: center;
  padding: 8px 6px;
  border-radius: 14px;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.08);
}
.scorecard__headtext {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.scorecard__name {
  font-size: 17px;
  font-weight: 500;
  letter-spacing: -0.01em;
}
.scorecard__status {
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--emphasis);
}
.scorecard__chev {
  flex: 0 0 auto;
  width: 22px; height: 22px;
  color: rgba(244, 244, 245, 0.5);
}
.scorecard__chev svg {
  width: 100%; height: 100%;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.scorecard__body {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding-top: 14px;
  border-top: 1px solid rgba(255, 255, 255, 0.07);
}
.scorecard__score,
.selected__score {
  font-size: 46px;
  font-weight: 300;
  line-height: 1;
  letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
}
.selected__score { font-size: 34px; margin-left: auto; }
.scorecard__headline,
.selected__headline {
  font-size: 21px;
  font-weight: 500;
  line-height: 1.25;
  letter-spacing: -0.02em;
}
.scorecard__blurb {
  font-size: 14px;
  line-height: 1.55;
  color: rgba(244, 244, 245, 0.72);
}
.scorecard__meta {
  margin-top: 2px;
  font-size: 11.5px;
  letter-spacing: 0.02em;
  color: rgba(244, 244, 245, 0.42);
  font-variant-numeric: tabular-nums;
}
.selected {
  flex: 0 0 auto;
  gap: 10px;
}
.selected__headline {
  font-size: 19px;
  margin-top: 2px;
}

/* ------------------------------------------------------------ stretch cards */
/* One card per stretch in the plan. Same dark shell as the score cards had;
   the head is a done-circle plus the name, the body a demo gif and how to
   do it. Crossing a stretch off strikes the name and fades the card. */
/* The slot the card sits in; it is what the list reflows around. flex-shrink
   0 matters: while it folds shut it gets overflow: hidden, which would let
   the column's overflow shrink it straight to zero and skip the fold. */
.stretch-slot { position: relative; flex: 0 0 auto; }

/* Swipeable: sideways drag is the card's, vertical scrolling stays the
   list's. will-change keeps the drag on the compositor. */
.stretch {
  --accent: #7fb3e8; --wash: rgba(28, 56, 88, 0.5);
  cursor: grab;
  touch-action: pan-y;
  user-select: none;
  -webkit-user-select: none;
  will-change: transform;
}
.stretch:active { cursor: grabbing; }
.stretch:hover { transform: none; box-shadow: 0 10px 30px rgba(24, 24, 27, 0.12); }
.stretch:active { transform: none; }
/* Finishing: a white layer under the content comes up (framer drives its
   opacity from the swipe distance or the tick), and the card's `color` is
   driven across from light to dark at the same time, so every part of the
   card is painted with currentColor rather than a fixed white. */
.stretch__white {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: #fff;
  opacity: 0;
  pointer-events: none;
}
.stretch > *:not(.stretch__white) { position: relative; }
.stretch .scorecard__chev { color: currentColor; opacity: 0.5; }
.stretch .stretch__fold { background: color-mix(in srgb, currentColor 8%, transparent); color: currentColor; }
.stretch .stretch__fold:hover { background: color-mix(in srgb, currentColor 13%, transparent); }
.stretch .stretch__desc { color: currentColor; }
.stretch .stretch__how { color: color-mix(in srgb, currentColor 70%, transparent); }
.stretch .stretch__dose,
.stretch .stretch__dose b { color: currentColor; }
.stretch .stretch__sets,
.stretch .stretch__body { border-top-color: color-mix(in srgb, currentColor 10%, transparent); }
/* The box's own `color` is the tick, so its ring is taken from the card's
   ink variable rather than from currentColor. */
.stretch .stretch__check { border-color: color-mix(in srgb, var(--card-ink, #f4f4f5) 45%, transparent); color: var(--tick, #0c0c0e); }
.stretch .stretch__check:hover { border-color: color-mix(in srgb, var(--card-ink, #f4f4f5) 85%, transparent); }
.stretch .stretch__check.is-done { background: var(--card-ink, #f4f4f5); border-color: var(--card-ink, #f4f4f5); }
/* The strike is drawn across a span that hugs the title's text, not the
   flex-stretched name cell, so it stops at the last letter. */
.stretch__title { position: relative; display: inline-block; max-width: 100%; }
.stretch__strike {
  position: absolute;
  left: 0; right: 0;
  top: 55%;
  height: 2px;
  background: currentColor;
  transform-origin: left center;
  border-radius: 1px;
}

.stretch__check {
  flex: 0 0 auto;
  width: 30px; height: 30px;
  appearance: none;
  border-radius: 50%;
  border: 1.5px solid rgba(244, 244, 245, 0.45);
  background: transparent;
  color: #0c0c0e;
  cursor: pointer;
  display: grid;
  place-items: center;
  transition: background 0.15s ease, border-color 0.15s ease, transform 0.15s ease;
}
.stretch__check:hover { border-color: rgba(244, 244, 245, 0.85); }
.stretch__check:active { transform: scale(0.9); }
.stretch__check svg {
  width: 16px; height: 16px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2.4;
  stroke-linecap: round;
  stroke-linejoin: round;
  opacity: 0;
  transition: opacity 0.15s ease;
}
.stretch__check.is-done { background: var(--dark-ink); border-color: var(--dark-ink); }
.stretch__check.is-done svg { opacity: 1; }

.stretch__open {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 12px;
  appearance: none;
  border: 0;
  background: transparent;
  padding: 0;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.stretch__open .scorecard__name { flex: 1 1 auto; min-width: 0; }

.stretch__body { gap: 10px; }
.stretch__media {
  display: flex;
  flex-direction: column;
  gap: 4px;
  appearance: none;
  border: 0;
  background: transparent;
  padding: 0;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.stretch__fold {
  display: flex;
  align-items: center;
  justify-content: space-between;
  appearance: none;
  border: 0;
  border-radius: 12px;
  padding: 10px 12px;
  background: rgba(255, 255, 255, 0.06);
  color: var(--dark-ink);
  font: 500 13.5px var(--font);
  cursor: pointer;
  transition: background 0.15s ease;
}
.stretch__fold:hover { background: rgba(255, 255, 255, 0.1); }
.stretch__fold svg {
  width: 18px; height: 18px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
/* The fold row and its text sit in one wrapper with no gap of their own, so
   the space between them belongs to the animated text (as top padding on the
   inner span) and shrinks away with it instead of vanishing on unmount. Height
   is animated by framer-motion, so the outer span only clips. */
.stretch__foldwrap { display: flex; flex-direction: column; }
.stretch__text { display: block; overflow: hidden; }
.stretch__text-inner {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 12px 4px 0;
}
.stretch__gif {
  display: block;
  width: 100%;
  max-height: 220px;
  object-fit: contain;
  border-radius: 14px;
  background: #fff;
}
.stretch__desc {
  font-size: 15px;
  line-height: 1.5;
  letter-spacing: -0.005em;
}
.stretch__how {
  font-size: 13.5px;
  line-height: 1.55;
  color: rgba(244, 244, 245, 0.68);
}

/* The peek-state card: name plus the muscles it works as tappable chips. */
.stretch-detail { --accent: #7fb3e8; --wash: rgba(28, 56, 88, 0.5); flex: 1 1 auto; min-height: 0; }
.stretch-detail__muscles {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-content: flex-start;
  min-height: 0;
  overflow-y: auto;
  scrollbar-width: none;
}
.stretch-detail__muscles::-webkit-scrollbar { display: none; }
.chip {
  --chip: #7fb3e8;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  appearance: none;
  padding: 8px 13px 8px 10px;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.1);
  background: rgba(255, 255, 255, 0.06);
  color: var(--dark-ink);
  font: 500 13px var(--font);
  cursor: pointer;
  transition: background 0.15s ease, transform 0.15s ease;
}
.chip:hover { background: rgba(255, 255, 255, 0.11); }
.chip:active { transform: scale(0.97); }
.chip.is-lead { border-color: rgba(255, 255, 255, 0.22); background: rgba(255, 255, 255, 0.1); }
.chip__dot { width: 9px; height: 9px; border-radius: 50%; background: var(--chip); box-shadow: 0 0 8px var(--chip); }

/* The plan's own words, as the first card in the list. */
.plan-intro { --accent: #5fd3a2; --wash: rgba(24, 66, 52, 0.55); gap: 10px; cursor: default; }
.plan-intro:hover { transform: none; box-shadow: 0 10px 30px rgba(24, 24, 27, 0.12); }
.plan-intro__text { font-size: 16px; line-height: 1.5; letter-spacing: -0.01em; }
.plan-intro__caution {
  padding: 10px 12px;
  border-radius: 12px;
  background: rgba(255, 125, 107, 0.12);
  border: 1px solid rgba(255, 125, 107, 0.35);
  font-size: 13.5px;
  line-height: 1.5;
  color: #ffb4a8;
}
.plan-intro__progress {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: 4px;
}
.plan-intro__label {
  flex: 0 0 auto;
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: rgba(244, 244, 245, 0.55);
}
.progress {
  flex: 1 1 auto;
  height: 8px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.1);
  overflow: hidden;
}
.progress__fill {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: var(--accent);
  transition: width 0.5s cubic-bezier(0.22, 1, 0.36, 1);
}

/* Today's progress, its own card above the plan. */
.today { --accent: #5fd3a2; gap: 10px; }

/* Streak: a big tabular number with its unit, the last seven days as dots
   on the right. Amber when alive, quiet grey when there is nothing to keep. */
.streak { --accent: #a1a1aa; gap: 8px; }
.streak.is-alive { --accent: #f5b544; }
.streak__head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.streak__best { font-size: 12px; color: rgba(244, 244, 245, 0.55); font-variant-numeric: tabular-nums; }
.streak__row { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; }
.streak__count { display: inline-flex; align-items: baseline; gap: 6px; transform-origin: left bottom; }
.streak__num {
  font-size: 44px;
  line-height: 1;
  font-weight: 500;
  letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
  color: var(--dark-ink);
}
.streak__unit { font-size: 14px; color: rgba(244, 244, 245, 0.6); }
.streak__week { display: inline-flex; align-items: center; gap: 6px; padding-bottom: 6px; }
.streak__dot {
  display: block;
  width: 10px; height: 10px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.12);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.08);
}
.streak__dot.is-done { background: var(--accent); box-shadow: 0 0 10px color-mix(in srgb, var(--accent) 55%, transparent); }
.streak__dot.is-today:not(.is-done) {
  box-shadow: inset 0 0 0 1.5px var(--accent);
  animation: streakBreathe 2.4s ease-in-out infinite;
}
@keyframes streakBreathe { 0%, 100% { opacity: 0.55; } 50% { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .streak__dot.is-today { animation: none; } }
.today__head { display: flex; align-items: baseline; justify-content: space-between; }
.today__pct {
  font-size: 30px;
  font-weight: 300;
  line-height: 1;
  letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
}
.today__sub { font-size: 12px; color: rgba(244, 244, 245, 0.5); font-variant-numeric: tabular-nums; }
/* The bow when everything is done: a highlight sweeps along the bar from
   left to right, a band of light travelling the length it just filled,
   and the number pops. */
.progress__fill { position: relative; overflow: hidden; }
.progress__fill::after {
  content: '';
  position: absolute;
  top: -4px; bottom: -4px;
  left: 0;
  width: 38%;
  border-radius: 999px;
  background: linear-gradient(90deg, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.85) 50%, rgba(255, 255, 255, 0) 100%);
  filter: blur(1px);
  transform: translateX(-120%);
  opacity: 0;
}
.today.is-complete .progress__fill::after { animation: barSweep 1.1s cubic-bezier(0.23, 1, 0.32, 1) 0.1s both; }
.today.is-complete .today__pct { animation: pctBow 0.9s cubic-bezier(0.22, 1, 0.36, 1) 0.15s both; color: var(--emphasis); }
.today.is-complete .today__sub { color: var(--emphasis); }
@keyframes barSweep {
  0% { transform: translateX(-120%); opacity: 0; }
  10% { opacity: 1; }
  90% { opacity: 1; }
  100% { transform: translateX(300%); opacity: 0; }
}
@keyframes pctBow {
  0% { transform: scale(1); }
  40% { transform: scale(1.35); }
  100% { transform: scale(1); }
}
.progress { overflow: visible; }
.today__pct { display: inline-block; transform-origin: right center; }

/* The plan card; tapping it drops the sheet to the live body's pain map. */
.plan-intro--tap { appearance: none; border: 0; font: inherit; text-align: left; cursor: pointer; }
.plan-intro--tap:hover { transform: translateY(-1px); }
.plan-intro__copy { display: flex; flex-direction: column; gap: 8px; }
.plan-intro__changes { font-size: 12.5px; line-height: 1.5; color: rgba(244, 244, 245, 0.6); }

/* The check-in's pain sliders, one row per tracked muscle. */
.scores { display: flex; flex-direction: column; gap: 6px; }
.scores__row {
  --chip: #7fb3e8;
  display: grid;
  grid-template-columns: minmax(0, 1fr) 1.2fr 24px;
  align-items: center;
  gap: 10px;
  font-size: 13px;
  color: var(--ink);
}
.scores__name { display: inline-flex; align-items: center; gap: 8px; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.scores__row input[type="range"] { width: 100%; accent-color: var(--flag); margin: 0; }
.scores__value { text-align: right; font-variant-numeric: tabular-nums; color: var(--ink-dim); }
.checkin__added { display: flex; flex-wrap: wrap; gap: 8px; }
.checkin__added .chip { background: var(--ink); color: var(--dark-ink); border-color: transparent; }
.checkin__hint { font-size: 12.5px; color: var(--ink-dim); }

/* Equipment chips on the intake and check-in forms: off is an outline, on
   is filled in ink, the same on/off language as the check-in's added
   muscles. */
.gear { position: relative; flex: 0 0 auto; }
/* Reads as a form field: the question small above, what is chosen below,
   the chevron on the right. */
.gear__toggle {
  width: 100%;
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  appearance: none;
  padding: 9px 40px 9px 14px;
  border-radius: 12px;
  border: 1px solid rgba(24, 24, 27, 0.12);
  background: rgba(255, 255, 255, 0.6);
  color: var(--ink);
  font: 500 14px var(--font);
  text-align: left;
  cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease;
}
.gear__toggle:hover, .gear__toggle.is-open { background: rgba(255, 255, 255, 0.9); border-color: rgba(24, 24, 27, 0.2); }
.gear__label { font-size: 12px; font-weight: 500; color: var(--ink-dim); }
.gear__value { max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.gear__chevron {
  position: absolute; right: 14px; top: 50%;
  width: 13px; height: 13px; margin-top: -6.5px;
  fill: none; stroke: var(--ink-dim); stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round;
  transition: transform 0.18s ease;
}
.gear__toggle.is-open .gear__chevron { transform: rotate(180deg); }
/* The list floats over what follows rather than pushing it down, so the
   text box and the button stay put while a choice is made. */
.gear__menu {
  position: absolute;
  left: 0; right: 0; top: calc(100% + 6px);
  z-index: 5;
  list-style: none;
  padding: 6px;
  border-radius: 14px;
  background: #fff;
  border: 1px solid rgba(24, 24, 27, 0.1);
  box-shadow: 0 14px 34px rgba(24, 24, 27, 0.18);
  transform-origin: top center;
}
.gear__option {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 10px;
  appearance: none;
  border: 0;
  padding: 9px 10px;
  border-radius: 9px;
  background: transparent;
  color: var(--ink);
  font: 500 13.5px var(--font);
  text-align: left;
  cursor: pointer;
  transition: background 0.12s ease;
}
.gear__option:hover { background: rgba(24, 24, 27, 0.05); }
.gear__check {
  flex: 0 0 auto;
  width: 18px; height: 18px;
  display: grid; place-items: center;
  border-radius: 6px;
  border: 1.5px solid rgba(24, 24, 27, 0.25);
  transition: background 0.12s ease, border-color 0.12s ease;
}
.gear__check svg { width: 12px; height: 12px; fill: none; stroke: var(--dark-ink); stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.gear__option.is-on .gear__check { background: var(--ink); border-color: var(--ink); }
.gear__note { padding: 8px 10px 4px; font-size: 12px; color: var(--ink-dim); }
.gear__divider { height: 1px; margin: 4px 6px; background: rgba(24, 24, 27, 0.08); list-style: 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;
}
/* On a phone the sheet is the bottom half of the screen, so the list
   opens upward over the model, where the room is. */
@media (max-width: 620px) {
  .gear__menu { top: auto; bottom: calc(100% + 6px); transform-origin: bottom center; }
}
/* The check-in has more in it than fits at peek height, so its pane scrolls
   and the text box keeps a fixed, usable size instead of shrinking to zero. */
.checkin { overflow-y: auto; scrollbar-width: none; }
.checkin::-webkit-scrollbar { display: none; }
.checkin .dictate { flex: 0 0 auto; }
.checkin .composer__input { min-height: 110px; }
.checkin .btn--primary { flex: 0 0 auto; }
.checkin-home { justify-content: flex-start; }
.btn--danger { background: var(--capture); color: #fff; }
.btn--danger:hover { background: var(--capture-hi); }

/* ------------------------------------------------------------ compose
   A chat: bubbles in a scrolling transcript, proposals as small rows with
   an Add button, a one-line box to type in at the foot. */
.compose { gap: 12px; }
.chat {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  scrollbar-width: none;
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 2px 0;
}
.chat::-webkit-scrollbar { display: none; }
.chat__starter { display: flex; flex-wrap: wrap; gap: 8px; }
.chat__suggest { border-color: rgba(24, 24, 27, 0.14); background: transparent; color: var(--ink); padding: 8px 12px; }
.chat__suggest:hover { background: rgba(24, 24, 27, 0.06); }
.chat__msg { display: flex; flex-direction: column; gap: 8px; max-width: 92%; }
.chat__msg--user { align-self: flex-end; align-items: flex-end; }
.chat__msg--assistant { align-self: flex-start; align-items: flex-start; }
.chat__bubble {
  padding: 10px 14px;
  border-radius: 16px;
  font-size: 14px;
  line-height: 1.5;
  letter-spacing: -0.005em;
  white-space: pre-wrap;
  user-select: text;
}
.chat__msg--user .chat__bubble { background: var(--ink); color: var(--dark-ink); border-bottom-right-radius: 6px; }
.chat__msg--assistant .chat__bubble { background: rgba(255, 255, 255, 0.75); border: 1px solid var(--glass-brd); color: var(--ink); border-bottom-left-radius: 6px; }
.chat__typing { display: inline-flex; gap: 4px; align-items: center; min-height: 40px; }
.chat__typing span { width: 6px; height: 6px; border-radius: 50%; background: var(--ink-dim); animation: chatDot 1.2s ease-in-out infinite; }
.chat__typing span:nth-child(2) { animation-delay: 0.15s; }
.chat__typing span:nth-child(3) { animation-delay: 0.3s; }
@keyframes chatDot { 0%, 60%, 100% { opacity: 0.35; transform: translateY(0); } 30% { opacity: 1; transform: translateY(-3px); } }
.chat__proposals { display: flex; flex-direction: column; gap: 6px; width: 100%; }
.chat__addall { align-self: flex-start; }
.proposal {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 8px 8px 8px;
  border-radius: 14px;
  background: var(--dark-bg);
  color: var(--dark-ink);
  border: 1px solid var(--dark-brd);
}
.proposal__gif { flex: 0 0 auto; width: 44px; height: 44px; border-radius: 10px; object-fit: cover; background: #fff; }
.proposal__gif--none { display: grid; place-items: center; background: rgba(255, 255, 255, 0.08); color: var(--dark-ink-dim); font-size: 18px; }
.proposal__text { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.proposal__title { font-size: 14px; font-weight: 500; letter-spacing: -0.01em; }
.proposal__meta { font-size: 12px; color: var(--dark-ink-dim); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.proposal__add {
  flex: 0 0 auto;
  appearance: none;
  border: 0;
  border-radius: 999px;
  padding: 8px 14px;
  background: var(--dark-ink);
  color: var(--dark-bg);
  font: 500 13px var(--font);
  cursor: pointer;
  transition: transform 0.15s ease, opacity 0.15s ease;
}
.proposal__add:active { transform: scale(0.96); }
.proposal__add:disabled { background: transparent; color: var(--dark-ink-dim); cursor: default; }
.proposal.is-added { opacity: 0.7; }
.chat__compose { position: relative; flex: 0 0 auto; display: flex; }
.chat__input { min-height: 46px; max-height: 120px; padding: 12px 52px 12px 16px; line-height: 1.4; }
.chat__send {
  position: absolute;
  right: 6px; bottom: 6px;
  width: 34px; height: 34px;
  appearance: none;
  border: 0;
  border-radius: 50%;
  background: var(--ink);
  color: var(--dark-ink);
  cursor: pointer;
  display: grid;
  place-items: center;
  transition: transform 0.15s ease, opacity 0.15s ease;
}
.chat__send svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; }
.chat__send:active { transform: scale(0.94); }
.chat__send:disabled { opacity: 0.35; cursor: default; }

/* ------------------------------------------------------------ the gate
   The weekly check-in card, shuttered like a shop front until it is due:
   metal bars across the body of the card, a padlock in the middle, and
   the countdown up on the lintel where it can be read through the bars'
   absence. See CheckinGate in CheckinSheet.jsx for the unlock sequence. */
.gate { --accent: #7fb3e8; --wash: rgba(28, 56, 88, 0.5); gap: 12px; cursor: default; }
.gate:hover { transform: none; box-shadow: 0 10px 30px rgba(24, 24, 27, 0.12); }
.gate__head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.gate__clock {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  font-size: 12.5px;
  color: rgba(244, 244, 245, 0.6);
  white-space: nowrap;
}
.gate__clock-time { font-variant-numeric: tabular-nums; font-weight: 600; color: var(--dark-ink); letter-spacing: 0.02em; }
.gate.is-open .gate__clock { color: var(--emphasis); font-weight: 600; }
.gate__front { position: relative; min-height: 148px; border-radius: 14px; overflow: hidden; }
.gate__body { display: flex; flex-direction: column; gap: 14px; padding: 2px; }
.gate__text { font-size: 15px; line-height: 1.5; letter-spacing: -0.01em; }
.gate__go { align-self: flex-start; padding: 12px 20px; background: var(--dark-ink); color: var(--dark-bg); }
.gate__go:disabled { opacity: 0.4; cursor: default; }

/* Bars fill the front top to bottom with a hairline of dark between them,
   so the closed shutter reads as one corrugated surface. Each is brushed
   steel: a light lip along the top edge, a shadowed underside. */
.gate__shutter {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  gap: 3px;
  background: #121214;
  pointer-events: none;
}
.gate__bar {
  flex: 1 1 0;
  min-height: 0;
  border-radius: 2px;
  background: linear-gradient(180deg, #d4d4d8 0%, #f4f4f5 18%, #b4b4ba 48%, #8a8a91 76%, #6b6b72 100%);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.55), inset 0 -1px 0 rgba(0, 0, 0, 0.35), 0 1px 2px rgba(0, 0, 0, 0.35);
  will-change: transform;
}
/* One light tone for the whole lock, so it reads as a single object on
   the shutter; the keyhole is the shutter's own dark showing through. */
.gate__lock {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 44px;
  height: 52px;
  filter: drop-shadow(0 4px 10px rgba(0, 0, 0, 0.45));
}
.gate__shackle { fill: none; stroke: #f4f4f5; stroke-width: 5; stroke-linecap: round; }
.gate__lockbody { fill: #f4f4f5; }
.gate__keyhole { fill: #3f3f46; stroke: #3f3f46; stroke-width: 2.5; }

/* A muscle isolated from a stretch's list: the others step back. */
.chip.is-focused { background: var(--dark-ink); color: var(--dark-bg); border-color: transparent; }
.chip.is-dim { opacity: 0.45; }

/* The dose row at the foot of a card: "3 × 30s hold" and a box per set. */
.stretch__sets {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding-top: 12px;
  border-top: 1px solid rgba(255, 255, 255, 0.07);
}
.stretch__dose { font-size: 13px; color: rgba(244, 244, 245, 0.72); }
.stretch__dose b { font-weight: 500; color: var(--dark-ink); }
.stretch__boxes { display: flex; gap: 8px; }

/* Dictation on the intake: a mic in the corner of the text box. */
.dictate { position: relative; display: flex; flex: 1 1 auto; min-height: 0; }
.dictate .composer__input { width: 100%; padding-right: 56px; }
.dictate__mic {
  position: absolute;
  right: 10px; bottom: 10px;
  width: 38px; height: 38px;
  appearance: none;
  border: 0;
  border-radius: 50%;
  background: var(--ink);
  color: var(--dark-ink);
  cursor: pointer;
  display: grid;
  place-items: center;
  transition: transform 0.15s ease, background 0.15s ease;
}
.dictate__mic svg {
  width: 18px; height: 18px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.dictate__mic:active { transform: scale(0.94); }
.dictate__mic.is-recording { background: var(--capture); animation: capturePulse 1.6s ease-in-out infinite; }
.dictate__mic.is-busy { opacity: 0.5; cursor: default; }
.dictate__status { font-size: 12.5px; line-height: 1.5; color: var(--ink-dim); }


/* The body image inside each badge, contained, never cropped or stretched. */
.glyph {
  height: 100%;
  width: auto;
  max-width: 100%;
  object-fit: contain;
  display: block;
}

/* ---------------------------------------------------------------- wordmark */
.wordmark {
  position: fixed;
  top: 22px; left: 24px;
  z-index: 40;
  font: 700 24px "Inter Tight", var(--font);
  letter-spacing: -0.02em;
  color: var(--ink);
  pointer-events: none;
}

/* ------------------------------------------------------------------ tabbar */
.tabbar {
  position: fixed;
  left: 50%;
  bottom: 22px;
  transform: translateX(-50%);
  z-index: 45;
  display: flex;
  align-items: center;
  gap: 12px;
  /* Oura's proportions: 70% of the screen's width (282pt on a 402pt
     iPhone), 64pt tall, the tabs sharing it evenly. */
  width: min(70vw, 340px);
  padding: 8px;
  border-radius: 999px;
  background: var(--dark-bg);
  border: 1px solid var(--dark-brd);
  box-shadow: 0 12px 34px rgba(24, 24, 27, 0.22);
}
.tabbar__item {
  appearance: none;
  border: 0;
  background: transparent;
  cursor: pointer;
  padding: 12px 24px;
  border-radius: 999px;
  font: 500 13px var(--font);
  color: var(--dark-ink-dim);
  transition: background 0.15s ease, color 0.15s ease;
}
/* Every tab is a glyph, not a word, sized for a thumb: the bar is 64px
   tall, which the pages' 112px bottom padding still clears. */
.tabbar__item { flex: 1 1 0; }
.tabbar__item svg,
.tabbar__capture svg {
  display: block;
  width: 26px; height: 26px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.tabbar__item svg, .tabbar__capture svg { margin: -2px auto; }
.tabbar__item:hover { color: var(--dark-ink); }
/* The lit pill slides between tabs (a shared layout animation) rather than
   jumping; the button itself stays transparent. */
.tabbar__item { position: relative; transition: color 0.2s ease; }
.tabbar__item.is-active { color: var(--dark-ink); }
.tabbar__item svg { position: relative; }
.tabbar__pill { position: absolute; inset: 0; border-radius: 999px; background: rgba(255, 255, 255, 0.12); }

/* Every page button is the same quiet glyph; the active one is lit. */
.tabbar__item,
.tabbar__capture { min-width: 76px; text-align: center; white-space: nowrap; }
.tabbar__capture {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  appearance: none;
  border: 0;
  padding: 7px 18px;
  border-radius: 999px;
  background: var(--capture);
  color: #fff;
  font: 500 13px var(--font);
  cursor: pointer;
  transition: background 0.15s ease, transform 0.15s ease;
}
.tabbar__capture:hover { background: var(--capture-hi); }
.tabbar__capture:active { transform: scale(0.97); }
.tabbar__capture.is-active { background: var(--capture-hi); }
/* The + is the app's one call to action, so it carries a red halo until the
   button has been pressed once (see is-glowing in App). The pieces of the
   shadow are named rather than inlined into the keyframes, so the dim and
   bright ends stay legible next to each other. */
:root {
  --glow-dur: 2.2s;
  --glow-dim: 0.73;
  --glow-bright: 0.95;
  --glow-blur: 21px;
  --glow-spread: 6px;
  --glow-halo-blur: 40px;
  --glow-halo: 0.75;
}
.tabbar__capture.is-glowing { animation: captureGlow var(--glow-dur) ease-in-out infinite; }

/* Holds dim, blooms fast, holds bright, a beat rather than a shimmer. */
@keyframes captureGlow {
  0%, 18% { box-shadow: var(--glow-off); }
  46%, 62% { box-shadow: var(--glow-on); }
  92%, 100% { box-shadow: var(--glow-off); }
}

:root {
  --glow-off:
    0 0 4px 0 rgba(229, 72, 77, var(--glow-dim)),
    0 0 10px 0 rgba(229, 72, 77, calc(var(--glow-dim) * 0.45));
  --glow-on:
    0 0 var(--glow-blur) var(--glow-spread) rgba(229, 72, 77, var(--glow-bright)),
    0 0 var(--glow-halo-blur) calc(var(--glow-spread) * 2.5) rgba(229, 72, 77, var(--glow-halo));
}

/* Recording replaces the glow with its own pulse so the two never stack. */
.tabbar__capture.is-recording { animation: capturePulse 1.6s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce) {
  .tabbar__capture.is-glowing {
    animation: none;
    box-shadow: 0 0 16px 3px rgba(229, 72, 77, 0.6), 0 0 36px 8px rgba(229, 72, 77, 0.3);
  }
}
/* Hold-to-dictate: the button has to survive a long press without the OS
   text-selection / callout gestures firing on top of it. */
.tabbar__capture {
  touch-action: manipulation;
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
}
.tabbar__capture.is-recording { background: var(--capture-hi); }
@keyframes capturePulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(240, 96, 95, 0.55); }
  50% { box-shadow: 0 0 0 9px rgba(240, 96, 95, 0); }
}

/* --------------------------------------------------------------- mobile */
@media (max-width: 620px) {
  /* The model gets the top half of the screen and nothing else, the sheet
     owns the bottom half, and the canvas is sized to the half it actually
     occupies (not the viewport) so the figure is framed inside that slice
     instead of being centred behind the sheet. */
  body.is-console #scene { height: 50%; }

  .sheet {
    top: 0; left: 0; right: 0; bottom: 0;
    width: 100%;
    border-left: 0;
    border-top: 1px solid var(--glass-brd);
    border-radius: 26px 26px 0 0;
    box-shadow: 0 -14px 40px rgba(24, 24, 27, 0.12);
  }
  /* Bands at both ends of the sheet: the floating tab bar owns the bottom,
     and the wordmark owns the top. When the sheet is expanded it covers the
     whole screen, so without the top band its heading would run straight
     under ATOMO. */
  .sheet__pane { padding: 64px 16px 92px; }
  /* The intro only ever shows with the sheet at half height, so the wordmark
     is over the model rather than over this pane, no top band needed. It
     does have to clear the sheet's 26px corner radius, though, or the first
     line starts inside the curve. */
  .sheet__pane--center { padding-top: 34px; padding-bottom: 108px; padding-left: 24px; padding-right: 24px; }

  /* Centred in the blank band the sheet reserves for it (the pane's 92px
     bottom padding): 92px band, 50px bar, so 21px of clearance either side. */
  .wordmark { top: 18px; left: 18px; }
  .tabbar { bottom: 21px; }

  .panel {
    top: auto; right: 0; left: 0; bottom: 0;
    width: 100%;
    max-height: 68vh;
    border-radius: 22px 22px 0 0;
    transform: translateY(40px);
  }
  .panel.is-open { transform: translateY(0); }

  /* In peek the sheet is still a full-height element slid halfway off the
     bottom, so only its top half is actually on screen. The selected pane is
     the one pane shown in that state, so it has to be bounded by the visible
     half, left at the sheet's own height, the flip card sizes itself to
     100vh and its back face lands below the fold. */
  /* Extra bottom padding over the usual 92px: this pane starts below the
     grip, so its box runs slightly past the viewport, and the card has to
     stop clear of the floating tab bar rather than tuck under it. */
  /* The peek pane starts halfway down the screen, so the wordmark is over
     the model rather than over it, no top band needed here. */
  .sheet__pane--selected { flex: 0 0 auto; height: 50vh; padding-top: 14px; padding-bottom: 106px; }

  /* Same half-height bound as the selected pane: the sheet sits at peek so
     the flagged muscles stay in view above it, and there is no tab bar
     underneath to reserve a band for. */
  .onb__pane { flex: 0 0 auto; height: 50vh; padding: 18px 16px 20px; gap: 10px; }
  /* Same pane over the console, where the floating tab bar owns the bottom.
     The sheet only slides down 38% here (see CaptureSheet), so the pane gets
     the extra height and the text box keeps a usable size above the bar. */
  .sheet__pane--capture { height: 62vh; padding-bottom: 92px; }
  .sheet__pane--capture.checkin, .sheet__pane--capture.compose { height: 70vh; }
  /* The chat must not grow past the visible 70% of the sheet, or its input
     ends up under the fold; the transcript scrolls inside instead. */
  .sheet__pane--capture.compose { flex: 0 0 auto; }
  .sheet__pane--capture .dictate { min-height: 0; }
  .onb__pane .composer__input { min-height: 72px; }
  .onb__pane .sheet__sub { font-size: 12px; }

  .hint { display: none; }
}

/* ------------------------------------------------------------------ search */
.search {
  flex: 0 0 auto;
  position: relative;
  display: flex;
  align-items: center;
}
.search__ico {
  position: absolute;
  left: 14px;
  width: 16px; height: 16px;
  fill: none;
  stroke: var(--ink-dim);
  stroke-width: 1.8;
  stroke-linecap: round;
  pointer-events: none;
}
.search__input {
  width: 100%;
  appearance: none;
  border-radius: 999px;
  border: 1px solid var(--glass-brd);
  background: rgba(255, 255, 255, 0.6);
  padding: 11px 14px 11px 38px;
  font: 400 14px var(--font);
  color: var(--ink);
  user-select: text;
}
.search__input:focus { outline: none; border-color: rgba(24, 24, 27, 0.28); }
.search__input::placeholder { color: #a8a8b0; }
.search__input::-webkit-search-cancel-button { display: none; }
.search__clear {
  position: absolute;
  right: 8px;
  width: 26px; height: 26px;
  display: grid;
  place-items: center;
  appearance: none;
  border: 0;
  border-radius: 50%;
  background: rgba(24, 24, 27, 0.08);
  color: var(--ink-dim);
  font-size: 17px;
  line-height: 1;
  cursor: pointer;
}
.search__empty {
  padding: 18px 2px;
  font-size: 13.5px;
  color: var(--ink-dim);
}

/* -------------------------------------------------------------- flip card */
/* Motion reproduced from the Framer "Flip Card 3D Animations" component,
   perspective 1500, preserve-3d, hidden backfaces, rotateY 0 -> -180. The
   rotation itself is animated by framer-motion on .flip__inner; everything
   here is the 3D staging that makes that rotation read as a card with two
   physical sides rather than a squashing rectangle. */
.flip {
  flex: 1 1 auto;
  min-height: 0;
  perspective: 1500px;
}
.flip__inner {
  position: relative;
  width: 100%;
  height: 100%;
  transform-style: preserve-3d;
}
.flip__face {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
  width: 100%;
  height: 100%;
  text-align: left;
  appearance: none;
  font: inherit;
  cursor: pointer;
  padding: 16px 18px;
  border-radius: 24px;
  border: 1px solid rgba(255, 255, 255, 0.07);
  background:
    radial-gradient(120% 90% at 0% 0%, var(--wash) 0%, transparent 62%),
    linear-gradient(155deg, #17171a 0%, #0c0c0e 100%);
  color: var(--dark-ink);
  box-shadow: 0 10px 30px rgba(24, 24, 27, 0.14);
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
  overflow: hidden;
}
.flip__face--back { transform: rotateY(180deg); }

/* Pushed to the bottom of whichever face is showing, the flip has no other
   affordance, so the card has to say it can be turned over. */
.flip__cue {
  margin-top: auto;
  flex: 0 0 auto;
  font-size: 11.5px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: rgba(244, 244, 245, 0.4);
}
/* The face is a fixed box (shortest on the mobile peek, where the sheet is
   only half the viewport) with overflow:hidden, and the narration sentence
   varies a lot in length by status. Clamped so a long one can never push
   "Tap for trends" below the visible edge, the one thing on this face that
   must never be the part that goes missing. */
.flip__face--front .scorecard__blurb {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  overflow: hidden;
}
.flip__backhead {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding-bottom: 12px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.07);
}

/* ---------------------------------------------------------------- trends */
.trends {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding: 4px 0 2px;
}
.trends__empty {
  font-size: 13.5px;
  line-height: 1.55;
  color: rgba(244, 244, 245, 0.6);
}

.trend { display: flex; flex-direction: column; gap: 7px; }
.trend__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
}
.trend__name {
  font-size: 13.5px;
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.trend__delta {
  flex: 0 0 auto;
  font-size: 12px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.trend__delta.is-up { color: #5fd3a2; }
.trend__delta.is-down { color: #ff7d6b; }
.trend__delta.is-flat { color: rgba(244, 244, 245, 0.45); }

.trend__bars {
  display: flex;
  align-items: flex-end;
  gap: 3px;
  height: 52px;
}
.trend__bar {
  flex: 1 1 0;
  min-width: 3px;
  border-radius: 2px 2px 0 0;
  background: rgba(244, 244, 245, 0.24);
}
/* The most recent session is the one the user is asking about. */
.trend__bar.is-last { background: var(--accent); }

.trend__foot {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  font-size: 11px;
  color: rgba(244, 244, 245, 0.42);
  font-variant-numeric: tabular-nums;
}
.trend__sessions { color: rgba(244, 244, 245, 0.3); }

/* The peek pane hands its whole remaining height to the flip card so both
   faces share one box and the back's charts have somewhere to live. */
.sheet__pane--selected { gap: 12px; }

/* -------------------------------------------------------------- voice pill */
/* Sits just above the tab bar while dictating, showing what is being heard so
   the user can tell it is working before they let go. */
.voicepill {
  position: fixed;
  left: 50%;
  bottom: 84px;
  transform: translateX(-50%);
  z-index: 46;
  display: flex;
  align-items: center;
  gap: 9px;
  max-width: min(88vw, 420px);
  padding: 10px 16px;
  border-radius: 999px;
  background: var(--dark-bg);
  border: 1px solid var(--dark-brd);
  box-shadow: 0 12px 34px rgba(24, 24, 27, 0.28);
  color: var(--dark-ink);
  font: 500 13px var(--font);
}
.voicepill.is-error { color: #ff9d94; }
.voicepill__dot {
  flex: 0 0 auto;
  width: 9px; height: 9px;
  border-radius: 50%;
  background: var(--capture-hi);
  animation: capturePulse 1.6s ease-in-out infinite;
}
.voicepill__text {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The one-time hold-to-dictate tip, in the same pill as the recording and
   error states so it reads as the same voice affordance speaking. */
.voicepill--hint {
  appearance: none;
  cursor: pointer;
  font: 500 13px var(--font);
  gap: 10px;
  padding-right: 12px;
  animation: voicepillIn 0.35s ease both;
}
.voicepill--hint:hover { background: #131315; }
.voicepill__mic {
  flex: 0 0 auto;
  width: 15px; height: 15px;
  fill: none;
  stroke: var(--capture-hi);
  stroke-width: 1.7;
  stroke-linecap: round;
}
.voicepill__close {
  flex: 0 0 auto;
  font-size: 16px;
  line-height: 1;
  color: var(--dark-ink-dim);
}
.voicepill--hint .voicepill__text { white-space: normal; }
@keyframes voicepillIn {
  from { opacity: 0; transform: translate(-50%, 8px); }
  to { opacity: 1; transform: translate(-50%, 0); }
}

/* Weekly-sets chart with the effective-range band behind the bars. */
.volume__plot { position: relative; height: 58px; }
.volume__band {
  position: absolute;
  left: 0; right: 0;
  border-radius: 3px;
  background: rgba(95, 211, 162, 0.16);
  border-top: 1px dashed rgba(95, 211, 162, 0.4);
  border-bottom: 1px dashed rgba(95, 211, 162, 0.28);
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}
/* Self-labeled, since this shape is exactly what's left visible when a
   group has no sets to bar-chart yet, nothing else on the card would tell
   you it's a target zone rather than a plotted value. */
.volume__band-label {
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: rgba(95, 211, 162, 0.65);
  white-space: nowrap;
}
.volume__bars { position: absolute; inset: 0; height: 100%; }
.trend__bar.is-inband { background: rgba(95, 211, 162, 0.75); }
.trend__bar.is-inband.is-last { background: #5fd3a2; }

/* Keeps the score honest about what it is: a dose proxy, not a measurement. */
.sheet__note {
  padding: 4px 2px 2px;
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--ink-dim);
}

/* The + becomes a live waveform while dictating. Bars are scaled on the
   compositor (transform, not height) so the meter stays smooth at 60fps
   alongside the 3D scene. */
.wave {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 2.5px;
  height: 18px;
}
.wave__bar {
  width: 2.5px;
  height: 100%;
  border-radius: 999px;
  background: #fff;
  transform: scaleY(0.2);
  transform-origin: center;
  will-change: transform;
}
@media (prefers-reduced-motion: reduce) {
  .wave__bar { transform: scaleY(0.55) !important; }
}

/* ------------------------------------------------------------------ review */
/* What the parser made of the text, shown before any of it becomes a score.
   The muscle mapping is its biggest guess, so the chips carry as much weight
   here as the numbers do. */
.review {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding-right: 2px;
}
.review__row {
  display: flex;
  flex-direction: column;
  gap: 7px;
  padding: 12px 14px;
  border-radius: 16px;
  border: 1px solid var(--glass-brd);
  background: rgba(255, 255, 255, 0.55);
}
.review__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
}
.review__name { font-size: 14.5px; font-weight: 500; }
.review__nums {
  flex: 0 0 auto;
  font-size: 12px;
  color: var(--ink-dim);
  font-variant-numeric: tabular-nums;
}
.review__groups { display: flex; flex-wrap: wrap; gap: 5px; }
.review__chip {
  padding: 3px 9px;
  border-radius: 999px;
  font-size: 11.5px;
  font-weight: 500;
  background: rgba(229, 72, 77, 0.12);
  color: #a8302f;
}
/* Half credit reads as a lighter chip, the same distinction the score makes. */
.review__chip.is-secondary {
  background: rgba(24, 24, 27, 0.06);
  color: var(--ink-dim);
  font-weight: 400;
}
/* The textarea gives up room once there is a review list to show. */
.composer__input.is-compact { flex: 0 0 auto; min-height: 62px; }

/* ---------------------------------------------------------------- calendar */
/* A month of training as one square per day, Sunday to Saturday. Same dark
   card shell as the scores so it reads as another row in the same list. */
.calcard {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 16px 18px 14px;
  border-radius: 24px;
  border: 1px solid rgba(255, 255, 255, 0.07);
  background: linear-gradient(155deg, #17171a 0%, #0c0c0e 100%);
  color: var(--dark-ink);
  box-shadow: 0 10px 30px rgba(24, 24, 27, 0.12);
}
.calcard__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}
.calcard__month {
  font-size: 14.5px;
  font-weight: 500;
  letter-spacing: -0.01em;
}
.calcard__nav {
  flex: 0 0 auto;
  width: 28px; height: 28px;
  display: grid;
  place-items: center;
  appearance: none;
  border: 0;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.07);
  color: rgba(244, 244, 245, 0.75);
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease, opacity 0.15s ease;
}
.calcard__nav:hover { background: rgba(255, 255, 255, 0.14); color: var(--dark-ink); }
.calcard__nav:disabled { opacity: 0.25; cursor: default; }
.calcard__nav svg {
  width: 15px; height: 15px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.calcard__grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 4px;
}
.calcard__weekday {
  text-align: center;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.06em;
  color: rgba(244, 244, 245, 0.35);
  padding-bottom: 2px;
}
/* Square cells: the padding trick keeps them square at any column width. */
.calcard__day {
  position: relative;
  aspect-ratio: 1 / 1;
  display: grid;
  place-items: center;
  border-radius: 5px;
  font-size: 10.5px;
  font-variant-numeric: tabular-nums;
  /* Untrained days sit just above the card, the tint (or lack of it) on the
     cell itself is what carries "trained vs. not"; the date number stays
     fully legible either way. */
  background: rgba(255, 255, 255, 0.06);
  color: var(--dark-ink);
}
/* Tinted days carry dark text, since the scale runs toward white. */
/* Tinted days get their ink colour inline with the tint (see CalendarCard),
   so an untinted day can never be mistaken for one. */
.calcard__day.is-hot { font-weight: 500; }
.calcard__day.is-blank { background: transparent; }
.calcard__day.is-today { box-shadow: inset 0 0 0 1.5px rgba(244, 244, 245, 0.55); }

.calcard__legend {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
  font-size: 10.5px;
  color: rgba(244, 244, 245, 0.4);
}
.calcard__scale {
  width: 72px;
  height: 5px;
  border-radius: 999px;
  background: linear-gradient(90deg, #ffffff, #c8181c);
}

/* ------------------------------------------------------------------ reveal */
/* Sequential load-in for the console list, each row arrives a beat after the
   one above it. animation-delay is set per row in ProgressSheet. */
.reveal {
  animation: revealIn 420ms cubic-bezier(0.22, 1, 0.36, 1) both;
}
@keyframes revealIn {
  from { opacity: 0; transform: translateY(14px); }
  to { opacity: 1; transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
  .reveal { animation: none; }
  .today.is-complete .progress__fill::after,
  .today.is-complete .today__pct { animation: none; }
}


/* ===================================================================== home
   The home page: week rings, Daily Sets, the pain check-in and My Data. A
   page of its own, the model is hidden behind it. */
body.is-home #scene, body.is-home #loader, body.is-home .wordmark { visibility: hidden; }
.home__brand { display: flex; align-items: center; gap: 9px; font: 700 24px "Inter Tight", var(--font); letter-spacing: -0.02em; padding: 0 8px 4px; }
/* The spheres mark, a touch taller than the caps so it reads at their weight. */
.home__logo { display: block; height: 22px; width: auto; }

.home {
  position: fixed;
  inset: 0;
  z-index: 35;
}
.home__scroll {
  height: 100%;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  padding: calc(env(safe-area-inset-top) + 22px) 16px calc(env(safe-area-inset-bottom) + 112px);
  display: flex;
  flex-direction: column;
  gap: 14px;
  max-width: 520px;
  margin: 0 auto;
}
.home__scroll::-webkit-scrollbar { display: none; }
.home__scroll > * { flex-shrink: 0; }
.home__empty { margin-top: 30vh; text-align: center; color: var(--ink-dim); }
.home__section { display: flex; flex-direction: column; gap: 14px; margin-top: 10px; }
.home__h2 { font-size: 26px; font-weight: 600; letter-spacing: -0.02em; padding: 0 4px; }
.home__note { font-size: 11.5px; line-height: 1.5; color: var(--ink-dim); text-align: center; padding: 6px 12px 0; }

/* The dark card every home section shares. */
.sets, .checkin-shell, .mobility, .painchart {
  border-radius: 26px;
  background: linear-gradient(165deg, #1c1d20, #0c0c0d 70%);
  border: 1px solid var(--dark-brd);
  box-shadow: 0 14px 34px rgba(24, 24, 27, 0.12);
  color: var(--dark-ink);
}
.card__eyebrow {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--emphasis);
}

/* ---------------------------------------------------------- week rings */

/* ---------------------------------------------------------- daily sets */
.sets { padding: 16px 16px 18px; overflow: hidden; }
.sets__fan { position: relative; height: 232px; margin: 0 -16px 14px; }
.sets__tile {
  position: absolute;
  top: 8px;
  width: 58%;
  height: 216px;
  left: 21%;
  border-radius: 18px;
  overflow: hidden;
  background: #fff;
  box-shadow: 0 16px 36px rgba(0, 0, 0, 0.45);
}
.sets__tile img { width: 100%; height: 100%; object-fit: contain; object-position: 50% 38%; }
.sets__tile--left, .sets__tile--right { top: 30px; height: 180px; width: 44%; opacity: 0.55; filter: saturate(0.6) brightness(0.8); }
.sets__tile--left { left: -14%; }
.sets__tile--right { left: 70%; }
.sets__tile--mid { z-index: 1; }
.sets__tile--mid::after {
  content: '';
  position: absolute;
  inset: 45% 0 0;
  background: linear-gradient(transparent, rgba(10, 10, 11, 0.82));
}
.sets__pill {
  position: absolute; top: 12px; left: 12px; z-index: 1;
  padding: 5px 12px;
  border-radius: 999px;
  background: #fff;
  color: var(--ink);
  font-size: 13px; font-weight: 600;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.12);
}
.sets__tile-text { position: absolute; left: 14px; right: 14px; bottom: 12px; z-index: 1; display: flex; flex-direction: column; gap: 2px; color: #fff; }
.sets__tile-text b { font-size: 17px; font-weight: 600; line-height: 1.2; }
.sets__tile-text span { font-size: 13px; color: rgba(255, 255, 255, 0.72); }
.sets__title { font-size: 34px; font-weight: 500; letter-spacing: -0.025em; padding: 0 4px; }
.sets__sub { margin-top: 2px; padding: 0 4px; font-size: 16px; color: var(--dark-ink-dim); }
.sets__start {
  appearance: none; border: 0;
  width: 100%;
  margin-top: 18px;
  padding: 17px;
  border-radius: 999px;
  background: var(--emphasis);
  color: #fff;
  font: 600 18px var(--font);
  display: flex; align-items: center; justify-content: center; gap: 10px;
  cursor: pointer;
  box-shadow: 0 10px 26px rgba(245, 4, 4, 0.32);
  transition: transform 0.15s ease;
}
.sets__start:active { transform: scale(0.98); }
.sets__start svg { width: 18px; height: 18px; fill: currentColor; }

/* ---------------------------------------------------------- check-in */
.checkin-shell { overflow: hidden; }
.checkin-card { padding: 18px 16px 16px; display: flex; flex-direction: column; gap: 12px; }
.checkin-card__q { font-size: 22px; font-weight: 500; letter-spacing: -0.015em; }
/* The pain dial (PainDial.jsx): the track is the whole control. --f is the
   fill, 0-1, driven from a motion value. */
.dial {
  position: relative;
  height: 54px;
  border-radius: 14px;
  background: rgba(255, 255, 255, 0.05);
  overflow: hidden;
  cursor: ew-resize;
  touch-action: pan-y; /* vertical swipes still scroll the page */
  -webkit-tap-highlight-color: transparent;
  transition: background 0.2s ease;
}
.dial.is-hot { background: rgba(255, 255, 255, 0.075); }
.dial:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
.dial__fill {
  position: absolute; inset: 0 auto 0 0;
  width: calc(var(--f) * 100%);
  background: rgba(255, 255, 255, 0.13);
  transition: background 0.2s ease;
}
.dial.is-hot .dial__fill { background: rgba(255, 255, 255, 0.18); }
.dial__ticks { position: absolute; inset: 0; pointer-events: none; opacity: 0; transition: opacity 0.2s ease; }
.dial.is-hot .dial__ticks { opacity: 1; }
.dial__ticks i { position: absolute; bottom: 7px; width: 1px; height: 7px; margin-left: -0.5px; background: rgba(255, 255, 255, 0.22); }
/* Inset from the fill's edge, and never closer to the left end than the
   inset itself. */
.dial__handle {
  position: absolute; top: 50%;
  left: max(calc(var(--f) * 100% - 11px), 8px);
  width: 3px; height: 22px; margin-top: -11px;
  border-radius: 2px;
  background: rgba(255, 255, 255, 0.6);
  pointer-events: none;
}
.dial__word, .dial__number { position: absolute; top: 50%; transform: translateY(-50%); pointer-events: none; }
.dial__word { left: 16px; font: 500 16px var(--font); color: var(--dark-ink); white-space: nowrap; }
.dial.is-empty .dial__word { color: var(--dark-ink-dim); }
.dial__number {
  right: 12px;
  display: flex; align-items: baseline; gap: 1px;
  padding: 4px 6px;
  border-radius: 8px;
  pointer-events: auto; /* the hover/hold that turns it into a text box */
  font: 500 17px ui-monospace, "SF Mono", Menlo, monospace;
  color: #fff;
  transition: background 0.2s ease;
}
.dial__number.is-holding { background: rgba(255, 255, 255, 0.12); }
.dial__of { font-size: 13px; color: var(--dark-ink-dim); }
.dial.is-empty .dial__digits { color: var(--dark-ink-dim); }
.dial__input {
  position: absolute; inset: 0;
  width: 100%;
  padding: 0 6px;
  border: 0; border-radius: 8px; outline: none;
  background: var(--card);
  box-shadow: inset 0 0 0 1.5px rgba(255, 255, 255, 0.5);
  color: #fff;
  font: inherit; font-size: 17px; /* 16px or more: iOS doesn't zoom in on focus */
  text-align: right;
  opacity: 0; pointer-events: none;
  user-select: text;
}
.dial.is-editing .dial__input { opacity: 1; pointer-events: auto; }
.dial.is-editing .dial__digits, .dial.is-editing .dial__of { visibility: hidden; }

/* The logged card's edit button: a pencil, not a word. */
.checkin-card__edit {
  appearance: none; border: 0;
  flex: none;
  width: 38px; height: 38px;
  display: grid; place-items: center;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.07);
  color: var(--dark-ink);
  cursor: pointer;
  transition: background 0.15s ease, transform 0.15s ease;
}
.checkin-card__edit:active { transform: scale(0.92); }
.checkin-card__edit svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.checkin-card__note {
  width: 100%;
  resize: none;
  padding: 12px 14px;
  border-radius: 14px;
  border: 1px solid var(--dark-brd);
  background: rgba(255, 255, 255, 0.05);
  color: var(--dark-ink);
  font: 16px/1.4 var(--font);
}
.checkin-card__note::placeholder { color: rgba(161, 161, 170, 0.8); }
.checkin-card__note:focus { outline: none; border-color: rgba(255, 255, 255, 0.28); }
.checkin-card__save {
  appearance: none; border: 0;
  padding: 14px;
  border-radius: 999px;
  background: #f4f4f5;
  color: var(--ink);
  font: 600 16px var(--font);
  cursor: pointer;
}
.checkin-card__save:disabled { opacity: 0.35; cursor: default; }
.checkin-card.is-done { flex-direction: row; flex-wrap: wrap; align-items: center; justify-content: space-between; }
.checkin-card__done { display: flex; align-items: center; gap: 14px; min-width: 0; flex: 1; }
.checkin-card__score { font-size: 38px; font-weight: 600; letter-spacing: -0.03em; font-variant-numeric: tabular-nums; }
.checkin-card__score small { font-size: 16px; color: var(--dark-ink-dim); font-weight: 500; }
/* The number is a pain score; the caption under it says so. */
.checkin-card__score { display: flex; flex-direction: column; align-items: center; line-height: 1; }
.checkin-card__score-label { margin-top: 5px; font-size: 11px; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; color: var(--dark-ink-dim); }
.checkin-card__done-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; font-size: 14px; color: var(--dark-ink-dim); }
.checkin-card__done-text b { color: var(--dark-ink); font-weight: 600; font-size: 15px; }
.checkin-card__done-text span { overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }

/* ---------------------------------------------------------- mobility */
.mobility { padding: 26px 18px 20px; display: flex; flex-direction: column; align-items: center; text-align: center; }
.mobility__ring { position: relative; width: 196px; height: 196px; }
.mobility__ring svg { width: 100%; height: 100%; }
.mobility__track { fill: none; stroke: rgba(255, 255, 255, 0.14); stroke-width: 16; }
.mobility__arc { fill: none; stroke: #4fbf8b; stroke-width: 16; stroke-linecap: round; }
.mobility__value { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; }
.mobility__value b { font-size: 64px; font-weight: 600; letter-spacing: -0.04em; line-height: 1; font-variant-numeric: tabular-nums; }
.mobility__value span { margin-top: 4px; font-size: 18px; color: var(--dark-ink-dim); }
.mobility__line { margin-top: 18px; font-size: 19px; line-height: 1.35; font-weight: 500; max-width: 26ch; text-wrap: balance; }
.mobility__line b { font-weight: 600; }
.mobility__line b.is-good { color: #6fd6a3; }
.mobility__line b.is-bad { color: #ff6b6b; }
.mobility__line--empty { font-size: 16px; font-weight: 400; color: var(--dark-ink-dim); max-width: 30ch; }
.mobility__since { margin-top: 6px; font-size: 13.5px; color: var(--dark-ink-dim); }
.mobility__chips { display: flex; flex-wrap: wrap; justify-content: center; gap: 8px; margin-top: 16px; }
.chip-stat { padding: 8px 14px; border-radius: 12px; background: rgba(79, 191, 139, 0.13); color: #6fd6a3; font-size: 15px; font-weight: 500; }

/* ---------------------------------------------------------- pain chart */
.painchart { padding: 18px 14px 16px; }
.painchart__head { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 0 4px 10px; }
.painchart__title { margin: 0; font-size: 17px; font-weight: 600; letter-spacing: -0.01em; color: #fff; white-space: nowrap; }
/* The span toggle: a pill that slides to the chosen span (layoutId). */
.span { display: flex; padding: 3px; border-radius: 999px; background: rgba(255, 255, 255, 0.07); }
.span__opt {
  position: relative;
  appearance: none; border: 0; background: none;
  min-width: 40px; padding: 6px 8px;
  border-radius: 999px;
  font: 600 12.5px var(--font);
  font-variant-numeric: tabular-nums;
  color: var(--dark-ink-dim);
  cursor: pointer;
  transition: color 0.2s ease;
}
.span__opt.is-on { color: var(--card); }
.span__pill { position: absolute; inset: 0; border-radius: 999px; background: #fff; }
.span__label { position: relative; }
/* The chart's own proportions (PainPlot's 320 x 150 viewBox), held for the
   empty state too. */
.painchart__body { position: relative; aspect-ratio: 320 / 150; }
.painchart__frame { position: absolute; inset: 0; }
.painchart__frame .painchart__empty { height: 100%; display: grid; place-items: center; margin: 0; }
.painchart__empty { padding: 30px 4px; text-align: center; color: var(--dark-ink-dim); font-size: 14px; }
.painplot { position: relative; }
.painplot__svg { display: block; width: 100%; height: auto; touch-action: pan-y; }
.painplot__grid { stroke: rgba(255, 255, 255, 0.08); stroke-width: 1; }
.painplot__tick { fill: var(--dark-ink-dim); font-size: 10px; font-family: var(--font); }
.painplot__line { fill: none; stroke: #ff4d4d; stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
.painplot__dot { fill: #ff4d4d; stroke: #111113; stroke-width: 2; transition: r 0.12s ease; }
.painplot__dot.is-weekly { fill: #111113; stroke: #ff4d4d; }
.painplot__cross { stroke: rgba(255, 255, 255, 0.3); stroke-width: 1; }
/* The tooltip's anchor: at the point, lifted above the plot, shifted by
   --tip-shift (centred, or flush to an edge near the ends). */
.painplot__tip-at {
  position: absolute;
  top: -6px;
  width: max-content;
  max-width: 220px;
  transform: translate(var(--tip-shift, -50%), -100%);
  transition: left 0.15s cubic-bezier(0.22, 1, 0.36, 1);
  pointer-events: none;
  z-index: 2;
}
.painplot__tip {
  max-width: 220px;
  padding: 8px 10px;
  border-radius: 12px;
  background: #f4f4f5;
  color: var(--ink);
  font-size: 12.5px;
  line-height: 1.35;
  display: flex; flex-direction: column; gap: 1px;
  pointer-events: none;
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.3);
}
.painplot__tip b { font-size: 15px; }
.painplot__tip span { color: var(--ink-dim); }
.painplot__tip em { font-style: normal; margin-top: 3px; }
.insights { margin: 12px 4px 0; padding-top: 14px; border-top: 1px solid var(--dark-brd); }
.insights__head { display: flex; gap: 8px; align-items: flex-start; font-size: 15.5px; line-height: 1.4; font-weight: 500; }
.insights__head svg { flex: none; width: 17px; height: 17px; margin-top: 2px; fill: var(--emphasis); }
.insights__list { list-style: none; display: flex; flex-direction: column; gap: 10px; margin-top: 12px; }
.insights__item { font-size: 14px; line-height: 1.45; color: var(--dark-ink-dim); }
.insights__kind {
  display: inline-block;
  margin-right: 8px;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 11.5px;
  font-weight: 600;
  vertical-align: 1px;
  background: rgba(255, 255, 255, 0.08);
  color: var(--dark-ink);
}
.insights__item--trigger .insights__kind { background: rgba(255, 77, 77, 0.16); color: #ff8a8a; }
.insights__item--relief .insights__kind { background: rgba(79, 191, 139, 0.14); color: #6fd6a3; }
.insights__flag { margin-top: 12px; padding: 10px 12px; border-radius: 12px; background: rgba(255, 196, 0, 0.12); color: #ffd666; font-size: 13.5px; line-height: 1.45; }

/* ================================================================ routine
   Today's routine before it starts (after Pliability's session sheet). */
.routine {
  position: fixed;
  inset: 0;
  z-index: 60;
  padding: calc(env(safe-area-inset-top) + 10px) 10px calc(env(safe-area-inset-bottom) + 10px);
  background: rgba(10, 10, 11, 0.55);
  display: flex;
}
.routine__card {
  flex: 1;
  max-width: 560px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  border-radius: 30px;
  background: #1a1a1c;
  color: var(--dark-ink);
  overflow: hidden;
  box-shadow: 0 30px 60px rgba(0, 0, 0, 0.4);
}
.routine__bar { display: flex; align-items: center; justify-content: space-between; padding: 14px 14px 6px; }
.routine__close, .player__close {
  appearance: none; border: 0;
  width: 44px; height: 44px;
  display: grid; place-items: center;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.08);
  color: #fff;
  cursor: pointer;
}
.routine__close svg, .player__close svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 2.2; stroke-linecap: round; }
.routine__pill { padding: 9px 18px; border-radius: 999px; background: rgba(255, 255, 255, 0.14); font-size: 16px; font-weight: 500; }
.routine__spacer { width: 44px; }
.routine__scroll { flex: 1; overflow-y: auto; padding: 8px 20px 20px; scrollbar-width: none; }
.routine__scroll::-webkit-scrollbar { display: none; }
.routine__title { font-size: 32px; font-weight: 500; letter-spacing: -0.025em; }
.routine__sub { margin-top: 2px; color: var(--dark-ink-dim); font-size: 15px; }
.routine__list { list-style: none; margin-top: 18px; display: flex; flex-direction: column; gap: 6px; }
.routine__row {
  appearance: none; border: 0; background: none; color: inherit;
  width: 100%;
  display: flex; align-items: center; gap: 16px;
  padding: 8px 0;
  text-align: left;
  font: inherit;
  cursor: pointer;
}
.routine__thumb { flex: none; width: 76px; height: 76px; border-radius: 14px; overflow: hidden; background: #fff; }
.routine__thumb img { width: 100%; height: 100%; object-fit: cover; }
.routine__name { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.routine__name b { font-size: 18px; font-weight: 500; line-height: 1.25; }
.routine__name span { font-size: 14px; color: var(--dark-ink-dim); }
.routine__chev { flex: none; width: 22px; height: 22px; fill: none; stroke: rgba(255, 255, 255, 0.85); stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; transition: transform 0.2s ease; }
.routine__row[aria-expanded="true"] .routine__chev { transform: rotate(90deg); }
.routine__item.is-done .routine__name b { color: var(--dark-ink-dim); }
.routine__how-wrap { overflow: hidden; }
.routine__how { padding: 0 0 10px 92px; font-size: 14px; line-height: 1.5; color: var(--dark-ink-dim); }
.routine__h3 { margin-top: 28px; font-size: 24px; font-weight: 500; letter-spacing: -0.02em; }
.routine__areas { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 14px; }
.routine__area { padding: 12px 20px; border-radius: 999px; background: rgba(255, 255, 255, 0.1); font-size: 17px; }
.routine__credit { margin-top: 22px; font-size: 11.5px; color: rgba(161, 161, 170, 0.7); }
.routine__foot { padding: 12px 20px 18px; border-top: 1px solid var(--dark-brd); }
.routine__tip { font-size: 13px; line-height: 1.45; color: var(--dark-ink-dim); text-align: center; margin-bottom: 12px; }
.routine__start {
  appearance: none; border: 0;
  width: 100%;
  padding: 18px;
  border-radius: 999px;
  background: #fff;
  color: var(--ink);
  font: 600 18px var(--font);
  cursor: pointer;
  transition: transform 0.15s ease;
}
.routine__start:active { transform: scale(0.98); }

/* ================================================================= player
   A guided session: progress with a notch between stretches, the stretch
   drawn in outline, the camera's view, and the hold timer. */
.player {
  position: fixed;
  inset: 0;
  z-index: 70;
  background: radial-gradient(120% 80% at 50% 30%, #1a1a1d, #070708);
  color: var(--dark-ink);
  display: flex;
  flex-direction: column;
  padding: calc(env(safe-area-inset-top) + 10px) 16px calc(env(safe-area-inset-bottom) + 16px);
}
.player__top { display: flex; align-items: center; gap: 12px; }
.player__progress { flex: 1; display: flex; gap: 5px; height: 8px; }
.player__seg { position: relative; flex: 1; border-radius: 999px; background: rgba(255, 255, 255, 0.14); overflow: hidden; }
.player__seg-done, .player__seg-live {
  position: absolute; top: 0; bottom: 0; left: 0; width: 100%;
  background: var(--emphasis);
  transform-origin: left center;
  transition: transform 0.4s ease;
}
.player__seg-live { background: rgba(245, 4, 4, 0.8); transition: none; transform: scaleX(0); }
.player__stage { position: relative; flex: 1; min-height: 0; display: flex; flex-direction: column; align-items: center; padding-top: 16px; }
.player__name { font-size: 24px; font-weight: 600; letter-spacing: -0.02em; text-align: center; }
.player__meta { margin-top: 2px; font-size: 14.5px; color: var(--dark-ink-dim); }
.figure { flex: 1; min-height: 0; width: 100%; display: flex; align-items: center; justify-content: center; }
.figure svg { width: min(100%, 44vh); height: auto; max-height: 100%; overflow: visible; }
.figure__line { fill: none; stroke: #f4f4f5; stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; transition: stroke 0.3s ease; }
.figure__accent { fill: rgba(245, 4, 4, 0.7); }
.figure--gif img { width: min(80%, 36vh); border-radius: 20px; background: #fff; }
.player__figure.is-locked .figure__line { stroke: #fff; filter: drop-shadow(0 0 6px rgba(245, 4, 4, 0.55)); }
.player__timer { position: relative; align-self: center; width: 196px; height: 196px; margin: 8px 0 18px; }
.player__time { font-size: 52px; font-weight: 600; letter-spacing: -0.03em; font-variant-numeric: tabular-nums; line-height: 1; }
.player__state { max-width: 140px; font-size: 13px; line-height: 1.3; color: var(--dark-ink-dim); text-wrap: balance; }
.player__timer.is-locked .player__state { color: #fff; }
.player__controls { display: flex; gap: 10px; }
.player__btn {
  appearance: none; border: 0;
  flex: 1;
  padding: 15px 10px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.1);
  color: #fff;
  font: 600 16px var(--font);
  cursor: pointer;
}
.player__btn--go { flex: 1.6; background: var(--emphasis); }

/* ============================================================== settings */
.page { position: fixed; inset: 0; z-index: 35; }
.settings__title { font-size: 34px; font-weight: 600; letter-spacing: -0.025em; padding: 4px 4px 6px; }
.settings__card {
  padding: 18px 16px;
  border-radius: 26px;
  background: linear-gradient(165deg, #1c1d20, #0c0c0d 70%);
  border: 1px solid var(--dark-brd);
  box-shadow: 0 14px 34px rgba(24, 24, 27, 0.12);
  color: var(--dark-ink);
  display: flex; flex-direction: column; gap: 14px;
}
.settings__who { display: flex; align-items: center; gap: 14px; }
.settings__avatar {
  flex: none;
  width: 52px; height: 52px;
  border-radius: 50%;
  display: grid; place-items: center;
  background: var(--emphasis);
  color: #fff;
  font-size: 19px; font-weight: 600;
}
.settings__id { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.settings__id b { font-size: 19px; font-weight: 600; }
.settings__id span { font-size: 14.5px; color: var(--dark-ink-dim); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.settings__since { font-size: 13.5px; color: var(--dark-ink-dim); }
/* The card is the button: no inner box, which read as a border around it. */
.settings__card--action { padding: 0; overflow: hidden; }
.settings__card--action .settings__logout { background: transparent; border-radius: 0; padding: 19px; }
.settings__card--action .settings__confirm { padding: 18px 14px 14px; }
.settings__logout, .settings__btn {
  appearance: none; border: 0;
  width: 100%;
  padding: 15px;
  border-radius: 18px;
  background: rgba(255, 255, 255, 0.06);
  color: #ff5c5c;
  font: 600 16px var(--font);
  cursor: pointer;
  transition: background 0.15s ease, transform 0.15s ease;
}
.settings__logout:active, .settings__btn:active { transform: scale(0.98); }
.settings__confirm { display: flex; flex-direction: column; gap: 12px; padding: 8px 6px 2px; }
.settings__confirm p { font-size: 15.5px; text-align: center; }
.settings__row { display: flex; gap: 10px; }
.settings__btn { color: var(--dark-ink); }
.settings__btn--danger { background: var(--emphasis); color: #fff; }
.settings__btn:disabled { opacity: 0.5; cursor: default; }
.settings__fine { font-size: 13.5px !important; line-height: 1.45; color: var(--dark-ink-dim); }
.settings__fine a { color: var(--dark-ink); text-underline-offset: 2px; }
/* The legal pages: one row each, a chevron on the right, hairlines between. */
.settings__card--links { padding: 4px 16px; gap: 0; }
.settings__link {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 15px 0;
  color: var(--dark-ink); text-decoration: none;
  font-size: 15.5px;
}
.settings__link + .settings__link { border-top: 1px solid var(--dark-brd); }
.settings__link svg { flex: none; width: 16px; height: 16px; fill: none; stroke: var(--dark-ink-dim); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.settings__link:active { opacity: 0.6; }

.painchart__more {
  appearance: none; border: 0;
  width: 100%;
  margin-top: 14px;
  padding: 13px 14px;
  border-radius: 16px;
  background: rgba(255, 255, 255, 0.07);
  color: var(--dark-ink);
  font: 500 15px var(--font);
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  text-align: left;
  cursor: pointer;
  transition: background 0.2s ease, transform 0.15s ease;
}
.painchart__more:active { transform: scale(0.985); }
.painchart__more svg { flex: none; width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; }

/* ========================================================= pain insights
   The red stage with its film grain; dark journal cards pinned around the
   AI mark and joined to it by soft wavy threads; the findings below. Small
   text on the red is near-black (white only for large type) to keep AA. */
.insp {
  position: fixed;
  inset: 0;
  background: #FF3131;
  color: #fff;
  font-family: "Inter Tight", var(--font);
}
.insp::before {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0.13;
  mix-blend-mode: multiply;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.4 -0.2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}
.insp__scroll {
  position: relative;
  height: 100%;
  overflow-y: auto;
  scrollbar-width: none;
  padding: calc(env(safe-area-inset-top) + 22px) 16px calc(env(safe-area-inset-bottom) + 112px);
  max-width: 960px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: 18px;
}
.insp__scroll::-webkit-scrollbar { display: none; }
.insp__scroll > * { flex-shrink: 0; }

.insp__flag { overflow: hidden; }
.insp__flag-inner {
  display: flex; gap: 12px; align-items: flex-start;
  padding: 14px 16px;
  border-radius: 18px;
  background: #FFF7EE;
  color: #2A1714;
  font: 15px/1.45 var(--font);
  box-shadow: 0 10px 30px rgba(120, 10, 10, 0.25);
}
.insp__flag-inner svg { flex: none; width: 22px; height: 22px; margin-top: 1px; fill: none; stroke: #B3261E; stroke-width: 2; stroke-linecap: round; }
.insp__flag-inner span { display: flex; flex-direction: column; gap: 2px; }
.insp__flag-inner b { font-weight: 600; }

.insp__head { padding: 2px 4px 0; }
.insp__eyebrow { font: 600 13px var(--font); letter-spacing: 0.1em; text-transform: uppercase; color: #2A0B0B; }
.insp__headline {
  margin-top: 8px;
  font-size: clamp(32px, 8.4vw, 40px);
  font-weight: 600;
  line-height: 1.08;
  letter-spacing: -0.025em;
  text-wrap: balance;
}
.insp__status { margin-top: 8px; font: 500 14px var(--font); color: #2A0B0B; }

/* ------------------------------------------------------------ the web */
.web { position: relative; width: 100%; }
.web__threads { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; pointer-events: none; }
.thread path { fill: none; stroke: #fff; stroke-width: 2.5; stroke-linecap: round; stroke-opacity: 0.85; transition: stroke-width 0.25s ease, stroke-opacity 0.25s ease, opacity 0.25s ease; }
.thread circle { fill: #fff; transition: opacity 0.25s ease; }
.thread--link path { stroke-width: 1.5; stroke-opacity: 0.4; }
.thread--link circle { opacity: 0.55; }
.thread--ghost path { stroke-opacity: 0.35; stroke-dasharray: 0.02 0.03; }
.thread--ghost circle { opacity: 0.35; }
.thread.is-lit path { stroke-width: 4.5; stroke-opacity: 1; }
.thread--link.is-lit path { stroke-width: 2.5; stroke-opacity: 0.8; }
.thread.is-dim { opacity: 0.3; }
.can-draw .thread:not(.thread--ghost) path { stroke-dasharray: 1; stroke-dashoffset: 1; animation: threadDraw 0.7s cubic-bezier(0.22, 1, 0.36, 1) var(--delay) forwards; }
.can-draw .thread circle { opacity: 0; animation: threadDot 0.25s ease calc(var(--delay) + 0.55s) forwards; }
.can-draw .thread--link circle { animation-name: threadDotLink; }
@keyframes threadDraw { to { stroke-dashoffset: 0; } }
@keyframes threadDot { to { opacity: 1; } }
@keyframes threadDotLink { to { opacity: 0.55; } }

.aimark { position: absolute; width: 0; height: 0; z-index: 2; pointer-events: none; }
.aimark__core, .aimark__ring {
  position: absolute;
  left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
}
.aimark__core {
  width: 64px; height: 64px;
  display: grid; place-items: center;
  background: #fff;
  box-shadow: 0 10px 30px rgba(120, 10, 10, 0.35);
}
.aimark__core svg { width: 32px; height: 29px; fill: #FF3131; }
.aimark__ring { width: 96px; height: 96px; border: 1.5px solid rgba(255, 255, 255, 0.32); }
.aimark__ring--outer { width: 136px; height: 136px; border-color: rgba(255, 255, 255, 0.16); }
.aimark.is-reading .aimark__ring--outer { animation: markPulse 1.8s ease-in-out infinite; }
@keyframes markPulse {
  0%, 100% { transform: translate(-50%, -50%) scale(1); opacity: 1; }
  50% { transform: translate(-50%, -50%) scale(1.12); opacity: 0.35; }
}

.jcard {
  position: absolute;
  z-index: 1;
  appearance: none;
  border: 0;
  padding: 12px 14px 12px;
  border-radius: 18px;
  background: #141416;
  color: #F5F3F1;
  text-align: left;
  display: flex; flex-direction: column; gap: 8px;
  box-shadow: 0 14px 30px rgba(110, 8, 8, 0.38), 0 2px 6px rgba(110, 8, 8, 0.25);
  cursor: pointer;
  font-family: "Inter Tight", var(--font);
  transition: filter 0.25s ease, box-shadow 0.25s ease;
}
/* Dimmed with a filter, not opacity: the card's entrance animation owns
   its inline opacity. */
.jcard.is-dim { filter: opacity(0.3); }
.jcard.is-lit { box-shadow: 0 0 0 2px #fff, 0 18px 36px rgba(110, 8, 8, 0.45); }
.jcard:focus-visible { outline: 3px solid #fff; outline-offset: 3px; }
.jcard__top { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.jcard__date { font-size: 13px; color: #9A9CA3; }
.jcard__note {
  font-size: 16px;
  line-height: 1.3;
  text-wrap: balance;
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
}
.jcard__note em { font-style: normal; color: #9A9CA3; }
.jcard--ghost {
  background: transparent;
  border: 1.5px dashed rgba(255, 255, 255, 0.5);
  box-shadow: none;
  cursor: default;
  justify-content: center;
  gap: 10px;
}
.jcard__bar { display: block; height: 9px; border-radius: 5px; background: rgba(255, 255, 255, 0.28); }
.jcard__bar--short { width: 55%; }

.pchip {
  flex: none;
  min-width: 30px;
  padding: 3px 9px;
  border-radius: 999px;
  font: 700 13px var(--font);
  text-align: center;
  font-variant-numeric: tabular-nums;
}
.pchip--high { color: #FF6B61; background: rgba(255, 107, 97, 0.14); }
.pchip--mid { color: #F2B35B; background: rgba(242, 179, 91, 0.14); }
.pchip--low { color: #56C795; background: rgba(86, 199, 149, 0.14); }

/* ------------------------------------------------------------ findings */
.insp__h2 { font: 600 13px var(--font); letter-spacing: 0.1em; text-transform: uppercase; color: #2A0B0B; padding: 0 4px 10px; }
.findings { list-style: none; display: flex; flex-direction: column; gap: 10px; }
.finding {
  appearance: none; border: 0;
  width: 100%;
  padding: 14px 16px;
  border-radius: 18px;
  background: #141416;
  color: #F5F3F1;
  text-align: left;
  display: flex; flex-direction: column; gap: 8px;
  cursor: pointer;
  box-shadow: 0 12px 26px rgba(110, 8, 8, 0.3);
  font-family: "Inter Tight", var(--font);
  transition: opacity 0.25s ease, box-shadow 0.25s ease, transform 0.15s ease;
}
.finding:active { transform: scale(0.99); }
.finding.is-dim { opacity: 0.3; }
.finding.is-lit { box-shadow: 0 0 0 2px #fff, 0 14px 30px rgba(110, 8, 8, 0.4); }
.finding:focus-visible { outline: 3px solid #fff; outline-offset: 3px; }
.finding__tag {
  align-self: flex-start;
  padding: 3px 10px;
  border-radius: 999px;
  font: 600 12px var(--font);
  letter-spacing: 0.02em;
}
.finding--trigger .finding__tag { color: #FF6B61; background: rgba(255, 107, 97, 0.14); }
.finding--relief .finding__tag { color: #56C795; background: rgba(86, 199, 149, 0.14); }
.finding--pattern .finding__tag { color: #F5F3F1; background: rgba(245, 243, 241, 0.12); }
.finding__text { font-size: 17px; line-height: 1.35; font-weight: 500; text-wrap: pretty; }
.finding__evidence { font-size: 13px; color: #9A9CA3; }
.insp__quiet { padding: 0 4px; font: 15px/1.45 var(--font); color: #2A0B0B; max-width: 46ch; }

.insp__chart {
  padding: 18px 14px 16px;
  border-radius: 24px;
  background: #141416;
  color: var(--dark-ink);
  box-shadow: 0 14px 30px rgba(110, 8, 8, 0.3);
}
.insp__foot { text-align: center; font: 13px var(--font); color: #2A0B0B; padding-top: 4px; }

@media (prefers-reduced-motion: reduce) {
  .can-draw .thread path, .can-draw .thread circle { animation: none; stroke-dashoffset: 0; opacity: 1; }
  .aimark.is-reading .aimark__ring--outer { animation: none; }
}

/* ============================================================ red stage
   The whole app on the Pain Insights palette: brand red with a faint film
   grain; content on near-black cards; large type white, small type near-black
   (white on this red only passes AA from 24px, or 18.7px bold). Red is kept
   for accents inside dark cards, where it reads. Later rules win, so this
   block restyles each area in one place. */
:root {
  --stage: #FF3131;
  --card: #141416;
  --card-ink: #F5F3F1;
  --card-dim: #9A9CA3;
  --stage-ink: #2A0B0B;      /* small text on the red */
  --emphasis: #FF3131;       /* accents, now only ever inside dark cards */
  --bg-0: #FF3131;
  --bg-1: #FF3131;
  --card-shadow: 0 14px 30px rgba(110, 8, 8, 0.35), 0 2px 6px rgba(110, 8, 8, 0.2);
}
html { background: var(--stage); }

/* One grain for every page, under everything; pages stay transparent so it
   shows through. Screens that cover the app (the session) carry their own. */
.grain-layer, .stage-grain::before {
  pointer-events: none;
  opacity: 0.13;
  mix-blend-mode: multiply;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.4 -0.2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}
.grain-layer { position: fixed; inset: 0; z-index: -1; }
.stage-grain, .player.stage-grain { background: var(--stage); }
.stage-grain::before { content: ''; position: absolute; inset: 0; }
.insp { background: transparent; }
.insp::before { display: none; }

/* ------------------------------------------------------------ onboarding */
.flow__title, .flow__stat { color: #fff; }
.flow__stat-unit { font-weight: 600; }
.flow__sub, .flow__lede, .flow__cite, .flow__feature-text, .timeline__what, .timeline__when,
.flow__hint, .flow .modal__label, .flow .modal__notice, .flow__code-msg.modal__notice { color: var(--stage-ink); }
.timeline__what b { color: #140404; }
.flow__feature-name { color: #fff; }
.flow__back { background: var(--card); color: #fff; }
.flow__progress { background: rgba(20, 5, 5, 0.18); }
.flow__progress-fill { background: #fff; }
.flow__dock--fade { background: linear-gradient(to bottom, rgba(255, 49, 49, 0), var(--stage) 28px); }
.btn--primary { background: var(--card); color: #fff; }
.flow__link, .flow__link:disabled { color: #140404; font-weight: 600; text-decoration: underline; text-underline-offset: 3px; }
.flow__link:disabled { opacity: 0.55; text-decoration: none; }
.flow__choice, .flow__chip, .flow__goal {
  background: var(--card);
  border-color: transparent;
  color: var(--card-ink);
  box-shadow: 0 8px 18px rgba(110, 8, 8, 0.22);
}
.flow__choice.is-on, .flow__chip.is-on { background: #fff; border-color: #fff; color: var(--card); }
.flow__goal.is-on { background: #fff; border-color: #fff; color: var(--card); }
.flow__goal-check { background: rgba(255, 255, 255, 0.1); }
.flow__goal.is-on .flow__goal-check { background: var(--stage); color: #fff; }
/* Translucent white rather than --card: the list runs from the red top of
   the stage into its black bottom, and a solid dark disc vanishes there. */
.flow__feature-icon { background: rgba(255, 255, 255, 0.14); color: var(--stage); }
.flow__toggle { background: rgba(20, 5, 5, 0.16); }
.flow__toggle-item { color: #140404; }
.flow__toggle-item.is-on { background: var(--card); color: #fff; }
.timeline::before { background: linear-gradient(to bottom, rgba(255, 255, 255, 0.35) 70%, rgba(255, 255, 255, 0)); }
.timeline__node { background: var(--card); color: #fff; }
.flow__badge { background: var(--card); color: #fff; }
.flow__wave i { background: #fff; }
.flow__code-box { background: var(--card); border-color: transparent; color: #fff; }
.flow__code:focus-within .flow__code-box.is-next { border-color: #fff; box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.35); }
.flow__code-box.is-wrong { border-color: #fff; }
.flow .modal__input { background: var(--card); border-color: transparent; color: #fff; }
.flow .modal__input::placeholder { color: #7d7f86; }
.flow .modal__input:focus { border-color: transparent; box-shadow: 0 0 0 2px #fff; }
.flow .composer__error, .flow__code-msg.composer__error {
  padding: 10px 12px;
  border-radius: 12px;
  background: #fff;
  color: #5A1210;
  font-weight: 500;
}

/* The sign-in popup: a dark card over a dimmed stage. */
.modal { background: rgba(40, 6, 6, 0.4); }
.modal__card { background: var(--card); border-color: transparent; color: var(--card-ink); box-shadow: 0 24px 60px rgba(60, 4, 4, 0.45); }
.modal__card .sheet__title { color: #fff; }
.modal__card .sheet__sub, .modal__card .modal__label, .modal__switch { color: var(--card-dim); }
.modal__card .modal__input { background: rgba(255, 255, 255, 0.06); border-color: transparent; color: #fff; }
.modal__card .modal__input:focus { box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.6); }
.modal__card .btn--primary { background: #fff; color: var(--card); }
.modal__card .composer__error { color: #FF8A80; }
.modal__close { background: rgba(255, 255, 255, 0.08); color: var(--card-dim); }
.modal__link { color: #fff; }

/* ------------------------------------------------------------------ home */
.home__brand, .home__h2, .settings__title { color: #fff; }
.home__note, .home__empty { color: var(--stage-ink); }
/* The week reads in white on the red; today is picked out by weight and a
   brighter ring rather than by a darker colour. */
.sets, .checkin-shell, .mobility, .painchart, .settings__card, .insp__chart, .routine__card {
  background: var(--card);
  border: 0;
  box-shadow: var(--card-shadow);
}
.sets__start { box-shadow: 0 10px 26px rgba(255, 49, 49, 0.3); }
.tabbar { background: var(--card); border-color: rgba(255, 255, 255, 0.06); box-shadow: 0 12px 30px rgba(90, 6, 6, 0.4); }

/* ---------------------------------------------------------------- routine */
.routine { background: rgba(40, 6, 6, 0.45); }

/* ----------------------------------------------------------------- player */
.player__name, .player__time, .player__summary-title { color: #fff; }
.player__meta, .player__state, .player__summary-sub, .player__timer.is-locked .player__state { color: var(--stage-ink); }
.player__close { background: var(--card); }
.player__seg { background: rgba(20, 5, 5, 0.18); }
.player__seg-done { background: #fff; }
.player__seg-live { background: rgba(255, 255, 255, 0.75); }
.player__figure {
  width: 100%;
  margin: 12px 0 0;
  border-radius: 28px;
  background: var(--card);
  box-shadow: var(--card-shadow);
}
.player__figure svg { padding: 6%; }
.player__btn { background: var(--card); }
.player__btn--go { background: #fff; color: var(--card); }

/* Keeps a covering screen's own grain beneath its content. */
.stage-grain::before { z-index: 0; }
.stage-grain > * { position: relative; z-index: 1; }

/* The camera preview is a button: tap to enlarge it over the stage, with the
   pose tracking's readout underneath; tap again to shrink it back. */

/* Where does it hurt: a touch darker than the render, so the back sits on
   the red stage rather than glowing off it. The render carries its own
   shadows now, so this stays light. Every layer (the base and the tinted
   zones) is dimmed alike, so a highlighted zone keeps its red. */
.backmap__figure img { filter: brightness(0.86) contrast(1.1) saturate(1.4); }



/* Edge to edge on iPhone: the tab bar sits above the home indicator. */
.tabbar { bottom: calc(env(safe-area-inset-bottom, 0px) + 10px); }

/* Daily Sets: the one white button on the page, so it reads as the thing
   to press. */
.sets__start { background: #fff; color: var(--card); box-shadow: 0 10px 26px rgba(0, 0, 0, 0.35); }

/* ------------------------------------------------------------ insights chat
   InsightsChat.jsx: the guide's messages are dark cards on the red, the
   person's are white; the composer and suggestion chips sit in a dock
   just above the tab bar (10px + 64px tall). Small text on the red is
   near-black, per the stage's contrast rule. */
.ichat { position: absolute; inset: 0; }
.ichat__scroll {
  height: 100%;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  max-width: 620px;
  margin: 0 auto;
  padding: calc(env(safe-area-inset-top) + 22px) 16px calc(env(safe-area-inset-bottom) + 220px);
}
.ichat__scroll::-webkit-scrollbar { display: none; }
/* The connected read (PainInsightsPage, embedded) laid out as it was on
   its own page, then the conversation under it. */
.insp-embed { display: flex; flex-direction: column; gap: 18px; color: #fff; font-family: "Inter Tight", var(--font); }
.insp-embed > * { flex-shrink: 0; }
.ichat__h2 { margin: 30px 0 0; }

.ichat__thread { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 12px; }
.msg { display: flex; align-items: flex-end; gap: 8px; }
.msg--me { justify-content: flex-end; }
.msg__mark {
  flex: 0 0 auto;
  width: 30px; height: 30px;
  display: grid; place-items: center;
  border-radius: 50%;
  background: var(--card);
  box-shadow: var(--card-shadow);
}
.msg__mark svg { width: 16px; height: 16px; fill: none; stroke: var(--emphasis); stroke-width: 1.8; stroke-linejoin: round; }
.msg__bubble {
  max-width: min(84%, 460px);
  padding: 13px 16px;
  border-radius: 22px;
  font-size: 16px;
  line-height: 1.5;
  user-select: text;
}
.msg__bubble p { margin: 0; }
.msg__bubble p + p { margin-top: 8px; }
.msg--guide .msg__bubble { background: var(--card); color: var(--card-ink); border-bottom-left-radius: 8px; box-shadow: var(--card-shadow); }
.msg--me .msg__bubble { background: #fff; color: var(--card); border-bottom-right-radius: 8px; box-shadow: 0 8px 20px rgba(110, 8, 8, 0.25); }

/* The opening read's numbers, the same ones it talks about. */
.msg__stats { list-style: none; margin: 12px 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: 6px; }
.msg__stat { padding: 6px 10px; border-radius: 10px; font-size: 13px; font-weight: 500; background: rgba(255, 255, 255, 0.07); color: var(--card-ink); }
.msg__stat.is-good { background: rgba(79, 191, 139, 0.14); color: #6fd6a3; }
.msg__stat.is-bad { background: rgba(255, 107, 107, 0.14); color: #ff8a8a; }
.msg__retry {
  margin-top: 10px;
  appearance: none; border: 0; border-radius: 999px;
  padding: 7px 14px;
  background: #fff; color: var(--card);
  font: 500 13px var(--font);
  cursor: pointer;
}

.msg__bubble--typing { display: flex; align-items: center; gap: 10px; color: var(--card-dim); font-size: 14px; }
.typing { display: inline-flex; gap: 4px; }
.typing i { width: 6px; height: 6px; border-radius: 50%; background: var(--card-dim); animation: typingDot 1.2s ease-in-out infinite; }
.typing i:nth-child(2) { animation-delay: 0.15s; }
.typing i:nth-child(3) { animation-delay: 0.3s; }
@keyframes typingDot { 0%, 60%, 100% { opacity: 0.3; transform: translateY(0); } 30% { opacity: 1; transform: translateY(-3px); } }

.ichat__foot { margin: 22px 0 0; text-align: center; font-size: 13px; color: var(--stage-ink); }

.ichat__dock {
  position: fixed;
  left: 0; right: 0;
  bottom: calc(env(safe-area-inset-bottom, 0px) + 84px);
  z-index: 40;
  width: min(100%, 620px);
  margin: 0 auto;
  padding: 0 16px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  pointer-events: none;
}
.ichat__dock > * { pointer-events: auto; }
.ichat__chips { display: flex; gap: 8px; overflow-x: auto; scrollbar-width: none; margin: 0 -16px; padding: 2px 16px; }
.ichat__chips::-webkit-scrollbar { display: none; }
.ichat__chip {
  flex: 0 0 auto;
  appearance: none; border: 0; border-radius: 999px;
  padding: 10px 15px;
  background: var(--card); color: var(--card-ink);
  font: 500 14px var(--font);
  white-space: nowrap;
  cursor: pointer;
  box-shadow: var(--card-shadow);
  transition: transform 0.15s ease;
}
.ichat__chip:active { transform: scale(0.96); }
.ichat__composer {
  display: flex; align-items: flex-end; gap: 8px;
  padding: 6px 6px 6px 18px;
  border-radius: 26px;
  background: var(--card);
  box-shadow: var(--card-shadow);
}
.ichat__input {
  flex: 1;
  min-height: 40px; max-height: 120px;
  padding: 10px 0;
  border: 0; outline: none; resize: none;
  background: transparent; color: var(--card-ink);
  font: 400 16px/1.25 var(--font); /* 16px: iOS zooms into anything smaller */
  field-sizing: content;
  user-select: text;
}
.ichat__input::placeholder { color: var(--card-dim); }
.ichat__send {
  flex: 0 0 auto;
  width: 40px; height: 40px;
  display: grid; place-items: center;
  appearance: none; border: 0; border-radius: 50%;
  background: #fff; color: var(--card);
  cursor: pointer;
  transition: opacity 0.15s ease, transform 0.15s ease;
}
.ichat__send:disabled { opacity: 0.35; cursor: default; }
.ichat__send:not(:disabled):active { transform: scale(0.93); }
.ichat__send svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; }
@media (prefers-reduced-motion: reduce) { .typing i { animation: none; opacity: 0.7; } }

/* ------------------------------------------------ insights page colours
   Text on the red is white, never near-black; and nothing red sits on the
   black cards, what was red there (the "Sets it off" tag, a high pain
   score, a number moving the wrong way, the guide's mark) is white. */
.insp__eyebrow, .insp__status, .insp__h2, .insp__quiet, .insp__foot, .ichat__foot { color: #fff; }
.finding--trigger .finding__tag { color: #fff; background: rgba(255, 255, 255, 0.14); }
.pchip--high { color: #fff; background: rgba(255, 255, 255, 0.16); }
.msg__stat.is-bad { color: #fff; background: rgba(255, 255, 255, 0.14); }
.msg__mark svg { stroke: #fff; }

/* ============================================================ ambient stage
   The backdrop, after Partiful's: colour only at the top, a moving mesh of
   the brand's reds (StageBackground.jsx on the web, Anatomic's Metal shader in
   the iOS app), melting into near-black the lower you go, with film grain
   over everything. Small text now sits on dark, so it is light; the guided
   session and the loading screen keep the red. */
:root {
  --ink-bg: #0B0B0D;                       /* the dark the stage fades into */
  --stage-ink: rgba(245, 243, 241, 0.62);  /* small text on the backdrop */
}
html { background: var(--ink-bg); }
/* The iOS app draws the backdrop natively, behind the transparent web view. */
html.is-native-stage { background: transparent; }

.stage-bg {
  position: fixed;
  inset: 0 0 auto 0;
  height: 62vh;
  z-index: -1;
  pointer-events: none;
  /* Full colour at the very top, gone by the bottom of the layer. */
  -webkit-mask-image: linear-gradient(to bottom, #000 0%, rgba(0, 0, 0, 0.85) 28%, rgba(0, 0, 0, 0.35) 62%, transparent 100%);
          mask-image: linear-gradient(to bottom, #000 0%, rgba(0, 0, 0, 0.85) 28%, rgba(0, 0, 0, 0.35) 62%, transparent 100%);
}
.stage-bg__mesh { display: block; }

/* Film grain for a dark ground: light specks, not the multiply grain that
   only showed on the red. */
.grain-layer {
  opacity: 0.07;
  mix-blend-mode: normal;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1.6 -0.35'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}
html.is-native-stage .grain-layer { display: none; } /* the shader grains itself */

/* Dark cards on a dark ground need an edge to stand off it. */
.sets, .checkin-shell, .mobility, .painchart, .settings__card, .insp__chart, .routine__card, .jcard, .finding, .msg--guide .msg__bubble, .ichat__composer, .ichat__chip {
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.07), 0 14px 30px rgba(0, 0, 0, 0.45);
}

/* Text that was near-black for the red. */
.timeline__what b, .flow__link, .flow__link:disabled, .flow__toggle-item { color: #fff; }
.flow__dock--fade { background: linear-gradient(to bottom, rgba(11, 11, 13, 0), var(--ink-bg) 28px); }
/* The guided session keeps its red stage, and the dark small text on it. */
.player { --stage-ink: #2A0B0B; }

/* Pain Trends: a white line on the black card, never red. */
.painplot__line { stroke: #fff; }
.painplot__dot { fill: #fff; }
.painplot__dot.is-weekly { fill: #111113; stroke: #fff; }
.insights__head svg { fill: #fff; stroke: none; }

/* ------------------------------------------------------------ week pills
   The week at the top as seven pills: the day, the date, and how it went,
   a grey dot, a green ring filling as sets are done, a green check once the
   routine is finished. Today is outlined in white. */
.week { display: grid; grid-template-columns: repeat(7, 1fr); gap: 6px; padding: 2px 0 6px; }
.wday {
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  padding: 10px 0 9px;
  border-radius: 999px;
  background: rgba(16, 16, 18, 0.78);
  -webkit-backdrop-filter: blur(12px);
          backdrop-filter: blur(12px);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.06);
}
.wday.is-today { background: rgba(58, 58, 62, 0.72); box-shadow: inset 0 0 0 2px #fff; }
.wday__name { font-size: 13px; font-weight: 500; color: rgba(255, 255, 255, 0.82); }
.wday.is-today .wday__name { color: rgba(255, 255, 255, 0.62); }
.wday__date { font-size: 18px; font-weight: 700; letter-spacing: -0.01em; color: #fff; font-variant-numeric: tabular-nums; }
.wday.is-future .wday__name, .wday.is-future .wday__date { opacity: 0.55; }
.wday__mark { width: 24px; height: 24px; margin-top: 3px; overflow: visible; }
.wday__dot { fill: rgba(255, 255, 255, 0.2); }
.wday.is-future .wday__dot { fill: rgba(255, 255, 255, 0.1); }
.wday__arc { fill: none; stroke: #5EE0A0; stroke-width: 2.5; stroke-linecap: round; }
.wday__done { fill: #5EE0A0; }
.wday__tick { fill: none; stroke: #0B0B0D; stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; }

/* The tab bar floats over near-black now: a neutral shadow, not the red
   one it had over the red stage. */
.tabbar { box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.07), 0 12px 30px rgba(0, 0, 0, 0.55); }

/* The blank Gate shows while the session is read (Gate.jsx): see-through,
   so the backdrop carries straight on instead of a flat black frame
   flashing between the launch screen and the app. */
.auth[aria-busy="true"] { background: transparent; }

/* ------------------------------------------------------- guided session
   A camera viewfinder, after Google Lens: the camera fills the screen and
   the session floats over it, the progress bar, the stretch's name and
   details, four corners framing where to stand, the countdown on its own
   (no ring), and the controls. Darkened at the top and bottom only, so the
   text reads and the middle stays clear. */
.player.player--lens { background: #000; color: #fff; }
.player--lens > * { position: relative; z-index: 1; }
.player__feed, .player__scrim { position: absolute; inset: 0; z-index: 0; }
.player__feed video, .player__feed canvas { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.player__feed video { transform: scaleX(-1); }
.player__feed.is-off video, .player__feed.is-off canvas { opacity: 0; }
.player__scrim {
  pointer-events: none;
  background:
    linear-gradient(to bottom, rgba(0, 0, 0, 0.62), rgba(0, 0, 0, 0) 26%),
    linear-gradient(to top, rgba(0, 0, 0, 0.72), rgba(0, 0, 0, 0) 34%);
}

.player--lens .player__seg { background: rgba(255, 255, 255, 0.22); }
.player--lens .player__close { background: rgba(0, 0, 0, 0.35); -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px); }
.player--lens .player__name { color: #fff; text-shadow: 0 1px 12px rgba(0, 0, 0, 0.45); }
.player--lens .player__meta { color: rgba(255, 255, 255, 0.78); }

/* The four corners: L-shaped strokes with a rounded outer corner. */
.player__frame { position: relative; flex: 1; width: 78%; min-height: 0; margin: 22px 0 12px; }
.player__corner {
  position: absolute;
  width: 38px; height: 38px;
  border: 0 solid rgba(255, 255, 255, 0.85);
  transition: border-color 0.3s ease;
}
.player__corner--tl { top: 0; left: 0; border-top-width: 3px; border-left-width: 3px; border-top-left-radius: 26px; }
.player__corner--tr { top: 0; right: 0; border-top-width: 3px; border-right-width: 3px; border-top-right-radius: 26px; }
.player__corner--bl { bottom: 0; left: 0; border-bottom-width: 3px; border-left-width: 3px; border-bottom-left-radius: 26px; }
.player__corner--br { bottom: 0; right: 0; border-bottom-width: 3px; border-right-width: 3px; border-bottom-right-radius: 26px; }
.player__frame.is-locked .player__corner { border-color: #5EE0A0; }
.player__cam-off { position: absolute; inset: 0; display: grid; place-items: center; font-size: 14px; color: rgba(255, 255, 255, 0.6); }

/* The stretch, drawn right over the camera in the middle of the frame: a
   white outline you can see the room through, with the muscles it works
   as a soft wash. Taps pass straight through it. */
.player--lens .player__figure {
  position: absolute; inset: 0;
  width: auto; height: auto; margin: 0;
  background: none; box-shadow: none; border-radius: 0;
  pointer-events: none;
  opacity: 0.8;
}
.player--lens .player__figure svg { width: 100%; height: 100%; max-height: none; padding: 4%; }
.player--lens .player__figure .figure__line { stroke: #fff; stroke-width: 2.5; filter: drop-shadow(0 0 3px rgba(0, 0, 0, 0.55)); }
.player--lens .player__figure .figure__accent { fill: rgba(255, 255, 255, 0.22); }
.player--lens .player__figure.is-locked .figure__line { stroke: #5EE0A0; filter: drop-shadow(0 0 3px rgba(0, 0, 0, 0.55)); }
/* A stretch with no traced outline falls back to its demo picture: white
   paper to the eye, so it is blended in rather than laid on as a card. */
.player--lens .player__figure--gif img, .player--lens .figure--gif img { width: auto; height: 100%; max-width: 100%; object-fit: contain; border-radius: 0; background: none; mix-blend-mode: multiply; opacity: 0.7; }

/* The countdown: just the number. */
.player--lens .player__timer {
  width: auto; height: auto;
  margin: 0 0 16px;
  display: flex; flex-direction: column; align-items: center; gap: 4px;
}
.player--lens .player__time {
  display: flex; justify-content: center;
  font-size: 64px; font-weight: 600; letter-spacing: -0.03em; line-height: 1;
  font-variant-numeric: tabular-nums;
  color: #fff;
  text-shadow: 0 2px 18px rgba(0, 0, 0, 0.4);
}
/* One character's cell: the leaving and arriving glyph share it. */
.player__slot { display: grid; justify-items: center; }
.player__digit { grid-area: 1 / 1; }
.player--lens .player__state { max-width: 260px; text-align: center; font-size: 15px; color: rgba(255, 255, 255, 0.82); }
.player--lens .player__timer.is-locked .player__state { color: #fff; }
.player--lens .player__btn { background: rgba(0, 0, 0, 0.42); -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px); color: #fff; }
.player--lens .player__btn--go { background: #fff; color: #0B0B0D; }
/* Small text on the session's black, including the summary at the end. */
.player.player--lens { --stage-ink: rgba(255, 255, 255, 0.7); }
.player--lens .player__state:empty { display: none; }


/* ------------------------------------------------------------ coaching
   What the coach says, how far in you are, and the corners' mood. Amber and
   green only on the dark camera view, never red. */
.player__corner { transition: border-color 0.3s ease; }
.player__frame.is-close .player__corner { border-color: #FFC857; }
.player--lens .player__state { transition: color 0.25s ease; min-height: 1.3em; }
.player--lens .player__state.is-good { color: #5EE0A0; }
.player--lens .player__state.is-fix { color: #FFC857; }
.player--lens .player__state.is-info { color: rgba(255, 255, 255, 0.82); }
.player--lens .player__state { font-weight: 500; text-shadow: 0 1px 12px rgba(0, 0, 0, 0.5); }

/* The range gauge: fills toward the target tick as the stretch deepens. */
.player__depth { width: 210px; display: flex; flex-direction: column; gap: 6px; margin-bottom: 10px; transition: opacity 0.3s ease; }
.player__depth[data-idle="1"] { opacity: 0; }
.player__depth-text { font-size: 12px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; color: rgba(255, 255, 255, 0.78); text-align: center; font-variant-numeric: tabular-nums; }
.player__depth-track { position: relative; height: 6px; border-radius: 999px; background: rgba(255, 255, 255, 0.22); overflow: visible; }
.player__depth-fill { position: absolute; left: 0; top: 0; bottom: 0; width: 0; border-radius: 999px; background: #fff; transition: width 0.18s ease-out, background 0.3s ease; }
.player__depth-fill[data-full="1"] { background: #5EE0A0; }
/* The target, at 100% of a gauge that runs to 120%. */
.player__depth-mark { position: absolute; left: 83.333%; top: -4px; bottom: -4px; width: 2px; margin-left: -1px; border-radius: 1px; background: rgba(255, 255, 255, 0.9); }
.player--lens .player__unit { font-size: 14px; color: rgba(255, 255, 255, 0.75); margin-top: -2px; }

/* The player's backdrop at the end: the camera fades out. */
.player__feed, .player__scrim { transition: opacity 0.7s ease; }
.player.is-summary .player__feed, .player.is-summary .player__scrim { opacity: 0; }
.player.player--lens { transition: background 0.7s ease; }
.player.player--lens.is-summary { background: radial-gradient(130% 70% at 50% 0%, #6a1020 0%, #1b0a10 38%, #0B0B0D 70%); }

/* ------------------------------------------------------------ results */
.results {
  position: absolute; inset: 0;
  z-index: 2;
  padding: calc(env(safe-area-inset-top) + 64px) 20px calc(env(safe-area-inset-bottom) + 24px);
  display: flex; flex-direction: column; align-items: center; gap: 14px;
  overflow-y: auto;
  scrollbar-width: none;
  text-align: center;
}
.results::-webkit-scrollbar { display: none; }
.results > * { flex-shrink: 0; width: 100%; max-width: 440px; }
.results__eyebrow { margin: 0; font-size: 13px; font-weight: 600; letter-spacing: 0.12em; text-transform: uppercase; color: rgba(255, 255, 255, 0.7); }
.results__hero { position: relative; display: flex; align-items: center; justify-content: center; gap: 18px; margin-top: 4px; }
.results__score { display: flex; align-items: baseline; gap: 4px; line-height: 1; }
.results__score span { font-size: 104px; font-weight: 700; letter-spacing: -0.045em; font-variant-numeric: tabular-nums; color: #fff; text-shadow: 0 6px 40px rgba(255, 255, 255, 0.18); min-width: 1.6ch; text-align: right; }
.results__score small { font-size: 22px; font-weight: 500; color: rgba(255, 255, 255, 0.55); }
.results__grade {
  display: grid; place-items: center;
  width: 64px; height: 64px; border-radius: 50%;
  background: #fff; color: #0B0B0D;
  font-size: 34px; font-weight: 800; letter-spacing: -0.02em;
  box-shadow: 0 0 40px rgba(255, 255, 255, 0.3);
}
.results__stars { display: flex; justify-content: center; gap: 10px; }
.results__star { width: 44px; height: 44px; fill: rgba(255, 255, 255, 0.14); stroke: rgba(255, 255, 255, 0.28); stroke-width: 1; stroke-linejoin: round; }
.results__star.is-earned { fill: #FFD66B; stroke: #FFE9A8; filter: drop-shadow(0 0 10px rgba(255, 214, 107, 0.55)); }
.results__pill { align-self: center; width: auto; margin: 0; padding: 7px 16px; border-radius: 999px; background: rgba(94, 224, 160, 0.16); color: #5EE0A0; font-size: 14px; font-weight: 600; }

.results__stats { list-style: none; margin: 4px 0 0; padding: 0; display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
.results__tile { padding: 14px 6px 12px; border-radius: 18px; background: rgba(255, 255, 255, 0.07); box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.07); display: flex; flex-direction: column; gap: 4px; }
.results__tile b { font-size: 30px; font-weight: 700; letter-spacing: -0.03em; font-variant-numeric: tabular-nums; color: #fff; }
.results__tile b small { font-size: 15px; font-weight: 600; margin-left: 1px; color: rgba(255, 255, 255, 0.6); }
.results__tile span { font-size: 12px; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; color: rgba(255, 255, 255, 0.6); }

.results__list { list-style: none; margin: 4px 0 0; padding: 0; display: flex; flex-direction: column; gap: 8px; text-align: left; }
.results__row { padding: 13px 16px 12px; border-radius: 18px; background: rgba(255, 255, 255, 0.07); box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.07); display: flex; flex-direction: column; gap: 8px; }
.results__row-head { display: flex; justify-content: space-between; gap: 12px; align-items: baseline; }
.results__row-name { font-size: 16px; font-weight: 600; color: #fff; }
.results__row-head b { font-size: 20px; font-weight: 700; font-variant-numeric: tabular-nums; color: #fff; }
.results__bar { height: 6px; border-radius: 999px; background: rgba(255, 255, 255, 0.14); overflow: hidden; }
.results__bar i { display: block; height: 100%; border-radius: 999px; background: #fff; transform-origin: left; }
.results__row-detail { font-size: 13px; color: rgba(255, 255, 255, 0.65); }

.results__note { padding: 16px 18px; border-radius: 20px; background: rgba(255, 255, 255, 0.07); box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.07); text-align: left; display: flex; flex-direction: column; gap: 8px; }
.results__note-title { font-size: 12px; font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase; color: rgba(255, 255, 255, 0.6); }
.results__note p { margin: 0; font-size: 15px; line-height: 1.45; color: rgba(255, 255, 255, 0.9); }
.results__note p b { color: #fff; font-weight: 600; }
.results__done { margin-top: 6px; appearance: none; border: 0; border-radius: 999px; padding: 17px; background: #fff; color: #0B0B0D; font: 600 17px var(--font); cursor: pointer; }
.results__done:active { transform: scale(0.98); }
.results__plain { margin: 6px 0 0; font-size: 40px; font-weight: 700; letter-spacing: -0.03em; color: #fff; }
.results__sub { margin: 0; font-size: 16px; line-height: 1.5; color: rgba(255, 255, 255, 0.75); }

/* The chat's words fade in one by one, after the old Coach page's. */
.msg__word { display: inline-block; opacity: 0; animation: msgWordIn 0.35s ease forwards; }
@keyframes msgWordIn { from { opacity: 0; transform: translateY(2px); } to { opacity: 1; transform: translateY(0); } }
.msg__stats.is-late { opacity: 0; animation: msgWordIn 0.5s ease forwards; animation-delay: var(--late, 0ms); }
@media (prefers-reduced-motion: reduce) {
  .msg__word, .msg__stats.is-late { animation: none; opacity: 1; transform: none; }
}

/* Section titles on Insights in ordinary case, not capitals. */
.insp__eyebrow, .insp__h2 { text-transform: none; letter-spacing: -0.01em; }
.insp__eyebrow { font-size: 16px; }
.insp__h2 { font-size: 19px; }

/* ---------------------------------------------------------- stretch frames */
/* Start and finish of a stretch stacked in one box (StretchFrames.jsx). One
   6.6s loop: start holds, then the finish fades in sharpening out of a blur
   while the start blurs out beneath it, so mid-way the two poses smear into
   each other like a long exposure of the movement; the finish holds, and the
   same smear runs back. The start never fades, so the box never flashes the
   background through the blend. */
.frames { position: relative; display: block; overflow: hidden; background: #fff; }
.frames__img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: var(--frames-fit, contain);
  object-position: var(--frames-pos, 50% 50%);
  animation: 6.6s cubic-bezier(0.45, 0, 0.55, 1) infinite both;
  will-change: opacity, filter, transform;
  user-select: none;
  -webkit-user-drag: none;
}
.frames__img--start { animation-name: frames-start; }
.frames__img--finish { animation-name: frames-finish; }
@keyframes frames-start {
  0%, 21% { filter: blur(0); transform: scale(1); }
  35%, 86% { filter: blur(calc(3.5px * var(--frames-blur, 1))); transform: scale(1.012); }
  98%, 100% { filter: blur(0); transform: scale(1); }
}
@keyframes frames-finish {
  0%, 21% { opacity: 0; filter: blur(calc(6px * var(--frames-blur, 1))); transform: scale(1.02); }
  33% { opacity: 0.5; filter: blur(calc(3px * var(--frames-blur, 1))); transform: scale(1.01); }
  44%, 75% { opacity: 1; filter: blur(0); transform: scale(1); }
  87% { opacity: 0.5; filter: blur(calc(3px * var(--frames-blur, 1))); transform: scale(1.01); }
  98%, 100% { opacity: 0; filter: blur(calc(6px * var(--frames-blur, 1))); transform: scale(1.02); }
}
@media (prefers-reduced-motion: reduce) {
  .frames__img--start { animation: none; }
  .frames__img--finish { animation-name: frames-fade; animation-duration: 6s; }
  @keyframes frames-fade {
    0%, 35% { opacity: 0; }
    50%, 85% { opacity: 1; }
    100% { opacity: 0; }
  }
}
.stretch__frames { width: 100%; height: 220px; border-radius: 14px; }
.proposal__gif.frames { --frames-fit: cover; --frames-blur: 0.4; }
.sets__tile .frames { width: 100%; height: 100%; --frames-pos: 50% 38%; }
.routine__thumb .frames { width: 100%; height: 100%; --frames-fit: cover; --frames-blur: 0.5; }

/* ---------------------------------------------------------- session: work vs rest */
/* The stage's two states share its space and cross over (SessionPlayer). */
.player__work, .player__rest { flex: 1; min-height: 0; width: 100%; display: flex; flex-direction: column; align-items: center; }
.player__work .player__frame { width: 78%; }

/* "How to do it": a quiet pill under the set line. */
.player__howto {
  display: inline-flex; align-items: center; gap: 6px;
  margin-top: 10px;
  padding: 7px 13px 7px 10px;
  border: 0; border-radius: 999px;
  background: rgba(0, 0, 0, 0.42);
  -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px);
  color: #fff; font: 600 13.5px var(--font);
  cursor: pointer;
  transition: transform 0.15s ease, background 0.2s ease;
}
.player__howto:active { transform: scale(0.96); }
.player__howto svg { width: 17px; height: 17px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; }

/* Rest: the camera disappears under a deep cool-blue wash, so it reads at
   a glance as a break, not more of the stretch. (A wash, not a blur: a
   full-screen filter on live video costs a frame's worth of GPU.) */
.player__rest-tint {
  position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background: radial-gradient(120% 80% at 50% 40%, rgba(40, 84, 168, 0.86), rgba(10, 20, 48, 0.96));
  opacity: 0;
  transition: opacity 0.6s ease;
}
.player.is-resting .player__rest-tint { opacity: 1; }
.player__feed canvas { transition: opacity 0.4s ease; }
.player.is-resting .player__feed canvas { opacity: 0; }
.player__rest { justify-content: center; gap: 6px; padding: 0 24px 8px; text-align: center; }
.player__rest-badge {
  padding: 5px 14px; border-radius: 999px;
  background: rgba(143, 194, 255, 0.2); color: #BFDBFF;
  font-size: 12.5px; font-weight: 700; letter-spacing: 0.12em; text-transform: uppercase;
}
.player__rest-title { margin-top: 6px; font-size: 30px; font-weight: 600; letter-spacing: -0.02em; color: #fff; }
.player__rest-sub { max-width: 290px; font-size: 15px; line-height: 1.4; color: rgba(225, 236, 255, 0.82); text-wrap: balance; }
/* The countdown turns the same cool blue while resting. */
.player--lens .player__timer.is-rest .player__time { color: #BFDBFF; }
.player--lens .player__timer.is-rest .player__state { color: rgba(225, 236, 255, 0.82); }
.player__time { transition: color 0.4s ease; }
/* Nothing is measured while resting, so the range meter steps aside. */
.player--lens .player__timer.is-rest .player__depth { opacity: 0; }

/* ---------------------------------------------------------- how to do it */
.howto {
  position: absolute; inset: 0; z-index: 30;
  display: flex; align-items: flex-end; justify-content: center;
  padding: 16px 16px calc(16px + env(safe-area-inset-bottom));
  background: rgba(0, 0, 0, 0.45);
}
.howto__card {
  width: 100%; max-width: 440px;
  display: flex; flex-direction: column; gap: 14px;
  padding: 18px 20px 18px;
  border-radius: 28px;
  background: rgba(22, 22, 26, 0.86);
  -webkit-backdrop-filter: blur(24px); backdrop-filter: blur(24px);
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
  color: #fff;
}
.howto__head { display: flex; align-items: center; justify-content: space-between; }
.howto__close { display: grid; place-items: center; width: 34px; height: 34px; border: 0; border-radius: 50%; background: rgba(255, 255, 255, 0.1); color: #fff; cursor: pointer; }
.howto__close svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 2.2; stroke-linecap: round; }
.howto__title { font-size: 22px; font-weight: 600; letter-spacing: -0.02em; }
.howto__head { gap: 12px; }
.howto__feel { display: flex; flex-direction: column; gap: 8px; }
.howto__feel-label { font-size: 13.5px; color: rgba(255, 255, 255, 0.7); }
.howto__chips { display: flex; flex-wrap: wrap; gap: 6px; }
.howto__chip { padding: 6px 11px; border-radius: 999px; background: rgba(245, 4, 4, 0.22); color: #FFC9C9; font-size: 13.5px; font-weight: 600; }
.howto__bars { display: flex; gap: 4px; }
.howto__bar { flex: 1; height: 3px; border-radius: 999px; background: rgba(255, 255, 255, 0.18); overflow: hidden; }
.howto__fill { display: block; height: 100%; border-radius: inherit; background: #fff; transform: scaleX(0); transform-origin: left; }
.howto__fill.is-done { transform: scaleX(1); transition: transform 0.3s ease; }
.howto__fill.is-on { animation: howto-fill linear forwards; }
@keyframes howto-fill { from { transform: scaleX(0); } to { transform: scaleX(1); } }
.howto__text {
  display: flex; flex-direction: column; gap: 6px; align-items: stretch;
  padding: 0; border: 0; background: none; color: inherit; text-align: left; font: inherit; cursor: pointer;
}
.howto__step { font-size: 13px; font-weight: 600; color: rgba(255, 255, 255, 0.55); font-variant-numeric: tabular-nums; }
/* One cell: the leaving and arriving sentence share it, like the digits. */
.howto__slot { display: grid; min-height: 5.4em; align-items: start; overflow: hidden; }
.howto__line { grid-area: 1 / 1; font-size: 20px; line-height: 1.35; font-weight: 500; letter-spacing: -0.01em; text-wrap: pretty; }
.howto__none { font-size: 16px; line-height: 1.4; color: rgba(255, 255, 255, 0.75); }
.howto__foot { display: flex; align-items: center; justify-content: flex-end; gap: 10px; }
/* The landing carousel's arrow buttons: a thin ring, and a fill that sweeps
   up from below on hover or press, turning the arrow dark. */
.howto__arrow {
  position: relative; display: grid; place-items: center;
  width: 52px; height: 52px; padding: 0; flex-shrink: 0;
  border: 1px solid currentColor; border-radius: 50%;
  background: transparent; color: #fff;
  cursor: pointer; overflow: hidden; isolation: isolate;
  -webkit-tap-highlight-color: transparent;
  transition: transform 0.25s, color 0.35s, border-color 0.35s, opacity 0.3s;
}
.howto__arrow::before {
  content: ""; position: absolute; inset: -1px; border-radius: 50%;
  background: #fff; transform: translateY(105%);
  transition: transform 0.45s cubic-bezier(0.22, 1, 0.36, 1); z-index: -1;
}
.howto__arrow:not(:disabled):hover::before,
.howto__arrow:not(:disabled):focus-visible::before,
.howto__arrow:not(:disabled):active::before { transform: translateY(0); }
.howto__arrow:not(:disabled):hover,
.howto__arrow:not(:disabled):focus-visible,
.howto__arrow:not(:disabled):active { color: #0B0B0D; }
.howto__arrow:focus-visible { outline: 2px solid #fff; outline-offset: 4px; }
.howto__arrow:not(:disabled):active { transform: scale(0.93); }
.howto__arrow:disabled { opacity: 0.3; cursor: default; }
@media (prefers-reduced-motion: reduce) {
  .howto__fill.is-on { animation: none; transform: scaleX(1); opacity: 0.6; }
  .player__rest-tint { transition: none; }
}
