/* ==========================================================================
   aiRA - /events, the tour hub. Light page under a DARK hero band: a centred
   hero over a dot map of the US with clickable tour pins and flat objects at its edges, a grid of
   map-thumbnail event cards, and the photo reel. Each event's page is
   styled by event.css. Loads after tokens -> global -> components.
   ========================================================================== */

/* THE HERO IS THE TOUR SHIRT since 13 Oct (the user's pick of four stacked
   concepts: "concept one looks the strongest ... lets go with concept 1";
   the road trip, the departures board and the postcards are in git, markup
   and rules). The back of the tour tee as a poster on --black: stacked caps
   with TOUR outlined and swept with colour, the date list with the played
   stop struck through, the 2027 line, the sub, one CTA, the dot map a
   halftone behind. KEPT from the previous dark hero: the torn ticket edge
   at the band's foot, the brand glow rising into it, the straddling fact
   ticket, the hover tiles and the transparent nav. The US pin map (.evus)
   and the four flat objects (.evobjs) left with the map hero (in git).
   Primitives throughout: a drawn object. */
.evhub {
  --nav-stack: calc(var(--nav-h) + var(--nav-float) + 2px);
  --scallop: 7px;
  position: relative;
  margin-top: calc(-1 * var(--nav-stack));
  padding: 0;
  background: var(--surface-2);
}
/* The ticket's height, on the body because the band and the grid section
   below it both measure off it. */
body.page-events { --tick-h: 56px; }
body.page-events .nav.nav-dark:not(.nav--scrolled) {
  background-color: transparent !important;
  border-color: transparent;
  box-shadow: none;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}
.evhub__panel {
  position: relative;
  overflow: clip;
  display: flex;
  flex-direction: column;
  justify-content: center;
  /* A MIN-height, not a height: the poster outgrows a short screen and must
     push the band rather than clip. Half the ticket hangs below the band,
     so it stops half a ticket short of the fold. */
  min-height: max(calc(100vh - var(--tick-h) / 2), 720px);
  min-height: max(calc(100svh - var(--tick-h) / 2), 720px);
  padding-top: calc(var(--nav-stack) + var(--sp-10));
  padding-bottom: calc(var(--tick-h) / 2 + var(--sp-8));
  background:
    radial-gradient(90% 110% at 50% -30%, color-mix(in srgb, var(--primary-600) 55%, transparent), transparent 62%),
    radial-gradient(55% 38% at 50% 104%, color-mix(in srgb, var(--primary-600) 46%, transparent), transparent 72%),
    var(--black);
  /* The torn ticket edge: one scallop per 22px tile, the section's
     --surface-2 ground showing through. */
  -webkit-mask: radial-gradient(circle var(--scallop) at 50% 100%, transparent 96%, #000 100%) 50% 0 / 22px 100% repeat-x;
  mask: radial-gradient(circle var(--scallop) at 50% 100%, transparent 96%, #000 100%) 50% 0 / 22px 100% repeat-x;
}
/* The hover-tile grid left with the particles (user, 13 Oct: "remove
   square grid effect"); everything in the panel sits above the band. */
.evhub__panel > * { position: relative; z-index: 1; }

/* ---- The poster ------------------------------------------------------------
   TOUR ANIMATES (user: "the tour word can be a animating something"): the
   outlined word is a background-clip: text gradient, a narrow two-colour
   band sweeping through the letters on background-position; the outline is
   always drawn, so reduced motion and old engines keep the resting shirt. */
.evh1__poster { position: relative; text-align: center; }
.evh1__dots {
  position: absolute;
  inset: -10% 0;
  background: color-mix(in srgb, var(--on-brand) 9%, transparent);
  -webkit-mask: url(/assets/events/us-dots.svg) center / contain no-repeat;
  mask: url(/assets/events/us-dots.svg) center / contain no-repeat;
}
/* THE MAP IS INTERACTIVE PARTICLES when it can be (user, 13 Oct, from
   Bencho's Particles): js/evh1-dots.js seats one dot per us-dots.svg
   point on this canvas and adds .is-live, standing the masked span down.
   The span above stays the ground with scripts off, under reduced motion
   and on touch, where nothing could push a dot anyway. The canvas takes
   pointer-events none: the PANEL is listened to, so the CTA and the date
   links stay plain links. */
/* The canvas is ALWAYS displayed (transparent until the script draws):
   hidden, it measures a zero box and the script could never size it. Only
   the span stands down. */
/* Explicit width and height, NOT inset alone: a canvas is a replaced
   element, so insets do not stretch it and it sits at its intrinsic
   300x150. */
.evh1__parts {
  position: absolute;
  top: -10%;
  left: 0;
  width: 100%;
  height: 120%;
  display: block;
  pointer-events: none;
  color: color-mix(in srgb, var(--on-brand) 14%, transparent);
}
.evh1__poster.is-live .evh1__dots { display: none; }
.evh1__poster > :not(.evh1__dots):not(.evh1__parts) { position: relative; }
/* The presenter is TYPED, not the lockup (13 Oct, user: "the dual aira
   logo looks weird ... we wanna keep the header one"). The lockup sat a few
   centimetres under the header's own, two logos stacked on one screen; a
   credit line is what a tour poster carries anyway. Sentence case on
   purpose: uppercasing would render the brand "AIRA". */
.evh1__pre {
  margin: 0 0 var(--sp-5);
  font-size: var(--t-body);
  font-weight: 500;
  color: color-mix(in srgb, var(--on-brand) 70%, transparent);
}
.evh1__t {
  margin: 0;
  font-family: var(--font-display);
  font-size: clamp(3rem, 10.5vw, 7.5rem);
  font-weight: 700;
  line-height: 0.92;
  letter-spacing: -0.03em;
  text-transform: uppercase;
  color: var(--on-brand);
}
.evh1__year {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-4);
  margin: var(--sp-6) 0 0;
  font-family: var(--font-display);
  font-size: var(--t-body);
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--tertiary-300);
}
.evh1__year i { font-style: normal; font-size: var(--t-caption); }
.evh1__dates { list-style: none; margin: var(--sp-8) 0 0; padding: 0; display: grid; gap: var(--sp-3); }
.evh1__stop {
  display: inline-flex;
  align-items: baseline;
  gap: var(--sp-5);
  font-family: var(--font-display);
  font-size: var(--t-title-lg);
  font-weight: 600;
  letter-spacing: 0.01em;
  text-transform: uppercase;
  color: var(--on-brand);
  text-decoration: none;
}
.evh1__stop b { font-weight: 600; font-variant-numeric: tabular-nums; color: var(--tertiary-300); }
a.evh1__stop { transition: color var(--dur-fast) var(--ease); }
a.evh1__stop:hover { color: var(--primary-200); }
a.evh1__stop:focus-visible { outline: 2px solid var(--primary-200); outline-offset: 4px; }
/* A played stop is GREYED, not struck (user, 13 Oct: "greyscaling is
   sufficient"), with a green done tick
   (Phosphor CheckCircle duotone, from the package) as a SUFFIX on the
   same line (user: "not top right but suffix same line"). Its negative
   right margin cancels its own width plus the gap, so it hangs off the end
   and the date and city stay centred like the live stops. A "Wrapped"
   rubber stamp replaced it for a few hours on 22 Oct and was reverted
   (user: "not looking good"). */
