/* ============================================================================
   Miniature hero animations, ported from the ivy.app showcase stages
   (hero-chat-stage / hero-draft-stage / hero-review-stage / hero-dashboard-stage).

   Two deliberate departures from the originals:

   1. DARK. The site renders these as light app mockups (#f3f3f3 chrome, #fff
      surface). A white box on a #0a0a0a slide is a lamp, not an accent, so these
      use the deck's own tokens. The silhouette and the motion are what carry the
      recognition, not the background colour.

   2. RE-PROPORTIONED, NOT SCALED. The real window is 1424px wide; at 250px that
      is 0.18x and 14px type becomes noise. So: same layout, fatter chrome,
      skeleton bars for all prose.

   Timings and easings are the real ones:
     rise        450ms  cubic-bezier(0.22, 1, 0.36, 1)   (the app's .tendril-rise)
     cursor move        cubic-bezier(0.4, 0, 0.2, 1)
     drag sweep  900ms  cubic-bezier(0.42, 0, 0.58, 1)
     KPI stagger  90ms  between tiles
   Each mini runs on ONE 12s clock so its parts stay phase-locked with no JS.
   ========================================================================== */

.mini {
  --clock: 12s;
  --ease-rise: cubic-bezier(0.22, 1, 0.36, 1);
  position: relative;
  /* Explicit height rather than aspect-ratio: this box is created and destroyed
     on every slide change, and an intrinsic ratio that resolves a frame late can
     shove the slide around. 258 x 172 is the 3:2 the ratio would have given. */
  width: 258px;
  height: 172px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  display: flex;
  font-size: 10px; /* internal unit; nothing here inherits slide type */
  flex: none;
}

/* left rail, present in all four stages */
.mini .rail {
  width: 22%;
  background: var(--muted);
  border-right: 1px solid var(--border);
  padding: 6px 5px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.mini .rail i {
  display: block;
  height: 3px;
  border-radius: 2px;
  background: rgba(255, 255, 255, 0.09);
}
.mini .rail i.cta { background: var(--primary); opacity: 0.9; height: 5px; }
.mini .rail i.on { background: rgba(255, 255, 255, 0.22); }

.mini .body {
  flex: 1;
  padding: 7px 8px;
  display: flex;
  flex-direction: column;
  gap: 5px;
  min-width: 0;
  position: relative;
}

/* skeleton text bar */
.mini .bar {
  height: 3px;
  border-radius: 2px;
  background: rgba(255, 255, 255, 0.13);
}
.mini .bar.dim { background: rgba(255, 255, 255, 0.07); }
.mini .bar.w80 { width: 80%; }
.mini .bar.w60 { width: 60%; }
.mini .bar.w45 { width: 45%; }
.mini .bar.w30 { width: 30%; }

/* caption sits under the frame, never over the artwork */
.mini-wrap { display: flex; flex-direction: column; gap: 0.45em; flex: none; }
.mini-wrap .cap {
  font-family: var(--font-mono);
  font-size: 0.4em;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted-foreground);
}

/* ---------------------------------------------------------------- 1. chat
   Prompt types itself, sends as a bubble, answer streams in, plan is created. */

.mini-chat .composer {
  margin-top: auto;
  height: 11px;
  border: 1px solid var(--border);
  border-radius: 3px;
  background: var(--muted);
  padding: 4px 4px;
  display: flex;
  align-items: center;
  gap: 3px;
  overflow: hidden;
}
.mini-chat .typed {
  height: 3px;
  border-radius: 2px;
  background: rgba(255, 255, 255, 0.3);
  width: 0;
  animation: mc-type var(--clock) linear infinite;
}
.mini-chat .send {
  margin-left: auto;
  width: 7px;
  height: 7px;
  border-radius: 2px;
  background: var(--primary);
  flex: none;
  animation: mc-send var(--clock) linear infinite;
}
.mini-chat .bubble {
  align-self: flex-end;
  width: 52%;
  height: 8px;
  border-radius: 3px;
  background: rgba(255, 255, 255, 0.26);
  opacity: 0;
  animation: mc-bubble var(--clock) var(--ease-rise) infinite;
}
.mini-chat .ans { display: flex; flex-direction: column; gap: 4px; }
.mini-chat .ans .bar { opacity: 0; animation: mc-line var(--clock) var(--ease-rise) infinite; }
.mini-chat .ans .bar:nth-child(1) { animation-delay: calc(var(--clock) * 0.05); }
.mini-chat .ans .bar:nth-child(2) { animation-delay: calc(var(--clock) * 0.09); }
.mini-chat .ans .bar:nth-child(3) { animation-delay: calc(var(--clock) * 0.13); }
.mini-chat .done {
  display: flex;
  align-items: center;
  gap: 4px;
  opacity: 0;
  animation: mc-done var(--clock) var(--ease-rise) infinite;
}
.mini-chat .done b {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--primary); flex: none;
}
.mini-chat .done i {
  height: 3px; width: 62%; border-radius: 2px;
  background: color-mix(in srgb, var(--primary) 45%, transparent);
}

