/* world.css: Star Map and expedition dispatch, inventory and Item Codex, factions, faction buildings. */
/* ===== STAR MAP (nodes + expeditions) ===== */
.map-layout { display: flex; flex-direction: column; gap: 18px; }
@media (min-width: 768px) {
    .map-layout { display: grid; grid-template-columns: 1.5fr 1fr; align-items: start; }
}
.map-node-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
    gap: 14px;
}
.map-node-card { display: flex; flex-direction: column; gap: 8px; }
.map-node-card__head { display: flex; justify-content: space-between; align-items: flex-start; gap: 10px; }
.map-node-card__title { margin: 0; font-size: 1.02rem; }
.map-node-card__desc { color: var(--text-secondary); font-size: 0.9rem; margin: 0; }
.map-node-card__meta { display: flex; gap: 12px; flex-wrap: wrap; }
.map-node-card__closed { color: var(--text-muted); font-size: 0.85rem; margin: 0; font-style: italic; }
.map-node-card form { max-width: none; gap: 10px; margin: 0; }
.node-pet-picker { display: flex; flex-direction: column; gap: 6px; }
.node-pet-check {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
    gap: 4px 8px;
    font-weight: 400;
    color: var(--text-primary);
    font-size: 0.9rem;
}
.node-pet-species { color: var(--text-muted); font-size: 0.82rem; }
/* Squad-first dispatch: the party going out, plus the customize/sub-in bench. */
.dispatch-party { list-style: none; margin: 6px 0; padding: 0; display: flex; flex-direction: column; gap: 4px; }
.dispatch-party__member { display: flex; align-items: center; gap: 8px; font-size: 0.88rem; padding: 4px 8px; background: var(--bg-cool); border-radius: var(--radius-sm); }
.dispatch-party__member .btn { margin-left: auto; }
.dispatch-customize { margin-bottom: 6px; }
.node-pet-check--busy { opacity: 0.5; }
.node-pet-busy { color: var(--color-warning-text); }
.node-pet-member { color: var(--text-muted); }
.node-duration {
    font-family: var(--font-mono);
    font-size: 0.8rem;
    color: var(--text-secondary);
    margin: 0;
    padding: 6px 10px;
    background: var(--bg-cool);
    border-radius: var(--radius-sm);
}

/* Fracture list view-mode toggle (panel header, "Expanded" / "Compact"): a plain button
   pair, not a checkbox/radio card, since there are only two mutually exclusive states and
   each button already carries its own label. The active state is never colour alone: the
   selected button is filled (.btn-primary) and carries aria-pressed="true", the other is
   outlined (.btn-secondary) with aria-pressed="false". */
/* The base `form { flex-direction: column }` rule (FORMS section) beats this on
   flex-direction alone (a class selector doesn't override a property it never sets), so
   flex-direction has to be named here explicitly or the two buttons stack vertically. */
.view-mode-toggle { display: inline-flex; flex-direction: row; gap: 6px; margin: 0; }

/* Compact Fracture view: same dispatch form, same hidden pet_ids inputs, just far less of
   it on screen. .dispatch-party/.squad-traits-line/.node-duration are hidden with plain
   CSS rather than dropped from the template: display:none on an ancestor does not stop a
   type="hidden" input from submitting, so the party-picker's hidden pet_ids inputs (the
   dispatch-party <li>s, rendered by Alpine's x-for) still ride the POST even though the
   list itself is never shown. Dropping them from the DOM instead, the way _pet_card.html's
   rearrange mode does, would have silently broken "Customize party" in this view: the
   Alpine component's state would still track a custom party, but nothing would be left to
   emit its pet_ids. The "why is Dispatch disabled" hint stays visible either way: it has its
   own copy below the button, outside .dispatch-party. */
.map-node-grid--compact { gap: 10px; }
.map-node-card--compact { gap: 6px; padding: 10px 14px; }
.map-node-card--compact .map-node-card__meta { gap: 8px; font-size: 0.82rem; }
/* Several squads ticked: the per-squad rows are the "who's going" list the player checks
   before pressing Dispatch, so they stay visible in compact too. Only the single-squad
   member rows hide (their hidden pet_ids still post, as above). */
.map-node-card--compact .dispatch-party__member:not(.dispatch-party__squad),
.map-node-card--compact .squad-traits-line,
.map-node-card--compact .node-duration { display: none; }
.map-node-card--compact .dispatch-customize { margin-bottom: 0; }

