/* Luten löp · event cards from the guest book service (js/events.js). */

/* invitation card for an upcoming run */
.invite {
  display: block;
  padding: 16px 16px 14px;
  border-radius: 3px;
  background: #fffdf8;
  color: var(--ink);
  font-family: var(--font-serif);
  font-variation-settings: var(--voice-text);
  box-shadow: 0 0 0 .5px #00000014, 0 1px 2px #0000001f, 0 12px 24px -10px #00000047;
}
.invite__label {
  display: block;
  margin-bottom: 6px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--blue);
  font-variation-settings: var(--voice-label);
}
/* A word wider than the card (a Swedish compound: "Midsommarlångpasset" at the phone card's 30 px, "Vårmaraton-
   träningsrundan" at 24) breaks inside itself instead of running out of the card; words that fit are never broken. */
.invite__title { margin: 0; font-size: 24px; font-weight: 600; line-height: 1.05; letter-spacing: -.015em; font-variation-settings: var(--voice-display); overflow-wrap: anywhere; }
.invite__when { margin: 5px 0 0; font-size: 15px; }
.invite__meta { margin: 0; font-size: 14px; color: var(--ink-2); }
.invite__hosts { margin: -2px 0 4px; font-size: 13px; letter-spacing: .04em; text-transform: uppercase; color: var(--blue); font-variation-settings: var(--voice-label, "SOFT" 0, "WONK" 0, "opsz" 9); }
/* At most three lines, cut with an ellipsis: the description runs to 2000 characters and the card grew with it (457
   wall px at 210 characters, against the 410 the guest book keeps free for a card, core/arrange.ts INVITE_CARD_H),
   so a long one ran over whatever hung below. The whole text is on the event page, behind .invite__more. A phone
   hides it (below). */
.invite__desc { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; line-clamp: 3; overflow: hidden; margin: 8px 0 0; font-size: 13.5px; line-height: 1.35; color: var(--ink-2); }

/* sign-up form inside the card */
.signup { margin-top: 12px; }
.signup__fields { display: grid; gap: 6px; }
.signup input {
  width: 100%;
  padding: 8px 10px;
  border: 1px solid #00000029;
  border-radius: 4px;
  background: #fff;
  color: var(--ink);
  font: inherit;
  font-size: 14px;
  user-select: text;
  -webkit-user-select: text;
}
.signup input:focus { outline: 2px solid var(--blue); outline-offset: 1px; border-color: transparent; }
.signup .hp { position: absolute; left: -10000px; width: 1px; height: 1px; opacity: 0; }
.signup button {
  justify-self: start;
  padding: 8px 14px;
  border: 0;
  border-radius: 999px;
  background: var(--blue);
  color: var(--on-blue);
  font: inherit;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  transition: opacity var(--duration-quick) ease-out, background-color var(--dur-fast), color var(--dur-fast), scale var(--duration-quick) var(--ease-out);
}
/* Hover only where there is a pointer: on a phone a tapped button kept the ink after the tap; the press is its answer. */
@media (hover: hover) { .signup button:hover { background: var(--ink); color: var(--bg); } }
.signup button[disabled] { opacity: .6; cursor: default; }
/* The dip answers the press, not the browser's square grey tap rectangle over the round button (it stays with reduced
   motion, where there is no dip). */