.evh1__stop--past { color: color-mix(in srgb, var(--on-brand) 38%, transparent); }
.evh1__done {
  flex: none;
  align-self: center;
  width: 20px;
  height: 20px;
  margin-left: calc(var(--sp-3) - var(--sp-5));
  margin-right: calc(-20px - var(--sp-3));
  color: var(--approve-on-dark);
}
.evh1__stop--past b { color: color-mix(in srgb, var(--on-brand) 38%, transparent); }
.evh1__later {
  margin: var(--sp-8) 0 0;
  font-family: var(--font-display);
  font-size: var(--t-body);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--on-brand) 50%, transparent);
}
.evh1__later span { font-weight: 500; letter-spacing: 0; text-transform: none; }

/* The highlight is CUSTOMER ANALYTICS in solid Tertiary/300 (13 Oct,
   user, option 1): colour, not the outline TOUR and FUTURE wore, because
   two outlined lines would leave half the title as hairlines. A lighter
   Primary/200 band still sweeps through it; at rest it is plain T300. */
.evh1__t em {
  font-style: normal;
  color: transparent;
  background: linear-gradient(105deg, var(--tertiary-300) 0 38%, var(--primary-200) 50%, var(--tertiary-300) 62% 100%);
  background-size: 260% 100%;
  background-position: 130% 0;
  -webkit-background-clip: text;
  background-clip: text;
}
@media (prefers-reduced-motion: no-preference) {
  .evh1__t em { animation: evh1-sweep 4.6s var(--ease) infinite; }
}
@keyframes evh1-sweep {
  0% { background-position: 130% 0; }
  60%, 100% { background-position: -30% 0; }
}

/* SHORT DESKTOP WINDOWS give up head room before the poster does. */
@media (min-width: 992px) and (max-height: 820px) {
  .evhub__panel { padding-top: calc(var(--nav-stack) + var(--sp-8)); }
}

/* The fact ticker, along the panel's foot; the globe above it carries the
   air, so it sits snug on the dome's clip line. */
/* SOLID TERTIARY BAND since 10 Oct (user: "add a solid bg ... teal or
   purple"): Tertiary/600, the teal-side anchor, a primitive like the rest of
   the drawn band. The dashed hairline went with the transparency. */
/* THE TICKET. Tertiary/600 (the teal-side anchor, a primitive like the
   rest of the drawn band), 12px corners, a semicircle notch cut from each
   short end by a mask, and a stub divided from the body by a dashed tear
   line: an admission ticket, not a news crawl. It sits under the tour map;
   the belt is clipped inside the body. */
/* A LINK since 13 Oct: the ticket to the next stop. The notch mask clips a
   box-shadow, so the lift is a drop-shadow FILTER (the evbar lesson), which
   follows the notches; hover raises it 2px and pauses the belt. */
