/* theme.css - Terminal Ledger component styles
   Source: docs/design/catalog.md (header, buttons, inputs, cards, chips
   sections) and the component-states table in
   design/dps-visual-system.dc.html:986-1017. Built on tokens.css custom
   properties - see public/css/tokens.css. */

/* ---- base / reset ---- */
html {
  box-sizing: border-box;
}
*, *::before, *::after {
  box-sizing: inherit;
}
body {
  background: var(--s0);
  color: var(--t1);
  font-family: var(--font-sans);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
input, button, select, textarea {
  font: inherit;
}
::placeholder {
  color: var(--t4);
}

/* ---- header: 64px hairline bar (design:61-68) ---- */
.tl-header {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: var(--h-header);
  padding: 0 var(--sp-6);
  background: var(--s0);
  border-bottom: 1px solid var(--hairline);
}
.tl-wordmark {
  font-family: var(--font-mono);
  font-weight: 600;
  font-size: 16px;
  letter-spacing: -0.01em;
  color: var(--t1);
  text-decoration: none;
}
.tl-wordmark .tl-slash {
  color: var(--t3);
}
.tl-nav {
  display: flex;
  align-items: center;
  gap: 28px;
}
.tl-nav a {
  font-family: var(--font-sans);
  font-size: 14px;
  font-weight: 500;
  color: var(--t2);
  text-decoration: none;
  transition: color var(--dur-fast) var(--ease);
}
.tl-nav a:hover {
  color: var(--t1);
}
.tl-nav a.active {
  color: var(--t1);
  border-bottom: 1px solid var(--t1);
  padding-bottom: 2px;
}
@media (max-width: 768px) {
  .tl-header {
    height: var(--h-header-mobile);
    padding: 0 var(--sp-4);
  }
  .tl-wordmark { font-size: 15px; }
}

/* ---- buttons (states table design:986-1017) ---- */
.btn-primary,
.btn-ghost,
.btn-google {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  height: var(--h-cta);
  padding: 0 28px;
  border-radius: var(--r);
  font-family: var(--font-sans);
  font-size: 15px;
  font-weight: 600;
  border: none;
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease),
              color var(--dur-fast) var(--ease);
  text-decoration: none;
}

/* Overnight wave 1 (2026-08-31): the two declarations below are the site's
   lime-CTA-contrast guarantee -- ink-on-accent text on an accent (lime)
   background, everywhere .btn-primary is used. Confirmed root-caused on
   premium.html: its own page-scoped ".tl-premium-section a { color:
   var(--t1) }" rule (class+element, specificity 0,1,1) legitimately
   outranks the plain-class ".btn-primary" (0,1,0) selector below, so the
   pricing CTA rendered near-white text on lime -- a real contrast/
   compliance violation, not a false alarm. !important on both the base
   rule and the [data-state="running"] ghost variant below keeps that
   variant's own color/background winning over the base (normal specificity
   still governs within the !important tier), while making the pair immune
   to any future page-level anchor-color rule doing the same thing again. */
.btn-primary {
  background: var(--accent) !important;
  color: var(--ink-on-accent) !important;
}
.btn-primary:hover {
  background: var(--accent-hover) !important;
}
.btn-primary:disabled,
.btn-primary[aria-disabled="true"] {
  opacity: var(--disabled-opacity);
  pointer-events: none;
}

.btn-ghost {
  background: transparent;
  color: var(--t1);
  border: 1px solid var(--ghost-border);
  font-weight: 500;
  padding: 0 24px;
}
.btn-ghost:hover {
  background: var(--s2);
}
.btn-ghost:disabled,
.btn-ghost[aria-disabled="true"] {
  opacity: var(--disabled-opacity);
  pointer-events: none;
}

/* "Continue with Google" nav button (design:66, catalog.md Artboard 01 §1) */
.btn-google {
  height: var(--h-nav);
  padding: 0 16px;
  background: var(--s2);
  color: var(--t1);
  border: 1px solid var(--hairline-strong);
  font-size: 13px;
  font-weight: 500;
  gap: 10px;
}
.btn-google:hover {
  background: var(--s3);
}
.btn-google .tl-g-glyph {
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: 13px;
}
@media (max-width: 768px) {
  .btn-google {
    padding: 0 12px;
    gap: 8px;
    font-size: 12px;
  }
}

