/* TurboPrep Design System
 *
 * Loaded AFTER styles.css so tokens here override anything inconsistent
 * upstream. Goal: every spacing/radius/elevation/transition value in
 * the app maps to one of these tokens, so the UI looks like a single
 * coherent product instead of a stack of one-off inline styles.
 *
 * Three layers:
 *   1. Tokens (custom properties) — the design vocabulary
 *   2. Primitives (.ds-*)        — bare building blocks (cards, rows,
 *                                  buttons) that consume tokens
 *   3. Patches                    — targeted overrides of legacy classes
 *                                  to bring them in line with the system
 *
 * Anything in this file is the source of truth. If something else fights
 * it, fix the upstream rule rather than weaken the token.
 */

:root {
  /* ── Spacing scale: every margin, padding, and gap maps to 4px units */
  --ds-space-0: 0;
  --ds-space-1: 4px;
  --ds-space-2: 8px;
  --ds-space-3: 12px;
  --ds-space-4: 16px;
  --ds-space-5: 20px;
  --ds-space-6: 24px;
  --ds-space-8: 32px;
  --ds-space-10: 40px;
  --ds-space-12: 48px;

  /* ── Radii: 3 levels, plus pill */
  --ds-radius-sm: 8px;
  --ds-radius-md: 12px;
  --ds-radius-lg: 16px;
  --ds-radius-pill: 999px;

  /* ── Type scale */
  --ds-text-xs:   11px;
  --ds-text-sm:   13px;
  --ds-text-base: 15px;
  --ds-text-lg:   17px;
  --ds-text-xl:   20px;
  --ds-text-2xl:  24px;
  --ds-text-3xl:  32px;
  --ds-leading-tight: 1.2;
  --ds-leading-snug:  1.35;
  --ds-leading-normal:1.5;

  /* ── Elevation: scrim → card → sheet → dialog */
  --ds-elev-1: 0 1px 2px rgba(0,0,0,.18);
  --ds-elev-2: 0 4px 12px rgba(0,0,0,.22);
  --ds-elev-3: 0 12px 32px rgba(0,0,0,.34);
  --ds-elev-pop: 0 8px 24px rgba(var(--primary-rgb),.18);

  /* ── Motion: 3 standard durations + one easing */
  --ds-dur-fast: 120ms;
  --ds-dur-base: 180ms;
  --ds-dur-slow: 280ms;
  --ds-ease-out: cubic-bezier(.2,.8,.2,1);

  /* ── Borders */
  --ds-border-thin: 1px solid var(--border);
  --ds-border-strong: 1px solid var(--border-strong);

  /* ── Tap target floor (iOS HIG) */
  --ds-touch: 44px;
}

/* ===========================================================================
 * 1. PRIMITIVES — use these directly in templates.
 * ========================================================================= */

/* .ds-stack: vertical flow with consistent gap. */
.ds-stack    { display: flex; flex-direction: column; gap: var(--ds-space-3); }
.ds-stack-2  { gap: var(--ds-space-2); }
.ds-stack-4  { gap: var(--ds-space-4); }
.ds-stack-6  { gap: var(--ds-space-6); }

/* .ds-row: horizontal flow, wraps on narrow viewports. */
.ds-row      { display: flex; align-items: center; gap: var(--ds-space-3); flex-wrap: wrap; }
.ds-row-tight{ gap: var(--ds-space-2); }
.ds-row-end  { justify-content: flex-end; }
.ds-spacer   { flex: 1; }

/* .ds-surface: any content container — card-like, but unstyled by default. */
.ds-surface {
  background: var(--card);
  border: var(--ds-border-thin);
  border-radius: var(--ds-radius-md);
  padding: var(--ds-space-4);
}
.ds-surface-lg { padding: var(--ds-space-5); border-radius: var(--ds-radius-lg); }

/* .ds-pressable: any tap target. Hits the 44px floor, kills double-tap zoom,
   and applies a quick scale-down on press for tactile feedback. */
.ds-pressable {
  min-height: var(--ds-touch);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
  transition: transform var(--ds-dur-fast) var(--ds-ease-out),
              opacity var(--ds-dur-fast) var(--ds-ease-out),
              background-color var(--ds-dur-fast) var(--ds-ease-out);
}
.ds-pressable:active { transform: scale(.97); opacity: .9; }

