/* Chrome shared by the trophy room and the per-event photo wall.
   The landing page keeps its own inline styles — it is one long scripted
   animation and nothing here should be able to reach into it. What is copied
   across is only the vocabulary: the same tokens, the same nav, the same two
   typefaces. */

:root {
  --ink: #14121f;
  --ink-soft: #4b4762;
  --paper: #fdfcf9;
  --accent: #6c3df4;
  --accent-hot: #ff5c8a;
  --line: #e8e5f0;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
html { overflow-x: hidden; }
body {
  font-family: "Avenir Next", Avenir, "Segoe UI", system-ui, -apple-system, sans-serif;
  background: var(--paper);
  color: var(--ink);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}
.wrap { max-width: 1080px; margin: 0 auto; padding: 0 24px; }
/* The photo wall alone runs wider than the 1080px column the reading pages use.
   Text has an ideal measure and photographs do not — held to the same width the
   wall reads as thumbnails of the night rather than as the night.

   Pulled back from 1320: the uploaded thumb is 700px on its long edge, and past
   about a thousand of them on screen the browser is inventing most of what you
   are looking at. This is the widest the wall goes while the picture still holds
   together. */
.wrap.wide { max-width: 1040px; }

/* Nav — same object as the landing page's, logo rules included, so moving
   between the two pages does not feel like moving between two sites. */
nav {
  position: sticky; top: 0; z-index: 10;
  background: rgba(253, 252, 249, 0.82);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid rgba(20, 18, 31, 0.08);
}
.nav-inner { display: flex; align-items: center; justify-content: space-between; padding: 14px 32px; }
.logo { font-weight: 800; font-size: 1.6rem; letter-spacing: -0.02em; text-decoration: none; color: #26312a; }
.logo span {
  background: linear-gradient(100deg, var(--accent), var(--accent-hot));
  -webkit-background-clip: text; background-clip: text; color: transparent;
}
/* the letters of "Blend" pulled into each other until the strokes clip. Margins
   on inline elements only — inline-block would restart the parent's
   background-clip gradient per letter. */
.logo b { font-weight: inherit; }
.logo b + b { margin-left: -0.05em; }
.logo b:nth-child(2) { margin-left: -0.07em; }
.logo b:nth-child(3) { margin-left: -0.04em; }
.logo b:nth-child(4) { margin-left: -0.06em; }
.logo b:nth-child(5) { margin-left: -0.045em; }
.nav-links { display: flex; gap: 28px; align-items: center; }
.nav-links a { text-decoration: none; color: #46514a; font-size: 0.95rem; font-weight: 600; }
.nav-links a:hover, .nav-links a[aria-current="page"] { color: #26312a; }
.nav-links a[aria-current="page"] { text-decoration: underline; text-underline-offset: 6px; text-decoration-thickness: 1.5px; }

.kicker {
  display: inline-block; font-size: 0.8rem; font-weight: 700; letter-spacing: 0.12em;
  text-transform: uppercase; color: #3f4b41;
  background: rgba(38, 49, 42, 0.08); padding: 6px 14px; border-radius: 999px;
}

.btn {
  display: inline-block; padding: 12px 24px; border-radius: 999px;
  background: var(--accent); color: #fff !important; font-weight: 700;
  text-decoration: none; font-size: 0.95rem; border: none; cursor: pointer;
  font-family: inherit;
  transition: transform 0.15s ease, box-shadow 0.15s ease, opacity 0.15s ease;
}
.btn:hover { transform: translateY(-2px); box-shadow: 0 8px 24px rgba(108, 61, 244, 0.35); }
.btn[disabled] { opacity: 0.45; cursor: not-allowed; transform: none; box-shadow: none; }
.btn.quiet {
  background: transparent; color: var(--ink) !important;
  border: 1.5px solid rgba(20, 18, 31, 0.22); font-weight: 600;
}
.btn.quiet:hover { box-shadow: none; border-color: var(--ink); }

/* ------------------------------------------------------------------ header --
   A lot of air above the objects. The room only works if the things in it are
   given room; tightening this is the fastest way to make it read as a product
   grid instead. */
.room-head { padding: 96px 0 20px; text-align: center; }
.room-title {
  font-family: "Instrument Serif", "Times New Roman", Georgia, serif;
  font-weight: 400; letter-spacing: -0.02em; line-height: 1.02;
  font-size: clamp(2.6rem, 7vw, 5rem);
  /* Pulled up hard so the kicker sits ON the headline rather than floating above
     it. The negative is doing less than it looks: at this size the serif carries
     a lot of empty leading above its cap height, so the optical gap is much
     bigger than the margin. */
  margin: -10px 0 0;
}
.room-title em {
  font-style: italic;
  background: linear-gradient(100deg, var(--accent), var(--accent-hot));
  -webkit-background-clip: text; background-clip: text; color: transparent;
  /* the italic descender is cropped out of the background-clip paint box by the
     tight line-height; pad it back without moving the layout */
  padding: 0.06em 0.04em 0.16em; margin: -0.06em -0.04em -0.16em;
}
/* The page-to-page pan is NOT defined here. A cross-document view transition is
   styled entirely by the document being navigated TO, and this stylesheet is
   shared with event.html — so an @view-transition rule in it would opt the photo
   wall in as well and pan the whole viewport on a drill-in. Each of index.html
   and gallery.html carries its own pair inline instead. */

/* ------------------------------------------------------------------ shelves --
   Three white planks. Objects stand ON the top one and fill left to right, and
   each object's label sits under the plank it stands on, the way a shelf label
   does. The row and the labels share the same column track so a label stays
   under its object however many are up there. */
.shelves { padding: 18px 0 70px; }
/* the perspective the board's top surface is tipped into. On the shelf rather
   than the whole list so each board converges to its own eye line instead of the
   lower ones fanning out. */
/* Perspective short enough that the far edge visibly converges. At 1500px the
   board's back edge came in by ~7% across a 1030px shelf, which is not enough to
   read as depth — it just looked like a second flat bar above the first. */
.shelf { margin-bottom: 22px; perspective: 760px; perspective-origin: 50% -70%; }
.shelf-row, .shelf-tags {
  display: grid;
  grid-template-columns: repeat(var(--slots, 4), minmax(0, 1fr));
  gap: 0 12px;
  align-items: end;
}
/* holds the tallest object plus headroom for the hover lift */
.shelf-row { min-height: 213px; }
/* The plank. White on near-white needs its edge given to it: a hairline top
   catches the light, the front face is a shade darker than the page, and the
   shadow underneath is what actually reads as "this is a shelf". */
.plank {
  position: relative;
  /* the front lip — the board's thickness, seen edge on */
  height: 14px;
  /* Objects stand this far BACK from the front edge. The row's bottom is where
     their bases land, so pushing the lip down by this much leaves them standing
     on the middle of the surface rather than teetering off the front. */
  margin-top: 26px;
  z-index: 0;
  /* The board's top is a pseudo-element, i.e. a GRANDCHILD of the .shelf that
     carries the perspective — and perspective only reaches direct children.
     Without this the surface still tips but flattens, so it projects as a plain
     squashed bar with parallel sides and no convergence at all. */
  transform-style: preserve-3d;
  border-radius: 0 0 3px 3px;
  /* The lip faces the viewer, away from the light, so it is clearly darker than
     the top. That value step IS the front arris — with both surfaces near-white
     the board had no edge and the depth read as a stripe. */
  background: linear-gradient(180deg, #ded9ea 0%, #d3cde2 55%, #c4bdd6 100%);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.75),
    /* a tight contact line so the board has a hard underside, then two soft
       casts. Without the tight one it reads as a floating band; the page is
       near-white, so a white shelf is defined entirely by what it casts. */
    0 2px 2px rgba(20, 18, 31, 0.16),
    0 12px 18px -8px rgba(20, 18, 31, 0.30),
    0 30px 40px -24px rgba(20, 18, 31, 0.34);
}
/* The top of the board, lying down and receding away. Laid out above the lip and
   then tipped flat, so its projected height IS the depth you see — at 74deg a
   120px board reads as a ~33px band. The parent's perspective makes the far edge
   converge, which is what stops it looking like a second flat bar. */
.plank::before {
  content: "";
  position: absolute; left: 0; right: 0; bottom: 100%;
  height: 150px;
  transform: rotateX(70deg);
  transform-origin: bottom center;
  /* Faces up into the light, so it is the brightest plane on the page. The far
     edge falls off a little — partly distance, partly the objects' own occlusion
     of the light — which is what gives the surface a direction. */
  background: linear-gradient(180deg, #dcd6ea 0%, #f0eef7 38%, #fdfcff 78%, #ffffff 100%);
  border-radius: 3px 3px 0 0;
}
.shelf-tags { padding-top: 14px; align-items: start; }
/* .trophy sets align-self:end so objects stand on the plank; the label under it
   has to go back to the top of its cell or short labels float */
.shelf-tags .trophy { align-self: start; }

/* The object is a SIZE, not a fraction of however many slots a shelf happens to
   have — capping it here rather than letting it fill the column means adding a
   fourth object later does not shrink the first three. Stays flush left because
   a block with a max-width does. */
.trophy {
  display: block; text-decoration: none; color: inherit;
  align-self: end;
  max-width: 260px;
  /* Above the board. The lip and its top surface come later in the document, so
     without this they paint over the object standing on them — the surface behind
     the object would cover the bottom of it, which reads as the thing being sunk
     into the shelf rather than sat on it. */
  position: relative; z-index: 1;
}
.trophy:focus-visible { outline: 2px solid var(--accent); outline-offset: 6px; }

/* The object and its shadow are separate layers so they can react to the hover
   differently: the thing rises, and its shadow spreads and weakens the way a
   real one does. Moving them together just slides the whole picture. */
/* The sprite carries ~18.2% transparent padding below the object. Pulling the
   box up by exactly that much stands the object ON the plank instead of floating
   it above one.

   The cap that sizes the object lives on .trophy, NOT here. This margin is a
   percentage, and percentage margins resolve against the CONTAINING BLOCK's
   width — so capping the stage itself would leave the offset computed from the
   full column while the stage was narrower, and the ball would sink through the
   shelf by the difference. Capping the parent keeps the two in step. */
.trophy-stage { position: relative; aspect-ratio: 1; margin-bottom: -18.2%; }
.trophy-stage img {
  position: relative; z-index: 1; display: block;
  width: 100%; height: auto;
  transition: transform 0.4s cubic-bezier(0.22, 1, 0.36, 1);
  will-change: transform;
}
/* The renders are framed identically: the object is centred and spans 63.6% of
   the sprite, so its underside always lands at 81.8% of the stage height. The
   shadow is parked there rather than at the bottom of the box, which is where
   the transparent padding ends, not where the object does. */
.trophy-shadow {
  position: absolute; left: 50%; top: 80.6%;
  width: 46%; height: 8%;
  transform: translate(-50%, -50%);
  /* tight and dark at the centre: the object is sitting on a surface a few
     millimetres away, not hovering over one. A wide soft pool reads as float */
  background: radial-gradient(ellipse at center,
    rgba(20, 18, 31, 0.34) 0%, rgba(20, 18, 31, 0.16) 42%, rgba(20, 18, 31, 0) 70%);
  transition: transform 0.4s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.4s ease;
  will-change: transform, opacity;
}
/* Picking it up is the whole interaction, so the lift is the whole feedback —
   no plate or panel behind it changing colour. The shadow spreads and weakens as
   it goes, which is what makes it read as height rather than as a slide. */
.trophy:hover .trophy-stage img,
.trophy:focus-visible .trophy-stage img { transform: translateY(-30px); }
.trophy:hover .trophy-shadow,
.trophy:focus-visible .trophy-shadow { transform: translate(-50%, -50%) scale(1.34, 0.72); opacity: 0.42; }

/* a very slow drift, staggered per item, so the room breathes without anything
   in it appearing to animate */
@keyframes trophy-bob { 0%, 100% { translate: 0 0; } 50% { translate: 0 -6px; } }
.trophy-stage img { animation: trophy-bob 7.5s ease-in-out infinite; animation-delay: var(--bob, 0s); }
/* hover uses transform and the bob uses translate, so the two compose instead of
   the lift being cancelled the moment the bob's next keyframe lands */

/* The shelf label, sitting under the plank its object stands on. */
.trophy-no {
  font-size: 0.72rem; font-weight: 700; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--accent);
}
.trophy-name {
  font-family: "Instrument Serif", "Times New Roman", Georgia, serif;
  font-weight: 400; font-size: 1.6rem; line-height: 1.12; letter-spacing: -0.01em;
  margin-top: 3px;
}
.trophy-note { margin-top: 5px; font-size: 0.85rem; color: var(--ink-soft); }
.trophy-quip {
  margin-top: 3px; font-size: 0.85rem; color: var(--ink-soft); font-style: italic;
  max-width: 30ch;
}

.shelf-note {
  text-align: center; color: var(--ink-soft); font-size: 0.95rem;
  padding: 30px 0 0;
}

/* -------------------------------------------------------------- event page --*/
.ev-head { padding: 64px 0 8px; text-align: center; }
.ev-back {
  display: inline-block; font-size: 0.88rem; font-weight: 600; color: var(--ink-soft);
  text-decoration: none; margin-bottom: 30px;
}
.ev-back:hover { color: var(--ink); }
/* Bigger than it was, for two reasons that happen to agree. It is the only
   thing in this header now that the name and the quip have gone, so it should
   carry it — and the flight's scale is set by how small the object starts,
   so every pixel here is scale the compositor does not have to raster. */
.ev-trophy { width: 208px; height: 208px; position: relative; margin: 0 auto 6px; }
/* the shadow is sized in percentages, so at a fifth of the shelf's scale it
   reads as a dark smudge rather than contact — thin it out here */
.ev-trophy .trophy-shadow { height: 10%; opacity: 0.6; }
/* the shadow is absolutely positioned, so a static image would paint UNDER it and
   the ball would sit behind its own shadow — same lift the shelf tiles get */
.ev-trophy img { position: relative; z-index: 1; width: 100%; height: auto; display: block; }
/* The object IS the title here, the same way it is on the shelf — the name and
   the quip are the trophy room's job, and repeating them over the photos only
   pushed the wall further down the page. What is left is the number, which is
   the one thing the object cannot tell you. */
.ev-no {
  font-size: 0.78rem; font-weight: 700; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--accent);
  margin-top: 14px;
}

/* Stacked, not a row: the status is empty most of the time, and as a flex
   sibling its gap still counts, which drags the button off centre by half of it. */
.ev-bar {
  display: flex; flex-direction: column; gap: 12px; align-items: center;
  padding: 34px 0 10px;
}
.ev-status { font-size: 0.9rem; color: var(--ink-soft); min-height: 1.4em; }
.ev-status.bad { color: #b3261e; }

/* The wall. Columns rather than a cropped square grid: these are photos of
   people, and object-fit: cover decides who gets cut out of frame. */
.wall { padding: 30px 0 90px; column-count: 1; column-gap: 22px; }
/* before the first photo lands there is nothing to space, and the padding would
   otherwise open 120px between the button and the message explaining why */
.wall:empty { padding: 0; }
/* Already one column at every width, so there is no count to drop. What the
   narrow case needs instead is its margins back: 1320px of wrap on a phone is
   just the 24px padding, and the photos run edge to edge. */
@media (max-width: 760px) { .wall figure { border-radius: 10px; } }
.wall figure {
  break-inside: avoid; margin: 0 0 34px;
  border-radius: 14px; overflow: hidden;
  background: #f1eef7;
  box-shadow: 0 1px 2px rgba(20, 18, 31, 0.06), 0 12px 24px -14px rgba(20, 18, 31, 0.35);
}
.wall img {
  display: block; width: 100%; height: auto; cursor: zoom-in;
  transition: transform 0.5s cubic-bezier(0.22, 1, 0.36, 1);
}
.wall figure:hover img { transform: scale(1.03); }
.wall figure.pending { opacity: 0.45; }

.wall-empty {
  text-align: center; color: var(--ink-soft); padding: 60px 20px 90px;
  font-size: 1rem;
}
.wall-empty strong { display: block; font-size: 1.15rem; color: var(--ink); margin-bottom: 8px; font-weight: 600; }

/* whole-page drop target, so a handful of photos can be thrown anywhere on the
   wall rather than aimed at a small dashed rectangle */
body.dragging::after {
  content: "Drop to add to the wall";
  position: fixed; inset: 16px; z-index: 40; pointer-events: none;
  border: 2px dashed rgba(108, 61, 244, 0.5); border-radius: 22px;
  background: rgba(108, 61, 244, 0.05);
  display: grid; place-content: center;
  font-weight: 700; color: var(--accent); letter-spacing: 0.02em;
}

/* ---------------------------------------------------------------- lightbox --*/
.lb {
  position: fixed; inset: 0; z-index: 50; display: none;
  background: rgba(14, 12, 22, 0.92);
  backdrop-filter: blur(6px);
  place-content: center; padding: 4vh 4vw;
}
.lb.open { display: grid; }
.lb img { max-width: 92vw; max-height: 88vh; width: auto; height: auto; border-radius: 8px; display: block; }
.lb button {
  position: absolute; background: rgba(255, 255, 255, 0.1); color: #fff;
  border: none; cursor: pointer; font-size: 1.6rem; line-height: 1;
  width: 46px; height: 46px; border-radius: 999px; font-family: inherit;
}
.lb button:hover { background: rgba(255, 255, 255, 0.2); }
.lb .lb-x { top: 18px; right: 18px; }
.lb .lb-prev { left: 18px; top: 50%; transform: translateY(-50%); }
.lb .lb-next { right: 18px; top: 50%; transform: translateY(-50%); }

footer { padding: 48px 0 64px; text-align: center; color: var(--ink-soft); font-size: 0.9rem; }
footer .foot-links { display: flex; gap: 24px; justify-content: center; margin-bottom: 18px; flex-wrap: wrap; }
footer a { color: var(--ink-soft); text-decoration: none; font-weight: 600; }
footer a:hover { color: var(--ink); }

@media (max-width: 640px) {
  .nav-inner { padding: 14px 20px; }
  .nav-links { gap: 20px; }
  .room-head { padding: 56px 0 8px; }
  .shelf { padding: 44px 0 24px; gap: 18px 28px; }
}

@media (prefers-reduced-motion: reduce) {
  .trophy-stage img { animation: none; }
  .trophy:hover .trophy-stage img { transform: none; }
  .trophy:hover .trophy-shadow { transform: translate(-50%, -50%); }
  .wall figure:hover img { transform: none; }
  * { transition-duration: 0.01ms !important; }
}
