/* ==========================================================================
   COHERENCE — EVENTS

   A printed programme that answers the reader.

   The tokens, the type and the nav come from styles.css. What is here is
   the programme itself. Each event is a spread on a twelve-column grid:
   the photograph on one side, the facts on the other, and the day printed
   large in the event's own colour across the seam between them. Spreads
   alternate sides and change shape, three shapes in turn, so the page
   never settles into a template.

   Three things make it move, and all three stay under the threshold of
   performance:
   Arrival   a photograph is uncovered from the side it faces the first time
             it comes into view, the day rises after it, the words last.
   Depth     while the page is read, each photograph drifts slower than its
             frame and each day faster, so the spread has layers.
   Presence  a pointer over a photograph looks through it like a window:
             the picture leans away, its outline leans with the hand, and
             a small mark in the event's colour trails the pointer.
   ========================================================================== */

/* --------------------------------------------------------------- the voices */

.xp[data-voice="creations"] { --v: var(--c-creations); }

.xp[data-voice="guided"] { --v: var(--c-guided); }

.xp[data-voice="by"] { --v: var(--c-by); }

.xp[data-voice="design"] { --v: var(--c-design); }

.xp[data-voice="and"] { --v: var(--c-and); }

.xp[data-voice="coherence"] { --v: var(--c-coherence); }


/* ----------------------------------------------------------------- the band

   A band of the same dark as the passage on the landing, with the way back
   over the words. It is as tall as it needs to be and not one screen: the
   calendar is what the page is for.
   -------------------------------------------------------------------------- */

