/* ---------------------------------------------------------------------------
   Wordle Solver

   Self-contained: no CDN imports. This used to pull Open Props and its
   normalize from unpkg with `@import "https://..."`, which sass leaves as a
   runtime CSS import - a render-blocking request to a third party before the
   page can paint, on a phone, for a handful of tokens that every call site
   already had a fallback for. The tokens below are those fallbacks, made
   explicit.

   Layout rule for everything here: nothing has a fixed width. The tile row is
   five fractional columns, the suggestion rows wrap, and every flex child that
   holds text sets `min-width: 0` so it can shrink instead of pushing the page
   sideways.
--------------------------------------------------------------------------- */
:root {
  color-scheme: dark light;
  --bg: #12151f;
  --panel: #191d2b;
  --chip: #212637;
  --tile: #272d42;
  --border: #2e3549;
  --border-strong: #3d4664;
  --text: #eef1f7;
  --text-2: #b4bdcf;
  --text-3: #8590a6;
  --accent: #7aa2ff;
  --accent-ink: #08102a;
  /* Wordle tile colours - fixed in both themes, they are the game's. */
  --g: #6aaa64;
  --y: #c9b458;
  --b: #3a3a3c;
  --radius: 10px;
  --radius-lg: 14px;
  --gutter: 1rem;
  --ease: cubic-bezier(0.3, 0, 0.2, 1);
}

@media (prefers-color-scheme: light) {
  :root {
    --bg: #f4f5f8;
    --panel: #ffffff;
    --chip: #f0f2f6;
    --tile: #e9ecf2;
    --border: #dfe3ea;
    --border-strong: #c3cad6;
    --text: #171a21;
    --text-2: #4a5263;
    --text-3: #737d90;
    --accent: #3c5ccc;
    --accent-ink: #ffffff;
  }
}
*,
*::before,
*::after {
  box-sizing: border-box;
}

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

body {
  margin: 0;
  padding: var(--gutter);
  padding-inline: max(var(--gutter), env(safe-area-inset-left), env(safe-area-inset-right));
  padding-block-end: calc(var(--gutter) * 2 + env(safe-area-inset-bottom));
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, Inter, Arial, sans-serif;
  font-size: 16px;
  line-height: 1.5;
  background: var(--bg);
  color: var(--text);
  min-height: 100dvh;
}

h1,
h2,
h3 {
  margin: 0;
  line-height: 1.25;
}

:where(button, input, summary):focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* This is a narrow tool, not a dashboard. Wide suggestion rows with a word at
   one end and a number at the other are hard to read; cap the measure. */
.container {
  width: min(34rem, 100%);
  margin-inline: auto;
  display: grid;
  gap: var(--gutter);
}

.header {
  display: flex;
  gap: 0.5rem 0.75rem;
  align-items: baseline;
  justify-content: space-between;
  flex-wrap: wrap;
}
.header h1 {
  font-size: 1.25rem;
  letter-spacing: -0.01em;
}

.header .sub {
  color: var(--text-3);
  font-size: 0.8125rem;
  font-variant-numeric: tabular-nums;
}

.card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: var(--gutter);
  display: grid;
  gap: var(--gutter);
}

.muted {
  color: var(--text-3);
  font-size: 0.8125rem;
  margin: 0;
}

/* --- the guess being entered ------------------------------------------- */
/* Keep the input, the tiles and the buttons in one column the width of the
   tile row. Without this the tiles float centred under a full-width input and
   the group reads as three unrelated pieces on a wide screen. Below 20rem the
   cap never binds, so phones are unaffected. */
.entry {
  display: grid;
  gap: 0.625rem;
  width: 100%;
  max-width: 20rem;
  margin-inline: auto;
}

.input {
  width: 100%;
  min-width: 0;
  padding: 0.625rem 0.75rem;
  border-radius: var(--radius);
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--text);
  font: inherit;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 1rem; /* < 16px makes iOS Safari zoom on focus */
  letter-spacing: 0.32em;
  text-transform: uppercase;
}

.input::placeholder {
  color: var(--text-3);
  letter-spacing: normal;
  text-transform: none;
}

/* Five fractional columns: this can never be wider than its parent, which is
   what the old `repeat(5, 56px)` could not promise - at 360px the fixed row
   plus card padding overflowed the viewport. */
