/**
 * War 2050 - HUD surfaces for previously headless backend features
 *
 * Three region-D panels (queues, transmissions, structure details) plus the
 * region-A progression block and the two badged action-bar buttons that open
 * the panels. See docs/HUD-LAYOUT.md — regions §1, z-ladder §2, breakpoints
 * §3, safe zones §6, a11y §7.
 *
 * The region-D shell deliberately mirrors .build-menu's geometry exactly
 * (same bottom anchor, same width clamp, same laptop-short shrink, same
 * phone bottom-sheet collapse) rather than inventing a second set of
 * numbers: they are the same slot and only one of them is ever on screen at
 * a time, so any divergence would only ever be a bug.
 */

/* =================================================================
   REGION A — PROGRESSION (XP + RANK)
   Sits under the resource row inside .top-left-hud.

   Vertical budget (§1: region A is top 0 to 160px): .top-left-hud is
   anchored at top: var(--space-4) = 16px and stacks
     profile ~42px + 3px + resources ~28px + 3px + this block ~42px
   = ~118px, so its bottom edge lands ~134px from the viewport top,
   inside the 160px bound with ~26px of headroom.

   Horizontal budget (region A is left 0 to 340px): the block is the
   widest child of an inline-flex column, so it sets the whole HUD's
   width — clamped to 240px, comfortably inside 340px.
   ================================================================= */

.player-progress {
    display: flex;
    flex-direction: column;
    gap: 3px;
    width: 240px;
    padding: 5px 8px;
    background: rgba(26, 39, 68, 0.95);
    border: 1px solid rgba(212, 175, 55, 0.2);
    border-radius: var(--radius-sm);
    backdrop-filter: blur(4px);
}

.pp-row {
    display: flex;
    align-items: center;
    gap: 6px;
}

.pp-key {
    flex-shrink: 0;
    min-width: 56px;
    font-family: var(--font-display);
    /* 9px, and 8px below 1024 - "LVL 10" and the rank name were the two
       smallest readouts in region A. 12px floor; the 0.08em tracking is
       halved so the key costs about the same width it did before. */
    font-size: var(--text-xs);
    line-height: 1.2;
    font-weight: var(--font-bold);
    letter-spacing: 0.04em;
    color: var(--color-accent-gold);
}

.pp-key-rank {
    color: var(--color-accent-cyan);
    /* Holds the *next* rank ("-> SERGEANT MAJOR"), which is both longer and
       more variable than a level number, so it gets a wider cap and clips
       rather than pushing the bar out of the 240px block.
       The insignia sits inside this cap, so it is a flex row and only the
       NAME is allowed to ellipsis — an insignia clipped in half is worse
       than no insignia. */
    display: flex;
    align-items: center;
    gap: 3px;
    overflow: hidden;
    white-space: nowrap;
    /* 112px buys back roughly what the 16px insignia and its gap cost, so the
       NAME still gets about the 96px it had before the insignia existed. The
       long ones ("CHIEF WARRANT OFFICER", "GENERAL OF THE ARMY") clip anyway —
       they always did — which is why progression.js also sets a title on this
       element so hover gives the full string. */
    max-width: 112px;
    min-width: 0;
}

.pp-rank-name {
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
}

.pp-rank-arrow {
    flex-shrink: 0;
    opacity: 0.75;
}

/* =================================================================
   RANK INSIGNIA (region A)

   The <symbol> sprite is injected by frontend/assets/ranks/insignia.js,
   which progression.js lazy-loads. Only the sizing lives here: each path
   inside a symbol paints with currentColor and RankInsignia.svg() sets the
   rank's own colour inline, so these rules never name a colour.

   24px is the working size and the one the artwork was checked at. Family
   silhouette (chevron wedge / bar / double bar / leaf / eagle / star
   cluster) survives it cleanly; the exact count within a family does blur
   at the top of the enlisted block, which is why the rank NAME is always
   next to the insignia here and never replaced by it.
   ================================================================= */

.rank-insignia {
    display: block;
    width: 1em;
    height: 1em;
    color: inherit;
    overflow: visible;
    flex-shrink: 0;
}

