/* Reset some default browser spacing so we control layout ourselves */
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

/* Browsers show their own default tap/focus highlight on buttons (often
   a white or blue box) unless we turn it off ourselves. */
button {
  -webkit-tap-highlight-color: transparent;
}

button:focus {
  outline: none;
}

/* Number inputs come with native up/down arrow buttons that browsers
   render in their own light/white style, ignoring our dark theme.
   Removing them keeps the input a single simple color. */
input[type="number"] {
  -moz-appearance: textfield;
}

input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

/* CSS variables ("custom properties"): named values we can reuse
   everywhere instead of repeating magic numbers. Changing one line here
   updates spacing/color/sizing across the whole page. */
:root {
  --space-xs: 6px;
  --space-sm: 10px;
  --space-md: 16px;
  --space-lg: 24px;
  --space-xl: 32px;
  --radius-sm: 8px; /* small controls inside rows */
  --radius-md: 12px; /* buttons and text fields */
  --radius: 16px; /* cards */

  /* Primary accent: muted clay/amber, used for the CTA and streak number.
     Desaturated on purpose - a subtle accent instead of a neon one. */
  --orange: #c08552;
  --orange-light: #d4a373;
  --orange-dark: #9c6b3f;

  /* Secondary accent: muted sage, used for secondary stats/highlights so
     the palette has more than one note instead of one color everywhere */
  --teal: #8a9a8e;
  --teal-light: #a8b8ac;

  /* Closure ring colors - deliberately bold/saturated (unlike the rest of
     the app's muted palette above), for the same high-contrast, energetic
     feel as Apple's Activity rings - but an original palette, not their
     actual hues. Squats leans toward Forja's own brand green (var(--accent))
     rather than Apple's cyan, so the boldest UI in the app still reads as
     Forja's own. Kept as their own variables rather than reusing
     --orange-light/--teal-light, since those are shared with other UI
     (streak flame, stat numbers) that should stay muted. */
  --ring-pushups: #ff5a5f;
  --ring-planks: #ffc233;
  --ring-squats: #2dd4bf;
  --ring-walkrun: #7c5cff;

  /* Trophies and challenge winners. */
  --trophy: #f5b83d;
  --trophy-tint: #f5b83d22; /* behind gold text, e.g. "New best set" */

  /* Ember: the sign-up flow's placeholder mascot and its streak flame. */
  --ember: #e08a4c;
  --ember-light: #ffc233;

  /* Background: a quiet, neutral charcoal gradient - no bright color
     blooms, just subtle depth. */
  --bg-start: #1a1b1f;
  --bg-mid: #17181b;
  --bg-end: #121316;

  /* Theme colors. Dark is the default; [data-theme="light"] below
     overrides every one of these, so no rule elsewhere in this file
     needs to know which theme is active. */
  color-scheme: dark;
  --page-bg: #09090b;
  --surface: #1c1c1f66;
  --surface-strong: #1c1c1f80;
  --surface-solid: #1c1c1f;
  --route-dot: #ffffff14; /* the dot grid behind a Feed route */
  --route-bg: #26262a; /* a Feed route's box, a step lighter than its card */
  --card-solid: #1c1c1f; /* Feed posts: solid, so each one stands apart */
  --card-line: #ffffff14;
  --popover-bg: #2c2c31; /* raised a step above cards, for tip bubbles */
  /* Hold-to-confirm buttons (walk Finish / Cancel): the fill that sweeps
     across while held - a lighter sheen on green, a red wash on Cancel. */
  --hold-fill: #ffffff38;
  --danger-tint: #ff453a40;
/* Brand green: --accent for fills, --accent-text for green words (a     darker green in light mode so it stays readable on white). */  --accent: #22c55e;  --accent-pressed: #16a34a;  --accent-text: #22c55e;  --accent-tint: #22c55e1f;  /* Apple's system red / orange, for errors, delete and warnings. */  --danger: #ff453a;  --danger-pressed: #d70015;  --warning: #ff9f0a;
  --field-bg: #232326;
  --field-bg-hover: #28282b;
  --control-bg: #3a3a3a;
  --overlay: #ffffff12;
  --nav-bg: rgba(20, 20, 22, 0.75);
  --heatmap-empty: #27272a;
  /* A rest day that kept the streak: halfway between an empty day and a
     workout day (solid - a see-through green goes muddy on black). */
  --heatmap-rest: #247644;
  --heatmap-rest-mark: #f4f4f5; /* its moon on the History calendar */
  /* League medals (rank numbers + the Monday result). */
  --medal-gold: #e0a800;
  --medal-silver: #9aa0a8;
  --medal-bronze: #c27a45;
  --streak-gradient: linear-gradient(135deg, #ffffff 0%, #a1a1aa 100%);
  --text-primary: #f4f4f5;
  --text-secondary: #9a9a96;
  --text-muted: #6b6b68;
  --text-soft: #e4e3e0;
  --text-faint: #71717a;
  --border: #3a3a3880;

  /* The area an iPhone's notch/Dynamic Island/home indicator/rounded
     corners occupy. Zero on devices without them, or without
     viewport-fit=cover set - env() just falls back to 0px either way. */
  --safe-top: env(safe-area-inset-top, 0px);
  --safe-right: env(safe-area-inset-right, 0px);
  --safe-bottom: env(safe-area-inset-bottom, 0px);
  --safe-left: env(safe-area-inset-left, 0px);
}

:root[data-theme="light"] {
  color-scheme: light;
  --trophy: #c08a12;
  --trophy-tint: #c08a121f;
  --ember: #c76a2c;
  --ember-light: #f0a500;
  --page-bg: #f2f2f7;
  --surface: #ffffff;
  --surface-strong: #ffffff;
  --surface-solid: #e5e5ea;
  --route-dot: #0000001a;
  --route-bg: #e5e5ea;
  --card-solid: #ffffff;
  --card-line: #3c3c4326;
  --popover-bg: #ffffff;
  --hold-fill: #ffffff52;
  --danger-tint: #d7001526;
--accent-text: #15803d;  --accent-tint: #22c55e1a;  --danger: #d70015;  --warning: #b25000;
  --field-bg: #f2f2f7;
  --field-bg-hover: #e9e9ee;
  --control-bg: #d1d1d6;
  --overlay: #0000001a;
  --nav-bg: rgba(255, 255, 255, 0.8);
  --heatmap-empty: #e5e5ea;
  --heatmap-rest: #22c55e59;
  --heatmap-rest-mark: #15803d;
  --streak-gradient: linear-gradient(135deg, #1c1c1e 0%, #6b6b72 100%);
  --text-primary: #1c1c1e;
  --text-secondary: #636366;
  --text-muted: #8e8e93;
  --text-soft: #3a3a3c;
  --text-faint: #6e6e73;
  --border: #3c3c4340;
}

/* Apple's text scale (iOS Dynamic Type default sizes, in pt = CSS px).
   Every text size in the app uses one of these, except hero numbers and
   the camera screen's read-from-across-the-room text. */
:root {
  --font-large-title: 34px;
  --font-title1: 28px;
  --font-title2: 22px;
  --font-title3: 20px;
  --font-body: 17px;
  --font-callout: 16px;
  --font-subhead: 15px;
  --font-footnote: 13px;
  --font-caption: 12px;
  --font-caption2: 11px;
  --font-tab: 10px; /* tab bar labels, as in iOS */
}

/* Belt-and-suspenders against side-to-side dragging: this stops the
   BROWSER's default response to a sideways swipe (panning/bouncing the
   whole page horizontally). It doesn't fix an actual layout overflow bug
   by itself - if something is wider than the screen, overflow-x: hidden
   below clips it rather than letting it scroll into view. Both belong
   here together; neither one alone is enough. */
html {
  overflow-x: hidden;
}

body {
  /* Apple's actual system font (San Francisco) first, so the app matches
     every native iOS app's typography instead of a downloaded web font -
     "Inter" is now just the fallback for non-Apple devices. */
  font-family: -apple-system, BlinkMacSystemFont, "Inter", "Segoe UI", Roboto, sans-serif;
  background-color: var(--page-bg);
  color: var(--text-primary);
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-lg) var(--space-md);
  overflow-x: hidden;
  touch-action: pan-y;
}

/* Full-screen overlay shown while the app loads, hidden via JavaScript
   once everything is ready to display. Sits above the card (z-index). */
#splash-screen {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  background-color: var(--page-bg); /* Matches premium dark fitness themes */
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 99999;
  transition: opacity 0.4s ease, visibility 0.4s ease;
}

.splash-content {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
}

/* The logo's sprite holds shapes only; it takes no space on the page. */
.logo-sprite {
  position: absolute;
  width: 0;
  height: 0;
  overflow: hidden;
}

/* Loading: the three arcs draw themselves in one after another, then the
   whole ring turns until the app is ready. Each arc is one 54-unit dash
   of the ring's 226.2-unit circle, offset to its own third. */
.splash-ring {
  width: 84px;
  height: 84px;
  animation: splash-turn 1.2s cubic-bezier(0.45, 0, 0.55, 1) 0.95s infinite;
}

.splash-arc {
  fill: none;
  stroke-width: 12;
  stroke-linecap: round;
  stroke-dasharray: 54 172.2;
  animation: splash-draw 0.6s cubic-bezier(0.2, 0.8, 0.2, 1) both;
}

.splash-arc:nth-child(1) {
  stroke: var(--ring-pushups);
}

.splash-arc:nth-child(2) {
  stroke: var(--ring-planks);
  stroke-dashoffset: -75.4;
  animation-delay: 0.12s;
}

.splash-arc:nth-child(3) {
  stroke: var(--ring-squats);
  stroke-dashoffset: -150.8;
  animation-delay: 0.24s;
}

.splash-word {
  width: 116px;
  aspect-ratio: 233 / 92.1;
  color: var(--text-primary);
  animation: splash-word-in 0.4s ease 0.3s both;
}

/* Class applied via JavaScript to dismiss the screen */
.splash-fade-out {
  opacity: 0;
  visibility: hidden;
}

@keyframes splash-draw {
  from {
    stroke-dasharray: 0 226.2;
    opacity: 0;
  }
  20% {
    opacity: 1;
  }
  to {
    stroke-dasharray: 54 172.2;
    opacity: 1;
  }
}

@keyframes splash-word-in {
  from {
    opacity: 0;
    transform: translateY(6px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

@keyframes splash-turn {
  to {
    transform: rotate(360deg);
  }
}

@media (prefers-reduced-motion: reduce) {
  .splash-ring,
  .splash-arc,
  .splash-word {
    animation: none;
  }
}

.card {
  position: relative;
  background: transparent;
  padding: var(--space-xl) var(--space-lg);
  width: 100%;
  max-width: 380px;
  text-align: center;
}

/* Below ~480px wide, treat this as a phone, not a webpage with a card
   floating on it - the app should fill the entire screen edge-to-edge,
   like every native app does, instead of sitting in a centered box with
   visible background around it. A phone turned on its side is wide but
   short, so "landscape and at most 560px tall" counts as a phone too
   (the same test the camera screen's landscape layout uses). */
@media (max-width: 480px), (orientation: landscape) and (max-height: 560px) {
  body {
    padding: 0;
    align-items: stretch;
  }

  .card {
    max-width: 100%;
    min-height: 100dvh;
    padding: calc(var(--space-xl) + var(--safe-top)) calc(var(--space-lg) + var(--safe-right))
      calc(var(--space-lg) + var(--safe-bottom)) calc(var(--space-lg) + var(--safe-left));
  }
}

.brand {
  margin-bottom: var(--space-lg);
}

/* The logo (ring + "Forja") at the top of the log-in screens. */
.brand-logo {
  display: block;
  height: 40px;
  width: auto;
  aspect-ratio: 367 / 110;
  margin: 0 auto;
  color: var(--text-primary);
}

/* !important is deliberate here: this is a utility class whose entire
   job is "always hide this, no matter what other classes are also on
   this element." Without it, a same-specificity class defined later in
   this file (like .tracking-buttons) can silently win the display
   property and make "hidden" elements show up anyway. */
.hidden {
  display: none !important;
}

/* A single unified strip along the bottom of the card, like a mobile
   app's bottom navigation bar - one row, evenly split between buttons. */
.tab-bar {
  display: flex;
  margin-top: var(--space-lg);
  padding-top: var(--space-sm);
  padding-bottom: var(--space-xs);
  border-top: 1px solid var(--border);
  margin-left: calc((var(--space-lg) + var(--safe-left)) * -1);
  margin-right: calc((var(--space-lg) + var(--safe-right)) * -1);
  padding-left: calc(var(--space-sm) + var(--safe-left));
  padding-right: calc(var(--space-sm) + var(--safe-right));
  /* iOS's native tab bars are frosted glass, not a flat color - this is
     one of the most recognizable Apple UI materials. */
  background: var(--nav-bg);
  -webkit-backdrop-filter: blur(20px);
  backdrop-filter: blur(20px);
}

.tab-btn {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: 6px 4px;
  font-family: inherit;
  background: none;
  border: none;
  cursor: pointer;
  color: var(--text-muted);
  transition: color 0.15s ease;
}

.tab-icon svg {
  width: 20px;
  height: 20px;
}

/* Only the drawing dims on an inactive tab - not the tab's red dot. */
.tab-icon {
  position: relative;
}

.tab-icon svg {
  opacity: 0.6;
  transition: opacity 0.15s ease;
}

/* A small unread-style dot on a tab icon (e.g. a pending friend request
   on Competition) - visible the moment the app opens, so it doesn't rely
   on someone happening to tap into that specific tab to find out. */
.tab-badge {
  position: absolute;
  top: -2px;
  right: -4px;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--danger);
  border: 1.5px solid var(--page-bg);
}

.tab-text {
  font-size: var(--font-tab);
  font-weight: 700;
}

.tab-btn.active {
  color: var(--accent-text);
}

.tab-btn.active .tab-icon svg {
  opacity: 1;
}

/* This has to come AFTER the unconditional .tab-bar rule above, not
   before it - CSS resolves equal-specificity conflicts by source order,
   so if this block appeared earlier in the file (as it briefly did),
   the unconditional rule's negative margins would win back on mobile and
   push the bar wider than the screen, which is exactly what let the
   whole page be dragged sideways. Same phone test as the layout rule
   above, so the bar stays pinned when the phone is turned sideways. */
@media (max-width: 480px), (orientation: landscape) and (max-height: 560px) {
  /* Pin the tab bar to the bottom of the actual screen instead of
     leaving it in normal document flow - otherwise it sits right after
     whichever tab's content happens to be visible, so it visually jumps
     to a different height every time you switch tabs. Fixed positioning
     also means the negative-margin edge-to-edge trick above is no longer
     needed here, since left:0/right:0 already spans the full viewport. */
  .tab-bar {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    margin: 0;
    padding-bottom: calc(var(--space-xs) + var(--safe-bottom));
    z-index: 100;
  }

  /* Leaves room at the bottom of every tab's content so its last item
     isn't hidden underneath the now-floating tab bar. */
  .tab-panel {
    padding-bottom: 90px;
  }
}

/* A phone on its side: keep each tab's content in a readable column
   instead of stretching cards across the whole width. The bar itself
   still spans the full screen. */
@media (orientation: landscape) and (max-height: 560px) {
  .tab-panel {
    max-width: 600px;
    margin-left: auto;
    margin-right: auto;
  }
}

.preview-note {
  font-size: var(--font-footnote);
  color: var(--text-muted);
  text-align: center;
  margin-bottom: var(--space-md);
}

/* A small icon sitting inline with a line of text, nudged to align
   with the text's baseline instead of sitting too high or low. */
.inline-icon {
  width: 13px;
  height: 13px;
  vertical-align: -2px;
}

.leaderboard {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  margin-bottom: var(--space-md);
}

.leaderboard-row {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  padding: var(--space-sm);
  background: var(--surface);
  border: 1px solid transparent;
  border-radius: var(--radius-md);
}

.leaderboard-row.is-you {
  background: #c0855218;
  border-color: #c0855250;
}

.leaderboard-rank {
  width: 20px;
  font-size: var(--font-subhead);
  font-weight: 700;
  color: var(--text-muted);
  text-align: center;
}

.leaderboard-avatar {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: var(--control-bg);
  color: var(--text-secondary);
  flex-shrink: 0;
}

.leaderboard-avatar svg {
  width: 16px;
  height: 16px;
}

/* Everyone's colored circle with their first letter (see avatarHtml). */
.leaderboard-avatar.has-letter,
.profile-avatar.has-letter {
  background: var(--avatar);
  color: #ffffff;
  font-weight: 700;
  line-height: 1;
}

.leaderboard-avatar.has-letter {
  font-size: var(--font-footnote);
}

.profile-avatar.has-letter {
  font-size: var(--font-title2);
}

/* The Me tab's circle is a button: a small pencil badge says it can be
   changed. */
.profile-avatar-button {
  position: relative;
  flex-shrink: 0;
  padding: 0;
  background: none;
  border: none;
  border-radius: 50%;
  cursor: pointer;
}

.profile-avatar-button:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 3px;
}

.profile-avatar-badge {
  position: absolute;
  right: -2px;
  bottom: -2px;
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  color: var(--text-primary);
  background: var(--popover-bg);
  box-shadow: 0 0 0 2px var(--page-bg);
}

.profile-avatar-badge svg {
  width: 12px;
  height: 12px;
}

/* "Your color" sheet: a big preview, then the 12 colors. */
.color-sheet {
  gap: var(--space-sm);
}

.color-sheet-top {
  position: relative;
  display: flex;
  justify-content: center;
  width: 100%;
}

.color-sheet-top h2 {
  margin: 0;
  font-size: var(--font-body);
  font-weight: 600;
  color: var(--text-primary);
}

.color-sheet-done {
  position: absolute;
  top: 50%;
  right: 0;
  min-height: 44px;
  padding: 0 4px;
  font-family: inherit;
  font-size: var(--font-body);
  font-weight: 600;
  color: var(--accent-text);
  background: none;
  border: none;
  cursor: pointer;
  transform: translateY(-50%);
}

#color-sheet-preview .profile-avatar {
  width: 96px;
  height: 96px;
  margin-top: var(--space-xs);
  font-size: 40px; /* hero letter, like the streak number */
  transition: background 0.2s ease;
}

