:root {
  --bg: #14161a;
  --panel: #1e2126;
  --border: #33373f;
  --text: #e8e9ec;
  --muted: #9a9fa8;
  --cyan: #00b7c3;
  --magenta: #e6007e;
  --yellow: #ffd400;
  --key: #f2f2f2;
  --accent: #4f8cff;
  --good: #35c470;
  --bad: #ff5470;
}

* { box-sizing: border-box; }

html {
  /* Belt-and-suspenders alongside user-scalable=no in the viewport meta tag:
     stops double-tap-to-zoom everywhere while still allowing normal one-finger
     scrolling, so a drag gesture during gameplay can never zoom the page. */
  touch-action: manipulation;
  height: 100%;
}

/* The page itself never scrolls. Y8 runs the game in a fixed-size iframe, where the
   browser's own scrollbar is easy to miss and swipe-to-refresh fires on a downward
   drag mid-play. The document is locked to exactly one viewport (header + main), and
   anything taller than that — the level map, long modals — scrolls inside its own
   element instead. */
body {
  margin: 0;
  font-family: -apple-system, "Segoe UI", Roboto, sans-serif;
  background: var(--bg);
  color: var(--text);
  height: 100%;
  overflow: hidden;
  overscroll-behavior: none;
  display: flex;
  flex-direction: column;
  /* Nothing in a game of drag-and-drop is meant to be selected: without this, a drag
     across the board paints the toolbar and slot labels blue, and a long press pops
     the callout menu on mobile. */
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}

/* Grabbing a layer must move the tile, not start the browser's own image drag. */
img,
canvas {
  -webkit-user-drag: none;
  user-drag: none;
}

header {
  flex: 0 0 auto;
  padding: 16px 24px;
  border-bottom: 1px solid var(--border);
  display: flex;
  align-items: center;
  gap: 24px;
  flex-wrap: wrap;
}

header h1 {
  font-size: 20px;
  margin: 0;
  background: linear-gradient(90deg, var(--cyan), var(--magenta), var(--yellow));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.controls {
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
  font-size: 14px;
}

select, button {
  background: var(--panel);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 6px 10px;
  font-size: 14px;
}

button {
  cursor: pointer;
  background: var(--accent);
  border-color: var(--accent);
  color: white;
  font-weight: 600;
}

button:hover { filter: brightness(1.1); }

.status { color: var(--muted); }

main {
  /* Scroll container for the level map (see the body rule above): min-height:0 lets
     this flex child shrink below its content height, so the overflow lands here and
     not on the document. During a level nothing overflows — fitGameToViewport() in
     js/game.js sizes the mosaic to what's left — so no scrollbar appears in play. */
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  overscroll-behavior: contain;
  padding: 24px;
  display: flex;
  flex-direction: column;
  gap: 24px;
  align-items: center;
}

/* #gameView needs an explicit width — otherwise, as a flex item under
   align-items:center, it shrink-wraps to its content's min-content size.
   That's normally harmless, but a <canvas> dropped into a slot carries its
   full *intrinsic* pixel dimensions (e.g. 300x488) even though CSS renders
   it as a small ~75px icon — that intrinsic size still counts toward
   min-content up the grid/flex chain. First slot fill -> #gameView's
   shrink-wrap suddenly jumps toward its content's min-content width instead
   of staying at 602ish, and everything visibly grows. Locking the width to
   100% up front removes the content-dependency entirely. */
#gameView {
  width: 100%;
}

/* Board + tray. Stacked on a phone; side by side on a wide screen (media query
   further down), where stacking would squeeze the picture into a strip and leave
   the sides empty. Y8's game frame is a wide 16:9 box, and the board only fills it if
   it is allowed to use the width. */
.play-area {
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 100%;
  min-width: 0;
}

.board-column {
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 100%;
  min-width: 0;
}

.puzzle-grid {
  display: grid;
  gap: 2px;
  background: var(--bg);
  max-width: 900px;
  width: 100%;
  margin: 0 auto;
}