a.evtick { text-decoration: none; }
.evtick {
  --notch: 10px;
  flex: none;
  display: flex;
  align-items: stretch;
  width: min(880px, calc(100% - 2 * var(--sp-5)));
  height: var(--tick-h);
  /* ACROSS THE SEAM: pulled up half its height over the band's foot and
     half hanging into the grid section, which the negative bottom margin
     keeps out of the section's height (the joint lands on the seam). Above
     the joint (z-index 2) and the next section. */
  position: relative;
  z-index: 3;
  margin: calc(var(--tick-h) / -2) auto calc(var(--tick-h) / -2);
  background: var(--tertiary-600);
  border-radius: var(--r-md);
  -webkit-mask:
    radial-gradient(circle var(--notch) at 0 50%, transparent 98%, #000) left / 51% 100% no-repeat,
    radial-gradient(circle var(--notch) at 100% 50%, transparent 98%, #000) right / 51% 100% no-repeat;
  mask:
    radial-gradient(circle var(--notch) at 0 50%, transparent 98%, #000) left / 51% 100% no-repeat,
    radial-gradient(circle var(--notch) at 100% 50%, transparent 98%, #000) right / 51% 100% no-repeat;
  filter: drop-shadow(0 14px 26px color-mix(in srgb, var(--black) 30%, transparent));
  transition: translate var(--dur-fast) var(--ease), filter var(--dur-fast) var(--ease);
}
a.evtick:hover,
a.evtick:focus-visible {
  translate: 0 -2px;
  filter: drop-shadow(0 20px 32px color-mix(in srgb, var(--black) 42%, transparent));
}
a.evtick:hover .evtick__belt,
a.evtick:focus-visible .evtick__belt { animation-play-state: paused; }
a.evtick:focus-visible { outline: 2px solid var(--brand); outline-offset: 3px; }
.evtick__stub {
  flex: none;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 0 var(--sp-6) 0 calc(var(--notch) + var(--sp-5));
  border-right: 2px dashed color-mix(in srgb, var(--on-brand) 45%, transparent);
  font-size: var(--t-body);
  font-weight: 500;
  white-space: nowrap;
  color: color-mix(in srgb, var(--on-brand) 80%, transparent);
}
.evtick__stub b {
  font-family: var(--font-display);
  font-size: var(--t-title-xl);
  font-weight: 500;
  color: var(--on-brand);
}
.evtick__body {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  overflow: clip;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 24px, #000 calc(100% - 24px), transparent);
  mask-image: linear-gradient(90deg, transparent, #000 24px, #000 calc(100% - 24px), transparent);
}
.evtick__belt {
  display: flex;
  width: max-content;
  animation: evtick 56s linear infinite;
}
.evtick__seq {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: var(--sp-6);
  padding-left: var(--sp-6);
  /* Inter, sentence case: no Geist Mono on new builds (user, 10 Oct). */
  font-size: var(--t-body);
  font-weight: 500;
  white-space: nowrap;
  color: var(--on-brand);
}
.evtick__seq i { font-style: normal; font-size: var(--t-caption); color: color-mix(in srgb, var(--on-brand) 70%, transparent); }
.evtick__seq b { font-weight: 700; color: var(--on-brand); }
/* The barcode on the right end, behind its own tear: two stripe pitches
   overlaid so the bars read irregular, like a printed code. */
.evtick__code {
  flex: none;
  align-self: center;
  width: 46px;
  height: 26px;
  margin: 0 calc(var(--notch) + var(--sp-4)) 0 var(--sp-4);
  border-left: 2px dashed color-mix(in srgb, var(--on-brand) 45%, transparent);
  padding-left: var(--sp-3);
  background:
    repeating-linear-gradient(90deg, var(--on-brand) 0 2px, transparent 2px 6px),
    repeating-linear-gradient(90deg, var(--on-brand) 0 1px, transparent 1px 9px);
  background-clip: content-box;
  opacity: 0.9;
}
@keyframes evtick { to { transform: translateX(-50%); } }

/* The tear line's ink, used by every ticket on this page: --border-strong
   (#E2E5EE) vanished on white and 1.5px rounded to 1, so it is 2px at 18%
   ink. (Deleted once by a block rewrite, which blanked every tear line on
   the page; keep it here, ahead of its first use.) */
:root { --evtear: color-mix(in srgb, var(--ink) 18%, transparent); }

/* THE FEATURED BLOCK (.evfeat, "Coming to Chicago.") CAME OUT 12 Oct (user:
   "lets not have the latest event single card there"); markup and rules are
   in git. The upcoming cards in the grid carry the next event now. */
.evcta__go { width: 16px; height: 16px; transition: transform var(--dur-fast) var(--ease); }

@media (prefers-reduced-motion: reduce) {
  .evcta__go { transition: none; }
}

/* ---- The toolbar head: title, search, status toggle ------------------------
   From a sarvam-style screenshot (user, 10 Oct): "All events" left, the
   controls right on one line, no eyebrow. The toggle is a SEGMENTED control
   with the chosen segment FILLED in --black: the reference shows exactly
   that, and a segmented control is the one place a fill is the state
   (the colour-not-fill rule is for hover and selected cards). Both
   controls are 48px tall so they read as one row. Below 768 they stack
   full width, the three segments sharing it equally. */

.evbar-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-5) var(--sp-8);
  margin-bottom: var(--sp-10);
}
.evbar-head__lead { max-width: 40rem; }
.evbar-head__t { margin: 0; }
.evtools { display: flex; align-items: center; gap: var(--sp-3); }

/* SMALL, WITH A SLIDING ACTIVE STATE (user, 10 Oct: "make evfilter small,
   the active state moves and animates"). 36px tall. One black pill under
   the buttons, placed by js/events-filter.js off the pressed button's own
   box and animated in x and width, so it travels between options instead of
   blinking. .is-live (added by the script) hands the fill to the pill;
   without it the pressed button carries the fill itself. */
.evfilter {
  position: relative;
  display: inline-flex;
  height: 36px;
  padding: 3px;
  gap: 2px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-pill);
}
.evfilter__pill {
  position: absolute;
  top: 3px;
  bottom: 3px;
  left: 0;
  width: var(--w, 0);
  transform: translateX(var(--x, 0));
  border-radius: var(--r-pill);
  background: var(--black);
  opacity: 0;
}
.evfilter.is-live .evfilter__pill {
  opacity: 1;
  transition: transform 360ms cubic-bezier(0.34, 1.36, 0.64, 1), width 360ms cubic-bezier(0.34, 1.36, 0.64, 1);
}
.evfilter__btn {
  position: relative;
  z-index: 1;
  appearance: none;
  background: none;
  border: 0;
  border-radius: var(--r-pill);
  padding: 0 14px;
  font: inherit;
  font-size: var(--t-body);
  font-weight: 500;
  color: var(--ink-2);
  cursor: pointer;
  transition: color 240ms var(--ease);
}
.evfilter__btn:hover { color: var(--ink); }
.evfilter__btn[aria-pressed="true"] { background: var(--black); color: var(--on-brand); }
.evfilter.is-live .evfilter__btn[aria-pressed="true"] { background: none; }
.evfilter__btn:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }


/* ---- The grid: every session, its own cover ---------------------------------
   Three across (sarvam), each card its square Luma cover over the facts. The
   drawn per-city poster bands this replaced are in git. */

/* The hero's "View events" anchors here. The section's top edge lands
   EXACTLY under the floating header (user, 10 Oct: it was "not coming
   exactly on all sessions section"). TRAP: html already carries
   scroll-padding-top (nav + float + 24px) and scroll-margin ADDS to it, so
   the old margin, which restated the same clearance, doubled it and stopped
   ~90px short with the featured section showing. This margin is only the
   DIFFERENCE between that padding and the nav stack (nav + float + 2px
   border): -22px. */
.evgrid-sec { scroll-margin-top: calc(2px - 24px); }
/* EACH SECTION ITS OWN GROUND (user, 12 Oct: "give both all events and past
   photos different bg section color"): the grid on --surface-2 (slate-100),
   so the white cards lift off it, and the photo reel on --surface (white),
   which keeps the photographs clean. Dark hero, grey grid, white reel, grey
   footer: no two neighbours share a tone. */
.evgrid-sec { position: relative; background: var(--surface-2); padding-top: calc(var(--section-pad) + var(--tick-h) / 2); }
.evgrid-sec > .padding-global { position: relative; z-index: 1; }
/* The dot paper: .evbody__ground's 20px / 11% spec (20 divides --cell), on
   its own element because the section pseudos are the rails. It dies before
   the foot so the boundary to the reel stays clean. */
.evgrid__ground {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image: radial-gradient(circle, color-mix(in srgb, var(--ink) 11%, transparent) 1px, transparent 1px);
  background-size: 20px 20px;
  -webkit-mask-image: linear-gradient(to bottom, #000 0 32%, transparent 86%);
  mask-image: linear-gradient(to bottom, #000 0 32%, transparent 86%);
}
/* ============ THE TICKET GRAMMAR ACROSS THE PAGE (13 Oct) ============
   (user: "that cutout we added in hero, lets have that kind of details and
   fun across events and all details page ... a different thing
   altogether"). Two moves, both this page and every /events/<slug> page
   (event.css restates the joint half):
   - EVERY JOINT IS A PERFORATION: the 1px hairline becomes a dashed tear
     with a PUNCH HOLE on each rail where the brand squares sat. Positions
     are global.css's own; only the drawing changes.
   - THE REEL TEARS INTO THE GRID: the white section's top edge carries a
     row of --surface-2 scallops, the grid's ground biting down, the hub
     hero's torn edge quoted at a seam. */
/* NO JOINT LINES ON THE HUB since 13 Oct (user: "since horizontally we are
   having that cutout effect, lets remove horizontal lines appearing here"):
   the hero already ends in a torn scallop edge with the ticket straddling
   the seam, and the reel tears in on its own scallops, so a dashed rule at
   the same boundaries was the same statement twice. The joint divs stay in
   the markup (joints are placed there, the site rule); this stands them
   down on THIS PAGE only, punch-hole rings included. The detail pages keep
   their perforated joints (event.css). */
/* THE JOINTS ARE BACK ON THE HUB since 22 Oct (user: "whenever there's a
   connection or cross lines, we show this square dot ... it's missing in a
   lot of sections in events"): the plain site joint, hairline and squares,
   at every boundary. The rule that stood them down is gone. */
/* The reel's scallop tear came OFF 22 Oct (user: "this separator looking
   weird"): grey bumps between two near-white grounds read as dirt, not a
   tear. A plain hairline takes the boundary; the hero keeps its torn edge,
   which works because its band is dark. */
/* The reel's ground is --surface-2 (#EFF1F6) since 22 Oct (user). */
.evroom-sec { background: var(--surface-2); }

/* THREE ACROSS (user, 13 Oct: "do 3 column grid"; it was four across from
   10 Oct, when there were four sessions). Six cards deal as two clean rows;
   2x2 below 992, one column on phones. */
.evgrid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 12px;
}
/* POSTER CARDS (user, 13 Oct, after laravel.com/community: "image top,
   and gradient to dark bottom, nice and clean look"; "kinda hard to
   differentiate between new and past events"). The photo fills the whole
   card and runs into --black at the foot, where the copy sits in white.
   STATE IS CARRIED BY THREE THINGS AT ONCE, none of them a label: an
   upcoming card is in full colour and ends in a white ticket button; a past
   card has no button and sits in greyscale, its colour coming back only
   part way on hover, the "you missed this one" feel. The "Upcoming" tag came
   off with it. The whole card is still the link; the button is a span
   inside it, so the event page (never Luma) is where both lead.
   Its height is the photo band (see .evcard__media) plus the copy. The region
   map rules (.evcard__map, --evmap-*) went with the white body; the maps in
   assets/events/maps/ stay as files. */
.evcard {
  position: relative;
  isolation: isolate;
  display: flex;
  flex-direction: column;
  background: var(--black);
  border-radius: var(--r-lg);
  overflow: clip;
  text-decoration: none;
  box-shadow: var(--shadow-sm);
  transition: box-shadow var(--dur-slow) var(--ease), translate var(--dur-slow) var(--ease);
}
.evcard:is(:hover, :focus-visible) { box-shadow: var(--shadow-md); translate: 0 -2px; }


/* The photo is the card's flexible top: it takes whatever height the copy
   leaves, then fades into the --black ground the copy sits on. */
/* FLATTER since 13 Oct (user: "can our cards be little flat? the images
   were cropped in certain size"): the photos are 2.4:1 panoramas, and a
   3:4 card cut them to a vertical slice. The card has no ratio of its own
   now; the photo is a 16:9 band and the card is that plus its copy. In a
   row the band grows to match the tallest card (a past card, having no
   button, gets a taller photo), so the copy still floors to the foot. */
/* overflow clip: the hover zoom otherwise pokes 2.5% of photo out of the
   media box over the copy below. The fade and the shade carry explicit
   z-indexes: a scaled img is a stacking context and Chrome paints it over
   a z-auto pseudo, which cut the fade off on hover (user, 13 Oct). */
.evcard__media { position: relative; flex: 1 0 auto; aspect-ratio: 16 / 9; display: block; overflow: clip; }
.evcard__media::after {
  content: '';
  position: absolute;
  inset: auto 0 0;
  height: 45%;
  z-index: 1;
  background: linear-gradient(to bottom, transparent, color-mix(in srgb, var(--black) 70%, transparent) 55%, var(--black));
  pointer-events: none;
}
.evcard__photo img,
img.evcard__photo {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: var(--pos, 50% 40%);
  transition: scale 900ms var(--ease), filter var(--dur-slow) var(--ease);
}
.evcard:hover .evcard__photo img,
.evcard:hover img.evcard__photo { scale: 1.05; }

/* CHICAGO AND DALLAS SIT BACK IN THE BAND (user, 13 Oct: "scale down ...
   show more of surroundings"). A 2.4:1 panorama in the 16:9 band loses a
   quarter of its width to the cover crop, which read as a zoomed-in
   skyline. The --wide photo hangs from the band's top at 82% of its
   height, so the visible crop widens from 74% to about 90% of the
   panorama, and the img carries its own mask so its foot dissolves into
   the band's fade instead of cutting to a hard edge. */
.evcard__photo--wide img { height: 82%; mask-image: linear-gradient(to bottom, #000 72%, transparent); }

/* THE COPY SITS ON THE DARK, NOT ON THE PHOTO. A first pass laid the
   copy over a full-bleed photo and the city panoramas (2.4:1, cropped into
   a portrait card) put their subject exactly under it: the Bean went under
   the title and the button. Now the photo is the top of the card and the
   copy pulls up 48px into its fade, so only the fade overlaps. A faint dim
   at the top (.evcard__shade) is all the photo itself carries. */
.evcard__shade {
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(to bottom, color-mix(in srgb, var(--black) 12%, transparent), transparent 30%);
  pointer-events: none;
}
/* UPCOMING CARDS WEAR THE TOUR TINT (user, 13 Oct, with the LinkedIn tour
   creatives' own gradient: "apply tint on the hub"), so the hub card and
   the ad that sent someone to it are the same picture. The TOP and MIDDLE
   stops are the creative's; the FOOT IS EASED (user, same day: "a bit too
   dark from below or text area"): the creative ran to opaque black because
   its type sits on the photo, while here the media's own ::after fade
   already melts the photo into the copy, and the two stacked crushed the
   lower half. So the tint stops at the night navy and lets the fade finish.
   PAST CARDS STAY GREYSCALE and untinted: tint against grey is the
   upcoming/past signal. */
.evcard:not(.evcard--past) .evcard__shade {
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--black) 55%, transparent) 0%,
    color-mix(in srgb, var(--primary-600) 55%, transparent) 17.53%,
    color-mix(in srgb, var(--tour-violet) 55%, transparent) 46.82%,
    color-mix(in srgb, var(--tour-night) 45%, transparent) 75%,
    color-mix(in srgb, var(--tour-night) 30%, transparent) 100%);
}

/* "Upcoming": the white tag top-right, back on the two live cards (user,
   13 Oct: "we are also missing the upcoming badge"). z-index 2 lifts it
   over the shade and the fade. */
/* THE ORGANISER'S MARK RIDES THE COVER since 22 Oct (user: "show the
   organizers logo as well on event cards, like adobe"; then "why they in
   a white box, looks bad, just show logo"): a BARE mark, top-left
   opposite the Upcoming badge, the event's organising or headline
   partner. The card is a photo fading to dark, so a dark-ink mark cannot
   sit on it bare: From Day One's text goes back to ITS ORIGINAL WHITE
   (from-day-one-white.svg, the supplied artwork before we inked it for
   light grounds; ribbon colours untouched) and The Product Folks takes
   the white knockout filter (--knock), the /customers dark-page
   exception. Adobe's red and the TBH badge read as they are. The
   aiRA-run stops (Nashville, the Captivate talks) carry none. */
.evcard__org {
  position: absolute;
  top: var(--sp-4);
  left: var(--sp-4);
  z-index: 2;
  display: inline-flex;
  align-items: center;
}
.evcard__org img { display: block; width: auto; height: 18px; }
.evcard__org--stack img { height: 30px; }
.evcard__org--badge img { height: 40px; }
.evcard__org--knock img { filter: brightness(0) invert(1); }

.evcard__badge {
  position: absolute;
  top: var(--sp-3);
  right: var(--sp-3);
  z-index: 2;
  padding: 3px 8px;
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--border);
  border-radius: var(--r-xs);
  font-size: var(--t-caption);
  font-weight: 600;
  line-height: 1.3;
}

