ui/weaponstatspanel.razor.scss
// WEAPON STATS card — bottom-right, over the HUD.
//
// ⚠️ Deliberately NOT centred and NOT modal. It is read while playing, next to
// the ammo counter it describes, so it must never take the middle of the screen
// away from the crosshair.
root {
position: absolute;
width: 100%;
height: 100%;
pointer-events: none;
flex-direction: column;
align-items: flex-end;
justify-content: flex-end;
font-family: Inter; // ⚠️ NOT Consolas — it is not shipped and s&box
// logged "Font 'Consolas' not found, falling back to system font",
// which means the face was whatever the OS chose. Inter is the one
// face this project actually ships.
}
.card {
flex-direction: column;
// ⚠️ 880, up from 720, FOR PACK-A-PUNCH. The value column is whatever the
// label (210) and the bar (190 + 44 margins) leave behind, which at 720 was
// about 212px — fine for "45" and far too tight for a packed shotgun's
// "8 × 703.1 = 5624.8". x15.63 turns every damage figure into four digits and
// the multi-pellet rows into three of them.
// ⛔ CAPPED, BECAUSE THE ROW COUNT OUTGREW THE SCREEN. 18 rows at this type size
// come to roughly 1010px before the 150px bottom margin, which overflows a 1080p
// viewport and clips off the TOP — taking the weapon NAME with it, the one line that
// tells you which gun you are reading. It grew a row at a time across three tiers of
// tech and no single addition looked like the one that broke it.
//
// ⚠️ IT IS 24 ROWS NOW (counted off the array in Stats, not estimated) — tier 5 added
// six, one per node that had nowhere else to appear. At the same 45px a row that is
// roughly 1350px, so the cap and the scroll below are doing the work they were added
// for and no style needed changing to take them.
//
// ⚠️ The margin also shrank 150 → 60. It existed to lift the card clear of the HUD's
// ammo counter, and 60 still does that while buying back 90px.
// ── NOW NINE ROWS, SO THE CARD TAKES THE HEIGHT RATHER THAN HUGGING ──
//
// `height`, NOT `max-height`. At 24 rows the content always overflowed and the cap was
// what set the size; at 9 the content is far shorter than the screen, so a cap left the
// card as a short block floating in the bottom corner with the rows crammed at the
// authored 45px each. A fixed height makes the space real, and `.row`'s `flex-grow`
// below is what spends it.
//
// The cap's original job is unchanged and still needed: `.rows` still scrolls, so
// restoring rows to `RowOrder` cannot push the weapon NAME off the top of the screen.
height: 92%;
width: 880px;
margin: 0px 48px 60px 0px;
padding: 26px 32px 30px 32px;
background-color: rgba(8, 8, 8, 0.88);
border: 2px solid rgba(214, 168, 74, 0.45);
border-radius: 2px;
}
.head {
flex-direction: row;
// ⚠️ WRAPS so the pack (`.pack`, below) can start a line of its own under the name, leaving this row as it was
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
padding-bottom: 16px;
margin-bottom: 20px;
border-bottom: 2px solid rgba(214, 168, 74, 0.25);
.name {
font-size: 42px;
font-weight: bold;
letter-spacing: 3px;
color: #d6a84a;
}
// RARITY, immediately after the name.
//
// ⛔ NO `color` HERE ON PURPOSE. It is set inline from Rarity.HexFor so the five
// colours live in one place in C# — the Arsenal's rarity tab needs the same
// palette. Declaring a colour here as well would give the tier two sources, and
// the dead one always reads like the authority.
//
// ⚠️ NO auto margin. `.wclass` below takes all the slack, so this stays in the
// left group beside the name; giving this one an auto margin too would split the
// slack between them and push the rarity into the middle of the header — the
// exact failure the .wclass note records from when a third child was added.
.rarity {
margin-left: 18px;
font-size: 24px;
font-weight: bold;
letter-spacing: 2px;
}
// The class, opposite the name.
//
// ⚠️ `margin-left: auto` RATHER THAN RELYING ON space-between. With two children
// that rule put the name left and the hint right on its own; a third child lands
// in the MIDDLE instead, which is not "the right side". The auto margin takes all
// the slack before this element, pinning it and the hint together at the end.
//
// ⚠️ Deliberately quieter than the name — same gold, dimmed and smaller. It is a
// qualifier for the title, and matching its weight would give the card two
// competing headings.
.wclass {
margin-left: auto;
margin-right: 18px;
font-size: 22px;
font-weight: bold;
letter-spacing: 2px;
color: rgba(214, 168, 74, 0.65);
}
.hint {
font-size: 18px;
letter-spacing: 1px;
color: rgba(255, 255, 255, 0.3);
}
// THE PACK THE GUN CAME FROM ("PACK · BLACK OPS III", `PackLabel`), a line of its own under the name.
//
// ⚠️ THE WHOLE WIDTH, which is what puts it on a new line of the wrapping row. In the hint's grey, not the gold: a second
// gold line would give the card two headings, the thing `.wclass` is dimmed to avoid.
.pack {
width: 100%;
margin-top: 6px;
font-size: 16px;
font-weight: bold;
letter-spacing: 2px;
color: rgba(255, 255, 255, 0.45);
}
}
// ⛔ THE ROWS SCROLL, THE HEAD DOES NOT — the weapon name and class stay pinned while a
// long list moves under them. Scrolling the whole card would take the name off screen,
// which is the exact failure the cap above exists to prevent.
//
// ⚠️ `.row` CARRIES `flex-shrink: 0` AND THAT IS LOAD-BEARING. This layout engine
// resolves an over-full container by COMPRESSING its children, not by overflowing them —
// so without it twenty-four 37px rows are silently squeezed into whatever height is left,
// drawn on top of each other, and no scrollbar ever appears. That failure cost two rebuilds
// of the Arsenal's tech page; see ArsenalPanel.razor.scss.
.rows {
flex-direction: column;
flex-grow: 1;
gap: 8px;
overflow-y: scroll;
}
.row {
flex-direction: row;
align-items: center;
flex-shrink: 0;
// ⛔ `flex-grow: 1` BESIDE `flex-shrink: 0`, AND BOTH MATTER. Grow spends the height the
// nine rows do not need, so they distribute evenly down the full card instead of stacking
// at the top. Shrink-0 is the older guard and is still load-bearing: this engine resolves
// an over-full container by COMPRESSING children rather than overflowing them, so if rows
// are ever restored to `RowOrder` they stop growing, refuse to compress, and scroll —
// exactly as they did at 24. The two flags cover the two directions and neither replaces
// the other.
//
// ⚠️ `align-items: center` above is what makes a taller row look intentional: the label,
// track and figure stay on one centreline instead of riding the top edge.
flex-grow: 1;
.label {
// ⚠️ Sized for the longest label at this font size. Too narrow and it wraps, which
// knocks that one row taller than the rest.
//
// ⚠️ 260, UP FROM 210, BECAUSE THE TYPE GREW WITH THE ROW. Still "Rate of fire" that
// sets it — 12 characters, and the longest of the nine ("Penetration" is 11). Checked
// against the current `RowOrder`, not assumed from the old 24-row list.
width: 260px;
font-size: 32px;
letter-spacing: 1px;
color: rgba(255, 255, 255, 0.55);
}
// The bar is a comparison aid, so it sits BETWEEN label and number rather
// than replacing either — the exact figure is what a player quotes.
.track {
// ⚠️ WIDER AND THICKER WITH THE TYPE. A 10px bar under 38px figures reads as a
// hairline; the ratio to the row is what makes it legible, not the absolute size.
// The value column is whatever the label (260) and the bar (240 + 44) leave of 880,
// which is still comfortably wider than the 212px that forced the card to 880 for
// Pack-a-Punch's four-digit damage strings.
width: 240px;
height: 14px;
margin: 0px 22px;
background-color: rgba(255, 255, 255, 0.09);
.fill {
height: 100%;
background-color: #d6a84a;
transition: width 0.12s ease-out;
}
}
.value {
flex-grow: 1;
text-align: right;
font-size: 38px;
color: #f2ead8;
// ⚠️ Digits are TABULAR so the column does not shimmer. Every one of these
// rows changes as you swap weapons or pack one, and proportional figures
// re-space the whole number when a 1 becomes a 7 — which reads as the panel
// twitching rather than as the value changing.
font-variant-numeric: tabular-nums;
// Never let a long damage string wrap onto a second line — that alone
// would make one row taller than the rest and break the grid.
white-space: nowrap;
}
}
// ═══ BASALT (`HudTheme`) — the map's face, Cinzel on the weapon's stats ═══
//
// *"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.
.theme-basalt
{
font-family: Cinzel;
line-height: 0.898;
}