/* ─── android-sage tokens, mapped 1:1 from theme/Color.kt + Type.kt ─── */
:root {
  --bg:          #F6F2EC;   /* page background */
  --bg-deep:     #EBE6DE;   /* recessed sections */
  --surface:     #FDFBF7;   /* cards */
  --surface-2:   #F1ECE3;   /* subtle chips / fills */
  --ink:         #1F1B16;   /* primary text */
  --ink-2:       #4A4239;   /* secondary text */
  --ink-3:       #8C8275;   /* tertiary / captions */
  --ink-4:       #B8AE9E;   /* disabled / hint */
  --line:        rgba(63,52,38,.10);
  --line-strong: rgba(63,52,38,.18);

  --accent:      #7F9F7F;   /* sage */
  --accent-soft: #E3EADC;
  --accent-deep: #4B6A4B;

  --secondary:      #CC7A5A;  /* terracotta */
  --secondary-soft: #F4DDCF;

  --good: var(--accent);
  --warn: #C9913A;
  --bad:  #B23B2A;

  /* Provider brand chips */
  --pv-garmin:  #007CC3;
  --pv-strava:  #FC4C02;
  --pv-oura:    #3D2D1F;
  --pv-whoop:   #111111;
  --pv-apple:   #FF375F;
  --pv-google:  #4285F4;
  --pv-withings:#00A8E1;
  --pv-fitbit:  #00B0B9;

  /* Type families */
  --serif: "Newsreader", Georgia, "Times New Roman", serif;
  --sans:  "IBM Plex Sans", system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
  --mono:  "IBM Plex Mono", ui-monospace, Menlo, Consolas, monospace;

  /* Shape (Theme.kt) */
  --r-xs: 10px;
  --r-sm: 16px;
  --r-md: 22px;
  --r-lg: 28px;
  --r-xl: 32px;
}

*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
html, body, #root { height: 100%; }
body {
  background: var(--bg);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 14px;
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  /* Disable iOS double-tap-to-zoom on tap-targets — interaction reads
     more like a native app. Buttons keep their own click semantics. */
  -webkit-tap-highlight-color: transparent;
  /* Don't let the document itself bounce — modals + content scrollers
     each opt back in where appropriate. */
  overscroll-behavior-y: none;
}

/* SPA shell — give it the dynamic viewport height where supported so the
   layout doesn't jump when the iOS Safari address bar collapses on scroll.
   100vh fallback for browsers that haven't shipped 100dvh yet. */
.app-shell {
  min-height: 100vh;
  min-height: 100dvh;
}

/* Sticky topbar — translucent + backdrop blur reads more like a native
   nav bar than a solid slab once content scrolls under it. */
.app-topbar {
  background: color-mix(in srgb, var(--bg) 78%, transparent);
  -webkit-backdrop-filter: saturate(160%) blur(12px);
  backdrop-filter: saturate(160%) blur(12px);
  /* Sit under the iOS notch / camera cutout via the safe area inset, but
     keep our own padding underneath so the title doesn't hug the cutout. */
  padding-top: max(env(safe-area-inset-top), 12px);
}
/* Bottom nav (mobile) needs the home-bar inset so the labels don't sit
   under the iOS gesture indicator or the Android navigation gesture bar.
   Floor bumped to 16px to match the v3 design's roomier feel — paired
   with the nav's minHeight: 64, this gives the tab labels plenty of
   clearance above the device gesture line even when env reports 0. */
/* Bottom nav padding-bottom — tightened on user feedback. Previous
   `max(env, 26) + 5` was 31 px on Android (no gesture inset) and
   felt like a chunky strip of empty space below the icons that
   bled into the Coach screen's bottom chrome. Floor reduced to
   10 px (still keeps labels off the screen edge on Androids
   without insets) and the +5 buffer dropped (iPhone home
   indicators are already accounted for by env()). */
.app-bottombar {
  padding-bottom: max(env(safe-area-inset-bottom), 10px);
}

button { font: inherit; color: inherit; }
::selection { background: var(--accent-soft); color: var(--ink); }