.grid {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 0.5rem;
  width: 100%;
  max-width: 20rem;
  margin-inline: auto;
  user-select: none;
}

.tile {
  aspect-ratio: 1;
  min-width: 0;
  display: grid;
  place-items: center;
  border-radius: var(--radius);
  font-weight: 700;
  font-size: clamp(1.125rem, 5vw, 1.5rem);
  background: var(--tile);
  border: 1px solid var(--border-strong);
  color: var(--text);
  transition: transform 120ms var(--ease);
}

.tile[data-s=b] {
  background: var(--b);
  border-color: var(--b);
  color: #fff;
}

.tile[data-s=y] {
  background: var(--y);
  border-color: var(--y);
  color: #1a1a1a;
}

.tile[data-s=g] {
  background: var(--g);
  border-color: var(--g);
  color: #fff;
}

.tile.tappable {
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.tile.tappable:active {
  transform: scale(0.94);
}

.hint {
  color: var(--text-3);
  font-size: 0.75rem;
  text-align: center;
  margin: 0;
}

/* --- buttons ------------------------------------------------------------ */
.actions {
  display: flex;
  gap: 0.5rem;
}

.btn {
  min-height: 2.75rem; /* comfortable touch target */
  padding: 0.5rem 1rem;
  border-radius: var(--radius);
  border: 1px solid var(--border);
  background: var(--chip);
  color: var(--text);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
  transition: background 120ms var(--ease), transform 120ms var(--ease);
}

.btn:active {
  transform: scale(0.98);
}

.btn[disabled] {
  opacity: 0.45;
  cursor: not-allowed;
}

.btn.primary {
  flex: 1 1 auto;
  min-width: 0;
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
}

.toggle {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-height: 2.75rem;
  font-size: 0.8125rem;
  color: var(--text-2);
  cursor: pointer;
}

.toggle input {
  inline-size: 1.125rem;
  block-size: 1.125rem;
  accent-color: var(--accent);
  flex: none;
}

/* --- status and banners ------------------------------------------------- */
.statusline {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.25rem 0.5rem;
  color: var(--text-2);
  font-size: 0.875rem;
}

.statusline strong {
  color: var(--text);
}

.statusline .sep {
  opacity: 0.4;
}

.banner {
  padding: 0.625rem 0.75rem;
  border-radius: var(--radius);
  border: 1px solid var(--border);
  background: var(--chip);
  font-size: 0.875rem;
  overflow-wrap: anywhere;
}

.banner.error {
  border-color: #e0574f;
  background: var(--chip);
  background: color-mix(in oklab, #e0574f, transparent 86%);
}

.banner.warn {
  border-color: var(--y);
  background: var(--chip);
  background: color-mix(in oklab, var(--y), transparent 86%);
}

/* --- the recommendation ------------------------------------------------- */
/* The answer to "what do I play now?", given the weight it deserves. */
.pick {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.5rem 0.75rem;
  padding: 0.875rem 1rem;
  border-radius: var(--radius);
  border: 1px solid var(--accent);
  background: var(--chip);
  background: color-mix(in oklab, var(--accent), transparent 88%);
  cursor: pointer;
  text-align: start;
  width: 100%;
  font: inherit;
  color: inherit;
  -webkit-tap-highlight-color: transparent;
}

.pick:active {
  transform: scale(0.995);
}

.pick .label {
  display: block;
  font-size: 0.6875rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-3);
}

.pick .word {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: clamp(1.375rem, 7vw, 1.75rem);
  font-weight: 700;
  letter-spacing: 0.08em;
  line-height: 1.2;
  min-width: 0;
  overflow-wrap: anywhere;
}

.pick .figure {
  margin-inline-start: auto;
  text-align: end;
  font-size: 0.8125rem;
  color: var(--text-2);
  font-variant-numeric: tabular-nums;
  min-width: 0;
}

.pick .figure b {
  display: block;
  color: var(--text);
  font-size: 1rem;
}

/* --- alternatives ------------------------------------------------------- */
.alts {
  display: grid;
  gap: 0.375rem;
}

.alts-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem;
  font-size: 0.75rem;
  color: var(--text-3);
}