.btn-stepper {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--h-control);
  height: var(--h-control);
  background: var(--s2);
  border: 1px solid var(--hairline-strong);
  border-radius: var(--r);
  font-family: var(--font-mono);
  font-size: 18px;
  color: var(--t1);
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease);
}
.btn-stepper:hover {
  background: var(--s3);
}
.btn-stepper:disabled {
  opacity: var(--disabled-opacity);
  pointer-events: none;
}
@media (max-width: 768px) {
  .btn-stepper { width: 48px; height: 48px; font-size: 20px; }
}

button:focus-visible,
a:focus-visible,
input:focus-visible,
[tabindex]:focus-visible {
  outline: var(--focus-ring);
  outline-offset: 2px;
}

/* ---- cards (catalog.md Artboard 01 §3-4, hover design:986-1017 analog) ---- */
.tl-card {
  background: var(--s1);
  border: 1px solid var(--hairline);
  border-radius: var(--r);
  padding: var(--sp-5);
  transition: background var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease);
}
a.tl-card:hover,
.tl-card.is-hoverable:hover {
  background: var(--s15);
  border-color: var(--hairline-strong);
}
.tl-card-caption {
  font-family: var(--font-mono);
  font-size: var(--text-caption);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--t3);
}

/* ---- inputs (states table design:1011-1015) ---- */
.tl-input,
.tl-input-group {
  height: var(--h-control);
  background: var(--s0);
  border: 1px solid var(--hairline-strong);
  border-radius: var(--r);
  color: var(--t1);
  font-family: var(--font-mono);
  font-size: 16px;
  font-variant-numeric: tabular-nums slashed-zero;
}
.tl-input {
  width: 100%;
  padding: 0 12px;
  text-align: center;
}
.tl-input:hover {
  border-color: rgba(255,255,255,0.28);
}
.tl-input:disabled {
  background: var(--s1);
  border-color: var(--hairline);
  color: var(--t4);
}
/* Chrome UX audit fix (2026-08-30): suppress the native up/down spinner on
   the Attendees stepper input. The custom +/- buttons are the intended
   controls; the browser's own spinner is redundant and, in Chrome, visually
   collides with them. */
#attendees {
  -moz-appearance: textfield;
  appearance: textfield;
}
#attendees::-webkit-outer-spin-button,
#attendees::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
.tl-input-group {
  display: flex;
  align-items: center;
  padding: 0 12px;
  gap: 4px;
}
.tl-input-group .tl-input-prefix {
  color: var(--t3);
  font-family: var(--font-mono);
  font-size: 16px;
}
.tl-input-group input {
  flex: 1;
  min-width: 0;
  background: transparent;
  border: none;
  color: var(--t1);
  font-family: var(--font-mono);
  font-size: 16px;
  text-align: right;
  font-variant-numeric: tabular-nums slashed-zero;
}
.tl-input-group input:focus {
  outline: none;
}
@media (max-width: 768px) {
  .tl-input, .tl-input-group { height: var(--h-control-mobile); }
}

/* ---- chips (catalog.md Artboard 01 §2) ---- */
.tl-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  background: var(--s15);
  border: 1px solid var(--hairline);
  border-radius: var(--r);
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--t2);
  font-variant-numeric: tabular-nums slashed-zero;
}
.tl-chip--burn {
  background: transparent;
  border-color: var(--burn-border);
  color: var(--burn);
  font-size: 11px;
  letter-spacing: 0.1em;
  padding: 3px 8px;
}

/* ---- utility (compatibility with index.html's existing .hidden) ---- */
.hidden { display: none !important; }

/* ==========================================================================
   Task 3 - header/footer rebuild + auth modal reorder
   ========================================================================== */

/* ---- header: nav-scoped ghost link + account cluster ---- */
.tl-header {
  flex-wrap: nowrap;
}
.tl-nav {
  flex-wrap: nowrap;
}
.tl-nav-ghost {
  height: var(--h-nav);
  padding: 0 16px;
  font-size: 13px;
  font-weight: 500;
  white-space: nowrap;
}
.tl-nav-ghost-short { display: none; }