/* The real stage takes 13.7s and is blank for its first third. Compressed here:
   the point is ambient recognition, and a mini that sits empty for five seconds
   reads as broken rather than as subtle. Beats and easings are kept, the dead
   air is not. */
@keyframes mc-type {
  0%, 2% { width: 0; }
  20%, 22% { width: 68%; }
  23%, 100% { width: 0; }
}
@keyframes mc-send {
  0%, 21% { transform: scale(1); }
  21.7% { transform: scale(0.9); }   /* the real 1 -> 0.96 -> 1 click pulse */
  22.9%, 100% { transform: scale(1); }
}
@keyframes mc-bubble {
  0%, 22% { opacity: 0; transform: translateY(6px); }
  25.8%, 88% { opacity: 1; transform: translateY(0); }   /* 450ms rise */
  94%, 100% { opacity: 0; transform: translateY(0); }
}
@keyframes mc-line {
  0%, 20% { opacity: 0; transform: translateY(5px); }
  23.8%, 82% { opacity: 1; transform: translateY(0); }
  88%, 100% { opacity: 0; transform: translateY(0); }
}
@keyframes mc-done {
  0%, 44% { opacity: 0; transform: translateY(6px); }
  47.8%, 86% { opacity: 1; transform: translateY(0); }
  92%, 100% { opacity: 0; transform: translateY(0); }
}

/* --------------------------------------------------------------- 2. draft
   A line is selected, a comment lands on it, it resolves. The colour hard-cuts
   at each beat — the snap is what sells "a state changed". */

.mini-draft .doc { display: flex; flex-direction: column; gap: 5px; position: relative; }
.mini-draft .target { position: relative; }
.mini-draft .sweep {
  position: absolute;
  inset: -1.5px -2px;
  border-radius: 2px;
  transform: scaleX(0);
  transform-origin: left;
  background: #4a7fb5;
  animation: md-sweep var(--clock) cubic-bezier(0.42, 0, 0.58, 1) infinite;
}
.mini-draft .badge-c {
  position: absolute;
  right: -3px;
  top: -4px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--warning);
  opacity: 0;
  animation: md-badge var(--clock) var(--ease-rise) infinite;
}
.mini-draft .update {
  margin-top: auto;
  align-self: flex-start;
  height: 9px;
  width: 40%;
  border-radius: 3px;
  background: var(--primary);
  opacity: 0;
  animation: md-update var(--clock) var(--ease-rise) infinite;
}

