/* Spin Up a Storm — MS-ESS2-5
 *
 * A dark shell on purpose. The scene is a backlit storm sky, and the palette is sampled
 * from the reference photographs the art was built to: desaturated blue-greys with one
 * warm strip at the horizon. A light cream page around that would fight the picture, so
 * the chrome recedes into the same storm palette instead.
 *
 * NOT in the machine-verifiable-checks exceptions ledger, which is what COLOR-3 asks for and
 * what this comment used to claim. Ben declined the entry on 2026-08-21 ("ignore the color-3
 * ledger"), so the divergence is deliberate and unrecorded, and a reviewer will see COLOR-3
 * reported as drift. Saying so here rather than asserting a record that does not exist.
 */

:root {
  --bg-top: #1b222c;
  --bg-bottom: #232c38;
  --panel: rgba(30, 38, 48, 0.96);
  --dock: rgba(37, 47, 59, 0.95);
  --border: rgba(150, 176, 200, 0.26);
  --ink: #dfe7ef;
  --muted: #93a7bc;
  /* Tuva green under the boilerplate's names (COLOR-1, COLOR-4). This sim's chrome is the dark
     storm palette below, so the green is used only where a sim's own brand colour belongs: the
     standard tag in the info dialog, which sits on the dialog's light ground. */
  --accent: #004445;
  --accent-light: #006668;
  /* The warm accent is the horizon glow out of the photographs, used sparingly: the hour
     marker and the storm callout. Against the dark panel it reads 8.9:1. */
  --glow: #e9dfae;
  --glow-deep: #c9a86a;
  /* The boilerplate's canvas-focus blue, which COLOR-1 names as the alternative to the gold
     glow. It was #63b3ff here, fifteen units off, which is close enough that it was meant
     to be this and drifted rather than being chosen. */
  --focus: #60a5fa;
  --focus-glow: #ffd166;
  --shadow: 0 16px 40px rgba(0, 0, 0, 0.55);
  --radius-xl: 24px;
  --radius-lg: 18px;
  --radius-md: 14px;
  --radius-sm: 10px;
}

*, *::before, *::after { box-sizing: border-box; }

html, body {
  width: 100%;
  height: 100%;
  margin: 0;
  overflow: hidden;
}

body {
  display: flex;
  flex-direction: column;
  background:
    radial-gradient(ellipse at 50% 0%, rgba(200, 170, 110, 0.10), transparent 42%),
    radial-gradient(ellipse at 80% 100%, rgba(20, 40, 60, 0.35), transparent 48%),
    linear-gradient(180deg, var(--bg-top) 0%, var(--bg-bottom) 100%);
  color: var(--ink);
  font-family: Roboto, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

button, input { font: inherit; }
button { cursor: pointer; }

button:focus-visible,
input:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 2px;
  box-shadow: 0 0 0 5px var(--focus-glow);
}

/* FULL BLEED. No page padding, no panel inset, no gap, no border, no rounded corners.
   The scene is the sim, so it gets every pixel between the toolbar and the readings bar —
   the framing chrome was costing about ninety pixels of sky and giving nothing back. */
#sim-app {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

#stage-panel {
  flex: 1;
  min-height: 0;
  display: grid;
  grid-template-rows: minmax(0, 1fr) auto;
  background: var(--panel);
  overflow: hidden;
  /* Positioned so #panel-tip, which is a child of this and not of the canvas frame, has a
     containing block the JS can actually measure against. Without it the tooltip's offsets
     were resolved against the body while the arithmetic was done against the canvas frame,
     which is why clamping its top did nothing and the shear tooltip clipped off the top. */
  position: relative;
}

/* The CSS gradient behind the canvas is a fallback only — the sim draws its own sky
   dome, so this is what shows for the instant before the first frame lands. Matched to
   the sky the renderer paints so the handover is invisible. */
