/* Sprout's Puzzles — a wooden tray on a table.
 *
 * The whole look rests on one idea: the tray is cut *into* something and the
 * pieces sit *on top of* it. Sockets are darker than the wood and shadowed
 * inwards; pieces are lit from above and cast shadows outwards. Get those two
 * directions right and it reads as a real puzzle without a single photograph.
 */

.puzzle-room {
    --oak: #b98a4e;
    --oak-dark: #8a6132;
    --oak-deep: #5f4120;
    --oak-light: #d9ae74;
    background:
        radial-gradient(760px 380px at 50% 0%, rgba(47, 143, 107, .12), transparent 70%);
    min-height: 100vh;
    padding-bottom: clamp(40px, 6vw, 80px);
}

.puzzle-head {
    max-width: 1400px; margin: 0 auto;
    padding: clamp(20px, 3vw, 36px) clamp(16px, 4vw, 48px) 6px;
    text-align: center; color: #fffdf4;
}
.puzzle-head h1 {
    margin: 0; font: 400 clamp(2rem, 4.4vw, 3.2rem)/1.05 var(--serif);
}
.puzzle-head p { margin: 8px auto 0; max-width: 60ch; opacity: .88; }

/* ---- the controls ---- */

.puzzle-controls {
    display: flex; flex-wrap: wrap; align-items: center;
    justify-content: center; gap: 10px;
    max-width: 1400px; margin: 0 auto;
    padding: 14px clamp(16px, 4vw, 48px) 0;
}
.puzzle-cuts { display: flex; flex-wrap: wrap; gap: 8px; }
.puzzle-cuts button {
    display: inline-flex; align-items: baseline; gap: 6px;
    padding: 10px 16px; border-radius: 999px;
    border: 2px solid rgba(255, 255, 255, .35);
    background: #fffaf0; color: #4a3212;
    font: 900 .82rem var(--serif); cursor: pointer;
}
.puzzle-cuts button span {
    font: 800 .58rem var(--serif); letter-spacing: .1em;
    text-transform: uppercase; opacity: .62;
}
.puzzle-cuts button:hover { background: var(--sprout-sun, #ffd76b); }
.puzzle-cuts button[aria-pressed="true"] {
    background: var(--sprout-sun, #ffd76b);
    border-color: #fffdf4;
    box-shadow: 0 0 0 3px rgba(255, 255, 255, .28);
}

.puzzle-shuffle {
    padding: 10px 18px; border-radius: 999px;
    border: 2px solid rgba(255, 255, 255, .35);
    background: transparent; color: #fffdf4;
    font: 900 .82rem var(--serif); cursor: pointer;
}
.puzzle-shuffle:hover { background: rgba(255, 255, 255, .16); }

.puzzle-count {
    display: flex; align-items: center; gap: 10px;
    color: #e8f5ec; font: 800 .82rem var(--serif);
}
/* A plain bar rather than a percentage — a small child reads filling up. */
.puzzle-bar {
    width: 140px; height: 12px; border-radius: 999px;
    background: rgba(0, 0, 0, .3); overflow: hidden;
}
.puzzle-bar:after {
    content: ""; display: block; height: 100%;
    width: calc(var(--done, 0) * 100%);
    background: linear-gradient(90deg, #8fd6a4, var(--sprout-sun, #ffd76b));
    transition: width .3s ease;
}

/* ---- the table: picture, tray, box ---- */

.puzzle-stage {
    position: relative;
    display: grid;
    grid-template-columns: minmax(96px, 150px) auto minmax(240px, auto);
    align-items: start; justify-items: center;
    gap: clamp(12px, 2vw, 28px);
    max-width: 1400px; margin: 0 auto;
    padding: clamp(16px, 2.4vw, 30px) clamp(12px, 3vw, 40px);
}

.puzzle-panel {
    width: 100%;
    border-radius: 14px;
    padding: 14px;
    background:
        repeating-linear-gradient(92deg,
            rgba(255, 255, 255, .05) 0 3px,
            rgba(0, 0, 0, .045) 3px 7px),
        linear-gradient(160deg, var(--oak-light), var(--oak) 45%, var(--oak-dark));
    box-shadow:
        0 16px 30px rgba(0, 0, 0, .34),
        inset 0 2px 0 rgba(255, 255, 255, .35),
        inset 0 -3px 0 rgba(0, 0, 0, .28);
}
.puzzle-panel > h2 {
    margin: 0 0 10px;
    font: 900 .64rem var(--serif); letter-spacing: .16em;
    text-transform: uppercase; text-align: center;
    color: #4a3212;
}

/* The finished picture, always visible — it is what makes a 4-piece puzzle
   solvable for the youngest child. */
.puzzle-model-frame {
    border-radius: 8px; overflow: hidden;
    background: rgba(0, 0, 0, .18);
    box-shadow: inset 0 0 0 2px rgba(74, 50, 18, .5);
}
.puzzle-model-frame img { display: block; width: 100%; height: auto; }
/* Deliberately small. It is a reference a child glances at, not the game -
   every pixel it gives up goes to the board and the piece box. */
.puzzle-model-panel { align-self: start; }

/* The tray. The sockets canvas sits flat in it and the pieces land on top.
   The heading is here only so the board starts at the same height as the two
   panels either side of it - without it the board floats a line higher. */
.puzzle-board-wrap { display: grid; justify-items: center; }
.puzzle-board-wrap > h2 {
    margin: 14px 0 10px;
    font: 900 .64rem var(--serif); letter-spacing: .16em;
    text-transform: uppercase; text-align: center;
    color: #cfe6d7;
}
.puzzle-board {
    position: relative;
    border-radius: 6px;
    background: linear-gradient(160deg, var(--oak-dark), var(--oak-deep));
    box-shadow:
        inset 0 3px 8px rgba(0, 0, 0, .55),
        inset 0 -2px 0 rgba(255, 255, 255, .12),
        0 0 0 10px var(--oak),
        0 0 0 12px var(--oak-deep),
        0 20px 36px rgba(0, 0, 0, .4);
    transition: box-shadow .4s ease;
}
.puzzle-sockets { position: absolute; inset: 0; pointer-events: none; }

/* The shallow box the pieces are tipped into. */
.puzzle-tray {
    position: relative; width: 100%;
    border-radius: 10px;
    background:
        repeating-linear-gradient(88deg,
            rgba(0, 0, 0, .05) 0 4px, rgba(255, 255, 255, .03) 4px 9px),
        linear-gradient(160deg, #6f4d26, #55391b);
    box-shadow: inset 0 4px 10px rgba(0, 0, 0, .5), inset 0 -2px 0 rgba(255, 255, 255, .1);
}

/* ---- the pieces ---- */

.puzzle-piece {
    position: absolute;
    cursor: grab;
    touch-action: none;                 /* the drag is ours, not the browser's */
    filter: drop-shadow(0 3px 3px rgba(0, 0, 0, .45));
    transition: filter .16s ease;
    z-index: 2;
}
.puzzle-piece:hover { filter: drop-shadow(0 5px 5px rgba(0, 0, 0, .5)); }

/* Lifted off the table while it is being carried. */
.puzzle-piece.is-lifted {
    cursor: grabbing;
    z-index: 40;
    filter: drop-shadow(0 16px 14px rgba(0, 0, 0, .5));
    transition: none;
}

/* Home. It sits flush in its socket and cannot be picked up again. */
.puzzle-piece.is-placed {
    cursor: default; z-index: 3;
    filter: drop-shadow(0 1px 1px rgba(0, 0, 0, .38));
    transition: left .18s ease, top .18s ease, transform .18s ease, filter .3s ease;
}
.puzzle-piece.just-placed { animation: puzzle-settle .4s ease; }
@keyframes puzzle-settle {
    0%   { transform: scale(1.09); filter: drop-shadow(0 12px 12px rgba(0, 0, 0, .5)) brightness(1.18); }
    60%  { transform: scale(.985); }
    100% { transform: scale(1); }
}

/* ---- finished ---- */

.puzzle-finished .puzzle-board {
    box-shadow:
        inset 0 3px 8px rgba(0, 0, 0, .3),
        0 0 0 10px var(--sprout-sun, #ffd76b),
        0 0 0 13px var(--oak-deep),
        0 0 60px 10px rgba(255, 215, 107, .55),
        0 20px 36px rgba(0, 0, 0, .4);
}
.puzzle-cheer {
    grid-column: 1 / -1;
    display: flex; align-items: center; justify-content: center; gap: 14px;
    margin-top: 6px; padding: 16px 26px; border-radius: 999px;
    background: var(--sprout-sun, #ffd76b); color: #3d2a06;
    font: 900 1.1rem var(--serif);
    box-shadow: 0 14px 28px rgba(0, 0, 0, .3);
    animation: puzzle-cheer-in .5s ease;
}
/* An explicit display beats the browser's [hidden] rule, so the banner has to
   be told to hide itself. Without this it congratulates a child who has placed
   three pieces out of sixteen. */
.puzzle-cheer[hidden] { display: none; }

@keyframes puzzle-cheer-in {
    from { opacity: 0; transform: translateY(14px) scale(.94); }
    to   { opacity: 1; transform: none; }
}

/* ---- the picture shelf ---- */

.puzzle-shelf {
    max-width: 1400px; margin: 0 auto;
    padding: clamp(18px, 3vw, 34px) clamp(16px, 4vw, 48px) 0;
}
.puzzle-group + .puzzle-group { margin-top: 26px; }
.puzzle-group h3 {
    display: flex; align-items: center; gap: 10px;
    margin: 0; font: 400 1.5rem var(--serif); color: #fffdf4;
}
.puzzle-group h3 b {
    padding: .2rem .6rem; border-radius: 999px;
    background: rgba(255, 255, 255, .16);
    font: 900 .6rem var(--serif); letter-spacing: .1em;
}
.puzzle-group > p { margin: 4px 0 12px; color: #cfe6d7; font-size: .92rem; }

.puzzle-thumbs {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(124px, 1fr));
    gap: 12px;
}
.puzzle-thumb {
    display: flex; flex-direction: column; gap: 6px;
    padding: 8px; border-radius: 12px;
    border: 3px solid transparent;
    background: rgba(255, 255, 255, .1);
    color: #fffdf4; font: 800 .72rem var(--serif);
    cursor: pointer; text-align: center;
    transition: transform .16s ease, background .16s ease;
}
.puzzle-thumb:hover { transform: translateY(-4px); background: rgba(255, 255, 255, .2); }
.puzzle-thumb img {
    width: 100%; aspect-ratio: 4 / 3; object-fit: cover;
    border-radius: 7px; background: rgba(0, 0, 0, .2);
}
.puzzle-thumb[aria-pressed="true"] {
    border-color: var(--sprout-sun, #ffd76b);
    background: rgba(255, 215, 107, .22);
}

.puzzle-missing {
    padding: 20px; border-radius: 12px;
    background: rgba(0, 0, 0, .28); color: #ffe9c9;
    font-size: .94rem; line-height: 1.6;
}

/* ---- smaller screens ----
   The three panels stack. The tray goes first because it is the game; the
   finished picture sits under it where a child can still glance at it. */
@media (max-width: 980px) {
    .puzzle-stage { grid-template-columns: 1fr; }
    .puzzle-board-wrap { order: 1; }
    .puzzle-tray-panel { order: 2; width: 100%; }
    .puzzle-model-panel { order: 3; max-width: 340px; }
}


/* ---- the finish: a 3D burst over the board ----
 *
 * Real depth, not a flat sprinkle. The stage carries a perspective and the
 * pieces of confetti are given a Z of their own, so the ones thrown toward the
 * viewer genuinely pass in front and grow, and the ones thrown away shrink
 * behind the board. Each one tumbles on its own axis while it travels, which is
 * what sells it as a solid object rather than a moving rectangle.
 */
.puzzle-burst {
    position: absolute; inset: 0;
    display: grid; place-items: center;
    pointer-events: none;
    perspective: 760px;
    perspective-origin: 50% 45%;
    transform-style: preserve-3d;
    z-index: 60;
}
.puzzle-burst[hidden] { display: none; }

.puzzle-spark {
    position: absolute;
    width: var(--size, 14px);
    height: calc(var(--size, 14px) * var(--ratio, .62));
    background: var(--tint, #ffd76b);
    border-radius: var(--round, 2px);
    transform-style: preserve-3d;
    /* Lit from the front, so a tumbling piece darkens as it turns edge-on. */
    box-shadow: inset 0 0 0 100px rgba(255, 255, 255, .001);
    animation: puzzle-spark var(--life, 1500ms) cubic-bezier(.16, .72, .32, 1) var(--wait, 0ms) both;
    will-change: transform, opacity;
}

@keyframes puzzle-spark {
    0% {
        transform: translate3d(0, 0, 0) rotate3d(var(--rx), var(--ry), var(--rz), 0deg) scale(.2);
        opacity: 0;
    }
    9% { opacity: 1; }
    /* The apex of the throw, still travelling outward and upward. */
    55% {
        transform:
            translate3d(calc(var(--dx) * .78), calc(var(--dy) * .72), calc(var(--dz) * .9))
            rotate3d(var(--rx), var(--ry), var(--rz), calc(var(--spin) * .6))
            scale(1);
        opacity: 1;
    }
    /* Then gravity takes it: it keeps drifting sideways but falls past the end
       of the throw, which is what stops it looking like a starburst decal. */
    100% {
        transform:
            translate3d(var(--dx), calc(var(--dy) + var(--fall)), var(--dz))
            rotate3d(var(--rx), var(--ry), var(--rz), var(--spin))
            scale(.72);
        opacity: 0;
    }
}

/* The board itself takes the hit — a quick 3D lift and settle. */
.puzzle-finished .puzzle-board-wrap { perspective: 900px; }
.puzzle-finished .puzzle-board { animation: puzzle-board-pop 1s cubic-bezier(.2, .9, .3, 1) both; }
@keyframes puzzle-board-pop {
    0%   { transform: perspective(900px) rotateX(0deg) scale(1); }
    22%  { transform: perspective(900px) rotateX(9deg) translateZ(46px) scale(1.045); }
    58%  { transform: perspective(900px) rotateX(-3deg) translateZ(10px) scale(.995); }
    100% { transform: perspective(900px) rotateX(0deg) translateZ(0) scale(1); }
}

/* The banner arrives with the burst rather than fading in flat. */
.puzzle-cheer { transform-style: preserve-3d; animation: puzzle-cheer-pop .72s cubic-bezier(.2, 1.5, .4, 1) both; }
@keyframes puzzle-cheer-pop {
    0%   { opacity: 0; transform: perspective(700px) translate3d(0, 26px, -180px) rotateX(38deg) scale(.7); }
    100% { opacity: 1; transform: perspective(700px) translate3d(0, 0, 0) rotateX(0deg) scale(1); }
}

@media (prefers-reduced-motion: reduce) {
    .puzzle-burst { display: none; }
    .puzzle-cheer,
    .puzzle-finished .puzzle-board { animation: none; }
}
