﻿/* ══════════════════════════════════════════════════════════════════════════
   cv-genbtn.css — THE GENERATE BUTTON, one look everywhere.

   Worn by every button that makes something: the 73 model pages (#mpGen),
   AI Studio (#stGo), Scene Creator (#scGo), the character sheet
   (data-act="go"), Grading's recipe button and the Streaming export command.
   Those pages load five different stylesheets between them, so this file
   depends on none of them — it is self-contained on purpose.

   ⚠ .cv-gen REPLACES a page's own button class, it does not stack on it.
   .btn / .cvs-btn / .generate-btn / .gen each paint their own background AND
   their own ::before; leave one on and you get somebody else's hover wipe
   scaling this button to nothing. Keep only the layout class (e.g. .gen's
   margin-left:auto).

   2026-08-31 — THE LOOK CHANGED. The rainbow bar is retired; the button is the
   dark glass pill the AI Studio introduced (a vanilla port of the shadcn
   AnimatedGenerateButton): flickering sparkles, per-letter glow on a stagger, a
   blue highlight that lifts from the bottom edge on hover, and a label that
   cross-fades Generate → Generating while a run is live (add .on).
   `.stgen` is kept as an alias so the Studio's existing markup still matches.
   The inner structure (icon + the two text layers) is built by cv-genbtn.js, so
   a page only needs <button class="cv-gen">Generate</button> — pair the two
   files or the button renders as a bare pill with no animation.
   ══════════════════════════════════════════════════════════════════════════ */

