@import "tailwindcss";

@import "./highlight.css";

@theme {
  --font-sans: "InterVariable", ui-sans-serif, system-ui, sans-serif;
  --font-sans--font-feature-settings: "cv02", "cv03", "cv04", "cv11";
  --font-display: "Bangers", "Impact", sans-serif;
  --font-mono: "IBM Plex Mono", ui-monospace, "SF Mono", monospace;

  /* Comic palette */
  --color-ink: #18120e;
  --color-paper: #fff8ec;
  --color-hero-red: #e62429;
  --color-hero-yellow: #ffd400;
  --color-hero-blue: #0072ce;

  --shadow-comic: 8px 8px 0 0 var(--color-ink);
  --shadow-comic-sm: 4px 4px 0 0 var(--color-ink);
}

/* Comic paper: warm page with a faint halftone dot screen. */
@utility bg-paper {
  background-color: var(--color-paper);
  background-image:
    radial-gradient(circle at 25% 15%, rgb(255 212 0 / 0.18), transparent 42%),
    radial-gradient(circle at 85% 80%, rgb(0 114 206 / 0.10), transparent 45%),
    radial-gradient(circle, rgb(24 18 14 / 0.07) 1px, transparent 1.4px);
  background-size: auto, auto, 12px 12px;
}

/* Ben-Day dot screen for panel corners and fills. */
@utility bg-halftone {
  background-image: radial-gradient(circle, currentColor 1.2px, transparent 1.7px);
  background-size: 9px 9px;
}

/* Comic headline: ink fill with a hard offset color shadow.
   The offset scales with the breakpoint so wrapped lines never collide. */
@utility text-kapow {
  letter-spacing: 0.02em;
  text-shadow:
    2px 2px 0 var(--color-paper),
    4px 4px 0 var(--color-hero-red);

  @variant sm {
    text-shadow:
      3px 3px 0 var(--color-paper),
      6px 6px 0 var(--color-hero-red);
  }
}

::selection {
  background-color: var(--color-hero-yellow);
  color: var(--color-ink);
}

/* In-site fullscreen open/close. The panel morphs between its inline spot
   and the overlay by animating its real geometry while pinned fixed (driven
   by the fullscreen controller), so it grows out of the page and shrinks
   back into it without anything inside it stretching — the toolbar keeps
   its natural size and the iframe resizes like a real window being zoomed.
   CSS owns only the backdrop cross-fade;
   data-closing holds the overlay up until the exit morph finishes. The
   backdrop is painted on a ::before so fading it never fades the panel.
   The slot keeps the panel's inline space reserved (height frozen by the
   controller), so the page never reflows or jumps around the overlay. */
[data-controller~="fullscreen"][data-fullscreen] .fs-shell::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  background-color: --alpha(var(--color-ink) / 85%);
  backdrop-filter: blur(4px);
  animation: fs-backdrop-in var(--modal-open-dur) var(--modal-ease) both;
}
[data-controller~="fullscreen"][data-fullscreen][data-closing] .fs-shell::before {
  animation: fs-backdrop-out var(--modal-close-dur) ease-out both;
}

@keyframes fs-backdrop-in { from { opacity: 0; } }
@keyframes fs-backdrop-out { to { opacity: 0; } }

@media (prefers-reduced-motion: reduce) {
  [data-controller~="fullscreen"] .fs-shell::before { animation: none !important; }
}

/* transitions-dev — semantic tokens shared by every transition snippet. */
:root {
  /* Icon swap */
  --icon-swap-dur: 200ms;
  --icon-swap-blur: 2px;
  --icon-swap-start-scale: 0.25;
  --icon-swap-ease: ease-in-out;
  /* Modal open / close (drives the in-site fullscreen) */
  --modal-open-dur: 300ms;
  --modal-close-dur: 240ms;
  --modal-ease: cubic-bezier(0.22, 1, 0.36, 1);
  /* Panel reveal */
  --panel-open-dur: 400ms;
  --panel-close-dur: 350ms;
  --panel-translate-y: 100px;
  --panel-blur: 2px;
  --panel-ease: cubic-bezier(0.22, 1, 0.36, 1);
  /* Success check */
  --check-opacity-dur: 550ms;
  --check-rotate-dur: 550ms;
  --check-rotate-from: 80deg;
  --check-bob-dur: 450ms;
  --check-y-amount: 40px;
  --check-blur-dur: 500ms;
  --check-blur-from: 10px;
  --check-path-dur: 550ms;
  --check-path-delay: 80ms;
  --check-ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --check-ease-opacity: cubic-bezier(0.22, 1, 0.36, 1);
  --check-ease-rotate: cubic-bezier(0.22, 1, 0.36, 1);
  --check-ease-bob: cubic-bezier(0.34, 1.35, 0.64, 1);
  --check-ease-path: cubic-bezier(0.22, 1, 0.36, 1);
  /* Error state shake */
  --shake-distance: 6px;
  --shake-overshoot: 4px;
  --shake-dur-a: 80ms;
  --shake-dur-b: 60ms;
  --shake-ease: cubic-bezier(0.22, 1, 0.36, 1);
  --revert-hold: 3000ms;
  --revert-dur: 280ms;
}

