/* The invitation wall.

   Sixteen show invitations stood in a circle and tilted back, turning slowly on
   their own and stopping the moment anyone reaches for them.

   The page reads top to bottom - what this is, the objects, the thing to do -
   rather than scattering those three into corners, which is how it started and
   it was not obvious what to do with it. */

:root{ --ink:#f6f1e0; --dim:#8b8178; --hot:#e8536a; }
*{box-sizing:border-box}
html,body{margin:0;background:#000;color:var(--ink);
  font-family:'Inter',system-ui,-apple-system,'Helvetica Neue',sans-serif;
  /* One screen, and it stays put.

     The unit matters more than the lock. On iOS, 100% and 100vh both resolve
     against the LARGE viewport - the one with the toolbar hidden - so while
     the toolbar is showing, the page is taller than what you can see. That is
     draggable, and because the column is centred, the content slides as the
     toolbar animates in and out: the up-and-down this was reported as. svh is
     the SMALL viewport, the toolbar's largest state, and it never changes. The
     page is then never taller than the screen, so there is nothing to drag and
     nothing to re-centre; when the toolbar retracts the extra room simply
     appears below, and not a pixel moves.

     overscroll-behavior alone would not have done it: that stops the scroll
     chaining, not the drag. */
  height:100svh;overflow:hidden;
  overscroll-behavior:none;
  /* Everything on this page is set lower case. Done in CSS rather than in the
     copy so the words themselves keep their capitals: the collection names come
     from the archive's data and are read by other pages, and a screen reader
     still says Alexander McQueen rather than a lower-case approximation. */
  text-transform:lowercase}
/* Form controls do not inherit text-transform in every browser, so the button
   kept its capital while everything around it had lost one. */
button,input,textarea,select{text-transform:inherit}
body{height:100svh;display:flex;flex-direction:column;align-items:center;
  justify-content:center;gap:clamp(.9rem,2.4vh,1.8rem);
  padding:clamp(1.6rem,4vh,2.8rem) clamp(1rem,4vw,2rem) clamp(1.4rem,3vh,2.2rem)}

/* ---- what this is ---- */
.lede{text-align:center;max-width:36rem;z-index:4}
.lede h1{margin:0;font-size:clamp(1.65rem,4.4vw,2.9rem);line-height:1.04;letter-spacing:-.02em;
  font-weight:800;text-wrap:balance}
.lede p{margin:.65rem auto 0;font-size:.85rem;line-height:1.6;color:var(--dim);max-width:27rem}

/* ---- the card held up, top left, on a screen with room for it ---- */
.held{position:fixed;top:clamp(1rem,3vh,2.2rem);left:clamp(1rem,3vw,2.6rem);z-index:5;
  width:clamp(104px,11vw,152px);pointer-events:none}
.held-frame{width:100%;aspect-ratio:4/5;overflow:hidden;background:#0b0a09;border-radius:2px;
  box-shadow:0 1px 1px rgba(0,0,0,.6),0 16px 40px rgba(0,0,0,.7)}
.held-frame img{width:100%;height:100%;object-fit:cover;display:block}
.held-cap{margin-top:.5rem;display:flex;flex-direction:column;gap:.12rem}
.held-title{font-size:.72rem;letter-spacing:.09em;text-transform:uppercase;color:var(--ink)}
.held-sub{font-size:.6rem;letter-spacing:.14em;text-transform:uppercase;color:var(--dim)}
.held.swap .held-frame img,.held.swap .held-cap{animation:heldIn .5s ease}
@keyframes heldIn{from{opacity:0;transform:translateY(5px)}to{opacity:1;transform:none}}

/* ---- the ring ---- */
/* Tall enough to hold the ring. The ring is absolutely positioned inside it,
   so it contributes no height of its own: too short a box and the circle
   spills over the headline above it. */
/* flex:0 0 auto, not 1: the fitting pass gives this box an explicit height
   that hugs the ring, and a growing flex item swallows it again - which is why
   the phone had six hundred pixels of stage around a hundred and eighty of
   ring, and the form could never come up to meet it. */
.stage{flex:0 0 auto;width:100%;min-height:clamp(24rem,46svh,30rem);
  position:relative;display:grid;place-items:center;
  perspective:1400px;perspective-origin:50% 44%;
  /* Not clipped: a card lifted out of the ring comes forward and grows, and
     overflow:hidden sliced the top off it. The radius is measured to fit the
     viewport, so there is nothing to clip horizontally either. */
  cursor:grab;touch-action:pan-y;user-select:none}
.stage:active{cursor:grabbing}
.ring{position:relative;width:1px;height:1px;transform-style:preserve-3d;
  /* --ringY is set after measuring: the ring's visual centre is not its
     geometric centre, because the tilt and the perspective push the near cards
     down and make them bigger. Centring it by eye means centring it by hand. */
  transform:translateY(var(--ringY,0px)) rotateX(22deg) rotateY(var(--spin,0deg))}
.card{position:absolute;left:50%;top:50%;
  width:var(--cw,150px);height:var(--ch,188px);
  margin:calc(var(--ch,188px) / -2) 0 0 calc(var(--cw,150px) / -2);
  cursor:pointer;background:none;border:0;padding:0;
  /* Deliberately NOT transform-style:preserve-3d. The ring is the 3D context;
     a second one per card gave Safari sixteen nested contexts to sort and it
     tore the images into vertical bands. */
  transition:transform .45s cubic-bezier(.22,1,.36,1)}
.card img{width:100%;height:100%;object-fit:cover;display:block;border-radius:2px;
  -webkit-user-drag:none;user-select:none;
  box-shadow:0 10px 26px rgba(0,0,0,.75);
  outline:1px solid rgba(255,255,255,.08);outline-offset:-1px}
/* Dimming is a flat black veil, not a CSS filter. A filter on an element inside
   a 3D transform forces WebKit to rasterise it on its own, and tiled
   rasterisation of a rotated layer is what sliced these cards into strips. */
.card::after{content:"";position:absolute;inset:0;border-radius:2px;pointer-events:none;
  background:#000;opacity:.3;transition:opacity .3s}
.card.on::after,.card:hover::after{opacity:0}
.card.on img{outline:1px solid rgba(232,83,106,.85)}
.card.away{pointer-events:none}
.card.away::after{opacity:.62}

/* The detail of a lifted card, shown under the ring rather than inside it.
   Text in a 3D context has to be counter-rotated to be readable and drags a
   backdrop filter along with it, which is the other half of what WebKit was
   tearing. Out here it is ordinary flat type. */
.tapped{opacity:0;transform:translateY(4px);transition:opacity .25s,transform .25s;
  text-align:center;pointer-events:none;min-height:2.4rem}
.tapped.show{opacity:1;transform:none}
.tapped b{display:block;font-size:.76rem;font-weight:600;letter-spacing:.09em;
  text-transform:uppercase;color:var(--ink)}
.tapped span{display:block;margin-top:.18rem;font-size:.6rem;letter-spacing:.14em;
  text-transform:uppercase;color:var(--dim)}
.card.open img{box-shadow:0 18px 48px rgba(0,0,0,.9);outline-color:rgba(232,83,106,.9)}
.card.open::after{opacity:0}
.card.open{z-index:3}

.hint{font-size:.56rem;letter-spacing:.3em;text-transform:uppercase;color:var(--dim);
  opacity:.55;z-index:4}

/* ---- the thing to do ---- */
.join{width:100%;display:flex;flex-direction:column;align-items:center;gap:.55rem;z-index:6}
.join form{display:flex;gap:.5rem;width:min(30rem,92vw)}
.join input{flex:1;min-width:0;background:rgba(255,255,255,.05);color:var(--ink);
  border:1px solid rgba(255,255,255,.18);border-radius:999px;padding:.78rem 1.15rem;
  font:inherit;font-size:.92rem;transition:.18s}
.join input::placeholder{color:var(--dim)}
.join input:focus{outline:none;border-color:var(--hot);background:rgba(255,255,255,.08)}
.join button{background:var(--hot);color:#140b0d;border:0;border-radius:999px;
  padding:.78rem 1.5rem;font:inherit;font-weight:700;font-size:.92rem;cursor:pointer;
  white-space:nowrap;transition:.18s}
.join button:hover:not(:disabled){filter:brightness(1.1)}
.join button:disabled{opacity:.55;cursor:default}
.join-note{margin:0;font-size:.66rem;letter-spacing:.06em;color:var(--dim);
  min-height:1em;text-align:center}
.join-note.bad{color:var(--hot)}
.join-note.good{color:var(--ink)}

/* ---- the arrangement, at every width ----
   The page reads: the invitation, the instruction, the ring, what you have
   picked, the form. The instruction sits directly under the headline rather
   than under the ring, so it is read before the thing it is about. This was
   desktop-only for a while; the phone has it now too, so the two do not
   describe themselves differently. */
/* Centred, so the black above the headline and the black below the button are
   the same. Pinned to the top it left all the slack at the bottom. */
body{justify-content:center;gap:clamp(.7rem,1.8vh,1.4rem);
     padding-top:clamp(1rem,2.5vh,1.8rem);padding-bottom:clamp(1rem,2.5vh,1.8rem)}
/* No padding of its own: it would sit above the headline and nowhere else,
   which is exactly the amount the top and bottom were out by. */
.lede{order:1}
.hint{order:2}
.stage{order:3;min-height:clamp(22rem,42svh,28rem)}
.tapped{order:4;min-height:0}
.join{order:5}
/* the submit sits under the field rather than beside it */
.join form{flex-direction:column;width:min(24rem,80vw)}
.join button{width:100%}

@media (max-width:760px){
  /* No room for a panel in the corner, and no cursor to hover with: the card
     itself comes out of the ring and says what it is. */
  .held{display:none}
  .lede h1{font-size:clamp(1.5rem,7vw,2rem)}
  .stage{min-height:clamp(15rem,32svh,19rem)}
  .tapped{min-height:2.2rem}      /* the caption is the only thing naming a card here */
  .join form{width:min(22rem,88vw)}
}
/* A short window - a laptop with the browser not full screen, or a bookmarks
   bar and a dock eating the height - was the one case this did not fit. The
   stage asks for 42svh but never less than 22rem, and on a six-hundred-pixel
   window that floor is a hundred pixels more than 42svh would have been, so
   the form slid under the fold and the page gained a scroll that went nowhere
   and came back. Below that height the stage gives up its floor: there is
   less room, so it takes less, and everything still lands on one screen. */
@media (max-height:720px){
  body{gap:clamp(.45rem,1.2vh,.9rem);
       padding-top:clamp(.6rem,1.6vh,1.1rem);padding-bottom:clamp(.6rem,1.6vh,1.1rem)}
  .stage{min-height:clamp(12rem,34svh,20rem)}
}

/* A phone turned sideways: under four hundred pixels of height, and the join
   button - the only thing the page is asking anyone to do - was below the
   fold. The headline comes down, the ring takes what is left, and the button
   is on screen without being reached for. */
@media (max-height:460px){
  body{gap:.35rem;padding-top:.5rem;padding-bottom:.5rem}
  .lede h1{font-size:clamp(1.15rem,3.4vw,1.6rem)}
  .stage{min-height:clamp(7.5rem,28svh,13rem)}
  .hint{margin:0}
  .join form{gap:.4rem}
}

/* The one case the lock must give way: a tapped field with the keyboard up.
   The keyboard takes half the screen, and a document that cannot move cannot
   bring the field back into view - so while something is being typed into,
   the page is allowed to scroll again. */
body.typing{overflow:auto}

@media (prefers-reduced-motion:reduce){ .held.swap *{animation:none} .card{transition:none} }