.color-sheet-handle {
  margin: 0;
  font-size: var(--font-subhead);
  color: var(--text-secondary);
}

.color-swatches {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  justify-items: center;
  gap: 12px;
  width: 100%;
  margin-top: var(--space-xs);
}

.color-swatch {
  position: relative;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  padding: 0;
  color: #ffffff;
  background: var(--avatar);
  border: none;
  border-radius: 50%;
  cursor: pointer;
}

/* The picked one gets a ring and a check. */
.color-swatch::after {
  content: "";
  position: absolute;
  inset: -5px;
  border: 2.5px solid transparent;
  border-radius: 50%;
  transition: border-color 0.15s ease;
}

.color-swatch[aria-checked="true"]::after {
  border-color: var(--text-primary);
}

.color-swatch svg {
  width: 20px;
  height: 20px;
  opacity: 0;
  transition: opacity 0.15s ease;
}

.color-swatch[aria-checked="true"] svg {
  opacity: 1;
}

.color-swatch:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 6px;
}

.color-sheet-status {
  min-height: 18px;
  margin: 0;
  font-size: var(--font-footnote);
  color: var(--text-secondary);
}

.color-sheet-status.is-error {
  color: var(--danger);
}

.leaderboard-name {
  flex: 1;
  text-align: left;
  font-size: var(--font-body);
  font-weight: 600;
  color: var(--text-primary);
}

.leaderboard-streak {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: var(--font-subhead);
  font-weight: 800;
  color: var(--orange-light);
}

.leaderboard-streak svg {
  width: 15px;
  height: 15px;
}

/* Name with "Done today" / "Not yet today" under it. */
.leaderboard-who {
  flex: 1;
  min-width: 0;
  min-height: 36px; /* as tall as the Nudge button, so every row matches */
  display: flex;
  flex-direction: column;
  justify-content: center;
  text-align: left;
}

.leaderboard-who .leaderboard-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.leaderboard-today {
  overflow: hidden;
  font-size: var(--font-caption);
  color: var(--text-secondary);
  text-overflow: ellipsis;
  white-space: nowrap;
}

.leaderboard-today.is-done {
  font-weight: 600;
  color: var(--accent-text);
}

/* "Nudge": the Kudos pill a size smaller, words only so names keep their
   room; warm once sent, like given Kudos. 30pt tall, 44pt touch area. */
.nudge-button {
  position: relative;
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
  min-height: 30px;
  padding: 0 12px;
  font-family: inherit;
  font-size: var(--font-footnote);
  font-weight: 700;
  color: var(--text-secondary);
  background: none;
  border: 1px solid var(--border);
  border-radius: 999px;
  cursor: pointer;
}

.nudge-button::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  height: 44px;
  transform: translateY(-50%);
}

.nudge-button.is-sent {
  color: var(--orange-light);
  background: #c0855226;
  border-color: var(--orange);
  cursor: default;
}

:root[data-theme="light"] .nudge-button.is-sent {
  color: var(--orange-dark);
}

.nudge-button:disabled:not(.is-sent) {
  opacity: 0.5;
  cursor: default;
}

.nudge-button:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
}

.nudge-button.pop {
  animation: nudge-pop 0.3s ease;
}

@keyframes nudge-pop {
  40% { transform: scale(1.1); }
}

@media (prefers-reduced-motion: reduce) {
  .nudge-button.pop {
    animation: none;
  }
}

/* The Feed: posts grouped under a heading per day, newest first. */
.feed-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}

/* "Today  Wed, Oct 14" - where one day's posts start. */
.feed-day {
  margin: var(--space-sm) 0 calc(var(--space-xs) - var(--space-md));
  font-size: var(--font-title3);
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--text-primary);
  text-align: left;
}

.feed-day:first-child {
  margin-top: 0;
}

.feed-day small {
  margin-left: var(--space-xs);
  font-size: var(--font-subhead);
  font-weight: 400;
  letter-spacing: 0;
  color: var(--text-secondary);
}

.feed-message {
  text-align: center;
  margin: var(--space-lg) 0;
}

.feed-message .auth-submit-button {
  margin-top: var(--space-md);
}

.feed-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  padding: var(--space-md);
  background: var(--card-solid);
  border: 1px solid var(--card-line);
  border-radius: var(--radius);
  text-align: left;
}

.feed-card.is-you {
  border-color: #c0855266;
}

.feed-card .leaderboard-avatar {
  width: 36px;
  height: 36px;
  font-size: var(--font-subhead);
}

/* The post's time, on the right (the day is in the heading above). */
.feed-card-time {
  font-size: var(--font-footnote);
  color: var(--text-secondary);
  font-variant-numeric: tabular-nums;
}

.feed-card-header {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
}

.feed-card-who {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}

.feed-card-name {
  font-size: var(--font-subhead);
  font-weight: 700;
  color: var(--text-primary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}


/* "Full day" / "Walk" / "Run" - which kind of post this is, at a glance. */
.feed-card-kind {
  display: inline-block;
  margin-bottom: 2px;
  font-size: var(--font-caption2);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--orange-light);
}

.feed-card-kind.is-walk,
.feed-card-kind.is-run {
  color: var(--ring-walkrun);
}

:root[data-theme="light"] .feed-card-kind.is-day {
  color: var(--orange-dark);
}

.feed-card-headline {
  margin: 0;
  font-size: var(--font-body);
  font-weight: 700;
  color: var(--text-primary);
}

.feed-card-stats {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-xs);
}

.feed-stat {
  display: flex;
  flex-direction: column;
}

.feed-stat-value {
  font-size: var(--font-title2);
  font-weight: 800;
  color: var(--text-primary);
  font-variant-numeric: tabular-nums;
}

.feed-stat-label {
  font-size: var(--font-caption);
  color: var(--text-secondary);
}

/* A walk/run post's route: the line only, on a faint dot grid. */
.feed-route {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 10;
  border-radius: var(--radius-md);
  background-color: var(--route-bg);
  background-image: radial-gradient(circle, var(--route-dot) 1.2px, transparent 1.6px);
  background-size: 18px 18px;
}

.feed-route-line {
  fill: none;
  stroke: var(--accent);
  stroke-width: 4.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.feed-route-start,
.feed-route-end {
  stroke: var(--route-bg);
  stroke-width: 3;
}

.feed-route-start {
  fill: var(--accent);
}

.feed-route-end {
  fill: var(--text-primary);
}

.feed-card-footer {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  min-height: 34px;
  padding-top: var(--space-sm);
  border-top: 1px solid var(--border);
}

.kudos-button {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
  padding: 7px 14px;
  font-family: inherit;
  font-size: var(--font-subhead);
  font-weight: 700;
  color: var(--text-secondary);
  background: none;
  border: 1px solid var(--border);
  border-radius: 999px;
  cursor: pointer;
}

.kudos-button svg {
  width: 16px;
  height: 16px;
}

.kudos-button.is-given {
  color: var(--orange-light);
  background: #c0855226;
  border-color: var(--orange);
}

:root[data-theme="light"] .kudos-button.is-given {
  color: var(--orange-dark);
}

.kudos-button.pop {
  animation: kudos-pop 0.3s ease;
}

@keyframes kudos-pop {
  40% { transform: scale(1.15); }
}

@media (prefers-reduced-motion: reduce) {
  .kudos-button.pop {
    animation: none;
  }
}

.kudos-summary {
  flex: 1;
  min-width: 0;
  font-size: var(--font-footnote);
  color: var(--text-secondary);
}

.friend-request-actions {
  display: flex;
  gap: 6px;
}

.friend-request-actions button {
  min-height: 32px;
  padding: 0 12px;
  font-family: inherit;
  font-size: var(--font-subhead);
  font-weight: 700;
  border-radius: var(--radius-sm);
  cursor: pointer;
  border: none;
}

.accept-request-button {
  background-color: var(--accent);
  color: white;
}

.accept-request-button:disabled {
  opacity: 0.6;
  cursor: default;
}

/* The non-interactive "Friends" / "Pending" state shown next to a search
   result when there's a relationship this row doesn't let you act on
   (already friends, or they requested you - handled via Friend Requests
   instead). */
.friend-status-label {
  font-size: var(--font-footnote);
  font-weight: 700;
  color: var(--text-muted);
  flex-shrink: 0;
}

/* Self-contained sizing for the search result row's own Add/Requested
   buttons - unlike .accept-request-button/.decline-request-button above,
   these don't sit inside a .friend-request-actions wrapper, so they need
   their own padding/font-size/radius rather than inheriting it. */
.friend-search-action-button {
  min-height: 32px;
  padding: 0 14px;
  font-family: inherit;
  font-size: var(--font-subhead);
  font-weight: 700;
  border-radius: var(--radius-sm);
  cursor: pointer;
  flex-shrink: 0;
}

.friend-search-action-button:disabled {
  opacity: 0.6;
  cursor: default;
}

.friend-search-add-button {
  background-color: var(--accent);
  color: white;
  border: none;
}

/* Tapping this cancels the request and reverts back to Add - same
   outlined, muted treatment as .decline-request-button, so it visually
   reads as "undoable" rather than a dead-end confirmation label. */
.friend-search-requested-button {
  background: none;
  border: 1px solid var(--border);
  color: var(--text-muted);
}

.decline-request-button {
  background: none;
  border: 1px solid var(--border) !important;
  color: var(--text-muted);
}

.row-options-button {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  flex-shrink: 0;
  padding: 0;
  font-size: 1.1rem;
  line-height: 1;
  color: var(--text-muted);
  background: none;
  border: none;
  border-radius: 50%;
  cursor: pointer;
}

.row-options-button:hover {
  background: var(--overlay);
}

.full-width-button {
  width: 100%;
  margin-top: var(--space-sm);
}

.block-confirm-button {
  background-color: var(--danger);
}

.block-confirm-button:hover {
  background-color: var(--danger-pressed);
}

.settings-panel {
  background: var(--surface-strong);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-md);
  margin-bottom: var(--space-md);
  text-align: left;
  animation: reveal 0.18s ease;
}

@keyframes reveal {
  from { opacity: 0; transform: translateY(-6px); }
  to { opacity: 1; transform: translateY(0); }
}

.settings-title {
  font-size: var(--font-footnote);
  font-weight: 700;
  color: var(--text-secondary);
  text-transform: uppercase;
  letter-spacing: 1.2px;
  margin-bottom: var(--space-sm);
}

.settings-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--space-sm);
  font-size: var(--font-body);
  color: var(--text-soft);
}

.settings-checkbox-row {
  padding-top: var(--space-xs);
  border-top: 1px solid var(--border);
}

.settings-checkbox-row input[type="checkbox"] {
  width: 18px;
  height: 18px;
  accent-color: var(--accent);
  cursor: pointer;
}

.settings-row input {
  width: 56px;
  min-height: 44px;
  padding: 6px;
  text-align: center;
  background: var(--field-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text-primary);
  font-family: inherit;
  font-size: var(--font-body);
}

/* Edit Profile's fields (username/email/phone) need real room to type
   and read back what you typed - unlike the Daily Minimums boxes above,
   which are genuinely just 1-2 digit numbers, so stack label-over-input
   instead of squeezing a full-width value into a small inline box. */
.profile-field {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin-bottom: var(--space-sm);
  text-align: left;
}

.profile-field label {
  font-size: var(--font-footnote);
  font-weight: 600;
  color: var(--text-secondary);
}

.profile-field input {
  width: 100%;
  min-height: 44px;
  padding: 10px 12px;
  font-size: var(--font-body);
  font-family: inherit;
  background: var(--field-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  color: var(--text-primary);
}

.profile-field input:focus {
  outline: none;
  border-color: var(--accent);
}

.settings-row select {
  min-height: 44px;
  padding: 6px 10px;
  background: var(--field-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text-primary);
  font-family: inherit;
  font-size: var(--font-body);
}


.stat-container {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  margin-bottom: var(--space-md);
}

#streak-count {
  display: inline-block;
  font-size: 3.5rem;
  font-weight: 800;
  line-height: 1;
  letter-spacing: -0.03em;
  color: transparent;
  /* Subtle white-to-gray gradient text instead of a solid color */
  background: var(--streak-gradient);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  transition: transform 0.25s cubic-bezier(0.34, 1.56, 0.64, 1);
}

/* Applied briefly via JavaScript right after a successful log, then
   removed - see the "pulse" class handling in script.js. */
#streak-count.pulse {
  transform: scale(1.18);
}

.rings-container {
  display: flex;
  justify-content: center;
  margin-bottom: var(--space-sm);
}

.rings-svg {
  width: 170px;
  height: 170px;
}

/* One ring, split into arc segments (one per exercise) rather than
   Apple's stack of separate concentric rings - every segment shares the
   same radius, and dasharray/dashoffset (set in script.js) carve out
   each one's own slice of the circle, with a gap between slices. */
.ring-track {
  fill: none;
  stroke: var(--overlay);
  stroke-width: 14;
  stroke-linecap: round;
  transform: rotate(-90deg);
  transform-origin: 60px 60px;
}

.ring-progress {
  fill: none;
  stroke-width: 14;
  stroke-linecap: round;
  /* Starts each segment's fill at its slice's own start point instead of
     SVG's default 3 o'clock, and fills clockwise within that slice. */
  transform: rotate(-90deg);
  transform-origin: 60px 60px;
  /* No CSS transition: iPhones animate a dash length unreliably, so a
     saved set's slice is grown frame by frame in celebrations.js. */
}