/* "Continue with Google" nav button: responsive label swap
   (catalog.md Artboard 01 §1 - mobile shortens to just "Continue").
   The nav packs 5 items (wordmark, 2 links, ghost upgrade link, Google
   button) - more than the design mock's 3, so the compact treatment
   starts at 900px, well above the page's 768px mobile breakpoint, to
   avoid the header wrapping/overflowing its fixed height in between. */
.btn-google .tl-g-label-short { display: none; }
@media (max-width: 900px) {
  .tl-nav { gap: 14px; }
  .tl-nav-ghost { padding: 0 12px; }
  .btn-google .tl-g-label-full { display: none; }
  .btn-google .tl-g-label-short { display: inline; }
}
/* Compact mobile nav (UX audit fix, 2026-08-30): the two rules this
   replaced hid Calculator, Cost of Your Time, and the Upgrade link
   entirely at <=640px, making Cost of Your Time unreachable from a phone
   (the "dead link" reasoning that justified hiding it no longer applies
   now that /cost-of-your-time/ is a real page). All three links stay in
   the markup and get smaller type and tighter gaps. Five nav items
   (2 links + ghost CTA + Google button/account cluster) plus the
   wordmark do not all fit unclipped at 640px down to ~360px phone
   widths even at the smallest legible sizes, so .tl-nav scrolls
   horizontally as a safety net instead of wrapping to a second line
   (which would blow out the fixed-height header) or clipping content
   outright. Calculator and Cost of Your Time land first in source order
   so they render on-screen without scrolling on real devices; the
   wordmark stays pinned outside the scroller. Anchors get inline-flex +
   min-height so the >=24px tap-target floor holds even though the
   visual line-height shrank. */
@media (max-width: 640px) {
  .tl-header { padding: 0 var(--sp-3); }
  .tl-wordmark { font-size: 12px; flex-shrink: 0; }
  .tl-nav {
    gap: 10px;
    flex-wrap: nowrap;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .tl-nav::-webkit-scrollbar { display: none; }
  .tl-nav a:not(.tl-nav-ghost) {
    font-size: 12px;
    white-space: nowrap;
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    min-height: 24px;
  }
  .tl-nav-ghost {
    height: 28px;
    padding: 0 8px;
    font-size: 11px;
    flex-shrink: 0;
    white-space: nowrap;
  }
  .tl-nav-ghost-full { display: none; }
  .tl-nav-ghost-short { display: inline; }
  .btn-google { padding: 0 8px; gap: 4px; flex-shrink: 0; }
  .tl-account { flex-shrink: 0; white-space: nowrap; }
}
@media (max-width: 480px) {
  /* Narrowest phones: the Google sign-in button drops its text label and
     shows only the "G" glyph so it takes less of the scroller's width;
     the glyph button is still var(--h-nav) = 36px tall, well over the
     24px floor. */
  .btn-google .tl-g-label-full,
  .btn-google .tl-g-label-short { display: none; }
  .btn-google { padding: 0 8px; gap: 0; }
}

.tl-account {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  margin-left: var(--sp-2);
}
.tl-account-email {
  font-family: var(--font-sans);
  font-size: 13px;
  color: var(--t2);
}
.tl-account-badge {
  background: var(--s2);
  color: var(--t1);
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  padding: 3px 8px;
  border: 1px solid var(--hairline-strong);
  border-radius: var(--r);
  cursor: default;
}
.tl-account-badge[role="button"] {
  cursor: pointer;
}
/* View-as-free toggle (overnight wave 1, 2026-08-31): a premium user's
   badge while they're previewing the free experience. Ghost treatment
   (transparent + dashed hairline) instead of the solid "Premium" chip, so
   it reads as a temporary, reversible state rather than the real one. */
.tl-account-badge--ghost {
  background: transparent;
  color: var(--t2);
  border: 1px dashed var(--ghost-border);
}
.tl-account-badge--ghost:hover {
  background: var(--s2);
}
.tl-signout {
  background: none;
  border: none;
  color: var(--t3);
  font-family: var(--font-sans);
  font-size: 12px;
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 3px;
  display: inline-flex;
  align-items: center;
  min-height: 24px;
  padding: 4px 0;
}
.tl-signout:hover {
  color: var(--t1);
}

@media (max-width: 768px) {
  .tl-nav { gap: 14px; }
  .tl-account-email { display: none; } /* keep badge + sign-out, save mobile width */
}

/* ---- footer: one-line ledger (catalog.md Artboard 01 §7) ---- */
.tl-footer {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-2) var(--sp-5);
  padding: var(--sp-4) var(--sp-6);
  border-top: 1px solid var(--hairline);
  font-family: var(--font-mono);
  font-size: var(--text-meta);
  color: var(--t3);
}
.tl-footer a {
  color: var(--t3);
  text-decoration: none;
}
.tl-footer a:hover {
  color: var(--t1);
}
.tl-footer-links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-4);
}
.tl-footer-tagline {
  color: var(--t3); /* not the design's raw 0.5 - folded up to the --t3 tier */
}
@media (max-width: 768px) {
  .tl-footer {
    justify-content: center;
    text-align: center;
    padding: var(--sp-4);
  }
}