.cell-canvas {
  width: 100%;
  min-width: 0;
  height: auto;
  display: block;
  outline: 2px solid transparent;
  outline-offset: -2px;
  transition: outline-color .2s;
  background: repeating-conic-gradient(#2a2d33 0% 25%, #24262b 0% 50%) 50% / 16px 16px;
}

.cell-canvas.solved {
  outline-color: var(--good);
}

.controls-row {
  display: grid;
  gap: 8px;
  max-width: 900px;
  width: 100%;
  margin: 0 auto;
}

.cell-controls {
  min-width: 0;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 8px;
  display: flex;
  flex-direction: column;
  transition: border-color .2s;
}

.cell-controls.solved {
  border-color: var(--good);
}

.slots {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 6px;
  padding: 8px;
}

.slot {
  aspect-ratio: 1;
  min-width: 0;
  border: 2px dashed var(--border);
  border-radius: 6px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  font-size: 13px;
  color: var(--muted);
  cursor: pointer;
  overflow: hidden;
  position: relative;
  touch-action: none;
}

.slot[data-channel="C"] { color: var(--cyan); }
.slot[data-channel="M"] { color: var(--magenta); }
.slot[data-channel="Y"] { color: var(--yellow); }
.slot[data-channel="K"] { color: var(--key); }

.slot.filled {
  border-style: solid;
  border-color: var(--good);
  cursor: default;
}

.slot.provisional {
  border-style: solid;
  border-color: var(--accent);
  cursor: pointer;
}

.slot.provisional:hover {
  border-color: var(--bad);
}

.slot.provisional::after {
  content: '\2715';
  position: absolute;
  top: 2px;
  right: 2px;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: rgba(20, 22, 26, 0.75);
  color: var(--text);
  font-size: 9px;
  line-height: 14px;
  text-align: center;
}

.slot canvas {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.slot.wrong {
  border-color: var(--bad);
  animation: flash .4s;
}

.slot.shake {
  animation: shake .3s;
}

@keyframes flash {
  0%, 100% { background: transparent; }
  50% { background: rgba(255, 84, 112, 0.25); }
}

@keyframes shake {
  0%, 100% { transform: translateX(0); }
  25% { transform: translateX(-4px); }
  75% { transform: translateX(4px); }
}

.cell-tray, .tray {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.cell-tray {
  border-top: 1px dashed var(--border);
  padding: 8px;
  min-height: 44px;
}

/* Size comes from --tile-size, which fitGameToViewport() in js/game.js lowers on
   short screens so the tray never pushes the mosaic off the viewport. */
.tile {
  width: var(--tile-size, 84px);
  height: var(--tile-size, 84px);
  min-width: 0;
  border: 2px solid var(--border);
  border-radius: 6px;
  overflow: hidden;
  cursor: grab;
  transition: transform .1s, border-color .1s;
  touch-action: none;
}

.tile:hover { transform: scale(1.05); }

.tile.selected {
  border-color: var(--accent);
  box-shadow: 0 0 0 2px var(--accent);
}

.tile canvas {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.dragging-source {
  opacity: .35;
}

.drag-ghost {
  position: fixed;
  top: 0;
  left: 0;
  pointer-events: none;
  z-index: 50;
  border: 2px solid var(--accent);
  border-radius: 6px;
  overflow: hidden;
  box-shadow: 0 8px 24px rgba(0, 0, 0, .4);
  opacity: .9;
}

.drag-ghost canvas {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

#trayContainer {
  width: 100%;
  max-width: 900px;
  margin: 0 auto;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 12px;
  /* Last resort on a very short screen: fitGameToViewport() caps this panel's
     height and the layers scroll here, inside the game, rather than the page. */
  overflow-y: auto;
  overscroll-behavior: contain;
}

.tray-label {
  font-size: 13px;
  color: var(--muted);
  margin-bottom: 8px;
}

.hidden { display: none !important; }

.modal {
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.7);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 10;
}

.modal-content {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 24px;
  max-width: 480px;
  width: 90%;
  max-height: 90vh;
  overflow-y: auto;
  text-align: center;
}

.modal-content img {
  max-width: 100%;
  border-radius: 8px;
  margin: 12px 0;
}

#artworkInfo {
  text-align: left;
  font-size: 14px;
  color: var(--muted);
  line-height: 1.5;
}

#artworkInfo h3 {
  color: var(--text);
  margin-bottom: 4px;
}

#artworkInfo .credit {
  font-style: italic;
  font-size: 12px;
}