.ring-pushups { stroke: var(--ring-pushups); }
.ring-planks { stroke: var(--ring-planks); }
.ring-squats { stroke: var(--ring-squats); }
.ring-walkrun { stroke: var(--ring-walkrun); }

/* Plays once, right when a ring crosses into 100% - the actual "closure"
   moment the whole feature exists for. Re-triggered by the JS removing
   and re-adding this class, since merely having a class present doesn't
   replay a CSS animation on its own. */
.ring-closed {
  animation: ring-close-pulse 0.5s ease;
}

@keyframes ring-close-pulse {
  0% { filter: brightness(1); }
  40% { filter: brightness(1.6); }
  100% { filter: brightness(1); }
}

.rings-legend {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: var(--space-sm) var(--space-md);
  margin-bottom: var(--space-md);
}

.rings-legend-item {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: var(--font-caption);
  font-weight: 600;
  color: var(--text-secondary);
}

.rings-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex-shrink: 0;
}

.rings-dot-pushups { background: var(--ring-pushups); }
.rings-dot-planks { background: var(--ring-planks); }
.rings-dot-squats { background: var(--ring-squats); }
.rings-dot-walkrun { background: var(--ring-walkrun); }

#status-message {
  color: var(--text-secondary);
  font-size: var(--font-subhead);
  font-weight: 500;
  margin-bottom: var(--space-lg);
  min-height: 20px;
}

/* The day just completed itself - the one line worth noticing. */
#status-message.is-done {
  color: var(--accent-text);
  font-weight: 700;
}

/* Exercises as a row of 3 tiles instead of stacked list rows - reads
   more like a dashboard, and puts equal visual weight on all 3. */
.exercise-list {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-sm);
  margin-bottom: var(--space-sm);
}

/* The optional 4th activity - a single full-width row rather than a
   tile, since it's a distinct "bonus" activity, not one of the core 3. */
.walkrun-row {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  padding: var(--space-sm); /* same as the cards above, so their edges line up */
  margin-bottom: var(--space-sm);
  background: var(--surface);
  border-radius: var(--radius);
}

.walkrun-row label {
  flex: 1;
  text-align: left;
  font-size: var(--font-subhead);
  font-weight: 600;
  color: var(--text-soft);
}

.walkrun-row input {
  width: 60px;
  min-height: 44px;
  padding: 6px;
  font-size: var(--font-body);
  font-weight: 700;
  font-family: inherit;
  text-align: center;
  background: var(--field-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text-primary);
}

.walkrun-row input:focus {
  outline: none;
  border-color: var(--accent);
}

.walkrun-row input:disabled {
  opacity: 0.5;
}

.start-tracking-button {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  flex-shrink: 0;
  background: var(--accent);
  border: none;
  border-radius: var(--radius-sm);
  color: white;
  cursor: pointer;
}

.start-tracking-button svg {
  width: 18px;
  height: 18px;
}

.start-tracking-button:disabled {
  background: var(--control-bg);
  color: var(--text-muted);
  cursor: not-allowed;
}

/* Tracking screen and post-run summary */
.tracking-stats {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-md) var(--space-xl);
  margin-bottom: var(--space-lg);
}

.tracking-stat {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
}

.tracking-stat-value {
  font-size: var(--font-title1);
  font-weight: 800;
  color: var(--text-primary);
  line-height: 1;
}

.tracking-stat-label {
  font-size: var(--font-caption);
  font-weight: 600;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 1px;
}

#record-map,
#summary-map {
  width: 100%;
  height: 240px;
  border-radius: var(--radius);
  margin-bottom: var(--space-md);
  background: var(--surface-solid);
}

/* Share: a route as a story image */
.share-route-open,
.share-route-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
}

/* (beats .tracking-cancel-button's muted gray, which comes later) */
.tracking-buttons .share-route-open {
  color: var(--text-primary);
}

.share-route-open svg,
.share-route-button svg {
  width: 20px;
  height: 20px;
}

#share-route-screen .large-title {
  margin-bottom: var(--space-md);
}

.share-route-preview {
  width: 240px;
  aspect-ratio: 9 / 16;
  margin: 0 auto var(--space-md);
  border-radius: 18px;
  overflow: hidden;
  box-shadow: 0 12px 40px #00000059;
}

/* The Sticker image is see-through - the preview fakes a photo behind it. */
.share-route-preview[data-template="sticker"] {
  background: linear-gradient(160deg, #3d5a6c, #6b7f5e 50%, #c9925e);
}

.share-route-preview canvas {
  display: block;
  width: 100%;
  height: 100%;
}

/* (.plan-pace, which sets 4 columns, comes later - hence the double class) */
.plan-pace.share-route-templates {
  grid-template-columns: repeat(3, 1fr);
  margin-bottom: var(--space-md);
}

.share-route-note {
  margin: var(--space-sm) 0 0;
  font-size: var(--font-footnote);
  color: var(--text-muted);
  text-align: center;
}


.tracking-buttons {
  display: flex;
  gap: var(--space-sm);
}

.tracking-cancel-button {
  flex: 1;
  min-height: 48px;
  font-family: inherit;
  font-size: var(--font-body);
  font-weight: 700;
  color: var(--text-secondary); /* muted read as disabled (under 4.5:1) */
  background: none;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  cursor: pointer;
}

.tracking-buttons .auth-submit-button {
  flex: 1;
}

.tracking-pause-button {
  flex: 1;
  min-height: 48px;
  font-family: inherit;
  font-size: var(--font-body);
  font-weight: 700;
  color: var(--text-primary);
  background: var(--control-bg);
  border: none;
  border-radius: var(--radius-md);
  cursor: pointer;
}

/* Hold-to-confirm (walk Finish / Cancel). The fill grows left to right
   while the button is held and drains back if it's let go early. No text
   selection or iOS long-press menu, which a hold would otherwise trigger. */
.hold-panel {
  position: relative;
}

.hold-button {
  position: relative;
  overflow: hidden;
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
}

.hold-fill {
  position: absolute;
  inset: 0;
  background: var(--hold-fill);
  transform: scaleX(0);
  transform-origin: left;
  pointer-events: none;
}

.hold-label {
  position: relative;
}

.tracking-cancel-button .hold-fill {
  background: var(--danger-tint);
}

.tracking-cancel-button.is-holding {
  color: var(--danger);
  border-color: var(--danger);
}

/* "Hold to finish" - a small tip bubble that floats just above the
   buttons for a moment after a quick tap, without moving anything. */
.hold-hint {
  position: absolute;
  bottom: calc(100% + var(--space-sm));
  left: 50%;
  margin: 0;
  padding: 6px 14px;
  font-size: var(--font-subhead);
  font-weight: 600;
  white-space: nowrap;
  color: var(--text-primary);
  background: var(--popover-bg);
  border: 1px solid var(--border);
  border-radius: 999px;
  box-shadow: 0 6px 20px #00000040;
  opacity: 0;
  transform: translate(-50%, 4px);
  transition: opacity 0.18s ease, transform 0.18s ease;
  pointer-events: none;
}

.hold-hint.is-shown {
  opacity: 1;
  transform: translate(-50%, 0);
}

@media (prefers-reduced-motion: reduce) {
  .hold-hint {
    transition: none;
  }
}

/* One rhythm in every card: the same 10pt inside on all four sides; name
   and minimum together, 10pt to the count, 6pt to Verify. */
.exercise-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0;
  padding: var(--space-sm);
  background: var(--surface);
  border: 1px solid transparent;
  border-radius: var(--radius);
  transition: border-color 0.15s ease;
}

.exercise-tile:focus-within {
  border-color: var(--orange);
}

.exercise-icon svg {
  width: 22px;
  height: 22px;
}

.exercise-icon {
  color: var(--text-secondary);
}

.exercise-tile label {
  font-size: var(--font-footnote);
  color: var(--text-soft);
  font-weight: 600;
}

.min-tag {
  font-size: var(--font-caption2);
  color: var(--text-muted);
  font-weight: 400;
}

.exercise-tile .min-tag {
  margin-top: 2px;
}

.exercise-tile input {
  width: 100%;
  min-height: 44px;
  margin-top: var(--space-sm);
  margin-bottom: var(--space-xs); /* Verify sits below with margin-top: auto */
  padding: 8px;
  font-size: var(--font-body);
  font-weight: 700;
  font-family: inherit;
  text-align: center;
  background: var(--field-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text-primary);
  transition: border-color 0.15s ease, background 0.15s ease;
}

.exercise-tile input:focus {
  outline: none;
  border-color: var(--orange);
  background: var(--field-bg-hover);
}

.exercise-tile input:disabled {
  opacity: 0.5;
}

.error {
  color: var(--danger);
  font-size: var(--font-footnote);
  min-height: 18px;
  margin-bottom: var(--space-sm);
}

.error.success {
  color: var(--accent-text);
}






.onboarding-text {
  font-size: var(--font-subhead);
  color: var(--text-secondary);
  line-height: 1.5;
}











.auth-fields {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  margin-bottom: var(--space-sm);
}

.auth-fields input {
  width: 100%;
  min-height: 44px;
  padding: 10px 12px;
  font-size: var(--font-body);
  font-family: inherit;
  background: var(--field-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  color: var(--text-primary);
  transition: border-color 0.15s ease;
}

.auth-fields input:focus {
  outline: none;
  border-color: var(--accent);
}

/* Password box with an eye button on the right to show what's typed. */
.password-field {
  position: relative;
}

.auth-fields .password-field input {
  padding-right: 48px;
}

.password-eye {
  position: absolute;
  top: 0;
  right: 0;
  width: 44px;
  height: 100%;
  min-height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  background: none;
  border: none;
  color: var(--text-secondary);
  cursor: pointer;
}

.password-eye svg {
  width: 20px;
  height: 20px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.password-eye .eye-off,
.password-field.showing .eye-on {
  display: none;
}

.password-field.showing .eye-off {
  display: block;
}

.auth-submit-button {
  width: 100%;
  min-height: 48px;
  font-size: var(--font-body);
  font-weight: 600;
  font-family: inherit;
  color: white;
  background-color: var(--accent);
  border: none;
  border-radius: var(--radius-md);
  cursor: pointer;
  transition: background-color 0.15s ease;
}

.auth-submit-button:hover {
  background-color: var(--accent-pressed);
}

.auth-submit-button:disabled {
  background: var(--control-bg);
  color: var(--text-muted);
  cursor: not-allowed;
}

.auth-forgot-password {
  display: block;
  margin: 0 0 var(--space-sm) auto;
  background: none;
  border: none;
  font-family: inherit;
  font-size: var(--font-subhead);
  font-weight: 600;
  color: var(--accent-text);
  cursor: pointer;
}

.auth-toggle {
  margin-top: var(--space-md);
  font-size: var(--font-subhead);
  color: var(--text-muted);
}

.link-button {
  background: none;
  border: none;
  font-family: inherit;
  font-size: var(--font-subhead);
  font-weight: 700;
  color: var(--accent-text);
  cursor: pointer;
  padding: 0;
  margin-left: 4px;
}

.guest-signup-button {
  width: 100%;
  min-height: 44px;
  margin-top: var(--space-md);
  font-family: inherit;
  font-size: var(--font-body);
  font-weight: 700;
  color: white;
  background-color: var(--accent);
  border: none;
  border-radius: var(--radius-md);
  cursor: pointer;
}

.guest-signup-button:hover {
  background-color: var(--accent-pressed);
}

.auth-skip {
  display: block;
  margin: var(--space-md) auto 0;
  background: none;
  border: none;
  font-family: inherit;
  font-size: var(--font-subhead);
  color: var(--text-muted);
  cursor: pointer;
}

/* Classic iOS Settings-app "grouped list" look: one rounded container
   with thin divider lines between rows, instead of separate floating
   buttons. */
.account-menu {
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border-radius: var(--radius);
  overflow: hidden;
  margin-bottom: var(--space-md);
}

.account-menu-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  min-height: 48px;
  padding: 0 var(--space-md);
  font-family: inherit;
  font-size: var(--font-body);
  font-weight: 600;
  color: var(--text-primary);
  background: none;
  border: none;
  border-bottom: 1px solid var(--border);
  cursor: pointer;
}

.account-menu-item:last-child {
  border-bottom: none;
}

.account-menu-chevron {
  color: var(--text-muted);
  font-size: var(--font-body);
}

.log-out-button {
  width: 100%;
  min-height: 44px;
  margin-top: var(--space-md);
  font-family: inherit;
  font-size: var(--font-body);
  font-weight: 600;
  color: var(--text-muted);
  background: none;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  cursor: pointer;
}

/* Two side-by-side stat tiles instead of plain stacked sentences -
   mirrors the exercise tiles above for visual consistency. */
/* Best streak and rest days, stacked beside the 8-week grid. */
.stats-stack {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--space-lg);
}

.stat-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
}

.stat-number {
  font-size: var(--font-title1);
  font-weight: 800;
  color: var(--teal-light);
  line-height: 1;
}

#best-streak-count {
  color: var(--orange-light);
}

.stat-label {
  font-size: var(--font-caption);
  font-weight: 600;
  color: var(--text-faint);
  text-transform: uppercase;
  letter-spacing: 0.1em;
}

.profile-header {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  margin-bottom: var(--space-lg);
}

.profile-avatar {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 56px;
  height: 56px;
  flex-shrink: 0;
  border-radius: 50%;
  background: var(--control-bg);
  color: var(--text-secondary);
}

.profile-avatar svg {
  width: 30px;
  height: 30px;
}

.profile-header-info {
  display: flex;
  flex-direction: column;
  gap: 4px;
  text-align: left;
}

.profile-username {
  font-size: var(--font-body);
  font-weight: 700;
  color: var(--text-primary);
}

.profile-follow-stats {
  display: flex;
  gap: var(--space-md);
}

.follow-stat-button {
  display: flex;
  align-items: baseline;
  gap: 4px;
  background: none;
  border: none;
  font-family: inherit;
  cursor: pointer;
  padding: 0;
}

.follow-stat-count {
  font-size: var(--font-subhead);
  font-weight: 800;
  color: var(--text-primary);
}

.follow-stat-label {
  font-size: var(--font-footnote);
  color: var(--text-muted);
}

/* The 8-week grid with the two stats beside it. */
.progress-summary {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  margin-bottom: var(--space-lg);
}

.heatmap-section {
  display: block;
  flex-shrink: 0;
  text-align: left;
  background: none;
  border: none;
  cursor: pointer;
  font-family: inherit;
}


.heatmap-label {
  font-size: var(--font-caption);
  font-weight: 700;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 1.2px;
  margin-bottom: var(--space-sm);
}

.heatmap-tap-hint {
  text-transform: none;
  letter-spacing: normal;
  font-weight: 400;
  opacity: 0.7;
}







/* Full calendar view */
.calendar-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--space-md);
}

.calendar-nav-button {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  font-size: var(--font-title2);
  font-family: inherit;
  background: var(--surface);
  border: none;
  border-radius: var(--radius-md);
  color: var(--text-primary);
  cursor: pointer;
}

.calendar-weekdays {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  margin-bottom: var(--space-sm);
}

.calendar-weekdays span {
  text-align: center;
  font-size: var(--font-caption2);
  font-weight: 700;
  color: var(--text-muted);
  text-transform: uppercase;
}

.calendar-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 4px;
  margin-bottom: var(--space-lg);
}

.calendar-day {
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 1;
  border-radius: var(--radius-sm);
  font-size: var(--font-subhead);
  font-weight: 600;
  color: var(--text-secondary);
  background: var(--surface);
}

.calendar-day.empty {
  background: none;
}

.calendar-day.completed {
  background-color: var(--accent);
  color: white;
  font-weight: 800;
}

.calendar-day.is-today {
  border: 2px solid var(--accent);
}

