/* ---------- Shared value grammar ---------- */

/* A lamp label is an embedded HUD lamp, never a sticker. A held lamp is itself an achievement,
   so its normal state is powered at full colour: a glass lens in the lamp's own saturated
   colour, a lit top rim, a restrained inner core and a crisp bezel. Reached inside the period,
   the same lamp is driven harder — nothing is swapped: the lens brightens from its top, the rim
   turns white-hot, the core blooms, a halo sized by --lamp-glow spills around it (on ::before)
   and, once on screen, it keeps a slow, gentle glow; EX-HARD, FULL COMBO and PERFECT add a rare
   light sweep across the glass. Only FAILED and NO PLAY are unpowered glass.
   Perpetual motion runs only on labels in view (.is-motion-live, set by app.js), and an
   updated lamp's entrance (the shared stamp, plus sparks from HARD) only the first time that
   achievement is presented in view (.is-motion-intro). */
.lamp-pill {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 20px;
  padding: 0 8px;
  border: 1px solid color-mix(in srgb, var(--lamp, #c3c9d0) 58%, #0b1322);
  border-radius: 3px;
  color: var(--lamp-on, #4a5360);
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.4), rgba(255, 255, 255, 0.12) 44%, rgba(255, 255, 255, 0) 52%, rgba(6, 12, 24, 0.07)),
    radial-gradient(85% 95% at 50% 125%, color-mix(in srgb, var(--lamp-hi, #e2e6ea) 55%, transparent), transparent 72%),
    var(--lamp-body, linear-gradient(180deg, color-mix(in srgb, var(--lamp, #c3c9d0) 80%, #ffffff), var(--lamp, #c3c9d0) 54%, color-mix(in srgb, var(--lamp, #c3c9d0) 86%, #0b1322)));
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.55),
    inset 0 0 0 1px rgba(255, 255, 255, 0.12),
    inset 0 -2px 3px -1px color-mix(in srgb, var(--lamp-hi, #e2e6ea) 65%, transparent),
    0 1px 1px rgba(11, 19, 34, 0.14);
  font-family: var(--font-num);
  font-size: var(--fs-meta);
  font-weight: 700;
  letter-spacing: 0.05em;
  line-height: 1;
  text-shadow:
    0 0 3px color-mix(in srgb, var(--lamp-hi, #ffffff) 45%, transparent),
    0 1px 0 rgba(0, 0, 0, 0.26);
  white-space: nowrap;
}

.lamp-pill.lamp-none,
.lamp-pill.lamp-exhard,
.lamp-pill.lamp-fullcombo,
.lamp-pill.lamp-perfect { text-shadow: 0 0 4px rgba(255, 255, 255, 0.6), 0 1px 0 rgba(255, 255, 255, 0.45); }

/* Held FULL COMBO and PERFECT lead the held lamps: the bright sheen body, a prismatic (FC) or
   gold (PERFECT) bezel and a soft static glow. No motion in the held state. */
.lamp-pill.lamp-fullcombo:not(.is-lit-update) {
  border: 1px solid transparent;
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0.14) 44%, rgba(255, 255, 255, 0) 52%, rgba(6, 12, 24, 0.05)) padding-box,
    var(--lamp-sheen) padding-box,
    linear-gradient(100deg, #c9547a, #c08f12 27%, #3f9a4a 50%, #2f86b8 73%, #6a4fc4) border-box;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.75),
    0 0 6px rgba(155, 123, 255, 0.42),
    0 1px 1px rgba(11, 19, 34, 0.16);
}

/* PERFECT is exceptional even when merely held, so it never reads as EX-HARD from a distance:
   where EX-HARD is saturated yellow glass, PERFECT is pearl white-gold glass with a prismatic
   foot band, a white-gold jewel bezel, a white corona, a sparkle and (in view, motion allowed)
   a slow shimmer across the glass. Still the same label object. */
.lamp-pill.lamp-perfect:not(.is-lit-update) {
  border: 1px solid transparent;
  color: #5a3c00;
  background:
    var(--label-sweep, linear-gradient(transparent, transparent)) padding-box,
    linear-gradient(180deg, rgba(255, 255, 255, 0.9), rgba(255, 255, 255, 0.25) 46%, rgba(255, 255, 255, 0) 52%) padding-box,
    linear-gradient(100deg, rgba(255, 143, 162, 0.42), rgba(255, 212, 94, 0.42) 27%, rgba(143, 224, 122, 0.36) 50%, rgba(111, 202, 245, 0.42) 73%, rgba(179, 155, 255, 0.42)) bottom / 100% 34% no-repeat padding-box,
    linear-gradient(180deg, #ffffff, #fff8e0 55%, #ffe7a0) padding-box,
    linear-gradient(110deg, #9a6b00, #fff7d1 24%, #d69a00 46%, #ffffff 62%, #c98a00 82%, #fff0b0) border-box;
  box-shadow:
    inset 0 1px 0 #ffffff,
    inset 0 0 0 1px rgba(255, 255, 255, 0.7),
    0 0 0 1px rgba(255, 255, 255, 0.95),
    0 0 0 2px rgba(214, 154, 0, 0.45),
    0 0 8px rgba(255, 200, 40, 0.75),
    0 0 18px rgba(255, 232, 160, 0.6),
    0 1px 1px rgba(11, 19, 34, 0.16);
  text-shadow: 0 0 4px #ffffff, 0 1px 0 rgba(255, 255, 255, 0.8);
}

.lamp-pill.lamp-perfect:not(.is-lit-update)::after {
  content: "\2726";
  position: absolute;
  top: -6px;
  right: -4px;
  color: #ffc21a;
  font-size: 9px;
  line-height: 1;
  text-shadow: 0 0 3px rgba(255, 236, 160, 0.95), 0 0 1px #8a5d00;
  pointer-events: none;
}

/* Non-emissive glass: no light tube, no segment glow. */
.lamp-pill.lamp-none {
  border-color: #b8bfc8;
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.6), rgba(255, 255, 255, 0.1) 50%, rgba(0, 0, 0, 0.04)),
    #dde1e6;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.4), 0 1px 0 rgba(255, 255, 255, 0.7);
  color: #5d6671;
  text-shadow: 0 1px 0 rgba(255, 255, 255, 0.6);
}

.lamp-pill.lamp-failed {
  border-color: #5d646e;
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.18), rgba(255, 255, 255, 0.03) 50%, rgba(0, 0, 0, 0.12)),
    linear-gradient(180deg, #979ea8, #7c838d);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.1), inset 0 2px 3px rgba(0, 0, 0, 0.12), 0 1px 0 rgba(255, 255, 255, 0.7);
  color: #eef0f3;
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.3);
}

.lamp-pill.is-lit-update {
  border-color: color-mix(in srgb, var(--lamp-hi) 70%, #ffffff);
  background:
    var(--label-sweep, linear-gradient(transparent, transparent)),
    radial-gradient(120% 85% at 50% -12%, rgba(255, 255, 255, 0.62), rgba(255, 255, 255, 0) 58%),
    radial-gradient(90% 120% at 50% 118%, color-mix(in srgb, var(--lamp-hi) 80%, transparent), transparent 70%),
    repeating-linear-gradient(90deg, rgba(255, 255, 255, 0.065) 0 1px, transparent 1px 3px),
    linear-gradient(180deg, var(--lamp-hi), var(--lamp) 52%, var(--lamp-off));
  box-shadow:
    0 0 0 1px color-mix(in srgb, var(--lamp) 58%, transparent),
    inset 0 1px 0 rgba(255, 255, 255, 0.78),
    inset 0 -3px 5px -2px rgba(255, 255, 255, 0.7),
    inset 0 0 7px color-mix(in srgb, var(--lamp-hi) 70%, transparent);
  text-shadow:
    0 0 6px color-mix(in srgb, var(--lamp-hi) 85%, #ffffff),
    0 1px 1px rgba(0, 0, 0, 0.3);
}

/* The halo lives on its own layer so the settled glow only animates opacity. */
.lamp-pill.is-lit-update::before {
  content: "";
  position: absolute;
  inset: -1px;
  border-radius: inherit;
  box-shadow:
    0 0 calc(var(--lamp-glow) * var(--glow-scale, 1) * var(--glow-boost, 1) * 0.55) color-mix(in srgb, var(--light, var(--lamp)) 85%, transparent),
    0 0 calc(var(--lamp-glow) * var(--glow-scale, 1) * var(--glow-boost, 1) * 1.7) color-mix(in srgb, var(--light, var(--lamp)) 42%, transparent);
  pointer-events: none;
}

/* HARD and above are driven harder than EASY / CLEAR: a white-hot double rim and a stronger
   halo (its pulse is in the motion section). */
.lamp-pill.is-lit-update:is(.lamp-hard, .lamp-exhard) {
  --glow-boost: 1.35;
  border-color: #ffffff;
  box-shadow:
    0 0 0 1px color-mix(in srgb, var(--lamp) 78%, #ffffff),
    0 0 0 2.5px color-mix(in srgb, var(--light, var(--lamp)) 38%, transparent),
    inset 0 1px 0 rgba(255, 255, 255, 0.88),
    inset 0 -3px 5px -2px rgba(255, 255, 255, 0.75),
    inset 0 0 8px color-mix(in srgb, var(--lamp-hi) 80%, transparent);
}

.lamp-pill.lamp-exhard.is-lit-update { text-shadow: 0 0 5px rgba(255, 255, 255, 0.95), 0 1px 0 rgba(255, 255, 255, 0.6); }

/* EX-HARD tightened: the shared HARD+ treatment above reads blurry at this level's brighter
   halo, so EX-HARD gets its own crisper core border and a narrower halo (lower --glow-boost)
   while keeping the label-sweep motion and the HARD/EX-HARD hierarchy intact. */
.lamp-pill.is-lit-update.lamp-exhard {
  --glow-boost: 1.05;
  border-color: color-mix(in srgb, var(--lamp) 55%, #0b1322);
  box-shadow:
    0 0 0 1px color-mix(in srgb, var(--lamp) 88%, #000000),
    0 0 0 1.5px color-mix(in srgb, var(--light, var(--lamp)) 30%, transparent),
    inset 0 1px 0 rgba(255, 255, 255, 0.82),
    inset 0 -3px 5px -2px rgba(255, 255, 255, 0.7),
    inset 0 0 6px color-mix(in srgb, var(--lamp-hi) 65%, transparent);
}

.lamp-pill.lamp-fullcombo.is-lit-update {
  border-color: #ffffff;
  background:
    var(--label-sweep, linear-gradient(transparent, transparent)),
    radial-gradient(120% 85% at 50% -12%, rgba(255, 255, 255, 0.55), rgba(255, 255, 255, 0) 58%),
    repeating-linear-gradient(90deg, rgba(255, 255, 255, 0.08) 0 1px, transparent 1px 3px),
    var(--lamp-sheen);
  box-shadow:
    0 0 0 1px rgba(122, 91, 208, 0.7),
    inset 0 1px 0 rgba(255, 255, 255, 0.7),
    inset 0 -3px 5px -2px rgba(255, 255, 255, 0.7);
  text-shadow: 0 0 5px rgba(255, 255, 255, 0.9), 0 1px 0 rgba(255, 255, 255, 0.6);
}

.lamp-pill.lamp-fullcombo.is-lit-update::before {
  box-shadow:
    0 0 calc(var(--lamp-glow) * var(--glow-scale, 1) * 1.3) rgba(155, 123, 255, 0.66),
    -3px 0 calc(var(--lamp-glow) * var(--glow-scale, 1) * 0.6) rgba(255, 143, 162, 0.5),
    3px 0 calc(var(--lamp-glow) * var(--glow-scale, 1) * 0.6) rgba(111, 202, 245, 0.5);
}

/* A second, prismatic rim just outside the bezel: FULL COMBO's depth beyond EX-HARD. */
.lamp-pill.lamp-fullcombo.is-lit-update::after {
  content: "";
  position: absolute;
  inset: -3px;
  padding: 1.5px;
  border-radius: 5px;
  background: linear-gradient(100deg, #ff8fa2, #ffd45e, #8fe07a, #6fcaf5, #b39bff, #ff8fa2) 0 0 / 200% 100%;
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
  pointer-events: none;
}

/* PERFECT is the top lamp: the gold body switched to molten, with a sparkle. */
/* Updated PERFECT: the pearl body switched fully on — a brighter prismatic foot, a white-hot
   rim and (below) a wide corona. */
.lamp-pill.lamp-perfect.is-lit-update {
  border-color: #ffffff;
  color: #4a3100;
  background:
    var(--label-sweep, linear-gradient(transparent, transparent)),
    radial-gradient(120% 85% at 50% -12%, rgba(255, 255, 255, 0.95), rgba(255, 255, 255, 0) 58%),
    linear-gradient(100deg, rgba(255, 110, 140, 0.85), rgba(255, 200, 60, 0.85) 27%, rgba(110, 215, 95, 0.78) 50%, rgba(80, 190, 245, 0.85) 73%, rgba(160, 130, 255, 0.85)) bottom / 100% 46% no-repeat,
    repeating-linear-gradient(90deg, rgba(255, 255, 255, 0.1) 0 1px, transparent 1px 3px),
    linear-gradient(180deg, #ffffff, #fffbea 50%, #ffe9a6);
  box-shadow:
    0 0 0 1px rgba(184, 131, 0, 0.85),
    0 0 0 2px #ffffff,
    0 0 0 3.5px rgba(255, 190, 30, 0.8),
    0 0 10px rgba(255, 200, 40, 0.9),
    0 0 24px rgba(255, 236, 170, 0.75),
    inset 0 1px 0 #ffffff,
    inset 0 -3px 5px -2px rgba(255, 255, 255, 0.8);
  text-shadow: 0 0 5px #ffffff, 0 0 2px #ffffff, 0 1px 0 rgba(255, 255, 255, 0.8);
}

.lamp-pill.lamp-perfect.is-lit-update {
  outline: 1.5px solid rgba(255, 244, 200, 0.98);
  outline-offset: 3px;
}

/* Updated PERFECT: a white-hot core, a wide golden corona and a rose-gold outer bloom. */
.lamp-pill.lamp-perfect.is-lit-update::before {
  inset: -2px;
  box-shadow:
    0 0 0 1px rgba(255, 250, 225, 0.9),
    0 0 calc(var(--lamp-glow) * var(--glow-scale, 1) * 0.55) rgba(255, 255, 255, 0.85),
    0 0 calc(var(--lamp-glow) * var(--glow-scale, 1) * 1.5) rgba(255, 194, 26, 0.9),
    0 0 calc(var(--lamp-glow) * var(--glow-scale, 1) * 2.8) rgba(255, 150, 60, 0.42);
}

.lamp-pill.lamp-perfect.is-lit-update::after {
  content: "\2726";
  position: absolute;
  top: -8px;
  right: -6px;
  color: #fff4c0;
  font-size: 13px;
  line-height: 1;
  text-shadow: 0 0 3px #ffffff, 0 0 7px rgba(255, 196, 20, 0.98), 0 0 12px rgba(255, 150, 60, 0.7);
  pointer-events: none;
}

/* Record rows: an updated DJ LEVEL or lamp carries a filled ▲ on its top-right corner as well
   as its glow — the same mark and orange (--event) as the period-update ▲ beside scores and BP.
   It is laid over the label's corner, so the label and the values beside it never move. */
.update-delta {
  position: absolute;
  z-index: 3;
  top: -6px;
  right: -6px;
  color: var(--event);
  font-family: var(--font-ui);
  font-size: 9px;
  font-style: normal;
  font-weight: 900;
  line-height: 1;
  letter-spacing: 0;
  text-shadow: 0 0 2px #ffffff, 0 0 2px #ffffff, 0 0 4px rgba(255, 255, 255, 0.9);
  pointer-events: none;
}

/* An updated PERFECT's sparkle yields the top-right corner to the ▲. */
.lamp-pill.lamp-perfect.is-lit-update.has-update-delta::after { right: auto; left: -6px; }

/* Every update label — score / DJ LEVEL milestone and lamp alike — enters the same way: it is
   stamped, landing from small with a firm overshoot, already lit (no flicker). */
@keyframes score-label-stamp {
  0% { opacity: 0; transform: scale(0.5); filter: brightness(1.35); }
  40% { opacity: 1; transform: scale(1.16); }
  70% { transform: scale(0.96); }
  100% { transform: scale(1); filter: none; }
}

/* Then the lit lamp keeps a slow, gentle glow: the halo breathes, the lamp never dims out. */
@keyframes lamp-glow-settle {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.55; }
}

@keyframes lamp-sparkle {
  0%, 100% { opacity: 1; transform: scale(1) rotate(0deg); }
  50% { opacity: 0.45; transform: scale(0.72) rotate(45deg); }
}

/* HARD and above: the halo swells and fades in a clearly visible beat. */
@keyframes lamp-glow-pulse {
  0%, 100% { opacity: 0.5; transform: scale(1); }
  50% { opacity: 1; transform: scale(1.08); }
}

/* The prismatic rim turns through the spectrum by a filter rather than a moving background. */
@keyframes lamp-prism-shift {
  to { filter: hue-rotate(360deg); }
}

/* A slow, narrow band of light crossing the glass of a high-value lit label: most of the
   cycle is still, the pass itself is short and soft (never a marquee). The band moves by
   transform, not an animated custom property: app.js gives each sweeping label one clip span
   (.label-sweep) whose ::before crosses the glass, so the label itself is not restyled and
   repainted every frame (measured: far less idle main-thread work). The span sits under the
   label text, inside its rim. */
@keyframes label-sweep {
  0%, 58% { transform: translateX(-100%); }
  100% { transform: translateX(230%); }
}

.has-label-sweep { isolation: isolate; }

.label-sweep {
  position: absolute;
  z-index: -1;
  inset: 0;
  overflow: hidden;
  border-radius: inherit;
  pointer-events: none;
}

.label-sweep::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: 44%;
  transform: translateX(-100%);
  background: linear-gradient(104deg, transparent, rgba(255, 255, 255, 0.16) 30%, rgba(255, 255, 255, 0.66) 50%, rgba(255, 255, 255, 0.16) 70%, transparent);
}

/* Motion is powered only where it can be seen (see the lamp comment above). The loop grows with
   the achievement, and every loop is short enough to be seen while reading:
   - EASY / CLEAR (and A / AA / AAA): the halo breathes slowly;
   - HARD and above: the halo visibly pulses;
   - EX-HARD and above: a light sweep crosses the glass every few seconds;
   - FULL COMBO / PERFECT (and MAX): a quicker prismatic pass, a shimmering second rim or a
     twinkling sparkle.
   MAX- is always lit while displayed (.is-perpetual-lit), its wording unchanged.
   Every loop runs on the page clock (--idle-clock / --motion-phase, set by app.js when a label
   comes into view), so a label re-rendered by a tab, period or sort change continues its loop
   where it was instead of restarting (a restart reads as a fresh entrance). Only achievement
   labels ever get .is-motion-intro, once per presented achievement; held labels never do. */
:is(.lamp-pill.is-lit-update, .dj-rank-badge.is-rank-update, .achievement-milestone.is-milestone-glow).is-motion-intro {
  animation: score-label-stamp 440ms var(--ease) backwards;
}

.lamp-pill.is-lit-update.is-motion-live::before,
.dj-rank-badge:is(.is-rank-update, .is-perpetual-lit).is-motion-live::before,
.achievement-milestone.is-milestone-glow.is-motion-live:not(.milestone-maximum)::before {
  animation: lamp-glow-settle 3600ms ease-in-out var(--idle-clock, 0ms) infinite;
}

.lamp-pill.is-lit-update:is(.lamp-hard, .lamp-exhard, .lamp-fullcombo, .lamp-perfect).is-motion-live::before,
.dj-rank-badge:is(.is-rank-update, .is-perpetual-lit):is(.rank-max-minus, .rank-max).is-motion-live::before,
.achievement-milestone.is-milestone-glow:is(.milestone-max-minus, .milestone-max-minus-update).is-motion-live::before {
  animation: lamp-glow-pulse 2200ms ease-in-out var(--idle-clock, 0ms) infinite;
}

.lamp-pill.is-lit-update:is(.lamp-fullcombo, .lamp-perfect).is-motion-live::before {
  animation-duration: 1800ms;
}

.lamp-pill.lamp-fullcombo.is-lit-update.is-motion-live::after {
  animation: lamp-prism-shift 3200ms linear var(--idle-clock, 0ms) infinite;
}

.lamp-pill.lamp-perfect.is-lit-update.is-motion-live::after,
.achievement-milestone.milestone-maximum.is-milestone-glow.is-motion-live::before {
  animation: lamp-sparkle 1800ms ease-in-out var(--idle-clock, 0ms) infinite;
}

.achievement-milestone.milestone-maximum.is-milestone-glow.is-motion-live::after {
  animation: lamp-sparkle 1800ms ease-in-out calc(var(--idle-clock, 0ms) + 900ms) infinite;
}

/* Held PERFECT: a slow, soft shimmer and a twinkle, quieter than an update. */
.lamp-pill.lamp-perfect:not(.is-lit-update).is-motion-live > .label-sweep::before {
  background: linear-gradient(104deg, transparent, rgba(255, 255, 255, 0.2) 30%, rgba(255, 255, 255, 0.7) 50%, rgba(255, 255, 255, 0.2) 70%, transparent);
  animation: label-sweep 5200ms cubic-bezier(0.45, 0, 0.3, 1) var(--motion-phase, 900ms) infinite;
}

.lamp-pill.lamp-perfect:not(.is-lit-update).is-motion-live::after {
  animation: lamp-sparkle 2600ms ease-in-out var(--idle-clock, 0ms) infinite;
}

/* A lit HARD-or-better lamp adds a spark burst to the shared stamp, sized by its rank
   (HARD < EX-HARD < FULL COMBO < PERFECT): more sparks (set by app.js), a longer reach and
   life, a shock ring from EX-HARD, prismatic sparks for FULL COMBO and white-gold for PERFECT.
   A spark appears, then only travels and fades — it never blinks. The burst is a temporary
   child of the label (removed after it ends), laid behind the label's text and never in
   layout. */
.lamp-pill.has-label-burst { isolation: isolate; }

.label-burst {
  --burst-reach: 16px;
  --burst-ms: 520ms;
  --spark: var(--lamp-hi, #ffffff);
  --spark-size: 3.6px;
  position: absolute;
  z-index: -1;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
}

.lamp-exhard > .label-burst { --burst-reach: 20px; --burst-ms: 600ms; --spark: #ffd21f; --spark-size: 3.9px; }
.lamp-fullcombo > .label-burst { --burst-reach: 24px; --burst-ms: 660ms; --spark-size: 4.2px; --spark: #a98bff; }
.lamp-perfect > .label-burst { --burst-reach: 28px; --burst-ms: 720ms; --spark: #ffc21a; --spark-size: 4.6px; }

.label-burst i {
  position: absolute;
  top: calc(50% - var(--spark-size) / 2);
  left: calc(50% - var(--spark-size) / 2);
  width: var(--spark-size);
  height: var(--spark-size);
  border-radius: 50%;
  background: radial-gradient(circle, #ffffff 0 35%, var(--spark) 75%);
  box-shadow: 0 0 3px 1px var(--spark), 0 0 7px var(--spark);
  opacity: 0;
  animation: label-spark var(--burst-ms) cubic-bezier(0.15, 0.7, 0.3, 1) 40ms both;
}

.label-burst i:nth-child(3n + 2) { --spark-travel: 0.78; }
.label-burst i:nth-child(3n) { --spark-travel: 0.9; }

.lamp-fullcombo > .label-burst i:nth-child(5n + 1) { --spark: #ff6f91; }
.lamp-fullcombo > .label-burst i:nth-child(5n + 2) { --spark: #ffd24a; }
.lamp-fullcombo > .label-burst i:nth-child(5n + 3) { --spark: #67d98b; }
.lamp-fullcombo > .label-burst i:nth-child(5n + 4) { --spark: #4cc3ff; }
.lamp-fullcombo > .label-burst i:nth-child(5n) { --spark: #a98bff; }
.lamp-perfect > .label-burst i:nth-child(2n) { --spark: #fff4c0; }

/* EX-HARD and above: one thin shock ring that leaves the label's own outline. */
:is(.lamp-exhard, .lamp-fullcombo, .lamp-perfect) > .label-burst::before {
  content: "";
  position: absolute;
  inset: -2px;
  border: 1.5px solid var(--spark);
  border-radius: 6px;
  opacity: 0;
  animation: label-burst-ring var(--burst-ms) cubic-bezier(0.15, 0.7, 0.3, 1) 40ms both;
}

.lamp-perfect > .label-burst::before { border-width: 2px; border-color: #ffe27a; box-shadow: 0 0 6px rgba(255, 210, 60, 0.7); }

/* Sparks leave from the label's rim (an ellipse through its half width / height, --hw / --hh
   set by app.js), so even a wide label shows its whole burst around it. */
@keyframes label-spark {
  0% { opacity: 0; transform: translate(calc(cos(var(--a)) * var(--hw, 20px)), calc(sin(var(--a)) * var(--hh, 10px))) scale(1); }
  12% { opacity: 1; }
  100% { opacity: 0; transform: translate(calc(cos(var(--a)) * (var(--hw, 20px) + var(--burst-reach) * var(--spark-travel, 1))), calc(sin(var(--a)) * (var(--hh, 10px) + var(--burst-reach) * var(--spark-travel, 1)))) scale(0.35); }
}

@keyframes label-burst-ring {
  0% { opacity: 0; inset: -2px; }
  15% { opacity: 0.85; }
  100% { opacity: 0; inset: calc(var(--burst-reach) * -0.8); }
}

:is(.lamp-pill.is-lit-update.lamp-exhard,
  .achievement-milestone.is-milestone-glow:is(.milestone-max-minus, .milestone-max-minus-update),
  .dj-rank-badge:is(.is-rank-update, .is-perpetual-lit).rank-max-minus).is-motion-live > .label-sweep::before {
  animation: label-sweep 4200ms cubic-bezier(0.45, 0, 0.3, 1) var(--motion-phase, 900ms) infinite;
}

:is(.lamp-pill.is-lit-update:is(.lamp-fullcombo, .lamp-perfect),
  .achievement-milestone.is-milestone-glow.milestone-maximum,
  .dj-rank-badge:is(.is-rank-update, .is-perpetual-lit).rank-max).is-motion-live > .label-sweep::before {
  background: linear-gradient(104deg, transparent, rgba(255, 190, 220, 0.3) 28%, rgba(255, 255, 255, 0.78) 50%, rgba(170, 225, 255, 0.3) 72%, transparent);
  animation: label-sweep 3200ms cubic-bezier(0.45, 0, 0.3, 1) var(--motion-phase, 900ms) infinite;
}

/* Without viewport observation (very old engines) lit labels simply stay lit and still. */

/* DJ ranks share the lamp grammar: the same instrument plate in its normal working state when
   held, lit up when reached inside the period. */
.dj-rank-badge,
.achievement-milestone {
  --rank: #8a94a0;
  --rank-hi: #bcc3cc;
  --rank-tint: #f2f4f6;
  --rank-ink: #4f5965;
  --rank-on: #ffffff;
  --rank-glow: 0px;
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid color-mix(in srgb, var(--rank) 58%, #0b1322);
  border-radius: 3px;
  color: var(--rank-on);
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.36), rgba(255, 255, 255, 0.1) 44%, rgba(255, 255, 255, 0) 52%, rgba(6, 12, 24, 0.07)),
    radial-gradient(85% 95% at 50% 125%, color-mix(in srgb, var(--rank-hi) 50%, transparent), transparent 72%),
    linear-gradient(180deg, color-mix(in srgb, var(--rank) 80%, #ffffff), var(--rank) 54%, color-mix(in srgb, var(--rank) 86%, #0b1322));
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.5),
    inset 0 0 0 1px rgba(255, 255, 255, 0.1),
    inset 0 -2px 3px -1px color-mix(in srgb, var(--rank-hi) 60%, transparent),
    0 1px 1px rgba(11, 19, 34, 0.14);
  text-shadow: 0 0 3px color-mix(in srgb, var(--rank-hi) 45%, transparent), 0 1px 0 rgba(0, 0, 0, 0.2);
  font-family: var(--font-num);
  font-weight: 700;
  line-height: 1;
  white-space: nowrap;
}

.dj-rank-badge {
  min-width: 30px;
  min-height: 18px;
  padding: 0 5px;
  font-size: var(--fs-unit);
  letter-spacing: 0.04em;
}

.achievement-milestone {
  min-height: 20px;
  padding: 0 7px;
  font-family: var(--font-ui);
  font-size: var(--fs-micro);
  font-weight: 800;
  letter-spacing: 0.01em;
}

.rank-a,
.milestone-a { --light: #6f8196; --rank-off: #5d6f84; --rank: #63788f; --rank-hi: #b3c3d4; --rank-tint: #eef1f5; --rank-ink: #3f4a57; --rank-glow: 6px; }
.rank-aa,
.milestone-aa { --light: #10b0c4; --rank-off: #0d8c9d; --rank: #10a6ba; --rank-hi: #7ae8f4; --rank-tint: #e0f5f7; --rank-ink: #0b6571; --rank-glow: 8px; }
.rank-aaa,
.milestone-aaa-plus,
.milestone-aaa-update { --light: #ffc000; --rank-off: #e2a900; --rank: #f7bb00; --rank-hi: #fff0a0; --rank-tint: #fff4cf; --rank-ink: #7a5500; --rank-on: #342500; --rank-glow: 11px; }
.rank-max-minus,
.milestone-max-minus,
.milestone-max-minus-update { --light: #ffc83d; --rank-off: #1b2332; --rank: #1b2332; --rank-hi: #34425c; --rank-tint: #1b2332; --rank-ink: #ffd664; --rank-on: #ffd664; --rank-glow: 11px; }
.rank-max,
.milestone-maximum { --light: #a98bff; --rank-off: #111722; --rank: #111722; --rank-hi: #2a3246; --rank-tint: #111722; --rank-ink: #ffffff; --rank-on: #ffffff; --rank-glow: 14px; }

/* MAX: the rarest score keeps a prismatic rim even when it is only a held state. */
.dj-rank-badge.rank-max,
.achievement-milestone.milestone-maximum {
  border: 1.5px solid transparent;
  background:
    linear-gradient(var(--rank-tint), var(--rank-tint)) padding-box,
    linear-gradient(100deg, #ff6f91, #ffd24a 28%, #67d98b 52%, #4cc3ff 74%, #a98bff) border-box;
}

/* Same powered-on grammar as a lit lamp: backlit body, bright rim, emissive text, halo. */
.achievement-milestone.is-milestone-glow,
.dj-rank-badge:is(.is-rank-update, .is-perpetual-lit) {
  border-color: color-mix(in srgb, var(--rank-hi) 60%, #ffffff);
  color: var(--rank-on);
  background:
    var(--label-sweep, linear-gradient(transparent, transparent)),
    radial-gradient(130% 90% at 50% -10%, rgba(255, 255, 255, 0.55), rgba(255, 255, 255, 0) 60%),
    radial-gradient(90% 110% at 50% 115%, color-mix(in srgb, var(--rank-hi) 75%, transparent), transparent 70%),
    repeating-linear-gradient(90deg, rgba(255, 255, 255, 0.065) 0 1px, transparent 1px 3px),
    linear-gradient(180deg, var(--rank-hi), var(--rank) 55%, color-mix(in srgb, var(--rank) 84%, #000));
  box-shadow:
    0 0 0 1px color-mix(in srgb, var(--rank) 60%, transparent),
    inset 0 1px 0 rgba(255, 255, 255, 0.7),
    inset 0 -3px 5px -2px rgba(255, 255, 255, 0.6);
  text-shadow:
    0 0 5px color-mix(in srgb, var(--rank-hi) 85%, #ffffff),
    0 1px 1px rgba(0, 0, 0, 0.28);
}

/* Settled glow on its own layer (MAX milestones use their pseudo-elements for sparkles). */
.achievement-milestone.is-milestone-glow:not(.milestone-maximum)::before,
.dj-rank-badge:is(.is-rank-update, .is-perpetual-lit)::before {
  content: "";
  position: absolute;
  inset: -1px;
  border-radius: inherit;
  box-shadow:
    0 0 calc(var(--rank-glow) * var(--glow-scale, 1) * 0.55) color-mix(in srgb, var(--light) 85%, transparent),
    0 0 calc(var(--rank-glow) * var(--glow-scale, 1) * 1.7) color-mix(in srgb, var(--light) 40%, transparent);
  pointer-events: none;
}

.rank-aaa:not(.is-rank-update),
.milestone-aaa-update:not(.is-milestone-glow) { text-shadow: 0 1px 0 rgba(255, 255, 255, 0.45); }

.milestone-aaa-plus.is-milestone-glow,
.milestone-aaa-update.is-milestone-glow,
.rank-aaa.is-rank-update {
  border-color: #a97600;
  background:
    var(--label-sweep, linear-gradient(transparent, transparent)),
    radial-gradient(130% 90% at 50% -10%, rgba(255, 255, 255, 0.75), rgba(255, 255, 255, 0) 60%),
    linear-gradient(180deg, #fff7c4, #ffd02a 52%, #f0ad00);
  box-shadow:
    0 0 0 1px rgba(255, 238, 150, 0.95),
    0 1px 2px rgba(122, 85, 0, 0.35),
    inset 0 1px 0 rgba(255, 255, 255, 0.9),
    inset 0 -3px 5px -2px rgba(255, 255, 255, 0.72);
  color: #2e2000;
  text-shadow: 0 0 4px rgba(255, 255, 255, 0.9), 0 1px 0 rgba(255, 255, 255, 0.6);
}

/* MAX- is a night plate lit in gold; inside it the MAX-N number is the result, so it leads. */
.milestone-max-minus.is-milestone-glow,
.milestone-max-minus-update.is-milestone-glow,
.rank-max-minus:is(.is-rank-update, .is-perpetual-lit) {
  border-color: #ffd664;
  background:
    var(--label-sweep, linear-gradient(transparent, transparent)),
    linear-gradient(180deg, rgba(255, 255, 255, 0.14), rgba(255, 255, 255, 0) 55%),
    linear-gradient(180deg, var(--rank-hi), var(--rank) 70%);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.16),
    inset 0 -3px 5px -2px rgba(255, 214, 100, 0.45);
  text-shadow: 0 0 6px rgba(255, 214, 100, 0.55);
}

.milestone-max-minus.is-milestone-glow::before,
.milestone-max-minus-update.is-milestone-glow::before,
.rank-max-minus:is(.is-rank-update, .is-perpetual-lit)::before {
  box-shadow: 0 0 calc(var(--rank-glow) * var(--glow-scale, 1)) rgba(255, 200, 61, 0.55);
}

.milestone-max-minus-update b {
  margin-left: 1px;
  font-family: var(--font-num);
  font-size: 13px;
  font-weight: 700;
}

/* MAX is allowed to celebrate: prismatic body and rim, rainbow light, a pair of sparkles. */
.milestone-maximum.is-milestone-glow,
.rank-max.is-rank-update {
  position: relative;
  border: 1.5px solid transparent;
  color: #ffffff;
  background:
    var(--label-sweep, linear-gradient(transparent, transparent)) padding-box,
    linear-gradient(180deg, rgba(255, 255, 255, 0.28), rgba(255, 255, 255, 0) 55%) padding-box,
    linear-gradient(100deg, #3b2a6e, #1b2a5c 45%, #143a4a) padding-box,
    linear-gradient(100deg, #ff6f91, #ffd24a 28%, #67d98b 52%, #4cc3ff 74%, #a98bff) border-box;
  box-shadow:
    0 0 calc(var(--rank-glow) * var(--glow-scale, 1)) rgba(169, 139, 255, 0.6),
    -4px 0 calc(var(--rank-glow) * var(--glow-scale, 1) * 0.7) rgba(255, 111, 145, 0.35),
    4px 0 calc(var(--rank-glow) * var(--glow-scale, 1) * 0.7) rgba(76, 195, 255, 0.4);
  text-shadow: 0 0 6px rgba(255, 255, 255, 0.7);
}

.rank-max.is-rank-update { box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2); }

.milestone-maximum.is-milestone-glow::before,
.milestone-maximum.is-milestone-glow::after {
  position: absolute;
  font-size: 10px;
  line-height: 1;
  pointer-events: none;
}

.rank-max.is-rank-update::before {
  box-shadow:
    0 0 calc(var(--rank-glow) * var(--glow-scale, 1)) rgba(169, 139, 255, 0.6),
    -4px 0 calc(var(--rank-glow) * var(--glow-scale, 1) * 0.7) rgba(255, 111, 145, 0.35),
    4px 0 calc(var(--rank-glow) * var(--glow-scale, 1) * 0.7) rgba(76, 195, 255, 0.4);
}

.milestone-maximum.is-milestone-glow::before {
  content: "\2726";
  top: -7px;
  left: -5px;
  color: #ff8fb0;
  text-shadow: 0 0 4px rgba(255, 143, 176, 0.9);
}

.milestone-maximum.is-milestone-glow::after {
  content: "\2727";
  right: -6px;
  bottom: -7px;
  color: #6fd3ff;
  text-shadow: 0 0 4px rgba(111, 211, 255, 0.9);
}

.metric-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  min-height: 20px;
  padding: 0 7px;
  border: 1px solid transparent;
  border-radius: var(--radius-xs);
  color: var(--ink);
  background: var(--surface-3);
  font-family: var(--font-num);
  font-size: var(--fs-meta);
  font-weight: 600;
  line-height: 1;
  white-space: nowrap;
}

.metric-badge > small:first-child {
  color: var(--ink-3);
  font-family: var(--font-ui);
  font-size: var(--fs-unit);
  font-weight: 700;
  letter-spacing: 0;
}

.metric-number-parts {
  display: inline-flex;
  align-items: baseline;
  gap: 0;
  white-space: nowrap;
}

.metric-integer {
  font-size: 1em;
  font-weight: 700;
  line-height: 1;
}

.metric-decimal {
  margin: 0;
  color: var(--ink-3);
  font-size: 0.86em;
  font-weight: 500;
  line-height: 1;
}

.metric-number {
  color: var(--ink);
  font-family: var(--font-num);
  font-size: var(--fs-body);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* Appropriate CPI: the figure takes the ink of its lamp basis (EASY … F-COMBO). */
.cpi-badge[class*="cpi-lamp-"] {
  border-color: color-mix(in srgb, var(--cpi) 30%, transparent);
  background: color-mix(in srgb, var(--cpi) 7%, var(--surface));
}

.cpi-badge[class*="cpi-lamp-"] .metric-integer,
.cpi-badge[class*="cpi-lamp-"] .metric-decimal,
.cpi-value[class*="cpi-lamp-"],
.cpi-value[class*="cpi-lamp-"] .metric-decimal { color: var(--cpi); }

/* BPI: the band colours the whole figure; a badge takes a faint tint of the same band. */
.bpi-badge:is(.bpi-negative, .bpi-blue, .bpi-green, .bpi-orange) {
  border-color: color-mix(in srgb, var(--bpi) 30%, transparent);
  background: var(--bpi-tint);
}

.bpi-badge:is(.bpi-negative, .bpi-blue, .bpi-green, .bpi-orange) .metric-integer,
.bpi-badge:is(.bpi-negative, .bpi-blue, .bpi-green, .bpi-orange) .metric-decimal,
.metric-number:is(.bpi-negative, .bpi-blue, .bpi-green, .bpi-orange) .metric-integer,
.metric-number:is(.bpi-negative, .bpi-blue, .bpi-green, .bpi-orange) .metric-decimal { color: var(--bpi); }

.metric-number:is(.bpi-negative, .bpi-blue, .bpi-green, .bpi-orange) .metric-decimal,
.bpi-badge:is(.bpi-negative, .bpi-blue, .bpi-green, .bpi-orange) .metric-decimal { opacity: 0.78; }

.bp-badge,
.history-bp-label {
  --tone: #6b7785;
  --tone-tint: #f2f4f6;
}

.bp-badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  min-height: 20px;
  padding: 0 6px;
  border: 1px solid color-mix(in srgb, var(--tone) 40%, transparent);
  border-radius: var(--radius-xs);
  color: var(--tone);
  background: var(--tone-tint);
  font-family: var(--font-num);
  font-variant-numeric: tabular-nums;
  line-height: 1;
  white-space: nowrap;
}

.bp-badge small {
  font-family: var(--font-ui);
  font-size: var(--fs-unit);
  font-weight: 700;
}

.bp-badge b {
  font-size: 12px;
  font-weight: 700;
}

.bp-normal { --tone: #5d6773; --tone-tint: #f2f4f6; }
.bp-twenty { --tone: #0f7a86; --tone-tint: #e2f4f5; }
.bp-five { --tone: #9a5a00; --tone-tint: #fdf1dc; }
.bp-zero { --tone: #8a6300; --tone-tint: #fff2c2; }

.speed-badge {
  display: inline-flex;
  flex: 0 0 auto;
  align-items: center;
  min-height: 18px;
  padding: 0 5px;
  border: 1px solid var(--line-2);
  border-radius: 4px;
  color: var(--ink-2);
  background: var(--surface);
  font-family: var(--font-num);
  font-size: var(--fs-unit);
  font-weight: 600;
  letter-spacing: 0.02em;
  line-height: 1;
}

.srandom-badge {
  border-style: dashed;
  font-family: var(--font-ui);
}

/* Shared score grammar: primary figure above; muted rate beside the rank delta below. */
.detail-score-stack {
  display: inline-grid;
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: auto auto;
  justify-items: end;
  row-gap: 2px;
  min-width: 0;
  font-variant-numeric: tabular-nums;
}

.detail-score-stack > strong {
  position: relative;
  grid-row: 1;
  color: var(--ink);
  font-family: var(--font-num);
  font-size: 15px;
  font-weight: 600;
  line-height: 1;
}

.detail-score-stack > .score-secondary { grid-row: 2; }

.score-secondary {
  display: inline-flex;
  align-items: baseline;
  justify-content: flex-end;
  gap: 3px;
  min-width: max-content;
  color: var(--ink-3);
  font-family: var(--font-num);
  font-variant-numeric: tabular-nums;
  line-height: 1;
  white-space: nowrap;
}

.score-secondary > .score-rate {
  margin: 0;
  color: var(--ink-3);
  font-size: var(--fs-micro);
  font-style: normal;
  font-weight: 500;
  line-height: 1;
}

.score-secondary > .score-rank-delta {
  margin: 0;
  color: var(--rank-delta);
  font-size: var(--fs-micro);
  font-weight: 700;
  line-height: 1;
}

/* ▲ = this best was set inside the selected period. */
.period-score-marker {
  position: absolute;
  top: -4px;
  right: -9px;
  color: var(--event);
  font-size: 8px;
  line-height: 1;
}


/* SCORE_TIER_BEGIN */
:root { --rank-delta-on-tint: #a81a2c; }
/* SCORE_TIER_END */
