/* O&Z games: five covers, each one the way into its game. The covers are the page: they stand as
   cards in a shallow space on the night, in a slight bow, facing the middle. They arrive one after
   the other; the one under the pointer (or holding the keyboard's focus) comes forward into a pool
   of its own cover's colour with a sheen across it, and the others step back (games.js turns it
   towards the pointer and lets the whole row follow the pointer a little; everything else is here).

   site.css comes first and gives the colours, the type, the spacing, the easing and the top bar.

   Three layouts, by the width of the window:
     row     1280 px and wider: all five in one row, each at least about 220 px wide
     3 + 2   narrower: three, and two centred under them
     swipe   a phone (560 px and narrower): one cover at a time, the next one showing at the edge
   In all of them a cover is as large as the window allows: by its width, and by its height so that
   a whole row stands on the screen under the heading (--w-room, --h-room). */

/* What games.js writes every frame while the pointer moves. They are numbers that go no further
   than the element they are set on, so a new value restyles that one element and nothing below it. */
@property --px { syntax: "<number>"; inherits: false; initial-value: 0; }
@property --py { syntax: "<number>"; inherits: false; initial-value: 0; }
@property --rx { syntax: "<number>"; inherits: false; initial-value: 0; }
@property --ry { syntax: "<number>"; inherits: false; initial-value: 0; }
@property --sx { syntax: "<number>"; inherits: false; initial-value: 0; }

.games {
  --n: 5;                                            /* covers in a row */
  --kk: 4;                                           /* the outermost cover's place in the bow, squared */
  --gap: clamp(12px, 1.4vw, 28px);
  --say: clamp(2.25rem, 0.6rem + 5.4vw, 6.75rem);    /* the heading's size */
  --say-gap: clamp(18px, 4.6vh, 56px);               /* between the heading and the covers */
  --pad: clamp(12px, 3.4vh, 44px);
  --base: 4.5rem;                                    /* the foot's height */
  --floor: 13.75rem;                                 /* a cover is not made narrower than this to fit the height */
  --turn: 5.5deg;                                    /* how far each cover in the bow is turned from the next */
  --bow: 0.045;                                      /* and how far it stands back for it (a share of its width, times its place squared) */
  --vh: 100vh;
  min-height: 100vh; min-height: 100svh;
  display: flex; flex-direction: column;
}
@supports (height: 100svh) {
  .games { --vh: 100svh; }
}

/* main is what the covers are measured against (100cqi is its width between the gutters) */
.games main {
  flex: 1 0 auto; display: flex; flex-direction: column; justify-content: center;
  padding: calc(var(--bar) + var(--pad)) var(--gutter) var(--pad);
  container-type: inline-size;
}

/* the heading and the covers, as one block in the middle; the heading begins where the first cover does */
.set {
  --w-room: calc((100cqi - (var(--n) - 1) * var(--gap)) / var(--n));
  --h-room: calc((var(--vh) - var(--bar) - var(--say) - var(--say-gap) - var(--base) - 3 * var(--pad)) * 0.75);
  --card-w: min(var(--w-room), max(var(--h-room), var(--floor)));
  --r: calc(var(--card-w) * 0.055);                  /* a card's corner */
  width: fit-content; max-width: 100%; margin: 0 auto;
}

.say {
  margin: 0 0 var(--say-gap) -0.05em;
  font-size: var(--say); font-weight: 700; line-height: 1; letter-spacing: -0.04em; white-space: nowrap;
}

/* ---------------------------------------------------------------- the space the cards stand in
   .stage is the eye (its perspective grows with the cards, so the bow looks the same at every size);
   .halo lies flat behind everything and holds each cover's pool of light; .deck is the row itself,
   which games.js turns a little after the pointer (--px, --py). */
.stage { position: relative; perspective: calc(var(--card-w) * 6.5); }

.deck, .halo {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(var(--n), var(--card-w)); gap: var(--gap); justify-content: center;
}
/* (The row and its places take no pointer themselves, only the cards do: in a 3D space what lies
   behind its parent's own plane would otherwise be out of the pointer's reach, and the places of
   the bow lie behind the row's.) */
