:root {
  --directional-text: 0.9rem;
  --bg: #D4DDC3;
  --tile: #fbf8f2;
  --ink: #1a1a1a;
  --hairline: rgba(26, 26, 26, 0.15);
  --accent: #3d5622;
  --accent-hover: #2d4018;
  --font-serif: "Roboto Serif", "Iowan Old Style", "Palatino Linotype", Palatino, Cambria, Georgia, serif;
  --font-sans: "Roboto", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}

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

html, body { overscroll-behavior-y: none; }

body {
  font-family: var(--font-sans);
  background: var(--bg);
}

.app {
  width: 100%;
  max-width: 360px;
  margin: 0 auto;
  padding: 12px 16px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* Thin back-link to the portal landing page. See
   back-link-to-portal-design-spec.md for the full visual spec and rationale.
   Rendered outside .app so it spans the full viewport width and sits flush
   at the very top of the page, unconstrained by .app's width cap/padding. */
.back-to-portal-bar {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.3rem;
  width: 100%;
  padding: 6px 0;
  background: var(--tile);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: 0.85rem;
  text-decoration: none;
  border-bottom: 1px solid var(--hairline);
}

.back-to-portal-bar svg {
  position: relative;
  top: 0;
}

.back-to-portal-bar:focus { outline: none; }
.back-to-portal-bar:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--tile), 0 0 0 4px var(--ink);
}

/* Masthead — wordmark | score | instructions, all on one row */
.masthead {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  column-gap: 0.5rem;
}

.masthead .wordmark { justify-self: start; }
.masthead .score-display { justify-self: center; }
.masthead .instructions-button { justify-self: end; }

.wordmark {
  font-family: var(--font-serif);
  font-size: 1.1rem;
  font-weight: 400;
  letter-spacing: -0.01em;
  color: var(--ink);
}

.wordmark-btn {
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  color: inherit;
  letter-spacing: inherit;
  cursor: default;
}
.wordmark-btn:not(:disabled) {
  cursor: pointer;
}
.wordmark-btn:focus { outline: none; }
.wordmark-btn:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--bg), 0 0 0 4px var(--ink);
  border-radius: 2px;
}

.wordmark-tm {
  font-size: 0.45em;
  vertical-align: super;
  margin-left: 0.05em;
  font-weight: 400;
  letter-spacing: 0;
}

/* Instructions trigger — small "?" circle in the masthead */
.instructions-button {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  border: 2px solid #1f2937;
  background: transparent;
  color: #1f2937;
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 700;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  cursor: pointer;
  transition: color 0.15s ease, border-color 0.15s ease;
}
.instructions-button:hover {
  border-color: #000;
  color: #000;
}
.instructions-button:focus { outline: none; }
.instructions-button:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--bg), 0 0 0 4px var(--ink);
}

/* Instructions modal */
@keyframes modal-backdrop-in {
  from { opacity: 0; }
  to { opacity: 1; }
}
@keyframes modal-panel-in {
  from { opacity: 0; transform: scale(0.9); }
  to { opacity: 1; transform: scale(1); }
}

.modal-backdrop {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.5);
  animation: modal-backdrop-in 0.15s ease-out;
}

.modal-panel {
  position: relative;
  background: #fff;
  border-radius: 0.75rem;
  box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1);
  max-width: 24rem;
  width: 100%;
  margin: 0 1rem;
  padding: 1.5rem;
  font-family: var(--font-sans);
  font-size: var(--directional-text);
  animation: modal-panel-in 0.15s ease-out;
}

.instructions-close {
  position: absolute;
  top: 0.75rem;
  right: 0.75rem;
  background: transparent;
  border: 0;
  padding: 0;
  color: #6b7280;
  font-size: 1.25rem;
  line-height: 1;
  cursor: pointer;
  transition: color 0.15s ease;
}
.instructions-close:hover {
  color: #374151;
}

.instructions-title {
  font-size: 1.125rem;
  font-weight: 600;
  color: #111827;
  margin-bottom: 0.75rem;
}
.instructions-title strong {
  font-weight: 600;
}
.instructions-tm {
  font-weight: 400;
}

.instructions-body {
  font-size: var(--directional-text);
  color: #374151;
  line-height: 1.625;
}
.instructions-body p + p,
.instructions-body ul,
.instructions-body ol {
  margin-top: 0.5rem;
}
.instructions-body ul,
.instructions-body ol {
  padding-left: 1.25rem;
}
.instructions-body ul { list-style: disc; }
.instructions-body ol { list-style: decimal; }