/* A rest day: pale green with a small moon in the corner. */
.calendar-day.rest {
  position: relative;
  background-color: var(--heatmap-rest);
  color: var(--text-primary);
}

.calendar-day.rest::after {
  content: "";
  position: absolute;
  top: 4px;
  right: 4px;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  /* A crescent: only the circle's offset shadow shows. */
  box-shadow: -2.5px 1.5px 0 0 var(--heatmap-rest-mark);
  transform: rotate(-20deg);
}

.calendar-legend {
  display: flex;
  gap: var(--space-md);
  margin-top: calc(var(--space-sm) - var(--space-lg)); /* tucked under the grid's own bottom margin */
  margin-bottom: var(--space-lg);
  font-size: var(--font-footnote);
  color: var(--text-secondary);
}

.calendar-legend span {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.calendar-legend i {
  width: 14px;
  height: 14px;
  border-radius: 3px;
}

.calendar-legend-done {
  background-color: var(--accent);
}

.calendar-legend-rest {
  background-color: var(--heatmap-rest);
}

.section-heading {
  font-size: var(--font-footnote);
  font-weight: 700;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 1.2px;
  text-align: left;
  margin-bottom: var(--space-sm);
}

.record-past-routes-heading {
  margin-top: var(--space-lg);
  padding-top: var(--space-md);
  border-top: 1px solid var(--border);
}

/* iOS's "Large Title" pattern: each screen gets its own bold heading
   instead of repeating the app's logo on every tab - this is what a
   native app's navigation title looks like at the top of a screen. */
.large-title {
  font-size: var(--font-large-title);
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--text-primary);
  text-align: left;
  margin-bottom: var(--space-lg);
}

/* Puts a screen's title and a top-right action (like the Me tab's gear
   icon) on one row instead of stacking them - the title keeps its usual
   spacing below, so screens that don't need a header button are unaffected. */
.tab-header-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm);
  margin-bottom: var(--space-lg);
}

.tab-header-row .large-title {
  margin-bottom: 0;
}

.gear-button {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  flex-shrink: 0;
  background: var(--surface);
  border: none;
  border-radius: 50%;
  color: var(--text-secondary);
  cursor: pointer;
}

.gear-button svg {
  width: 19px;
  height: 19px;
}

.gear-button:hover {
  background: var(--overlay);
}

/* A calendar-style grid: 7 cells per row (Sun-Sat), wrapping to a new
   row each week automatically - no horizontal scrolling needed. */
/* Fixed small cells instead of stretching to the card's full width -
   before, each of the 8 rows was as tall as 1/7 of the card's width
   (roughly 45px), making the whole grid nearly 400px tall on its own. */
.heatmap-grid {
  display: grid;
  grid-template-columns: repeat(7, 14px);
  grid-auto-rows: 14px;
  gap: 3px;
  padding: 6px 0;
}

.heatmap-day {
  border-radius: 3px;
  background-color: var(--heatmap-empty);
  transition: transform 0.2s ease;
}

.heatmap-day.completed {
  background-color: var(--accent);
  box-shadow: 0 0 8px rgba(34, 197, 94, 0.4);
}

.heatmap-day.rest {
  background-color: var(--heatmap-rest);
}

.heatmap-day:hover {
  transform: scale(1.1);
}

/* Small phones: tighten spacing and scale down the biggest text so
   nothing forces horizontal scrolling or feels oversized. */
@media (max-width: 380px) {
  .card {
    padding: var(--space-lg) var(--space-md);
  }

  #streak-count {
    font-size: 3.4rem;
  }

  .brand-logo {
    height: 34px;
  }

  .exercise-tile {
    padding: var(--space-xs);
  }

  .exercise-icon svg {
    width: 18px;
    height: 18px;
  }
}

/* Every exercise count comes from the camera, so the number boxes are
   read-only displays of today's verified total with a Verify button
   underneath, not inputs. */
.exercise-tile input[readonly] {
  cursor: default;
  pointer-events: none;
}

.verify-button {
  width: 100%;
  min-height: 34px;
  margin-top: var(--space-xs);
  font-family: inherit;
  font-size: var(--font-subhead);
  font-weight: 700;
  color: white;
  background: var(--accent);
  border: none;
  border-radius: var(--radius-sm);
  cursor: pointer;
}

.verify-button:disabled {
  background: var(--control-bg);
  color: var(--text-muted);
  cursor: default;
}

.camera-stage {
  position: relative;
  width: 100%;
  aspect-ratio: 4 / 3;
  background: var(--surface-solid);
  border-radius: var(--radius);
  overflow: hidden;
  margin-bottom: var(--space-md);
}

.camera-stage video,
.camera-stage canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* The front camera is shown mirrored (like a mirror) so moving your
   left side moves the left side of the screen. */
.camera-stage.mirrored video,
.camera-stage.mirrored canvas {
  transform: scaleX(-1);
}

/* Everything someone reads MID-SET is large: they're doing push-ups or a
   plank six feet from a phone propped on the floor, so the count, the
   instruction and the feedback have to be readable from across a room.
   Smaller text (the live percentages, the tip) is for people near it. */
.camera-count {
  font-size: clamp(6rem, 30vw, 9rem);
  font-weight: 800;
  line-height: 1;
  color: var(--text-primary);
}

.camera-count-label {
  font-size: 1rem;
  font-weight: 700;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  margin-bottom: var(--space-sm);
}

/* Two lines of height are always reserved so the layout doesn't jump
   every time the instruction changes length. */
.camera-state {
  font-size: clamp(1.5rem, 6vw, 2.2rem);
  font-weight: 800;
  line-height: 1.2;
  min-height: 2.4em;
  margin-bottom: var(--space-sm);
  color: var(--text-primary);
}

.camera-feedback {
  font-size: clamp(1.25rem, 5vw, 1.8rem);
  font-weight: 700;
  line-height: 1.25;
  color: var(--text-secondary);
  min-height: 1.3em;
  margin-bottom: var(--space-sm);
}

.camera-feedback.good { color: var(--accent); }
.camera-feedback.bad { color: var(--danger); }

.camera-live {
  font-size: 1rem;
  color: var(--text-muted);
  min-height: 1.2em;
  margin-bottom: var(--space-md);
}

.camera-review {
  margin-bottom: var(--space-md);
}

/* After a set: "Nice work!", one big number, what it counts, and the
   buttons - none of the live coaching text. */
#camera-screen.is-review #camera-tip,
#camera-screen.is-review .camera-count,
#camera-screen.is-review .camera-count-label,
#camera-screen.is-review .camera-state,
#camera-screen.is-review .camera-feedback,
#camera-screen.is-review .camera-live {
  display: none;
}

/* The review has its own small heading (.reward-eyebrow). */
#camera-screen.is-review .large-title {
  display: none;
}

/* ---------- after a set: what it earned ----------
   --set-color is the exercise's ring color (set by camera-verify.js). */
.camera-review {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-md);
  padding-top: var(--space-md);
}

.camera-review.hidden {
  display: none;
}

.reward-eyebrow {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  font-size: var(--font-footnote);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-secondary);
}

.reward-eyebrow i {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--set-color);
}

.reward-ring {
  position: relative;
  width: 210px;
  height: 210px;
}

.reward-ring svg {
  display: block;
  width: 100%;
  height: 100%;
  overflow: visible;
}

.reward-ring circle {
  fill: none;
  stroke-width: 12;
  stroke-linecap: round;
}

.reward-ring-track {
  stroke: var(--heatmap-empty);
}

.reward-ring-fill {
  stroke: var(--set-color);
}

/* The number + label are centered on what you SEE: text-box trims each
   line to its letters (cap height to baseline), so the empty room a big
   font keeps above its digits doesn't push them low. Works in any font. */
.reward-ring-center {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
}

@supports (text-box: trim-both cap alphabetic) {
  .reward-ring-center {
    gap: 14px;
  }

  .camera-result,
  .camera-result-label {
    text-box: trim-both cap alphabetic;
  }
}

.camera-result {
  margin: 0;
  /* Negative letter-spacing also trims after the last digit; this gives
     it back so the digits sit in the middle, not a hair right. */
  padding-right: 0.02em;
  font-size: 64px;
  font-weight: 800;
  line-height: 1;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
  color: var(--text-primary);
}

/* "120s" and up: room to breathe inside the ring. */
.camera-result.is-long {
  font-size: 52px;
}

.camera-result.is-zero {
  color: var(--text-muted);
}

.camera-result-label {
  margin: 0;
  font-size: var(--font-subhead);
  font-weight: 600;
  color: var(--text-secondary);
}

.reward-today {
  margin: calc(var(--space-sm) * -1) 0 0;
  min-height: 20px;
  font-size: var(--font-subhead);
  font-weight: 600;
  color: var(--text-secondary);
  font-variant-numeric: tabular-nums;
}

.reward-today b {
  color: var(--text-primary);
}

.reward-badges {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px;
  min-height: 34px;
}

.reward-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 7px 12px;
  border-radius: 999px;
  font-size: var(--font-subhead);
  font-weight: 700;
}

.reward-badge svg {
  width: 16px;
  height: 16px;
}

.reward-badge.is-done {
  color: var(--accent-text);
  background: var(--accent-tint);
}

.reward-badge.is-best {
  color: var(--trophy);
  background: var(--trophy-tint);
}

.reward-badge.is-day {
  color: white;
  background: var(--accent);
}

.reward-coach {
  display: flex;
  align-items: flex-end;
  gap: 10px;
  width: 100%;
  min-height: 64px;
}

.reward-pet {
  flex: none;
  width: 54px;
  transform-origin: 50% 100%;
}

.reward-pet svg {
  display: block;
  width: 54px;
  height: auto;
  overflow: visible;
}

/* The sign-up pet bounces in on its own; here the reward moves it instead. */
.reward-pet .ob-mascot {
  animation: none;
}

.reward-coach .pet-bubble {
  flex: 1;
  min-width: 0;
  margin-bottom: 4px;
  text-align: left;
}

.reward-coach .pet-bubble::before {
  left: -7px;
  bottom: 13px;
  transform: rotate(45deg);
}

.reward-coach.is-quiet {
  visibility: hidden;
}

.pet-bubble .gold {
  color: var(--trophy);
}

.reward-day {
  display: flex;
  gap: 8px;
  width: 100%;
}

.reward-day:empty {
  display: none;
}

.reward-day span {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 36px;
  font-size: var(--font-footnote);
  font-weight: 600;
  color: var(--text-muted);
  border: 1px solid var(--border);
  border-radius: 999px;
}

.reward-day span i {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--c);
  opacity: 0.45;
}

.reward-day span.is-done {
  color: var(--text-primary);
  background: var(--surface-solid);
}

.reward-day span.is-done i {
  opacity: 1;
}

.reward-day span.is-done::after {
  content: "✓";
  font-weight: 800;
  color: var(--accent-text);
}

.reward-pop {
  animation: reward-pop 0.45s cubic-bezier(0.3, 1.5, 0.5, 1) both;
}

.reward-pet.hop {
  animation: pet-coach-hop-in 0.6s cubic-bezier(0.3, 1.4, 0.5, 1) both;
}

.reward-pet.shake {
  animation: pet-coach-shake 0.6s ease-in-out both;
}

#camera-save-button.is-verified {
  animation: reward-stamp 0.4s ease-out both;
}

/* Confetti in the ring colors, bursting from the ring. */
.reward-bit {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 8px;
  height: 12px;
  border-radius: 2px;
  pointer-events: none;
}

@keyframes reward-pop {
  0% {
    opacity: 0;
    transform: scale(0.5);
  }
  60% {
    opacity: 1;
    transform: scale(1.08);
  }
  100% {
    transform: none;
  }
}

@keyframes reward-stamp {
  0% {
    transform: scale(1.2);
  }
  60% {
    transform: scale(0.95);
  }
  100% {
    transform: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .reward-pop,
  .reward-pet.hop,
  .reward-pet.shake,
  #camera-save-button.is-verified {
    animation: none;
  }
}

.camera-result-label:empty,
.camera-result-note:empty {
  display: none;
}

.camera-result-note {
  margin: 0;
  font-size: var(--font-subhead);
  color: var(--text-secondary);
}

.camera-review .tracking-buttons {
  width: 100%;
}

/* The title and the setup tip only matter before the set starts - once
   it's running they just push the big text off the bottom of the screen,
   where nobody propping a phone against a wall can scroll to it. */
#camera-screen.is-running .large-title,
#camera-screen.is-running #camera-tip {
  display: none;
}

/* The buttons you press from the floor / just after finishing. */
#camera-start-button,
#camera-finish-button {
  min-height: 72px;
  font-size: 1.5rem;
}

/* A phone propped on the floor is usually sideways. Put the video beside
   the text instead of above it, so the big text isn't pushed off screen,
   and let the screen use the full width instead of the narrow phone card. */
@media (orientation: landscape) and (max-height: 560px) {
  #camera-screen {
    max-width: 100%;
    display: grid;
    grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr);
    column-gap: var(--space-lg);
    align-content: start;
    padding-top: calc(var(--space-md) + var(--safe-top));
    padding-bottom: var(--space-md);
  }

  #camera-screen > * {
    grid-column: 2;
  }

  #camera-screen .camera-stage {
    grid-column: 1;
    grid-row: 1 / span 14;
    margin-bottom: 0;
    align-self: start;
  }

  /* Sized from the screen's HEIGHT here, since height is the scarce
     thing sideways - and nothing may fall below the bottom edge, because
     nobody can scroll a phone that's propped up on the floor. */
  .camera-count {
    font-size: clamp(4rem, 23vh, 8rem);
  }

  .camera-state {
    font-size: clamp(1.1rem, 6.5vh, 1.8rem);
    min-height: 0;
    margin-bottom: 4px;
  }

  .camera-feedback {
    font-size: clamp(1rem, 5.5vh, 1.5rem);
    min-height: 0;
    margin-bottom: var(--space-sm);
  }

  #camera-start-button,
  #camera-finish-button {
    min-height: 56px;
    font-size: 1.3rem;
  }

  #camera-screen .camera-stage {
    max-height: calc(100vh - 32px);
  }

  /* While a set is running, only what matters from across the room stays. */
  #camera-screen.is-running .large-title,
  #camera-screen.is-running #camera-tip,
  #camera-screen.is-running .camera-count-label,
  #camera-screen.is-running .camera-live,
  #camera-screen.is-running #camera-flip-button {
    display: none;
  }
}
/* Every rep attempt from the set just finished, with the reason any didn't count. */
.camera-attempts {
  list-style: none;
  text-align: left;
  font-size: var(--font-subhead);
  margin: 0 0 var(--space-md);
}

.camera-attempts li {
  padding: 7px 0;
  border-bottom: 1px solid var(--border);
}

.camera-attempts li.ok { color: var(--accent); }
.camera-attempts li.no { color: var(--danger); }

a.account-menu-item {
  text-decoration: none;
}

.delete-account-button {
  width: 100%;
  min-height: 44px;
  margin-top: var(--space-md);
  font-family: inherit;
  font-size: var(--font-body);
  font-weight: 600;
  color: var(--danger);
  background: none;
  border: none;
  cursor: pointer;
}

.auth-legal {
  margin-top: var(--space-md);
  font-size: var(--font-caption);
  color: var(--text-muted);
}

.auth-legal a {
  color: var(--text-secondary);
}
/* ---------- My Plan ---------- */
/* Deliberately minimal: today's targets, the next level-up, pace, walk/run.
   Each ⓘ opens one short tip bubble that any tap closes. */
.nav-back {
  display: flex;
  align-items: center;
  gap: 2px;
  width: fit-content;
  /* auto on the right pins it to the left, like an iPhone back button */
  margin: 0 auto var(--space-sm) -6px;
  padding: 4px 6px;
  font-family: inherit;
  font-size: var(--font-body);
  font-weight: 600;
  color: var(--accent-text);
  background: none;
  border: none;
  cursor: pointer;
}

