/* ══════════════════════════════════════════════════════════════════════════
   GLASS LAGOON — UI refresh: tile-based table + mode pickers (mdxs/ only)
   ──────────────────────────────────────────────────────────────────────────
   PURE ADDITIVE. Loaded after times-tables.css / glass-skin.css. Styles the
   grid + mode tiles that table-picker.js injects into the Play and Practice
   modals (desktop dropdown-card AND mobile bottom sheet), and hides the native
   <select>s / custom dropdown they proxy. No base file is edited. Tokens and
   sizes are from the UI-refresh handoff spec (exact).
   ══════════════════════════════════════════════════════════════════════════ */

/* Hide the native controls the tiles stand in for (kept in the DOM so every
   existing change-handler / Play-Start wiring still fires when a tile taps). */
.gl-native-hidden { display: none !important; }

/* One small label above the grid (the only heading in either modal). */
.gl-picker-label {
  font-family: inherit;
  font-size: 12px;
  font-weight: 600;
  color: #5F6E8C;
  margin: 0 2px 8px;
}

/* ─── Shared table grid (both modals) ─────────────────────────────────────── */
.gl-table-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 8px;
  margin-bottom: 18px;
}
.gl-tile {
  font-family: inherit;
  border-radius: 14px;
  padding: 12px 4px;
  text-align: center;
  background: #FFFFFF;
  border: 1px solid #C9D9F2;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  /* 62, not 44: at 44 the tile rendered 45px tall and the numeral filled it
     top to bottom, which read as cramped and left no corner for the padlock to
     sit in without landing on the numeral's line. 54 was tried first and still
     read tight on a device. Raising min-height rather than padding adds the
     room without disturbing the padding compensation that .gl-selected,
     .gl-done and the ≤360px variant each depend on. */
  min-height: 62px;
  /* Hairline of lift. This is what the recessed locked tile is read against —
     without it the inset shadow below has nothing to contrast with.

     !important is REQUIRED here, not sloppiness: flat-experiment.css carries a
     universal `*, *::before, *::after { box-shadow: none !important }` that
     deliberately flattens the app, and an !important declaration beats a
     normal one at any specificity. That file's documented way to opt out is to
     declare the shadow !important (it already keeps the app's modal elevation
     that way). Drop the !important on any shadow in this block and the whole
     recessed/raised treatment silently disappears. */
  box-shadow: 0 1px 3px rgba(30, 60, 120, 0.14) !important;
  transition: border-color 150ms ease, color 150ms ease,
    background-color 150ms ease, transform 120ms ease, box-shadow 120ms ease;
}
/* :not(.gl-locked) on both — a locked tile that lifted on hover would undo the
   whole point of the treatment. */
