/* Crossword solver — shared styles */

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

/* ---------- Theme tokens ----------
   Defined on :root so the surrounding page shell can use them too.
   Dark mode follows the OS by default; an explicit data-theme on <html>
   (set by the toolbar toggle) overrides that in either direction. */

:root {
  --xw-accent: #8c4a3a;
  --xw-text: #1a1a1a;
  --xw-muted: #666;
  --xw-light: #8a8a8a;
  --xw-bg: #f7f5ef;
  --xw-card: #fff;
  --xw-border: #e1ddd2;
  --xw-hover: #faf7f0;
  --xw-hover-border: #cfc7b5;
  --xw-shadow: rgba(0, 0, 0, 0.12);
  --xw-nav-hover: rgba(0, 0, 0, 0.06);

  --xw-line: #1a1a1a;        /* gridlines */
  --xw-block: #1a1a1a;       /* blocked squares */
  --xw-cell-bg: #fff;        /* open squares */
  --xw-num: #444;

  --xw-word: #d6e6f5;
  --xw-focus: #ffd75e;
  --xw-cross: #eef4fa;

  --xw-wrong: #d23b2f;
  --xw-revealed: #1a6fb5;
  --xw-good: #2e7d4f;
  --xw-good-bg: #e6f5ec;
  --xw-good-text: #1d5e3a;
  --xw-solved-cell: #e6f5ec;

  --xw-accent-text: #fff;    /* text sitting on an accent-filled surface */

  --xw-cell: 44px;
  color-scheme: light;
}

/* Blocked squares stay darker than open ones, while gridlines lighten so the
   lattice is still readable against a dark grid. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --xw-accent: #d1907a;
    --xw-text: #e9e6e0;
    --xw-muted: #a9a49b;
    --xw-light: #7f838b;
    --xw-bg: #15171b;
    --xw-card: #1e2127;
    --xw-border: #333941;
    --xw-hover: #272c34;
    --xw-hover-border: #4b515c;
    --xw-shadow: rgba(0, 0, 0, 0.55);
    --xw-nav-hover: rgba(255, 255, 255, 0.09);

    --xw-line: #4e5661;
    --xw-block: #0c0e11;
    --xw-cell-bg: #23272e;
    --xw-num: #939aa4;

    --xw-word: #24405c;
    --xw-focus: #6b5210;
    --xw-cross: #1c2c3c;

    --xw-wrong: #ff6f61;
    --xw-revealed: #8ec5f5;
    --xw-good: #3fa06a;
    --xw-good-bg: #17301f;
    --xw-good-text: #83d3a4;
    --xw-solved-cell: #1c3a2a;
    /* The dark accent is a light peach, so it takes dark text, not white. */
    --xw-accent-text: #15171b;
    color-scheme: dark;
  }
}

:root[data-theme="dark"] {
  --xw-accent: #d1907a;
  --xw-text: #e9e6e0;
  --xw-muted: #a9a49b;
  --xw-light: #7f838b;
  --xw-bg: #15171b;
  --xw-card: #1e2127;
  --xw-border: #333941;
  --xw-hover: #272c34;
  --xw-hover-border: #4b515c;
  --xw-shadow: rgba(0, 0, 0, 0.55);
  --xw-nav-hover: rgba(255, 255, 255, 0.09);

  --xw-line: #4e5661;
  --xw-block: #0c0e11;
  --xw-cell-bg: #23272e;
  --xw-num: #939aa4;

  --xw-word: #24405c;
  --xw-focus: #6b5210;
  --xw-cross: #1c2c3c;

  --xw-wrong: #ff6f61;
  --xw-revealed: #8ec5f5;
  --xw-good: #3fa06a;
  --xw-good-bg: #17301f;
  --xw-good-text: #83d3a4;
  --xw-solved-cell: #1c3a2a;
  --xw-accent-text: #15171b;
  color-scheme: dark;
}

.xw {
  font-family: ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  color: var(--xw-text);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  /* Rapidly tapping a control (e.g. the clue arrows) would otherwise register
     as a double-tap and zoom the page. This disables double-tap zoom only —
     pinch-to-zoom and scrolling still work. */
  touch-action: manipulation;
}