/* ---- premium banner: REMOVED (owner ruling, 2026-08-30). The free daily
   limit is dead, so premium.js#showPremiumBanner and the #premium-banner it
   used to inject are gone. This also retires the banner-overlaps-title bug
   that offset was patching around. ---- */

/* ---- endowment card (owner ruling, 2026-08-30): the one-time sales moment
   after a visitor's 5th completed meeting (index.html #dps-endowment-card,
   populated by premium.js#maybeShowEndowmentCard). Inline in the premium
   cluster, not a modal: no scrim, no fixed position. Shares .tl-card for
   the shell and .tl-card-caption for the eyebrow. No entrance animation by
   design, so there is nothing to gate behind prefers-reduced-motion. ---- */
.tl-endowment-card {
  margin: var(--sp-4) 0;
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
}
.tl-endowment-body {
  margin: 0;
  font-family: var(--font-sans);
  font-size: 15px;
  line-height: var(--leading-ui);
  color: var(--t2);
}
.tl-endowment-figure {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  color: var(--t1);
}
.tl-endowment-actions {
  display: flex;
  gap: var(--sp-3);
  flex-wrap: wrap;
  padding-top: var(--sp-1);
}
@media (max-width: 480px) {
  .tl-endowment-actions .btn-primary,
  .tl-endowment-actions .btn-ghost {
    flex: 1 1 100%;
  }
}

/* ---- modal shell (Terminal Ledger). Built originally for the Google/
   email sign-in modal (removed, Phase 5a Task 7: Google sign-in runs via
   signInWithPopup and needs no modal); the shell lives on here for the
   daily-limit upsell modal below. ---- */
.tl-modal {
  position: fixed;
  inset: 0;
  z-index: 10000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--sp-4);
  background: var(--scrim);
  backdrop-filter: blur(3px);
}
.tl-modal-panel {
  width: 100%;
  max-width: 420px;
  background: var(--s15);
  border: 1px solid var(--hairline-strong);
  box-shadow: var(--shadow-modal);
  padding: var(--sp-6);
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
}
.tl-modal-title {
  margin: 0;
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: var(--text-h3);
  color: var(--t1);
}

/* ---- daily-limit upsell modal (js/premium.js showUpsellModal - Terminal
   Ledger restyle, catalog.md Artboard 04 §1 / design:743-754). Shares the
   .tl-modal scrim + .tl-modal-panel shell with the auth modal above. ---- */
.tl-upsell-panel {
  max-width: 480px;
  gap: var(--sp-4);
}
.tl-upsell-kicker {
  font-family: var(--font-mono);
  font-size: var(--text-caption);
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  color: var(--burn);
}
.tl-upsell-body {
  margin: 0;
  font-family: var(--font-sans);
  font-size: 15px;
  line-height: var(--leading-ui);
  color: var(--t2);
}
.tl-upsell-figure {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}
.tl-upsell-figure--burn {
  color: var(--burn);
}
.tl-upsell-actions {
  display: flex;
  gap: var(--sp-3);
  padding-top: var(--sp-2);
}
.tl-upsell-actions .btn-primary {
  flex: 1;
}
.tl-upsell-fine {
  font-family: var(--font-mono);
  font-size: var(--text-caption);
  color: var(--t3);
}

/* ==========================================================================
   Task 4 - hero odometer meter (catalog.md Artboard 01 §2)
   ========================================================================== */

/* ---- visually-hidden clip technique (shared by #totalCost / #meterSrSummary) ---- */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
}

