ui/arsenalpanel.razor.scss
// THE ARSENAL — the four-tab shell.
//
// ⛔ SIZED IN PERCENTAGES AND CENTRED BY ITS OWN INSETS. It was a fixed 1100x620
// box relying on the parent's `align-items: center`, and it sat hard against the
// top-left — the FOURTH time this layout system has refused to centre a child
// through its parent. WunderfizzPanel.scss records the first three (itself, and the
// powerup banner twice) and works around it with half-size negative margins.
//
// ⚠️ THE NEGATIVE-MARGIN TRICK IS NOT USED HERE, deliberately: it needs a KNOWN
// fixed size, and a percentage `margin-top` resolves against the parent's WIDTH, not
// its height — so it cannot centre a percentage-sized box vertically without being
// silently wrong on any non-square screen.
//
// The insets centre it arithmetically instead: left 5% + width 90% leaves 5% either
// side, top 7% + height 86% leaves 7% above and below. No flex centring, no negative
// margins, no transform support needed — and it cannot overflow, which was the
// actual cause of the pinning: 620px was TALLER than the 613px viewport it was being
// asked to centre inside.

root
{
    position: absolute;
    width: 100%;
    height: 100%;
}

.arsenal
{
    position: absolute;
    left: 5%;
    top: 7%;
    width: 90%;
    height: 86%;

    flex-direction: column;
    align-items: center;

    background-color: rgba( 8, 9, 12, 0.94 );
    border: 2px solid rgba( 255, 255, 255, 0.14 );
    border-radius: 4px;

    font-family: Inter, system-ui, sans-serif;
    color: rgba( 235, 235, 235, 0.95 );
}

.title
{
    margin-top: 26px;
    font-size: 52px;
    font-weight: 700;
    letter-spacing: 8px;
    color: rgba( 255, 190, 70, 0.95 );
    text-shadow: 0px 2px 8px rgba( 0, 0, 0, 0.9 );
}

// ── tabs ────────────────────────────────────────────────────────────────────

.tabs
{
    flex-direction: row;
    width: 94%;
    margin-top: 26px;
    gap: 18px;
}

// ⚠️ flex-grow, NOT a fixed width. Four 250px tabs plus gaps need 1060px, which
// overflowed the box on a narrow window — part of what pushed the panel off-centre.
// Growing into whatever is available cannot overflow at any size.
.tab
{
    flex-grow: 1;
    height: 54px;
    align-items: center;
    justify-content: center;

    font-size: 22px;
    font-weight: 600;
    letter-spacing: 1px;

    background-color: rgba( 255, 255, 255, 0.05 );
    border: 2px solid rgba( 255, 255, 255, 0.12 );
    color: rgba( 235, 235, 235, 0.7 );

    &:hover
    {
        background-color: rgba( 255, 255, 255, 0.10 );
        color: rgba( 255, 255, 255, 0.95 );
    }
}

// The open tab. Gold, matching the title.
.tab.on
{
    background-color: rgba( 255, 190, 70, 0.16 );
    border-color: rgba( 255, 190, 70, 0.75 );
    color: rgba( 255, 215, 130, 1 );
}

// ⚠️ DIMMED, NOT DISABLED. An unbuilt tab still opens and still says why it is
// empty — a tab that refuses to click reads as broken, and the point of showing all
// four is that the shape of the finished machine is visible while it fills in.
//
// ⛔ `border-style: dashed` WAS HERE AND NEVER WORKED. s&box logs
// "dashed is not valid with border-style" and drops the declaration, so the dashed
// edge these two rules were written for has never once been drawn — the tabs were
// only ever dimmed. It went unnoticed because dim alone looks deliberate, and because
// the warning only surfaces when the stylesheet is recompiled.
//
// Replaced with a FAINTER BORDER, which is supported and carries the same meaning.
// A silently-dropped property is worse than a plainer one that works.
.tab.todo
{
    color: rgba( 235, 235, 235, 0.42 );
    border-color: rgba( 255, 255, 255, 0.06 );
}

.tab.todo.on
{
    color: rgba( 255, 215, 130, 0.85 );
    border-color: rgba( 255, 190, 70, 0.35 );
}

// ── page body ───────────────────────────────────────────────────────────────

// ⛔ `flex-direction: column` IS LOAD-BEARING AND WAS MISSING. Without it this
// container laid its children out as a ROW, so on any page with more than one child the
// second sat BESIDE the first instead of under it — the weapon name ended up stranded in
// a narrow column down the left edge with the node list squeezed beside it, and the
// squeeze is what made the tier bands overflow into each other.
//
// ⚠️ Every other flex container in this stylesheet states its direction explicitly.
// This one relied on a default, and the default is not the one CSS uses.
.content
{
    width: 94%;
    flex-grow: 1;
    margin-top: 26px;
    flex-direction: column;
    align-items: center;
    justify-content: center;
}

// ── armor cards ───────────────────────────────────────────────

.cards
{
    flex-direction: row;
    justify-content: center;
    align-items: center;
    gap: 40px;
}

// ⚠️ A FIXED CARD, unlike the tabs. The original's are 260x340 and their tall
// portrait shape is what makes them read as cards rather than buttons — letting them
// flex-grow would stretch three of them into wide panels on a big screen. Three at
// 260 plus gaps is 900, which fits inside the 90%-wide box at any sane resolution.
.card
{
    width: 260px;
    height: 340px;
    flex-direction: column;
    align-items: center;

    background-color: rgba( 255, 255, 255, 0.04 );
    border: 2px solid rgba( 255, 255, 255, 0.12 );
    border-radius: 3px;
}

.kind
{
    margin-top: 26px;
    font-size: 16px;
    font-weight: 600;
    letter-spacing: 2px;
    color: rgba( 235, 235, 235, 0.55 );
}

.tier
{
    margin-top: 14px;
    font-size: 46px;
    font-weight: 700;
    letter-spacing: 2px;
    color: rgba( 255, 225, 170, 0.95 );
}

.hp
{
    margin-top: 12px;
    font-size: 22px;
    font-weight: 600;
    color: rgba( 235, 235, 235, 0.6 );
}

// ⛔ PUSHED DOWN BY margin-top:auto, NOT POSITIONED ABSOLUTELY. It was
// `position: absolute; bottom: 26px` and it escaped the card completely — all three
// costs rendered in a row along the bottom of the PANEL, beside the CLOSE button.
//
// The reason: in this layout system an absolutely-positioned child anchors to the
// nearest ancestor that is ITSELF positioned, and `.card` was not — so they resolved
// against `.arsenal`. WunderfizzPanel's absolute children work only because `.fizz`
// is `position: absolute` itself.
//
// Auto margin in a flex column does the same job with nothing to anchor to, so it
// cannot come loose. This layout system has already refused to centre a child through
// its parent four times; giving it fewer chances is the cheaper fix.
.cost
{
    margin-top: auto;
    margin-bottom: 24px;
    flex-direction: row;
    align-items: center;
    gap: 9px;

    // A green diamond, the same shape and colour as the salvage pickup's outline and
    // the HUD counter — so the currency is recognisable without the word.
    .coin
    {
        width: 13px;
        height: 13px;
        background-color: rgba( 60, 210, 90, 0.95 );
        transform: rotate( 45deg );
    }

    .amount
    {
        font-size: 26px;
        font-weight: 700;
        color: rgba( 255, 255, 255, 0.95 );
    }
}

