/*
  Basic styling for the daily laddergram game.  
  The layout is responsive and scales down gracefully on small screens.  
*/

body {
  font-family: Arial, Helvetica, sans-serif;
  /* Dark mode background based on HSV(240,5,7) ≈ rgb(17,17,18) */
  background-color: #111112;
  color: #d7dadc;
  margin: 0;
  padding: 0;
  /* Remove flex layout on the body so the page height can expand
     naturally when the board grows beyond the viewport.  Without flex
     and a fixed minimum height, the browser will generate a normal
     document flow, allowing the page to scroll vertically on mobile. */
  /* display: flex; */
  /* justify-content: center; */
  /* align-items: flex-start; */
  /* min-height: 100vh; */
  /* Prevent horizontal scroll but allow vertical scrolling so the page can
     move naturally on mobile devices.  Previously vertical overflow was
     hidden, but restoring auto scrolling improves usability when the
     board exceeds the viewport height. */
  overflow-x: hidden;
  overflow-y: auto;
}

/*
  Define a CSS variable for the game tile size.  This variable
  controls the width and height of each tile as well as the
  font-size of the letter inside the tile.  Using a single
  variable makes it easy to scale the entire board based on
  viewport dimensions without having to override multiple
  properties.  The default size corresponds to the desktop design.
*/
:root {
  /* Tile size scales based on viewport height using a clamp().  The
     middle value ties the tiles to 5% of the viewport height, which
     helps ensure the entire board and keyboard fit within the screen
     without scrolling.  A minimum of 20px prevents the tiles from
     becoming too small, and a maximum of 60px matches the desktop
     design. */
  --tile-size: clamp(20px, 5vh, 60px);
}

/* Prevent the page itself from scrolling.  By hiding vertical overflow
   on the html element, the entire document remains static on mobile
   devices. */
html {
  overflow-x: hidden;
  /* Allow vertical scrolling on the root element.  Combined with the
     body’s overflow-y: auto, this enables the page to scroll while
     keeping the keyboard fixed to the bottom. */
  overflow-y: auto;
}

/* Position the game container relative so that absolutely positioned
   decorative elements (like the ladder) can be placed inside it. */
.container {
  position: relative;
}
/* Wrap the board and camera button together so the camera sits next to the board.
   The container is a flex row to align items horizontally.  On wider screens
   this will place the camera button to the right of the board.  The wrapper
   expands to the available width but allows the board to shrink/grow
   naturally with the viewport height. */
.board-container {
  display: flex;
  /* Stretch items vertically so the camera button can extend to match
     the height of the puzzle board.  This keeps the board and its
     controls aligned in a tidy column while allowing the camera
     button to become a tall sidebar on the right. */
  align-items: stretch;
  justify-content: center;
  gap: 16px;
  width: 100%;

  /* Provide top margin so that the puzzle and the camera button are
     offset downward from the header.  Moving the margin to the
     container (instead of the board itself) ensures the camera aligns
     perfectly with the top of the board. */
  margin-top: 20px;
}

/* Board area
   ----------
   Wrap the board container and the retry button in a vertical flexbox.  This
   centres both the horizontal board/camera row and the retry button beneath
   it.  Without this wrapper the retry button would align to the left due to
   the board container’s own margins. */
.board-area {
  display: flex;
  flex-direction: column;
  align-items: center;
  /* Set relative positioning so that absolutely positioned children
     (such as the hide-all button) can be placed relative to this
     container. */
  position: relative;
}

/* Add bottom padding to the main content to account for the fixed
   keyboard. Without this padding, the last rows of the board can be
   obscured by the keyboard. The padding is set as a percentage of
   the viewport height to scale with different screen sizes. */
main {
  padding-bottom: 35vh;
}

/* When the screen is very narrow, stack the camera below the board so it
   remains visible.  This rule kicks in below 400px width. */
@media (max-width: 400px) {
  /* Keep the board and camera side by side even on very narrow screens.
     Previously the camera dropped below the board on small widths, but
     with a tall camera button the layout remains usable while
     preserving alignment. */
  .board-container {
    flex-direction: row;
  }
}

/*
  Decorative ladder positioned to the right of the game board.  It subtly
  reinforces the laddergram theme without interfering with gameplay.  The
  ladder consists of two vertical rails and evenly spaced rungs created
  via CSS gradients.  Opacity is kept low so that it appears faintly in
  the background.
*/
.ladder {
  position: absolute;
  /* Position the ladder so it aligns roughly with the board.  These values
     place the ladder starting below the header and ending above the
     keyboard area.  Adjust as needed for your layout. */
  top: 110px;
  right: 10px;
  /* Fix the ladder height so it does not change when the game board grows
     or shrinks.  With enlarged tiles (60px) and arrow rows (10px),
     the board height becomes approximately 540px.  Adjust the ladder
     height accordingly so the decorative rungs align with the board. */
  height: 540px;
  width: 40px;
  /* Make sure the ladder does not capture any pointer events so it
     doesn’t interfere with input. */
  pointer-events: none;
  /* Keep the ladder semi‑transparent so it appears faintly on the dark
     background without distracting from gameplay. */
  opacity: 0.15;
  /* Use repeating-linear-gradient to draw the rungs.  Rungs are 2px tall
     lines separated by 46px of empty space to match the taller board. */
  background-image: repeating-linear-gradient(
    to bottom,
    #565758 0,
    #565758 2px,
    transparent 2px,
    transparent 46px
  );
  /* Offset the pattern downward so that the first rung is not at the very
     top of the ladder.  Increase the offset slightly to scale with
     the larger tile height, leaving room for two handle rails at the
     top. */
  background-position: 0 30px;
  /* Transparent background behind the ladder; only the rails and rungs
     should be visible. */
  background-color: transparent;
  /* Remove the debug outline used during development. */
  outline: none;
}
/* Create the two vertical rails of the ladder using pseudo-elements. */
.ladder::before,
.ladder::after {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  width: 2px;
  background-color: #565758;
}
.ladder::before {
  left: 0;
}
.ladder::after {
  right: 0;
}