#canvas-frame {
  position: relative;
  min-height: 0;
  overflow: hidden;
  background: linear-gradient(180deg, #2c4a70 0%, #4a5c6e 58%, #b8b394 88%, #2c3626 100%);
}

#sim-canvas {
  display: block;
  width: 100%;
  height: 100%;
  touch-action: none;
}

/* The canvas is the sim's main control surface — tabindex=0, role=application, and the
   three ingredient keys plus the release key all live on it — so it has to show when it
   holds focus. Drawn inside the frame in a blue that survives both the dark cloud deck
   and the bright horizon strip. */
#sim-canvas:focus-visible {
  /* Inset, since there is no frame around the canvas any more for an outline to sit on. */
  outline: 3px solid var(--focus);
  outline-offset: -3px;
  box-shadow: inset 0 0 0 7px rgba(99, 179, 255, 0.30);
}

/* ── Hour track ──────────────────────────────────────────────────────────── */

#hour-track {
  position: absolute;
  left: 18px;
  right: 18px;
  bottom: 14px;
  pointer-events: none;
}

#hour-head {
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin-bottom: 6px;
}

#hour-head strong {
  font-size: 18px;
  font-weight: 700;
  color: #ffffff;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.75);
}

#hour-head span {
  font-size: 14px;
  font-weight: 500;
  color: #e4ecf3;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.75);
}

#hour-bar {
  position: relative;
  height: 22px;
  border-radius: 11px;
  background: rgba(10, 16, 22, 0.60);
  border: 1px solid rgba(200, 220, 235, 0.34);
  overflow: hidden;
}

#hour-fill {
  position: absolute;
  inset: 0 auto 0 0;
  width: 0%;
  /* Darkened from the obvious mid-blue/gold pair so the MIDDAY / AFTERNOON / EVENING labels
     printed over it still clear WCAG AA. At the lighter tones the 11.5px white text measured
     4.0:1 on the blue end and 2.6:1 on the gold — the text-shadow makes that look fine and
     does nothing for the ratio. These read 6.4:1 and 4.8:1 and still say dawn-to-dusk. */
  background: linear-gradient(90deg, rgba(58, 96, 138, 0.92), rgba(146, 108, 40, 0.92));
  transition: width 0.1s linear;
}

#hour-marker {
  position: absolute;
  top: -3px;
  bottom: -3px;
  left: 0%;
  width: 3px;
  border-radius: 2px;
  background: var(--focus-glow);
  box-shadow: 0 0 6px rgba(255, 209, 102, 0.9);
  transition: left 0.1s linear;
}

.hour-band {
  position: absolute;
  top: 0;
  bottom: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border-right: 1px solid rgba(200, 220, 235, 0.26);
}

.hour-band:last-child { border-right: none; }

.hour-band span {
  /* 12px floor on every piece of content text in the sim (WCAG 2.1 and the house's own
     responsive-layout-playbook). The band labels hide below 640px rather than shrinking. */
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.4px;
  text-transform: uppercase;
  color: #eff5fa;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.8);
  white-space: nowrap;
  overflow: hidden;
  padding: 0 4px;
}

@media (max-width: 640px) {
  .hour-band span { display: none; }
}

/* ── The air-column cut-away ─────────────────────────────────────────────── */

/* Sits over the left of the scene rather than beside it in the layout, so the storm keeps
   the whole frame to build in and the panel can bow out of the way once it has done its
   job. Bottom clears the hour track. */
#profile-canvas {
  position: absolute;
  left: 8px;
  top: 8px;
  /* An explicit height, not top plus bottom. A canvas is a replaced element with an
     intrinsic size, so height:auto resolves to its 300x150 default rather than stretching
     between the two offsets — which left the cross-section a squat box with the three layers
     crushed into strips. */
  height: calc(100% - 68px);
  /* Wide enough for three labelled layers, capped so it never eats the storm. The floor of
     132px is what still holds a legible height scale and the short-form labels at the
     narrowest supported frame. */
  /* Floor raised from 132px: the labels inside are 12px now, and 132 could not hold them.
     Narrow frames get shorter strings, never smaller type. */
  width: clamp(154px, 31%, 330px);
  border-radius: 14px;
  border: 1px solid rgba(170, 200, 224, 0.32);
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.45);
  background: rgba(20, 27, 37, 0.55);
  touch-action: none;
  /* Driven by JS as the event runs; the transition covers the reset snap back. */
  transition: transform 0.25s ease, opacity 0.25s ease;
}

