/* micropolisJS. Adapted by Graeme McCutcheon from Micropolis.
 *
 * This code is released under the GNU GPL v3, with some additional terms.
 * Please see the files LICENSE and COPYING for details. Alternatively,
 * consult http://micropolisjs.graememcc.co.uk/LICENSE and
 * http://micropolisjs.graememcc.co.uk/COPYING
 *
 * The name/term "MICROPOLIS" is a registered trademark of Micropolis (https://www.micropolis.com) GmbH
 * (Micropolis Corporation, the "licensor") and is licensed here to the authors/publishers of the "Micropolis"
 * city simulation game and its source code (the project or "licensee(s)") as a courtesy of the owner.
 *
 */

@import url(https://fonts.googleapis.com/css?family=Open+Sans:400,300,600,300italic,400italic);

* {
  padding: 0;
  margin: 0;
}

@font-face {
  font-family: 'Chunk';
  font-style: normal;
  font-weight: 400;
  src: local('ChunkFive'), url(chunk.woff) format('woff');
}

body {
  background-image: url('../images/dirtbg.png');
}

button, input[type="submit"] {
  width: 140px;
  height: 19px;
  border-radius: 3px;
  cursor: pointer;
  border: none;
  font-family: sans-serif;
}

footer {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0px;
  height: 5em;
  background-color: darkslategray;
}

header {
  background-color: darkslategray;
  background-image: repeating-linear-gradient(-45deg, transparent, transparent 35px, rgba(255, 255, 255, 0.2) 35px, rgba(255, 255, 255, 0.2) 70px);
  font-family: 'Chunk', serif;
  height: 35px;
}

header h1 {
  margin-left: 50px;
}

/* :not(.initialHidden) rather than the shared .inlineblock class -- .inlineblock is
   declared later in this file than .initialHidden, so at equal specificity it would
   silently win and defeat the hiding before the game reveals this element */
#mobileDrawerToggles:not(.initialHidden) {
  display: inline-block;
  vertical-align: middle;
}

.mobileDrawerToggle {
  display: none;
  width: auto;
  height: auto;
  padding: 6px 10px;
  margin-left: 10px;
  background-color: darkslategray;
  color: white;
  border: 1px solid white;
  font-family: sans-serif;
  font-size: 13px;
}

#cancelToolToggle {
  background-color: darkred;
}

body.showInfoDrawer #infoDrawerToggle, body.showToolsDrawer #toolsDrawerToggle {
  background-color: cadetblue;
}

main {
  position: absolute;
  top: 0;
  bottom: 26px;
  width: 100%;
  overflow: hidden;
}

#MicropolisCanvas {
  /* One-finger drag pans the map (see inputStatus.js) -- without this the browser's own
     touch scroll/zoom gestures fight with it */
  touch-action: none;
}

nav a, nav a:link, nav a:visited, nav a:hover, footer a, footer a:link, footer a:visited, footer a:hover,
#aboutHeader a, #aboutHeader a:link, #aboutHeader a:visited, #aboutHeader a:hover,
#licenseHeader a, #licenseHeader a:link, #licenseHeader a:visited, #licenseHeader a:hover {
  text-decoration: none;
  color: white;
}

footer a, footer a:link, footer a:visited, footer a:hover {
  text-decoration: underline;
  margin-left: 5px;
}

nav li {
  display: inline;
}

.alignCenter {
  text-align: center;
}

.awaitGeneration, .hidden, .initialHidden, .imageData {
  display: none;
}

.bad {
  background-color: red;
  color: yellow;
}

.block {
  display: block;
}

.bold {
  font-weight: bold;
}

.border-bottom {
  border-bottom: 1px solid black;
}

.budgetRight {
   margin-left: 7px
}

.cancel, #debugRequest, #pauseRequest {
  background-color: darkslategray;
  color: white;
}

.centred, #tooSmallInner {
  position: absolute;
  top: 50%;
  left: 50%;
  border: 1px solid black;
  width: 400px;
  transform: translate(-50%, -50%);
}

.chunk {
  font-family: 'Chunk', serif;
  font-weight: normal;
}

.dialogOK {
  color: white;
  background-color: green;
}

.difficulty {
  margin-left: 10px;
}

