/*
   CSS framework: Bootstrap 5.3. It is imported into its own cascade layer,
   so every rule in this file outranks it and the custom design stays in
   charge. Bootstrap provides the accessibility utilities used in the HTML:
   .visually-hidden (screen-reader-only text) and .visually-hidden-focusable
   (the "Skip to main content" link that appears on keyboard focus).
*/
@import url("https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css") layer(framework);

/* Pixelify Sans stands in for Cristik, which has no legitimate web-font source. */
@import url("https://fonts.googleapis.com/css2?family=Pixelify+Sans:wght@500;600;700&family=Roboto:wght@400;500;600;700&display=swap");

:root {
  /* Display font is reserved for the logo and page titles */
  --font-display: "Pixelify Sans", "Roboto", system-ui, sans-serif;
  --font-body: "Roboto", system-ui, -apple-system, "Segoe UI", sans-serif;

  /* Surfaces */
  --bg: #0d0f12;
  --surface: #15181c;
  --surface-raised: #1c2025;
  --surface-hover: #252a31;

  /* Lines */
  --line: #252a31;
  --line-strong: #3d444d;

  /* Text */
  --text: #f2f3f5;
  --text-secondary: #a9afb7;
  --text-faint: #8a919a;
  --text-on-light: #0d0f12;

  /* Core experiment colors */
  --red: #ff6b6e;
  --green: #5bd691;
  --blue: #5aa2ff;

  /* Secondary colors */
  --cyan: #5ad6e6;
  --magenta: #ec74bc;
  --yellow: #f2c94c;

  /* Meaning */
  --color-open: var(--cyan);
  --color-success: var(--green);
  --color-focus: var(--text);

  --radius: 12px;
  --radius-particle: 50%;

  --shadow-medium: 0 6px 22px rgba(0, 0, 0, 0.08);

  --z-base: 10;
  --z-nav: 20;
  --z-overlay: 30;

  --gutter: 16px;
  --tap: 44px;
}

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

body {
  margin: 0;
  min-height: 100vh;

  color: var(--text);
  background: var(--bg);

  font-family: var(--font-body);
  font-size: 1rem;
  line-height: 1.55;
}

h1,
h2,
h3 {
  margin-top: 0;

  color: var(--text);

  line-height: 1.2;

  text-wrap: balance;
}

h1,
h2 {
  font-family: var(--font-display);
  font-weight: 600;
}

h1 {
  margin-bottom: 0;
  font-size: 1.3rem;
}

h2 {
  margin-bottom: 0.5rem;
  font-size: clamp(1.75rem, 1.2rem + 2vw, 2.5rem);
}

h3 {
  margin-bottom: 0.75rem;

  font-family: var(--font-body);
  font-size: 1.05rem;
  font-weight: 600;
}

p,
dd {
  margin: 0 0 1rem;
  text-wrap: pretty;
}

a {
  color: inherit;
  text-underline-offset: 3px;
}

button,
input {
  font: inherit;
}

button {
  cursor: pointer;
}

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

.eyebrow,
.section-label {
  margin: 0 0 0.5rem;

  color: var(--text-secondary);

  font-size: 0.875rem;
  font-weight: 500;
}

.shell {
  width: min(1280px, 100% - 2 * var(--gutter));
  min-height: 100vh;
  margin: 0 auto;

  display: flex;
  flex-direction: column;
}

main {
  flex: 1;
  padding: 2.5rem 0 4rem;
}

.page-heading {
  margin-bottom: 2rem;
}

.page-heading > p:last-child {
  max-width: 640px;
  margin-bottom: 0;

  color: var(--text-secondary);

  font-size: 1.05rem;
}

.skip-link {
  position: absolute;
  top: 0.75rem;
  left: 0.75rem;
  z-index: var(--z-overlay);

  padding: 0.7rem 1rem;

  border-radius: var(--radius);

  color: var(--text-on-light);
  background: var(--text);

  font-weight: 600;
  text-decoration: none;
}

