/* ==================================================================
   DAILY LEADERBOARD - display screen
   Designed for exactly 688 x 1032 (portrait LED panel).
   Nothing here scrolls. Everything fits inside .stage.
   ================================================================== */

:root {
  --bg-0:      #04070d;
  --bg-1:      #0a1020;
  --card:      #121a28;
  --card-2:    #172333;
  --line:      rgba(255, 255, 255, .07);

  --txt:       #ffffff;
  --dim:       #8d9bb1;
  --dim-2:     #667690;

  --cyan:      #24d9f5;
  --cyan-hot:  #7ff0ff;

  --gold:      #ffcf4d;
  --silver:    #dbe5f2;
  --bronze:    #e28a45;

  --radius:    16px;
}

* { margin: 0; padding: 0; box-sizing: border-box; }

html, body {
  width: 100%;
  height: 100%;
  overflow: hidden;              /* the LED screen must never scroll */
  background: #000;
  cursor: none;                  /* no mouse pointer on the LED panel */
  user-select: none;
  -webkit-user-select: none;
}

body {
  /* Segoe UI / Tahoma / Noto cover Arabic as well as Latin, so names in
     either script render properly on Windows and on the LED player. */
  font-family: "Segoe UI", Tahoma, "Noto Sans Arabic", "Noto Naskh Arabic",
               Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif;
  color: var(--txt);
  -webkit-font-smoothing: antialiased;
}

/* Arabic letters join up. Letter-spacing pulls them apart and makes the word
   look broken, so display.js tags Arabic names and the spacing is removed.
   dir="auto" also flips the box to right-to-left, which would push the name
   away from its photo - text-align keeps it tucked in beside it. */
.ar { letter-spacing: 0 !important; }

/* In a ranking row the name must stay tucked beside its photo. The champion's
   name is centred instead, so that rule is scoped to rows only. */
.row .name.ar { text-align: left; }