.rank-insignia-chip {
    width: 16px;
    height: 16px;
}

/* The player's own rank on the profile chip, which progression.js now
   renders as insignia + name. Overrides the plain-text rule in
   game/css/layout.css by load order (hudPanels.css comes after it in
   game/index.html); it lives here rather than there because the markup it
   styles is created by this region's own renderer. */
.user-profile-floating .user-rank {
    display: flex;
    align-items: center;
    gap: 4px;
    min-width: 0;
}

.user-profile-floating .user-rank .rank-insignia {
    width: 16px;
    height: 16px;
}

.user-rank-name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
}

.pp-track {
    position: relative;
    flex: 1;
    height: 6px;
    min-width: 0;
    background: rgba(10, 15, 26, 0.9);
    border: 1px solid rgba(212, 175, 55, 0.15);
    border-radius: 3px;
    overflow: hidden;
}

.pp-fill {
    display: block;
    height: 100%;
    width: 0;
    border-radius: 2px;
    transition: width var(--transition-slow);
}

.pp-fill-xp {
    background: linear-gradient(90deg, var(--color-accent-gold-dark), var(--color-accent-gold-light));
}

.pp-fill-rank {
    background: linear-gradient(90deg, var(--color-accent-cyan), var(--color-accent-cyan-light));
}

.pp-value {
    flex-shrink: 0;
    min-width: 46px;
    text-align: right;
    font-family: var(--font-mono, monospace);
    font-size: var(--text-xs);
    line-height: 1.2;
    color: var(--color-text-muted);
}

/* At max rank the second bar is decorative — it is always full — so it is
   toned down rather than reading as "almost promoted". */
.player-progress.is-max-rank .pp-fill-rank {
    opacity: 0.5;
}

@media (max-width: 1024px) {
    .player-progress {
        width: 210px;
        padding: 4px 6px;
    }

    /* Font sizes no longer step down here — the 12px floor holds at every
       breakpoint (docs/TYPE-LEGIBILITY.md rule 1). Only the widths move. */
    .pp-key { min-width: 52px; }
    /* +12px over the old cap to pay for the insignia, which does not shrink
       below 16px — under that the chevron strokes merge and it is noise.
       Raised again to 112px because the rank name is now 12px rather than
       8px: at 96px the longer rungs clipped to five or six characters. */
    .pp-key-rank { max-width: 112px; }
    .pp-value { min-width: 42px; }
    .rank-insignia-chip { width: 16px; height: 16px; }
}

@media (max-width: 640px) {
    /* Was a fixed 180px inside a region A that is 206-219px wide (layout.css
       widened the phone split to 56%). Following the corner instead of
       under-filling it is ~30px the rank name did not have, and it needs
       them now that it is 12px rather than 8px. */
    .player-progress {
        width: 100%;
        gap: 2px;
    }

    .pp-track { height: 5px; }
    /* The numeric readout is the first thing to go: the bar itself still
       communicates progress, and region A is tightest here. */
    .pp-value { display: none; }

    /* The insignia stays and the NAME gives ground instead. At 180px the
       shape is the faster read of the two, and the profile chip two rows up
       still spells the current rank out.
       78px -> 124px: the numeric readout above is display:none here and the
       block now spans the full width of region A, so the width that frees up
       goes to the rank name, which is 12px rather than 8px. The track still
       keeps ~70px, which is a readable bar. */
    .pp-key-rank { max-width: 124px; }
    /* Both rows' first column is pinned to the same 124px so the two tracks
       start on the same vertical. They were already ragged (50px against
       78px) and widening the rank name to 124px made it obvious. */
    .pp-key { min-width: 124px; }
    .pp-rank-arrow { display: none; }
}

/* =================================================================
   REGION C — BADGED ACTION-BAR BUTTONS
   ================================================================= */

.hud-badge-btn {
    /* .btn has no positioning of its own; the badge is absolutely placed
       against this box. */
    position: relative;
}