// ⚠️ ONLY THE BUYABLE CARD REACTS TO THE CURSOR. A hover on a locked or owned
// card would say "click me" about something that refuses, which is the same lie the
// overlay exists to prevent.
.card.buyable
{
    border-color: rgba( 255, 190, 70, 0.55 );

    &:hover
    {
        background-color: rgba( 255, 190, 70, 0.13 );
        border-color: rgba( 255, 190, 70, 0.9 );
    }
}

// ⛔ A FLOW LABEL, NOT A COVERING BOX. The original draws its status as an overlay
// across the whole card; an absolutely-positioned one here broke loose exactly as the
// cost did, leaving stray "LOCKED" text across the tier name and one card shaded
// differently from its neighbour in the same state.
//
// So the status sits in the card's own column, and the DIMMING moves onto the card
// itself — which reads the same and cannot be mispositioned.
.status
{
    margin-top: 10px;
    font-size: 22px;
    font-weight: 700;
    letter-spacing: 3px;
}

// Colours from the original: owned green, locked grey, unaffordable red.
.card.owned  .status { color: rgba( 120, 220, 140, 1 ); }
.card.locked .status { color: rgba( 140, 140, 140, 1 ); }
.card.poor   .status { color: rgba( 215, 120, 120, 1 ); }

// The dimming the overlay used to provide, applied to the card so every card in the
// same state looks the same.
.card.owned,
.card.locked,
.card.poor
{
    background-color: rgba( 255, 255, 255, 0.02 );

    .kind, .tier, .hp { opacity: 0.42; }
    .cost { opacity: 0.42; }
}

// Owned is a success, not a refusal — it keeps a green edge rather than reading as
// another thing you cannot have.
.card.owned
{
    border-color: rgba( 120, 220, 140, 0.45 );
}

// ── ammo type page ────────────────────────────────────────────────────────────
//
// ⛔ SEVEN CARDS, NOT FIVE, AND THAT IS WHAT SETS THE SIZE. The rarity page's own
// note records the trap: five 260px cards overflowed the 90%-wide panel and pinned it to
// the top-left. Seven is worse — six mods plus RANDOM. At 168 wide with an 18px gap
// that is 7x168 + 6x18 = 1284, which still fits; at rarity's 196 it would be 1480 and
// would not.
//
// ⚠️ THE CARDS ARE ALSO TALLER, because each carries a badge and a sentence of
// description that neither of the other pages has. 316 tall with that content clipped the
// blurb.
.cards.ammo
{
    gap: 18px;

    .card
    {
        width: 168px;
        height: 352px;
    }

    // ⚠️ SMALL, because "RADIOACTIVE DECAY" is seventeen characters in a 168px
    // card. The rarity page dropped to 26px for nine; this needs less again, and the
    // names WRAP rather than being truncated — a clipped mod name is unreadable
    // and there is vertical room for two lines.
    .tier
    {
        margin-top: 10px;
        font-size: 15px;
        letter-spacing: 1px;
        text-align: center;
        width: 90%;
    }

    // The chance/cooldown line.
    .hp
    {
        margin-top: 8px;
        font-size: 16px;
    }

    // ⚠️ THE `kind` LABEL IS SHRUNK AND ITS TOP MARGIN CUT. At 26px of margin
    // (the shared value) plus a badge plus a wrapped name plus a blurb, the foot ran off
    // the bottom of the card.
    .kind
    {
        margin-top: 12px;
        font-size: 12px;
        letter-spacing: 1px;
    }
}

// The upstream badge, the same image the HUD shows beside the weapon name.
//
// ⚠️ NOT `Additive` OR TINTED HERE, unlike the HUD's copy: on the HUD the badge
// dims to show a cooldown, but on a shop card the only question is which mod this is, and
// a dimmed icon there would read as the card being disabled.
.cards.ammo .badge
{
    width: 62px;
    height: 62px;
    margin-top: 10px;
}

// The one-sentence description. The only wrapped prose on any Arsenal card.
.cards.ammo .blurb
{
    margin-top: 10px;
    width: 86%;
    font-size: 13px;
    text-align: center;
    color: rgba( 225, 225, 225, 0.6 );
}

// ⛔ THE UNWIRED WARNING, AND IT IS DELIBERATELY LOUD. Four of the six mods roll,
// show their badge on the HUD and do nothing at all. A card that looked identical to a
// working one would be selling 500 salvage of nothing, so this says so before the click
// rather than after — the same argument the unbuilt TABS make about themselves.
.cards.ammo .nofx
{
    margin-top: 8px;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 1px;
    color: rgba( 235, 170, 90, 0.95 );
}

// ⚠️ THE SAME `.foot` WRAPPER TREATMENT AS THE RARITY CARD, and for the identical
// reason its note gives: a fitted card shows no price, so the auto margin has to live on
// the wrapper. Declaring it on both `.cost` and `.status` SPLITS the slack rather than
// falling back.
.cards.ammo .card .foot
{
    margin-top: auto;
    margin-bottom: 16px;
    flex-direction: column;
    align-items: center;
    gap: 6px;

    .cost
    {
        margin-top: 0px;
    }
}

// FITTED is this page's "you are here", the same role EQUIPPED plays on the rarity
// ladder — so it borrows that styling rather than inventing a second colour for the
// same idea.
.card.fitted
{
    border-color: rgba( 255, 190, 70, 0.75 );
    background-color: rgba( 255, 190, 70, 0.07 );

    .status { color: rgba( 255, 215, 130, 1 ); }
}

// The RANDOM card — no badge, no blurb, so it needs to read as deliberate rather
// than as a card that failed to load. A dashed border does that; it is the one card that
// is not a specific product.
//
// ⛔ `justify-content: center` WAS HERE AND COULD NEVER HAVE WORKED. The shared
// `.cards.ammo .card .foot` rule puts `margin-top: auto` on the foot, and an auto margin
// absorbs ALL the free space — so there is none left for the container to centre
// with, and the rest of the card stays pinned to the top. It rendered top-aligned while
// the stylesheet claimed otherwise. Removed rather than fought: top-aligned content with
// the price at the bottom is what every other card on the page does, so matching them is
// both simpler and more consistent than making this one card special.
//
// ⚠️ THE PADDING REPLACES IT, pushing the three lines down to roughly where the
// other cards' badges sit, so the row of names lines up across all seven.
.card.ammo-random
{
    border-style: dashed;

    .kind { margin-top: 44px; }
    .tier { font-size: 22px; }
    .hp { opacity: 0.7; }
}