/* Increase the overall container size so the game takes up more of
   the available viewport.  A larger max‑width allows the board and
   buttons to scale better on desktop screens. */
.container {
  width: 100%;
  max-width: 800px;
  padding: 20px;
  box-sizing: border-box;
  /* Centre the game container horizontally now that the body no longer
     uses flexbox.  The automatic margins will ensure the container
     remains centred on larger screens while still filling the width on
     mobile. */
  margin-left: auto;
  margin-right: auto;
}

/* Help button in the header.  Position absolute inside the
   container so it sits at the top right.  Keep the size small and the
   colour muted so it doesn’t distract from the puzzle. */
.help-btn {
  position: absolute;
  top: 16px;
  right: 16px;
  background: transparent;
  border: none;
  color: #565758;
  /* When used for the donation button, additional classes override
     the background, padding and colour.  The base help button retains
     these defaults for other deployments. */
  /* Reduce the font size so longer labels such as "Buy me a Coffee" fit
     neatly in the header without overwhelming the layout. */
  font-size: 1rem;
  font-weight: bold;
  cursor: pointer;
  padding: 4px 8px;
  line-height: 1;
}

/* Streak display near the help button.  Positioned absolutely so it sits
   just to the left of the question mark icon.  Uses the same muted
   colour as the help button to avoid drawing too much attention. */
.streak-info {
  position: absolute;
  top: 16px;
  /* Position streak display farther left to accommodate a longer label on
     the help/donation button.  Increasing this value ensures the
     elements do not overlap when the button text is wider. */
  /* Move the streak display further left to provide spacing next to the
     wider donation button. */
  right: 200px;
  font-size: 1rem;
  color: #565758;
  font-weight: bold;
}
.help-btn:hover {
  color: #d7dadc;
}

/* Restart button
   ---------------
   A small button that appears in the top-left corner after the first
   valid guess.  It allows players to reset the current puzzle to
   its initial state without affecting the daily streak.  Styled to
   match the help and streak controls with subtle colouring and
   absolute positioning. */
.restart-btn {
  position: absolute;
  top: 16px;
  left: 16px;
  background: transparent;
  border: none;
  color: #565758;
  font-size: 1rem;
  font-weight: bold;
  cursor: pointer;
  padding: 4px 8px;
  line-height: 1;
}
.restart-btn:hover {
  color: #d7dadc;
}

/* Small credit line at the bottom.  Centre the text and reduce
   opacity so it remains unobtrusive. */
/*
  Credit line styling
  --------------------
  Position the credit line at the very bottom of the page and keep
  the text small and unobtrusive.  A reduced opacity and softer colour
  ensure it doesn’t draw attention away from the puzzle.  On very
  narrow screens the font size shrinks further to maintain legibility.
*/
.credit {
  text-align: center;
  margin-top: 20px;
  font-size: 0.8rem;
  /* Use a muted grey for the credit text and reduce the opacity to
     make it even more subtle.  This combination keeps the credit
     visible without distracting from the game. */
  color: #565758;
  opacity: 0.4;
}

/* Responsive design adjustments for smaller screens.  Reduce the
   tile size and related elements so the game fits comfortably on
   mobile devices.  The breakpoints here are approximate; adjust
   further if needed for very narrow screens. */
@media (max-width: 600px) {
  /* Scale down the tile size for medium-small screens.  The tile
     dimension will not exceed 48px on devices narrower than 600px.
     The middle value ties the size to the viewport height so that
     the board also responds to changes in the y axis. */
  :root {
    --tile-size: clamp(20px, 5vh, 48px);
  }
  .ladder {
    /* Hide the decorative ladder on small screens to maximise the
       available horizontal space for the board and keyboard.  When
       visible, the ladder reduces the usable width on narrow devices,
       causing the layout to feel cramped. */
    display: none;
  }

  /* On mobile screens, enlarge the on‑screen keyboard keys for easier
     tapping.  Increase the padding and minimum width so that the
     buttons are more finger-friendly.  Also bump the font size
     slightly. */
  .keyboard .key {
    padding: 12px;
    min-width: 40px;
    font-size: 0.9rem;
  }
  .keyboard .key.large {
    min-width: 72px;
  }
}

/* Responsive adjustments based on viewport height
   ------------------------------------------------
   In addition to width-based breakpoints, the game should also scale
   down gracefully on devices with limited vertical space.  These
   breakpoints adjust the tile and font sizes when the available
   height drops below certain thresholds. */
@media (max-height: 700px) {
  /* No need to set the h1 font size here because the clamp() in the
     base h1 definition handles scaling relative to the viewport
     height. */
  /* Limit the maximum tile size on shorter screens to prevent the
     board from overflowing vertically.  Tiles will not exceed 48px
     when the viewport height drops below 700px. */
  :root {
    --tile-size: clamp(20px, 5vh, 48px);
  }
}

@media (max-height: 500px) {
  /* The h1 font size scales via clamp() so no explicit override is
     necessary in this breakpoint. */
  /* On very short screens, shrink the tiles further so the entire
     game remains visible.  Cap the tiles at 40px with the same
     viewport-height scaling. */
  :root {
    --tile-size: clamp(20px, 5vh, 40px);
  }
}