.elided {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.fontlarge {
  font-size: 36px;
}

.good {
  background-color: LightGreen;
  color: black;
}

.helpPointer {
  cursor: help;
}

.inline {
  display: inline;
}

.inlineblock {
  display: inline-block;
}

.left {
  float: left;
}

.leftedge {
  left: 50px;
}

.loadSave, .saveHeader {
  background-color: cadetblue;
  color: white;
}

.loadSave:disabled {
  background-color: darkgray;
  color: silver;
  cursor: not-allowed;
}

.margin10 {
  margin: 10px 0;
}

.modal {
  position: fixed;
  top: 50%;
  left: 50%;
  border: 1px solid black;
  transform: translate(-50%, -50%);
  z-index: 20;
  background-color: white;
  width: 500px;
}

.modal header {
  height: 18px;
  /* panel.js grabs any window's <header> as its drag handle -- these already needed
     one for the title bar, so this is the only CSS change needed to make every
     .modal window draggable alongside it. */
  cursor: move;
  user-select: none;
}

.mintcream, .neutral {
  background-color: mintcream;
}

.neutral {
  color: black;
}

.open {
  font-family: 'Open Sans', sans-serif;
}

.opinion, .statistics {
  font-size: 12px;
}

.opinionList dd {
  font-style: italic;
}

.padding10 {
  padding: 10px;
}

.panelHeader {
  height: 18px;
  padding: 4px 6px;
  background-color: darkslategray;
  color: white;
  font-family: sans-serif;
  font-size: 12px;
  cursor: move;
  user-select: none;
}

/* Bottom-right resize grip, added by panel.js for any Panel constructed with
   opts.resizable -- a striped square in the style of a native textarea resize
   handle. Positioned relative to the panel itself, which is always position:
   fixed while floated (desktop-only, same as dragging -- see MOBILE_BREAKPOINT
   in panel.js) or position:relative below as a fallback so it's never left
   positioning against some unrelated ancestor. */
.panelResizeHandle {
  position: absolute;
  right: 0;
  bottom: 0;
  width: 14px;
  height: 14px;
  cursor: nwse-resize;
  background-image: linear-gradient(135deg,
    transparent 0 40%, rgba(0, 0, 0, 0.35) 40% 50%,
    transparent 50% 60%, rgba(0, 0, 0, 0.35) 60% 70%,
    transparent 70% 80%, rgba(0, 0, 0, 0.35) 80% 90%,
    transparent 90% 100%);
}

.pointer {
  cursor: pointer;
}

.queryItem, .statisticsItem {
  display: inline-block;
  width: 200px;
}

.queryList dt {
  font-weight: 600;
}

.right {
  float: right;
}

.rightedge {
  right: 50px;
}

.row button {
  display: inline-block;
}

.selected {
  font-weight: bold;
  border: 1px solid black;
}

.semibold {
  font-weight: 600px;
}

/* Scenario mode's objective/deadline readout, appended inside Town Info (see
   index.html and scenarioController.js). Ruled off and slightly smaller so it reads
   as a second section rather than more of the city stats above it -- those describe
   the city, these describe what's being asked of it. Only ever visible when a
   scenario is actually running; scenarioController.js unhides it. */
#scenarioInfo {
  margin-top: 8px;
  padding-top: 6px;
  border-top: 1px solid rgba(0, 0, 0, 0.25);
  font-size: 11px;
  line-height: 1.35;
}

#scenarioName {
  font-size: 12px;
}

#scenarioDeadline {
  opacity: 0.75;
}

.shadow, .controlShadow {
  -ms-box-shadow: 10px 0px 35px 0px rgba(31,28,31, 0.8);
  -webkit-box-shadow: 10px 0px 35px 0px rgba(31,28,31, 0.8);
  -moz-box-shadow: 10px 0px 35px 0px rgba(31,28,31, 0.8);
  box-shadow: 10px 0px 35px 0px rgba(31,28,31, 0.8);
}

.statisticsItem {
  display: inline-block;
  width: 200px;
}

.statisticsRight {
  display: inline-block;
  width: 150px;
}

/* The Main Menu button at the foot of the Menu panel. Ruled off and set apart from
   the buttons above it because it is the only one that throws the city away -- the
   rest of that list opens a window or nudges the view. Deliberately not red: this
   is a normal thing to want to do, and it asks for confirmation before it acts. */
.quitButton {
  margin-top: 8px;
  border-top: 1px solid rgba(0, 0, 0, 0.25);
  padding-top: 8px;
  height: auto;
  background-color: darkslategray;
  color: white;
}

/* flex: 0 0 auto on both button classes so a squeezed panel scrolls its buttons
   rather than crushing them -- Tools and Menu are both flex columns whose body the
   column layout is allowed to shrink (see shrinkColumnToFit in panel.js). */
.miscButton {
  flex: 0 0 auto;
}

.toolButton {
  width: 140px;
  flex: 0 0 auto;
  color: black;
  background-color: white;
  font-size: 12px;
  display: block;
}

.toolButton:hover:not(.selected) {
  filter: brightness(1.1);
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.4);
}

.unselected {
  font-weight: normal;
}

.white {
  color: white;
}

.width140 {
  width: 140px;
}

.z1 {
  z-index: 1;
}

.nameLicense {
  margin-top: 1em;
  margin-bottom: 0.5em;
  font-size: 10px;
  opacity: 0.55;
}

/* The OK/Cancel row, which wants the full width rather than the 230px column the
   rule below gives every other div in the form. Was `> div:nth-child(9)`, i.e. "the
   ninth thing in the form, whatever that turns out to be" -- which stopped being the
   button row the moment the Ordinances and Power contracts lines were added above it,
   and would have gone wrong again on any later addition. Addressed by its own id now. */
#budgetForm > #budgetButtons {
  align: center;
  width: 100%;
}