.hud-badge {
    position: absolute;
    top: 1px;
    right: 1px;
    min-width: 18px;
    height: 18px;
    padding: 0 4px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-family: var(--font-display);
    /* 9px. This is a count the player is meant to act on, so it is one of the
       last places in the HUD that should be squinted at. 12px floor; the pill
       grows 2px to hold it and moves 1px further into the button's corner,
       so it still sits inside the 44px hit area. */
    font-size: var(--text-xs);
    font-weight: var(--font-bold);
    line-height: 1;
    color: var(--color-primary-900);
    background: var(--color-accent-gold);
    border-radius: 9px;
    pointer-events: none;
}

/* The one genuine contrast failure in the always-on HUD: --color-text-primary
   on --color-accent-red-light measures 3.58:1, well under AA, on the count
   that tells the player they have unread transmissions. The darker red takes
   it to 6.2:1 without turning the badge any less obviously an alert. */
.hud-badge-alert {
    background: var(--color-accent-red-dark);
    color: var(--color-text-primary);
}

/* Two more 44px buttons in the right cluster on an action bar that §3
   already measures as close to its phone-width limit. Tightening that
   cluster's gap reclaims ~20px and keeps region C's "single row at every
   breakpoint" invariant intact at 640px.

   Flagged for Lane C: the bar is already wider than a 375px-class phone
   *before* these buttons existed (four 44px primaries + three icon buttons
   + a labelled Menu + the tutorial skip button), so sub-640 widths need a
   structural answer, not another gap reduction. Out of scope here. */
@media (max-width: 640px) {
    .action-buttons-floating .action-bar-right {
        gap: var(--space-1);
    }
}

/* base.css clears the focus outline on every button globally, which leaves
   these new controls with no keyboard affordance at all. :focus-visible
   restores one for keyboard users without putting a ring on mouse clicks
   (§7 asks for a working focus order across the HUD). */
.hud-badge-btn:focus-visible,
.hp-close:focus-visible,
.hp-action:focus-visible,
.queue-cancel:focus-visible,
.alert-row:focus-visible,
.bp-upgrade-btn:focus-visible {
    outline: 2px solid var(--color-accent-gold);
    outline-offset: 2px;
}

/* =================================================================
   REGION D — SHARED PANEL SHELL
   ================================================================= */

.hud-panel {
    position: fixed;
    /* Derived from the action bar's real footprint (variables.css) rather
       than the old hard-coded 92px, which sat 6px *inside* a bar that
       occupies 98px at desktop - the panel and the bar overlapped by that
       6px at every desktop width, and both edges are interactive. */
    bottom: var(--region-d-bottom);
    left: 50%;
    transform: translateX(-50%);
    width: min(560px, calc(100vw - 32px));
    /* Never taller than the gap between region A and region C, so the
       panel scrolls internally instead of growing off-screen (§6). */
    max-height: min(520px, var(--region-d-max-height));
    display: flex;
    flex-direction: column;
    min-height: 0;
    /* OPAQUE, NOT NEARLY OPAQUE.
       rgba(...,0.96) is too little transmission to read as glass and too much
       to read as solid, so it read as a rendering fault: the Overseer's card
       sits directly behind these panels, and at 4% his portrait, his current
       line and the word EXPENDABLE were all legible as ghost text THROUGH the
       panel's own content. Two layers of type in the same pixels.

       Solid rather than a blurred material, deliberately. The HUD chrome
       already blurs over the world, and stacking a second glass layer on top
       of a first is the thing every platform's material guidance warns against
       — a panel over the HUD is the content layer, not another pane. */
    background: var(--color-primary-900);
    border: 1px solid var(--color-primary-700);
    border-radius: 8px;
    box-shadow: 0 12px 40px rgba(0, 0, 0, 0.6);
    z-index: var(--z-panel);
    overflow: hidden;
    animation: hpSlideUp 0.22s ease;
}

/* The structure panel holds three stat chips and a health bar, not a list —
   at 560px it reads as mostly empty space. */
.hud-panel-narrow {
    width: min(380px, calc(100vw - 32px));
}

.hud-panel.hidden {
    display: none;
}