#profile-canvas:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 2px;
}

/* The keyboard layer sits exactly on top of the cut-away and holds one tab stop per handle.
   Every geometry value is duplicated from #profile-canvas above, narrow override included,
   because the two boxes have to coincide for the stops to land on the handles they stand for.
   pointer-events: none throughout, so a mouse still talks to the canvas and these are reached
   by Tab alone. */
#kb-layer {
  position: absolute;
  /* Above the cut-away it overlays, because it comes EARLIER in the document than that canvas
     (for tab order) and would otherwise paint underneath it, hiding the focus ring. */
  z-index: 2;
  left: 8px;
  top: 8px;
  height: calc(100% - 68px);
  width: clamp(154px, 31%, 330px);
  pointer-events: none;
}

.kb-stop {
  position: absolute;
  pointer-events: none;
  background: transparent;
  border: 0;
  border-radius: 8px;
  padding: 0;
}

/* :focus, not :focus-visible. These are only ever reached by keyboard, so there is no
   mouse-click case to suppress, and a div[tabindex] focused programmatically does not reliably
   match :focus-visible. */
.kb-stop:focus {
  outline: 3px solid var(--focus);
  outline-offset: 1px;
}

@media (max-width: 520px) {
  /* At the narrow end the panel takes more of the frame, because a cross-section that
     cannot be read is worse than a storm that is partly covered — and it slides away
     within the first three hours anyway. */
  #profile-canvas { width: clamp(154px, 44%, 330px); left: 8px; top: 8px; height: calc(100% - 66px); }
  #kb-layer      { width: clamp(154px, 44%, 330px); left: 8px; top: 8px; height: calc(100% - 66px); }
}

/* ── Cut-away tooltip ────────────────────────────────────────────────────── */

#panel-tip {
  position: absolute;
  z-index: 40;
  max-width: 220px;
  padding: 8px 11px;
  border-radius: var(--radius-sm);
  background: rgba(7, 13, 20, 0.94);
  border: 1px solid var(--glow-deep);
  color: #eaf2f9;
  font-size: 12.5px;
  line-height: 1.4;
  box-shadow: 0 8px 22px rgba(0, 0, 0, 0.55);
  pointer-events: none;
  opacity: 0;
  transform: translateY(-50%) translateX(-6px);
  transition: opacity 0.12s ease, transform 0.12s ease;
}

#panel-tip.show {
  opacity: 1;
  transform: translateY(-50%) translateX(0);
}

@media (max-width: 620px) {
  /* No room beside the column at this width, so the tip sits over the scene instead. */
  #panel-tip { max-width: 46%; }
}

/* ── Storm callout ───────────────────────────────────────────────────────── */

/* What the sky has become, named at the moment it becomes it. Top-centre, out of the way
   of the horizon where all three controls live, and out of the way of the storm itself,
   which builds to the right of centre. */
#stage-callout {
  position: absolute;
  top: 14px;
  left: 50%;
  transform: translateX(-50%);
  display: none;
  align-items: center;
  gap: 8px;
  padding: 6px 16px;
  border-radius: 999px;
  background: rgba(10, 16, 22, 0.82);
  border: 1px solid var(--glow-deep);
  color: var(--glow);
  font-size: 16px;
  font-weight: 700;
  letter-spacing: 0.3px;
  white-space: nowrap;
  pointer-events: none;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.7);
}

#stage-callout.show { display: flex; }

/* ── Readings dock ───────────────────────────────────────────────────────── */