/* Multi-squad picker: a disclosure of checkbox rows, so tapping several squads is one tap
   each on every device. The ticked state is the checkbox itself; the row tint only
   echoes it. */
.squad-multi { position: relative; }
.squad-multi__trigger {
    display: flex; align-items: center; justify-content: space-between; gap: 8px;
    width: 100%; text-align: left; cursor: pointer;
}
.squad-multi__trigger > span:first-child { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.squad-multi__menu {
    position: absolute; top: calc(100% + 4px); left: 0; right: 0; z-index: 6;
    display: flex; flex-direction: column; gap: 2px;
    max-height: min(60dvh, 320px); overflow-y: auto; padding: 6px;
    background: var(--bg-elevated); border: 1px solid var(--border-color);
    border-radius: var(--radius-md); box-shadow: var(--shadow-md);
}
.squad-multi__option {
    flex-direction: row; align-items: center; gap: 8px;
    padding: 6px 8px; font-size: 0.88rem; font-weight: 400; color: var(--text-primary);
    border-radius: var(--radius-sm); cursor: pointer;
}
.squad-multi__option:has(input:disabled) { color: var(--text-muted); cursor: default; }
.squad-multi__option--on { background: var(--bg-cool); }
@media (hover: hover) { .squad-multi__option:not(:has(input:disabled)):hover { background: var(--bg-cool); } }
.squad-multi__menu .hint { margin: 4px 8px 2px; }
.dispatch-party__squad { flex-wrap: wrap; }
.dispatch-party__squad .node-pet-species { min-width: 0; flex: 1 1 auto; }
@media (pointer: coarse) {
    .squad-multi__option { min-height: 44px; }
}
.trip-length { border: 0; margin: 0; padding: 0; min-width: 0; display: flex; flex-direction: column; gap: 6px; }
.trip-length legend { padding: 0; margin-bottom: 4px; }
.trip-length__options { display: flex; flex-wrap: wrap; gap: 4px 14px; }
.trip-length__option {
    flex-direction: row; align-items: center; gap: 6px;
    font-size: 0.88rem; font-weight: 400; color: var(--text-primary); cursor: pointer;
}
.trip-length .hint { margin: 0; }
.map-trip-hint { margin: 0 0 12px; }
.expedition-card__trip { margin: 0; }
@media (pointer: coarse) {
    .trip-length__option { min-height: 44px; }
}

.expedition-count { margin: 0 0 14px; display: flex; align-items: center; gap: 8px; }
.expedition-count .mono { color: var(--text-primary); font-weight: 700; }
.expedition-grid { display: flex; flex-direction: column; gap: 12px; }
.expedition-card { display: flex; flex-direction: column; gap: 10px; }
.expedition-card__head { display: flex; justify-content: space-between; align-items: flex-start; gap: 10px; }
.expedition-card__title { margin: 0; font-size: 1rem; }
.expedition-card__timer { margin: 0; color: var(--text-secondary); }
.expedition-card__timer .mono { color: var(--text-primary); font-weight: 700; }
.expedition-card__squad { margin: 0; color: var(--text-secondary); font-size: 0.85rem; }
.expedition-card__squad .eyebrow { margin-right: 6px; }
.expedition-card__actions { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
.expedition-card__actions form { max-width: none; margin: 0; }
.expedition-loot {
    list-style: none;
    margin: 0;
    padding: 10px 12px;
    background: var(--bg-subtle);
    border-radius: var(--radius-md);
    display: flex;
    flex-direction: column;
    gap: 4px;
}
.expedition-loot li { display: flex; justify-content: space-between; gap: 10px; font-size: 0.9rem; }
.expedition-loot__name { color: var(--text-secondary); }
.expedition-loot .mono { color: var(--text-primary); font-weight: 700; }
.expedition-xp-summary { margin-top: 8px; }

/* The Rested-XP credit line, shared by the expedition collect card, the collect-all
   reveal and the incursion room result: one rule so a doubled grant can't be worded or
   weighted differently depending on which surface paid it. It is a real second line
   rather than a title= tooltip, which is dead to touch and keyboard alike. */
.rested-credit {
    display: block;
    font-size: 0.74rem;
    color: var(--text-secondary);
}

/* The daily-XP-taper note: shared by the same three surfaces as .rested-credit above,
   plus the incursion run debrief (.debrief__pet), which reads the run's own accrued
   pet_xp_earned/pet_xp_credited ledger rather than a live grant. An XP grant capped
   (partially, or entirely at 0) by pets.services.grant_pet_xp's midnight-reset daily
   taper must say so, or "+0 XP" reads as a bug rather than "this pet is done for
   today." Glyph + word, never colour alone, per the house rule. */
.xp-capped-note {
    display: block;
    font-size: 0.74rem;
    color: var(--color-warning-ink);
}

/* ===== INVENTORY ===== */
.inventory-items {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    gap: 14px;
}
.inventory-item {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: 6px;
    overflow: hidden;
}
/* Top accent bar keyed to rarity (data-rarity set from item.rarity). */
.inventory-item::before {
    content: '';
    position: absolute;
    top: 0; left: 0; right: 0;
    height: 3px;
    background: var(--rarity-accent, transparent);
}
.inventory-item[data-rarity="uncommon"]  { --rarity-accent: var(--color-success); }
.inventory-item[data-rarity="rare"]      { --rarity-accent: var(--color-info); }
.inventory-item[data-rarity="epic"]      { --rarity-accent: var(--color-violet); }
.inventory-item[data-rarity="legendary"] { --rarity-accent: var(--color-reward); }
.inventory-item__icon { font-size: 2.2rem; line-height: 1; margin-top: 4px; }
.inventory-item__image { width: 100px; height: 100px; object-fit: cover; border-radius: var(--radius-md); margin-top: 4px; }
.inventory-item__name { font-family: var(--font-heading); font-weight: 600; font-size: 0.95rem; }
.inventory-item__qty { color: var(--text-secondary); font-weight: 700; }
.inventory-item__effect { color: var(--color-success-text); font-size: 0.74rem; font-weight: 700; }
/* The scoped label a system is shown under elsewhere in the UI, beside its canonical name
   on the page that exists to teach that the Void systems are distinct. */
.help-scope { display: inline-block; color: var(--text-muted); font-size: 0.78rem; font-weight: 600; }

/* Item Codex: a reference card rather than a buy/equip surface. */
.item-codex-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(270px, 1fr));
    gap: 16px;
}
.item-codex-card {
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 16px;
    border: 1px solid var(--border-color);
    border-top: 3px solid var(--rarity-accent, var(--border-color));
    border-radius: var(--radius-md);
    background: var(--bg-surface);
}
.item-codex-card[data-rarity="uncommon"] { --rarity-accent: var(--color-success); }
.item-codex-card[data-rarity="rare"] { --rarity-accent: var(--color-info); }
.item-codex-card[data-rarity="epic"] { --rarity-accent: var(--color-violet); }
.item-codex-card[data-rarity="legendary"] { --rarity-accent: var(--color-reward); }
.item-codex-card__art {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 88px;
    height: 88px;
    border-radius: var(--radius-md);
    background: var(--bg-hover);
    font-size: 2.3rem;
}
.item-codex-card__art img { width: 88px; height: 88px; object-fit: cover; border-radius: var(--radius-md); }
.item-codex-card__heading { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.item-codex-card__heading h2 { margin: 0; font-size: 1.05rem; }
.item-codex-card__category { color: var(--text-muted); font-size: 0.75rem; margin: -4px 0 0; text-transform: uppercase; letter-spacing: 0.06em; }
.item-codex-card__limited { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 0; font-size: 0.82rem; color: var(--text-secondary); }
.item-codex-card__description { color: var(--text-secondary); font-size: 0.88rem; margin: 0; }
.item-codex-card__sources { list-style: none; display: flex; flex-direction: column; gap: 5px; margin: 2px 0 0; padding: 0; font-size: 0.82rem; }
.item-codex-card__sources li { display: flex; flex-direction: column; gap: 1px; }
.item-codex-card__sources strong { color: var(--color-primary); }
.item-codex-card__sources span { color: var(--text-secondary); }
.item-codex-card__recipe { font-size: 0.82rem; margin-top: 2px; }
.item-codex-card__recipe summary { cursor: pointer; color: var(--text-secondary); }
.item-codex-card__recipe ul { margin: 6px 0 0; padding-left: 18px; }
.item-codex-card__used-in { color: var(--text-muted); font-size: 0.78rem; margin: 0; }
.item-codex-card__shop-search { align-self: flex-start; margin-top: auto; }

.rarity-chip {
    font-family: var(--font-mono);
    font-size: 0.62rem;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    font-weight: 700;
    padding: 2px 8px;
    border-radius: var(--radius-pill);
    border: 1px solid var(--border-color);
    color: var(--text-muted);
    background: var(--bg-subtle);
}
.rarity-chip[data-rarity="uncommon"]  { color: var(--color-success); border-color: color-mix(in srgb, var(--color-success) 45%, var(--border-color)); }
.rarity-chip[data-rarity="rare"]      { color: var(--color-info);    border-color: color-mix(in srgb, var(--color-info) 45%, var(--border-color)); }
.rarity-chip[data-rarity="epic"]      { color: var(--color-violet);  border-color: color-mix(in srgb, var(--color-violet) 45%, var(--border-color)); }
.rarity-chip[data-rarity="legendary"] { color: var(--color-reward);  border-color: color-mix(in srgb, var(--color-reward) 50%, var(--border-color)); }
.rarity-chip--made { color: var(--text-primary); border-style: dashed; }

/* ===== FACTIONS ===== */
.faction-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: 16px;
}
/* --faction-accent is set inline from each faction's emblem_hex (data-driven). */
.faction-card {
    display: flex;
    flex-direction: column;
    gap: 12px;
    border-left: 4px solid var(--faction-accent, var(--border-color));
}
.faction-card__head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.faction-card__dot {
    width: 12px;
    height: 12px;
    border-radius: 50%;
    background: var(--faction-accent, var(--color-primary));
    flex-shrink: 0;
}
.faction-card__name { margin: 0; font-size: 1.1rem; flex: 1; min-width: 0; }
.faction-card__desc { color: var(--text-secondary); font-size: 0.9rem; margin: 0; }
/* auto-fit, not `1fr 1fr`: a fixed track list stays two-column at 320px. This card is
   313px wide on a 390px phone, so the two yield figures were held in 151.5px columns and
   their labels wrapped to three lines each. Needs no @media query of its own. */