/* .ds-divider: a thin rule that respects safe spacing on either side. */
.ds-divider { height: 1px; background: var(--border); margin: var(--ds-space-3) 0; }

/* .ds-meta: muted secondary text — labels, timestamps, helpers. */
.ds-meta {
  font-size: var(--ds-text-xs);
  color: var(--muted-fg);
  line-height: var(--ds-leading-snug);
}

/* .ds-title-row: page-section heading paired with optional action. */
.ds-title-row {
  display: flex; align-items: baseline; gap: var(--ds-space-3);
  margin-bottom: var(--ds-space-3);
}
.ds-title-row .ds-title {
  font-size: var(--ds-text-xs); font-weight: 700; letter-spacing: .05em;
  text-transform: uppercase; color: var(--muted-fg);
}
.ds-title-row .ds-spacer + .ds-action {
  font-size: var(--ds-text-sm); font-weight: 600; color: var(--primary);
}

/* ===========================================================================
 * 2. PATCHES — bring legacy components into the system.
 * ========================================================================= */

/* Auth screens — vertically centred so the card sits in the middle of the
   viewport instead of pinned to the top. min-height is the dynamic viewport
   so it still scrolls if the signup form is tall. */
.auth-screen {
  justify-content: center;
  padding: max(var(--ds-space-6), env(safe-area-inset-top))
           var(--ds-space-5)
           max(var(--ds-space-6), env(safe-area-inset-bottom));
  gap: var(--ds-space-5);
}
/* Both .auth-form (legacy) and .auth-card (current) target the same widget
   in different parts of the codebase. Patch both so refactor risk is zero.
   Slightly larger max-width + roomier padding so the card feels solid on
   modern phones (Pro/Plus/Ultra) without overflowing the smallest screens. */
.auth-form,
.auth-card {
  width: 100%;
  max-width: 440px;
  padding: var(--ds-space-6) var(--ds-space-5);
  border-radius: var(--ds-radius-lg);
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  gap: var(--ds-space-3);
}
/* Form-group margin pre-existed; now we use the card's flex gap instead so
   spacing is uniform top-to-bottom and the last child has no trailing space. */
.auth-card .form-group,
.auth-form .form-group { margin-bottom: 0; }
.auth-card .input,
.auth-form .input,
.input { box-sizing: border-box; width: 100%; }
/* Identity sits centred above the card. Tighten gap so wordmark + tagline
   feel like one unit instead of two stacked elements. */
.auth-identity { gap: var(--ds-space-1); margin-bottom: 0; }
.auth-logo, .auth-logo-mark { display: none; }  /* wordmark carries brand */
.auth-wordmark, .auth-wordmark-2 {
  font-size: var(--ds-text-2xl);
  letter-spacing: .08em;
}
.auth-tagline {
  font-size: var(--ds-text-sm);
  color: var(--muted-fg);
  letter-spacing: .02em;
}
/* Auth switch link — nudge spacing so it visually belongs to the card group. */
.auth-switch { margin-top: 0; padding-top: var(--ds-space-1); }
/* Login/signup primary button always full width inside the card. */
.auth-card .btn-primary,
.auth-form .btn-primary { width: 100%; margin-top: var(--ds-space-1); }

/* Inputs — baseline so every .input (and bare textarea/select) reads
   correctly. Sizing/border-radius applies in any theme; the dark-theme
   surface fill is scoped to body:not(.light-theme) so light-theme inputs
   keep their native white background instead of being forced to dark. */
.input,
input.input,
textarea.input,
select.input {
  border-radius: var(--ds-radius-sm);
  padding: 10px 12px;
  font-size: 16px;
  width: 100%;
  box-sizing: border-box;
  transition: border-color var(--ds-dur-fast) var(--ds-ease-out),
              background-color var(--ds-dur-fast) var(--ds-ease-out);
}
body:not(.light-theme) .input,
body:not(.light-theme) input.input,
body:not(.light-theme) textarea.input,
body:not(.light-theme) select.input {
  background-color: var(--surface);
  color: var(--fg);
  border: 1px solid var(--border-strong);
}
body:not(.light-theme) .input::placeholder,
body:not(.light-theme) input.input::placeholder,
body:not(.light-theme) textarea.input::placeholder { color: var(--muted-fg); opacity: .8; }
.input:focus,
input.input:focus,
textarea.input:focus,
select.input:focus {
  border-color: var(--primary);
  outline: none;
}
textarea.input { min-height: 72px; resize: vertical; line-height: 1.4; }