.instructions-body .a11y-notes {
  margin-top: 0.75rem;
}
.instructions-body .a11y-notes summary {
  cursor: pointer;
  font-weight: 600;
}
.instructions-body .a11y-notes dl {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.25rem 0.75rem;
  margin: 0.5rem 0 0;
}
.instructions-body .a11y-notes dt {
  font-weight: 600;
}
.instructions-body .a11y-notes dd {
  margin: 0;
}
.instructions-body .a11y-notes kbd {
  font: inherit;
  font-size: 0.8em;
  padding: 0 0.3em;
  border: 1px solid currentColor;
  border-radius: 3px;
  white-space: nowrap;
}

.instructions-footer {
  margin-top: 1rem;
  font-size: 0.75rem;
  color: #6b7280;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.instructions-feedback {
  color: #2563eb;
  text-decoration: underline;
}

.instructions-feedback:hover {
  color: #1e40af;
}

.loading {
  font-family: var(--font-sans);
  font-size: 1rem;
  font-style: italic;
  color: #666;
  text-align: center;
}

.app-error {
  min-height: 100vh;
  justify-content: center;
}

.load-error {
  text-align: center;
  padding: 0 16px;
}

.play-again {
  display: block;
  margin: 24px auto 0;
  padding: 10px 24px;
  font-family: var(--font-sans);
  font-size: 1rem;
  font-weight: 500;
  color: #fff;
  background: var(--accent);
  border: 0;
  border-radius: 8px;
  cursor: pointer;
  transition: background 0.15s ease;
}
.play-again:hover {
  background: var(--accent-hover);
}
.play-again:focus { outline: none; }
.play-again:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 3px;
}

.share-button {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  margin: 20px auto 0;
  padding: 8px 18px;
  font-family: var(--font-sans);
  font-size: 0.9rem;
  font-weight: 500;
  color: var(--ink);
  background: transparent;
  border: 1.5px solid var(--ink);
  border-radius: 999px;
  cursor: pointer;
  opacity: 0.85;
  transition: opacity 0.15s ease;
}
.share-button:hover { opacity: 1; }
.share-button:focus { outline: none; }
.share-button:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 3px;
}

.share-modal-text {
  display: block;
  width: 240px;
  max-width: 100%;
  padding: 8px 10px;
  font-family: var(--font-sans);
  font-size: 0.85rem;
  color: var(--ink);
  background: var(--bg);
  border: 1px solid var(--hairline);
  border-radius: 6px;
}

/* Clipboard-copy confirmation — a transient status message, not a modal
   (see spec: doesn't need focus-trap machinery). */
.toast {
  position: fixed;
  left: 50%;
  bottom: 24px;
  transform: translateX(-50%);
  padding: 10px 20px;
  font-family: var(--font-sans);
  font-size: 0.85rem;
  color: var(--tile);
  background: var(--ink);
  border-radius: 999px;
  z-index: 200;
  pointer-events: none;
}

/* Auto-expand / auto-end notice */
.auto-notice {
  text-align: center;
  font-family: var(--font-sans);
  font-style: italic;
  font-size: 0.95rem;
  color: var(--accent);
  padding: 4px 8px;
}
.auto-notice.end {
  margin-top: 4px;
}

.percentile-notice {
  text-align: center;
  font-family: var(--font-sans);
  font-size: 0.95rem;
  color: var(--accent);
  padding: 0 8px;
  margin-top: 1rem;
}
.percentile-notice strong {
  font-size: calc(1em + 1pt);
}

/* Score */
.score-display {
  font-family: var(--font-sans);
  font-size: 2rem;
  font-weight: 400;
  text-align: center;
  color: var(--ink);
  letter-spacing: 0.03em;
  transform-origin: center;
  transition: transform 0.5s ease;
}

/* Game-over transition — playing UI fades, then unmounts; score grows. */
.app.ended .grid,
.app.ended .grid-meta-strip,
.app.ended .assembly-area,
.app.ended .controls {
  opacity: 0;
  transition: opacity 0.35s ease;
  pointer-events: none;
}

/* Score grows once the playing UI has unmounted, in sync with the FLIP into
   the column layout. Use transform (not font-size) so the masthead's layout
   height stays constant — otherwise the growing score would push the FoundWords
   list down throughout the transition, after the FLIP captured its target. */
