/*
 * The "Three Steps, No Install" reel.
 *
 * Loaded after ds-anim.css. Everything here is prefixed .ds-reel / .ds-act /
 * .ds-sc- and touches nothing else on the page.
 *
 * The depth is a real perspective projection. .ds-reel-stage sets the camera,
 * .ds-reel-track is preserve-3d, and the three acts sit at different Z with a
 * Y rotation, so an off-camera act foreshortens on its inner edge the way a
 * card turned away from a lens does. A scale + shadow cannot produce that, and
 * the difference is the whole reason this reads as a stage.
 *
 * Everything that moves is transform, opacity or filter. Nothing here changes
 * a width, a height or a position, so no frame of any of it costs a layout.
 */

/* The section is a dark band, and that is not decoration.

   A stage's depth is carried by its floor, its key light and the sheen across
   the front card. All three are light on dark by nature — on the page's own
   near-white ground the floor grid was a grey hairline, the key light was
   nothing at all, and the whole thing flattened back into three cards with a
   blur on two of them. On a dark plate the acts read as lit panels standing in
   a room, which is the effect the section exists for.

   The plate is the same deep teal the hero's product card uses, so this is the
   page's second appearance of one surface rather than a third colour. */
.ds-reel-section {
  position: relative;
  overflow: hidden;
  background: linear-gradient(168deg, #01211e 0%, #02322d 52%, #01211e 100%);
}
.ds-reel-section .ds-head h2 {
  color: #ffffff;
}
.ds-reel-section .ds-head-note {
  color: rgba(226, 244, 241, 0.72);
}

.ds-reel {
  --dsr-dwell: 6.2s;
  --dsr-ease: cubic-bezier(0.22, 0.61, 0.28, 1);
  position: relative;
}

/* ------------------------------------------------------------------ */
/* the stage                                                           */
/* ------------------------------------------------------------------ */

.ds-reel-stage {
  position: relative;
  height: 470px;
  margin-top: 26px;
  perspective: 1500px;
  /* Above centre: the camera looks slightly down onto the floor, which is what
     puts a horizon behind the acts instead of a flat backdrop. */
  perspective-origin: 50% 38%;
}

/* The floor. One plane laid down under the acts, ruled in both directions and
   faded out towards the horizon so it reads as distance rather than as a
   pattern. It drifts towards the camera, slowly enough not to compete. */
.ds-reel-floor {
  position: absolute;
  left: 50%;
  bottom: -30px;
  width: 240%;
  height: 460px;
  transform: translateX(-50%) rotateX(76deg);
  transform-origin: 50% 100%;
  background-image:
    repeating-linear-gradient(90deg, rgba(64, 226, 208, 0.42) 0 1px, transparent 1px 62px),
    repeating-linear-gradient(0deg, rgba(64, 226, 208, 0.42) 0 1px, transparent 1px 62px);
  -webkit-mask-image: linear-gradient(0deg, #000 0%, rgba(0, 0, 0, 0.35) 46%, transparent 82%);
  mask-image: linear-gradient(0deg, #000 0%, rgba(0, 0, 0, 0.35) 46%, transparent 82%);
  animation: ds-reel-floor 9s linear infinite;
}
@keyframes ds-reel-floor {
  to {
    background-position: 0 62px, 0 62px;
  }
}

/* The key light, behind everything, breathing. */
.ds-reel-key {
  position: absolute;
  left: 50%;
  top: 4%;
  width: 780px;
  height: 380px;
  max-width: 92%;
  transform: translateX(-50%);
  background: radial-gradient(closest-side, rgba(45, 212, 191, 0.30), transparent 72%);
  filter: blur(6px);
  animation: ds-reel-key 7s ease-in-out infinite alternate;
}
@keyframes ds-reel-key {
  to {
    transform: translateX(-50%) scale(1.12);
    opacity: 0.75;
  }
}

.ds-reel-track {
  position: absolute;
  inset: 0;
  transform-style: preserve-3d;
}

/* ------------------------------------------------------------------ */
/* an act                                                              */
/* ------------------------------------------------------------------ */

.ds-act {
  position: absolute;
  top: 0;
  left: 50%;
  width: min(560px, 82%);
  height: 100%;
  margin-left: calc(min(560px, 82%) / -2);
  padding: 26px 30px 30px;
  display: flex;
  flex-direction: column;
  gap: 18px;
  border-radius: 22px;
  background: linear-gradient(160deg, #ffffff 0%, #eef7f6 100%);
  border: 1px solid rgba(120, 235, 220, 0.28);
  overflow: hidden;
  transform-style: preserve-3d;
  will-change: transform, opacity;
  transition:
    transform 0.9s var(--dsr-ease),
    opacity 0.38s ease-out,
    filter 0.5s ease-out,
    box-shadow 0.9s var(--dsr-ease);
}

.ds-act[data-state="live"] {
  transform: translate3d(0, 0, 0) rotateY(0deg);
  opacity: 1;
  filter: none;
  box-shadow: 0 40px 90px -26px rgba(0, 0, 0, 0.6), 0 0 60px -18px rgba(45, 212, 191, 0.45);
  z-index: 3;
}
/* The two off-camera acts. The Y rotation is what foreshortens the inner edge;
   the Z is what makes them smaller without being scaled, so their border and
   their type shrink together the way a real card would. */
.ds-act[data-state="next"] {
  transform: translate3d(62%, 16px, -400px) rotateY(-30deg);
  opacity: 0.42;
  filter: blur(1.4px) saturate(0.75);
  box-shadow: 0 30px 70px -30px rgba(0, 0, 0, 0.55);
  z-index: 2;
}
.ds-act[data-state="prev"] {
  transform: translate3d(-62%, 16px, -400px) rotateY(30deg);
  opacity: 0.42;
  filter: blur(1.4px) saturate(0.75);
  box-shadow: 0 30px 70px -30px rgba(0, 0, 0, 0.55);
  z-index: 2;
}
/* Nothing off camera should be clickable or tabbable through. */
.ds-act:not([data-state="live"]) {
  pointer-events: none;
}

/* A band of light travelling across the live card, once per dwell. This is the
   one purely cinematic flourish in the section — it is what makes the surface
   read as glass under a moving light rather than as a printed panel. */
.ds-act-sheen {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(
    104deg,
    transparent 38%,
    rgba(255, 255, 255, 0.72) 50%,
    transparent 62%
  );
  transform: translateX(-120%);
  opacity: 0;
}
.ds-act[data-state="live"] .ds-act-sheen {
  animation: ds-reel-sheen var(--dsr-dwell) var(--dsr-ease) infinite;
}
@keyframes ds-reel-sheen {
  0% { transform: translateX(-120%); opacity: 0; }
  12% { opacity: 0.85; }
  34% { transform: translateX(120%); opacity: 0; }
  100% { transform: translateX(120%); opacity: 0; }
}

.ds-act-scene {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  display: grid;
  place-items: center;
  border-radius: 14px;
  background:
    radial-gradient(120% 90% at 50% 0%, rgba(0, 150, 136, 0.08), transparent 70%),
    #f7fbfb;
  border: 1px solid rgba(0, 150, 136, 0.10);
}
.ds-reel-svg {
  width: 100%;
  height: 100%;
  max-height: 250px;
  display: block;
}

.ds-act-copy {
  position: relative;
  flex: 0 0 auto;
}
.ds-act-n {
  display: inline-block;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.18em;
  color: var(--ds-teal);
  margin-bottom: 6px;
}
.ds-act-copy h3 {
  margin: 0 0 8px;
  font-size: 26px;
  font-weight: 700;
  color: var(--ds-ink);
  line-height: 1.15;
}
.ds-act-copy p {
  margin: 0;
  font-size: 15px;
  line-height: 1.6;
  color: var(--ds-muted);
}

/* The copy of an off-camera act is unreadable at that angle anyway, and left
   at full contrast it turns the blur into noise behind the live card. */
.ds-act:not([data-state="live"]) .ds-act-copy {
  opacity: 0.55;
}

/* ------------------------------------------------------------------ */
/* the rail                                                            */
/* ------------------------------------------------------------------ */

.ds-reel-rail {
  display: flex;
  justify-content: center;
  gap: 14px;
  margin: 30px 0 0;
  padding: 0;
  list-style: none;
}
.ds-reel-dot {
  position: relative;
  display: grid;
  place-items: center;
  width: 74px;
  height: 38px;
  padding: 0;
  border: 1px solid rgba(120, 235, 220, 0.3);
  border-radius: 10px;
  background: rgba(255, 255, 255, 0.06);
  overflow: hidden;
  cursor: pointer;
  transition: border-color 0.3s ease, color 0.3s ease;
}
.ds-reel-dot:hover {
  border-color: #5eead4;
}
.ds-reel-dot-n {
  position: relative;
  z-index: 1;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.1em;
  color: rgba(214, 244, 240, 0.7);
  transition: color 0.3s ease;
}
.ds-reel-dot.is-live .ds-reel-dot-n {
  color: #01211e;
}
/* Fills over exactly one dwell, so the rail is a clock as well as a position.
   Once the reader has taken over there is no clock left to show, so it simply
   fills and stays. */
.ds-reel-dot-fill {
  position: absolute;
  inset: 0;
  transform: scaleX(0);
  transform-origin: left center;
  background: #5eead4;
}
.ds-reel-dot.is-live .ds-reel-dot-fill {
  animation: ds-reel-fill var(--dsr-dwell) linear forwards;
}
.ds-reel.is-taken .ds-reel-dot.is-live .ds-reel-dot-fill {
  animation: none;
  transform: scaleX(1);
}
@keyframes ds-reel-fill {
  to { transform: scaleX(1); }
}

/* ------------------------------------------------------------------ */
/* scene 1 — boot                                                      */
/* ------------------------------------------------------------------ */

.ds-sc-screen { fill: #08302c; }
.ds-sc-stand { fill: #0b3f3a; }
.ds-sc-port { fill: #0b3f3a; }

.ds-sc-stick-body { fill: #1c1c25; }
.ds-sc-stick-tip { fill: #b8c0c4; }
.ds-sc-stick-led { fill: #2fd4c4; }

.ds-sc-lines rect { fill: #2fd4c4; opacity: 0.9; }
.ds-sc-caret { fill: #2fd4c4; }

/* Everything below only runs while the act is in front of the camera. An
   animation playing on a card the reader cannot read is work for nothing. */
[data-state="live"] .ds-sc-stick {
  animation: ds-sc-stick-in 1s var(--dsr-ease) both;
}
@keyframes ds-sc-stick-in {
  from { transform: translateX(-70px); opacity: 0; }
  to { transform: translateX(0); opacity: 1; }
}
[data-state="live"] .ds-sc-stick-led {
  animation: ds-sc-blink 0.9s steps(1, end) 1.1s infinite;
}
@keyframes ds-sc-blink {
  50% { opacity: 0.15; }
}
[data-state="live"] .ds-sc-screen {
  animation: ds-sc-wake 0.8s ease-out 0.9s both;
}
@keyframes ds-sc-wake {
  from { fill: #08302c; }
  to { fill: #06403a; }
}
/* Each line scales out from its left edge — a fill, not a fade, so it reads as
   output arriving rather than as text appearing. */
[data-state="live"] .ds-sc-lines rect {
  transform-box: fill-box;
  transform-origin: left center;
  animation: ds-sc-type 0.42s var(--dsr-ease) both;
  animation-delay: calc(1.25s + var(--i) * 0.34s);
}
@keyframes ds-sc-type {
  from { transform: scaleX(0); }
  to { transform: scaleX(1); }
}
[data-state="live"] .ds-sc-caret {
  transform-box: fill-box;
  transform-origin: left center;
  animation: ds-sc-type 0.4s var(--dsr-ease) 2.35s both,
             ds-sc-blink 1s steps(1, end) 2.8s infinite;
}

/* ------------------------------------------------------------------ */
/* scene 2 — erase & verify                                            */
/* ------------------------------------------------------------------ */

.ds-sc-bay-shell { fill: #ffffff; stroke: rgba(0, 150, 136, 0.22); }
.ds-sc-bay-track { fill: #e4efee; }
.ds-sc-bay-fill { fill: var(--ds-teal); }
.ds-sc-bay-head { fill: #1c1c25; }
.ds-sc-bay-disc { fill: none; stroke: #9fb3b1; stroke-width: 2; }
.ds-sc-bay-spindle { fill: #9fb3b1; }
.ds-sc-bay-tick {
  fill: none;
  stroke: var(--ds-teal);
  stroke-width: 2.6;
  stroke-linecap: round;
  stroke-linejoin: round;
  opacity: 0;
}

/* The bays arrive as a stack, then all eight run together — which is the claim
   the copy makes, so it is the claim the picture has to make too. */
[data-state="live"] .ds-sc-bay {
  animation: ds-sc-bay-in 0.5s var(--dsr-ease) both;
  animation-delay: calc(var(--i) * 0.06s);
}
@keyframes ds-sc-bay-in {
  from { opacity: 0; transform: translateY(10px); }
}
[data-state="live"] .ds-sc-bay-fill {
  transform-box: fill-box;
  transform-origin: left center;
  animation: ds-sc-wipe 2.3s var(--dsr-ease) both;
  animation-delay: calc(0.6s + var(--i) * 0.09s);
}
@keyframes ds-sc-wipe {
  from { transform: scaleX(0); }
  to { transform: scaleX(1); }
}
[data-state="live"] .ds-sc-bay-head {
  animation: ds-sc-head 2.3s var(--dsr-ease) both;
  animation-delay: calc(0.6s + var(--i) * 0.09s);
}
@keyframes ds-sc-head {
  from { transform: translateX(0); }
  to { transform: translateX(86px); }
}
/* The tick lands after the wipe, never with it: the mark means read back and
   passed, and a mark that appears while the bar is still moving would be
   claiming the verify happened before the erase finished. */
[data-state="live"] .ds-sc-bay-tick {
  animation: ds-sc-pass 0.4s var(--dsr-ease) both;
  animation-delay: calc(3.0s + var(--i) * 0.09s);
}
@keyframes ds-sc-pass {
  from { opacity: 0; transform: scale(0.4); }
  to { opacity: 1; transform: scale(1); }
}

/* ------------------------------------------------------------------ */
/* scene 3 — certify                                                   */
/* ------------------------------------------------------------------ */

.ds-sc-paper { fill: #ffffff; stroke: rgba(0, 150, 136, 0.20); }
.ds-sc-paper-band { fill: var(--ds-teal-deep); }
.ds-sc-rows rect { fill: #d8e6e4; }
.ds-sc-seal-ring { fill: none; stroke: var(--ds-teal); stroke-width: 1.6; opacity: 0.4; }
.ds-sc-seal-disc { fill: var(--ds-teal); }
.ds-sc-seal-mark { fill: rgba(255, 255, 255, 0.22); }
.ds-sc-seal-tick {
  fill: none;
  stroke: #ffffff;
  stroke-width: 2.4;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.ds-sc-seal-wave { fill: none; stroke: var(--ds-teal); stroke-width: 2; opacity: 0; }

/* The sheet comes up and turns to face the reader — a rotateX on the SVG
   group, so it is the same camera the acts themselves are standing in. */
[data-state="live"] .ds-sc-sheet {
  transform-box: fill-box;
  transform-origin: 50% 100%;
  animation: ds-sc-sheet-up 0.9s var(--dsr-ease) both;
}
@keyframes ds-sc-sheet-up {
  from { opacity: 0; transform: translateY(30px) rotateX(48deg); }
  to { opacity: 1; transform: translateY(0) rotateX(0deg); }
}
[data-state="live"] .ds-sc-rows rect {
  transform-box: fill-box;
  transform-origin: left center;
  animation: ds-sc-type 0.36s var(--dsr-ease) both;
  animation-delay: calc(0.95s + var(--i) * 0.16s);
}
[data-state="live"] .ds-sc-seal {
  transform-box: fill-box;
  transform-origin: 50% 50%;
  animation: ds-sc-stamp 0.55s cubic-bezier(0.2, 1.5, 0.4, 1) 1.75s both;
}
@keyframes ds-sc-stamp {
  from { opacity: 0; transform: scale(2.1) rotate(-14deg); }
  to { opacity: 1; transform: scale(1) rotate(0deg); }
}
[data-state="live"] .ds-sc-seal-wave {
  transform-box: fill-box;
  transform-origin: 50% 50%;
  animation: ds-sc-wave 0.9s ease-out 2.2s both;
}
@keyframes ds-sc-wave {
  from { opacity: 0.7; transform: scale(1); }
  to { opacity: 0; transform: scale(2.2); }
}

/* ------------------------------------------------------------------ */
/* smaller screens                                                     */
/* ------------------------------------------------------------------ */

@media (max-width: 1199px) {
  .ds-reel-stage { height: 440px; }
  .ds-act { width: min(520px, 84%); margin-left: calc(min(520px, 84%) / -2); }
  .ds-act[data-state="next"] { transform: translate3d(58%, 14px, -420px) rotateY(-29deg); }
  .ds-act[data-state="prev"] { transform: translate3d(-58%, 14px, -420px) rotateY(29deg); }
}

/* Below a tablet there is no room either side for an act to stand off camera —
   it would sit under the live one rather than beside it. The side acts step
   out of the picture and the live one takes the full width; the floor, the key
   light and the scenes all still run, so the section keeps its depth. */
@media (max-width: 767px) {
  .ds-reel-stage {
    height: 430px;
    perspective: 1000px;
  }
  .ds-act {
    width: 100%;
    margin-left: -50%;
    padding: 20px 18px 22px;
    border-radius: 18px;
  }
  .ds-act:not([data-state="live"]) {
    opacity: 0;
    transform: translate3d(0, 18px, -260px);
    filter: none;
  }
  .ds-act-copy h3 { font-size: 21px; }
  .ds-act-copy p { font-size: 14px; }
  .ds-reel-svg { max-height: 190px; }
  .ds-reel-floor { height: 340px; }
  .ds-reel-rail { gap: 10px; }
  .ds-reel-dot { width: 62px; height: 34px; }
}

/* ------------------------------------------------------------------ */
/* no motion                                                           */
/* ------------------------------------------------------------------ */

/*
 * Reduced motion gets the section as three plain cards, every scene in its
 * finished state. Nothing is lost: the three scenes exist to show the reader
 * the sequence once, and the sequence is also written down beside them.
 *
 * The stage stops being a stage — no perspective, no absolute positioning, no
 * timer (the component never starts one under this setting either).
 */
@media (prefers-reduced-motion: reduce) {
  .ds-reel-stage {
    height: auto;
    perspective: none;
    margin-top: 8px;
  }
  .ds-reel-floor,
  .ds-reel-key,
  .ds-act-sheen,
  .ds-reel-rail {
    display: none;
  }
  .ds-reel-track {
    position: static;
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 24px;
    transform-style: flat;
  }
  .ds-act,
  .ds-act[data-state="next"],
  .ds-act[data-state="prev"] {
    position: static;
    width: auto;
    height: auto;
    margin-left: 0;
    transform: none;
    opacity: 1;
    filter: none;
    pointer-events: auto;
    box-shadow: 0 18px 40px -28px rgba(0, 60, 55, 0.4);
  }
  .ds-act:not([data-state="live"]) .ds-act-copy { opacity: 1; }
  .ds-reel *,
  .ds-reel *::before,
  .ds-reel *::after {
    animation: none !important;
    transition: none !important;
  }
  /* The still frames: the stick seated, every bay wiped and passed, the sheet
     up with its seal on. */
  .ds-sc-stick { transform: none; opacity: 1; }
  .ds-sc-bay-fill { transform: none; }
  .ds-sc-bay-head { transform: translateX(86px); }
  .ds-sc-bay-tick { opacity: 1; }
  .ds-sc-sheet { opacity: 1; transform: none; }
  .ds-sc-seal-wave { opacity: 0; }
  @media (max-width: 767px) {
    .ds-reel-track { grid-template-columns: minmax(0, 1fr); }
  }
}