/* ---------- Page shell (standalone solve pages) ---------- */

body.xw-page {
  margin: 0;
  background: var(--xw-bg);
  color: var(--xw-text);
  font-family: ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}

.xw-page-inner { max-width: 1080px; margin: 0 auto; padding: 1.75rem 1.25rem 3rem; }

.xw-backlink {
  display: inline-block;
  margin-bottom: 1rem;
  font-size: 0.85rem;
  color: var(--xw-accent);
  text-decoration: none;
}

.xw-backlink:hover { text-decoration: underline; }

.xw-error {
  padding: 1rem 1.1rem;
  background: var(--xw-card);
  border: 1px solid var(--xw-border);
  border-left: 4px solid var(--xw-accent);
  border-radius: 6px;
  color: var(--xw-muted);
}

/* ---------- Header ---------- */

.xw-head { margin-bottom: 0.85rem; }

.xw-title {
  font-family: "Iowan Old Style", Georgia, Charter, serif;
  font-size: 1.5rem;
  font-weight: 700;
  line-height: 1.15;
  margin: 0;
}

.xw-byline {
  font-size: 0.85rem;
  color: var(--xw-muted);
  margin: 0.15rem 0 0;
}

/* Constructor's commentary — spans the full content width. */
.xw-note {
  margin: 0.55rem 0 0;
  font-size: 0.89rem;
  font-style: italic;
  line-height: 1.45;
  color: var(--xw-muted);
}

/* ---------- Toolbar ---------- */

.xw-toolbar {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
  padding: 0.5rem 0.65rem;
  background: var(--xw-card);
  border: 1px solid var(--xw-border);
  border-radius: 6px;
  margin-bottom: 1rem;
}

.xw-timer {
  font-variant-numeric: tabular-nums;
  font-size: 0.95rem;
  font-weight: 600;
  min-width: 3.6em;
  color: var(--xw-text);
}

.xw-timer.is-paused { color: var(--xw-light); }

/* Stopped clock after a solve — clearly final, not merely paused. */
.xw-timer.is-final {
  color: var(--xw-good-text);
  font-weight: 700;
  background: var(--xw-good-bg);
  border-radius: 4px;
  padding: 0.1rem 0.5rem;
  min-width: 0;
}

.xw-btn.is-copied {
  color: var(--xw-good-text);
  border-color: var(--xw-good);
  background: var(--xw-good-bg);
}

.xw-spacer { flex: 1; }

.xw-btn {
  font: inherit;
  font-size: 0.82rem;
  font-weight: 500;
  padding: 0.32rem 0.7rem;
  border: 1px solid var(--xw-border);
  border-radius: 5px;
  background: var(--xw-card);
  color: var(--xw-muted);
  cursor: pointer;
  transition: background 0.12s ease, border-color 0.12s ease, color 0.12s ease;
  white-space: nowrap;
}

.xw-btn:hover { background: var(--xw-hover); border-color: var(--xw-hover-border); color: var(--xw-text); }
.xw-btn:focus-visible { outline: 2px solid var(--xw-accent); outline-offset: 1px; }

/* Repeated taps on a control shouldn't select its label or raise the iOS
   callout menu. Clue text stays selectable so it can still be copied. */
.xw-btn,
.xw-cluebar-nav {
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
}

/* touch-action does NOT inherit, so it has to be set on the tap targets
   themselves — setting it only on the container leaves these at `auto`. */
.xw-btn,
.xw-cluebar-nav,
.xw-cell,
.xw-clue,
.xw-menu-list button {
  touch-action: manipulation;
}

/* A display rule on a button would otherwise outrank the UA's
   [hidden] { display: none }, leaving "hidden" controls on screen. */
.xw [hidden] { display: none !important; }

/* Compact icon-only buttons (pause, light/dark toggle) */
.xw-btn.xw-icon-btn {
  padding: 0.32rem 0.5rem;
  display: flex;
  align-items: center;
  justify-content: center;
}

