/* Poppins, served from this site instead of from Google. That removes the only third party
   from a visitor's request path, removes a render-blocking round trip to someone else's server,
   and lets the content policy stay at 'self'.

   SIL Open Font License 1.1. The full text sits beside the files in fonts/OFL.txt, which the
   licence requires to travel with them.

   Latin and latin-ext are separate files behind their own unicode-range, exactly as Google
   served them, so a browser only fetches the second if the page shows a character from it.
   The five weights are the ones the hosted version was asked for. */

@font-face {
    font-family: 'Poppins';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url('../fonts/poppins-latin-400-normal.woff2') format('woff2');
    unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}

@font-face {
    font-family: 'Poppins';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url('../fonts/poppins-latin-ext-400-normal.woff2') format('woff2');
    unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}

@font-face {
    font-family: 'Poppins';
    font-style: normal;
    font-weight: 500;
    font-display: swap;
    src: url('../fonts/poppins-latin-500-normal.woff2') format('woff2');
    unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}

@font-face {
    font-family: 'Poppins';
    font-style: normal;
    font-weight: 500;
    font-display: swap;
    src: url('../fonts/poppins-latin-ext-500-normal.woff2') format('woff2');
    unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}

@font-face {
    font-family: 'Poppins';
    font-style: normal;
    font-weight: 600;
    font-display: swap;
    src: url('../fonts/poppins-latin-600-normal.woff2') format('woff2');
    unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}

@font-face {
    font-family: 'Poppins';
    font-style: normal;
    font-weight: 600;
    font-display: swap;
    src: url('../fonts/poppins-latin-ext-600-normal.woff2') format('woff2');
    unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}

@font-face {
    font-family: 'Poppins';
    font-style: normal;
    font-weight: 700;
    font-display: swap;
    src: url('../fonts/poppins-latin-700-normal.woff2') format('woff2');
    unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}

@font-face {
    font-family: 'Poppins';
    font-style: normal;
    font-weight: 700;
    font-display: swap;
    src: url('../fonts/poppins-latin-ext-700-normal.woff2') format('woff2');
    unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}

@font-face {
    font-family: 'Poppins';
    font-style: normal;
    font-weight: 800;
    font-display: swap;
    src: url('../fonts/poppins-latin-800-normal.woff2') format('woff2');
    unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}

@font-face {
    font-family: 'Poppins';
    font-style: normal;
    font-weight: 800;
    font-display: swap;
    src: url('../fonts/poppins-latin-ext-800-normal.woff2') format('woff2');
    unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}

/* Routle -- palette follows the Connections family the design references: an off-white ground,
   dark slate ink, and the category colours carrying the three encounter buckets. */

:root {
    --ground: #efefe6;
    --surface: #ffffff;
    --ink: #121212;
    --muted: #6f6c60;
    --faint: #8b8779;
    --line: #d6d3c6;
    --edge: #c9c5b4;
    --dark: #2f2e29;

    --land: #a0c35a;
    --land-ink: #3d4a24;
    --water: #b0c4ef;
    --water-ink: #2f3d63;
    --special: #ba81c5;
    --special-ink: #4a2a52;

    --correct: #a0c35a;
    --close: #f2cd5c;
    --wrong: #d4715f;
    --wrong-soft: #ecb7ab;

    --off-bg: #e6e5da;
    --off-line: #cfcbbb;
    --off-ink: #a8a496;
}

* { box-sizing: border-box; }

body {
    margin: 0;
    background: var(--ground);
    color: var(--ink);
    font-family: Poppins, "Segoe UI", system-ui, sans-serif;
}

.boot { padding: 48px; color: var(--muted); }

a, .linkish {
    color: var(--dark);
    text-decoration: none;
    background: none;
    border: 0;
    padding: 0;
    font: inherit;
    font-weight: 500;
    cursor: pointer;
}

a:hover, .linkish:hover { color: #000; text-decoration: underline; }

.app {
    max-width: 1180px;
    min-height: 100vh;
    margin: 0 auto;
    display: flex;
    flex-direction: column;
}

/* ---- header / footer ---------------------------------------------------- */

.topbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 22px 40px 18px;
    border-bottom: 1px solid var(--line);
}

.topbar-side { width: 150px; display: flex; }
.topbar-end { justify-content: flex-end; }