@keyframes hpSlideUp {
    from { opacity: 0; transform: translateX(-50%) translateY(12px); }
    to   { opacity: 1; transform: translateX(-50%) translateY(0); }
}

/* Laptop-short (§3, §4.5) no longer needs its own rule: --region-d-max-height
   subtracts region A and region C from the live viewport height, so the
   panel is clear of the top-left HUD at 700px, at 600px and at every height
   in between. The old pair of constants (45vh/360px at bottom: 72px) only
   approximated that, and did so with numbers the action bar never agreed to. */

/* Phone (§5): full-width bottom sheet, but docked *above* the action bar,
   not over it.
   §5 as written let the sheet take bottom: 0 and rely on stacking order to
   occlude region C. That is what the owner photographed: the bar is 96%
   opaque underneath the sheet, so it reads as two overlapping panels, and
   at the same time the bar was overflowing the viewport (layout.css) so
   parts of it were unreachable regardless. Docking the sheet above a bar
   that now fits means neither has to disappear for the other, and the
   player can switch panels without closing the one they are in. */
@media (max-width: 640px) {
    .hud-panel,
    .hud-panel-narrow {
        left: 0;
        right: 0;
        bottom: var(--region-d-bottom);
        transform: none;
        width: 100%;
        max-height: var(--region-d-max-height);
        border-radius: 8px 8px 0 0;
        animation: hpSlideUpSheet 0.22s ease;
    }
}

@keyframes hpSlideUpSheet {
    from { opacity: 0; transform: translateY(24px); }
    to   { opacity: 1; transform: translateY(0); }
}

/* ---------- Header ---------- */

.hp-header {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 10px 14px;
    border-bottom: 1px solid var(--color-primary-700);
    background: rgba(26, 39, 68, 0.4);
    flex-shrink: 0;
}