.nav-back svg {
  width: 22px;
  height: 22px;
}

#my-plan-screen .large-title {
  margin-bottom: var(--space-md);
}

.plan-card,
.plan-pace-wrap {
  position: relative;
  margin-bottom: var(--space-md);
  text-align: left;
}

.plan-card {
  padding: var(--space-md);
  background: var(--surface-strong);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

.plan-targets {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-xs);
  text-align: center;
}

.plan-target-value {
  font-size: 2rem;
  font-weight: 800;
  letter-spacing: -0.02em;
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
  color: var(--text-primary);
}

.plan-target-name {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 5px;
  margin-top: 4px;
  font-size: var(--font-caption);
  font-weight: 700;
  color: var(--text-secondary);
}

.plan-target-name i {
  width: 8px;
  height: 8px;
  border-radius: 50%;
}

.plan-mini-label {
  margin: 0 0 var(--space-xs);
  font-size: var(--font-caption);
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
  text-align: center;
}

.plan-eased {
  margin: var(--space-sm) 0 0;
  font-size: var(--font-footnote);
  color: var(--text-secondary);
  text-align: center;
}

.plan-title-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm);
}

.plan-title-row b {
  font-size: var(--font-body);
  color: var(--text-primary);
}

.plan-bar {
  height: 12px;
  margin: 12px 0;
  border-radius: 99px;
  background: var(--control-bg);
  overflow: hidden;
}

.plan-bar i {
  display: block;
  height: 100%;
  border-radius: 99px;
  background: var(--accent);
  transition: width 0.4s ease;
}

.plan-next {
  display: flex;
  justify-content: center;
  gap: 10px;
  font-size: var(--font-subhead);
  font-weight: 800;
  color: var(--accent-text);
  font-variant-numeric: tabular-nums;
}

.plan-next .sep {
  color: var(--text-muted);
  font-weight: 400;
}

.plan-next .held {
  color: var(--warning);
}

.plan-pace {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 4px;
  padding: 4px;
  background: var(--surface-strong);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

.plan-pace button {
  padding: 10px 2px;
  font-family: inherit;
  font-size: var(--font-footnote);
  font-weight: 700;
  color: var(--text-secondary);
  background: none;
  border: none;
  border-radius: var(--radius-md);
  cursor: pointer;
}

.plan-pace button[aria-checked="true"] {
  color: white;
  background: var(--accent);
}

.plan-pace-line {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  margin: 8px 0 0;
  min-height: 24px;
  font-size: var(--font-footnote);
  color: var(--text-secondary);
}

.plan-switch {
  position: relative;
  flex-shrink: 0;
  width: 51px;
  height: 31px;
  padding: 0;
  border: none;
  border-radius: 99px;
  background: var(--control-bg);
  cursor: pointer;
  transition: background 0.2s;
}

.plan-switch::after {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 27px;
  height: 27px;
  border-radius: 50%;
  background: white;
  box-shadow: 0 2px 4px #0000004d;
  transition: transform 0.2s;
}

.plan-switch[aria-checked="true"] {
  background: var(--accent);
}

.plan-switch[aria-checked="true"]::after {
  transform: translateX(20px);
}

.plan-chips {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 6px;
  margin-top: 12px;
}

.plan-chips button {
  padding: 8px 2px;
  font-family: inherit;
  font-size: var(--font-footnote);
  font-weight: 700;
  color: var(--text-primary);
  background: var(--control-bg);
  border: 2px solid transparent;
  border-radius: 99px;
  cursor: pointer;
}

.plan-chips button[aria-checked="true"] {
  color: var(--ring-walkrun);
  border-color: var(--ring-walkrun);
  background: transparent;
}

/* Drawn small like iOS's info icon (18px), with Apple's 44pt minimum tap
   area around it (the invisible ::after) so it's still easy to hit. */
.tip-btn {
  position: relative;
  display: inline-grid;
  place-items: center;
  flex-shrink: 0;
  width: 18px;
  height: 18px;
  padding: 0;
  font-family: inherit;
  font-size: 11px;
  font-weight: 700;
  line-height: 1;
  color: var(--text-muted);
  background: none;
  border: 1.25px solid var(--text-muted);
  border-radius: 50%;
  cursor: pointer;
}

.tip-btn::after {
  content: "";
  position: absolute;
  inset: -13px;
}

.tip-btn[aria-expanded="true"] {
  color: var(--accent-text);
  border-color: var(--accent);
}

.tip-corner {
  position: absolute;
  top: 12px;
  right: 12px;
}

/* room for the corner ⓘ so it never touches the numbers - on both sides,
   so the three numbers stay centered in the card */
#plan-today-card {
  padding-left: 34px;
  padding-right: 34px;
}

/* iOS-style pop-up: a raised color above the cards, a soft shadow, and an
   arrow pointing at the ⓘ it belongs to. Placed right under that ⓘ by
   script (top and --arrow-x). */
.plan-tip {
  position: absolute;
  left: 10px;
  right: 10px;
  z-index: 20;
  padding: 12px 14px;
  font-size: var(--font-subhead);
  line-height: 1.45;
  text-align: left;
  color: var(--text-primary);
  background: var(--popover-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: 0 10px 34px #00000066, 0 2px 8px #00000033;
}

.plan-tip::before {
  content: "";
  position: absolute;
  top: -7px;
  left: calc(var(--arrow-x, 50%) - 7px);
  width: 12px;
  height: 12px;
  background: var(--popover-bg);
  border-top: 1px solid var(--border);
  border-left: 1px solid var(--border);
  transform: rotate(45deg);
}

/* Opened above its ⓘ (no room below): the arrow points down. */
.plan-tip.above::before {
  top: auto;
  bottom: -7px;
  transform: rotate(225deg);
}

.plan-tip b {
  color: var(--accent-text);
}

.plan-tip .amber {
  color: var(--warning);
  font-weight: 800;
}

.plan-tip ul {
  margin: 6px 0;
  padding-left: 18px;
}

.plan-tip li {
  margin: 2px 0;
}

.plan-tip li.mine {
  font-weight: 800;
  color: var(--accent-text);
}

.plan-tip .tip-note {
  color: var(--text-secondary);
}

/* On Today, a signed-in player's "min" tags open My Plan. */
.min-tag.is-link {
  cursor: pointer;
  text-decoration: underline dotted;
  text-underline-offset: 3px;
}

@media (prefers-reduced-motion: reduce) {
  .plan-bar i,
  .plan-switch,
  .plan-switch::after {
    transition: none;
  }
}

/* ---------- Touch targets: Apple's 44x44pt minimum ---------- */
/* Main controls get a real 44pt height. */
.verify-button,
.plan-chips button,
.nav-back {
  min-height: 44px;
}

/* Icon buttons: 44pt boxes, pulled back by negative margins so the icon
   stays where it was. */
.row-options-button {
  width: 44px;
  height: 44px;
  margin: -9px -9px -9px 0;
}

/* Your own leaderboard row has no ⋮; this takes its place so every
   streak number lines up in one column. */
.row-options-spacer {
  flex-shrink: 0;
  width: 35px; /* the ⋮ button's 44 minus its -9 right margin */
}

.gear-button {
  width: 44px;
  height: 44px;
  margin: -4px;
}

/* Small inline controls keep their look but get an invisible 44pt-tall
   touch area around them (the ::before), so a slightly-off tap still
   counts - the same approach as the iPhone's own small buttons. */
.auth-forgot-password,
.auth-skip,
.link-button,
.auth-legal a,
.follow-stat-button,
.kudos-button,
.friend-request-actions button,
.friend-search-action-button,
.min-tag.is-link {
  position: relative;
}

.auth-forgot-password::before,
.auth-skip::before,
.link-button::before,
.auth-legal a::before,
.follow-stat-button::before,
.kudos-button::before,
.friend-request-actions button::before,
.friend-search-action-button::before,
.min-tag.is-link::before {
  content: "";
  position: absolute;
  top: 50%;
  left: -6px;
  right: -6px;
  height: 44px;
  transform: translateY(-50%);
}

/* On/off settings are iPhone switches (51x31pt), like My Plan's walk/run. */
.settings-checkbox-row input[type="checkbox"] {
  -webkit-appearance: none;
  appearance: none;
  flex-shrink: 0;
  width: 51px;
  height: 31px;
  min-height: 0; /* .settings-row input's 44px would stretch it */
  margin: 0;
  padding: 0;
  border: none;
  border-radius: 999px;
  background: radial-gradient(circle at 15.5px 50%, white 0 13.5px, transparent 14px), var(--control-bg);
  cursor: pointer;
  transition: background 0.2s;
}

.settings-checkbox-row input[type="checkbox"]:checked {
  background: radial-gradient(circle at 35.5px 50%, white 0 13.5px, transparent 14px), var(--accent);
}

.settings-checkbox-row input[type="checkbox"]:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
}

/* ---------- Titles and subtitles ---------- */
/* A plain line under a screen's title or logo (never italic - Apple keeps
   italics for quotes, like the one on Today). */
.screen-subtitle {
  margin-bottom: var(--space-md);
  font-size: var(--font-subhead);
  line-height: 1.4;
  color: var(--text-secondary);
}

/* The walk/run summary: a centered title, like the camera's "Nice work!". */
.summary-title {
  text-align: center;
  margin-bottom: var(--space-sm);
}

/* The calendar's month reads as a headline, like the iPhone's Calendar. */
.calendar-month {
  margin: 0;
  font-size: var(--font-body);
  font-weight: 600;
  color: var(--text-primary);
}

/* ---------- One style for small uppercase labels ---------- */
/* Section headers and stat labels ("LEADERBOARD", "BEST STREAK", "TIME"...)
   share one weight, spacing and grey - Apple's secondary label color, which
   also reads better than the darker grey some used. */
.section-heading,
.settings-title,
.stat-label,
.heatmap-label,
.tracking-stat-label,
.plan-mini-label,
.feed-card-kind,
.ob-label,
.ob-plan .ob-head span,
.distance-summary-label {
  font-weight: 600;
  letter-spacing: 0.06em;
}

.section-heading,
.settings-title,
.stat-label,
.heatmap-label,
.tracking-stat-label,
.plan-mini-label {
  color: var(--text-secondary);
}

/* List rows read like iOS lists: regular weight, not bold. */
.account-menu-item {
  font-weight: 400;
}

/* Today's three tiles line up: the Verify button always sits at the bottom
   of its tile, whatever the label above it. */
.exercise-tile .verify-button {
  margin-top: auto;
}

/* A status line inside a settings card only takes space when it says
   something, so empty ones don't leave gaps between rows. */
.settings-panel .error:empty {
  min-height: 0;
  margin: 0;
}

/* ---------- Sign-up flow (onboarding.js) ---------- */
/* One question per screen: a top bar (back + progress), the step in the
   middle, and its buttons at the bottom. */
.ob {
  display: flex;
  flex-direction: column;
  min-height: min(760px, calc(100dvh - 2 * var(--space-lg)));
  text-align: left;
}

@media (max-width: 480px), (orientation: landscape) and (max-height: 560px) {
  .ob {
    min-height: 100dvh;
    padding-top: calc(var(--space-sm) + var(--safe-top));
  }
}

.invisible {
  visibility: hidden;
}

.ob-topbar {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  min-height: 44px;
}

.ob-back {
  display: grid;
  place-items: center;
  flex-shrink: 0;
  width: 44px;
  height: 44px;
  margin-left: -12px;
  color: var(--accent-text);
  background: none;
  border: none;
  cursor: pointer;
}

.ob-back svg {
  width: 24px;
  height: 24px;
}

.ob-progress {
  flex: 1;
  height: 8px;
  overflow: hidden;
  border-radius: 999px;
  background: var(--control-bg);
}

.ob-progress-fill {
  width: 0;
  height: 100%;
  border-radius: inherit;
  background: var(--accent);
  transition: width 0.35s ease;
}

.ob-body {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
  padding: var(--space-md) 0;
  outline: none;
}

.ob-body.centered {
  justify-content: center;
  align-items: center;
  text-align: center;
}

.ob-body.entering {
  animation: ob-enter 0.28s ease;
}