/* Quiet text-style button: Step, Undo, hints */
.text-btn {
  min-height: var(--tap);
  padding: 0 0.6rem;

  border: 0;
  border-radius: var(--radius);

  color: var(--text-secondary);
  background: transparent;

  text-decoration: underline;
  text-decoration-color: transparent;
  text-underline-offset: 4px;

  transition:
    color 120ms ease,
    text-decoration-color 120ms ease;
}

.text-btn:hover {
  color: var(--text);
  text-decoration-color: currentColor;
}

/* Horizontal list separated by middle dots */
.inline-list {
  margin: 0;
  padding: 0;

  display: flex;
  flex-wrap: wrap;
  align-items: center;

  list-style: none;
}

.inline-list > li + li::before {
  content: "·" / "";

  padding: 0 0.35rem;

  color: var(--text-faint);
}

.status {
  color: var(--text-faint);

  font-size: 0.85rem;
  white-space: nowrap;
}

.status-open {
  color: var(--color-open);
  font-weight: 600;
}

/* ============================================================
   Header and navigation
   ============================================================ */

.site-header {
  padding-top: 1.25rem;
}

.topbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem 2rem;
}

.brand-row {
  min-width: 0;

  display: flex;
  align-items: center;
  gap: 0.75rem;
}

.brand-row > div {
  min-width: 0;
}

/* The logo is Lumi herself: a ring with two dot eyes */
.brand-mark {
  position: relative;

  width: 30px;
  height: 30px;
  flex-shrink: 0;

  border: 2px solid var(--text);
  border-radius: var(--radius-particle);
}

.brand-mark::before,
.brand-mark::after {
  content: "";

  position: absolute;
  top: 38%;

  width: 4px;
  height: 4px;

  border-radius: var(--radius-particle);

  background: var(--text);
}

.brand-mark::before {
  left: 28%;
}

.brand-mark::after {
  right: 28%;
}

.player-line {
  margin: 0.1rem 0 0;

  color: var(--text-secondary);

  font-size: 0.85rem;
}

.player-name {
  color: var(--text);
  font-weight: 600;
}

.nav-list {
  margin: 0;
  padding: 0;

  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem;

  list-style: none;
}

.nav-list a {
  min-height: var(--tap);
  padding: 0 0.75rem;

  display: flex;
  align-items: center;
  justify-content: center;

  border-radius: var(--radius);

  color: var(--text-secondary);
  text-decoration: none;

  font-size: 0.95rem;
  font-weight: 500;

  transition: color 120ms ease;
}

.nav-list a:hover {
  color: var(--text);
}

.nav-list a[aria-current="page"] {
  color: var(--text);

  text-decoration: underline;
  text-decoration-thickness: 2px;
  text-underline-offset: 8px;
}

.site-header hr,
.site-footer hr {
  height: 1px;
  margin: 1rem 0 0;

  border: 0;
  opacity: 1;

  background: var(--line);
}

/* ============================================================
   Containers: used only where a real boundary exists
   ============================================================ */

.panel {
  padding: 1.5rem;

  border-radius: var(--radius);

  background: var(--surface);
  box-shadow: var(--shadow-medium);
}

.two-column {
  margin-top: 3rem;

  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  align-items: start;
  gap: 3rem 4rem;
}

/* ============================================================
   Login and registration
   ============================================================ */

.auth-main {
  display: grid;
  place-items: start center;
  padding-top: clamp(1.5rem, 6vw, 4rem);
}

.login-card {
  width: min(440px, 100%);
  padding: clamp(1.25rem, 5vw, 2.25rem);
}

.auth-copy {
  margin-bottom: 1.75rem;
  text-align: center;
}

.auth-copy h2 {
  margin-bottom: 0.4rem;
  font-size: clamp(1.6rem, 1.2rem + 1.5vw, 2rem);
}

.auth-copy p:last-child {
  margin-bottom: 0;
  color: var(--text-secondary);
}

.field {
  margin-bottom: 1rem;
}

.field label {
  display: block;
  margin-bottom: 0.4rem;

  color: var(--text-secondary);

  font-size: 0.875rem;
  font-weight: 500;
}

input[type="text"],
input[type="password"] {
  width: 100%;
  min-height: 48px;
  padding: 0.75rem 0.9rem;

  border: 0;
  border-radius: var(--radius);

  color: var(--text);
  background: var(--surface-raised);

  /* 16px keeps iOS from zooming into the field */
  font-size: 1rem;

  transition: background-color 120ms ease;
}