// ── ammo type page: the grid and the detail panel (2026-10-04) ─────────────────────────────────────────
//
// ⛔ THE USER'S LAYOUT: "all the ammo mods in 5 rows of 4 on the left, jsut the icons and name / and when i click one the
// right side shows me the mod and what it does including cooldown and chance, and a button to buy". RANDOM plus 19 mods is
// 20 tiles, so the grid is exactly full. The `.cards.ammo` rules above drew the old row of cards and no longer match
// anything.
//
// ⚠️ SIZED TO FILL THE PAGE (2026-10-04, the user: "we can make everyhing a bit bigger to fill the scree mor"): five 92-tall
// rows with four 10px gaps are 500 (520 if the borders count), which with the weapon's name above it leaves about 30px
// before CLOSE on the user's 16:9-ish window. The tiles wrap inside a fixed-width box, since this layout system has no
// grid, and the panel stretches to the grid's height.
.ammopage
{
    flex-direction: row;
    align-items: stretch;
    gap: 30px;
}

// ⛔ NEVER AN EXACT FIT. 424 (4 x 100 + 3 x 8) wrapped at three a row on the user's screen: the UI scale's rounding, or
// the 2px borders if they count toward a tile's width, pushed the fourth tile down. 580 holds four 128s either way
// (4 x 132 + 30 = 558) and can't hold five (680). `justify-content: center` splits the spare width on both sides of a row.
.ammogrid
{
    width: 580px;
    flex-direction: row;
    flex-wrap: wrap;
    justify-content: center;
    gap: 10px;
}

.atile
{
    width: 128px;
    height: 92px;
    flex-direction: column;
    align-items: center;

    background-color: rgba( 255, 255, 255, 0.04 );
    border: 2px solid rgba( 255, 255, 255, 0.12 );
    border-radius: 3px;

    .aicon
    {
        width: 54px;
        height: 54px;
        margin-top: 7px;
    }

    // ⚠️ SMALL, AND IT WRAPS: "RADIOACTIVE DECAY" takes two lines in a 128px tile.
    //
    // ⛔ `justify-content: center` AS WELL AS `text-align`. The text is a child box sized to itself and sitting at the start
    // of this one, so `text-align` alone centred the lines on each other and left the block hard against the tile's left
    // edge (2026-10-04, the user's screenshot).
    .aname
    {
        margin-top: 4px;
        width: 94%;
        justify-content: center;
        font-size: 12px;
        font-weight: 700;
        letter-spacing: 1px;
        text-align: center;
        color: rgba( 255, 225, 170, 0.9 );
    }

    &:hover
    {
        background-color: rgba( 255, 190, 70, 0.1 );
        border-color: rgba( 255, 190, 70, 0.6 );
    }
}

// The one on the gun: green, like owned things elsewhere.
.atile.fitted
{
    border-color: rgba( 96, 200, 120, 0.85 );
}

// The one being looked at. After `.fitted`, so a selected tile always shows as selected.
.atile.on
{
    background-color: rgba( 255, 190, 70, 0.14 );
    border-color: rgba( 255, 190, 70, 0.95 );
}

// Designed, not built: dimmed, but still clickable to read about.
.atile.soon
{
    opacity: 0.45;
}

// RANDOM's mark, until it has an icon of its own.
.ricon
{
    width: 54px;
    height: 54px;
    margin-top: 7px;
    align-items: center;
    justify-content: center;
    font-size: 38px;
    font-weight: 700;
    color: rgba( 255, 190, 70, 0.9 );
    border: 2px solid rgba( 255, 190, 70, 0.5 );
    border-radius: 6px;
}

// ⚠️ NO HEIGHT OF ITS OWN: `.ammopage` stretches it to the grid's, so the two always end level.
//
// ⚠️ PADDING 16, NOT 22, SINCE 2026-10-06: five upgrade rows in the height three had (the ladder's note, below).
.ammodetail
{
    width: 600px;
    flex-direction: column;
    align-items: center;
    padding: 16px 26px;

    background-color: rgba( 255, 255, 255, 0.04 );
    border: 2px solid rgba( 255, 255, 255, 0.12 );
    border-radius: 3px;

    .dicon
    {
        width: 140px;
        height: 140px;
    }

    .ricon.big
    {
        width: 140px;
        height: 140px;
        margin-top: 0px;
        font-size: 90px;
    }

    .dname
    {
        margin-top: 12px;
        font-size: 32px;
        font-weight: 700;
        letter-spacing: 3px;
        color: rgba( 255, 225, 170, 0.95 );
    }

    .drows
    {
        margin-top: 16px;
        width: 100%;
        flex-direction: column;
        gap: 7px;
    }

    .drow
    {
        flex-direction: row;
        justify-content: space-between;

        .k
        {
            font-size: 15px;
            font-weight: 700;
            letter-spacing: 2px;
            color: rgba( 235, 235, 235, 0.45 );
        }

        .v
        {
            font-size: 18px;
            font-weight: 600;
            color: rgba( 235, 235, 235, 0.92 );
        }
    }

    .ddesc
    {
        margin-top: 16px;
        width: 100%;
        font-size: 18px;
        text-align: center;
        color: rgba( 225, 225, 225, 0.75 );
    }

    .dsoon
    {
        margin-top: 10px;
        font-size: 14px;
        font-weight: 700;
        letter-spacing: 2px;
        color: rgba( 235, 170, 90, 0.95 );
    }

    // ⚠️ THE PRICE AND THE BUTTON SIT AT THE FOOT (`margin-top: auto`), where every Arsenal card keeps its price. The
    // shared `.cost` carries its own auto margin and a bottom margin; both are taken off here, inside the row.
    //
    // ⚠️ A SIZE DOWN SINCE 2026-10-06 (30 → 26, the button 22 → 20 and 10 → 8 of padding), for the five upgrade rows above it.
    .dbuy
    {
        margin-top: auto;
        flex-direction: row;
        align-items: center;
        gap: 24px;

        .cost
        {
            margin-top: 0px;
            margin-bottom: 0px;

            .coin
            {
                width: 15px;
                height: 15px;
            }

            .amount
            {
                font-size: 26px;
            }
        }
    }

    .buy
    {
        padding: 8px 30px;
        font-size: 20px;
        font-weight: 700;
        letter-spacing: 3px;
        border: 2px solid rgba( 255, 255, 255, 0.2 );
        border-radius: 3px;
        color: rgba( 235, 235, 235, 0.5 );
    }

    .buy.buyable
    {
        border-color: rgba( 255, 190, 70, 0.8 );
        color: rgba( 255, 210, 120, 1 );

        &:hover
        {
            background-color: rgba( 255, 190, 70, 0.18 );
            border-color: rgba( 255, 190, 70, 1 );
        }
    }

    .buy.fitted
    {
        border-color: rgba( 96, 200, 120, 0.7 );
        color: rgba( 130, 220, 150, 1 );
    }

    .buy.poor
    {
        color: rgba( 220, 110, 90, 0.95 );
    }
}

