/* ============================================================================
   Base: reset, typography, shell, buttons, header, drawer, modal, footer
   ========================================================================== */

*,
*::before,
*::after {
  box-sizing: border-box;
}

* {
  margin: 0;
  padding: 0;
}

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  min-height: 100dvh;
  font-family: var(--font-ui);
  font-size: 16px;
  line-height: 1.5;
  color: var(--ink);
  background-color: var(--page-base);
  background-image:
    radial-gradient(70rem 40rem at 12% -10%, var(--page-glow-a), transparent 60%),
    radial-gradient(60rem 36rem at 92% 8%, var(--page-glow-b), transparent 62%),
    radial-gradient(80rem 50rem at 50% 118%, var(--page-glow-c), transparent 68%);
  background-attachment: fixed;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* Deliberately no `overflow-x: hidden` on <body>. It makes <body> the scrolling
   box, which caps window scrolling short of the real document height (the
   footer ends up unreachable under the fixed keyboard) and breaks position:
   sticky in some engines. Wide content is contained by its own scroller
   instead: see .grid-frame and .example-grid-wrapper. */

img,
svg {
  display: block;
  max-width: 100%;
  height: auto;
}

button,
input,
select,
textarea {
  font: inherit;
  color: inherit;
}

button {
  cursor: pointer;
  background: none;
  border: 0;
}

a {
  color: var(--brand-700);
  text-decoration-color: color-mix(in srgb, var(--brand-700) 35%, transparent);
  text-underline-offset: 2px;
}

a:hover {
  text-decoration-color: currentcolor;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme='light']) a {
    color: var(--brand-300);
  }
}

:root[data-theme='dark'] a {
  color: var(--brand-300);
}

:focus-visible {
  outline: 3px solid var(--focus-ring);
  outline-offset: 2px;
  border-radius: var(--r-xs);
}

::selection {
  background: color-mix(in srgb, var(--brand-400) 45%, transparent);
}

h1,
h2,
h3,
h4 {
  line-height: 1.2;
  text-wrap: balance;
  font-weight: 600;
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

[hidden],
.hidden {
  display: none !important;
}

/* ------------------------------------------------------------------ header */

.site-header {
  position: sticky;
  top: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  height: var(--header-h);
  padding: 0 clamp(0.5rem, 2vw, 1rem);
  background: color-mix(in srgb, var(--surface) 88%, transparent);
  backdrop-filter: saturate(150%) blur(14px);
  -webkit-backdrop-filter: saturate(150%) blur(14px);
  border-bottom: 1px solid color-mix(in srgb, var(--border) 85%, transparent);
  box-shadow: 0 1px 12px rgb(23 33 46 / 6%);
}

.site-header__brand {
  font-family: var(--font-display);
  font-size: 1.35rem;
  line-height: 1;
  color: var(--ink);
  text-decoration: none;
  padding-block: 0.35rem;
  margin-inline-end: auto;
}

/* The temporary way in to a new game. On a narrow phone the label drops and it
   becomes a chain plus a New pill, which still reads as "there is something new
   here" and costs 60px instead of 160px. */
.site-header__fresh {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  margin-inline-end: 0.35rem;
  padding: 0.3rem 0.6rem;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--ink);
  text-decoration: none;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--r-pill);
  white-space: nowrap;
}

.site-header__fresh:hover {
  background: var(--surface-3);
  border-color: var(--border-strong);
}

@media (max-width: 430px) {
  .site-header__fresh-label { display: none; }
}

.site-header__spacer {
  margin-inline-start: auto;
}

.icon-btn {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border-radius: var(--r-sm);
  color: var(--ink-2);
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}

.icon-btn:hover {
  background: var(--surface-2);
  color: var(--ink);
}

.icon-btn svg {
  width: 22px;
  height: 22px;
  fill: none;
  stroke: currentcolor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* theme toggle swaps which glyph is visible */
.theme-toggle .icon-moon {
  display: none;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme='light']) .theme-toggle .icon-sun {
    display: none;
  }

  :root:not([data-theme='light']) .theme-toggle .icon-moon {
    display: block;
  }
}

:root[data-theme='dark'] .theme-toggle .icon-sun {
  display: none;
}

:root[data-theme='dark'] .theme-toggle .icon-moon {
  display: block;
}

/* ------------------------------------------------------------------ drawer */

.drawer-scrim {
  position: fixed;
  inset: 0;
  z-index: 70;
  background: rgb(6 24 29 / 45%);
  backdrop-filter: blur(2px);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--t-med) var(--ease);
}

.drawer-scrim.is-open {
  opacity: 1;
  pointer-events: auto;
}

.drawer {
  position: fixed;
  inset-block: 0;
  inset-inline-start: 0;
  z-index: 80;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  width: min(88vw, 320px);
  padding: 1rem 1rem 1.5rem;
  background: var(--surface);
  border-inline-end: 1px solid var(--border);
  box-shadow: var(--shadow-xl);
  transform: translateX(-102%);
  transition: transform var(--t-med) var(--ease);
  overflow-y: auto;
  overscroll-behavior: contain;
}