@media (prefers-reduced-motion: no-preference) {
  .signup button { -webkit-tap-highlight-color: transparent; }
  .signup button:active:not([disabled]) { scale: .97; transition-duration: var(--duration-micro); }
}
.signup__note { margin: 6px 0 0; min-height: 1.2em; font-size: 13px; line-height: 1.3; color: var(--ink-2); }
.signup__note[data-kind="ok"] { color: var(--blue); }
.signup__note[data-kind="error"] { color: #a3463d; } /* 5.5:1 on #f8f4e5 paper (#b4534a read 4.5:1 there, under the line) */
/* The field a refusal points at (js/events.js refuse), until it is typed in again; focused, its ring says so. */
.signup .is-invalid { border-color: #a3463d; }
.signup .is-invalid:focus { outline-color: #a3463d; }
/* A seat taken (js/events.js settle): the fields fade (120 ms) while the note's old words leave, then they go, and the
   form moves from its old height to its new one in a single move as the new words arrive. Folding the fields on their
   own shrank the card by their height and then, 100 ms in, jumped it back by the new words' extra lines. */
.signup--done .signup__fields { opacity: 0; pointer-events: none; transition: opacity 120ms ease-out; }
.signup--settled .signup__fields { display: none; }
/* The check before the note's words, drawn in 350 ms after an 80 ms beat: the guest book's CheckMark. */
.signup__check { display: inline-block; width: 14px; height: 14px; margin-right: 5px; vertical-align: -2px; }
.signup__check path { stroke-dasharray: 1; animation: signup-draw 350ms var(--ease-out) 80ms backwards; } /* at rest it is drawn */
@keyframes signup-draw { from { stroke-dashoffset: 1; } }
@media (prefers-reduced-motion: reduce) {
  .signup__check path { animation: none; }
}
/* the club's questions at sign-up: one field each, in the same clothes as name and email */
.signup__q { display: grid; gap: 4px; margin: 0; padding: 0; border: 0; min-width: 0; font: inherit; }
.signup__q legend { padding: 0; margin-bottom: 4px; }
.signup__qlabel { display: block; font-size: 13px; line-height: 1.3; color: var(--ink-2); }
.signup__qlabel em { font-style: normal; color: var(--ink-2); } /* --ink-3 read 3.0:1 on the card; this is 5.7:1 */
.signup select {
  width: 100%;
  padding: 8px 10px;
  border: 1px solid #00000029;
  border-radius: 4px;
  background: #fff;
  color: var(--ink);
  font: inherit;
  font-size: 14px;
}
.signup select:focus { outline: 2px solid var(--blue); outline-offset: 1px; border-color: transparent; }
.signup__choices { display: flex; gap: 14px; flex-wrap: wrap; }
.signup__choices label { display: inline-flex; align-items: center; gap: 6px; font-size: 14px; user-select: none; -webkit-user-select: none; }
.signup input[type="radio"] { width: auto; margin: 0; padding: 0; accent-color: var(--blue); }
.signup input[type="radio"]:focus { outline-offset: 2px; }

/* a past run: the stamped polaroid with its guest list */
.stamped__photo {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 5%;
  padding: 7%;
  text-align: center;
  background: #eee7d8;
}
.stamped__photo img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; opacity: .5; filter: saturate(.8); }
/* The stamp and the names stand on the photo, which may be dark: a wash of paper behind them keeps the stamp at 6.6:1
   and the handwriting at 10.9:1 over a black picture (the bare photo, at half strength, read 1.7:1 and 3.0:1). */
.stamp {
  position: relative;
  display: inline-block;
  padding: .28em .55em .24em;
  border: 2px solid var(--blue);
  border-radius: 6px;
  background: #f8f4e5e6;
  color: var(--blue);
  font-family: var(--font-serif);
  font-weight: 700;
  font-variation-settings: var(--voice-display);
  font-size: clamp(12px, 9cqi, 26px);
  line-height: 1;
  letter-spacing: .02em;
  text-transform: uppercase;
  transform: rotate(-7deg);
  opacity: .88;
  white-space: nowrap;
}
.stamp b { font-size: 1.6em; margin-right: .12em; font-weight: 800; }
.guests {
  position: relative;
  max-height: 46%;
  overflow: hidden;
  padding: .15em .4em;
  border-radius: 4px;
  background: #f8f4e5d9;
  font-family: var(--font-hand);
  font-size: clamp(10px, 6.4cqi, 16px);
  line-height: 1.3;
  color: #2a2a2a;
  text-wrap: balance;
}
/* The stamped polaroid's parts and the badges fade to blue with the frame (css/site.css .polaroid), 200 ms in and out. */
.stamp, .guests, .stamped__photo, .badge { transition: background-color var(--dur) var(--ease-out), color var(--dur) var(--ease-out), border-color var(--dur) var(--ease-out); }
.pin:hover .stamp, .pin:focus-within .stamp { border-color: var(--on-blue); background: #0f2fa6e6; color: var(--on-blue); }
.pin:hover .stamped__photo, .pin:focus-within .stamped__photo { background: #0f2fa6; }
.pin:hover .guests, .pin:focus-within .guests { background: #0f2fa6d9; color: var(--on-blue); }

/* inside the wall the fish's marker is the cursor, on every element */
.world[data-cursor-active="true"] input, .world[data-cursor-active="true"] select, .world[data-cursor-active="true"] button, .world[data-cursor-active="true"] textarea, .world[data-cursor-active="true"] label { cursor: none !important; }
.invite__title a { color: inherit; text-decoration: none; transition: color var(--dur-fast); }
.invite__more { display: inline-block; margin-top: 8px; font-size: 13px; color: var(--blue); text-decoration: none; }
/* The phone's way to sign up (js/events.js): a pill across the card that opens the event page, 60 wall px tall, 43 on
   a phone's screen at rest. Hidden where the card's own form is shown. Its longest words ("Ställ mig på väntelistan",
   the event page's own) take two lines inside the same 60 px rather than running past the card. */
.invite__go {
  display: none;
  align-items: center;
  justify-content: center;
  height: 60px;
  margin-top: 14px;
  padding: 0 14px;
  text-align: center;
  line-height: 1.05;
  border-radius: 999px;
  background: var(--blue);
  color: var(--on-blue);
  font-size: 21px;
  font-weight: 600;
  text-decoration: none;
  transition: background-color var(--dur-fast), color var(--dur-fast), scale var(--duration-fast) var(--ease-spring);
}
/* Hover only where there is a pointer (on a phone a tapped link kept its colour after the tap). A tap's answer is the
   way in itself (the room darkens at once) and, on the pill, a dip in 80 ms and a spring back in 250 (the guest book's
   press); the browser's grey tap rectangle goes where that answer plays, and stays with reduced motion, where the
   pill does not dip and the page is simply opened. */
@media (hover: hover) {
  .invite__title a:hover { color: var(--blue); }
  .invite__more:hover { text-decoration: underline; }
  .invite__go:hover { background: var(--ink); color: var(--bg); }
}
@media (prefers-reduced-motion: no-preference) {
  .invite__title a, .invite__more, .invite__go { -webkit-tap-highlight-color: transparent; }
  .invite__go:active { scale: var(--scale-press); transition: background-color var(--dur-fast), color var(--dur-fast), scale var(--duration-micro) ease-out; }
  /* A drag that began on the pill is a pan, not a press: it springs back as soon as board.js marks the pan, as a
     polaroid does (css/site.css), instead of staying dipped until the finger or the button lets go. */
  .world[data-panning="true"]:not([data-edit="true"]) .invite__go:active { scale: 1; transition: background-color var(--dur-fast), color var(--dur-fast), scale var(--duration-fast) var(--ease-spring); }
}
/* On a phone (js/board.js `narrow`: narrow, or on its side) the invitation is its title, its day and the pill, set
   larger for the phone's zoom; the rest is on the event page. The card keeps its place and width, and is shorter. A
   card someone is typing in (`data-typing`, js/board.js: a tablet whose keyboard made the page short) keeps its form.
   A full run's "Fullt just nu." and a closed one's "Anmälan stängde …" stay under the pill, at 13 px on screen instead
   of the computer's 13 wall px (9 on a phone at rest). */
@media (max-width: 719px), (max-height: 499px) {
  .invite--go:not([data-typing]) :is(.invite__label, .invite__hosts, .invite__meta, .invite__desc, .signup, .invite__more) { display: none; }
  .invite--go:not([data-typing]) .invite__go { display: flex; }
  .invite--go:not([data-typing]) .invite__title { font-size: 30px; }
  .invite--go:not([data-typing]) .invite__when { font-size: 19px; }
  .invite--go:not([data-typing]) .signup__note { margin-top: 10px; font-size: 18px; }
  /* A tap is not a hover. Under a finger css/site.css's 3 px hover lift stayed on the card after any tap on it, and
     after Back too (Chrome keeps the tapped link focused, and :focus-within lifts as well), while the pill's dip is the
     press's answer. On a touch screen the card holds still; a keyboard's focus still lifts it, and a pointer's hover
     (a short computer window) is left alone, as is the editor's pick-up. */
  @media (hover: none) {
    .world:not([data-edit="true"]) .pin:not(:has(:focus-visible)) > .pin__hit:has(> .invite--go:not([data-typing])) { transform: none; }
  }
}

/* a picture that is an event: a small badge on the photo, the whole polaroid links to the event page */
.badge {
  position: absolute;
  z-index: 2;
  right: 5%;
  top: 5%;
  padding: .38em .65em .32em;
  border-radius: 999px;
  background: var(--blue);
  color: var(--on-blue);
  font-family: var(--font-serif);
  font-weight: 600;
  font-size: clamp(9px, 5.2cqi, 13px);
  letter-spacing: .06em;
  text-transform: uppercase;
  font-variation-settings: var(--voice-label);
  white-space: nowrap;
  box-shadow: 0 1px 2px #00000040;
}
.badge--stamp {
  right: auto;
  left: 5%;
  top: 6%;
  padding: .22em .5em .18em;
  border-radius: 5px;
  border: 2px solid var(--blue);
  background: #f8f4e5e6;
  color: var(--blue);
  font-weight: 700;
  font-size: clamp(10px, 6.4cqi, 16px);
  letter-spacing: .03em;
  transform: rotate(-8deg);
  box-shadow: none;
}
.badge--stamp b { font-size: 1.5em; margin-right: .1em; font-weight: 800; }
.pin:hover .badge--go, .pin:focus-within .badge--go { background: var(--on-blue); color: var(--blue); }

/* edit mode (organisers, via the link from admin) */
.hud--edit {
  left: 50%;
  bottom: 16px;
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 9px 16px;
  border-radius: 999px;
  background: var(--ink);
  color: var(--bg);
  font-size: 13px;
  white-space: nowrap;
  box-shadow: 0 8px 24px -10px #00000080;
}
.hud--edit a { color: var(--on-blue); font-weight: 600; text-decoration: none; }
.hud--edit a:hover { text-decoration: underline; }
.hud__status { min-width: 4em; color: #ffffffa6; }
.hud__status[data-error="true"] { color: #ffb4a8; }
.world[data-edit="true"] [data-id] > .pin__hit, .world[data-edit="true"] .text[data-id] { outline: 1px dashed #153dcc70; outline-offset: 6px; }
.world[data-edit="true"] .is-dragging, .world[data-edit="true"] .is-dropping { z-index: 200000; } /* above every pin's stack and the fish (css/site.css) */
.world[data-edit="true"] .is-dragging .pin__hit { transform: scale(1.02); }
.world[data-edit="true"] .pin__hit { transition: none; }
/* The pick-up and the drop ease (150 ms) instead of snapping; the z raise is instant and holds through the settle
   (js/board.js endItemDrag), and --x/--y follow the pointer untransitioned. */
.world[data-edit="true"] .is-dragging .pin__hit, .world[data-edit="true"] .is-dropping .pin__hit { transition: transform var(--duration-quick) var(--ease-smooth-out); }
@media (prefers-reduced-motion: reduce) {
  .world[data-edit="true"] .is-dragging .pin__hit, .world[data-edit="true"] .is-dropping .pin__hit { transition: none; }
}
@media (max-width: 719px) { .hud--edit { left: 14px; right: 14px; transform: none; white-space: normal; font-size: 12px; } }

/* ---------- the way into an event: darken, then the page rises and the fish rides the lip out of frame ---------- */
.curtain { position: fixed; inset: 0; z-index: 60; pointer-events: none; }
.curtain[hidden] { display: none; }
.curtain__scrim { position: absolute; inset: 0; background: #141414; opacity: 0; transition: opacity 400ms ease; }
.curtain.is-dark .curtain__scrim { opacity: .55; }
/* Called off (js/transition.js cancelGo): the dark leaves faster than it came, as a close does. */
.curtain:not(.is-dark) .curtain__scrim { transition-duration: var(--duration-quick); transition-timing-function: ease-out; }
/* While the room is dark and the panel has not risen, a tap on the dark calls the way in off (js/transition.js cancelGo). */
.curtain.is-dark:not(.is-rising) .curtain__scrim { pointer-events: auto; cursor: pointer; }
.curtain__panel {
  position: absolute;
  left: 0; right: 0; top: 0;
  height: 100vh;
  background: #f8f4e5;
  transform: translateY(100vh);
  transition-property: transform;
  will-change: transform;
  box-shadow: 0 -12px 40px -20px #00000066;
}
.curtain.is-rising .curtain__panel { transform: translateY(0); }
/* The fish's copy that rides the rising page's top edge out of frame (js/transition.js ride), above the panel. */
.curtain__rider { position: absolute; left: 0; top: 0; pointer-events: none; will-change: transform; }
/* The page's copy (an event page, the About page), painted into the panel before it rises (js/transition.js paint). */
.curtain__page { position: absolute; inset: 0; overflow: hidden; }
.curtain__page:empty { display: none; }
.curtain__frame { display: block; width: 100%; height: 100%; border: 0; background: #f8f4e5; }

/* Same-origin navigations (hosted wall → event page) swap documents only once the next one has painted, so the panel's
   copy of the page is replaced by the real one without a blank frame. Browsers without cross-document view
   transitions simply navigate. */
@view-transition { navigation: auto; }
::view-transition-group(root), ::view-transition-old(root), ::view-transition-new(root) { animation: none; }
/* Back from the event page (its logo, "Tillbaka till väggen"): the page's snapshot drops down off the finished wall, the
   way in played backwards and faster, as a close is: 500 ms that start fast (the wall shows within about 100 ms), while
   the room lightens from 80 ms; all settled by 500 ms, and the wall takes input from then on. */
html:active-view-transition-type(from-event)::view-transition-old(root) { animation: curtain-down var(--duration-very-slow) var(--ease-smooth-out) both; z-index: 2; }
html:active-view-transition-type(from-event)::view-transition-new(root) { animation: room-lighten var(--duration-medium) ease-out var(--duration-micro) both; }
@keyframes curtain-down { from { transform: translateY(0); } to { transform: translateY(100vh); } }
@keyframes room-lighten { from { filter: brightness(.45); } to { filter: none; } }
@keyframes vt-fade { to { opacity: 0; } }
/* Reduced motion: no travel and no brightness, the page's snapshot fades off the wall. */
@media (prefers-reduced-motion: reduce) {
  html:active-view-transition-type(from-event)::view-transition-old(root) { animation: vt-fade var(--duration-quick) ease-out both; }
  html:active-view-transition-type(from-event)::view-transition-new(root) { animation: none; }
}
body.curtain-open .hud { pointer-events: none; }
@media (prefers-reduced-motion: reduce) { .curtain__scrim, .curtain__panel { transition: none; } }