/* Sub-tab strips — sit flush against the page gutter. The previous edge
   mask faded out the last visible tab label ("Pl…" instead of "Plans")
   when the strip overflowed, which read as a UI bug. Hard clipping +
   normal overflow-x:auto from the per-strip CSS is honest: users can
   see the last label is partly hidden and swipe to scroll. */
.fitness-sub-bar,
.admin-tabs,
.lb-sub-tabs {
  padding-left: 0;
  padding-right: 0;
}

/* In-app announcement toast — sits above the bottom tab bar so it never
   covers page content or the header. Slides up from below the safe area
   with a subtle primary accent stripe on the left edge. Tap to dismiss
   or wait 8s for auto-dismiss. */
#tp-ann-banner {
  position: fixed;
  left: 50%;
  bottom: calc(env(safe-area-inset-bottom, 0px) + 86px);
  transform: translate(-50%, calc(100% + 24px));
  width: min(92vw, 420px);
  z-index: 9999;
  pointer-events: none;
  transition: transform .28s var(--ds-ease-out, cubic-bezier(.2,.8,.2,1)),
              opacity .28s var(--ds-ease-out, cubic-bezier(.2,.8,.2,1));
  opacity: 0;
}
#tp-ann-banner.show {
  transform: translate(-50%, 0);
  pointer-events: auto;
  opacity: 1;
}
#tp-ann-banner .tp-ann-banner-inner {
  display: flex; align-items: center; gap: 10px;
  padding: 12px 12px 12px 14px;
  background: var(--card);
  border: 1px solid var(--border-strong);
  border-left: 3px solid var(--primary);
  border-radius: var(--ds-radius-md);
  box-shadow: 0 8px 24px rgba(0,0,0,.32);
}
#tp-ann-banner .tp-ann-banner-icon {
  font-size: 16px; line-height: 1; flex-shrink: 0;
  width: 26px; height: 26px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: rgba(var(--primary-rgb),.14);
}
#tp-ann-banner .tp-ann-banner-text { flex: 1; min-width: 0; }
#tp-ann-banner .tp-ann-banner-title {
  font-size: 13px; font-weight: 700; color: var(--fg);
  margin-bottom: 1px;
}
#tp-ann-banner .tp-ann-banner-msg {
  font-size: 12px; line-height: 1.4; color: var(--muted-fg);
  word-break: break-word;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
#tp-ann-banner .tp-ann-banner-close {
  flex-shrink: 0; width: 26px; height: 26px;
  border-radius: 50%; border: none;
  background: transparent; color: var(--muted-fg);
  font-size: 18px; line-height: 1; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
}
#tp-ann-banner .tp-ann-banner-close:active { background: var(--surface); }

/* Cards — soften the shadow and sync the radius to one of the 3 tokens. */
.card {
  border-radius: var(--ds-radius-md);
  transition: border-color var(--ds-dur-fast) var(--ds-ease-out);
}
.card-pad { padding: var(--ds-space-4); }

/* Buttons — guarantee the touch floor and consistent radius. */
.btn {
  border-radius: var(--ds-radius-sm);
  min-height: var(--ds-touch);
  transition: transform var(--ds-dur-fast) var(--ds-ease-out),
              background-color var(--ds-dur-fast) var(--ds-ease-out),
              opacity var(--ds-dur-fast) var(--ds-ease-out);
}
.btn:active { transform: scale(.97); }
.btn[disabled] { opacity: .5; cursor: not-allowed; }

/* Modal scrim — lighter than before so the page behind is still legible.
   This is the single source of truth for modal backgrounds; older inline
   rgba(0,0,0,.6) styles in JS templates should migrate to .modal-overlay. */
.modal-overlay {
  background: rgba(0, 0, 0, .35);
  animation: ds-fade-in var(--ds-dur-base) var(--ds-ease-out);
}
.modal-card {
  border-radius: var(--ds-radius-lg);
  padding: var(--ds-space-5);
  box-shadow: var(--ds-elev-3);
  animation: ds-pop-in var(--ds-dur-base) var(--ds-ease-out);
}
.tutorial-overlay { background: rgba(0, 0, 0, .5); }

