/* Adapted from dumbphone-apps/s3/connections/css/stylesheet.css — same
   tile/answer/guess-dot/shake structure, recolored to the 4 difficulty
   hexes, but laid out with normal responsive CSS (grid + relative units)
   instead of vw-locked dumbphone sizing. */

/* Self-hosted instead of the Google Fonts CDN — one less external runtime
   dependency, same as everything else this app doesn't rely on at runtime.
   One variable-font file (confirmed via its fvar table: a wght axis,
   100-900, with named instances at exactly the weights used below) backs
   all four @font-face declarations — this is the exact same file Google's
   own CDN was already serving for this family/weight combination, just
   fetched once and committed instead of requested live, so rendering is
   unchanged. Gecko 84 (the KaiOS 3 target) has supported variable fonts
   since Firefox 62, well before that. Only the "latin" unicode subset is
   included — cyrillic/vietnamese/etc. aren't needed for an English-only
   word game. */
@font-face {
  font-family: "Libre Franklin";
  src: url("../assets/fonts/LibreFranklin-Variable.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Libre Franklin";
  src: url("../assets/fonts/LibreFranklin-Variable.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Libre Franklin";
  src: url("../assets/fonts/LibreFranklin-Variable.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Libre Franklin";
  src: url("../assets/fonts/LibreFranklin-Variable.woff2") format("woff2");
  font-weight: 800;
  font-style: normal;
  font-display: swap;
}

:root {
  --color-bg: #ffffff;
  --color-text: #1a1a1a;
  --tile-bg: #efefe6;
  --tile-text: #1a1a1a;
  --skeleton-pulse-bg: #e2e2d6;
  --chosen-bg: #3a3a34;
  --toast-bg: #3a3a34;
  --difficulty-4: #c10000;
  --difficulty-1: #00b000;
  --difficulty-3: #ff8a05;
  --difficulty-2: #fddb00;
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  padding: 24px 16px 48px;
  font-family: "Libre Franklin", -apple-system, BlinkMacSystemFont, sans-serif;
  background-color: var(--color-bg);
  color: var(--color-text);
  display: flex;
  flex-direction: column;
  align-items: center;
}

.panel {
  width: 100%;
  max-width: 560px;
}

h1 {
  text-align: center;
  font-size: 2rem;
  margin: 0 0 4px;
}

.tagline {
  text-align: center;
  opacity: 0.7;
  margin: 0 0 20px;
}

#comment {
  position: fixed;
  top: 16px;
  left: 0;
  width: 100%;
  display: none;
  justify-content: center;
  z-index: 10;
  pointer-events: none;
}

#comment-span {
  font-size: 0.95rem;
  background-color: var(--toast-bg);
  color: #fff;
  border-radius: 8px;
  padding: 8px 16px;
}

.slow-fade {
  visibility: hidden;
  opacity: 0;
  transition: visibility 0s 2s, opacity 2s linear;
}

#date-picker-row {
  display: flex;
  justify-content: center;
  margin-bottom: 20px;
}

/* The nav-selectable D-pad stop is this wrapper, not #date-picker itself —
   see puzzle.js's wireDatePickerWrapper() for why (landing on the real
   input during navigation would pop its native picker prematurely). */
.input-wrap {
  display: inline-block;
  border-radius: 8px;
}

/* Outward, not inset like .card below — .input-wrap has zero padding, and
   its child (the native <input type="date">) renders its own opaque
   background filling the box edge-to-edge, so an inset ring here would
   paint entirely underneath it and never actually be visible (confirmed:
   it wasn't). No confirmed overflow risk from this one on real hardware
   either, unlike .card's last-column tile — it's centered in
   #date-picker-row, not structurally flush against a container edge. */
.input-wrap[nav-selected="true"] {
  box-shadow: 0 0 0 3px rgba(58, 58, 52, 0.4);
}

#date-picker {
  font-family: inherit;
  font-size: 1rem;
  padding: 6px 10px;
  border-radius: 8px;
  border: 1px solid #ccc;
}

