/* ==========================================================================
   COLONY LIFE — Experience skin ("petri dish" living-culture)
   A kid-authorable Conway's Game of Life. This file holds ONLY this app's
   styling; cross-app chrome (login/save/share modals) comes from shared.css
   and is re-skinned by overriding shared.css's tokens in :root below.

   Colour discipline (CLAUDE.md): 4 UI colours only, as variables —
     --ink (text/lines) · --ground (page) · --surface (panels/dish) · --accent.
   Neutrals are alpha of --ink, not new colours. The living-cell greens are
   CONTENT colours (--cell-*) and sit outside the 4-colour UI budget.
   Font sizes: exactly 4 (--fs-xl/lg/md/sm), reused everywhere.
   ========================================================================== */

:root {
  /* --- This app's UI palette (4) --- */
  --ink: #2b3a33; /* text, icons, hairlines (via alpha) */
  --ground: #e7ddc6; /* the page — warm agar cream */
  --surface: #f6efdf; /* dish + panels + cards */
  --accent: #e8674c; /* coral — the one featured colour */

  /* Neutrals: alpha of the one ink, so they aren't extra colours. */
  --line: rgba(43, 58, 51, 0.16);
  --line-strong: rgba(43, 58, 51, 0.32);
  --dim: rgba(43, 58, 51, 0.62);
  --faint: rgba(43, 58, 51, 0.42);

  /* --- Living-cell CONTENT colours (unbounded by the UI budget) --- */
  /* Age is carried by HUE, not brightness: green (148°) swings round to teal
     (196°) while lightness barely moves (L* 71 -> 61, where it used to run
     77 -> 36). The eye reads a lightness edge as the boundary between two
     objects and a hue edge as a marking on one surface — so the old palette
     cut a fused colony in half wherever a newborn sat beside an elder, no
     matter how continuous the geometry underneath was. */
  --cell-young: #6dbf80; /* fresh green — a newly born cell */
  --cell-mid: #2bb494; /* green-teal — a settled cell */
  --cell-old: #28a3a2; /* teal — a long-lived cell */

  /* --- Type --- */
  --font-display: "Fredoka", system-ui, sans-serif;
  --font-body: "Nunito", system-ui, sans-serif;
  --fs-xl: 28px;
  --fs-lg: 17px;
  --fs-md: 14px;
  --fs-sm: 11px;

  /* ---- Re-skin the hosted shared.css chrome ----
     shared.css's modals/buttons read these token names. Remapping them onto
     the petri palette re-skins the login/save/share modals to match — no
     copies, no edits to the hosted file. */
  --font: var(--font-body);
  --white: var(--surface); /* modal + input surfaces */
  --black: var(--ink); /* modal text */
  --bg: var(--ground); /* load-card rows */
  --primary: var(--ink); /* modal bars, borders, active tabs */
  --secondary: var(--accent); /* primary-button fill */
  --border: var(--line-strong);
}

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

/* Accessibility: honour the OS "reduce motion" setting. This is the standard
   global opt-out pattern, not a BEM component override — it neutralises every
   transition/animation at once for users who ask for calm. (The canvas itself
   also slows its generations under this setting; see effectiveInterval() in
   app.js.) The !important is intentional and scoped to this media query. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    scroll-behavior: auto !important;
  }
}

/* Override shared.css's flex-column body: this app owns the full viewport.
   The app has a fixed min-width (see .colony__app) so its side-by-side layout
   never collapses into overlap on smaller laptops; below that width the whole
   thing scrolls horizontally instead. */
.colony {
  height: 100vh;
  overflow-x: auto;
  overflow-y: hidden;
  padding: 0;
  display: block;
  font-family: var(--font-body);
  font-size: var(--fs-md);
  color: var(--ink);
  /* Agar ground: warm cream with a faint culture bloom in the centre. */
  background:
    radial-gradient(
      120% 90% at 50% 8%,
      rgba(47, 160, 136, 0.08),
      transparent 60%
    ),
    var(--ground);
}

.colony__app {
  height: 100vh;
  /* Floor the layout so it stays side-by-side and legible on a 14" laptop
     (~1280 CSS px at typical scaling); narrower viewports scroll instead. */
  min-width: 1180px;
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 14px 20px 18px;
}

