/* ══════════════════════════════════════════════════════════════
   Ion Test Lab — qualitative analysis sandbox. Lives as two tabs
   ("Ion Test Anion" / "Ion Test Cation") inside the shared #lab card
   alongside Reaction Lab — see the "Lab hub" block below for the outer
   3-way tab shell. Reuses .lab-wrap / .lab-sound-toggle / .lab-intro /
   .section-label / .home-heading-friendly em / .lab-sub /
   .lab-shelf-label / .lab-shelf-frame / .lab-shelf / .lab-mix /
   .lab-rinse / .lab-result classes verbatim from reaction-lab.css —
   only genuinely new components are styled here.
   Depends on tokens defined in scholars-ui.css (:root vars).
   ══════════════════════════════════════════════════════════════ */

/* #ion-lab is now an inner tabpanel (not its own outer section) — this
   rule only scopes the descendant selectors below it, no section-level
   box model here (that comes from #lab in reaction-lab.css). */
#ion-lab {
  position: relative;
}

#labHubReaction {
  position: relative;
}

#ion-lab .home-heading-friendly {
  font-size: clamp(1.6rem, 2.6vw, 2.3rem);
}

#ion-lab .section-label {
  margin-bottom: 8px;
}

/* ── Lab hub: the outer 3-way tab shell (Reaction Lab / Ion Test Anion /
   Ion Test Cation) that both features now live inside. Visually mirrors
   .ion-tabs but sized for three segments and given a bit more presence
   since it's the top-level navigation for the whole card now. ── */
/* Editorial underline tabs — no pill, no gradient, no glow. A full-width
   hairline runs under the row; the active tab is marked by a solid
   terracotta underline bar sliding beneath it, with the label in strong
   ink. Reads as a deliberate, magazine-style tab bar rather than a
   glowing UI-kit switch. */
.lab-hub-tabs {
  position: relative;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0;
  margin: 0 auto 24px;
  padding: 0;
  background: none;
  border: 0;
  border-bottom: 1px solid var(--border-soft);
  border-radius: 0;
  width: min(100%, 420px);
  max-width: 100%;
}

.lab-hub-tab {
  position: relative;
  z-index: 1;
  font: inherit;
  font-weight: 600;
  font-size: 14px;
  letter-spacing: 0.01em;
  text-align: center;
  color: var(--text-muted);
  background: none;
  border: 0;
  padding: 4px 12px 13px;
  border-radius: 0;
  cursor: pointer;
  transition: color 0.25s ease;
  -webkit-tap-highlight-color: transparent;
  white-space: nowrap;
  min-width: 0;
}

.lab-hub-tab-sub {
  font-weight: 500;
  opacity: 0.72;
  font-size: 0.9em;
}

.lab-hub-tab:hover {
  color: var(--text);
}

.lab-hub-tab:focus-visible {
  outline: 2px solid var(--purple-600);
  outline-offset: 3px;
  border-radius: 5px;
}

.lab-hub-tab[aria-selected="true"] {
  color: var(--text);
  font-weight: 700;
}

.lab-hub-tab-indicator {
  position: absolute;
  bottom: -1px;
  left: 0;
  height: 2.5px;
  width: calc(100% / 3);
  border-radius: 2px;
  background: #c44dd6;
  box-shadow: none;
  transition: transform 0.3s var(--ease-out);
  pointer-events: none;
}

.lab-hub-tabs[data-active="ion-anion"] .lab-hub-tab-indicator {
  transform: translateX(100%);
}

.lab-hub-tabs[data-active="ion-cation"] .lab-hub-tab-indicator {
  transform: translateX(200%);
}

[data-theme="dark"] .lab-hub-tabs {
  background: none;
  border-bottom-color: rgba(255, 255, 255, 0.1);
}

[data-theme="dark"] .lab-hub-tab {
  color: #9b8ec4;
}

[data-theme="dark"] .lab-hub-tab[aria-selected="true"] {
  color: #f4eeff;
}

[data-theme="dark"] .lab-hub-tab-indicator {
  background: #da85e8;
}

/* the inner Anions/Cations toggle is now redundant — the hub tabs above
   select anion vs. cation mode directly — but the two buttons stay in
   the DOM because ion-lab.js owns them (#ionTabAnions/#ionTabCations);
   see js/lab-hub-tabs.js, which clicks whichever one matches instead of
   duplicating ion-lab.js's state machine.
   Two classes on the selector so this beats the plain `.ion-tabs`
   `display: inline-flex` rule regardless of source order (equal
   single-class specificity would otherwise let whichever comes last
   win, and `.ion-tabs` is defined below this block). */