@keyframes md-sweep {
  0%, 8% { transform: scaleX(0); background: #4a7fb5; }
  15.5% { transform: scaleX(1); background: #4a7fb5; }      /* real 900ms drag */
  29.9% { transform: scaleX(1); background: #4a7fb5; }
  30% { transform: scaleX(1); background: rgba(222, 177, 69, 0.7); }   /* comment */
  54.9% { transform: scaleX(1); background: rgba(222, 177, 69, 0.7); }
  55% { transform: scaleX(1); background: rgba(0, 204, 146, 0.42); }   /* resolved */
  92% { transform: scaleX(1); background: rgba(0, 204, 146, 0.42); }
  93%, 100% { transform: scaleX(0); background: #4a7fb5; }
}
@keyframes md-badge {
  0%, 30% { opacity: 0; transform: scale(0.6); }
  33.8%, 90% { opacity: 1; transform: scale(1); }
  94%, 100% { opacity: 0; transform: scale(0.6); }
}
@keyframes md-update {
  0%, 58% { opacity: 0; transform: translateY(5px); }
  61.8%, 90% { opacity: 1; transform: translateY(0); }
  94%, 100% { opacity: 0; transform: translateY(0); }
}

/* -------------------------------------------------------------- 3. review
   Tab switches to Changes, a diff rises, then the share dialog goes live. */

.mini-review .tabs {
  display: flex;
  gap: 6px;
  align-items: center;
  border-bottom: 1px solid var(--border);
  padding-bottom: 4px;
  position: relative;
}
.mini-review .tabs i { height: 3px; width: 16px; border-radius: 2px; background: rgba(255, 255, 255, 0.13); }
.mini-review .ul {
  position: absolute;
  bottom: -1px;
  left: 0;
  width: 16px;
  height: 1.5px;
  background: var(--foreground);
  animation: mr-tab var(--clock) cubic-bezier(0.4, 0, 0.2, 1) infinite;
}
.mini-review .diff { display: flex; flex-direction: column; gap: 2px; }
.mini-review .diff i {
  height: 2.5px;
  border-radius: 1px;
  opacity: 0;
  animation: mr-row var(--clock) var(--ease-rise) infinite;
}
.mini-review .diff i.add { background: rgba(134, 210, 111, 0.5); }
.mini-review .diff i.del { background: rgba(221, 88, 96, 0.45); }
.mini-review .diff i:nth-child(1) { width: 78%; animation-delay: calc(var(--clock) * 0.005); }
.mini-review .diff i:nth-child(2) { width: 62%; animation-delay: calc(var(--clock) * 0.012); }
.mini-review .diff i:nth-child(3) { width: 84%; animation-delay: calc(var(--clock) * 0.019); }
.mini-review .diff i:nth-child(4) { width: 55%; animation-delay: calc(var(--clock) * 0.026); }
.mini-review .diff i:nth-child(5) { width: 70%; animation-delay: calc(var(--clock) * 0.033); }
.mini-review .diff i:nth-child(6) { width: 46%; animation-delay: calc(var(--clock) * 0.040); }

.mini-review .scrim {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.55);
  opacity: 0;
  animation: mr-scrim var(--clock) linear infinite;
}
.mini-review .dialog {
  position: absolute;
  left: 12%;
  right: 12%;
  top: 26%;
  padding: 7px;
  border-radius: 4px;
  background: var(--popover);
  border: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  gap: 5px;
  opacity: 0;
  animation: mr-dialog var(--clock) var(--ease-rise) infinite;
}
.mini-review .dialog .pill {
  height: 9px;
  border-radius: 3px;
  background: rgba(68, 105, 192, 0.35);
  animation: mr-pill var(--clock) linear infinite;
}
/* the real QR asset, just tiny — reads as a QR because it is one */
.mini-review .dialog .qr {
  align-self: center;
  width: 24px;
  height: 24px;
  border-radius: 1px;
  display: block;
  margin: 0;
  opacity: 0;
  animation: mr-qr var(--clock) var(--ease-rise) infinite;
}

@keyframes mr-tab {
  0%, 8% { transform: translateX(0); }
  10.9%, 100% { transform: translateX(22px); }   /* real 350ms slide */
}
@keyframes mr-row {
  0%, 9% { opacity: 0; transform: translateY(3px); }
  12.8%, 88% { opacity: 1; transform: translateY(0); }
  93%, 100% { opacity: 0; transform: translateY(0); }
}
@keyframes mr-scrim {
  0%, 32% { opacity: 0; }
  35%, 76% { opacity: 1; }
  80%, 100% { opacity: 0; }
}
@keyframes mr-dialog {
  0%, 33% { opacity: 0; transform: translateY(8px); }
  36.8%, 76% { opacity: 1; transform: translateY(0); }
  80%, 100% { opacity: 0; transform: translateY(0); }
}
@keyframes mr-pill {
  0%, 44% { background: rgba(68, 105, 192, 0.35); }   /* starting */
  45%, 76% { background: rgba(0, 204, 146, 0.4); }    /* active */
  77%, 100% { background: rgba(68, 105, 192, 0.35); }
}
@keyframes mr-qr {
  0%, 47% { opacity: 0; transform: scale(0.88); }
  50.8%, 76% { opacity: 0.85; transform: scale(1); }
  80%, 100% { opacity: 0; transform: scale(0.88); }
}

/* ------------------------------------------------------------ 4. dashboard
   The KPI cascade with the real 90ms stagger, a sparkline that draws itself,
   and PR bars growing from their baseline. */

.mini-dash .body { gap: 4px; }
.mini-dash .greet { height: 3px; width: 34%; border-radius: 2px; background: rgba(255, 255, 255, 0.16); }
.mini-dash .kpis { display: grid; grid-template-columns: repeat(4, 1fr); gap: 3px; }
.mini-dash .kpis i {
  height: 16px;
  border-radius: 3px;
  background: color-mix(in srgb, var(--primary) 14%, transparent);
}
.mini-dash .kpis i:nth-child(2) { background: color-mix(in srgb, var(--primary) 8%, transparent); }
.mini-dash .kpis i:nth-child(3) { background: color-mix(in srgb, var(--primary) 11%, transparent); }
.mini-dash .kpis i:nth-child(4) { background: color-mix(in srgb, var(--primary) 6%, transparent); }
.mini-dash .cards { display: grid; grid-template-columns: 1.4fr 1fr; gap: 3px; flex: 1; min-height: 0; }
.mini-dash .card {
  border-radius: 3px;
  background: rgba(255, 255, 255, 0.04);
  padding: 4px;
  display: flex;
  align-items: flex-end;
  overflow: hidden;
}
.mini-dash .spark { width: 100%; height: 100%; display: block; overflow: visible; }
.mini-dash .spark path {
  fill: none;
  stroke: var(--primary);
  stroke-opacity: 0.8;
  stroke-width: 1.1;
  vector-effect: non-scaling-stroke;
  stroke-linecap: round;
  stroke-dasharray: 100;
  stroke-dashoffset: 100;
  animation: md-draw var(--clock) var(--ease-rise) infinite;
}
.mini-dash .prs { display: flex; align-items: flex-end; gap: 2.5px; width: 100%; height: 100%; }
.mini-dash .prs i {
  flex: 1;
  border-radius: 1.5px 1.5px 0 0;
  background: rgba(255, 255, 255, 0.22);
  transform-origin: bottom;
  transform: scaleY(0);
  animation: md-grow var(--clock) var(--ease-rise) infinite;
}
.mini-dash .prs i:nth-child(1) { height: 40%; animation-delay: calc(var(--clock) * 0.026); }
.mini-dash .prs i:nth-child(2) { height: 65%; animation-delay: calc(var(--clock) * 0.033); }
.mini-dash .prs i:nth-child(3) { height: 30%; animation-delay: calc(var(--clock) * 0.040); }
.mini-dash .prs i:nth-child(4) { height: 85%; animation-delay: calc(var(--clock) * 0.047); }
.mini-dash .prs i:nth-child(5) { height: 55%; animation-delay: calc(var(--clock) * 0.054); }
.mini-dash .prs i:nth-child(6) { height: 72%; animation-delay: calc(var(--clock) * 0.061); }

/* the cascade. Shared delays are reproduced from the original on purpose: 270ms
   drives both KPI-2 and the activity card, 450ms both KPI-4 and the PR card —
   that doubling is what makes it read as diagonal rather than as a list. */
.mini-dash .rise { animation: md-rise var(--clock) var(--ease-rise) infinite; }
.mini-dash .greet { animation: md-rise var(--clock) var(--ease-rise) infinite; }
.mini-dash .kpis i { animation: md-rise var(--clock) var(--ease-rise) infinite; }
.mini-dash .kpis i:nth-child(1) { animation-delay: calc(var(--clock) * 0.0075); }
.mini-dash .kpis i:nth-child(2) { animation-delay: calc(var(--clock) * 0.015); }
.mini-dash .kpis i:nth-child(3) { animation-delay: calc(var(--clock) * 0.0225); }
.mini-dash .kpis i:nth-child(4) { animation-delay: calc(var(--clock) * 0.0375); }
.mini-dash .cards .card { animation: md-rise var(--clock) var(--ease-rise) infinite; }
.mini-dash .cards .card:nth-child(1) { animation-delay: calc(var(--clock) * 0.0225); }
.mini-dash .cards .card:nth-child(2) { animation-delay: calc(var(--clock) * 0.0375); }

@keyframes md-rise {
  0% { opacity: 0; transform: translateY(6px); }
  3.75%, 88% { opacity: 1; transform: translateY(0); }   /* 450ms rise */
  95%, 100% { opacity: 0; transform: translateY(0); }
}
@keyframes md-draw {
  0%, 2.5% { stroke-dashoffset: 100; }
  15.8%, 88% { stroke-dashoffset: 0; }                   /* real 1600ms draw */
  95%, 100% { stroke-dashoffset: 100; }
}
@keyframes md-grow {
  0% { transform: scaleY(0); }
  4%, 88% { transform: scaleY(1); }
  95%, 100% { transform: scaleY(0); }
}

/* The site branches to a static final frame under reduced-motion; do the same. */
@media (prefers-reduced-motion: reduce) {
  .mini *,
  .mini {
    animation: none !important;
  }
  .mini .bar,
  .mini-chat .bubble,
  .mini-chat .done,
  .mini-draft .badge-c,
  .mini-draft .update,
  .mini-review .diff i,
  .mini-dash .greet,
  .mini-dash .kpis i,
  .mini-dash .card { opacity: 1 !important; transform: none !important; }
  .mini-chat .typed { width: 68%; }
  .mini-draft .sweep { transform: scaleX(1); background: rgba(0, 204, 146, 0.42); }
  .mini-review .scrim, .mini-review .dialog { opacity: 0; }
  .mini-review .ul { transform: translateX(22px); }
  .mini-dash .spark path { stroke-dashoffset: 0; }
  .mini-dash .prs i { transform: scaleY(1); }
}