/* The five quantities MS-ESS2-5's clarification statement names, read at one fixed place.
   Deliberately an instrument strip and not a dashboard: no charts, no history, no
   duplicates of anything the toolbar or the Data Tools already give. The numbers are here
   because the standard is about weather changing at a fixed location over time, and
   without them the sky is a picture rather than a measurement. */
/* ONE COMPACT ROW, caption included. It used to be a caption on its own line above a grid
   of tall cards inside a bordered, rounded, padded dock — about ninety pixels for five
   numbers. Now the caption sits at the left of the same row and everything is tighter, which
   is roughly forty pixels back to the sky for no loss of meaning. */
#readings-dock {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 5px 8px;
  background: var(--dock);
  border-top: 1px solid var(--border);
}

.readings-caption {
  flex: 0 0 auto;
  /* Two lines of "Weather station" at 12px uppercase. "Surface weather station" wrapped to
     three and made the whole bar taller than the numbers in it needed. */
  /* Sized for the LONGEST translation, not for the English. "ESTACIÓN METEOROLÓGICA" is nine
     characters longer than "WEATHER STATION" and at 84px it overflowed into the temperature
     card. min-content on the width lets the box take what the words need, and the cap keeps a
     hypothetical very long one from eating a reading. */
  width: min-content;
  min-width: 84px;
  max-width: 128px;
  overflow-wrap: break-word;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.4px;
  line-height: 1.15;
  text-transform: uppercase;
  color: var(--muted);
}

/* A grid rather than a wrapping flex row. At 350px the five cards cannot sit on one line,
   and a flex row wraps them into a ragged 3-then-2 with the stragglers stretched to double
   width. auto-fit keeps every card the same size however many rows it takes. */
.readings-row {
  flex: 1;
  min-width: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(88px, 1fr));
  gap: 6px;
}

.reading {
  display: flex;
  flex-direction: column;
  gap: 0;
  padding: 3px 8px 4px;
  border-radius: 9px;
  background: rgba(10, 16, 22, 0.55);
  border: 1px solid rgba(120, 155, 185, 0.18);
}

.reading-label {
  font-size: 12px;
  font-weight: 500;
  line-height: 1.25;
  color: var(--muted);
  white-space: nowrap;
}

.reading-value {
  font-size: 17px;
  font-weight: 700;
  line-height: 1.2;
  color: #f2f8ff;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* A reading that is moving fast gets the warm accent, so the moment the front arrives is
   visible in the numbers and not only in the sky. */
.reading.moving {
  border-color: var(--glow-deep);
  background: rgba(50, 40, 22, 0.55);
}

.reading.moving .reading-value { color: var(--glow); }

@media (max-width: 720px) {
  /* The caption is the first thing to go: at this width the five numbers need the room more
     than they need to be told where they came from, and the info dialog says it anyway. */
  #readings-dock { padding: 4px 6px; gap: 6px; }
  .readings-caption { display: none; }
  .readings-row { grid-template-columns: repeat(auto-fit, minmax(80px, 1fr)); gap: 5px; }
  /* Five cards will not fit one row at the narrow end, and auto-fit leaves the fifth sitting
     alone at one column's width with a gap beside it, which reads as a mistake. Spanning the
     row makes the wrap look deliberate, and shortening the labels instead would either clip
     them or drop below the 12px floor. */
  .readings-row .reading:last-child { grid-column: 1 / -1; }
  .reading { padding: 3px 6px 4px; }
  .reading-value { font-size: 16px; }
}

/* ── Sound toggle ────────────────────────────────────────────────────────── */

/* Injected into the shared toolbar, so it wears .ttb-btn and inherits the toolbar's own
   hover and focus treatment. All this adds is which of the two icons shows and a lit state
   when the sound is on. */
#audio-toggle-btn .audio-icon {
  width: 20px;
  height: 20px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
}

