:root { --hex-w: 63px; --hex-h: 56px; --panel-bg: linear-gradient(160deg, rgba(40, 52, 70, 0.96), rgba(18, 24, 34, 0.96)); --panel-border: rgba(150, 190, 240, 0.35); --text: #e8eef7; --muted: #9fb0c8; --accent: #6cb6ff; } * { box-sizing: border-box; } html, body { margin: 0; height: 100%; overflow: hidden; background: #05070c; color: var(--text); font-family: "Segoe UI", "DejaVu Sans", Roboto, sans-serif; font-size: 14px; user-select: none; } #screens { position: absolute; inset: 0; } .screen { position: absolute; inset: 0; display: none; } .screen.active { display: block; } /* ---------------------------------------------------------------- menus -- */ #screen-menu, #screen-new, #screen-join, #screen-choose { display: none; align-items: center; justify-content: center; background: radial-gradient(circle at 50% 20%, rgba(60, 90, 140, 0.35), transparent 60%), linear-gradient(180deg, #0b1220, #05070c); } #screen-menu.active, #screen-new.active, #screen-join.active, #screen-choose.active { display: flex; } .menu-card { width: 380px; padding: 28px 32px; border-radius: 14px; background: var(--panel-bg); border: 1px solid var(--panel-border); box-shadow: 0 18px 50px rgba(0, 0, 0, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.08); text-align: center; } .menu-card.wide { width: 520px; text-align: left; } .menu-card h1 { margin: 0 0 6px; font-size: 30px; letter-spacing: 1px; } .menu-card.wide h1 { text-align: center; } .tagline { color: var(--muted); margin: 0 0 24px; } .field { display: block; margin: 14px 0; } .field > span:first-child { display: block; color: var(--muted); margin-bottom: 5px; font-size: 12px; text-transform: uppercase; letter-spacing: 0.06em; } .field .inline { display: flex; gap: 8px; } .field .inline input { flex: 1; } input, select { width: 100%; padding: 9px 11px; border-radius: 7px; border: 1px solid rgba(160, 190, 230, 0.3); background: rgba(8, 12, 20, 0.85); color: var(--text); font-size: 14px; outline: none; } input:focus, select:focus { border-color: var(--accent); } .row { display: flex; gap: 10px; justify-content: center; margin-top: 18px; } .menu-card.wide .row { justify-content: flex-end; } .aero { cursor: pointer; padding: 9px 18px; border-radius: 8px; border: 1px solid rgba(150, 190, 240, 0.45); background: linear-gradient(180deg, rgba(90, 130, 190, 0.55), rgba(35, 55, 90, 0.85)); color: var(--text); font-size: 14px; transition: filter 0.12s ease, transform 0.05s ease; } .aero:hover { filter: brightness(1.25); } .aero:active { transform: translateY(1px); } .aero:disabled { opacity: 0.4; cursor: default; filter: none; } .aero.primary { background: linear-gradient(180deg, rgba(110, 175, 255, 0.7), rgba(40, 95, 175, 0.9)); } .aero.big { display: block; width: 100%; margin: 10px 0; padding: 14px; font-size: 17px; } .status { min-height: 20px; margin: 14px 0 0; color: #ffcf7a; text-align: center; } .game-list { list-style: none; margin: 6px 0 14px; padding: 0; max-height: 190px; overflow-y: auto; border-radius: 8px; background: rgba(0, 0, 0, 0.3); border: 1px solid rgba(150, 190, 240, 0.15); } .game-list li { padding: 9px 12px; cursor: pointer; border-bottom: 1px solid rgba(255,255,255,0.05); } .game-list li:hover { background: rgba(110, 175, 255, 0.15); } .game-list li.selected { background: rgba(110, 175, 255, 0.3); } /* Flag + name pickers used on the new-game and join screens. */ .civ-list { list-style: none; margin: 6px 0 14px; padding: 0; max-height: 220px; overflow-y: auto; border-radius: 8px; background: rgba(0, 0, 0, 0.3); border: 1px solid rgba(150, 190, 240, 0.15); } .civ-list li { display: flex; align-items: center; gap: 10px; padding: 7px 12px; cursor: pointer; border-bottom: 1px solid rgba(255,255,255,0.05); } .civ-list li:hover { background: rgba(110, 175, 255, 0.15); } .civ-list li.selected { background: rgba(110, 175, 255, 0.3); } .civ-list img { width: 32px; height: 32px; object-fit: cover; border-radius: 3px; border: 1px solid rgba(255, 255, 255, 0.15); flex: 0 0 auto; } /* ------------------------------------------------------------- world map -- */ #screen-game.active { display: block; } #map-viewport { position: absolute; inset: 0; overflow: hidden; background-image: url("../assets/map_background.png"); background-size: 512px 512px; background-color: #05070c; cursor: grab; } #map-viewport.dragging { cursor: grabbing; } #map-world { position: absolute; left: 0; top: 0; transform-origin: 0 0; } /* Promote the map to its own layer only while panning, so a drag is a GPU translation instead of a repaint of every hex. Keeping it off when idle avoids holding a huge composited layer in memory. */ #map-viewport.dragging #map-world { will-change: transform; } .layer { position: absolute; left: 0; top: 0; } /* A materialised block of the detailed map. Positioned at its world origin and moved with `transform`; its tiles are positioned relative to it. */ .chunk { position: absolute; left: 0; top: 0; } /* Zoomed-out level of detail: the detailed hex/fog/border layers are hidden and a single simplified canvas is shown instead. */ #layer-overview { display: none; } #map-world.lod #layer-overview { display: block; } #map-world.lod .detail-layer { display: none; } .overview-block { position: absolute; } .hex { position: absolute; width: var(--hex-w); height: var(--hex-h); /* Terrain and fog tiles are drawn from pre-masked art (transparent corners), so no per-tile clip-path is needed. Clipping thousands of hexes forced a repaint of the whole map on every pan frame. */ background-repeat: no-repeat; } .hex.terrain { background-image: url("../assets/terrain.png"); background-size: 441px 56px; } .hex.fog { pointer-events: none; background-size: var(--hex-w) var(--hex-h); } .fog-unexplored { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='63' height='56'%3E%3Cpolygon points='63,28 47.25,0 15.75,0 0,28 15.75,56 47.25,56' fill='%2304040a'/%3E%3C/svg%3E"); } .fog-explored { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='63' height='56'%3E%3Cpolygon points='63,28 47.25,0 15.75,0 0,28 15.75,56 47.25,56' fill='%23040612' fill-opacity='0.62'/%3E%3C/svg%3E"); } .border-seg { position: absolute; height: 6px; border-radius: 3px; transform-origin: 0 50%; pointer-events: none; } /* Rounds off the junction where two border edges meet, so the outline has no notches at the corners. */ .border-corner { position: absolute; border-radius: 50%; pointer-events: none; } /* A single pre-generated road link, running from tile centre to tile centre. */ .road-seg { position: absolute; background: #8a5a2b; border-radius: 2px; box-shadow: 0 0 1px rgba(0, 0, 0, 0.6); transform-origin: 0 50%; pointer-events: none; } /* A ranged unit's bombardment: a red dashed line from the battery to the target and a red ring around the target. Drawn on the layer below the entity icons so the units themselves stay on top. */ .target-line { position: absolute; height: 0; border-top: 3px dashed #ff2b2b; transform-origin: 0 50%; pointer-events: none; filter: drop-shadow(0 0 2px rgba(0, 0, 0, 0.7)); } .target-ring { position: absolute; width: 46px; height: 46px; margin-left: -23px; margin-top: -23px; box-sizing: border-box; border: 3px solid #ff2b2b; border-radius: 50%; pointer-events: none; box-shadow: 0 0 6px rgba(255, 43, 43, 0.85); } .tile-selection { position: absolute; width: var(--hex-w); height: var(--hex-h); clip-path: polygon(100% 50%, 75% 0, 25% 0, 0 50%, 25% 100%, 75% 100%); border: none; box-shadow: inset 0 0 0 3px rgba(255, 255, 255, 0.85); pointer-events: none; } .entity { position: absolute; } .unit { --unit-size: 38px; width: 38px; height: 38px; margin-left: -19px; margin-top: -19px; cursor: pointer; } .unit .unit-statuses, .city .city-statuses { position: absolute; display: flex; gap: 1px; pointer-events: none; z-index: 2; } .unit .unit-statuses { top: -4px; right: -4px; } .city .city-statuses { top: -6px; left: 50%; transform: translateX(-50%); } .status-icon { width: calc(var(--unit-size, 38px) / 5); height: calc(var(--unit-size, 38px) / 5); display: block; filter: drop-shadow(0 0 1px rgba(0, 0, 0, 0.9)); } .unit .status-icon { width: calc(var(--unit-size, 38px) / 3); height: calc(var(--unit-size, 38px) / 3); } .city .status-icon { width: calc(var(--city-size, 26px) / 3); height: calc(var(--city-size, 26px) / 3); } .unit .body { width: 100%; height: 100%; border-radius: 50%; border: 2px solid rgba(0, 0, 0, 0.65); box-shadow: 0 2px 5px rgba(0, 0, 0, 0.6); overflow: hidden; display: flex; align-items: center; justify-content: center; } .unit .body img { width: 78%; height: 78%; image-rendering: auto; } /* Owner's flag centred on the unit's top-left corner, so only a sliver of it overlaps the unit and the unit icon stays readable. */ .unit .unit-flag { position: absolute; top: 0; left: 0; width: 50%; height: 50%; transform: translate(-50%, -50%); object-fit: cover; border: 1px solid rgba(0, 0, 0, 0.65); border-radius: 3px; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.5); pointer-events: none; z-index: 3; display: block; } .unit.selected .body { box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.9), 0 2px 6px rgba(0,0,0,0.6); } .unit .health { position: absolute; left: 3px; right: 3px; bottom: -7px; height: 5px; background: rgba(30, 0, 0, 0.85); border: 1px solid rgba(0, 0, 0, 0.7); border-radius: 3px; overflow: hidden; } .unit .health .fill { height: 100%; background: #46d36a; } /* Stack size, sitting on the lower-right corner of the unit icon. */ .unit .unit-count { position: absolute; right: 0; bottom: 0; min-width: 14px; height: 14px; padding: 0 3px; box-sizing: border-box; border-radius: 7px; background: #000000; color: #ffffff; font-size: 10px; font-weight: 700; line-height: 14px; text-align: center; pointer-events: none; z-index: 4; } /* On a battlefield both stacks shrink to half size and face each other across the battle icon. */ .unit.in-battle { transform: scale(0.5); } .battle-marker { position: absolute; width: 22px; height: 22px; margin-left: -11px; margin-top: -11px; z-index: 4; pointer-events: none; filter: drop-shadow(0 0 2px rgba(0, 0, 0, 0.9)); } .battle-marker img { width: 100%; height: 100%; } .city { --city-size: 26px; transform: translate(-50%, -50%); text-align: center; } .city .marker { width: var(--city-size); height: var(--city-size); margin: 0 auto; border-radius: 5px; border: 2px solid rgba(255, 255, 255, 0.8); background-color: #333; background-size: cover; cursor: pointer; box-shadow: 0 2px 6px rgba(0, 0, 0, 0.7); } /* City names live in their own layer above every unit, marker and status icon, so they stay legible instead of being covered by whatever is nearby. */ .city-label-layer { pointer-events: none; } .city-label { position: absolute; transform: translate(-50%, -100%); margin-top: -21px; font-size: 11px; color: #fff; text-align: center; text-shadow: 0 1px 2px #000, 0 0 4px #000; white-space: nowrap; pointer-events: none; } .city-label.capital::before { content: "\2605"; color: #ffd54a; margin-right: 3px; } .path-seg { position: absolute; height: 4px; border-radius: 2px; transform-origin: 0 50%; pointer-events: none; opacity: 0.85; } /* ------------------------------------------------------------------ HUD -- */ #hud { position: absolute; inset: 0; pointer-events: none; } #hud .panel { pointer-events: auto; } .hidden { display: none !important; } .panel { position: absolute; background: var(--panel-bg); border: 1px solid var(--panel-border); border-radius: 12px; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.55), inset 0 1px 0 rgba(255, 255, 255, 0.08); } #top-panel { left: 12px; top: 12px; min-width: 420px; padding: 12px 16px; display: flex; align-items: center; gap: 14px; /* Lifted above the modal overlay so the nation summary — and the approval figure it now carries — stays bright and readable while a panel is open. */ z-index: 60; } .flag-button { width: 64px; height: 64px; padding: 0; border-radius: 10px; border: 1px solid var(--panel-border); background: rgba(0, 0, 0, 0.3); cursor: pointer; overflow: hidden; flex: 0 0 auto; } .flag-button img { width: 100%; height: 100%; object-fit: cover; } .flag-button:hover { filter: brightness(1.2); } .top-text { flex: 0 0 auto; min-width: 150px; } .civ-name { font-size: 19px; font-weight: 600; } .player-name { color: var(--muted); } .admin-label { color: #ffcf7a; font-size: 12px; } .top-stats { display: flex; gap: 16px; margin-left: auto; } .stat { display: flex; flex-direction: column; align-items: flex-end; } .stat-label { font-size: 10px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--muted); } .stat-value { font-size: 15px; font-variant-numeric: tabular-nums; } /* Approval reads white in the neutral 40–60% band, red when the people disapprove and green when they approve. */ .stat-value.approval-neutral { color: var(--text); } .stat-value.approval-positive { color: #6ee787; } .stat-value.approval-negative { color: #f27272; } #fps-label { position: absolute; right: 14px; top: 12px; font-size: 13px; color: #8fdc8f; text-shadow: 0 1px 2px #000; } #bottom-panel { left: 50%; bottom: 14px; transform: translateX(-50%); min-width: 420px; max-width: 720px; padding: 12px 18px; } .info-section.hidden { display: none !important; } .info-title { font-size: 17px; font-weight: 600; margin-bottom: 4px; } .tile-coords { font-size: 11px; color: var(--muted); font-variant-numeric: tabular-nums; margin-bottom: 2px; } .tile-gdp-head { font-size: 13px; font-variant-numeric: tabular-nums; } .tile-gdp-toggle { cursor: pointer; } .tile-gdp-toggle:hover { color: #fff; } .tile-gdp-caret { margin-left: 6px; color: var(--muted); font-size: 11px; } .tile-gdp-mods { margin-top: 2px; } .tile-gdp-mod { font-size: 12px; color: var(--muted); padding-left: 12px; font-variant-numeric: tabular-nums; } #bottom-panel .row { justify-content: flex-start; margin-top: 10px; } .health-row { display: flex; align-items: center; gap: 8px; } .health-bar { width: 220px; height: 12px; background: rgba(30, 0, 0, 0.8); border-radius: 6px; overflow: hidden; margin-top: 6px; } #unit-health-fill { height: 100%; background: linear-gradient(180deg, #6fe08a, #2fa04d); } #unit-health-label { margin-top: 6px; font-size: 12px; color: var(--muted); font-variant-numeric: tabular-nums; } .unit-stats { display: flex; gap: 16px; margin-top: 6px; } .unit-stat { font-size: 13px; color: var(--muted); border-bottom: 1px dotted rgba(159, 176, 200, 0.5); cursor: help; } .unit-stat:hover { color: var(--text); } /* Chat-like feed of short-lived news in the lower-left corner, with the News button anchored beneath it. */ .game-corner { position: absolute; left: 14px; bottom: 14px; width: 320px; max-width: calc(50vw - 30px); display: flex; flex-direction: column; align-items: flex-start; gap: 8px; pointer-events: none; z-index: 40; } .game-feed { width: 100%; display: flex; flex-direction: column; gap: 4px; } .game-corner .news-button { pointer-events: auto; padding: 6px 14px; font-size: 13px; } .feed-line { align-self: flex-start; max-width: 100%; padding: 6px 10px; border-radius: 7px; background: rgba(8, 12, 20, 0.86); border: 1px solid rgba(150, 190, 240, 0.35); color: var(--text); font-size: 12px; line-height: 1.35; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5); opacity: 1; transform: translateY(0); transition: opacity 0.4s ease, transform 0.4s ease; animation: feed-in 0.18s ease; } .feed-line.leaving { opacity: 0; transform: translateY(8px); } .feed-line.feed-alert { background: rgba(80, 14, 14, 0.92); border-color: #ff6a6a; color: #ffd9d9; font-weight: 600; } @keyframes feed-in { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } } .tooltip { position: absolute; z-index: 80; max-width: 280px; padding: 6px 10px; border-radius: 7px; background: rgba(8, 12, 20, 0.97); border: 1px solid var(--panel-border); color: var(--text); font-size: 12px; white-space: nowrap; pointer-events: none; box-shadow: 0 6px 18px rgba(0, 0, 0, 0.6); } /* Right-click menu for the units sharing a tile. */ .stack-menu { position: absolute; z-index: 70; min-width: 200px; padding: 4px; border-radius: 8px; background: var(--panel-bg); border: 1px solid var(--panel-border); box-shadow: 0 10px 30px rgba(0, 0, 0, 0.55); display: flex; flex-direction: column; gap: 2px; } .stack-item { text-align: left; padding: 7px 10px; border: none; border-radius: 5px; background: transparent; color: var(--text); font-size: 13px; cursor: pointer; } .stack-item:hover { background: rgba(110, 175, 255, 0.2); } .stack-item.stack-all { margin-top: 2px; border-top: 1px solid rgba(255, 255, 255, 0.12); color: #ffcf7a; } /* --------------------------------------------------------------- modals -- */ .modal { position: absolute; inset: 0; background: rgba(0, 0, 0, 0.55); display: flex; align-items: center; justify-content: center; z-index: 50; } .modal.hidden { display: none; } /* The city and government panels dock below the top-left nation summary instead of covering it, so the approval figure stays in view while they are open. The 104px clears the summary's 12px offset plus its padded height. */ #modal-city, #modal-nation { align-items: flex-start; padding: 104px 16px 16px; } .modal-card { width: 620px; max-height: 80vh; display: flex; flex-direction: column; border-radius: 14px; background: var(--panel-bg); border: 1px solid var(--panel-border); box-shadow: 0 24px 70px rgba(0, 0, 0, 0.7); overflow: hidden; } .modal-card-small { width: 420px; } .modal-card-small .row { padding-bottom: 18px; } /* The nation panel carries wide tables and long policy/campaign rows, so it takes most of the screen. A fixed height keeps it from resizing as tabs with wildly different content are switched. */ #modal-nation .modal-card { width: 90vw; max-width: 1600px; height: min(80vh, 900px); max-height: calc(100vh - 120px); } .modal-head { display: flex; justify-content: space-between; align-items: center; padding: 14px 18px; font-size: 19px; font-weight: 600; border-bottom: 1px solid rgba(255, 255, 255, 0.08); } .modal-sub { padding: 10px 18px; color: var(--muted); } .modal-body { padding: 12px 18px 18px; overflow-y: auto; } .modal-close { padding: 4px 11px; } .tabs { display: flex; /* Wrap rather than scroll: every tab stays visible with no clipped edge, and the bar keeps a stable height whatever tab is selected. */ flex-wrap: wrap; flex: 0 0 auto; gap: 6px; padding: 10px 18px 8px; border-bottom: 1px solid rgba(255, 255, 255, 0.08); } .tab { cursor: pointer; flex: 0 0 auto; white-space: nowrap; padding: 8px 16px; border-radius: 8px 8px 0 0; border: 1px solid transparent; border-bottom: none; background: rgba(0, 0, 0, 0.25); color: var(--muted); } .tab.active { background: rgba(110, 175, 255, 0.22); color: var(--text); } .tab-page { display: none; } /* The active tab fills the rest of the card and lets its body scroll: without min-height: 0 the flex item would grow past the card and be clipped by the card's overflow: hidden, hiding e.g. long diplomacy tables. */ .tab-page.active { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; } .tab-page.active > .modal-sub { flex: 0 0 auto; } .tab-page.active > .modal-body { flex: 1 1 auto; min-height: 0; } .row-card { display: flex; gap: 14px; padding: 10px 12px; margin-bottom: 8px; border-radius: 9px; background: rgba(255, 255, 255, 0.05); border: 1px solid rgba(255, 255, 255, 0.05); } .row-card .info { flex: 1; } .row-card .building-icon { width: 46px; height: 46px; flex: 0 0 auto; align-self: center; opacity: 0.9; } .unit-icon { width: 26px; height: 26px; flex: 0 0 auto; opacity: 0.9; } .row-card .name { font-weight: 600; } .row-card .desc { color: var(--muted); font-size: 12px; margin: 3px 0; } .row-card .effects { font-size: 12px; color: #bcd4ff; } .row-card .cost { font-size: 12px; color: var(--muted); margin-top: 3px; } .row-card .actions { display: flex; flex-direction: column; gap: 6px; justify-content: center; } .row-card .actions .aero { min-width: 170px; } .row-card .value { display: flex; align-items: flex-start; justify-content: flex-end; min-width: 140px; font-weight: 600; } .row-card .value.positive { color: #8fe3a8; } .row-card .value.negative { color: #ff9a9a; } .row-card .budget-detail { margin-top: 4px; } .row-card .budget-line { font-size: 12px; color: var(--muted); margin-top: 2px; } .row-card.conflict-card { display: block; cursor: pointer; } .row-card.conflict-card:hover { background: rgba(255, 255, 255, 0.09); } .conflict-detail { margin-top: 8px; padding-top: 8px; border-top: 1px solid rgba(255, 255, 255, 0.08); } .conflict-detail .conflict-line { font-size: 12px; color: var(--muted); } .conflict-detail .conflict-side { font-size: 13px; margin-top: 4px; } .diplomacy-table { width: 100%; border-collapse: collapse; } .diplomacy-table th, .diplomacy-table td { padding: 8px 10px; border-bottom: 1px solid rgba(255, 255, 255, 0.08); text-align: left; } .diplomacy-table th { cursor: pointer; user-select: none; color: var(--muted); font-weight: 600; white-space: nowrap; } .diplomacy-table th:hover { color: var(--text); } .diplomacy-table th.sorted-asc, .diplomacy-table th.sorted-desc { color: var(--text); } .diplomacy-table th.numeric, .diplomacy-table td.numeric { text-align: right; } .diplomacy-table .sort-arrow { font-size: 10px; margin-left: 4px; } .diplomacy-table tbody tr:hover { background: rgba(255, 255, 255, 0.05); } .diplomacy-table tbody tr.local-nation { background: rgba(120, 170, 255, 0.16); } .diplomacy-table tbody tr.local-nation:hover { background: rgba(120, 170, 255, 0.24); } .diplomacy-table tbody tr.local-nation td.name { font-weight: 600; } .diplomacy-table td.status { white-space: nowrap; } .diplomacy-table td.status .aero { margin-left: 8px; padding: 4px 10px; } /* Budget table: income green, every cost red, alternating rows and the net total pinned to the last row. */ .budget-table { width: 100%; border-collapse: collapse; } .budget-table th, .budget-table td { padding: 7px 10px; border-bottom: 1px solid rgba(255, 255, 255, 0.06); text-align: left; } .budget-table th { color: var(--muted); font-weight: 600; } .budget-table td.numeric, .budget-table th.numeric { text-align: right; } .budget-table tbody tr:nth-child(even) { background: rgba(255, 255, 255, 0.045); } .budget-table tbody tr:hover { background: rgba(110, 175, 255, 0.12); } .budget-table .budget-value, .budget-table td.numeric { text-align: right; font-variant-numeric: tabular-nums; } .budget-table tr.income .budget-value, .budget-table tr.income-detail .budget-value, .budget-table td.income { color: #8fe3a8; } .budget-table tr.budget-group .budget-value, .budget-table tr.expense .budget-value, .budget-table tr.expense-detail .budget-value { color: #ff9a9a; } .budget-table tr.budget-section td { padding-top: 14px; color: var(--muted); font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase; } .budget-table tr.budget-group .budget-name.clickable { cursor: pointer; } .budget-table tr.budget-group .budget-name.clickable:hover { color: var(--accent); } .budget-caret { display: inline-block; width: 12px; color: var(--muted); } .budget-note { margin-top: 2px; color: var(--muted); font-size: 11px; } .budget-table tfoot .budget-total td { padding-top: 10px; border-top: 2px solid rgba(255, 255, 255, 0.25); font-weight: 700; } .budget-table tfoot .budget-total .budget-value { color: #fff; } .training-status { padding: 0 18px 16px; } .training-queue { padding-top: 0; } .training-queue .queue-empty { padding: 4px 0; } .training-queue .queue-row { padding: 8px 12px; } .training-queue .queue-row .actions .aero { min-width: 110px; } /* The city modal keeps a fixed size whatever tab is open, so switching between train, buildings, people and budget never resizes the card. The unit buttons sit in a fixed-height box that scrolls when there are more units than fit; the queue below is the flexible part and scrolls too, so adding units never stretches the card or pushes the buttons out of reach. */ #modal-city .modal-card { width: 680px; max-width: calc(100vw - 32px); height: min(80vh, 740px); max-height: calc(100vh - 120px); } #modal-city #city-train-list { flex: 0 0 auto; width: 100%; max-height: 168px; overflow-y: auto; display: flex; flex-direction: column; gap: 8px; } #modal-city #city-train-list .train-button { flex: 0 0 auto; display: flex; align-items: center; gap: 10px; text-align: left; } /* Local test game: the free, instant twin of an order button. */ .free-button { border: 1px dashed rgba(130, 225, 170, 0.75) !important; color: #b8f5cd; } .free-button:disabled { opacity: 0.4; } #modal-city .training-status { flex: 0 0 auto; } #modal-city .training-queue { flex: 1 1 auto; min-height: 0; } .progress { height: 14px; margin-top: 8px; background: rgba(0, 0, 0, 0.5); border-radius: 7px; overflow: hidden; } #city-train-progress { height: 100%; width: 0; background: linear-gradient(180deg, #7fc4ff, #2f7fd0); } .news-item { padding: 10px 12px; margin-bottom: 8px; border-radius: 9px; background: rgba(255, 255, 255, 0.05); border: 1px solid rgba(255, 255, 255, 0.05); } .news-item .news-text { font-weight: 600; } .news-item .news-date { color: var(--muted); font-size: 12px; margin-top: 3px; } .news-empty { color: var(--muted); padding: 8px 0; } #news-button { flex: 0 0 auto; } .banner { position: absolute; left: 50%; top: 12px; transform: translateX(-50%); padding: 10px 20px; border-radius: 8px; background: rgba(140, 20, 20, 0.9); border: 1px solid rgba(255, 120, 120, 0.6); z-index: 60; } /* Politics: the nation Politics tab and the city People tab. */ .politics-card { padding: 12px; margin-bottom: 10px; border-radius: 9px; background: rgba(255, 255, 255, 0.05); border: 1px solid rgba(255, 255, 255, 0.06); } .politics-card > .name { font-weight: 700; margin-bottom: 4px; } .politics-controls { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 10px; margin-top: 8px; } .politics-field { display: flex; flex-direction: column; gap: 4px; font-size: 12px; } .politics-field .politics-label { color: var(--muted); text-transform: uppercase; letter-spacing: 0.04em; } .politics-select { background: rgba(0, 0, 0, 0.4); color: inherit; border: 1px solid rgba(255, 255, 255, 0.15); border-radius: 6px; padding: 4px 6px; } .politics-readout { color: var(--muted); font-size: 12px; margin-left: 8px; } .politics-policy { padding: 8px 0; border-top: 1px solid rgba(255, 255, 255, 0.07); } .politics-policy:first-of-type { border-top: none; } .politics-row { padding: 4px 0; font-size: 13px; border-top: 1px solid rgba(255, 255, 255, 0.05); } .politics-penalty { color: #f0b060; } .ethnic-chart { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; margin-top: 8px; } .ethnic-legend { display: flex; flex-direction: column; gap: 4px; font-size: 13px; } .ethnic-legend-row { display: flex; align-items: center; gap: 8px; } .ethnic-swatch { width: 12px; height: 12px; border-radius: 3px; display: inline-block; } .ethnic-donut { flex: 0 0 auto; } .politics-policy-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; } .politics-new { margin-top: 10px; } .politics-wizard { margin-top: 10px; padding: 12px; border-radius: 8px; background: rgba(0, 0, 0, 0.28); border: 1px solid rgba(255, 255, 255, 0.08); } .politics-estimate { margin-top: 8px; font-size: 13px; } .politics-cost { color: #f0b060; } .politics-wizard .politics-controls { margin-top: 0; } .ethnic-trend { font-weight: 700; margin-left: 2px; } .trend-up { color: #6ee787; } .trend-down { color: #f27272; } #tile-ethnicity { margin-top: 6px; } #tile-ethnicity .tile-population { font-size: 13px; font-variant-numeric: tabular-nums; } #tile-ethnicity .ethnic-chart { margin-top: 4px; gap: 10px; } #tile-ethnicity .ethnic-legend { font-size: 12px; } .politics-row-actions { display: flex; justify-content: flex-end; margin-top: 4px; } .politics-spend { display: flex; align-items: center; gap: 8px; margin-top: 6px; } .politics-slider { flex: 1 1 auto; accent-color: #7fc4ff; } .opinion-list { margin-top: 10px; display: flex; flex-direction: column; gap: 4px; } .opinion-heading { margin-top: 8px; color: var(--muted); text-transform: uppercase; font-size: 11px; letter-spacing: 0.05em; } .opinion-row { display: grid; grid-template-columns: 170px 1fr 48px; align-items: center; gap: 8px; font-size: 13px; } .opinion-bar { height: 10px; border-radius: 5px; background: rgba(0, 0, 0, 0.4); overflow: hidden; } .opinion-fill { height: 100%; background: #6ee787; } .opinion-fill.negative { background: #f27272; } .opinion-pct { text-align: right; color: var(--muted); }