.game-div {
  display: flex;
  flex-direction: column;
  align-items: center;
}

#results {
  display: none;
  text-align: center;
  margin-bottom: 24px;
}

/* Set as the nav-selected item the moment the puzzle ends (see gameOver()
   in puzzle.js) — same halo treatment as the other nav-selectable elements. */
#results-content {
  border-radius: 8px;
  height: calc(100vh - 40px);
  display: flex;
  flex-direction: column;
  justify-content: center;
}

/* .share-enabled is added by wireResultsContentClick()/wireAnswersClick()
   only when their SHARE_*_ENABLED flag (top of puzzle.js) is on — the
   whole-area hover cue that goes with the .share-hint button inside it,
   clicking anywhere in either (not just the button) triggers the share. */
#results-content.share-enabled,
#answers.share-enabled {
  cursor: pointer;
}

#results-content[nav-selected="true"] {
  outline: 3px solid rgba(58, 58, 52, 0.4);
  outline-offset: 4px;
}

#display-text {
  font-size: 2.25rem;
  font-weight: 800;
  margin: 0 0 20px;
}

#date-text {
  font-size: 1rem;
  opacity: 0.7;
  margin: 20px 0 0;
}

#result-blocks {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
}

.row {
  display: flex;
  gap: 4px;
}

.result-cell {
  width: 28px;
  height: 28px;
  border-radius: 6px;
}

#puzzle {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 8px;
  width: 100%;
}

#answers {
  grid-column: 1 / -1;
  display: flex;
  flex-direction: column;
  gap: 8px;
  border-radius: 8px;
}

/* Visible "this is clickable" affordance above KaiOS width — at 240px the
   softkey bar's center label ("Share") already conveys it, and there's no
   spare vertical space for a second cue there. Purely visual: both
   buttons are aria-hidden/tabindex="-1", their click just bubbles up to
   the real #results-content/#answers listener. */
.share-hint {
  display: block;
  margin: 12px auto 0;
  background-color: #fff;
  border: 1px solid var(--chosen-bg);
  padding: 8px 16px;
  border-radius: 20px;
  color: #000;
  font-size: 0.85rem;
  font-family: inherit;
  cursor: pointer;
  user-select: none;
}

.share-hint:hover {
  background-color: var(--tile-bg);
}

@media (max-width: 240px) {
  .share-hint {
    display: none;
  }
}

/* #answers itself is the D-pad stop for every solved group so far (see
   drawAnswer()/clearThePuzzle() in puzzle.js) — one halo around the whole
   stack, not one per row. Outline rather than the .card/.answer-row
   box-shadow style: #answers has no background of its own to box-shadow
   against, just whatever solved rows happen to be inside it. */
#answers[nav-selected="true"] {
  outline: 3px solid rgba(58, 58, 52, 0.4);
  outline-offset: 4px;
}

.card {
  /* aspect-ratio isn't available on the KaiOS 3 / Gecko 84 target (shipped
     in Firefox 89) — the classic percentage-padding trick instead: a
     padding-top percentage always resolves against the element's own
     WIDTH regardless of axis, so padding-top: 100% forces the height to
     match the width in any CSS engine back to ~2010. */
  position: relative;
  width: min(calc(((100vw - 32px - 24px) / 4)), 134px);
  height: min(calc(((100vw - 32px - 24px) / 4)), 134px);
}

@media (max-width: 240px) {
  .card {
    width: calc((100vw - 8px - 6px) / 4);
    height: calc((100vw - 8px - 6px) / 4);
  }
}

.card > div {
  width:100%;
  height:100%;
  border-radius: 8px;
  background-color: var(--tile-bg);
  color: var(--tile-text);
  display: flex;
  justify-content: center;
  align-items: center;
  overflow-wrap: anywhere;
  text-align: center;
  padding: 6px;
  font-weight: 700;
  font-size: clamp(0.65rem, 3.2vw, 1rem);
  text-transform: uppercase;
  cursor: pointer;
  user-select: none;
  -webkit-user-select: none;
  transition: background-color 0.1s ease;
}

