/* Animation and motion, then the accessibility overrides, which come last so they win. */

/* Gentle animation: fades, color and glow. Nothing on the page moves.
   It runs for everyone, including people who asked their system for less motion (on Windows:
   "Animation effects" off), since it causes no movement. */

.hero-copy > *,
.chip::before,
.chip::after {
  animation: fade-in 0.9s var(--ease-out) both;
}

.hero-copy > :nth-child(2) {
  animation-delay: 0.1s;
}

.hero-copy > :nth-child(3) {
  animation-delay: 0.2s;
}

.hero-copy > :nth-child(4) {
  animation-delay: 0.3s;
}

/* The headline appears word by word: each word fades in, without moving. */
.hero-copy h1 {
  animation: none;
}

.hero-title-word {
  animation: fade-in 0.9s var(--ease-out) both;
}

.hero-title-word:nth-child(2) {
  animation-delay: 0.14s;
}

.hero-title-word:nth-child(3) {
  animation-delay: 0.28s;
}

.hero-title-word:nth-child(4) {
  animation-delay: 0.42s;
}

/* The chip powers up: once the cores are hot, its pins glow ember for a moment. */
@property --pin {
  syntax: "<color>";
  inherits: true;
  initial-value: #a5a7ad;
}

.chip::before,
.chip::after {
  animation: fade-in 0.9s var(--ease-out) both, pins-power 1.8s ease-in-out 1s both;
}

/* A core under the pointer runs a little hotter. */
.heatmap a:hover,
.heatmap a:focus-visible {
  filter: brightness(1.06) saturate(1.2);
}

/* Rows of the ranking light up softly under the pointer or keyboard focus. */
.review:hover,
.review:focus-within {
  background-color: var(--hover);
}

/* The chip heats up: each core goes from cold grey to its score's heat, hottest first. */
.heatmap li {
  animation: core-heat 1.2s var(--ease-out) both;
}

.heatmap li:nth-child(1) {
  animation-delay: 0.3s;
}

.heatmap li:nth-child(2) {
  animation-delay: 0.45s;
}

.heatmap li:nth-child(3) {
  animation-delay: 0.6s;
}

.heatmap li:nth-child(4) {
  animation-delay: 0.75s;
}

.heatmap li:nth-child(5) {
  animation-delay: 0.9s;
}

/* Then every core glows softly a few times, one after another, like a chip under load. Few cycles, so it
   stops on its own (WCAG 2.2.2). */
.heatmap a {
  animation: core-glow 3.2s ease-in-out 1.6s 4 alternate;
}

.heatmap li:nth-child(2) a {
  animation-delay: 2.2s;
}

.heatmap li:nth-child(3) a {
  animation-delay: 2.8s;
}

.heatmap li:nth-child(4) a {
  animation-delay: 3.4s;
}

.heatmap li:nth-child(5) a {
  animation-delay: 4s;
}

/* Hover and focus answer with light, not movement. */
.button,
.heatmap a,
.review .score-tile,
.consensus .fill,
.review-more a,
.review summary,
.nav-list a,
.footer-links a,
.toc a {
  transition: filter 0.25s ease, background-color 0.25s ease, color 0.25s ease, box-shadow 0.35s ease;
}

.review:hover .score-tile,
.review:focus-within .score-tile {
  box-shadow: inset 0 1px 0 rgb(255 255 255 / 0.3), 0 0 0 4px color-mix(in srgb, var(--heat) 28%, transparent), 0 10px 26px -10px var(--heat);
}

.review:hover .consensus .fill,
.review:focus-within .consensus .fill {
  filter: brightness(1.08) saturate(1.15);
}

/* The notice frosts over as it appears: the glass starts clear and the blur behind it builds up. */
.cookie-notice {
  transition: opacity 0.4s ease, backdrop-filter 0.9s var(--ease-out), -webkit-backdrop-filter 0.9s var(--ease-out);
}

.cookie-notice:not(.is-shown) {
  opacity: 0;
  -webkit-backdrop-filter: blur(0);
  backdrop-filter: blur(0);
}

/* A row that comes back after filtering fades in instead of popping. */
.review {
  /* No transition on display: it would keep a filtered-out row in the layout for its whole duration
     (transitions outrank even [hidden] {display: none !important}), and the cards would not slide. */
  transition: opacity 0.35s ease, background-color 0.3s ease;
}

@starting-style {
  .review {
    opacity: 0;
  }
}

/* What the sources said fades in when opened. */
.review details[open] > :not(summary) {
  animation: fade-in 0.45s var(--ease-out) both;
}

/* Rows and bars fade in as they come into view, where scroll-driven animations exist. */
@supports (animation-timeline: view()) {
  .review article,
  .steps li {
    animation: fade-in linear both;
    animation-timeline: view();
    animation-range: entry 0% entry 55%;
  }

  .consensus .tick {
    animation: fade-in linear both;
    animation-timeline: view();
    animation-range: entry 50% entry 100%;
  }

  /* Each score heats up like a core in the chip as its row comes into view. */
  .review .score-tile {
    animation: core-heat linear both;
    animation-timeline: view();
    animation-range: entry 0% entry 100%;
  }

  /* Each step's number lights up as it reaches the middle of the screen. */
  .steps li::before {
    animation: node-light linear both;
    animation-timeline: view();
    animation-range: entry 30% cover 40%;
  }
}