.drawer.is-open {
  transform: translateX(0);
}

.drawer__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 0.75rem;
}

.drawer__title {
  font-family: var(--font-display);
  font-size: 1.5rem;
}

.drawer a,
.drawer button.drawer__item {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  padding: 0.7rem 0.75rem;
  border-radius: var(--r-sm);
  color: var(--ink);
  font-weight: 500;
  text-decoration: none;
  text-align: start;
  width: 100%;
  transition: background var(--t-fast) var(--ease);
}

.drawer a:hover,
.drawer button.drawer__item:hover {
  background: var(--surface-2);
}

.drawer a[aria-current='page'] {
  background: var(--surface-2);
  box-shadow: inset 3px 0 0 var(--brand-500);
}

.drawer__emoji {
  font-size: 1.05rem;
  line-height: 1;
  width: 1.4rem;
  text-align: center;
}

.drawer__rule {
  height: 1px;
  background: var(--border);
  margin: 0.75rem 0.25rem;
}

.drawer__socials {
  display: flex;
  gap: 0.5rem;
  padding: 0.5rem 0.75rem 0;
}

.drawer__socials a {
  width: 40px;
  height: 40px;
  padding: 0;
  display: grid;
  place-items: center;
  border-radius: var(--r-pill);
  background: var(--action);
  color: var(--action-ink);
}

.drawer__socials svg {
  width: 19px;
  height: 19px;
  fill: currentcolor;
}

/* ----------------------------------------------------------------- buttons */

.btn {
  --btn-bg: var(--action);
  --btn-ink: var(--action-ink);
  --btn-border: transparent;

  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.45rem;
  min-height: 44px;
  padding: 0.6rem 1.1rem;
  font-size: 0.95rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--btn-ink);
  background: var(--btn-bg);
  border: 1.5px solid var(--btn-border);
  border-radius: var(--r-pill);
  box-shadow: var(--shadow-sm);
  transition: transform var(--t-fast) var(--ease),
    background var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease),
    opacity var(--t-fast) var(--ease);
  white-space: nowrap;
}

.btn:hover:not(:disabled) {
  background: var(--action-hover);
  box-shadow: var(--shadow-md);
}

.btn:active:not(:disabled) {
  transform: translateY(1px);
  box-shadow: var(--shadow-sm);
}

.btn:disabled,
.btn.is-disabled {
  opacity: 0.45;
  cursor: not-allowed;
  box-shadow: none;
}

.btn--ghost {
  --btn-bg: var(--action-ghost-bg);
  --btn-ink: var(--ink);
  --btn-border: var(--action-ghost-border);
}

.btn--ghost:hover:not(:disabled) {
  background: var(--action-ghost-hover);
}

.btn--sm {
  min-height: 36px;
  padding: 0.35rem 0.8rem;
  font-size: 0.85rem;
}

.btn--block {
  width: 100%;
}

/* ------------------------------------------------------------------- modal */

.modal {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: grid;
  place-items: center;
  padding: 1rem;
  background: rgb(6 24 29 / 55%);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  overflow-y: auto;
  overscroll-behavior: contain;
  animation: fade-in var(--t-med) var(--ease) both;
}

.modal__panel {
  position: relative;
  width: min(100%, 440px);
  max-height: calc(100dvh - 2rem);
  overflow-y: auto;
  padding: 2rem 1.5rem 1.5rem;
  text-align: center;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-xl);
  box-shadow: var(--shadow-xl);
  animation: pop-in var(--t-slow) var(--ease-spring) both;
}

.modal__close {
  position: absolute;
  inset-block-start: 0.6rem;
  inset-inline-end: 0.6rem;
}

.modal__title {
  font-family: var(--font-display);
  font-size: clamp(1.35rem, 4.5vw, 1.7rem);
  margin-bottom: 0.25rem;
}

.modal__sub {
  color: var(--ink-2);
  margin-bottom: 1.25rem;
}

/* The line under the play-another button, saying what it will serve. */
.modal__note {
  margin-top: 0.4rem;
  font-size: 0.8rem;
  color: var(--ink-3);
}

/* Two stacked block buttons in a modal need a gap between them. */
.modal__panel .btn--block + .btn--block {
  margin-top: 0.55rem;
}

.modal__links {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  margin-top: 1.25rem;
  padding-top: 1rem;
  border-top: 1px solid var(--border);
}

.modal__links a {
  padding: 0.5rem;
  border-radius: var(--r-sm);
  font-weight: 500;
  text-decoration: none;
}

.modal__links a:hover {
  background: var(--surface-2);
}

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

@keyframes pop-in {
  from {
    opacity: 0;
    transform: translateY(14px) scale(0.94);
  }
}

/* ------------------------------------------------------------------- toast */

.toast-stack {
  position: fixed;
  inset-block-start: calc(var(--header-h) + 0.5rem);
  inset-inline: 0;
  z-index: 120;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.4rem;
  pointer-events: none;
  padding: 0 1rem;
}