.evcard__body {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
  margin-top: -36px;
  padding: 0 var(--sp-4) var(--sp-4);
  transition: translate var(--dur-slow) var(--ease);
}
.evcard__kinds { display: flex; flex-wrap: wrap; gap: var(--sp-1) var(--sp-3); margin-bottom: var(--sp-1); }
.evkind {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: var(--t-caption);
  font-weight: 600;
  line-height: 1.2;
  color: var(--brand-ink);
}
.evkind__ic { flex: none; width: 16px; height: 16px; }
.evcard .evkind { color: var(--tertiary-300); }
/* Two lines at most, then an ellipsis (user, 12 Oct): Luma's titles are long
   and the full title is on the event page. */
.evcard__t {
  font-family: var(--font-display);
  /* 14, the pre-poster size (user, 13 Oct: "we did not have large font"). */
  font-size: var(--t-body);
  font-weight: 500;
  line-height: var(--lh-title);
  color: var(--ink-inv);
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
}
/* Date and place on one row at opposite ends (user, 13 Oct).
   THE FOOT IS A TICKET STUB since later the same day (user: "they still
   dont look very event like"): the events pages' own tear grammar, a
   dashed line where a stub would tear off with a punch notch half-clipped
   out of each card edge (discs in the grid's own ground, cropped by the
   card's overflow), so the date-and-place row reads as the ticket's stub.
   This supersedes the no-separator note from the flat-card pass: that was
   a plain hairline, this is the tear. */
.evcard__meta {
  position: relative;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-3);
  margin-top: var(--sp-3);
  padding-top: var(--sp-3);
  border-top: 2px dashed color-mix(in srgb, var(--ink-inv) 28%, transparent);
}
.evcard__meta::before,
.evcard__meta::after {
  content: '';
  position: absolute;
  top: -9px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--surface-2);
}
.evcard__meta::before { left: calc(-1 * var(--sp-4) - 8px); }
.evcard__meta::after { right: calc(-1 * var(--sp-4) - 8px); }
.evcard__date { font-size: var(--t-caption); font-weight: 600; color: var(--ink-inv); }
/* The start time rides the date at a quieter weight (22 Oct, user: "add more
   detail to evcard__date, we have space on event card"). It is part of the
   <time>, so the stub still reads as one fact. */