/* Motion. Only with no preference for reduced motion, and each piece is optional:
   a browser without support shows the finished, static page. */

@media (prefers-reduced-motion: no-preference) {
  html {
    scroll-behavior: smooth;
  }

  @view-transition {
    navigation: auto;
  }

  /* The light behind the page stays still: every glass surface would have to be redrawn as it moved. */

  /* With motion allowed, the same moments also move. */
  .hero-copy > *,
  .hero-title-word {
    animation: rise 0.9s var(--ease-out) both;
  }

  /* The headline rises word by word. */
  .hero-copy h1 {
    animation: none;
  }

  .hero-title-word {
    display: inline-block;
  }

  .hero-title-word:nth-child(2) {
    animation-delay: 0.07s;
  }

  .hero-title-word:nth-child(3) {
    animation-delay: 0.14s;
  }

  .hero-title-word:nth-child(4) {
    animation-delay: 0.21s;
  }

  .heatmap li {
    animation-name: core-on;
  }

  .review:hover .score-tile {
    transform: translateY(-2px) scale(1.04);
  }

  .review .score-tile {
    transition: transform 0.5s var(--spring), box-shadow 0.35s ease;
  }

  @supports (animation-timeline: view()) {
    /* Each source's tick drops onto the bar after the bar has grown. */
    .consensus .tick {
      transform-box: fill-box;
      transform-origin: 50% 0;
      animation: tick-drop linear both;
      animation-timeline: view();
      animation-range: entry 50% entry 100%;
    }

    /* The line between the steps draws itself. */
    .steps::before {
      transform-origin: top;
      animation: draw-down linear both;
      animation-timeline: view();
      animation-range: entry 20% cover 55%;
    }

    .steps li::before {
      animation-name: node-pop;
    }
  }

  @media (min-width: 860px) {
    @supports (animation-timeline: view()) {
      .steps::before {
        transform-origin: right;
        animation-name: draw-across;
      }

      [dir="ltr"] .steps::before {
        transform-origin: left;
      }
    }
  }

  .chip-lid::after {
    animation: sweep 1.6s var(--ease-out) 1.2s both;
  }

  /* Everything below moves because the user did something. */
  .button,
  .heatmap a,
  .review summary::before,
  .marquee-toggle {
    transition: transform 0.5s var(--spring), filter 0.25s ease, background-color 0.25s ease;
  }

  .button:hover,
  .heatmap a:hover {
    transform: translateY(-2px);
  }

  /* Choosing a plan: the card rises on a spring, the tick draws itself and the price settles in. */
  .plan {
    transition: box-shadow 0.3s ease, background-color 0.3s ease, transform 0.55s var(--spring);
  }

  .plan:has(.plan-input:checked) {
    transform: translateY(-4px);
  }

  .plan-check svg {
    transition: stroke-dashoffset 0.35s var(--ease-out) 0.1s;
  }

  .plan:has(.plan-input:checked) .plan-price span {
    animation: price-settle 0.5s var(--spring);
  }

  /* Pressed glass gives like a drop: wider and flatter, then springs back. */
  .button:active {
    transform: scale(1.03, 0.9);
    transition-duration: 0.12s;
  }

  .marquee-toggle:active {
    transform: scale(0.9);
    transition-duration: 0.12s;
  }

  /* The chip tilts toward the pointer, as if held up to the light (main.js sets --tx and --ty, -1 to 1). */
  @media (hover: hover) and (pointer: fine) {
    .chip-body {
      transform: perspective(1100px) rotateX(calc(var(--ty, 0) * -5deg)) rotateY(calc(var(--tx, 0) * 7deg));
      transition: transform 0.7s var(--spring);
    }
  }

  .filters.is-ready .filter-indicator {
    transition: transform 0.55s var(--spring), width 0.55s var(--spring), height 0.55s var(--spring);
  }

  .filters button {
    transition: color 0.3s ease;
  }

  .marquee.is-running .marquee-track {
    animation: marquee 38s linear infinite;
  }

  .marquee.is-paused .marquee-track,
  .marquee.is-running:hover .marquee-track {
    animation-play-state: paused;
  }

  .cookie-notice {
    transition: transform 0.6s var(--spring), opacity 0.3s ease, backdrop-filter 0.9s var(--ease-out),
      -webkit-backdrop-filter 0.9s var(--ease-out);
  }

  .cookie-notice:not(.is-shown) {
    opacity: 0;
    transform: translateY(calc(100% + 24px));
  }

  /* "What the sources said" opens smoothly, and the bars grow to their scores. */
  .review details::details-content {
    block-size: 0;
    overflow-y: clip;
    transition: block-size 0.45s var(--ease-out), content-visibility 0.45s allow-discrete;
  }

  .review details[open]::details-content {
    block-size: auto;
  }

  details[open] .bar rect {
    transform-box: fill-box;
    transform-origin: 0 50%;
    animation: bar-grow 0.9s var(--ease-out) 0.1s both;
  }

  .verdict .bar rect {
    transform-box: fill-box;
    transform-origin: 0 50%;
    animation: bar-grow 1s var(--ease-out) 0.45s both;
  }

  .lens {
    animation: float 7s ease-in-out infinite;
  }

  /* Filtering: the remaining cards slide into place, the hidden ones fade. */
  ::view-transition-group(*) {
    animation-duration: 0.5s;
    animation-timing-function: var(--spring);
  }

  .vt-filter::view-transition-old(root),
  .vt-filter::view-transition-new(root) {
    animation: none;
  }

  /* The header tightens and the rings fill as the page scrolls, where scroll-driven animations exist. */
  @supports (animation-timeline: view()) {
    .header-bar {
      animation: header-tighten linear both;
      animation-timeline: scroll(root);
      animation-range: 0 160px;
    }

    .review article {
      animation: rise linear both;
      animation-timeline: view();
      animation-range: entry 0% entry 60%;
    }

    .consensus .fill {
      transform-box: fill-box;
      transform-origin: 0 50%;
      animation: bar-grow linear both;
      animation-timeline: view();
      animation-range: entry 10% entry 100%;
    }
  }
}

