ui/scoreboard.razor.scss
// SCOREBOARD — held on TAB.
//
// ⚠️ COOL GREY ON BLACK, not the blood red of DownedHud. That panel is an emergency and shouts;
// this is a glance at a table mid-round and must not read as damage.
// ⛔ TWO COLUMNS (the user's layout, 2026-09-29: *"score on the left, map on the right, zombies left on the top, and current
// Easter egg step bellow the score"*): the score with the Easter egg's own panel under it, and the map beside them. On a map
// without a TAB map the right column is not drawn and the left one centres, as the board always did. The zombies left are
// RoundBarHud's, above it all.
.scoreboard {
position: absolute;
width: 100%;
height: 100%;
pointer-events: none;
flex-direction: row;
align-items: flex-start;
justify-content: center;
padding: 90px 40px 0px 40px;
font-family: Inter;
// ⛔ NOT `display: none`. A hidden panel with zero opacity keeps its layout, so the table does
// not reflow on the frame TAB goes down — which showed as a visible snap on the first press.
&.hidden {
opacity: 0;
}
}
// The left column: the score, then the Easter egg's panel
.side {
flex-direction: column;
flex-shrink: 0;
}
.board {
flex-direction: column;
background-color: rgba(8, 10, 12, 0.88);
border: 1px solid rgba(255, 255, 255, 0.09);
border-radius: 4px;
padding: 18px 22px 20px;
}
// ── title ───────────────────────────────────────────────────────────────────
.head {
flex-direction: row;
align-items: baseline;
justify-content: space-between;
margin-bottom: 14px;
padding-bottom: 10px;
border-bottom: 1px solid rgba(255, 255, 255, 0.12);
.title {
font-size: 22px;
font-weight: 700;
letter-spacing: 3px;
color: rgba(235, 240, 245, 1);
}
.round {
font-size: 14px;
letter-spacing: 2px;
color: rgba(150, 160, 172, 1);
}
}
// ── rows ────────────────────────────────────────────────────────────────────
.row {
flex-direction: row;
align-items: center;
height: 32px;
// ⚠️ The local player is marked, not moved to the top. The order is the standing — pinning
// yourself first would hide whether you are actually winning.
&.me {
background-color: rgba(90, 140, 190, 0.14);
}
&.hdr {
height: 26px;
color: rgba(140, 150, 162, 1);
font-size: 11.5px;
letter-spacing: 0.8px;
text-transform: uppercase;
// ⛔ ON THE CELLS, NOT ONLY THE ROW (2026-10-05): *"The score columns are overlapping a bit in their names"*. `.c` sets
// its own 16 px and near-white, and a rule on the element itself beats a value inherited from the row, so the headers
// were drawn at 16 px. There "HEADSHOTS" and "POINTS EARNED" are wider than their columns and ran into the one beside
// them (measured with the real fonts: HEADSHOTS 105 px in Inter and 113 px in basalt's Cinzel, in a 96 px column). At the
// 11.5 px meant here every header fits, with 12 px or more to spare in both faces.
.c {
font-size: 11.5px;
color: rgba(140, 150, 162, 1);
}
}
}
.c {
font-size: 16px;
color: rgba(224, 230, 236, 1);
padding: 0 10px;
white-space: nowrap;
// ⚠️ Digits line up column to column. Without tabular figures the numbers jitter sideways as
// they tick, which on a table this dense reads as the whole panel shifting.
font-variant-numeric: tabular-nums;
}
// ⚠️ NARROWED FOR THE MAP BESIDE IT (2026-09-29): 240 / 128 / 172 were 1,052 wide; 190 / 96 / 136 are 806, and "Headshots" and
// "Points earned" still fit their headers in basalt's wider Cinzel (measured at 11.5 px).
.name {
width: 190px;
justify-content: flex-start;
text-align: left;
}
// ⛔ `justify-content`, NOT `text-align` ALONE. These cells are flex boxes holding a label, so the
// label is a CHILD — text-align sets how the glyphs sit inside the label, which is already exactly
// its own width, and the label itself stayed pinned left. On screen every number read as
// left-aligned despite the rule being there.
.num {
width: 96px;
justify-content: flex-end;
text-align: right;
}
// ⚠️ Widened because "Points earned" and "Headshots" WRAPPED at the old width — the header came out
// as "HEADSHO / TS". `white-space: nowrap` above stops the wrap; this makes room for it.
.wide {
width: 136px;
}
.empty {
padding: 18px 4px;
color: rgba(140, 150, 162, 1);
font-size: 14px;
}
// ── the Easter egg, its own panel under the score ───────────────────────────
//
// ⛔ THE MAIN STEP, THEN ITS LITTLE STEPS (the user, 2026-09-29: *"I want the steps to be divided in main step, and the little
// steps to complete it"*): the step's name in ember; each little step with its mark — ticked when done, a lit diamond on the one
// on now, a ring on the rest — and its count where it keeps one; nine hexes for the nine main steps. A panel like the score's, so
// its head is `.head`; basalt's Cinzel reaches it from the root.
.egg-card {
margin-top: 24px;
.egg-name {
font-size: 21px;
letter-spacing: 1.5px;
color: rgba(248, 150, 72, 1);
text-shadow: 1px 1px 0px rgba(0, 0, 0, 0.9);
margin-bottom: 8px;
white-space: nowrap;
}
.little {
flex-direction: row;
align-items: center;
height: 30px;
padding: 0px 12px 0px 6px;
.mark {
width: 22px;
height: 22px;
margin-right: 12px;
align-items: center;
justify-content: center;
}
// a ring, for a step still to come
.glyph {
width: 10px;
height: 10px;
border: 2px solid rgba(96, 103, 112, 1);
border-radius: 5px;
}
.text {
font-size: 16px;
letter-spacing: 0.6px;
color: rgba(96, 103, 112, 1);
white-space: nowrap;
}
.count {
margin-left: auto;
padding-left: 16px;
font-size: 16px;
letter-spacing: 1px;
color: rgba(248, 150, 72, 1);
white-space: nowrap;
}
// done: a tick (two sides of a box, turned), in Color Smash's green
&.done {
.glyph {
width: 7px;
height: 13px;
margin-top: -4px;
border-width: 0px;
border-radius: 0px;
border-right-width: 3px;
border-bottom-width: 3px;
border-right-color: rgba(126, 196, 122, 1);
border-bottom-color: rgba(126, 196, 122, 1);
transform: rotate(45deg);
}
.text {
color: rgba(140, 150, 162, 1);
}
}
// the one on now: a lit diamond, and the row warmed
&.now {
background-color: rgba(248, 150, 72, 0.09);
.glyph {
border-width: 0px;
border-radius: 0px;
background-color: rgba(248, 150, 72, 1);
transform: rotate(45deg);
}
.text {
color: rgba(235, 240, 245, 1);
}
}
}
// the nine main steps, a hex each — ⚠️ THREE BARS TURNED 0, 60 AND -60 DEGREES MAKE ONE: a panel has no hexagon of its own
.pips {
flex-direction: row;
margin-top: 14px;
padding-left: 6px;
}
.pip {
position: relative;
width: 16px;
height: 16px;
margin-right: 8px;
.h {
position: absolute;
left: 1px;
top: 4px;
width: 14px;
height: 8px;
background-color: rgba(70, 75, 82, 1);
}
.b {
transform: rotate(60deg);
}
.c {
transform: rotate(-60deg);
}
&.done .h {
background-color: rgba(228, 217, 195, 1);
}
&.now .h {
background-color: rgba(248, 150, 72, 1);
}
}
}
// ── the map of the whole place (`TabMap`), the right column ────────────────
//
// ⚠️ A PANEL LIKE THE SCORE'S, taking the width the left column leaves (`flex-grow`). `TabMap.Fit` sizes the picture's frame in it
// from code: as wide as the panel, and shorter if the screen runs out. The picture is the walkable ground (`basalt_map.py
// --game`); the room names and the players are panels on it, placed from code in percent of the picture.
.map-card {
margin-left: 24px;
flex-grow: 1;
}
.tabmap {
flex-direction: column;
align-items: center;
&.none {
display: none;
}
.frame {
position: relative;
background-size: contain;
background-repeat: no-repeat;
border: 1px solid rgba(255, 255, 255, 0.09);
border-radius: 3px;
overflow: hidden;
}
// A room's name, centred on its place, in the picture's own ember
.room {
position: absolute;
transform: translateX(-50%) translateY(-50%);
font-size: 13px;
font-weight: 700;
letter-spacing: 1.5px;
white-space: nowrap;
color: rgba(248, 150, 72, 0.95);
text-shadow: 1px 1px 0px rgba(0, 0, 0, 0.95);
}
// ⛔ A PLAYER, POINTING THE WAY THEY LOOK (*"all players can be seen on the map directionally"*): a dot and a nose, centred on
// where they stand by the margins; the turn is set from code (`TabMap.Face`). Teammates in the tags' blue. YOU BIGGER AND
// WHITE (*"the player sees themselves as bigger on the map"*).
.marker {
position: absolute;
width: 18px;
height: 18px;
margin-left: -9px;
margin-top: -9px;
align-items: center;
justify-content: center;
.dot {
width: 10px;
height: 10px;
border-radius: 5px;
background-color: rgba(150, 215, 255, 1);
border: 1.5px solid rgba(0, 0, 0, 0.85);
}
// ⚠️ A SQUARE TURNED 45°, NOT A GLYPH (`PlayerTagsHud`'s reason): the nose, along +x until turned, under the dot
.nose {
position: absolute;
left: 12px;
top: 5.5px;
width: 7px;
height: 7px;
background-color: rgba(150, 215, 255, 1);
border: 1px solid rgba(0, 0, 0, 0.8);
transform: rotate(45deg);
}
.name {
position: absolute;
left: 21px;
top: 0px;
font-size: 12px;
font-weight: 700;
white-space: nowrap;
color: rgba(150, 215, 255, 0.97);
text-shadow: 1px 1px 0px rgba(0, 0, 0, 0.9);
}
// You: over half as big again, white, and no name
&.me {
width: 28px;
height: 28px;
margin-left: -14px;
margin-top: -14px;
.dot {
width: 16px;
height: 16px;
border-radius: 8px;
border-width: 2px;
background-color: rgba(255, 255, 255, 1);
}
.nose {
left: 19px;
top: 8.5px;
width: 11px;
height: 11px;
background-color: rgba(255, 255, 255, 1);
}
.name {
display: none;
}
}
// Down: red, the revive badge's own
&.down {
.dot {
background-color: rgba(255, 112, 100, 0.98);
}
.nose {
background-color: rgba(255, 112, 100, 0.98);
}
.name {
color: rgba(255, 112, 100, 0.98);
}
}
}
}
// ═══ BASALT (`HudTheme`) — the map's face, Cinzel on the scoreboard ═══
//
// *"i want the font for this map to affect … all hud messages that appear on screen"* (2026-09-27). The root wears the theme's class; the rules name it and each box
// here that sets Inter itself.
// ⛔ ON INTER'S LINE BOX, so no row grows taller: 0.898 of Cinzel's own line, which is Inter's 1.211 of the size. See
// `.round` in SurvivalHud.razor.scss for why it is not 1.211. It is wider on mixed-case text, which it sets in small capitals. Cinzel-Bold is the one face shipped,
// so every weight draws bold.
.scoreboard.theme-basalt
{
font-family: Cinzel;
line-height: 0.898;
}