.ion-tabs.ion-tabs--legacy {
  display: none;
}

@media (max-width: 560px) {
  .lab-hub-tabs {
    width: 100%;
  }

  .lab-hub-tab {
    padding: 3px 4px 11px;
    font-size: 12.5px;
    line-height: 1.2;
  }

  .lab-hub-tab-sub {
    display: block;
    font-size: 0.82em;
  }
}

/* ── Bench layout: reagent shelf as a vertical rail on wide screens ──
   Below 900px there's no separate rule needed — .ion-bench-main and
   .ion-reagent-rail are plain blocks, so they just stack (reagent shelf
   under the ion shelf) exactly like the old single-column markup did. */
.ion-bench-main {
  min-width: 0;
}

@media (min-width: 900px) {

  /* MUST stay scoped to #ion-lab — .lab-bench is a class shared with the
     Reaction Lab's bench (same card shell family), and an unscoped grid
     here once scattered the flask console/shelf into the rail column. */
  #ion-lab .lab-bench {
    position: relative;
  }

  .ion-reagent-rail {
    position: absolute;
    top: 2px;
    right: 0;
    width: 132px;
    padding-top: 2px;
  }

  .ion-reagent-rail .lab-shelf-label {
    justify-content: center;
    text-align: center;
  }

  /* the line-fill after the label reads as a stray sliver in a 132px
     column — it exists to balance a long horizontal shelf, not a rail */
  .ion-reagent-rail .lab-shelf-label::after {
    display: none;
  }

  .ion-reagent-rail .ion-reagent-shelf {
    flex-direction: column;
    flex-wrap: nowrap;
    align-items: center;
    gap: 16px;
  }

  /* Keep the experiment and both ion collections centred on the whole
     card. The reagent rail is an overlay, so it no longer pulls the main
     content left or prematurely truncates the ion shelf. */
  #ion-lab .ion-scene,
  #ion-lab .ion-console {
    box-sizing: border-box;
    padding-inline: 154px;
  }
}

/* ── Anion step bubble ──
   Step-by-step reveal: only the CURRENT step shows, as a comic speech bubble
   beside the tube, and it alternates sides as the test advances (see the
   scene's data-step-side). One bubble, well sized — no overflow, no crammed
   list. Positioning (which side + the balancing spacer) lives with the scene
   layout rules further down. */
.ion-step-bubble {
  position: relative;
  flex: 0 0 auto;
  width: clamp(150px, 22vw, 188px);
  box-sizing: border-box;
  background: rgba(255, 255, 255, 0.92);
  border: 1.5px solid rgba(147, 51, 234, 0.22);
  border-radius: 18px;
  padding: 11px 13px 12px;
  box-shadow: var(--shadow-md);
  text-align: left;
  opacity: 0;
  transform: scale(0.82);
}
.ion-step-bubble.show {
  animation: ion-step-pop 0.42s var(--ease-spring) forwards;
}
@keyframes ion-step-pop {
  0%   { opacity: 0; transform: scale(0.6) translateY(8px); }
  60%  { opacity: 1; }
  100% { opacity: 1; transform: scale(1) translateY(0); }
}

.ion-step-bubble-head {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  row-gap: 4px;
  gap: 8px;
  margin-bottom: 8px;
}
.ion-step-bubble-badge {
  flex-shrink: 0;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 12.5px;
  font-weight: 800;
  background: var(--purple-deep);
  color: #fff;
  box-shadow: 0 4px 10px -5px rgba(124, 58, 237, 0.7);
}
.ion-step-bubble-icon {
  flex-shrink: 0;
  width: 20px;
  height: 20px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--purple-deep);
}
.ion-step-bubble-icon svg { width: 19px; height: 19px; }
.ion-step-bubble-of {
  font-size: 9.5px;
  font-weight: 800;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--purple-700);
  white-space: nowrap;
}
/* the reagent name gets the FULL bubble width (icon lives up in the head),
   and wraps only at spaces — never mid-word — so "Sodium Hydroxide" breaks
   as "Sodium / Hydroxide", never "Hydroxid / e". */
