/* ============================================================================
   ui.css  —  shadcn-style primitives, vanilla (no build, no framework)
   ----------------------------------------------------------------------------
   Class-based ports of the shadcn component anatomy. Behaviour that shadcn gets
   from Radix (roving focus, open/close, selection state) lives in ui.js; this
   sheet is the styling only. Depends on tokens.css.
   ============================================================================ */

/* ---- Button ------------------------------------------------------------- */
.btn{
  display:inline-flex; align-items:center; justify-content:center; gap:6px;
  font-size:var(--fs-xs); font-weight:500; line-height:1; white-space:nowrap;
  border:1px solid var(--border); background:var(--card); color:var(--foreground);
  border-radius:var(--radius-md); padding:8px 12px; cursor:pointer;
  transition:background .12s, border-color .12s, color .12s;
}
.btn:hover{ border-color:var(--ring); }
.btn:disabled{ opacity:.5; cursor:not-allowed; border-color:var(--border); }
.btn .icon{ width:15px; height:15px; flex:none; }

.btn-primary{ background:var(--primary); color:var(--primary-foreground); border-color:var(--primary); }
.btn-primary:hover{ filter:brightness(1.05); }
.btn-secondary{ background:var(--secondary); color:var(--secondary-foreground); }
.btn-ghost{ background:transparent; border-color:transparent; }
.btn-ghost:hover{ background:var(--muted); border-color:transparent; }
.btn-destructive{ background:var(--destructive); color:var(--destructive-foreground); border-color:var(--destructive); }
.btn-link{ background:transparent; border-color:transparent; color:var(--primary); padding:8px 4px; }
.btn-link:hover{ text-decoration:underline; }

.btn-sm{ padding:5px 10px; }
.btn-lg{ padding:10px 16px; font-size:var(--fs-sm); }
.btn-icon{ padding:7px; }            /* square icon-only button */

/* ---- Card --------------------------------------------------------------- */
.card{
  background:var(--card); color:var(--card-foreground);
  border:1px solid var(--border); border-radius:var(--radius-lg); padding:16px;
}
.card-header{ display:flex; align-items:center; justify-content:space-between; gap:8px; margin-bottom:12px; }
.card-title{ font-size:var(--fs-md); font-weight:600; margin:0; }
.card-desc{ font-size:var(--fs-xs); color:var(--muted-foreground); margin:2px 0 0; line-height:1.5; }
.card-content{ display:block; }
.card-footer{ display:flex; align-items:center; gap:8px; margin-top:14px; flex-wrap:wrap; }

/* clickable card (the dashboard "analyse" affordance), keyboard reachable */
.card-interactive{ cursor:pointer; position:relative; }
.card-interactive:hover,
.card-interactive:focus-visible{ border-color:var(--ring); }

/* ---- Badge -------------------------------------------------------------- */
.badge{
  display:inline-flex; align-items:center; gap:5px;
  font-size:11px; font-weight:500; padding:2px 9px; border-radius:var(--radius-pill);
  border:1px solid var(--border); background:var(--muted); color:var(--muted-foreground);
}
.badge-success{ background:var(--bg-success); color:var(--success); border-color:var(--bd-success); }
.badge-warning{ background:var(--bg-warning); color:var(--warning); border-color:var(--bd-warning); }
.badge-destructive{ background:var(--bg-destructive); color:var(--destructive); border-color:var(--bd-destructive); }
.badge-info{ background:var(--bg-primary); color:var(--primary); border-color:var(--bd-primary); }

/* The settings status pills are just badges. Use the .badge classes directly,
   e.g. <span class="badge badge-success">connected</span>. No separate .pill
   class is needed (composes: is a CSS-Modules feature, not vanilla CSS). */

