/* Deep Time */

:root {
  --accent:       #004445;
  --accent-light: #006668;
  --focus-glow:   #FFD166;
  --rail:         42px;   /* the sliver a way out is drawn in */
  /* The field kit's width. Everything that has to clear the kit reads it from
     here, so the kit can be resized in one place. */
  --kit:         368px;
  --slide:        420ms;  /* how long the workbench takes to arrive */
  --ink:          #2C3E3E;
  --muted:        #5A6E6E;

  /* Scene chrome — a cool dark shell, so the pale desert ground and the black
     sky above the globe both read as daylight rather than as a bright page.
     Recorded as a COLOR-3 exception. */
  --shell-top:    #1b2530;
  --shell-bottom: #0b0e12;
  --panel:        rgba(24, 32, 41, 0.96);
  --card:         rgba(38, 50, 62, 0.72);
  --border:       rgba(150, 180, 205, 0.26);
  /* The same edge, at the strength a control's own boundary has to have:
     3.1:1 against the panel behind these cards, where --border is 1.7:1.
     Used only where the edge is the thing that says "this is a control". */
  --border-ui:    rgba(150, 180, 205, 0.55);
  --scene-ink:    #EAF0F5;
  --scene-muted:  #A8B8C6;
  --focus:        #60a5fa;

  --radius-xl: 22px;
  --radius-lg: 16px;
  --radius-md: 12px;
  --radius-sm: 9px;
  --shadow: 0 16px 40px rgba(0, 0, 0, 0.5);
}

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

/* An author display rule outranks the UA stylesheet's [hidden] { display:none },
   so anything toggled with `hidden` needs this to actually hide. */
[hidden] { display: none !important; }

html, body {
  height: 100%;
  overflow: hidden;
  font-family: Roboto, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
}

body {
  background: linear-gradient(180deg, var(--shell-top) 0%, var(--shell-bottom) 100%);
  color: var(--scene-ink);
}

button, input { font: inherit; }
button { cursor: pointer; font-family: Roboto, Arial, sans-serif; }

.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;
}

#sim-app { height: 100%; display: flex; }

#stage-panel {
  flex: 1;
  min-width: 0;
  display: flex;
  padding: 0;
}

#canvas-frame {
  position: relative;
  flex: 1;
  min-width: 0;
  overflow: hidden;
}

#canvas-frame.is-booting { visibility: hidden; }
/* Without WebGL the loader lifts at once; the message must show while the rest of the frame stays hidden. */
#canvas-frame.is-booting > #no-gl { visibility: visible; }

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

#sim-canvas:focus-visible {
  box-shadow: inset 0 0 0 3px var(--focus);
}

/* Build-time only: the camera and tier readout behind ?spike=1. Never
   reachable from the interface. */
.spike-readout {
  position: absolute;
  bottom: 14px;
  left: 14px;
  padding: 8px 11px;
  font: 400 11px/1.55 ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  white-space: pre;
  color: #b9f6c4;
  background: rgba(6, 10, 14, 0.88);
  border: 1px solid rgba(120, 220, 160, 0.3);
  border-radius: var(--radius-sm);
  user-select: text;
}

.spike-copy {
  position: absolute;
  bottom: 14px;
  left: 14px;
  transform: translateY(-100%) translateY(-6px);
  padding: 4px 10px;
  font: 500 11px/1.2 Roboto, Arial, sans-serif;
  color: #0b0e12;
  background: #b9f6c4;
  border: 0;
  border-radius: 6px;
  z-index: 3;
}
.spike-copy:focus-visible { outline: 2px solid var(--focus); }

/* ── Info dialog ──────────────────────────────────────────────────────────
   Matches the shared pattern; the toolbar handles tab gating and appends the
   version, author and credits lines into the About panel. */
#info-dialog {
  width: min(680px, 92vw);
  max-height: 86vh;
  padding: 0;
  border: none;
  border-radius: var(--radius-lg);
  background: #fff;
  color: var(--ink);
  box-shadow: var(--shadow);
  /* A dialog with no margin lands in the top-left corner inside an iframe,
     which is where this one was landing. auto centres it on both axes. */
  margin: auto;
}

#info-dialog::backdrop { background: rgba(11, 14, 18, 0.62); }

/* ── Start over? ──────────────────────────────────────────────────────────
   The same card as the info dialog, smaller. Keep working is the filled
   button and takes the focus; Start over is the quiet one. */
.reset-dialog {
  width: min(420px, 92vw);
  padding: 22px 24px 20px;
  border: none;
  border-radius: var(--radius-lg);
  background: #fff;
  color: var(--ink);
  box-shadow: var(--shadow);
  margin: auto;
}
.reset-dialog::backdrop { background: rgba(11, 14, 18, 0.62); }
.reset-dialog h2 { margin: 0 0 8px; font-size: 1.15rem; color: var(--accent); }
.reset-dialog p { margin: 0 0 20px; font-size: 0.95rem; line-height: 1.45; }
.reset-actions { display: flex; gap: 10px; justify-content: flex-end; flex-wrap: wrap; }
.reset-btn {
  min-height: 44px;
  padding: 8px 18px;
  border-radius: var(--radius-sm);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
}
.reset-btn--keep { background: var(--accent); color: #fff; border: 1px solid var(--accent); }
.reset-btn--keep:hover { background: var(--accent-light); }
.reset-btn--go { background: #fff; color: var(--ink); border: 1px solid #b8c2c2; }
.reset-btn--go:hover { background: #f1f4f4; }
.reset-btn:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }

.dialog-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 18px 22px 14px;
  border-bottom: 1px solid #e4eaea;
}

.dialog-header h2 {
  font-size: 1.15rem;
  font-weight: 700;
  color: #fff;
}

#dialog-close-btn {
  width: 32px; height: 32px;
  border: none;
  border-radius: 50%;
  background: transparent;
  color: #fff;
  font-size: 0.95rem;
  line-height: 1;
}

#dialog-close-btn:hover { background: rgba(255, 255, 255, 0.16); }
#dialog-close-btn:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }

.dialog-tabs {
  display: flex;
  gap: 4px;
  padding: 12px 22px 0;
  border-bottom: 1px solid #e4eaea;
}

.tab-btn {
  padding: 9px 15px;
  border: none;
  border-bottom: 3px solid transparent;
  background: none;
  font-size: 0.9rem;
  font-weight: 500;
  color: var(--muted);
}

.tab-btn.active {
  color: var(--accent);
  border-bottom-color: var(--accent);
  font-weight: 600;
}

.dialog-body {
  padding: 18px 22px 24px;
  overflow-y: auto;
  max-height: calc(86vh - 132px);
  font-size: 0.92rem;
  line-height: 1.62;
}

.dialog-body h3 {
  margin: 18px 0 7px;
  font-size: 0.95rem;
  font-weight: 700;
  color: var(--accent);
}

.dialog-body h3:first-child { margin-top: 0; }
.dialog-body p { margin-bottom: 10px; }
.dialog-body ul, .dialog-body ol { margin: 0 0 10px 20px; }
.dialog-body li { margin-bottom: 6px; }

.dialog-body details {
  margin-top: 12px;
  padding: 11px 14px;
  background: #f5f8f8;
  border-radius: var(--radius-sm);
}

.dialog-body summary {
  cursor: pointer;
  font-weight: 600;
  color: var(--accent);
}

.dialog-body details p { margin: 9px 0 0; }

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: 0.001ms !important; transition-duration: 0.001ms !important; }
}

/* ── Globe overlay ────────────────────────────────────────────────────────── */
.globe-pins { position: absolute; inset: 0; pointer-events: none; }
.globe-pin {
  position: absolute;
  transform: translate(-50%, -50%);
  padding: 5px 10px 6px;
  font: 600 12.5px/1.2 Roboto, Arial, sans-serif;
  color: var(--scene-ink);
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow);
  white-space: nowrap;
  pointer-events: auto;
  cursor: pointer;
}
/* The label sits away from its spot, so a leader and an anchor dot say where
   the spot is. Three of the sites are within 300 km of each other and their
   labels would otherwise stack. */