.faction-card__yields {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
    gap: 10px;
    padding: 12px 0;
    border-top: 1px solid var(--border-subtle);
    border-bottom: 1px solid var(--border-subtle);
}
.faction-yield { display: flex; flex-direction: column; gap: 3px; }
.faction-yield .mono { font-size: 1.15rem; font-weight: 700; color: var(--text-primary); }
.faction-yield-hint { margin: 8px 0 0; font-size: 0.78rem; line-height: 1.4; color: var(--text-secondary); }
.faction-yield-hint .mono { font-weight: 700; color: var(--color-success-text); }
.faction-yield-hint__cap { display: block; margin-top: 2px; color: var(--text-muted); }
.faction-yield-hint__muted { color: var(--text-muted); }
.faction-card__signature {
    padding: 2px 0;
}
.faction-card__signature .faction-yield-hint {
    margin: 4px 0 0;
}
.faction-card__meta {
    display: flex;
    flex-wrap: wrap;
    gap: 6px 16px;
    font-size: 0.82rem;
    color: var(--text-secondary);
}
.faction-stat { display: inline-flex; align-items: center; gap: 5px; }
.faction-stat .mono { color: var(--text-primary); font-weight: 700; }
@media (hover: hover) { .faction-stat--link:hover { text-decoration: underline; } }
.faction-stat--link:focus-visible { text-decoration: underline; }
.faction-card__foot { display: flex; flex-direction: column; gap: 8px; margin-top: auto; }
.faction-card__foot form { max-width: none; margin: 0; }
.faction-card__note { color: var(--text-muted); font-size: 0.82rem; margin: 0; }