/* Placeholder tiles shown while getThePuzzle() is fetching -- see
   showPuzzleSkeleton() in puzzle.js. Same aspect-ratio/sizing trick as
   .card above (deliberately not sharing its class -- code elsewhere
   queries .card assuming every match is a real tile) so the grid holds its
   4x4 shape with no layout jump once the real tiles replace these. */
.skeleton-tile {
  position: relative;
  width: min(calc(((100vw - 32px - 24px) / 4)), 134px);
  height: min(calc(((100vw - 32px - 24px) / 4)), 134px);
}

@media (max-width: 240px) {
  .skeleton-tile {
    width: calc((100vw - 8px - 6px) / 4);
    height: calc((100vw - 8px - 6px) / 4);
  }
}

.skeleton-tile > div {
  width:100%;
  height:100%;
  border-radius: 8px;
  background-color: var(--tile-bg);
  animation: skeleton-pulse 1.2s ease-in-out infinite;
}

@keyframes skeleton-pulse {
  0%,
  100% {
    background-color: var(--tile-bg);
  }
  50% {
    background-color: var(--skeleton-pulse-bg);
  }
}

.answer {
  grid-column: 1 / -1;
  /* Same height as a single tile. Container queries (cqw) aren't available
     on Gecko 84 either (shipped in Firefox 110), and there's no old-CSS way
     to size one element off a *different* element's width — puzzle.js's
     syncAnswerRowHeight() measures a live .card's rendered height and sets
     this custom property instead; the px fallback only matters for the
     brief instant before that JS runs. */
  height: min(calc(((100vw - 32px - 24px) / 4)), 134px);
  border-radius: 8px;
}

@media (max-width: 240px) {
  .answer {
    height: calc((100vw - 8px - 6px) / 4);
  }
}

.answer > div {
  width: 100%;
  height: 100%;
  border-radius: 8px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  overflow-wrap: anywhere;
  text-align: center;
  padding: 10px;
}

.answer > div > p {
  margin: 0;
}

.answer > div > p:first-child {
  font-size: 1.05rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin-bottom: 6px;
}

.answer > div > p:last-child {
  font-size: 0.85rem;
  text-transform: uppercase;
}

#guesses {
  display: flex;
  align-items: center;
  margin: 16px 0;
  user-select: none;
}

#guesses > span {
  margin-right: 8px;
  font-size: 0.95rem;
}

#guess-spot {
  display: flex;
  align-items: center;
}

.guess {
  width: 12px;
  height: 12px;
  border-radius: 6px;
  background-color: #999;
  display: inline-block;
  margin: 2px;
}

#game-controls {
  display: flex;
  gap: 8px;
}

#game-controls > button {
  background-color: #fff;
  border: 1px solid var(--chosen-bg);
  padding: 10px 18px;
  border-radius: 20px;
  color: #000;
  font-size: 0.9rem;
  cursor: pointer;
  user-select: none;
}

#game-controls > button:hover {
  background-color: var(--tile-bg);
}

#game-controls > button:active {
  background-color: #c7c7bd;
}

.chosen {
  background-color: var(--chosen-bg) !important;
  color: #fff !important;
}

.card[nav-selected="true"] > div {
  box-shadow: 0 0 0 3px rgba(58, 58, 52, 0.5);
}

/* Every nav-selectable element manages its own focus halo above (.card,
   .input-wrap, and #submit-group-link a below) via the nav-selected
   attribute — suppress the browser's native focus outline so that halo is
   the only indicator shown, not both layered on top of each other. */
[nav-selectable="true"] {
  outline: none;
}

/* box-shadow, not outline — outline doesn't render on real KaiOS hardware
   for these two (confirmed on-device), unlike everywhere else in this file
   that already uses box-shadow (.card, .input-wrap, .nav-selectable-ad,
   the settings modal's buttons), which does. */
/* Still outward, unlike .card/.input-wrap/etc below — these two are
   zero-padding inline text links (see #settings-anchor's padding: 0), so
   an inset ring here would sit right on top of the text glyphs instead of
   around them. Lower overflow risk than .card anyway: centered text in a
   full-width <p>, not structurally flush against a container edge the way
   the grid's last column always is. */
