/* Shared design tokens, reset, and reusable components for all games.
   See README.md "Design guidelines" before changing any of this. */

:root {
    --bg: oklch(0.3073 0.0106 253.99);
    --die: oklch(0.3838 0.0143 252.26);
    --die-kept: oklch(from var(--die) calc(l + 0.06) c h);
    --ink: oklch(0.624 0.0035 247.86);
    --bar: oklch(0.5969 0.0442 248.57);
    --die-border: oklch(from var(--die) calc(l - 0.10) c h);
    --die-shadow: oklch(from var(--bg) calc(l - 0.28) c h / 0.25);
    --btn: oklch(from var(--die) calc(l - 0.06) c h);
    --btn-press: oklch(from var(--die) calc(l - 0.14) c h);
    --accent: oklch(0.6969 0.1442 248.57);
    --warn: oklch(0.66 0.06 55);
}

/* Muted light theme, opted into via the footer toggle (persisted in
   localStorage, applied early in <head> to avoid a flash of the wrong
   theme). Only the base tokens are overridden -- --die-kept, --die-border,
   --die-shadow, --btn, and --btn-press are all defined in terms of --die/--bg
   via oklch(from ...) above, so they recompute automatically. The one
   exception is --die-kept, which normally lightens the die to stand out
   against a dark background; near the top of the lightness range that
   would wash out against the light background instead, so it's flipped to
   darken.
   --bg/--die/--ink also carry a faint warm hue (85, a soft beige) instead of
   reusing --bar/--accent's cool blue -- a plain gray "paper" read flatter
   and more sterile than the dark theme's tinted charcoal, and a warm neutral
   against the blue heading/accent color is a more deliberate pairing than
   an accidental one.
   The lightness values themselves were picked by matching WCAG contrast
   ratios against the *dark* theme's equivalent pairs (measured, not just
   eyeballed from the OKLCH numbers -- lightness and perceptual contrast
   don't move together linearly, especially near the light end of the
   scale). An earlier version mirrored the dark theme's lightness gaps
   directly and ended up with ~10:1 text contrast -- far higher than the
   dark theme's own ~3.7:1, and exactly the "too much contrast" this whole
   palette exists to avoid. */
:root[data-theme="light"] {
    --bg: oklch(0.95 0.012 85);
    --die: oklch(0.86 0.016 85);
    --die-kept: oklch(from var(--die) calc(l - 0.10) c h);
    --ink: oklch(0.54 0.01 85);
    --bar: oklch(0.60 0.05 248.57);
    --accent: oklch(0.51 0.10 248.57);
    --warn: oklch(0.55 0.07 55);
}

*,
*::before,
*::after {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
    animation: none !important;
    transition: none !important;
}

/* Every browser's default focus ring is a bright, saturated blue -- exactly
   the kind of jarring accent this whole palette is built to avoid. --warn
   (amber) reads clearly against the site's blue-gray tones without adding a
   new color, and isn't already the "this is a link/heading" blue that
   --accent/--bar carry elsewhere. input:focus-visible below overrides this
   back to none -- that's an existing, deliberate choice for text inputs,
   left as-is. */
:focus-visible {
    outline: 2px solid var(--warn);
    outline-offset: 2px;
}

html,
body {
    /* Stop horizontal drags near the edges from being interpreted as a
       back/forward navigation swipe -- without this, an overshot drag
       rubber-bands (a visible wiggle) or navigates away mid-game. */
    overscroll-behavior-x: none;
}

body {
    background-color: var(--bg);
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: clamp(16px, 3vmin, 40px);
    padding: 16px;
    overflow: hidden;
    font-family: 'Lato', 'Open Sans', sans-serif, serif;
}

/* For pages whose content can grow past the viewport (a scorecard, a word
   list) -- content starts below the fixed button bar instead of centering,
   and the page is allowed to scroll instead of clipping. "safe center"
   keeps short content (e.g. an empty setup screen) looking balanced in the
   middle of the viewport like the non-scrollable default, but falls back
   to top-aligned the moment content is too tall to center without clipping
   the top of it above the fixed button bar. */
body.scrollable {
    justify-content: safe center;
    gap: clamp(12px, 2.2vmin, 26px);
    padding-top: clamp(80px, 10vmin, 96px);
    /* env(safe-area-inset-bottom) is the iOS home-indicator strip when
       installed as a standalone PWA (see viewport-fit=cover in <head>,
       which is what makes the page's own background extend under it
       instead of the OS showing a plain bar there) -- adding it here
       keeps the footer clear of that strip instead of sitting flush
       against it. Falls back to 0px on anything that doesn't support it. */
    padding-bottom: calc(clamp(48px, 7vmin, 72px) + env(safe-area-inset-bottom, 0px));
    /* Vertical growth is fine and expected here (a scorecard, a word list);
       horizontal growth never is -- pages should never gain a sideways
       scrollbar, so that axis stays clipped even where content is allowed
       to grow taller than the viewport. */
    overflow-x: hidden;
    overflow-y: visible;
}