@keyframes ob-enter {
  from {
    opacity: 0;
    transform: translateX(16px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

.ob-footer {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  padding-top: var(--space-sm);
}

.ob-footer .auth-skip {
  min-height: 44px;
  margin: 0 auto;
}

/* The coach: the mascot and a speech bubble with the question. */
.ob-mascot {
  flex-shrink: 0;
  width: 56px;
  height: 63px;
}

.ob-body.centered > .ob-mascot {
  width: 96px;
  height: 108px;
}

.ob-coach {
  display: flex;
  align-items: flex-end;
  gap: 12px;
}

.ob-bubble {
  position: relative;
  padding: 12px 14px;
  font-size: var(--font-body);
  font-weight: 600;
  line-height: 1.3;
  background: var(--popover-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  text-wrap: balance;
}

.ob-bubble::before {
  content: "";
  position: absolute;
  left: -7px;
  bottom: 16px;
  width: 12px;
  height: 12px;
  background: var(--popover-bg);
  border-left: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
  transform: rotate(45deg);
}

.ob-em {
  color: var(--accent-text);
}

/* The welcome screen's logo: the ring above the word. */
.ob-logo-ring {
  width: 112px;
  height: 112px;
}

.ob-logo-word {
  width: 150px;
  aspect-ratio: 233 / 92.1;
  color: var(--text-primary);
}

.ob-title {
  font-size: var(--font-title1);
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: -0.02em;
  text-wrap: balance;
}

.ob-lead {
  font-size: var(--font-body);
  line-height: 1.4;
  color: var(--text-secondary);
  text-wrap: balance;
}

.ob-fine {
  font-size: var(--font-footnote);
  line-height: 1.4;
  color: var(--text-muted);
}

.ob-label {
  margin-bottom: calc(-1 * var(--space-xs));
  font-size: var(--font-footnote);
  text-transform: uppercase;
  color: var(--text-secondary);
}

/* Answers: big rows, a green outline on the picked one. */
.ob-options {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}

.ob-option {
  display: flex;
  align-items: center;
  gap: 14px;
  width: 100%;
  min-height: 60px;
  padding: 10px 14px;
  font-family: inherit;
  text-align: left;
  color: var(--text-primary);
  background: var(--surface-strong);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  cursor: pointer;
  transition: background 0.15s ease, box-shadow 0.15s ease, opacity 0.15s ease, transform 0.08s ease;
}

.ob-option:active {
  transform: scale(0.98);
}

.ob-option[aria-checked="true"] {
  background: var(--accent-tint);
  border-color: var(--accent);
  box-shadow: inset 0 0 0 1px var(--accent);
}

.ob-option.dimmed {
  opacity: 0.45;
}

.ob-option:focus-visible,
.ob-chips button:focus-visible,
.ob-back:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
}

.ob-option-icon {
  display: grid;
  place-items: center;
  flex-shrink: 0;
  width: 40px;
  height: 40px;
  color: var(--text-secondary);
  background: var(--field-bg);
  border-radius: var(--radius-md);
}

.ob-option[aria-checked="true"] .ob-option-icon {
  color: var(--accent-text);
}

.ob-option-icon svg {
  width: 22px;
  height: 22px;
}

.ob-option-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.ob-option-text b {
  font-size: var(--font-body);
  font-weight: 600;
}

.ob-option-text small {
  font-size: var(--font-footnote);
  line-height: 1.35;
  color: var(--text-secondary);
}

.ob-tag {
  display: inline-block;
  margin-left: 4px;
  padding: 2px 7px;
  vertical-align: 2px;
  font-size: var(--font-caption2);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--accent-text);
  background: var(--accent-tint);
  border-radius: 999px;
}

.ob-bars {
  display: flex;
  align-items: flex-end;
  gap: 3px;
  height: 20px;
}

.ob-bars i {
  width: 5px;
  border-radius: 2px;
  background: var(--border);
}

.ob-bars i.on {
  background: currentColor;
}

.ob-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.ob-chips button {
  min-height: 44px;
  padding: 0 16px;
  font-family: inherit;
  font-size: var(--font-subhead);
  font-weight: 600;
  color: var(--text-primary);
  background: var(--surface-strong);
  border: 1px solid var(--border);
  border-radius: 999px;
  cursor: pointer;
}

.ob-chips button[aria-checked="true"] {
  color: var(--accent-text);
  background: var(--accent-tint);
  border-color: var(--accent);
  box-shadow: inset 0 0 0 1px var(--accent);
}

/* The reminder screen's "Pick a time": any whole hour. */
.ob-select {
  width: 100%;
  min-height: 44px;
  padding: 10px 12px;
  font-family: inherit;
  font-size: var(--font-body);
  color: var(--text-primary);
  background: var(--field-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}

.ob-select:focus {
  outline: none;
  border-color: var(--accent);
}

/* Lists: Day 1's checklist, the test results, the plan. */
.ob-list {
  width: 100%;
  padding: 0 var(--space-md);
  text-align: left;
  font-variant-numeric: tabular-nums;
  background: var(--surface-strong);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

.ob-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  min-height: 44px;
  font-size: var(--font-body);
}

.ob-row + .ob-row {
  border-top: 1px solid var(--border);
}

.ob-row span:last-child {
  text-align: right;
  color: var(--text-secondary);
}

.ob-row.done span:last-child {
  font-weight: 600;
  color: var(--accent-text);
}

.ob-plan .ob-row span:first-child {
  flex: 1;
}

.ob-plan .ob-row span:not(:first-child) {
  width: 64px;
  text-align: right;
}

.ob-plan .ob-row .ob-goal {
  font-weight: 700;
  color: var(--accent-text);
}

.ob-plan .ob-head {
  min-height: 36px;
}

.ob-plan .ob-head span {
  font-size: var(--font-caption);
  text-transform: uppercase;
  color: var(--text-muted);
}

.ob-chart {
  padding: 12px 14px 6px;
  background: var(--surface-strong);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

.ob-chart-label {
  margin-bottom: 6px;
  font-size: var(--font-footnote);
  font-weight: 600;
  color: var(--text-secondary);
}

.ob-chart svg {
  display: block;
  width: 100%;
  height: auto;
}

/* Day 1 done: the streak flame. */
.ob-flame {
  position: relative;
  width: 150px;
  height: 168px;
}

.ob-flame svg {
  width: 100%;
  height: 100%;
}

.ob-flame-number {
  position: absolute;
  inset: 52px 0 0;
  display: grid;
  place-items: center;
  font-size: 60px; /* hero number */
  font-weight: 800;
  color: #fff;
  text-shadow: 0 2px 10px #00000040;
  font-variant-numeric: tabular-nums;
}

.ob-pop {
  animation: ob-pop 0.6s cubic-bezier(0.3, 1.6, 0.5, 1);
}

@keyframes ob-pop {
  from {
    opacity: 0;
    transform: scale(0.3);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

.ob-week {
  display: flex;
  justify-content: center;
  gap: 8px;
}

.ob-week span {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  font-size: var(--font-caption);
  font-weight: 700;
  color: var(--text-muted);
  background: var(--field-bg);
  border-radius: 50%;
}

.ob-week span.done {
  color: #1c1c1e;
  background: var(--ember);
}

/* The account step reuses the log-in screen's fields. */
.ob .auth-fields {
  margin-bottom: 0;
}

.ob .auth-legal {
  margin-top: 0;
}

.ob .error {
  min-height: 0;
  margin-bottom: 0;
}

.ob-login {
  align-self: flex-start;
  min-height: 44px;
  margin-left: 0;
}

/* The pet comes alive: a quick blink every few seconds, and on
   celebration screens the wow face bounces in and its sparkles twinkle. */
.ob-pet-eyes {
  transform-box: fill-box;
  transform-origin: center;
  animation: ob-pet-blink 4.6s ease-in-out infinite;
}

@keyframes ob-pet-blink {
  0%,
  90%,
  96%,
  100% {
    transform: none;
  }

  93% {
    transform: scaleY(0.1);
  }
}

.ob-mascot.is-wow {
  transform-origin: 50% 70%;
  animation: ob-pet-bounce 0.9s cubic-bezier(0.3, 1.5, 0.5, 1) both;
}

@keyframes ob-pet-bounce {
  0% {
    opacity: 0;
    transform: scale(0.4) translateY(12px);
  }

  45% {
    opacity: 1;
    transform: scale(1.08) translateY(-6px);
  }

  70% {
    transform: scale(0.97) translateY(0);
  }

  85% {
    transform: scale(1.02) translateY(-2px);
  }

  100% {
    transform: none;
  }
}

.ob-pet-sparkle {
  transform-box: fill-box;
  transform-origin: center;
  animation: ob-pet-twinkle 1.4s ease-in-out 0.5s infinite alternate both;
}

.ob-pet-sparkle + .ob-pet-sparkle {
  animation-delay: 1.2s;
}

@keyframes ob-pet-twinkle {
  from {
    opacity: 0.35;
    transform: scale(0.6) rotate(0deg);
  }

  to {
    opacity: 1;
    transform: scale(1.1) rotate(20deg);
  }
}

@media (prefers-reduced-motion: reduce) {
  .ob-body.entering,
  .ob-pop,
  .ob-pet-eyes,
  .ob-mascot.is-wow,
  .ob-pet-sparkle {
    animation: none;
  }

  .ob-progress-fill,
  .ob-option {
    transition: none;
  }
}

/* ---------- Distance chart (Me tab) ---------- */
/* Bars like the Health app: one green series, a recessive grid with its
   numbers on the right, the total (or the tapped bar) as the headline. */
.distance-card {
  margin-bottom: var(--space-lg);
  text-align: left;
}

.distance-summary {
  margin: var(--space-md) 0 var(--space-sm);
}

.distance-summary-label {
  font-size: var(--font-caption);
  text-transform: uppercase;
  color: var(--text-secondary);
}

.distance-summary-value {
  font-size: var(--font-title1);
  font-weight: 700;
  line-height: 1.15;
  font-variant-numeric: tabular-nums;
  color: var(--text-primary);
}

.distance-summary-unit {
  font-size: var(--font-body);
  font-weight: 600;
  color: var(--text-secondary);
}

.distance-summary-sub {
  min-height: 18px;
  font-size: var(--font-footnote);
  color: var(--text-secondary);
}

.distance-chart {
  /* Vertical swipes still scroll the page; sideways drags read the bars. */
  touch-action: pan-y;
  user-select: none;
  -webkit-user-select: none;
  cursor: pointer;
  outline: none;
  border-radius: var(--radius-sm);
}

.distance-chart:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 4px;
}

.dc-plot {
  position: relative;
  height: 160px;
  margin: 8px 36px 0 0;
}

.dc-grid {
  position: absolute;
  left: 0;
  right: -36px;
  border-top: 1px dashed var(--border);
}

.dc-grid.dc-baseline {
  border-top-style: solid;
}

.dc-grid span {
  position: absolute;
  right: 0;
  width: 32px;
  transform: translateY(-50%);
  font-size: var(--font-caption2);
  text-align: right;
  font-variant-numeric: tabular-nums;
  color: var(--text-muted);
  background: var(--page-bg);
}

.dc-bars {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: flex-end;
  gap: 2px;
}

.dc-slot {
  flex: 1;
  min-width: 0;
  height: 100%;
  display: flex;
  align-items: flex-end;
  justify-content: center;
}

.dc-bar {
  width: 100%;
  max-width: 28px;
  height: 0;
  background: var(--accent);
  border-radius: 4px 4px 0 0;
  transition: height 0.5s cubic-bezier(0.2, 0.8, 0.2, 1), opacity 0.15s ease;
}

/* A tapped bar stays bright; the rest step back. */
.distance-chart.has-selection .dc-slot:not(.selected) .dc-bar {
  opacity: 0.35;
}

.dc-labels {
  display: flex;
  gap: 2px;
  margin: 6px 36px 0 0;
}

.dc-labels span {
  flex: 1;
  min-width: 0;
  overflow: visible;
  white-space: nowrap;
  text-align: center;
  font-size: var(--font-caption2);
  color: var(--text-muted);
}

.distance-range {
  margin-top: var(--space-xs);
}

@media (prefers-reduced-motion: reduce) {
  .dc-bar {
    transition: none;
  }
}

/* ---------- Challenges (challenges.js) ---------- */
/* For now a preview on sample data, shown only on localhost. Built from
   the app's own pieces: leaderboard rows, My Plan's segmented control and
   target card, and Feed cards. */
.round-icon-button {
  position: relative;
  display: grid;
  place-items: center;
  flex-shrink: 0;
  width: 36px;
  height: 36px;
  padding: 0;
  color: var(--accent-text);
  background: var(--accent-tint);
  border: none;
  border-radius: 50%;
  cursor: pointer;
}

.round-icon-button svg {
  width: 20px;
  height: 20px;
}

.round-icon-button::before {
  content: "";
  position: absolute;
  inset: -4px;
}

/* No challenges yet: one row to start the first. */
.challenge-new-row {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 100%;
  min-height: 52px;
  font-family: inherit;
  font-size: var(--font-body);
  font-weight: 600;
  color: var(--accent-text);
  background: var(--accent-tint);
  border: none;
  border-radius: var(--radius-md);
  cursor: pointer;
}

.challenge-new-row svg {
  width: 20px;
  height: 20px;
}

/* "Couldn't load..." with a Try Again next to it. */
.challenge-message {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm);
  padding: 4px 4px 4px 12px;
  font-size: var(--font-subhead);
  color: var(--text-secondary);
  background: var(--surface);
  border-radius: var(--radius-md);
}

.challenge-message .challenge-text-button {
  margin: 0;
}

.challenge-result-tile {
  margin: var(--space-md) 0;
}

/* Under the leaderboard: the same gap as between the other sections. */
.challenges-heading {
  margin-top: var(--space-sm);
}

/* Challenges need an account with a username, like the leaderboard. */
#tab-competition:has(#friends-section.hidden) #new-challenge-button {
  display: none;
}

.challenge-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  margin-bottom: var(--space-lg);
}

.challenge-card,
.challenge-pick {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  min-height: 64px;
  padding: 12px;
  font-family: inherit;
  text-align: left;
  color: var(--text-primary);
  background: var(--surface);
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  cursor: pointer;
}

.challenge-tile {
  display: grid;
  place-items: center;
  flex-shrink: 0;
  width: 40px;
  height: 40px;
  color: var(--c);
  background: color-mix(in srgb, var(--c) 18%, transparent);
  border-radius: var(--radius-md);
}

.challenge-tile svg {
  width: 22px;
  height: 22px;
}

.challenge-tile.is-large {
  width: 64px;
  height: 64px;
  border-radius: 18px;
}

.challenge-tile.is-large svg {
  width: 34px;
  height: 34px;
}

.challenge-card-text,
.challenge-pick-text {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.challenge-card-name,
.challenge-pick-text b {
  font-size: var(--font-body);
  font-weight: 600;
}

.challenge-card-sub,
.challenge-pick-text span {
  font-size: var(--font-subhead);
  color: var(--text-secondary);
}

.challenge-card-sub b {
  font-weight: 600;
  color: var(--text-primary);
}

.challenge-chevron {
  flex-shrink: 0;
  width: 18px;
  height: 18px;
  color: var(--text-muted);
}

.challenge-chevron svg {
  width: 100%;
  height: 100%;
}

.challenge-pill {
  flex-shrink: 0;
  padding: 4px 8px;
  font-size: var(--font-caption2);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-secondary);
  background: var(--control-bg);
  border-radius: 999px;
}

.challenge-pill.is-invite {
  color: white;
  background: var(--danger);
}

.challenge-pill.is-result {
  color: var(--trophy);
  background: color-mix(in srgb, var(--trophy) 18%, transparent);
}

#challenge-screen {
  text-align: left;
}

.challenge-picks {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}

.challenge-pick {
  gap: 14px;
  min-height: 76px;
  padding: 16px 14px;
  border-radius: var(--radius);
}

.challenge-pick .challenge-tile {
  width: 48px;
  height: 48px;
  border-radius: 14px;
}

.challenge-pick .challenge-tile svg {
  width: 26px;
  height: 26px;
}

.challenge-title-row {
  position: relative;
  display: flex;
  align-items: center;
  gap: 10px;
}

.challenge-title-row .large-title {
  margin-bottom: 0;
}

.challenge-title-row .large-title.is-long {
  font-size: var(--font-title1);
}

.challenge-setup-head {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 14px;
  margin-bottom: var(--space-lg);
}

.challenge-field {
  margin-bottom: var(--space-lg);
}

/* My Plan's segmented control, at the full 44pt touch height. */
.challenge-seg button {
  min-height: 44px;
}

.challenge-rule {
  margin-bottom: var(--space-lg);
  font-size: var(--font-callout);
  color: var(--text-secondary);
}

.challenge-steppers {
  background: var(--surface-strong);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

.challenge-stepper {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 56px;
  padding: 8px 12px;
}

.challenge-stepper + .challenge-stepper {
  border-top: 1px solid var(--border);
}

.challenge-dot {
  flex-shrink: 0;
  width: 10px;
  height: 10px;
  background: var(--c);
  border-radius: 50%;
}

.challenge-stepper-name {
  flex: 1;
  font-size: var(--font-body);
  color: var(--text-primary);
}

.challenge-step-button {
  position: relative;
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  padding: 0;
  color: var(--text-primary);
  background: var(--control-bg);
  border: none;
  border-radius: 50%;
  cursor: pointer;
}

.challenge-step-button::before {
  content: "";
  position: absolute;
  inset: -4px;
}

.challenge-step-button svg {
  width: 18px;
  height: 18px;
}

.challenge-step-button:disabled {
  opacity: 0.35;
  cursor: default;
}

.challenge-step-value {
  width: 56px;
  text-align: center;
  font-size: var(--font-title3);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--text-primary);
}

.challenge-climb-line,
.challenge-starts,
.challenge-tomorrow {
  margin: 10px 0 0;
  font-size: var(--font-footnote);
  color: var(--text-secondary);
  text-align: center;
  font-variant-numeric: tabular-nums;
}

.challenge-starts {
  margin: 0 0 var(--space-sm);
}

.challenge-tomorrow b {
  font-weight: 700;
  color: var(--accent-text);
}

/* Friend picker rows are leaderboard rows you can tap. */
.challenge-friend {
  width: 100%;
  min-height: 52px;
  font-family: inherit;
  cursor: pointer;
}

.challenge-check {
  display: grid;
  place-items: center;
  flex-shrink: 0;
  width: 26px;
  height: 26px;
  color: transparent;
  border: 2px solid var(--control-bg);
  border-radius: 50%;
  transition: background 0.15s, border-color 0.15s;
}

.challenge-check svg {
  width: 16px;
  height: 16px;
}

.challenge-friend[aria-checked="true"] .challenge-check {
  color: white;
  background: var(--accent);
  border-color: var(--accent);
}

.challenge-days {
  display: flex;
  gap: 3px;
  margin: 14px 0 6px;
}

.challenge-days span {
  flex: 1;
  height: 5px;
  background: var(--control-bg);
  border-radius: 3px;
}

.challenge-days span.is-done {
  background: var(--c);
}

.challenge-status {
  margin-bottom: var(--space-lg);
  font-size: var(--font-subhead);
  color: var(--text-secondary);
  font-variant-numeric: tabular-nums;
}

.challenge-status.is-spaced {
  margin-top: 12px;
}

.challenge-status b {
  font-weight: 600;
  color: var(--text-primary);
}

.challenge-standings {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  margin-bottom: var(--space-lg);
}

.challenge-standing {
  display: grid;
  grid-template-columns: 20px 28px minmax(0, 1fr) auto;
  align-items: center;
  column-gap: var(--space-sm);
  row-gap: 8px;
  padding: 12px 10px;
  background: var(--surface);
  border: 1px solid transparent;
  border-radius: var(--radius-md);
}

.challenge-standing.no-rank {
  grid-template-columns: 28px minmax(0, 1fr) auto;
}

.challenge-standing.is-you {
  background: #c0855218;
  border-color: #c0855250;
}

.challenge-standing.is-out {
  opacity: 0.45;
}

.leaderboard-rank.is-first {
  color: var(--trophy);
}

.challenge-standing-who {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
}

.challenge-standing-note {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: var(--font-caption);
  color: var(--text-secondary);
  font-variant-numeric: tabular-nums;
}

.challenge-standing-note.is-up {
  color: var(--accent-text);
}

.challenge-standing-note svg {
  width: 13px;
  height: 13px;
}

.challenge-standing-value {
  text-align: right;
  font-size: var(--font-title3);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--text-primary);
}