.deck {
  position: relative; pointer-events: none;
  transform-style: preserve-3d;
  transform: rotateX(calc(var(--px, 0) * 1deg)) rotateY(calc(var(--py, 0) * 1deg));
}
.halo { position: absolute; inset: 0; pointer-events: none; }

/* each cover: its place in the five (the order of arrival), its place in the bow (0 is the middle),
   and its own colour */
.deck > :nth-child(1), .halo > :nth-child(1) { --i: 0; --k: -2; --glow: #ff3348; }
.deck > :nth-child(2), .halo > :nth-child(2) { --i: 1; --k: -1; --glow: #8b43ff; }
.deck > :nth-child(3), .halo > :nth-child(3) { --i: 2; --k: 0;  --glow: #ffc928; }
.deck > :nth-child(4), .halo > :nth-child(4) { --i: 3; --k: 1;  --glow: #2eea78; }
.deck > :nth-child(5), .halo > :nth-child(5) { --i: 4; --k: 2;  --glow: #18cf9c; }

/* A place in the row. The bow: every cover is turned towards the middle, and the ones nearer the
   middle stand further back, as on the inside of a wide drum. */
.deck li {
  position: relative; z-index: 0; aspect-ratio: 3 / 4;
  transform-style: preserve-3d;
  transform: translateZ(calc((var(--k) * var(--k) - var(--kk)) * var(--card-w) * var(--bow))) rotateY(calc(var(--k) * var(--turn) * -1));
  transition: translate 0.7s var(--ease-out);
}

/* the card: the link itself. Lifted towards the eye when lit; games.js turns it (--rx, --ry). Lifting
   and turning both bring the card's edge nearer the pointer, never away, so a card at whose edge
   the pointer rests does not flicker. */
.card {
  position: absolute; inset: 0; display: block; border-radius: var(--r); pointer-events: auto;
  transform: rotateX(calc(var(--rx, 0) * 1deg)) rotateY(calc(var(--ry, 0) * 1deg));
  transition: translate 0.6s var(--ease-out), scale 0.2s var(--ease-out);
  -webkit-tap-highlight-color: transparent;
}
.face {
  position: absolute; inset: 0; overflow: hidden; border-radius: inherit;
  background: #0d0e12;
}
@supports (color: color-mix(in srgb, red 10%, blue)) {
  .face { background: color-mix(in srgb, var(--glow) 12%, #0b0c10); }   /* until its picture is in */
}
.face img { display: block; width: 100%; height: 100%; object-fit: cover; }
/* the card's edge catches a little light, so a dark cover still ends where it ends */
.face::after {
  content: ""; position: absolute; inset: 0; border-radius: inherit; pointer-events: none;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.1), inset 0 1px 0 0 rgba(255, 255, 255, 0.16);
}

/* the veil: a card that steps back goes darker under a veil of the page's own colour. The card
   itself stays solid, so the light behind the row never shows through it. (It is an element of its
   own, and the rules that draw it are the only ones here that ask about the other cards: the card,
   which games.js restyles every frame while it turns, stays cheap to restyle.) */
.veil {
  position: absolute; inset: 0; pointer-events: none;
  background: var(--page); opacity: 0; transition: opacity 0.5s var(--ease-out);
}

/* the sheen: a band of light lying across the card. Under the pointer it goes where the pointer is
   (games.js: --sx on the band itself, its place as a share of the card's width); with the keyboard,
   or without script, it passes across once. .face::before is the same band for the glint each card gives as it lands. */
.sheen, .face::before {
  position: absolute; inset: 0; pointer-events: none; opacity: 0;
  background: linear-gradient(108deg, transparent 33%, rgba(255, 255, 255, 0.06) 41%, rgba(255, 255, 255, 0.26) 50%, rgba(255, 255, 255, 0.06) 59%, transparent 67%);
}
.sheen { transform: translateX(calc(var(--sx, 0) * 1%)); transition: opacity 0.45s var(--ease-out); }
.face::before { content: ""; z-index: 1; transform: translateX(-130%); }

/* a cover's pool of light: behind every card, in its cover's colour (lit through and through, so
   that where a phone's row shows a gap between two cards in passing, there is light in it, not a hole) */
.halo span { position: relative; aspect-ratio: 3 / 4; opacity: 0.16; transition: opacity 0.6s var(--ease-out), scale 0.8s var(--ease-out); }
.halo span::before {
  content: ""; position: absolute; inset: 10%; border-radius: var(--r); background: var(--glow);
  box-shadow: 0 0 calc(var(--card-w) * 0.36) calc(var(--card-w) * 0.07) var(--glow);
}

/* ---------------------------------------------------------------- lit: under the pointer, or focused
   The card comes forward and up a little; its light comes on; the others step back and go darker. */
.card:focus-visible {
  outline: 3px solid var(--ink); outline-offset: 5px; border-radius: var(--r);
  translate: 0 -2% calc(var(--card-w) * 0.26);
}
.card:active { scale: 0.985; }
.stage:has(li:nth-child(1) .card:focus-visible) .halo :nth-child(1),
.stage:has(li:nth-child(2) .card:focus-visible) .halo :nth-child(2),
.stage:has(li:nth-child(3) .card:focus-visible) .halo :nth-child(3),
.stage:has(li:nth-child(4) .card:focus-visible) .halo :nth-child(4),
.stage:has(li:nth-child(5) .card:focus-visible) .halo :nth-child(5) { opacity: 0.9; scale: 1.06; }
.deck:has(.card:focus-visible) li:not(:hover, :has(.card:focus-visible)) { translate: 0 0 calc(var(--card-w) * -0.2); }
.deck:has(.card:focus-visible) li:not(:hover, :has(.card:focus-visible)) .veil { opacity: 0.4; }
.stage:has(.card:focus-visible) .halo span { opacity: 0; }

@media (hover: hover) {
  .deck li:hover .card { translate: 0 -2% calc(var(--card-w) * 0.26); }
  .stage:has(li:hover) .halo span { opacity: 0; }
  .stage:has(li:nth-child(1):hover) .halo :nth-child(1),
  .stage:has(li:nth-child(2):hover) .halo :nth-child(2),
  .stage:has(li:nth-child(3):hover) .halo :nth-child(3),
  .stage:has(li:nth-child(4):hover) .halo :nth-child(4),
  .stage:has(li:nth-child(5):hover) .halo :nth-child(5) { opacity: 0.9; scale: 1.06; }
  .deck:has(li:hover) li:not(:hover, :has(.card:focus-visible)) { translate: 0 0 calc(var(--card-w) * -0.2); }
  .deck:has(li:hover) li:not(:hover, :has(.card:focus-visible)) .veil { opacity: 0.4; }
}

/* ---------------------------------------------------------------- the foot: small and quiet */
.base {
  position: relative; z-index: 1; display: flex; align-items: center; justify-content: space-between; gap: var(--space-md);
  min-height: var(--base); padding: 0 var(--gutter) env(safe-area-inset-bottom);
  font-size: var(--t-sm); color: var(--ink-mute);
}
.base .who { margin: 0; display: flex; align-items: center; gap: 0.8em; }
.base .who img { display: block; height: 0.75rem; width: auto; opacity: 0.72; }
.base nav { display: flex; flex-wrap: wrap; align-items: center; gap: 0 clamp(14px, 2.2vw, 28px); }
.base .back { padding: var(--space-sm) 0; color: var(--ink-soft); transition: color 0.2s var(--ease-out); white-space: nowrap; }
.base .back span { display: inline-block; margin-left: 0.25em; transition: transform 0.25s var(--ease-out); }
.base .back:hover { color: var(--ink); }
.base .back:hover span { transform: translateX(4px); }

/* ---------------------------------------------------------------- arriving, and the sheen in motion
   Only where motion is welcome; elsewhere everything is simply there. */
@media (prefers-reduced-motion: no-preference) {
  /* held back until the pictures are in (games.js marks the page .go; the page's first script sees to it that this is never long) */
  html.js:not(.go) .card, html.js:not(.go) .say, html.js:not(.go) .base, html.js:not(.go) .halo { opacity: 0; }
  html.js:not(.go) .card, html.js:not(.go) .say, html.js:not(.go) .base { transition: none; }

  html.go .say { animation: say 0.9s var(--ease-out) backwards; }
  html.go .base { animation: fade 1.2s var(--ease-out) 0.7s backwards; }
  html.go .halo { animation: fade 1.4s var(--ease-out) 0.7s backwards; }
  /* each card comes up out of the dark from behind and below, lying back, and stands up */
  html.go .card { animation: arrive 1.1s var(--ease-out) calc(var(--i) * 0.11s + 0.12s) backwards; }
  html.go .face::before { animation: glint 1.15s cubic-bezier(0.4, 0, 0.2, 1) calc(var(--i) * 0.11s + 0.62s) both; }

  /* a picture that comes late (games.js: html.live, img.in) fades in instead of dropping in */
  html.live .face img { opacity: 0; transition: opacity 0.5s var(--ease-out); }
  html.live .face img.in { opacity: 1; }

  @media (hover: hover) {
    html.tilt .deck li:hover .sheen { opacity: 1; }
    html:not(.tilt) .deck li:hover .sheen { animation: sweep 1.05s cubic-bezier(0.4, 0, 0.2, 1) both; }
  }
  .card:focus-visible .sheen { animation: sweep 1.05s cubic-bezier(0.4, 0, 0.2, 1) both; }
}
@keyframes say { from { opacity: 0; translate: 0 0.22em; } }
@keyframes fade { from { opacity: 0; } }
@keyframes arrive {
  from { opacity: 0; translate: 0 16% calc(var(--card-w) * -1.1); rotate: x 26deg; }
  45% { opacity: 1; }
}
@keyframes glint {
  0% { transform: translateX(-130%); opacity: 0; }
  14%, 80% { opacity: 1; }
  100% { transform: translateX(130%); opacity: 0; }
}
@keyframes sweep {
  0% { transform: translateX(-130%); opacity: 0; }
  14%, 80% { opacity: 1; }
  100% { transform: translateX(130%); opacity: 0; }
}

/* ---------------------------------------------------------------- 3 + 2
   Six half-columns: a cover takes two, and the fourth begins at the second, so the lower two stand
   centred under the upper three. Each row has its own bow. */
@media (max-width: 1279px) {
  .games { --n: 3; --kk: 1; --floor: 10rem; --turn: 7deg; }
  .deck, .halo { grid-template-columns: repeat(6, calc((var(--card-w) - var(--gap)) / 2)); }
  .deck > *, .halo > * { grid-column: span 2; }
  .deck > :nth-child(4), .halo > :nth-child(4) { grid-column: 2 / span 2; }
  .deck > :nth-child(1), .halo > :nth-child(1) { --k: -1; }
  .deck > :nth-child(2), .halo > :nth-child(2) { --k: 0; }
  .deck > :nth-child(3), .halo > :nth-child(3) { --k: 1; }
  .deck > :nth-child(4), .halo > :nth-child(4) { --k: -0.5; }
  .deck > :nth-child(5), .halo > :nth-child(5) { --k: 0.5; }
}

/* ---------------------------------------------------------------- swipe: a phone
   One cover at a time in the middle of the screen, the next one showing at the edge; the row runs
   from edge to edge and is swiped. Where the browser can tie an animation to that scroll, the
   covers turn as they pass (the one in the middle faces you, its neighbours lean away behind it)
   and the light behind the middle changes to each cover's colour as it comes. */
@media (max-width: 560px) {
  .games { --gap: 14px; --say-gap: clamp(14px, 3.4vh, 28px); --base: 4rem; }
  .games main { padding-left: 0; padding-right: 0; }
  .set { --card-w: min(74cqi, max(var(--h-room), 11rem)); width: 100%; }
  .say { margin-left: calc(var(--gutter) - 0.05em); }
  .stage { perspective: none; }
  .deck {
    display: flex; gap: var(--gap); justify-content: flex-start; transform: none; transform-style: flat; pointer-events: auto;
    overflow-x: auto; overflow-y: hidden; scroll-snap-type: x mandatory; overscroll-behavior-x: contain;
    -webkit-overflow-scrolling: touch; scrollbar-width: none;
    padding: 20px calc((100cqi - var(--card-w)) / 2); margin: -20px 0;
  }
  .deck::-webkit-scrollbar { display: none; }
  .deck li { flex: 0 0 var(--card-w); scroll-snap-align: center; transform: none; translate: none; pointer-events: auto; }
  .card { transform: none; }
  .card:focus-visible { translate: none; outline-offset: 4px; }
  /* the light: all five in one place behind the middle of the row, off until their cover is there */
  .halo { display: block; inset: auto; left: 50%; top: 50%; width: var(--card-w); aspect-ratio: 3 / 4; translate: -50% -50%; }
  .halo span { position: absolute; inset: 0; opacity: 0; scale: 1; transition: none; }
  .halo span::before { inset: 6%; box-shadow: 0 0 calc(var(--card-w) * 0.3) calc(var(--card-w) * 0.05) var(--glow); }
}
@media (max-width: 560px) and (prefers-reduced-motion: no-preference) {
  html.go .card { animation: arrive-flat 0.9s var(--ease-out) calc(var(--i) * 0.1s + 0.1s) backwards; }
}
@keyframes arrive-flat { from { opacity: 0; translate: 0 28px; scale: 0.94; } }

@supports (animation-timeline: view()) and (timeline-scope: --a) {
  @media (max-width: 560px) and (prefers-reduced-motion: no-preference) {
    .stage { timeline-scope: --c1, --c2, --c3, --c4, --c5; }
    .deck li:nth-child(1) { view-timeline: --c1 inline; }
    .deck li:nth-child(2) { view-timeline: --c2 inline; }
    .deck li:nth-child(3) { view-timeline: --c3 inline; }
    .deck li:nth-child(4) { view-timeline: --c4 inline; }
    .deck li:nth-child(5) { view-timeline: --c5 inline; }
    .deck li { animation: pass linear both; animation-timeline: view(inline); }
    .deck .veil { animation: far linear both; animation-timeline: view(inline); }
    .halo span { animation: lit linear both; }
    .halo span:nth-child(1) { animation-timeline: --c1; }
    .halo span:nth-child(2) { animation-timeline: --c2; }
    .halo span:nth-child(3) { animation-timeline: --c3; }
    .halo span:nth-child(4) { animation-timeline: --c4; }
    .halo span:nth-child(5) { animation-timeline: --c5; }
  }
}
/* from coming in at the right edge (0%) through the middle (50%) to leaving at the left (100%) */
@keyframes pass {
  0% { transform: perspective(60rem) translateZ(-3.6rem) rotateY(24deg); }
  50% { transform: perspective(60rem) translateZ(0) rotateY(0deg); }
  100% { transform: perspective(60rem) translateZ(-3.6rem) rotateY(-24deg); }
}
@keyframes far { 0%, 100% { opacity: 0.55; } 50% { opacity: 0; } }
@keyframes lit { 0%, 10%, 90%, 100% { opacity: 0; } 50% { opacity: 0.8; } }

/* ---------------------------------------------------------------- motion not welcome
   (site.css already ends every animation and makes every transition instant.) Nothing moves: a lit
   card keeps its place and only its light comes on. */
@media (prefers-reduced-motion: reduce) {
  .deck { transform: none; }
  .deck li, .deck li:hover, .card, .deck li:hover .card, .card:focus-visible { translate: none !important; }
  .card { transform: none; }
  .halo span { scale: 1 !important; }
  .sheen, .face::before { display: none; }
}