.hidden {
    display: none !important;
}

.button-bar {
    position: fixed;
    display: flex;
    justify-content: space-between;
    right: 0;
    top: 0;
    left: 0;
    /* The extra top padding clears the notch/Dynamic Island area the same
       way .site-footer's bottom padding clears the home indicator. */
    padding: calc(1rem + env(safe-area-inset-top, 0px)) 1rem 1rem 1rem;
    z-index: 2;
    /* Solid, same as the page background -- see .site-footer for why: on a
       page taller than the viewport, this fixed bar sits over whatever's
       scrolled up beneath it, and without an opaque background that
       content showed through and tangled visually with the buttons. */
    background: var(--bg);
    border-bottom: 1px solid var(--die-border);
}

/* A page with three button-bar buttons at once (Home, End Game, New Game --
   see boggle.html/yahtzee.html mid-game) can be a few pixels too wide for
   the narrowest phones at the button's normal minimum size, which used to
   force a label to wrap onto a second line. Shrinking a little further
   here keeps every label on one line instead, without needing to wrap the
   whole bar onto two rows (which would eat into the space reserved above
   it for the fixed bar -- see body.scrollable). */
@media (max-width: 360px) {
    .button-bar button {
        padding-left: clamp(8px, 2vmin, 18px);
        padding-right: clamp(8px, 2vmin, 18px);
        font-size: clamp(11px, 1.9vmin, 20px);
    }
}

/* Boggle's mid-game bar can reach four buttons at once (Home, Rules, End
   Game, New Game). That's one more than the 360px rule above was ever
   tuned for, and needs the help starting at a noticeably wider screen --
   but only while there really are four of them. Scoped to a class toggled
   in JS (updateButtonBarDensity()) rather than firing for every
   button-bar on every page at this width, which shrank a lone Home button
   (or Home + Rules) for no reason on almost every phone. */
@media (max-width: 480px) {
    .button-bar.many-buttons button {
        padding-left: clamp(6px, 1.6vmin, 18px);
        padding-right: clamp(6px, 1.6vmin, 18px);
        font-size: clamp(10px, 1.7vmin, 20px);
    }

    .button-bar.many-buttons .left,
    .button-bar.many-buttons .right {
        gap: 6px;
    }
}

.button-bar .left,
.button-bar .right {
    display: flex;
    gap: 10px;
}

.status,
.heading {
    color: var(--bar);
    font-size: clamp(20px, 2.6vmin, 30px);
    font-weight: bold;
    letter-spacing: 0.08em;
    user-select: none;
    text-align: center;
}

.controls {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: clamp(12px, 2.4vmin, 28px);
}

button {
    padding: clamp(7px, 1.1vmin, 13px) clamp(18px, 2.8vmin, 34px);
    font-size: clamp(13px, 1.9vmin, 20px);
    font-weight: bold;
    background: var(--btn);
    color: var(--ink);
    border: 2px solid var(--die-border);
    border-radius: clamp(4px, 0.7vmin, 9px);
    cursor: pointer;
    letter-spacing: 0.06em;
    /* A button's label should never break onto a second line -- on a
       narrow phone with three button-bar buttons ("Home", "End Game",
       "New Game"), that mid-word wrap made the bar noticeably (and
       unevenly) taller. See the narrow-width rule below, which shrinks
       button-bar buttons instead so all three still fit on one line. */
    white-space: nowrap;
}

button:active {
    background: var(--btn-press);
}

button:disabled {
    opacity: 0.4;
    cursor: not-allowed;
}

input[type="number"],
input[type="text"] {
    padding: clamp(6px, 1vmin, 11px) clamp(10px, 1.6vmin, 16px);
    font-size: clamp(13px, 1.9vmin, 18px);
    background: var(--btn);
    color: var(--ink);
    border: 2px solid var(--die-border);
    border-radius: clamp(4px, 0.7vmin, 9px);
}

input:focus-visible {
    outline: none;
}

/* ---- Site footer (About + support link, shown on every page) ---- */
.site-footer {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    display: flex;
    justify-content: space-between;
    gap: clamp(16px, 3vmin, 32px);
    /* The extra bottom padding is the iOS home-indicator strip, present
       when this is installed as a standalone PWA (see viewport-fit=cover
       in <head>). Without it, the footer's About/Support/Theme buttons
       sat flush against that strip -- easy to catch the indicator's own
       swipe gesture instead of the button underneath it. */
    padding: 0.85rem 1rem calc(0.85rem + env(safe-area-inset-bottom, 0px)) 1rem;
    z-index: 2;
    /* Solid, same as the page background -- on a page whose content grows
       taller than the viewport (a long word list), this fixed bar sits
       over whatever's scrolled beneath it. Without an opaque background,
       that content showed through and visually tangled with the footer
       links. */
    background: var(--bg);
    border-top: 1px solid var(--die-border);
}