/* ==========================================================================
   TOP BAR
   ========================================================================== */

.topbar {
  display: flex;
  align-items: center;
  gap: 18px;
  flex-shrink: 0;
}

.topbar__brand {
  display: flex;
  align-items: center;
  gap: 9px;
  flex-shrink: 0;
}

/* A little living spore next to the wordmark — the app's mascot cell. */
.topbar__spore {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--cell-mid);
  box-shadow: 0 0 0 4px rgba(47, 160, 136, 0.22);
}

.topbar__title {
  font-family: var(--font-display);
  font-size: var(--fs-xl);
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--ink);
}

.topbar__title b {
  color: var(--accent);
}

.topbar__saves {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}

/* Playback controls sit in their own zone that grows to fill the gap between
   the saves cluster and the account cluster, so the buttons ride the centre of
   the bar above the dish. */
.topbar__transport {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 1;
  justify-content: center;
}

.topbar__btn {
  font-family: var(--font-display);
  font-size: var(--fs-md);
  font-weight: 500;
  color: var(--ink);
  background: var(--surface);
  border: 1.5px solid var(--line);
  border-radius: 11px;
  padding: 7px 15px;
  cursor: pointer;
  transition:
    background 0.15s,
    border-color 0.15s;
}

.topbar__btn:hover {
  border-color: var(--line-strong);
  background: #fbf6ea;
}

.topbar__name {
  font-size: var(--fs-md);
  color: var(--dim);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  padding-left: 4px;
}

.topbar__account {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-shrink: 0;
}

.topbar__greeting {
  font-size: var(--fs-md);
  color: var(--dim);
}

.topbar__greeting span {
  color: var(--ink);
  font-weight: 700;
}

.topbar__signout {
  font-family: var(--font-body);
  font-size: var(--fs-sm);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--dim);
  background: none;
  border: none;
  cursor: pointer;
  padding: 4px;
}

.topbar__signout:hover {
  color: var(--ink);
}

/* Class-code widget: auth.js toggles .active on #classCodeWidget and fills the
   badge; visibility is driven by that class (mirrors the sibling apps). */
.topbar__class {
  display: flex;
  align-items: center;
  gap: 6px;
}

.topbar__class-input {
  font-family: var(--font-body);
  font-size: var(--fs-sm);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  width: 96px;
  padding: 7px 11px;
  border: 1.5px solid var(--line);
  border-radius: 10px;
  background: var(--surface);
  color: var(--ink);
  outline: none;
}

.topbar__class-input::placeholder {
  color: var(--faint);
  text-transform: none;
  font-weight: 600;
  letter-spacing: 0;
}

.topbar__class-input:focus {
  border-color: var(--accent);
}

.topbar__class-badge {
  display: none;
  font-size: var(--fs-sm);
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: #fff;
  background: var(--cell-mid);
  padding: 6px 11px;
  border-radius: 10px;
}

.topbar__class-leave {
  display: none;
  background: none;
  border: none;
  color: var(--dim);
  font-size: var(--fs-lg);
  line-height: 1;
  cursor: pointer;
  padding: 0 2px;
}

.topbar__class-leave:hover {
  color: var(--ink);
}

.topbar__class.active .topbar__class-input {
  display: none;
}

.topbar__class.active .topbar__class-badge {
  display: inline-flex;
  align-items: center;
}

.topbar__class.active .topbar__class-leave {
  display: inline-flex;
}

/* ==========================================================================
   STAGE + DISH
   ========================================================================== */

/* Workspace: dish (2/3) beside the console rail (1/3), both full height. This
   is what keeps the dish close to square instead of wide-and-short. */
.workspace {
  flex: 1;
  min-height: 0;
  display: flex;
  gap: 16px;
}

