/* ==================================================================
   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 */
}

.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); }
}

/* 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 written on the admin page. The window is exactly one line tall
   and the track inside steps up through them, pausing on each. display.js
   builds the keyframes because the number of comments varies. */
.champ-comment {
  width: 100%;
  margin-top: 12px;
  padding-top: 10px;
  border-top: 1px solid rgba(255, 207, 77, .16);
}
.champ-comment.empty { display: none; }

/* The clip has to be its own element with no padding: overflow:hidden cuts at
   the padding box, so padding here would let the outgoing comment show through
   the gap under the divider while it slides away. */
.cc-window {
  height: 22px;
  overflow: hidden;
}

.cc-track { will-change: transform; }

.cc-item {
  height: 22px;
  line-height: 22px;
  font-size: 15.5px;         /* display.js shrinks this if a comment is long */
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.cc-who {
  font-weight: 900;
  color: var(--gold);
  margin-right: 9px;
}
.cc-who::after {
  content: '•';
  margin-left: 9px;
  color: rgba(255, 207, 77, .45);
  font-weight: 700;
}

.cc-text {
  font-weight: 700;
  font-style: italic;
  color: #dcc99c;
}

/* ---- list 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);
}
/* #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;
  align-items: center;
  justify-content: center;
  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;
}

/* ==================================================================
   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.
   ================================================================== */
