@font-face {
  font-family: 'Areal';
  src: url('/fonts/ABCAreal-Regular.ttf') format('truetype');
  font-display: swap;
}

:root {
  --tile: clamp(130px, 15vw, 210px);
  --row: calc(var(--tile) * 0.75);
  --gap: 3px;
  --margin: 2vh;    /* the space around the whole grid, on all four sides */
}

/* Phones: a thinner margin, and photos as large as they can be with 2 columns and 5 rows still fitting
   on the screen. The first value is the width that fits 2 columns; the second is the width whose height
   fits 5 rows. */
@media (max-width: 600px) {
  :root {
    --margin: 0.5vh;
    --tile: min(calc((100vw - 2 * var(--margin) - var(--gap)) / 2), calc((100vh - 2 * var(--margin) - 4 * var(--gap)) / 5 / 0.75));
  }

  /* "svh" is the screen height with the phone browser's bars showing, so 5 rows fit even then. */
  @supports (height: 100svh) {
    :root {
      --tile: min(calc((100vw - 2 * var(--margin) - var(--gap)) / 2), calc((100svh - 2 * var(--margin) - 4 * var(--gap)) / 5 / 0.75));
    }
  }
}

/* The grid scrolls inside this screen-sized frame, both ways, instead of the page itself scrolling.
   The page then is never wider or taller than the screen, which is what keeps phone browsers from adding
   blank space below it or moving the button off the bottom of the screen. Scrolling ends at the last photo. */
.scroller {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  overflow: auto;
  -webkit-overflow-scrolling: touch;
}

body {
  margin: 0;
  background: #fff;
  color: #000;
  font-family: 'Areal', -apple-system, 'Helvetica Neue', Helvetica, Arial, sans-serif;
}

/* A grid where the script gives every showing thing its column and row.
   The margin is the same on all four sides, so the page ends with the same space it starts with
   (change it with --margin at the top of this file). */
.packing {
  position: relative;
  display: grid;
  grid-auto-columns: var(--tile);
  grid-auto-rows: var(--row);
  gap: var(--gap);
  width: max-content;
  padding: var(--margin);
}

.slot {
  position: relative;
}

/* Anything inside a bag stays invisible until the bag is opened. */
.slot[data-inside] {
  visibility: hidden;
}

/* The default: closed things take up no space at all, and the script squeezes out the empty columns
   and rows so everything sits close together. (/packing/?spread keeps each thing's own slot reserved.) */
.compact .slot[data-inside]:not(.shown) {
  display: none;
}

.slot.shown {
  visibility: visible;
  /* How a bag's contents appear: 0.4s long, with the fast-start, soft-landing feel. */
  animation: unpack 0s cubic-bezier(.15, .02, 0, 1) both;
  animation-delay: var(--d, 0ms);
}

/* The hovered slot goes on top so its note covers the neighbours, including one that was just clicked. */
.slot:focus-within {
  z-index: 2;
}

.slot:hover {
  z-index: 3;
}

@keyframes unpack {
  from { opacity: 0; transform: translateY(-10px); }
  to   { opacity: 1; transform: none; }
}

/* How a bag's contents leave when it closes: the same thing backwards, floating up as they fade. */
.slot.shown.leaving {
  animation: repack 0s cubic-bezier(.15, .02, 0, 1) both;
  animation-delay: var(--d, 0ms);
  pointer-events: none;
}

@keyframes repack {
  from { opacity: 1; transform: none; }
  to   { opacity: 0; transform: translateY(-10px); }
}

.tile {
  position: relative;
  display: block;
  box-sizing: border-box;
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  overflow: hidden;
  background: #d0cbc1;
  color: inherit;
  font: inherit;
}

/* A photo that has not been fetched yet shows as the plain tile colour, not a broken-image icon. */
.tile img:not([src]) {
  visibility: hidden;
}

.tile img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.label {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 6px;
  border: 1px dotted #fcb1f2;
  background: #fff;
  font-size: 13px;
  line-height: 1.3;
  text-align: center;
}

.opens {
  cursor: pointer;
}

.tile:focus-visible {
  outline: 2px solid #000;
  outline-offset: -2px;
}

.note {
  position: absolute;
  top: 0;
  left: calc(100% + var(--gap));
  box-sizing: border-box;
  width: 100%;
  min-height: 100%;
  padding: 8px 10px;
  /* Frosted glass: a see-through white, with whatever is behind the box blurred. */
  border: 1px dotted #fcb1f2;
  background: rgba(255, 255, 255, 0.6);
  -webkit-backdrop-filter: blur(40px);
  backdrop-filter: blur(8px);
  font-size: 12px;
  line-height: 1.45;
  white-space: pre-wrap;
  pointer-events: none;
  visibility: hidden;
}

/* In the last column there is no room on the right, so the note opens to the left instead. */
.slot.edge > .note {
  left: auto;
  right: calc(100% + var(--gap));
}

/* The name sits on the photo's top-left corner. */
.name {
  position: absolute;
  top: 6px;
  left: 6px;
  max-width: calc(100% - 12px);
  padding: 2px 6px;
  background: rgba(255, 255, 255, 0.6);
  -webkit-backdrop-filter: blur(40px);
  backdrop-filter: blur(8px);
  font-size: 12px;
  line-height: 1.3;
  pointer-events: none;
  visibility: hidden;
}

/* Names and notes show only while the pointer is on the photo (or it has keyboard focus);
   plain :focus would keep them open after a click. */
.slot:hover > .name,
.slot:hover > .note,
.tile:focus-visible ~ .name,
.tile:focus-visible ~ .note {
  visibility: visible;
}

/* The unpack / repack button stays in the bottom-left corner while the page scrolls. */
.controls {
  position: fixed;
  left: 2vh;
  bottom: calc(2vh + env(safe-area-inset-bottom, 0px));   /* clear of the home bar on phones that have one */
  z-index: 10;
  display: flex;
  gap: 6px;
}

.controls button {
  padding: 4px 10px;
  border: 1px dotted #fcb1f2;
  border-radius: 0;
  background: rgba(255, 255, 255, 0.6);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  color: inherit;
  font: inherit;
  font-size: 12px;
  cursor: pointer;
}

/* While it is running. */
.controls button.playing {
  text-decoration: underline;
}

@media (prefers-reduced-motion: reduce) {
  .slot.shown, .slot.shown.leaving { animation: none; }
}