/* Shared global construction goal (Incursion Star-Steel) */
.construction-goal {
    background: var(--bg-surface);
    border: 1px solid var(--border-color);
    border-left: 4px solid var(--color-reward, var(--color-primary));
    border-radius: var(--radius-lg);
    padding: 16px 18px;
    margin-bottom: 20px;
    display: flex;
    flex-direction: column;
    gap: 10px;
}
.construction-goal__head {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
}
.construction-goal__name { margin: 2px 0 0; font-size: 1.15rem; }
.construction-goal__count { color: var(--text-primary); font-weight: 700; white-space: nowrap; }
.construction-goal__bar {
    height: 10px;
    border-radius: var(--radius-pill);
    background: var(--bg-subtle);
    overflow: hidden;
}
.construction-goal__fill {
    display: block;
    height: 100%;
    background: var(--color-reward, var(--color-primary));
    border-radius: var(--radius-pill);
    transition: width 0.4s ease;
}
.construction-goal__note { color: var(--text-muted); font-size: 0.82rem; margin: 0; }

/* Supply Drive banner (limited-time Star-Steel multiplier) */
.supply-drive-banner {
    display: flex;
    align-items: center;
    gap: 14px;
    background: color-mix(in srgb, var(--color-reward) 12%, var(--bg-surface));
    border: 1px solid color-mix(in srgb, var(--color-reward) 40%, var(--border-color));
    border-radius: var(--radius-lg);
    padding: 12px 16px;
    margin-bottom: 20px;
}
.supply-drive-banner__icon { font-size: 1.6rem; }
.supply-drive-banner__title { margin: 0; font-weight: 700; }
.supply-drive-banner__note { margin: 2px 0 0; color: var(--text-secondary); font-size: 0.85rem; }