#artworkInfo .campaign-done {
  color: var(--good);
  font-weight: 700;
  text-align: center;
  font-size: 15px;
}

#artworkInfo a {
  color: var(--accent);
}

.modal-actions {
  display: flex;
  gap: 8px;
  justify-content: center;
  margin-top: 8px;
}

/* The win / artwork modal, laid out so "Next level" is never below the fold.
   Stacked — title, painting, caption, buttons — a portrait canvas eats the whole
   viewport on its own and pushes the buttons out of sight behind a scrollbar, which
   is what Y8 review flagged. Y8 embeds the game in a wide, short frame, so the room
   that is actually going spare is horizontal: the painting takes the left column at
   whatever height the viewport allows, and its caption and the buttons sit beside it
   instead of underneath. Height then comes out as title + picture, which fits.
   The narrow-screen fallback below goes back to stacking, with the picture capped
   low enough to leave the buttons on screen. */
#winModal .modal-content {
  max-width: min(940px, 94vw);
  display: grid;
  grid-template-columns: auto minmax(240px, 1fr);
  grid-template-areas:
    "title title"
    "image info"
    "image actions";
  column-gap: 20px;
  align-content: start;
  justify-items: center;
}

#winModal h2 {
  grid-area: title;
  margin: 0 0 12px;
}

#winImage {
  grid-area: image;
  margin: 0;
  /* Cap the height, not the width: a tall portrait is what overflows, and letting it
     size from its own aspect ratio keeps the column exactly as wide as it needs. */
  max-height: 70vh;
  max-width: 100%;
  width: auto;
  align-self: center;
  object-fit: contain;
}

#winModal #artworkInfo {
  grid-area: info;
  justify-self: stretch;
  /* Long credit lines scroll inside the caption rather than growing the modal — the
     picture is what sets the height here, and nothing else is allowed to. */
  max-height: 70vh;
  overflow-y: auto;
}

#winModal .modal-actions {
  grid-area: actions;
  justify-self: stretch;
  /* The row this sits in is whatever height the painting leaves over, and a stretched
     flex child would take all of it — buttons four times their normal height. Pin them
     to their own size, at the foot of the column so they line up with the picture. */
  align-self: end;
  align-items: center;
  margin-top: 16px;
}

/* Too narrow to stand a second column: stack, and buy the buttons their room back by
   giving the picture less of the height. */
@media (max-width: 760px) {
  #winModal .modal-content {
    grid-template-columns: 1fr;
    grid-template-areas:
      "title"
      "image"
      "info"
      "actions";
  }

  #winImage {
    max-height: 42vh;
    margin: 0 0 12px;
  }

  #winModal #artworkInfo {
    max-height: 26vh;
  }

  #winModal .modal-actions {
    margin-top: 12px;
  }
}

.muted { color: var(--muted); }

/* language switcher */

.lang-switch {
  display: flex;
  gap: 4px;
}

.lang-btn {
  padding: 4px 8px;
  font-size: 12px;
  background: var(--panel);
  border-color: var(--border);
  color: var(--muted);
}

.lang-btn.active {
  background: var(--accent);
  border-color: var(--accent);
  color: white;
}

/* level map — fills the whole viewport below the header; the page scrolls
   normally once the level grid grows past one screen. */

