:root{
    --page-top: #3b2d5c;
    --page-bottom: #1d1730;
    --board: #4a3d6e;
    --cell: #5d4f85;
    --text: #fff7ec;
    --text-muted: #c9bde6;
    --accent: #ffd23f;
    --accent-text: #2a2140;
    --font: "Trebuchet MS", "Segoe UI", Arial, sans-serif;
    --gutter: 16px;
    /* The board fills the width on phones and the height on wide screens; the rest of the page is ~210px. */
    --board-size: min(100vw - 2 * var(--gutter), 100dvh - 210px, 500px);
    --slide-ms: 110ms;
}

*{
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

html{
    touch-action: manipulation;
}

body{
    min-height: 100dvh;
    background: var(--page-bottom) linear-gradient(180deg, var(--page-top), var(--page-bottom)) no-repeat;
    color: var(--text);
    font-family: var(--font);
    overflow: hidden;
    -webkit-tap-highlight-color: transparent;
}

.noscript{
    padding: 24px;
    font-size: 18px;
}

.noscript a{
    color: var(--accent);
}

/* Swipes anywhere move the tiles, so the page itself must never scroll or select text. */
.app{
    position: fixed;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 14px;
    padding:
        max(8px, env(safe-area-inset-top))
        max(var(--gutter), env(safe-area-inset-right))
        max(8px, env(safe-area-inset-bottom))
        max(var(--gutter), env(safe-area-inset-left));
    touch-action: none;
    user-select: none;
    -webkit-user-select: none;
}

.top,
.controls{
    width: var(--board-size);
    display: flex;
    align-items: center;
    gap: 8px;
}

.back{
    flex-shrink: 0;
    width: 40px;
    height: 40px;
    display: grid;
    place-items: center;
    color: var(--text);
    font-size: 32px;
    line-height: 1;
    text-decoration: none;
    border-radius: 50%;
    background: rgba(255, 255, 255, .1);
}

.logo{
    flex: 1;
    font-size: clamp(30px, 10vw, 48px);
    line-height: 1;
    color: var(--accent);
    text-shadow: 0 3px 0 #c98b00;
}

.score{
    position: relative;
    min-width: 72px;
    padding: 6px 10px;
    text-align: center;
    border-radius: 12px;
    background: var(--board);
}

.score__label{
    display: block;
    font-size: 11px;
    font-weight: bold;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--text-muted);
}

.score__value{
    display: block;
    font-size: 20px;
    font-weight: bold;
}

/* "+24" floating up from the score after a merge. */
.score__gain{
    position: absolute;
    left: 0;
    right: 0;
    top: 50%;
    font-weight: bold;
    color: var(--accent);
    opacity: 0;
    pointer-events: none;
}

.score__gain--show{
    animation: gain .7s ease-out;
}

@keyframes gain{
    from{ opacity: 1; transform: translateY(0); }
    to{ opacity: 0; transform: translateY(-40px); }
}

.controls{
    justify-content: flex-start;
}

.button{
    min-height: 44px;
    padding: 0 18px;
    font: bold 16px var(--font);
    color: var(--text);
    background: var(--board);
    border: 0;
    border-radius: 999px;
    cursor: pointer;
    transition: transform .1s, opacity .2s;
}

.button:active:not(:disabled){
    transform: scale(.94);
}

.button:disabled{
    opacity: .45;
    cursor: default;
}

.button:focus-visible,
.back:focus-visible{
    outline: 3px solid var(--accent);
    outline-offset: 2px;
}

.button--round{
    width: 44px;
    padding: 0;
}

.controls .button--round:first-of-type{
    margin-left: auto;
}

.button--accent{
    color: var(--accent-text);
    background: var(--accent);
}

/* ---------- Board ---------- */

.board{
    /* Here, not on :root, so a board given another --board-size gets matching cells. */
    --gap: calc(var(--board-size) * .03);
    --cell-size: calc((var(--board-size) - 5 * var(--gap)) / 4);
    position: relative;
    width: var(--board-size);
    height: var(--board-size);
    flex-shrink: 0;
    border-radius: calc(var(--gap) * 2);
    background: var(--board);
    box-shadow: 0 14px 40px rgba(0, 0, 0, .35);
}

.board__cells{
    position: absolute;
    inset: var(--gap);
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: var(--gap);
}

.board__cells i{
    border-radius: calc(var(--cell-size) * .14);
    background: var(--cell);
}

.board__tiles{
    position: absolute;
    inset: 0;
}

.tile{
    --tile-bg: #eee4da;
    --tile-text: #5b4636;
    position: absolute;
    left: 0;
    top: 0;
    width: var(--cell-size);
    height: var(--cell-size);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: calc(var(--cell-size) * .02);
    border-radius: calc(var(--cell-size) * .14);
    background: var(--tile-bg);
    color: var(--tile-text);
    box-shadow: inset 0 calc(var(--cell-size) * -.05) 0 rgba(0, 0, 0, .12);
    transform: translate(
        calc(var(--gap) + var(--col) * (var(--cell-size) + var(--gap))),
        calc(var(--gap) + var(--row) * (var(--cell-size) + var(--gap)))
    );
    transition: transform var(--slide-ms) ease-in-out;
}