/* ---- hero meter container (#costDisplay) - always visible, no .active gate;
   controller ruling overrides the old hidden-until-.active behavior. ---- */
.tl-meter {
  background: var(--s0);
  border: 1px solid var(--hairline);
  border-radius: var(--r);
  padding: var(--sp-6) var(--sp-5);
  margin: var(--sp-6) 0;
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
}

/* ---- status row: LIVE/PAUSED pip + caption + elapsed chip + burn badge ---- */
.tl-meter-status {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
}
.tl-meter-badge {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-mono);
  font-size: var(--text-caption);
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  color: var(--t3);
}
.tl-meter-pip {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--t3);
  display: inline-block;
}
.tl-meter-badge.is-live {
  color: var(--t2); /* lime is reserved for the pip + ticker value + CTA + focus -
                        the LIVE text itself stays gray per accent discipline */
}
.tl-meter-badge.is-live .tl-meter-pip {
  background: var(--accent);
  animation: var(--pulse);
}
.tl-meter-caption {
  font-family: var(--font-mono);
  font-size: var(--text-caption);
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  color: var(--t3);
}

/* ---- odometer digits (design:82-87 · brief Task 4 Step 4, verbatim) ---- */
#meterDigits { display: inline-flex; font-family: 'Geist Mono', ui-monospace, monospace;
  font-weight: 600; font-size: var(--ticker-size); line-height: 1; color: var(--accent);
  letter-spacing: -0.02em; font-variant-numeric: tabular-nums slashed-zero; }
.meter-cell { display: inline-block; height: 1em; overflow: hidden; }
.meter-strip { display: inline-flex; flex-direction: column; will-change: transform; }
.meter-row { display: block; height: 1em; line-height: 1; }
.meter-sep { display: inline-block; }
#meterSrSummary { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }

/* ---- rate line (#tickerContext) - "{n} people @ $X/year" ---- */
.tl-meter-rate {
  font-family: var(--font-mono);
  font-size: 15px;
  color: var(--t2);
  font-variant-numeric: tabular-nums slashed-zero;
}

/* ---- controls: Start/Stop + meter reset ---- */
.tl-meter-controls {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding-top: var(--sp-2);
}
.btn-primary[data-state="running"] {
  background: transparent !important;
  color: var(--t1) !important;
  border: 1px solid var(--ghost-border);
}
.btn-primary[data-state="running"]:hover {
  background: var(--s2) !important;
}
.btn-primary[data-state="running"]::before {
  content: '';
  display: inline-block;
  width: 10px;
  height: 10px;
  background: var(--burn);
}
.tl-meter-reset {
  background: none;
  border: none;
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--t3);
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  min-height: 24px;
  padding: 4px 0;
}
.tl-meter-reset:hover {
  color: var(--t1);
}
.tl-meter-upgrade {
  padding-top: var(--sp-1);
  text-align: center;
}

@media (max-width: 768px) {
  .tl-meter { padding: var(--sp-5) var(--sp-4); }
}

/* ==========================================================================
   Task 5 - "PRICE THIS MEETING" inputs card with steppers
   (catalog.md Artboard 01 §3)
   ========================================================================== */
.tl-inputs-card {
  display: flex;
  flex-direction: column;
  gap: var(--sp-5);
  margin: var(--sp-6) 0;
}
.tl-field-group {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
}
.tl-field-label {
  font-family: var(--font-sans);
  font-size: 14px;
  font-weight: 500;
  color: var(--t2);
}
.tl-stepper-row {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
}
.tl-stepper-row .tl-input,
.tl-stepper-row .tl-input-group {
  flex: 1;
}
.tl-field-footnote {
  font-family: var(--font-mono);
  font-size: 12px;
  line-height: 1.6;
  color: var(--t3);
}

/* ==========================================================================
   Task 6 - comparison ladder "WHAT YOU'VE BURNED" (catalog.md Artboard 01 §4)
   ========================================================================== */