#budgetForm > div, #budgetForm > fieldset {
  display: inline-block;
  width: 230px;
}

#budgetForm input[type="range"] {
  max-width: 210px;
  padding: 5px;
}

#budgetReset {
  color: white;
  background-color: dodgerblue;
}

/* City ordinances (src/ordinanceWindow.js) and the neighbouring cities
   (src/neighbourWindow.js). Both windows build their own rows at open time, so there
   is nothing in index.html for these to hang off but the two list containers -- every
   class below is applied in JS. Both lists scroll rather than growing the dialog: eight
   ordinances already make a tall window, and the modal has to keep its OK button
   reachable on a short screen. */
#ordinanceList, #neighbourList {
  max-height: 340px;
  overflow-y: auto;
  margin: 6px 0;
}

.ordinanceRow, .neighbourRow {
  padding: 6px 4px;
  border-bottom: 1px solid #ddd;
  font-size: 12px;
}

.ordinanceRow:last-child, .neighbourRow:last-child {
  border-bottom: none;
}

.ordinanceLabel {
  font-weight: bold;
  margin-left: 4px;
  cursor: pointer;
}

.ordinanceCost, .neighbourRowCost {
  float: right;
  font-size: 11px;
  opacity: 0.8;
}

.ordinanceBlurb, .neighbourBlurb, .neighbourRates {
  margin: 2px 0 0 22px;
  font-size: 11px;
  font-style: italic;
  opacity: 0.75;
}

/* An ordinance that cannot be enacted right now keeps its row, greyed, with the reason
   in place of the blurb -- a policy that silently vanished from the list would be a
   worse mystery than one that says why it is unavailable. */
.ordinanceBlocked {
  opacity: 0.5;
}

#ordinanceTotal, #neighbourPower {
  padding: 6px;
  font-weight: bold;
  font-size: 12px;
  background-color: #f2f2f2;
  border-radius: 3px;
}

/* Empty except in the one situation it warns about, so it takes no room the rest of
   the time -- :empty rather than a class toggled from JS, since "is there any text in
   here" is exactly the condition. */
#neighbourWarning {
  margin-top: 4px;
  padding: 6px;
  font-size: 11px;
  color: #7a3b00;
  background-color: #fdf0d5;
  border-radius: 3px;
}

#neighbourWarning:empty {
  display: none;
}

.neighbourName {
  font-weight: bold;
}

.neighbourRelations {
  float: right;
  font-size: 11px;
  opacity: 0.8;
}

.neighbourRates {
  margin-left: 0;
}

.neighbourControls {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 4px;
}

.neighbourControls select {
  flex: 0 0 auto;
}

.neighbourControls input[type="range"] {
  flex: 1 1 auto;
  min-width: 0;
  padding: 0;
}

.neighbourReadout {
  font-size: 11px;
  opacity: 0.85;
}

.neighbourCooloff {
  opacity: 0.5;
}

#congratsHeader {
  background-color: limegreen;
}

/* The scrolling body shared by the two button panels: Tools (#buttons) and Menu
   (#menuButtons). Both hold a single column of fixed-width buttons that can run
   taller than the panel, so both need the same flex:1/min-height:0/overflow-y
   combination to scroll internally rather than overflowing their panel. */
#buttons, #menuButtons {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 8px;
  box-sizing: border-box;
}

/* Sizing/position/flex-direction apply on both breakpoints -- the mobile
   drawer uses this exact box too (see body.showToolsDrawer below), just
   toggled via display instead of floated. flex-direction is meaningless
   until something actually sets display:flex, so it's harmless to declare
   here even though display itself has to stay conditionally gated below. */
#controls {
  position: absolute;
  top: 40px;
  width: 172px;
  /* A single column of every build tool runs taller than most viewports --
     #buttons above scrolls internally past this cap rather than letting the
     panel (or the resize handle on its bottom-right corner) run off-screen. */
  max-height: calc(100vh - 80px);
  flex-direction: column;
}

/* Sized and structured to match #controls above, for the same reason: a single
   column of 140px-wide buttons (see the global "button" rule) needs 156px of
   content box, which .width140 + .padding10 never gave it -- every button used
   to sit 10px proud of this panel. 172px is #controls' own width, so the two
   button panels now read as a matched pair. Sizing/flex-direction apply on both
   breakpoints; only display is gated below. */
#miscButtons {
  width: 172px;
  max-height: calc(100vh - 80px);
  flex-direction: column;
}

/* Town Info sits directly above Menu in the same column -- matched to its width so
   the stack has one edge rather than two. It has no fixed-width content of its own
   (just wrapping text), so this is purely cosmetic. #rciPanel below is deliberately
   left narrower: its graph scales on the smaller of the two axes (see rci.js's
   _rectSize), so widening it alone would only letterbox the graph. */
#infobar {
  width: 172px;
}

/* Only the flex switch needs :not(.initialHidden)/breakpoint-gating (see
   #rciPanel below for the full reasoning) -- display is the one property
   that actually competes with .initialHidden/the max-width:768px drawer
   rules on specificity; everything else above doesn't, so it can just apply
   unconditionally like the rest of this rule always did. */