/* ---- Label + Input + Select --------------------------------------------- */
.field{ margin:0 0 10px; }
.label{ display:block; font-size:var(--fs-xs); color:var(--muted-foreground); margin-bottom:4px; }
.input,.select{
  width:100%; background:var(--muted); color:var(--foreground);
  border:1px solid var(--input); border-radius:var(--radius-md);
  padding:8px 10px; font-size:var(--fs-sm); font-family:inherit;
}
.input:focus,.select:focus{ outline:none; border-color:var(--ring); box-shadow:0 0 0 3px var(--ring-shadow); }
.input::placeholder{ color:var(--muted-foreground); }
input[type=date].input{ color-scheme:dark; }
.field-row{ display:flex; gap:10px; }
.field-row .field{ flex:1; }

/* ---- ToggleGroup / SegmentedControl (range + source filters) ------------ */
.toggle-group{ display:inline-flex; gap:4px; flex-wrap:wrap; align-items:center; }
.toggle{
  font-size:var(--fs-xs); border:1px solid var(--border); background:var(--card);
  color:var(--foreground); border-radius:var(--radius-md); padding:5px 10px; cursor:pointer;
}
.toggle:hover{ border-color:var(--ring); }
.toggle[aria-checked="true"],
.toggle[aria-pressed="true"]{ background:var(--primary); color:var(--primary-foreground); border-color:var(--primary); }

/* ---- DropdownMenu (secondary nav behind a kebab/avatar) ----------------- */
.menu{ position:relative; display:inline-block; }
.menu-content{
  position:absolute; right:0; top:calc(100% + 6px); min-width:200px; z-index:50;
  background:var(--popover); color:var(--popover-foreground);
  border:1px solid var(--border); border-radius:var(--radius-md); padding:6px;
  box-shadow:0 10px 30px rgba(0,0,0,.45); display:none;
}
.menu[data-open="true"] .menu-content{ display:block; }
.menu-item{
  display:flex; align-items:center; gap:8px; width:100%; text-align:left;
  font-size:var(--fs-sm); color:var(--foreground); background:none; border:none;
  border-radius:var(--radius-sm); padding:8px 10px; cursor:pointer;
}
.menu-item:hover,.menu-item:focus-visible{ background:var(--muted); }
.menu-item.destructive{ color:var(--destructive); }
.menu-sep{ height:1px; background:var(--border); margin:6px 4px; }

/* ---- Dialog (wraps native <dialog>) ------------------------------------- */
dialog.dialog{
  background:var(--popover); color:var(--popover-foreground);
  border:1px solid var(--border); border-radius:var(--radius-xl);
  padding:0; max-width:460px; width:92%;
}
dialog.dialog::backdrop{ background:var(--overlay); }
.dialog-header{ display:flex; align-items:center; justify-content:space-between; padding:14px 16px; border-bottom:1px solid var(--border); }
.dialog-title{ font-size:var(--fs-md); font-weight:600; }
.dialog-body{ padding:8px 16px 16px; max-height:66vh; overflow-y:auto; }
.dialog-footer{ display:flex; align-items:center; gap:8px; padding:12px 16px; border-top:1px solid var(--border); flex-wrap:wrap; }
.dialog-footer .spacer{ flex:1; }

/* ---- Tabs --------------------------------------------------------------- */
.tabs-list{ display:inline-flex; gap:4px; background:var(--muted); border:1px solid var(--border); border-radius:var(--radius-md); padding:3px; }
.tab{ font-size:var(--fs-xs); border:none; background:none; color:var(--muted-foreground); border-radius:var(--radius-sm); padding:6px 12px; cursor:pointer; }
.tab[aria-selected="true"]{ background:var(--card); color:var(--foreground); }
.tab-panel[hidden]{ display:none; }

/* ---- Accordion (collapsible stream charts) ------------------------------ */
.accordion-item{ border-bottom:1px solid var(--border); }
.accordion-trigger{
  display:flex; align-items:center; justify-content:space-between; width:100%;
  font-size:var(--fs-md); font-weight:500; color:var(--foreground);
  background:none; border:none; padding:14px 4px; cursor:pointer;
}
.accordion-trigger .chev{ transition:transform .15s; }
.accordion-trigger[aria-expanded="true"] .chev{ transform:rotate(180deg); }
.accordion-panel{ padding:0 4px 16px; }
.accordion-panel[hidden]{ display:none; }