.hp-title {
    flex: 1;
    min-width: 0;
    font-family: var(--font-display);
    /* 11.5px -> 12.8px. Orbitron caps at 2px of tracking read as a row of
       separate letters at panel-header size; 1.4px groups the word again
       (docs/TYPE-LEGIBILITY.md rule 5) and pays for the extra size. */
    font-size: 0.8rem;
    letter-spacing: 1.4px;
    color: var(--color-accent-gold);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.hp-action {
    min-height: 44px;
    padding: 0 10px;
    background: none;
    border: 1px solid var(--color-primary-600);
    border-radius: 4px;
    font-family: var(--font-display);
    /* 8.8px on a control with a 44px hit area — the target was fine, the
       label was not. */
    font-size: var(--text-xs);
    letter-spacing: 0.8px;
    color: var(--color-text-secondary);
    cursor: pointer;
    transition: color var(--transition-fast), border-color var(--transition-fast);
}

.hp-action:hover {
    color: var(--color-accent-gold);
    border-color: var(--color-accent-gold);
}

.hp-close {
    display: flex;
    align-items: center;
    justify-content: center;
    /* 44x44 touch target (WCAG 2.5.5, §7) around a small glyph */
    min-width: 44px;
    min-height: 44px;
    background: none;
    border: none;
    color: var(--color-text-secondary);
    font-size: 1.4rem;
    line-height: 1;
    cursor: pointer;
    padding: 0 4px;
    transition: color var(--transition-fast);
}

.hp-close:hover { color: var(--color-text-primary); }

/* ---------- Body ---------- */

.hp-body {
    overflow-y: auto;
    padding: 6px;
    /* The shell is a flex column with a derived max-height; the body has to
       be allowed to shrink below its content size or it pushes the panel
       past that cap instead of scrolling. min-height:0 is what makes
       "scroll internally rather than grow" actually happen. */
    flex: 1 1 auto;
    min-height: 0;
    /* Stop a flick at the end of the list from scrolling the page (and, on
       iOS, from rubber-banding the whole game) behind the panel. */
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
}

.hp-empty {
    padding: 28px 16px;
    text-align: center;
    font-family: var(--font-heading);
    color: var(--color-text-muted);
    font-size: 0.9rem;
    line-height: 1.6;
}

.hp-row {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    min-height: 44px;
    padding: 8px 10px;
    background: transparent;
    border: 1px solid transparent;
    border-radius: 6px;
    text-align: left;
    color: var(--color-text-primary);
    font-family: var(--font-body);
}

.hp-info {
    display: flex;
    flex-direction: column;
    gap: 2px;
    flex: 1;
    min-width: 0;
}

.hp-name {
    font-family: var(--font-heading);
    font-weight: var(--font-semibold);
    font-size: 0.92rem;
    letter-spacing: 0.5px;
}

.hp-desc {
    font-size: 0.8rem;
    color: var(--color-text-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* =================================================================
   QUEUE PANEL
   ================================================================= */

.queue-row + .queue-row {
    border-top: 1px solid rgba(26, 39, 68, 0.8);
    border-radius: 0;
}

.queue-kind {
    flex-shrink: 0;
    /* 34px held a 3-character kind tag (BLD/UPG/TRN) at 8px — the smallest
       type in any region-D panel. 12px floor, tracking halved, and the chip
       widens by 8px to hold it. */
    width: 42px;
    text-align: center;
    padding: 3px 0;
    font-family: var(--font-display);
    font-size: var(--text-xs);
    line-height: 1.2;
    letter-spacing: 0.5px;
    border-radius: 3px;
    border: 1px solid currentColor;
}

.queue-kind-building { color: var(--color-accent-gold); }
.queue-kind-upgrade  { color: var(--color-accent-cyan); }
.queue-kind-training { color: var(--color-accent-red-light); }

.queue-track {
    display: block;
    position: relative;
    height: 5px;
    margin-top: 4px;
    background: rgba(26, 39, 68, 0.9);
    border-radius: 3px;
    overflow: hidden;
}

.queue-fill {
    display: block;
    height: 100%;
    width: 0;
    background: linear-gradient(90deg, var(--color-accent-gold-dark), var(--color-accent-gold-light));
    /* Matches the 1s client-side tick, so the bar crawls continuously
       instead of stepping once a second. */
    transition: width 1s linear;
}

.queue-remaining {
    flex-shrink: 0;
    min-width: 66px;
    text-align: right;
    font-family: var(--font-mono, monospace);
    font-size: 0.8rem;
    color: var(--color-accent-cyan);
}

.queue-cancel {
    flex-shrink: 0;
    min-width: 72px;
    min-height: 44px;
    padding: 0 10px;
    background: none;
    border: 1px solid var(--color-primary-600);
    border-radius: 4px;
    font-family: var(--font-heading);
    font-size: 0.8rem;
    font-weight: var(--font-semibold);
    letter-spacing: 0.5px;
    color: var(--color-text-secondary);
    cursor: pointer;
    transition: color var(--transition-fast), border-color var(--transition-fast),
                background var(--transition-fast);
}

.queue-cancel:hover {
    color: var(--color-accent-red-light);
    border-color: var(--color-accent-red-light);
}

/* Armed state: the second press is the one that spends the 50% penalty, so
   it has to look different from the first. */
.queue-cancel.is-confirming {
    color: var(--color-text-primary);
    background: var(--color-accent-red);
    border-color: var(--color-accent-red-light);
}

/* =================================================================
   NOTIFICATION PANEL
   ================================================================= */

.alert-row {
    cursor: pointer;
    transition: background var(--transition-fast), border-color var(--transition-fast);
}

.alert-row:hover:not([disabled]) {
    background: rgba(26, 39, 68, 0.6);
    border-color: var(--color-primary-600);
}

/* Read entries stay legible but stop competing for attention; they are
   disabled because clicking them (to mark read) is a no-op.
   This used to be opacity: 0.55, which took the message text to 2.75:1
   against the panel - well under half of AA, on the surface the owner named
   ("notes"). Opacity is also invisible to the contrast audit, which reads
   the declared colour and never the composited one, so this failed silently.
   The de-emphasis is now a step down the text ramp instead: both tiers still
   clear AA (5.77:1 and 4.69:1) and the read/unread distinction is carried by
   the row's own tinted background and border, which is where it was always
   doing most of the work. */
.alert-row[disabled] {
    cursor: default;
}

.alert-row[disabled] .hp-name {
    color: var(--color-text-secondary);
    font-weight: var(--font-normal);
}

.alert-row[disabled] .alert-message {
    color: var(--color-text-muted);
}

.alert-row[disabled] .alert-icon {
    opacity: 0.6;
}

.alert-row.is-unread {
    background: rgba(212, 175, 55, 0.06);
    border-color: rgba(212, 175, 55, 0.25);
}

.alert-row.is-unread.prio-high,
.alert-row.is-unread.prio-urgent {
    background: rgba(220, 38, 38, 0.08);
    border-color: rgba(239, 68, 68, 0.35);
}

.alert-icon {
    width: 22px;
    height: 22px;
    flex-shrink: 0;
    color: var(--color-accent-gold);
}

.alert-row.prio-high .alert-icon,
.alert-row.prio-urgent .alert-icon {
    color: var(--color-accent-red-light);
}

.alert-message {
    font-size: 0.78rem;
    color: var(--color-text-secondary);
    /* Messages are a sentence at most; two lines is plenty and keeps rows
       scannable. */
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.alert-time {
    flex-shrink: 0;
    min-width: 42px;
    text-align: right;
    font-family: var(--font-mono, monospace);
    font-size: var(--text-xs);
    line-height: 1.3;
    color: var(--color-text-muted);
}

/* =================================================================
   BUILDING INFO PANEL
   ================================================================= */

.bp-stats {
    display: flex;
    gap: 6px;
    padding: 4px 4px 10px;
}

.bp-stat {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: 3px;
    padding: 8px 6px;
    background: rgba(26, 39, 68, 0.5);
    border: 1px solid var(--color-primary-700);
    border-radius: 5px;
    text-align: center;
    min-width: 0;
}

.bp-stat-label {
    font-family: var(--font-display);
    /* 8.3px, uppercase, 1.2px tracked — rule 5's worst case, on the labels
       that say what the numbers under them mean. */
    font-size: var(--text-xs);
    line-height: 1.2;
    letter-spacing: 0.4px;
    color: var(--color-text-muted);
}

.bp-stat-value {
    font-family: var(--font-heading);
    font-size: 0.88rem;
    font-weight: var(--font-semibold);
    color: var(--color-text-primary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.bp-status-active      { color: var(--color-accent-green-light); }
.bp-status-constructing,
.bp-status-upgrading   { color: var(--color-accent-gold); }
.bp-status-damaged     { color: var(--color-accent-orange); }
.bp-status-destroyed   { color: var(--color-accent-red-light); }

.bp-health {
    padding: 0 4px 10px;
}

.bp-health-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    margin-bottom: 4px;
}

.bp-health-value {
    font-family: var(--font-mono, monospace);
    font-size: 0.8rem;
    color: var(--color-text-secondary);
}

.bp-health-track {
    height: 8px;
    background: rgba(26, 39, 68, 0.9);
    border: 1px solid var(--color-primary-700);
    border-radius: 4px;
    overflow: hidden;
}

.bp-health-fill {
    height: 100%;
    background: var(--color-accent-green-light);
    transition: width var(--transition-base);
}

.bp-health-track.is-damaged .bp-health-fill { background: var(--color-accent-orange); }
.bp-health-track.is-critical .bp-health-fill { background: var(--color-accent-red-light); }

.bp-countdown {
    margin: 0 4px 10px;
    padding: 6px 8px;
    background: rgba(212, 175, 55, 0.08);
    border: 1px solid rgba(212, 175, 55, 0.25);
    border-radius: 5px;
    font-family: var(--font-mono, monospace);
    font-size: 0.76rem;
    color: var(--color-accent-gold);
    text-align: center;
}

.bp-upgrade {
    padding: 10px 4px 4px;
    border-top: 1px solid var(--color-primary-700);
}

.bp-upgrade-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    margin-bottom: 6px;
}

.bp-next-level {
    font-family: var(--font-heading);
    font-size: 0.8rem;
    font-weight: var(--font-semibold);
    color: var(--color-accent-cyan);
}

.bp-maxed {
    font-family: var(--font-display);
    font-size: var(--text-xs);
    line-height: 1.2;
    letter-spacing: 0.6px;
    color: var(--color-accent-green-light);
}

.bp-upgrade-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    font-family: var(--font-mono, monospace);
    font-size: 0.8rem;
    margin-bottom: 6px;
}

.bp-cost { color: var(--color-text-secondary); }
.bp-cost.bp-short { color: var(--color-accent-red-light); }
.bp-cost.bp-free { color: var(--color-accent-green-light); }
.bp-time { color: var(--color-accent-cyan); margin-left: auto; }

/* Shown when the types endpoint doesn't carry levels[] — the honest "we do
   not know yet" state, styled as a note rather than as a value. */
.bp-cost-unknown {
    color: var(--color-text-muted);
    font-style: italic;
}

.bp-note {
    margin: 0 0 8px;
    font-family: var(--font-body);
    font-size: 0.76rem;
    color: var(--color-text-muted);
    line-height: 1.4;
}

.bp-upgrade-btn {
    width: 100%;
}

.bp-upgrade-btn[disabled] {
    opacity: 0.45;
    cursor: not-allowed;
}

/* =================================================================
   REDUCED MOTION (§7)
   ================================================================= */

@media (prefers-reduced-motion: reduce) {
    .hud-panel {
        animation: none;
    }

    .pp-fill,
    .queue-fill,
    .bp-health-fill,
    .hp-action,
    .hp-close,
    .queue-cancel,
    .alert-row {
        transition: none;
    }
}

/* Relocation control. Sits under the upgrade block and is deliberately
   quieter than it — moving is a free convenience, upgrading costs resources
   and is the action worth drawing the eye. */
.bp-move {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 4px 4px;
    border-top: 1px solid var(--color-primary-700);
}

.bp-move-btn {
    flex-shrink: 0;
    padding: 7px 12px;
    /* This is the control the owner's phone screenshot showed sitting on
       top of the action bar. It is now inside a panel that clears the bar,
       and it gets a real 44px hit area while it is there (§7). */
    min-height: var(--touch-target);
    font-size: 0.78rem;
}

.bp-move-hint {
    font-size: 0.8rem;
    line-height: 1.35;
    color: var(--color-text-muted);
}

/* Button beside hint text needs ~300px to read as a row. Below that the
   hint squeezes to two or three words per line and the pair looks broken;
   stacked, it reads as a labelled action. */
@media (max-width: 420px) {
    .bp-move {
        flex-direction: column;
        align-items: stretch;
        gap: 6px;
    }

    .bp-move-btn {
        width: 100%;
    }
}

/* Research in the orders list. Its own hue so the three kinds are told apart
   by colour as well as by the three-letter tag — the tag alone is 13px and the
   list is scanned, not read. */
.queue-kind-research {
    color: var(--color-accent-cyan);
    border-color: rgba(6, 182, 212, 0.45);
    background: rgba(6, 182, 212, 0.12);
}

/* The way out of the queue the player is looking at. A footer under the order
   rows rather than a control on each one: the tent is a second production line
   for the whole base, not an action against one order, and repeating the same
   button down a list of five reads as nagging. */
.queue-tent {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 10px;
    padding: 12px;
    margin-top: 8px;
    border-top: 1px solid var(--color-primary-700, #1a2744);
    background: rgba(236, 231, 218, 0.05);
}

.queue-tent-copy {
    font-size: var(--text-sm);
    line-height: 1.4;
    color: var(--color-text-secondary, #94a3b8);
    min-width: 0;
}

/* Two offers can be live at once — a Barracks Tent and a Workshop — when the
   player is queued on both lines. They share the row evenly and wrap when they
   cannot: an abbreviated price is worse than a second row.
   
   The copy used to sit beside them in the same flex row, which on a phone left
   it a column about ten characters wide — one or two words per line down a
   dozen lines. Text above, buttons below. */
.queue-tent-offers {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

.queue-tent-offers .bm-rent {
    /* Share the row, but keep a floor: the labels do not wrap (a broken price
       is worse than a second row), so on a narrow phone two of them have to be
       allowed to drop onto separate lines rather than overflow the panel. */
    flex: 1 1 auto;
    min-width: 150px;
    justify-content: center;
}

/* --- The menu sheet -----------------------------------------------------
   `#btn-menu` had no handler at all: 56x44 in the best right-thumb position
   on the screen, doing nothing. It is the only place a player would look for
   their profile, the handbook, the store, a motion preference or a way out,
   and none of those had any route from the base view. */
.game-menu .hp-body {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.gm-item {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 2px;
    width: 100%;
    min-height: var(--touch-target);
    padding: var(--space-2) var(--space-3);
    background: var(--color-primary-800);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    color: var(--color-text-primary);
    text-align: left;
    text-decoration: none;
    cursor: pointer;
}

.gm-item:hover { border-color: var(--color-accent-gold); }

.gm-item-name {
    font-family: var(--font-display);
    font-size: var(--text-sm);
    font-weight: var(--font-semibold);
    letter-spacing: 0.04em;
}

.gm-item-note {
    font-size: var(--text-xs);
    color: var(--color-text-muted);
    line-height: 1.4;
}

/* The toggle keeps the row as the target: the whole line is a label, so the
   checkbox itself never has to be hit. */
.gm-toggle {
    position: relative;
    padding-right: 48px;
}

.gm-toggle input {
    position: absolute;
    top: 50%;
    right: var(--space-3);
    transform: translateY(-50%);
    width: 20px;
    height: 20px;
    accent-color: var(--color-accent-gold);
}

.gm-legal {
    display: flex;
    gap: var(--space-3);
    padding: var(--space-1) var(--space-3);
    font-size: var(--text-xs);
}

.gm-legal a { color: var(--color-text-muted); }
.gm-legal a:hover { color: var(--color-accent-gold); }

.gm-signout {
    margin-top: var(--space-2);
    border-color: var(--color-accent-red-dark);
}

.gm-signout:hover { border-color: var(--color-accent-red); }

/* --- The player's own motion preference ---------------------------------
   Matched alongside every `prefers-reduced-motion` block rather than being a
   second code path, so one rule serves both the operating system's answer and
   the player's. The game could not previously be asked for less motion from
   inside the game at all. */
html.reduce-motion .life-bar-container.state-red,
html.reduce-motion .life-bar-container.state-orange,
html.reduce-motion .bm-skeleton {
    animation: none;
}

html.reduce-motion .hud-panel {
    animation: none;
}

html.reduce-motion * {
    transition-duration: 0.01ms !important;
}

/* --- A BROADCAST: a dispatch, not a line item ---------------------------

   Release announcements go to every player at once and are the one thing in
   this panel that is not something that happened to the player personally. The
   two-line clamp above is right for "Automated Refining is operational" and
   wrong for a notice whose second half says a player's structures were moved —
   that half was simply invisible.

   So a broadcast is set apart rather than squeezed in: a full message, a rule
   down the left in bond paper, and room to breathe. The paper colour is the
   one War Bonds already use for a document, which is what this is. */

.alert-row.is-broadcast {
    align-items: flex-start;
    padding-block: 0.85rem;
    border-left: 2px solid var(--color-bond-paper, #ece5d3);
    background: linear-gradient(
        90deg,
        rgba(236, 229, 211, 0.07) 0%,
        rgba(236, 229, 211, 0) 42%
    );
}

.alert-row.is-broadcast .alert-message {
    /* The whole thing. This is the rule the class exists for. */
    display: block;
    -webkit-line-clamp: none;
    overflow: visible;
    line-height: 1.55;
    margin-top: 0.15rem;
}

.alert-row.is-broadcast .hp-name {
    font-family: var(--font-display, inherit);
    letter-spacing: 0.02em;
    color: var(--color-bond-paper, #ece5d3);
}

.alert-row.is-broadcast .alert-icon,
.alert-row.is-broadcast .alert-time {
    /* Both sit beside a block of text now rather than a single line, so they
       align to the top of it instead of floating at its vertical centre. */
    margin-top: 0.1rem;
    align-self: flex-start;
}