#burnLadderCard {
  margin: 0 0 var(--sp-6);
}
.tl-ladder-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-3);
  margin-bottom: var(--sp-3);
}
.tl-ladder-quip {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--t3); /* 0.62 lightness tier - compliance fix, not the raw 0.55 value */
}
.ladder-row {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: var(--sp-2) var(--sp-3);
  padding: 14px 0;
  border-top: 1px solid var(--hairline);
}
.ladder-row:first-child {
  border-top: none;
}
.ladder-label {
  grid-column: 1;
  font-family: var(--font-sans);
  font-size: 15px;
  color: var(--t2);
}
.ladder-count {
  grid-column: 2;
  font-family: var(--font-mono);
  font-weight: 600;
  font-size: 22px;
  color: var(--t1);
  text-align: right;
  font-variant-numeric: tabular-nums slashed-zero;
}
.ladder-track {
  grid-column: 1 / -1;
  display: block;
  height: 2px;
  background: var(--hairline);
}
.ladder-fill {
  display: block;
  height: 100%;
  width: 0%;
  background: rgba(255, 255, 255, 0.32);
  transition: width var(--dur-roll) var(--ease);
}

/* ==========================================================================
   Task 7 - stat tiles: fact cards + lifetime stats (catalog.md Artboard 01 §5)
   ========================================================================== */
.tl-stats-section {
  margin: 0 0 var(--sp-6);
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
}
.tl-stats-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--sp-3);
}
.tl-stats-grid--lifetime {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
.tl-stat-tile {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  padding: var(--sp-4); /* 16px - tighter than the 24px .tl-card default (design:84) */
}
/* 5 fact tiles in a 3-col grid leave a dangling cell in row 2 (3+2) - span the
   odd tile out across the remainder of its row so no empty cell shows the
   page's white .app-card background through the grid gap. */
.tl-stats-grid .tl-stat-tile--last {
  grid-column: span 2;
}
.tl-stat-value,
.tl-stat-value-row .tl-stat-value {
  font-family: var(--font-mono);
  font-weight: 600;
  font-size: var(--text-stat);
  line-height: 1.2;
  color: var(--t1);
  font-variant-numeric: tabular-nums slashed-zero;
}
.tl-stat-value-row {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
}
.tl-stat-reset {
  background: none;
  border: none;
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--t3);
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
  padding: 4px 6px;
  min-height: 24px;
  display: inline-flex;
  align-items: center;
}
.tl-stat-reset:hover {
  color: var(--t1);
}
@media (max-width: 768px) {
  .tl-stats-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--sp-2);
  }
  .tl-stats-grid .tl-stat-tile--last {
    grid-column: 1 / -1;
  }
  .tl-stat-tile {
    padding: 12px;
  }
  .tl-stat-tile .tl-card-caption {
    font-size: 10px;
  }
  .tl-stat-value,
  .tl-stat-value-row .tl-stat-value {
    font-size: 18px;
  }
}

/* ==========================================================================
   Task 9 - mobile bottom dock (catalog.md Artboard 01 §8) + responsive polish
   ========================================================================== */
:root {
  /* Derived from the dock's own box model (1px top border + 12px top pad +
     48px button + 20px base bottom pad, catalog.md Artboard 01 §8) plus the
     iOS home-indicator inset, so the body's reserved bottom padding always
     matches the dock's real rendered height exactly instead of the design's
     rounded "~76px" note. */
  --dock-height: calc(1px + 12px + 48px + 20px + env(safe-area-inset-bottom, 0px));
}

.tl-dock {
  display: none; /* desktop: no dock, no reserved space */
}