.xw-btn.xw-icon-btn svg { display: block; }

/* Paused clock: the resume control calls attention to itself. */
.xw-btn.xw-icon-btn.is-paused {
  color: var(--xw-accent);
  border-color: var(--xw-accent);
}

/* ---------- Dropdown menus ---------- */

.xw-menu { position: relative; }

.xw-menu-list {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  z-index: 20;
  min-width: 9rem;
  background: var(--xw-card);
  border: 1px solid var(--xw-border);
  border-radius: 6px;
  box-shadow: 0 6px 18px var(--xw-shadow);
  padding: 0.25rem;
  display: none;
}

.xw-menu.is-open .xw-menu-list { display: block; }

.xw-menu-list button {
  display: block;
  width: 100%;
  text-align: left;
  font: inherit;
  font-size: 0.85rem;
  padding: 0.35rem 0.6rem;
  border: 0;
  border-radius: 4px;
  background: none;
  color: var(--xw-text);
  cursor: pointer;
}

.xw-menu-list button:hover { background: var(--xw-hover); }

/* ---------- Layout ---------- */

.xw-body {
  display: flex;
  gap: 1.75rem;
  align-items: flex-start;
}

/* Grid column: grows no wider than its natural size, but may shrink so the
   clue column always keeps a seat beside it instead of wrapping underneath. */
.xw-main {
  flex: 0 1 var(--xw-gridw, 690px);
  max-width: var(--xw-gridw, 690px);
  min-width: 220px;
}

/* ---------- Active clue bar ---------- */

.xw-cluebar {
  display: flex;
  align-items: stretch;
  gap: 0;
  background: var(--xw-word);
  border: 1px solid var(--xw-border);
  border-radius: 5px;
  margin-bottom: 1rem;
  min-height: 2.9rem;
  overflow: hidden;
}

/* SVG chevrons: centred by geometry, immune to font metrics. */
.xw-cluebar-nav {
  font: inherit;
  line-height: 1;
  border: 0;
  background: none;
  color: var(--xw-muted);
  cursor: pointer;
  padding: 0 0.75rem;
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
}

.xw-cluebar-nav svg { display: block; }

.xw-cluebar-nav:hover { background: var(--xw-nav-hover); color: var(--xw-text); }

.xw-cluebar-body {
  flex: 1;
  display: flex;
  align-items: center;
  padding: 0.15rem 0.2rem;
  min-width: 0;
}

/* Number and clue text align on their centers. Baseline-aligning them makes
   the all-caps number look low, since it has no descenders to balance it. */
.xw-cluebar-inner {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-width: 0;
}

.xw-cluebar-label {
  /* Matches .xw-clue in the clue list, so "1A" here and "1" there are the
     same size. Keep the two in step if either changes. */
  font-size: 0.86rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--xw-accent);
  white-space: nowrap;
}

.xw-cluebar-text { font-size: 0.92rem; }

/* Leading-trim: shrink each box to cap-height→baseline so that centring the
   box centres the glyphs themselves, at any font size. Without this, a line
   box carries font-size-proportional half-leading and ascent/descent padding,
   so two different sizes centre their boxes but not their text. */
@supports (text-box-trim: trim-both) {
  .xw-cluebar-label,
  .xw-cluebar-text {
    text-box-trim: trim-both;
    text-box-edge: cap alphabetic;
  }
}

/* ---------- Grid ---------- */

.xw-grid {
  display: grid;
  grid-template-columns: repeat(var(--xw-cols, 15), 1fr);
  gap: 1px;
  padding: 1px;
  background: var(--xw-line);
  border: 1px solid var(--xw-line);
  border-radius: 2px;
  width: 100%;
  touch-action: manipulation;
  user-select: none;
  -webkit-user-select: none;
}

.xw-cell {
  position: relative;
  aspect-ratio: 1 / 1;
  background: var(--xw-cell-bg);
  cursor: pointer;
  overflow: hidden;
}

.xw-cell.is-black { background: var(--xw-block); cursor: default; }