/* ---- Table + DataTable -------------------------------------------------- */
.table-scroll{ overflow:auto; border-radius:var(--radius-md); }
table.table{ width:100%; border-collapse:collapse; font-size:var(--fs-xs); }
.table caption{ text-align:left; color:var(--muted-foreground); font-size:var(--fs-xs); padding:0 8px 8px; }
.table th{ text-align:left; font-weight:500; color:var(--muted-foreground); padding:6px 8px; position:sticky; top:0; background:var(--card); }
.table td{ padding:6px 8px; border-top:1px solid var(--border); }
.table .num{ text-align:right; font-variant-numeric:tabular-nums; }
.table tbody tr.clickable{ cursor:pointer; transition:background .12s; }
.table tbody tr.clickable:hover,
.table tbody tr.clickable:focus-visible{ background:var(--muted); outline:none; }
/* sticky first column for wide matrices (cadence comparison) */
.table.sticky-first th:first-child,
.table.sticky-first td:first-child{ position:sticky; left:0; background:var(--card); z-index:1; }

/* DataTable -> stacked cards on mobile. Add data-k="Label" to each <td>. */
@media (max-width:639px){
  .table-cards thead{ display:none; }
  .table-cards, .table-cards tbody, .table-cards tr, .table-cards td{ display:block; }
  .table-cards tr{
    background:var(--muted); border:1px solid var(--border);
    border-radius:var(--radius-md); padding:12px 14px; margin-bottom:10px;
  }
  .table-cards td{ border:none; padding:2px 0; font-size:var(--fs-sm); display:flex; justify-content:space-between; gap:12px; }
  .table-cards td::before{
    content:attr(data-k); color:var(--muted-foreground);
    font-size:11px; text-transform:uppercase; letter-spacing:.05em;
  }
}

/* ---- Progress (training block) ------------------------------------------ */
.progress{ height:8px; background:var(--muted); border:1px solid var(--border); border-radius:var(--radius-pill); overflow:hidden; }
.progress-bar{ height:100%; background:var(--primary); width:0; transition:width .3s; }

/* ---- Spinner ------------------------------------------------------------ */
.spinner{ width:15px; height:15px; border:2px solid var(--border); border-top-color:var(--primary); border-radius:50%; display:inline-block; animation:spin .8s linear infinite; }
@keyframes spin{ to{ transform:rotate(360deg); } }

/* ---- Insight callout (left-border note used on several screens) --------- */
.callout{ border-left:3px solid var(--primary); background:var(--muted); border-radius:var(--radius-sm); padding:10px 12px; margin-bottom:8px; }
.callout.success{ border-left-color:var(--success); }
.callout.warning{ border-left-color:var(--warning); }
.callout.destructive{ border-left-color:var(--destructive); }
.callout.info{ border-left-color:var(--info); }
.callout .t{ font-size:var(--fs-sm); font-weight:500; margin-bottom:2px; }
.callout .b{ font-size:var(--fs-xs); color:var(--muted-foreground); line-height:1.5; }

/* ---- KPI ---------------------------------------------------------------- */
.kpi .lbl{ font-size:var(--fs-xs); color:var(--muted-foreground); margin-bottom:6px; }
.kpi .val{ font-size:var(--fs-2xl); font-weight:600; line-height:1.1; }
.kpi .dlt{ display:inline-flex; align-items:center; gap:4px; font-size:11px; margin-top:4px; }
.kpi .dlt.up{ color:var(--success); }
.kpi .dlt.down{ color:var(--destructive); }

/* ============================================================================
   Compatibility layer
   ----------------------------------------------------------------------------
   Class hooks the existing page scripts toggle directly (setPill rewrites
   className to "pill ok|bad|busy"; segmented buttons and several actions toggle
   ".on"; the sync spinner toggles ".spin show"). Defining them here, against the
   shared tokens, lets those scripts keep working unchanged while the markup moves
   onto the new primitives. They are visually the Badge / primary-Button / Spinner
   looks under their legacy names.
   ============================================================================ */