.challenge-standing-value small {
  margin-left: 2px;
  font-size: var(--font-footnote);
  font-weight: 600;
  color: var(--text-secondary);
}

.challenge-standing-bar {
  grid-column: 3 / -1;
  height: 4px;
  overflow: hidden;
  background: var(--control-bg);
  border-radius: 2px;
}

.challenge-standing-bar i {
  display: block;
  height: 100%;
  background: var(--c);
  border-radius: 2px;
}

.challenge-state {
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  border-radius: 50%;
}

.challenge-state svg {
  width: 16px;
  height: 16px;
}

.challenge-state.is-done {
  color: var(--accent-text);
  background: var(--accent-tint);
}

.challenge-state.is-waiting {
  color: var(--text-secondary);
  background: var(--control-bg);
}

.challenge-state.is-out {
  color: var(--text-muted);
}

.challenge-icon-button {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
}

.challenge-icon-button svg {
  width: 20px;
  height: 20px;
}

.challenge-text-button {
  display: block;
  min-height: 44px;
  margin: var(--space-xs) auto 0;
  padding: 0 16px;
  font-family: inherit;
  font-size: var(--font-body);
  font-weight: 600;
  color: var(--accent-text);
  background: none;
  border: none;
  cursor: pointer;
}

.challenge-text-button.is-muted {
  color: var(--text-secondary);
}

/* The results screen: a trophy that pops in with a burst of sparks. */
.challenge-result {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding-top: var(--space-md);
  text-align: center;
}

.challenge-result .large-title {
  margin-bottom: 6px;
}

.challenge-result .challenge-standings {
  width: 100%;
  text-align: left;
}

.challenge-burst {
  position: relative;
  display: grid;
  place-items: center;
  width: 150px;
  height: 150px;
  margin-bottom: var(--space-sm);
}

.challenge-trophy {
  display: block;
  width: 104px;
  height: 104px;
  color: var(--trophy);
  animation: challenge-pop 0.6s cubic-bezier(0.3, 1.6, 0.5, 1);
}

.challenge-trophy svg {
  width: 100%;
  height: 100%;
}

.challenge-burst i {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 7px;
  height: 12px;
  background: var(--k);
  border-radius: 2px;
  opacity: 0;
  animation: challenge-spark 1.1s ease-out 0.15s forwards;
}

@keyframes challenge-pop {
  from {
    transform: scale(0.3);
    opacity: 0;
  }
}

@keyframes challenge-spark {
  0% {
    opacity: 1;
    transform: translate(-50%, -50%) rotate(0);
  }
  100% {
    opacity: 0;
    transform: translate(calc(-50% + var(--dx)), calc(-50% + var(--dy))) rotate(220deg);
  }
}

/* Invite sheet: slides up from the bottom over a dimmed screen. */
.challenge-scrim {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  background: #00000080;
  animation: challenge-fade 0.2s ease;
}

.challenge-sheet {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  width: 100%;
  max-width: 480px;
  padding: 8px var(--space-lg) calc(var(--space-lg) + var(--safe-bottom));
  text-align: center;
  background: var(--popover-bg);
  border-radius: 18px 18px 0 0;
  animation: challenge-rise 0.28s ease;
}

.challenge-grabber {
  width: 36px;
  height: 5px;
  margin-bottom: 14px;
  background: var(--control-bg);
  border-radius: 3px;
}

.challenge-sheet-from {
  margin-top: 8px;
  font-size: var(--font-subhead);
  color: var(--text-secondary);
}

.challenge-sheet-from b {
  font-weight: 600;
  color: var(--text-primary);
}

.challenge-sheet h2 {
  font-size: var(--font-title2);
  font-weight: 700;
  color: var(--text-primary);
}

.challenge-faces {
  display: flex;
  justify-content: center;
  margin: 4px 0 2px;
}

.challenge-faces .leaderboard-avatar {
  margin-left: -8px;
  border: 2px solid var(--popover-bg);
}

.challenge-faces .leaderboard-avatar:first-child {
  margin-left: 0;
}

.challenge-sheet-meta {
  margin-bottom: var(--space-md);
  font-size: var(--font-subhead);
  color: var(--text-secondary);
}

@keyframes challenge-fade {
  from {
    opacity: 0;
  }
}

@keyframes challenge-rise {
  from {
    transform: translateY(40%);
  }
}

.challenge-toast {
  position: fixed;
  top: calc(16px + var(--safe-top));
  left: 50%;
  z-index: 300;
  padding: 9px 16px;
  font-size: var(--font-subhead);
  font-weight: 600;
  white-space: nowrap;
  color: var(--text-primary);
  background: var(--popover-bg);
  border: 1px solid var(--border);
  border-radius: 999px;
  box-shadow: 0 10px 30px #00000040;
  pointer-events: none;
  animation: challenge-toast 2.2s ease forwards;
}

@keyframes challenge-toast {
  0% {
    opacity: 0;
    transform: translate(-50%, -8px);
  }
  12%,
  80% {
    opacity: 1;
    transform: translate(-50%, 0);
  }
  100% {
    opacity: 0;
    transform: translate(-50%, 0);
  }
}

/* Me tab: one tile per challenge won. */
.trophy-shelf {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-xs);
  margin-bottom: var(--space-lg);
}

.trophy-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 14px 6px 12px;
  text-align: center;
  background: var(--surface);
  border-radius: var(--radius-md);
}

.trophy-tile svg {
  width: 30px;
  height: 30px;
  color: var(--trophy);
}

.trophy-tile b {
  font-size: var(--font-footnote);
  font-weight: 600;
  line-height: 1.25;
  color: var(--text-primary);
}

.trophy-tile span {
  font-size: var(--font-caption);
  color: var(--text-muted);
}

.feed-card-kind.is-challenge {
  color: var(--trophy);
}

@media (prefers-reduced-motion: reduce) {
  .challenge-trophy,
  .challenge-scrim,
  .challenge-sheet,
  .challenge-toast,
  .challenge-check {
    animation: none;
    transition: none;
  }

  .challenge-burst i {
    display: none;
  }
}

/* ---------- Celebrations on Today (celebrations.js) ---------- */
/* A saved set: "+25" floats up off its card. */
.exercise-tile {
  position: relative;
}

.celebrate-plus {
  position: absolute;
  top: 44%;
  left: 50%;
  font-size: var(--font-title3);
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  pointer-events: none;
  animation: celebrate-plus 1.2s ease-out forwards;
}

@keyframes celebrate-plus {
  0% {
    opacity: 0;
    transform: translate(-50%, 10px) scale(0.8);
  }
  20% {
    opacity: 1;
    transform: translate(-50%, 0) scale(1.1);
  }
  100% {
    opacity: 0;
    transform: translate(-50%, -38px) scale(1);
  }
}

/* A target met: a green check on its "min" tag. */
.min-tag.is-met {
  font-weight: 700;
  color: var(--accent-text);
}

.min-tag.is-met::after {
  content: " \2713";
}

.min-tag.pop {
  display: inline-block;
  animation: celebrate-tag-pop 0.45s cubic-bezier(0.3, 1.8, 0.5, 1);
}

@keyframes celebrate-tag-pop {
  40% {
    transform: scale(1.35);
  }
}

/* The day done: a gapless ring snaps in over the segments, with the pet's
   face in the middle. Stays for the rest of the day. */
.rings-container {
  position: relative;
}

#ring-complete {
  opacity: 0;
  transform-origin: 60px 60px;
}

.rings-container.is-complete #ring-complete {
  opacity: 1;
}

.ring-complete-arc {
  fill: none;
  stroke-width: 14;
  transform: rotate(-90deg);
  transform-origin: 60px 60px;
}

/* The pet's face in a closed ring - sized so the coach's own ring body,
   grown to the big ring when it jumps in, leaves exactly this face
   (0.694 x the ring's 170px). Tappable on a finished day. */
.ring-face {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 118px;
  transform: translate(-50%, -50%);
  pointer-events: none;
  border-radius: 50%;
  -webkit-tap-highlight-color: transparent;
}

.ring-face.is-tappable {
  pointer-events: auto;
  cursor: pointer;
}

.ring-face:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 4px;
}

.ring-face svg {
  display: block;
  width: 100%;
  height: auto;
}

.ring-face.snap .pet-face {
  transform-origin: 50% 60%;
  animation: ob-pet-bounce 0.9s cubic-bezier(0.3, 1.5, 0.5, 1) 0.15s both;
}

.ring-face .pet-face {
  transform-origin: 50% 100%;
}

.ring-face.land .pet-face {
  animation: pet-face-land 0.45s ease-out both;
}

.ring-face.hop .pet-face {
  animation: pet-face-hop 0.6s ease-out both;
}

@keyframes pet-face-land {
  0% {
    transform: scale(1.12, 0.86);
  }
  60% {
    transform: scale(0.97, 1.03);
  }
  100% {
    transform: none;
  }
}

@keyframes pet-face-hop {
  0%,
  100% {
    transform: none;
  }
  35% {
    transform: translateY(-10px) scale(0.95, 1.05);
  }
  65% {
    transform: scale(1.07, 0.93);
  }
}

/* ---------- Today's pet coach ---------- */
/* The pet and its speech bubble at the top of Today. When the day is done
   it jumps into the ring, this spot folds away and the bubble under the
   ring (.pet-bubble.is-below) takes over. */
.pet-coach {
  display: flex;
  align-items: flex-end;
  gap: 12px;
  min-height: 72px;
  max-height: 160px;
  margin-bottom: var(--space-md);
  transition: max-height 0.35s ease, min-height 0.35s ease, margin-bottom 0.35s ease, opacity 0.2s ease, visibility 0s;
}

.pet-coach.is-gone {
  min-height: 0;
  max-height: 0;
  margin-bottom: 0;
  opacity: 0;
  visibility: hidden;
  transition: max-height 0.35s ease, min-height 0.35s ease, margin-bottom 0.35s ease, opacity 0.2s ease, visibility 0s 0.35s;
}

.pet-coach.no-motion {
  transition: none;
}

.pet-coach-pet {
  flex: none;
  width: 60px;
  min-height: 44px;
  padding: 0;
  background: none;
  border: 0;
  cursor: pointer;
  transform-origin: 50% 100%;
  -webkit-tap-highlight-color: transparent;
}

.pet-coach-pet:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 4px;
  border-radius: var(--radius-md);
}

.pet-coach-pet svg {
  display: block;
  width: 60px;
  height: auto;
  overflow: visible;
}

/* The sign-up pet bounces in on its own; here the coach moves it instead. */
.pet-coach .ob-mascot {
  animation: none;
}

/* The copy that jumps into the ring, flying above the page (the extra
   class beats the sign-up pet's own bounce-in). */
.ob-mascot.pet-coach-flyer {
  position: fixed;
  left: 0;
  top: 0;
  margin: 0;
  overflow: visible;
  transform-origin: 0 0;
  pointer-events: none;
  z-index: 50;
  animation: none;
}

.pet-bubble {
  position: relative;
  margin: 0;
  padding: 11px 14px;
  font-size: var(--font-body);
  font-weight: 600;
  line-height: 1.3;
  color: var(--text-primary);
  background: var(--popover-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  text-wrap: balance;
}

.pet-bubble::before {
  content: "";
  position: absolute;
  width: 12px;
  height: 12px;
  background: var(--popover-bg);
  border-left: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
}

.pet-coach .pet-bubble {
  flex: 1;
  min-width: 0;
  margin-bottom: 6px;
  text-align: left;
}

.pet-coach .pet-bubble::before {
  left: -7px;
  bottom: 14px;
  transform: rotate(45deg);
}

.pet-bubble.is-below {
  margin: 0 auto var(--space-md);
  max-width: 340px;
  text-align: center;
}

.pet-bubble.is-below::before {
  left: calc(50% - 6px);
  top: -7px;
  transform: rotate(135deg);
}

.pet-bubble b {
  font-weight: 700;
  color: var(--accent-text);
}

.pet-bubble .warn {
  color: var(--warning);
}

/* The pet asking to turn on notifications: two small pills in its bubble
   (34pt tall, 44pt touch area). */
.pet-ask-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm);
  margin-top: 10px;
}

.pet-bubble.is-below .pet-ask-actions {
  justify-content: center;
}

.pet-ask-button {
  position: relative;
  min-height: 34px;
  padding: 0 14px;
  font-family: inherit;
  font-size: var(--font-subhead);
  font-weight: 700;
  color: var(--text-secondary);
  background: none;
  border: 1px solid var(--border);
  border-radius: 999px;
  cursor: pointer;
}

.pet-ask-button::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  height: 44px;
  transform: translateY(-50%);
}

.pet-ask-button.is-go {
  color: #ffffff;
  background: var(--accent);
  border-color: var(--accent);
}

.pet-ask-button:disabled {
  opacity: 0.6;
  cursor: default;
}

.pet-ask-button:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
}

/* iPhone Safari: the Add to Home Screen steps, in a sheet. */
.notif-sheet h2 {
  margin: 6px 0 0;
  font-size: var(--font-title3);
  font-weight: 700;
  text-wrap: balance;
}

.notif-sheet-pet svg {
  display: block;
  width: 72px;
  height: 81px;
}

.notif-sheet-sub {
  margin: 0 0 var(--space-sm);
  font-size: var(--font-subhead);
  line-height: 1.35;
  color: var(--text-secondary);
  text-wrap: balance;
}

.notif-steps {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  width: 100%;
  margin: 0 0 var(--space-md);
  padding: 0;
  list-style: none;
  text-align: left;
}

.notif-steps li {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 12px;
  font-size: var(--font-subhead);
  line-height: 1.3;
  background: var(--surface-solid);
  border-radius: var(--radius-md);
}

.notif-steps small {
  display: block;
  margin-top: 2px;
  font-size: var(--font-footnote);
  color: var(--text-secondary);
}

.notif-step-n {
  display: grid;
  place-items: center;
  flex-shrink: 0;
  width: 26px;
  height: 26px;
  font-size: var(--font-footnote);
  font-weight: 800;
  color: var(--accent-text);
  background: #22c55e1f;
  border-radius: 50%;
}

.notif-step-icon {
  display: inline-grid;
  place-items: center;
  width: 26px;
  height: 26px;
  margin: 0 2px;
  vertical-align: middle;
  color: #0a84ff;
  background: var(--control-bg);
  border-radius: 7px;
}

.notif-step-icon svg {
  width: 17px;
  height: 17px;
}

.notif-sheet .auth-submit-button {
  width: 100%;
}

/* "Ramsesn nudged you": their faces and name on a line of its own. */
.pet-nudge-from {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 4px;
}

.pet-nudge-faces {
  display: inline-flex;
  flex-shrink: 0;
}

.pet-nudge-avatar {
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--avatar, var(--control-bg));
  color: #ffffff;
  font-size: 11px;
  font-weight: 700;
  line-height: 1;
}