#levelMapView {
  width: 100%;
  max-width: none;
  padding: 8px 4px 40px;
}

#levelMapView h2 {
  font-size: 16px;
  color: var(--muted);
  font-weight: 600;
  margin-top: 0;
}

.level-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  gap: 14px;
}

/* Fixed to the viewport (not the scrolling level list) so it stays put as a
   backdrop no matter how tall the level grid grows. */
.home-background {
  position: fixed;
  inset: 0;
  overflow: hidden;
  z-index: -1;
  pointer-events: none;
}

.floating-art {
  position: absolute;
  top: 100%;
  left: var(--x);
  width: var(--size);
  height: auto;
  border-radius: 8px;
  opacity: .14;
  filter: saturate(.6) blur(.3px);
  transform: rotate(var(--rotate));
  box-shadow: 0 10px 30px rgba(0, 0, 0, .5);
  animation: floatUp var(--duration) linear var(--delay) infinite;
}

@keyframes floatUp {
  from { transform: translateY(0) rotate(var(--rotate)); }
  to { transform: translateY(-140vh) rotate(var(--rotate)); }
}

.level-band-heading {
  grid-column: 1 / -1;
  font-size: 14px;
  color: var(--muted);
  margin: 16px 0 0;
}

.level-band-heading:first-child {
  margin-top: 0;
}

.level-tile {
  position: relative;
  width: 100%;
  aspect-ratio: 3 / 4;
  border-radius: 12px;
  overflow: hidden;
  background: var(--panel) center / cover no-repeat;
  border: 2px solid var(--border);
  color: white;
  display: flex;
  align-items: flex-end;
  justify-content: flex-start;
  padding: 8px;
  cursor: pointer;
  transition: transform .15s, border-color .15s;
}

.level-tile:hover:not(.locked) {
  transform: translateY(-4px);
  border-color: var(--accent);
}

.level-tile .tile-number {
  background: rgba(10, 10, 14, .7);
  padding: 3px 10px;
  border-radius: 20px;
  font-size: 15px;
  font-weight: 800;
  line-height: 1.4;
}

.level-tile.completed {
  border-color: var(--good);
}

.level-tile.completed .tile-number {
  background: var(--good);
  color: #06301a;
}

.level-tile.locked {
  cursor: not-allowed;
  filter: grayscale(1) brightness(.45);
}

.level-tile .lock-icon {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 30px;
}

.level-tile-info {
  position: absolute;
  top: 4px;
  right: 4px;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: rgba(10, 10, 14, .65);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 13px;
  cursor: pointer;
  transition: transform .1s, background .1s;
}

.level-tile-info:hover {
  background: var(--accent);
  transform: scale(1.1);
}

/* in-game toolbar */

.game-toolbar {
  width: 100%;
  max-width: 900px;
  margin: 0 auto 16px;
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
}

.target-thumbnail {
  width: 40px;
  height: 40px;
  object-fit: cover;
  border-radius: 6px;
  border: 2px solid var(--border);
  cursor: pointer;
  flex-shrink: 0;
}

.target-thumbnail:hover {
  border-color: var(--accent);
}

.level-label {
  color: var(--muted);
  font-size: 14px;
}

/* toast */

.toast {
  position: fixed;
  left: 50%;
  bottom: 24px;
  transform: translateX(-50%);
  background: var(--panel);
  border: 1px solid var(--border);
  color: var(--text);
  padding: 10px 16px;
  border-radius: 8px;
  font-size: 14px;
  z-index: 20;
}

/* No leaderboard styles in this build: the table is Y8's own hosted modal, drawn
   outside this page's markup. See js/y8.js. */

/* how to play */

.howto-content {
  max-width: 560px;
  text-align: left;
}

.howto-content h2 {
  text-align: center;
}

.howto-swatches {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 10px;
  margin: 4px 0 20px;
}