.pill{
  display:inline-flex; align-items:center; gap:5px; white-space:nowrap;
  font-size:11px; font-weight:500; padding:2px 9px; border-radius:var(--radius-pill);
  border:1px solid var(--border); background:var(--muted); color:var(--muted-foreground);
}
.pill.ok{ background:var(--bg-success); color:var(--success); border-color:var(--bd-success); }
.pill.bad{ background:var(--bg-destructive); color:var(--destructive); border-color:var(--bd-destructive); }
.pill.busy{ background:var(--bg-primary); color:var(--primary); border-color:var(--bd-primary); }

/* a .btn (or bare button) gains the primary fill when script adds .on */
.btn.on, button.on{ background:var(--primary); color:var(--primary-foreground); border-color:var(--primary); }

.spin{
  width:15px; height:15px; border:2px solid var(--border); border-top-color:var(--primary);
  border-radius:50%; display:none; animation:spin .8s linear infinite;
}
.spin.show{ display:inline-block; }

/* ============================================================================
   Global filter bar  (UI.filter.mount)  —  range + source + custom window
   Shared by the dashboard, cadence and health pages so the look is identical
   wherever it is mounted. Active buttons reuse the .on primary-fill rule above.
   ============================================================================ */
.filter-bar{
  display:flex; flex-wrap:wrap; align-items:center; justify-content:space-between;
  gap:10px 16px; margin:12px 0 16px; padding:10px 12px;
  background:var(--card); border:1px solid var(--border); border-radius:var(--radius-lg);
}
.filter-group{ display:flex; align-items:center; gap:6px; flex-wrap:wrap; }
.filter-label{ color:var(--muted-foreground); font-size:var(--fs-xs); }
.filter-bar .fbtn{
  border:1px solid var(--border); background:var(--card); color:var(--foreground);
  border-radius:var(--radius-md); padding:5px 10px; font-size:var(--fs-xs); cursor:pointer;
}
.filter-bar .fbtn:hover{ border-color:var(--ring); }
.filter-bar .fbtn.on{ background:var(--primary); color:var(--primary-foreground); border-color:var(--primary); }
.filter-dates{ display:inline-flex; gap:6px; align-items:center; }
.filter-dates[hidden]{ display:none; }
.filter-arrow{ color:var(--muted-foreground); font-size:var(--fs-xs); }
.filter-dates input[type=date]{
  background:var(--muted); border:1px solid var(--border); color:var(--foreground);
  border-radius:var(--radius-md); padding:4px 8px; font-size:var(--fs-xs); color-scheme:dark;
}
@media (max-width:639px){
  .filter-bar{ flex-direction:column; align-items:stretch; gap:8px; }
  .filter-group{ flex-wrap:nowrap; overflow-x:auto; -ms-overflow-style:none; scrollbar-width:none; }
  .filter-group::-webkit-scrollbar{ display:none; }
  .filter-bar .fbtn{ flex:none; }
}
/* #7(b): the Source row's 4 buttons collapse to one compact <select> on
   mobile -- Merged covers ~95% of use, the per-source buttons are a
   debugging tool not worth a whole extra scrollable row on a phone. Desktop
   keeps the button row and hides the select. */
.filter-source-select{ display:none; }
@media (max-width:639px){
  [data-filter-sources] .fbtn{ display:none; }
  [data-filter-sources] .filter-label{ display:none; }
  .filter-source-select{ display:inline-flex; width:auto; flex:none; }
}

/* ============================================================================
   Bottom navigation  (UI.bottomNav.mount)  —  mobile only
   ----------------------------------------------------------------------------
   A fixed tab bar for quick movement between the primary pages (dashboard,
   cadence, health). Appended to <body> by ui.js and shown only below the mobile
   breakpoint (matches the 639px cutoff used elsewhere). The active item is set
   from location.pathname via aria-current="page". mount() also adds
   .has-bottom-nav to <body> so page content clears the fixed bar on mobile.
   ============================================================================ */