.site-footer a,
.theme-toggle {
    color: var(--ink);
    opacity: 0.65;
    font-size: clamp(11px, 1.5vmin, 14px);
    letter-spacing: 0.04em;
    text-decoration: none;
}

.site-footer a:hover,
.site-footer a:focus-visible,
.theme-toggle:hover,
.theme-toggle:focus-visible {
    opacity: 1;
}

/* Sits in the flow alongside About/Ko-fi (see .site-footer's
   justify-content: space-between) rather than off on its own, so the three
   links read as one evenly-spaced row -- About on the left, Ko-fi in the
   middle, theme toggle on the right -- instead of two links huddled in the
   center with this one stranded apart from them. */
.theme-toggle {
    background: none;
    border: none;
    padding: 0;
    border-radius: 0;
    font-weight: normal;
    cursor: pointer;
}

.theme-toggle:active {
    background: none;
}

/* ---- Rules dialog, opened via the "Rules" button that sits in the
   button-bar's .left group (next to Home) on every game page. Living in
   the button-bar rather than the setup screen means it's reachable in
   both the setup *and* in-game states with a single instance -- important
   for Boggle in particular, whose in-game layout on narrow screens is
   already tuned to a tight vertical budget (see boggle.html's own
   <style>), which an inline expandable block would compete with. A native
   <dialog> overlays instead of pushing that layout around, needs no
   script for Escape-to-close, and (like <details> elsewhere on this site)
   doesn't need any authored open/close animation to feel non-jarring. ---- */
dialog.rules-dialog {
    /* The global *, *::before, *::after reset above zeroes every element's
       margin, which is also what the browser's own UA stylesheet relies on
       (margin: auto with inset: 0) to center a <dialog>. Restoring it here,
       on a selector specific enough to win over that reset, is what keeps
       this centered instead of pinned to the top-left corner. */
    margin: auto;
    background: var(--die);
    color: var(--ink);
    border: 2px solid var(--die-border);
    border-radius: 10px;
    width: min(94vw, 560px);
    max-width: min(94vw, 560px);
    max-height: min(88vh, 720px);
    overflow-y: auto;
    padding: clamp(18px, 3vmin, 30px);
}

/* The dialog itself is given programmatic focus when it opens (see each
   page's rules-btn click handler) purely so Tab has somewhere to start
   from -- it isn't a control in its own right, and already has its own
   visible border, so a focus outline around the entire box would just be
   a redundant second border. Buttons and links inside the dialog still
   get the site-wide :focus-visible outline normally. */
dialog.rules-dialog:focus-visible {
    outline: none;
}

/* Belt-and-suspenders alongside the *, *::before, *::after reset above --
   ::backdrop is a generated box the universal selector doesn't reach, and
   some browsers give a dialog's backdrop its own default open/close
   transition. */
dialog.rules-dialog::backdrop {
    background: oklch(from var(--bg) calc(l - 0.2) c h / 0.6);
    animation: none !important;
    transition: none !important;
}

.rules-dialog-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 8px;
}

.rules-dialog-title {
    color: var(--bar);
    font-weight: bold;
    letter-spacing: 0.06em;
    font-size: clamp(16px, 2.2vmin, 20px);
}

.rules-dialog-close {
    background: none;
    border: none;
    padding: 2px 8px;
    color: var(--ink);
    font-size: clamp(18px, 2.4vmin, 22px);
    font-weight: bold;
    line-height: 1;
    cursor: pointer;
    border-radius: 6px;
}

.rules-dialog-close:hover,
.rules-dialog-close:focus-visible {
    background: var(--btn);
}

.rules-dialog h3 {
    color: var(--bar);
    font-size: clamp(14px, 1.8vmin, 17px);
    letter-spacing: 0.05em;
    margin-top: 14px;
    margin-bottom: 8px;
}

.rules-dialog h3:first-of-type {
    margin-top: 0;
}

.rules-dialog ul {
    padding-left: 1.2em;
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.rules-dialog li {
    color: var(--ink);
    font-size: clamp(13px, 1.6vmin, 15px);
    line-height: 1.55;
}

/* ---- Setup panel (used by any game with a pre-game options screen) ---- */
.setup {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 20px;
    background: var(--die);
    border: 2px solid var(--die-border);
    border-radius: 10px;
    padding: clamp(20px, 3vmin, 36px);
    width: min(94vw, 460px);
}