.evcard__hr { font-weight: 500; color: color-mix(in srgb, var(--ink-inv) 72%, transparent); }
.evcard__place { font-size: var(--t-caption); color: color-mix(in srgb, var(--ink-inv) 72%, transparent); }

/* THE TICKET BUTTON, upcoming only, REVEALED ON HOVER (user, 13 Oct: "lets
   do our original event card size we had, before we added button, just add
   the button on hover, so things move up"). The button is OUT OF FLOW, hung
   just below the copy and clipped by the card, so every card is the
   no-button height and a row no longer stretches to the upcoming ones. On
   hover and keyboard focus the whole copy block slides up by the button's
   height plus its gap (--cta-lift) and the button rises in under it. The
   block carries its own dark ramp above it (::before), so the title stays
   legible as it climbs onto the photo. Touch has no hover, so under
   (hover: none) the card simply rests in the lifted state. White, 12px
   corners, the Phosphor Ticket duotone; on reveal it fills Tertiary/100. */
.evcard { --cta-lift: 48px; }
.evcard__body::before {
  content: '';
  position: absolute;
  inset: calc(-1 * var(--cta-lift) - 24px) 0 0;
  z-index: -1;
  background: linear-gradient(to bottom, transparent, color-mix(in srgb, var(--black) 85%, transparent) var(--cta-lift), var(--black) calc(var(--cta-lift) + 40px));
  opacity: 0;
  transition: opacity var(--dur-slow) var(--ease);
  pointer-events: none;
}
/* COMPACT, the header button's size, with the arrow-right suffix (user,
   13 Oct: "use smaller button, like the one in our header"); the full-width
   44px ticket bar is gone. The glyph is the harvested Phosphor ArrowRight
   the ticket's own CTA already carries. */
.evcard__cta {
  position: absolute;
  left: var(--sp-4);
  top: calc(100% - var(--sp-4) + var(--sp-3));
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  height: 36px;
  padding: 0 var(--sp-4);
  background: var(--surface);
  color: var(--black);
  border-radius: var(--r-md);
  font-size: var(--t-body);
  font-weight: 600;
  line-height: 1;
  opacity: 0;
  transition: opacity var(--dur-slow) var(--ease), background-color var(--dur-fast) var(--ease);
}
/* Past cards lift too since 13 Oct (user: "for past card, show button too
   but make it ghost"): same reveal, the button just outlined. */
.evcard:is(:hover, :focus-visible) .evcard__body { translate: 0 calc(-1 * var(--cta-lift)); }
.evcard:is(:hover, :focus-visible) .evcard__body::before,
.evcard:is(:hover, :focus-visible) .evcard__cta { opacity: 1; }
@media (hover: none) {
  .evcard .evcard__body { translate: 0 calc(-1 * var(--cta-lift)); }
  .evcard__body::before, .evcard__cta { opacity: 1; }
}
/* The ghost: white outline, no fill, the ended state's own weight. Same
   12px-cornered button shape as the live one. */
.evcard__cta--ghost {
  background: transparent;
  border: 1px solid color-mix(in srgb, var(--ink-inv) 48%, transparent);
  color: var(--ink-inv);
}
.evcard:is(:hover, :focus-visible) .evcard__cta--ghost { background: color-mix(in srgb, var(--ink-inv) 8%, transparent); }

/* PAST: no button, greyscale, everything a step quieter. Hover brings the
   colour back part way: the room is still there to look at, you just were
   not in it. */
.evcard--past .evcard__photo img,
.evcard--past img.evcard__photo { filter: grayscale(1) brightness(0.82) contrast(0.95); }
.evcard--past:hover .evcard__photo img,
.evcard--past:hover img.evcard__photo { filter: grayscale(0.35) brightness(0.9); }
.evcard--past .evcard__t { color: color-mix(in srgb, var(--ink-inv) 78%, transparent); }
.evcard--past .evkind { color: color-mix(in srgb, var(--ink-inv) 60%, transparent); }
.evcard--past .evcard__date { color: color-mix(in srgb, var(--ink-inv) 72%, transparent); }
.evcard--past .evcard__hr { color: color-mix(in srgb, var(--ink-inv) 56%, transparent); }
.evcard--past .evcard__place { color: color-mix(in srgb, var(--ink-inv) 56%, transparent); }
@media (prefers-reduced-motion: reduce) {
  .evcard__photo img, img.evcard__photo, .evcard__cta, .evcard__body, .evcard__body::before { transition: none; }
  .evcard:is(:hover, :focus-visible) { translate: none; }
}


/* ---- Responsive ----------------------------------------------------------- */

@media (max-width: 991px) {
  .evhub__panel { padding-top: calc(var(--nav-stack) + var(--sp-16)); }
}
/* PHONES: the poster flows from the top under the nav rather than
   centring in the band. */
@media (max-width: 767px) {
  .evhub__panel { justify-content: flex-start; padding-top: calc(var(--nav-stack) + var(--sp-12)); }
  .evh1__dates { margin-top: var(--sp-6); }
}
@media (max-width: 640px) {
  body.page-events { --tick-h: 52px; }
  .evtick__code { display: none; }
  .evtick__body { margin-right: var(--notch); }
  .evtick__stub { padding-right: var(--sp-4); }
  .evtick__stub b { font-size: var(--t-title); }
}
@media (max-width: 991px) {
  .evgrid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 639px) {
  .evgrid { grid-template-columns: 1fr; }
  /* The date list steps down with the phone poster (22 Oct, user). */
  .evh1__stop { font-size: var(--t-title-sm); }
}

@media (prefers-reduced-motion: reduce) {
  .evtick__belt { animation: none; }
  .evtick { transition: none; }
  a.evh1__stop { transition: none; }
  .evcard, .evfilter__btn, .evfilter.is-live .evfilter__pill,
  .evcard { transition: none; }
}

/* ---- BRING THE TOUR TO YOUR CITY (#tour-city) -----------------------------
   The tour's city-request ticket (13 Oct; redesigned the same day, user:
   "surprise me with a good design ... height increasing is not great").
   It sits BETWEEN the grid and the reel on the grid's own --surface-2, so
   no scallop of its own: the reel's torn top edge still punches this
   ground. ONE PAPER TICKET: the left half a MINI POSTER in the hub cards'
   grammar (the photo fading into --black, the pitch on the dark, the em in
   the on-dark accent), a dashed tear with punch notches, the form right.
   The right half is a CITY POLL since 21 Oct (the request form, its
   dropdown and its confirmation are in git). */
.evcity-sec { background: var(--surface-2); scroll-margin-top: calc(2px - 24px); }
.evcity {
  display: grid;
  grid-template-columns: minmax(0, 5fr) auto minmax(0, 6fr);
  align-items: stretch;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
  /* NO overflow clip on the card (14 Oct, user: "dropdown stuck in box"):
     the city list hangs past its foot. The two things the clip used to do
     moved onto their own parts: the photo side clips its own corners, and
     the tear clips its own punch discs. */
}