.ion-step-bubble-name {
  font-size: 13.5px;
  font-weight: 700;
  line-height: 1.24;
  color: var(--text);
  text-transform: capitalize;
  overflow-wrap: normal;
  word-break: keep-all;
  hyphens: none;
}

/* balancing spacer opposite the step bubble — reserves the mirror slot so
   the tube stays centred while only one side bubble is showing */
.ion-step-spacer {
  flex: 0 0 auto;
  width: clamp(150px, 22vw, 188px);
  align-self: stretch;
  visibility: hidden;
  pointer-events: none;
}
.ion-step-spacer[hidden] { display: none; }

/* speech-bubble tail — points at the tube, flips with the side */
.ion-step-bubble::before,
.ion-step-bubble::after {
  content: "";
  position: absolute;
  top: 50%;
  width: 0;
  height: 0;
  border-style: solid;
}
/* default (left side): tail on the right edge, pointing right toward tube */
.ion-step-bubble::before {
  right: -11px; margin-top: -9.5px;
  border-width: 9.5px 0 9.5px 11.5px;
  border-color: transparent transparent transparent rgba(147, 51, 234, 0.22);
}
.ion-step-bubble::after {
  right: -9px; margin-top: -8px;
  border-width: 8px 0 8px 10px;
  border-color: transparent transparent transparent rgba(255, 255, 255, 0.92);
}
/* right side: tail on the left edge, pointing left toward tube */
.ion-scene[data-step-side="right"] .ion-step-bubble::before {
  right: auto; left: -11px;
  border-width: 9.5px 11.5px 9.5px 0;
  border-color: transparent rgba(147, 51, 234, 0.22) transparent transparent;
}
.ion-scene[data-step-side="right"] .ion-step-bubble::after {
  right: auto; left: -9px;
  border-width: 8px 10px 8px 0;
  border-color: transparent rgba(255, 255, 255, 0.92) transparent transparent;
}

/* place the bubble + spacer on the chosen sides (desktop flex uses order,
   the mobile grid override below uses grid-column) */
.ion-scene[data-step-side="left"]  .ion-step-bubble { order: -1; }
.ion-scene[data-step-side="left"]  .ion-step-spacer { order: 1; }
.ion-scene[data-step-side="right"] .ion-step-bubble { order: 1; }
.ion-scene[data-step-side="right"] .ion-step-spacer { order: -1; }