// ── the ammo page's mod header and upgrades (2026-10-05) ──────────────────────────────────────────────
//
// ⛔ THE ICON MOVED BESIDE THE NAME, AND THE PANEL GREW 40px (THE GAP GAVE 10 OF IT), FOR THE UPGRADES. Stacked, the 140px icon,
// the name, three rows and the blurb already took about 450 of the panel's 500, the grid's height, which the panel must not
// pass: `.ammopage` would grow and push CLOSE off the screen. Side by side they take about 100, and the three upgrade rows
// about 160. 580 + 30 + 600 still fits a 4:3 screen, as 580 + 40 + 560 did.
//
// ⚠️ EVERY EFFECT LINE IS WRITTEN SHORT (`AmmoModUpgrades.All`) so a row stays one line in Inter. Cinzel, basalt's face, is wider
// and wraps a few to two; the panel has room for that, not for three-line rows.
//
// ⛔ FIVE ROWS SINCE 2026-10-06 (IV and V), IN THE SAME 500. At the sizes above they came to about 550, past the grid, so every
// part of the panel gave a little: its padding (22 → 16), the header's name and rows, the blurb, the ladder's gaps, each row's
// padding (5 → 3) and type, the buy button. Worked from Inter's line box, 1.211 of the size, which basalt pins Cinzel to: padding
// and border 36, header 89, blurb 44 (two lines), ladder 257 (its head and five rows of 42), button 44 — about 470, which leaves
// room for an effect line or two wrapping in Cinzel (16 each). ⚠️ COMPUTED, NOT SEEN: open a mod at IV or V in both faces and look.
.ammodetail
{
    .dhead
    {
        width: 100%;
        flex-direction: row;
        align-items: center;
        gap: 18px;

        .dicon
        {
            width: 84px;
            height: 84px;
            flex-shrink: 0;
        }

        .dname
        {
            margin-top: 0px;
            font-size: 26px;
        }

        .drows
        {
            margin-top: 4px;
            gap: 1px;
        }

        .drow .k { font-size: 12px; }
        .drow .v { font-size: 14px; }
    }

    .dtitle
    {
        flex-grow: 1;
        flex-shrink: 1;
        flex-direction: column;
    }

    .ddesc.tight
    {
        margin-top: 8px;
        font-size: 15px;
    }

    // The ladder. Owned is green, the next level gold and clickable, the later ones dim: the armor cards' meanings, in rows.
    .dups
    {
        margin-top: 8px;
        margin-bottom: 6px;
        width: 100%;
        flex-direction: column;
        gap: 4px;
    }

    .uhead
    {
        flex-direction: row;
        justify-content: space-between;
        align-items: center;

        .ulabel
        {
            font-size: 13px;
            font-weight: 700;
            letter-spacing: 3px;
            color: rgba( 235, 235, 235, 0.45 );
        }

        .unote
        {
            font-size: 12px;
            letter-spacing: 1px;
            color: rgba( 235, 235, 235, 0.35 );
        }
    }

    // ⚠️ 3px OF PADDING AND A POINT OFF THE TYPE SINCE 2026-10-06 (the note above): a row is 42 tall, its effect line kept at 13.
    .uprow
    {
        flex-direction: row;
        align-items: center;
        gap: 10px;
        padding: 3px 8px;

        background-color: rgba( 255, 255, 255, 0.03 );
        border: 2px solid rgba( 255, 255, 255, 0.1 );
        border-radius: 3px;

        .unum
        {
            width: 34px;
            flex-shrink: 0;
            justify-content: center;
            font-size: 18px;
            font-weight: 700;
            color: rgba( 255, 225, 170, 0.55 );
        }

        .ubody
        {
            flex-grow: 1;
            flex-shrink: 1;
            flex-direction: column;
        }

        .uname
        {
            flex-direction: row;
            align-items: center;
            gap: 8px;
        }

        .un
        {
            font-size: 13px;
            font-weight: 700;
            letter-spacing: 1px;
            color: rgba( 255, 225, 170, 0.92 );
        }

        .usoon
        {
            font-size: 10px;
            font-weight: 700;
            letter-spacing: 1px;
            color: rgba( 235, 170, 90, 0.9 );
        }

        .ueff
        {
            font-size: 13px;
            color: rgba( 225, 225, 225, 0.72 );
        }

        // ⚠️ THE SHARED `.cost` CARRIES AN AUTO MARGIN AND A BOTTOM MARGIN, as `.dbuy` notes; both are taken off here.
        .uside
        {
            width: 88px;
            flex-shrink: 0;
            flex-direction: column;
            align-items: flex-end;

            .cost
            {
                margin-top: 0px;
                margin-bottom: 0px;

                .coin
                {
                    width: 11px;
                    height: 11px;
                }

                // ⚠️ 15, NOT 17 (2026-10-06): the row's height, and V's "10,000" is a figure wider than III's "3,000".
                .amount
                {
                    font-size: 15px;
                }
            }
        }

        .ustate
        {
            font-size: 10px;
            font-weight: 700;
            letter-spacing: 1px;
            color: rgba( 235, 235, 235, 0.45 );
        }
    }

    .uprow.owned
    {
        border-color: rgba( 96, 200, 120, 0.55 );

        .unum, .ustate { color: rgba( 130, 220, 150, 1 ); }
    }

    .uprow.buyable
    {
        border-color: rgba( 255, 190, 70, 0.75 );
        background-color: rgba( 255, 190, 70, 0.07 );

        .unum, .ustate { color: rgba( 255, 210, 120, 1 ); }

        &:hover
        {
            background-color: rgba( 255, 190, 70, 0.18 );
            border-color: rgba( 255, 190, 70, 1 );
        }
    }

    .uprow.poor .ustate { color: rgba( 220, 110, 90, 0.95 ); }

    // A level that needs the one before it first: dim, as a designed mod's tile is.
    .uprow.locked { opacity: 0.5; }
}