input::placeholder {
  color: var(--text-faint);
}

input:focus {
  background: var(--surface-hover);
}

.check {
  min-height: var(--tap);
  margin: 0.25rem 0 1rem;

  display: flex;
  align-items: center;
  gap: 0.6rem;

  color: var(--text-secondary);
  font-size: 0.9rem;
}

.check input {
  width: 20px;
  height: 20px;
  margin: 0;

  accent-color: var(--text);
}

.check label {
  flex: 1;
  padding: 0.6rem 0;
  cursor: pointer;
}

.primary-btn,
.run-btn {
  min-height: 48px;
  padding: 0.8rem 1.5rem;

  border: 0;
  border-radius: var(--radius);

  color: var(--text-on-light);
  background: var(--text);

  font-weight: 700;

  transition:
    background-color 120ms ease,
    transform 120ms ease;
}

.primary-btn {
  width: 100%;
}

.primary-btn:hover,
.run-btn:hover {
  background: #ffffff;
  transform: translateY(-1px);
}

.primary-btn:active,
.run-btn:active {
  transform: translateY(0);
}

.auth-switch {
  margin: 1.25rem 0 0;

  color: var(--text-secondary);
  text-align: center;

  font-size: 0.9rem;
}

.auth-switch a {
  min-height: var(--tap);
  padding: 0 0.25rem;

  display: inline-flex;
  align-items: center;

  color: var(--text);
  font-weight: 600;
}

/* ============================================================
   Sanctuary map: the one defined game area on the page
   ============================================================ */

.map-panel {
  padding: 1.5rem 1.5rem 1.75rem;
}

.map-heading {
  margin-bottom: 1rem;

  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.25rem 1rem;
}

.map-heading h3,
.map-heading p {
  margin: 0;
}

.map-heading p {
  color: var(--text-secondary);
  font-size: 0.875rem;
}

.sanctuary-map {
  /* The path's <line> y values in map.html are derived from these three */
  --node-height: 124px;
  --map-high: 16px;
  --map-low: 176px;

  position: relative;
}

.map-path {
  position: absolute;
  inset: 0;

  width: 100%;
  height: 100%;

  overflow: visible;
}

.map-path line {
  stroke: var(--line-strong);
  stroke-width: 2;
  stroke-dasharray: 5 7;
  stroke-linecap: round;
}

.map-stops {
  position: relative;
  z-index: var(--z-base);

  min-height: calc(var(--map-low) + var(--node-height) + var(--map-high));
  margin: 0;
  padding: 0;

  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));

  list-style: none;
}

.map-stop {
  padding: var(--map-high) 0.35rem 0;

  display: flex;
  justify-content: center;
  align-items: flex-start;
}

.map-stop:nth-child(even) {
  padding-top: var(--map-low);
}

/* Locked chambers are dashed outlines, as in the original sketch */
.map-node {
  width: min(100%, 150px);
  min-height: var(--node-height);
  padding: 0.9rem 0.6rem;

  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.15rem;

  border: 2px dashed var(--line-strong);
  border-radius: var(--radius);

  color: var(--text-faint);
  background: var(--surface);

  text-align: center;
  text-decoration: none;
}

.map-node-name {
  color: var(--text-secondary);

  font-weight: 600;
  line-height: 1.25;
}

.map-node-kind {
  font-size: 0.85rem;
}

.map-node .status {
  margin-top: 0.35rem;
}

.map-node.is-open {
  border: 2px solid var(--color-open);

  color: var(--text-secondary);
  background: var(--surface-raised);

  transition: transform 120ms ease;
}

.map-node.is-open .map-node-name {
  color: var(--text);
}

.map-node.is-open:hover {
  transform: translateY(-2px);
}

/* ============================================================
   Chamber list, progress, and scores
   ============================================================ */

.chamber-list {
  margin: 0;
  padding: 0;

  list-style: none;
}

.chamber-list li {
  position: relative;

  padding: 0.8rem 0;

  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 0 1rem;

  border-top: 1px solid var(--line);

  color: var(--text-secondary);
}