[data-theme="dark"] .ion-step-bubble {
  background: rgba(30, 22, 48, 0.94);
  border-color: rgba(167, 139, 250, 0.28);
}
[data-theme="dark"] .ion-step-bubble-name { color: #f0eaff; }
[data-theme="dark"] .ion-step-bubble-of { color: #c4b5fd; }
[data-theme="dark"] .ion-step-bubble-icon { color: #c4b5fd; }
[data-theme="dark"] .ion-step-bubble-badge { background: #8b5cf6; }
[data-theme="dark"] .ion-step-bubble::after {
  border-color: transparent transparent transparent rgba(30, 22, 48, 0.96);
}
[data-theme="dark"] .ion-scene[data-step-side="right"] .ion-step-bubble::after {
  border-color: transparent rgba(30, 22, 48, 0.96) transparent transparent;
}

/* ── Segmented tabs ── */
.ion-tabs {
  position: relative;
  display: inline-flex;
  gap: 2px;
  margin: 4px auto 18px;
  padding: 4px;
  background: rgba(147, 51, 234, 0.07);
  border: 1px solid var(--border-soft);
  border-radius: 999px;
  width: max-content;
}

.ion-tab {
  position: relative;
  z-index: 1;
  font: inherit;
  font-weight: 700;
  font-size: 14px;
  color: var(--text-muted);
  background: none;
  border: 0;
  padding: 9px 22px;
  border-radius: 999px;
  cursor: pointer;
  transition: color 0.3s ease;
  -webkit-tap-highlight-color: transparent;
}

.ion-tab:hover {
  color: var(--purple-700);
}

.ion-tab:focus-visible {
  outline: 2px solid var(--purple-600);
  outline-offset: 2px;
}

.ion-tab[aria-selected="true"] {
  color: #fff;
}

.ion-tab-indicator {
  position: absolute;
  top: 4px;
  left: 4px;
  height: calc(100% - 8px);
  width: calc(50% - 4px);
  border-radius: 999px;
  background: var(--gradient-brand);
  box-shadow: 0 6px 16px -6px rgba(168, 85, 247, 0.55), inset 0 1px 0 rgba(255, 255, 255, 0.4);
  transition: transform 0.32s var(--ease-spring);
  pointer-events: none;
}

.ion-tabs[data-active="cations"] .ion-tab-indicator {
  transform: translateX(100%);
}

/* ── Scene: tube flanked by comic-style result callouts ──
   Flanking the bubbles left/right of the tube (rather than stacking the
   observation/conclusion text in a block below it) is what keeps the card
   short — there's no longer a tall dead zone between the action button and
   the shelves once a test completes. */
.ion-scene {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(10px, 2.4vw, 28px);
  max-width: 940px;
  margin: 18px auto 0;
}

/* .ion-stage's children (canvas/svg) are absolutely positioned, so it has
   no intrinsic size of its own — as a flex item it would collapse to zero
   width without an explicit width (which is what happened here). */
.ion-scene .ion-stage {
  margin: 0;
  width: min(230px, 40vw);
  flex: 0 0 auto;
}

.ion-bubble {
  position: relative;
  flex: 1 1 200px;
  max-width: 226px;
  background: rgba(255, 255, 255, 0.86);
  border: 1.5px solid rgba(147, 51, 234, 0.22);
  border-radius: 18px;
  padding: 12px 15px;
  box-shadow: var(--shadow-md);
  text-align: left;
  opacity: 0;
  transform: scale(0.8);
  transition: opacity 0.3s ease, transform 0.4s var(--ease-spring);
}

.ion-bubble.show {
  opacity: 1;
}

.ion-bubble--observation.show {
  transform: translateY(var(--ion-bubble-y, 0px)) scale(1) rotate(-1.4deg);
}

.ion-bubble--conclusion.show {
  transform: translateY(var(--ion-bubble-y, 0px)) scale(1) rotate(1.4deg);
}

.ion-bubble-label {
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--purple-700);
  margin-bottom: 4px;
}

.ion-bubble-text {
  margin: 0;
  color: var(--text);
  font-size: 12.5px;
  line-height: 1.52;
  overflow-wrap: normal;
  word-break: normal;
  hyphens: none;
  text-wrap: pretty;
}

/* speech-bubble tails — a slightly larger tinted triangle sitting behind a
   smaller white one gives the tail a clean border without extra markup */
.ion-bubble--observation::before,
.ion-bubble--observation::after {
  content: "";
  position: absolute;
  top: 50%;
  width: 0;
  height: 0;
  border-style: solid;
}

.ion-bubble--observation::before {
  right: -11px;
  margin-top: -9.5px;
  border-width: 9.5px 0 9.5px 11.5px;
  border-color: transparent transparent transparent rgba(147, 51, 234, 0.22);
}

.ion-bubble--observation::after {
  right: -9px;
  margin-top: -8px;
  border-width: 8px 0 8px 10px;
  border-color: transparent transparent transparent rgba(255, 255, 255, 0.94);
}

.ion-bubble--conclusion::before,
.ion-bubble--conclusion::after {
  content: "";
  position: absolute;
  top: 50%;
  width: 0;
  height: 0;
  border-style: solid;
}

.ion-bubble--conclusion::before {
  left: -11px;
  margin-top: -9.5px;
  border-width: 9.5px 11.5px 9.5px 0;
  border-color: transparent rgba(147, 51, 234, 0.22) transparent transparent;
}

.ion-bubble--conclusion::after {
  left: -9px;
  margin-top: -8px;
  border-width: 8px 10px 8px 0;
  border-color: transparent rgba(255, 255, 255, 0.94) transparent transparent;
}

/* the desktop rail reservation isn't needed in anion mode (no rail there),
   so reclaim that side padding to give the step bubble room by the tube */
@media (min-width: 900px) {
  #ion-lab[data-mode="anions"] .ion-scene,
  #ion-lab[data-mode="anions"] .ion-console {
    padding-inline: 0;
  }
}

/* ── Stage (test tube canvas) ── */
.ion-stage {
  position: relative;
  height: clamp(190px, 30vh, 280px);
  margin: 0 auto;
  max-width: 720px;
}

.ion-stage canvas {
  position: absolute;
  top: 0;
  left: 0;
  display: block;
  pointer-events: none;
}

.ion-stage canvas,
.ion-glass {
  z-index: 2;
}

.ion-glass {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  overflow: visible;
  transition: filter 0.25s ease;
}

/* tube lights up while a dragged chip hovers over it */
.ion-stage.drag-target .ion-glass {
  filter: drop-shadow(0 0 12px rgba(192, 132, 252, 0.65));
}

/* ── Drag ghost: the chip disc that rides along under the pointer ── */
.ion-ghost {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 400;
  pointer-events: none;
  will-change: transform;
  margin: 0;
}

.ion-ghost .ion-chip-art,
.ion-ghost .ion-chip-formula {
  width: 52px;
  height: 52px;
  filter: drop-shadow(0 10px 14px rgba(43, 25, 62, 0.35));
}

#ion-lab .lab-hint {
  left: 50%;
  top: 46%;
  transform: translate(-50%, -50%) rotate(-3deg);
}

#ion-lab .lab-hint.gone {
  transform: translate(-50%, -30%) rotate(-3deg);
}

