/* fx.css — "the Tracer" (SPEC.md §8). Every rule is prefixed .fx-, and
   nothing else in the app styles these elements: the feature is deleted
   by removing this file, fx.js, and the three touch points. The one
   exception is the exit: the view-transition rules at the end name the
   result screen's index as the landing spot, and go with the rest.

   The stage is always dark pine in both themes, so text uses the
   constant on-pine tokens. The graphic colours are mixed off --gold and
   --flag toward --on-pine: both tokens are tuned for use as ink on
   paper, and neither is bright enough against --pine-deep on its own. */

.fx-overlay {
  position: fixed;
  inset: 0;
  /* Above everything: the tab bar is 10 and the total bar is 5. */
  z-index: 100;
  overflow: hidden;
  touch-action: none;
  overscroll-behavior: contain;
  animation: fx-in 140ms linear;
  transition: opacity 400ms ease;

  /* Flat fallbacks first: where color-mix is unsupported the custom
     property is invalid at computed-value time, and stroke would fall
     back to its initial `none` — an invisible tracer. */
  --fx-gold: #beaf83;
  --fx-gold-hot: #ece2c0;
  --fx-red: #c7725e;
  --fx-lit: #1c3f2a;
  --fx-dark: #0d2015;
  --fx-gold: color-mix(in srgb, var(--gold) 58%, var(--on-pine) 42%);
  --fx-gold-hot: color-mix(in srgb, var(--gold) 22%, var(--on-pine) 78%);
  --fx-red: color-mix(in srgb, var(--flag) 70%, var(--on-pine) 30%);
  --fx-lit: color-mix(in srgb, var(--pine-deep) 78%, var(--pine-soft) 22%);
  --fx-dark: color-mix(in srgb, var(--pine-deep) 70%, black 30%);

  /* Light falls on the green and dies in the corners. */
  background: var(--pine-deep);
  background: radial-gradient(ellipse 95% 62% at 54% 66%, var(--fx-lit) 0%, var(--pine-deep) 58%, var(--fx-dark) 100%);
}

.fx-overlay.fx-out { opacity: 0; }

