/* Fonts (Fraunces/Figtree/JetBrains Mono) loaded globally via tokens.css. */

:root {
  --news-bg: #f5f0eb;
  --news-paper: #fdfbf9;
  --news-ink: #3d3d3d;
  --news-ink-light: #5a5a5a;
  --news-ink-muted: var(--text-muted);
  --news-rule: #3d3d3d;
  --news-rule-light: rgba(61, 61, 61, 0.16);
  --news-accent: #8b3a3a;
  --news-btn-bg: #fdfbf9;
  --news-btn-bg-hover: #3d3d3d;
  --news-highlight: rgba(139, 58, 58, 0.12);
  --news-active-bg: #8b3a3a;
  --news-correct: #5a8a4a;
  --news-incorrect: #c44d4d;
  --news-font-display: 'Fraunces', Georgia, 'Times New Roman', serif;
  --news-font-body: 'Figtree', Georgia, 'Times New Roman', sans-serif;

  --font-family-base: 'Figtree', Georgia, 'Times New Roman', sans-serif;
  --font-family-mono: 'JetBrains Mono', 'JetBrains Mono Fallback', ui-monospace, monospace;
}

html[data-theme='dark'] {
  --news-bg: #15141a;
  --news-paper: #1f1c26;
  --news-ink: #ece2cc;
  --news-ink-light: #b1a78f;
  /* Was #8a8478, the same value the 3D sheet carried: 4.11:1 on the dark sidebar
     surface, under the 4.5:1 the 12px labels using it need. The light block above
     already defers to var(--text-muted); this brings the dark one into line. */
  --news-ink-muted: #a29a8a;
  --news-rule: #ece2cc;
  --news-rule-light: rgba(236, 227, 207, 0.16);
  --news-accent: #ff5762;
  --news-btn-bg: #2a2636;
  --news-btn-bg-hover: #ece2cc;
  --news-highlight: rgba(255, 87, 98, 0.18);
  --news-active-bg: #ff5762;
  --news-correct: #5cd68b;
  --news-incorrect: #ff7a7a;
}

body {
  background: var(--news-bg) !important;
  background-image:
    repeating-linear-gradient(0deg, transparent, transparent 39px, rgba(0,0,0,0.02) 39px, rgba(0,0,0,0.02) 40px) !important;
  background-attachment: fixed !important;
  color: var(--news-ink);
  font-family: var(--news-font-body);
}

html[data-theme='light'] body {
  background: var(--news-bg) !important;
  background-image:
    repeating-linear-gradient(0deg, transparent, transparent 39px, rgba(0,0,0,0.02) 39px, rgba(0,0,0,0.02) 40px) !important;
  color: var(--news-ink);
}

html[data-theme='dark'] body {
  background: var(--news-bg) !important;
  background-image:
    repeating-linear-gradient(0deg, transparent, transparent 39px, rgba(236,227,207,0.03) 39px, rgba(236,227,207,0.03) 40px) !important;
  background-attachment: fixed !important;
  color: var(--news-ink);
}

.puzzle-page-wrapper {
  max-width: 960px;
}

h1, h2, h3, h4 {
  font-family: var(--news-font-display);
  font-weight: 700;
  color: var(--news-ink);
}

.puzzle-main h1,
.puzzle-header h1 {
  font-family: var(--news-font-display);
  font-size: 1.75rem;
  font-weight: 900;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.subtitle,
.puzzle-header .subtitle,
.puzzle-main .subtitle {
  font-family: var(--news-font-body);
  color: var(--news-ink-light);
}

.header-text h1 {
  font-family: var(--news-font-display);
  font-size: 1.75rem;
  font-weight: 900;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--news-ink);
}

.header-text p {
  font-family: var(--news-font-body);
  color: var(--news-ink-light);
}

.crossword-page-title { font-family: var(--news-font-display); }
.crossword-page-subtitle { font-family: var(--news-font-body); color: var(--news-ink-light); }

.card {
  background: var(--news-paper) !important;
  border: 1px solid var(--news-rule-light) !important;
  box-shadow: 2px 2px 0 rgba(0,0,0,0.06) !important;
  color: var(--news-ink) !important;
}

.card h2, .card h3 {
  font-family: var(--news-font-display) !important;
  color: var(--news-ink) !important;
  font-size: 0.85rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  border-bottom: 1px solid var(--news-rule-light);
  padding-bottom: 6px;
}

.sidebar-card {
  background: var(--news-paper) !important;
  border: 1px solid var(--news-rule-light) !important;
  box-shadow: 2px 2px 0 rgba(0,0,0,0.06) !important;
  color: var(--news-ink) !important;
}