/* The inner parts scale, so the slide (on .tile) and the pop (on the parts) never fight over transform. */
.tile__face,
.tile__value{
    transform-origin: center;
}

.tile--new .tile__face,
.tile--new .tile__value{
    animation: appear .2s ease-out calc(var(--slide-ms) * .8) backwards;
}

.tile--new{
    animation: fade-in .2s ease-out calc(var(--slide-ms) * .8) backwards;
}

.tile--merged{
    z-index: 2;
    animation: pop .22s ease-out var(--slide-ms) backwards;
}

.tile--leaving{
    z-index: 1;
}

.tile--hop .tile__face,
.tile--hop .tile__value{
    animation: hop .5s ease-in-out 2;
}

@keyframes appear{
    from{ transform: scale(0); }
    to{ transform: scale(1); }
}

@keyframes fade-in{
    from{ opacity: 0; }
}

/* Uses scale (not transform), so the tile keeps its position while it pops. */
@keyframes pop{
    0%{ scale: 0; }
    60%{ scale: 1.15; }
    100%{ scale: 1; }
}

@keyframes hop{
    0%, 100%{ transform: translateY(0); }
    40%{ transform: translateY(-18%); }
}

/* The critter's eyes; the pupils look the way the tiles last moved. */
.tile__face{
    display: flex;
    gap: calc(var(--cell-size) * .14);
}

.tile__eye{
    --eye: calc(var(--cell-size) * .13);
    position: relative;
    width: var(--eye);
    height: var(--eye);
    border-radius: 50%;
    background: #fff;
    box-shadow: 0 0 0 calc(var(--cell-size) * .015) rgba(0, 0, 0, .15);
}

.tile__eye::after{
    content: "";
    position: absolute;
    left: 25%;
    top: 25%;
    width: 50%;
    height: 50%;
    border-radius: 50%;
    background: #2a2140;
    transition: transform .2s;
}

.board[data-look="left"] .tile__eye::after{ transform: translateX(-45%); }
.board[data-look="right"] .tile__eye::after{ transform: translateX(45%); }
.board[data-look="up"] .tile__eye::after{ transform: translateY(-45%); }
.board[data-look="down"] .tile__eye::after{ transform: translateY(45%); }

.tile__value{
    font-weight: bold;
    line-height: 1;
    /* Fewer digits, bigger number. */
    font-size: calc(var(--cell-size) * (.44 - var(--digits) * .055));
}

.tile[data-value="2"]{ --tile-bg: #f3e9df; }
.tile[data-value="4"]{ --tile-bg: #f2dfc4; }
.tile[data-value="8"]{ --tile-bg: #ffb46b; --tile-text: #fff; }
.tile[data-value="16"]{ --tile-bg: #ff8f5a; --tile-text: #fff; }
.tile[data-value="32"]{ --tile-bg: #ff6b6b; --tile-text: #fff; }
.tile[data-value="64"]{ --tile-bg: #f0476b; --tile-text: #fff; }
.tile[data-value="128"]{ --tile-bg: #8fd16a; --tile-text: #fff; }
.tile[data-value="256"]{ --tile-bg: #3fc1a5; --tile-text: #fff; }
.tile[data-value="512"]{ --tile-bg: #45a7f2; --tile-text: #fff; }
.tile[data-value="1024"]{ --tile-bg: #7c6cf2; --tile-text: #fff; }
.tile[data-value="2048"]{ --tile-bg: #ffd23f; --tile-text: #5b3d00; box-shadow: 0 0 calc(var(--cell-size) * .25) rgba(255, 210, 63, .7); }
.tile[data-value="super"]{ --tile-bg: #1f1830; --tile-text: #ffd23f; box-shadow: 0 0 calc(var(--cell-size) * .2) rgba(255, 210, 63, .5); }

/* ---------- Win and game over ---------- */

.overlay{
    position: absolute;
    inset: 0;
    z-index: 5;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 12px;
    padding: 24px;
    text-align: center;
    border-radius: inherit;
    background: rgba(29, 23, 48, .9);
    animation: fade-in .4s ease-out;
}

.overlay[hidden]{
    display: none;
}

.overlay__title{
    font-size: clamp(26px, 8vw, 40px);
    font-weight: bold;
    color: var(--accent);
}

.overlay__text{
    max-width: 320px;
    font-size: 18px;
}

.overlay__actions{
    display: flex;
    flex-direction: column;
    gap: 10px;
    margin-top: 8px;
}

.overlay .button[hidden]{
    display: none;
}

.hint{
    width: var(--board-size);
    font-size: 14px;
    text-align: center;
    color: var(--text-muted);
}

.hint strong{
    color: var(--accent);
}

/* Short screens (a phone on its side): no hint, the board gets the room. */
@media (max-height: 520px){
    :root{
        --board-size: min(100vw - 2 * var(--gutter), 100dvh - 150px, 500px);
    }

    .hint{
        display: none;
    }
}

@media (prefers-reduced-motion: reduce){
    :root{
        --slide-ms: 1ms;
    }

    .tile--hop .tile__face,
    .tile--hop .tile__value,
    .score__gain--show{
        animation: none;
    }
}