@media (max-width: 400px) {
  /* For extremely narrow phones, reduce the maximum tile size further.
     Tiles will not exceed 40px and will continue to scale with the
     viewport height thanks to the clamp() definition. */
  :root {
    --tile-size: clamp(20px, 5vh, 40px);
  }
  .ladder {
    display: none;
  }
  .help-btn {
    font-size: 1.2rem;
    top: 12px;
    right: 12px;
  }
  .credit {
    font-size: 0.7rem;
  }

  /* On very narrow screens, reduce the key padding slightly to fit
     within the limited width while still keeping keys comfortably
     tapable. */
  .keyboard .key {
    padding: 10px;
    min-width: 36px;
    font-size: 0.85rem;
  }
  .keyboard .key.large {
    min-width: 64px;
  }
}

header {
  text-align: center;

  /* Add top padding to push the title and date below the top control row (restart, streak, help/donation).
     Without this padding, the h1 and puzzle info appear on the same horizontal line as the buttons,
     which can cause overlap.  Setting a fixed padding ensures the title sits on a separate row
     underneath the streak counter and help/donation buttons.  Adjust this value as needed
     depending on the height of the controls. */
  padding-top: 60px;

  /* Raise the header above the confetti overlay so the puzzle title and
     date remain visible during celebrations.  Without a higher z-index,
     falling confetti may temporarily cover the header, making it appear
     to disappear on puzzle completion. */
  position: relative;
  z-index: 10001;
}

h1 {
  margin: 0 0 10px;
  /* Scale the title relative to the viewport height so that it
     decreases on short screens and grows up to 2rem on larger
     displays.  The clamp() function keeps the font between 1rem
     (16px) and 2rem (32px) while tying the middle value to 3% of
     the viewport height. */
  font-size: clamp(1rem, 3vh, 2rem);
}

#puzzle-info {
  margin: 0;
  font-size: 1.1rem;
  color: #666;
}

.board {
  display: grid;
  /* Use different row and column gaps so that the vertical spacing can be
     fine‑tuned independently of the horizontal spacing.  A smaller row gap
     makes the arrow row feel more subtle. */
  column-gap: 6px;
  /* Remove vertical gap between rows entirely.  The arrow row height now controls
     when space appears between words, and no gap is shown until a guess is
     submitted. */
  row-gap: 0;
  justify-content: center;
  /* Remove the top margin on the board itself.  The board container
     will provide vertical spacing so that both the board and camera
     align correctly.  Without this removal, the camera button would
     overshoot the top of the board. */
  margin-top: 0;
  /* Allow the board to grow vertically.  Instead of constraining its
     height and forcing an internal scroll, rely on the page scroll to
     reveal additional guess rows.  This improves usability on mobile,
     where swiping anywhere on the page should scroll naturally. */
  overflow-y: visible;
}

/* When the board is displaying numbered rows after the puzzle is solved,
   allow vertical scrolling on small screens.  Limiting the height and
   enabling overflow ensures that players can scroll through all their
   guesses on mobile devices where the page height may be constrained. */
.board.show-numbers {
  /* Restrict the visible height of the board to a portion of the
     viewport.  On narrow screens this height allows several rows
     to remain visible while enabling natural scrolling for longer
     ladders.  Adjust the percentage as needed to balance between
     board visibility and space for other controls. */
  max-height: 60vh;
  overflow-y: auto;
}

/* Each row is a flex container containing tiles */
.row {
  display: flex;
  gap: 6px;
  /* Make rows positioning context for the toggle buttons.  Using
     position: relative allows absolute positioning of small icons
     within each row without affecting the flex layout of tiles. */
  position: relative;
}

/* Enlarge each tile to make the game board fill more of the screen.  A
   larger font size keeps the letters readable within the bigger tiles. */
/*
  Base tile styling.  The width and height of each tile are derived
  from the --tile-size variable defined on :root.  A clamp() is used
  for the font-size so that letters shrink proportionally as the
  tiles become smaller while capping the maximum size at the
  original design.  This ensures that the board remains legible on
  devices with very short viewports without overflowing the tile.
*/
.tile {
  width: var(--tile-size);
  height: var(--tile-size);
  border: 2px solid #3a3a3c;
  display: flex;
  justify-content: center;
  align-items: center;
  /* Scale the font relative to the tile size.  The clamp prevents
     the text from exceeding 1.8rem on large screens and from
     dropping below 0.7rem on very small screens.  The middle value
     ties the font to half the tile size so the letters remain
     centred and proportional. */
  font-size: clamp(0.7rem, calc(var(--tile-size) * 0.5), 1.8rem);
  font-weight: bold;
  text-transform: uppercase;
  background-color: #121213;
  color: #d7dadc;
}

.tile.filled {
  border-color: #565758;
}

/* Colour feedback similar to Wordle */
.tile.correct {
  background-color: #538d4e; /* Wordle green */
  border-color: #538d4e;
  color: #ffffff;
}

.tile.present {
  background-color: #b59f3b; /* Wordle yellow */
  border-color: #b59f3b;
  color: #ffffff;
}

.tile.absent {
  background-color: #3a3a3c; /* Wordle grey */
  border-color: #3a3a3c;
  color: #ffffff;
}

/* Animation: tile pulse when letter is typed */
@keyframes pulse {
  0% { transform: scale(1); }
  50% { transform: scale(1.1); }
  100% { transform: scale(1); }
}
.tile.pulse {
  animation: pulse 0.2s ease;
}

/* Animation: green glow for final word when matching letters are aligned */
@keyframes finalGlow {
  0% {
    box-shadow: 0 0 0px 0 rgba(83, 141, 78, 0);
  }
  50% {
    box-shadow: 0 0 12px 6px rgba(83, 141, 78, 0.8);
  }
  100% {
    box-shadow: 0 0 0px 0 rgba(83, 141, 78, 0);
  }
}
.final-row.glow {
  animation: finalGlow 0.8s ease-out;
}

/* Row collapse animation when game is won */
.row {
  transform-origin: top;
}
/* Collapse animation definition removed; replaced by fade‑out for unused rows when solved */