.brand { display: flex; flex-direction: column; align-items: center; gap: 2px; }

.wordmark {
    margin: 0;
    line-height: 0;
}

.wordmark img {
    display: block;
    height: 72px;
    width: auto;
}

.puzzle-no {
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--muted);
}

.footbar {
    margin-top: auto;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    padding: 14px 40px;
    border-top: 1px solid var(--line);
    font-size: 13px;
    color: var(--muted);
}

/* ---- how-to-play dialog ------------------------------------------------- */

.modal-backdrop {
    position: fixed;
    inset: 0;
    z-index: 100;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 20px;
    background: rgba(18, 18, 18, 0.45);
}

.modal {
    display: flex;
    flex-direction: column;
    gap: 16px;
    width: 100%;
    max-width: 520px;
    max-height: 86vh;
    overflow-y: auto;
    padding: 28px;
    background: var(--ground);
    border-radius: 14px;
    box-shadow: 0 18px 50px rgba(0, 0, 0, 0.28);
}

.modal-title { margin: 0; font-size: 26px; font-weight: 800; letter-spacing: -0.02em; }

.modal-heading {
    margin: 0 0 5px;
    font-size: 12px;
    font-weight: 700;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--muted);
}

.modal p { margin: 0; font-size: 14px; line-height: 1.6; }

.modal-key { display: flex; flex-direction: column; gap: 7px; }

.modal-key-row { display: flex; align-items: center; gap: 10px; font-size: 13px; line-height: 1.45; }

.modal-key-row .chip { flex: none; min-width: 84px; text-align: center; }

.modal-note { margin-top: 9px !important; font-size: 13px !important; color: var(--muted); }

.preview-strip {
    margin: 0 40px;
    padding: 9px 14px;
    border-radius: 8px;
    background: var(--close);
    font-size: 13px;
    font-weight: 600;
    color: var(--ink);
}