/* ─── primitives ─── */
.label-lg {
  font-family: var(--mono);
  font-weight: 500;
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.label-md {
  font-family: var(--mono);
  font-weight: 400;
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.label-sm {
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.mono { font-family: var(--mono); font-feature-settings: "tnum" 1, "lnum" 1; }
.serif { font-family: var(--serif); }

/* SageCard — Card.kt */
.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  box-shadow: 0 2px 6px rgba(31,27,22,.05), 0 1px 1px rgba(31,27,22,.04);
  padding: 16px;
}
.card-clickable { cursor: pointer; transition: border-color .15s ease, transform .15s ease, box-shadow .15s ease; }
.card-clickable:hover { border-color: var(--line-strong); transform: translateY(-1px); box-shadow: 0 6px 14px rgba(31,27,22,.07); }

/* Section header */
.section-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 4px 8px;
}
.section-header > a {
  color: var(--accent-deep);
  font-size: 12px;
  text-decoration: none;
}
.section-header > a:hover { text-decoration: underline; }

/* Pills + chips */
.pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border-radius: 999px;
  background: var(--surface-2);
  border: 1px solid var(--line);
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-2);
}
.pill.dot::before {
  content: "";
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--accent);
}

/* Buttons */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 10px 18px;
  border-radius: 999px;
  border: 1px solid var(--line-strong);
  background: var(--surface);
  color: var(--ink);
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  cursor: pointer;
  transition: all .15s ease;
}
.btn:hover { border-color: var(--ink-3); }
.btn-primary {
  background: var(--accent-deep);
  color: var(--bg);
  border-color: var(--accent-deep);
}
.btn-primary:hover { background: #3F5A3F; border-color: #3F5A3F; }
.btn-ghost { background: transparent; border-color: transparent; color: var(--ink-2); }
.btn-ghost:hover { background: var(--surface-2); border-color: var(--line); }

/* Scrollbars — single slim treatment used everywhere (global, .app-scroll,
   .modal-scroll, the explorer's overflow-x tables, coach panes). Browsers
   that overlay their own scrollbar (iOS Safari, modern Android Chrome)
   ignore these rules; they're a no-op there. The .no-scrollbar class
   below is the explicit override for chip rows where we want no chrome
   even though there's overflow. */
* {
  scrollbar-width: thin;
  scrollbar-color: var(--line-strong) transparent;
}
::-webkit-scrollbar { width: 6px; height: 6px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--line-strong); border-radius: 3px; }
::-webkit-scrollbar-thumb:hover { background: var(--ink-3); }

/* Helpers */
.row { display: flex; }
.col { display: flex; flex-direction: column; }
.gap-4 { gap: 4px; } .gap-6 { gap: 6px; } .gap-8 { gap: 8px; } .gap-10 { gap: 10px; }
.gap-12 { gap: 12px; } .gap-14 { gap: 14px; } .gap-16 { gap: 16px; } .gap-20 { gap: 20px; } .gap-24 { gap: 24px; }
.grow { flex: 1 1 auto; }
.center { align-items: center; }
.between { justify-content: space-between; }
.end { justify-content: flex-end; }

#root { min-height: 100vh; }

/* Skeleton fallback for FOUC */
.pre-react {
  height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--ink-3);
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

@keyframes indet {
  0%   { left: -30%; }
  100% { left: 100%; }
}
.dots::after { content: ""; animation: dots 1.2s steps(3, end) infinite; }
@keyframes dots { 0% { content: ""; } 33% { content: "."; } 66% { content: ".."; } 100% { content: "..."; } }

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

/* Scroll lock — applied while any DetailModal is mounted. The SPA's inner
   scroll container holds the page content (see app.jsx), so locking body
   alone isn't enough; we also freeze .app-scroll. Touch devices need
   touch-action:none on body to stop the iOS rubber-band from chaining
   into the underlying page when the user drags inside a modal. */
body.modal-open {
  overflow: hidden;
  touch-action: none;
}
body.modal-open .app-scroll {
  overflow: hidden;
}

/* Reserves a clear zone at the right edge of a modal's first row so any
   right-aligned chips / Back button / action don't slide under the
   absolutely-positioned close × at top:14, right:14 (32px button + 16px
   gap = 48px clear zone). Apply to any header row inside a DetailModal
   that has content reaching the right edge. */
.modal-header-row {
  padding-right: 48px;
}