/* Numbering rows after win: removed default style as numbers are now positioned via .board.show-numbers */

/* Bobbing animation for final row after win */
@keyframes bobbing {
  0% { transform: translateY(0); }
  50% { transform: translateY(-8px); }
  100% { transform: translateY(0); }
}

/* Congratulations box styling */
.congrats-box {
  margin-top: 10px;
  font-size: 1.6rem;
  font-weight: bold;
  color: #538d4e;
  animation: fadeIn 1s ease;
}
@keyframes fadeIn {
  from { opacity: 0; }
  to { opacity: 1; }
}

/* Final row styling shows the goal word at the bottom */
.final-row .tile {
  background-color: #121213;
  border-color: #565758;
  color: #565758;
}

/* When numbers are displayed, the board gets a show-numbers class.
   Each row is given left padding for the number, and numbered rows add a pseudo‑element positioned absolutely.
   This prevents the tiles from shifting when numbers are shown. */
.board.show-numbers .row {
  /* Increase the left padding to make room for both the toggle icon
     (24px) and the row number (32px). */
  padding-left: 56px;
  position: relative;
}
.board.show-numbers .row.numbered::before {
  content: attr(data-num);
  position: absolute;
  /* Offset the row number to sit immediately to the right of the
     toggle icon. */
  left: 24px;
  top: 50%;
  transform: translateY(-50%);
  /* Reserve 32px for the number column */
  width: 32px;
  text-align: center;
  color: #565758;
  font-weight: bold;
}

/* Quick glow effect for matched letters in the final row.
   The background becomes slightly green and a soft glow flashes once when a correct letter is entered. */
@keyframes quickGlow {
  0% {
    box-shadow: 0 0 0 0 rgba(83, 141, 78, 0);
  }
  50% {
    box-shadow: 0 0 12px 6px rgba(83, 141, 78, 0.8);
  }
  100% {
    box-shadow: 0 0 0 0 rgba(83, 141, 78, 0);
  }
}
.final-row .tile.final-glow {
  background-color: rgba(83, 141, 78, 0.2);
  animation: quickGlow 0.5s ease-out;
}

/* Arrow rows sit between guesses to show which letter changed. */
/* Reduce the vertical space between rows.  The arrow row sits between
   guesses and provides a small gap; a shorter height keeps the board
   compact while still showing the arrow. */
/* The arrow row provides a small gap between guesses and shows an arrow
   indicating which letter changed.  Its height is deliberately small to
   minimise vertical space. */
/* The arrow row provides a small gap between guesses and shows an arrow
   indicating which letter changed.  It transitions smoothly from
   collapsed to expanded over one second. */
/* Increase the arrow row height slightly to match the enlarged tiles. */
.arrow-row {
  display: flex;
  gap: 6px;
  justify-content: center;
  align-items: center;
  /* Scale the gap between rows relative to the tile size.  A clamp() ensures
     the gap stays within a sensible range: at most 10px on large screens
     (matching the original design) and never smaller than 4px on very
     short viewports.  The middle value ties the gap to roughly 15% of
     the tile height so that the space scales down along with the board. */
  height: clamp(4px, calc(var(--tile-size) * 0.15), 10px);
  opacity: 1;
  /* Animate the height/opacity of the gap more quickly when it appears.  A shorter
     duration makes the gap feel snappier when the arrow row expands. */
  transition: height 0.3s ease, opacity 0.3s ease;
}

/* When collapsed, the arrow row has no height and is invisible.  This
   prevents any gap appearing until the guess is submitted. */
.arrow-row.collapsed {
  height: 0;
  opacity: 0;
  overflow: hidden;
  pointer-events: none;
}

/* Cells within the arrow row that sit between each guess.  By default the
   arrow cells are invisible; only the one that indicates the changed
   letter will display a downward arrow.  The font-size is deliberately
   larger than normal text to make the arrow more obvious. */
/* Arrow cells default to transparent; only the one marking the changed letter
   will become visible.  A smaller font size makes the arrow more subtle. */
/* Arrow cells default to transparent; only the cell marking the changed
   letter will become visible.  A smaller font size keeps the arrow
   unobtrusive. */
/* Match arrow cell width to the enlarged tile size.  Keep the arrow
   relatively small to maintain a subtle cue between rows. */
.arrow-cell {
  /* Match the arrow cell width to the tile size so the arrows align
     perfectly under each column. */
  width: var(--tile-size);
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 1rem;
  color: transparent;
}

/* When a letter has changed between guesses, the corresponding arrow cell
   receives the .arrow class.  A neutral grey colour is used so the
   arrow stands out against the dark background. */
/* When a letter has changed between guesses, the arrow cell becomes visible.
   Use a subdued grey so the arrow doesn’t distract from the puzzle. */
.arrow-cell.arrow {
  color: #565758;
}

/* Align arrow rows with numbered rows when board.show-numbers is active */
.board.show-numbers .arrow-row {
  padding-left: 32px;
  position: relative;
}

/* Fade out animation used to hide unused rows and the keyboard when solved */
@keyframes fadeOut {
  from {
    opacity: 1;
  }
  to {
    opacity: 0;
  }
}
.fade-out {
  animation: fadeOut 1s ease forwards;
}

/* Flash red animation for invalid guesses.  The tile background turns red
   briefly and then reverts to its original colour. */
@keyframes flashRed {
  0%, 100% {
    /* At the start and end, leave the existing styles unchanged. */
  }
  50% {
    background-color: #b71c1c;
    border-color: #b71c1c;
    color: #ffffff;
  }
}

/* Class to trigger the flash red animation on a tile or row.  The animation
   runs for one second and then is removed from the element via JavaScript. */
.flash-red {
  animation: flashRed 1s ease;
}

/* Smoother jump animation for letters when the player changes two or more letters.
   The tile moves up and down over one second with a softer motion and a brief
   pause at the top. */