/* ── Sequence pills ── */
.ion-console {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  margin-top: 2px;
  min-height: 62px;
  transition: opacity 0.35s ease, transform 0.35s var(--ease-out);
}

.ion-console.gone {
  opacity: 0;
  transform: translateY(8px);
  pointer-events: none;
  position: absolute;
  visibility: hidden;
}

.ion-sequence {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 6px 8px;
  min-height: 30px;
  font-weight: 700;
  font-size: 13.5px;
  color: var(--text-muted);
}

.ion-seq-pill {
  display: inline-flex;
  align-items: center;
  padding: 5px 13px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.72);
  border: 1px solid rgba(147, 51, 234, 0.22);
  color: var(--text);
  white-space: nowrap;
  animation: ion-pill-pop 0.4s var(--ease-spring);
}

.ion-seq-pill.pending {
  color: var(--text-muted);
  border-style: dashed;
  background: rgba(255, 255, 255, 0.35);
}

.ion-seq-arrow {
  color: var(--text-muted);
  opacity: 0.7;
  font-weight: 800;
}

@keyframes ion-pill-pop {
  0% {
    transform: scale(0.7);
    opacity: 0;
  }

  100% {
    transform: scale(1);
    opacity: 1;
  }
}

/* #ionAction/#ionRinse/#ionAgain need no colour/surface overrides of their
   own — the shared `.lab-mix`/`.lab-rinse`/`.lab-again` base classes (see
   reaction-lab.css) already give the deep-violet CTA + bordered rinse pill
   look, identical to the Reaction Lab's Mix/Rinse. `.ion-action` only adds a
   width guard: its label text is longer and more variable ("Choose a
   reagent", "Warm gently"...) than Reaction Lab's fixed "Mix", so without a
   min-width the button would resize between states. */
.ion-action {
  min-width: 190px;
}

/* The post-result action sits between the evidence and the shelf — this
   offset is layout-only (positioning it in that gap); its actual button
   look now comes entirely from the shared `.lab-rinse.lab-again` styling. */
#ionAgain {
  position: relative;
  top: 40px;
  z-index: 5;
}

/* .lab-result / .ion-comparison set `display:flex` unconditionally (reused
   verbatim from reaction-lab.css), which — being an author rule — beats the
   UA `[hidden]{display:none}` default at equal specificity. Without this,
   both stayed laid out (an invisible ~60px box plus margin) even while
   hidden, which was the dead space bloating the idle-state card height. */
#ion-lab .ion-result[hidden],
#ion-lab .ion-comparison[hidden] {
  display: none;
}

/* ── Result panel additions (shell reused from .lab-result) ── */
.ion-result-sequence {
  margin-top: 10px;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--text-muted);
  letter-spacing: 0.01em;
}

/* ── Comparison card (cations only) ── */
.ion-comparison {
  max-width: 560px;
  margin: 14px auto 0;
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  justify-content: center;
}

.ion-comparison-card {
  flex: 1 1 220px;
  background: rgba(255, 255, 255, 0.68);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-sm);
  padding: 12px 16px;
  text-align: left;
}

.ion-comparison-card__label {
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--purple-700);
  margin-bottom: 4px;
}

.ion-comparison-card__text {
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--text-muted);
}

/* ── Ion / reagent chips (typographic, formula-driven) ── */
.ion-chip {
  flex: 0 0 76px;
  background: none;
  border: 0;
  font: inherit;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 8px 4px 6px;
  border-radius: 16px;
  cursor: grab;
  touch-action: pan-x;
  -webkit-tap-highlight-color: transparent;
  transition: background 0.25s ease, transform 0.3s var(--ease-spring), opacity 0.25s ease;
}