.chamber-list li:last-child {
  border-bottom: 1px solid var(--line);
}

.chamber-list a {
  color: var(--text);
  font-weight: 600;
}

/* The whole row is the link's tap target */
.chamber-list a::after {
  content: "";
  position: absolute;
  inset: 0;
}

.chamber-list a:focus-visible {
  outline: 0;
}

.chamber-list li:has(a:focus-visible) {
  outline: 2px solid var(--color-focus);
  outline-offset: 3px;
}

.kind {
  color: var(--text-faint);
  font-size: 0.875rem;
}

.chamber-list .status {
  grid-row: 1 / 3;
  grid-column: 2;
}

.progress-count {
  margin-bottom: 0.6rem;
  color: var(--text-secondary);
}

/* Standard reset first; vendor pseudo-elements below draw the bar and fill */
meter {
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;

  display: block;
  width: 100%;
  height: 8px;

  border: 0;
  border-radius: var(--radius);

  background: var(--surface-raised);
}

meter::-webkit-meter-bar {
  height: 8px;
  border: 0;
  border-radius: var(--radius);
  background: var(--surface-raised);
}

meter::-webkit-meter-optimum-value,
meter::-webkit-meter-suboptimum-value,
meter::-webkit-meter-even-less-good-value {
  border-radius: var(--radius);
  background: var(--color-success);
}

meter::-moz-meter-bar {
  border-radius: var(--radius);
  background: var(--color-success);
}

table {
  width: 100%;
  margin-top: 2rem;

  border-collapse: collapse;

  font-size: 0.95rem;
}

caption {
  padding: 0 0 0.5rem;

  caption-side: top;

  color: var(--text-secondary);
  text-align: left;

  font-size: 0.875rem;
  font-weight: 500;
}

th,
td {
  padding: 0.65rem 0.5rem;

  border-bottom: 1px solid var(--line);

  text-align: left;
  overflow-wrap: anywhere;
}

th:first-child,
td:first-child {
  padding-left: 0;
}

th:last-child,
td:last-child {
  padding-right: 0;
  text-align: right;
}

thead th {
  color: var(--text-faint);
  font-size: 0.875rem;
  font-weight: 500;
}

/* ============================================================
   Chamber
   ============================================================ */

/* The puzzle sits directly on the page; Bit and activity form a quieter column */
.layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 280px;
  align-items: start;
  gap: 4rem;
}

.chamber-header h2 {
  margin-bottom: 0.5rem;
}

.goal-copy {
  margin: 0;

  color: var(--text-secondary);

  font-size: 1.05rem;
}

.pulse-limit {
  margin: 0.15rem 0 0;
  color: var(--text-faint);
}

/* Lumi and the target are the focal point: big, quiet, and roomy */
.stage {
  max-width: 620px;
  margin: 0 auto;
  padding: 4rem 1rem 3.5rem;

  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  align-items: center;
  gap: 1.5rem;
}

.state-card {
  margin: 0;
  text-align: center;
}

.state-label {
  margin-bottom: 1.25rem;

  color: var(--text-secondary);

  font-size: 0.9rem;
}

.lumi-wrap {
  min-height: 150px;

  display: grid;
  place-items: center;
}

/*
   Lumi: an outlined circle with two dot eyes. The eyes are her own
   pseudo-elements, so any transform on .lumi moves the whole character.
   React will swap .motion-0 to .motion-3 to show her motional energy;
   without a motion class she holds still. lumi.js shifts only the eyes,
   inside the body, toward the pointer via --look-x and --look-y.
*/
.lumi {
  position: relative;

  width: 120px;
  height: 120px;

  border: 2px solid var(--text);
  border-radius: var(--radius-particle);
}

.lumi::before,
.lumi::after {
  content: "";

  position: absolute;
  top: 42%;

  width: 7px;
  height: 7px;

  border-radius: var(--radius-particle);

  background: var(--text);

  transform: translate(var(--look-x, 0), var(--look-y, 0));
  transition: transform 140ms ease-out;
  animation: lumi-look 7s ease-in-out infinite;
}

.lumi::before {
  left: 33%;
}