@media (min-width: 769px) and (min-height: 601px) {
  #controls:not(.initialHidden),
  #miscButtons:not(.initialHidden) {
    display: flex;
  }
}

#debug {
  position: absolute;
  top: 580px;
  width: 280px;
  display: none;
}

#difficultyLabel {
  margin-right: 5px;
}

#disasterLabel {
  display: block;
}

#evalContainer {
  font-size: 14px;
}

#flagsTable {
  text-align: center;
  padding: 5px;
  margin: 0 auto;
  border: 1px solid black;
}

#flagsTable td, #flagsTable th {
  padding: 0 5px;
}

/* Shared flow container for the #infobar/#miscButtons stack -- each panel's
   position derives from the flex column instead of hand-copied top offsets,
   so only this rule (and its media-query overrides below) needs to change if
   the stack's height budget changes. #rciPanel also starts life here (so the
   mobile drawer treats it like any other drawer panel), but panel.js lifts it
   out of this flow on desktop via position:fixed once the window is wide
   enough to float it -- see panel.js and its MOBILE_BREAKPOINT constant. */
#leftStack {
  position: absolute;
  top: 40px;
  display: flex;
  flex-direction: column;
  gap: 40px;
}

#loadingBanner[data-hasscript=false] {
  display: none;
}

/* The city history graph (src/graphWindow.js). Same construction as #mapWindow below --
   read that block first; everything here follows the same reasoning, including the
   inline-style hiding in index.html. Wider than the map window and shorter, because a
   time series wants width where a 120x100 map wants square. */
#graphWindow {
  width: 470px;
  height: 430px;
  min-width: 300px;
  min-height: 280px;
  display: flex;
  flex-direction: column;
}

#graphContainer {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  min-height: 0;
  gap: 8px;
}

#graphRange {
  flex: 0 0 auto;
}

.graphRangeButton {
  width: auto;
  padding: 2px 12px;
  margin: 0 3px;
  font-size: 12px;
}

.graphRangeSelected {
  background-color: darkslateblue;
  color: white;
}

/* The one row that grows. min-height:0 for the same reason #mapCanvasContainer needs
   it: a flex item defaults to min-height:auto and would otherwise refuse to shrink,
   pushing the legend and OK button out of the bottom of a resized window. */
#graphCanvasContainer {
  flex: 1 1 auto;
  min-height: 0;
  background-color: white;
  border: 1px solid #555;
}

#graphCanvas {
  display: block;
}

/* The legend is also the series toggles -- one list, not two to keep in step. */
#graphLegend {
  flex: 0 0 auto;
  display: flex;
  flex-wrap: wrap;
  gap: 2px 10px;
  font-size: 11px;
}

.graphLegendItem {
  display: flex;
  align-items: center;
  gap: 3px;
  cursor: pointer;
  user-select: none;
}

.graphLegendItem input {
  margin: 0;
}

.graphSwatch {
  width: 10px;
  height: 10px;
  border: 1px solid rgba(0, 0, 0, 0.3);
  flex: 0 0 auto;
}

.graphSeriesValue {
  font-weight: 600;
}

#graphAxisNote {
  flex: 0 0 auto;
  margin: 0;
  font-size: 10px;
  font-style: italic;
  color: #555;
}

#graphButtons {
  flex: 0 0 auto;
}

/* Segmented simulation-speed control in the Menu panel, replacing the old single Pause
   button. Four on one row so it costs the panel one button's worth of height rather
   than four -- the left-hand column's height is contended (see panel.js). */
#speedControls {
  display: flex;
  gap: 2px;
  width: 140px;
  margin: 0 auto;
  flex: 0 0 auto;
}

.speedButton {
  width: auto;
  flex: 1 1 0;
  min-width: 0;
  padding: 2px 0;
  font-size: 11px;
}

.speedSelected {
  background-color: darkslategray;
  color: white;
  font-weight: 600;
}

/* The city data map (src/mapWindow.js). Overrides .modal's shared 500px width with a
   size derived from the map instead: 120x100 tiles at 3px a tile is 360x300, which is
   the smallest the overlays are actually readable at, plus room for the chrome around
   it. Resizable from there -- see the Panel entry in game.js -- so the whole thing is a
   flex column with exactly one growable row (#mapCanvasContainer below).

   Unconditionally display:flex, with the hiding done by an inline style in index.html
   instead of the .hidden class -- see the comment on the element there for why that
   swap is required rather than cosmetic. */
#mapWindow {
  width: 400px;
  height: 500px;
  min-width: 260px;
  min-height: 320px;
  display: flex;
  flex-direction: column;
}

#mapContainer {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  min-height: 0;
  gap: 8px;
}

#mapControls {
  flex: 0 0 auto;
}

#mapOverlaySelect {
  margin-left: 6px;
  max-width: 220px;
}

/* The one row that grows and shrinks with the window. min-height:0 because a flex item
   defaults to min-height:auto, which refuses to shrink below its content and would let
   the canvas push the legend and OK button out of the bottom of a shrunk window. */
