/* The chest panel.

   The box is drawn, not shipped: a base, a lid on a hinge, a lock band and a
   glow, all gradients and borders so it costs no image and scales with the
   widget. game.js drives it with data-state on #chest-art:

     idle     closed, slow glow breathing under it
     opening  the lid rattles on the latch for half a second
     open     lid thrown back, light out of the box, a burst of rays

   Then the reveal card takes over the stage: the chest and the card share one
   grid cell, so the prize covers the box instead of pushing the panel taller.
   Every colour below comes from the theme variables in game.css. */

.chest-body {
  width: 100%;
  gap: 6px;
}

/* The rule of the chest, next to the box that keeps it. Side by side they cost
   one short block instead of a full stage plus a paragraph, which is the height
   the rack needs. */
.chest-intro {
  display: flex;
  align-items: center;
  width: 100%;
  gap: 9px;
}

.chest-lead {
  flex: 1 1 auto;
  min-width: 0;
  font-size: .64rem;
  line-height: 1.38;
  text-align: left;
  color: var(--ink-soft);
}

/* ---------------------------------------------------------------- the box */

/* The mark is the chest from the sprite, not the drawn box: at 40px the box's
   gradients and lid read as a brown block, while the icon is the same chest the
   side button wears, which is the shape the player already knows. */
.chest-mark {
  flex: 0 0 auto;
  width: 41px;
  height: 36px;
  filter: drop-shadow(1px 2px 0 rgba(61, 50, 32, .18));
}


.chest-box,
.chest-reveal { grid-area: stage; }

.chest-box {
  position: relative;
  width: 100px;
  height: 82px;
  perspective: 340px;
  perspective-origin: 50% 30%;
  transform-style: preserve-3d;
}