.lumi::after {
  right: 33%;
}

/* Once the pointer is tracked, the idle glance gives way */
.lumi.is-watching::before,
.lumi.is-watching::after {
  animation: none;
}

.lumi.motion-0 {
  animation: lumi-motion-0 4s ease-in-out infinite;
}

.lumi.motion-1 {
  animation: lumi-motion-1 1.6s ease-in-out infinite;
}

.lumi.motion-2 {
  animation: lumi-motion-2 0.8s ease-in-out infinite;
}

.lumi.motion-3 {
  animation: lumi-motion-3 0.18s linear infinite;
}

/* Same character, shown as a goal: dashed and dimmed, holding still */
.lumi-target {
  border-style: dashed;
  border-color: var(--text-secondary);

  opacity: 0.65;
}

.lumi-target::before,
.lumi-target::after {
  background: var(--text-secondary);
  animation: none;
}

.state-data {
  margin-top: 1.25rem;

  display: flex;
  flex-direction: column;
  gap: 0.1rem;

  color: var(--text-secondary);
}

.state-data > span:first-child {
  color: var(--text);
  font-weight: 500;
}

.match-arrow {
  color: var(--text-faint);
  font-size: 1.4rem;
}

.puzzle-area {
  padding-top: 2.5rem;
  border-top: 1px solid var(--line);
}

/* Sequence slots are experiment positions, not metric cards */
.track {
  margin: 0 0 2rem;
  padding: 0;

  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 0.75rem;

  list-style: none;
  counter-reset: pulse;
}

.slot {
  --accent: transparent;

  display: flex;
  flex-direction: column;
  gap: 0.35rem;

  counter-increment: pulse;
}

.slot::before {
  content: counter(pulse);

  color: var(--text-faint);

  font-size: 0.8rem;
}

.slot-well {
  min-height: 64px;
  padding: 0.5rem;

  display: grid;
  place-items: center;

  border: 2px solid var(--surface-raised);
  border-radius: var(--radius);

  color: var(--text);
  background: var(--surface-raised);
  box-shadow: inset 0 -3px 0 var(--accent);

  font-weight: 600;
}

.slot.is-empty .slot-well {
  border-style: dashed;
  border-color: var(--line-strong);

  color: var(--text-faint);
  background: transparent;
  box-shadow: none;

  font-size: 0.875rem;
  font-weight: 400;
}

/* Pulses: flat controls marked by a single colored rule */
.palette {
  margin-bottom: 2rem;

  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0.75rem 1.25rem;
}

.pulse {
  --accent: var(--text-faint);

  min-height: 64px;
  padding: 0.75rem 0.75rem 0.8rem;

  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.1rem;

  border: 0;
  border-bottom: 3px solid var(--accent);
  border-radius: var(--radius) var(--radius) 0 0;

  color: var(--text);
  background: transparent;

  text-align: left;

  transition: background-color 120ms ease;
}

.pulse:hover {
  background: var(--surface-raised);
}

.pulse-carrier {
  --accent: var(--green);
}

.pulse-blue {
  --accent: var(--blue);
}

.pulse-red {
  --accent: var(--red);
}

.pulse-name {
  font-weight: 600;
}

.pulse-effect {
  color: var(--text-secondary);
  font-size: 0.9rem;
}

.run-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 0.75rem;
}

.run-btn {
  margin-right: 0.5rem;
}

.readout {
  margin: 1.75rem 0 0;

  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 2rem;

  color: var(--text-faint);

  font-size: 0.875rem;
}

.readout div {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
}

.readout dt {
  font-weight: 400;
}

.readout dt::after {
  content: ":";
}

.readout dd {
  margin: 0;
  color: var(--text-secondary);
  font-weight: 500;
}

/*
   Intentional ID selector: the experiment result is the one unique
   outcome on the page (also an aria-live and future script target),
   so it reads brighter than the secondary readouts beside it.
*/
#result {
  color: var(--text);
}

/* Bit and multiplayer: plain text that recedes beside the puzzle */
.side-stack {
  padding-left: 2.5rem;

  display: grid;
  gap: 2.5rem;

  border-left: 1px solid var(--line);
}

