/* podium-motion.css: motion for theme "Podium" (owner request 22.10.2026, _variants/podium/motion_brief.md).
   Loaded after podium.css and podium-media.css. Self-drawn CSS/SVG motion only; photos never carry an animation.
   - Content first: every text, number and graphic is final in the HTML. Motion changes only opacity, transform
     (translate/scale), stroke-dasharray, clip-path and background-position. No count-ups.
   - First paint: what is on the first screen is painted in its place and animates from the first paint (pure CSS);
     it is never hidden afterwards. Only elements that start below the first screen get a hidden start (.m-wait, set
     by assets/js/motion.js before they are ever seen) and reveal when scrolled in (.is-in, --d = batch stagger).
     Without scripting, in print and with reduced motion everything is shown at once, in its final state.
   - The unit list of the reveal (:where(.hero__lead > * ... .section...)) is UNIT in motion.js; keep both equal.
   - One set of motion tokens for everything. */

:root {
  --ease-out: cubic-bezier(.2, .7, .2, 1);
  --ease-in-out: cubic-bezier(.65, 0, .35, 1);
  --dur-1: 180ms;   /* hover */
  --dur-2: 360ms;   /* reveal */
  --dur-3: 700ms;   /* hero, draw */
  --stagger: 45ms;
  --rise: 14px;
}