.cv-gen, .stgen {
  --hue: 210;
  position: relative; display: inline-flex; align-items: center; gap: 9px;
  cursor: pointer; user-select: none; border: 1px solid rgba(255,255,255,.14);
  border-radius: 24px; padding: 11px 20px; background: #0b0b0c; color: #fff;
  font-family: inherit; font-size: 13.5px; line-height: 1.25;
  box-shadow:
    inset 0 1px 1px rgba(255,255,255,.2), inset 0 2px 2px rgba(255,255,255,.12),
    inset 0 4px 4px rgba(255,255,255,.07), inset 0 8px 8px rgba(255,255,255,.04),
    0 8px 22px rgba(0,0,0,.28);
  transition: box-shadow .4s, border-color .4s, background-color .4s;
}
/* the highlight that lifts from the bottom edge on hover */
.cv-gen::after, .stgen::after {
  content: ""; position: absolute; inset: 0; border-radius: inherit; pointer-events: none;
  background-image: linear-gradient(0deg, #fff, hsl(var(--hue),100%,70%), hsla(var(--hue),100%,70%,.5) 8%, transparent);
  -webkit-mask-image: linear-gradient(0deg, #fff, transparent);
          mask-image: linear-gradient(0deg, #fff, transparent);
  opacity: 0; transition: opacity .4s;
}
.cv-gen:hover, .stgen:hover { border-color: hsla(var(--hue),100%,80%,.4); }
.cv-gen:hover::after, .stgen:hover::after { opacity: 1; }
.cv-gen:active, .stgen:active { border-color: hsla(var(--hue),100%,80%,.7); background-color: hsla(var(--hue),50%,20%,.5); }
.cv-gen:disabled, .stgen:disabled { opacity: .6; cursor: not-allowed; }

/* the sparkles */
.cv-gen svg, .stgen svg {
  width: 20px; height: 20px; flex: 0 0 20px; fill: #e8e8e8;
  filter: drop-shadow(0 0 2px #fff9);
  animation: cvGenFlick 2s linear infinite .5s;
  transition: fill .4s, filter .4s;
}
.cv-gen:hover svg, .stgen:hover svg {
  fill: #fff; filter: drop-shadow(0 0 3px hsl(var(--hue),100%,70%)); animation: none;
}
@keyframes cvGenFlick { 50% { opacity: .3; } }

/* the two stacked labels: idle and running */
.cv-gen .tw, .stgen .tw { position: relative; min-width: 6.4em; height: 1.25em; display: block; text-align: left; }
/* ⚠ Both labels are absolutely positioned, so the wrapper has no width of its own — it is
   sized for the IDLE word, and "Generating video…" is far wider than "Generate". Left alone
   the running label overflowed and ran under the price chip beside it.
   While running the wrapper becomes a GRID with both labels in the same cell, so it measures
   itself against the wider one. A fixed min-width cannot do this job: the labels are a fixed
   13.5px while the button's font-size varies per surface, so any em value is wrong somewhere
   and any px value over-widens the shorter "Generating" on the Studio. */
.cv-gen.on .tw, .stgen.on .tw { display: grid; min-width: 0; }
.cv-gen.on .tl, .stgen.on .tl { position: static; grid-area: 1 / 1; }
.cv-gen .tl, .stgen .tl {
  position: absolute; left: 0; top: 0; white-space: nowrap;
  font-size: 13.5px; font-weight: 600; letter-spacing: .3px; transition: opacity .3s;
}
.cv-gen .t2, .stgen .t2 { opacity: 0; }
.cv-gen.on .t1, .stgen.on .t1 { opacity: 0; }
.cv-gen.on .t2, .stgen.on .t2 { opacity: 1; }
.cv-gen .tl span, .stgen .tl span {
  display: inline-block; color: #ffffff88;
  animation: cvGenSweep 2.4s linear infinite;
  transition: color .4s, text-shadow .4s;
}
/* A fade that travels left -> right across the word: each letter is dimmed, brightens
   as the wave reaches it, and dims again. The per-letter animation-delay that
   cv-genbtn.js stamps on the spans (0.08s each) is what makes it travel rather than
   pulse in unison. While running (.on) the same wave repeats faster. */
@keyframes cvGenSweep {
  0%, 100% { opacity: .45; }
  30%      { opacity: 1; text-shadow: 0 0 7px rgba(255,255,255,.55); }
  60%      { opacity: .45; }
}
.cv-gen.on .t2 span, .stgen.on .t2 span { animation-duration: 1.3s; }

/* The price rides INSIDE the button as plain text — no pill, no fill. A second rounded
   box nested in a rounded button read as a button within a button, which is not what the
   price is; it is part of the label. Spacing and weight carry it instead. */
.cv-gen .ct, .stgen .ct {
  position: relative; display: inline-flex; align-items: center; gap: 4px;
  margin-left: 2px; padding: 0; background: none; border: 0;
  font-size: 12px; font-weight: 700; letter-spacing: .2px; color: #fff; white-space: nowrap;
}
/* THE CREDIT MARK. Was a ✦, which reads as "sparkle / AI" and not as money. It is a
   coin now — drawn as a mask so it takes the button's own colour and needs no icon
   font. The ✦ character is still in the markup on every page that writes the chip;
   font-size:0 hides it, which means one CSS change covers all of them and nothing
   breaks if a page rewrites the chip's innerHTML. */
.cv-gen .ct .sp, .stgen .ct .sp {
  display: inline-block; width: 12px; height: 12px; font-size: 0; opacity: .9;
  background: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Ccircle cx='8' cy='8' r='6.6' fill='none' stroke='%23000' stroke-width='1.5'/%3E%3Cpath d='M10.4 5.9a3.1 3.1 0 1 0 0 4.2' fill='none' stroke='%23000' stroke-width='1.5' stroke-linecap='round'/%3E%3C/svg%3E") center / contain no-repeat;
          mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Ccircle cx='8' cy='8' r='6.6' fill='none' stroke='%23000' stroke-width='1.5'/%3E%3Cpath d='M10.4 5.9a3.1 3.1 0 1 0 0 4.2' fill='none' stroke='%23000' stroke-width='1.5' stroke-linecap='round'/%3E%3C/svg%3E") center / contain no-repeat;
}

@media (prefers-reduced-motion: reduce) {
  .cv-gen svg, .stgen svg, .cv-gen .tl span, .stgen .tl span { animation: none; }
}