.bottom-nav{
  display:none;                       /* desktop: hidden; shown in the query below */
  position:fixed; left:0; right:0; bottom:0; z-index:60;
  background:var(--card); border-top:1px solid var(--border);
  padding:6px 8px calc(6px + env(safe-area-inset-bottom, 0px));
  gap:4px;
}
.bnav-item{
  flex:1 1 0; min-width:0; min-height:44px;                 /* 44px tap target */
  display:flex; flex-direction:column; align-items:center; justify-content:center; gap:3px;
  text-decoration:none; color:var(--muted-foreground);
  font-size:11px; font-weight:500; padding:4px 2px; border-radius:var(--radius-md);
}
.bnav-item:hover{ color:var(--foreground); }
.bnav-item[aria-current="page"]{ color:var(--primary); }
.bnav-icon{ width:22px; height:22px; flex:none; }
.bnav-label{ line-height:1; }

@media (max-width:639px){
  .bottom-nav{ display:flex; }
  body.has-bottom-nav{ padding-bottom:calc(68px + env(safe-area-inset-bottom, 0px)); }
}

/* ============================================================================
   Mobile & touch ergonomics (mobile-ux-audit batches 1–2)
   ----------------------------------------------------------------------------
   Applied at the phone breakpoint AND on any coarse-pointer device, so touch
   tablets wider than 640px get proper targets too.
   1. 44px minimum tap targets on the small controls (Apple HIG / Material).
      Page-local classes (.fbtn, .seg button, .dots, .lw-arrows) are included
      here so every page inherits the floor without per-page overrides; later
      page styles may still win padding ties, min-height keeps the target size.
   2. 16px inputs: mobile Safari auto-zooms any focused input under 16px.
      Fixed here rather than via maximum-scale, which would break pinch-zoom.
   3. :active press feedback: all interactive states above are hover-based,
      which touch browsers ignore or apply stickily. Filled (.btn-primary/.on)
      controls darken instead of flipping to muted.
   ============================================================================ */
@media (max-width:639px), (pointer:coarse){
  .btn, .fbtn, .toggle, .tab, .seg button, .menu-item{
    min-height:44px; padding-top:10px; padding-bottom:10px;
  }
  .btn-icon{ min-width:44px; }
  .dots{ min-width:44px; min-height:44px; }
  .lw-arrows button{ min-width:38px; min-height:38px; }
  .input, .select, .filter-dates input[type=date]{ font-size:16px; }
}
@media (pointer:coarse){
  .btn:active, .fbtn:active, .toggle:active, .tab:active,
  .menu-item:active, .dots:active{ background:var(--muted); }
  .btn-primary:active, .btn.on:active, button.on:active,
  .fbtn.on:active, .toggle[aria-pressed="true"]:active,
  .toggle[aria-checked="true"]:active{
    background:var(--primary); filter:brightness(.88);
  }
  #log tbody tr:active{ background:var(--muted); }
}

/* ============================================================================
   Mobile ergonomics batch 5 (mobile-ux-audit #12, #13)
   ============================================================================ */
/* #12: the layout editor's drag handle is dead weight on touch (HTML5 DnD is
   mouse-only on iOS/Android); the up/down arrows are the working fallback, so
   just stop advertising an interaction that can't happen. */
@media (pointer:coarse){ .lw-handle{ display:none; } }

/* #13: dialogs read as boxes centered on the page by default; anchor them to
   the bottom of the viewport on mobile instead, the more "app-like" native
   pattern and easier to reach one-handed. Covers the Plan page's note dialog
   (class="dialog"); index.html's #modal/#layoutModal use a bare <dialog> with
   their own local rule and get the matching treatment there. */
@media (max-width:639px){
  dialog.dialog{
    margin:auto auto 0; width:100%; max-width:100%;
    border-radius:16px 16px 0 0; border-bottom:none;
    max-height:85dvh;
  }
}