.speech {
  margin-bottom: 1.25rem;
  padding-left: 1rem;

  border-left: 2px solid var(--line-strong);

  color: #d8dbdf;

  font-size: 1.05rem;
}

.hint-label {
  margin: 0;

  color: var(--text-faint);

  font-size: 0.875rem;
}

.hint-links {
  margin-left: -0.6rem;

  flex-direction: column;
  align-items: flex-start;
}

.hint-links > li + li::before {
  content: none;
}

.hint-links .text-btn {
  font-size: 0.9rem;
}

.together {
  padding-top: 2rem;
  border-top: 1px solid var(--line);
}

.players {
  margin-bottom: 1.25rem;
  color: var(--text);
}

.feed {
  margin: 0.25rem 0 0;
  padding: 0;

  list-style: none;
}

.feed li {
  padding: 0.45rem 0;

  color: var(--text-secondary);

  font-size: 0.9rem;
}

.feed li + li {
  border-top: 1px solid var(--line);
}

/* ============================================================
   Journal: two pages of notes, not two widgets
   ============================================================ */

.journal-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  align-items: start;
  gap: 3rem 4rem;
}

.journal-list {
  margin: 0;
}

.journal-entry {
  padding: 1rem 0;
  border-top: 1px solid var(--line);
}

.journal-entry dt {
  margin-bottom: 0.25rem;

  color: var(--text);

  font-weight: 600;
}

.journal-entry dd {
  margin: 0;
  color: var(--text-secondary);
}

.journal-entry.is-locked dt {
  color: var(--text-faint);
  font-weight: 500;
}

.journal-entry.is-locked dd {
  color: var(--text-faint);
  font-style: italic;
}

/* Where a note was learned, so the journal works as a reference */
.journal-entry .note-source {
  margin-top: 0.6rem;

  color: var(--text-faint);

  font-size: 0.875rem;
}

.note-source-label {
  display: block;
  color: var(--text-secondary);
  font-weight: 500;
}

/* ============================================================
   About: editorial column
   ============================================================ */

.about {
  max-width: 820px;
}

.about > p {
  max-width: 680px;
  color: var(--text-secondary);
}

.about > .lead {
  margin-top: 1rem;

  color: var(--text);

  font-size: 1.2rem;
}

.hardware {
  margin-top: 3rem;
}

.hardware-list {
  margin: 0;
}

.hardware-list > div {
  padding: 1rem 0;

  display: grid;
  grid-template-columns: 13rem 9rem minmax(0, 1fr);
  align-items: baseline;
  gap: 0.25rem 1.5rem;

  border-top: 1px solid var(--line);
}

.hardware-list dt {
  font-weight: 600;
}

.hardware-list dd {
  margin: 0;
  color: var(--text-secondary);
}

.hardware-list .chamber-name {
  color: var(--text-faint);
  font-size: 0.9rem;
}

/* ============================================================
   Footer
   ============================================================ */

.site-footer {
  padding-bottom: 1.5rem;
}

.footer-inner {
  padding-top: 0.5rem;

  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0 1rem;

  color: var(--text-faint);

  font-size: 0.875rem;
}

.footer-inner p {
  margin: 0;
}

.footer-inner a {
  min-height: var(--tap);

  display: inline-flex;
  align-items: center;

  color: var(--text-secondary);
}

/* ============================================================
   Motion
   ============================================================ */

@keyframes lumi-motion-0 {
  0%,
  100% {
    transform: translateY(1px);
  }

  50% {
    transform: translateY(-3px);
  }
}

@keyframes lumi-motion-1 {
  0%,
  100% {
    transform: translateY(3px);
  }

  50% {
    transform: translateY(-3px);
  }
}

@keyframes lumi-motion-2 {
  0%,
  100% {
    transform: translateY(5px);
  }

  50% {
    transform: translateY(-5px);
  }
}

@keyframes lumi-motion-3 {
  0%,
  100% {
    transform: translate(0, 0) rotate(0deg);
  }

  25% {
    transform: translate(-2px, 1px) rotate(-2deg);
  }

  75% {
    transform: translate(2px, -1px) rotate(2deg);
  }
}