@keyframes smoothJump {
  /* Start at rest */
  0% { transform: translateY(0); }
  /* Begin lifting off gently */
  15% { transform: translateY(-6px); }
  /* Reach the peak of the jump slightly higher and hold briefly */
  35% { transform: translateY(-14px); }
  55% { transform: translateY(-14px); }
  /* Descend smoothly */
  75% { transform: translateY(-6px); }
  /* Return to rest */
  100% { transform: translateY(0); }
}

/* Apply the smooth jump animation to tiles.  Use ease-in-out so the motion
   accelerates and decelerates smoothly rather than stopping abruptly. */
.tile.jump {
  animation: smoothJump 1s ease-in-out;
}

/* Hide the old message area; messages are now displayed as toasts */
.message {
  display: none;
}

/* Toggle letters button
   ---------------------
   A small, faint icon displayed on each guessed row of the solved
   puzzle.  Clicking this icon toggles the visibility of the letters
   on that row while leaving the boxes intact.  The button is
   positioned absolutely within the row and kept unobtrusive via a
   low opacity. */
/* Toggle letters button
   ---------------------
   A small icon displayed on each guessed row of the solved puzzle.
   Clicking this icon toggles the visibility of the letters on that
   row while leaving the boxes intact.  On the results screen the
   button sits to the left of the row number column and is slightly
   larger for better accessibility.  Opacity remains low to keep it
   unobtrusive until hovered. */
.toggle-letters-btn {
  position: absolute;
  /* Align the icon at the far left of the row when numbers are shown. */
  left: 0;
  right: auto;
  top: 50%;
  transform: translateY(-50%);
  background: transparent;
  border: none;
  color: #565758;
  opacity: 0.3;
  /* Increase the icon size so it’s easier to tap or click. */
  font-size: 1.4rem;
  cursor: pointer;
  padding: 2px 4px;
  width: 24px;
  text-align: center;
}
.toggle-letters-btn:hover {
  opacity: 0.6;
}
/* When toggled, hide the letters in the tiles of the row.  Only the
   text colour is changed to transparent so the tile boxes remain
   visible with their feedback colours intact. */
.letters-hidden .tile {
  color: transparent;
}

/* Hide All Button
   ---------------
   A button that appears when the puzzle is solved to allow players
   to hide or show all guess rows at once (except the starting and
   ending words).  It is positioned absolutely within the board-area
   container.  The icon is similar to the row toggle icons but
   slightly larger.  Opacity is kept low until hovered. */
.hide-all-btn {
  position: absolute;
  left: 0;
  top: 0;
  /* Provide some margin so it does not overlap row numbers or other
     controls. */
  margin-top: 4px;
  margin-left: 4px;
  background: transparent;
  border: none;
  color: #565758;
  opacity: 0.3;
  /* Reduce the font size so text fits comfortably within the button. */
  font-size: 0.85rem;
  cursor: pointer;
  /* Add padding so the text isn’t cramped and the button has a
     comfortable hit target. */
  padding: 4px 8px;
  line-height: 1.2;
  /* Round the corners slightly to match the overall aesthetic */
  border-radius: 4px;
}
.hide-all-btn:hover {
  opacity: 0.6;
}

/* ------------------------------------------------------------------
   Toast message styling

   Laddergram displays error and informational messages in a manner
   similar to Wordle.  Messages appear in a popup near the top of the
   screen, then fade away after a short delay.  The toast container is
   fixed at the top centre of the viewport and ignores pointer
   interactions so it doesn’t block gameplay.  Each individual toast
   element animates into view using a simple opacity/translate
   transition.  After the specified duration, the toast fades out and
   is removed from the DOM by JavaScript.
------------------------------------------------------------------ */

/* Container that holds one or more toasts.  It sits at the top centre
   of the page and allows toasts to stack vertically if multiple
   messages are displayed close together. */
.toast-container {
  position: fixed;
  top: 20px;
  left: 50%;
  transform: translateX(-50%);
  /* Raise the toast container above all other elements (header, confetti, etc.)
     so that notifications are never obscured by the streak icon or other
     controls.  Use a z-index higher than the header (10001). */
  z-index: 11000;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  pointer-events: none;
}

/* Base styling for individual toast messages.  A light background and
   dark text ensure high contrast on the dark site background.  The
   opacity and transform properties allow a smooth slide-down fade-in
   animation when the .show class is added. */
.toast {
  background-color: #f0f0f0;
  color: #111112;
  padding: 8px 14px;
  border-radius: 6px;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
  font-weight: bold;
  font-size: 1rem;
  opacity: 0;
  transform: translateY(-10px);
  transition: opacity 0.25s ease, transform 0.25s ease;
  pointer-events: all;
}

/* When the toast appears, the .show class is added by JavaScript.  It
   triggers the fade-in and slide-down animation defined above. */
.toast.show {
  opacity: 1;
  transform: translateY(0);
}

/* Keyboard styling */
/*
  Base keyboard styling
  ---------------------
  Make the on‑screen keyboard a fixed element anchored to the bottom of the
  viewport.  This ensures it remains visible at all times regardless of
  page scroll or screen size.  The keyboard spans the full width of the
  page (up to 600px) and its rows fill the available space.  The margin
  on top is removed because the keyboard no longer sits within the
  document flow.
*/
.keyboard {
  position: fixed;
  /* Centre the on‑screen keyboard horizontally on larger screens.  Use
     left: 50% and translateX(-50%) to align the keyboard with the game
     container.  The keyboard spans the full available width up to
     600px.  A bottom offset of 10vh leaves some breathing room
     between the keyboard and the bottom edge of the viewport, as
     requested. */
  left: 50%;
  transform: translateX(-50%);
  bottom: 10vh;
  width: 100%;
  max-width: 600px;
  z-index: 500;
  display: flex;
  flex-direction: column;
  gap: 6px;
  justify-content: center;
  /* Stretch row contents horizontally so keys fill the entire row width */
  align-items: stretch;
}