.xp-band { --lit: #f4f2ec; --lit-52: rgba(244,242,236,.52); --lit-28: rgba(244,242,236,.28); position: relative; display: flex; flex-direction: column; justify-content: flex-end; gap: var(--sp-4); min-height: calc(var(--screen) * 0.46); padding: var(--sp-7) max(var(--sp-5), calc((100% - 84rem) / 2)) var(--sp-6); background: #121210; color: var(--lit); }

/* the way back, with the name's own gesture in it */
.xp-back-up { align-self: flex-start; font-family: Anybody, system-ui, sans-serif; font-weight: 500; font-size: var(--t-label); letter-spacing: 0.12em; text-transform: uppercase; color: var(--lit-52); text-decoration: none; transition: color .35s var(--ease); }

.xp-back-up::before { content: "\2190 "; }

.xp-back-up:hover, .xp-back-up:focus-visible { color: var(--lit); }

.xp-band-title { margin: 0px; max-width: 18ch; font-family: Outfit, system-ui, sans-serif; font-weight: 400; font-size: clamp(2.4rem, 6.4vw, 6rem); line-height: 0.98; letter-spacing: -0.035em; color: var(--lit); text-wrap: balance; }

/* what follows the band, said once and quietly */
.xp-upcoming { display: flex; align-items: baseline; gap: var(--sp-3); margin: 0px auto; width: min(100% - 2 * var(--sp-5), 84rem); padding-top: var(--sp-6); }

.xp-up-word { margin: 0px; font-family: Outfit, system-ui, sans-serif; font-weight: 400; font-size: clamp(1.9rem, 4.4vw, 3.6rem); line-height: 1; letter-spacing: -0.03em; color: var(--ink); }

/* a line that runs from the word to the edge of the calendar */
.xp-up-rule { flex: 1 1 auto; height: 1px; background: var(--hair); }

/* how many are ahead, kept small beside it */
.xp-up-n { font-family: Anybody, system-ui, sans-serif; font-weight: 500; font-size: var(--t-label); letter-spacing: 0.18em; color: var(--ink-38); }

.js .xp-band > *, .js .xp-upcoming > * { opacity: 0; translate: 0px 18px; transition: opacity 1s var(--ease), translate 1.2s var(--ease); }

.js .xp-band.is-on > *, .js .xp-upcoming.is-on > * { opacity: 1; translate: 0px 0px; }

.js .xp-band.is-on > .xp-band-title { transition-delay: .14s; }

@media (max-width: 900px){
  .xp-band { min-height: 0px; gap: var(--sp-3); padding: var(--sp-6) var(--sp-3) var(--sp-5); }
  .xp-upcoming { width: calc(100% - 2 * var(--sp-3)); padding-top: var(--sp-5); }
}

@media (prefers-reduced-motion: reduce){
  .js .xp-band > *, .js .xp-upcoming > * { opacity: 1; translate: none; transition: none; }
}


/* ------------------------------------------------------------- the spreads */

.xp-list { list-style: none; margin: 0px auto; padding: var(--sp-7) var(--sp-5) var(--sp-7); max-width: 84rem; display: grid; gap: clamp(8rem, 16vw, 15rem); }

.xp-inner { position: relative; display: grid; grid-template-columns: repeat(12, minmax(0px, 1fr)); column-gap: var(--sp-3); align-items: center; margin: 0px; }

/* three shapes, taken in turn, each mirrored on alternate spreads */
.xp[data-shape="a"][data-side="left"] { --gc-cover: 1 / span 7; --gc-date: 6 / span 3; --gc-text: 9 / span 4; --ratio: 5 / 4; }

.xp[data-shape="a"][data-side="right"] { --gc-cover: 6 / span 7; --gc-date: 5 / span 3; --gc-text: 1 / span 4; --ratio: 5 / 4; }

.xp[data-shape="b"][data-side="left"] { --gc-cover: 2 / span 5; --gc-date: 6 / span 3; --gc-text: 9 / span 4; --ratio: 4 / 5; }

.xp[data-shape="b"][data-side="right"] { --gc-cover: 7 / span 5; --gc-date: 5 / span 3; --gc-text: 1 / span 4; --ratio: 4 / 5; }

.xp[data-shape="c"][data-side="left"] { --gc-cover: 1 / span 6; --gc-date: 5 / span 3; --gc-text: 8 / span 5; --ratio: 1 / 1; }

.xp[data-shape="c"][data-side="right"] { --gc-cover: 7 / span 6; --gc-date: 6 / span 3; --gc-text: 1 / span 5; --ratio: 1 / 1; }

/* the photograph */
.xp-cover { --off: 14px; --dir: 1; position: relative; display: block; grid-row: 1; grid-column: var(--gc-cover); color: inherit; text-decoration: none; }

.xp[data-side="right"] .xp-cover { --dir: -1; }

.xp-crop { position: relative; display: block; overflow: hidden; aspect-ratio: var(--ratio, 5 / 4); border-radius: 4px; background: rgb(233, 230, 223); }

/* larger than its frame on every side, so it can drift and lean without an
   edge ever showing */
/* A poster or a flyer is a whole thing, made at a size someone chose: it
   is shown entire, without the drift that a photograph is cropped for. */
.xp[data-fit="whole"] .xp-crop { background: rgb(250, 249, 246); }

.xp[data-fit="whole"] .xp-img { position: static; top: auto; left: auto; width: 100%; height: 100%; object-fit: contain; transform: none; }

.xp-img { position: absolute; top: -10%; left: -5%; width: 110%; height: 120%; max-width: none; object-fit: cover; transform: translate3d(calc(var(--lx, 0) * 1px), calc(var(--p, 0) * -5% + var(--ly, 0) * 1px), 0px) scale(calc(1 + var(--hov, 0) * 0.05)); will-change: transform; transition: filter .8s var(--ease); }

/* an outline in the event's colour, printed a little off register */
.xp-frame { position: absolute; inset: 0px; z-index: 2; border: 1.5px solid var(--v); border-radius: 4px; pointer-events: none; transform: translate3d(calc(var(--dir) * (var(--off) + var(--hov, 0) * 6px) - var(--lx, 0) * 0.8px), calc(var(--off) + var(--p, 0) * 12px + var(--hov, 0) * 6px - var(--ly, 0) * 0.8px), 0px); }

/* the mark that trails a pointer over the photograph */
.xp-badge { position: absolute; left: 0px; top: 0px; z-index: 4; display: grid; place-items: center; width: 6.2rem; height: 6.2rem; margin: -3.1rem 0 0 -3.1rem; border-radius: 50%; background: var(--v); color: var(--paper); font-family: Anybody, system-ui, sans-serif; font-weight: 600; font-size: var(--t-label); letter-spacing: 0.12em; text-transform: uppercase; pointer-events: none; transform: translate3d(calc(var(--bx, 0) * 1px), calc(var(--by, 0) * 1px), 0px) scale(var(--cov, 0)); }

@media not all and (hover: hover) and (pointer: fine){
  .xp-badge { display: none; }
}

/* the day, printed over the seam */
/* The day is printed across the seam of the photograph and the page. It
   used to be multiplied into the image, which sank it into the dark parts
   of a photograph; now it is simply set in its own voice, at full strength,
   and nothing is added around it. */
.xp-date { grid-row: 1; grid-column: var(--gc-date); align-self: start; z-index: 2; display: flex; align-items: flex-start; gap: 0.5rem; margin: -0.34em 0 0; font-size: clamp(7rem, 16vw, 16rem); color: var(--v); pointer-events: none; transform: translate3d(calc(var(--lx, 0) * -0.6px), calc(var(--p, 0) * 70px), 0px); }

.xp[data-side="right"] .xp-date { justify-content: flex-end; }

.xp-day { font-family: Anybody, system-ui, sans-serif; font-weight: 700; font-size: 1em; line-height: 0.8; letter-spacing: -0.06em; font-variant-numeric: tabular-nums; }

.xp-month { margin-top: 0.9rem; font-family: Anybody, system-ui, sans-serif; font-weight: 600; font-size: var(--t-label); letter-spacing: 0.18em; text-transform: uppercase; writing-mode: vertical-rl; }

/* an event without a date carries a word where the day would be */
/* A gathering without a date says so in a word, printed as large as the day
   it stands for. The only difference is where it is allowed to be: a word is
   long, so it is kept over the photograph — at the inner corner, hanging
   over its top edge like a numeral does — instead of over the seam, where it
   would land on the name. */
.xp[data-state="soon"] .xp-date { grid-column: var(--gc-cover); align-self: start; justify-content: flex-end; margin-top: -0.2em; font-size: clamp(4rem, 8vw, 8rem); }

.xp[data-side="right"][data-state="soon"] .xp-date { justify-content: flex-start; }

.xp[data-state="soon"] .xp-day { font-weight: 600; letter-spacing: -0.02em; }

/* the words */
.xp-text { position: relative; z-index: 3; grid-row: 1; grid-column: var(--gc-text); text-align: left; }

.xp-index { margin: 0px; display: flex; align-items: center; gap: 0.9em; font-family: Anybody, system-ui, sans-serif; font-weight: 500; font-size: var(--t-label); letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink-52); }