.stage {
  flex: 1;
  min-width: 0;
  min-height: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* The petri dish: a rounded surface with a faint rim + culture rings. The
   canvas is centred inside it. */
.dish {
  position: relative;
  flex: 1;
  height: 100%;
  /* min-width/height:0 let the dish shrink below the canvas's fixed pixel size
     when the window shrinks; without min-width:0 the canvas holds the column
     open and layoutCanvas() reads a stale (too-wide) width, so the board only
     re-fit on reload. */
  min-width: 0;
  min-height: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 18px;
  border-radius: 26px;
  background:
    radial-gradient(
      circle at 50% 46%,
      rgba(47, 160, 136, 0.06),
      transparent 62%
    ),
    var(--surface);
  border: 1.5px solid var(--line);
  box-shadow:
    inset 0 0 0 8px rgba(255, 255, 255, 0.35),
    inset 0 0 60px rgba(43, 58, 51, 0.06),
    0 10px 30px rgba(43, 58, 51, 0.08);
}

/* The grid canvas — the microscope's field of view onto the 128x128 world.
   JS sizes it to fill the dish; the camera decides which part of the world is
   showing, and the cell ruling + world edge are drawn in-canvas so they track
   the zoom. touch-action:none so a drag draws instead of scrolling the page. */
.dish__grid {
  display: block;
  border-radius: 8px;
  background-color: rgba(43, 58, 51, 0.02);
  cursor: crosshair;
  touch-action: none;
}

.readout {
  position: absolute;
  top: 26px;
  left: 30px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
  pointer-events: none;
}

.readout__row {
  display: flex;
  gap: 8px;
}

.readout__chip {
  font-family: var(--font-body);
  font-size: var(--fs-sm);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--dim);
  background: rgba(246, 239, 223, 0.82);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 5px 11px;
}

.readout__chip b {
  color: var(--ink);
  font-size: var(--fs-md);
}

.dish__hint {
  position: absolute;
  bottom: 24px;
  left: 50%;
  transform: translateX(-50%);
  font-size: var(--fs-md);
  color: var(--faint);
  pointer-events: none;
  transition: opacity 0.3s;
}

/* Hide the "draw here" hint once the colony is running or has cells. */
.dish--busy .dish__hint {
  opacity: 0;
}

/* Ghost-mode legend — only shown while Ghost is on. Swatches echo the overlay:
   dashed green ring = about to be born, solid coral ring = about to die. */
.ghostkey {
  position: absolute;
  top: 26px;
  right: 30px;
  display: none;
  gap: 14px;
  pointer-events: none;
}

.ghostkey--on {
  display: flex;
}

.ghostkey__item {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: var(--fs-sm);
  font-weight: 700;
  color: var(--dim);
}

.ghostkey__item::before {
  content: "";
  width: 13px;
  height: 13px;
  border-radius: 50%;
  flex-shrink: 0;
}

.ghostkey__item--born::before {
  border: 2px dashed var(--cell-mid);
}

.ghostkey__item--die::before {
  border: 2px solid var(--accent);
}

/* Age legend — bottom-left of the dish. The gradient bar mirrors how the
   engine colours a cell by how long it has been alive: mint (newborn) ->
   teal -> deep green (long-lived). */
.agekey {
  position: absolute;
  bottom: 24px;
  left: 30px;
  display: flex;
  align-items: center;
  gap: 8px;
  pointer-events: none;
}

.agekey__label {
  font-size: var(--fs-sm);
  font-weight: 700;
  color: var(--dim);
}

.agekey__bar {
  width: 84px;
  height: 9px;
  border-radius: 999px;
  background: linear-gradient(
    90deg,
    var(--cell-young),
    var(--cell-mid),
    var(--cell-old)
  );
}

/* --- Camera controls (bottom-right of the dish) ------------------------
   A 3x3 d-pad with Fit at its centre, plus a zoom stepper below. Scroll and
   pinch already do this; these are the visible, tappable path for touch,
   wheel-less mice, and teacher demos. */
.navpad {
  position: absolute;
  right: 28px;
  bottom: 74px;
  display: grid;
  grid-template-columns: repeat(3, 30px);
  grid-template-rows: repeat(3, 30px);
  gap: 3px;
}

.navpad__btn {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--fs-sm);
  color: var(--dim);
  background: rgba(246, 239, 223, 0.9);
  border: 1.5px solid var(--line);
  border-radius: 9px;
  cursor: pointer;
  padding: 0;
  transition:
    background 0.12s,
    color 0.12s;
}

.navpad__btn:hover {
  background: var(--surface);
  color: var(--ink);
  border-color: var(--line-strong);
}

.navpad__btn--up {
  grid-column: 2;
  grid-row: 1;
}

