/* ── Neon frame: a light that runs around the picture ──────────────────────
 *
 * Loaded from index.html AFTER dist/assets/app.css, and deliberately overriding
 * the ring that bundle paints. It exists because that ring is wrong in a way
 * only shows up on non-square tiles.
 *
 * THE BUG IT UNDOES
 * -----------------
 * The @keyframes neon-run shipped in app.css rotated the whole ::before layer
 * by one turn. That layer carries BOTH the conic gradient and the mask, so the
 * mask rotated with it and stopped lining up with the tile's edges. On a 16:9
 * photograph the painted border tilted off the frame and the highlight read as
 * a skewed smear rather than a light going round the picture -- which is
 * exactly what "a static neon border" looked like on screen. A tile that
 * happened to be nearly square hid the bug, so the effect only ever looked
 * broken on real photographs.
 *
 * WHAT MOVES NOW
 * --------------
 * The layer never transforms. What animates is the conic gradient's own start
 * angle, --neon-angle, so the bright head walks the perimeter and follows the
 * rectangle exactly: it runs down the long edge, turns the corner and comes
 * back along the short edge, like a light circling a picture frame.
 *
 * `--neon-angle` is REGISTERED with @property because an unregistered custom
 * property is an untyped token the engine cannot interpolate -- a 0deg to
 * 360deg keyframe on one flips once at the midpoint instead of sweeping. That
 * flip is worse than the static ring, which is why the animation is gated on
 * @supports below: a browser without registration gets the gradient and no
 * motion at all, i.e. a lit frame with the head parked at the top.
 *
 * `inherits: false` keeps the angle on the ring itself. The tile is transformed
 * every frame by the field engine, and an inherited animated property would be
 * recomputed against a moving box -- which is how the gradient ends up sliding
 * relative to the picture it is supposed to be travelling around.
 *
 * TWO LAYERS, ONE CLOCK
 * ---------------------
 * ::before is the crisp stroke. ::after is the halo: the same sweep, spread
 * over a wider arc and blurred, so the light blooms off the edge instead of
 * being cut flat against it. They are one pseudo-element apart because a
 * pseudo-element can only carry one background, and a neon frame needs both the
 * stroke and the glow.
 *
 * Both animate over the same duration from the same start angle, so the halo
 * stays locked to the head of the stroke on every lap rather than drifting
 * ahead of it.
 *
 * `mask` is what turns the full-size gradient into a ring. Without the composite
 * the gradient would fill the whole tile and hide the photograph. The -webkit-
 * forms are repeated because Safari needs the prefixed one, and the standard
 * alone would leave Safari with an opaque tile.
 *
 * src/neon-frame.css carries the same rules, so a rebuild produces the same
 * frame and this file becomes a no-op rather than a divergence.
 */
@property --neon-angle {
  syntax: '<angle>';
  inherits: false;
  initial-value: 0deg;
}

.image-tile {
  --neon: #22f5d0;
  --neon-soft: #7ef9ff;
  --neon-run: 2.6s;
}

/* The crisp stroke: a white-hot head at 0deg, a comet tail fading back round
   to 250deg, and a clear gap before the head returns -- so the eye reads a
   single light travelling, not a ring that pulses in place. */
.image-tile.exact::before,
.image-tile.featured::before {
  padding: 1px;
  background: conic-gradient(from var(--neon-angle, 0deg),
    var(--neon) 0deg,
    var(--neon-soft) 38deg,
    #ffffff 70deg,
    var(--neon-soft) 102deg,
    var(--neon) 148deg,
    #22f5d05c 250deg,
    #22f5d000 344deg,
    var(--neon) 360deg);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask-composite: exclude;
  /* Any transform here drags the mask off the frame, which is the bug above.
     Stated explicitly so a later edit cannot reintroduce it by accident. */
  transform: none;
}

/* The halo. Same sweep from the same angle, spread wider so the light spills
   both inward off the edge and outward into the dark around the tile. */
.image-tile.exact::after,
.image-tile.featured::after {
  padding: 2.5px;
  background: conic-gradient(from var(--neon-angle, 0deg),
    #22f5d000 0deg,
    #22f5d085 62deg,
    #b6fdffc4 92deg,
    #22f5d05e 138deg,
    #22f5d01f 250deg,
    #22f5d000 348deg,
    #22f5d000 360deg);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask-composite: exclude;
  filter: blur(4px);
  opacity: .82;
  /* The stock rule animated box-shadow here instead. A box-shadow breathes in
     place -- it cannot run along the perimeter, because the shadow's shape is
     the tile's shape and never travels round it. */
  box-shadow: none;
}

/* The head sweeps one full turn clockwise; the halo sweeps the same turn over
   the same duration, so the two re-align every lap instead of drifting apart.
 *
 * NOTE this is a REPLACEMENT for app.css's neon-run, not an addition. Keyframes
 * are global and the last definition of a name wins, so this block takes the
 * name over from the built one for the whole page -- which is exactly what
 * retires its `rotate(1turn)`, and also why the ungated reset below matters.
 */
@keyframes neon-run {
  from { --neon-angle: 0deg; }
  to { --neon-angle: 360deg; }
}

/* Both pseudo-elements default to NO animation, outside any @supports gate.
 *
 * This has to be unconditional, and it is the subtle part of this file.
 * app.css applies `animation: neon-run ...` to these selectors with no feature
 * test of its own, so a browser that cannot register @property would otherwise
 * run the keyframes above -- animating an untyped token, which snaps at the
 * halfway point. The ring would blink once per cycle and look broken in a way
 * the old static ring never did.
 *
 * Setting `animation: none` first and enabling it only inside @supports makes
 * the unsupported case the safe one: a lit frame, head parked at the top, no
 * motion. It also has to win on order against app.css, which this file loads
 * after, so the @supports rule below re-asserts the animation.
 */
.image-tile.exact::before,
.image-tile.featured::before,
.image-tile.exact::after,
.image-tile.featured::after { animation: none; }

@supports at-rule(@property) {
  .image-tile.exact::before,
  .image-tile.featured::before,
  .image-tile.exact::after,
  .image-tile.featured::after {
    animation: neon-run var(--neon-run) linear infinite;
  }
}

/* A featured tile -- the exact answer to the query, or a photograph just added
   on vai-avatar -- runs faster and thinner, so the newest photographs are
   distinguishable from an exact hit by pace as well as by size. */
.image-tile.featured { --neon-run: 2s; }
.image-tile.featured::before { padding: 1.5px; }

/* A slow, low-frequency sweep reads as "lit"; a fast strobe reads as an alarm,
   and this frame marks a correct answer rather than an error. */
@media (prefers-reduced-motion: reduce) {
  .image-tile.exact::before, .image-tile.exact::after,
  .image-tile.featured::before, .image-tile.featured::after { animation: none; }
}