.notice { padding: 40px; color: var(--muted); }
.notice-bad { color: #8a2f2f; }

/* ---- board -------------------------------------------------------------- */

.board {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 320px;
    gap: 44px;
    padding: 30px 40px 24px;
}

.buckets { display: flex; flex-direction: column; gap: 26px; }

.bucket { display: flex; flex-direction: column; gap: 12px; }

.bucket-head { display: flex; align-items: baseline; gap: 12px; }

.bucket-title {
    margin: 0;
    font-size: 14px;
    font-weight: 700;
    letter-spacing: 0.16em;
    text-transform: uppercase;
}

.bucket-count { font-size: 13px; color: var(--muted); }

.cards {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 16px;
}

.card {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    padding: 16px 10px 13px;
    border-radius: 10px;
    min-height: 190px;
    justify-content: center;
}

.card-land { background: var(--land); }
.card-land .card-meta { color: var(--land-ink); }
.card-water { background: var(--water); }
.card-water .card-meta { color: var(--water-ink); }
.card-special { background: var(--special); }
.card-special .card-meta { color: var(--special-ink); }

.sprite { width: 112px; height: 112px; image-rendering: -webkit-optimize-contrast; }

.card-name { font-size: 15px; font-weight: 700; text-align: center; }

/* Names how the Pokemon is met when that is not "walk into it", so an odd rate or a missing
   level reads as the method rather than as a broken number. */
.card-tag {
    padding: 2px 7px;
    border-radius: 999px;
    background: rgba(18, 18, 18, 0.09);
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    white-space: nowrap;
}
.card-meta { font-size: 12px; }

/* ---- guess history ------------------------------------------------------ */

.chip {
    padding: 7px 10px;
    border-radius: 6px;
    font-size: 13px;
    font-weight: 600;
    color: var(--ink);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.chip-correct { background: var(--correct); }
.chip-close { background: var(--close); }
.chip-wrong { background: var(--wrong); }
.chip-blank { background: var(--off-bg); color: var(--off-ink); }

/* ---- region map --------------------------------------------------------- */

.picker { display: flex; flex-direction: column; gap: 10px; }

.map-tabs { display: flex; gap: 6px; }

.map-tab {
    flex: 1;
    height: 32px;
    background: var(--surface);
    border: 1px solid var(--edge);
    border-radius: 7px;
    font: inherit;
    font-size: 12px;
    font-weight: 700;
    color: var(--ink);
    cursor: pointer;
}

.map-tab:hover { border-color: var(--dark); }
.map-tab-on { background: var(--dark); border-color: var(--dark); color: var(--ground); }

/* The stage is a plain wrapper in the page and a full-screen backdrop when asked. */
.map-stage { position: relative; }

.map-stage-full {
    position: fixed;
    inset: 0;
    z-index: 100;
    display: grid;
    place-items: center;
    padding: 24px;
    background: rgba(18, 18, 18, 0.72);
}

/* On a phone the map is capped by the screen's width whatever we do, so every pixel of padding
   comes straight off the size of the pins. */
@media (max-width: 900px) {
    .map-stage-full { padding: 6px; }
    .map-stage-full .map-frame { width: min(calc(100vw - 12px), calc((100vh - 12px) * var(--map-ar, 1.3333))); }
}

/* Fills its column; full screen is the only other size. */
.map-frame {
    position: relative;
    z-index: 5;
    line-height: 0;
    /* Nothing here is text, so dragging across it should do nothing rather than paint a blue
       selection over the map and leave the pins looking highlighted. */
    user-select: none;
    -webkit-user-select: none;
    -webkit-tap-highlight-color: transparent;
    border: 1px solid var(--edge);
    border-radius: 8px;
    background: var(--surface);
}

/* The picture and the pin overlay, clipped to the rounded corners. This clipping used to sit on
   the frame itself, which also cut off the hover label of any pin near an edge. */
.map-clip {
    position: relative;
    line-height: 0;
    border-radius: 7px;
    overflow: hidden;
}

/* The window the map is seen through. Its shape comes from the picture's own pixel dimensions,
   written inline, so it holds that shape no matter how far the map inside is zoomed. Without a
   fixed box here the page itself would grow taller every time somebody zoomed in. */
.map-scroll {
    width: 100%;
    overflow: hidden;
}

/* Only a zoomed map scrolls. At natural size there is nothing to scroll to, and leaving scrolling
   on would put scrollbars on the desktop that eat into the picture and letterbox it. */
.map-scroll-on {
    overflow: auto;
    scrollbar-width: thin;
    /* One finger drags the map rather than the page, which is the whole point of zooming in. */
    touch-action: pan-x pan-y;
    overscroll-behavior: contain;
}

/* The map itself. Widening it is what does the zooming: the picture and the pin overlay are both
   sized from this box, so the pins keep their places and grow with the art rather than staying
   4px dots on a picture that got bigger. */
.map-zoom {
    position: relative;
    width: calc(100% * var(--zoom, 1));
    line-height: 0;
}

/* Draws nothing. It exists so the browser has something at the middle of the map to scroll to. */
.map-center {
    position: absolute;
    left: 50%;
    top: 50%;
    width: 1px;
    height: 1px;
    pointer-events: none;
}

.map-stage-full .map-frame {
    /* Fit whichever of width or height runs out first, using this map's own shape. A hardcoded 4:3
       here made the frame too wide for the 400x356 Sevii art: the picture overflowed the height
       cap and got clipped, while the pin overlay stretched to the frame instead, so every pin sat
       above where it belonged. Height follows from the picture, so there is no cap to clip against.
       */
    width: min(calc(100vw - 56px), calc((100vh - 56px) * var(--map-ar, 1.3333)));
    margin: 0;
    box-shadow: 0 18px 50px rgba(0, 0, 0, 0.45);
}

/* The art is pixel art at its own small size. Smoothing it turns the roads to mush, so keep the
   pixels. */
/* pointer-events off so the picture is never the thing being clicked or dragged: only the pins
   in the overlay above it are interactive, and a long press on a phone should not offer to save
   the image. */
.map-img {
    display: block;
    width: 100%;
    height: auto;
    image-rendering: pixelated;
    pointer-events: none;
    -webkit-user-drag: none;
}

.map-pins { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }

.pin-dot { stroke: var(--dark); stroke-width: calc(1 * var(--pin-k, 1)); }
.pin-hit { fill: transparent; cursor: pointer; }

/* The hit area is a wide invisible circle, so the browser's own focus ring drew a big black
   rounded square over the map, on click as well as on tab. The dot itself is the focus ring
   instead: it takes a heavy dark outline, which is the right size and sits where you are
   actually looking. */
.pin-hit:focus,
.pin-hit:focus-visible { outline: none; }

.pin:has(.pin-hit:focus-visible) .pin-dot {
    stroke: var(--dark);
    stroke-width: calc(3.4 * var(--pin-k, 1));
}
.map-off .pin-hit { cursor: not-allowed; }
.pin:hover .pin-dot { stroke-width: calc(2 * var(--pin-k, 1)); }

.pin-open .pin-dot { fill: var(--surface); }

/* Somewhere you cannot pick: not in this game, or never an answer anywhere. Hollow and grey, so it
   reads as a place on the map rather than as an option you have missed. */
.pin-dead .pin-dot { fill: var(--off-bg); stroke: var(--faint); stroke-width: calc(0.9 * var(--pin-k, 1)); opacity: 0.85; }
.pin-dead .pin-hit { cursor: default; }
.pin-dead:hover .pin-dot { opacity: 1; stroke-width: calc(1.4 * var(--pin-k, 1)); }

/* The label is HTML rather than SVG text: text inside the viewBox would scale with the map, so it
   would be unreadable at sidebar size and enormous in full screen. */
.pin-tip {
    position: absolute;
    transform: translate(-50%, -168%);
    padding: 3px 8px;
    border-radius: 6px;
    background: var(--dark);
    color: var(--ground);
    font-size: 12px;
    font-weight: 700;
    line-height: 1.3;
    white-space: nowrap;
    pointer-events: none;
    z-index: 6;
}

/* Anchored to a side rather than centred on the dot, so a long name near the edge stays whole. */
.pin-tip.tip-w,
.pin-tip.tip-e { transform: translateY(-168%); }

/* No room above the dot, so the label goes under it. */
.pin-tip.tip-s { transform: translate(-50%, 130%); }
.pin-tip.tip-w.tip-s,
.pin-tip.tip-e.tip-s { transform: translateY(130%); }

/* A touch screen has no hover, so a phone player taps a dot and learns nothing about what they
   just picked. The chosen pin wears the same label instead. A mouse already gets this from hover,
   and a label sitting on the map permanently would cover its neighbours, so it stops at 900px. */
.pin-tip.tip-pick { display: none; }

@media (max-width: 900px) {
    .pin-tip.tip-pick { display: block; }
}
/* The answer, once the game is over. A ring pings outward from it so it can be found at a
   glance among six guessed dots, and the dot itself goes green whether it was guessed or not. */
.pin-answer .pin-dot {
    fill: var(--correct);
    stroke: var(--dark);
    stroke-width: calc(1.8 * var(--pin-k, 1));
}

.pin-halo {
    fill: none;
    stroke: var(--correct);
    stroke-width: calc(2.5 * var(--pin-k, 1));
    transform-box: fill-box;
    transform-origin: center;
    animation: pin-found 1.9s ease-out infinite;
    pointer-events: none;
}

@keyframes pin-found {
    0% { transform: scale(1); opacity: 0.85; }
    70% { transform: scale(2.9); opacity: 0; }
    100% { transform: scale(2.9); opacity: 0; }
}

.pin-picked .pin-dot { fill: var(--dark); stroke: var(--ground); stroke-width: calc(1.6 * var(--pin-k, 1)); }
.pin-correct .pin-dot { fill: var(--correct); }
.pin-close .pin-dot { fill: var(--close); }
.pin-wrong .pin-dot { fill: var(--wrong); }

/* Only on a small screen, and only while the map is inline: a tap anywhere opens it full size,
   because a 25px pin is not a thumb target and Kanto's pins overlap at that width. Sits over the
   picture but under the tools, so the close button still works. */
.map-tools {
    position: absolute;
    z-index: 5;
    right: 7px;
    bottom: 7px;
    display: flex;
    align-items: center;
    gap: 4px;
    padding: 4px;
    border-radius: 8px;
    background: rgba(255, 255, 255, 0.9);
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.22);
    line-height: 1;
}

.map-btn {
    width: 26px;
    height: 26px;
    display: grid;
    place-items: center;
    background: transparent;
    border: 1px solid var(--edge);
    border-radius: 5px;
    font: inherit;
    font-size: 15px;
    font-weight: 700;
    color: var(--ink);
    cursor: pointer;
}

.map-btn:hover:not(:disabled) { border-color: var(--dark); }
.map-btn:disabled { color: var(--off-ink); border-color: var(--off-line); cursor: not-allowed; }

.off-map { display: flex; flex-direction: column; gap: 6px; }

.off-map-label {
    margin: 0;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--faint);
}