/* The score is already the tallest item in the masthead row (2rem vs. the
   wordmark's 1.1rem), so it fills the row regardless of align-items — no
   need to touch the masthead's own alignment, which would otherwise also
   shift the wordmark and instructions button. translateY (not padding-top)
   so the masthead's layout height stays constant — otherwise the growing
   masthead would push the FoundWords list down after the FLIP captured its
   target. */
.app.ended.ui-gone .score-display {
  transform: translateY(24px) scale(1.5);
}
/* Breathing room beneath the enlarged score on the game-over screen. */
.app.ended.ui-gone main {
  margin-top: 16px;
}

.cross-promo-caption {
  width: 100%;
  margin-top: 36px;
  font-family: var(--font-sans);
  font-size: 0.875rem;
  color: var(--accent);
  text-align: center;
}

/* Each link is only as wide as it needs to be, not the row's full width — so
   a short tagline wraps within that narrower box and stays centered under
   its icon. One link's width matches its icon's width exactly, so it sits
   flush with the row's outer edge; the other link is wider than its icon
   (see its own comment below), so it sits a bit inset from the edge.
   `justify-content: space-between` anchors both to the row's outer edges
   rather than centering them within the full row width. */
.cross-promo-row {
  width: 100%;
  display: flex;
  justify-content: space-between;
  margin-top: 12px;
}

.cross-promo-link {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  text-align: center;
  text-decoration: none;
}
.cross-promo-link:focus { outline: none; }
.cross-promo-link:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 3px;
  border-radius: 2px;
}

/* One wordmark is a single row of tiles; the other is a staircase of the
   same tile unit across three rows. Sizing both icons to the same height
   would make the single-row icon's tiles dwarf the staircase icon's, so
   instead each is sized so the staircase icon's middle "U N G" row of tiles
   (278/908 of its image height) renders at the same height as the
   single-row icon's tile row (278/317 of its image height) — chosen at a
   32px height for the single-row icon, which is why the staircase icon ends
   up taller overall (92px) despite matching tile size.
   The single-row icon has no such middle row — its one row of tiles sits
   near the very top of its image (starting ~2px in) — so top-aligning both
   links (the row's default behavior) leaves the single-row icon's tiles
   level with the staircase icon's *top* tile, not its "U N G" row. Nudging
   the single-row link down by the "U N G" row's own offset (~30px, minus
   the single-row icon's ~2px of built-in top padding) brings the two tile
   rows level. */
.cross-promo-link--threads { width: 164px; margin-top: 30px; }
/* 140px (not the 72px icon width) so "Find the 5 words that form a ladder"
   wraps across ~2 lines instead of 4 — the icon, narrower than this box,
   centers within it via .cross-promo-link's align-items: center, same as the
   tagline below it. The pair sits a bit inset from the row's left edge as a
   result, but reads as one centered unit instead of the icon hugging the
   edge while the wider tagline sits off-center under it. */
.cross-promo-link--rungs { width: 140px; }

.cross-promo-icon {
  display: block;
}
.cross-promo-icon--threads { width: 164px; height: 32px; }
.cross-promo-icon--rungs { width: 72px; height: 92px; }

.cross-promo-tagline {
  width: 100%;
  margin: 0;
  text-align: center;
  font-family: var(--font-sans);
  font-size: 0.9rem;
  color: var(--accent);
}

/* Grid — fixed height reserves space for the optional 4th row at top */
.grid-wrapper {
  position: relative;
}

.bonus-popup {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  background: var(--ink);
  color: var(--tile);
  font-family: var(--font-sans);
  font-weight: 400;
  font-size: 1.4rem;
  padding: 0.6em 1.2em;
  border-radius: 8px;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
  pointer-events: none;
  z-index: 10;
  white-space: nowrap;
}

.grid {
  display: grid;
  grid-template-columns: repeat(3, 64px);
  gap: 8px;
  height: 280px; /* 4 × 64px rows + 3 × 8px gaps */
  align-content: end;
  justify-content: center;
  overflow: hidden;
}

button.fragment {
  height: 64px;
  font-family: var(--font-serif);
  font-size: 1.3rem;
  font-weight: 500;
  letter-spacing: 0.05em;
  color: var(--ink);
  border: 1px solid var(--hairline);
  border-radius: 0;
  background: var(--tile);
  cursor: pointer;
  transition: background 0.1s, color 0.1s, border-color 0.1s;
  touch-action: none; /* let pointer drag fire cleanly; browser no longer hijacks as scroll */
  -webkit-tap-highlight-color: transparent;
  -webkit-user-select: none;
  user-select: none;
}