.navpad__btn--left {
  grid-column: 1;
  grid-row: 2;
}

.navpad__btn--right {
  grid-column: 3;
  grid-row: 2;
}

.navpad__btn--down {
  grid-column: 2;
  grid-row: 3;
}

/* Center sits in the middle of the d-pad — "take me to the middle of the
   canvas" (at the current zoom). A target glyph: the label wouldn't fit a
   30px key. */
.navpad__center {
  grid-column: 2;
  grid-row: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--fs-lg);
  line-height: 1;
  color: #fff;
  background: var(--cell-mid);
  border: 1.5px solid var(--cell-mid);
  border-radius: 9px;
  cursor: pointer;
  padding: 0;
}

.navpad__center:hover {
  filter: brightness(1.08);
}

.zoomer {
  position: absolute;
  right: 28px;
  bottom: 28px;
  display: flex;
  gap: 3px;
}

.zoomer__btn {
  width: 30px;
  height: 30px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-display);
  font-size: var(--fs-lg);
  font-weight: 600;
  color: var(--dim);
  background: rgba(246, 239, 223, 0.9);
  border: 1.5px solid var(--line);
  border-radius: 9px;
  cursor: pointer;
  padding: 0;
  line-height: 1;
}

.zoomer__btn:hover {
  background: var(--surface);
  color: var(--ink);
  border-color: var(--line-strong);
}

/* Fit lives beside the zoom keys: a labelled key (wider than the 30px squares),
   frames the whole colony. */
.zoomer__btn--fit {
  width: auto;
  padding: 0 10px;
  font-size: var(--fs-sm);
  font-weight: 700;
}

/* ==========================================================================
   CONSOLE
   ========================================================================== */

/* The console is a full-height vertical rail (~1/3 of the workspace). It stacks
   three titled sections top-to-bottom: Rules -> Options -> Starters. Scrolls if
   a short viewport can't hold it all. */
.console {
  flex: 0 0 300px;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 22px;
  padding: 20px;
  border-radius: 20px;
  background: var(--surface);
  border: 1.5px solid var(--line);
  overflow-y: auto;
}

/* A titled group. The title labels the controls beneath it. */
.console__section {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 12px;
}

.console__title {
  font-family: var(--font-display);
  font-size: var(--fs-md);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--dim);
}


/* --- Speed pill: a −/value/+ stepper floating in the dish's top-right
   corner, styled as a sibling of the readout chips opposite it. --- */

.speed {
  position: absolute;
  top: 26px;
  right: 30px;
  display: flex;
  align-items: center;
  gap: 6px;
  background: rgba(246, 239, 223, 0.82);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 4px 6px;
}

.speed__label {
  font-family: var(--font-body);
  font-size: var(--fs-sm);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--dim);
  padding: 0 4px;
}

.speed__btn {
  width: 24px;
  height: 24px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-display);
  font-size: var(--fs-md);
  font-weight: 700;
  color: var(--ink);
  background: var(--surface);
  border: 1.5px solid var(--line);
  border-radius: 50%;
  cursor: pointer;
  transition:
    background 0.12s,
    border-color 0.12s,
    color 0.12s;
}

.speed__btn:hover {
  border-color: var(--ink);
  background: #fbf6ea;
}

.speed__btn:disabled {
  opacity: 0.35;
  cursor: default;
}

.speed__value {
  min-width: 16px;
  text-align: center;
  font-family: var(--font-display);
  font-size: var(--fs-md);
  font-weight: 700;
  color: var(--ink);
}

/* --- Custom Rules toggle grid ------------------------------------------ */

.custom {
  display: flex;
  flex-direction: column;
  gap: 12px;
  width: 100%;
}

.custom__row {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 7px;
}

/* The rule reads as a sentence with the toggle grid slotted in — the same
   `if ⟨ ⟩ then` shape kids already know from block coding. */
.custom__clause {
  font-size: var(--fs-md);
  font-weight: 600;
  color: var(--ink);
  text-align: left;
  line-height: 1.3;
}

/* The cell's state (empty / living) is the pivot of the conditional — the same
   neighbour count branches on it — so each phrase is bolded. "living cell"
   takes a settled-cell green so it reads as the thing on the grid; "empty cell"
   stays ink, just heavier. */