/* Layout footer as a horizontal flex container.  This positions the
   share button on the left and the camera button on the right of the
   puzzle area.  The maximum width matches the main container width so
   the controls align nicely with the board. */
footer {
  display: flex;
  justify-content: space-between;
  align-items: center;
  max-width: 600px;
  width: 100%;
  margin: 20px auto 0;
  padding: 0;
  box-sizing: border-box;
}

.keyboard-row {
  display: flex;
  gap: 6px;
  /* Centre the keys horizontally within each row on larger screens.
     Without this, the keys align flush to the left, which does not
     resemble the layout of a real keyboard. */
  justify-content: center;
}

/*
  The on‑screen keyboard should maintain its three discrete rows even on
  very narrow devices.  Instead of wrapping the keys onto additional
  lines (which jumbles the layout), allow the individual keys to shrink
  proportionally so that the entire row always fits within the viewport.
  Keys use flexbox to distribute available width evenly and have no
  minimum width on small screens.  Larger keys such as “Enter” and
  backspace receive a slightly larger flex‑grow value so they remain
  visually prominent.
*/
@media (max-width: 600px) {
  /* Remove fixed minimum widths and allow the rows to scale down */
  .keyboard-row .key {
    flex-grow: 1 !important;
    flex-basis: 0 !important;
    min-width: 0 !important;
    /* Use a slightly larger padding and font to help the keys fill
       the available width.  Even though the keys can shrink, they
       should expand proportionally to occupy the entire row whenever
       space allows. */
    padding: 10px !important;
    font-size: 0.9rem !important;
  }
  .keyboard-row .key.large {
    /* Give the enter and backspace keys extra space to distinguish them */
    flex-grow: 1.5 !important;
    /* Remove the fixed minimum width so the key can shrink along with
       the rest of the keyboard on small screens. */
    min-width: 0 !important;
  }
  /* Override the min‑width values defined on .keyboard .key for small
     screens.  Without this override, the keys cannot shrink enough to
     prevent overflowing the row. */
  .keyboard .key {
    min-width: 0 !important;
  }

  /* On small screens override the keyboard positioning.  Remove the
     horizontal centering transform and anchor the keyboard to the left
     edge so it fills the full width.  Allow the max-width to expand
     fully on mobile. */
  .keyboard {
    left: 0;
    transform: none;
    max-width: 100%;
  }
  .keyboard-row {
    width: 100%;
  }

  /* Add extra space at the bottom of the main content so the board
     doesn’t get obscured by the fixed keyboard.  Increase the
     padding to 35vh to accommodate the keyboard being anchored
     directly at the bottom. */
  main {
    padding-bottom: 35vh;
  }
}

@media (max-width: 400px) {
  /* Further reduce padding and font size on very narrow screens */
  .keyboard-row .key {
    padding: 6px !important;
    font-size: 0.7rem !important;
  }
}

.key {
  background-color: #818384;
  border: none;
  border-radius: 4px;
  padding: 10px;
  min-width: 32px;
  text-align: center;
  cursor: pointer;
  font-weight: bold;
  text-transform: uppercase;
  user-select: none;
  color: #ffffff;
}

.key.correct {
  background-color: #538d4e;
  color: #ffffff;
}

.key.present {
  background-color: #b59f3b;
  color: #ffffff;
}

.key.absent {
  background-color: #3a3a3c;
  color: #ffffff;
}

.key.large {
  flex: 1;
  min-width: 60px;
}

/* Share button styling
   ---------------------
   The share button is displayed as a pill to match the design in the
   user’s example.  It uses flexbox to align the text and the share
   icon side by side, with generous padding and a large border radius
   to create a rounded pill shape.  The icon will scale with the
   button and inherits the button’s text colour via currentColor. */
/*
  Share button
  -------------
  Style the share button to look more like the example provided by the
  user.  It appears as a large pill with generous padding and a
  slightly brighter green hue.  A very high border‑radius ensures the
  button becomes an elongated pill regardless of its width.  The text
  and icon are centred via flexbox.  Increasing the horizontal
  padding improves the spacing around the label and icon, helping the
  button feel balanced on both desktop and mobile.
*/
.share-btn {
  margin-top: 20px;
  padding: 14px 32px;
  /* Brighter green similar to Wordle’s share button */
  background-color: #6aaa64;
  color: #ffffff;
  border: none;
  /* Very high radius to create a pill shape */
  border-radius: 9999px;
  font-size: 1rem;
  font-weight: bold;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  line-height: 1;
}

.share-btn:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

/* Style the share icon inside the share button.  The icon has a
   consistent size and inherits the current text colour to ensure it
   remains visible on the green background. */
.share-btn .share-icon {
  width: 24px;
  height: 24px;
  fill: currentColor;
}

/* Camera button styling.  This button appears after the puzzle is solved and
   allows the player to save a screenshot of the completed board.  The style
   mirrors the share button so the controls feel cohesive. */
/*
  Camera button
  -------------
  Mirror the styling of the share button so the two actions feel
  consistent.  Increase the padding and border radius to form a
  pill shape.  Align the icon centrally within the button.  A small
  margin separates the camera button from the share button when both
  are visible.
*/
.camera-btn {
  /* Remove the original top margin since the camera now sits beside the board */
  margin: 0;
  /* Make the camera button a tall sidebar.  It stretches vertically to
     match the height of the board and uses minimal horizontal padding.
     The button maintains the same green colour as other primary
     controls but with a narrower width. */
  padding: 0 12px;
  /* Use a subtle neutral colour for the camera button to reduce its
     visual prominence compared to the primary green controls. */
  background-color: #565758;
  color: #ffffff;
  border: none;
  border-radius: 12px;
  font-size: 1rem;
  font-weight: bold;
  cursor: pointer;
  /* Use flexbox to centre the icon within the tall button */
  display: flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
  /* Stretch the button to fill the height of the board */
  align-self: stretch;
  /* The camera button no longer floats over the ladder.  It sits beside the board
     within the board-container. */
  position: static;
  transform: none;
  z-index: auto;
}

