/* ════════════════════════════════════════════════════════════════
   Sets · motion.css
   CSS-kant van het bewegingssysteem. js/motion.js publiceert de
   tokens (--m-dur-*, --m-ease-*, --m-spring-*) op :root; hier staat
   alles wat daarop bouwt. Geen enkele regel in dit bestand — of in
   nieuwe componentstijlen — hardcodeert nog een duur of curve.

   Schakelaars, allemaal op <html>:
     .m-reduce — spiegelt de OS-voorkeur "verminder beweging"
                 (het globale kill-blok in polish.css doet het
                 zware werk; hier alleen wat daar buiten valt).
     .m-lite   — de app-instelling "Minder effecten": glans- en
                 gloeiloops, blur en sierlijke reveals uit, de
                 kernfeedback blijft staan.
   ════════════════════════════════════════════════════════════════ */

/* ── Hero-vlucht (lijst → detail) ─────────────────────────────── */

.m-hero-ghost {
  position: fixed;
  z-index: 90;
  margin: 0;
  pointer-events: none;
  transform-origin: top left;
  will-change: transform, opacity;
}

/* ── Rollende cijfers ─────────────────────────────────────────── */

.m-digit {
  display: inline-block;
  will-change: transform;
}

/* ── Rusttimer: aflopende baan ────────────────────────────────────
   Een dunne balk onderin de timerkaart die met de resterende tijd
   leegloopt. De voortgang is één WAAPI-animatie op transform:scaleX
   over de hele resterende duur — nul JS per frame. */

.rest-progress-track {
  position: absolute;
  left: 12px;
  right: 12px;
  bottom: 5px;
  height: 3px;
  border-radius: 2px;
  overflow: hidden;
  background: rgba(255, 255, 255, 0.07);
  pointer-events: none;
}

html:not(.dark) .rest-progress-track { background: rgba(0, 0, 0, 0.08); }