/* Faction Season leaderboard */
.season-panel { margin-bottom: 20px; }
.season-panel__header { display: flex; align-items: flex-end; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.season-panel__timer { margin: 0; color: var(--text-secondary); font-size: 0.85rem; }
.season-champion {
    display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
    margin: 4px 0 14px; color: var(--text-secondary); font-size: 0.9rem;
}
.season-standings { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.season-row {
    display: grid;
    grid-template-columns: minmax(120px, 1fr) minmax(80px, 3fr) auto;
    align-items: center;
    gap: 12px;
}
.season-row__name { font-weight: 600; min-width: 0; }
.season-row--leader .season-row__name { color: var(--color-reward); }
.season-row__bar {
    height: 12px;
    border-radius: var(--radius-pill);
    background: var(--bg-subtle);
    overflow: hidden;
}
.season-row__fill {
    display: block; height: 100%;
    background: var(--faction-accent, var(--color-primary));
    border-radius: var(--radius-pill);
    transition: width 0.4s ease;
}
.season-row__value { color: var(--text-primary); font-weight: 700; white-space: nowrap; }
.season-panel__empty, .season-panel__you { color: var(--text-muted); font-size: 0.85rem; margin: 12px 0 0; }
.season-panel__you { color: var(--text-secondary); }

/* Faction Details: two slim data columns side by side */
.faction-details-columns { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 20px; align-items: start; }
@media (width < 768px) { .faction-details-columns { grid-template-columns: 1fr; } }

/* Top contributors + activity feed */
.faction-social {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: 18px;
    margin-bottom: 20px;
}
.faction-social__col { margin: 0; }
.contributor-list, .activity-feed { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.contributor-row { display: flex; align-items: center; gap: 10px; font-size: 0.9rem; }
.contributor-rank { color: var(--text-muted); width: 1.4em; text-align: right; }
.contributor-name { font-weight: 600; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.contributor-faction { color: var(--text-muted); font-size: 0.8rem; margin-left: auto; }
.contributor-steel { color: var(--text-primary); font-weight: 700; white-space: nowrap; }
.activity-row { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; font-size: 0.85rem; }
.activity-text { color: var(--text-secondary); min-width: 0; }
.activity-time { color: var(--text-muted); font-size: 0.78rem; white-space: nowrap; }
.champion-archive { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.champion-archive__row { display: flex; align-items: center; gap: 10px; font-size: 0.88rem; }
.champion-archive__season { color: var(--text-muted); width: 2.4em; }
.champion-archive__winner { font-weight: 600; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.champion-archive__dates { color: var(--text-muted); font-size: 0.78rem; margin-left: auto; white-space: nowrap; }
.season-history { list-style: none; margin: 0; padding: 0; }
.season-history__row { display: grid; grid-template-columns: 42px 1fr; gap: 8px; align-items: baseline; padding: 7px 0; border-bottom: 1px solid var(--border-subtle); }
.season-history__row:last-child { border-bottom: 0; }
.season-history__result { display: flex; flex-wrap: wrap; gap: 4px 8px; }
.season-history__factions { color: var(--text-muted); font-size: 0.8rem; }

/* ===== FACTION BUILDINGS ===== */
.building-hub-empty { display: flex; flex-direction: column; gap: 12px; align-items: flex-start; }
.building-week-strip {
    display: flex; align-items: flex-start; justify-content: space-between;
    gap: 16px; flex-wrap: wrap; margin-bottom: 20px;
}
.building-week-strip__note { color: var(--text-muted); font-size: 0.85rem; margin: 4px 0 0; max-width: 46em; }
.building-week-strip__meta { text-align: right; }
.building-week-strip__timer { margin: 0; color: var(--text-secondary); font-size: 0.85rem; }
.building-week-strip__balance { margin: 4px 0 0; font-size: 0.9rem; }
.building-active-perks { margin-bottom: 20px; }
.building-banked-perks { margin-bottom: 20px; }
.building-weekly-projects { margin-bottom: 20px; }
.faction-infrastructure { margin-bottom: 20px; }
.faction-infrastructure__intro { color: var(--text-secondary); font-size: 0.88rem; margin: -8px 0 16px; max-width: 60em; }
.building-perk-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.building-perk-list__row { color: var(--color-reward); font-size: 0.9rem; }
.building-perk-list__row--pending { color: var(--color-warning-ink); }
/* The per-card infrastructure perk ladder (every level, reached or not): smaller and
   tighter than the section-level perk lists above, since it sits inside a building-card
   alongside its own description and progress bar rather than being its own panel. */
.building-perk-list--ladder { gap: 3px; margin-top: 2px; }
.building-perk-list--ladder .building-perk-list__row { font-size: 0.82rem; }
.building-active-perks__empty { color: var(--text-muted); font-size: 0.85rem; margin: 0; }

.building-slots {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
    gap: 16px;
}
.building-slots__empty { color: var(--text-muted); grid-column: 1 / -1; }
/* --building-accent is set inline from the faction's emblem_hex, same as .faction-card. */
.building-card {
    display: flex; flex-direction: column; gap: 10px;
    min-width: 0;
    min-height: 100%;
    padding: 16px;
    background: var(--bg-surface);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-lg);
    border-left: 4px solid var(--building-accent, var(--border-color));
}
.building-card__head { display: flex; align-items: center; gap: 8px; }
.building-card__icon { font-size: 1.3rem; }
.building-card__name { margin: 0; }
.building-card__desc { color: var(--text-secondary); font-size: 0.88rem; margin: 0; }
.building-card__effect { color: var(--color-primary); font-size: 0.85rem; margin: 0; }
.building-card__bar {
    height: 10px;
    border-radius: var(--radius-pill);
    background: var(--bg-subtle);
    overflow: hidden;
}
.building-card__fill {
    display: block; height: 100%;
    background: var(--color-reward, var(--color-primary));
    border-radius: var(--radius-pill);
    transition: width 0.4s ease;
}
.building-card__count { color: var(--text-muted); font-size: 0.8rem; margin: 0; }
.building-card__form { display: flex; gap: 8px; margin-top: auto; }
.building-card__form input[type="number"] {
    width: 6.5em; min-height: 44px; padding: 6px 10px;
    border: 1px solid var(--border-color); border-radius: var(--radius-md);
    background: var(--bg-surface); color: var(--text-primary);
}

/* Weekly rankings: same visual language as .season-standings/.season-row, plus a
   member-count/raw-total note column (per-member average is the ranking metric,
   see factions.services.building_week_standings). Kept as its own class family
   rather than widening .season-row's grid, so the season page's own layout can't
   regress from a change made here. */
.building-rank-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.building-rank-row {
    display: grid;
    grid-template-columns: minmax(120px, 1fr) minmax(80px, 3fr) auto;
    grid-template-areas: "name bar value" "name bar note";
    align-items: center;
    column-gap: 12px;
}
.building-rank-row__name { grid-area: name; font-weight: 600; min-width: 0; }
.building-rank-row--leader .building-rank-row__name { color: var(--color-reward); }
.building-rank-row__bar {
    grid-area: bar;
    height: 12px;
    border-radius: var(--radius-pill);
    background: var(--bg-subtle);
    overflow: hidden;
}
.building-rank-row__fill {
    display: block; height: 100%;
    background: var(--color-reward, var(--color-primary));
    border-radius: var(--radius-pill);
    transition: width 0.4s ease;
}
.building-rank-row__value { grid-area: value; font-weight: 600; }
.building-rank-row__note { grid-area: note; font-size: 0.78rem; }