/* Size the SVG camera icon within the button so it scales clearly.  The
   width and height are larger than the surrounding text to improve
   legibility.  A small right margin separates the icon from any
   optional text. */
.camera-btn .camera-icon {
  width: 24px;
  height: 24px;
  margin: 0;
  fill: currentColor;
}

/* Retry button styling.  This button appears when the player runs out of
   guesses and offers a way to restart the puzzle.  It mirrors the
   share button’s appearance with a pill shape and generous padding so
   all controls feel cohesive. */
.retry-btn {
  /* Position the play again button near the bottom of the viewport rather than
     below the board.  Anchoring it with fixed positioning ensures it
     remains visible where the keyboard used to be.  The horizontal centering
     is achieved via a 50% left offset and a translateX transform. */
  position: fixed;
  bottom: 15vh;
  left: 50%;
  transform: translateX(-50%);
  margin-top: 0;
  padding: 14px 32px;
  background-color: #6aaa64;
  color: #ffffff;
  border: none;
  border-radius: 9999px;
  font-size: 1rem;
  font-weight: bold;
  cursor: pointer;
  display: block;
  line-height: 1;
}

.retry-btn:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

.retry-btn[hidden] {
  display: none !important;
}

/* Post‑game note styling
   ----------------------
   When the player completes a puzzle, a gentle message invites them to
   return tomorrow for a new ladder.  Position this note just above
   the Play Again button near the bottom of the viewport. */
.play-again-note {
  position: fixed;
  bottom: 25vh;
  left: 50%;
  transform: translateX(-50%);
  color: #6aaa64;
  font-size: 1rem;
  text-align: center;
  z-index: 10002;
}

/* Completion info styling
   -----------------------
   This paragraph appears under the puzzle once the user has
   completed the daily ladder.  It shows how many other players
   finished today’s puzzle.  The element remains hidden until
   JavaScript updates its text and removes the hidden attribute.
*/
.completion-info {
  margin-top: 8px;
  font-size: 0.85rem;
  color: #6aaa64;
  text-align: center;
  /* Use a higher z-index than the board so the message remains
     visible on top of the puzzle background. */
  z-index: 10002;
}

/* Donate button styling for Buy Me A Coffee.  This button appears
   after the puzzle is solved.  It uses the Buy Me A Coffee colour
   scheme and a pill shape similar to other controls. */
.donate-btn {
  margin-top: 12px;
  padding: 12px 24px;
  background-color: #ffdd00;
  color: #000000;
  border: none;
  border-radius: 9999px;
  font-size: 1rem;
  font-weight: bold;
  cursor: pointer;
  text-decoration: none;
  display: block;
  text-align: center;
}

.donate-btn:hover {
  background-color: #e6c700;
}

.donate-btn[hidden] {
  display: none !important;
}

/* Donation button in the header (replaces the help icon).  Styled as a
   compact pill with Buy Me A Coffee colours.  Applied alongside
   .help-btn to reuse positioning. */
.donate-btn-header {
  /* Use a softer pastel yellow for the donation button to make it feel
     more gentle than the standard Buy Me A Coffee gold.  Reduce the
     font weight and adjust padding so the button blends into the
     header without drawing too much attention. */
  background-color: #ffe480;
  color: #000000;
  border: none;
  border-radius: 9999px;
  /* Use a smaller font and tighter padding so the Buy Me a Coffee
     button remains compact and unobtrusive.  This ensures the
     donation call‑to‑action does not dominate the header on either
     desktop or mobile. */
  font-size: 0.8rem;
  padding: 2px 6px;
  font-weight: 500;
  text-decoration: none;
  line-height: 1;
  /* Fade transition on opacity for gentle appearance */
  transition: opacity 0.5s ease, background-color 0.3s ease;
}

.donate-btn-header:hover {
  /* Slightly darker shade on hover for visual feedback */
  background-color: #ffd04d;
  color: #000000;
}

.camera-btn:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

/* Responsive adjustments for small screens.  The donation button and streak
   display must shrink on narrow devices to avoid overlap.  Use a
   breakpoint around 500px width to reduce font size and padding on
   the donation button and move the streak closer to the left. */
@media (max-width: 500px) {
  .donate-btn-header {
    font-size: 0.7rem;
    padding: 2px 6px;
  }
  .streak-info {
    right: 140px;
  }

  /* On small screens, enlarge the puzzle tiles and reduce bottom padding so the board
     stretches closer to the on‑screen keyboard.  Increasing the viewport percentage
     in the clamp() function makes each tile taller, while a smaller padding‑bottom
     allows more of the board to be visible above the keyboard. */
  :root {
    --tile-size: clamp(24px, 7vh, 64px);
  }

  main {
    padding-bottom: 30vh;
  }
}

/*
 * Confetti animation
 *
 * When a puzzle is solved, colourful confetti pieces fall from the top of
 * the screen to celebrate the win.  Each confetti piece is absolutely
 * positioned within a fixed container that spans the viewport.  CSS
 * animations move the pieces downward while rotating them.  Opacity and
 * duration are varied via inline styles for a natural effect.
 */
.confetti-container {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  overflow: visible;
  z-index: 9999;
}