.ion-chip:active {
  cursor: grabbing;
}

.ion-chip.dragging {
  opacity: 0.3;
}

/* the illustrated chips are <img>s — never let the browser's native
   image-drag start instead of the pointer-driven ghost */
.ion-chip img {
  -webkit-user-drag: none;
  -webkit-touch-callout: none;
  user-select: none;
}

.ion-chip:hover {
  background: rgba(255, 255, 255, 0.8);
  transform: translateY(-3px);
}

.ion-chip:focus-visible {
  outline: 2px solid var(--purple-600);
  outline-offset: 1px;
}

.ion-chip[aria-pressed="true"] {
  background: rgba(233, 213, 255, 0.55);
  box-shadow: inset 0 0 0 1.5px rgba(147, 51, 234, 0.4);
}

.ion-chip[aria-disabled="true"] {
  opacity: 0.4;
  cursor: not-allowed;
}

.ion-chip.deny {
  animation: ion-deny 0.4s ease;
}

@keyframes ion-deny {

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

  25% {
    transform: translateX(-5px) rotate(-2deg);
  }

  60% {
    transform: translateX(4px) rotate(1.5deg);
  }
}

.ion-chip-formula {
  position: relative;
  display: grid;
  place-items: center;
  width: 48px;
  height: 48px;
  border-radius: 50%;
  background: linear-gradient(160deg, rgba(233, 213, 255, 0.6), rgba(244, 114, 182, 0.14));
  border: 1.5px solid rgba(147, 51, 234, 0.28);
  font-weight: 800;
  font-size: 13px;
  color: var(--purple-700);
  letter-spacing: 0.01em;
}

.ion-chip-formula sub,
.ion-chip-formula sup {
  font-size: 0.65em;
}

/* illustrated ion buttons (Buttons/ folder artwork) — replaces the plain
   formula-circle icon on the anion shelf with the molecule illustration */
.ion-chip-art {
  position: relative;
  display: grid;
  place-items: center;
  width: 48px;
  height: 48px;
}

.ion-chip-art-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  transition: transform 0.3s var(--ease-spring);
}

.ion-chip:hover .ion-chip-art-img {
  transform: scale(1.06);
}

.ion-chip-art-dark {
  display: none;
}

[data-theme="dark"] .ion-chip-art-light {
  display: none;
}

[data-theme="dark"] .ion-chip-art-dark {
  display: block;
}

.ion-chip-name {
  font-size: 9.5px;
  font-weight: 600;
  color: var(--text-muted);
  letter-spacing: 0.02em;
  text-align: center;
  white-space: nowrap;
}

/* the little swatch dot previews the ion's characteristic result colour
   right on the shelf, so students can start building colour intuition
   before they even run the test */
.ion-chip-dot {
  position: absolute;
  bottom: -2px;
  right: -2px;
  width: 15px;
  height: 15px;
  border-radius: 50%;
  background: var(--chip-swatch, transparent);
  border: 2px solid #fff8fc;
  box-shadow: 0 1px 4px rgba(43, 25, 62, 0.3);
}

[data-theme="dark"] .ion-chip-dot {
  border-color: #1e1632;
}

/* reagent chips: icon-driven, tinted by the reagent's own swatch colour
   so acid / silver nitrate / heat etc. are visually distinct at a glance */