.fx-stage {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

/* Stage furniture. The green is a soft-edged mound of lit pine, so the
   readout can sit over it without crossing a hard edge; the hole is
   darker than the scrim with a lit far lip, which is what makes it a
   hole rather than a disc. */
.fx-grad-green stop:nth-child(1) { stop-color: var(--pine-soft); stop-opacity: 0.30; }
.fx-grad-green stop:nth-child(2) { stop-color: var(--pine-soft); stop-opacity: 0; }
.fx-lip { fill: var(--pine-soft); opacity: 0.55; }
.fx-hole { fill: var(--fx-dark); }
.fx-ring { fill: none; stroke: var(--fx-gold); stroke-width: 1.5; opacity: 0; }
.fx-shadow { fill: var(--fx-dark); opacity: 0; }

.fx-flag line { stroke: var(--cream-soft); stroke-width: 2; }
.fx-flag .fx-pennant { fill: var(--fx-red); }

/* The treeline the hook disappears into: a lighter silhouette, the
   same material as the green, drawn over the ball. */
.fx-trees path { fill: var(--pine-soft); opacity: 0.34; }

/* The tracer is three strokes on one path, no filter: a wide halo, the
   trail, and a short bright head behind the ball. Layering reads as a
   glow that decays along the tail, and costs nothing per frame — a
   filter on a stroke whose dash moves every frame re-rasters every
   frame, which is where the stutter came from. */
.fx-tracer path {
  fill: none;
  stroke: var(--fx-gold);
  stroke-linecap: round;
  stroke-linejoin: round;
}
.fx-halo { stroke-width: 11; opacity: 0.13; }
.fx-line-path { stroke-width: 2.2; opacity: 0.62; }
.fx-head { stroke: var(--fx-gold-hot); stroke-width: 3; transition: opacity 260ms ease; }

.fx-tracer { transition: opacity 320ms ease; }
/* Once the number is up, the line steps back and lets it lead. */
.fx-quiet .fx-tracer { opacity: 0.32; }

/* The ball is a sphere with the light top-left, and carries its own
   glow so it reads as the hot end of the tracer. */
.fx-grad-ball stop:nth-child(1) { stop-color: #ffffff; }
.fx-grad-ball stop:nth-child(2) { stop-color: var(--on-pine); }
.fx-grad-ball stop:nth-child(3) { stop-color: var(--cream-soft); }
.fx-grad-glow stop:nth-child(1) { stop-color: var(--fx-gold); stop-opacity: 0.42; }
.fx-grad-glow stop:nth-child(2) { stop-color: var(--fx-gold); stop-opacity: 0; }

/* Readout. Positioned at the hole by fx.js off the live SVG CTM; this
   is the fallback if that lookup fails. */
.fx-readout {
  position: absolute;
  top: 64%;
  left: 0;
  right: 0;
  padding: 0 22px;
  text-align: center;
  opacity: 0;
  /* --fx-dx carries the hole's offset from the centre of the frame, so
     the number rises out of the hole rather than out of the middle. */
  transform: translate(var(--fx-dx, 0px), 32px);
}
.fx-readout.fx-in { animation: fx-rise 520ms cubic-bezier(0.2, 0.7, 0.25, 1) forwards; }

.fx-label {
  margin: 0;
  font-family: var(--font-display);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: var(--cream-soft);
}
.fx-value {
  margin: 2px 0 0;
  font-family: var(--font-display);
  font-size: 64px;
  font-weight: 600;
  line-height: 1.05;
  font-variant-numeric: tabular-nums;
  color: var(--on-pine);
}
.fx-readout-up .fx-value { color: var(--fx-red); }
/* The settle: a small pop as the roll lands on the final figure. */
.fx-value.fx-pop { animation: fx-pop 320ms cubic-bezier(0.2, 0.9, 0.3, 1.2); }

.fx-delta {
  margin: 4px 0 0;
  font-size: 17px;
  font-weight: 600;
  color: var(--cream-soft);
  font-variant-numeric: tabular-nums;
  opacity: 0;
}
.fx-line {
  margin: 14px 0 0;
  font-size: 16px;
  font-style: italic;
  color: var(--cream-soft);
  opacity: 0;
}
/* Delta and line wait for the number to settle, then follow in turn. */
.fx-settled .fx-delta { animation: fx-fade 360ms ease 80ms forwards; }
.fx-settled .fx-line { animation: fx-fade 420ms ease 420ms forwards; }

@keyframes fx-in { from { opacity: 0; } }
@keyframes fx-fade { to { opacity: 1; } }
@keyframes fx-rise { to { opacity: 1; transform: translate(var(--fx-dx, 0px), 0); } }
@keyframes fx-pop { 50% { transform: scale(1.06); } }

/* Reduced motion: the same scrim and the same readout, faded up, with
   the final number shown outright. The moment still exists; nothing
   moves, and the exit is the plain fade rather than the morph. */
.fx-still .fx-tracer,
.fx-still .fx-ball,
.fx-still .fx-trees,
.fx-still .fx-ring,
.fx-still .fx-shadow { display: none; }
.fx-still .fx-delta,
.fx-still .fx-line { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .fx-overlay { animation: none; }
  .fx-readout { transform: translate(var(--fx-dx, 0px), 0); }
  .fx-readout.fx-in { animation: fx-fade 200ms linear forwards; }
  .fx-value.fx-pop { animation: none; }
}

/* The exit. fx.js names the settled figure and the result screen's
   index `fx-index` for one view transition, so the big number shrinks
   into the small one while the scrim lifts. Where the API is missing
   the overlay fades instead, and these rules do nothing. */
::view-transition-group(fx-index) {
  animation-duration: 560ms;
  animation-timing-function: cubic-bezier(0.2, 0.7, 0.2, 1);
}
::view-transition-old(fx-index),
::view-transition-new(fx-index) {
  height: 100%;
  animation-duration: 560ms;
}
::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: 440ms;
}
@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) { animation: none !important; }
}
