/* ============================================================
   Cipherverse — effects.css (redesign)
   One orchestrated page-load: hero lines rise, then the actions,
   then the showreel ring turns into place. Everything else is a reply
   to the visitor (hover, focus, open, the scroll-driven ring).

   The hero showreel lives entirely in this file, @media rules included.
   ============================================================ */

@keyframes rise {
  from { opacity: 0; transform: translateY(0.35em); }
  to   { opacity: 1; transform: none; }
}
@keyframes ring-in {
  from { opacity: 0; transform: translateY(48px) scale(0.94); }
  to   { opacity: 1; transform: none; }
}
@keyframes glow-in {
  from { opacity: 0; }
  to   { opacity: var(--glow-o); }
}

.js .hero-line,
.js .hero-foot { opacity: 0; animation: rise 0.9s var(--ease) forwards; }
.js .hero-line:nth-child(2) { animation-delay: 0.08s; }
.js .hero-line:nth-child(3) { animation-delay: 0.2s; }
.js .hero-foot { animation-delay: 0.38s; }

/* ── Hero copy: one clean line each on wide screens ──────── */
.hero--ring .hero-title { font-size: clamp(2.35rem, 0.4rem + 5.1vw, 4.6rem); }
.hero--ring .hero-sub { max-width: 46ch; font-size: var(--t-base); color: var(--grey); }
.hero--ring .hero-foot { align-items: flex-end; }

/* ── Showreel ring ───────────────────────────────────────────
   A coverflow ring of real sites. main.js turns it as the page
   scrolls through the sticky track; the front card is sharp and lit,
   the rest recede, dim and blur. Sizes are all derived from --cw.   */
.ring {
  --cw: min(900px, 62vw, calc((100svh - var(--bar-h) - 210px) * 1.6)); /* front card width */
  --ch: calc(var(--cw) / 1.6 + 40px);                                  /* card height: view + bar */
  --stage-h: min(calc(100svh - var(--bar-h)), calc(var(--ch) + 230px));
  --step: 72vh;          /* scroll per card */
  --n: 8;
  --glow-o: 0.42;
  position: relative;
  margin-top: var(--s-4);
}

.ring-stage {
  position: relative;
  display: flex; flex-direction: column; justify-content: center;
  padding-block: var(--s-3);
}
.ring-scene { position: relative; }
.ring-list { position: relative; z-index: 1; }