@media (hover: hover) {
  button.fragment:hover {
    background: #efece6;
  }
}

button.fragment:focus { outline: none; }
button.fragment:focus-visible {
  outline: 1.5px solid rgba(26, 26, 26, 0.55);
  outline-offset: -3px;
}

/* Lift-and-place: a third distinct state. Default is cream+ink, selected is
   ink+cream, lifted is accent+cream. The fill change persists regardless of
   focus so keyboard users can navigate away and come back. */
button.fragment.lifted {
  background: var(--accent);
  color: var(--tile);
  border-color: var(--accent);
}
button.fragment.lifted:focus-visible {
  outline: 1.5px solid rgba(251, 248, 242, 0.7);
  outline-offset: -3px;
}
/* Selected + lifted: keep the ink fill (so assembly membership stays legible)
   but add a thick accent ring around it to mark the lift. */
button.fragment.selected.lifted,
button.fragment.selected.lifted:focus-visible {
  background: var(--ink);
  color: var(--tile);
  border-color: var(--ink);
  outline: 3px solid var(--accent);
  outline-offset: 2px;
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
button.fragment.selected:focus-visible {
  outline: none;
}

button.fragment.selected {
  background: #495F4A;
  color: var(--tile);
  border-color: #495F4A;
}

.fragment.empty {
  height: 64px;
}

/* Grid-meta strip — dots cluster + hint button, centered as a tight pair */
.grid-meta-strip {
  display: grid;
  grid-template-columns: repeat(3, 64px);
  gap: 8px;
  justify-content: center;
  align-items: center;
  margin-top: 8px;
}

.bullseye-button { justify-self: end; }
.words-left-indicator { justify-self: center; }
.hint-button { justify-self: start; }

/* Reserve indicator — quiet row of dots beneath the grid */
.words-left-indicator {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  justify-content: center;
  align-items: center;
  min-height: 8px;
  font-family: var(--font-sans);
  font-size: var(--directional-text);
}

/* Shared styling for quiet icon buttons (hint, bullseye) */
.icon-button {
  background: none;
  border: none;
  cursor: pointer;
  padding: 2px;
  color: var(--ink);
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0.55;
  transition: opacity 0.15s;
  -webkit-tap-highlight-color: transparent;
}

@media (hover: hover) {
  .icon-button:hover { opacity: 1; }
}

.hint-glyph {
  font-family: var(--font-sans);
  font-size: var(--directional-text);
  font-weight: 700;
}

/* Hint modal */
.info-modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.4);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 100;
  padding: 16px;
}

.info-modal {
  background: var(--tile);
  padding: 32px 32px 24px;
  border-radius: 8px;
  width: fit-content;
  position: relative;
  font-family: var(--font-sans);
  font-size: var(--directional-text);
  color: var(--ink);
}

.info-modal-close {
  position: absolute;
  top: 6px;
  right: 6px;
  background: none;
  border: none;
  cursor: pointer;
  padding: 6px;
  color: var(--ink);
  opacity: 0.6;
  display: flex;
  align-items: center;
  justify-content: center;
  -webkit-tap-highlight-color: transparent;
}

@media (hover: hover) {
  .info-modal-close:hover { opacity: 1; }
}

.hint-content {
  text-align: center;
}


.words-left-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--ink);
  opacity: 0.25;
}

/* Assembly area — holds the assembled word and its score preview */
.assembly-area {
  min-height: 56px;
  padding: 8px 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
  width: max-content;
  min-width: 208px; /* matches grid width: 3 × 64px + 2 × 8px gap */
  max-width: 100%;
  margin: 0 auto;
}

.word-display {
  font-family: var(--font-serif);
  font-size: 1.6rem;
  font-weight: 500;
  letter-spacing: 0.05em;
  color: var(--ink);
  line-height: 1.8rem;
  min-height: 1.8rem;
  text-align: center;
}

button.word-display {
  display: block;
  width: 100%;
  border: none;
  padding: 0;
  background: transparent;
  cursor: pointer;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
  transition: color 0.15s;
}

button.word-display.valid {
  color: var(--accent);
}

@media (hover: hover) {
  button.word-display.valid:hover {
    color: var(--accent-hover);
  }
}

.word-score-preview {
  font-family: var(--font-sans);
  font-weight: 400;
  font-size: var(--directional-text);
  text-align: center;
  color: var(--ink);
  line-height: 1.4rem;
  min-height: 1.4rem;
}