.off-map-list {
    display: flex;
    flex-wrap: wrap;
    gap: 5px;
    max-height: 156px;
    overflow-y: auto;
}

.off-chip {
    padding: 5px 9px;
    background: var(--surface);
    border: 1px solid var(--edge);
    border-radius: 6px;
    font: inherit;
    font-size: 12px;
    color: var(--ink);
    cursor: pointer;
}

.off-chip:hover:not(:disabled) { border-color: var(--dark); }
.off-chip:disabled { background: var(--off-bg); color: var(--off-ink); cursor: not-allowed; }
.off-chip-picked { background: var(--dark); border-color: var(--dark); color: var(--ground); font-weight: 700; }
.off-chip-correct { background: var(--correct); border-color: var(--correct); }
.off-chip-close { background: var(--close); border-color: var(--close); }
.off-chip-wrong { background: var(--wrong); border-color: var(--wrong); }

.picked { margin: 0; font-size: 13px; font-weight: 600; color: var(--muted); }

/* ---- controls ----------------------------------------------------------- */

.controls { display: flex; flex-direction: column; gap: 18px; }

.field { display: flex; flex-direction: column; gap: 9px; }

.field-label {
    margin: 0;
    font-size: 13px;
    font-weight: 700;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--muted);
}

.gen-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 9px; }