.sidebar-card h2, .sidebar-card h3 {
  font-family: var(--news-font-display) !important;
  color: var(--news-ink) !important;
  font-size: 0.85rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  border-bottom: 1px solid var(--news-rule-light);
  padding-bottom: 6px;
}

.btn {
  font-family: var(--news-font-body);
  font-weight: 600;
  border-radius: 0;
}

.btn-primary {
  background: var(--news-ink) !important;
  color: var(--news-paper) !important;
  box-shadow: none !important;
  border: 1px solid var(--news-ink);
}

.btn-primary:hover:not(:disabled) {
  background: var(--news-accent) !important;
  box-shadow: none !important;
}

.btn-secondary,
.btn-ghost {
  background: var(--news-paper) !important;
  color: var(--news-ink) !important;
  border: 1px solid var(--news-rule) !important;
  box-shadow: none !important;
}

.btn-secondary:hover:not(:disabled),
.btn-ghost:hover:not(:disabled) {
  background: var(--news-ink) !important;
  color: var(--news-paper) !important;
}

.control-btn {
  font-family: var(--news-font-body);
  font-weight: 600;
  border-radius: 0;
  background: var(--news-paper) !important;
  color: var(--news-ink) !important;
  border: 1px solid var(--news-rule) !important;
}

.control-btn:hover:not(:disabled) {
  background: var(--news-ink) !important;
  color: var(--news-paper) !important;
  filter: none !important;
}

.control-btn-new-game,
.control-btn-hint,
.control-btn-preview,
#hint-btn.control-btn,
#get-hint-btn.control-btn,
#get-hint.control-btn,
#previewBtn.control-btn {
  background: var(--news-ink) !important;
  color: var(--news-paper) !important;
  border: 1px solid var(--news-ink) !important;
}

.control-btn-new-game:hover:not(:disabled),
.control-btn-hint:hover:not(:disabled),
.control-btn-preview:hover:not(:disabled),
#hint-btn.control-btn:hover:not(:disabled),
#get-hint-btn.control-btn:hover:not(:disabled),
#get-hint.control-btn:hover:not(:disabled),
#previewBtn.control-btn:hover:not(:disabled) {
  background: var(--news-accent) !important;
}

.puzzle-sidebar .card .control-btn,
.puzzle-sidebar .sidebar-card .control-btn {
  background: var(--news-paper) !important;
  color: var(--news-ink) !important;
  border: 1px solid var(--news-rule) !important;
}

.puzzle-sidebar .card .control-btn.control-btn-new-game,
.puzzle-sidebar .sidebar-card .control-btn.control-btn-new-game,
.puzzle-sidebar .card .control-btn.control-btn-hint,
.puzzle-sidebar .sidebar-card .control-btn.control-btn-hint,
.puzzle-sidebar .card .control-btn.control-btn-preview,
.puzzle-sidebar .sidebar-card .control-btn.control-btn-preview,
.puzzle-sidebar .card #hint-btn.control-btn,
.puzzle-sidebar .sidebar-card #hint-btn.control-btn,
.puzzle-sidebar .card #get-hint-btn.control-btn,
.puzzle-sidebar .sidebar-card #get-hint-btn.control-btn,
.puzzle-sidebar .card #get-hint.control-btn,
.puzzle-sidebar .sidebar-card #get-hint.control-btn,
.puzzle-sidebar .card #previewBtn.control-btn,
.puzzle-sidebar .sidebar-card #previewBtn.control-btn {
  background: var(--news-ink) !important;
  color: var(--news-paper) !important;
  border: 1px solid var(--news-ink) !important;
}

.form-control,
select,
input[type="text"],
input[type="number"] {
  background: var(--news-paper);
  border: 1px solid var(--news-rule);
  color: var(--news-ink);
  font-family: var(--news-font-body);
  border-radius: 0;
}

.form-control:focus,
select:focus,
input[type="text"]:focus,
input[type="number"]:focus {
  border-color: var(--news-ink);
  box-shadow: none;
}

.game-stats-item {
  color: var(--news-ink-light) !important;
  border-bottom-color: var(--news-rule-light) !important;
}

.stat-label {
  color: var(--news-ink-light) !important;
}

.stat-value {
  color: var(--news-ink) !important;
  font-family: var(--news-font-body);
}

.stats-list span:first-child { color: var(--news-ink-light); }
.stats-list .stat-value { color: var(--news-ink); }