.custom__state {
  font-weight: 800;
}

.custom__state--empty {
  color: var(--ink);
}

.custom__state--living {
  color: var(--cell-mid);
}

/* The 1-8 neighbour counts as two even rows (1-4 / 5-8) — legible in the
   narrow console rail. */
.seg {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 5px;
}

.seg__btn {
  font-family: var(--font-display);
  font-size: var(--fs-md);
  font-weight: 600;
  color: var(--dim);
  background: transparent;
  border: 1.5px solid var(--line);
  border-radius: 9px;
  padding: 8px 0;
  cursor: pointer;
  transition:
    background 0.12s,
    border-color 0.12s,
    color 0.12s;
}

.seg__btn:hover {
  border-color: var(--line-strong);
  color: var(--ink);
}

.seg__btn--on {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}

/* --- Board actions (Options) + Starters -------------------------------- */

/* Run/step playback moved to the topbar; the rail keeps the board actions as
   a single row of three: [Reset · Clear · Ghost]. */
.transport {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
}

.ctrl {
  font-family: var(--font-display);
  font-size: var(--fs-md);
  font-weight: 500;
  color: var(--ink);
  background: var(--surface);
  border: 1.5px solid var(--line-strong);
  border-radius: 11px;
  padding: 9px 18px;
  cursor: pointer;
  transition:
    background 0.15s,
    border-color 0.15s,
    color 0.15s;
}

.ctrl:hover {
  background: #fbf6ea;
  border-color: var(--ink);
}

.ctrl--small {
  padding: 7px 12px;
  font-size: var(--fs-sm);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  border-color: var(--line);
}

.ctrl--play {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}

.ctrl--play:hover {
  background: #e0543a;
  border-color: #e0543a;
  color: #fff;
}

/* Back / Forward stepping — a touch tighter than the primary controls. */
.ctrl--step {
  padding: 9px 14px;
  border-color: var(--line);
}

/* Restore-the-classic-rule button under the editor — a quiet secondary action.
   Disables itself (via the shared .ctrl:disabled) once the rule is Conway. */
.ctrl--reset {
  padding: 8px 12px;
  font-size: var(--fs-sm);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--dim);
  border-color: var(--line);
}

.ctrl:disabled {
  opacity: 0.4;
  cursor: default;
}

.ctrl:disabled:hover {
  background: var(--surface);
  border-color: var(--line);
}

/* On/off toggle (Ghost). --on is the lit state. */
.ctrl--toggle {
  border-color: var(--line);
}

.ctrl--on,
.ctrl--on:hover {
  background: var(--cell-mid);
  border-color: var(--cell-mid);
  color: #fff;
}

.starters {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

/* --- Live status pill: what the colony is doing, under the readout. A small
   coloured dot carries the state at a glance; the text spells it out. --- */
.status {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 4px 11px;
  border-radius: 999px;
  background: rgba(246, 239, 223, 0.82);
  border: 1px solid var(--line);
  font-family: var(--font-body);
  font-size: var(--fs-sm);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--dim);
}

.status[hidden] {
  display: none;
}

.status__dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--dim);
  flex-shrink: 0;
}

/* Dot colours are content greens/coral, not UI tokens. */
.status--growing .status__dot {
  background: var(--cell-young);
}
.status--shrinking .status__dot {
  background: var(--accent);
}
.status--steady .status__dot {
  background: var(--cell-old);
}
.status--settled .status__dot {
  background: var(--ink);
}
.status--looping .status__dot {
  background: var(--cell-mid);
}
.status--died .status__dot {
  background: var(--line-strong);
}

/* ==========================================================================
   TOAST (own, small)
   ========================================================================== */

.toast {
  position: fixed;
  bottom: 26px;
  left: 50%;
  transform: translateX(-50%) translateY(12px);
  background: var(--ink);
  color: var(--surface);
  font-family: var(--font-body);
  font-size: var(--fs-md);
  font-weight: 700;
  padding: 11px 20px;
  border-radius: 12px;
  box-shadow: 0 8px 24px rgba(43, 58, 51, 0.28);
  opacity: 0;
  transition:
    opacity 0.25s,
    transform 0.25s;
  z-index: 300;
  pointer-events: none;
}

.toast--visible {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}