.sugg {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 0.5rem 0.75rem;
  border-radius: var(--radius);
  background: var(--chip);
  border: 1px solid transparent;
  border-inline-start: 3px solid var(--border-strong);
  cursor: pointer;
  width: 100%;
  font: inherit;
  color: inherit;
  text-align: start;
  -webkit-tap-highlight-color: transparent;
}

/* A live candidate could win outright this turn; a probe cannot. The bar is
   the quick signal, the label the unambiguous one. */
.sugg.valid {
  border-inline-start-color: var(--g);
}

.sugg:active {
  background: var(--tile);
}

.sugg .word {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-weight: 700;
  letter-spacing: 0.08em;
  min-width: 0;
  overflow-wrap: anywhere;
}

.sugg .meta {
  display: flex;
  align-items: baseline;
  gap: 0.625rem;
  flex: none;
  color: var(--text-3);
  font-size: 0.8125rem;
  font-variant-numeric: tabular-nums;
}

.sugg .kind {
  font-size: 0.6875rem;
}

.sugg .eg {
  color: var(--text-2);
}

/* --- history ------------------------------------------------------------ */
.history {
  display: grid;
  gap: 0.5rem;
}

.history h2 {
  font-size: 0.75rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-3);
  font-weight: 600;
}

.history .grid {
  max-width: 13rem;
  margin-inline: 0;
  gap: 0.25rem;
}

.history .tile {
  border-radius: 6px;
  font-size: clamp(0.75rem, 3vw, 0.875rem);
}

/* --- remaining candidates ----------------------------------------------- */
.details summary {
  cursor: pointer;
  color: var(--text-3);
  font-size: 0.8125rem;
  min-height: 2.75rem;
  display: flex;
  align-items: center;
}

/* Three columns, and only the bar is allowed to take the slack: the word is a
   fixed five characters and the percentage a fixed shape, so neither needs to
   grow, and letting the bar absorb the remainder keeps the table inside the
   card at any width. */
.cands {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.8125rem;
  table-layout: fixed;
}

.cands th.num {
  text-align: end;
}

.cands th {
  text-align: start;
  font-weight: 600;
  color: var(--text-3);
  font-size: 0.6875rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding-block: 0.25rem;
  border-bottom: 1px solid var(--border);
}

.cands td {
  padding-block: 0.3rem;
  border-bottom: 1px solid color-mix(in oklab, var(--border), transparent 55%);
}

.cands tr:last-child td {
  border-bottom: 0;
}

.cands .w {
  width: 5.5rem;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-weight: 700;
  letter-spacing: 0.06em;
  color: var(--text);
}

.cands .num {
  text-align: end;
  font-variant-numeric: tabular-nums;
  color: var(--text-2);
}

/* The last column carries "N left" and needs the slack; the first two are
   fixed-shape, so give them fixed widths and let this one absorb the rest. */
.cands th.num:last-child,
.cands td.left {
  width: auto;
  color: var(--text-3);
}

.more {
  margin: 0.5rem 0 0;
  color: var(--text-3);
  font-size: 0.75rem;
}

/* --- loading ------------------------------------------------------------ */
.loading {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.625rem;
  color: var(--text-3);
  font-size: 0.875rem;
  min-height: 2.75rem;
}

.spinner {
  inline-size: 1.125rem;
  block-size: 1.125rem;
  flex: none;
  border-radius: 50%;
  border: 2px solid var(--border-strong);
  border-top-color: var(--accent);
  animation: spin 0.9s linear infinite;
}

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}
/* --- pointer and motion preferences ------------------------------------- */
/* Hover lifts are for mice. On touch they stick after a tap and read as a
   stuck button. */
@media (hover: hover) and (pointer: fine) {
  .btn:hover:not([disabled]) {
    background: var(--tile);
    background: color-mix(in oklab, var(--chip), white 8%);
  }
  .btn.primary:hover:not([disabled]) {
    background: color-mix(in oklab, var(--accent), white 12%);
  }
  .tile.tappable:hover {
    transform: translateY(-2px);
  }
  .sugg:hover,
  .pick:hover {
    border-color: var(--accent);
  }
  .sugg:hover {
    border-inline-start-color: var(--accent);
  }
}
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}
@media (min-width: 30rem) {
  :root {
    --gutter: 1.25rem;
  }
  .header h1 {
    font-size: 1.5rem;
  }
}