.progress-bar-container {
  background: var(--news-rule-light);
  border-color: var(--news-rule-light);
  border-radius: 0;
}

.progress-bar {
  background: var(--news-ink);
  border-radius: 0;
}

.message-area {
  background: var(--news-paper);
  color: var(--news-ink-light);
  border: 1px solid var(--news-rule-light);
  font-family: var(--news-font-body);
}

.message-area.success {
  background: rgba(46,125,50,0.08);
  color: var(--news-correct);
  border-color: var(--news-correct);
}

.message-area.error {
  background: rgba(198,40,40,0.08);
  color: var(--news-incorrect);
  border-color: var(--news-incorrect);
}

.message-area.info {
  background: rgba(59,130,246,0.08);
  color: var(--news-active-bg);
  border-color: var(--news-active-bg);
}

.alert {
  font-family: var(--news-font-body);
}

.how-to-play p,
.how-to-play li {
  font-family: var(--news-font-body);
  color: var(--news-ink-light);
}

.how-to-play kbd {
  background: var(--news-paper);
  border: 1px solid var(--news-rule);
  color: var(--news-ink);
  font-family: var(--news-font-body);
}

#message-box {
  font-family: var(--news-font-body);
  background: var(--news-ink);
  color: var(--news-paper);
  box-shadow: 2px 2px 0 rgba(0,0,0,0.15);
}

.keyboard-shortcuts {
  font-family: var(--news-font-body);
  color: var(--news-ink-light);
}

.keyboard-shortcuts kbd {
  background: var(--news-paper);
  border: 1px solid var(--news-rule);
  color: var(--news-ink);
  font-family: var(--news-font-body);
}

.difficulty-bar {
  background: var(--news-paper);
  border-color: var(--news-rule-light);
}

.difficulty-selector {
  gap: 4px;
}

.difficulty-btn {
  font-family: var(--news-font-body);
  border: 1px solid var(--news-rule);
  border-radius: 0;
  color: var(--news-ink-light);
}

.difficulty-btn.active {
  background: var(--news-ink);
  border-color: var(--news-ink);
  color: var(--news-paper);
}

.big-btn.primary {
  background: var(--news-ink) !important;
  box-shadow: none !important;
  animation: none !important;
}

.big-btn.secondary {
  background: var(--news-paper) !important;
  box-shadow: 0 2px 6px rgba(0,0,0,0.1) !important;
  animation: none !important;
}

.game-stats-grid {
  color: var(--news-ink-light);
}

.game-stats-grid .stat-value {
  color: var(--news-ink);
  font-family: var(--news-font-body);
}

/* ============================================================================
   CLASSIC NEWSPAPER LAYOUT (shared with crossword)
   Used by all 2D puzzle pages for consistent masthead + toolbar + content + footer
   ============================================================================ */

.classic-page-wrapper {
  max-width: 960px;
  margin: 0 auto;
  padding: 24px 20px 60px;
}

.classic-masthead {
  text-align: center;
  border-bottom: 3px double var(--news-rule);
  padding-bottom: 16px;
  margin-bottom: 20px;
}

.classic-masthead-title {
  font-family: var(--news-font-display);
  font-size: 2.6rem;
  font-weight: 900;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--news-ink);
  margin: 0;
  line-height: 1.1;
}

.classic-masthead-subtitle {
  font-family: var(--news-font-body);
  font-size: 0.85rem;
  font-weight: 400;
  color: var(--news-ink-light);
  margin: 6px 0 0;
  letter-spacing: 0.15em;
  text-transform: uppercase;
}

.classic-masthead-rule {
  border: none;
  border-top: 1px solid var(--news-rule-light);
  margin: 10px 0 0;
}

.classic-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 10px;
  padding: 10px 0;
  border-bottom: 1px solid var(--news-rule-light);
  margin-bottom: 20px;
}