@keyframes core-heat {
  0% {
    opacity: 0.35;
    filter: grayscale(1) brightness(0.92);
  }

  60% {
    opacity: 1;
    filter: grayscale(0) brightness(1.18);
  }

  100% {
    opacity: 1;
    filter: none;
  }
}

@keyframes pins-power {
  45% {
    --pin: var(--accent-fill);
  }
}

@keyframes price-settle {
  from {
    transform: scale(0.94);
  }
}

@keyframes core-glow {
  from {
    box-shadow: var(--core-rest);
  }

  to {
    box-shadow: inset 0 1px 0 rgb(255 255 255 / 0.3), 0 0 34px -4px var(--heat);
  }
}

@keyframes node-light {
  from {
    background: var(--line-strong);
    color: var(--ink-2);
  }
}

@keyframes node-pop {
  0% {
    background: var(--line-strong);
    color: var(--ink-2);
    transform: scale(0.6);
  }

  70% {
    transform: scale(1.12);
  }

  100% {
    transform: none;
  }
}

@keyframes tick-drop {
  from {
    opacity: 0;
    transform: translateY(-40%) scaleY(0.4);
  }
}

@keyframes draw-down {
  from {
    transform: scaleY(0);
  }
}

@keyframes draw-across {
  from {
    transform: scaleX(0);
  }
}

@keyframes rise {
  from {
    opacity: 0;
    transform: translateY(24px);
  }
}

@keyframes fade-in {
  from {
    opacity: 0;
  }
}

@keyframes core-on {
  0% {
    opacity: 0.2;
    filter: grayscale(1) brightness(0.9);
    transform: scale(0.94);
  }

  55% {
    opacity: 1;
    filter: grayscale(0) brightness(1.15);
  }

  100% {
    opacity: 1;
    filter: none;
    transform: none;
  }
}

@keyframes sweep {
  0% {
    background-position: 150% 0;
    opacity: 1;
  }

  100% {
    background-position: -60% 0;
    opacity: 0;
  }
}

/* The copies of the list are laid out toward the end of the line, so the track moves toward its start:
   right in Hebrew, left in English. */
@keyframes marquee {
  to {
    transform: translateX(calc(100% * var(--flow)));
  }
}

@keyframes bar-grow {
  from {
    transform: scaleX(0);
  }
}

@keyframes header-tighten {
  to {
    width: min(100% - 24px, 960px);
    background: var(--glass-strong);
  }
}

@keyframes float {
  50% {
    transform: translateY(-10px);
  }
}

/* Accessibility settings */

@media (prefers-contrast: more) {
  .glass,
  .tile,
  .review-list,
  .chip-body {
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }

  .chip-lid,
  .glass::after,
  .tile::after,
  .review-list::after {
    display: none;
  }
}

@media (forced-colors: active) {
  .glass,
  .tile,
  .review-list,
  .review article,
  .review summary,
  .marquee-track li {
    border: 1px solid CanvasText;
  }

  .glass::before,
  .glass::after,
  .tile::before,
  .tile::after,
  .review-list::before,
  .review-list::after,
  .chip-lid,
  .filter-indicator {
    display: none;
  }

  .plan:has(.plan-input:checked) {
    outline: 3px solid Highlight;
  }

  .filters.has-indicator button[aria-pressed="true"]:not(:focus-visible) {
    outline: 2px solid Highlight;
  }

  .score-tile {
    border-color: CanvasText;
    -webkit-mask: none;
    mask: none;
  }
}