#audio-toggle-btn .audio-icon-on  { display: none; }
#audio-toggle-btn .audio-icon-off { display: block; }
#audio-toggle-btn.audio-on .audio-icon-off { display: none; }
#audio-toggle-btn.audio-on .audio-icon-on  { display: block; }

#audio-toggle-btn.audio-on {
  background: #efe3c6 !important;
  border-color: var(--glow-deep) !important;
  color: #4a3a16 !important;
}

/* ── Info dialog ─────────────────────────────────────────────────────────── */

/* Verbatim copy of the boilerplate block, not a per-sim restyle (machine-verifiable
   COLOR-2 / TYPO-4). The dialog is Tuva Sims chrome, not the sim's: the shared toolbar
   injects #info-dialog-scoped rules after this stylesheet and wins on specificity, so a
   per-sim recolor here would be dead weight that only drifts. */
#info-dialog {
  border: none;
  border-radius: 12px;
  padding: 0;
  width: min(560px, calc(100vw - 32px));
  max-height: calc(100vh - 80px);
  overflow: hidden;
  margin: auto;
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.22);
  background: #ffffff;
}

#info-dialog::backdrop {
  background: rgba(30, 16, 48, 0.55);
}

.dialog-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 16px;
  background: #5c4738;
  color: #ffffff;
}

.dialog-header h2 {
  font-size: 16px;
  font-weight: 600;
}

#dialog-close-btn {
  background: transparent;
  border: none;
  color: #ffffff;
  font-size: 18px;
  cursor: pointer;
  width: 32px;
  height: 32px;
  border-radius: 6px;
  display: flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
}

#dialog-close-btn:hover {
  background: rgba(255, 255, 255, 0.15);
}

#dialog-close-btn:focus-visible {
  outline: 3px solid var(--focus-glow);
  outline-offset: 2px;
}

.dialog-tabs {
  display: flex;
  border-bottom: 2px solid #E5EAE9;
  background: #F8FBFB;
}

.tab-btn {
  flex: 1;
  background: none;
  border: none;
  border-bottom: 3px solid transparent;
  margin-bottom: -2px;
  padding: 10px 0;
  font-family: Roboto, Arial, sans-serif;
  font-size: 14px;
  font-weight: 600;
  color: #5A6E6E;
  cursor: pointer;
  transition: color 0.15s, border-color 0.15s;
}

.tab-btn:hover {
  color: #5c4738;
}

.tab-btn.active,
.tab-btn[aria-selected="true"] {
  color: #5c4738;
  border-bottom-color: #5c4738;
}

.tab-btn:focus-visible {
  outline: 3px solid var(--focus-glow);
  outline-offset: -3px;
}

.dialog-body {
  padding: 16px 20px;
  overflow-y: auto;
  max-height: calc(100vh - 240px);
  color: #2C3E3E;
  font-size: 14px;
  line-height: 1.55;
}

.dialog-body h3 {
  font-size: 14px;
  font-weight: 600;
  color: #5c4738;
  margin-top: 14px;
  margin-bottom: 6px;
}

.dialog-body h3:first-child {
  margin-top: 0;
}

.dialog-body p {
  margin-bottom: 8px;
}

.dialog-body ul,
.dialog-body ol {
  padding-left: 20px;
  margin-bottom: 8px;
}

.dialog-body li {
  margin-bottom: 4px;
}

/* Structure only, no colour. The shared toolbar owns every colour inside the dialog and injects
   its rules after this stylesheet (COLOR-2), so a colour here would either lose or fight it.
   These three properties are the affordance for the collapsible Model details block that INFO-1
   requires, and the boilerplate supplies nothing for it. */
#info-dialog summary {
  cursor: pointer;
  font-weight: 600;
  margin-top: 12px;
}

.standard-tag {
  display: inline-block;
  padding: 1px 7px;
  margin-right: 5px;
  border-radius: 5px;
  background: #e7f1f0;
  color: var(--accent);
  font-weight: 600;
  font-size: 13px;
}