.classic-toolbar-left,
.classic-toolbar-right {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

.classic-btn {
  font-family: var(--news-font-body);
  font-size: 0.82rem;
  font-weight: 600;
  padding: 6px 14px;
  border: 1.5px solid var(--news-ink);
  background: var(--news-btn-bg);
  color: var(--news-ink);
  cursor: pointer;
  transition: background 0.15s, color 0.15s, border-color 0.15s;
  letter-spacing: 0.02em;
  box-shadow: 1px 1px 0 rgba(0,0,0,0.10);
}

.classic-btn:hover {
  background: var(--news-btn-bg-hover);
  color: var(--news-btn-bg);
  border-color: var(--news-accent);
}

.classic-btn:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

.classic-btn--primary {
  background: var(--news-ink);
  color: var(--news-paper);
}

.classic-btn--primary:hover {
  background: var(--news-accent);
}

.classic-stats {
  display: flex;
  gap: 18px;
  font-size: 0.82rem;
  color: var(--news-ink-light);
  font-family: var(--news-font-body);
}

.classic-stats span {
  font-weight: 600;
  color: var(--news-ink);
}

.classic-difficulty-select {
  font-family: var(--news-font-body);
  font-size: 0.82rem;
  padding: 5px 10px;
  border: 1px solid var(--news-rule);
  background: var(--news-paper);
  color: var(--news-ink);
  cursor: pointer;
  appearance: auto;
}

.classic-content {
  display: flex;
  gap: 28px;
  align-items: flex-start;
}

/* CLS ON THE 2D PUZZLE PAGES — both puzzle pages are now clean.
   -------------------------------------------------------------------------
   /play/wordsearch: Lighthouse CLS 0. The board and the clue list share a flex row;
   while the board was empty it was 10px wide, so the clue list got the whole row and
   its prose laid out wide, and when the board arrived the list was squeezed and every
   paragraph rewrapped. Measured: the list went 882px -> 562px wide and 670px -> 767px
   tall. Below 768px the row stacks and the same growth pushes the list down instead
   (board 10px -> 348px, list moved 338px). Fixed by reserving the board's width in the
   markup (gridSize * 40 + 10, exact because a cell is a fixed 40px) and its height on
   mobile via aspect-ratio, plus the clue list's own height at 1.5em + 8px per item.

   /play/logic: Lighthouse CLS 0.001, all four difficulties. This one was NOT fixed by
   reserving anything, and two earlier attempts to reserve it were wrong to try: the
   board is a content-sized <table> that I measured at 230px, 305px, 313px and 1751px
   across runs, so there is no value to reserve. What made it fixable is that the puzzle
   data is already inline in the page, so the render never needed to wait for a fetch —
   it was waiting on DOMContentLoaded and an async localStorage read, and on mobile the
   late board pushed the clue list down 162px. It now renders before first paint; see
   the note above the call in php_folder/2D/logic2D.php for the guards that keep it from
   swapping the puzzle after paint.

   /play/wordladder: 0.036, always inside the 0.1 threshold. Not touched. */
.classic-grid-section {
  flex-shrink: 0;
  /* Reserved to the width the board will occupy, so the clues column beside it is
     already the right width at first paint and its prose does not reflow when the
     board appears. A cell is a fixed 40px and the board adds 4px padding + 1px
     border per side, so the value is gridSize * 40 + 10, set by the template from
     the difficulty config. Without it the clues column measured 882px and then
     562px, which rewrapped the text and moved the page. */
  /* The fallback is 0, not a guess at a typical board: only the puzzle that sets
     --ws-board-w gets a reservation. A px fallback here widened every other
     puzzle's board area to that value — measured: logic 267 -> 330, wordladder
     246 -> 330, wordscramble 268 -> 330 — which changed their layouts. */
  min-width: var(--ws-board-w, 0);
}

.classic-grid-section .classic-grid-wrapper {
  background: var(--news-paper);
  border: 2px solid var(--news-ink);
  padding: 4px;
  box-shadow: 4px 4px 0 rgba(0,0,0,0.08);
}

.classic-clues-section {
  flex: 1;
  min-width: 0;
}

.classic-clues-heading {
  font-family: var(--news-font-display);
  font-size: 1rem;
  font-weight: 900;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  border-bottom: 2px solid var(--news-ink);
  padding-bottom: 4px;
  margin: 0 0 10px;
}

.classic-clue-list {
  list-style: none;
  padding: 0;
  margin: 0;
  /* Reserved to the height the list will reach, so filling it cannot move the page.
     Without it the list grew from 0px once the words arrived, taking the clues
     column — and everything below it — with it.

     The per-row height is derived, not guessed. A <li> is 0.88em of this element's
     font-size, with line-height 1.5 and 4px padding top and bottom:
         0.88 * 1.5 = 1.32em  ->  21.12px at the ul's 16px  ->  +8px = 29.12px
     Six words then measure 174.7px, and the browser reported 175px for easy and
     437px for expert — both match. Written in `em` rather than px so it follows a
     root font-size change, which this site lets the reader make. */
  min-height: calc(var(--ws-word-rows, 9) * (1.32em + 8px));
}

.classic-clue-item {
  font-family: var(--news-font-body);
  font-size: 0.88rem;
  line-height: 1.5;
  padding: 3px 6px;
  cursor: default;
  border-left: 2px solid transparent;
}

.classic-clue-item .clue-num {
  font-weight: 700;
  margin-right: 4px;
}

.classic-progress-bar {
  width: 100%;
  height: 6px;
  background: var(--news-rule-light);
  margin-top: 12px;
  border-radius: 0;
}

.classic-progress-fill {
  height: 100%;
  background: var(--news-ink);
  transition: width 0.3s ease;
  width: 0;
}

.classic-footer {
  text-align: center;
  padding-top: 20px;
  border-top: 1px solid var(--news-rule-light);
  margin-top: 24px;
  font-family: var(--news-font-body);
  font-size: 0.75rem;
  color: var(--news-ink-light);
  letter-spacing: 0.05em;
}

.classic-keyboard-hints {
  font-family: var(--news-font-body);
  font-size: 0.75rem;
  color: var(--news-ink-light);
  margin-top: 12px;
}

.classic-keyboard-hints kbd {
  font-family: var(--news-font-body);
  font-size: 0.7rem;
  padding: 1px 5px;
  border: 1px solid var(--news-rule-light);
  background: var(--news-paper);
}

.classic-message-box {
  position: fixed;
  top: 20px;
  left: 50%;
  transform: translateX(-50%) translateY(-20px);
  padding: 10px 24px;
  font-family: var(--news-font-body);
  font-size: 0.88rem;
  font-weight: 600;
  background: var(--news-ink);
  color: var(--news-paper);
  opacity: 0;
  transition: opacity 0.25s, transform 0.25s;
  pointer-events: none;
  z-index: 10000;
  box-shadow: 2px 2px 0 rgba(0,0,0,0.15);
}

.classic-message-box.visible {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}

.classic-message-box.success { background: var(--news-correct); }
.classic-message-box.error { background: var(--news-incorrect); }
.classic-message-box.info { background: var(--news-ink); }

.classic-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.7);
  display: none;
  align-items: center;
  justify-content: center;
  z-index: 10020;
}