@media (max-width: 768px) {
  /* The fixed dock sits above everything at the true bottom of the page.
     The SEO content-wrapper (FAQ etc.) renders after .app-card, so a
     spacer inside the card can't reserve room for it; reserve on body
     instead so the last on-page content always clears the dock. The extra
     16px (Chrome UX audit, 2026-08-30) is breathing room beyond the dock's
     own height so the footer's last line doesn't just barely clear it. */
  body {
    padding-bottom: calc(var(--dock-height) + 16px);
  }
  .tl-dock {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-3);
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 50;
    min-height: var(--dock-height);
    padding: 12px 16px calc(20px + env(safe-area-inset-bottom, 0px));
    background: var(--dock-bg);
    -webkit-backdrop-filter: blur(12px);
    backdrop-filter: blur(12px);
    border-top: 1px solid var(--hairline-frame);
  }
  .tl-dock-info {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
  }
  .tl-dock-caption {
    font-family: var(--font-mono);
    font-size: 10px;
    letter-spacing: var(--tracking-caps);
    text-transform: uppercase;
    color: var(--t3);
  }
  .tl-dock-rate {
    font-family: var(--font-mono);
    font-weight: 600;
    font-size: 16px;
    color: var(--t1);
    font-variant-numeric: tabular-nums slashed-zero;
    white-space: nowrap;
  }
  .tl-dock-btn {
    flex: 1;
    max-width: 220px;
    height: 48px;
    border-radius: var(--r);
    font-family: var(--font-sans);
    font-size: 15px;
    font-weight: 600;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    background: var(--accent) !important;
    color: var(--on-accent) !important;
    border: none;
    transition: background var(--dur-fast) var(--ease);
  }
  .tl-dock-btn:hover {
    background: var(--accent-hover) !important;
  }
  .tl-dock-btn[data-state="running"] {
    background: transparent !important;
    color: var(--t1) !important;
    border: 1px solid var(--ghost-border);
  }
  .tl-dock-btn[data-state="running"]:hover {
    background: var(--s2) !important;
  }
  .tl-dock-btn[data-state="running"]::before {
    content: '';
    display: inline-block;
    width: 10px;
    height: 10px;
    background: var(--burn);
  }

  /* Compliance fix: the mobile meter-reset text button (#meterReset) was a
     bare underlined text link with no real hit area - bump it to the 44px
     mobile touch-target floor without changing its desktop treatment. */
  .tl-meter-reset {
    min-height: 44px;
    display: inline-flex;
    align-items: center;
  }
}

/* ==========================================================================
   Task 10 fix - Saved Presets rows (premium.js renderPresetList()). The
   rows were built from a JS template string with hardcoded light-theme
   inline styles (border #e5e7eb, Apply #3b82f6, Edit #6b7280,
   Delete #ef4444, white text) that the .preset-item/.btn-apply-preset/
   .btn-delete-preset CSS never matched (buttons only ever carried
   data-act attributes). premium.js now emits these classes instead;
   data-act attributes and all click wiring are unchanged. Apply is a
   small primary-ghost (var(--s3)/var(--t1)) - not lime, since the
   preset apply action is not the meter's primary CTA. ========================================================================== */
.tl-preset-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-2) var(--sp-1);
  border-bottom: 1px solid var(--hairline);
}
.tl-preset-name {
  font-weight: 600;
  font-size: 15px;
  color: var(--t1);
}
.tl-preset-meta {
  font-size: 0.875rem;
  color: var(--t2);
}
.tl-preset-actions {
  display: flex;
  gap: var(--sp-2);
}
.tl-preset-btn {
  padding: 4px 8px;
  border-radius: var(--r);
  border: 1px solid transparent;
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
  background: transparent;
  color: var(--t1);
  transition: background var(--dur-fast) var(--ease);
}
.tl-preset-btn--apply {
  background: var(--s3);
  color: var(--t1);
}
.tl-preset-btn--apply:hover {
  background: var(--hairline-strong);
}
.tl-preset-btn--edit {
  border-color: var(--ghost-border);
  color: var(--t1);
}
.tl-preset-btn--edit:hover {
  background: var(--s2);
}
.tl-preset-btn--delete {
  border-color: var(--burn-border);
  color: var(--burn);
}
.tl-preset-btn--delete:hover {
  background: var(--s2);
}
/* ==========================================================================
   Premium Experience wave - locked presets teaser (free/signed-out
   visitors). The panel frame (#saved-presets-panel, .presets-header,
   .tl-preset-item) stays shared with the real premium panel; only the
   contents differ. No blur filters, opacity + non-interactive per the
   owner's brief. ========================================================== */
.tl-presets-locked .tl-preset-item--locked {
  opacity: var(--disabled-opacity);
  pointer-events: none;
}
.tl-presets-lock-chip {
  font-family: var(--font-mono);
}
.tl-presets-teaser-line {
  margin: var(--sp-3) 0 0;
  font-family: var(--font-sans);
  font-size: 14px;
  color: var(--t2);
}
.tl-presets-teaser-line a {
  color: var(--t1);
  text-decoration: underline;
  text-underline-offset: 3px;
}

.tl-preset-empty {
  padding: var(--sp-2);
  color: var(--t2);
}