.gl-tile:hover:not(.gl-locked) {
  transform: translateY(-1px);
  box-shadow: 0 2px 5px rgba(30, 60, 120, 0.18) !important;
}
.gl-tile:active:not(.gl-locked) {
  transform: translateY(0);
  box-shadow: inset 0 1px 3px rgba(30, 60, 120, 0.16) !important;
}
.gl-tile .gl-num { font-size: 19px; font-weight: 600; color: #444441; line-height: 1; }
.gl-tile .gl-tick { font-size: 13px; color: #0F6E56; line-height: 1; margin-top: 2px; display: flex; }
/* Completed: all game modes 100% for that table */
.gl-tile.gl-done { background: #E1F5EE; border: 1px solid #9FE1CB; padding: 6px 4px; }
.gl-tile.gl-done .gl-num { color: #085041; }
/* Selected: 2px border, -1px padding keeps tile size stable (no layout shift) */
.gl-tile.gl-selected { border: 2px solid #1E6FD9; padding: 11px 3px; }
.gl-tile.gl-selected.gl-done { padding: 5px 3px; }
.gl-tile.gl-selected .gl-num { color: #185FA5; }
.gl-tile:focus-visible { outline: 2px solid #1E6FD9; outline-offset: 2px; }
/* Entitlement lock — tables 4–12 for non-entitled users (Parent Model P1).
   Tables 1–3 stay free. Muted tile + corner padlock; still tappable so the tap
   surfaces the "unlock" nudge (handled in table-picker.js / times-tables.js). */
/* Recessed treatment: locked tiles sit INSIDE the panel surface while unlocked
   ones sit above it, so the two states separate by apparent depth before the
   padlock is read. Three things carry it and all three are load-bearing —
   the `inset` keyword (an outset shadow reads as lifted, an inset one as a
   dent), the missing outline (an outline is what makes a tile look pressable),
   and a fill DARKER than the panel behind it (anything lighter reads as
   raised; the old #F4F5F7 was nearly the panel's own value, which is why these
   still floated).

   The fill is #EEEDF3 — a touch lighter than the spec's #E3E6EA, and slightly
   violet rather than flat grey, so the block reads as quiet rather than dead.
   It is still darker than the #FBFCFE panel, which is what the recess depends
   on, just by less; the inset shadow carries proportionally more of the effect
   at this value. If the dent ever reads too weak, deepen that shadow rather
   than darkening the fill back.

   Must stay after .gl-selected so a stale selected class on a locked tile
   cannot reintroduce a visible border.

   `border-color: transparent` rather than `border: none`: the outline goes
   away either way, but keeping the 1px reserved means the tile cannot change
   size. Dropping the border outright would shrink every locked tile by 2px in
   both axes, and would need separate padding compensation for the base tile,
   .gl-done, .gl-selected and the ≤360px variant — four places to keep in sync
   for no visible gain. */
.gl-tile.gl-locked {
  background: #EEEDF3;
  border-color: transparent;
  box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.1) !important; /* see .gl-tile */
  cursor: not-allowed;
  position: relative;
}
.gl-tile.gl-locked .gl-num { color: #9AA0A8; }
/* The spec's 15px at top 7px / right 9px was written against a taller tile.
   In this one it put the padlock at y 8–23 against a numeral at y 13–32 — a
   10px overlap, so it read as part of the number rather than a corner badge.
   13px at 6/7 sits clear in the corner: on tile 12, the worst case, the badge
   and the numeral are 8px apart horizontally so they never meet. */
.gl-tile.gl-locked .gl-lock {
  position: absolute; top: 6px; right: 7px; display: flex; color: #8B9199;
}
.gl-tile.gl-locked .gl-lock i,
.gl-tile.gl-locked .gl-lock svg { width: 13px; height: 13px; }

/* The lift and the dent are both motion-free at rest; only the hover nudge
   needs suppressing. */
@media (prefers-reduced-motion: reduce) {
  .gl-tile { transition: border-color 150ms ease, color 150ms ease, background-color 150ms ease; }
  .gl-tile:hover:not(.gl-locked) { transform: none; }
}

/* ─── Picker header: label left, unlock link right ────────────────────────
   The link is shown only when the grid contains locked tiles (toggled in
   table-picker.js). Locked tiles are inert on tap, so it is the only route
   from "why can't I pick 7?" to the sign-in surface — it carries the upgrade
   path for the whole picker rather than nine padlocks each trying to.

   Baseline alignment, not centre: the two have different sizes and weights, so
   sitting them on a shared baseline is what makes them read as one row. */
.gl-picker-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}
/* A real <button>, so it is keyboard reachable and announced as an action —
   styled as a plain underlined link because a filled button here would compete
   with Start Game for the child's attention, and this line is aimed at a
   grown-up. */
.gl-unlock-link {
  font-family: inherit;
  /* Same 12px as .gl-picker-label so the two read as one row. The label's size
     is a UI-refresh handoff token, so the link matches down to it rather than
     the label being pushed up. Weight stays lighter than the label's 600 — the
     underline is what marks it as tappable, not extra weight. */
  font-size: 12px;
  font-weight: 500;
  color: #33415C;
  background: none;
  border: 0;
  /* Vertical padding buys back a usable tap target at 12px text without
     changing the visual size; baseline alignment is unaffected by it. The
     negative margin cancels the padding so the row height does not grow. */
  padding: 8px 2px;
  margin: -8px 0 0;
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 3px;
  white-space: nowrap; /* never wraps mid-phrase against the label */
}
.gl-unlock-link:hover { color: #1E6FD9; }
.gl-unlock-link:focus-visible { outline: 2px solid #1E6FD9; outline-offset: 2px; border-radius: 3px; }

/* ─── Mode tiles (Play = 3, Practice = 2) ─────────────────────────────────── */
.gl-mode-row { display: grid; gap: 8px; margin-top: 2px; margin-bottom: 16px; }
.gl-mode-row-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.gl-mode-row-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.gl-mode {
  font-family: inherit;
  background: #FFFFFF;
  border: 1px solid #C9D9F2;
  border-radius: 14px;
  padding: 12px 8px 10px;
  text-align: center;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  align-items: center;
  min-height: 44px;
  transition: border-color 150ms ease, color 150ms ease;
}
.gl-mode.gl-selected { border: 2px solid #1E6FD9; padding: 11px 7px 9px; }
.gl-mode .gl-icon-circle {
  width: 30px; height: 30px;
  border-radius: 50%;
  background: #F1EFE8;
  color: #5F5E5A;
  display: flex; align-items: center; justify-content: center;
  margin-bottom: 8px;
  flex: 0 0 auto;
}
.gl-mode .gl-icon-circle svg { width: 16px; height: 16px; }
.gl-mode.gl-selected .gl-icon-circle { background: #E6F1FB; color: #185FA5; }
.gl-mode .gl-name {
  font-size: 12px; font-weight: 600; color: #444441; line-height: 1.3;
  min-height: 2.6em; /* reserve two lines so bars align across the row (N=4..12) */
  display: flex; align-items: center; justify-content: center;
}
.gl-mode.gl-selected .gl-name { color: #185FA5; }
.gl-mode .gl-bar {
  width: 100%; height: 4px; background: #EDEBE4; border-radius: 999px;
  overflow: hidden; margin: 8px 0 4px;
}
.gl-mode.gl-selected .gl-bar { background: #E6F1FB; }
.gl-mode .gl-bar .gl-fill {
  display: block; height: 100%; background: #888780; border-radius: 999px;
  transition: width 200ms ease;
}
.gl-mode.gl-selected .gl-bar .gl-fill { background: #1E6FD9; }
.gl-mode .gl-pct { font-size: 11px; color: #5F5E5A; }
.gl-mode.gl-selected .gl-pct { color: #185FA5; font-weight: 600; }
.gl-mode:focus-visible { outline: 2px solid #1E6FD9; outline-offset: 2px; }

/* Practice tiles: larger icon, single-line label, no bars/percentages */
.gl-mode-row-2 .gl-mode { padding: 14px 8px; }
.gl-mode-row-2 .gl-mode.gl-selected { padding: 13px 7px; }
.gl-mode-row-2 .gl-icon-circle { width: 36px; height: 36px; margin-bottom: 8px; }
.gl-mode-row-2 .gl-icon-circle svg { width: 18px; height: 18px; }
.gl-mode-row-2 .gl-name { font-size: 13px; min-height: 0; }

/* ── Game-mode tile icon circles tinted to that mode's CARD colour ──
   Each game type's cards (behind the modal) use a signature colour; tint the
   picker's icon circle to the primary/dark of that card gradient (see
   times-tables.css) so the tile reads as the same game type. White icon for
   clear contrast + to make the colour obvious. Placed after the base and
   .gl-selected circle rules (equal specificity → source order wins), so the
   mode colour shows whether the tile is selected or not. Game modes only;
   Practice's table/slides tiles keep the neutral circle. */
.gl-mode[data-mode="findResults"]  .gl-icon-circle { background: #1564dc; color: #ffffff; } /* Match Equations — blue */
.gl-mode[data-mode="findEquations"] .gl-icon-circle { background: #7c3aed; color: #ffffff; } /* Match Results — purple */
.gl-mode[data-mode="findFactors"]  .gl-icon-circle { background: #bd354e; color: #ffffff; } /* Match Factors — maroon */

/* Narrow viewport safety (≥340px): the reference sizes already fit, this just
   guards the very smallest phones from horizontal overflow. */
@media (max-width: 360px) {
  .gl-table-grid { gap: 6px; }
  .gl-mode-row { gap: 6px; }
  .gl-tile { padding: 10px 2px; }
}

/* ══════════════════════════════════════════════════════════════════════════
   TOP NAV (in-game header) — mobile refresh (≤768px)
   ──────────────────────────────────────────────────────────────────────────
   Scoped to the mobile header (the mockup is mobile/tablet; the desktop header
   keeps its top-nav tabs). One row: avatar · colour-coded stat pills (Correct /
   Miss / Coins / the original % progress capsule) · profile. The stat data
   already lives in #scoreDisplay — the single score cluster shared with the
   wide view — and this layer no longer restyles it, only positions it.
   ══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 768px) {
  /* more opaque white header */
  .menu-container {
    background: rgba(255, 255, 255, 0.96) !important;
    -webkit-backdrop-filter: blur(10px);
    backdrop-filter: blur(10px);
    border-bottom: 1px solid rgba(30, 111, 217, 0.12);
    box-shadow: 0 2px 10px rgba(5, 18, 58, 0.10) !important;
  }

  /* No score-cluster overrides here at all. There is one #scoreDisplay now, and
     it renders at the same size, in the same pill, at every width — which is
     the whole point of collapsing the two elements into one.

     What used to live here: a transparent background with its own gap and
     padding, per-item padding and radius, and a narrowed progress capsule. Each
     was a size or look difference with no reason beyond the two elements having
     been styled independently. Centring is handled in glass-skin.css, and
     deliberately without a transform — see below. */
  /* The tick and miss tallies deliberately have no pill and no colour override:
     they render exactly as the wide view does, on the bar's own background.

     There WERE green and red pill rules here, selecting with :has() nested
     inside :has(). That is invalid — Selectors 4 forbids it — so the browser
     dropped those rules and the pills never painted, in any build. What did
     apply were the icon and number colours darkened to sit on those pills, so
     mobile ended up with a dark teal tick and a dark red X floating on white
     while the wide view used the standard green and red. Removed rather than
     repaired: the flat treatment is what has actually been shipping and looks
     right, and matching the wide view is the point.

     If pills are ever wanted here, the selector must be a single :has() —
     e.g. .score-item:has(> .score-icon-wrapper > svg[stroke="#28a745"]) — and
     the icon/number colours have to be darkened again in the same change. */

  /* No tally or coin overrides at all — shape, colour and the coin's swap
     behaviour all come from the shared layers, so every stat in this cluster is
     the same at any width.

     The coin had the worst of these overrides: it hid `.score-coin-icon > svg`
     and painted its own amber coin via ::before. times-tables.js only renders an
     <svg> while the count is 0 — the moment a child earns a coin it swaps in a
     <lottie-player>, which `> svg` does not match. So the CSS coin stayed lit
     and the animated coin appeared beside it, and mobile showed two coins from
     the first coin onward. Measured before the fix: 2 coin visuals at 3 coins on
     mobile, 1 on desktop.

     Hiding <lottie-player> too would have kept them in sync but kept the static
     coin, losing the animation that marks earning one. Dropping the override
     restores the wide view's behaviour, which is what was asked for.

     The amber pill and its #633806 count colour went with it: they existed to
     frame that static coin, and with the tick and X now pill-less the coin would
     have been the only stat still wearing one. */

  /* The progress capsule keeps its shared 70px, and centring is left to
     glass-skin's inset:0 + margin:auto.

     A translate(-50%,-50%) centring rule used to sit here, and it is why the
     cluster was visibly smaller on a phone: .score carries transform:
     scale(1.2), so replacing the transform to centre silently dropped the
     cluster back to 1.0 while the wide view stayed at 1.2. Centring via
     margin:auto leaves the transform alone, so the scale — and the pulse
     animation that also uses it — survive. */
}

/* On smaller phones the cluster + progress would crowd the buddy/profile — drop
   the progress capsule there and let the remaining pills re-centre (fit-content
   + the translate-centre above keep them balanced). */
/* Small phones are the one place the cluster still differs from the wide view,
   and it is a fit problem rather than a style one: at full size it is 272px
   wide, and centred in a 375px header that runs into the buddy avatar (and into
   the profile button by 320px). Measured — the collision is real, not
   defensive. Dropping the % capsule is what buys the room; the stats that
   remain keep the shared size, so nothing looks scaled-down. */
@media (max-width: 400px) {
  #scoreDisplay.score { min-width: 0; }
}

/* Below ~350px even the shrink-wrapped cluster reaches the avatar and the
   profile button, so the % capsule comes out — the last thing added and the one
   stat repeated elsewhere in the UI. Everything left keeps the shared size.
   Measured at 320px: with the capsule it overlaps both neighbours, without it,
   neither. */
@media (max-width: 350px) {
  #scoreDisplay .score-item:has(.score-progress-bar) { display: none !important; }
}

/* ══════════════════════════════════════════════════════════════════════════
   PROGRESS MODAL — view tiles + stat cards (progress-picker.js)
   ──────────────────────────────────────────────────────────────────────────
   Replaces the Overview/Aquarium segmented toggle with two large view tiles
   (reusing the mode-tile selected/default recipe) and restyles the health +
   coin pills as clearly NON-interactive stat cards (squarer 12px radius, no
   border, not focusable). Both modal renderings (desktop card + mobile sheet).
   ══════════════════════════════════════════════════════════════════════════ */
.gl-view-row {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px;
  margin-bottom: 18px;
}
.gl-view {
  font-family: inherit;
  background: #FFFFFF;
  border: 1px solid #C9D9F2;
  border-radius: 14px;
  padding: 16px 10px 14px;
  text-align: center;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  align-items: center;
  min-height: 44px;
  transition: border-color 150ms ease, color 150ms ease;
  /* The label text is selectable, so clicking directly on "My progress" /
     "My aquarium" registered as the start of a text selection and swallowed
     the click. Make the tile non-selectable so a tap on the text is a tap. */
  user-select: none;
  -webkit-user-select: none;
}
.gl-view.gl-selected { border: 2px solid #1E6FD9; padding: 15px 9px 13px; }
.gl-view .gl-view-icon {
  width: 40px; height: 40px;
  border-radius: 50%;
  background: #F1EFE8;
  color: #5F5E5A;
  display: flex; align-items: center; justify-content: center;
  margin-bottom: 8px;
  flex: 0 0 auto;
  pointer-events: none; /* clicks always attribute to the tile button */
}
.gl-view .gl-view-icon svg { width: 20px; height: 20px; }
.gl-view.gl-selected .gl-view-icon { background: #E6F1FB; color: #185FA5; }
.gl-view .gl-view-name { font-size: 14px; font-weight: 600; color: #444441; pointer-events: none; }
.gl-view.gl-selected .gl-view-name { color: #185FA5; }
.gl-view:focus-visible { outline: 2px solid #1E6FD9; outline-offset: 2px; }

/* Stat cards — presentational, NOT buttons (squarer 12px, no border/cursor) */
.gl-stats {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px;
  padding: 2px 0;
}
.gl-stat {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  font-size: 15px;
  font-weight: 600;
  border-radius: 12px;
  padding: 12px 10px;
}
.gl-stat-health { background: #FDF3F3; color: #791F1F; }
.gl-stat-coins  { background: #FBF4E4; color: #633806; }
.gl-stat .gl-stat-glyph { display: flex; align-items: center; flex: 0 0 auto; }
/* Coin card: number over its caption. The heart card stays a plain row — it
   needs no caption, and stacking it would only pad the taller of the two. */
.gl-stat .gl-stat-text {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  line-height: 1.15;
  min-width: 0;
}
.gl-stat .gl-stat-cap {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.02em;
  opacity: 0.72;
  white-space: nowrap;
}
.gl-stat .gl-stat-glyph svg { width: 17px; height: 17px; display: block; }
.gl-stat .gl-stat-bar {
  width: 64px; height: 8px;
  background: #F5D9D9;
  border-radius: 999px;
  overflow: hidden;
  flex: 0 0 auto;
}
.gl-stat .gl-stat-bar .gl-stat-fill {
  display: block; height: 100%;
  background: #E05252;
  border-radius: 999px;
  transition: width 300ms ease;
}

/* Student sign-in modal: the code field turns green on focus, matching the
   header and the button that opened the modal. !important because the field
   carries its border in an inline style attribute, which no plain selector can
   outrank.

   Only the border. The keyboard-focus RING is deliberately left alone:
   flat-experiment.css drops every box-shadow app-wide (* { box-shadow: none })
   and then restores exactly one focus ring for a/button/input/select/textarea,
   both with !important. That single ring is the app's focus indicator
   everywhere, so recolouring it for this one field would either fail silently
   or break a deliberate accessibility convention for a cosmetic gain. */
#unlockCodeInput:focus {
  border-color: #10b981 !important;
  /* Matches the other code field (licenseKeyInputDesktop), which also renders
     outline:none — otherwise the browser's default outline draws a dark ring
     over the green border. Safe to suppress: the app-wide :focus-visible ring
     described above is what marks keyboard focus, and it still applies. */
  outline: none;
}

/* Student Sign In button icon — resources/signin_starfish.png (replaced
   child.svg, which replaced the original graduation cap).

   Still masked and painted with currentColor rather than shown as a plain
   background image. The artwork is already pure white on transparency, so both
   routes look identical today; the mask keeps it following the button's own
   colour, so a white icon can't vanish if that button is ever restyled onto a
   light background. A mask reads the alpha channel, which for this file is
   exactly the drawn shape plus its anti-aliased edge.

   Referencing the file keeps one copy of the artwork for all three places the
   button is built. The 32px box is bigger than the mark looks: the starfish
   fills ~82% of its 300x300 canvas, so it draws about 26px. The box size is
   also what sets the button's height, so it cannot be changed on its own — the
   padding rule below absorbs the difference to hold the button at 40px. */
.gl-student-icon {
  display: inline-block;
  width: 32px;
  height: 32px;
  flex: 0 0 auto;
  background-color: currentColor;
  -webkit-mask: url("/resources/signin_starfish.png") center / contain no-repeat;
  mask: url("/resources/signin_starfish.png") center / contain no-repeat;
}

/* One turn of the starfish as the profile menu drops. No JS: .expanded flips
   the dropdown from display:none to flex, and an element that becomes
   displayed starts its animations from the beginning — so this replays on
   every open and never on a re-render. Scoped to the dropdown, so the same
   starfish sitting in the sign-in modal and the welcome slide stays still. */
#settingsDropdown.expanded .gl-student-icon {
  animation: starfishSpin 0.7s cubic-bezier(0.34, 0.8, 0.3, 1);
}

@keyframes starfishSpin {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}

@media (prefers-reduced-motion: reduce) {
  #settingsDropdown.expanded .gl-student-icon {
    animation: none;
  }
}

/* The desktop settings dropdown is min-width:220/max-width:300 and sizes to
   content. The bigger icon left the label enough room to wrap to two lines
   rather than push the dropdown wider, so pin it to one line — the dropdown
   then grows a couple of px within the bounds it already had. */
#desktopUnlockBtn > span[data-i18n="student_sign_in"],
#mobileUnlockBtn > span[data-i18n="student_sign_in"],
#signOutBtnDesktop > span[data-i18n="sign_out"],
#mobileUnlockBtn > span[data-i18n="sign_out"],
#signOutBtnMobile > span[data-i18n="sign_out"],
#desktopShareCodeLogoutBtn > span[data-i18n="sign_out"],
#mobileShareCodeLogoutBtn > span[data-i18n="sign_out"] {
  white-space: nowrap;
}

/* Hold the button at the height it had with the old 16px cap (40px). The icon
   is the tallest flex item, so it — not the 16px text line — drives the content
   box, and the padding has to absorb whatever the icon box is: 4 + 32 + 4 = 40.
   (It was 2 + 36 + 2 when the icon was 36px; shrinking the icon without lifting
   the padding would have quietly dropped the button to 36px.) The mark clears
   the button edge by ~7px top and bottom — the starfish fills ~82% of its
   canvas, so it draws ~26px inside the 32px box.
   !important because every copy of this button carries padding:12px inline,
   which a plain rule cannot outrank. Horizontal padding is left alone.

   The ShareCode ids are the share-code student's Sign Out — a fourth copy, built
   at runtime in license-handler.js, and the one a code-activated child actually
   sees (signOutBtn* is the signed-in free user's). It occupies the same slot, so
   it takes the same 40px. */
#desktopUnlockBtn,
#mobileUnlockBtn,
#signOutBtnDesktop,
#signOutBtnMobile,
#desktopShareCodeLogoutBtn,
#mobileShareCodeLogoutBtn {
  padding-top: 4px !important;
  padding-bottom: 4px !important;
}

/* Welcome-modal Sign In slide.

   The title carries the starfish, so it becomes a centred flex row. The mark is
   sized in em, not the class's default 32px: .slide-title runs 28px on desktop
   down to 18px on the smallest phone breakpoint, and a fixed icon would swamp
   the label at the bottom of that range. 1.15em tracks it (32px at 28, 21px at
   18) and keeps the desktop size the button already uses. */
#welcomeSlide2SignIn .slide-title {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.35em;
}

#welcomeSlide2SignIn .slide-title .gl-student-icon {
  width: 1.15em;
  height: 1.15em;
}

/* The worked example moved into the input's placeholder, so this hint renders
   empty — but it is still the slot the JS writes "Please enter a code" and
   "Code not found…" into. Reserve its line so an error appearing does not shove
   the Sign In button down. Matches the height one line of 15px text occupied. */
#welcomeSlide2SignIn .form-hint,
#unlockCodeHint {
  min-height: 21px;
}

/* #unlockCodeHint sits in a modal that never carried a hint before, so it needs
   the base .form-hint look too — that class is defined in times-tables.css for
   the welcome slide's markup, and applies here by class, but this modal's own
   spacing wants the gap above the button kept tight. */
#unlockCodeHint {
  margin-top: 8px;
  margin-bottom: 0;
}

/* Dismiss button — reuses the .play-button candy style from glass-skin.css so
   it reads as the same control as Start Game on the Play/Practice panels. Only
   layout is set here; the look is inherited deliberately, so restyling the
   candy button restyles this too.

   Spacing matches the Start Game button rather than being picked by eye: on the
   desktop card that button clears the mode row by 28px (its own margin-top is 0
   — the gap comes from .gl-mode-row's margin-bottom), and in the mobile sheet
   glass-skin.css gives it margin-top:32px as "an extra blank row of breathing
   space". Same two values here, so the two panels read alike. */
.gl-progress-picker .gl-ok {
  width: 100%;
  margin: 28px 0 0;
}
@media (max-width: 768px) {
  .gl-progress-picker .gl-ok { margin-top: 32px; }
}

/* Progress header stripped to just the help "?" (its pills are hidden and the
   view tiles + stat cards above replace them) — kill the glass-skin card look
   so no empty bordered box remains. */
.gl-progress-header-stripped {
  background: none !important;
  border: none !important;
  box-shadow: none !important;
  padding: 0 !important;
  min-height: 0 !important;
  margin: 8px 0 0 !important;
  display: flex !important;
  align-items: center !important;
  justify-content: center !important;
  gap: 0 !important;
}

/* glass-skin scopes the header card via #slideMenu / #menuPanelContainer (with
   !important), so match that specificity to fully strip it to just the help. */
#slideMenu .mobile-progress-header.gl-progress-header-stripped,
#menuPanelContainer .progress-header.gl-progress-header-stripped {
  background: none !important;
  border: none !important;
  box-shadow: none !important;
  border-radius: 0 !important;
  padding: 0 !important;
  min-height: 0 !important;
  margin: 8px 0 0 !important;
}

/* On the desktop card the header's 8px top margin sat on top of the space the
   help icon already gets, putting Progress's ? 20px under the OK button while
   the Play panel's ? sits at the panel's own 12px row-gap. Drop it so all three
   desktop panels place the ? identically. Desktop-scoped: in the mobile sheet
   this header is ABOVE the picker, where the 8px is still wanted. */
#menuPanelContainer .progress-header.gl-progress-header-stripped {
  margin-top: 0 !important;
}

/* glass-skin pins this one ? to padding:2px (left over from when the header was
   a grid holding the pills), making its box 34px against 38px on Practice/Play.
   Equal padding means equal box, so the icon CENTRES line up too and not just
   the 12px above them. Beats the !important it overrides on specificity. */
#menuPanelContainer .progress-header.gl-progress-header-stripped #desktop-progress-help {
  padding: 4px !important;
}

/* animated gold coin (Lottie) sized to match the stat-card glyph */
.gl-stat .gl-stat-glyph .gl-coin-lottie { width: 20px; height: 20px; display: block; }

/* ══════════════════════════════════════════════════════════════════════════
   LANGUAGE bottom sheet — dock launcher only
   ──────────────────────────────────────────────────────────────────────────
   When opened from the dock's Language item (mobile-nav.js adds .gl-langsheet)
   the Voices + Language modal is re-presented as the app's options bottom sheet
   (bottom-anchored, 20px top radius, frost, grab handle, slide-up, ink scrim) —
   matching the Play/Practice/Progress sheets. The profile-menu Languages row
   (no class) keeps the centred modal.
   ══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 768px) {
  #languagesModal.gl-langsheet {
    align-items: flex-end !important;   /* card sticks to the bottom edge */
    justify-content: center !important;
    /* sit BEHIND the dock (z 1700) like the other options sheets, so the nav
       bar stays visible + tappable above it (not the 2000 modal tier). */
    z-index: 1600 !important;
  }
  #languagesModal.gl-langsheet .settings-modal-box {
    width: 100% !important;
    max-width: none !important;
    margin: 0 !important;
    border-radius: 20px 20px 0 0 !important;
    border-bottom: 0 !important;
    max-height: 82vh !important;
    max-height: 82dvh !important;
    padding-top: 8px !important;
    /* reserve the dock's height (+ gap) so the last control never tucks under
       or touches the nav bar the sheet sits behind */
    padding-bottom: calc(var(--mc-dock-space, 60px) + 20px) !important;
    box-shadow: 0 -8px 30px rgba(0, 0, 0, 0.22) !important;
    animation: glLangSheetUp 0.26s cubic-bezier(0.22, 0.9, 0.34, 1) both;
  }
  /* grab-handle pill (the card is display:flex column) */
  #languagesModal.gl-langsheet .settings-modal-box::before {
    content: "";
    flex: 0 0 auto;
    align-self: center;
    width: 44px; height: 5px;
    margin: 2px 0 10px;
    border-radius: 999px;
    background: rgba(27, 36, 64, 0.22);
  }
  /* The Language dropdown is the LAST control in the sheet, so its options list
     (max-height ~5 rows, already scrolls) would drop downward behind the dock /
     off-screen. Flip it to open UPWARD so the rows stay visible. Voice sits
     higher up and keeps opening downward. */
  #languagesModal.gl-langsheet #custom-language-dropdown-languageSelect .dropdown-options,
  #languagesModal.gl-langsheet #slide-language-custom-dropdown .dropdown-options {
    top: auto !important;
    bottom: calc(100% + 6px) !important;
  }
}
@keyframes glLangSheetUp {
  from { transform: translateY(100%); }
  to   { transform: translateY(0); }
}

/* Languages popup: darken the backdrop only — no blur — to match the sibling
   option sheets (Play/Practice/Progress use .menu-overlay, which darkens with
   no blur). The base .modal-overlay-container applies backdrop blur(3px); the
   Languages overlay IS a .modal-overlay-container, so cancel just its blur here
   (the rgba darken from that base rule stays). Applies at all widths. */
#languagesModal {
  -webkit-backdrop-filter: none !important;
  backdrop-filter: none !important;
}

/* ══════════════════════════════════════════════════════════════════════════
   GLOBAL single-tap fix — Lucide icons never swallow a tap (app-wide)
   ──────────────────────────────────────────────────────────────────────────
   Lucide renders each <i data-lucide> into an <svg> that fills its control
   edge-to-edge. On touch a tap can land on that icon and never reach the
   button/pill, so the control feels dead (shop pills, slideshow arrows, and any
   other icon button). Make EVERY Lucide icon transparent to pointer events so
   taps always fall through to the interactive parent. Every icon here is
   decorative — the parent button owns the click handler (verified: no Lucide
   icon is a direct click target) — so this is safe app-wide and harmless with a
   mouse. Covers the pre-render <i data-lucide> and the rendered <svg.lucide>.
   The per-component pointer-events rules below are now instances of this. */
i[data-lucide],
svg.lucide {
  pointer-events: none !important;
}

/* ══════════════════════════════════════════════════════════════════════════
   SHOP category pills — single-tap fix on touch (mobile)
   ──────────────────────────────────────────────────────────────────────────
   rewards.css gives .category-tab a :hover lift (transform: translateY(-2px)).
   On touch devices the first tap applies :hover and lifts the pill out from
   under the finger, so the click misses and a second tap is needed. Where there
   is no real hover, neutralise the lift so a single tap always registers.
   (rewards.css is a base app file — fixed here in the override layer.)
   ══════════════════════════════════════════════════════════════════════════ */
@media (hover: none) {
  .category-tab { touch-action: manipulation; }
  /* Fully neutralise :hover (visual identical to the resting state) so iOS
     doesn't treat the first tap as a "hover-acquisition" tap that suppresses
     the click — that's what made a pill work once and then need extra taps.
     Reset the lift AND the hover background; keep the active pill white. */
  .category-tab:hover { transform: none !important; }
  .category-tab:not(.active):hover { background: #f8fafc !important; }
  .category-tab.active:hover { background: #ffffff !important; }
}

/* THE fix for "tapping the icon does nothing, tapping the padding works":
   make the pill's Lucide icon transparent to pointer/touch events so every tap
   lands on the button itself (a single, stable hit target). Applies at all
   widths — harmless with a mouse, essential on touch. */
.category-tab i,
.category-tab svg {
  pointer-events: none;
}

/* ══════════════════════════════════════════════════════════════════════════
   Practice SLIDESHOW controls — SAME single-tap fix (Prev / Next / Close)
   ──────────────────────────────────────────────────────────────────────────
   These buttons are filled edge-to-edge by a Lucide <svg>; on touch the tap
   lands on the icon and never reaches the button, so the controls feel dead
   (identical to the shop-pill symptom above). Make the icons transparent to
   pointer events so every tap hits the button, neutralise the sticky :hover,
   and set touch-action. Base times-tables.js keeps its own click handlers.
   ══════════════════════════════════════════════════════════════════════════ */
.slideshow-nav-btn i,
.slideshow-nav-btn svg,
#slideshowClose i,
#slideshowClose svg {
  pointer-events: none;
}
@media (hover: none) {
  .slideshow-nav-btn,
  #slideshowClose { touch-action: manipulation; }
  /* no hover lift here, but neutralise the hover background so the first tap
     isn't consumed as a hover-acquisition tap (as with the shop pills) */
  .slideshow-nav-btn:hover { background-color: rgba(9, 16, 40, 0.5) !important; }
  #slideshowClose:hover { background-color: rgba(0, 0, 0, 0.4) !important; }
}

/* ══════════════════════════════════════════════════════════════════════════
   EXPERIMENT — TAB PILL ICONS (desktop top nav)
   ──────────────────────────────────────────────────────────────────────────
   Brings the mobile dock's four Lucide glyphs up to the desktop tab pills, so
   the two navs read as the same control at every width. Markup change is in
   index.html (.gl-tab-icon + .gl-tab-label inside each tab button).

   No colour is set on the icon. Lucide strokes with currentColor, so it simply
   inherits the pill's own label colour — indigo (#271ECC) on the glass pill,
   white on the royal-blue active pill — and stays correct if either is
   restyled. That is the whole reason for using the icon font-style glyphs here
   rather than a background image.

   Loads after glass-skin.css, so these plain rules win on source order against
   its equally-specific `.tab-buttons button`.

   TO REVERT: delete this block and restore the four one-line tab buttons in
   index.html. Nothing else references .gl-tab-icon / .gl-tab-label.
   ══════════════════════════════════════════════════════════════════════════ */
.tab-buttons.desktop-tabs button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* 7px matches the dock's icon→label gap */
  gap: 7px;
  /* 16px, down from glass-skin's 18px: the icon adds ~23px to each pill, and at
     18px the four pills started crowding the logo on a 1024px laptop. The pills
     stay the same height — only the horizontal padding moves. */
  padding: 8px 16px;
}
.tab-buttons.desktop-tabs .gl-tab-icon {
  display: flex;
  align-items: center;
  flex: 0 0 auto;
}
.tab-buttons.desktop-tabs .gl-tab-icon svg {
  width: 16px;
  height: 16px;
  display: block;
  /* the label carries a light emboss on the glass pill; the icon should not —
     a text-shadow on a stroked glyph reads as a blur */
  filter: none;
}
.tab-buttons.desktop-tabs .gl-tab-label { display: inline-block; }

/* Narrow desktop / small laptop: drop the icons rather than the labels. The
   words are what a child reads; the glyph is reinforcement. */
@media (min-width: 769px) and (max-width: 900px) {
  .tab-buttons.desktop-tabs .gl-tab-icon { display: none; }
  .tab-buttons.desktop-tabs button { padding: 8px 18px; }
}

/* Creatures pill: draw the squid 10% larger than the Lucide icons beside it.
   ──────────────────────────────────────────────────────────────────────────
   Done with a transform, not width/height, for two reasons. The viewBox is
   already tightened to "24.8 24.8 50.4 50.4" — the artwork is 48 units tall in
   a 50.4 box, so there is only ~5% of headroom left before it starts clipping,
   and +10% needs 45.8 which is smaller than the art itself. And a transform
   leaves the layout box alone, so the pill height and the icon→label gap do not
   move, and the one rule covers both icon sizes (20px, and 24px under the
   small-screen rule in rewards.css) without repeating the breakpoint.

   Why bigger at all: it is a FILLED glyph sitting among stroked outlines, and a
   filled shape reads lighter at the same nominal size. */
.category-tab[data-category="creatures"] svg {
  transform: scale(1.1);
}