// A mod's upgrade level on its grid tile: a pip a level in the top corner, once it has a level.
//
// ⛔ THE TILE IS POSITIONED SO THE PIPS ANCHOR TO IT. In this layout an absolute child anchors to the nearest ancestor that is
// ITSELF positioned (`.cost`'s note): without this rule all nineteen tiles' pips would stack in a corner of `.arsenal`.
// Scoreboard's `.pip` and the HUD's `.boot` anchor their pieces the same way.
//
// ⚠️ FIVE PIPS SINCE 2026-10-06 (IV and V), 7px with 2px between where three were 8px with 3: a row 43 wide, where five of the
// old ones would be 52 and reach well over the 54px icon, which starts 37 from the tile's edge. These still touch its corner.
.atile
{
    position: relative;
}

.atile .apips
{
    position: absolute;
    top: 5px;
    right: 6px;
    flex-direction: row;
    gap: 2px;

    .pip
    {
        width: 7px;
        height: 7px;
        border-radius: 4px;
        background-color: rgba( 255, 255, 255, 0.12 );
        border: 1px solid rgba( 255, 190, 70, 0.5 );
    }

    .pip.on
    {
        background-color: rgba( 255, 200, 90, 1 );
    }
}

// ── weapon rarity page ───────────────────────────────────────

// The gun this page is about, above the ladder.
//
// ⚠️ `.content` is `justify-content: center`, so this and the card row share the
// vertical middle rather than this sitting at the top. That is deliberate: the subject
// and its ladder read as one block.
.subject
{
    flex-direction: column;
    align-items: center;
    margin-bottom: 22px;

    .wep
    {
        font-size: 30px;
        font-weight: 700;
        letter-spacing: 3px;
        color: rgba( 255, 190, 70, 0.9 );
    }

    // The current tier. Colour comes INLINE from Rarity.HexFor — no colour here.
    .cur
    {
        margin-top: 6px;
        font-size: 21px;
        font-weight: 700;
        letter-spacing: 3px;
    }
}

// ⛔ FIVE CARDS, NARROWER THAN ARMOR'S THREE. The armor row is 3x260 + 2x40 gaps =
// 860. Five at that size would be 1460 and would overflow the 90%-wide panel on any
// normal window — the exact overflow that pinned this panel to the top-left the first
// time (see the header note). 5x196 + 4x24 = 1076, which fits.
//
// ⚠️ STILL FIXED, NOT flex-grow, for the same reason armor's are: the tall portrait
// shape is what makes them read as cards rather than buttons.
.cards.rarity
{
    gap: 24px;

    .card
    {
        width: 196px;
        height: 316px;
    }

    // Smaller than armor's 46px — "LEGENDARY" is nine characters in a 196px card.
    .tier
    {
        margin-top: 12px;
        font-size: 26px;
        letter-spacing: 1px;
    }

    .hp
    {
        margin-top: 10px;
        font-size: 19px;
    }
}

// ⚠️ SIX CARDS ONCE BASALT'S EASTER EGG IS COMPLETE — Godly's too. At five's size six would be
// 6x196 + 5x24 = 1296 and overflow a narrow window; narrower cards and gaps keep the row to
// 6x172 + 5x16 = 1112, about what five took.
.cards.rarity.six
{
    gap: 16px;

    .card
    {
        width: 172px;
    }

    .tier
    {
        font-size: 23px;
    }
}

// The solid colour bar from the original — what makes the ladder readable without
// reading five words. Colour is inline.
.band
{
    width: 78%;
    height: 8px;
    margin-top: 16px;
    border-radius: 2px;
}

// ⛔ THE ONLY margin-top:auto ON THE RARITY CARD. See the razor note: the armor card
// can put it on `.cost` because a cost is always drawn, but a rarity card at or below
// the current tier has no price, and declaring it on both `.cost` and `.status` would
// SPLIT the free space instead of falling back.
.cards.rarity .card .foot
{
    margin-top: auto;
    margin-bottom: 18px;
    flex-direction: column;
    align-items: center;
    gap: 8px;

    // Overrides the shared `.cost` rule, whose own auto margin would otherwise fight
    // the wrapper's for the same slack.
    .cost
    {
        margin-top: 0px;
        margin-bottom: 0px;
    }

    .status
    {
        margin-top: 0px;
        font-size: 18px;
        letter-spacing: 2px;
    }
}

// EQUIPPED is the state armor has no equivalent of — it is not a purchase, it is where
// the weapon currently sits. Bordered in the tier's own colour would need inline style
// on the card; a warm neutral says "you are here" without a second colour source.
.card.equipped
{
    border-color: rgba( 255, 190, 70, 0.75 );
    background-color: rgba( 255, 190, 70, 0.07 );

    .status { color: rgba( 255, 215, 130, 1 ); }
}

// ⚠️ NOT DIMMED, unlike owned/locked/poor. The equipped card is the one true
// statement on the page about the gun in your hands; dimming it would bury it among
// the tiers you cannot have.

// ── weapon tech page ─────────────────────────────────────────

// ⛔ ONE TIER AT A TIME, WHICH IS WHY NOTHING HERE SCROLLS ANY MORE. Two earlier
// attempts failed for the same underlying reason and both are worth recording:
//
//   1. A WRAPPING GRID of 41 small cards. `flex-wrap` did not grow its container, so
//      tier 4's second row of cards drew straight over the tier 5 header.
//   2. FULL-WIDTH ROWS for all 41 at once. Flex children SHRINK below their stated
//      height when the container cannot fit them, so `height: 44px` became about 10px
//      and all seven rows of tier 1 drew on top of each other. `overflow-y: scroll` did
//      not save it — the content had already been compressed to fit rather than
//      overflowing.
//
// The lesson is the same both times: this layout engine solves an over-full container by
// squashing children, not by overflowing them. So the fix is to stop over-filling it —
// 11 rows is the most any tier holds, and that fits at a readable size.

// ⚠️ `flex-shrink: 0` ON THE ROWS AND THE BAND regardless, so if a tier ever grows past
// the space the rows keep their height and the band overflows visibly instead of silently
// collapsing into an unreadable stack. A layout that breaks loudly is debuggable.

.subject
{
    // stays a column: weapon name, then the tier selector under it
    flex-shrink: 0;
}

// Tier selector — five small buttons.
.tierpick
{
    flex-direction: row;
    justify-content: center;
    margin-top: 8px;
    gap: 8px;

    .tbtn
    {
        width: 56px;
        height: 40px;
        align-items: center;
        justify-content: center;

        font-size: 22px;
        font-weight: 700;

        background-color: rgba( 255, 255, 255, 0.05 );
        border: 2px solid rgba( 255, 255, 255, 0.12 );
        color: rgba( 235, 235, 235, 0.7 );

        &:hover
        {
            background-color: rgba( 255, 255, 255, 0.12 );
            color: rgba( 255, 255, 255, 0.95 );
        }
    }

    // The tier being viewed.
    .tbtn.on
    {
        background-color: rgba( 255, 190, 70, 0.18 );
        border-color: rgba( 255, 190, 70, 0.8 );
        color: rgba( 255, 215, 130, 1 );
    }
}

