/*
  This puzzle uses CSS @property values to allow styling based on the value of the sliders (range inputs)

  In short, the three-way mechanism is:
  - define view-timeline on the range inputs to track their position
  - link view-timeline to @property values on :root using animation-timeline
  - use a style @container to define styles for certain values of the inputs
    (in our case, when all the inputs have the middle value selected, we show
    the "solved" state of the puzzle, and reveal of the prize)

  I borrowed this _brilliant_ technique from Temani Afif of CSS Tip - see
  https://css-tip.com/css-variables-range-slider for his excellent article
  explaining how it all works.

  Thanks Temani!
*/
@property --_sun {
  syntax: "<number>";
  inherits: true;
  initial-value: 0;
}
@property --_moon {
  syntax: "<number>";
  inherits: true;
  initial-value: 0;
}
@property --_earth {
  syntax: "<number>";
  inherits: true;
  initial-value: 0;
}

@property --puzzle-1-solved {
  syntax: "<integer>";
  inherits: true;
  initial-value: 0;
}

@keyframes --_sun {
  0% {
    --_sun: 1;
  }
}
@keyframes --_moon {
  0% {
    --_moon: 1;
  }
}
@keyframes --_earth {
  0% {
    --_earth: 1;
  }
}

:root {
  timeline-scope: --_sun, --_moon, --_earth;
  animation: linear both;
  animation-name: --_sun, --_moon, --_earth;
  animation-timeline: --_sun, --_moon, --_earth;
  animation-range: entry 100% exit 0%;

  --sun-position: calc(1 + 4 * var(--_sun));
  --moon-position: calc(1 + 4 * var(--_moon));
  --earth-position: calc(1 + 4 * var(--_earth));

  /* 1 when all three sliders hit their target positions, else 0 */
  --puzzle-1-solved: clamp(
    0,
    clamp(0, 1 - abs(var(--sun-position) - 3), 1) *
      clamp(0, 1 - abs(var(--moon-position) - 3), 1) *
      clamp(0, 1 - abs(var(--earth-position) - 3), 1),
    1
  );
}

/*
  Define style container at top-level. This makes prize/puzzle solved state
  available globally, so we can use it to "unlock" other parts of the game.
  In this particular game, we only use it to reveal an item within the same
  map, but in theory we could make it open up a new area on another map, or
  trigger some new NPC conversation, etc.
*/
.app {
  container-name: game;
  container-type: style;
}

/*
  Puzzle tile styles
*/
.tile.puzzle {
  background-image: url("../assets/stone-puzzle/tile.webp");
}

/*
  Once the puzzle is solved, we will reveal the prize! For now, just set the
  floor background and hide the item
*/
.tile.tile.prize-puzzle-1 {
  background-image: url("../assets/tiles/cave-floor1.png");
}
.tile.prize .item-wrapper {
  opacity: 0;
  pointer-events: none;
}

/*
  Puzzle interface styles
*/

/*
  Unlike maps and the stego encounter, we want the puzzle UI to "close" on
  it's own, without the user having to press a button. This rules out using a
  <dialog>, so instead we use <details> and <summary>. This does seem to
  introduce a quirk with the positioning of the initial reveal button, so we
  nudge it here
*/
.tile.puzzle summary {
  translate: 0 calc(var(--tile-height) / 2);
}

/*
  Pre-create stacking context so filter animation doesn't cause re-stacking
  This prevents other tiles from appearing over the puzzle when the filter-based
  "solved" animation begins
*/
details.puzzle-wrapper {
  position: relative;
  z-index: var(--stacking-layer-overlay);
  will-change: filter, opacity;
}

/* Hide default arrow marker on expand button of <details>/<summary> */
details.puzzle-wrapper > summary {
  list-style: none;

  &::-webkit-details-marker {
    display: none;
  }
}

/*
  Puzzle interface proper
*/
/*
  Remove stacking context from tile so we can easily position the puzzle UI
  relative to the whole map
*/
.map:has(.puzzle-wrapper[open]) .tile.puzzle {
  position: static;
}
.puzzle-wrapper .puzzle-content {
  width: var(--viewport-width);
  height: var(--viewport-height);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: space-around;
  padding-block: 6rem;
  background-image: url("../assets/stone-puzzle/stone-bg.webp");
}

/*
  Slider styles
*/
/* Labels positioned over each groove */
.puzzle-wrapper .puzzle-content label {
  width: 356px; /* Width of groove image - not responsive */
  height: 5rem;
  display: flex;
  align-items: center;
  font-size: 0; /* Hide label text */
  background: url("../assets/stone-puzzle/groove.webp") center center no-repeat;
}

.puzzle-wrapper input[type="range"] {
  overflow: hidden;
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: 100%;
  background-color: transparent;

  &::-webkit-slider-thumb {
    translate: 0 -2rem;
    appearance: none;
    width: 4rem;
    height: 4rem;
    border: none;
    border-radius: 50%;
    background-size: contain;
    background-repeat: no-repeat;
    background-position: center;
    cursor: pointer;
  }

  /* Transparent track — the groove image is the visual */
  &::-webkit-slider-runnable-track {
    background: transparent;
    height: 0;
  }
}

/* Set slider thumb images and named view-timelines */
input[name="sun"]::-webkit-slider-thumb {
  view-timeline: --_sun inline;
  background-image: url("../assets/stone-puzzle/sun.webp");
}
input[name="moon"]::-webkit-slider-thumb {
  view-timeline: --_moon inline;
  background-image: url("../assets/stone-puzzle/moon.webp");
}
input[name="earth"]::-webkit-slider-thumb {
  view-timeline: --_earth inline;
  background-image: url("../assets/stone-puzzle/earth.webp");
}

/*
  Puzzle solution effects and state:
  - the range input thumbs shake
  - we show a whiteout effect
  - the puzzle interface fades out
  - we replace the stone dias on the puzzle tile with rubble
  - the prize tile shows its item

  This uses the CSS var set way up top, derived from the @property values
  which are tracking the values of each range input
*/
@keyframes puzzle-solved-outro {
  0% {
    opacity: 1;
    filter: brightness(1);
  }
  40% {
    opacity: 1;
    filter: brightness(8);
  }
  100% {
    opacity: 0;
    filter: brightness(1);
    visibility: hidden;
    pointer-events: none;
  }
}
@container game style(--puzzle-1-solved: 1) {
  /* Switch dias tile to rubble */
  .tile.puzzle {
    background-image: url("../assets/stone-puzzle/tile-after.webp");
  }

  /* Shake all three inputs for DRAMA */
  input[name="sun"],
  input[name="moon"],
  input[name="earth"] {
    animation: tremor 0.5s ease-in-out 3;
  }
  /* After 2 shake cycles, flash white then fade puzzle out */
  details.puzzle-wrapper {
    animation: puzzle-solved-outro 1.5s 1s forwards;
  }

  /* Show item prize and allow picking up */
  .tile.prize-puzzle-1 .item-wrapper {
    opacity: 1;
    pointer-events: auto;
  }

  /*
    Hide open puzzle button - the puzzle can only be done once
    (because, well, it explodes)
  */
  .tile.puzzle .summary-open {
    display: none;
  }
}