.xw-num {
  position: absolute;
  top: 1px;
  left: 2px;
  font-size: calc(var(--xw-cell) * 0.26);
  line-height: 1;
  color: var(--xw-num);
  pointer-events: none;
}

.xw-letter {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding-top: calc(var(--xw-cell) * 0.10);
  font-size: calc(var(--xw-cell) * 0.56);
  font-weight: 500;
  line-height: 1;
  pointer-events: none;
}

/* Selection states */
.xw-cell.is-word { background: var(--xw-word); }
.xw-cell.is-focus { background: var(--xw-focus); }

/* Check / reveal marks */
.xw-cell.is-wrong::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(to bottom left, transparent calc(50% - 0.7px), var(--xw-wrong) calc(50% - 0.7px), var(--xw-wrong) calc(50% + 0.7px), transparent calc(50% + 0.7px));
  pointer-events: none;
}

.xw-cell.is-revealed .xw-letter { color: var(--xw-revealed); }

/* Solved */
.xw.is-solved .xw-cell:not(.is-black) { background: var(--xw-solved-cell); }
.xw.is-solved .xw-grid { border-color: var(--xw-good); background: var(--xw-good); }
.xw.is-solved .xw-cell.is-wrong::after { display: none; }

/* ---------- Pre-start curtain ---------- */

.xw-gridwrap { position: relative; }

/* Transparent: it exists to place the button and to intercept taps aimed at
   the cells behind it, not to hide the grid. */
.xw-start {
  position: absolute;
  inset: 0;
  z-index: 5;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0.75rem;
}

.xw-start-btn {
  font: inherit;
  font-size: 1rem;
  font-weight: 600;
  padding: 0.65rem 1.6rem;
  border: 1px solid var(--xw-accent);
  border-radius: 999px;
  background: var(--xw-accent);
  color: var(--xw-accent-text);
  cursor: pointer;
  box-shadow: 0 4px 14px var(--xw-shadow);
  touch-action: manipulation;
  user-select: none;
  -webkit-user-select: none;
}

.xw-start-btn:hover { filter: brightness(1.08); }
.xw-start-btn:focus-visible { outline: 2px solid var(--xw-accent); outline-offset: 3px; }

/* The clue list clouds over whole — wording and numbers — as does the clue
   text in the bar. Headings, the bar's "1A" marker and the grid stay as they
   are. Blur alone would leave the text selectable, so it is stood down too.
   Kept light: this is an honor system, not a lock. */
.xw.is-idle .xw-clue-text,
.xw.is-idle .xw-clue-num,
.xw.is-idle .xw-cluebar-text {
  filter: blur(2.25px);
  opacity: 0.75;
  user-select: none;
  -webkit-user-select: none;
}

/* Inoperable, but not hidden. */
.xw.is-idle .xw-clues { pointer-events: none; }

.xw.is-idle .xw-cluebar-nav {
  pointer-events: none;
  opacity: 0.35;
}

.xw-btn:disabled {
  opacity: 0.45;
  cursor: default;
}

.xw-btn:disabled:hover {
  background: var(--xw-card);
  border-color: var(--xw-border);
  color: var(--xw-muted);
}

.xw-banner {
  display: none;
  margin-top: 0.75rem;
  padding: 0.7rem 0.9rem;
  background: var(--xw-good-bg);
  border: 1px solid var(--xw-good);
  border-radius: 6px;
  color: var(--xw-good-text);
  font-size: 0.92rem;
}

.xw.is-solved .xw-banner {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  flex-wrap: wrap;
}

.xw-banner-text { flex: 1 1 auto; }

.xw-banner .xw-share { flex: 0 0 auto; }

/* Invisible input that captures all typing. It stays inside the viewport —
   parking it off-screen stops iOS from opening the keyboard — and uses a 16px
   font so mobile Safari doesn't zoom when it takes focus. */
.xw-input {
  position: fixed;
  top: 0;
  left: 0;
  width: 1px;
  height: 1px;
  padding: 0;
  border: 0;
  outline: 0;
  opacity: 0;
  font-size: 16px;
  z-index: -1;
  caret-color: transparent;
}