@keyframes ds-fade-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes ds-pop-in {
  from { opacity: 0; transform: scale(.96) translateY(6px); }
  to   { opacity: 1; transform: scale(1)   translateY(0);  }
}

/* Sheets — same easing language as modals so transitions feel related. */
.sheet { transition: transform var(--ds-dur-base) var(--ds-ease-out); }

/* Empty-state — one canonical look anywhere it appears. */
.empty-state {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  text-align: center; padding: var(--ds-space-8) var(--ds-space-5); gap: var(--ds-space-2);
}
.empty-state-title {
  font-size: var(--ds-text-base); font-weight: 700; color: var(--fg);
  line-height: var(--ds-leading-snug);
}
.empty-state-desc {
  font-size: var(--ds-text-sm); color: var(--muted-fg);
  line-height: var(--ds-leading-normal); max-width: 320px;
}

/* Tab bar — slightly tighter padding on small phones so labels never wrap.
   The icon + label stack still hits 44px touch through the icon's margin. */
.tab-btn { min-height: var(--ds-touch); padding: 4px 2px; }
.tab-btn-label {
  font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
}

/* Sub-tab bar (fitness/coach/admin tab strips) — make active state read more
   clearly, and tighten the inactive button so they don't compete. */
.fitness-sub-tab.active {
  background: var(--primary) !important;
  color: var(--primary-fg) !important;
}
.fitness-sub-tab {
  transition: background-color var(--ds-dur-fast) var(--ds-ease-out),
              color var(--ds-dur-fast) var(--ds-ease-out);
}