/* .modal-scroll — kept as a marker class for grep-ability ("this div is
   the scrolling container of a DetailModal"). Visual styling now comes
   from the global rules above; the only modal-specific rule is the
   rounded-corner mask on the outer wrapper, which lives in JSX inline
   styles. Re-add per-class rules here only if modal scrollbars need to
   diverge from the global treatment. */

/* Horizontal chip rows that scroll on overflow but should stay visually
   clean — no scrollbar chrome. Used for filters / chip rows where we
   set scrollbar-width:none in JSX style; this is the WebKit equivalent
   so neither engine renders a track. */
.no-scrollbar::-webkit-scrollbar { display: none; }

/* Settings collapsible sections — stable height when collapsed (just the
   header) so the page doesn't jump around as content lazy-loads. The
   header is a button so the whole row is keyboard-focusable; the chevron
   rotates on aria-expanded=true. */
.settings-section {
  border: 1px solid var(--line);
  border-radius: 14px;
  background: var(--bg);
  overflow: hidden;
}
.settings-section + .settings-section { margin-top: 10px; }
.settings-section-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  padding: 16px 18px;
  background: transparent;
  border: none;
  cursor: pointer;
  text-align: left;
  color: var(--ink);
  font-family: inherit;
  gap: 16px;
}
.settings-section-header:hover { background: var(--surface-2); }
.settings-section-chevron {
  flex: 0 0 auto;
  font-size: 22px;
  color: var(--ink-3);
  transition: transform .15s ease;
}
.settings-section-header[aria-expanded="true"] .settings-section-chevron {
  transform: rotate(90deg);
}
.settings-section-body {
  padding: 4px 18px 18px;
  border-top: 1px solid var(--line);
}

.settings-signout {
  width: 100%;
  padding: 14px;
  font-size: 15px;
  color: var(--bad);
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 14px;
  cursor: pointer;
}
.settings-signout:hover { background: var(--surface-2); }

/* Connector card layout — desktop puts info+actions side by side; mobile stacks
   them and lets buttons split the row evenly so labels never overflow. The
   status label lives inside the info block (alongside the connector name)
   instead of in the actions row, so the actions row contains only buttons. */
.connector-card {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 12px;
}
.connector-card-info {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
  flex: 1 1 240px;
}
.connector-card-actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  flex-shrink: 0;
}
@media (max-width: 560px) {
  .connector-card { flex-direction: column; align-items: stretch; gap: 12px; }
  .connector-card-info { flex: 1 1 auto; }
  .connector-card-actions { width: 100%; flex-wrap: nowrap; gap: 6px; }
  /* All three buttons (Sync / Delete data / Disconnect) share one
     row at equal thirds. Previously the 50%-basis wrapped Disconnect
     onto a second row — user feedback was that it added noise to
     the card without a payoff. `1 1 0` evenly splits the row so the
     buttons end up at ~33% each. `min-width: 0` lets long labels
     ellipsis-truncate rather than push neighbours off-screen. */
  .connector-card-actions .btn,
  .connector-card-actions .btn-ghost,
  .connector-card-actions .btn-primary {
    flex: 1 1 0;
    min-width: 0;
    padding-left: 6px;
    padding-right: 6px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
}

/* Short column headers are print-only; the full labels show on screen. */
.summary-table .lbl-short { display: none; }

@media print {
  aside, .row > .btn, .row > .btn-ghost, .no-print,
  .app-topbar, .app-bottombar { display: none !important; }  /* drop app chrome from the PDF */
  body { background: white; color: black; }
  .card { box-shadow: none; border: 1px solid #ccc; }

  /* Coach summary: fit the whole 15-metric table on one landscape page
     rather than clipping columns off the right edge. */
  @page { size: landscape; margin: 8mm; }

  .summary-card { overflow: visible !important; }
  .summary-table {
    font-size: 6.5pt !important;
    table-layout: fixed;
    /* keep cell tints + progress bars in the PDF */
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }
  .summary-table th, .summary-table td { padding: 2px 3px !important; }
  .summary-table .col-group { display: none !important; }  /* drop repeated group label */
  .summary-table .lbl-full { display: none !important; }
  .summary-table .lbl-short { display: inline !important; }
}