#submit-group-link a[nav-selected="true"],
#blog-results-link a[nav-selected="true"],
#settings-anchor[nav-selected="true"] {
  box-shadow: 0 0 0 3px rgba(58, 58, 52, 0.4);
  border-radius: 4px;
}

/* Text color per swatch comes from a --difficulty-N-text custom property,
   computed once at boot by puzzle.js's computeContrastColors() (a WCAG
   relative-luminance calculation) and written onto :root — relative color
   syntax (oklch(from ...)) would do this in pure CSS, but that's a Firefox
   128 feature and isn't available on the KaiOS 3 / Gecko 84 target. The
   #fff fallback only matters for the brief instant before that JS runs. */
.difficulty-1 {
  background-color: var(--difficulty-1);
  color: var(--difficulty-1-text, #fff);
}

.difficulty-2 {
  background-color: var(--difficulty-2);
  color: var(--difficulty-2-text, #fff);
}

.difficulty-3 {
  background-color: var(--difficulty-3);
  color: var(--difficulty-3-text, #fff);
}

.difficulty-4 {
  background-color: var(--difficulty-4);
  color: var(--difficulty-4-text, #fff);
}

@keyframes horizontal-shaking {
  0% {
    transform: translateX(0);
  }
  25% {
    transform: translateX(5px);
  }
  50% {
    transform: translateX(0px);
  }
  75% {
    transform: translateX(-5px);
  }
  100% {
    transform: translateX(0);
  }
}

.shake {
  animation-name: horizontal-shaking;
  animation-duration: 0.125s;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
}

#submit-group-link {
  text-align: center;
  font-size: 0.9rem;
}

/* Hidden until gameOver() in puzzle.js reveals it -- unlike
   #submit-group-link, this stays hidden at every width until the puzzle's
   actually finished, not just at KaiOS width, since "how did you stack up"
   makes no sense before there's a result to compare against. */
#blog-results-link {
  display: none;
  margin-top: 12px;
  text-align: center;
  font-size: 0.9rem;
}

body.game-ended #blog-results-link {
  display: block;
}

#settings-link {
  margin-top: 8px;
  text-align: center;
  font-size: 0.9rem;
}

#submit-group-link a,
#blog-results-link a,
#settings-anchor {
  color: var(--chosen-bg);
}

/* #settings-anchor is a <button>, not an <a> like #submit-group-link — it
   doesn't navigate anywhere, it opens #settings-modal-backdrop (see
   puzzle.js's openSettingsModal()) — so it needs its native button chrome
   stripped to read as the same plain text link. */
#settings-anchor {
  background: none;
  border: none;
  padding: 0;
  margin: 0;
  font: inherit;
  text-decoration: underline;
  cursor: pointer;
}

/* Settings modal (see puzzle.js's openSettingsModal()/selectables()) — a
   fixed-position backdrop, hidden by default and toggled to flex by JS.
   z-index above both #comment (10) and #softkey (20), the only other
   position:fixed elements in this file, so it covers everything including
   the softkey bar. */
#settings-modal-backdrop {
  display: none;
  position: fixed;
  inset: 0;
  align-items: center;
  justify-content: center;
  background-color: rgba(0, 0, 0, 0.5);
  z-index: 30;
  padding: 16px;
}

#settings-modal-backdrop.open {
  display: flex;
}

/* width:90% rather than a fixed px value — same percentage-based approach
   as everything else in this file, comfortably fits down to a real 240px
   KaiOS screen with no extra breakpoint needed (unlike #ad-container's
   fixed-240px case above). */
#settings-modal {
  background-color: #fff;
  border-radius: 12px;
  padding: 20px;
  width: 90%;
  max-width: 320px;
  text-align: center;
}

#settings-modal h2 {
  margin: 0 0 12px;
  font-size: 1.2rem;
}

#settings-modal p {
  margin: 0 0 16px;
  font-size: 0.9rem;
}