/* Toasts — softer shadow, snappier enter. */
.toast {
  border-radius: var(--ds-radius-md);
  box-shadow: var(--ds-elev-2);
  animation: ds-toast-in var(--ds-dur-base) var(--ds-ease-out);
}
@keyframes ds-toast-in {
  from { opacity: 0; transform: translateY(-8px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* ===========================================================================
 * 3. UI RULES — invariants that must hold app-wide.
 * ========================================================================= */

/* Perfect circles. Any element marked .ds-circle (or already round via the
   legacy avatar/dot/badge classes) is forced to a 1:1 aspect ratio so
   flexbox can't squash it into an oval. Width or height alone sets size.
   Targets the user-avatar (top-right initial), record-tab-circle (centre
   tab), and any other round badges. */
.ds-circle,
.user-avatar,
.record-tab-circle,
.lb-podium-avatar,
.team-hero-code,
.coach-feat-toggle::after,
.admin-toggle::after,
button.user-avatar {
  aspect-ratio: 1 / 1 !important;
  border-radius: 50% !important;
  flex-shrink: 0;
}

/* Avatar sizing — explicit dimensions on every axis so neither flex
   stretching nor user-agent button styles can deform it into an oval.
   width/height/min/max all locked, padding 0, line-height 1, overflow
   hidden so any pseudo-content is clipped to the circle. */
.user-avatar {
  width: 36px !important;
  height: 36px !important;
  min-width: 36px !important;
  max-width: 36px !important;
  min-height: 36px !important;
  max-height: 36px !important;
  display: inline-flex !important;
  align-items: center !important;
  justify-content: center !important;
  font-weight: 700;
  font-size: 14px;
  line-height: 1 !important;
  color: var(--primary-fg);
  background: var(--primary);
  border: none;
  padding: 0 !important;
  box-sizing: border-box !important;
  overflow: hidden;
}

/* Sub-tab overflow guard — Race Day was clipping in the Coach tab because
   the strip wasn't allowing horizontal scroll. Permit scroll, hide the
   scrollbar, let each tab size to its content, and ensure children can
   shrink (min-width: 0) so the strip stays in one row. */
.fitness-sub-bar {
  overflow-x: auto;
  overflow-y: hidden;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  flex-wrap: nowrap;
  min-width: 0;
}
.fitness-sub-bar::-webkit-scrollbar { display: none; }
/* Tabs fill the strip equally so there's no dead space on the right.
   Was previously `flex:0 0 auto` which sized tabs to content and left
   a gap when the strip was wider than the sum of the tabs. */
.fitness-sub-tab {
  flex: 1 1 0 !important;
  min-width: 0;
  white-space: nowrap;
}

/* Touch-target floor — 44px is Apple HIG. Several legacy controls were
   under (timer-close 36px, profile-back 36px, admin-toggle 26px). Bump
   them up so they're tappable without hitting the wrong thing. */
.timer-close,
.profile-back,
.btn-icon {
  width: 44px !important;
  height: 44px !important;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.admin-toggle {
  width: 52px !important;
  height: 32px !important;
}
.admin-toggle::after { width: 24px; height: 24px; top: 4px; left: 4px; }
.admin-toggle.on::after { transform: translateX(20px); }

/* Body scroll lock when a modal/sheet is open — without this, swiping
   past the modal scrolls the page behind it on iOS. App.js toggles this
   class on body when an overlay is presented. */
body.ds-modal-open {
  overflow: hidden !important;
  touch-action: none;
}

/* Long-text guards on header + page titles so a long team or user name
   never breaks the layout. */
.header-wordmark,
.ai-header-title,
.user-menu-name {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}

/* Page title — phone-first sizing. Was 28px globally (set for desktop) so
   on iPhone it dwarfed everything around it. Scale up only on tablet+. */
.page-title {
  font-size: 22px !important;
  font-weight: 800 !important;
  line-height: var(--ds-leading-tight) !important;
  letter-spacing: -.02em !important;
  margin-bottom: var(--ds-space-3) !important;
}
@media (min-width: 600px) {
  .page-title { font-size: 26px !important; }
}
@media (min-width: 900px) {
  .page-title { font-size: 32px !important; margin-bottom: var(--ds-space-5) !important; }
}

/* Mobile-first content density — the app was clearly tuned on a wide
   screen. Tighten the default phone padding so cards fill the viewport
   without huge dead zones at the edges. */
@media (max-width: 599px) {
  .content { padding: 12px !important; }
  .card-pad, .ds-surface { padding: var(--ds-space-3) !important; }
  .ds-stack { gap: var(--ds-space-2); }
  .ds-stack-4 { gap: var(--ds-space-3); }
}

/* Centre the page column on iPhone — without this, a 6.7" Pro Max
   shows content left-aligned to the safe-area inset and feels off-
   centre. A tight max-width with auto margins fixes it. */
#main-app > #content > .page,
#main-app > #content > .page.active {
  max-width: 600px;
  margin-left: auto;
  margin-right: auto;
}

/* Header — logo + wordmark were both orange and competing. Hide the T tile
   on screens where the wordmark already carries the brand; keep it visible
   on very narrow viewports as a fallback identity. */
.header { min-height: 48px; padding-left: var(--ds-space-3); padding-right: var(--ds-space-3); }
.header-logo {
  width: 28px; height: 28px;
  border-radius: var(--ds-radius-sm) !important;
  font-size: 13px;
  box-shadow: none;
  display: none;  /* wordmark wins; brings up if the wordmark is hidden */
}
.hw-turbo, .hw-prep { font-size: 16px; letter-spacing: .05em; }
.hw-prep { color: var(--primary) !important; opacity: .65; }

/* Bottom tab bar polish — subtle separator above, label sized down, active
   indicator becomes a dot above the icon instead of full-width line so the
   centre Record circle reads as the focal point. */
.tab-bar {
  border-top: var(--ds-border-thin);
  background: var(--bg);
}
.tab-btn {
  color: var(--muted-fg);
  transition: color var(--ds-dur-fast) var(--ds-ease-out);
}
.tab-btn.active { color: var(--primary); }
.tab-btn-label {
  margin-top: 2px;
  opacity: .9;
}

/* Card polish — softer ambient shadow + subtle press scale. */
.card {
  background: var(--card);
  border: var(--ds-border-thin);
  box-shadow: var(--ds-elev-1);
}
.card.card-pad { padding: var(--ds-space-4); }

/* Sheet — round only the top edges and add the same elevation as a dialog. */
.sheet {
  border-radius: var(--ds-radius-lg) var(--ds-radius-lg) 0 0;
  box-shadow: var(--ds-elev-3);
}

/* Focus ring — single color, generous offset, never inside the element. */
:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
  border-radius: var(--ds-radius-sm);
}

/* Skeletons — match the card surface so loading state doesn't look broken. */
.skeleton { background: var(--surface); }

/* Reduce motion — honor the system preference everywhere. */
@media (prefers-reduced-motion: reduce) {
  *, ::before, ::after {
    animation-duration: 1ms !important;
    transition-duration: 1ms !important;
  }
}