.classic-overlay.is-visible {
  display: flex;
}

.classic-overlay-content {
  background: var(--news-paper);
  border: 3px double var(--news-ink);
  padding: 36px 44px;
  text-align: center;
  max-width: 420px;
  width: 90%;
  box-shadow: 6px 6px 0 rgba(0,0,0,0.1);
}

.classic-overlay-content h2 {
  font-family: var(--news-font-display);
  font-size: 1.8rem;
  font-weight: 900;
  margin: 0 0 8px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--news-ink) !important;
  border-bottom: none !important;
  padding-bottom: 0 !important;
}

.classic-overlay-content p {
  font-family: var(--news-font-body);
  font-size: 0.92rem;
  color: var(--news-ink-light);
  margin: 0 0 20px;
}

.classic-overlay-content .classic-btn {
  margin: 0 6px;
}

@media (max-width: 768px) {
  .classic-content {
    flex-direction: column;
  }

  .classic-grid-section {
    width: 100%;
    display: flex;
    justify-content: center;
  }

  .classic-masthead-title {
    font-size: 1.8rem;
  }

  .classic-toolbar {
    flex-direction: column;
    align-items: stretch;
  }

  .classic-toolbar-left,
  .classic-toolbar-right {
    justify-content: center;
  }
}

@media (max-width: 480px) {
  .classic-masthead-title {
    font-size: 1.4rem;
  }

  .classic-toolbar {
    flex-direction: column;
    align-items: stretch;
  }

  .classic-toolbar-left,
  .classic-toolbar-right {
    justify-content: center;
  }
}

/* ---------------------------------------------------------------------------
   Clickable crossword clue (added when the clue <li> stopped carrying
   role="button" — see php_folder/2D/crossword2D.php).

   The clue text is now a real <button> nested inside the <li>, so the list keeps
   its listitem semantics. It must NOT inherit the app-wide accent button skin,
   which public/css/style.css declares with !important, so the reset has to match
   that weight. `all: unset` restores inheritance (colour, font, background,
   border, shadow) and the declarations after it re-apply only what is wanted.
   --------------------------------------------------------------------------- */
.classic-clue-btn {
    all: unset !important;
    display: inline !important;
    cursor: pointer;
    border-radius: 4px;
    font: inherit;
    color: inherit;
}

.classic-clue-btn:hover {
    text-decoration: underline;
}

.classic-clue-btn:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    box-shadow: none;
}