/* ---- the fixed 688 x 1032 canvas -------------------------------- */
.stage {
  position: absolute;
  top: 0;
  left: 0;
  width: 688px;
  height: 1032px;
  transform-origin: 0 0;         /* display.js scales this to fit the window */
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background:
    radial-gradient(1000px 620px at 50% -12%, #142a4a 0%, rgba(20, 42, 74, 0) 62%),
    linear-gradient(180deg, var(--bg-1) 0%, var(--bg-0) 55%, #03060b 100%);
  padding-bottom: 14px;

  /* One shared column layout for the heading and every ranking row.
     display.js recalculates the sizes so any number of players still fits. */
  --ph: 48px;                                            /* photo size      */
  --col-gap: 10px;
  --cols: 34px var(--ph) 1fr auto 88px 84px;             /* rank photo name category onboarding monthly */
  --gap: 7px;                                            /* space between rows */
  --row-h: 56px;
  --fs-name: 19px;
  --fs-points: 21px;
  --fs-rank: 20px;
  --fs-cat: 11px;
}

/* ---- background glow + particles -------------------------------- */
.bg { position: absolute; inset: 0; pointer-events: none; overflow: hidden; }

.glow { position: absolute; border-radius: 50%; }

.glow-top {
  width: 620px; height: 420px;
  top: -180px; left: 34px;
  background: radial-gradient(circle, rgba(36, 217, 245, .16) 0%, rgba(36, 217, 245, 0) 70%);
}

.glow-bottom {
  width: 560px; height: 380px;
  bottom: -170px; right: -90px;
  background: radial-gradient(circle, rgba(255, 46, 120, .12) 0%, rgba(255, 46, 120, 0) 70%);
}

.particles { position: absolute; inset: 0; }
.particles i {
  position: absolute;
  width: 3px; height: 3px;
  border-radius: 50%;
  background: rgba(36, 217, 245, .55);
  opacity: .5;                /* static specks - they no longer drift */
}

/* ---- top accent bar --------------------------------------------- */
.accent-bar {
  position: relative;
  flex: 0 0 5px;
  background: linear-gradient(90deg, #0ff2ff 0%, #24d9f5 45%, #ff2e78 100%);
  box-shadow: 0 0 18px rgba(36, 217, 245, .65);
}

/* ---- header ------------------------------------------------------ */
.header {
  position: relative;
  flex: 0 0 auto;
  padding: 18px 22px 0 26px;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 18px;
}

.head-left  { min-width: 0; }
.head-right { flex: 0 0 auto; text-align: right; }

.kicker {
  font-size: 13px;
  line-height: 16px;
  font-weight: 800;
  letter-spacing: 3.4px;
  color: var(--dim);
  margin-bottom: 5px;
}

.title {
  font-size: 48px;
  line-height: 50px;
  font-weight: 900;
  letter-spacing: 1px;
  text-shadow: 0 0 26px rgba(36, 217, 245, .28);
}

.subtitle {
  margin-top: 9px;
  height: 18px;
  font-size: 12.5px;
  font-weight: 800;
  letter-spacing: 2.4px;
  color: var(--cyan);
}
.subtitle .bullet { opacity: .8; }

.date {
  font-size: 12.5px;
  font-weight: 700;
  letter-spacing: 1.6px;
  color: var(--dim);
  font-variant-numeric: tabular-nums;
  line-height: 16px;
}

/* the square bonus callout, top right under the date */
.bonus {
  margin-top: 11px;
  width: 120px;
  height: 86px;
  padding: 10px 6px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  border-radius: 14px;
  border: 1px solid rgba(255, 207, 77, .45);
  background: linear-gradient(180deg, rgba(255, 207, 77, .15) 0%, rgba(255, 207, 77, .04) 100%);
  box-shadow: 0 0 20px rgba(255, 180, 45, .18), inset 0 1px 0 rgba(255, 255, 255, .07);
}
.bonus.empty { display: none; }         /* no amount set -> no box */

.bonus-amount {
  font-size: 25px;
  line-height: 29px;
  font-weight: 900;
  letter-spacing: -.3px;
  color: var(--gold);
  text-shadow: 0 0 18px rgba(255, 190, 60, .45);
  white-space: nowrap;
}

.bonus-label {
  margin-top: 4px;
  font-size: 8.5px;    /* display.js shrinks this if the wording is long */
  line-height: 11px;
  font-weight: 800;
  letter-spacing: 1.5px;
  color: #cbab6a;
  text-align: center;
  overflow: hidden;
}

/* ---- champion card ------------------------------------------------
   Half the panel. Everything stacks and centres: title, photo, name,
   category, then the two figures side by side. */
.champion {
  position: relative;
  flex: 0 0 516px;              /* half of 1032 */
  min-height: 0;                /* a flex item defaults to min-height:auto and
                                   would quietly grow past 516 if its content
                                   ever got taller, squeezing the list below */
  margin: 16px 22px 0;
  padding: 20px 22px 22px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  border-radius: 22px;
  background:
    radial-gradient(120% 80% at 50% 0%, rgba(255, 207, 77, .13) 0%, rgba(255, 207, 77, 0) 62%),
    linear-gradient(180deg, var(--card-2) 0%, var(--card) 100%);
  border: 1px solid rgba(255, 207, 77, .26);
  box-shadow: 0 14px 40px rgba(0, 0, 0, .55), inset 0 1px 0 rgba(255, 255, 255, .06);
}

/* the title, with a slow gold sweep travelling across the letters */
.champ-label {
  font-size: 23px;
  font-weight: 900;
  letter-spacing: 2.6px;
  white-space: nowrap;
  margin-bottom: 22px;   /* clears the rank badge on the photo corner */
}
.champ-label .trophy { margin-right: 8px; }
.champ-label .shine {
  background: linear-gradient(90deg,
              #a8792170 0%, #ffcf4d 34%, #fff6d8 50%, #ffcf4d 66%, #a8792170 100%);
  background-size: 260% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  animation: labelShine 5s linear infinite;
}
@keyframes labelShine {
  from { background-position: 160% 0; }
  to   { background-position: -60% 0; }
}

/* photo pushed to the left edge, the two figures to the right edge */
.champ-main {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 32px;        /* pulls the photo and the figures in from the edges */
}

/* Holds the champion's photo - or, when the day is drawn at the top, all
   of the people who drew, side by side with their names under them. */
.champ-faces { display: flex; align-items: center; gap: 14px; }

/* one column: the photo, and that person's own name beneath it */
.champ-face { display: flex; flex-direction: column; align-items: center; }

.face-name {
  margin-top: 12px;
  max-width: 100%;
  font-size: 27px;
  line-height: 32px;
  font-weight: 900;
  letter-spacing: .4px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  text-shadow: 0 2px 16px rgba(0, 0, 0, .6);
}
/* Arabic letters join up, so they must not be letter-spaced */
.face-name.ar { letter-spacing: 0; }

/* the & belongs between the two photos, not inside anybody's name */
.champ-amp {
  flex: 0 0 auto;
  align-self: center;
  margin-bottom: 44px;          /* sits level with the photos, not the names */
  font-size: 40px;
  font-weight: 900;
  color: var(--gold);
  opacity: .85;
  text-shadow: 0 2px 18px rgba(255, 190, 60, .35);
}

.champ-photo {
  width: 268px;
  height: 268px;
  flex: 0 0 268px;
  border-radius: 26px;
  animation: champZoom 6.5s ease-in-out infinite;
  will-change: transform;
}
@keyframes champZoom {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.045); }
}

/* ---- a draw at the top ----
   Two or three faces have to share the width the one had. The slow
   breathing stays only on a single face; three of them moving at once is
   just noise from across a room. */
.champion.faces-2 .champ-faces { gap: 26px; }
.champion.faces-3 .champ-faces { gap: 18px; }

.champion.faces-2 .champ-photo {
  width: 186px; height: 186px; flex: 0 0 186px;
  border-radius: 20px;
  animation: none;
}
.champion.faces-3 .champ-photo {
  width: 138px; height: 138px; flex: 0 0 138px;
  border-radius: 16px;
  animation: none;
}
.champion.faces-3 .face-name { font-size: 21px; line-height: 25px; margin-top: 10px; }
.champion.faces-2 .champ-badge,
.champion.faces-3 .champ-badge {
  top: -10px; left: -10px;
  width: 38px; height: 38px;
  font-size: 20px;
}

/* four or more drew: the first three have faces, the rest are counted */
.champ-more {
  flex: 0 0 auto;
  align-self: center;
  font-size: 30px;
  font-weight: 900;
  letter-spacing: .5px;
  color: var(--gold);
  border: 2px solid rgba(255, 207, 77, .45);
  border-radius: 16px;
  padding: 22px 14px;
  background: rgba(255, 207, 77, .08);
}

/* the two figures make no sense for a group, so the block goes and the
   faces take the middle. A draw where the admin has picked one person to
   show keeps the ordinary layout - it is one face like any other day. */
.champion.faces-2 .champ-main,
.champion.faces-3 .champ-main { justify-content: center; }
.champion.faces-2 .champ-score,
.champion.faces-3 .champ-score { display: none; }

/* the names of everybody who drew need the whole width, not the column
   that was sized to one photo */
.champion.faces-2 .champ-under,
.champion.faces-3 .champ-under {
  align-self: stretch;
  width: auto;
  margin-left: 0;
  padding: 0 26px;
}
/* the names are under the faces now, so this line carries the number
   they all drew on */
.champion.faces-2 .champ-name,
.champion.faces-3 .champ-name {
  margin-top: 18px;
  font-size: 17px;
  line-height: 22px;
  font-weight: 800;
  letter-spacing: 3px;
  color: var(--dim);
  white-space: normal;
  overflow: visible;
}
.champ-name.tally b {
  color: var(--cyan);
  font-size: 30px;
  font-weight: 900;
  letter-spacing: 0;
  margin-left: 6px;
  vertical-align: -3px;
  text-shadow: 0 0 18px rgba(36, 217, 245, .45);
}

/* the gold number 1, sitting on the corner of the photo */
.champ-badge {
  position: absolute;
  z-index: 4;
  top: -14px;
  left: -14px;
  width: 52px;
  height: 52px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 27px;
  font-weight: 900;
  color: #2a1c00;
  background: linear-gradient(150deg, #fff2bd 0%, #ffd257 42%, #e0a417 100%);
  border: 2px solid rgba(255, 246, 214, .9);
  box-shadow: 0 4px 14px rgba(0, 0, 0, .45), 0 0 20px rgba(255, 190, 60, .55);
}

.champ-under {
  align-self: flex-start;
  width: 268px;          /* matches the photo, so the name centres on it */
  margin-left: 32px;     /* matches .champ-main padding, keeps the name on the photo */
  text-align: center;
}

.champ-name {
  margin-top: 20px;
  max-width: 100%;
  font-size: 54px;     /* display.js shrinks this if a name is very long */
  line-height: 62px;
  font-weight: 900;
  letter-spacing: .5px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  text-shadow: 0 2px 20px rgba(0, 0, 0, .6);
}

.champ-cat {
  margin-top: 6px;
  font-size: 13px;
  font-weight: 800;
  letter-spacing: 2.4px;
  color: var(--dim-2);
}

/* ---- nobody has been onboarded yet today ----
   There is no champion to show, so the gold frame holds a question mark,
   the two figures come off (0 and 0 say nothing), and the name line
   becomes the question itself. Everybody stays in the list below. */
.champion.waiting .champ-badge { display: none; }
.champion.waiting .champ-score { display: none; }

.champion.waiting .champ-main {
  justify-content: center;   /* the photo takes the middle on its own */
  padding: 0;
}

.champion.waiting .champ-under {
  align-self: stretch;
  width: auto;
  margin-left: 0;
  padding: 0 26px;
}

.champion.waiting .champ-name {
  margin-top: 26px;
  font-size: 40px;
  line-height: 48px;
  white-space: normal;       /* the question may take two lines */
  overflow: visible;
  color: var(--gold);
  text-shadow: 0 2px 24px rgba(255, 190, 60, .25);
}

.champion.waiting .champ-cat {
  margin-top: 12px;
  color: var(--dim);
}

/* the two figures, side by side under the name */
.champ-score {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: stretch;     /* every child takes the column width */
  gap: 22px;
}
.cs-block { text-align: right; }
.cs-sep {
  height: 1px;
  background: rgba(255, 255, 255, .14);
}
.cs-num {
  font-size: 50px;
  line-height: 52px;
  font-weight: 900;
  letter-spacing: 0;        /* no trailing space, so the right edge is true */
  color: var(--cyan);
  font-variant-numeric: tabular-nums;
  text-shadow: 0 0 24px rgba(36, 217, 245, .45);
}
.cs-num.gold {
  color: var(--gold);
  text-shadow: 0 0 24px rgba(255, 190, 60, .45);
}
/* Letter-spacing adds a trailing gap after the last character, which would
   push the label 2.6px left of the number above it. The negative margin
   cancels exactly that, so both right edges line up on the pixel. */
.cs-lbl {
  margin-top: 7px;
  font-size: 12px;
  font-weight: 800;
  letter-spacing: 2.6px;
  margin-right: -2.6px;
  color: var(--dim-2);
}

/* soft edges so rows melt in and out instead of being cut off */
.list::before,
.list::after {
  content: '';
  position: absolute;
  left: 0; right: 0;
  height: 34px;
  z-index: 3;
  pointer-events: none;
}
.list::before { top: 0;    background: linear-gradient(180deg, #050811 0%, rgba(5, 8, 17, 0) 100%); }
.list::after  { bottom: 0; background: linear-gradient(0deg,   #03060b 0%, rgba(3, 6, 11, 0) 100%); }

/* ---- comments rolling up the middle of the card -------------------
   The photo is a fixed 268 and the two figures take what their labels
   need; whatever is left between them is this column, and the comments
   travel up through it. The list is written out enough times to keep the
   column full and the belt travels exactly one copy, so the join never
   shows and the roll never stops - even on a day with one comment. */
.champ-says {
  position: relative;
  flex: 1 1 auto;
  min-width: 0;             /* a flex item will not shrink under its content otherwise */
  align-self: stretch;      /* exactly as tall as the photo beside it */
  margin: 0 13px;
}

/* Nothing has been said yet: the column keeps its place in the row so the
   photo and the figures stay where they always are, and shows nothing. */
.champ-says.empty .cm-roll { display: none; }

/* Nobody has been onboarded, so there is no champion to comment on. The
   question-mark card takes the middle on its own, as it was drawn. */
.champion.waiting .champ-says { display: none; }

/* The comments melt in at the top and out at the bottom rather than being
   cut off. A mask does it over the card's gradient; where it is not
   supported the column simply clips, which is no worse than before. */
.cm-window {
  /* Taken out of the flow on purpose. Left in it, a long list of comments
     would make this column the tallest thing in the row and push the card
     past the 516 it is allowed - the photo has to be what sets the height,
     however much has been said. */
  position: absolute;
  top: 0; right: 0; bottom: 0; left: 0;
  overflow: hidden;
  -webkit-mask-image: linear-gradient(180deg, rgba(0,0,0,0) 0%, #000 13%, #000 87%, rgba(0,0,0,0) 100%);
          mask-image: linear-gradient(180deg, rgba(0,0,0,0) 0%, #000 13%, #000 87%, rgba(0,0,0,0) 100%);
}

/* display.js sets the two numbers: how far to travel (one copy of the
   list) and how long that takes, so every comment moves at one pace. */
.cm-roll {
  will-change: transform;
  animation: cmRoll var(--cm-time, 20s) linear infinite;
}
@keyframes cmRoll {
  from { transform: translateY(var(--cm-from, 0px)); }
  to   { transform: translateY(var(--cm-to, 0px)); }
}

.cm-item {
  padding: 10px 2px;
  text-align: center;
  overflow-wrap: break-word;    /* a long word must not push the column wider */
  word-break: break-word;
}
.cm-item + .cm-item { border-top: 1px solid rgba(255, 255, 255, .07); }

/* who said it, over what they said */
.cm-who {
  display: block;
  margin-bottom: 3px;
  font-size: 10.5px;
  font-weight: 900;
  letter-spacing: 1.3px;
  color: var(--gold);
}
.cm-who.ar { letter-spacing: 0; }

.cm-text {
  display: -webkit-box;
  -webkit-line-clamp: 4;        /* a very long comment is cut, not left to fill the column */
  -webkit-box-orient: vertical;
  overflow: hidden;
  font-size: 11px;
  line-height: 15px;
  font-weight: 700;
  font-style: italic;
  color: #dcc99c;              /* the same warm gold the strip used to read in */
}

/* the mark on a comment that came in from WhatsApp */
.cm-wa {
  display: inline-block;
  vertical-align: -2px;
  margin-right: 5px;
  color: #25d366;                 /* WhatsApp green, readable on the LED wall */
  filter: drop-shadow(0 0 6px rgba(37, 211, 102, .45));
}

/* soft edges so rows melt in and out instead of being cut off */
.list::before,
.list::after {
  content: '';
  position: absolute;
  left: 0; right: 0;
  height: 34px;
  z-index: 3;
  pointer-events: none;
}
.list::before { top: 0;    background: linear-gradient(180deg, #050811 0%, rgba(5, 8, 17, 0) 100%); }
.list::after  { bottom: 0; background: linear-gradient(0deg,   #03060b 0%, rgba(3, 6, 11, 0) 100%); }

/* ---- the ranking heading ---------------------------------------- */
.list-head {
  flex: 0 0 auto;
  height: 22px;
  margin: 14px 22px 6px;
  /* +1px each side: the rows below have a 1px border, this has none, and the
     two column tracks have to line up exactly */
  padding: 0 17px 0 13px;
  display: grid;
  grid-template-columns: var(--cols);
  gap: var(--col-gap);
  align-items: center;
  font-size: 9.5px;
  font-weight: 800;
  letter-spacing: 1.9px;
  color: var(--dim-2);
}
/* the rank column is narrow, so its label gets tighter spacing */
.list-head span:first-child { letter-spacing: .6px; }
.list-head .lh-player { grid-column: span 2; padding-left: 4px; }
.list-head .right { text-align: right; }

/* ---- ranking rows ------------------------------------------------ */
/* The list is a fixed window. The track inside it holds the rows and slides
   upward for as long as there are more rows than fit. display.js duplicates
   the rows once and works out exactly how far to travel, so the loop joins
   up with no jump. Only transform animates - cheap on the LED player. */
.list {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  margin: 0 22px;
  overflow: hidden;
}

.track {
  display: flex;
  flex-direction: column;
  gap: var(--gap);
  will-change: transform;
}
.track.scrolling {
  animation: listScroll var(--scroll-time, 24s) linear infinite;
}
/* Holds on the first rows, travels down to the last, holds again, then
   returns to the top and starts over from rank 2. Not a continuous belt -
   every pass begins at the top of the list. */
@keyframes listScroll {
  0%   { transform: translateY(0); animation-timing-function: ease-in-out; }
  14%  { transform: translateY(0); animation-timing-function: ease-in-out; }
  78%  { transform: translateY(calc(-1 * var(--scroll-shift, 0px))); }
  90%  { transform: translateY(calc(-1 * var(--scroll-shift, 0px))); animation-timing-function: ease-in-out; }
  100% { transform: translateY(0); }
}

/* when everything fits, centre the rows and do not move them */
.track.still { justify-content: center; height: 100%; }

.row {
  flex: 0 0 var(--row-h);
  height: var(--row-h);
  display: grid;
  grid-template-columns: var(--cols);
  align-items: center;
  gap: var(--col-gap);
  padding: 0 16px 0 12px;
  border-radius: 13px;
  background: linear-gradient(180deg, rgba(23, 35, 51, .95) 0%, rgba(16, 24, 37, .95) 100%);
  border: 1px solid var(--line);
}

.row .rank {
  font-size: var(--fs-rank);
  font-weight: 900;
  color: var(--dim);
  text-align: center;
  font-variant-numeric: tabular-nums;
}

.row .photo { width: var(--ph); height: var(--ph); }

.row .name {
  font-size: var(--fs-name);
  font-weight: 900;
  letter-spacing: .4px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.row .cat {
  font-size: var(--fs-cat);
  font-weight: 800;
  letter-spacing: 1.4px;
  color: var(--dim-2);
  white-space: nowrap;
  text-align: right;
  min-width: 58px;
  overflow: hidden;
  text-overflow: ellipsis;
}

.row .points,
.row .monthly {
  font-weight: 900;
  text-align: right;
  font-variant-numeric: tabular-nums;
  letter-spacing: -.2px;
  white-space: nowrap;
}

.row .points  { font-size: var(--fs-points); }

/* month-to-date sits beside today's number, quieter so today still leads */
.row .monthly {
  font-size: calc(var(--fs-points) * .84);
  color: #93a6bf;
}

/* --- top three get the special treatment --- */
.row.top {
  border-width: 1px;
  position: relative;
}
.row.top::before {
  content: '';
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 4px;
  border-radius: 13px 0 0 13px;
  background: var(--accent);
}

.row.rank-1 {
  --accent: var(--gold);
  background: linear-gradient(100deg, rgba(255, 207, 77, .16) 0%, rgba(255, 207, 77, .03) 42%, rgba(16, 24, 37, .95) 100%);
  border-color: rgba(255, 207, 77, .35);
  box-shadow: 0 0 22px rgba(255, 160, 30, .13);
}
.row.rank-2 {
  --accent: var(--silver);
  background: linear-gradient(100deg, rgba(219, 229, 242, .12) 0%, rgba(219, 229, 242, .02) 42%, rgba(16, 24, 37, .95) 100%);
  border-color: rgba(219, 229, 242, .26);
}
.row.rank-3 {
  --accent: var(--bronze);
  background: linear-gradient(100deg, rgba(226, 138, 69, .13) 0%, rgba(226, 138, 69, .02) 42%, rgba(16, 24, 37, .95) 100%);
  border-color: rgba(226, 138, 69, .28);
}

.row.rank-1 .rank { color: var(--gold);   text-shadow: 0 0 14px rgba(255, 207, 77, .55); }
.row.rank-2 .rank { color: var(--silver); }
.row.rank-3 .rank { color: var(--bronze); }

.row .points        { color: #e7eefb; }
.row.rank-1 .points { color: var(--cyan); text-shadow: 0 0 16px rgba(36, 217, 245, .45); }
.row.rank-2 .points,
.row.rank-3 .points { color: var(--cyan-hot); }

/* only the leader's monthly figure is highlighted, so today's number still
   leads the eye down the board */
.row.rank-1 .monthly { color: var(--gold); }

/* ---- photo frames ------------------------------------------------ */
.frame {
  position: relative;
  flex: 0 0 auto;
  border-radius: 14px;
  background: #0d1320;
}

.frame img {
  position: relative;
  z-index: 1;
  width: 100%;
  height: 100%;
  object-fit: cover;          /* never stretches a face */
  object-position: 50% 30%;   /* favour the head, not the chest */
  border-radius: inherit;
  display: block;
}

/* coloured ring drawn on top of the photo */
.frame::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 2;
  border-radius: inherit;
  border: 2px solid rgba(255, 255, 255, .10);
  pointer-events: none;
}

/* #1 keeps only a thin edge - the running loop outside it is the gold
   treatment, and a full-strength border would drown the moving light */
.frame-gold::after {
  border-color: rgba(255, 207, 77, .32);
  box-shadow: inset 0 0 10px rgba(0, 0, 0, .45);
}
/* Gold in the list happens only on a draw, where somebody shares first
   place but is not the face on the card. There is no running light down
   here to be drowned out, so it gets the full treatment like #2 and #3. */
.row .frame-gold::after {
  border-color: rgba(255, 207, 77, .95);
  box-shadow: 0 0 16px rgba(255, 207, 77, .55), inset 0 0 10px rgba(255, 207, 77, .18);
}

/* #2 and #3 are marked out by colour alone - a coloured edge and a steady
   glow, with no movement of any kind. */
.frame-silver::after {
  border-color: rgba(226, 235, 247, .95);
  box-shadow: 0 0 16px rgba(219, 229, 242, .55), inset 0 0 10px rgba(219, 229, 242, .18);
}
.frame-bronze::after {
  border-color: rgba(233, 146, 76, .95);
  box-shadow: 0 0 16px rgba(226, 138, 69, .60), inset 0 0 10px rgba(226, 138, 69, .20);
}


/* --- #1: a light running around the frame ---
   A conic gradient spins behind the photo. The photo itself sits on top and
   covers the middle, so all you see is the bright head chasing its tail around
   the rounded rectangle. Only "transform: rotate" animates, which the LED
   player composites on the GPU - no repaints, no blur filters. */
.runner {
  position: absolute;
  inset: -4px;
  z-index: 0;                 /* behind the photo */
  border-radius: 18px;
  overflow: hidden;           /* clips the spinning square to the frame shape */
  pointer-events: none;
  background: rgba(255, 200, 70, .13);      /* the dim rest of the loop */
  box-shadow: 0 0 14px rgba(255, 180, 45, .22);
}
.champ-photo .runner {
  inset: -7px;
  border-radius: 25px;          /* photo radius 18 + 7 = concentric */
  box-shadow: 0 0 24px rgba(255, 180, 45, .30);
}

.runner::before {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: 240%;                /* big enough to cover the frame's diagonal */
  height: 240%;
  /* one bright head with a short tail; the rest of the circle is clear, so
     the light reads as a single spark travelling around the rectangle */
  background: conic-gradient(from 0deg,
              #fffdf4                  0deg,
              #ffe89a                  4deg,
              #ffc93f                 12deg,
              rgba(255, 186, 48, .48)  32deg,
              rgba(255, 172, 40, .14)  68deg,
              rgba(255, 160, 30, 0)   104deg,
              rgba(255, 160, 30, 0)   360deg);
  animation: runner 2.4s linear infinite;
  will-change: transform;
}

@keyframes runner {
  from { transform: translate(-50%, -50%) rotate(0deg); }
  to   { transform: translate(-50%, -50%) rotate(360deg); }
}

/* ---- footer ------------------------------------------------------- */
.footer {
  flex: 0 0 50px;
  margin: 12px 22px 0;
  padding: 0 16px;
  display: flex;
  flex-direction: column;      /* the message, and under it the number to write to */
  align-items: center;
  justify-content: center;
  gap: 3px;
  border-radius: 13px;
  background: linear-gradient(90deg, rgba(36, 217, 245, .10) 0%, rgba(255, 46, 120, .08) 100%);
  border: 1px solid rgba(36, 217, 245, .20);
}

.message {
  min-width: 0;
  font-size: 13.5px;
  font-weight: 900;
  letter-spacing: 1.8px;
  text-align: center;
  color: #cfe6f2;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* The line that tells the room where to send a comment about the champion.
   Same green mark the crowd's comments carry upstairs, so the two read as
   one thing: this is the number those came from. */
.say {
  display: flex;
  align-items: center;
  gap: 7px;
  max-width: 100%;
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 1.6px;
  white-space: nowrap;
  color: var(--dim);
}

.say-wa {
  display: flex;
  color: #25d366;                 /* WhatsApp green, readable on the LED wall */
  filter: drop-shadow(0 0 6px rgba(37, 211, 102, .45));
}

.say-num {
  font-size: 13px;
  font-weight: 900;
  letter-spacing: 2.2px;
  color: var(--gold);
  text-shadow: 0 0 14px rgba(255, 207, 77, .35);
}

/* ==================================================================
   NO ENTRANCE ANIMATION
   The board is drawn once and then holds perfectly still. The only thing on
   the whole panel that moves is the gold light running around the champion's
   photo. #2 and #3 are marked out by colour alone. Nothing replays, nothing
   fades in, and new data simply swaps into place.
   ================================================================== */