.toast {
  max-width: min(92vw, 30rem);
  padding: 0.6rem 1rem;
  font-size: 0.9rem;
  font-weight: 500;
  color: var(--ink-invert);
  background: color-mix(in srgb, var(--ink) 92%, transparent);
  border-radius: var(--r-pill);
  box-shadow: var(--shadow-lg);
  animation: toast-in var(--t-med) var(--ease-spring) both;
}

.toast--warn {
  background: color-mix(in srgb, var(--wrong) 92%, black 8%);
  color: #fff;
}

.toast.is-leaving {
  animation: toast-out var(--t-med) var(--ease) forwards;
}

@keyframes toast-in {
  from {
    opacity: 0;
    transform: translateY(-10px);
  }
}

@keyframes toast-out {
  to {
    opacity: 0;
    transform: translateY(-8px);
  }
}

/* ------------------------------------------------------------------ footer */

.site-footer {
  padding: 2rem 1rem 2.5rem;
  text-align: center;
  font-size: 0.85rem;
  color: color-mix(in srgb, var(--ink) 80%, var(--page-base));
}

.site-footer a {
  color: color-mix(in srgb, var(--ink) 92%, var(--page-base));
  font-weight: 600;
  text-decoration-color: color-mix(in srgb, currentcolor 40%, transparent);
}

.site-footer a:hover {
  color: var(--ink);
}

.site-footer__links {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem 0.9rem;
  justify-content: center;
  margin-bottom: 0.4rem;
}

/* ------------------------------------------------------- reduced transparency
   Some users and some older iOS versions do not handle backdrop-filter well. */

@supports not (backdrop-filter: blur(2px)) {
  .site-header {
    background: var(--surface);
  }

  .modal {
    background: rgb(6 24 29 / 78%);
  }
}

/* ------------------------------------------------------------ reduced motion */

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/*
 * One blank word in the clue: a single line on the baseline, not two dashes.
 *
 * Width is fixed rather than proportional to the hidden word, so the line
 * never leaks its letter count. The em units keep it in step with the clue's
 * clamped font size.
 */
.blank {
  display: inline-block;
  width: 2.2em;
  height: 0.72em;
  margin: 0 0.06em;
  border-bottom: 0.11em solid currentColor;
  border-radius: 0.06em;
  opacity: 0.65;
  vertical-align: baseline;
}


/* ============================================================================
   Ad placements.

   Two rules run this whole block, both learned the hard way on this site:

   1. A slot that has nothing in it must take up no space. Reserved-then-unfilled
      space is exactly what the "big blank spaces on mobile" report was, and an
      empty labelled box looks more broken than no box at all. So every slot
      collapses while it is empty, and the Advertisement label only appears once
      something has actually been injected into it.

   2. Nothing here may move the game. The board is a fixed 860px column, so the
      side rails are positioned in the gutter beside it and only exist at widths
      where the gutter is genuinely wide enough. They never reflow the board,
      and they are not there at all on a phone.

   Gutter measurements, board at 860px: 1280px viewport leaves 210px a side,
   1440px leaves 290px, 1600px leaves 370px, 1920px leaves 530px. Hence a
   160-wide rail from 1240px and a 300-wide rail from 1560px.
   ========================================================================== */

.ad-slot:empty {
  display: none;
}

/* Off by default. The media queries below turn the rails on where they fit. */
.ad-rail {
  display: none;
}

.ad-rail__label,
.ad-inline__label {
  display: none;
  margin-bottom: 0.3rem;
  font-size: 0.6rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  text-align: center;
  color: var(--ink-3);
}

/* Only label a slot that has something in it. */
.ad-rail:has(.ad-slot:not(:empty)) .ad-rail__label,
.ad-inline:has(.ad-slot:not(:empty)) .ad-inline__label {
  display: block;
}

/* In-content, below the board. This is the one that works on a phone, which is
   where most of the traffic is. */
.ad-inline {
  display: flex;
  flex-direction: column;
  align-items: center;
  margin: clamp(1rem, 3vw, 1.75rem) auto 0;
  padding-inline: 0.5rem;
}

.ad-inline:not(:has(.ad-slot:not(:empty))) {
  margin: 0;
}

@media (min-width: 1240px) and (min-height: 700px) {
  .ad-rail {
    display: block;
    position: fixed;
    inset-block-start: calc(var(--header-h) + 1.25rem);
    width: 160px;
    z-index: 20;
  }

  /* 430px is half the board. Then a gutter of its own, then the rail. */
  .ad-rail--left { inset-inline-start: calc(50% - 430px - 0.75rem - 160px); }
  .ad-rail--right { inset-inline-start: calc(50% + 430px + 0.75rem); }
}

@media (min-width: 1560px) and (min-height: 780px) {
  .ad-rail { width: 300px; }
  .ad-rail--left { inset-inline-start: calc(50% - 430px - 1rem - 300px); }
  .ad-rail--right { inset-inline-start: calc(50% + 430px + 1rem); }
}

/* z-index 20 on the rails is deliberate and low: the header sits at 60, the
   drawer at 70 and 80, modals and toasts above those. A rail is decoration
   around the game and must never cover a control. Do not raise it. */