.confetti-piece {
  position: absolute;
  top: -10px;
  opacity: 0.8;
  will-change: transform, opacity;
  animation-name: confetti-fall;
  animation-timing-function: linear;
  animation-iteration-count: 1;
  /* Duration and delay are set inline via JavaScript */
}

@keyframes confetti-fall {
  to {
    transform: translateY(100vh) rotate(360deg);
  }
}

/* Ensure that buttons marked with the hidden attribute are not displayed.
   Some browsers may ignore the `hidden` attribute on certain elements
   when styles are applied.  Explicitly hide any share or camera button
   that retains the hidden attribute. */
.share-btn[hidden],
.camera-btn[hidden] {
  display: none !important;
}

/* Hide elements when necessary */
.hidden {
  display: none;
}

/* Full‑screen flash overlay used to mimic a camera flash when saving
   the board image.  By default the overlay is invisible; when the
   .flash class is added, a keyframe animation briefly fades the
   overlay in and out.  Pointer events are disabled so it doesn’t
   intercept clicks. */
/*
  Full‑screen flash overlay
  -------------------------
  Covers the entire viewport and flashes white when a screenshot is
  taken.  A very high z‑index ensures it appears above all other
  elements.  Pointer events are disabled so the overlay never
  intercepts input.  The initial opacity is zero; when the .flash
  class is added a brief animation toggles the opacity up and back
  down to simulate a camera flash.  The animation duration is kept
  short to avoid lingering on the screen.
*/
.flash-overlay {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  background-color: #ffffff;
  opacity: 0;
  pointer-events: none;
  /* Use a very high z‑index to ensure the flash appears above
     everything, including modals and toasts. */
  z-index: 10000;
}

/*
  Loading overlay
  ---------------
  Covers the entire viewport and displays a spinner and message when
  checking a word.  A dark semi‑transparent background dims the
  underlying game content, signaling that processing is underway.
  The overlay is hidden by default via the `.hidden` class and is
  shown only when a dictionary lookup takes longer than one second.
  A high z‑index ensures it appears above other elements.  Pointer
  events are enabled to block user interaction while the overlay is
  visible.  The spinner uses a simple border‑based animation.
*/
.loading-overlay {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  /* Dark background with transparency to dim the page */
  background-color: rgba(0, 0, 0, 0.6);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  z-index: 9999;
  /* Use the same text colour as the game for consistency */
  color: #d7dadc;
  font-size: 1.2rem;
  /* Block interaction with underlying elements while visible */
  pointer-events: all;
}
.loading-overlay.hidden {
  display: none;
}

/* Spinner animation for the loading overlay */
@keyframes spin {
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(360deg);
  }
}
.loading-overlay .spinner {
  border: 6px solid rgba(255, 255, 255, 0.2);
  border-top: 6px solid #565758;
  border-radius: 50%;
  width: 48px;
  height: 48px;
  animation: spin 0.8s linear infinite;
  margin-bottom: 12px;
}

@keyframes screenFlash {
  /* Start fully transparent */
  0% { opacity: 0; }
  /* Ramp up quickly to full white */
  20% { opacity: 1; }
  /* Hold the white flash very briefly */
  40% { opacity: 1; }
  /* Fade back down to transparent */
  100% { opacity: 0; }
}

/* When the flash class is added, run the screenFlash animation for one
   second to mimic a camera flash. */
 .flash-overlay.flash {
  /* Further shorten the duration so the flash is even quicker. */
  animation: screenFlash 0.3s ease forwards;
}

/* When the modal also has the hidden class, ensure it overrides its own display */
.how-to-modal.hidden {
  display: none;
}

/* When the hidden checkbox is checked, hide the modal completely.
   This enables CSS‑only dismissal without relying on JavaScript. */
.how-toggle:checked + .how-to-modal {
  display: none;
}

/* How To Play modal overlay */
.how-to-modal {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, 0.8);
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 1000;
}

.how-to-content {
  background-color: #121213;
  border: 2px solid #3a3a3c;
  border-radius: 8px;
  padding: 20px;
  max-width: 480px;
  color: #d7dadc;
  position: relative;
  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.5);
}

.how-to-content h2 {
  margin-top: 0;
}

.how-to-content ul {
  margin-top: 0;
  padding-left: 20px;
}

.how-to-content p {
  margin-bottom: 10px;
}

/* Dismiss button inside the how‑to modal */
.how-to-ok {
  margin-top: 15px;
  padding: 10px 20px;
  background-color: #538d4e;
  border: none;
  border-radius: 4px;
  color: #ffffff;
  font-size: 1rem;
  font-weight: bold;
  cursor: pointer;
}

.how-to-ok:hover {
  background-color: #467741;
}

.how-to-close {
  position: absolute;
  top: 10px;
  right: 10px;
  background: transparent;
  border: none;
  color: #d7dadc;
  font-size: 1.5rem;
  cursor: pointer;
}

/* Example tiles inside modal */
/*
  Example tiles inside the modal are sized the same as the game tiles so that
  the arrows align perfectly beneath each letter.  Margins are removed and
  spacing between tiles is handled by the flex gap on the parent row.  The
  font size is reduced slightly so the example letters look proportional
  within the smaller example area.
*/
.tile.example {
  width: 48px;
  height: 48px;
  font-size: 1.2rem;
  margin: 0;
}

.example-row {
  display: flex;
  gap: 6px;
}

/* Additional spacing around the example section in the How To Play modal */
/*
  Centre the example section horizontally within the instructions modal and
  stack the example rows and arrow rows vertically.  Using flexbox here
  ensures that the example stays centred regardless of the modal width.
*/
.example-section {
  margin-top: 10px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  row-gap: 4px;
}

/* Bold heading for the example section.  Centre-align to match the
   example below and provide a small margin below for separation. */
.example-heading {
  font-weight: bold;
  text-align: center;
  margin: 12px 0 4px;
  font-size: 1.1rem;
}