// ⚠️ `flex-grow: 1` SO THE BAND OWNS WHATEVER HEIGHT IS LEFT under the subject, and
// `align-self: stretch` because `.content` centres its children — without it a tall tier
// would be centred and clipped at BOTH ends, with the top unreachable.
//
// ⚠️ `.content` keeps `justify-content: center`, which is right for the armor page's
// three cards. This page does not depend on it: a flex-grow child absorbs the free space
// either way, so the two pages can disagree about vertical alignment without fighting.
.tierband
{
    width: 100%;
    flex-grow: 1;
    align-self: stretch;
    flex-direction: column;
    margin-top: 14px;
    padding: 12px 14px;
    background-color: rgba( 255, 255, 255, 0.02 );
    border-left: 4px solid rgba( 255, 190, 70, 0.55 );
}

// ⛔ THE ROWS SCROLL, THE HEADER DOES NOT. Tiers 1-3 hold 5-7 rows and never scroll;
// tiers 4 and 5 hold 11 and scroll a little. Scrolling the whole band would take the
// PICK count — the one number that explains why a row says TIER FULL — off screen
// exactly when a player is scrolling to find something to buy.
//
// ⚠️ This only works because the rows carry `flex-shrink: 0`. Without it the engine
// solves an over-full container by COMPRESSING children rather than overflowing them, so
// eleven 42px rows silently became eleven 10px rows drawn on top of each other and the
// scrollbar never appeared. That failure is what this whole page was rebuilt to escape.
.technodes
{
    width: 100%;
    flex-direction: column;
    flex-grow: 1;
    overflow-y: scroll;
}

.tierhead
{
    flex-direction: row;
    align-items: baseline;
    flex-shrink: 0;
    margin-bottom: 12px;

    .tnum
    {
        margin-right: 12px;
        font-size: 34px;
        font-weight: 700;
        letter-spacing: 2px;
        color: rgba( 255, 190, 70, 0.95 );
    }

    .tname
    {
        font-size: 34px;
        font-weight: 700;
        letter-spacing: 2px;
        color: rgba( 255, 235, 200, 0.85 );
    }

    // ⚠️ `margin-left: auto` ON THE FIRST OF THE TRAILING PAIR ONLY. Two auto margins
    // split the slack between them and strand the pick count mid-header — the failure
    // WeaponStatsPanel's `.wclass` note records from a three-child header.
    .tpick
    {
        margin-left: auto;
        margin-right: 20px;
        font-size: 27px;
        font-weight: 700;
        color: rgba( 120, 220, 140, 0.95 );
    }

    .tcost
    {
        font-size: 23px;
        color: rgba( 235, 235, 235, 0.5 );
    }
}

// One row per node.
// ⚠️ SCALED 1.5x FROM 42px, which is what lets the type be this size — the row height
// was the constraint, not the font. Every dimension moved together so the proportions
// hold: 42->63 tall, 20->30 name, 18->27 effect, 17->25 state.
//
// ⚠️ 63px x 11 ROWS IS 693px, SO TIERS 4 AND 5 NOW ALWAYS SCROLL. That is the trade
// this size buys and it is fine — the scroll works properly now that the rows carry
// `flex-shrink: 0`. Tiers 1-3 at 5-7 rows still fit without scrolling.
.technode
{
    width: 100%;
    flex-direction: row;
    align-items: center;
    flex-shrink: 0;
    height: 63px;
    padding: 0px 21px;
    margin-bottom: 9px;

    background-color: rgba( 255, 255, 255, 0.04 );
    border: 2px solid rgba( 255, 255, 255, 0.10 );
    border-radius: 3px;

    // Fixed, so every name starts on the same column and the list scans vertically.
    .nname
    {
        width: 340px;
        flex-shrink: 0;
        font-size: 30px;
        font-weight: 700;
        letter-spacing: 1px;
        color: rgba( 255, 235, 200, 0.95 );
    }

    // ⚠️ ALLOWED TO WRAP TO A SECOND LINE. At 27px the longest effect strings —
    // Railgun's and Chimera's are both ~86 characters — need more width than the row
    // has left after a 340px name and a 180px state column. Two lines of 27px is 54px,
    // which still fits inside 63px, so a wrap costs nothing; forcing one line would clip
    // exactly the nodes whose text matters most.
    .neffect
    {
        flex-grow: 1;
        font-size: 27px;
        color: rgba( 235, 235, 235, 0.75 );
    }

    // ⚠️ FIXED WIDTH AND ALWAYS RENDERED, even when empty. A label that only exists
    // sometimes lets the effect text reflow between rows, so buyable rows end up a
    // different length from owned ones and the column wanders down the list.
    .nstate
    {
        width: 180px;
        flex-shrink: 0;
        text-align: right;
        font-size: 25px;
        font-weight: 700;
        letter-spacing: 2px;
    }
}

// ⚠️ ONLY A BUYABLE ROW REACTS TO THE CURSOR — a hover on something that refuses is
// the same lie the state label exists to prevent.
.technode.buyable
{
    border-color: rgba( 255, 190, 70, 0.55 );

    &:hover
    {
        background-color: rgba( 255, 190, 70, 0.13 );
        border-color: rgba( 255, 190, 70, 0.9 );
    }
}

.technode.owned
{
    border-color: rgba( 120, 220, 140, 0.45 );
    .nstate { color: rgba( 120, 220, 140, 1 ); }
}

.technode.poor   .nstate { color: rgba( 215, 120, 120, 1 ); }
.technode.full   .nstate { color: rgba( 235, 190, 110, 1 ); }
.technode.locked .nstate { color: rgba( 140, 140, 140, 1 ); }

// ⛔ THE NON-CLASS AUGMENTS EACH HAVE A COLOUR (the user, 2026-10-04): fire mode (action) blue, clip
// size (magazine) purple, shell load (one round at a time) orange. A thick left edge and a tinted name,
// so the card's state border (buyable, owned) still reads. Restated in the basalt block, whose
// `.technode .nname` would otherwise win on specificity.
.technode.set-action { border-left: 6px solid rgba( 90, 160, 255, 0.95 ); }
.technode.set-action .nname { color: rgba( 150, 200, 255, 1 ); }
.technode.set-mag { border-left: 6px solid rgba( 175, 110, 255, 0.95 ); }
.technode.set-mag .nname { color: rgba( 210, 170, 255, 1 ); }
.technode.set-reload { border-left: 6px solid rgba( 255, 150, 50, 0.95 ); }
.technode.set-reload .nname { color: rgba( 255, 190, 120, 1 ); }