/* The C/M/Y/K swatches are followed by the word "Puzzle" so the row spells out the
   game's full name. It matches the header, the page title and the Y8 listing exactly —
   "CMYK Puzzle". Never localize it. */
.howto-swatch-word {
  font-weight: 800;
  font-size: 22px;
  line-height: 1;
  color: var(--text);
  margin-left: 2px;
}

.howto-swatch {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 800;
  font-size: 15px;
  color: #14161a;
  background: var(--swatch-color);
}

.howto-section {
  margin-bottom: 16px;
}

/* Short window (a small desktop browser, or a phone in landscape): trim the fixed
   chrome so the mosaic keeps as much height as possible and still fits without
   scrolling — see fitGameToViewport() in js/game.js. */
@media (max-height: 720px) {
  header {
    padding: 8px 16px;
    gap: 16px;
  }

  main {
    padding: 12px;
    gap: 12px;
  }
}

/* Wide screen: lay the board and the tray side by side and cap the slot size, so
   the picture gets the height the tray and the stretched slots used to eat. The
   aspect-ratio guard keeps a tall window (phone, tablet portrait) on the stacked
   layout, where a side column would leave the picture nothing to grow into. */
@media (min-width: 700px) and (min-aspect-ratio: 5/4) {
  .play-area {
    flex-direction: row;
    align-items: flex-start;
    justify-content: center;
    gap: 16px;
    width: auto;
  }

  .board-column {
    width: auto;
  }

  #trayContainer {
    flex: 0 0 clamp(180px, 20vw, 260px);
    width: auto;
    max-width: none;
    margin: 0;
  }

  /* Square drop targets stretched to the cell's full width are as tall as they are
     wide, which on a wide board is most of the height. Cap them and centre them
     under their cell instead. */
  .slots {
    grid-template-columns: repeat(4, minmax(0, 56px));
    justify-content: center;
  }

  /* Easy levels keep each cell's layers in its own panel: side by side with the
     slots that panel is half as tall, and the height goes to the picture. */
  .cell-controls {
    flex-direction: row;
    align-items: flex-start;
  }

  .cell-tray {
    border-top: none;
    border-left: 1px dashed var(--border);
    flex: 1 1 auto;
    min-width: 0;
  }

  /* One-cell level: its panel has no column to line up with, so it sits beside the
     picture (fitGameToViewport() adds .board-row) and stacks internally again. */
  #gameView.board-row .board-column {
    flex-direction: row;
    align-items: flex-start;
    gap: 16px;
  }

  #gameView.board-row .cell-controls {
    flex-direction: column;
  }

  #gameView.board-row .cell-tray {
    border-top: 1px dashed var(--border);
    border-left: none;
  }
}

/* Very short window (phone in landscape): the square drop targets are the tallest
   fixed thing left, so cap them harder than the wide-screen rule above does. */
@media (max-height: 460px) {
  .slots {
    grid-template-columns: repeat(4, minmax(0, 40px));
    justify-content: center;
  }

  /* Easy levels: a cell's own layer strip is narrow here, so its tiles stack up and
     the panel grows taller than the picture. Bound it and let it scroll in place. */
  .cell-tray {
    max-height: 64px;
    overflow-y: auto;
    overscroll-behavior: contain;
  }
}

/* Narrow screen: the header wraps onto three rows on a phone, and every pixel it
   takes comes straight out of the mosaic (see fitGameToViewport()). */
@media (max-width: 600px) {
  header {
    padding: 8px 12px;
    gap: 10px;
  }

  header h1 {
    font-size: 18px;
  }

  main {
    padding: 12px;
    gap: 12px;
  }
}

.howto-section h3 {
  margin: 0 0 4px;
  font-size: 14px;
  color: var(--accent);
}

.howto-section p {
  margin: 0;
  font-size: 14px;
  color: var(--text);
  line-height: 1.5;
}

#howToModal #closeHowToBtn {
  display: block;
  margin: 8px auto 0;
}