/* The poster half: photo into dark, copy on the dark. */
.evcity__side { position: relative; min-height: 440px; overflow: clip; border-radius: calc(var(--r-lg) - 1px) 0 0 calc(var(--r-lg) - 1px); }
.evcity__ph { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: 50% 30%; }
/* The poster wears the TOUR TINT, the same creative gradient the hub's
   upcoming cards carry (black into Primary/600 into the tour violet), so
   this card and the ads asking for the next city are one picture. The
   foot RUNS TO SOLID BLACK because the 2027 shortlist sits on the photo,
   which is the case the creative's opaque foot exists for. (The section
   heading lives in the regular head above since 14 Oct.) */
.evcity__shade {
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--black) 45%, transparent) 0%,
    color-mix(in srgb, var(--primary-600) 48%, transparent) 18%,
    color-mix(in srgb, var(--tour-violet) 48%, transparent) 44%,
    color-mix(in srgb, var(--black) 76%, transparent) 72%,
    var(--black) 96%);
}
/* THE TOUR LINE ON THE PHOTO (user, 14 Oct: no dates, then no cities,
   "more advertising language"): the tour's name in a tracked Tertiary/300
   caps label (.evh1__year's spec) over "Coming to a city near you." in
   display type, white on the shade's solid foot, bottom-left. */
.evcity__poster { position: absolute; inset: auto 0 0; padding: var(--sp-8); }
.evcity__year {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--t-caption);
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--tertiary-300);
}
.evcity__stops {
  margin: var(--sp-2) 0 0;
  font-family: var(--font-display);
  font-size: var(--t-display-3);
  font-weight: 500;
  line-height: var(--lh-title);
  color: var(--on-brand);
  text-wrap: balance;
}

/* The tear is a 20px CLIPPING STRIP that takes no room (margins give the
   20px back, so its grid track is 0): it draws the dashed line down its
   middle and clips its own punch discs to half circles at the card's top
   and bottom edges, which the card's overflow clip used to do. */
.evcity__tear {
  position: relative;
  z-index: 1;
  width: 20px;
  margin-inline: -10px;
  overflow: clip;
  background: linear-gradient(to bottom, color-mix(in srgb, var(--ink) 18%, transparent) 0 6px, transparent 6px 10px) center / 2px 10px repeat-y;
}
.evcity__tear::before,
.evcity__tear::after {
  content: '';
  position: absolute;
  left: 0;
  width: 20px;
  height: 20px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 50%;
  box-sizing: border-box;
}
.evcity__tear::before { top: -11px; }
.evcity__tear::after { bottom: -11px; }

.evcity__pane { padding: var(--sp-8); display: grid; align-content: center; }
/* THE POLL AS HORIZONTAL BARS (22 Oct, user: "like horizontal bars, once
   the field is active, do it accent color, default state, do it nice").
   It replaced the four ticket stubs standing in a row.
   THREE STATES PER ROW:
   - REST: a white 52px row on a hairline, an empty 18px ring, the city in
     ink and its state code muted. No numbers: results show after a vote.
   - PICKED (and focus inside the typed row): the brand edge with a soft
     brand ring, the ring filled to a brand dot, the city in brand ink. The
     accent is the edge and the dot, never a fill: a fill is the result.
   - VOTED: the form takes .is-voted, the rows stop being buttons and each
     grows a horizontal BAR from its left edge to its share of all votes
     (--pct, set by js/tour-poll.js), with the percentage and count at the
     right. Others are a quiet ink tint; the visitor's own row keeps the
     brand edge and its bar is a brand tint. The typed row becomes the
     "Other cities" total. */
.evpoll { display: grid; gap: var(--sp-6); }
.evpoll__set { margin: 0; padding: 0; border: 0; min-width: 0; display: grid; gap: var(--sp-4); }
.evpoll__q {
  padding: 0;
  font-family: var(--font-display);
  font-size: var(--t-title-xl);
  font-weight: 500;
  line-height: var(--lh-title);
  color: var(--ink);
}
.evpoll__help { margin: 6px 0 0; font-size: var(--t-body-sm); color: var(--ink-3); }
.evpoll__opts { display: grid; gap: var(--sp-2); }
.evpoll__opt {
  position: relative;
  isolation: isolate;
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  min-height: 52px;
  padding: 0 var(--sp-4);
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-md);
  overflow: clip;
  cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease);
}
/* The native radio stays for keyboard and screen readers; the row draws it. */
.evpoll__radio { position: absolute; width: 1px; height: 1px; margin: 0; opacity: 0; pointer-events: none; }
.evpoll__dot {
  flex: none;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  border: 1.5px solid var(--ink-faint);
  background: var(--surface);
  transition: border-color var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease);
}
.evpoll__name {
  display: flex;
  align-items: baseline;
  gap: var(--sp-2);
  min-width: 0;
  font-size: var(--t-body);
  font-weight: 500;
  color: var(--ink);
  transition: color var(--dur-fast) var(--ease);
}
.evpoll__st { font-size: var(--t-caption); font-weight: 500; letter-spacing: 0.04em; color: var(--ink-3); }
.evpoll__name--other { display: none; }
/* The typed row: the field IS the row's label, borderless inside it. */
.evpoll__input {
  flex: 1;
  min-width: 0;
  height: 50px;
  padding: 0;
  font: inherit;
  font-size: var(--t-body);
  font-weight: 500;
  color: var(--ink);
  background: none;
  border: 0;
  outline: none;
}
.evpoll__input::placeholder { color: var(--ink-3); font-weight: 400; }
/* The row carries the focus ring; the field inside it draws none. */
.evpoll__input:focus,
.evpoll__input:focus-visible { outline: none; box-shadow: none; }

/* HOVER IS FOR A MOUSE ONLY (23 Oct, user's iPhone: the first tap on a
   city did nothing, the second picked it). On touch, iOS Safari applies
   :hover on the first tap and can hold the click back when the hover
   changes the row, so a row needed two taps. touch-action: manipulation
   also drops the double-tap-to-zoom wait on the label. */
.evpoll__opt { touch-action: manipulation; -webkit-tap-highlight-color: transparent; }
@media (hover: hover) and (pointer: fine) {
  .evpoll__opt:hover { border-color: var(--ink-faint); }
  .evpoll__opt:hover .evpoll__dot { border-color: var(--ink-3); }
}
/* ACTIVE IS THE /book FIELD'S FOCUS STATE (22 Oct, user: "use same
   active state style used on book a demo form field"): white ground, the
   brand edge and a 3px brand ring at 18%, exactly book.css's .input:focus.
   The ring fills to a brand dot and the name goes brand ink. A solid-brand
   pass and a Primary/50 wash each lived one turn. */
.evpoll__opt:has(.evpoll__radio:checked),
.evpoll__opt--other:focus-within {
  border-color: var(--brand);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand) 18%, transparent);
}
.evpoll__opt:has(.evpoll__radio:checked) .evpoll__dot {
  border-color: var(--brand);
  background: var(--brand);
  box-shadow: inset 0 0 0 4px var(--surface);
}
.evpoll__opt:has(.evpoll__radio:checked) .evpoll__name { color: var(--brand-ink); }
.evpoll__opt:has(.evpoll__radio:focus-visible) { outline: 2px solid var(--brand); outline-offset: 2px; }
/* A typed name the checker refused. Doubled class so it beats the picked
   row's :has() rule. */
.evpoll__opt.evpoll__opt--other.is-bad { border-color: var(--reject); box-shadow: 0 0 0 3px color-mix(in srgb, var(--reject) 14%, transparent); }

