/* Frogdoku polish from the independent critique of 25/10/2026 (dev/critic/TRIAGE.md). Loaded last, so each rule here
   is a deliberate override; the number in each comment is the defect it answers. */

/* #16 one primary button style everywhere: the mint glass of the home Play (the gold glass went olive over the
   paintings and read as disabled) */
html[data-art-direction] .btn.gold:not(.badge) {
  background: linear-gradient(160deg, #8dffc4b0, #3fc79080 55%, #a8ffd690);
  border-color: #c9ffe08c;
  color: #f6fff9;
  text-shadow: 0 1px 2px #06301ea0;
  box-shadow:
    inset 0 1.5px 0 #e9fff1d8,
    inset 0 -1px 0 #9dffc048,
    inset 0 0 22px #7dffb838,
    0 0 26px #5fe0a066,
    0 8px 22px #0818105c;
}

/* #4 wide layouts: Ribi floats beside the board on a lily pad of his own instead of hovering over the water */
@media (min-aspect-ratio: 6/5) {
  .play .buddy .bo::before {
    content: '';
    position: absolute;
    left: -22%;
    right: -22%;
    top: 82%;
    height: 30%;
    pointer-events: none;
    background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 64'%3E\
      %3Cellipse cx='100' cy='36' rx='98' ry='26' fill='none' stroke='%23d6fff4' stroke-opacity='.35' stroke-width='2'/%3E\
      %3Cellipse cx='100' cy='34' rx='88' ry='22' fill='%23143d2a' fill-opacity='.45'/%3E\
      %3Cpath d='M100 30L186 24A88 22 0 1 0 186 34Z' fill='%235aae4c' stroke='%23245a28' stroke-width='3' stroke-linejoin='round'/%3E\
      %3Cpath d='M100 30L30 22M100 30L48 44M100 30L120 51M100 30L160 46M100 30L80 10M100 30L140 11' stroke='%233f8a3a' stroke-width='2' stroke-linecap='round'/%3E\
      %3Cpath d='M40 20Q70 10 110 11' stroke='%23b6e86a' stroke-opacity='.6' stroke-width='3' fill='none' stroke-linecap='round'/%3E\
      %3C/svg%3E")
      center / 100% 100% no-repeat;
  }
  .play .buddy .bo .boShadow {
    display: none;
  }
}

/* #1 short portrait screens (small phones): every row above the board costs board size. Ribi sits smaller on the frame,
   and the colour pips and rule icons step aside (the rules stay in the lesson and in the pause menu) */
@media (max-aspect-ratio: 6/5) and (max-height: 700px) {
  .play .buddy .bo {
    --bo: 82px;
  }
  .play:not(.lesson) .rules,
  .play:not(.lesson) .strip {
    display: none;
  }
}

/* #2 #14 the home tagline never wraps behind Ribi, and steps fully aside (not a ghost at 10%) while he talks */
.home .tagline {
  white-space: nowrap;
}
.home:has(.bubble:not(.out)) .tagline {
  opacity: 0;
}
@media (max-width: 379px) and (max-aspect-ratio: 6/5) {
  .home .tagline {
    display: none;
  }
}

/* #3 wardrobe on phones: a smaller mirror and a slimmer action bar leave room for two rows of items */
@media (max-aspect-ratio: 6/5) {
  .shop .mirror {
    height: min(25vh, 230px);
  }
  .shop .mirrorStage .bo {
    --bo: min(30vw, 17vh, 160px);
  }
  .shop .shopAction {
    min-height: 50px;
    padding: 6px 12px;
  }
}
@media (max-aspect-ratio: 6/5) and (max-height: 700px) {
  .shop .mirror {
    height: 20vh;
  }
  .shop .mirrorStage .bo {
    --bo: min(26vw, 14vh, 130px);
  }
}
/* #10 #3 the tab bar is a dark glass (the bright painting no longer bleeds through the labels), labels never split */
html .screen.shop .tabs {
  background: linear-gradient(160deg, #0a2120e0, #0f2a26cc);
  border-color: #ffffff2e;
}
html .screen.shop .tabs .tab {
  color: #e6f4ec;
  overflow-wrap: normal;
  white-space: nowrap;
  font-size: clamp(0.6rem, 2.7vw, 0.78rem);
  text-overflow: ellipsis;
  overflow: hidden;
}
html .screen.shop .tabs .tab.on {
  background: linear-gradient(160deg, #3fc790a8, #2f8a5a99);
}
/* #11 "Reach level 121" stays on one line inside the card */
.card .lockp {
  white-space: nowrap;
  font-size: 0.7rem;
}

/* #17 daily: its header is the same glass as every other screen, not a solid slab */
:is(.daily, .modes) .screenNav::before {
  background: #0a2120b8;
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
}

/* #20 settings on small or short screens: the language picker shrinks to fit its row (never cut mid-word), and the panel
   scrolls inside the screen instead of running off it */
.panel.settings {
  max-height: calc(100dvh - 24px);
  overflow-y: auto;
}
.setRow select {
  flex: 0 1 auto;
  min-width: 0;
  max-width: 70%;
  text-overflow: ellipsis;
}
@media (max-height: 700px) {
  .setRow {
    padding-top: 6px;
    padding-bottom: 6px;
  }
}

/* #15 portrait: the wrong-move explanation sits under the board in one compact line (placeError in play.ts) */
.play > .boExplanation.atDock {
  width: auto;
  flex-direction: row;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
}
.play > .boExplanation.atDock::after {
  display: none;
}
.play > .boExplanation.atDock > div {
  flex: 1;
  min-width: 0;
}
.play > .boExplanation.atDock strong {
  font-size: 0.86rem;
  line-height: 1.3;
}
.play > .boExplanation.atDock .btn {
  align-self: center;
  flex: none;
}

/* #1 phone in landscape: height is the board's only limit, so the HUD leaves the top row. Pause and settings float in
   the corners, the title and hearts sit at the top of the left column (above Ribi), and the rule icons step aside
   (they stay in the lesson); the board then gets the whole height */
@media (min-aspect-ratio: 6/5) and (max-height: 560px) {
  .play:not(.lesson) > .rules {
    display: none;
  }
  .play > .navSpace {
    position: absolute;
    z-index: 6;
    top: calc(var(--sat) + 6px);
    left: calc(var(--sal) + 10px);
    right: calc(var(--sar) + 10px);
    height: 0;
    min-height: 0;
    pointer-events: none;
  }
  .play > .navSpace .topbar {
    justify-content: space-between;
  }
  .play > .navSpace .topbar > * {
    pointer-events: auto;
  }
  .play > .navSpace .hudRight {
    grid-column: 3;
    justify-self: end;
  }
  .play > .navSpace .hudTitle {
    position: absolute;
    left: 56px;
    top: 2px;
    text-align: left;
    flex: none;
    max-width: min(30vw, 240px);
  }
  .play > .navSpace .hudTitle .lives {
    justify-content: flex-start;
  }
}

/* #23 map: the star chip shares its class name with the code-drawn starry sky (.stars) and inherited its white specks */
.chip.stars {
  background-image: none;
  animation: none;
}
/* the panel blurs the painting behind it, so a lily pad never shows through as a dark stain */
.levels .levelPanel {
  -webkit-backdrop-filter: blur(12px) saturate(1.1);
  backdrop-filter: blur(12px) saturate(1.1);
}
/* locked levels: muted pond green instead of the castle's lilac */
.levels .lvl.lock {
  color: #a9c6b8;
}
/* phones: less empty band above the first row (Ribi's perch over row 1 still fits) */
@media (max-width: 600px) {
  .levels .levelGrid {
    padding-top: 26px;
  }
}

/* ---------------------------------------------------------------- round-3 art layers (classes set in scene.ts) */
/* Ribi's lily-pad seat: home-seat-pad.png (1024x512), anchor (512, 230) under his feet. His box is 160x210 drawing units
   with the feet at 76.7% of its height; the pad is 1.4x his width, so its top sits at 52.7% and it is 53.3% tall. */
.seatArt .home .boStage .bo::before {
  content: '';
  position: absolute;
  left: -20%;
  right: -20%;
  top: 52.7%;
  height: 53.3%;
  pointer-events: none;
  background: var(--seat-art) center / 100% 100% no-repeat;
  transform-origin: 50% 45%;
  animation: seatBob 4s ease-in-out infinite;
}
@media (min-aspect-ratio: 6/5) {
  .seatArt .play .buddy .bo::before {
    left: -20%;
    right: -20%;
    top: 52.7%;
    height: 53.3%;
    background: var(--seat-art) center / 100% 100% no-repeat;
    transform-origin: 50% 45%;
    animation: seatBob 4s ease-in-out infinite;
  }
}
@keyframes seatBob {
  50% {
    transform: rotate(1.2deg) translateY(1px);
  }
}

/* foreground plants: anchored at their corner/edge, swaying around it, drifting with the pointer or the tilt */
.scene > .fg {
  inset: auto;
  width: auto;
  height: auto;
  pointer-events: none;
  z-index: 2;
  translate: calc(var(--px, 0) * var(--depth, 1) * 1px) calc(var(--py, 0) * var(--depth, 1) * 1px);
  animation: fgSway var(--sw, 6s) ease-in-out var(--sd, 0s) infinite alternate;
}
@keyframes fgSway {
  from {
    rotate: -1.2deg;
  }
  to {
    rotate: 1.3deg;
  }
}
/* overscan: every layer starts --fgo beyond the screen edge it is cut by, more than its sway (≈2% of its length) plus the
   parallax drift (≤13 px) can ever pull it in, so the cut edge of the painting never shows */
.scene {
  --fgo: 56px;
}
.scene > .sceneImg {
  translate: calc(var(--px, 0) * -0.3px) calc(var(--py, 0) * -0.3px);
}
.scene > .fg-l {
  left: calc(-1 * var(--fgo));
  bottom: calc(-1 * var(--fgo));
  height: min(82vh, 150vw);
  transform-origin: 0 100%;
}
.scene > .fg-r {
  right: calc(-1 * var(--fgo));
  bottom: calc(-1 * var(--fgo));
  height: min(82vh, 150vw);
  transform-origin: 100% 100%;
}
.scene > .fg-bl {
  left: calc(-1 * var(--fgo));
  bottom: calc(-1 * var(--fgo));
  width: min(52vw, 62vh);
  transform-origin: 0 100%;
}
.scene > .fg-br {
  right: calc(-1 * var(--fgo));
  bottom: calc(-1 * var(--fgo));
  width: min(52vw, 62vh);
  transform-origin: 100% 100%;
}
.scene > .fg-tl {
  left: calc(-1 * var(--fgo));
  top: calc(-1 * var(--fgo));
  width: min(72vw, 95vh);
  transform-origin: 0 0;
}
.scene > .fg-tr {
  right: calc(-1 * var(--fgo));
  top: calc(-1 * var(--fgo));
  width: min(52vw, 62vh);
  transform-origin: 100% 0;
}
/* mirrored copies: the image's anchor edge is placed just past the opposite screen edge and the flip happens around it,
   so the copy grows back into the screen and sways around the real corner */
.scene > .fg-br.mir {
  right: auto;
  left: calc(100% + var(--fgo));
  scale: -1 1;
  transform-origin: 0 100%;
}
.scene > .fg-tl.mir {
  left: auto;
  right: calc(100% + var(--fgo));
  scale: -1 1;
  transform-origin: 100% 0;
}
/* in a level the corners stay small, outside the board (portrait) or in the side strips (landscape) */
.scene-world > .fg-bl,
.scene-world > .fg-br,
.scene-world > .fg-tl,
.scene-world > .fg-tr {
  width: min(34vw, 30vh);
}
@media (min-aspect-ratio: 6/5) {
  .scene-world > .fg-bl,
  .scene-world > .fg-br,
  .scene-world > .fg-tl,
  .scene-world > .fg-tr {
    width: min(26vw, 46vh);
  }
}
@media (prefers-reduced-motion: reduce) {
  .scene > .fg,
  .seatArt .bo::before {
    animation: none;
  }
  .scene > .fg,
  .scene > .sceneImg {
    translate: none;
  }
}
.seatArt body.calm .bo::before,
body.calm .scene > .fg {
  animation: none;
}

/* water caustics on the board (water-caustics.png, seamless 512): a slow light drift under the frogs and marks */
.causticsArt body:not(.contrast) .play .board::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  border-radius: inherit;
  opacity: 0.5;
  background: var(--caustics-art) 0 0 / calc(var(--cell) * 3) repeat;
  mix-blend-mode: screen;
  animation: caustics 24s linear infinite;
}
@keyframes caustics {
  to {
    background-position: calc(var(--cell) * 3) calc(var(--cell) * 3);
  }
}
@media (prefers-reduced-motion: reduce) {
  .causticsArt .play .board::before {
    animation: none;
  }
}
/* portrait: the side reeds frame the screen without crowding Ribi (their tips stay below his eyes, outside the column) */
@media (max-aspect-ratio: 6/5) {
  .scene > .fg-l,
  .scene > .fg-r {
    height: min(60vh, 118vw);
  }
  .scene > .fg-bl,
  .scene > .fg-br {
    width: min(46vw, 40vh);
  }
  .scene > .fg-tl {
    width: min(64vw, 50vh);
  }
}
/* phones: the layers are smaller (worst inward pull ≈ 25 px), so a smaller overscan keeps more of the small branch */
@media (max-aspect-ratio: 6/5) {
  .scene {
    --fgo: 30px;
  }
}
/* End of src/styles/frog-polish.css */

/* End of dist-static/assets/frog-polish-BYHplJtU.css */