#mapCanvasContainer {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: #101014;
  border: 1px solid #555;
}

/* pixelated for the same reason mapWindow.js disables imageSmoothingEnabled: the map is
   a grid of samples blitted up from one pixel per tile, and interpolating it invents
   gradients between blocks that the simulation never computed. Both are needed -- the
   canvas is scaled by its own backing-store size AND, on a fractional scale, by the
   browser's layout of it. */
#mapCanvas {
  display: block;
  image-rendering: pixelated;
  cursor: pointer;
}

#mapLegend {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 11px;
}

#mapLegendBar {
  flex: 1 1 auto;
  height: 10px;
  border: 1px solid #555;
}

#mapDescription {
  flex: 0 0 auto;
  margin: 0;
  font-size: 11px;
  font-style: italic;
  min-height: 28px;
}

#mapButtons {
  flex: 0 0 auto;
}

#monstertv {
  position: absolute;
  top: 364px;
  width: 200px;
  background-color: red;
}

#monsterTVForm {
  margin-top: 5px;
}

#nameForm {
  font-size: 18px;
  width: 150px;
}

#notifications {
  position: absolute;
  bottom: 65px;
  left: 50%;
  border: 1px solid black;
  width: 400px;
  height: 19px;
  transform: translateX(-50%);
}

#opaque {
  position: absolute;
  top: 0;
  left: 0;
  bottom: 0;
  right: 0;
  opacity: .70;
  filter: Alpha(Opacity=70);
  background-color: gray;
  z-index: 10;
}

#playit {
  background-color: green;
  color: white;
  font-size: 18px;
  height: 24px;
}

#rciPanel {
  /* 126x171 is the graph's fixed layout (see rci.js's _widthUnits/
     _heightUnits) at its original 5px rect size -- tall enough for a
     full-height bar on both the positive AND negative side of the demand
     graph (the old 88px height only ever budgeted room for positive bars,
     silently clipping negative demand). Resizing this panel bigger (desktop
     only) scales the whole graph up to fill it -- see RCI.prototype.update --
     rather than just adding blank space. Sizing/flex-direction apply on both
     breakpoints, same as #controls above: the mobile drawer uses this exact
     box too (see body.showInfoDrawer below), and flex-direction is a no-op
     until display:flex actually applies. */
  position: relative;
  flex-direction: column;
  width: 126px;
  height: 171px;
}

/* :not(.initialHidden) rather than a plain selector -- an ID selector always
   beats .initialHidden's class selector on specificity regardless of source
   order, so a bare "display: flex" here would defeat the hiding before the
   game even starts (same trap #mobileDrawerToggles avoids above). Scoped to
   the desktop breakpoint for the same reason as #controls above: without
   that, this would also outrank the max-width:768px block's own #rciPanel
   display rules once .initialHidden is gone, even on a phone. */
@media (min-width: 769px) and (min-height: 601px) {
  #rciPanel:not(.initialHidden) {
    display: flex;
  }
}

#RCIContainer {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 0;
  margin: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}

#screenshotLink, #screenshotLink:link, #screenshotLink:visited, #screenshotLink:hover {
  text-decoration: none;
  color: dodgerblue;
}

#splash {
  position: absolute;
  width: 540px;
  height: 380px;
}

#splashButton {
  margin-bottom: 10px;
}

#splashButtons {
  position: absolute;
  left: 10px;
  top: 105px;
}

#splashButtonContainer {
  position: absolute;
  top: 90px;
  left: 390px;
}

#splashContainer {
  position: absolute;
  top: 90px;
  left: 20px;
}

#splashGenerate {
  background-color: dodgerBlue;
  color: white
}

#splashPlay {
  background-color: lightgreen;
}

#start {
  display: none;
  text-align: center;
  font-size: 18px;
}

#loadList {
  display: none;
  width: 500px;
  text-align: center;
}

#scenarioList {
  display: none;
  width: 500px;
  text-align: center;
}

#fullScenarioList {
  display: none;
  width: 700px;
  text-align: center;
}

#fullScenarioTableEl .scenarioBlurb {
  text-align: left;
  font-size: 13px;
}

#fullScenarioTableEl .scenarioYear {
  font-weight: normal;
  color: dimgray;
}

#statisticsList, .queryList {
  width: 400px;
  margin: 0 auto;
  padding: 0 110px;
}

#tooSmall {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  display: none;
  z-index: 200;
}

#tooSmallInner {
  max-width: 90vw;
  box-sizing: border-box;
}

/* Desktop mouse users don't need this -- shown only at mobile widths, see the
   max-width:768px override below */
#touchNotice {
  display: none;
  font-size: 13px;
  font-style: italic;
  color: dimgray;
}

#tvContainer {
  width: 177px;
  height: 128px;
  margin: 0 auto;
}