/* Games stack full width rather than sharing a row: two of them are "HeartGold & SoulSilver" and
   "Black 2 & White 2", which do not survive being squeezed into half a 320px column. */
.game-list { display: flex; flex-direction: column; gap: 8px; }
.game-list .gen { height: 42px; }

.gen {
    height: 44px;
    background: var(--surface);
    border: 1px solid var(--edge);
    border-radius: 8px;
    font: inherit;
    font-size: 15px;
    font-weight: 700;
    color: var(--ink);
    cursor: pointer;
}

.gen:hover:not(:disabled) { border-color: var(--dark); }
.gen-on { background: var(--dark); border-color: var(--dark); color: var(--ground); }

.gen-off,
.gen:disabled {
    background: var(--off-bg);
    border: 1px dashed var(--off-line);
    color: var(--off-ink);
    font-weight: 500;
    cursor: not-allowed;
}

.gen-on:disabled { background: var(--dark); border: 1px solid var(--dark); color: var(--ground); font-weight: 700; }

/* With the guess list gone the buttons are the record, so they carry the verdict as well as the
   selection. These come last and repeat themselves for :disabled, because a field that has locked
   must keep saying what it already said. */
/* Green is correct everywhere else on the page, so a field you have got right locks green rather
   than dark. Dark stays what it always was: the thing you have merely selected. */
.gen-correct, .gen-correct:disabled {
    background: var(--correct);
    border: 1px solid var(--correct);
    color: var(--ink);
    font-weight: 700;
    cursor: pointer;
}

.gen-close, .gen-close:disabled {
    background: var(--close);
    border: 1px solid var(--close);
    color: var(--ink);
    font-weight: 700;
    cursor: pointer;
}

.gen-wrong, .gen-wrong:disabled {
    background: var(--wrong);
    border: 1px solid var(--wrong);
    color: var(--ink);
    font-weight: 700;
    cursor: pointer;
}

.gen-correct:disabled, .gen-close:disabled, .gen-wrong:disabled { cursor: not-allowed; }

/* Ruled out and still the one you have selected: the verdict keeps the fill, the selection becomes
   a ring, so neither piece of information hides the other. */
.gen-close.gen-on, .gen-wrong.gen-on { box-shadow: inset 0 0 0 2px var(--dark); }

.select {
    height: 48px;
    padding: 0 12px;
    background: var(--surface);
    border: 1px solid var(--edge);
    border-radius: 8px;
    font: inherit;
    font-size: 15px;
    color: var(--ink);
    cursor: pointer;
}

.select:disabled { background: var(--off-bg); color: var(--off-ink); cursor: not-allowed; }

/* Highlighting a game in the open list was browser blue, the one colour on the page that belongs
   to nothing in the palette. The list itself is drawn by the browser rather than by us, so its
   highlight is only partly ours to set: accent-color is what Chrome and Edge use, and the option
   rules cover the browsers that style options directly. Where a browser honours neither, it keeps
   its own default rather than breaking. The warm grey of the borders is used rather than a green,
   so the highlight reads as the list working rather than as a result. */