#settings-modal-main,
#settings-modal-confirm {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* JS keeps exactly one of these two views visible at a time (see
   showClearDataConfirm()/hideClearDataConfirm()) — hidden here just sets
   the initial state. */
#settings-modal-confirm {
  display: none;
}

#settings-modal button {
  background-color: #fff;
  border: 1px solid var(--chosen-bg);
  padding: 10px 18px;
  border-radius: 20px;
  color: #000;
  font-size: 0.9rem;
  font-family: inherit;
  cursor: pointer;
  user-select: none;
}

#settings-modal button:hover {
  background-color: var(--tile-bg);
}

#settings-modal button:active {
  background-color: #c7c7bd;
}

#settings-modal button[nav-selected="true"] {
  box-shadow: 0 0 0 3px rgba(58, 58, 52, 0.5);
}

#settings-confirm-delete {
  border-color: var(--difficulty-4);
  color: var(--difficulty-4);
}

/* KaiOS ad banner (KaiAds SDK, see puzzle.js's displayAd()) — a persistent
   60px-tall placeholder block regardless of whether an ad is currently
   loaded, same as kaios-gps-location-sharer's own #ad-container, so the
   page doesn't reflow every time displayAd() clears/recreates the ad on its
   refresh interval. Deliberately the very last thing on the page. */
#ad-container {
  width: 100%;
  max-width: 560px;
  height: 60px;
  display: flex;
  justify-content: center;
  align-items: center;
  background-color: var(--tile-bg);
  border-radius: 8px;
  margin: 32px 0 0;
}

/* Not this file's usual nav-selectable="true" attribute — see the comment
   on selectables() in puzzle.js for why the SDK's own navClass has to be a
   plain class instead. Fixed 240x60 to match the size requested from
   getKaiAd(). */
.nav-selectable-ad {
  width: 240px;
  height: 60px;
  max-height: 60px;
  min-height: 60px;
  overflow: hidden;
  outline: none;
}

.nav-selectable-ad[nav-selected="true"] {
  box-shadow: 0 0 0 3px rgba(58, 58, 52, 0.5);
}

/* showAdPlaceholder()'s img (see puzzle.js) — fills the same 240x60 box a
   real ad would. */
.nav-selectable-ad img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* The ad's fixed 240px width exactly matches a real KaiOS screen, but
   body's 4px side padding at this width (see below) would otherwise clip
   8px off it — bleed #ad-container out to the true edges here only. */
@media (max-width: 240px) {
  #ad-container {
    margin-left: -4px;
    margin-right: -4px;
    width: calc(100% + 8px);
    border-radius: 0;
  }
}

/* --- KaiOS softkey bar ------------------------------------------------
   Ported from kaios-calorie-counter/frontend-v3/css/softkey.css: fixed
   bottom bar labeling the phone's physical SoftLeft/center/SoftRight keys.
   Hidden above 240px width — every screen with room for the on-screen
   Shuffle/Deselect/Submit buttons has no hardware softkeys to label. */
@media (max-width: 240px) {
  body {
    padding-bottom: 46px;
    margin-bottom: 30px;
  }
}

#softkey {
  position: fixed;
  bottom: 0;
  left: 0;
  width: 240px;
  height: 30px;
  background: #fff;
  border-top: 1px solid #ccc;
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: 12px;
  font-weight: 600;
  z-index: 20;
}

/* Physical KaiOS softkeys, not usually mouse-clickable — but these labels
   are clickable anyway (see puzzle.js's wireSoftkeyClicks()) so the KaiOS
   interaction model can be tested with a mouse on a laptop, without
   simulating real SoftLeft/SoftRight key events. */
#sk-left,
#sk-center,
#sk-right {
  cursor: pointer;
}

#sk-left,
#sk-right {
  padding: 0 6px;
  color: var(--chosen-bg);
  min-width: 60px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

#sk-right {
  text-align: right;
}

/* "Guess" while fewer than 4 tiles are chosen -- see updateSoftkeys() in
   puzzle.js. Not just a color change: pointer-events:none too, since
   #sk-right is otherwise clickable (see wireSoftkeyClicks()). */