/* Clears the header, which is a sibling rather than an ancestor, so this has to be a
   length rather than something that follows automatically. It used to be a hardcoded
   guess per breakpoint, and every guess was wrong somewhere: the header's real height
   moves with the breakpoint, with whether the mobile drawer toggles are showing, and
   with whether the title row wraps. At phone width the guess was ~24px short, so the
   game canvas was painted over the bottom half of the Menu/Tools buttons -- and a tap
   there landed on the map, placing a building instead of opening the drawer.
   --headerHeight is the header's actual measured height, republished on resize and
   after webfonts land (see syncHeaderHeight in src/micropolis.js); the fallback is
   only for the instant before that first runs. */
#wrapper {
  position: absolute;
  top: var(--headerHeight, 55px);
  left: 0px;
  right: 0px;
  bottom: 0px;
}


/* Miscellaneous buttons */

#budgetRequest, #budgetHeader {
  background-color: dodgerblue;
  color: white;
}

#disasterRequest, #disasterHeader {
  background-color: red;
  color: yellow;
}

#evalRequest, #evalHeader {
  background-color: forestgreen;
  color: white;
}

#mapRequest, #mapHeader {
  background-color: darkcyan;
  color: white;
}

#graphRequest, #graphHeader {
  background-color: darkslateblue;
  color: white;
}

.screenshotHeader, #screenshotRequest {
  background-color: tomato;
  color: white;
}

#settingsHeader, #settingsRequest {
  background-color: #663399;
  color: white;
}

#ordinanceHeader, #ordinanceRequest {
  background-color: #8b5a2b;
  color: white;
}

#neighbourHeader, #neighbourRequest {
  background-color: #2f6f6f;
  color: white;
}

/* Tool buttons. b0r3d-city's custom buildings (Hospital/School/Casino/Library, etc.)
   aren't listed here -- their colours are set inline from src/customBuildings.js when
   game.js renders their buttons, not via a per-ID rule. */
#airportButton {
  background-color: violet;
}

#bulldozerButton {
  background-color: salmon;
}

#coalButton {
  background-color: gray;
}

#commercialButton {
  background-color: blue;
  color: white;
}

#fireButton {
  background-color: red;
}

#industrialButton {
  background-color: yellow;
}

#nuclearButton {
  background-color: mistyrose;
}

#parkButton {
  background-color: darkgreen;
  color: white;
}

#policeButton {
  background-color: darkblue;
  color: white;
}

#portButton {
 background-color: dodgerblue;
 color: white;
}

#queryButton, #queryHeader {
  background-color: cyan;
}

#railButton {
  background-color: brown;
  color: white;
}

#residentialButton {
  background-color: lime;
}

#roadButton {
  background-color: black;
  color: white;
}

#stadiumButton {
  background-color: indigo;
  color: white;
}

#wireButton {
  background-color: khaki;
}

@media (max-height: 742px) {
  header {
    height: 15px;
  }

  .border-bottom {
    border: none;
  }

  .fontlarge {
      font-size: 16px;
  }

  #author {
    display: none;
  }

  #notifications {
    bottom: unset;
    border: none;
    top: 593px;
  }

  /* The #wrapper override that used to live here (top: 35px, to match this block's
     shorter header) is gone: #wrapper now clears the header's real measured height,
     which already accounts for this. */
}

@media (max-height: 702px) {
  #controls {
    top: 30px;
  }

  #debug {
    top: 544px;
  }

  #leftStack {
    top: 30px;
    gap: 30px;
  }

  #monstertv {
    top: 324px;
  }

  #notifications {
    top: 563px;
  }
}

@media (max-height: 672px) {
  #controls {
    top: 20px;
  }

  #debug {
    top: 504px;
  }

  #leftStack {
    top: 20px;
    gap: 20px;
  }

  #monstertv {
    top: 304px;
  }

  #notifications {
    top: 533px;
  }
}

@media (max-height: 642px) {
  .controlShadow {
    -ms-box-shadow: unset;
    -webkit-box-shadow: unset;
    -moz-box-shadow: unset;
    box-shadow: unset;
  }

  #controls {
    top: 10px;
  }

  #debug {
    top: 460px;
  }

  #leftStack {
    top: 10px;
    gap: 10px;
  }

  #monstertv {
    top: 274px;
  }

  #notifications {
    top: 503px;
  }
}

@media (max-height: 612px) {
  #controls {
    top: 0px;
  }

  #debug {
    top: 396px;
  }

  #leftStack {
    top: 0px;
    gap: 0px;
  }

  #monstertv {
    top: 220px;
  }

  #notifications {
    top: 473px;
  }
}

@media (max-height: 400px) {
  #tooSmall[data-hasscript=true] {
    display: block;
  }
}