@keyframes lumi-look {
  0%,
  40% {
    transform: translateX(0);
  }

  50%,
  70% {
    transform: translateX(3px);
  }

  80%,
  100% {
    transform: translateX(0);
  }
}

/* !important is needed here so this wins over every component animation */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation: none !important;
    transition: none !important;
    scroll-behavior: auto !important;
  }
}

/* ============================================================
   Responsive
   Three project breakpoints, widest first:
     992px  tablet: the chamber sidebar drops below the puzzle and
            two-column page sections stack
     767px  phone: 768px tablets keep the tablet layout; below that the
            header, map, chamber controls, and tables switch to
            single-column phone layouts
     359px  narrow phone (320px class): Lumi and the target no longer
            fit side by side, so they stack; gutters tighten
   ============================================================ */

@media (max-width: 992px) {
  .layout,
  .two-column,
  .journal-grid {
    grid-template-columns: minmax(0, 1fr);
  }

  /* Bit and the shared activity sit side by side under the puzzle */
  .side-stack {
    padding: 2.5rem 0 0;

    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 2rem 3rem;

    border-left: 0;
    border-top: 1px solid var(--line);
  }

  .together {
    padding-top: 0;
    border-top: 0;
  }

  .hardware-list > div {
    grid-template-columns: minmax(0, 1fr) auto;
  }

  .hardware-list dd:last-child {
    grid-column: 1 / -1;
  }
}

@media (max-width: 767px) {
  main {
    padding: 1.75rem 0 2.5rem;
  }

  /* Header: brand on its own line, five links as a 3-column grid */
  .topbar {
    flex-direction: column;
    align-items: stretch;
  }

  nav {
    margin: 0 -0.25rem;
  }

  .nav-list:has(> li:nth-child(3)) {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }

  .panel,
  .map-panel {
    padding: 1.1rem;
  }

  /* Map: a vertical path, not a squeezed desktop map */
  .map-path {
    display: none;
  }

  .map-stops {
    min-height: 0;

    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1.75rem;
  }

  .map-stop,
  .map-stop:nth-child(even) {
    position: relative;

    width: min(100%, 380px);
    padding: 0;
  }

  .map-stop + .map-stop::before {
    content: "";

    position: absolute;
    left: 50%;
    bottom: 100%;

    height: 1.75rem;

    border-left: 2px dashed var(--line-strong);
  }

  .map-node {
    width: 100%;
    min-height: 0;
    padding: 0.85rem 1rem;

    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: center;
    gap: 0 0.75rem;

    text-align: left;
  }

  .map-node .status {
    grid-row: 1 / 3;
    grid-column: 2;
    margin: 0;
  }

  /* Chamber: smaller Lumi, 2x2 sequence, stacked pulses and controls */
  .stage {
    padding: 2.75rem 0 2.5rem;
    gap: 0.75rem;
  }

  .lumi {
    width: 96px;
    height: 96px;
  }

  .lumi-wrap {
    min-height: 116px;
  }

  .puzzle-area {
    padding-top: 2rem;
  }

  .track {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .palette {
    grid-template-columns: minmax(0, 1fr);
    gap: 0.25rem;
  }

  .pulse {
    min-height: 56px;

    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
  }

  .run-row {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 0.5rem;
  }

  .run-btn {
    grid-column: 1 / -1;
    margin: 0;
  }

  .readout {
    flex-direction: column;
  }

  /* Bit, then shared activity, below the puzzle */
  .side-stack {
    grid-template-columns: minmax(0, 1fr);
    gap: 2rem;
  }

  .together {
    padding-top: 2rem;
    border-top: 1px solid var(--line);
  }

  th,
  td {
    padding-top: 0.6rem;
    padding-bottom: 0.6rem;
  }
}

@media (max-width: 359px) {
  :root {
    --gutter: 12px;
  }

  h1 {
    font-size: 1.15rem;
  }

  .nav-list a {
    padding: 0 0.4rem;
  }

  .stage {
    grid-template-columns: minmax(0, 1fr);
    gap: 1.25rem;
  }

  .match-arrow {
    justify-self: center;
    transform: rotate(90deg);
  }
}