/* RESULTS. */
.evpoll__fill {
  position: absolute;
  inset: 0;
  z-index: -1;
  background: color-mix(in srgb, var(--ink) 6%, transparent);
  transform-origin: 0 50%;
  scale: 0 1;
  transition: scale 760ms var(--ease);
}
.evpoll__res {
  display: none;
  margin-left: auto;
  align-items: baseline;
  gap: var(--sp-2);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.evpoll__res b { font-size: var(--t-body); font-weight: 600; color: var(--ink); }
.evpoll__res span { font-size: var(--t-caption); color: var(--ink-3); }
/* VOTED IS A RESULTS LIST, NOT A FORM (22 Oct, user: the is-mine state
   "should look better", then "do like that horizontal bar is filled ...
   greyed out"). Each row stays a 52px box but drops its input chrome (no
   border, no ring): the box is a --surface-2 track and the FILL runs its
   full height to the row's share. Other rows are greyed out (slate fill,
   name and share in --ink-2); the visitor's row fills in Primary/100 with
   the name and share in brand ink, a CheckCircle and "Your vote"
   (.evpoll__you, in each row's markup, shown on .is-mine; words sr-only
   below 640). */
.evpoll.is-voted .evpoll__opt {
  cursor: default;
  background: var(--surface-2);
  border-color: transparent;
  box-shadow: none;
}
.evpoll.is-voted .evpoll__fill { background: color-mix(in srgb, var(--slate-300) 70%, var(--surface-2)); }
.evpoll.is-voted .evpoll__name,
.evpoll.is-voted .evpoll__res b { color: var(--ink-2); }
.evpoll.is-voted .evpoll__dot { display: none; }
.evpoll.is-voted .evpoll__res { display: inline-flex; }
.evpoll.is-voted .evpoll__input { display: none; }
.evpoll.is-voted .evpoll__name--other { display: flex; }
.evpoll.is-shown .evpoll__fill { scale: var(--pct, 0) 1; }
/* The visitor's row stays FILLED BRAND after the vote, its share drawn
   as a deeper brand bar inside it, all type white. */
.evpoll.is-voted .evpoll__opt.is-mine { background: var(--brand); }
.evpoll.is-voted .evpoll__opt.is-mine .evpoll__fill { background: color-mix(in srgb, var(--black) 28%, var(--brand)); }
.evpoll.is-voted .evpoll__opt.is-mine .evpoll__name,
.evpoll.is-voted .evpoll__opt.is-mine .evpoll__res b { color: var(--on-brand); font-weight: 600; }
.evpoll.is-voted .evpoll__opt.is-mine .evpoll__st,
.evpoll.is-voted .evpoll__opt.is-mine .evpoll__res span { color: color-mix(in srgb, var(--on-brand) 72%, transparent); }
.evpoll__you { display: none; }
.evpoll.is-voted .evpoll__opt.is-mine .evpoll__you {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: var(--t-caption);
  font-weight: 600;
  color: var(--on-brand);
}
.evpoll__you svg { width: 16px; height: 16px; flex: none; }
.evpoll.is-voted .evpoll__name { white-space: nowrap; }
/* A phone has room for the city and its share, not the words too: the
   check stays and "Your vote" goes to screen readers only. */
@media (max-width: 639px) {
  .evpoll__you-t { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
}

.evpoll__foot { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-3) var(--sp-4); min-height: 44px; }
.evpoll__go:disabled { opacity: 0.45; cursor: default; }
.evpoll__go.is-busy { opacity: 0.6; cursor: progress; }
.evpoll__go[hidden] { display: none; }
.evpoll__status { margin: 0; font-size: var(--t-body-sm); color: var(--ink-2); }
.evpoll__status:empty { display: none; }
.evpoll__status.is-bad { color: var(--reject); }

/* The hero's 2027 line leads here. */
.evh1__later a { color: inherit; text-decoration: none; }
.evh1__later a:hover { text-decoration: underline; text-underline-offset: 3px; }

@media (max-width: 991px) {
  .evcity { grid-template-columns: 1fr; }
  .evcity__side { min-height: 0; aspect-ratio: 16 / 8; }
  .evcity__poster { padding: var(--sp-6); }
  /* The stacked poster is short, so the route climbs out of the solid
     foot onto the bright stage: the ramp starts higher here. */
  .evcity__shade {
    background: linear-gradient(180deg,
      color-mix(in srgb, var(--black) 45%, transparent) 0%,
      color-mix(in srgb, var(--primary-600) 48%, transparent) 14%,
      color-mix(in srgb, var(--black) 80%, transparent) 42%,
      var(--black) 78%);
  }
  .evcity__side { border-radius: calc(var(--r-lg) - 1px) calc(var(--r-lg) - 1px) 0 0; }
  .evcity__tear {
    width: auto;
    height: 20px;
    margin: -10px 0;
    background: linear-gradient(to right, color-mix(in srgb, var(--ink) 18%, transparent) 0 6px, transparent 6px 10px) center / 10px 2px repeat-x;
  }
  .evcity__tear::before, .evcity__tear::after { top: 0; bottom: auto; }
  .evcity__tear::before { left: -11px; }
  .evcity__tear::after { left: auto; right: -11px; }
  .evcity__pane { padding: var(--sp-6); }
}
@media (max-width: 639px) {
  .evpoll__go { width: 100%; }
  .evcity__side { aspect-ratio: 4 / 3; }
}
@media (prefers-reduced-motion: reduce) {
  .evspeak__reel { overflow-x: auto; }
  .evspeak__belt { animation: none; }
  .evspeak__set[aria-hidden="true"] { display: none; }
  .evpoll__opt, .evpoll__dot, .evpoll__name, .evpoll__fill { transition: none; }
}

/* ---- SPEAKERS AND PARTNERS (#speakers, 21 Oct) ----------------------------
   Hardik: faces and logos are what visitors read. THE SPEAKERS ARE A
   LOOPING TICKER since 22 Oct (user, from laracon.au's speaker belt:
   "increase speaker card size and turn it into ticker ... dont overlap
   like them"): 280px tiles in a belt sharing js/evreel.js (the photo
   reel's driver, generic over [data-reel] now). A ticket-card pass
   (border, tear, stub) was built and REVERTED the same day (user: "dont
   make everything card with ticket ui, old one was fine of square
   photo"). The ORGANISERS are their own highlighted mini band (.evorg,
   below). */
.evspeak-sec { background: var(--surface); }
/* minmax(0,1fr), not a bare auto track: the ticker belt inside is
   width: max-content, and an auto column would size itself to the belt
   (3,600px) and push the whole grid past the container. */
.evspeak { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--sp-16); margin-top: var(--sp-12); }
.evspeak__group { display: grid; gap: var(--sp-6); }
/* THE BELT AGAIN, SMALLER (user, 22 Oct: "are there only 6 spearks? do
   ticker then and reduce size"; the one-day grid is in git). Same
   contract as .evreel: two sets, the set's right padding is the loop's
   period, CSS keyframe at rest, js/evreel.js (.is-js) takes over for the
   slow-on-hover and the drag. Bleeds to the rails from 992 up. */
/* clip x only: unlike `hidden`, `clip` pairs with visible per axis
   without creating a scroll container, so the belt still cuts at the rail
   bleed while the cards' --shadow-sm paints past their bottom edge (22
   Oct, user: "shadow cutting to bottom"). */