.select { accent-color: var(--edge); }
.select option:checked { background: var(--edge); color: var(--ink); }
.select option:hover { background: var(--edge); color: var(--ink); }
/* A locked select is a solved field, the same state the generation buttons show, so it wears the
   same dark box rather than the greyed-out look :disabled would otherwise give it. Both selectors
   are needed: locking always disables the control, and .select:disabled would win on the colours
   otherwise, since it is written first at equal specificity. */
.select-locked,
.select-locked:disabled {
    background: var(--dark);
    border: 1px solid var(--dark);
    color: var(--ground);
    font-weight: 700;
    cursor: default;
}

.primary {
    height: 50px;
    background: var(--dark);
    border: 1px solid var(--dark);
    border-radius: 8px;
    font: inherit;
    font-size: 16px;
    font-weight: 700;
    color: var(--ground);
    cursor: pointer;
}

.primary:disabled { background: var(--off-bg); border-color: var(--off-line); color: var(--off-ink); cursor: not-allowed; }

.secondary {
    height: 44px;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    background: transparent;
    border: 1px solid var(--edge);
    border-radius: 8px;
    font: inherit;
    font-size: 14px;
    font-weight: 500;
    color: var(--ink);
    cursor: pointer;
}

.secondary:hover:not(:disabled) { border-color: var(--dark); }
.secondary:disabled { color: var(--off-ink); border-color: var(--off-line); cursor: not-allowed; }

.note { margin: 0; font-size: 12px; color: var(--faint); }
.note-center { text-align: center; }

.result {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 14px 16px;
    border-radius: 10px;
    font-size: 14px;
}

.result-win { background: var(--land); color: var(--ink); }
.result-lose { background: var(--off-bg); border: 1px solid var(--off-line); color: var(--ink); }

/* The box turns red on the same beat the ball tips over, so the two read as one event rather than
   as an animation followed by a colour change. It starts on the neutral grey, which is what the
   backwards fill holds during the flight and the wobble. */
.result-lose { animation: result-fail 0.5s ease-out 1.32s both; }

@keyframes result-fail {
    0%   { background: var(--off-bg); border-color: var(--off-line); }
    40%  { background: var(--wrong); border-color: var(--wrong); }
    100% { background: var(--wrong-soft); border-color: var(--wrong); }
}

/* The result block turns into a row so the ball can land beside the text rather than over it. */
.result { flex-direction: row; align-items: center; gap: 12px; overflow: hidden; }
.result-text { display: flex; flex-direction: column; gap: 3px; min-width: 0; flex: 1; }

.share-btn {
    flex: none;
    align-self: center;
    padding: 7px 12px;
    background: rgba(255, 255, 255, 0.55);
    border: 1px solid rgba(18, 18, 18, 0.18);
    border-radius: 7px;
    font: inherit;
    font-size: 12px;
    font-weight: 700;
    color: var(--ink);
    cursor: pointer;
}

.share-btn:hover { border-color: var(--dark); }

/* ---- the throw ---------------------------------------------------------- */

.throw { flex: none; width: 46px; height: 46px; pointer-events: none; }
.ball { display: block; width: 46px; height: 46px; }