.pet-nudge-avatar svg {
  width: 13px;
  height: 13px;
}

.pet-nudge-avatar + .pet-nudge-avatar {
  margin-left: -6px;
  box-shadow: 0 0 0 2px var(--popover-bg);
}

.pet-bubble b.pet-nudge-name {
  color: var(--orange-light);
}

:root[data-theme="light"] .pet-bubble b.pet-nudge-name {
  color: var(--orange-dark);
}

.pet-bubble.is-new {
  animation: pet-bubble-in 0.3s ease-out both;
}

.pet-bubble.is-quiet {
  visibility: hidden;
}

.pet-coach-pet.hop-in {
  animation: pet-coach-hop-in 0.6s cubic-bezier(0.3, 1.4, 0.5, 1) both;
}

.pet-coach-pet.wave {
  animation: pet-coach-wave 1s ease-in-out both;
}

.pet-coach-pet.nod {
  animation: pet-coach-nod 0.7s ease-in-out both;
}

.pet-coach-pet.bounce {
  animation: pet-coach-bounce 0.8s ease-out both;
}

.pet-coach-pet.shake {
  animation: pet-coach-shake 0.7s ease-in-out both;
}

.pet-coach-pet.idle {
  animation: pet-coach-breathe 3.2s ease-in-out infinite;
}

@keyframes pet-bubble-in {
  from {
    opacity: 0;
    transform: translateY(-6px) scale(0.96);
  }
}

@keyframes pet-coach-hop-in {
  0% {
    opacity: 0;
    transform: translateY(26px) scale(0.6);
  }
  55% {
    opacity: 1;
    transform: translateY(-8px) scale(1.06, 0.95);
  }
  75% {
    transform: scale(1.08, 0.9);
  }
  100% {
    transform: none;
  }
}

@keyframes pet-coach-wave {
  0%,
  100% {
    transform: none;
  }
  20% {
    transform: rotate(-12deg);
  }
  40% {
    transform: rotate(10deg);
  }
  60% {
    transform: rotate(-8deg);
  }
  80% {
    transform: rotate(5deg);
  }
}

@keyframes pet-coach-nod {
  0%,
  100% {
    transform: none;
  }
  30% {
    transform: translateY(4px) scale(1.04, 0.95);
  }
  60% {
    transform: translateY(-3px);
  }
}

@keyframes pet-coach-bounce {
  0%,
  100% {
    transform: none;
  }
  25% {
    transform: translateY(-12px) scale(0.96, 1.05);
  }
  50% {
    transform: scale(1.08, 0.9);
  }
  70% {
    transform: translateY(-5px);
  }
}

@keyframes pet-coach-shake {
  0%,
  100% {
    transform: none;
  }
  15%,
  45%,
  75% {
    transform: translateX(-4px) rotate(-3deg);
  }
  30%,
  60%,
  90% {
    transform: translateX(4px) rotate(3deg);
  }
}

@keyframes pet-coach-breathe {
  0%,
  100% {
    transform: none;
  }
  50% {
    transform: scale(1.025, 0.975);
  }
}

@keyframes celebrate-fade-in {
  from {
    opacity: 0;
    transform: translateY(-4px);
  }
}

/* Every 7th day of a streak: one full-screen celebration. */
.celebrate-screen {
  position: fixed;
  inset: 0;
  z-index: 300;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: calc(var(--space-lg) + var(--safe-top)) var(--space-lg) calc(var(--space-lg) + var(--safe-bottom));
  overflow: hidden;
  text-align: center;
  background: var(--page-bg);
  animation: celebrate-fade-in 0.25s ease both;
}

/* The pet flexing (FlexPet in celebrations.js moves it). */
.flex-pet {
  width: 230px;
  height: auto;
  margin-bottom: -18px;
  overflow: visible;
}

.fp-shadow {
  fill: var(--text-primary);
  fill-opacity: 0.12;
}

/* With Reduce Motion it just stands in its final pose. */
.flex-pet.still .fp-happy-r,
.flex-pet.still .fp-brows,
.flex-pet.still .fp-angry-eyes,
.flex-pet.still .fp-brows-angry,
.flex-pet.still .fp-mouth-angry,
.flex-pet.still .fp-vein,
.flex-pet.still .fp-mouth-o,
.flex-pet.still .fp-mouth-grit,
.flex-pet.still .fp-ting {
  display: none;
}

.celebrate-number {
  display: inline-block;
  font-size: 88px;
  font-weight: 800;
  line-height: 1;
  letter-spacing: -0.04em;
  font-variant-numeric: tabular-nums;
  color: var(--text-primary);
}

.celebrate-number.bump {
  animation: celebrate-tag-pop 0.45s cubic-bezier(0.3, 1.6, 0.5, 1);
}

.celebrate-screen h2 {
  font-size: var(--font-title1);
  font-weight: 700;
  color: var(--text-primary);
}

.celebrate-screen p {
  font-size: var(--font-callout);
  color: var(--text-secondary);
}

.celebrate-week {
  display: flex;
  gap: 8px;
  margin: var(--space-xs) 0 var(--space-md);
}

.celebrate-week span {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  font-size: var(--font-caption);
  font-weight: 700;
  color: white;
  background: var(--accent);
  border-radius: 50%;
}

.celebrate-week span.is-today {
  animation: celebrate-day-fill 0.5s cubic-bezier(0.3, 1.8, 0.5, 1) 1.6s both;
}

@keyframes celebrate-day-fill {
  from {
    color: var(--text-muted);
    background: var(--field-bg);
    transform: scale(0.6);
  }
}

.celebrate-close {
  max-width: 330px;
}

.celebrate-confetti {
  position: absolute;
  top: 38%;
  left: 50%;
  width: 8px;
  height: 13px;
  border-radius: 2px;
  background: var(--c);
  opacity: 0;
  pointer-events: none;
  animation: celebrate-confetti 1.6s cubic-bezier(0.15, 0.6, 0.4, 1) var(--d) forwards;
}

@keyframes celebrate-confetti {
  0% {
    opacity: 1;
    transform: translate(-50%, -50%) rotate(0);
  }
  70% {
    opacity: 1;
  }
  100% {
    opacity: 0;
    transform: translate(calc(-50% + var(--x)), calc(-50% + var(--y))) rotate(var(--r));
  }
}

@media (prefers-reduced-motion: reduce) {
  .celebrate-plus,
  .min-tag.pop,
  .ring-face.snap .pet-face,
  .ring-face.land .pet-face,
  .ring-face.hop .pet-face,
  .pet-bubble.is-new,
  .pet-coach-pet,
  .celebrate-screen,
  .celebrate-number.bump,
  .celebrate-week span.is-today {
    animation: none;
  }

  .celebrate-plus,
  .celebrate-confetti {
    display: none;
  }
}

/* Shown instead of the app when a signed-in player's data can't be loaded
   (showLoadError in script.js). */
.load-error-screen {
  position: fixed;
  inset: 0;
  z-index: 400;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-md);
  padding: calc(var(--space-lg) + var(--safe-top)) var(--space-lg) calc(var(--space-lg) + var(--safe-bottom));
  text-align: center;
  background: var(--page-bg);
}

.load-error-screen p {
  font-size: var(--font-body);
  color: var(--text-primary);
}

.load-error-screen button {
  max-width: 330px;
}

/* ---------- Launch pack (2026-10-15): invite links, leagues, weekly recap ---------- */

/* Me tab: your invite link. */
.invite-row {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  margin-bottom: var(--space-lg);
  padding: 12px 12px 12px 16px;
  background: var(--card-solid);
  border: 1px solid var(--card-line);
  border-radius: var(--radius);
}

.invite-row-text {
  flex: 1;
  min-width: 0;
  text-align: left;
}

.invite-row-title {
  font-size: var(--font-body);
  font-weight: 600;
  color: var(--text-primary);
}

.invite-row-sub {
  margin-top: 2px;
  font-size: var(--font-footnote);
  color: var(--text-secondary);
}

.invite-share-button {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
  min-height: 36px;
  padding: 0 14px;
  font-family: inherit;
  font-size: var(--font-subhead);
  font-weight: 700;
  color: var(--accent-text);
  background: var(--accent-tint);
  border: 1px solid var(--accent);
  border-radius: 999px;
  cursor: pointer;
}

.invite-share-button::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  height: 44px;
  transform: translateY(-50%);
}

.invite-share-button svg {
  width: 16px;
  height: 16px;
}

/* Sign-up's welcome screen: who invited you. */
.ob-invited {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  margin-top: var(--space-md);
  padding: 12px 14px;
  text-align: left;
  background: var(--card-solid);
  border: 1px solid var(--orange);
  border-radius: var(--radius);
}

.ob-invited-avatar {
  display: grid;
  place-items: center;
  flex-shrink: 0;
  width: 44px;
  height: 44px;
  font-size: var(--font-title3);
  font-weight: 700;
  color: #ffffff;
  background: var(--avatar, var(--control-bg));
  border-radius: 50%;
}

.ob-invited-avatar svg {
  width: 22px;
  height: 22px;
}

.ob-invited p {
  font-size: var(--font-subhead);
  line-height: 1.35;
  color: var(--text-primary);
}

.ob-invited p b {
  color: var(--orange-light);
}

:root[data-theme="light"] .ob-invited p b {
  color: var(--orange-dark);
}

.ob-invited p span {
  display: block;
  font-size: var(--font-footnote);
  color: var(--text-secondary);
}

/* Settings: under "Show Me in Leagues". */
.settings-note {
  margin: calc(-1 * var(--space-xs)) 0 var(--space-sm);
  font-size: var(--font-footnote);
  line-height: 1.35;
  color: var(--text-secondary);
  text-align: left;
}

/* Competition: this week's league. */
.league-card {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: var(--space-xs);
  padding: 12px 14px;
  background: var(--card-solid);
  border: 1px solid var(--card-line);
  border-radius: var(--radius);
}

.league-badge {
  display: grid;
  place-items: center;
  flex-shrink: 0;
  width: 44px;
  height: 44px;
  color: var(--accent-text);
  background: var(--accent-tint);
  border-radius: 12px;
}

.league-badge svg {
  width: 24px;
  height: 24px;
}

.league-card-text {
  flex: 1;
  min-width: 0;
  text-align: left;
}

.league-card-name {
  font-size: var(--font-body);
  font-weight: 700;
  color: var(--text-primary);
}

.league-card-sub {
  margin-top: 2px;
  font-size: var(--font-footnote);
  color: var(--text-secondary);
}

.league-time-left {
  flex-shrink: 0;
  font-size: var(--font-footnote);
  font-weight: 700;
  color: var(--orange-light);
  white-space: nowrap;
}

:root[data-theme="light"] .league-time-left {
  color: var(--orange-dark);
}

.league-points {
  flex-shrink: 0;
  min-width: 44px;
  font-size: var(--font-subhead);
  font-weight: 800;
  line-height: 1.1;
  text-align: right;
  color: var(--text-primary);
  font-variant-numeric: tabular-nums;
}

.league-points small {
  display: block;
  font-size: var(--font-caption);
  font-weight: 600;
  color: var(--text-secondary);
}

.leaderboard-rank.is-medal-1 { color: var(--medal-gold); }
.leaderboard-rank.is-medal-2 { color: var(--medal-silver); }
.leaderboard-rank.is-medal-3 { color: var(--medal-bronze); }

.leaderboard-today.is-friend {
  font-weight: 600;
  color: var(--accent-text);
}

.league-gap {
  padding: 2px 0;
  font-size: var(--font-footnote);
  color: var(--text-secondary);
  text-align: center;
}

.league-how {
  margin: var(--space-xs) 4px var(--space-md);
  font-size: var(--font-footnote);
  line-height: 1.4;
  color: var(--text-secondary);
  text-align: left;
}

/* Monday to Wednesday: last week's finish. */
.league-result {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: var(--space-sm);
  padding: 12px 14px;
  text-align: left;
  background: var(--card-solid);
  border: 1px solid var(--card-line);
  border-radius: var(--radius);
  color: var(--text-secondary);
}

.league-result.is-gold { border-color: var(--medal-gold); color: var(--medal-gold); }
.league-result.is-silver { border-color: var(--medal-silver); color: var(--medal-silver); }
.league-result.is-bronze { border-color: var(--medal-bronze); color: var(--medal-bronze); }

.league-result svg {
  flex-shrink: 0;
  width: 36px;
  height: 36px;
}

.league-result p {
  font-size: var(--font-subhead);
  line-height: 1.35;
  color: var(--text-secondary);
}

.league-result-title {
  display: block;
  font-size: var(--font-body);
  font-weight: 700;
  color: var(--text-primary);
}

/* Today, Monday to Wednesday: your week. */
.week-recap {
  margin: 0 0 var(--space-md);
  padding: 16px;
  text-align: left;
  background: var(--card-solid);
  border: 1px solid var(--card-line);
  border-radius: var(--radius);
}

.week-recap-top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-sm);
}

.week-recap-top h2 {
  font-size: var(--font-title3);
  font-weight: 700;
  color: var(--text-primary);
}

.week-recap-top span {
  font-size: var(--font-footnote);
  color: var(--text-secondary);
  white-space: nowrap;
}

.week-recap-days {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 4px;
  margin: 14px 0 8px;
  padding: 0;
  list-style: none;
}

.week-recap-days li {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  font-size: var(--font-caption);
  font-weight: 600;
  color: var(--text-secondary);
}

.week-recap-dot {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  color: #ffffff;
  background: var(--overlay);
  border-radius: 50%;
}

.week-recap-dot.is-on {
  background: var(--accent);
}

.week-recap-dot.is-rest {
  color: var(--heatmap-rest-mark);
  background: var(--heatmap-rest);
}

.week-recap-dot svg {
  width: 15px;
  height: 15px;
}

.week-recap-count {
  margin-bottom: 12px;
  font-size: var(--font-subhead);
  color: var(--text-secondary);
  text-align: center;
}

.week-recap-count b {
  color: var(--text-primary);
}

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

.week-recap-stats div {
  padding: 10px 12px;
  background: var(--page-bg);
  border-radius: var(--radius-md);
}

.week-recap-stats b {
  display: block;
  font-size: var(--font-title2);
  font-weight: 800;
  color: var(--text-primary);
  font-variant-numeric: tabular-nums;
}

.week-recap-stats span {
  font-size: var(--font-footnote);
  color: var(--text-secondary);
}

.week-recap-stats i {
  display: inline-block;
  width: 8px;
  height: 8px;
  margin-right: 5px;
  background: var(--dot);
  border-radius: 50%;
}

.week-recap-best {
  margin-top: 10px;
  padding: 10px 12px;
  font-size: var(--font-subhead);
  line-height: 1.35;
  color: var(--text-primary);
  background: #c0855226;
  border-radius: var(--radius-md);
}

.week-recap-best b {
  color: var(--orange-light);
}

:root[data-theme="light"] .week-recap-best b {
  color: var(--orange-dark);
}

.week-recap-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm);
  margin-top: 14px;
}

.week-recap-hide,
.week-recap-share {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 36px;
  padding: 0 14px;
  font-family: inherit;
  font-size: var(--font-subhead);
  font-weight: 700;
  border-radius: 999px;
  cursor: pointer;
}

.week-recap-hide::before,
.week-recap-share::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  height: 44px;
  transform: translateY(-50%);
}

.week-recap-hide {
  color: var(--text-secondary);
  background: none;
  border: 1px solid var(--border);
}

.week-recap-share {
  color: #ffffff;
  background: var(--accent);
  border: 1px solid var(--accent);
}

.week-recap-share:disabled {
  opacity: 0.5;
  cursor: default;
}

.week-recap-share svg {
  width: 16px;
  height: 16px;
}

.invite-share-button:focus-visible,
.week-recap-hide:focus-visible,
.week-recap-share:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
}