#sk-right.softkey-disabled {
  color: #999;
  pointer-events: none;
}

#sk-center {
  flex: 1;
  text-align: center;
  text-transform: uppercase;
  color: #111;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

@media (min-width: 241px) {
  #softkey {
    display: none;
  }
}

/* The reverse pairing: on-screen Shuffle/Deselect All/Submit are redundant
   with the softkey bar at KaiOS width (and Shuffle has no softkey
   equivalent at all — not needed there, per the user), and would otherwise
   end up scrolled to just behind the fixed softkey bar. */
@media (max-width: 240px) {
  #game-controls {
    display: none !important;
  }
}

/* Tighter spacing for a real ~240px-wide KaiOS screen — the desktop-tuned
   8px gap/16px side padding eats too much of a screen this small. */
@media (max-width: 240px) {
  body {
    padding: 4px;
  }

  #puzzle,
  #answers {
    gap: 2px;
  }
}

/* Compact header: title + date picker share one row, tagline centered on
   its own line below both. #header is otherwise just a plain wrapper — at
   normal widths this is a no-op, since h1/.tagline/#date-picker-row already
   center themselves individually via their own rules above. order (rather
   than reordering the HTML) is what puts #date-picker-row on the first
   line here, since .tagline actually sits between the two in source order. */
@media (max-width: 240px) {
  #header {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
  }

  #header h1 {
    order: 1;
    /* Not flex-grow: 1 — that forces h1 to claim the entire row width
       (it did, up to all 232px available at 240px viewport width), leaving
       nothing for #date-picker-row to sit beside it, so it wrapped to its
       own line. Sized to its own content instead; space-between above is
       what pushes the two apart to the row's opposite edges. */
    flex: 0 1 auto;
    text-align: left;
    font-size: 1.3rem;
    margin: 0;
  }

  #header #date-picker-row {
    order: 2;
    flex: 0 0 auto;
    margin: 0;
  }

  /* The native date input's own intrinsic width (font-size 1rem, 6px/10px
     padding) plus the title still don't both fit in 232px available at
     240px viewport width, even with the title sized to its own content
     above — shrink the input itself too, not just the title. */
  #header #date-picker {
    font-size: 0.8rem;
    padding: 4px 6px;
  }

  #header .tagline {
    order: 3;
    flex-basis: 100%;
    margin: 4px 0;
  }
}

/* Smaller recap grid + tighter gaps to fit a ~240px-wide screen. */
@media (max-width: 240px) {
  #result-blocks {
    gap: 2px;
  }

  .row {
    gap: 2px;
  }

  .result-cell {
    width: 20px;
    height: 20px;
  }

  .answer > div > p:first-child {
    font-size: 0.8rem;
  }

  .answer > div > p:last-child {
    font-size: 0.65rem;
  }
}

/* Submit-a-group is a distraction from today's puzzle on a screen this
   small — only show it once the puzzle's actually done (win or lose).
   Settings and the ad banner stay visible throughout — unlike submitting a
   group, neither competes for attention with actively playing the puzzle.
   document.body.classList "game-ended" is kept in sync with the gameEnded
   variable by puzzle.js's clearThePuzzle()/gameOver(). Opening the link
   itself is handled in JS now (see wireSubmitGroupLink() in puzzle.js) so
   it doesn't navigate away from an in-progress/just-finished puzzle. */
@media (max-width: 240px) {
  #submit-group-link {
    display: none;
  }

  body.game-ended #submit-group-link {
    display: block;
  }

  #ad-container {
    margin: 0;
  }
}

/* Chances above the tile grid, not below — lets the guess count stay
   visible without the puzzle itself being in view on a screen this short.
   .game-div is already display:flex; flex-direction:column, so reordering
   is just an order swap, no HTML/JS changes needed. */
@media (max-width: 240px) {
  #guesses {
    order: 1;
    margin: 4px 0;
  }

  #puzzle {
    order: 2;
  }
}