/* ---------- Clue lists ---------- */

/* One column holding Across then Down, scrolling as a single list whose
   height tracks the grid. */
.xw-clues {
  flex: 1 1 260px;
  min-width: 190px;
  display: flex;
  flex-direction: column;
  gap: 1rem;
  overflow-y: auto;
  max-height: var(--xw-gridh, none);
  scrollbar-width: thin;
}

.xw-cluecol { min-width: 0; }

.xw-cluecol h3 {
  position: sticky;
  top: 0;
  z-index: 1;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--xw-light);
  background: var(--xw-bg);
  margin: 0 0 0.4rem;
  padding-bottom: 0.3rem;
  border-bottom: 1px solid var(--xw-border);
}

.xw-cluelist {
  list-style: none;
  margin: 0;
  padding: 0;
}

.xw-clue {
  display: flex;
  gap: 0.45rem;
  padding: 0.2rem 0.35rem;
  font-size: 0.86rem;
  line-height: 1.35;
  border-radius: 3px;
  border-left: 3px solid transparent;
  cursor: pointer;
}

.xw-clue:hover { background: var(--xw-hover); }
.xw-clue.is-active { background: var(--xw-word); border-left-color: var(--xw-accent); }
.xw-clue.is-crossing { background: var(--xw-cross); }
.xw-clue.is-done { color: var(--xw-light); }

.xw-clue-num {
  font-weight: 700;
  min-width: 1.6em;
  text-align: right;
  flex: 0 0 auto;
}

/* ---------- Responsive ---------- */

/* Only at phone widths do the clues move below the grid. */
@media (max-width: 620px) {
  .xw-body { flex-direction: column; gap: 1.25rem; }

  /* Cap the grid to what is actually visible. dvh tracks the *dynamic*
     viewport, so it shrinks when the on-screen keyboard appears — the grid
     scales down to keep clue bar + grid above the fold instead of forcing a
     scroll. The subtracted allowance covers the sticky clue bar and gaps. */
  .xw-main {
    flex: 0 0 auto;
    width: 100%;
    min-width: 0;
    max-width: min(100%, calc((100dvh - 7.5rem) * var(--xw-cols, 15) / var(--xw-rows, 15)));
  }
  .xw-clues { width: 100%; max-height: none; overflow-y: visible; }
  .xw-cluecol h3 { position: static; }

  /* Controls stay on ONE line: no wrapping, and every control trims down
     enough to fit the narrowest phones. */
  .xw-toolbar {
    flex-wrap: nowrap;
    gap: 0.28rem;
    padding: 0.32rem 0.4rem;
    margin-bottom: 0.3rem;      /* sits right on top of the clue bar */
  }

  .xw-timer { font-size: 0.85rem; min-width: 0; }
  .xw-timer.is-final { padding: 0.1rem 0.35rem; }
  .xw-btn { font-size: 0.74rem; padding: 0.28rem 0.42rem; }
  .xw-btn.xw-icon-btn { padding: 0.28rem 0.34rem; }
  .xw-caret { display: none; }

  /* The clue bar rides along at the top of the viewport, so the current clue
     stays readable while solving without needing the clue list below. */
  .xw-cluebar {
    position: sticky;
    top: 0;
    z-index: 10;
    margin-bottom: 0.5rem;
    /* Tall enough for a two-line clue, so selecting a long clue never makes
       the bar jump and shove the grid down. */
    min-height: 3.15rem;
    box-shadow: 0 2px 6px var(--xw-shadow);
  }

  /* The button has to fit inside a grid that may be only a few rows tall. */
  .xw-start { padding: 0.5rem; }
  .xw-start-btn { font-size: 0.94rem; padding: 0.55rem 1.25rem; }

  /* Trim the header so more of the grid clears the fold. */
  .xw-head { margin-bottom: 0.6rem; }
  .xw-title { font-size: 1.25rem; }
  .xw-note { margin-top: 0.4rem; font-size: 0.84rem; }
}