.globe-anchor {
  position: absolute; width: 9px; height: 9px;
  transform: translate(-50%, -50%);
  border-radius: 50%; background: var(--focus-glow);
  box-shadow: 0 0 0 2px rgba(11,14,18,.6);
  pointer-events: auto; cursor: pointer;
  transition: transform .15s;
}
.globe-anchor.pending { background: #7f8a96; }
/* A site on the far side of the planet: a hollow dot just outside the limb. */
.globe-anchor.behind { background: transparent; border: 2px solid var(--focus-glow); box-shadow: none; }
.globe-anchor.behind.pending { border-color: #7f8a96; }
.globe-pin.behind { border-style: dashed; }
.globe-anchor.visited { background: #9be7c4; }
.globe-anchor[hidden] { display: none !important; }
.globe-leaders { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
.globe-leaders line { stroke: rgba(234,240,245,.55); stroke-width: 1.2; }
.globe-pin[hidden] { display: none !important; }
.globe-pin.pending { color: var(--scene-muted); }
.globe-pin.visited { border-color: #9be7c4; }
.globe-pin:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.globe-pin small { display: block; font-weight: 400; font-size: 12px; color: var(--scene-muted); }

.site-card {
  position: absolute; right: 14px; top: 14px;
  width: min(268px, 42%);
  padding: 16px 18px 14px;
  background: var(--panel); color: var(--scene-ink);
  border: 1px solid var(--border); border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
}
.site-card-name { font-size: 1.15rem; font-weight: 700; }
.site-card-region { color: var(--scene-muted); font-size: .85rem; margin: 2px 0 10px; }
.site-card-why { font-size: .92rem; line-height: 1.5; }
.site-card-meta { font-size: .8rem; color: var(--scene-muted); margin-top: 8px; }
.site-card-actions { display: flex; gap: 8px; margin-top: 14px; }
.site-card-actions button {
  padding: 8px 14px; border-radius: var(--radius-sm); font-weight: 600;
  border: 1px solid var(--border); background: transparent; color: var(--scene-ink);
}
.site-card-actions button.primary { background: var(--accent); border-color: var(--accent); color: #fff; }
.site-card-actions button:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }

/* ── Field notebook ────────────────────────────────────────────────────────
   Docked right, from under the toolbar to above the mode bar. The column is
   an SVG whose beds are buttons. */
.notebook {
  position: absolute; right: 14px; top: 14px; max-height: calc(100% - 28px);
  /* Wider again: a layer's whole record opens inside this panel now,
     photographs included, so it is the only card the site view needs. */
  width: min(var(--kit), 46%);
  overflow-x: hidden;
  display: flex; flex-direction: column;
  background: var(--panel); color: var(--scene-ink);
  border: 1px solid var(--border); border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
  overflow-y: auto;
}
.nb-head { padding: 12px 14px 8px; border-bottom: 1px solid var(--border); }
.nb-kicker { font-size: .78rem; letter-spacing: .08em; text-transform: uppercase; color: var(--scene-muted); }
.nb-site { font-size: 1.05rem; font-weight: 700; margin-top: 2px; }
.nb-count { font-size: .78rem; color: var(--scene-muted); margin-top: 2px; }
/* Below the rows now, so its arrival moves nothing above it. */
.nb-aerial { padding: 8px 14px 10px; border-top: 1px solid var(--border); font-size: .8rem; line-height: 1.45; flex: 0 0 auto; }
.nb-shape > summary { cursor: pointer; color: var(--focus-glow); font-weight: 600; list-style: none; }
.nb-shape > summary::-webkit-details-marker { display: none; }
.nb-shape > summary::before { content: ""; display: inline-block; width: 5px; height: 5px; margin-right: 7px;
  border-right: 2px solid currentColor; border-bottom: 2px solid currentColor; transform: rotate(-45deg); vertical-align: middle; }
.nb-shape[open] > summary::before { transform: rotate(45deg); }
.nb-aerial-note { color: var(--scene-ink); }
.nb-aerial-hint { color: var(--scene-muted); margin-top: 6px; }
.nb-column { flex: 0 0 auto; overflow-x: hidden; }
.nb-foot { padding: 10px 14px 12px; border-top: 1px solid var(--border); position: sticky; bottom: 0; background: var(--panel);
           display: flex; flex-direction: column; gap: 6px; }
.nb-btn.secondary { background: transparent; color: var(--scene-ink); border-color: var(--border-ui); }
.nb-btn {
  display: inline-block; width: 100%;
  padding: 8px 12px; border-radius: var(--radius-sm); font-weight: 600; font-size: .86rem;
  background: var(--accent, #004445); border: 1px solid var(--accent, #004445); color: #fff;
}
.nb-btn:hover, .nb-btn:focus-visible { background: #006061; border-color: #006061; color: #fff; }
.nb-btn.secondary:hover, .nb-btn.secondary:focus-visible { background: rgba(255,255,255,.1); border-color: var(--scene-ink); color: var(--scene-ink); }
.nb-btn:disabled { opacity: .5; cursor: not-allowed; }
.nb-btn:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }

/* Column beds */
/* ── Full-frame panels: the workbench ───────────────────────────── */
.panel-view {
  position: absolute; inset: 0;
  display: flex; flex-direction: column;
  background: linear-gradient(180deg, var(--shell-top) 0%, var(--shell-bottom) 100%);
  color: var(--scene-ink);
}
.panel-head { padding: 16px 22px 8px; }
.panel-head h2 { font-size: 1.15rem; font-weight: 700; }
.panel-status { font-size: .86rem; color: var(--scene-muted); margin-top: 4px; line-height: 1.45; min-height: 1.3em; }
@media (max-width: 760px) {
  /* Narrower kit, and everything that clears it follows the variable. */
  :root { --kit: 300px; }
  .notebook { width: min(var(--kit), 62%); }
}

/* ── Contextual navigation ─────────────────────────────────────────────────── */
.mini-earth {
  flex: 0 0 auto; width: 34px; height: 34px; border-radius: 50%;
  background: #1c3f6e url("assets/earth_day.jpg") no-repeat;
  background-size: 230% auto; background-position: 34% 36%;
  box-shadow: inset -7px -6px 12px rgba(0,0,0,.55), inset 3px 3px 6px rgba(255,255,255,.18), 0 0 0 1px rgba(150,180,205,.35);
}
.fade { position: absolute; inset: 0; background: #0b0e12; opacity: 0; pointer-events: none; }
.panel-btn {
  padding: 7px 14px; border-radius: 999px; font-weight: 600; font-size: .84rem;
  border: 1px solid var(--border); background: transparent; color: var(--scene-ink);
}
.panel-btn.primary { background: var(--accent); border-color: var(--accent); color: #fff; }
.panel-btn:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }

/* ── Tool belt ─────────────────────────────────────────────────────────────── */
.tool-belt {
  display: flex; align-items: stretch; gap: 6px;
  padding: 10px 12px 4px;
}
.tool-belt button {
  flex: 1; display: flex; flex-direction: column; align-items: center; gap: 2px;
  padding: 7px 6px 6px; border-radius: var(--radius-sm);
  border: 1px solid transparent; background: rgba(255,255,255,.05); color: var(--scene-ink);
  font-weight: 600; font-size: .8rem;
}
.tool-belt button svg { width: 22px; height: 22px; }
.tool-belt button small { font-weight: 400; font-size: .66rem; color: var(--scene-muted); }
.tool-belt button:hover { border-color: var(--border); }
.tool-belt button[aria-pressed="true"] { background: var(--focus-glow); border-color: var(--focus-glow); color: #0b0e12; }
.tool-belt button[aria-pressed="true"] small { color: #3d3420; }
.tool-belt button:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }

/* The pointer is the tool in hand, over the terrain and over the column. */
.armed-lens #sim-canvas, .armed-lens #nb-column .strat-row { cursor: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20viewBox%3D%22-2%20-2%2024%2024%22%20width%3D%2230%22%20height%3D%2230%22%3E%3Cg%20fill%3D%22none%22%20stroke%3D%22%230b0e12%22%20stroke-width%3D%224.5%22%20stroke-linejoin%3D%22round%22%20stroke-linecap%3D%22round%22%20opacity%3D%22.9%22%3E%3Ccircle%20cx%3D%228.5%22%20cy%3D%228.5%22%20r%3D%225.5%22%20fill%3D%22none%22/%3E%3Cline%20x1%3D%2212.5%22%20y1%3D%2212.5%22%20x2%3D%2218%22%20y2%3D%2218%22/%3E%3C/g%3E%3Cg%20fill%3D%22%23fff%22%20stroke%3D%22%23fff%22%20stroke-width%3D%222%22%20stroke-linejoin%3D%22round%22%20stroke-linecap%3D%22round%22%3E%3Ccircle%20cx%3D%228.5%22%20cy%3D%228.5%22%20r%3D%225.5%22%20fill%3D%22none%22/%3E%3Cline%20x1%3D%2212.5%22%20y1%3D%2212.5%22%20x2%3D%2218%22%20y2%3D%2218%22/%3E%3C/g%3E%3C/svg%3E") 10 10, crosshair; }
.armed-pick #sim-canvas, .armed-pick #nb-column .strat-row { cursor: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20viewBox%3D%22-2%20-2%2024%2024%22%20width%3D%2230%22%20height%3D%2230%22%3E%3Cg%20fill%3D%22none%22%20stroke%3D%22%230b0e12%22%20stroke-width%3D%224.5%22%20stroke-linejoin%3D%22round%22%20stroke-linecap%3D%22round%22%20opacity%3D%22.9%22%3E%3Cpath%20d%3D%22M3%206%20Q10%201%2017%206%20L15%208%20Q10%205%205%208%20Z%22/%3E%3Cline%20x1%3D%2210%22%20y1%3D%226%22%20x2%3D%2210%22%20y2%3D%2218%22/%3E%3C/g%3E%3Cg%20fill%3D%22%23fff%22%20stroke%3D%22%23fff%22%20stroke-width%3D%222%22%20stroke-linejoin%3D%22round%22%20stroke-linecap%3D%22round%22%3E%3Cpath%20d%3D%22M3%206%20Q10%201%2017%206%20L15%208%20Q10%205%205%208%20Z%22/%3E%3Cline%20x1%3D%2210%22%20y1%3D%226%22%20x2%3D%2210%22%20y2%3D%2218%22/%3E%3C/g%3E%3C/svg%3E") 12 20, crosshair; }

/* A finding, where it was made. */
.finding-note {
  /* Above the bench (7) and the rails (8), because a reading the student
     asked for must never be painted over by furniture. */
  position: absolute; z-index: 9; width: clamp(300px, calc(100% - 400px), 560px);
  padding: 10px 34px 11px 12px;
  background: var(--panel); color: var(--scene-ink);
  border: 1px solid var(--focus-glow); border-radius: var(--radius-md); box-shadow: var(--shadow);
  font-size: .84rem; line-height: 1.45;
}
.fn-title { font-size: .7rem; letter-spacing: .08em; text-transform: uppercase; color: var(--focus-glow); margin-bottom: 3px; }
.fn-title #fn-layer { color: var(--scene-ink); letter-spacing: 0; text-transform: none; font-size: .84rem; font-weight: 600; margin-left: 4px; }
.fn-close { position: absolute; right: 6px; top: 6px; width: 24px; height: 24px; border: 0; border-radius: 50%; background: rgba(255,255,255,.08); color: var(--scene-muted); font-size: .75rem; }
.fn-close:hover { color: var(--scene-ink); }
.fn-close:focus-visible { outline: 2px solid var(--focus); }

/* ── Parks with several stops ──────────────────────────────────────────────── */
.site-card-lands { display: flex; flex-direction: column; gap: 6px; flex: 1; }
.site-card-lands button { width: 100%; text-align: left; }
.site-card-lands button small { display: block; font-weight: 400; font-size: .72rem; opacity: .85; }
.nb-stop-note { padding: 0 14px 10px; font-size: .76rem; line-height: 1.45; color: var(--scene-muted); }

/* ── The workbench ─────────────────────────────────────────────────────────── */
.wb-body { flex: 1; min-height: 0; display: flex; gap: 14px; padding: 12px 16px; }
.wb-time-shell { flex: 1; min-width: 0; min-height: 0; display: flex; flex-direction: column; }
.wb-time-view { flex: 1; min-height: 0; position: relative; }
.wb-time-wrap { height: 100%; background: rgba(0,0,0,.18); border: 1px solid var(--border); border-radius: var(--radius-md); overflow-y: scroll; overflow-x: auto; scrollbar-width: thin; scrollbar-color: rgba(234,240,245,.45) rgba(0,0,0,.2); }
.wb-time-wrap::-webkit-scrollbar { width: 9px; }
.wb-time-wrap::-webkit-scrollbar-thumb { background: rgba(234,240,245,.4); border-radius: 5px; }
.wb-time-wrap::-webkit-scrollbar-track { background: rgba(0,0,0,.2); }
/* The cue at the foot of the timeline while there is more below. */
.wb-more { position: absolute; left: 0; right: 10px; bottom: 0; height: 64px; pointer-events: none; display: flex; align-items: flex-end; justify-content: center; gap: 8px; padding-bottom: 8px;
  background: linear-gradient(to bottom, rgba(11,14,18,0) 0%, rgba(11,14,18,.85) 100%); color: var(--scene-ink); font-size: .78rem; border-radius: 0 0 var(--radius-md) var(--radius-md); }
.wb-more-chevron { width: 10px; height: 10px; border-right: 2px solid var(--focus-glow); border-bottom: 2px solid var(--focus-glow); transform: translateY(-3px) rotate(45deg); animation: wb-more-bob 1.6s ease-in-out infinite; }
@keyframes wb-more-bob { 0%, 100% { transform: translateY(-3px) rotate(45deg); } 50% { transform: translateY(1px) rotate(45deg); } }
@media (prefers-reduced-motion: reduce) { .wb-more-chevron { animation: none; } }
/* A tray piece being lifted. */
.wb-ghost { position: absolute; z-index: 6; pointer-events: none; display: flex; align-items: center; gap: 6px; padding: 4px 8px 4px 4px; background: rgba(11,14,18,.9); border: 1px solid var(--focus-glow); border-radius: 8px; color: var(--scene-ink); font-size: .78rem; box-shadow: 0 6px 18px rgba(0,0,0,.5); }
.wb-ghost svg { width: 26px; height: 40px; }
.wb-tray-card.lifted { opacity: .35; }
.wb-piece.lifted rect { filter: drop-shadow(0 4px 8px rgba(0,0,0,.6)); }
.wb-piece.lifted { cursor: grabbing; }

.wb-svg { display: block; }
/* The left column does not scroll as a whole. Ben: "once the rock sampler
   appears, i would like it to have a dedicated space so it doesnt scroll away
   as i'm scrolling through the places. i'd prefer the scroll bar be placed on
   the places, and not include the rock sampler in the scrollable area."
   So the laboratory and the Places heading hold their ground and the list of
   places below them is the only thing that moves. */
.wb-side { flex: 0 0 190px; min-height: 0; overflow: hidden; display: flex; flex-direction: column; gap: 10px; }
.wb-side > .wb-lab, .wb-side > .wb-fold { flex: 0 0 auto; }
.wb-h { font-size: .74rem; letter-spacing: .08em; text-transform: uppercase; color: var(--scene-muted); }
.wb-tray { display: flex; flex-direction: column; gap: 6px;
  flex: 1 1 auto; min-height: 0; overflow-y: auto; padding-right: 6px;
  /* The same bar the timeline uses, so the places read as a list you scroll
     rather than a list that happens to be cut off. */
  scrollbar-width: thin; scrollbar-color: rgba(234,240,245,.45) rgba(0,0,0,.2); }
.wb-tray::-webkit-scrollbar { width: 9px; }
.wb-tray::-webkit-scrollbar-thumb { background: rgba(234,240,245,.4); border-radius: 5px; }
.wb-tray::-webkit-scrollbar-track { background: rgba(0,0,0,.2); border-radius: 5px; }
.wb-tray-card { display: flex; align-items: center; gap: 10px; padding: 6px 10px; background: var(--card); border: 1px solid var(--border-ui); border-radius: var(--radius-sm); cursor: grab; touch-action: none; }
.wb-tray-card.empty { cursor: default; opacity: .7; }
.wb-tray-card:focus-visible { outline: 2px solid var(--focus); }
.wb-tray-text { display: flex; flex-direction: column; gap: 2px; font-size: .84rem; }
.wb-tray-text small { font-size: .8rem; color: var(--scene-muted); line-height: 1.35; }
.wb-tray-empty { font-size: .82rem; color: var(--scene-muted); }
.wb-lab { padding: 10px 12px; background: var(--card); border: 1px solid var(--border); border-radius: var(--radius-md); display: flex; flex-direction: column; gap: 8px; }
.wb-lab .nb-btn { width: auto; }
.wb-note { font-size: .76rem; line-height: 1.45; color: var(--scene-muted); }
.lab-busy .wb-lab { border-color: var(--focus-glow); }
.wb-boundaries { display: flex; flex-direction: column; gap: 8px; }
.wb-bnd { display: grid; grid-template-columns: 1fr 26px; gap: 4px 6px; align-items: center; }
.wb-bnd input { padding: 5px 8px; border-radius: 6px; border: 1px solid var(--border); background: rgba(255,255,255,.06); color: var(--scene-ink); font-size: .84rem; }
.wb-bnd input:focus-visible { outline: 2px solid var(--focus); }
.wb-x { width: 26px; height: 26px; border-radius: 50%; border: 0; background: rgba(255,255,255,.08); color: var(--scene-muted); }
.wb-bnd-reason { grid-column: 1 / -1; font-size: .76rem; line-height: 1.4; color: var(--scene-muted); }
.wb-flag { margin-top: 4px; }
.wb-flag.soft { color: var(--scene-muted); }
.wb-axis { font: 400 12px Roboto, Arial, sans-serif; fill: var(--scene-muted); }
.wb-axis.dim { font-style: italic; fill: rgba(168,184,198,.6); }
.wb-label { font: 500 12px Roboto, Arial, sans-serif; fill: var(--scene-ink); pointer-events: none; }
.wb-label.dim { fill: var(--scene-muted); }
.wb-park { font: 600 12px Roboto, Arial, sans-serif; fill: var(--scene-ink); pointer-events: none; }
.wb-date { font: 500 11.5px Roboto, Arial, sans-serif; fill: var(--focus-glow); pointer-events: none; }
.wb-win { font: 600 11px Roboto, Arial, sans-serif; fill: #9be7c4; pointer-events: none; }
.wb-boundary { font: 600 11px Roboto, Arial, sans-serif; fill: var(--focus-glow); pointer-events: none; }
.wb-piece { cursor: grab; }
.wb-piece:focus { outline: none; }
.wb-piece:focus-visible .wb-park, .wb-piece.grabbed .wb-park { fill: var(--focus-glow); }
/* A piece under the keyboard is ringed, not just relabelled: the name is a
   small word at the top of a column half the screen high. */
.wb-piece:focus-visible { outline: none; }
.wb-piece:focus-visible rect { filter: drop-shadow(0 0 5px var(--focus)) drop-shadow(0 0 2px var(--focus)); }
.wb-bed[tabindex]:focus-visible { outline: 2px solid var(--focus-glow); outline-offset: 1px; }
.wb-piece.grabbed rect { filter: drop-shadow(0 0 6px rgba(255,209,102,.7)); }
.armed-sampler .wb-bed { cursor: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20viewBox%3D%22-2%20-2%2024%2024%22%20width%3D%2230%22%20height%3D%2230%22%3E%3Cg%20fill%3D%22none%22%20stroke%3D%22%230b0e12%22%20stroke-width%3D%224.5%22%20stroke-linejoin%3D%22round%22%20stroke-linecap%3D%22round%22%20opacity%3D%22.9%22%3E%3Cpath%20d%3D%22M7%202%20h6%20v3%20l3%2010%20a3%203%200%200%201%20-3%203%20h-6%20a3%203%200%200%201%20-3%20-3%20l3%20-10%20z%22/%3E%3Cline%20x1%3D%226%22%20y1%3D%2212%22%20x2%3D%2214%22%20y2%3D%2212%22/%3E%3C/g%3E%3Cg%20fill%3D%22%23fff%22%20stroke%3D%22%23fff%22%20stroke-width%3D%222%22%20stroke-linejoin%3D%22round%22%20stroke-linecap%3D%22round%22%3E%3Cpath%20d%3D%22M7%202%20h6%20v3%20l3%2010%20a3%203%200%200%201%20-3%203%20h-6%20a3%203%200%200%201%20-3%20-3%20l3%20-10%20z%22/%3E%3Cline%20x1%3D%226%22%20y1%3D%2212%22%20x2%3D%2214%22%20y2%3D%2212%22/%3E%3C/g%3E%3C/svg%3E") 10 18, crosshair; }
.armed-boundary .wb-time-bg { cursor: crosshair; }
@media (max-width: 900px) { .wb-side { flex-basis: 190px; } }

/* ── Where you are (left) ─────────────────────────────────────────────────── */
.place-card {
  position: absolute; left: 14px; top: 14px; width: 232px;
  display: flex; flex-direction: column;
  background: var(--panel); color: var(--scene-ink);
  border: 1px solid var(--border); border-radius: var(--radius-lg);
  box-shadow: var(--shadow); overflow: hidden;
}
.place-hud { display: flex; flex-direction: column; gap: 1px; padding: 12px 14px 10px; }
.hud-value { font-size: 1.05rem; font-weight: 700; letter-spacing: .01em; color: var(--scene-ink); }
.hud-sub { font-size: .75rem; color: var(--scene-muted); }

/* Workbench: detail card and the lock pulse */
.wb-detail { position: absolute; z-index: 5; max-width: 300px; padding: 8px 12px; background: var(--panel); color: var(--scene-ink);
             border: 1px solid var(--border); border-radius: var(--radius-sm); box-shadow: var(--shadow); font-size: .8rem; line-height: 1.4; pointer-events: none;
             max-height: calc(100% - 16px); overflow-y: auto; overscroll-behavior: contain; }
.wb-detail strong { font-weight: 600; }
.wb-detail-park { color: var(--scene-muted); }
.wb-detail p { margin-top: 3px; color: var(--scene-muted); }
.wb-detail-live { pointer-events: auto; }
.wb-marklist { list-style: none; margin: 6px 0 0; padding: 0; display: flex; flex-direction: column; gap: 4px; color: var(--scene-muted); }
.wb-marklist li { line-height: 1.3; }
.wb-marklist button { display: block; width: 100%; text-align: left; font: inherit; color: var(--scene-ink); background: rgba(255,138,128,.12);
                      border: 1px solid rgba(255,138,128,.45); border-radius: var(--radius-sm); padding: 6px 8px; min-height: 32px; cursor: pointer; }
.wb-marklist button:hover { background: rgba(255,138,128,.24); }
.wb-marklist button:focus-visible { outline: 2px solid #fff; outline-offset: 1px; }
.wb-markmore button { background: transparent; border-style: dashed; color: var(--scene-muted); }
.wb-body { position: relative; }
@keyframes wb-lock-pulse { 0% { stroke-width: 1.4; fill-opacity: .24; } 30% { stroke-width: 5; fill-opacity: .6; } 100% { stroke-width: 1.4; fill-opacity: .24; } }
.wb-lock.fresh polygon { animation: wb-lock-pulse 900ms ease-out 1; }
@media (prefers-reduced-motion: reduce) { .wb-lock.fresh polygon { animation: none; } }

/* Tool models as pictures: in the belt, on the sampler, and as the pointer. */
.tool-belt button .tool-img { width: 34px; height: 34px; display: block; image-rendering: auto; }
.tool-belt button .tool-img:not([hidden]) + svg { display: none; }
.wb-sampler { display: inline-flex; align-items: center; gap: 9px; padding-left: 6px; }
/* The tool sits in a holder. Picking it up empties the holder, so the bench
   shows where the sampler is without a sentence about it. */
.wb-sampler-slot { width: 36px; height: 36px; border-radius: 7px; display: grid; place-items: center; flex: 0 0 auto;
  background: radial-gradient(circle at 50% 32%, rgba(255,209,102,.14), rgba(0,0,0,.4)); border: 1px solid rgba(255,209,102,.4); box-shadow: inset 0 2px 6px rgba(0,0,0,.55); transition: border-color .18s, background-color .18s; }
.wb-sampler .tool-img { width: 30px; height: 30px; transition: opacity .18s, transform .18s; }
.wb-sampler.taken .wb-sampler-slot { border-style: dashed; border-color: rgba(255,255,255,.28); background: rgba(0,0,0,.45); }
.wb-sampler.taken .tool-img { opacity: 0; transform: translateY(-7px) scale(.75); }

/* Pictures with a finding. One photograph, or a row of small ones when a
   layer holds several kinds of fossil; a caption names who took it. */
.strat-find .fn-pics { margin: 4px 0 6px; }

/* A date on the axis, followed to its bed: hover the age and the leader and
   the bed light together. */
.wb-tick { cursor: default; }
.wb-tick.hot .wb-leader { stroke: #ffd166; stroke-dasharray: none; stroke-width: 2; }
.wb-tick.hot .wb-date { fill: #fff; }
.wb-bed.hot rect:first-of-type { stroke: #ffd166; stroke-width: 2.4; filter: drop-shadow(0 0 5px rgba(255,209,102,.8)); }
.wb-date { pointer-events: auto; }

/* Locked pieces, joinable so they move as one. */
.wb-locks { margin-top: 10px; padding-top: 8px; border-top: 1px solid var(--border); }
.wb-locks-title { margin: 0 0 4px; font-size: .72rem; letter-spacing: .04em; text-transform: uppercase; color: var(--scene-muted); }
.wb-lockrow { display: flex; align-items: center; justify-content: space-between; gap: 8px; font-size: .78rem; line-height: 1.3; margin: 3px 0; }
.wb-join { flex: none; font: 500 .74rem Roboto, Arial, sans-serif; color: var(--scene-ink); background: rgba(255,209,102,.16); border: 1px solid rgba(255,209,102,.55); border-radius: 6px; padding: 3px 9px; cursor: pointer; }
.wb-join:hover { background: rgba(255,209,102,.32); }
.wb-join:focus-visible { background: rgba(255,209,102,.32); outline: 2px solid var(--focus-glow); outline-offset: 2px; }
.wb-lock.joined.supported polygon { stroke-dasharray: none; }
.wb-tray-card { cursor: pointer; }

/* The column asked for: a pulse of the highlight colour round it. */

/* The globe's landing screen. */
.pin-frac { display: block; color: var(--focus-glow); font-weight: 500; }
#globe-pins.selecting .globe-pin.selected { z-index: 3; }

/* Disagreements marked on the timeline. */
.wb-flagmark { cursor: default; }
.wb-flagmark circle { fill: #ffb36b; stroke: #0b0e12; stroke-width: 1.2; }
.wb-flagmark line { stroke: #0b0e12; stroke-width: 2; stroke-linecap: round; }
.wb-flagmark .dot { fill: #0b0e12; stroke: none; }
.wb-flagmark.soft circle { fill: #d9c9a8; }
.wb-flagmark:focus { outline: none; }
.wb-flagmark:focus-visible circle, .wb-flagmark:hover circle { stroke: #fff; stroke-width: 2; }
.wb-flagtie { fill: rgba(255,179,107,.10); stroke: #ffb36b; stroke-width: 1.2; stroke-dasharray: 4 3; }
.wb-flagbracket { stroke: #ffb36b; stroke-width: 2; stroke-linecap: round; }
/* The surface a piece can be torn along, and the part that tears away. */
.wb-cutline { stroke: #ffd166; stroke-width: 1.6; fill: none; }
.wb-bed.sheet { cursor: grab; }

/* Badge, dot and leader light together under the pointer. */
.globe-pin { transition: border-color .15s, box-shadow .15s; }
.globe-pin.hot, .globe-pin:hover { border-color: var(--focus-glow); box-shadow: 0 0 0 2px var(--focus-glow), var(--shadow); z-index: 2; }
.globe-anchor.hot { transform: translate(-50%, -50%) scale(1.6); }
.globe-anchor.behind.hot { border-color: #fff; }
.globe-leaders line.hot { stroke: var(--focus-glow); stroke-width: 2; }
.globe-leaders line.hit { stroke: transparent; stroke-width: 14; pointer-events: stroke; cursor: pointer; }

/* A picture of the place on its card, and on the way back from the workbench. */
.site-card-pic { display: block; width: 100%; aspect-ratio: 16 / 9; object-fit: cover; border-radius: var(--radius-sm); margin: 8px 0 10px; border: 1px solid var(--border); background: #0b0e12; }

/* Event cards: in the tray, and on the timeline. */
.wb-events { display: flex; flex-direction: column; gap: 6px; }
.wb-events-title { margin: 0; font-size: .72rem; letter-spacing: .04em; text-transform: uppercase; color: var(--scene-muted); }
.wb-events-hint { margin: 0; font-size: .76rem; line-height: 1.35; color: var(--scene-muted); }
.wb-event-cards { display: flex; flex-wrap: wrap; gap: 6px; }
.wb-event-card { display: inline-flex; align-items: center; gap: 6px; padding: 5px 9px 5px 6px; border-radius: 8px; border: 1px solid var(--border-ui);
  background: rgba(255,255,255,.04); color: var(--scene-ink); font: 500 .78rem Roboto, Arial, sans-serif; cursor: grab; }
.wb-event-card svg { color: var(--focus-glow); flex: none; }
.wb-event-card:hover, .wb-event-card:focus-visible { border-color: var(--focus-glow); outline: none; }
.wb-event-card.lifted { opacity: .35; }
.wb-event { cursor: grab; }
.wb-event:focus { outline: none; }
.wb-event-line { stroke: var(--focus-glow); stroke-width: 1.6; stroke-dasharray: 6 4; opacity: .85; }
.wb-event-box { fill: rgba(11,14,18,.92); stroke: var(--focus-glow); stroke-width: 1.2; }
.wb-event .wb-glyph { color: var(--focus-glow); }
.wb-event-label { font: 600 11px Roboto, Arial, sans-serif; fill: var(--scene-ink); pointer-events: none; }
.wb-event:hover .wb-event-box, .wb-event:focus-visible .wb-event-box, .wb-event.grabbed .wb-event-box { stroke-width: 2.2; fill: rgba(11,14,18,1); }
.wb-event:focus-visible { outline: 2px solid var(--focus-glow); outline-offset: 2px; }
.wb-event.lifted .wb-event-box { filter: drop-shadow(0 4px 8px rgba(0,0,0,.6)); }
.wb-event.grabbed .wb-event-line, .wb-event.lifted .wb-event-line { stroke-dasharray: none; }

/* Events as a column of their own, left of the timeline. */
.wb-events-col { flex: 0 0 190px; min-height: 0; overflow-y: auto; padding-right: 4px; }
.wb-events-col .wb-event-cards { flex-direction: column; }
.wb-events-col .wb-event-card { width: 100%; justify-content: flex-start; text-align: left; line-height: 1.25; }
.wb-event.stretched .wb-event-label { font-size: 10.5px; }
/* A bed that is the evidence for the card under the pointer. */
.wb-bed.cue rect:first-of-type { stroke: var(--focus-glow); stroke-width: 2.4; filter: drop-shadow(0 0 5px rgba(255,209,102,.85)); }

/* The site card's close, top right (the card is already positioned). */
.site-card-close { position: absolute; top: 8px; right: 8px; width: 28px; height: 28px; border-radius: 50%; border: 1px solid var(--border-ui); background: rgba(255,255,255,.06); color: var(--scene-ink); font-size: 18px; line-height: 1; cursor: pointer; }
.site-card-close:hover, .site-card-close:focus-visible { background: rgba(255,255,255,.14); outline: none; border-color: var(--focus-glow); }
.site-card-name { padding-right: 32px; }
.wb-side[hidden], .wb-events-col[hidden] { display: none !important; }

/* The status keeps two lines of room, so a longer sentence from the laboratory never pushes the timeline down. */
#wb-status { min-height: 2.9em; }

.wb-date.landing { transform-box: fill-box; transform-origin: right center; animation: wb-date-land .26s ease-out; }
@keyframes wb-date-land { from { transform: translateX(5px) scale(.8); } to { transform: none; } }
@media (prefers-reduced-motion: reduce) { .wb-date.landing { animation: none; } }

/* The key to the drawing, in the field notebook. */
.strat-swatch { background-repeat: repeat; }

/* The key to the drawing, under the timeline. */
.wb-names { flex: 0 0 auto; background: transparent; color: var(--scene-muted); border: 1px solid var(--border); border-radius: 999px;
            font: 500 .72rem Roboto, Arial, sans-serif; padding: 3px 10px; cursor: pointer; }
.wb-names:hover { color: var(--scene-ink); border-color: var(--focus-glow); }
.wb-label.inbar { font-size: 11px; font-weight: 600; pointer-events: none; }

/* A mark on the timeline, counted in the header row. Nothing moves when it comes
   or goes: the row's height is the sampler's. */
.wb-marks { flex: 0 0 auto; display: inline-flex; align-items: center; gap: 6px; padding: 5px 12px; border-radius: 999px; cursor: pointer;
  background: rgba(255,138,128,.14); border: 1px solid rgba(255,138,128,.65); color: #ffb3aa;
  font: 600 .78rem Roboto, Arial, sans-serif; animation: wb-mark-in .22s ease-out; }
.wb-marks::before { content: ""; width: 8px; height: 8px; border-radius: 50%; background: #ff8a80; }
.wb-marks:hover, .wb-marks:focus-visible { background: rgba(255,138,128,.24); color: #fff; }
@keyframes wb-mark-in { from { opacity: 0; transform: translateY(-3px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .wb-marks { animation: none; } }
.wb-flagmark.found circle { animation: wb-found 1.4s ease-out; }
@keyframes wb-found { 0%, 100% { r: 8; } 40% { r: 13; } }
/* The way back: a chevron, the place, and nothing else. */

/* The instrument, in the hand, at the layer it is reading. */
.wb-instr { position: fixed; z-index: 40; pointer-events: none; filter: drop-shadow(0 3px 8px rgba(0,0,0,.6)); }
.wb-instr svg { display: block; }
.lab-busy .wb-sampler-slot { border-color: var(--focus-glow); box-shadow: inset 0 2px 6px rgba(0,0,0,.5), 0 0 0 2px rgba(255,209,102,.35); }

/* A bed a piece in the hand could be tied to. Several may light at once: the
   student chooses, and the choice is the work. */
.wb-bed.couldtie rect { stroke: #5ce1ff; stroke-width: 1.8; }
.wb-bed.couldtie { animation: wb-couldtie 1.1s ease-in-out infinite; }
@keyframes wb-couldtie { 0%, 100% { opacity: 1; } 50% { opacity: .72; } }
@media (prefers-reduced-motion: reduce) { .wb-bed.couldtie { animation: none; } }
/* A tie drawn for what it is worth. */
.wb-contact { pointer-events: none; }
.wb-lock.contradicted polygon { animation: wb-tie-warn .9s ease-in-out 2; }
@keyframes wb-tie-warn { 0%, 100% { opacity: 1; } 50% { opacity: .55; } }
@media (prefers-reduced-motion: reduce) { .wb-lock.contradicted polygon { animation: none; } }

/* A tie can be pressed apart, so it says so with the pointer. */
.wb-lock.joined { cursor: pointer; }
/* Not yet joined: the whole band is the same press target as the small
   offer button drawn over it, so it reads as clickable too. */
.wb-lock:not(.joined) { cursor: pointer; }
.wb-lock.joined:focus-visible { outline: 2px solid var(--focus-glow); outline-offset: 2px; }

/* Outcrops marked on the ground: a small column of the beds exposed at a place
   the section was measured, standing on that place. */
.site-marks { position: absolute; inset: 0; pointer-events: none; }
.site-mark { position: absolute; transform: translate(-50%, -100%); display: flex; flex-direction: column; align-items: center;
             pointer-events: auto; cursor: pointer; }
.site-mark-name { font: 600 .84rem Roboto, Arial, sans-serif; color: var(--scene-ink); background: rgba(11,14,18,.72);
                  border-radius: 4px; padding: 1px 6px; margin-bottom: 3px; white-space: nowrap; }
.site-mark.far .site-mark-name { display: none; }
/* Ben: "for places like chiba where we show pins meant to indicate a dig site,
   i'd like for the pins + the lines pointing to the dig site to be larger.
   especially the click regions where i then am supposed to take the pick and
   click the floating rectangular right above the dig site."
   These markers are the ONLY thing to press at the nine sites that have them,
   because those sites have no painted beds. A bed was 38 x 20 px, well under
   the 44 px a finger needs and small for a cursor carrying a tool. */
.site-mark-col { display: flex; flex-direction: column; width: 46px; border: 2px solid rgba(11,14,18,.8); border-radius: 5px;
                 overflow: hidden; box-shadow: 0 3px 14px rgba(0,0,0,.6); }
.site-mark-bed { height: 24px; border: 0; border-bottom: 1px solid rgba(11,14,18,.5); padding: 0; cursor: inherit;
                 background: #8d8577; background-repeat: repeat; }
/* A tall section would run off the screen at thirty pixels a bed: Olduvai has
   seven. Past four beds they share the height four would have taken. */
.site-mark-col.tall .site-mark-bed { height: 20px; }
.site-mark-bed:last-child { border-bottom: 0; }
.site-mark-bed.unread { background: #45505c; box-shadow: none; position: relative; }
/* A rock column not yet read says so with a question mark, not with an empty
   dashed box, which read as a loading placeholder. */
.site-mark-bed.unread::after { content: "?"; position: absolute; inset: 0; display: grid; place-items: center;
  font: 700 .9rem Roboto, Arial, sans-serif; color: rgba(234,240,245,.8); }
.site-mark-bed.unread:not(:first-child)::after { content: ""; }
/* The marker bed is centimetres of rock and the point of its place, so in the
   column it is drawn as a thin strip with a pale edge on both sides: a line
   between two beds, not a bed like the others. Rajat #779 item 24, Hell Creek.
   The strip is drawn inside the button, which keeps its 24/30 px height, so
   the thing to press stays as easy to hit as any other bed. */
.site-mark-bed.marker, .site-mark-bed.marker.unread { position: relative; background: rgba(11,14,18,.8); box-shadow: none; }
.site-mark-bed.marker::before { content: ""; position: absolute; left: 0; right: 0; top: calc(50% - 6px); height: 12px;
  background: var(--bed-fill, #45505c); box-shadow: inset 0 2px 0 #f3ead2, inset 0 -2px 0 #f3ead2; }
.site-mark-bed.marker.lit { box-shadow: inset 0 0 0 3px var(--focus-glow); }
.site-mark-bed:enabled:hover, .site-mark-bed:focus-visible { outline: 2px solid var(--focus-glow); outline-offset: -2px; }
/* The bed a claim is pointing at, where the ground carries markers rather than
   painted bands. The whole marker lifts, so it can be found from across the
   site, and the one bed inside it is ringed. */
.site-mark-bed.lit { box-shadow: inset 0 0 0 3px var(--focus-glow); }
.site-mark.lit { z-index: 2; }
.site-mark.lit .site-mark-col { border-color: var(--focus-glow); box-shadow: 0 0 0 2px rgba(255,209,102,.45), 0 4px 18px rgba(0,0,0,.7); }
.site-mark.lit .site-mark-stem { background: linear-gradient(var(--focus-glow), rgba(255,209,102,.25)); }
.site-mark.lit.kind-section .site-mark-stem { background: none; border-left-color: var(--focus-glow); }
.site-mark.lit .site-mark-ground { border-color: rgba(255,209,102,.75); }
/* An unread bed has nothing behind it, so it does not offer itself. */
.site-mark-bed:disabled { cursor: default; }
/* The line down to the ground the marker stands on. */
.site-mark-stem { width: 3px; height: 34px; background: linear-gradient(rgba(234,240,245,.9), rgba(234,240,245,.18)); }

/* An outcrop off the edge of the view: a direction to press rather than a place. */
.site-mark.edge { transform: translate(-50%, -50%); }
.site-mark.edge .site-mark-name { display: none; }
.site-mark.edge .site-mark-stem { display: none; }
.site-mark.edge .site-mark-col { width: 42px; opacity: .92; }
.site-mark.edge .site-mark-bed { height: 22px; }
.site-mark.edge::after { content: ""; margin-top: 3px; width: 0; height: 0;
  border-left: 5px solid transparent; border-right: 5px solid transparent; border-top: 7px solid var(--focus-glow); }
.site-mark.edge::before { content: ""; position: absolute; inset: -6px; border-radius: 8px; background: rgba(11,14,18,.5); }

/* A section marker: the beds are read hereabouts, and the drawing says only
   that. A dashed column over a patch of ground, rather than a point. */
.site-mark.kind-section .site-mark-col { border-color: rgba(234,240,245,.7); }
.site-mark.kind-section .site-mark-stem { background: none; border-left: 3px dashed rgba(234,240,245,.6); width: 0; height: 20px; }
.site-mark-ground { width: 64px; height: 22px; border-radius: 50%; border: 1.5px dashed rgba(234,240,245,.5);
                    background: radial-gradient(ellipse at center, rgba(234,240,245,.16), rgba(234,240,245,0) 70%); margin-top: -3px; }
.site-mark.edge .site-mark-ground { display: none; }

/* The header row: the laboratory is a holder with a tool in it, and the things
   that do something stand beside it rather than inside it. */
.wb-scale { flex: 0 0 auto; width: auto; padding: 6px 14px; }
/* Columns that fold. Folded, a column is a rail with its name down the side. */
.wb-fold { display: flex; align-items: center; gap: 6px; width: 100%; background: none; border: 0; cursor: pointer;
           color: var(--scene-muted); font: 600 .74rem Roboto, Arial, sans-serif; letter-spacing: .08em; text-transform: uppercase;
           padding: 2px 0 4px; }
.wb-fold:hover { color: var(--scene-ink); }
.wb-fold:focus-visible { color: var(--scene-ink); outline: 2px solid var(--focus); outline-offset: 2px; }
.wb-fold-chevron { width: 7px; height: 7px; border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
                   transform: rotate(45deg); transition: transform .15s; }
.wb-fold[aria-expanded="false"] .wb-fold-chevron { transform: rotate(-45deg); }
.wb-fold-count { margin-left: auto; font-variant-numeric: tabular-nums; opacity: .85; }
/* A folded Events column has nothing left to show, so it becomes a rail. */
.wb-events-col.folded { flex: 0 0 30px; overflow: hidden; }
.wb-events-col.folded .wb-events-list, .wb-events-col.folded .wb-events-hint { display: none; }
.wb-events-col.folded .wb-fold {
  writing-mode: vertical-rl; height: 100%; width: 30px; justify-content: flex-start; padding: 6px 0; gap: 10px; }
.wb-events-col.folded .wb-fold-count { margin-left: 0; }

/* Folding Places narrows it rather than closing it: the place names stay,
   because they are the thing worth keeping in view, and the rock sampler
   keeps its whole width. A 30 px rail was narrower than the sampler's own
   36 px slot, which is what broke it. */
.wb-side.folded { flex: 0 0 auto; width: auto; max-width: 190px; overflow: hidden; }
.wb-side.folded .wb-tray-card { padding: 5px 8px; gap: 0; }
.wb-side.folded .wb-tray-card .wb-mini,
.wb-side.folded .wb-tray-card small { display: none; }
.wb-side.folded .wb-tray-text strong { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.wb-side.folded .wb-fold-count { margin-left: auto; }
/* The key to the drawing, big enough to read. */
.wb-names { font-size: .78rem; padding: 4px 12px; }

.wb-events-list { display: flex; flex-direction: column; gap: 8px; min-height: 0; overflow-y: auto; }
.wb-events-col { display: flex; flex-direction: column; min-height: 0; }

/* A placed event: a token against the number line. */
.wb-event-token { fill: rgba(11,14,18,.9); stroke: var(--focus-glow); stroke-width: 1.4; }
.wb-event.grabbed .wb-event-token, .wb-event:focus-visible .wb-event-token { stroke-width: 2.4; }

/* The offer to put two pieces together, on the band between them. */
.wb-group { cursor: pointer; }
.wb-group-box { fill: rgba(11,14,18,.86); stroke: var(--focus-glow); stroke-width: 1.4; }
.wb-group-mark { fill: var(--focus-glow); }
.wb-group:hover .wb-group-box, .wb-group:focus-visible .wb-group-box { fill: var(--focus-glow); }
.wb-group:hover .wb-group-mark, .wb-group:focus-visible .wb-group-mark { fill: #0b0e12; }
.wb-group:focus-visible { outline: none; }
.wb-group:focus-visible .wb-group-box { stroke-width: 2.6; }

/* A date lit because the bed it belongs to is under the pointer. */
.wb-tick.hot .wb-date { fill: #fff; font-weight: 700; }

/* Each tool leaves its holder in its own way.
   The animation lifts whatever is in the holder, the drawn icon or the
   photographed model, and it ends where the holder's own empty state begins.
   It used to be written for an <img> alone, which exists only once the baked
   models have loaded, so before that nothing moved and Ben was right that the
   animations were missing. */
.tool-belt button[data-tool="pick"][aria-pressed="true"] .tool-slot > * { animation: tool-pick .42s ease-out; }
@keyframes tool-pick { 0% { opacity: 1; transform: none; } 30% { opacity: 1; transform: rotate(-26deg); } 55% { opacity: 1; transform: rotate(12deg) translateY(-4px); } 100% { opacity: 0; transform: rotate(0) translateY(-7px) scale(.75); } }
@media (prefers-reduced-motion: reduce) {
  .tool-belt button[aria-pressed="true"] .tool-slot > * { animation: none; }
}

/* What the work was for. */
.wb-reward { position: absolute; left: 50%; top: 22%; transform: translateX(-50%); z-index: 8; width: min(420px, 80%);
  padding: 16px 18px; background: var(--panel); color: var(--scene-ink); border: 1px solid var(--focus-glow);
  border-radius: var(--radius-lg); box-shadow: 0 14px 40px rgba(0,0,0,.6); }
.wb-reward-title { margin: 0 0 6px; font-size: 1rem; color: var(--focus-glow); }
.wb-reward-body { margin: 0 0 12px; font-size: .88rem; line-height: 1.5; }
.wb-reward-actions { display: flex; gap: 8px; }
.wb-reward-actions .nb-btn { width: auto; }

/* Nothing a student reads is under twelve pixels: the field kit's own small
   print was eleven, which is a squint on a classroom projector. */
.tool-belt button small { font-size: .76rem; }
.wb-fold { font-size: .78rem; }

/* The survey list on the globe: its notes were the smallest print in the sim. */
.gp-notes { font-size: .8rem; }

/* Three columns, in the order the work happens: the places on the left with
   the sampler above them, the timeline in the middle, the events on the right
   with the marks above them. */
.wb-body > .wb-side { order: 1; }
.wb-body > .wb-time-shell { order: 2; }
.wb-body > .wb-events-col { order: 3; }
.wb-side .wb-lab { margin-bottom: 10px; }
.wb-side .wb-sampler { width: 100%; justify-content: flex-start; }
.wb-events-col .wb-marks { align-self: flex-start; margin-bottom: 8px; }
.wb-events-col.folded .wb-marks { display: none; }
/* The header is the way back and nothing else, so the timeline starts at the
   top of the panel. */

/* ── The globe, as a survey view ──────────────────────────────────────────
   A site is a mark on the Earth. Its name appears when the pointer or the
   keyboard is on it, or when it is the one being looked at, so sixteen places
   do not put sixteen labels between the student and the planet. */
/* A site the camera can see says who it is: name, region and how much of it has
   been read. The small diamond is the spot itself, under the words. */
/* The spot on the Earth is already drawn, as the anchor dot with its leader,
   so a near-side badge does not need a second mark inside it. The mark only
   does work round the far side, where it becomes the triangle. */
.pin-mark { display: none; }
.globe-pin.behind .pin-mark { display: block; }
.globe-pin.visited .pin-mark { background: var(--focus-glow); border-color: var(--focus-glow); }
.globe-pin.pending .pin-mark { border-style: dashed; }
.globe-pin.hot .pin-mark, .globe-pin:focus-visible .pin-mark, .globe-pin.selected .pin-mark {
  border-color: var(--focus-glow); box-shadow: 0 0 0 3px rgba(255,209,102,.25); }
/* Round the far side there is nothing to read at that distance, so the badge
   goes and a triangle on the limb points at where the place is. */
.globe-pin.behind { background: none; border: 0; box-shadow: none; padding: 0; }
.globe-pin.behind .pin-name, .globe-pin.behind small { display: none; }
/* Gold, not grey. A dark grey triangle on a black star field is the lowest
   contrast pairing in the sim, and this is the only sign that there is
   anything round the other side of the planet. Gold is already the sim's
   colour for a thing worth pressing. */
.globe-pin.behind .pin-mark { width: 0; height: 0; margin: 0; transform: rotate(var(--pin-turn, 0deg)); border: 0;
  border-left: 7px solid transparent; border-right: 7px solid transparent;
  border-bottom: 11px solid var(--focus-glow); background: none; box-shadow: none;
  filter: drop-shadow(0 0 3px rgba(0,0,0,.9)); }
.globe-pin.behind.visited .pin-mark { border-bottom-color: var(--focus-glow); }
.globe-pin.behind.hot .pin-mark, .globe-pin.behind:focus-visible .pin-mark {
  border-bottom-color: var(--focus-glow); filter: drop-shadow(0 0 4px rgba(255,209,102,.6)); }

/* ── The Earth card ───────────────────────────────────────────────────── */
.earth-card { position: absolute; left: 14px; top: 14px; width: min(300px, 34%); max-height: calc(100% - 28px);
  display: flex; flex-direction: column; gap: 10px; padding: 14px 14px 12px; overflow-y: auto;
  background: var(--panel); color: var(--scene-ink); border: 1px solid var(--border);
  border-radius: var(--radius-lg); box-shadow: var(--shadow); }
/* The mission is the heading of the first screen, so it is sized like one. */
.earth-mission { margin: 0 0 4px; font-size: 1.25rem; font-weight: 700; letter-spacing: .01em; }
.earth-name { margin: 0 0 2px; font-size: 1.2rem; font-weight: 700; }
.earth-task { margin: 0; font-size: .92rem; line-height: 1.5; }
.earth-facts { display: grid; grid-template-columns: 1fr auto; gap: 3px 10px; margin: 0; font-size: .82rem; }
.earth-facts dt { color: var(--scene-muted); }
.earth-facts dd { margin: 0; text-align: right; font-variant-numeric: tabular-nums; }
.earth-summary { margin: 0; font-size: .82rem; color: var(--scene-muted); line-height: 1.45; }

/* What a place tells you, once it has been read all the way through. */
/* Centred in the space the field kit leaves, not in the frame. A 560-wide
   card centred on a 1400-wide frame put its own right edge two pixels under
   the kit's left edge. */
.site-reward { position: absolute; top: 12%; left: 14px; right: calc(var(--rail) + var(--kit) + 26px);
  margin: 0 auto; z-index: 9; width: auto; max-width: 560px;
  display: flex; gap: 14px; padding: 14px; background: var(--panel); color: var(--scene-ink);
  border: 1px solid var(--focus-glow); border-radius: var(--radius-lg); box-shadow: 0 16px 44px rgba(0,0,0,.62); }
.site-reward-pic { width: 150px; height: 100px; object-fit: cover; border-radius: var(--radius-sm); flex: 0 0 auto; }
.site-reward-text { min-width: 0; }
.site-reward-kicker { margin: 0; font-size: .72rem; letter-spacing: .1em; text-transform: uppercase; color: var(--focus-glow); }
.site-reward-title { margin: 2px 0 6px; font-size: 1.05rem; }
.site-reward-body { font-size: .86rem; line-height: 1.5; }
.site-reward-body p { margin: 0 0 6px; }
.site-reward-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.site-reward-actions .nb-btn { width: auto; }
/* A claim in the field kit is a thing to press, and it says so. */

/* A card a finding is about says so itself, in the rose the marks pill uses.
   Not the amber of --focus-glow, which is what an unflagged card already
   wears, so a student could not tell the two apart. */
.wb-event.flagged .wb-event-token { stroke: #ff8a80; stroke-width: 2.2; fill: rgba(255,138,128,.18); }
.wb-event.flagged .wb-event-box { stroke: #ff8a80; stroke-width: 2.2; fill: rgba(38,16,16,.94); }
.wb-event.flagged .wb-event-line { stroke: #ff8a80; opacity: 1; stroke-dasharray: none; }
.wb-event.flagged .wb-event-label { fill: #ffb3aa; }

/* ── The rails ─────────────────────────────────────────────────────────────
   Navigation lives at the edges and costs the frame no width. The left rail
   is where you came from, the right rail is the workbench, and which of them
   is present is the whole of the navigation state: on the globe there is
   nowhere back to, and at a site the workbench arrives once a place has been
   read all the way through. */
.rail {
  position: absolute; top: 50%; transform: translateY(-50%); z-index: 8;
  width: var(--rail); min-height: 216px;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px;
  padding: 16px 0; cursor: pointer;
  border: 1px solid var(--border); background: var(--panel); color: var(--scene-ink);
  box-shadow: var(--shadow);
}
.rail:hover { background: rgba(255,255,255,.07); }
.rail:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.rail-left  { left: 0; border-left-width: 0;  border-radius: 0 var(--radius-lg) var(--radius-lg) 0; }
.rail-right { right: 0; border-right-width: 0; border-radius: var(--radius-lg) 0 0 var(--radius-lg); }
.rail-name {
  writing-mode: vertical-rl; white-space: nowrap;
  font-size: .82rem; font-weight: 600; letter-spacing: .05em;
}
.rail-left .rail-name { transform: rotate(180deg); }
.rail-icon { flex: 0 0 auto; width: 26px; height: 26px; display: grid; place-items: center; }
.rail-icon .mini-earth { width: 26px; height: 26px; }
.rail-pic { width: 26px; height: 26px; border-radius: 50%; object-fit: cover; }
.rail-bench-icon { width: 26px; height: 26px; border-radius: 6px; background: rgba(255,255,255,.06); border: 1px solid var(--border); }
.rail-bench-icon svg { width: 20px; height: 20px; }
/* A chevron for the direction the rail goes. */
.rail-chev { width: 7px; height: 7px; border-top: 2px solid var(--scene-muted); border-right: 2px solid var(--scene-muted); }
.rail-left  .rail-chev { transform: rotate(-135deg); }
.rail-right .rail-chev { transform: rotate(45deg); }
/* The workbench rail is earned at a site, so the first time it appears it
   arrives, rather than simply being there. */
@keyframes rail-arrive { from { transform: translate(100%, -50%); opacity: 0; } to { transform: translate(0, -50%); opacity: 1; } }
.rail-right.arriving { animation: rail-arrive .42s cubic-bezier(.2,.9,.3,1) 1; }
@media (prefers-reduced-motion: reduce) { .rail-right.arriving { animation: none; } }

/* The panels docked at the edges stand clear of whichever rail is present. */
.rail-l .place-card, .rail-l .earth-card { left: calc(var(--rail) + 12px); }
.rail-r .site-card, .rail-r .notebook { right: calc(var(--rail) + 12px); }
.rail-r .site-marks, .rail-l .site-marks { right: 0; }
#workbench { padding-left: var(--rail); }

/* ── The workbench as a drawer ──────────────────────────────────────────────
   It arrives from the edge its own rail stands on, and it leaves the same
   way, so the two rails read as opposite directions rather than one direction
   twice. It sits under the rails, which stay put while it travels.

   `hidden` cannot do this: display:none has nothing to animate from. So the
   panel is parked off-screen and made invisible instead. The visibility flip
   is immediate on the way in and held back by the length of the slide on the
   way out, so the drawer is never invisible while it is moving, and never
   focusable once it has gone. */
#workbench { z-index: 6; transform: translateX(0); }
#workbench.wb-off { transform: translateX(100%); visibility: hidden; }
#workbench.wb-sliding { transition: transform var(--slide) cubic-bezier(.22,.85,.3,1), visibility 0s; will-change: transform;
  /* A press mid-slide would be measured against a moving transform. */
  pointer-events: none; }
#workbench.wb-off.wb-sliding { transition: transform var(--slide) cubic-bezier(.22,.85,.3,1), visibility 0s linear var(--slide); }
@media (prefers-reduced-motion: reduce) { #workbench.wb-sliding { transition: none; } }

/* A place with nothing collected is a trip rather than a piece. */
.wb-tray-card.goes { cursor: pointer; }
.wb-tray-card.goes:hover, .wb-tray-card.goes:focus-visible { border-color: var(--focus-glow); background: rgba(255,255,255,.05); }

/* ── The bench of tools ────────────────────────────────────────────────────
   Out of the field kit and across the bottom middle, which is where a hand
   reaches for a tool, and which leaves the whole of the right-hand card to
   the layers and their readings. */
.tool-bench {
  position: absolute; left: 50%; bottom: 16px; transform: translateX(-50%); z-index: 7;
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  padding: 10px 12px 8px;
  background: var(--panel); color: var(--scene-ink);
  border: 1px solid var(--border); border-radius: var(--radius-lg); box-shadow: var(--shadow);
}
.tool-bench[hidden] { display: none !important; }
.tool-hint { margin: 0; max-width: 34ch; text-align: center; font-size: .78rem; line-height: 1.3; color: var(--scene-muted); }
.tool-hint[hidden] { display: none; }
.tool-bench .tool-belt { display: flex; gap: 8px; margin: 0; }
/* Centred in the frame while there is room for that, and centred in the space
   the field kit leaves once there is not. Ben, at 1000 px: "the pick axe card
   will sometimes overlap with the field kit when the field kit is tall."
   Measured with ten layers read and a row open: the bench clears the kit by
   127 px at 1280 and by 37 px at 1100, then overlaps it by 13 px at 1000, 63
   at 900 and 103 at 820. The two only collide once the kit grows tall enough
   to reach the bottom of the frame, which is why it comes and goes.
   The breakpoint is 1100 rather than 1026, where the arithmetic says the
   English overlap begins, because the buttons carry their labels and Spanish
   labels are longer.
   Only `left` moves. The transform stays, because every keyframe of
   bench-going and bench-coming carries translateX(-50%) and a keyframe that
   sets transform at all replaces the whole of it. */
@media (max-width: 1100px) {
  .tool-bench { left: calc((100% - var(--rail) - var(--kit) - 12px) / 2); }
}

/* ── Changing where you look from ─────────────────────────────────────────── */
.place-moves { display: flex; flex-direction: column; gap: 6px; padding: 0 12px 10px; }
.place-moves .nb-btn { width: 100%; }
.place-moves .nb-btn[hidden] { display: none !important; }

/* A card that is already on the timeline keeps its place in the tray, so
   nothing slides under the hand of a student reaching for its neighbour. */
/* An event out on the timeline. It used to wear a mint tick at 45 per cent,
   which said finished in the colour this sim keeps for a verdict. It carries
   the same mark the places carry instead, and it is not ghosted, because a
   card on the board is in play. */
.wb-event-card.placed { opacity: .72; cursor: default; }
.wb-event-card.placed .wb-out-mark { margin-left: auto; }

/* What the student's own evidence makes of a placed card. Supported is the
   sim's green, unsupported is muted rather than alarming (nothing is wrong,
   nothing has been read yet), and contradicted keeps the marks' rose. */
/* 9.5 px was smaller than anything else in the sim. One line, legible, and
   right-aligned to the card above it so it never reaches out over the layers. */
.wb-event-standing { font: 500 11px Roboto, Arial, sans-serif; fill: var(--scene-muted); pointer-events: none; }
/* Held up by the student's own evidence. This used to colour the token's
   outline and nothing else, while being contradicted colours the token, the
   box, the leader line and the label: so the wrong answer announced itself
   across the whole card and the right one changed one small ring. The two
   states say the same amount now, in the two colours this sim already uses
   for them. */
.wb-event.st-supported .wb-event-token { stroke: #9be7c4; stroke-width: 2.2; fill: rgba(155,231,196,.18); }
.wb-event.st-supported .wb-event-box { stroke: #9be7c4; stroke-width: 2.2; fill: rgba(16,34,26,.94); }
.wb-event.st-supported .wb-event-line { stroke: #9be7c4; opacity: 1; stroke-dasharray: none; }
.wb-event.st-supported .wb-event-label { fill: #b9f6c4; }
.wb-event.st-supported .wb-event-standing { fill: #9be7c4; }
/* Being contradicted still wins where both could apply: a card the evidence
   argues against is the more urgent thing to say. */
.wb-event.flagged.st-supported .wb-event-token { stroke: #ff8a80; fill: rgba(255,138,128,.18); }
.wb-event.flagged.st-supported .wb-event-box { stroke: #ff8a80; fill: rgba(38,16,16,.94); }
.wb-event.flagged.st-supported .wb-event-line { stroke: #ff8a80; }
.wb-event.flagged.st-supported .wb-event-label { fill: #ffb3aa; }
.wb-event.st-unsupported .wb-event-standing { fill: var(--scene-muted); }
.wb-event.st-contradicted .wb-event-standing { fill: #ffb3aa; }
/* Taking a card off again, because trial and error needs the error to be
   reversible. */
.wb-event-off { cursor: pointer; opacity: 0; transition: opacity .12s; }
.wb-event:hover .wb-event-off, .wb-event:focus-within .wb-event-off { opacity: 1; }
.wb-event-off circle { fill: rgba(11,14,18,.92); stroke: var(--scene-muted); stroke-width: 1.2; }
.wb-event-off line { stroke: var(--scene-ink); stroke-width: 1.6; stroke-linecap: round; }
.wb-event-off:hover circle, .wb-event-off:focus-visible circle { stroke: #ff8a80; }
.wb-event-info { cursor: pointer; }
.wb-event-info circle { fill: rgba(11,14,18,.92); stroke: var(--scene-muted); stroke-width: 1.2; }
.wb-event-info text { fill: var(--scene-ink); font: 700 10px/1 Roboto, sans-serif; pointer-events: none; }
.wb-event-info:hover circle, .wb-event-info:focus-visible circle { stroke: var(--focus-glow); }
.wb-event-info:focus-visible { outline: none; }

/* A cross-site recognition. Green where the evidence carries a correlation,
   muted where it only carries an overlap in time, because the two license
   different things and the sim must not blur them. */
.fn-link { margin: 8px 0 0; padding: 8px 10px; font-size: .82rem; line-height: 1.45;
  border-radius: var(--radius-sm); background: rgba(255,255,255,.05); }
.fn-link.firm { color: #cdEFdd; background: rgba(155,231,196,.10); }
.fn-link.soft { color: var(--scene-ink); }
.fn-link[hidden] { display: none !important; }

/* Each field tool in a holder, the way the rock sampler is: taking one out
   should look like lifting an object off a rack, not selecting a radio
   button. The slot stays behind when the tool is in hand. */
.tool-slot {
  width: 40px; height: 40px; border-radius: 8px; display: grid; place-items: center;
  flex: 0 0 auto; margin-bottom: 2px;
  background: rgba(0,0,0,.38); border: 1px solid rgba(255,255,255,.16);
  box-shadow: inset 0 2px 6px rgba(0,0,0,.55);
}
.tool-slot svg { width: 22px; height: 22px; }
.tool-slot .tool-img { width: 34px; height: 34px; transition: opacity .18s, transform .18s; }
.tool-belt button { padding-top: 8px; }
.tool-belt button[aria-pressed="true"] .tool-slot {
  border-style: dashed; border-color: rgba(0,0,0,.3); background: rgba(0,0,0,.5); }
/* Out of the holder: the tool lifts away and the slot is left empty. */
.tool-belt button[aria-pressed="true"] .tool-slot svg,
.tool-belt button[aria-pressed="true"] .tool-slot .tool-img { opacity: 0; transform: translateY(-7px) scale(.75); }
.tool-belt button[aria-pressed="true"] .tool-slot::after {
  content: ""; width: 18px; height: 3px; border-radius: 2px; background: rgba(0,0,0,.35); }

/* The lens is an action, so it is a button on the bench and not one of the
   two tools that can be in a hand. Made plainly different, because three
   identical squares told a student these were three equal jobs. */
#tool-belt button[data-tool="lens"] {
  flex: 1.3; background: rgba(155,231,196,.12); border-color: rgba(155,231,196,.45);
}
#tool-belt button[data-tool="lens"]:hover { background: rgba(155,231,196,.2); }
#tool-belt button[data-tool="lens"] .tool-slot { background: rgba(155,231,196,.14); border-color: rgba(155,231,196,.4); }
/* While the face is being read. */
#tool-belt.reading button[data-tool="lens"] .tool-slot { animation: tool-reading .5s ease-in-out infinite alternate; }
@keyframes tool-reading { from { box-shadow: inset 0 2px 6px rgba(0,0,0,.55); } to { box-shadow: inset 0 2px 6px rgba(0,0,0,.55), 0 0 0 3px rgba(155,231,196,.4); } }
@media (prefers-reduced-motion: reduce) { #tool-belt.reading button[data-tool="lens"] .tool-slot { animation: none; } }
/* The one card a sweep leaves behind sits under the field kit's heading
   rather than at a press point, because a sweep has no press point. */
.finding-note.fn-sweep { left: auto !important; right: calc(var(--rail) + var(--kit) + 26px); top: 54px !important; }

/* ── The layers, as one accordion ──────────────────────────────────────────
   A row for each layer, youngest at the top, carrying the rock's own colour
   and the pattern its kind is drawn with once the lens has named it. Opening
   a row reveals everything else about that layer. This is the only place a
   layer is described now; it used to be three, and keeping those three in
   step was most of the UI debt in the site view.

   Uniform rows give up the drawn column's sense of thickness. The thickness
   is stated in the open row instead, and true proportion is what the
   workbench's See it to scale is for. This panel's job is the order. */
.nb-strata { display: flex; flex-direction: column; gap: 3px; padding: 6px 8px 8px; }
.strat { border: 1px solid var(--border); border-radius: var(--radius-sm); overflow: hidden;
  background: rgba(255,255,255,.03); }
.strat.unread { background: transparent; }
.strat.elsewhere { border-style: dashed; }
.strat.open { background: rgba(255,255,255,.07); border-color: rgba(255,209,102,.5); }
.strat-row { display: flex; align-items: center; gap: 8px; width: 100%; text-align: left;
  padding: 6px 8px; border: 0; background: transparent; color: var(--scene-ink); font: inherit; cursor: pointer; }
/* An unread layer has nothing behind it, but one that is exposed at the
   other stop of this place holds the way there. */
.strat.unread:not(.elsewhere) .strat-row { cursor: default; }
/* Ben: "I didn't even notice the hover states and highlighting of the
   accordions." A six per cent white wash on a dark panel is about two levels
   of grey. A row under the pointer now moves, brightens and turns its name
   amber. No bar down the edge: it read as machine-made. */
.strat-row { position: relative; transition: background .12s, transform .12s; }
.strat:not(.unread):not(.elsewhere) .strat-row:hover,
.strat:not(.unread):not(.elsewhere) .strat-row.hover { background: rgba(255,255,255,.13); transform: translateX(2px); }
.strat:not(.unread):not(.elsewhere) .strat-row:hover .strat-name,
.strat:not(.unread):not(.elsewhere) .strat-row.hover .strat-name { color: var(--focus-glow); }
@media (prefers-reduced-motion: reduce) { .strat-row:hover { transform: none; } }
.strat-row:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }
.strat-swatch { flex: 0 0 auto; width: 30px; height: 26px; border-radius: 3px;
  border: 1px solid rgba(0,0,0,.45); }
.strat.unread .strat-swatch { border: 1px dashed rgba(234,240,245,.4); }
.strat-text { display: flex; flex-direction: column; min-width: 0; flex: 1; line-height: 1.25; }
.strat-name { font-size: .84rem; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.strat.unread .strat-name { font-weight: 500; color: var(--scene-muted); }
.strat-sub { font-size: .68rem; color: var(--scene-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Which tools have been used here, drawn as the tools themselves. */
.strat-marks { display: flex; align-items: center; gap: 4px; flex: 0 0 auto; }
.strat-mark { width: 12px; height: 12px; color: #9be7c4; }

/* The pick a read layer carries, and the swing that put it there. Ben: "let's
   have it come in from the left, but then continue moving in a straight line
   until it reaches the spot [the marks slot], and the pickaxe can do a small
   rotation like it's picking at the bottom border of the accordian... then the
   pickaxe icon can remain on the right side of the accordian".
   Nothing is drawn over the swatch: the pattern there is the thing the sim is
   teaching, and a mark across it covers the lesson to report on it. */
.strat-pick { flex: 0 0 auto; width: 18px; height: 18px; color: var(--focus-glow); }
.strat-pick-svg { display: block; width: 100%; height: 100%; }
/* The handle's lower end is the hand, so that is what the swing turns about,
   and the strike is at 70%, where the row starts opening.
   Which way the blow goes is not a matter of taste. The head's lowest corner
   sits up and to the LEFT of that pivot, so turning the tool anticlockwise
   brings the head down and turning it clockwise lifts it. The first version
   had this backwards: it wound to -26 and struck at +19, so the head was
   RISING on the blow, and the lowest it ever got was during the wind-up.
   Measured: 3.3 px down at the wind-up against 1.4 px down at the blow. */
.strat-pick.swinging { transform-origin: 50% 88%; animation: pick-in .62s cubic-bezier(.32,0,.26,1) both; }
@keyframes pick-in {
  0%   { transform: translateX(calc(-1 * var(--pick-travel, 300px))); }
  38%  { transform: translateX(0) rotate(0deg); }
  56%  { transform: rotate(30deg); }              /* wind up: the head lifts */
  70%  { transform: translateY(3px) rotate(-34deg); }  /* the blow: it drives down */
  80%  { transform: rotate(9deg); }
  90%  { transform: rotate(-6deg); }
  100% { transform: none; }
}
/* The blow, on the edge the body opens from. `::before` on this row is the
   hover bar, so the flash is `::after`. */
.strat-row::after { content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 2px;
  background: var(--focus-glow); opacity: 0; pointer-events: none; }
.strat.swinging .strat-row::after { animation: pick-hit .62s linear both;
  animation-delay: var(--swing-at, 0ms); }
@keyframes pick-hit { 0%, 64% { opacity: 0; } 71% { opacity: 1; } 100% { opacity: 0; } }
@media (prefers-reduced-motion: reduce) {
  .strat-pick.swinging { animation: none; }
  .strat.swinging .strat-row::after { animation: none; }
}
.strat-chev { flex: 0 0 auto; width: 6px; height: 6px; margin-right: 2px;
  border-right: 2px solid var(--scene-muted); border-bottom: 2px solid var(--scene-muted);
  transform: rotate(45deg); transition: transform .15s; }
.strat-row[aria-expanded="true"] .strat-chev { transform: rotate(-135deg); }
.strat-body { padding: 2px 10px 10px; font-size: .82rem; line-height: 1.45; }
.strat-where { margin: 6px 0 0; color: var(--scene-muted); font-size: .74rem; }
.strat-find { margin: 10px 0 0; }
.strat-find-h { display: flex; align-items: center; gap: 5px; margin: 0 0 3px;
  font-size: .7rem; letter-spacing: .08em; text-transform: uppercase; color: var(--focus-glow); }
.strat-find-t { margin: 3px 0 0; }
.strat-hint { margin: 10px 0 0; font-size: .76rem; color: var(--scene-muted); }
.strat-go { margin-top: 8px; }
/* What lies between two layers, and so has no row of its own. */
.strat-gap { margin: 1px 0; padding: 4px 8px; font-size: .72rem; line-height: 1.4;
  color: #ffd166;
  background: repeating-linear-gradient(135deg, rgba(255,209,102,.13) 0 5px, transparent 5px 10px); }
.strat-gap.thrust { color: #c8a2ff;
  background: repeating-linear-gradient(135deg, rgba(200,162,255,.13) 0 5px, transparent 5px 10px); }
/* A layer arriving during the reveal, in the order it is read: the colour and
   its pattern first, then the name, then the rock it is made of. All three at
   once was too much to take in at one beat, and keying the animation to "read"
   rather than to "just read" replayed it on every row already named. */
.strat.just .strat-swatch { animation: strat-in .34s ease-out both; }
.strat.just .strat-name { animation: strat-line .3s ease-out .16s both; }
.strat.just .strat-sub { animation: strat-line .3s ease-out .32s both; }
@keyframes strat-in { from { opacity: 0; transform: scale(.6); } to { opacity: 1; transform: none; } }
@keyframes strat-line { from { opacity: 0; transform: translateY(3px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) {
  .strat.just .strat-swatch, .strat.just .strat-name, .strat.just .strat-sub { animation: none; }
}
/* A tool that is not available yet says so by looking unavailable. */
#tool-belt button:disabled { opacity: .4; cursor: not-allowed; }
#tool-belt button:disabled .tool-slot { box-shadow: none; }

/* The cross-site receipt, as a line in the row that earned it rather than in
   a card that flashed past. */
.strat-find .fn-link { margin: 5px 0 0; font-size: .76rem; line-height: 1.4;
  padding-left: 8px; }
.strat-find .fn-link.firm { color: #9be7c4; }
.strat-find .fn-link.soft { color: var(--scene-muted); }

/* A tool arriving on the bench, once it has something to do. Not a disabled
   square waiting to be ruled out: absent, then present. */
#tool-belt button.tool-new { animation: tool-arrive .34s ease-out; }
@keyframes tool-arrive { from { opacity: 0; transform: translateY(16px) scale(.94); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { #tool-belt button.tool-new { animation: none; } }

/* The world opening: the fifteen badges that were held back arrive together. */
#globe-pins.arriving .globe-pin { animation: pin-arrive .5s ease-out; }
@keyframes pin-arrive { from { opacity: 0; transform: scale(.72); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { #globe-pins.arriving .globe-pin { animation: none; } }

/* The drop area, outlined while the timeline is empty. Dashed because it is a
   region and not a slot: the first piece means the same thing wherever in it
   the student puts it. */
.wb-drop-hint { fill: rgba(255,255,255,.02); stroke: rgba(234,240,245,.3); stroke-width: 1.5;
  stroke-dasharray: 8 7; }
.wb-drop-text { fill: var(--scene-muted); font-size: 13px; font-family: Roboto, Arial, sans-serif; }

/* What to do next, on the timeline rather than in a band above it. */
.wb-step { fill: var(--scene-ink); font-size: 13px; font-family: Roboto, Arial, sans-serif; }


/* ── The bench answers the pointer ─────────────────────────────────────────
   A place in the column had a grab cursor and a focus ring and nothing
   between them, so there was nothing to say it could be picked up until it
   had been. */
.wb-tray-card:not(.empty):hover { border-color: rgba(234,240,245,.5); background: rgba(255,255,255,.07); }
.wb-tray-card:not(.empty):hover .wb-tray-text strong { color: var(--focus-glow); }

/* A piece on the timeline answered by showing its rock names in full, which
   is easy to read as the drawing redrawing itself. Now the piece itself
   lifts, a lighter version of what being held looks like. */
.wb-piece:hover rect { filter: drop-shadow(0 0 4px rgba(234,240,245,.45)); }
.wb-piece.grabbed:hover rect { filter: drop-shadow(0 0 6px rgba(255,209,102,.7)); }

/* Where a thing can be put down. The timeline column already carries a
   transparent rectangle so that a press on empty space still counts as a
   press on the timeline; it is the drop zone, and it lights while anything is
   in hand. */
.wb-dragging .wb-time-bg { fill: rgba(92,225,255,.055); stroke: rgba(92,225,255,.5);
  stroke-width: 2; stroke-dasharray: 10 8; }
@media (prefers-reduced-motion: no-preference) {
  .wb-dragging .wb-time-bg { animation: wb-zone .9s ease-in-out infinite alternate; }
  @keyframes wb-zone { from { stroke-opacity: .35; } to { stroke-opacity: .85; } }
}
/* Nothing to aim at while a piece is being torn along a slid surface. */
.wb-dragging.armed-boundary .wb-time-bg { stroke: none; }

/* What the layers do here: the reasoning is prose, and the button is a
   button. The reasoning used to be printed INSIDE the button with the action
   in small print underneath it, which buried the one thing a press does. */

/* One picture at a time, with a strip to change it. Kaibab carries five
   fossils; five stacked figures in a 300 px column was the worst row in the
   sim, and Ben asked for a way to see each one on its own. */
.fn-thumb { width: 40px; height: 30px; padding: 0; border: 1px solid var(--border); border-radius: 3px;
  background: none; cursor: pointer; overflow: hidden; opacity: .55; transition: opacity .12s, border-color .12s; }
.fn-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.fn-thumb:hover { opacity: .85; }
.fn-thumb.on { opacity: 1; border-color: var(--focus-glow); }
.fn-thumb:focus-visible { outline: 2px solid var(--focus); outline-offset: 1px; }

/* The credits, in the About dialog where a reference belongs. */
.credits-list { list-style: none; margin: 8px 0 0; padding: 0; display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 10px 16px; }
.credits-list li { display: flex; gap: 9px; align-items: flex-start; font-size: .84rem; }
.credits-thumb { width: 54px; height: 40px; object-fit: cover; border-radius: 3px; flex: 0 0 auto; }
.credits-list small { color: #767676; }

/* The band's press target: invisible, drawn after the piece bars so they do
   not swallow the press, and the whole band rather than two dashed lines. */
.wb-lock-hit { fill: transparent; cursor: pointer; }

/* ── What to do next, pointed at ───────────────────────────────────────────
   Ben's sequence: the place to take, then the rock sampler, then a layer on
   the piece, then one word about what just happened. A pulse rather than a
   narration, so it costs no audio and a student who skips a tour still has
   something pointing at the next thing. */
@keyframes coach-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(255,209,102,0); }
  50%      { box-shadow: 0 0 0 5px rgba(255,209,102,.42); }
}
.coach-pulse { animation: coach-pulse 1.5s ease-in-out infinite; border-color: var(--focus-glow) !important; }
@media (prefers-reduced-motion: reduce) {
  .coach-pulse { animation: none; box-shadow: 0 0 0 3px rgba(255,209,102,.5); }
}
/* The bed on the placed piece, which is drawn rather than boxed. */
.wb-coach-bed { fill: none; stroke: var(--focus-glow); stroke-width: 2.5; }
@media (prefers-reduced-motion: no-preference) {
  .wb-coach-bed { animation: coach-bed 1.5s ease-in-out infinite; }
  @keyframes coach-bed { 0%, 100% { stroke-opacity: .35; } 50% { stroke-opacity: 1; } }
}

/* One word of encouragement, once. */
.wb-coach { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); z-index: 12;
  /* Opaque. --card is 72 per cent, and a dialog read over a drawn timeline
     needs its own background. */
  width: min(400px, calc(100% - 40px)); padding: 16px 18px; background: #26323e;
  border: 1px solid var(--focus-glow); border-radius: var(--radius);
  box-shadow: 0 16px 42px rgba(0,0,0,.6); }
.wb-coach-title { margin: 0 0 6px; font-size: 1.02rem; font-weight: 700; color: var(--scene-ink); }
.wb-coach-body { margin: 0 0 12px; font-size: .86rem; line-height: 1.5; color: var(--scene-ink); }
.wb-coach-actions { display: flex; justify-content: flex-end; }

/* ── A bed's fuller card: its own photographs ──────────────────────────────
   Appended after the existing name/place/body of #wb-detail, only when a
   pressed bed's fossils have a match in assets/img/credits.json and only
   once the fossil pick has actually run there. Wraps so two or three
   pictures for one layer do not force the card wider than the panel. */

/* ── Travel here: the second press for an unvisited place ──────────────────
   The tray card's own press only reveals this; only the button itself hands
   off to the globe (see renderTray). Sized to sit under the place name
   rather than stretch the way a full-width nb-btn would in this narrow
   card. */
/* Gold on dark ink, which is what "press this" looks like everywhere else in
   the sim. It was the Tuva green on a dark panel, two dark things together.
   And it arrives rather than appearing, because a button that materialises
   under the pointer is easy to press without having read. */
.wb-travel-btn { display: inline-block; width: auto; margin-top: 5px; padding: 5px 11px;
  font-size: .78rem; font-weight: 700; border-radius: var(--radius-sm);
  background: var(--focus-glow); border: 1px solid var(--focus-glow); color: #241c05; cursor: pointer;
  animation: travel-in .26s cubic-bezier(.2,.9,.3,1) both; }
@keyframes travel-in { from { opacity: 0; transform: translateY(-4px) scale(.94); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .wb-travel-btn { animation: none; } }
.wb-travel-btn:hover { background: #ffdd8a; border-color: #ffdd8a; }
.wb-travel-btn:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }

/* ── One picture, big, with a carousel under it ─────────────────────────────
   Ben: "can we make the large image larger please? ... Can it be a carousel
   of images under it and then have the larger image take full width in its
   column?" So the figure and the strip stack rather than sharing a flex row,
   the picture takes the whole column at 4:3, and the thumbnails run in one
   scrolling line that snaps, instead of wrapping onto a second row and
   burying the last of Kaibab's five. */
.fn-pics { display: flex; flex-direction: column; align-items: stretch; gap: 6px; margin: 6px 0 8px; }
.fn-pics figure { margin: 0; width: 100%; }
.fn-pics .fn-gloss { margin: 4px 0 0; font-size: .76rem; line-height: 1.35; color: var(--scene-muted); }
.fn-pics figure img, .fn-pics .fn-readout {
  display: block; width: 100%; aspect-ratio: 4 / 3; max-height: 260px; object-fit: cover;
  border-radius: 6px; border: 1px solid var(--border); background: #0b0e12; }
/* A readout that only says "there is nothing here" is half the height of one
   that draws something. Its own artwork is redrawn to fill the shorter box,
   so the panel is shorter rather than the drawing being letterboxed in it. */
.fn-pics .fn-readout.compact { aspect-ratio: 8 / 3; max-height: 130px; }
/* The name of what a picture is of now stands above it, with the tool that
   found it, so an open row can be skimmed. Under the picture it read as a
   credit line. */
.strat-find-h .fh-ico { flex: 0 0 auto; width: 13px; height: 13px; color: var(--focus-glow); }
.fn-pics .strat-find-h { margin: 2px 0 0; }
.fn-strip { display: flex; flex-wrap: nowrap; gap: 4px; margin-top: 1px;
  overflow-x: auto; scroll-snap-type: x proximity; padding-bottom: 2px; }
.fn-thumb { flex: 0 0 auto; width: 52px; height: 40px; scroll-snap-align: start; }

/* ── A finding that has just arrived ───────────────────────────────────────
   Ben: "the new section of information + pictures should pop-in to signify
   that new information has been added. Maybe even give it a slightly
   different color background for a few beats before setting the background
   to what it normally is."

   Keyed on `.just`, which notebook.js puts on the one block named by
   state.justFound for the single render after a fresh press and then clears.
   Keying it on the block merely existing would replay it on every later
   render, which is exactly the bug that made the rows re-pop through the
   whole reveal. The green is the sim's "your evidence carries this" colour. */
.strat-find.just { animation: finding-pop 1.9s ease-out .08s both; border-radius: 6px; }
@keyframes finding-pop {
  0%   { opacity: 0; transform: translateY(5px) scale(.97); background: rgba(155,231,196,.24); }
  10%  { opacity: 1; transform: none; }
  65%  { background: rgba(155,231,196,.24); }
  100% { background: transparent; }
}
@media (prefers-reduced-motion: reduce) { .strat-find.just { animation: none; } }

/* ── The completion mark ───────────────────────────────────────────────────
   A layer that has been opened up and recorded. Not the pick, because the
   pick is no longer the point: one press does the looking. Bigger than the
   old tool glyph so it reads at a glance. */
.strat-mark.strat-complete { width: 15px; height: 15px; }

/* Two ways back from the bench, sharing the left edge: the globe above, the
   place the bench was opened from below. A single rail is centred, so the
   pair is placed off the same midline rather than given a new layout. */
.rail.rail-pair-top    { top: calc(50% - 7px); transform: translateY(-100%); min-height: 168px; }
.rail.rail-pair-bottom { top: calc(50% + 7px); transform: none; min-height: 168px; }

/* A row easing open, measured from its own content by notebook.js so a short
   row eases like a tall one. The height is cleared once it has arrived, so
   nothing below is clipped afterwards. */
.strat.open > .strat-body { overflow: hidden; transition: max-height .34s cubic-bezier(.2,.8,.3,1); }
@media (prefers-reduced-motion: reduce) {
  .strat.open > .strat-body { transition: none; }
}

/* ── How the timeline is shown ─────────────────────────────────────────────
   Showing every rock name and seeing it to scale are ways of LOOKING at the
   timeline, not things to do to it, so they fold into a gear in the quiet
   corner rather than taking a strip of height under the drawing. Bottom
   right: the top carries the piece names and the next step. */
.wb-views { position: absolute; right: 10px; bottom: 10px; z-index: 8; }
.wb-views-btn { display: grid; place-items: center; width: 32px; height: 32px; padding: 0;
  border-radius: 999px; border: 1px solid var(--border); background: var(--panel);
  color: var(--scene-muted); cursor: pointer; box-shadow: var(--shadow); }
.wb-views-btn svg { width: 19px; height: 19px; }
.wb-views-btn:hover, .wb-views-btn[aria-expanded="true"] { color: var(--focus-glow); border-color: var(--focus-glow); }
.wb-views-btn:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.wb-views-menu { position: absolute; right: 0; bottom: 38px; min-width: 190px; padding: 5px;
  background: #182029; border: 1px solid var(--border); border-radius: var(--radius-sm);
  box-shadow: 0 12px 30px rgba(0,0,0,.55); display: flex; flex-direction: column; gap: 2px; }
.wb-views-item { display: flex; align-items: center; gap: 8px; width: 100%; text-align: left;
  padding: 7px 10px; font: inherit;
  font-size: .82rem; background: transparent; border: 0; border-radius: 4px;
  color: #eaf0f5; cursor: pointer; }
.wb-views-item:hover { background: rgba(255,255,255,.12); color: #fff; }
/* A view that is on says so with a mark as well as a colour: colour on its
   own is not a state a student who cannot see it can read. */
.wb-views-item[aria-checked="true"] { color: var(--focus-glow); font-weight: 600; }
.wb-views-item[aria-checked="true"]::after { content: "\2713"; margin-left: auto; font-weight: 700; }
.wb-views-item:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }

/* The offer to come apart shows only under the pointer or in focus. It is not
   clickable while it is invisible: an unseen button that separates two pieces
   is worse than no button. The band underneath does the same thing, so
   nothing is lost by making the hidden one inert. */
/* The way to separate two joined pieces stands where the join is, always.
   Ben: "once i join two layers together, i would actually like the unjoin
   button to be visible. right now it reveals itself on hover, and it becomes
   hidden and a user will struggle with discovering how to unjoin layers." */
.wb-group-label { fill: var(--scene-ink); font: 600 9.5px/1 Roboto, sans-serif; paint-order: stroke; stroke: rgba(11,14,18,.85); stroke-width: 3px; pointer-events: none; }
.wb-group.undo { opacity: .72; pointer-events: auto; transition: opacity .12s; }
.wb-band.joined:hover .wb-group.undo, .wb-group.undo:hover,
.wb-group.undo:focus-visible { opacity: 1; }

/* On a touch screen there is no hover, so an offer that only appears on hover
   never appears. It used to be reachable anyway, because pressing the band did
   the same thing; now that joining and separating need their own button, that
   way is gone and this is the only one. So on a device that cannot hover, the
   offer to separate is simply there. */
/* `(hover: none)` alone is not the test: a headless browser, and a desktop
   with no pointing device attached at that moment, both report it, and the
   button would then sit visible on every joined band on a desktop, which is
   the thing Ben asked it not to do. The pair is the reliable test. */
/* The touch rule that used to make this visible is gone: it is visible on
   every device now, so there is nothing left for it to do. */

/* A section of the bench arriving. The laboratory and the events are held back
   until the first piece is on the timeline, so the bench a student first meets
   is a list of places and somewhere to put them. */
@media (prefers-reduced-motion: no-preference) {
  .wb-arriving { animation: wb-arrive .42s cubic-bezier(.2,.9,.3,1) both; }
  @keyframes wb-arrive { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
}

/* The lens leaving, and its replacement arriving after it. Two movements
   rather than one frame, so the swap has an account of itself. */
@media (prefers-reduced-motion: no-preference) {
  /* The WHOLE bench, not a button inside it. A button sliding within its own
     panel is a small movement inside a frame that does not move, and the
     frame is what the eye is holding on to. */
  .tool-bench.bench-going { animation: bench-going .52s cubic-bezier(.32,0,.67,0) both; pointer-events: none; }
  .tool-bench.bench-coming { animation: bench-coming .46s cubic-bezier(.2,.9,.3,1) both; }
  /* EVERY keyframe carries translateX(-50%). The bench is centred by that
     transform, and a keyframe that sets `transform` at all replaces the whole
     of it: without it here the bench jumped half its own width sideways as it
     moved, which is why it read as sliding away at an angle and why it came
     back in the wrong place and then snapped. */
  @keyframes bench-going {
    0%   { opacity: 1; transform: translateX(-50%) translateY(0); }
    /* Still fully there through the first half of the drop: it is leaving,
       and something already faded out has nothing left to show. */
    50%  { opacity: 1; transform: translateX(-50%) translateY(34px); }
    100% { opacity: 0; transform: translateX(-50%) translateY(104px); }
  }
  @keyframes bench-coming {
    0%   { opacity: 0; transform: translateX(-50%) translateY(104px); }
    35%  { opacity: 1; transform: translateX(-50%) translateY(58px); }
    100% { opacity: 1; transform: translateX(-50%) translateY(0); }
  }
}

/* ── The camera's own controls ─────────────────────────────────────────────
   Beside the height they act on. The camera was the one part of the sim with
   nothing on screen to say what it could do: no sign that dragging turns, that
   scrolling changes the height, or that pressing the ground travels there. */
/* It is a sibling of the height readout rather than inside it, so it does not
   inherit the readout's padding and was running to within a pixel of the
   card's edges. */
.hud-cam { display: flex; align-items: center; gap: 8px; padding: 2px 14px 12px; }
.hud-zoom { display: flex; gap: 4px; }
.hud-cam-btn { display: grid; place-items: center; width: 30px; height: 30px; padding: 0;
  border: 1px solid var(--border); border-radius: 8px; background: rgba(255,255,255,.05);
  color: var(--scene-ink); cursor: pointer; }
.hud-cam-btn svg { width: 19px; height: 19px; }
.hud-cam-btn:hover { background: rgba(255,255,255,.12); border-color: var(--focus-glow); color: var(--focus-glow); }
.hud-cam-btn:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.hud-north { width: 34px; height: 34px; border-radius: 999px; margin-left: 2px; }
.hud-north svg { width: 24px; height: 24px; }
/* No CSS transform on the needle. The rotation is the SVG attribute
   rotate(angle 12 12), which carries its own centre; adding a CSS
   transform-origin on top applies an origin twice and swings the needle off
   the circle it sits in. Nothing is lost by dropping the transition either:
   the heading is written on every camera change, so the needle already tracks
   continuously, and a transition on a continuously driven value adds lag
   rather than smoothness. */

/* ── Touch ─────────────────────────────────────────────────────────────────
   Pointer events already cover finger, pen and mouse, but the browser claims
   a gesture for itself unless told otherwise: on a touch screen a drag down
   the timeline is a scroll, and the pointermove events stop the moment the
   scroller takes it. Measured on an emulated tablet: dragging a piece moved
   it zero pixels.

   `touch-action: none` goes on the things you GRAB, not on the timeline
   itself. Putting it on the whole surface would fix dragging and take away
   scrolling, and the timeline is taller than the screen; this way a finger on
   a piece moves the piece and a finger on empty timeline scrolls it. */
/* On the SVG ITSELF, not only on the shapes inside it. Blink honours
   touch-action on the element it hit-tests for a gesture, and inside an SVG
   that is far less dependable: Ben's phone kept having the drag taken away
   after a few pixels, which is the browser deciding the gesture belongs to
   its scroller and sending pointercancel. The drawn shapes keep it too, and
   the Places cards and event cards need it in their own columns. */
#wb-svg, .wb-piece, .wb-piece *, .wb-bed, .wb-bed *, .wb-event, .wb-event *,
.wb-lock-hit, .wb-group, .wb-group *,
.wb-event-card, .wb-event-card *, .wb-sampler, .wb-tray-card { touch-action: none; }

/* And no long-press callout or text selection on the two surfaces a drag
   happens on. Android raises both from the same press that starts a drag. */
#sim-canvas, #wb-svg {
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
}

/* The timeline is taller than its box, and the drawing now refuses touch
   gestures, so a finger cannot scroll it. This is the way down. */
/* It is a button now, so that a finger has a way down a timeline that no
   longer scrolls under one, but it is a 64 px band across the whole foot of
   the drawing and a button's own border on something that wide reads as a
   line ruled across the timeline. The words are the target; the band is only
   the gradient that fades the drawing out behind them. */
.wb-more { border: 0; background: none; font: inherit; color: var(--scene-ink); padding-bottom: 8px; }
.wb-more > span:last-child { cursor: pointer; pointer-events: auto;
  padding: 3px 10px; border-radius: 999px; background: rgba(11,14,18,.72);
  border: 1px solid var(--border); }
.wb-more > span:last-child:hover { border-color: var(--focus-glow); color: var(--focus-glow); }
.wb-more:focus-visible > span:last-child { outline: 2px solid var(--focus); outline-offset: 2px; }

/* A place is over the timeline and will be taken if it is let go here. The
   drop area is lit while anything is in hand; this is the stronger statement,
   that this particular spot is ready to accept it. */
.wb-over-zone .wb-time-bg { fill: rgba(92,225,255,.1); stroke: var(--focus-glow);
  stroke-width: 3; stroke-dasharray: none; }

/* ── The mission card ──────────────────────────────────────────────────────
   The only card a student meets before they have done anything, so it is the
   only one with a heading of its own. The column beside the words is the empty
   shape of what they are going to build, in the same language the field kit
   and the timeline are drawn with. */
.earth-mission { color: var(--focus-glow); }

/* The one badge a student can press, on a globe they have just arrived at.
   The bench points at the next thing once they get there; the globe, which is
   where they land, did not. */
@media (prefers-reduced-motion: no-preference) {
  #globe-pins:not(.arriving) .globe-pin:not(.visited) .pin-mark { animation: pin-wait 2.4s ease-in-out infinite; }
  @keyframes pin-wait {
    0%, 100% { box-shadow: 0 0 0 0 rgba(255,209,102,.5); }
    50%      { box-shadow: 0 0 0 7px rgba(255,209,102,0); }
  }
}

/* ── The key to the drawing, in the vocabulary dialog ──────────────────────
   Ben: "for this sim, i actually think we need to include a larger square
   showing each rock type pattern and rock color so students can always study
   it independently." The squares are 64 x 48, four times the area of the
   swatch a row carries, and the grid wraps so the dialog can be any width.
   The shared dialog brings its own chrome and no per-sim styles, so these
   rules reach it the only way they can: it is appended to this document. */
.vocab-rock-key { display: flex; flex-wrap: wrap; gap: 10px 12px; margin-top: 8px; }
.vocab-rock { width: 64px; }
.vocab-rock svg { display: block; border-radius: 3px; }
.vocab-rock span { display: block; margin-top: 3px; font-size: .68rem; line-height: 1.25; }

/* A place that has pulled apart to make room for another place's dates. The
   run between two of its layers is empty because that is time this place has
   no rock for, and the dotted edges carry the column across it so it still
   reads as one place. */
.wb-gap line { stroke: rgba(234,240,245,.5); stroke-width: 1.5; stroke-dasharray: 3 4; }
.wb-gap .wb-gap-cap { stroke: rgba(234,240,245,.35); stroke-width: 1; stroke-dasharray: 2 3; }
.wb-piece:hover .wb-gap line, .wb-piece.grabbed .wb-gap line { stroke: var(--focus-glow); }
/* The open end of a place: past its last date, nothing says how far it goes
   on. Said once, at the end, rather than by fading every layer beyond the
   date, which dimmed six layers for one press of the sampler. */
.wb-open-end { stroke: rgba(234,240,245,.55); stroke-width: 1.6; stroke-dasharray: 3 3; }
/* The mark on a gap: what this run of missing time is, on hover or focus,
   rather than as more words printed on an already busy timeline. */
.wb-gap-info circle { fill: rgba(11,14,18,.8); stroke: rgba(234,240,245,.55); stroke-width: 1.2; }
.wb-gap-info line { stroke: rgba(234,240,245,.85); stroke-width: 1.6; stroke-linecap: round; }
.wb-gap-info:hover circle, .wb-gap-info:focus-visible circle { stroke: var(--focus-glow); }
.wb-gap-info:hover line, .wb-gap-info:focus-visible line { stroke: var(--focus-glow); }
.wb-gap-info:focus { outline: none; }

/* A place that is out on the timeline: one line in the list rather than a
   card, so the list stays the index of every place without becoming a wall of
   finished work. The order never changes, so a place is always in the same
   spot whether it is out on the board or not. */
.wb-tray-card.out { padding: 5px 10px; cursor: pointer; align-items: center; gap: 8px;
  background: transparent; border-style: dashed; border-color: rgba(150,180,205,.3); }
.wb-tray-card.out:hover, .wb-tray-card.out.armed { background: rgba(255,255,255,.05); border-color: var(--focus-glow); }
.wb-tray-card.out .wb-tray-text strong { font-weight: 500; color: var(--scene-muted); }
.wb-tray-card.out.armed .wb-tray-text strong, .wb-tray-card.out:hover .wb-tray-text strong { color: var(--scene-ink); }
/* The mark: a piece standing out on the board, not a tick. A tick would say
   this place is finished, and a place on the timeline is the opposite. */
.wb-out-mark { flex: 0 0 auto; width: 15px; height: 15px; color: var(--focus-glow); }
.wb-tray-card.out .wb-travel-btn { margin-top: 5px; }

/* Sending a whole place to the laboratory at once. Earned, and only offered
   while the rock sampler is in hand, so the board carries nothing extra when
   it is not: Ben, "i'm hoping it reduces visual clutter when the rock sampler
   is active." It leaves a place for good once every layer there has a date. */
.wb-stack { cursor: pointer; }
/* The title reads as a button while the offer stands: the laboratory's own
   tube at its own size, and the place's whole name beside it. */
/* Opaque: the pill that has opened has to cover the tube standing behind it,
   not let it show through. */
.wb-stack-pill { fill: #0f141a; stroke: rgba(255,209,102,.6); stroke-width: 1.3;
  transition: stroke .12s, stroke-width .12s, fill .12s; }
/* The hover says three things: the button rises, its edge goes definite, and
   the tube fills to the top to show what pressing it would add. What it had
   was gold on dark at sixty per cent against gold on dark at a hundred, and a
   fill six per cent lighter, which nobody was going to see. */
.wb-stack:hover .wb-stack-pill, .wb-stack:focus-visible .wb-stack-pill {
  stroke: var(--focus-glow); stroke-width: 2.2; fill: #1d2732; }
.wb-stack-body { transition: transform .12s; }
.wb-stack:hover .wb-stack-body, .wb-stack:focus-visible .wb-stack-body {
  transform: translateY(-2px); filter: drop-shadow(0 3px 7px rgba(0,0,0,.65)); }
.wb-stack-name { fill: var(--scene-ink); font: 600 13.5px Roboto, Arial, sans-serif; pointer-events: none; }
.wb-stack:hover .wb-stack-name, .wb-stack:focus-visible .wb-stack-name { fill: #fff; }
/* The part of the tube the press would fill: nothing until the pointer is on
   the button, then a paler gold above the level already reached. */
.wb-tube-ghost { fill: var(--focus-glow); opacity: 0; transition: opacity .14s; }
.wb-stack:hover .wb-tube-ghost, .wb-stack:focus-visible .wb-tube-ghost { opacity: .34; }
@media (prefers-reduced-motion: reduce) {
  .wb-stack-body, .wb-stack-pill, .wb-tube-ghost { transition: none; }
  .wb-stack:hover .wb-stack-body, .wb-stack:focus-visible .wb-stack-body { transform: none; }
}
.wb-stack-tube { color: var(--focus-glow); }
.wb-tube-glass { fill: rgba(11,14,18,.72); stroke: #f2ede2; stroke-width: 1.1; }
.wb-tube-collar { fill: #f2ede2; }
.wb-tube-fill { fill: var(--focus-glow); }
.wb-stack:hover .wb-tube-glass, .wb-stack:focus-visible .wb-tube-glass { stroke: var(--focus-glow); }
.wb-stack:focus { outline: none; }
/* The layers that press would send, while the pointer is over the tube. */
.wb-bed.tosend rect { stroke: var(--focus-glow); stroke-width: 1.8; }
/* A pill lifted clear of its neighbour keeps a line down to the stack it
   names, so it is never in doubt which place it belongs to. */
.wb-stack-stem { stroke: rgba(255,209,102,.55); stroke-width: 2; stroke-dasharray: 3 3; }
.wb-stack:hover .wb-stack-stem, .wb-stack:focus-visible .wb-stack-stem { stroke: var(--focus-glow); stroke-dasharray: none; }

/* ── Narrow frames at a site ───────────────────────────────────────────────
   Rajat at about 600 px: the field kit covered the altitude card and most of
   the scene, the tool belt was cut off at the left edge, and the "Fully
   surveyed" card collapsed to a column one word wide. Below 700 px the kit is
   a sheet along the bottom (it scrolls inside itself), the belt sits above it
   and may wrap, and the reward card spans the frame. */
@media (max-width: 700px) {
  .notebook { left: 8px; right: 8px; top: auto; bottom: 8px; width: auto; max-height: 36%; }
  .tool-bench { left: 50%; bottom: calc(36% + 16px); max-width: calc(100% - 16px); padding: 8px 10px 6px; }
  .tool-bench .tool-belt { flex-wrap: wrap; justify-content: center; gap: 6px; }
  .site-reward { left: 8px; right: 8px; top: 8px; max-width: none; margin: 0; flex-direction: column; gap: 10px; max-height: calc(100% - 16px); overflow-y: auto; }
  .site-reward-pic { width: 100%; height: 110px; }
  .site-reward-actions .nb-btn { flex: 1 1 auto; }
}

/* ── Narrow workbench ──────────────────────────────────────────────────────
   Folded, the Places column is a rail like Events, not a column of name
   chips, so the timeline gets the room. The sampler stays, as its slot. */
@media (max-width: 700px) {
  .wb-side.folded { max-width: 46px; align-items: center; }
  .wb-side.folded .wb-tray { display: none; }
  .wb-side.folded .wb-sampler-name { display: none; }
  .wb-side.folded .wb-sampler { padding: 4px; width: auto; }
  .wb-side.folded .wb-fold { writing-mode: vertical-rl; width: 30px; justify-content: flex-start; padding: 6px 0; gap: 10px; }
  .wb-side.folded .wb-fold-count { margin-left: 0; }
}

/* ── Undo ──────────────────────────────────────────────────────────────────
   One step back on the timeline, in the bottom corner beside the gear. Same
   dark pill as the gear; greyed, not hidden, when there is nothing to undo,
   so it is always where a student expects it. */
.wb-undo { position: absolute; right: 50px; bottom: 10px; z-index: 8; display: flex; align-items: center; gap: 6px;
  height: 32px; padding: 0 12px 0 9px; font: inherit; font-size: .82rem; border-radius: 999px;
  border: 1px solid var(--border); background: var(--panel); color: var(--scene-text, #eaf0f5);
  cursor: pointer; box-shadow: var(--shadow); }
.wb-undo svg { width: 17px; height: 17px; }
.wb-undo:hover { color: var(--focus-glow); border-color: var(--focus-glow); }
.wb-undo[aria-disabled="true"] { color: var(--scene-muted); opacity: .55; cursor: default; }
.wb-undo[aria-disabled="true"]:hover { color: var(--scene-muted); border-color: var(--border); }
.wb-undo:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
/* On a narrow screen the "timeline goes on below" cue runs under the pill, so
   it moves up and stacks over the gear instead. Under the gear's menu. */
@media (max-width: 600px) {
  .wb-undo { right: 10px; bottom: 48px; z-index: 7; }
}

/* Shown only when WebGL cannot start: the scene is empty, and this says why. */
#no-gl { position: absolute; z-index: 5; top: 50%; left: 50%; transform: translate(-50%, -50%); max-width: min(380px, calc(100% - 24px)); padding: 12px 16px; font: 500 14px/1.45 Roboto, Arial, sans-serif; text-align: center; color: var(--scene-ink); background: var(--panel); border: 1px solid #B26464; border-radius: var(--radius-md); pointer-events: none; }