.ball-bottom { fill: #cfe3a3; }
.ball-top { fill: #6d9a2c; }
.ball-rim circle, .ball-rim rect { fill: #2e4d0e; }
.ball-rim > circle { fill: none; stroke: #2e4d0e; stroke-width: 4; }
.ball-eye { fill: #cfe3a3; }

/* A losing ball is grey. The greens are the brand's, and on the red box they read as a second
   status colour arguing with the first. */
.throw-lose .ball-bottom { fill: var(--off-bg); }
.throw-lose .ball-top { fill: var(--faint); }
.throw-lose .ball-rim circle, .throw-lose .ball-rim rect { fill: var(--dark); }
.throw-lose .ball-rim > circle { fill: none; stroke: var(--dark); }
.throw-lose .ball-eye { fill: var(--off-bg); }

/* Two nested elements: the outer one flies, the inner one wobbles. Combining them into one
   transform would mean every wobble keyframe restating the flight's final position. */
.throw-flight { animation: ball-fly 0.72s cubic-bezier(0.2, 0.7, 0.3, 1) both; }
.throw-wobble { transform-origin: 50% 62%; }

.throw-win .throw-wobble { animation: ball-wobble 1s ease-in-out 0.72s both, ball-click 0.3s ease-out 1.72s both; }
.throw-lose .throw-wobble {
    animation: ball-wobble 0.6s ease-in-out 0.72s both,
               ball-tip 0.5s cubic-bezier(0.3, 1.3, 0.5, 1) 1.32s both;
}

@keyframes ball-fly {
    0%   { opacity: 0; transform: translate(-120px, 26px) scale(0.6) rotate(-40deg); }
    12%  { opacity: 1; }
    55%  { transform: translate(-34px, -40px) scale(1) rotate(200deg); }
    82%  { transform: translate(0, 5px) rotate(340deg); }
    91%  { transform: translate(0, -4px) rotate(355deg); }
    100% { opacity: 1; transform: translate(0, 0) rotate(360deg); }
}

@keyframes ball-wobble {
    0%, 100% { transform: rotate(0deg); }
    15% { transform: rotate(-13deg); }
    32% { transform: rotate(11deg); }
    50% { transform: rotate(-8deg); }
    68% { transform: rotate(6deg); }
    84% { transform: rotate(-3deg); }
}

/* The click: a single tightening pulse, which is all the ball does when it stays shut. */
@keyframes ball-click {
    0%   { transform: scale(1); }
    45%  { transform: scale(1.14); }
    100% { transform: scale(1); }
}

/* The one that got away: the ball gives up and rolls onto its side. Opening the lid was the
   first attempt and it read as a rendering fault rather than as a Pokemon escaping, because half
   a flat two-tone circle rotating out of the other half is not a shape anyone recognises. */
@keyframes ball-tip {
    0%   { transform: rotate(0deg); }
    100% { transform: rotate(88deg); }
}

/* ---- share card --------------------------------------------------------- */

.modal-share { max-width: 340px; }

.share-grid {
    margin: 0;
    padding: 14px;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: 10px;
    font-family: ui-monospace, "Cascadia Mono", Menlo, monospace;
    font-size: 17px;
    line-height: 1.45;
    letter-spacing: 0.06em;
    white-space: pre;
    overflow-x: auto;
}

.share-actions { display: flex; gap: 10px; }
.share-actions .primary, .share-actions .secondary { flex: 1; height: 44px; }

/* ---- reveal ------------------------------------------------------------- */

/* Newly revealed Pokemon rise in, staggered, so the last guess is a beat rather than a dump.
   Cards that were already on the board keep their DOM node and so never re-run this. */
@keyframes card-in {
    from { opacity: 0; transform: translateY(9px) scale(0.97); }
    to   { opacity: 1; transform: none; }
}

.card { animation: card-in 0.32s ease-out both; }

@media (prefers-reduced-motion: reduce) {
    /* Still ring rather than none: it is the only thing marking the answer. */
    .pin-halo { animation: none; opacity: 0.9; transform: scale(1.9); }

    .throw-flight, .throw-wobble, .card { animation: none; }

    /* No animation, so the end state has to be set outright or a loss would stay grey. */
    .result-lose {
        animation: none;
        background: var(--wrong-soft);
        border-color: var(--wrong);
    }
    .throw { opacity: 1; }
}

.tally { display: flex; align-items: center; justify-content: space-between; gap: 12px; font-size: 13px; color: var(--muted); }

.pips { display: flex; gap: 6px; }

.pip { width: 13px; height: 13px; border-radius: 3px; border: 1px solid var(--edge); }
.pip-on { background: var(--dark); border-color: var(--dark); }

/* ---- narrow screens ----------------------------------------------------- */

@media (max-width: 900px) {
    .board { grid-template-columns: minmax(0, 1fr); gap: 28px; padding: 22px 20px; }
    .topbar, .footbar { padding-left: 20px; padding-right: 20px; }
    .topbar-side { width: 72px; }
    .wordmark img { height: 52px; }
    .sprite { width: 84px; height: 84px; }
    .card { min-height: 150px; }
}

#blazor-error-ui {
    display: none;
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 1000;
    padding: 12px 20px;
    background: #f7e08a;
    color: #121212;
    font-size: 14px;
}