.xp-index::before { content: ""; width: 1.8rem; height: 1.5px; background: var(--v); }

.xp-flag { color: var(--ink); }

.xp-name { margin: var(--sp-3) 0 0; font-weight: 400; font-size: clamp(2rem, 3.4vw, 3.4rem); line-height: 1.02; letter-spacing: -0.03em; color: var(--ink); text-wrap: balance; }

/* underlined in its colour while anyone is with the spread */
.xp-name-line { background-image: linear-gradient(var(--v), var(--v)); background-repeat: no-repeat; background-position: 0 94%; background-size: calc(var(--hov, 0) * 100%) 2px; }

.xp-desc { margin: var(--sp-3) 0 0; max-width: 30rem; font-size: var(--t-body); line-height: 1.55; color: var(--ink-72); }

.xp-meta { margin: var(--sp-4) 0 0; display: grid; grid-template-columns: auto minmax(0px, 1fr); gap: 0.55rem 1.4rem; font-size: var(--t-sm); }

.xp-meta div { display: contents; }

.xp-meta dt { align-self: center; font-family: Anybody, system-ui, sans-serif; font-weight: 500; font-size: var(--t-label); letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink-38); }

.xp-meta dd { margin: 0px; color: var(--ink); }

/* the way to the event: its colour pours in from the left */
.xp-go { position: relative; isolation: isolate; overflow: hidden; display: inline-flex; align-items: center; gap: 0.8rem; margin-top: var(--sp-4); padding: 0.95rem 1.4rem 0.95rem 1.5rem; border: 1px solid var(--ink-18); border-radius: 999px; font-family: Anybody, system-ui, sans-serif; font-weight: 600; font-size: var(--t-label); letter-spacing: 0.12em; text-transform: uppercase; color: var(--ink); text-decoration: none; transition: color .45s var(--ease), border-color .45s var(--ease); }

.xp-go::before { content: ""; position: absolute; inset: 0px; z-index: -1; border-radius: inherit; background: var(--v); transform: scaleX(0); transform-origin: 0% 50%; transition: transform .6s cubic-bezier(.7, 0, .2, 1); }

.xp-go:hover, .xp-go:focus-visible { color: var(--paper); border-color: var(--v); }

.xp-go:hover::before, .xp-go:focus-visible::before { transform: scaleX(1); }

.xp-arrow { display: inline-block; transition: transform .5s var(--ease); }

.xp-go:hover .xp-arrow, .xp-go:focus-visible .xp-arrow { transform: translateX(4px); }

/* what has already happened keeps its place, in black and white until it is
   looked at */
.xp[data-state="past"] .xp-img { filter: grayscale(1) contrast(1.04); }

.xp[data-state="past"] .xp-inner:hover .xp-img { filter: none; }

.xp-empty { margin: 0px auto; font-size: var(--t-md); color: var(--ink-72); text-align: center; }


/* ---------------------------------------------------------------- arrival */