.ion-reagent-shelf .ion-chip-formula {
  background: color-mix(in srgb, var(--chip-swatch, #a78bfa) 22%, rgba(255, 255, 255, 0.7));
  border-color: color-mix(in srgb, var(--chip-swatch, #a78bfa) 55%, transparent);
  color: color-mix(in srgb, var(--chip-swatch, #7c3aed) 72%, #2c1a0e);
}

.ion-reagent-shelf .ion-chip-formula svg {
  width: 22px;
  height: 22px;
}

.ion-reagent-shelf .ion-chip-name {
  white-space: normal;
  line-height: 1.15;
  max-width: 78px;
}

/* tab panels: hide inactive panel's shelf entirely */
#ionPanelAnions[hidden],
#ionPanelCations[hidden] {
  display: none;
}

/* ── Dark mode ── */
[data-theme="dark"] .ion-tabs {
  background: rgba(139, 92, 246, 0.12);
  border-color: rgba(139, 92, 246, 0.14);
}

[data-theme="dark"] .ion-tab {
  color: #9b8ec4;
}

[data-theme="dark"] .ion-tab[aria-selected="true"] {
  color: #fff;
}

[data-theme="dark"] .ion-seq-pill {
  background: rgba(255, 255, 255, 0.06);
  border-color: rgba(192, 132, 252, 0.3);
  color: #ede8f5;
}

[data-theme="dark"] .ion-seq-pill.pending {
  background: rgba(255, 255, 255, 0.03);
  color: #9b8ec4;
}

[data-theme="dark"] .ion-comparison-card {
  background: rgba(255, 255, 255, 0.05);
  border-color: rgba(192, 132, 252, 0.2);
}

[data-theme="dark"] .ion-comparison-card__text {
  color: #9b8ec4;
}

[data-theme="dark"] .ion-chip:hover {
  background: rgba(139, 92, 246, 0.14);
}

[data-theme="dark"] .ion-chip[aria-pressed="true"] {
  background: rgba(139, 92, 246, 0.22);
  box-shadow: inset 0 0 0 1.5px rgba(192, 132, 252, 0.5);
}

[data-theme="dark"] .ion-chip-name {
  color: #9b8ec4;
}

[data-theme="dark"] .ion-chip-formula {
  background: linear-gradient(160deg, rgba(139, 92, 246, 0.22), rgba(244, 114, 182, 0.1));
  border-color: rgba(192, 132, 252, 0.4);
  color: #e9d5ff;
}

[data-theme="dark"] .ion-reagent-shelf .ion-chip-formula {
  background: color-mix(in srgb, var(--chip-swatch, #a78bfa) 30%, rgba(30, 22, 50, 0.6));
  border-color: color-mix(in srgb, var(--chip-swatch, #a78bfa) 60%, transparent);
  color: color-mix(in srgb, var(--chip-swatch, #e9d5ff) 82%, #f5f3ff);
}

[data-theme="dark"] .ion-bubble {
  background: rgba(30, 22, 50, 0.88);
  border-color: rgba(192, 132, 252, 0.3);
}

[data-theme="dark"] .ion-bubble-text {
  color: #ede8f5;
}

[data-theme="dark"] .ion-bubble--observation::before {
  border-color: transparent transparent transparent rgba(192, 132, 252, 0.3);
}

[data-theme="dark"] .ion-bubble--observation::after {
  border-color: transparent transparent transparent rgba(30, 22, 50, 0.92);
}

[data-theme="dark"] .ion-bubble--conclusion::before {
  border-color: transparent rgba(192, 132, 252, 0.3) transparent transparent;
}

[data-theme="dark"] .ion-bubble--conclusion::after {
  border-color: transparent rgba(30, 22, 50, 0.92) transparent transparent;
}

/* ── Responsive ── */
@media (max-width: 768px) {
  #ion-lab .home-heading-friendly {
    font-size: clamp(1.35rem, 6vw, 1.6rem);
  }

  .ion-tabs {
    margin-bottom: 14px;
  }

  .ion-tab {
    padding: 8px 16px;
    font-size: 13px;
  }

  .ion-stage {
    height: clamp(190px, 28vh, 240px);
  }

  /* keep the comic flanking layout (bubble / tube / bubble) on phones too:
     the bubbles share the tube's grid ROW, in the two side columns, with a
     fixed centre column reserved for the glass. Because they sit beside the
     tube (not under it), the desktop side-facing tails still apply and
     their tips land on the tube's side walls. The 84px centre column is
     sized to the drawn glass (58–80px wide depending on stage height) so
     each 11px tail just reaches it at any viewport. */
  .ion-scene {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 84px minmax(0, 1fr);
    align-items: center;
    gap: 0;
    margin-top: 18px;
    max-width: 420px;
  }

  .ion-scene .ion-stage {
    grid-column: 1 / -1;
    grid-row: 1;
    width: min(150px, 42vw);
    margin-inline: auto;
    justify-self: center;
  }

  .ion-bubble {
    grid-row: 1;
    /* above the stage canvas/svg so both tails overlap the glass edge
       symmetrically (the stage sits between the two bubbles in the DOM) */
    z-index: 2;
    display: flex;
    flex-direction: column;
    justify-content: center;
    min-width: 0;
    min-height: 84px;
    max-width: none;
    width: 100%;
    box-sizing: border-box;
    padding: 12px 10px;
    border-width: 2px;
    border-radius: 20px;
    text-align: center;
  }

  /* `display: flex` above would otherwise beat the UA's `[hidden]`
     rule (same trap documented at .ion-result[hidden]) and leave two
     invisible boxes overlapping the idle tube */
  .ion-bubble[hidden] {
    display: none;
  }

  .ion-bubble--observation {
    grid-column: 1;
  }

  .ion-bubble--conclusion {
    grid-column: 3;
  }

  /* the current-step bubble takes whichever side it's on; its spacer takes
     the opposite column so the tube stays centred in the middle track. The
     bubble sizes to its CONTENT (capped) rather than the narrow side track —
     on small phones that track is only ~100px, which chopped the header —
     and it overlaps the tube edge from its side (z-index 2), exactly like the
     result bubbles do. */
  .ion-step-bubble {
    grid-row: 1;
    z-index: 2;
    width: max-content;
    max-width: min(210px, 64vw);
    min-width: 0;
    padding: 10px 11px;
    box-sizing: border-box;
  }
  .ion-step-spacer {
    grid-row: 1;
    width: 100%;
    max-width: none;
    box-sizing: border-box;
  }
  .ion-step-bubble-name { font-size: 12.5px; }
  /* hug the tube: left bubble anchors its RIGHT edge to the centre track
     (justify-self:end), right bubble anchors its LEFT edge (start), so each
     overlaps the tube from its side and the tail lands on the glass. */
  .ion-scene[data-step-side="left"]  .ion-step-bubble { grid-column: 1; justify-self: end; }
  .ion-scene[data-step-side="left"]  .ion-step-spacer { grid-column: 3; }
  .ion-scene[data-step-side="right"] .ion-step-bubble { grid-column: 3; justify-self: start; }
  .ion-scene[data-step-side="right"] .ion-step-spacer { grid-column: 1; }

  .ion-bubble-label {
    font-size: 8.5px;
    letter-spacing: 0.07em;
  }

  .ion-bubble-text {
    /* iOS has no Comic Sans, and its generic `cursive` maps to Snell
       Roundhand (an ornate calligraphy script) — so give WebKit its own
       comic face (Chalkboard SE) and never fall through to `cursive` */
    font-family: "Comic Sans MS", "Comic Sans", "Chalkboard SE", "Comic Neue", sans-serif;
    font-size: 11.5px;
    font-weight: 700;
    line-height: 1.35;
    text-align: center;
  }

  .ion-bubble--observation.show,
  .ion-bubble--conclusion.show {
    transform: translateY(var(--ion-bubble-y, 0px)) scale(1);
  }

  .ion-shelf,
  .ion-reagent-shelf {
    display: grid;
    justify-content: safe center;
    grid-template-columns: none;
    grid-auto-flow: column;
    grid-template-rows: repeat(2, auto);
    grid-auto-columns: 66px;
    overflow-x: auto;
    padding: 6px 0;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }

  /* Ion choices are a small, fixed collection. Let them wrap as centred
     flex rows so an incomplete final row is centred instead of starting
     at the left edge of the shelf. */
  .ion-shelf {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 6px 2px;
    overflow-x: visible;
  }

  .ion-reagent-shelf {
    display: flex;
    flex-flow: row wrap;
    justify-content: center;
    align-items: flex-start;
    gap: 8px 10px;
    overflow-x: visible;
  }

  .ion-shelf::-webkit-scrollbar,
  .ion-reagent-shelf::-webkit-scrollbar {
    display: none;
  }

  .ion-action {
    min-width: 0;
  }

  .ion-chip-art {
    width: 54px;
    height: 54px;
  }

  .ion-reagent-shelf .ion-chip-formula {
    width: 52px;
    height: 52px;
  }

  /* the desktop 40px drop left the post-result rinse stranded halfway to
     the shelf on phones — keep it tucked just under the tube instead */
  #ionAgain {
    top: 30px;
  }

  .ion-comparison {
    flex-direction: column;
  }
}

/* ── Reduced motion ── */
@media (prefers-reduced-motion: reduce) {
  .ion-tab-indicator {
    transition: none;
  }

  .ion-seq-pill {
    animation: none;
  }

  .ion-chip,
  .ion-chip-formula,
  .ion-console {
    transition: none;
  }

  .ion-chip.deny {
    animation: none;
  }

  .ion-bubble {
    transition: none;
  }
}