/* THE COMPACT (DRAWER) LAYOUT.

   A comma in a media query is OR, so this fires when the viewport is small in EITHER
   dimension. It must stay the exact complement of the "(min-width: 769px) and
   (min-height: 601px)" queries above and of roomyMediaQuery in panel.js -- negating
   an OR needs AND, which is why those two are and-joined. If the three ever drift,
   there is a band of viewport sizes where both layouts apply at once, or neither.

   Width was the only test until 2026-09-09. A phone in landscape (~932x430) clears
   768px easily and then has nowhere near the ~609px of height the left-hand column
   needs, so it took the floating-panel path and stacked three panels on top of each
   other -- see the measurements in panel.js. #tooSmall did not catch it either; that
   only appears below 400px.

   Only the rules that decide WHICH LAYOUT runs live in here. The width-driven
   cosmetics (header trimming, 94vw dialog caps, the splash reflow) stay in the
   max-width block further down, because a 932px-wide landscape phone doesn't need a
   header trimmed to fit 375px. */
@media (max-width: 768px), (max-height: 600px) {
  /* The left (info/menu) and right (tools) panel stacks no longer fit next to each
     other and the canvas -- they become toggleable overlay drawers instead of
     always-on. #monstertv and #debug aren't included: both already manage their own
     show/hide independently of game start (inline style via jQuery .show()/.hide()), so
     folding them into the drawer classes here would just fight that. */
  #infobar, #miscButtons, #rciPanel, #controls {
    display: none;
  }

  body.showInfoDrawer #infobar {
    display: block;
  }

  /* #rciPanel and #controls are flex columns (header + a growable/scrollable
     body) so their resizable desktop layout also works as a drawer -- display:
     block here would silently drop that flex context and let their content
     collapse or overflow unpredictably. */
  body.showInfoDrawer #rciPanel,
  body.showInfoDrawer #miscButtons {
    display: flex;
  }

  body.showToolsDrawer #controls {
    display: flex;
  }

  /* Town Info + Menu + Demand stack to ~634px, which is taller than a landscape
     phone's entire viewport (430px). Without bounding the stack to the room it
     actually has, the bottom of it -- Zoom In, Zoom Out and the whole demand graph
     -- simply sits below the fold with nothing to scroll. #controls needs no
     equivalent: it already caps itself at calc(100vh - 80px) and scrolls #buttons.

     Scoped to the drawer actually being open, not to #leftStack generally: with its
     children display:none the closed container collapses to zero width and height,
     and giving it bottom:0 unconditionally would leave a full-height invisible box
     down the left edge of the map, swallowing clicks meant for the tiles under it. */
  body.showInfoDrawer #leftStack {
    bottom: 0;
    overflow-y: auto;
  }

  /* Dragging and resizing only happen on a roomy viewport (see isRoomy in panel.js)
     -- the grip has nothing to do once a panel is a static drawer. */
  .panelResizeHandle {
    display: none;
  }

  /* The toggles that open those drawers. The #mobileDrawerToggles container is
     always inline-block once the game reveals it; these are the buttons inside it,
     which are display:none by default, so this rule is what actually makes the
     drawers reachable -- it has to move with the layout rules above, or a landscape
     phone gets hidden panels and no way to open them. */
  .mobileDrawerToggle {
    display: inline-block;
  }
}

/* Dialogs are a height problem as much as a width one, and this half was previously
   trapped in the width-only block below. A .modal is 500px wide and can be taller
   than a 430px landscape viewport, which puts its own OK/Cancel buttons off-screen
   with no way to reach them. dvh over vh because mobile browsers slide their address
   bar in and out of the viewport, which vh does not track. */
@media (max-height: 600px) {
  .centred, #tooSmallInner, .modal {
    max-height: 90vh;
    max-height: 90dvh;
    overflow-y: auto;
  }
}

/* WIDTH-ONLY COSMETICS. Everything below is about fitting ~375-412px of real phone
   width -- not about which layout runs, which the compact query above decides. */