/* One card = a minimal browser window around a screenshot */
.ring-card { width: var(--cw); margin-inline: auto; }
.ring-shot {
  position: relative; display: block; width: 100%;
  padding: 5px; text-align: left;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  box-shadow: 0 40px 80px -30px rgba(0, 0, 0, 0.9);
  transition: border-color var(--fast) ease;
}
.ring-shot::after {             /* shade for receding cards (opacity set by main.js) */
  content: ""; position: absolute; inset: -1px; border-radius: inherit;
  background: var(--black); opacity: var(--shade, 0); pointer-events: none;
}
.ring-bar { display: flex; align-items: center; gap: 10px; height: 30px; padding-inline: 9px 12px; }
.ring-dots { display: flex; gap: 5px; flex: none; }
.ring-dots i { width: 8px; height: 8px; border-radius: 50%; background: var(--line-2); }
.ring-url {
  flex: 0 1 300px; margin-inline: auto; min-width: 0;
  transform: translateX(-20px);
  text-align: center; font-size: 0.75rem; color: var(--grey);
  background: var(--black); border-radius: var(--r-pill); padding: 2px 12px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ring-img {
  width: 100%; aspect-ratio: 16 / 10; object-fit: cover; object-position: top;
  border-radius: calc(var(--r-md) - 6px); background: var(--black);
}
.ring-card.is-front .ring-shot:hover,
.ring-card.is-front .ring-shot:focus-visible { border-color: var(--lime); }
.ring-shot:focus-visible { outline-offset: 4px; border-radius: var(--r-md); }

/* Soft lime light pooled under the front card */
.ring-glow {
  position: absolute; left: 50%; top: 100%; z-index: 0;
  width: var(--cw); height: calc(var(--ch) * 0.55);
  transform: translate(-50%, -60%);
  background: radial-gradient(closest-side, rgba(198, 255, 53, 0.8), rgba(198, 255, 53, 0.25) 50%, transparent);
  filter: blur(50px);
  opacity: var(--glow-o);
  pointer-events: none;
}

/* Caption under the ring: name, trade + city, n of 8, visit link */
.ring-caption {
  position: relative; z-index: 20;
  display: flex; flex-wrap: wrap; align-items: center; column-gap: var(--s-3);
  width: var(--cw); max-width: 100%; margin: var(--s-2) auto 0;
  padding-inline: 4px;
}
.ring-name { font-weight: 500; margin-right: auto; }
.ring-meta { color: var(--grey); font-weight: 400; margin-left: 0.4em; }
.ring-count { color: var(--grey); font-size: var(--t-sm); font-variant-numeric: tabular-nums; }
.ring-link {
  white-space: nowrap;
  display: inline-flex; align-items: center; gap: 0.5em; min-height: 44px;
  font-size: var(--t-sm); color: var(--white);
  transition: color var(--fast) ease;
}
.ring-link::before { content: ""; width: 8px; height: 8px; border-radius: 50%; background: var(--lime); }
.ring-link:hover { color: var(--lime); }

/* No JS: the first site on its own, as a plain framed screenshot */
html:not(.js) .ring-card:not(:first-child) { display: none; }

/* ── With JS: the sticky 3D track ────────────────────────── */
.js .ring { height: calc(var(--stage-h) + (var(--n) - 1) * var(--step)); }
.js .ring-stage {
  position: sticky;
  top: calc(var(--bar-h) + (100svh - var(--bar-h) - var(--stage-h)) / 2);
  height: var(--stage-h);
  overflow-x: clip;         /* side cards never widen the page */
  padding-block: 0;
}
.js .ring-scene {
  flex: none; height: calc(var(--ch) + var(--s-4));
  opacity: 0; animation: ring-in 1.3s var(--ease) 0.45s forwards;
}
.js .ring-list {
  position: absolute; inset: 0;
  perspective: calc(var(--cw) * 2.4);
  perspective-origin: 50% 45%;
}
.js .ring-card {
  position: absolute; left: 50%; top: 50%;
  margin-left: calc(var(--cw) / -2);
  margin-top: calc(var(--ch) / -2);
  transform-origin: 50% 50%;
  backface-visibility: hidden;
  will-change: transform, opacity;
}
.js .ring-card:not(.is-front) .ring-shot { cursor: pointer; box-shadow: none; }
.js .ring-glow { top: calc(50% + var(--ch) / 2); opacity: 0; animation: glow-in 1.6s ease 0.9s forwards; }

/* ── Sizes ───────────────────────────────────────────────── */
@media (min-width: 600px) and (max-width: 1023px) {
  .ring { --cw: min(80vw, calc((100svh - var(--bar-h) - 220px) * 1.6)); --step: 54vh; }
}
@media (min-width: 1440px) {
  .ring { --glow-o: 0.46; }
}

/* Phones: no scroll track. The ring sits in the page and turns with a
   swipe (or a tap on a side card), so there is no long empty sticky run. */
@media (max-width: 599px) {
  .ring {
    --cw: 78vw;
    --ch: calc(var(--cw) / 1.6 + 30px);
    --glow-o: 0.5;
    margin-top: var(--s-4);
  }
  .ring-shot { padding: 3px; border-radius: 12px; box-shadow: 0 24px 40px -20px rgba(0, 0, 0, 0.9); }
  .ring-shot:focus-visible { border-radius: 12px; }
  .ring-bar { height: 24px; gap: 6px; padding-inline: 7px 8px; }
  .ring-dots i { width: 6px; height: 6px; }
  .ring-url { font-size: 0.66rem; padding: 1px 8px; transform: translateX(-12px); }
  .ring-img { border-radius: 9px; }
  .ring-glow { filter: blur(36px); }
  .ring-caption { column-gap: var(--s-2); margin-top: var(--s-1); }
  .ring-name { flex-basis: 100%; line-height: 1.35; }
  .ring-meta { display: block; margin-left: 0; font-size: var(--t-sm); }
  .ring-count { margin-right: auto; }
  .js .ring-count::after { content: "  \00B7  Swipe to turn"; }
}

/* ── ERP Agent: the voice → entry panel ──────────────────────
   Page-load: the status chip and headline rise with the rest of the hero,
   then the panel arrives. main.js then plays each example: the wave moves
   while the sentence types out, and the entry rows land one by one. */
@keyframes wave {
  from { transform: scaleY(0.35); }
  to   { transform: scaleY(1); }
}
@keyframes pulse {
  0%   { box-shadow: 0 0 0 0 rgba(var(--lime-rgb), 0.55); }
  100% { box-shadow: 0 0 0 10px rgba(var(--lime-rgb), 0); }
}
.js .status { opacity: 0; animation: rise 0.9s var(--ease) forwards; }
.js .erp-hero .vx { opacity: 0; animation: ring-in 1.2s var(--ease) 0.45s forwards; }
.status-dot { animation: pulse 1.8s ease-out infinite; }

.vx-wave i { transform-origin: 50% 50%; transition: background-color var(--fast) ease; }
.vx.is-listening .vx-wave i { background: var(--lime); animation: wave 0.5s ease-in-out infinite alternate; }
.vx.is-listening .vx-wave i:nth-child(2n)  { animation-duration: 0.38s; }
.vx.is-listening .vx-wave i:nth-child(3n)  { animation-duration: 0.62s; animation-delay: -0.2s; }
.vx.is-listening .vx-wave i:nth-child(5n)  { animation-duration: 0.45s; animation-delay: -0.1s; }
.vx.is-listening .vx-mic { animation: pulse 1.2s ease-out infinite; }
.vx.is-listening .vx-said::after {   /* the caret while the sentence types */
  content: ""; display: inline-block; width: 2px; height: 0.95em; margin-left: 3px;
  vertical-align: -0.1em; background: var(--lime);
}

/* While the sentence types, the entry form waits empty: labels stay, values
   and the approval line are hidden, then land one by one (delays from main.js). */
.vx-gloss, .vx-row dd, .vx-foot { transition: opacity 0.45s var(--ease), transform 0.45s var(--ease); }
.vx-row dt { transition: color var(--fast) ease; }
.vx.is-typing .vx-gloss,
.vx.is-typing .vx-row dd,
.vx.is-typing .vx-foot { opacity: 0; transform: translateY(6px); }
.vx.is-typing .vx-row dt { color: rgba(140, 147, 160, 0.55); }
.vx-approve { transition: background-color var(--fast) ease, color var(--fast) ease; }
.vx.is-approved .vx-approve { background: var(--lime); color: var(--ink); }

/* Phones and touch tablets: no sticky scroll track. The ring sits in the page
   and turns with a swipe, so a tablet held upright never shows a long run of
   empty black while the ring turns. main.js uses the same query. */
@media (max-width: 599px), (max-width: 1023px) and (pointer: coarse) {
  .js .ring { height: auto; }
  .js .ring-stage { position: relative; top: auto; height: auto; }
  .js .ring-scene { height: calc(var(--ch) + var(--s-4)); }
  .js .ring-list { touch-action: pan-y; }
}
@media (min-width: 600px) and (max-width: 1023px) and (pointer: coarse) {
  .js .ring-count::after { content: "  \00B7  Swipe to turn"; }
}

/* ── Reduced motion: no entrance, no easing; main.js snaps card to card ── */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .js .status, .js .erp-hero .vx { opacity: 1; animation: none; }
  .status-dot, .vx-mic, .vx-wave i { animation: none !important; }
  .vx-gloss, .vx-row dd, .vx-foot, .feature-link::after { transition: none; }
  .js .hero-line, .js .hero-foot, .js .ring-scene { opacity: 1; animation: none; }
  .js .ring-glow { opacity: var(--glow-o); animation: none; }
  .ring-shot, .ring-link, .tile-shot img, .vmodal-window, .vmodal-backdrop { transition: none; }
  .tile-shot:hover img, .mosaic-item:hover img, .mosaic-item:focus-visible img { transform: none; }
  .mosaic-name { transition: none; }
}