/* ================================================================ graphics drawn for the motion (all modes, final state) */
/* profile: score ring in the panel corner (value = the score, as a dash length) */
.scorepanel__ring { position: absolute; top: 1.15rem; right: 1.15rem; width: 3.25rem; height: 3.25rem; transform: rotate(-90deg); pointer-events: none; }
.scorepanel__ring-track { fill: none; stroke: #262930; stroke-width: 9; }
.scorepanel__ring-fill { fill: none; stroke: var(--tc); stroke-width: 9; stroke-linecap: round; }
.scorepanel--plain .scorepanel__ring-fill { stroke: #c4c9d1; }
/* final2 23.10.: a free-standing 52 px corner ring read as an empty circle (96 %) or a loading spinner (75 %); the
   big metallic numeral already carries the score, so the profile shows no ring (the home #1 card keeps its ring) */
.scorepanel__ring { display: none; }

/* home "Key findings": the item number sits in a ring that shows the finding's share; the median rates get a 0-100 % bar */
.findings__item { grid-template-columns: 2.75rem minmax(0, 1fr); column-gap: 0.9rem; }
.findings__item::before { grid-area: 1 / 1; justify-self: center; align-self: start; line-height: 2.75rem; position: relative; z-index: 1; }
.findings__link { grid-column: 2; grid-row: 1; align-self: start; padding-top: 0.45rem; }
.findings__ring { grid-area: 1 / 1; width: 2.75rem; height: 2.75rem; transform: rotate(-90deg); }
.findings__ring-track { fill: none; stroke: var(--line); stroke-width: 2.5; }
.findings__ring-fill { fill: none; stroke: var(--gold); stroke-width: 2.5; stroke-linecap: round; }
.findings__bar { grid-column: 2; grid-row: 2; display: block; width: 100%; max-width: 13rem; height: 5px; margin-top: 0.6rem; }
.findings__bar__track { fill: #272a31; }
.findings__bar__fill { fill: var(--gold); }

/* "How scoring works": four tokens per point value (12 in 3 groups), in place of the single dot */
.points__item:has(> .points__tokens)::before { display: none; }
.points__tokens { display: block; width: 4.5rem; height: 0.97rem; overflow: visible; }
.ptok__base { fill: none; stroke: var(--ring-none); stroke-width: 1.5; }
.ptok__fill { fill: var(--gold); }
.ptok__ring { fill: none; stroke: var(--gold); stroke-width: 1.5; }

/* /commission/: the published min-max on a 0-100 % track */
.ratebar { display: block; width: 100%; max-width: 6rem; height: 4px; margin-top: 0.6rem; }
.ratebar__track { fill: #272a31; }
.ratebar__fill { fill: var(--ink); }
@media (max-width: 47.99rem) { .commission-table--stack .ratebar { width: 5rem; margin-left: auto; } }

/* compare: a score bar under each score */
.vs .scorebar { width: 100%; max-width: none; height: 4px; margin: 0.8rem 0 0; }

/* key numbers: a thin gold line under each number */
.stats-strip .stat-tile__value { position: relative; margin-bottom: 0.55rem; }
.stats-strip .stat-tile__value::after {
  content: ""; position: absolute; left: 0; bottom: -0.45rem; width: 1.5rem; height: 2px; border-radius: 1px; background: var(--gold);
  opacity: 0.85; transform-origin: 0 50%;
}

/* ================================================================ hover and focus (all modes; base.css turns transitions off for reduced motion) */
/* home leaderboard cards: champagne edge glow, the rank numeral brightens (the 2 px lift is motion, see below) */
.rank-table--cards .rank-row::before {
  content: ""; position: absolute; inset: -1px; border-radius: inherit; pointer-events: none;
  box-shadow: 0 0 0 1px rgba(217, 191, 138, 0.42), 0 18px 40px -26px rgba(217, 191, 138, 0.55);
  opacity: 0; transition: opacity var(--dur-1) var(--ease-out);
}
.rank-table--cards .rank-row:hover::before, .rank-table--cards .rank-row:focus-within::before { opacity: 1; }
.rank-table--cards .rank-num { transition: filter var(--dur-1) var(--ease-out); }
.rank-table--cards .rank-row:hover .rank-num { filter: brightness(1.35); }

/* buttons: a soft champagne shimmer passes once on hover (a background band moved by background-position) */
.button:not(.button--ghost):not(.button--small), .leader__cta {
  background-image: linear-gradient(105deg, transparent 38%, rgba(255, 255, 255, 0.5) 50%, transparent 62%), linear-gradient(180deg, var(--gold-hi), var(--gold));
  background-size: 260% 100%, 100% 100%; background-position: 100% 0, 0 0; background-repeat: no-repeat;
}
.button.button--ghost, .button.button--small {
  background-image: linear-gradient(105deg, transparent 38%, rgba(217, 191, 138, 0.2) 50%, transparent 62%);
  background-size: 260% 100%; background-position: 100% 0; background-repeat: no-repeat;
}
.button:not(.button--ghost):not(.button--small):hover, .leader__cta:hover {
  background-image: linear-gradient(105deg, transparent 38%, rgba(255, 255, 255, 0.5) 50%, transparent 62%), linear-gradient(var(--gold-hi), var(--gold-hi));
  background-position: 0 0, 0 0;
}
.button.button--ghost:hover, .button.button--small:hover { background-position: 0 0; }
.button:hover, .leader__cta:hover { transition: background-position var(--dur-3) var(--ease-out), border-color var(--dur-1), color var(--dur-1); }

/* main navigation (wide screens): one gold line that slides between the items (motion.js places it; without it the
   current item keeps its own underline) */
.site-nav__ink { display: none; }
@media (min-width: 60rem) {
  .site-nav { position: relative; }
  .site-nav.has-ink .site-nav__ink {
    display: block; position: absolute; left: 0; top: 0; width: 1px; height: 2px; background: var(--gold); pointer-events: none;
    transform-origin: 0 0; transform: translate(var(--x, 0px), var(--y, 0px)) scaleX(var(--w, 0));
    transition: transform var(--dur-2) var(--ease-out), opacity var(--dur-1) var(--ease-out);
  }
  .site-nav.has-ink .site-nav__ink.is-off { opacity: 0; }
  .site-nav.has-ink .site-nav__ink.is-snap { transition: none; }
  .site-nav.has-ink .site-nav__link[aria-current="page"] { box-shadow: none; }
}

/* ================================================================ page changes: a calm crossfade, the header stays put */
@media (prefers-reduced-motion: no-preference) {
  @view-transition { navigation: auto; }
}
.site-header { view-transition-name: site-header; }
::view-transition-old(root), ::view-transition-new(root) { animation-duration: 200ms; animation-timing-function: var(--ease-in-out); }
::view-transition-old(site-header) { display: none; }
::view-transition-new(site-header), ::view-transition-group(site-header) { animation: none; }

/* guide header pictures: a slow settle on load (scale only, never hidden; no JS needed, none without scripting) */
@media (scripting: enabled) and (prefers-reduced-motion: no-preference) {
  .pimg--guides-header { animation: m-settle 2.4s var(--ease-out) backwards; }
}

/* ================================================================ motion (screens, no reduced-motion preference, scripting on)
   Flash fix (23.10.2026): content that was painted is never hidden again. Two ways in, one set of rules:
   - Intro: whatever is on the first screen animates from the first paint, in pure CSS (no class, no JS needed).
   - Scroll reveal: motion.js measures once, before it hides anything, which elements start BELOW the first screen
     and gives only those .m-wait (the hidden start). When they scroll in, the observer swaps .m-wait for .is-in;
     that restarts the same animation (.m-wait cancels every animation inside it, see the last rule of this block).
   Without scripting, with reduced motion and in print none of this applies: the final state, at once. If motion.js
   fails or is blocked, the intro ends visible and nothing is ever hidden. Rings draw from stroke-dasharray (the
   score is the attribute, pathLength 100), so no value has to come from JS. */
@media screen and (prefers-reduced-motion: no-preference) {
  /* in-page links glide (html-wide: iOS Safari does not focus a tapped link, so not :focus-within). Only after load
     (motion.js sets .m-smooth): a link to /#ranking or a reload must land at once, not scroll through the page */
  html.m-smooth { scroll-behavior: smooth; }
  .rank-table--cards .rank-row { transition: border-color 0.15s, translate var(--dur-1) var(--ease-out); }
  .rank-table--cards .rank-row:hover { translate: 0 -2px; }
}
@media screen and (scripting: enabled) and (prefers-reduced-motion: no-preference) {
  /* ---- uniform reveal: fade + rise; --d = start (intro: the head reads first, then the rest; scroll: batch stagger) */
  :where(.hero__lead > *, .page-head > :not(.pimg), .profile__head > *, .leader, .rank-table--cards .rank-row, .home .ranking > .section-head,
    .home .ranking > .critlegend, .key-terms > .section-head, .key-terms__row, .content-page > .prose > *, .section:not(.stats-strip):not(.key-terms):not(.home .ranking)):not(.m-wait) {
    animation: m-rise var(--dur-2) var(--ease-out) var(--d, 0ms) backwards;
  }
  :where(.hero__lead > *, .page-head > :not(.pimg), .profile__head > *, .leader, .rank-table--cards .rank-row, .home .ranking > .section-head,
    .home .ranking > .critlegend, .key-terms > .section-head, .key-terms__row, .content-page > .prose > *, .section:not(.stats-strip):not(.key-terms):not(.home .ranking)).m-wait { opacity: 0; }
  :where(.hero__lead, .page-head, .profile__head) > :not(.m-wait) { --rise: 10px; animation-duration: 520ms; }
  :where(.hero__lead, .page-head, .profile__head) > :nth-child(2) { --d: 60ms; }
  :where(.hero__lead, .page-head, .profile__head) > :nth-child(3) { --d: 120ms; }
  :where(.hero__lead, .page-head, .profile__head) > :nth-child(n+4) { --d: 180ms; }
  :where(.leader, .stats-strip, .section, .content-page > .prose > *) { --d: 160ms; }

  /* ---- home leaderboard: calm. A row fades up quickly (from a dim start, never blank), then its score bar grows and
     its 12 dots appear in ONE left-to-right sweep; rows come in batches of at most 5 (stagger cap in motion.js) */
  .rank-table--cards .rank-row.m-wait { opacity: 0.2; }
  .rank-table--cards .rank-row:not(.m-wait) { animation: m-row 280ms var(--ease-out) var(--d, 0ms) backwards; }
  .rank-table--cards .rank-row .critbar { animation: m-wipe 380ms var(--ease-out) calc(var(--d, 0ms) + 90ms) backwards; }
  .rank-table--cards .rank-row .scorebar__fill { animation: m-grow 380ms var(--ease-out) calc(var(--d, 0ms) + 90ms) backwards; }
  /* other tables (/best/): the same sweep per bar */
  .critbar:not(.m-wait) { animation: m-wipe 380ms var(--ease-out) calc(var(--d, 0ms) + 90ms) backwards; }
  .critbar.m-wait { clip-path: inset(0 100% 0 0); }

  /* ---- home hero: the podium. Plinths rise from the floor in the order 2 -> 1 -> 3, then numeral, monogram, name, score */
  .top3__item:nth-child(2) { --pd: 120ms; }
  .top3__item:nth-child(1) { --pd: 255ms; }
  .top3__item:nth-child(3) { --pd: 390ms; }
  .top3.m-wait :is(.top3__title, .top3__tie, .top3__more, .top3__rank, .top3__num, .top3__mono, .top3__name, .top3__score),
  .top3.m-wait .top3__item--gold::before { opacity: 0; }
  .top3 :is(.top3__title, .top3__tie, .top3__more) { animation: m-rise var(--dur-2) var(--ease-out) backwards; }
  .top3 .top3__rank { transform-origin: 50% 100%; animation: m-plinth var(--dur-3) var(--ease-out) var(--pd) backwards; }
  .top3 .top3__num { animation: m-rise var(--dur-2) var(--ease-out) calc(var(--pd) + 380ms) backwards; }
  .top3 .top3__mono { animation: m-rise var(--dur-2) var(--ease-out) calc(var(--pd) + 440ms) backwards; }
  .top3 .top3__name { animation: m-rise var(--dur-2) var(--ease-out) calc(var(--pd) + 485ms) backwards; }
  .top3 .top3__score { animation: m-rise var(--dur-2) var(--ease-out) calc(var(--pd) + 530ms) backwards; }
  /* the spotlight beam fades in, then breathes (opacity 0.85 <-> 1, 6 s) only while the podium is on screen (.is-live) */
  .top3 .top3__item--gold::before {
    animation: m-fade 1.2s var(--ease-out) 700ms backwards, m-breathe 3s var(--ease-in-out) 1.9s infinite alternate;
    animation-play-state: running, paused;
  }
  .top3.is-live .top3__item--gold::before { animation-play-state: running, running; }

  /* ---- key numbers: tiles fade up one after another, then the gold line draws under each number */
  .stat-tile:nth-child(2) { --i: 1; } .stat-tile:nth-child(3) { --i: 2; } .stat-tile:nth-child(4) { --i: 3; } .stat-tile:nth-child(5) { --i: 4; }
  .stats-strip.m-wait .stat-tile, .stats-strip.m-wait .stat-tile__value::after { opacity: 0; }
  .stats-strip .stat-tile { animation: m-rise var(--dur-2) var(--ease-out) calc(var(--d, 0ms) + var(--i, 0) * var(--stagger)) backwards; }
  .stats-strip .stat-tile__value::after { animation: m-line var(--dur-3) var(--ease-out) calc(var(--d, 0ms) + 260ms + var(--i, 0) * var(--stagger)) backwards; }

  /* ---- rings draw (stroke-dasharray from 0 to the score; the circles have pathLength 100) */
  .leader.m-wait .leader__ring-fill, .scorepanel.m-wait .scorepanel__ring-fill, .findings__item.m-wait .findings__ring-fill { opacity: 0; }
  .leader__ring-fill { animation: m-draw calc(var(--dur-3) * 1.6) var(--ease-out) calc(var(--d, 0ms) + 200ms) backwards; }
  .scorepanel__ring-fill { animation: m-draw calc(var(--dur-3) * 1.4) var(--ease-out) calc(var(--d, 0ms) + 250ms) backwards; }
  .findings__ring-fill { animation: m-draw var(--dur-3) var(--ease-out) calc(var(--d, 0ms) + 120ms) backwards; }

  /* ---- bars grow from 0 (compare score bars, /commission/ min-max, the findings median bar) */
  .scorebar__fill { transform-box: fill-box; transform-origin: 0 50%; }
  .ratebar__fill, .findings__bar__fill { transform-box: view-box; transform-origin: 0 0; }
  .scorebar.m-wait .scorebar__fill, .ratebar.m-wait .ratebar__fill, .findings__item.m-wait .findings__bar__fill { opacity: 0; }
  .vs .scorebar__fill { animation: m-grow var(--dur-3) var(--ease-out) calc(var(--d, 0ms) + 140ms) backwards; }
  .vs__card ~ .vs__card .scorebar__fill { animation-delay: calc(var(--d, 0ms) + 220ms); }
  .ratebar__fill, .findings__bar__fill { animation: m-grow var(--dur-3) var(--ease-out) calc(var(--d, 0ms) + 80ms) backwards; }
  /* compare: "vs" fades in after the two bars */
  .vs__sep { animation: m-fade var(--dur-2) var(--ease-out) calc(var(--d, 0ms) + 520ms) backwards; }

  /* ---- compare pages, #1 card, profile counts and scorecard: the dots light up left to right (a grey ring first,
     then the fill (::after) of each full / partial dot). The final look equals the static design (fill over ring). */
  .critbar__cell:nth-child(2), .leader__critem:nth-child(2), .counts__item:nth-child(2), .ptok:nth-child(2) { --i: 1; }
  .critbar__cell:nth-child(3), .leader__critem:nth-child(3), .counts__item:nth-child(3), .ptok:nth-child(3) { --i: 2; }
  .critbar__cell:nth-child(4), .leader__critem:nth-child(4), .ptok:nth-child(4) { --i: 3; }
  .critbar__cell:nth-child(5), .leader__critem:nth-child(5) { --i: 4; } .critbar__cell:nth-child(6), .leader__critem:nth-child(6) { --i: 5; }
  .critbar__cell:nth-child(7), .leader__critem:nth-child(7) { --i: 6; } .critbar__cell:nth-child(8), .leader__critem:nth-child(8) { --i: 7; }
  .critbar__cell:nth-child(9), .leader__critem:nth-child(9) { --i: 8; } .critbar__cell:nth-child(10), .leader__critem:nth-child(10) { --i: 9; }
  .critbar__cell:nth-child(11), .leader__critem:nth-child(11) { --i: 10; } .critbar__cell:nth-child(12), .leader__critem:nth-child(12) { --i: 11; }
  .points__item:nth-child(2) { --g: 1; } .points__item:nth-child(3) { --g: 2; }
  .critbar__cell:first-child, .leader__critem:first-child, .counts__item:first-child, .ptok:first-child, .stat-tile:first-child { --i: 0; }
  .points__item:first-child { --g: 0; }
  :is(.vs, .compare-table) .critbar { --dot: var(--ink); animation: none; clip-path: none; }
  :is(.vs, .compare-table) .critbar :is(.critbar__cell--full, .critbar__cell--partial) {
    position: relative; background: none; box-shadow: inset 0 0 0 1.5px var(--ring-none);
  }
  :is(.vs, .compare-table) .critbar :is(.critbar__cell--full, .critbar__cell--partial)::after,
  :is(.leader__critem--full, .leader__critem--partial, .counts__item--full, .counts__item--partial)::after,
  :is(.scorecard__row--full, .scorecard__row--partial) > th:first-child::after {
    content: ""; position: absolute; width: 11px; height: 11px; border-radius: 50%; background: var(--dot);
  }
  :is(.vs, .compare-table) .critbar .critbar__cell::after { inset: 0; width: auto; height: auto; }
  :is(.vs, .compare-table) .critbar .critbar__cell--partial::after, :is(.leader__critem--partial, .counts__item--partial)::after,
  .scorecard__row--partial > th:first-child::after {
    background: linear-gradient(90deg, var(--dot) 50%, transparent 50%); box-shadow: inset 0 0 0 1.5px var(--dot);
  }
  .vs .critbar .critbar__cell--partial::after { box-shadow: inset 0 0 0 1.25px var(--dot); }
  .leader__critem, .counts__item { --dot: var(--ink); position: relative; }
  .leader__critem { --dot: var(--gold); }
  :is(.leader__critem--full, .leader__critem--partial, .counts__item--full, .counts__item--partial)::before,
  :is(.scorecard__row--full, .scorecard__row--partial) > th:first-child::before { background: none; box-shadow: inset 0 0 0 1.5px var(--ring-none); }
  :is(.leader__critem, .counts__item)::after { left: 0; top: 50%; margin-top: -5.5px; }
  .scorecard__row > th:first-child { --dot: var(--ink); }
  .scorecard__row > th:first-child::after { left: 1.05rem; top: 1.2rem; }
  /* hidden until their group scrolls in */
  :is(.vs, .compare-table) .critbar.m-wait .critbar__cell::after, .leader.m-wait .leader__critem::after,
  .scorepanel.m-wait .counts__item::after, .scorecard__row.m-wait > th:first-child::after { opacity: 0; }
  :is(.vs, .compare-table) .critbar .critbar__cell::after { animation: m-pop var(--dur-2) var(--ease-out) calc(var(--d, 0ms) + 200ms + var(--i, 0) * var(--stagger)) backwards; }
  .leader__critem::after { animation: m-pop var(--dur-2) var(--ease-out) calc(var(--d, 0ms) + 520ms + var(--i, 0) * var(--stagger)) backwards; }
  .counts__item::after { animation: m-pop var(--dur-2) var(--ease-out) calc(var(--d, 0ms) + 380ms + var(--i, 0) * 90ms) backwards; }
  .scorecard__row > th:first-child::after { animation: m-pop var(--dur-2) var(--ease-out) calc(var(--d, 0ms) + 120ms) backwards; }

  /* ---- "How scoring works": the 12 tokens fill one after another (10 / 5 / 0) */
  .ptok__fill { transform-box: fill-box; transform-origin: 50% 50%; }
  .points.m-wait .ptok__fill { opacity: 0; }
  .points .ptok__fill { animation: m-pop var(--dur-2) var(--ease-out) calc(var(--d, 0ms) + 150ms + (var(--g, 0) * 4 + var(--i, 0)) * var(--stagger)) backwards; }

  /* ---- #1 card: a soft gold sheen passes once along the card edge */
  .leader::after {
    content: ""; position: absolute; inset: 0; z-index: 2; border-radius: inherit; padding: 1px; pointer-events: none; opacity: 0;
    background: linear-gradient(115deg, transparent 42%, rgba(234, 214, 168, 0.95) 50%, transparent 58%) 100% 0 / 300% 100% no-repeat;
    -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); -webkit-mask-composite: xor;
    mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); mask-composite: exclude;
    animation: m-sheen 1.6s var(--ease-in-out) calc(var(--d, 0ms) + 1100ms) backwards;
  }

  /* ---- a waiting element (below the first screen) runs nothing: lifting .m-wait starts every animation inside fresh */
  .m-wait, .m-wait::before, .m-wait::after, .m-wait *, .m-wait *::before, .m-wait *::after { animation: none !important; }
}
@media screen and (scripting: enabled) and (prefers-reduced-motion: no-preference) and (max-width: 47.99rem) {
  .scorecard__row > th:first-child::after { left: 1rem; top: 1.4rem; }
  .compare-table--stack .compare__row--short .critbar .critbar__cell--partial::after { box-shadow: inset 0 0 0 1.25px var(--dot); }
}

/* m-rise starts at 0.02, not 0: Chrome counts the largest text as painted (LCP) only once it is not fully transparent,
   so a start at 0 would report the first-screen title at the END of its fade; 0.02 looks the same */
@keyframes m-rise { from { opacity: 0.02; translate: 0 var(--rise); } }
@keyframes m-row { from { opacity: 0.2; translate: 0 8px; } }
@keyframes m-fade { from { opacity: 0; } }
@keyframes m-plinth { from { scale: 1 0; } }
@keyframes m-line { from { scale: 0 1; } }
@keyframes m-pop { from { opacity: 0; transform: scale(0.2); } }
@keyframes m-draw { from { stroke-dasharray: 0 100; } }
@keyframes m-grow { from { transform: scaleX(0); } }
@keyframes m-wipe { from { clip-path: inset(0 100% 0 0); } }
@keyframes m-breathe { to { opacity: 0.85; } }
@keyframes m-settle { from { scale: 1.02; } }
@keyframes m-sheen { from { opacity: 1; background-position: 100% 0; } to { opacity: 1; background-position: 0 0; } }