// Dimmed for every state you cannot act on. Owned is excluded — it is a success, not a
// refusal.
.technode.locked,
.technode.full,
.technode.poor
{
    background-color: rgba( 255, 255, 255, 0.015 );

    .nname, .neffect { opacity: 0.45; }
}

.placeholder
{
    flex-direction: column;
    align-items: center;
    gap: 12px;

    .head
    {
        font-size: 38px;
        font-weight: 700;
        letter-spacing: 3px;
        color: rgba( 235, 235, 235, 0.55 );
    }

    .line
    {
        font-size: 22px;
        color: rgba( 235, 235, 235, 0.75 );
        text-align: center;
    }

    // The explanation of WHY it is empty — quieter than the statement that it is.
    .line.dim
    {
        max-width: 860px;
        font-size: 17px;
        color: rgba( 235, 235, 235, 0.45 );
    }
}

// The one tab with a system behind it reads warmer, so "not built yet" and
// "blocked on a missing system" are visibly different states.
.placeholder.ready .head
{
    color: rgba( 255, 190, 70, 0.7 );
}

// ── footer ──────────────────────────────────────────────────────────────────

.foot
{
    width: 94%;
    height: 70px;
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
}

.close
{
    width: 180px;
    height: 44px;
    align-items: center;
    justify-content: center;

    font-size: 19px;
    font-weight: 600;
    letter-spacing: 2px;

    background-color: rgba( 255, 255, 255, 0.06 );
    border: 2px solid rgba( 255, 255, 255, 0.16 );
    color: rgba( 235, 235, 235, 0.85 );

    &:hover
    {
        background-color: rgba( 220, 60, 50, 0.20 );
        border-color: rgba( 220, 60, 50, 0.8 );
        color: rgba( 255, 255, 255, 1 );
    }
}

// Green, matching the salvage counter on the HUD and the outline on the pickup, so
// the currency reads as one thing across all three places it appears.
.salvage
{
    flex-direction: row;
    align-items: baseline;
    gap: 8px;

    .amount
    {
        font-size: 30px;
        font-weight: 700;
        color: rgba( 60, 210, 90, 0.95 );
    }

    .label
    {
        font-size: 15px;
        font-weight: 600;
        letter-spacing: 2px;
        color: rgba( 235, 235, 235, 0.45 );
    }
}

// The tech page's "right-click to take it off" line (ArsenalMenu.TechNote). Quiet, like the
// Wunderfizz's `.anote`: it is a standing instruction, not a warning to act on. It may shrink
// and wrap, so CLOSE and the salvage counter on either side keep their size.
.fnote
{
    flex-shrink: 1;
    padding: 0px 24px;
    text-align: center;
    font-size: 14px;
    font-style: italic;
    color: rgba( 235, 235, 235, 0.45 );
}

// ═══ BASALT (`HudTheme`) — the map's face, Cinzel on every word of the machine ═══
//
// *"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,
.theme-basalt .arsenal
{
    font-family: Cinzel;
    line-height: 0.898;
}

// ═══ BASALT (`HudTheme`) — the Arsenal in the HUD's stone, bone and ember ════════════════════════════════════════════════════
//
// *"Stone-dark panels, bone text, ember accents on selected and affordable items, and one white light strip"* — the user's pick
// (2026-09-27). ⛔ COLOURS ONLY: no border added or removed, no size or spacing touched, so nothing here can move. Every rule
// names the theme and the box, a class deeper than the rule it recolours, and each state and hover is restated, so none
// depends on where it sits in the file. The meanings stay: ember is what you can buy or have picked, jade is owned (and
// salvage, as on the HUD), rust is what you cannot afford.
$stone: rgba( 46, 42, 37, 0.9 );
$stone-hi: rgba( 92, 83, 72, 1 );
$stone-lo: rgba( 17, 14, 11, 1 );
$groove: rgba( 15, 12, 9, 0.95 );
$bone: rgba( 228, 217, 195, 1 );
$bone-dim: rgba( 160, 148, 130, 1 );
$ember: rgba( 222, 108, 46, 1 );
$ember-hot: rgba( 248, 150, 72, 1 );
$lite: rgba( 234, 246, 255, 1 );
$gold: rgba( 226, 204, 158, 1 );
$jade: rgba( 96, 170, 120, 1 );
$rust: rgba( 196, 86, 60, 1 );