/* Found-words row — accrues during play, below the assembly area.
   Single horizontal line; overflow scrolls. align-self lets the row shrink
   to its content (so a few words sit centred), max-width caps it at the
   parent so overflow can scroll; the edge-mask hints at scrollability.
   justify-content uses the `safe` keyword so that once the row overflows,
   it falls back to start-alignment — plain `center` would centre the
   overflow itself, leaving the start of the list (the earliest word)
   unreachable by scrolling in Chrome/WebKit. */
.found-words-row {
  list-style: none;
  display: flex;
  flex-wrap: nowrap;
  justify-content: safe center;
  gap: 6px 22px;
  align-self: center;
  max-width: 100%;
  padding: 0 12px 6px;
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: thin;
  scrollbar-color: rgba(26, 26, 26, 0.3) transparent;
  -webkit-mask-image: linear-gradient(to right, transparent 0, #000 12px, #000 calc(100% - 12px), transparent 100%);
  mask-image: linear-gradient(to right, transparent 0, #000 12px, #000 calc(100% - 12px), transparent 100%);
  font-family: var(--font-serif);
  font-size: var(--directional-text);
  letter-spacing: 0.03em;
  color: var(--ink);
}
.found-words-row::-webkit-scrollbar { height: 8px; }
.found-words-row::-webkit-scrollbar-track { background: transparent; }
.found-words-row::-webkit-scrollbar-thumb {
  background: rgba(26, 26, 26, 0.3);
  border-radius: 4px;
}

.found-words-row li {
  position: relative;
  user-select: none;
  -webkit-user-select: none;
}

/* Middle-dot separator sits absolutely in the column gap so its presence
   doesn't change the item's width — otherwise toggling .row-start (below)
   would feed back into flex-wrap and oscillate the layout.
   JS adds .row-start to the first item of each wrapped row to suppress
   a leading dot there. */
.found-words-row li:not(:first-child)::before {
  content: '\00B7';
  position: absolute;
  left: -11px;
  top: 50%;
  transform: translate(-50%, -50%);
  color: var(--ink);
  font-weight: 700;
}

.found-words-row li.row-start::before {
  content: none;
}

.found-words-row.column {
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 0;
  max-width: none;
  overflow-x: visible;
  overflow-y: visible;
  -webkit-mask-image: none;
  mask-image: none;
}

.found-words-row.column li::before {
  content: none;
}

.found-words-heading {
  font-family: var(--font-sans);
  font-size: 1rem;
  text-align: center;
  color: var(--ink);
  margin: 25.6px 0 6px;
}

/* Expand-row confirm modal */
.expand-row-modal {
  max-width: 18rem;
}

.expand-row-message {
  font-family: var(--font-sans);
  font-size: 0.95rem;
  color: #374151;
  line-height: 1.55;
  margin-bottom: 1.25rem;
  text-align: center;
}

.expand-row-buttons {
  display: flex;
  gap: 0.625rem;
  justify-content: center;
}

.expand-row-btn {
  padding: 0.45rem 1.1rem;
  border-radius: 6px;
  font-family: var(--font-sans);
  font-size: var(--directional-text);
  font-weight: 400;
  cursor: pointer;
  border: 2px solid var(--ink);
  background: transparent;
  color: var(--ink);
  transition: background 0.15s;
}

@media (hover: hover) {
  .expand-row-btn:hover {
    background: rgba(26, 26, 26, 0.08);
  }
}

/* Controls */
.controls {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 16px;
  margin-bottom: 16px;
}

.controls-secondary {
  display: flex;
  align-items: center;
  gap: 8px;
  justify-content: center;
}

.controls-secondary > button {
  flex: 0 0 64px;
}

.controls-stack {
  display: flex;
  flex-direction: column;
  gap: 8px;
  flex: 0 0 64px;
}

.controls button {
  padding: 10px 8px;
  font-family: var(--font-sans);
  font-size: var(--directional-text);
  font-weight: 400;
  border: 2px solid var(--ink);
  border-radius: 8px;
  background: var(--ink);
  color: var(--tile);
  cursor: pointer;
  transition: opacity 0.15s, background 0.15s;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
}

.controls button:disabled {
  opacity: 0.3;
  cursor: not-allowed;
  pointer-events: none;
}

.controls button:focus { outline: none; }
.controls button:focus-visible {
  outline: 1.5px solid rgba(251, 248, 242, 0.7);
  outline-offset: -5px;
}

@media (hover: hover) {
  .controls button:not(:disabled):hover {
    background: #333;
  }
}