/* transitions-dev: panel reveal */
.t-panel-slide {
  transform: translateY(var(--panel-translate-y));
  opacity: 0;
  filter: blur(var(--panel-blur));
  pointer-events: none;
  transition:
    transform var(--panel-close-dur) var(--panel-ease),
    opacity   var(--panel-close-dur) var(--panel-ease),
    filter    var(--panel-close-dur) var(--panel-ease);
  will-change: transform, opacity, filter;
}
.t-panel-slide[data-open="true"] {
  transform: translateY(0);
  opacity: 1;
  filter: blur(0);
  pointer-events: auto;
  transition:
    transform var(--panel-open-dur) var(--panel-ease),
    opacity   var(--panel-open-dur) var(--panel-ease),
    filter    var(--panel-open-dur) var(--panel-ease);
}

@media (prefers-reduced-motion: reduce) {
  .t-panel-slide { transition: none !important; }
}

/* transitions-dev: success check */
.t-success-check {
  display: inline-block;
  transform-origin: center;
  opacity: 0;
  will-change: transform, opacity, filter;
}
.t-success-check svg { display: block; overflow: visible; }
/* stroke-dasharray/-offset are measured per-path at runtime by the
   success-check Stimulus controller. */

.t-success-check[data-state="in"] {
  animation:
    t-check-fade   var(--check-opacity-dur) var(--check-ease-opacity) forwards,
    t-check-rotate var(--check-rotate-dur)  var(--check-ease-rotate)  forwards,
    t-check-blur   var(--check-blur-dur)    var(--check-ease-out)     forwards,
    t-check-bob    var(--check-bob-dur)     var(--check-ease-bob)     forwards;
}
.t-success-check[data-state="in"] svg path {
  animation: t-check-draw var(--check-path-dur) var(--check-ease-path) var(--check-path-delay, 0ms) forwards;
}

@keyframes t-check-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes t-check-rotate {
  from { transform: rotate(var(--check-rotate-from)); }
  to   { transform: rotate(0deg); }
}
@keyframes t-check-blur {
  from { filter: blur(var(--check-blur-from)); }
  to   { filter: blur(0); }
}
@keyframes t-check-bob {
  from { translate: 0 var(--check-y-amount); }
  to   { translate: 0 0; }
}
@keyframes t-check-draw { to { stroke-dashoffset: 0; } }

@media (prefers-reduced-motion: reduce) {
  .t-success-check { animation: none !important; opacity: 1; }
  .t-success-check svg path { animation: none !important; stroke-dashoffset: 0 !important; }
}

/* transitions-dev: icon swap */
.t-icon-swap {
  position: relative;
  display: inline-grid;
}
.t-icon-swap .t-icon {
  grid-area: 1 / 1;
  transition:
    opacity   var(--icon-swap-dur) var(--icon-swap-ease),
    filter    var(--icon-swap-dur) var(--icon-swap-ease),
    transform var(--icon-swap-dur) var(--icon-swap-ease);
  will-change: opacity, filter, transform;
}
.t-icon-swap[data-state="a"] .t-icon[data-icon="a"],
.t-icon-swap[data-state="b"] .t-icon[data-icon="b"] {
  opacity: 1;
  filter: blur(0);
  transform: scale(1);
}
.t-icon-swap[data-state="a"] .t-icon[data-icon="b"],
.t-icon-swap[data-state="b"] .t-icon[data-icon="a"] {
  opacity: 0;
  filter: blur(var(--icon-swap-blur));
  transform: scale(var(--icon-swap-start-scale));
}

@media (prefers-reduced-motion: reduce) {
  .t-icon-swap .t-icon { transition: none !important; }
}

/* transitions-dev: error state shake */
.t-input {
  transition: border-color 150ms ease-out;
  will-change: transform;
}
.t-input.is-error {
  border-color: var(--color-hero-red);
}
.t-input.is-error {
  transition: border-color var(--revert-dur, 280ms) ease-out;
}

.t-error-msg {
  opacity: 0;
  visibility: hidden;
  transition:
    opacity    var(--revert-dur, 280ms) ease-out,
    visibility 0s linear var(--revert-dur, 280ms);
}
.t-input-wrap.is-error .t-error-msg {
  opacity: 1;
  visibility: visible;
  transition:
    opacity    var(--revert-dur, 280ms) ease-out,
    visibility 0s linear 0s;
}

.t-input.is-shaking {
  animation: t-input-shake calc(
      var(--shake-dur-a) * 2 + var(--shake-dur-b) * 2
    ) linear;
}
@keyframes t-input-shake {
  0%      { transform: translateX(0);                                animation-timing-function: var(--shake-ease); }
  28.57%  { transform: translateX(var(--shake-distance));            animation-timing-function: var(--shake-ease); }
  57.14%  { transform: translateX(calc(var(--shake-distance) * -1)); animation-timing-function: var(--shake-ease); }
  78.57%  { transform: translateX(var(--shake-overshoot));           animation-timing-function: var(--shake-ease); }
  100%    { transform: translateX(0); }
}

@media (prefers-reduced-motion: reduce) {
  .t-input { animation: none !important; transform: none !important; }
}