.rest-progress-fill {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: var(--sets-accent, #c6f432);
  transform-origin: left center;
  transform: scaleX(0);
  will-change: transform;
}

/* De laatste seconden kleuren mee met de bestaande urgent-status; de klasse
   staat op de balk zelf (de pulse-klasse op het cijfer mag hier niet ook
   schalen). */
.rest-bar-urgent .rest-progress-fill { background: #f59e0b; }

/* ── Nieuwe set-rij: klein binnenveren ────────────────────────────
   Alleen rijen die echt nieuw zijn krijgen dit (renderWorkout merkt
   ze met .set-row-enter); bestaande rijen staan gewoon stil. */

.set-row-enter {
  animation: m-row-in var(--m-dur-md, 260ms) var(--m-ease-pop, ease-out) both;
}

@keyframes m-row-in {
  from { opacity: 0; transform: translateY(7px) scale(0.985); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}

/* ── PR-badge: entree in plaats van pop-in ────────────────────────
   De badge stond altijd al in de DOM en verscheen hard zodra
   .pr-glow kwam. Nu veert hij binnen — goud verdient een entree. */

.pr-glow .pr-badge {
  animation: m-pr-badge-in var(--m-spring-pop-dur, 360ms) var(--m-spring-pop, var(--m-ease-pop)) both;
}

@keyframes m-pr-badge-in {
  from { opacity: 0; transform: translateY(4px) scale(0.85); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}

/* ── Laad-skeletten ───────────────────────────────────────────────
   Gevormde blokken op de plek van echte inhoud; een rustige
   opaciteitspuls, geen shimmer-verf. */

.m-skel {
  border-radius: 10px;
  background: rgba(255, 255, 255, 0.06);
  animation: m-skel-pulse 1100ms ease-in-out infinite alternate;
}

html:not(.dark) .m-skel { background: rgba(0, 0, 0, 0.07); }

.m-skel-circle { border-radius: 9999px; }

@keyframes m-skel-pulse {
  from { opacity: 0.55; }
  to   { opacity: 1; }
}

/* ── Lege grafiek: spookgrafiek achter een banner ─────────────────
   Rendert vanuit js/dashboard.js (renderChartEmptyState). De vage
   grafiek eronder laat de vórm van het antwoord zien; de banner zegt
   wat er nog moet gebeuren. Zodra er twee metingen zijn vervangt de
   echte grafiek deze laag. */

.chart-empty {
  position: relative;
  display: grid;
  place-items: center;
  min-height: 122px;
  padding: 10px 12px;
  border-radius: 14px;
  overflow: hidden;
  border: 1px solid rgba(255, 255, 255, 0.05);
  background: rgba(255, 255, 255, 0.015);
}

.chart-empty-ghost {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  padding: 0 6px;
  color: #71717a;
  opacity: 0.42;
  /* Statische blur: één keer gerasterd, nooit geanimeerd — dit kost de
     compositor niets per frame. */
  filter: blur(3.5px);
  pointer-events: none;
}

.chart-empty-ghost svg {
  width: 100%;
  height: auto;
}

.chart-empty-banner {
  position: relative;
  max-width: 250px;
  padding: 8px 16px;
  text-align: center;
  border-radius: 16px;
  /* Leesbaarheidsscrim: de spookgrafiek loopt eronder door, maar de tekst
     moet zonder moeite te lezen blijven. */
  background: radial-gradient(115% 118% at 50% 50%, var(--sets-surface-1, #0e0e11) 52%, transparent 100%);
  animation: m-chart-empty-in var(--m-dur-lg, 380ms) var(--m-ease-out, ease-out) both;
}

@keyframes m-chart-empty-in {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: translateY(0); }
}

.chart-empty-icon {
  display: block;
  width: 20px;
  height: 20px;
  margin: 0 auto 5px;
  color: var(--sets-accent-text);
  opacity: 0.75;
}

.chart-empty-icon svg { width: 100%; height: 100%; }

.chart-empty-title {
  font-size: 12.5px;
  font-weight: 650;
  color: #e4e4e7;
  line-height: 1.3;
}

.chart-empty-value {
  margin-top: 4px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 15px;
  font-weight: 700;
  color: var(--sets-accent-text);
  font-variant-numeric: tabular-nums;
}

.chart-empty-hint {
  margin-top: 3px;
  font-size: 10.5px;
  line-height: 1.4;
  color: #71717a;
}

html:not(.dark) .chart-empty {
  border-color: rgba(0, 0, 0, 0.06);
  background: rgba(0, 0, 0, 0.015);
}
html:not(.dark) .chart-empty-ghost { color: #a1a1aa; opacity: 0.5; }
html:not(.dark) .chart-empty-title { color: #27272a; }
html:not(.dark) .chart-empty-hint { color: var(--sets-text-2); }

/* "Minder effecten": geen blur meer, alleen een vlakke gedimde vorm.
   Onder prefers-reduced-motion rendert dashboard.js de spookgrafiek
   helemaal niet — dan blijft de kale banner over. */
html.m-lite .chart-empty-ghost {
  filter: none;
  opacity: 0.16;
}

/* ── Heatmap: warmte trekt over het silhouet ──────────────────────
   heatmap.js zet --heat eerst op 0 en direct daarna op de echte
   waarde; de vertraging per spier maakt er één golf van. */

.heat-reveal .heat-region-shape {
  transition-duration: var(--m-dur-xl, 560ms);
  transition-delay: var(--heat-delay, 0ms);
}

/* ── Streak in gevaar: de vlam sputtert ───────────────────────────
   Een streak die deze week nog niet veiliggesteld is, brandt
   zichtbaar onrustig. Kleine wreedheid, bewust verdiend gedrag. */

.home-streak-chip.is-at-risk .home-streak-flame {
  animation: m-flame-gutter 2400ms ease-in-out infinite;
  transform-origin: 50% 85%;
}

@keyframes m-flame-gutter {
  0%, 100% { opacity: 1; transform: scale(1); }
  38%      { opacity: 0.55; transform: scale(0.94) rotate(-2deg); }
  46%      { opacity: 0.9;  transform: scale(0.98); }
  62%      { opacity: 0.5;  transform: scale(0.92) rotate(2deg); }
  74%      { opacity: 0.85; transform: scale(0.97); }
}

/* ── Lange schermen: blokken komen omhoog als je ze bereikt ───────
   js/screen-reveal.js zet [data-reveal-block] alleen op blokken die hij daarna
   gegarandeerd weer onthult (observer + tijdklok als vangnet). Zonder die
   module staat er dus nooit een verborgen blok op het scherm. */

[data-reveal-block] {
  opacity: 0;
}

[data-reveal-block].is-revealed {
  /* De opacity staat er los van de animatie bij. Een blok dat op het moment
     van onthullen nog `display: none` is (de draft-banner, de weekrecap, een
     leeg krachtblok) draait geen animatie — dan zou alleen de keyframe hem
     nooit terugzetten en bleef hij onzichtbaar zodra hij wél inhoud kreeg.
     Tijdens de animatie wint de keyframe hiervan, dus de entree blijft. */
  opacity: 1;
  animation: m-block-reveal var(--m-dur-lg, 380ms) var(--m-ease-out, ease-out) both;
}

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

/* ── Minder effecten (html.m-lite) ────────────────────────────────
   Eén centrale plek die de zware laag uitzet. Componentcode weet
   hier niets van; dat is precies de bedoeling. */

html.m-lite .pr-glow,
html.m-lite .aura-bronze,
html.m-lite .aura-silver,
html.m-lite .aura-gold,
html.m-lite .ladder-row-me,
html.m-lite #btn-start-workout::after,
html.m-lite .celebrate-halo,
html.m-lite .toast-gold::after {
  animation: none !important;
}

html.m-lite .heat-reveal .heat-region-shape { transition-delay: 0ms; }

html.m-lite .screen-first-enter > * {
  animation-delay: 0ms;
}

/* ── OS-reduce (html.m-reduce) ────────────────────────────────────
   Het globale kill-blok in polish.css dooft vrijwel alles al via de
   media query; deze klasse bestaat zodat JS-gedreven beslissingen
   en CSS één waarheid delen. Wat hier staat is de rest. */

html.m-reduce .m-skel { animation: none; opacity: 0.75; }
/* Zet de OS-voorkeur om terwijl de app draait, dan staan de blokken die nog
   niet onthuld zijn al op onzichtbaar. Ze horen er gewoon te staan. */
html.m-reduce [data-reveal-block],
html.m-lite [data-reveal-block] { opacity: 1; animation: none; }
html.m-reduce .home-streak-chip.is-at-risk .home-streak-flame { animation: none; opacity: 0.7; }