.evspeak__reel { overflow-x: clip; overflow-y: visible; }
.evspeak__reel:focus-visible { outline: 2px solid var(--brand); outline-offset: 4px; }
.evspeak__belt {
  display: flex;
  width: max-content;
  animation: evspeak-roll 31s linear infinite;
}
.evspeak__reel:hover .evspeak__belt,
.evspeak__reel:focus-within .evspeak__belt { animation-play-state: paused; }
.evspeak__reel.is-js { cursor: grab; touch-action: pan-y; user-select: none; -webkit-user-select: none; }
.evspeak__reel.is-js.is-dragging { cursor: grabbing; }
.evspeak__reel.is-js .evspeak__belt { animation: none; will-change: transform; }
.evspeak__reel.is-js img { pointer-events: none; -webkit-user-drag: none; }
.evspeak__set {
  flex: none;
  display: flex;
  align-items: stretch;
  gap: var(--sp-4);
  list-style: none;
  margin: 0;
  padding: 0 var(--sp-4) 0 0;
}
/* THREE SETS since 26 Oct: with Cooper Manning off (user) a set of five is
   1280px, narrower than the reel from 1440 up, so two copies left a gap at
   the seam. The belt carries the set three times and moves ONE set, a third
   of its width; js/evreel.js already wraps on one set's width. */
@keyframes evspeak-roll { to { transform: translateX(calc(-100% / 3)); } }
/* THE CARD IS THE USER'S GLEAN REFERENCE (22 Oct): the portrait with
   its own background filling the top, the company mark a solid white
   plate docked flush in the photo's bottom-right corner (only its inner
   corner rounded), then name over title on the card's own ground. */
.evspeak__card {
  flex: none;
  width: 240px;
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
  overflow: clip;
  text-align: left;
}
.evspeak__card .evspeak__ph { position: relative; }
.evspeak__card .evspeak__n {
  margin: var(--sp-3) var(--sp-4) 0;
  font-size: var(--t-title-sm);
  font-weight: 600;
}
.evspeak__card .evspeak__t {
  max-width: none;
  margin: 2px var(--sp-4) 0;
}
.evspeak__card > :last-child { margin-bottom: var(--sp-3); }
/* SPEAKERS IN ONE ROW, ORGANISERS IN ANOTHER (user, 21 Oct). Both rows use
   one tile width, a sixth of the container, so faces match across rows;
   the three-step wave repeats every three tiles. */
.evspeak__ph {
  width: 100%;
  margin: 0;
  aspect-ratio: 1;
  overflow: hidden;
  background: var(--surface-2);
}
.evspeak__ph img { display: block; width: 100%; height: 100%; object-fit: cover; }
.evspeak__n { font-size: var(--t-body-lg); font-weight: 500; color: var(--ink); }
.evspeak__t { max-width: 26ch; font-size: var(--t-caption); line-height: 1.4; color: var(--ink-2); }
/* THE ORGANISERS' STRIP (user, 22 Oct: "shorten this section, no need
   for big box. should sit well with our partners"; the tinted
   futureproof-style panel lived a few hours, in git). The partners row's
   own register: centred h3, one attribution line, the three people as
   disc-and-name pairs on open ground. */
/* ONE RULED BAND since later 22 Oct (user: "any better way to style
   this"): organisers and partners as two rows sharing hairlines, each a
   label cell beside its content with one vertical rule between, bled to
   the rails at 992+ with the ticker's giveback. The frame's grammar, not
   two centred strips floating under the ticker. */
.evband {
  margin-top: var(--sp-16);
  border-block: 1px solid var(--frame-line);
  /* White paper under the hairlines (22 Oct, user). */
  background: var(--surface);
}
.evband__row { display: grid; }
.evband__row + .evband__row { border-top: 1px solid var(--frame-line); }
/* A row can be parked with `hidden` (the organisers row since 26 Oct); the
   grid display above would otherwise beat the attribute, and the row after
   it must not draw a second rule under the band's own top edge. */
.evband__row[hidden] { display: none; }
.evband__row[hidden] + .evband__row { border-top: 0; }
.evband__label {
  display: grid;
  gap: var(--sp-2);
  align-content: center;
  padding: var(--sp-6) var(--sp-6) var(--sp-6) 0;
}
/* Plain ink, no brand em (user: "black black"): a row label, not a
   section heading. */
.evband__h { margin: 0; font-size: var(--t-title); font-weight: 500; letter-spacing: -0.01em; color: var(--ink); }
.evband__sub {
  margin: 0;
  font-size: var(--t-caption);
  line-height: var(--lh-body);
  color: var(--ink-3);
  text-wrap: pretty;
}
.evband__cell { padding: var(--sp-6) 0; }
@media (min-width: 992px) {
  .evband { margin-inline: calc(-1 * var(--sp-5)); }
  .evband__row { grid-template-columns: minmax(220px, 300px) 1fr; }
  .evband__label { padding-left: var(--sp-6); }
  .evband__cell { border-left: 1px solid var(--frame-line); padding: var(--sp-6) 0 var(--sp-6) var(--sp-8); }
  /* NO CROSSING SQUARES on this band (user, 22 Oct: "remove corner
     squares in this, its too much"); they lived a few hours, in git. */
}
.evorg__list {
  list-style: none;
  margin: 0;
  padding-block: var(--sp-6);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-4) var(--sp-10);
}
.evorg__list li { display: flex; align-items: center; gap: var(--sp-3); }
.evorg__av {
  display: block;
  width: 60px;
  aspect-ratio: 1;
  border-radius: 50%;
  overflow: hidden;
  border: 1px solid var(--border);
  background: var(--surface-2);
}
.evorg__av img { display: block; width: 100%; height: 100%; object-fit: cover; }
.evorg__n { font-size: var(--t-body-lg); font-weight: 500; color: var(--ink); }

/* THE COMPANY CHIP SITS ON THE PHOTO since 22 Oct (user: "place the
   logo on their pic like moengage ... hide the event they were in"): a
   small white chip, bottom-left of the portrait with a slight tilt, the
   mark full colour, alt carrying the name. The per-speaker event ticket
   tags are GONE with it (in git): the hub cards say where each event is.
   Cooper has no company and carries none. */
.evspeak__card .evspeak__ph { position: relative; }
.evspeak__chip {
  position: absolute;
  right: 0;
  bottom: 0;
  display: inline-flex;
  align-items: center;
  padding: 8px 12px;
  background: var(--surface);
  border-top-left-radius: var(--r-md);
}
.evspeak__chip img { display: block; width: auto; height: 14px; }
.evspeak__chip--sq img { height: 20px; }
@media (prefers-reduced-motion: reduce) {
}

/* PARTNERS: ONE PLAIN ROW OF MARKS (user, 22 Oct: "just show the logos
   man simple, dont make everything links"), the band's second row since
   later that day. */
.evpart__list {
  list-style: none;
  margin: 0;
  padding-block: var(--sp-6);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-6) var(--sp-12);
}
.evpart__logo { display: block; width: auto; }
.evpart__logo--word { height: 26px; }
.evpart__logo--stack { height: 40px; }
.evpart__logo--badge { height: 56px; }

@media (min-width: 992px) {
  .evspeak__reel { margin-inline: calc(-1 * var(--sp-5)); }
}
@media (max-width: 991px) {
  .evpart__list { gap: var(--sp-8) var(--sp-10); }
}
@media (max-width: 639px) {
  .evspeak { gap: var(--sp-12); }
  .evspeak__card { width: 200px; }
  /* The partners and organisers h3s STAY CENTRED on phones (user, 22
     Oct: "center align on mobile"), their rows being centred strips; the
     reel's head too, against global.css's phone left-ranging. */
  .evroom-sec .section-head--center { text-align: center; margin-inline: auto; }
  .evroom-sec .section-head--center .section-heading { margin-inline: auto; }
}
