/* verse-fx.css — presentation layer for the VGFX effect kit (verse-fx.js).
   Everything here is driven by the same --vg-* tokens as verse.css, so the
   effects re-colour themselves in dark, light, eye-protect and night modes
   without a single hard-coded palette value. */

/* ---- generic canvas host ------------------------------------------------- */
.vgfx-layer {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  z-index: 0;
}
.vgfx-canvas {
  display: block;
  width: 100%;
  height: 100%;
}

/* ---- prism grid: page background --------------------------------------- */
/* Fixed so the grid stays put while content scrolls. The eye/night filters are
   applied to <html> (see verse.css) precisely so that stays true — a filter on
   any non-root element would make it a containing block and re-anchor this
   layer to the document-tall body box. */
.vgfx-prism {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  opacity: .85;
  /* fade the grid out toward the bottom so dense tables stay legible */
  -webkit-mask-image: linear-gradient(to bottom, #000 0, #000 55%, rgba(0, 0, 0, .35) 100%);
  mask-image: linear-gradient(to bottom, #000 0, #000 55%, rgba(0, 0, 0, .35) 100%);
}
html[data-theme="light"] .vgfx-prism { opacity: .7; }

/* Contained variant (opts.contain) — the public site's section bands host their
   own lattice because they paint opaque backgrounds over the full viewport. The
   mask fades it into the band's own colour instead of down the page. */
.vgfx-prism-in {
  position: absolute;
  opacity: .55;
  -webkit-mask-image: radial-gradient(120% 100% at 50% 0, #000 0, rgba(0, 0, 0, .15) 100%);
  mask-image: radial-gradient(120% 100% at 50% 0, #000 0, rgba(0, 0, 0, .15) 100%);
}

/* The panel shells paint their own opaque backgrounds, so the app root is
   lifted above the grid layer explicitly (opts.lift). Only that one element is
   promoted — body-level fixed overlays (modals z-index 90, toasts 100, the
   display menu 300) keep their own stacking so nothing lands behind a backdrop. */
.vgfx-lift { position: relative; z-index: 1; }

/* ---- user cursor -------------------------------------------------------- */
/* Above every overlay in the app (modal backdrop 90, toasts 100, display menu
   300, the merchant full-screen demo player 9999) — while the native cursor is
   hidden there must never be a layer that can swallow the arrow. */
.vgfx-cursor {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 10050;
  pointer-events: none;
  will-change: transform;
  opacity: 0;
  transition: opacity .18s ease;
}
.vgfx-cursor.on { opacity: 1; }
.vgfx-cursor-arrow {
  display: block;
  overflow: visible;
  transition: transform .2s cubic-bezier(.22, 1, .36, 1);
}
.vgfx-cursor-arrow path { fill: var(--vg-brand); stroke: var(--vg-bg); stroke-width: 1; }
/* Clickable affordance. The OS hand cursor is gone while ours is on screen, so
   the ring is the only thing left telling the user a target is interactive:
   it expands under the arrow over links, buttons, tabs and cards. */
.vgfx-cursor-ring {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 34px;
  height: 34px;
  margin: -17px 0 0 -17px;
  border-radius: 999px;
  border: 1.5px solid var(--vg-brand);
  opacity: 0;
  transform: scale(.35);
  transition: opacity .18s ease, transform .26s cubic-bezier(.22, 1, .36, 1);
}
.vgfx-cursor.act .vgfx-cursor-ring { opacity: .55; transform: scale(1); }
.vgfx-cursor.act .vgfx-cursor-arrow { transform: scale(.82); }
.vgfx-cursor-label {
  position: absolute;
  top: 16px;
  left: 12px;
  padding: 2px 8px;
  border-radius: 999px;
  font: 600 11px/1.5 var(--vg-t-f, ui-sans-serif), system-ui, sans-serif;
  letter-spacing: .02em;
  white-space: nowrap;
  color: #fff;
  background: var(--vg-brand);
  box-shadow: 0 2px 8px rgba(2, 6, 23, .35);
}
/* Native cursor is hidden only while the pointer sits on a non-typing surface.
   The rule has to be universal and !important: every interactive element in the
   app carries its own `cursor: pointer` (links, buttons, summary, label,
   .vg-check, .vg-switch, th.sortable, .vg-tab, the landing nav pills), and an
   element-plus-class selector loses to some of those. Anything short of this
   left the OS arrow painted underneath ours — two cursors on every link.
   Gated on the same conditions VGFX.cursor() checks before it mounts, so the
   OS cursor can never be hidden without our arrow taking over — including when
   the motion preference or the primary pointer flips mid-session. Dropping the
   class over inputs restores the caret wholesale. */
@media (pointer: fine) and (prefers-reduced-motion: no-preference) {
  html.vgfx-cursor-hide,
  html.vgfx-cursor-hide *,
  html.vgfx-cursor-hide *::before,
  html.vgfx-cursor-hide *::after { cursor: none !important; }
}

/* ---- 3D stagger-flip text ---------------------------------------------- */
.vgfx-t3d {
  display: inline-block;
  line-height: 1;
  perspective: 900px;
  perspective-origin: 50% 50%;
}
.vgfx-t3d-c {
  position: relative;
  display: inline-block;
  transform-style: preserve-3d;
  transform: translateZ(-.5em) rotateX(0deg);
  transition: transform .46s cubic-bezier(.22, 1, .36, 1);
  transition-delay: var(--vgfx-d, 0s);
}
.vgfx-t3d.vgfx-flip .vgfx-t3d-c { transform: translateZ(-.5em) rotateX(90deg); }
.vgfx-t3d.vgfx-snap .vgfx-t3d-c { transition: none; }
.vgfx-t3d-f,
.vgfx-t3d-b {
  display: block;
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
}
.vgfx-t3d-f { transform: translateZ(.5em); }
.vgfx-t3d-b {
  position: absolute;
  inset: 0;
  transform: rotateX(-90deg) translateZ(.5em);
  color: var(--vg-brand);
}

/* ---- interactive grid: swell under the pointer -------------------------- */
/* position:relative so the hovered tile's z-index actually applies — the grids
   this attaches to (game cards, plan cards) are static grid items. */
.vgfx-ig-cell {
  position: relative;
  transition: transform .34s cubic-bezier(.22, 1, .36, 1),
              box-shadow .34s ease,
              border-color .34s ease,
              z-index 0s;
  will-change: transform;
}
.vgfx-ig-cell.vgfx-ig-hot {
  transform: scale(1.055);
  z-index: 3;
  border-color: color-mix(in srgb, var(--vg-brand) 55%, var(--vg-line));
  box-shadow: var(--vg-sh-2, 0 8px 24px rgba(2, 6, 23, .35));
}
.vgfx-ig-cell.vgfx-ig-warm { transform: scale(1.018); z-index: 2; }
.vgfx-ig-cell.vgfx-ig-cool { transform: scale(.982); opacity: .88; }

/* ---- globe -------------------------------------------------------------- */
.vgfx-globe {
  position: relative;
  width: 100%;
  aspect-ratio: 1 / 1;
  max-width: 520px;
  margin-inline: auto;
  touch-action: pan-y;
}
.vgfx-globe canvas { display: block; width: 100%; height: 100%; cursor: grab; }
.vgfx-globe.vgfx-drag canvas { cursor: grabbing; }
.vgfx-globe-tip {
  position: absolute;
  padding: 4px 9px;
  border: 1px solid var(--vg-line);
  border-radius: var(--vg-r-s, 6px);
  background: var(--vg-srf);
  color: var(--vg-ink);
  font: 600 11px/1.45 var(--vg-t-f, ui-sans-serif), system-ui, sans-serif;
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  transform: translate(-50%, -140%);
  transition: opacity .16s ease;
  box-shadow: var(--vg-sh-2, 0 8px 24px rgba(2, 6, 23, .35));
}
.vgfx-globe-tip.on { opacity: 1; }
.vgfx-globe-tip b { color: var(--vg-brand); font-weight: 700; }

/* ---- motion & pointer opt-outs ----------------------------------------- */
@media (pointer: coarse) {
  .vgfx-cursor { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .vgfx-cursor { display: none; }
  .vgfx-prism { opacity: .5; }
  .vgfx-t3d-c { transition: none; }
  .vgfx-ig-cell { transition: none; }
  .vgfx-ig-cell.vgfx-ig-hot,
  .vgfx-ig-cell.vgfx-ig-warm,
  .vgfx-ig-cell.vgfx-ig-cool { transform: none; opacity: 1; }
}
@media print {
  .vgfx-prism, .vgfx-cursor, .vgfx-layer { display: none !important; }
}