/* the photograph is uncovered from the side it faces */
.js .xp-crop { clip-path: inset(0 100% 0 0); transition: clip-path 1.35s cubic-bezier(.7, 0, .2, 1); }

.js .xp[data-side="right"] .xp-crop { clip-path: inset(0 0 0 100%); }

.js .xp.is-in .xp-crop { clip-path: inset(0 0 0 0); }

.js .xp-frame { opacity: 0; transition: opacity .9s var(--ease) 1s; }

.js .xp.is-in .xp-frame { opacity: 1; }

/* the day rises once the picture is there */
.js .xp-date { opacity: 0; translate: 0px 60px; transition: opacity 1s var(--ease) .55s, translate 1.4s var(--ease) .55s; }

.js .xp.is-in .xp-date { opacity: 1; translate: 0px 0px; }

/* and the words come last, one after another */
.js .xp-text > * { opacity: 0; translate: 0px 22px; transition: opacity .9s var(--ease), translate 1.1s var(--ease); }

.js .xp.is-in .xp-text > * { opacity: 1; translate: 0px 0px; }

.js .xp.is-in .xp-text > :nth-child(1) { transition-delay: .4s; }

.js .xp.is-in .xp-text > :nth-child(2) { transition-delay: .5s; }

.js .xp.is-in .xp-text > :nth-child(3) { transition-delay: .6s; }

.js .xp.is-in .xp-text > :nth-child(4) { transition-delay: .7s; }

.js .xp.is-in .xp-text > :nth-child(5) { transition-delay: .8s; }


/* ------------------------------------------------------------------ close */

/* the calendar ends where it began: a field of presences, around the last
   two lines. Fewer of them, and slower — this is a room being left, not one
   being entered. */
.xp-end { position: relative; overflow: clip; display: flex; flex-direction: column; align-items: center; gap: var(--sp-4); padding: var(--sp-6) var(--sp-4) var(--sp-7); text-align: center; }

.xp-end > :not(.field) { position: relative; z-index: 1; }

.xp-end-line { margin: 0px; max-width: 18ch; font-size: var(--t-lg); line-height: 1.1; letter-spacing: -0.02em; color: var(--ink); text-wrap: balance; }

.xp-back { font-family: Anybody, system-ui, sans-serif; font-weight: 500; font-size: var(--t-label); letter-spacing: 0.12em; text-transform: uppercase; color: var(--ink-52); text-decoration: none; border-bottom: 1px solid var(--ink-18); padding-bottom: 3px; transition: color .3s var(--ease), border-color .3s var(--ease); }

.xp-back::before { content: "\2190  "; }

.xp-back:hover { color: var(--ink); border-color: var(--ink-52); }

.xp-noscript { max-width: 34rem; margin: 0px auto var(--sp-6); padding: 0 var(--sp-4); text-align: center; color: var(--ink-72); }


/* ---------------------------------------------------------- smaller screens */

@media (max-width: 900px){
  .xp-head { min-height: calc(var(--screen) * 0.92); padding: 9rem 2.4rem var(--sp-5); }
  .xp-list { padding: var(--sp-5) var(--sp-3) var(--sp-6); gap: var(--sp-7); }
  .xp-inner { grid-template-columns: minmax(0px, 1fr); column-gap: 0px; }
  .xp[data-shape][data-side] { --gc-cover: 1; --gc-date: 1; --gc-text: 1; --ratio: 4 / 3; --ar: 1.3333; }
  .xp-cover { --off: 8px; width: min(100%, calc(calc(var(--screen) * 0.56) * var(--ar, 1.3333))); margin-inline: auto; }
  .xp-date { align-self: end; align-items: flex-end; margin: 0 0 -0.16em; font-size: clamp(5.5rem, 28vw, 10rem); }
  .xp[data-state="soon"] .xp-date { align-self: start; justify-content: flex-end; margin: -0.18em 0 0; font-size: clamp(2.8rem, 14vw, 5rem); }
  .xp-month { writing-mode: horizontal-tb; margin: 0 0 0.22em; }
  .xp[data-side="left"] .xp-date { justify-content: flex-end; }
  .xp[data-side="right"] .xp-date { justify-content: flex-start; }
  .xp-text { grid-row: 2; margin-top: var(--sp-4); }
  .xp-name { font-size: clamp(1.9rem, 8vw, 2.6rem); }
}

@media (prefers-reduced-motion: reduce){
  .js .xp-head > :not(.field), .js .xp-date, .js .xp-text > *, .js .xp-crop, .js .xp-frame { opacity: 1; translate: none; filter: none; clip-path: none; transition: none; }
}