/* Wood grain, a soft shadow under the chest, and the warm light it sits in. */
.chest-base {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 54px;
  border: 2px solid var(--ink);
  border-radius: 4px 4px 9px 9px;
  background:
    radial-gradient(120% 80% at 50% 108%, rgba(0, 0, 0, .32) 0%, transparent 62%),
    repeating-linear-gradient(90deg, rgba(72, 42, 8, .22) 0 2px, transparent 2px 15px),
    linear-gradient(180deg, #d7a23a 0%, #b9822a 46%, #8d6519 100%);
  box-shadow:
    inset 0 2px 0 rgba(255, 236, 180, .55),
    inset 0 -6px 10px rgba(84, 50, 10, .38),
    0 4px 0 -1px rgba(44, 36, 22, .35);
}

/* Two iron bands up the body, drawn once as one background each side. */
.chest-base::before {
  content: '';
  position: absolute;
  inset: 4px 14px 4px;
  border-left: 4px solid rgba(255, 240, 190, .34);
  border-right: 4px solid rgba(255, 240, 190, .34);
  border-radius: 2px;
  pointer-events: none;
}

/* The mouth of the chest: dark when shut, a wash of warm light when open. */
.chest-base::after {
  content: '';
  position: absolute;
  left: 6px;
  right: 6px;
  top: -3px;
  height: 18px;
  border-radius: 50% 50% 4px 4px;
  background: radial-gradient(closest-side, rgba(28, 16, 4, .85) 0%, rgba(28, 16, 4, .2) 78%, transparent 100%);
  transition: background .25s ease;
  pointer-events: none;
}

.chest-lid {
  position: absolute;
  left: -1px;
  right: -1px;
  top: 0;
  height: 34px;
  border: 2px solid var(--ink);
  border-radius: 10px 10px 4px 4px;
  background:
    linear-gradient(180deg, rgba(255, 250, 220, .55) 0%, transparent 42%),
    linear-gradient(180deg, #e9bf60 0%, #d5a03a 58%, #b68129 100%);
  box-shadow:
    inset 0 -2px 0 rgba(90, 54, 10, .4),
    0 3px 0 -1px rgba(44, 36, 22, .3);
  transform-origin: 50% 6%;
  transform: rotateX(0deg);
  transition: transform .38s cubic-bezier(.5, -0.2, .4, 1.4);
  z-index: 2;
}

/* The lid's own strap, so the top reads as planks rather than a slab. */
.chest-lid::before {
  content: '';
  position: absolute;
  inset: 5px 16px;
  border-top: 3px solid rgba(255, 244, 210, .38);
  border-bottom: 3px solid rgba(96, 58, 12, .28);
  border-radius: 2px;
}

.chest-lock {
  position: absolute;
  left: 50%;
  top: 26px;
  width: 20px;
  height: 18px;
  margin-left: -10px;
  z-index: 3;
  border: 2px solid var(--ink);
  border-radius: 3px;
  background:
    radial-gradient(circle at 50% 42%, #6d4c12 0 3px, transparent 3.5px),
    linear-gradient(180deg, #ffe9a0 0%, #f4c84f 55%, #cf9a22 100%);
  box-shadow: inset 0 -2px 0 rgba(120, 76, 12, .45);
  transition: transform .2s ease, opacity .2s ease;
}

.chest-glow {
  position: absolute;
  left: 50%;
  top: 8px;
  width: 132px;
  height: 112px;
  margin-left: -66px;
  border-radius: 50%;
  background: radial-gradient(closest-side, rgba(255, 214, 120, .5) 0%, rgba(255, 196, 84, .2) 52%, transparent 100%);
  opacity: .55;
  animation: chest-breathe 3.6s ease-in-out infinite;
  pointer-events: none;
  z-index: 0;
}

/* The ray burst that only exists in the open state. */
.chest-spark {
  position: absolute;
  left: 50%;
  top: 30px;
  width: 152px;
  height: 152px;
  margin-left: -76px;
  opacity: 0;
  pointer-events: none;
  z-index: 1;
  background: repeating-conic-gradient(from 0deg, rgba(255, 226, 140, .95) 0deg 4deg, transparent 4deg 24deg);
  -webkit-mask: radial-gradient(closest-side, transparent 30%, #000 42% 72%, transparent 84%);
  mask: radial-gradient(closest-side, transparent 30%, #000 42% 72%, transparent 84%);
}

/* -- idle ------------------------------------------------------------------ */

.chest-box {
  animation: chest-idle 4.8s ease-in-out infinite;
}

/* -- opening --------------------------------------------------------------- */

.chest-box[data-state='opening'] {
  animation: chest-rattle .55s cubic-bezier(.36, .07, .19, .97) both;
}

.chest-box[data-state='opening'] .chest-glow {
  animation: chest-flare .55s ease-out both;
}

.chest-box[data-state='opening'] .chest-lock {
  transform: translateY(1px) rotate(-4deg);
}

/* -- open ------------------------------------------------------------------ */

.chest-box[data-state='open'] .chest-lid {
  transform: rotateX(-118deg);
}

.chest-box[data-state='open'] .chest-lock {
  transform: translateY(9px) rotate(6deg);
  opacity: .8;
}

.chest-box[data-state='open'] .chest-base::after {
  background: radial-gradient(closest-side, rgba(255, 236, 170, .98) 0%, rgba(255, 196, 84, .55) 62%, transparent 100%);
}

.chest-box[data-state='open'] .chest-glow {
  opacity: 1;
  animation: chest-flare .6s ease-out both;
}

.chest-box[data-state='open'] .chest-spark {
  animation: chest-rays .62s ease-out both;
}

/* --------------------------------------------------------------- the prize */

.chest-reveal {
  --rarity: #5f7a63;
  --rarity-soft: rgba(95, 122, 99, .16);
  position: relative;
  z-index: 4;
  width: 100%;
  max-width: 210px;
  padding: 9px 10px 10px;
  display: grid;
  justify-items: center;
  gap: 4px;
  border: 3px solid var(--ink);
  border-radius: 14px;
  background:
    radial-gradient(circle at 50% 0%, var(--rarity-soft) 0%, transparent 68%),
    var(--panel);
  box-shadow: 4px 5px 0 var(--shadow), inset 0 2px 0 rgba(255, 255, 255, .8);
  text-align: center;
  animation: reveal-pop .34s cubic-bezier(.2, 1.5, .5, 1) both;
}

.chest-reveal[data-rarity='rare'] {
  --rarity: #4b5bd6;
  --rarity-soft: rgba(75, 91, 214, .18);
}

.chest-reveal[data-rarity='wild'] {
  --rarity: #c9860a;
  --rarity-soft: rgba(255, 193, 49, .26);
  animation:
    reveal-pop .34s cubic-bezier(.2, 1.5, .5, 1) both,
    wild-halo 2.6s ease-in-out .4s infinite;
}

.reveal-badge {
  position: absolute;
  top: -9px;
  left: 50%;
  padding: 2px 8px;
  transform: translateX(-50%);
  border: 2px solid var(--ink);
  border-radius: 999px;
  background: var(--rarity);
  color: #fffdf5;
  font-family: var(--pixel);
  font-size: .48rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  white-space: nowrap;
  box-shadow: 2px 2px 0 var(--shadow);
}

.reveal-art {
  width: 46px;
  height: 46px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  border: 3px solid var(--rarity);
  background: #fffdf7;
  font-size: 22px;
  line-height: 1;
  box-shadow: inset 0 -3px 6px rgba(120, 70, 20, .14);
  overflow: hidden;
}

.reveal-art .ico,
.reveal-art svg { width: 26px; height: 26px; }

.reveal-art img { width: 100%; height: 100%; object-fit: cover; }

.reveal-name {
  font-family: var(--shout);
  font-size: .82rem;
  font-weight: 400;
  line-height: 1.15;
  color: var(--ink);
}

.reveal-tag {
  font-family: var(--pixel);
  font-size: .48rem;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--rarity);
}

.reveal-tag.is-dup { color: var(--ink-soft); }

/* ------------------------------------------------------------- the readout */

.chest-result {
  min-height: 2.2em;
  font-size: .66rem;
  line-height: 1.4;
  color: var(--ink-soft);
}

.chest-open {
  width: 100%;
  max-width: 208px;
  font-size: .74rem;
}

.chest-idle-hint {
  font-size: .6rem;
  color: var(--ink-soft);
}

/* Odds: one row per tier, a track and a number. game.js writes --fill as a
   bare percentage on the row and may also set the bar width itself. */
.chest-odds {
  width: 100%;
  margin-top: 3px;
  padding-top: 6px;
  display: grid;
  gap: 3px;
  border-top: 2px dashed rgba(44, 36, 22, .14);
}

.odd-row {
  --fill: 0;
  display: grid;
  grid-template-columns: 44px minmax(0, 1fr) 30px;
  align-items: center;
  gap: 6px;
  font-family: var(--pixel);
  font-size: .48rem;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

.odd-row b {
  font-weight: 400;
  text-align: left;
  color: var(--odd-ink, inherit);
}

.odd-row i {
  display: block;
  width: var(--fill-w, 100%);
  height: 7px;
  border: 1px solid rgba(44, 36, 22, .22);
  border-radius: 999px;
  background-color: rgba(44, 36, 22, .08);
  background-image: linear-gradient(90deg, var(--odd-a, #8bb38f), var(--odd-b, #5f7a63));
  background-repeat: no-repeat;
  background-size: calc(var(--fill, 0) * 1%) 100%;
}

.odd-row span {
  text-align: right;
  color: var(--odd-ink, var(--ink-soft));
}

.odd-row[data-tier='rare'] { --odd-a: #8f9bf0; --odd-b: #4b5bd6; --odd-ink: #35409c; }
.odd-row[data-tier='wild'] { --odd-a: #ffd873; --odd-b: #c9860a; --odd-ink: #96620a; }
.odd-row[data-tier='common'] { --odd-a: #a8c9a8; --odd-b: #5f7a63; }

.chest-pity {
  font-size: .6rem;
  line-height: 1.35;
  color: var(--ink-soft);
}

.chest-pity b { font-family: var(--pixel); font-size: .55rem; color: var(--ink); }

.chest-pity.is-due {
  color: #96620a;
  font-weight: 700;
}

/* The last pulls, newest first: squares coloured by what came out. */
.chest-log {
  width: 100%;
  min-height: 0;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 4px;
}

.chest-log:empty { display: none; }

.pull-chip {
  width: 24px;
  height: 24px;
  display: grid;
  place-items: center;
  border: 2px solid rgba(44, 36, 22, .3);
  border-radius: 7px;
  background: #fffdf7;
  font-size: 11px;
  line-height: 1;
}

.pull-chip[data-rarity='rare'] { border-color: #4b5bd6; background: #f2f3ff; }
.pull-chip[data-rarity='wild'] { border-color: #c9860a; background: #fff8e2; }
.pull-chip.is-new { box-shadow: 0 0 0 2px rgba(255, 193, 49, .8); }

.chest-tip-row {
  display: flex;
  gap: 6px;
  width: 100%;
}

/* ------------------------------------------------------------- animations */

@keyframes chest-idle {
  0%, 88%, 100% { transform: translateY(0) rotate(0); }
  92% { transform: translateY(-1px) rotate(-.7deg); }
  96% { transform: translateY(0) rotate(.5deg); }
}

@keyframes chest-breathe {
  0%, 100% { opacity: .42; transform: scale(.94); }
  50% { opacity: .72; transform: scale(1.04); }
}

@keyframes chest-rattle {
  0%, 100% { transform: rotate(0) translateY(0); }
  12% { transform: rotate(-5deg) translateY(-3px); }
  26% { transform: rotate(4.5deg) translateY(-1px); }
  40% { transform: rotate(-3.5deg) translateY(-2px); }
  56% { transform: rotate(3deg) translateY(0); }
  72% { transform: rotate(-1.5deg) translateY(-1px); }
  86% { transform: rotate(.8deg) translateY(0); }
}

@keyframes chest-flare {
  0% { opacity: .5; transform: scale(.9); }
  55% { opacity: 1; transform: scale(1.16); }
  100% { opacity: .9; transform: scale(1.02); }
}

@keyframes chest-rays {
  0% { opacity: 0; transform: scale(.5) rotate(0deg); }
  35% { opacity: .95; }
  100% { opacity: 0; transform: scale(1.18) rotate(26deg); }
}

@keyframes reveal-pop {
  0% { opacity: 0; transform: scale(.72) translateY(6px); }
  62% { opacity: 1; transform: scale(1.04) translateY(0); }
  100% { opacity: 1; transform: scale(1) translateY(0); }
}

@keyframes wild-halo {
  0%, 100% { box-shadow: 4px 5px 0 var(--shadow), 0 0 0 0 rgba(255, 193, 49, .55); }
  50% { box-shadow: 4px 5px 0 var(--shadow), 0 0 0 7px rgba(255, 193, 49, 0); }
}

/* A smaller box (the hero embed) still has to fit the ritual. */
@media (max-height: 460px) {
  .chest-stage { min-height: 108px; }
  .chest-box { width: 86px; height: 70px; }
  .chest-base { height: 46px; }
  .chest-lid { height: 30px; }
  .chest-glow { width: 112px; height: 96px; margin-left: -56px; }
  .chest-spark { width: 132px; height: 132px; margin-left: -66px; }
  .chest-odds { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .chest-box,
  .chest-glow,
  .chest-reveal,
  .chest-reveal[data-rarity='wild'] { animation: none !important; }
  .chest-lid { transition: none; }
  .chest-box[data-state='opening'] .chest-lock,
  .chest-box[data-state='open'] .chest-lock { transform: none; }
  .chest-box[data-state='open'] .chest-spark { opacity: .5; animation: none; }
  .chest-box[data-state='open'] .chest-glow { opacity: .9; }
}

/* ------------------------------------------------------------ the pin rack
   A pin is a button, so it is drawn like the button: a face, a rim in the
   event's colour, a gloss at the top left and a ledge under it. The glyph is
   the kit's own vocabulary (holiday.css), which is why a pumpkin pin exists
   exactly when a pumpkin kit does. */

/* The count sits in the panel header: it is what the rack is for, and in the
   body it was one more line between the player and the pins. */
.chest-progress {
  margin: 0 6px 0 auto;
  font-family: var(--pixel);
  font-size: .5rem;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--ink-soft);
  white-space: nowrap;
}

.pin-rack {
  display: grid;
  width: 100%;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 6px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.pin {
  display: grid;
  justify-items: center;
  align-content: start;
  /* 4px keeps the printed name clear of the badge's 3px drop shadow. */
  gap: 4px;
  padding: 6px 3px 5px;
  border: 2px solid rgba(61, 50, 32, .22);
  border-radius: 12px;
  background: rgba(255, 255, 255, .42);
  text-align: center;
}

.pin-badge {
  position: relative;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border: 3px solid var(--pin-rim, #8b6914);
  border-radius: 50%;
  background:
    radial-gradient(ellipse 68% 48% at 34% 22%, rgba(255, 255, 255, .9), transparent 64%),
    var(--pin-face, #f5f0e8);
  box-shadow:
    0 0 0 2px rgba(61, 50, 32, .42),
    0 3px 0 color-mix(in srgb, var(--pin-rim, #8b6914) 52%, #000),
    inset 0 -3px 5px rgba(90, 60, 20, .16);
}

/* The glyph is the event's own shape, drawn in the rim's ink so a pin reads as
   one printed object rather than as a circle with a sticker on it. */
.pin-glyph {
  display: block;
  width: 24px;
  height: 24px;
  color: var(--pin-rim, #6b4a12);
}

/* A pin nobody has pressed for is a blank in the rack, not a missing picture. */
.pin:not(.is-kept) .pin-badge { filter: grayscale(1) opacity(.38); }
.pin:not(.is-kept) .pin-name { color: var(--ink-soft); }

/* The event that is running: the one pin a press right now would add. */
.pin.is-live {
  border-color: color-mix(in srgb, var(--tapp-accent) 62%, rgba(61, 50, 32, .4));
  background: color-mix(in srgb, var(--tapp-accent) 14%, rgba(255, 255, 255, .5));
}
.pin.is-live:not(.is-kept) .pin-badge { filter: none; opacity: 1; }
.pin.is-live .pin-badge {
  box-shadow:
    0 0 0 2px rgba(61, 50, 32, .42),
    0 0 0 5px color-mix(in srgb, var(--tapp-accent) 34%, transparent),
    0 3px 0 color-mix(in srgb, var(--pin-rim, #8b6914) 52%, #000);
}

.pin-name {
  font-size: .54rem;
  font-weight: 600;
  line-height: 1.15;
  color: var(--ink);
}

.pin-when {
  font-family: var(--pixel);
  font-size: .4rem;
  letter-spacing: .02em;
  text-transform: uppercase;
  color: var(--ink-soft);
  white-space: nowrap;
}
.pin.is-live .pin-when { color: #a53a22; }

/* The hero frame gives the panel 320px: the box mark steps out and the rack
   keeps all three rows, which is the whole point of the panel. */
html.embed #panel-chest .panel-tip { display: none; }
html.embed .chest-mark { width: 34px; height: 30px; }
html.embed .chest-lead { font-size: .8rem; line-height: 1.45; }
html.embed .chest-progress { font-size: .78rem; margin-right: 4px; }
html.embed .pin-rack { gap: 5px; }
html.embed .pin-badge { width: 31px; height: 31px; border-width: 2.5px; }
html.embed .pin-glyph { width: 17px; height: 17px; }
html.embed .pin { padding: 5px 2px 4px; gap: 3px; }
html.embed .pin-name { font-size: .78rem; }
html.embed .pin-when { font-size: .76rem; }

/* ------------------------------------------------------------ readable type
   The pin grid carried the smallest words in the widget: in the embedded frame
   a pin name was set at .44rem - 6.2px - and its state at .34rem, which is not
   a label but a texture. These rules come last and carry `html.embed` where the
   frame-shrinking block above does, so they win in both frames. The counter is
   one step under the pin names because it shares the header with the title. */
.pin-name { font-size: .8rem; line-height: 1.25; }
.pin-when { font-size: .78rem; letter-spacing: .01em; }
.chest-progress { font-size: .78rem; }
.chest-lead { font-size: .84rem; line-height: 1.45; }
html.embed .pin-name { font-size: .78rem; line-height: 1.25; }
html.embed .pin-when { font-size: .76rem; }
html.embed .chest-progress { font-size: .78rem; }

/* The readout: what came out, the odds table, the pity counter and the line that
   says what to do next. All of it was 8.4px or less. */
.chest-result { font-size: .84rem; line-height: 1.45; color: var(--ink-soft); }
.chest-idle-hint { font-size: .78rem; line-height: 1.45; }
.chest-pity { font-size: .78rem; line-height: 1.4; }
.chest-pity b { font-size: .72rem; }
.odd-row { font-size: .76rem; grid-template-columns: 52px minmax(0, 1fr) 34px; }
html.embed .odd-row { font-size: .74rem; grid-template-columns: 50px minmax(0, 1fr) 32px; }
/* The pull card: the tier ribbon, the name and the rarity word. */
.reveal-badge { font-size: .64rem; letter-spacing: .08em; }
.reveal-name { font-size: .92rem; }
.reveal-tag { font-size: .7rem; letter-spacing: .06em; }

/* The title, the count and the close cross cannot share one 320px line at
   reading size, and letting the count win pushed "Button chest" onto two lines
   with the count tucked under the second one. The count wraps to a line of its
   own under the title instead. */
#panel-chest .panel-head { flex-wrap: wrap; }
#panel-chest .panel-head h2 { flex: 1 1 auto; }
#panel-chest .chest-progress { order: 3; flex: 0 0 100%; margin: 2px 0 0; }

/* The intro is a mark and a sentence. The sentence is the panel's whole reason
   for existing, so it gets a full column beside the mark rather than a narrow
   strip between it and the edge. */
.chest-intro { align-items: flex-start; }
.chest-intro .chest-lead { margin-top: 0; }