.theme-basalt .arsenal
{
    background-color: rgba( 20, 17, 14, 0.96 );
    border-color: $stone-hi;
    border-right-color: $stone-lo;
    border-bottom-color: $lite;          // the light strip along its foot, as the HUD's
    color: $bone;

    .title { color: $ember-hot; }

    // ── the tabs ──
    .tab
    {
        background-color: rgba( 92, 83, 72, 0.18 );
        border-color: rgba( 92, 83, 72, 0.8 );
        color: $bone-dim;
    }
    .tab:hover
    {
        background-color: rgba( 92, 83, 72, 0.35 );
        color: $bone;
    }
    .tab.on
    {
        background-color: rgba( 222, 108, 46, 0.18 );
        border-color: rgba( 222, 108, 46, 0.8 );
        color: $ember-hot;
    }
    .tab.todo
    {
        color: rgba( 160, 148, 130, 0.5 );
        border-color: rgba( 92, 83, 72, 0.35 );
    }
    .tab.todo.on
    {
        color: rgba( 248, 150, 72, 0.8 );
        border-color: rgba( 222, 108, 46, 0.4 );
    }

    // ── the cards: armor, rarity, ammo ──
    .card
    {
        background-color: rgba( 15, 12, 9, 0.5 );
        border-color: rgba( 92, 83, 72, 0.7 );
    }
    .kind { color: $bone-dim; }
    .tier { color: $gold; }
    .hp { color: $bone-dim; }
    .cost .coin { background-color: $jade; }
    .cost .amount { color: $bone; }

    .card.buyable { border-color: rgba( 222, 108, 46, 0.6 ); }
    .card.buyable:hover
    {
        background-color: rgba( 222, 108, 46, 0.14 );
        border-color: rgba( 248, 150, 72, 0.95 );
    }
    .card.owned .status { color: rgba( 110, 190, 140, 1 ); }
    .card.locked .status { color: rgba( 120, 110, 95, 1 ); }
    .card.poor .status { color: $rust; }
    .card.owned, .card.locked, .card.poor { background-color: rgba( 15, 12, 9, 0.35 ); }
    .card.owned { border-color: rgba( 96, 170, 120, 0.5 ); }

    .cards.ammo .blurb { color: rgba( 200, 190, 172, 0.75 ); }
    .cards.ammo .nofx { color: $ember-hot; }

    .card.fitted, .card.equipped
    {
        border-color: rgba( 222, 108, 46, 0.8 );
        background-color: rgba( 222, 108, 46, 0.08 );
    }
    .card.fitted .status, .card.equipped .status { color: $ember-hot; }

    .subject .wep { color: $ember-hot; }

    // ── the ammo page's grid and detail panel (2026-10-04) ──
    .atile { background-color: rgba( 15, 12, 9, 0.5 ); border-color: rgba( 92, 83, 72, 0.7 ); }
    .atile:hover { background-color: rgba( 222, 108, 46, 0.12 ); border-color: rgba( 248, 150, 72, 0.8 ); }
    .atile .aname { color: $gold; }
    .atile.fitted { border-color: rgba( 96, 170, 120, 0.85 ); }
    .atile.on { background-color: rgba( 222, 108, 46, 0.16 ); border-color: rgba( 248, 150, 72, 0.95 ); }
    .ricon { color: $ember-hot; border-color: rgba( 222, 108, 46, 0.6 ); }
    .ammodetail { background-color: rgba( 15, 12, 9, 0.5 ); border-color: rgba( 92, 83, 72, 0.7 ); }
    .ammodetail .dname { color: $gold; }
    .ammodetail .drow .k { color: $bone-dim; }
    .ammodetail .drow .v { color: $bone; }
    .ammodetail .ddesc { color: rgba( 200, 190, 172, 0.8 ); }
    .ammodetail .dsoon { color: $ember-hot; }
    .ammodetail .buy { border-color: rgba( 92, 83, 72, 0.8 ); color: $bone-dim; }
    .ammodetail .buy.buyable { border-color: rgba( 222, 108, 46, 0.8 ); color: $ember-hot; }
    .ammodetail .buy.buyable:hover { background-color: rgba( 222, 108, 46, 0.18 ); border-color: $ember-hot; }
    .ammodetail .buy.fitted { border-color: rgba( 96, 170, 120, 0.7 ); color: $jade; }
    .ammodetail .buy.poor { color: $rust; }

    // ── the ammo page's upgrades (2026-10-05) ──
    .atile .apips .pip { background-color: rgba( 15, 12, 9, 0.8 ); border-color: rgba( 222, 108, 46, 0.55 ); }
    .atile .apips .pip.on { background-color: $ember-hot; }
    .ammodetail .uhead .ulabel { color: $bone-dim; }
    .ammodetail .uhead .unote { color: $bone-dim; }
    .ammodetail .uprow { background-color: rgba( 15, 12, 9, 0.35 ); border-color: rgba( 92, 83, 72, 0.7 ); }
    .ammodetail .uprow .unum { color: $bone-dim; }
    .ammodetail .uprow .un { color: $gold; }
    .ammodetail .uprow .ueff { color: rgba( 200, 190, 172, 0.8 ); }
    .ammodetail .uprow .usoon { color: $ember-hot; }
    .ammodetail .uprow .ustate { color: $bone-dim; }
    .ammodetail .uprow.owned { border-color: rgba( 96, 170, 120, 0.6 ); }
    .ammodetail .uprow.owned .unum, .ammodetail .uprow.owned .ustate { color: $jade; }
    .ammodetail .uprow.buyable { background-color: rgba( 222, 108, 46, 0.08 ); border-color: rgba( 222, 108, 46, 0.8 ); }
    .ammodetail .uprow.buyable .unum, .ammodetail .uprow.buyable .ustate { color: $ember-hot; }
    .ammodetail .uprow.buyable:hover { background-color: rgba( 222, 108, 46, 0.18 ); border-color: $ember-hot; }
    .ammodetail .uprow.poor .ustate { color: $rust; }

    // ── the tech tree ──
    .tierpick .tbtn
    {
        background-color: rgba( 92, 83, 72, 0.18 );
        border-color: rgba( 92, 83, 72, 0.8 );
        color: $bone-dim;
    }
    .tierpick .tbtn:hover
    {
        background-color: rgba( 92, 83, 72, 0.35 );
        color: $bone;
    }
    .tierpick .tbtn.on
    {
        background-color: rgba( 222, 108, 46, 0.2 );
        border-color: rgba( 222, 108, 46, 0.85 );
        color: $ember-hot;
    }

    .tierband
    {
        background-color: rgba( 15, 12, 9, 0.35 );
        border-left-color: rgba( 222, 108, 46, 0.6 );
    }

    .tierhead .tnum { color: $ember-hot; }
    .tierhead .tname { color: $bone; }
    .tierhead .tpick { color: $jade; }
    .tierhead .tcost { color: $bone-dim; }

    .technode
    {
        background-color: rgba( 15, 12, 9, 0.5 );
        border-color: rgba( 92, 83, 72, 0.6 );
    }
    .technode .nname { color: $bone; }
    .technode .neffect { color: rgba( 200, 190, 172, 0.85 ); }
    .technode.buyable { border-color: rgba( 222, 108, 46, 0.6 ); }
    .technode.buyable:hover
    {
        background-color: rgba( 222, 108, 46, 0.14 );
        border-color: rgba( 248, 150, 72, 0.95 );
    }
    .technode.owned { border-color: rgba( 96, 170, 120, 0.5 ); }
    .technode.owned .nstate { color: $jade; }
    .technode.poor .nstate { color: $rust; }
    .technode.full .nstate { color: $gold; }
    .technode.locked .nstate { color: rgba( 120, 110, 95, 1 ); }
    .technode.locked, .technode.full, .technode.poor { background-color: rgba( 15, 12, 9, 0.3 ); }

    // the set colours, restated for basalt (see the default section)
    .technode.set-action { border-left: 6px solid rgba( 90, 160, 255, 0.95 ); }
    .technode.set-action .nname { color: rgba( 150, 200, 255, 1 ); }
    .technode.set-mag { border-left: 6px solid rgba( 175, 110, 255, 0.95 ); }
    .technode.set-mag .nname { color: rgba( 210, 170, 255, 1 ); }
    .technode.set-reload { border-left: 6px solid rgba( 255, 150, 50, 0.95 ); }
    .technode.set-reload .nname { color: rgba( 255, 190, 120, 1 ); }

    // ── the empty pages, the close, the wallet ──
    .placeholder .head { color: $bone-dim; }
    .placeholder .line { color: rgba( 200, 190, 172, 0.85 ); }
    .placeholder .line.dim { color: rgba( 160, 148, 130, 0.6 ); }
    .placeholder.ready .head { color: rgba( 248, 150, 72, 0.8 ); }

    .close
    {
        background-color: rgba( 92, 83, 72, 0.2 );
        border-color: rgba( 92, 83, 72, 0.8 );
        color: $bone;
    }
    .close:hover
    {
        background-color: rgba( 222, 108, 46, 0.22 );
        border-color: rgba( 248, 150, 72, 0.9 );
        color: rgba( 255, 240, 225, 1 );
    }

    .salvage .amount { color: $jade; }
    .salvage .label { color: $bone-dim; }
    .fnote { color: $bone-dim; }
}