@media (max-width: 768px) {

  /* Trimmed from the desktop defaults (10px/20px padding, 10px margin) -- the title
     plus the 3 toggle buttons (Menu/Tools/the cancel-tool button) need to fit in
     ~375-412px of real phone width. The About/GitHub links used to live here too and
     were the reason this row didn't fit even trimmed -- they've since moved down to
     the footer, next to the b0r3d.org link (see #footerLinks), which is what actually
     fixed the wrapping rather than this trimming alone. */
  .mobileDrawerToggle {
    padding: 4px 6px;
    margin-left: 4px;
    font-size: 12px;
  }

  #title {
    font-size: 16px;
    margin-left: 6px;
  }

  /* Fallback for phones too narrow even for title+toggles alone -- wraps to a second
     row instead of a fixed height that everything overflows (float doesn't wrap the
     way flex does, so this also drops the .left float) */
  header {
    height: auto;
    min-height: 35px;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
  }

  header .left {
    float: none;
  }

  /* No #wrapper override here any more either. This was the guess that actually bit:
     40px of clearance for a header that really measures ~64px once the drawer toggle
     buttons are in it, leaving the canvas covering their bottom halves. #wrapper
     tracks the header's measured height now, so the header is free to be as tall as
     its contents need -- including wrapping to a second row on a very narrow phone,
     which the fixed value could never have survived. */

  /* All the pre-game/dialog boxes below (.centred, .modal and their fixed-width
     variants) were sized for desktop and simply run off the edges of a phone-width
     screen -- #tooSmallInner was the one place that already used the vw-relative,
     border-box pattern that fixes this; extended here to the rest. max-height +
     overflow-y matters just as much as max-width: Budget/Settings/High Scores all
     stack their content into a single column at this width (see #budgetForm below),
     which makes them taller, not just narrower -- without a height cap a tall enough
     one pushes its own OK/Cancel buttons off-screen with no way to reach them. The
     dvh line is a progressive enhancement over vh for browsers that support it --
     real mobile browsers resize their address bar in and out of the viewport, which
     vh doesn't track but dvh does, so a dialog sized against vh can still end up
     taller than the actually-visible area. */
  .centred, #tooSmallInner, .modal {
    max-width: 94vw;
    max-height: 90vh;
    max-height: 90dvh;
    overflow-y: auto;
    box-sizing: border-box;
  }

  #statisticsList, .queryList {
    max-width: 100%;
    padding: 0 10px;
    box-sizing: border-box;
  }

  /* #splash alone has a two-column layout (map preview + buttons) built from fixed
     pixel offsets tuned for its 540px desktop width, so capping its own width isn't
     enough -- those children need to become normal flow so they actually reflow
     into the narrower box instead of sitting at their old absolute offsets. */
  #splash {
    width: 94vw;
    max-width: 94vw;
    height: auto;
    max-height: 90vh;
    overflow-y: auto;
    box-sizing: border-box;
  }

  #splashContainer {
    position: static;
    margin-top: 10px;
  }

  #splashContainer canvas {
    max-width: 100%;
    height: auto;
    display: block;
    margin: 0 auto;
  }

  #splashButtonContainer, #splashButtons {
    position: static;
  }

  #splashButtonContainer {
    margin-top: 10px;
  }

  #touchNotice {
    display: block;
  }

  #start {
    max-width: 94vw;
    box-sizing: border-box;
  }

  #loadList, #scenarioList, #fullScenarioList {
    width: 94vw;
    max-width: 94vw;
    box-sizing: border-box;
  }

  /* The Load Game and High Score tables' columns/buttons keep their desktop sizing
     rather than being redesigned to stack -- if they don't fit, the table scrolls
     horizontally inside the dialog instead of clipping. */
  #loadTableEl, #highScoreTableEl {
    max-width: 100%;
    overflow-x: auto;
    display: block;
    white-space: nowrap;
  }

  /* Fixed desktop width, same overflow problem as the dialogs above -- this is often
     the very first thing shown once a game starts (the welcome notification), so it
     matters as much as any dialog. */
  #notifications {
    max-width: 94vw;
    box-sizing: border-box;
  }
}

@media (max-width: 850px) {
  .leftedge {
    left: 20px;
  }

  .rightedge {
    right: 20px;
  }

  #notifications {
    bottom: 40px;
    left: unset;
    right: 0;
    transform: unset;
  }
}

@media (max-width: 750px) {
  .leftedge {
    left: 10px;
  }

  .rightedge {
    right: 10px;
  }
}

@media (max-width: 650px) {
  .leftedge {
    left: 0px;
  }

  .rightedge {
    right: 0px;
  }
}

@media (max-width: 320px) {
  #tooSmall[data-hasscript=true] {
    display: block;
  }
}

/* About/nag page styles */

#aboutMain, #licenseMain {
  position: relative;
  top: 50px;
  margin-bottom: 100px;
}

#aboutContainer, #licenseContainer {
  position: relative;
  left: 50%;
  border: 1px solid black;
  width: 75%;
  line-height: 1.7em;
  text-align: justify;
  transform: translate(-50%);
}

#aboutContainer p, #aboutContainer li, #aboutContainer h3, #licenseContainer p, #licenseContainer li, #licenseContainer h3 {
  margin: 10px;
}

#aboutContainer h3, #licenseContainer h3 {
  margin-top: 20px;
}

#aboutContainer ul {
  margin-left: 50px;
}

#aboutFooter, #licenseFooter {
  position: relative;
}

#appeal {
  border: 1px dotted grey;
  font-size: 12px;
  width: 50%;
  margin: auto;
  line-height: 1.5em;
  background-color: lightyellow;
}

#aboutMain a, #aboutMain a:link, #aboutMain a:visited, #aboutMain a:hover,
#licenseMain a, #licenseMain a:link, #licenseMain a:visited, #licenseMain a:hover,
#nagContainer a, #nagContainer a:link, #nagContainer a:visited, #nagContainer a:hover {
  text-decoration: none;
  color: dodgerblue;
}

#licenseContainer ol {
  margin-left: 3em;
  margin-right: 3em;
}

/* Pre refactoring below */

.info {
  width: 20%;
  text-align: centre;
}

#build {
  position: absolute;
  left: 5px;
  bottom: 5px;
}

#footer {
  height: 26px;
  box-sizing: border-box;
}

#footer #build {
  font-size: 11px;
}

#footerLinks {
  position: absolute;
  right: 5px;
  bottom: 5px;
  font-size: 11px;
}
