/* ============================================================================================
   EVENT WEBSITES (/e/{slug}) and the builder's live preview of them.

   Its own file because the public site is served as plain server HTML (static SSR) with none of
   the app's stylesheets: this file has to stand on its own there. In the app it loads right after
   custom.css (AppAssets), so the builder preview renders from the same rules.

   Everything is scoped under .es. The shims below cover the only Bootstrap pieces the
   renderer leans on, so the static page needs no Bootstrap at all; inside the app Bootstrap
   already sets the same values and they change nothing.
   ============================================================================================ */

.es, .es *, .es *::before, .es *::after { box-sizing: border-box; }
.es .p-4 { padding: 1.5rem !important; }
.es .p-2 { padding: .5rem !important; }
.es .p-1 { padding: .25rem !important; }
.es .text-center { text-align: center !important; }

/* ============================================================================================
   EVENT WEBSITE TEMPLATES  (.es)
   Rendered by Shared/EventSite/EventSiteRenderer.razor, at /e/{slug} and in the builder preview.

   Two independent axes:
     data-site-theme  what it looks like - palette, type, weight. Four industry skins.
     .es--{template}  what it is shaped like - hero, rhythm, column count. Five structures.

   Everything is scoped under .es so a site can never leak into the admin around it, and every
   size is fluid (clamp) rather than stepped, because these pages are read on a phone far more
   often than on a desk.
   ============================================================================================ */

.es {
    /* Overridden per theme below; declared here so a site with an unknown theme still paints. */
    --es-ink: #0e1420;
    --es-soft-ink: #5a6478;
    --es-paper: #ffffff;
    --es-canvas: #f5f7fb;
    --es-line: #e3e7ef;
    --es-accent: #2f4bff;
    --es-accent-ink: #ffffff;
    --es-accent-soft: #e8ecff;
    --es-display: "Poppins", "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;
    --es-body: "Poppins", system-ui, -apple-system, "Segoe UI", sans-serif;
    --es-display-weight: 700;
    --es-tracking: -.02em;
    --es-radius: 18px;
    --es-gap: clamp(56px, 7vw, 104px);

    background: var(--es-paper);
    color: var(--es-ink);
    font-family: var(--es-body);
    font-size: 16px;
    line-height: 1.65;
    /* clip, NOT hidden: overflow:hidden makes this element the sticky containing block, and since
       it is not itself scrollable the bar inside it never sticks - it scrolled away with the page.
       clip trims the same overflow without creating a scroll container. */
    overflow-x: clip;
}

/* ---------- themes ---------- */

/* Corporate: ink and indigo, geometric, tight. Reads as a business event without saying so. */
.es[data-site-theme="corporate"] {
    --es-ink: #0e1420;
    --es-soft-ink: #5a6478;
    --es-canvas: #f5f7fb;
    --es-line: #e3e7ef;
    --es-accent: #2f4bff;
    --es-accent-soft: #e8ecff;
    --es-tracking: -.025em;
}

/* Medical: clinical teal on near-white, serif headings. Calm, unhurried, credible. */
.es[data-site-theme="medical"] {
    --es-ink: #10262b;
    --es-soft-ink: #5b7076;
    --es-canvas: #f1f8f7;
    --es-line: #dceceb;
    --es-accent: #0f766e;
    --es-accent-soft: #dcf1ee;
    --es-display: "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, serif;
    --es-display-weight: 600;
    --es-tracking: -.01em;
    --es-radius: 14px;
}

/* Trade show: graphite and amber, heavy type, squarer corners. Loud on a busy floor. */
.es[data-site-theme="expo"] {
    --es-ink: #14161a;
    --es-soft-ink: #5b6472;
    --es-canvas: #f4f4f2;
    --es-line: #e2e2df;
    --es-accent: #d97706;
    --es-accent-ink: #ffffff;
    --es-accent-soft: #fdf0dd;
    --es-display-weight: 800;
    --es-tracking: -.03em;
    --es-radius: 6px;
}

/* Celebration: plum on champagne, expressive headings. Awards nights and galas. */
.es[data-site-theme="social"] {
    --es-ink: #2a1029;
    --es-soft-ink: #6f5568;
    --es-paper: #fffaf7;
    --es-canvas: #f7ece6;
    --es-line: #ecdcd3;
    --es-accent: #7c2d5a;
    --es-accent-soft: #f6e6ee;
    --es-display: "Iowan Old Style", Georgia, "Times New Roman", serif;
    --es-display-weight: 600;
    --es-tracking: -.015em;
    --es-radius: 26px;
}

/* ---------- primitives ---------- */

.es h1, .es h2, .es h3 {
    font-family: var(--es-display);
    font-weight: var(--es-display-weight);
    letter-spacing: var(--es-tracking);
    line-height: 1.1;
    margin: 0;
}

.es p { margin: 0; }

.esBtn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    /* 48px: these pages are tapped, standing up, one-handed. */
    min-height: 48px;
    padding: 12px 22px;
    border: 1px solid var(--es-line);
    border-radius: 999px;
    background: var(--es-paper);
    color: var(--es-ink);
    font-family: inherit;
    font-size: .95rem;
    font-weight: 600;
    text-decoration: none;
    cursor: pointer;
    transition: transform .18s cubic-bezier(.22, 1, .36, 1), background-color .18s ease,
                border-color .18s ease, color .18s ease, box-shadow .18s ease;
}

.esBtn:hover { transform: translateY(-2px); }

.esBtn:focus-visible {
    outline: 2px solid var(--es-accent);
    outline-offset: 3px;
}

.esBtn--solid {
    border-color: var(--es-accent);
    background: var(--es-accent);
    color: var(--es-accent-ink);
    box-shadow: 0 10px 26px -14px var(--es-accent);
}

.esBtn--solid:hover {
    color: var(--es-accent-ink);
    box-shadow: 0 16px 34px -14px var(--es-accent);
}

.esBtn--ghost {
    border-color: currentColor;
    background: transparent;
    color: inherit;
}

.esBtn--lg {
    min-height: 54px;
    padding: 14px 30px;
    font-size: 1rem;
}

/* One motion idea for the whole page: content arrives from just below, once, on first sight.
   Handed out by siteReveal.js.

   The hidden state is applied ONLY under .es-animate, which that script adds to the page itself
   before it starts observing. Without it - script missing, blocked, still loading, or JS off -
   every element is simply visible. A page whose content depends on an animation script running is
   a page that shows nothing the day the script 404s, which is exactly what happened. */
.es-reveal {
    transition: opacity .6s cubic-bezier(.22, 1, .36, 1), transform .6s cubic-bezier(.22, 1, .36, 1);
}

.es-animate .es-reveal {
    opacity: 0;
    transform: translateY(18px);
}

.es-animate .es-reveal.is-in {
    opacity: 1;
    transform: none;
}

/* Lists cascade rather than arriving as a block: the row, card or figure sets --esStagger in the
   markup and the shared transition picks it up. Capped in the markup so a hundred speakers do not
   end in a card that arrives four seconds late. */
.es-animate .es-reveal { transition-delay: var(--esStagger, 0ms); }

@media (prefers-reduced-motion: reduce) {
    .es-animate .es-reveal {
        opacity: 1;
        transform: none;
        transition: none;
    }

    .esBtn:hover { transform: none; }
}

/* ---------- the bar ---------- */

.esBar {
    position: sticky;
    top: 0;
    z-index: 20;
    background: color-mix(in srgb, var(--es-paper) 88%, transparent);
    backdrop-filter: blur(10px);
    border-bottom: 1px solid var(--es-line);
}

.esBar__in {
    display: flex;
    align-items: center;
    gap: 18px;
    max-width: 1180px;
    margin: 0 auto;
    padding: 12px 20px;
}

.esBar__brand {
    display: inline-flex;
    align-items: center;
    min-width: 0;
    color: inherit;
    text-decoration: none;
}

.esBar__brand img {
    max-height: 40px;
    /* A wide banner used as a logo would otherwise push the nav off the bar on a phone. */
    max-width: min(46vw, 260px);
    width: auto;
    object-fit: contain;
}

.esBar__word {
    font-family: var(--es-display);
    font-weight: var(--es-display-weight);
    font-size: 1.05rem;
    letter-spacing: var(--es-tracking);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 40vw;
}

.esBar__nav {
    display: flex;
    gap: 4px;
    margin-left: auto;
    overflow-x: auto;
    scrollbar-width: none;
}

.esBar__nav::-webkit-scrollbar { display: none; }

.esBar__link {
    padding: 8px 12px;
    border: 0;
    border-radius: 999px;
    background: none;
    color: var(--es-soft-ink);
    font: inherit;
    font-size: .9rem;
    white-space: nowrap;
    cursor: pointer;
    transition: color .15s ease, background-color .15s ease;
}

.esBar__link:hover {
    color: var(--es-ink);
    background: var(--es-canvas);
}

.esBar__cta { min-height: 42px; padding: 9px 18px; flex: none; }

@media (max-width: 860px) {
    .esBar__nav { display: none; }
    .esBar__cta { margin-left: auto; }
}

/* ---------- heroes: one per template ---------- */

.esHero { position: relative; }

.esHero__kicker {
    font-size: .95rem;
    font-weight: 600;
    color: var(--es-accent);
    margin-bottom: 10px !important;
}

.esHero__title {
    font-size: clamp(2.1rem, 1.2rem + 4.2vw, 4.6rem);
    max-width: 18ch;
}

.esHero__sub {
    margin-top: 16px !important;
    font-size: clamp(1rem, .95rem + .35vw, 1.25rem);
    color: var(--es-soft-ink);
    max-width: 56ch;
}

.esHero__meta {
    display: flex;
    flex-wrap: wrap;
    gap: 10px 22px;
    margin-top: 22px;
    font-size: .95rem;
    color: var(--es-soft-ink);
}

.esHero__meta span { display: inline-flex; align-items: center; gap: 8px; }

.esHero__acts {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    margin-top: 30px;
}

/* spotlight — a photograph, and the essentials laid over it.

   Three layers, because a hero has to fill its box AND show the whole picture, and one image
   cannot do both: ::before is the same picture blown up and blurred, filling the band; ::after is
   the picture itself, contained, so nothing a client uploaded is ever cropped away. A portrait
   photograph in a wide band sits in the middle of its own colours instead of being cut in half. */
.esHero--spotlight {
    position: relative;
    display: flex;
    align-items: flex-end;
    min-height: min(88vh, 760px);
    padding: clamp(28px, 6vw, 80px) 20px;
    background-color: var(--es-ink);
    color: #fff;
    overflow: hidden;
}

.esHero--spotlight::before,
.esHero--spotlight::after {
    content: "";
    position: absolute;
    inset: 0;
    background-image: var(--es-hero, none);
    background-position: center;
    background-repeat: no-repeat;
}

.esHero--spotlight::before {
    background-size: cover;
    /* Scaled past the edges so the blur has something to bleed into. */
    transform: scale(1.15);
    filter: blur(26px) saturate(1.15) brightness(.72);
}

.esHero--spotlight::after {
    background-size: contain;
}

.esHero--spotlight .esHero__scrim {
    position: absolute;
    inset: 0;
    z-index: 1;
    background: linear-gradient(180deg, rgba(6, 10, 20, .18) 0%, rgba(6, 10, 20, .82) 100%);
}

.esHero--spotlight .esHero__in {
    position: relative;
    z-index: 2;
    width: 100%;
    max-width: 1180px;
    margin: 0 auto;
}

.esHero--spotlight .esHero__kicker { color: #fff; opacity: .9; }
.esHero--spotlight .esHero__sub,
.esHero--spotlight .esHero__meta { color: rgba(255, 255, 255, .86); }

/* split — words held against a picture, brochure-cover proportions */
.esHero--split {
    display: grid;
    grid-template-columns: 1.05fr .95fr;
    align-items: center;
    gap: clamp(24px, 4vw, 64px);
    max-width: 1180px;
    margin: 0 auto;
    padding: clamp(40px, 6vw, 90px) 20px;
}

/* The picture is held, not cropped: contained inside its panel, on a blurred bed of itself so the
   panel still reads as a block of colour rather than a letterboxed slide. */
.esHero--split .esHero__art {
    position: relative;
    min-height: min(60vh, 520px);
    border-radius: var(--es-radius);
    background-color: var(--es-canvas);
    overflow: hidden;
}

.esHero--split .esHero__art::before,
.esHero--split .esHero__art::after {
    content: "";
    position: absolute;
    inset: 0;
    background-image: var(--es-hero, none);
    background-position: center;
    background-repeat: no-repeat;
}

.esHero--split .esHero__art::before {
    background-size: cover;
    transform: scale(1.15);
    filter: blur(24px) brightness(.9);
}

.esHero--split .esHero__art::after {
    background-size: contain;
}

/* poster — type as the artwork, no photograph anywhere */
.esHero--poster {
    background: var(--es-accent);
    color: var(--es-accent-ink);
    padding: clamp(44px, 7vw, 110px) 20px;
}

.esHero--poster .esHero__poster { max-width: 1180px; margin: 0 auto; }
.esHero--poster .esHero__kicker { color: var(--es-accent-ink); opacity: .8; }

.esHero--poster .esHero__title {
    font-size: clamp(2.6rem, 1rem + 8vw, 7rem);
    max-width: 14ch;
}

.esHero--poster .esHero__rule {
    height: 2px;
    margin: clamp(24px, 4vw, 48px) 0;
    background: currentColor;
    opacity: .35;
}

.esHero--poster .esHero__posterFoot {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: clamp(20px, 4vw, 60px);
    align-items: end;
}

.esHero--poster .esHero__bigDate {
    font-family: var(--es-display);
    font-size: clamp(3rem, 1rem + 8vw, 6rem);
    font-weight: var(--es-display-weight);
    line-height: .9;
}

.esHero--poster .esHero__bigMonth { font-size: 1.1rem; opacity: .85; }
.esHero--poster .esHero__sub,
.esHero--poster .esHero__meta { color: rgba(255, 255, 255, .88); }
.esHero--poster .esBtn { border-color: transparent; }
.esHero--poster .esBtn--solid { background: var(--es-accent-ink); color: var(--es-accent); }
.esHero--poster .esBtn--ghost { color: var(--es-accent-ink); border-color: currentColor; }

/* programme — a compact band, because the schedule underneath is the point */
/* The gradient covers; the picture behind it is contained, so a tall photograph is not sliced to
   a band's height. */
.esHero--programme {
    padding: clamp(34px, 5vw, 72px) 20px;
    background-image: linear-gradient(120deg, var(--es-canvas) 0%, var(--es-accent-soft) 100%), var(--es-hero, none);
    background-size: cover, contain;
    background-position: center, center;
    background-repeat: no-repeat, no-repeat;
    border-bottom: 1px solid var(--es-line);
}

.esHero--programme .esHero__band { max-width: 1180px; margin: 0 auto; }
.esHero--programme .esHero__title { font-size: clamp(1.9rem, 1.1rem + 3vw, 3.4rem); }

/* minimal — one narrow column, nothing but the facts */
.esHero--minimal {
    max-width: 660px;
    margin: 0 auto;
    padding: clamp(52px, 9vw, 130px) 20px clamp(28px, 4vw, 56px);
}

.esHero--minimal .esHero__mark { max-height: 54px; margin-bottom: 26px; }
.esHero--minimal .esHero__title { font-size: clamp(2rem, 1.3rem + 2.6vw, 3.2rem); max-width: 20ch; }
.esHero--minimal .esHero__kicker { margin-top: 16px !important; }

@media (max-width: 860px) {
    .esHero--split { grid-template-columns: 1fr; }
    .esHero--split .esHero__art { min-height: 260px; order: -1; }
    .esHero--poster .esHero__posterFoot { grid-template-columns: 1fr; }
}

/* ---------- sections ---------- */

.esSec { padding: var(--es-gap) 20px; }

.esSec__in { max-width: 1180px; margin: 0 auto; }

.es--minimal .esSec__in { max-width: 660px; }

.esSec__head { margin-bottom: clamp(22px, 3vw, 40px); }

.esSec__title { font-size: clamp(1.5rem, 1.1rem + 1.6vw, 2.4rem); }

.esSec__sub {
    margin-top: 8px !important;
    color: var(--es-soft-ink);
    max-width: 62ch;
}

.esSec__foot { margin-top: 28px; }

/* Alternating grounds so a long page has rhythm without every block being a card. */
.esSec--stats,
.esSec--speakers,
.esSec--faq,
.esSec--sponsors { background: var(--es-canvas); }

.esProse { color: var(--es-soft-ink); max-width: 70ch; }
.esProse :is(p, ul, ol) { margin-bottom: 12px; }
.esProse a { color: var(--es-accent); }

/* A section the client wrote themselves holds whatever they pasted — a table, a video, a photo
   at its original size. It gets the full column rather than the reading measure, and everything
   inside is held to the page width so one wide table cannot make the whole site scroll. */
.esProse--custom { max-width: none; }

.esProse--custom :is(img, video, iframe) { max-width: 100%; height: auto; border-radius: 12px; }

.esProse--custom iframe { width: 100%; aspect-ratio: 16 / 9; border: 0; }

.esProse--custom table {
    width: 100%;
    border-collapse: collapse;
    margin-bottom: 14px;
}

.esProse--custom :is(th, td) {
    padding: 8px 10px;
    border: 1px solid var(--es-line, #e4e7ec);
    text-align: left;
    vertical-align: top;
}

/* Tables and pre blocks are the two things allowed to be wider than the screen, each scrolling
   inside its own box rather than taking the page with it. */
.esProse--custom :is(table, pre) { display: block; overflow-x: auto; max-width: 100%; }

.esProse--custom :is(h1, h2, h3, h4) { color: var(--es-ink); margin: 18px 0 8px; }

/* stats — the figures strip between About and the programme.

   A flex row, not an auto-fit grid: with two figures the grid gave each one half the page and
   left them stranded at the quarter points, reading as two loose items rather than one row. They
   now sit together in the middle whatever the count, with a hairline between them. */
.esStats {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    align-items: stretch;
    /* No gap: the spacing is the items' own padding, which is what keeps the row symmetrical. */
    gap: 0;
    text-align: center;
}

/* Every figure carries the SAME padding on both sides. Putting the spacing on one side only -
   a gap, or padding-left on all but the first - makes the row's ink wider on the right than the
   left, and a centred flex line then sits visibly off centre. */
.esStat {
    flex: 0 1 auto;
    min-width: 128px;
    max-width: 280px;
    padding: 0 clamp(18px, 3vw, 44px);
}

/* Reads as one row rather than separate blocks. Nothing to the left of the first. */
.esStat + .esStat {
    border-left: 1px solid var(--es-line);
}

/* A strip, not a full section: it carries no heading, so the section's own rhythm is too much. */
.esSec--stats {
    padding-top: clamp(32px, 4vw, 56px);
    padding-bottom: clamp(32px, 4vw, 56px);
}

.esStat__num {
    display: block;
    font-family: var(--es-display);
    font-weight: var(--es-display-weight);
    font-size: clamp(2rem, 1.2rem + 2.4vw, 3.2rem);
    letter-spacing: var(--es-tracking);
    color: var(--es-accent);
}

.esStat__label { display: block; margin-top: 4px; color: var(--es-soft-ink); }

/* agenda */
.esDay {
    display: grid;
    grid-template-columns: 92px 1fr;
    gap: clamp(16px, 3vw, 36px);
    padding: 26px 0;
    border-top: 1px solid var(--es-line);
}

.esDay__mark {
    position: sticky;
    top: 84px;
    align-self: start;
    text-align: center;
    padding: 12px 6px;
    border-radius: var(--es-radius);
    background: var(--es-accent-soft);
    color: var(--es-accent);
}

.esDay__dow { display: block; font-size: .78rem; }
.esDay__num { display: block; font-size: 1.7rem; font-weight: 700; line-height: 1.05; }
.esDay__mon { display: block; font-size: .78rem; }

.esSession {
    display: grid;
    grid-template-columns: 78px 1fr;
    gap: 16px;
    padding: 14px 0;
    border-top: 1px solid var(--es-line);
}

.esDay__list .esSession:first-child { border-top: 0; padding-top: 0; }

.esSession__time {
    font-variant-numeric: tabular-nums;
    font-weight: 700;
}

.esSession__to { display: block; font-weight: 400; font-size: .85rem; color: var(--es-soft-ink); }
.esSession__title { font-size: 1.05rem; }
.esSession__sub { margin-top: 4px !important; color: var(--es-soft-ink); }

.esSession__meta {
    display: flex;
    flex-wrap: wrap;
    gap: 6px 16px;
    margin-top: 8px;
    font-size: .85rem;
    color: var(--es-soft-ink);
}

.esSession__meta span { display: inline-flex; align-items: center; gap: 6px; }

/* speakers */
.esFaces {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
    gap: clamp(18px, 2.5vw, 34px);
}

/* A button, because it opens the bio - restyled back down to the card it looks like. */
.esFace {
    display: block;
    width: 100%;
    margin: 0;
    padding: 0;
    border: 0;
    background: none;
    font: inherit;
    color: inherit;
    text-align: center;
    cursor: pointer;
}

.esFace:focus-visible,
.esOpen:focus-visible {
    outline: 2px solid var(--es-accent);
    outline-offset: 3px;
}

.esFace__text { display: block; }

.esFace:hover .esFace__pic {
    transform: translateY(-3px);
}

.esFace__pic {
    transition: transform .25s cubic-bezier(.22, 1, .36, 1);
}

.esFace:hover .esFace__name {
    color: var(--es-accent);
}

/* The session title, as a control: it keeps the heading's type, and the underline on hover is the
   only thing that says it opens something. */
.esOpen {
    padding: 0;
    border: 0;
    background: none;
    font: inherit;
    color: inherit;
    text-align: left;
    cursor: pointer;
}

.esOpen:hover {
    text-decoration: underline;
    text-underline-offset: 3px;
    text-decoration-thickness: 1px;
}

/* contain, not cover: a speaker photograph cropped to a square loses the top of somebody's head
   often enough that it is not worth the tidier grid. The square is kept; the picture sits in it. */
.esFace__pic {
    width: 100%;
    aspect-ratio: 1;
    object-fit: contain;
    border-radius: var(--es-radius);
    background: var(--es-accent-soft);
    color: var(--es-accent);
    display: flex;
    align-items: center;
    justify-content: center;
    font-family: var(--es-display);
    font-size: 2rem;
    font-weight: 700;
}

.esFace__name {
    display: block;
    margin-top: 12px;
    font-weight: 600;
    transition: color .2s ease;
}

.esFace__byline { display: block; font-size: .85rem; color: var(--es-soft-ink); }

/* tickets */
.esTicket {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: clamp(20px, 3vw, 44px);
    padding: clamp(24px, 4vw, 48px);
    border-radius: var(--es-radius);
    background: var(--es-accent-soft);
}

.esTicket .esSec__head { margin-bottom: 0; }
.esTicket__note { margin-top: 10px !important; color: var(--es-soft-ink); max-width: 52ch; }
.esTicket__acts { display: grid; gap: 12px; justify-items: stretch; }
.esCal { display: flex; flex-wrap: wrap; gap: 8px; }
.esCal .esBtn { min-height: 42px; padding: 9px 16px; font-size: .85rem; }

/* venue */
.esVenue {
    display: grid;
    grid-template-columns: minmax(240px, .8fr) 1.2fr;
    gap: clamp(20px, 3vw, 40px);
    align-items: stretch;
}

.esVenue__name { font-family: var(--es-display); font-size: 1.3rem; font-weight: var(--es-display-weight); }
.esVenue__addr { margin: 8px 0 20px !important; color: var(--es-soft-ink); max-width: 40ch; }

.esVenue__map {
    min-height: 320px;
    border-radius: var(--es-radius);
    overflow: hidden;
    border: 1px solid var(--es-line);
}

.esVenue__map iframe { width: 100%; height: 100%; min-height: 320px; border: 0; display: block; }

/* gallery */
.esGallery {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    align-items: start;
    gap: 14px;
}

.esShot { margin: 0; }

/* Natural proportions rather than a forced 4:3 box: whatever was uploaded is shown whole, and the
   grid takes the varied heights. Nothing is cut off to make the rows line up. */
.esShot img {
    display: block;
    width: 100%;
    height: auto;
    border-radius: var(--es-radius);
    transition: transform .4s cubic-bezier(.22, 1, .36, 1);
}

.esShot:hover img { transform: scale(1.02); }
.esShot figcaption { margin-top: 8px; font-size: .82rem; color: var(--es-soft-ink); }

/* sponsors — the shared SponsorFooter strip, wearing this template's colours.

   The markup and behaviour come from Shared/ExternalPages/SponsorFooter.razor, so there is one
   sponsor strip in the app. Only the surface is restated here: it is built for a page footer, and
   inside a site it is a section like any other. */

.es .spFooter {
    margin: 0;
    padding: 0;
    border: 0;
    background: transparent;
    color: inherit;
}

.es .spFooter__inner { padding: 0; }

/* The section already carries its own heading; the strip's own eyebrow would say it twice. */
.es .spFooter__eyebrow { display: none; }

.es .spFooter__tierName span {
    color: var(--es-soft-ink);
    font-family: var(--es-body);
}

.es .spFooter__logo img {
    /* Sponsor logos arrive in every colour there is; muting them until hover keeps the row calm
       without hiding anyone. */
    filter: grayscale(1);
    opacity: .74;
    transition: filter .25s ease, opacity .25s ease;
}

.es .spFooter__logo:hover img,
.es .spFooter__logo:focus-visible img {
    filter: none;
    opacity: 1;
}

.es .spFooter__hint {
    background: var(--es-accent);
    color: var(--es-accent-ink);
}

/* faq */
.esFaq { display: grid; gap: 10px; max-width: 860px; }

.esFaq__item {
    padding: 16px 20px;
    border: 1px solid var(--es-line);
    border-radius: var(--es-radius);
    background: var(--es-paper);
}

.esFaq__item summary {
    cursor: pointer;
    font-weight: 600;
    list-style: none;
}

.esFaq__item summary::-webkit-details-marker { display: none; }

.esFaq__item summary::after {
    content: "+";
    float: right;
    font-weight: 400;
    color: var(--es-accent);
}

.esFaq__item[open] summary::after { content: "–"; }
.esFaq__item[open] .esProse { margin-top: 10px; }

/* contact + links */
.esContact__note { color: var(--es-soft-ink); max-width: 56ch; }

.esContact__rows { display: flex; flex-wrap: wrap; gap: 12px 28px; margin-top: 16px; }

.esContact__row {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    color: var(--es-ink);
    text-decoration: none;
    font-weight: 600;
}

.esContact__row:hover { color: var(--es-accent); }

.esLinks {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: 12px;
}

.esLinkCard {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 16px 20px;
    border: 1px solid var(--es-line);
    border-radius: var(--es-radius);
    background: var(--es-paper);
    color: var(--es-ink);
    text-decoration: none;
    font-weight: 600;
    transition: border-color .18s ease, transform .18s cubic-bezier(.22, 1, .36, 1);
}

.esLinkCard:hover { border-color: var(--es-accent); transform: translateY(-2px); }
.esLinkCard i:first-child { color: var(--es-accent); font-size: 1.2rem; }
.esLinkCard__go { margin-left: auto; color: var(--es-soft-ink); }

.esSocial { display: flex; gap: 10px; }

.esSocial a {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 42px;
    height: 42px;
    border: 1px solid var(--es-line);
    border-radius: 50%;
    color: var(--es-ink);
    text-decoration: none;
    transition: border-color .18s ease, color .18s ease;
}

.esSocial a:hover { border-color: var(--es-accent); color: var(--es-accent); }

/* footer */
.esFoot {
    padding: 34px 20px;
    border-top: 1px solid var(--es-line);
    background: var(--es-canvas);
}

.esFoot__in {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    max-width: 1180px;
    margin: 0 auto;
}

.esFoot__name { font-family: var(--es-display); font-weight: var(--es-display-weight); }
.esFoot__meta { font-size: .88rem; color: var(--es-soft-ink); }

@media (max-width: 560px) {
    /* Two across on a phone; the divider would then cut the wrong pair, so it goes. */
    .esStat {
        flex: 1 1 40%;
        min-width: 0;
        max-width: none;
        padding: 0 8px;
    }

    .esStat + .esStat {
        border-left: 0;
    }
}

@media (max-width: 720px) {
    .esDay { grid-template-columns: 1fr; }

    .esDay__mark {
        position: static;
        display: inline-flex;
        gap: 8px;
        align-items: baseline;
        padding: 6px 14px;
    }

    .esDay__num { font-size: 1.2rem; }
    .esSession { grid-template-columns: 64px 1fr; gap: 12px; }
    .esVenue { grid-template-columns: 1fr; }
    .esTicket__acts { width: 100%; }
}

@keyframes esThumbWord {
    0%, 70%, 100% { opacity: 1; transform: none; }
    35%           { opacity: .35; transform: translateY(-2px); }
}

@keyframes esThumbAur {
    from { background-position: 0% 0%, 100% 100%, 0 0; }
    to   { background-position: 30% 25%, 70% 75%, 0 0; }
}

/* countdown — sits in the figures strip, or in a band of its own when that strip is off.

   Tabular figures and fixed-width boxes, because a number that changes every second must not
   shuffle everything beside it. The seconds are quieter than the days: nobody reads the seconds,
   they just want to see the thing is alive. */

.esCd {
    display: flex;
    justify-content: center;
    margin-bottom: clamp(20px, 3vw, 34px);
}

/* Nothing under it when it stands alone. */
.esSec--countdown .esCd { margin-bottom: 0; }

.esCd__boxes {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 0;
}

.esCd__box {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    /* Symmetric padding, same rule as the figures beside it: spacing on one side only puts the
       row's ink off centre. */
    padding: 0 clamp(12px, 2.4vw, 28px);
    min-width: 82px;
}

.esCd__box + .esCd__box {
    border-left: 1px solid var(--es-line);
}

.esCd__box span {
    font-family: var(--es-display);
    font-weight: var(--es-display-weight);
    font-size: clamp(1.7rem, 1.1rem + 1.9vw, 2.7rem);
    line-height: 1.05;
    letter-spacing: var(--es-tracking);
    color: var(--es-accent);
    font-variant-numeric: tabular-nums;
}

.esCd__box small {
    font-size: .72rem;
    letter-spacing: .02em;
    color: var(--es-soft-ink);
}

.esCd__status {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 10px 20px;
    border-radius: 999px;
    font-weight: 600;
}

.esCd__status.is-live {
    background: var(--es-accent);
    color: var(--es-accent-ink);
}

/* A dot that breathes, so "happening now" reads as now. */
.esCd__status.is-live::before {
    content: "";
    width: 9px;
    height: 9px;
    border-radius: 50%;
    background: currentColor;
    animation: esCdPulse 1.6s ease-in-out infinite;
}

.esCd__status.is-ended {
    background: var(--es-canvas);
    color: var(--es-soft-ink);
    border: 1px solid var(--es-line);
}

@keyframes esCdPulse {
    0%, 100% { opacity: 1; transform: scale(1); }
    50%      { opacity: .45; transform: scale(.82); }
}

@media (prefers-reduced-motion: reduce) {
    .esCd__status.is-live::before { animation: none; }
}

@media (max-width: 520px) {
    .esCd__box {
        min-width: 0;
        flex: 1 1 22%;
        padding: 0 6px;
    }

    .esCd__box + .esCd__box { border-left: 0; }
}

/* ---------- the site's own dialogs (programme + speakers) ----------
   A plain overlay, wearing the template's tokens. Same motion as the public agenda page: the
   backdrop fades, the box rises behind it, and leaving is quicker than arriving. */

.esModal {
    position: fixed;
    inset: 0;
    z-index: 60;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 20px;
    background: rgba(10, 14, 24, .58);
    overflow-y: auto;
    animation: esVeil 180ms ease-out both;
}

/* The static page's one dialog shell waits in the document; display:flex above would beat the
   browser's own [hidden] rule. */
.esModal[hidden] { display: none; }

.esModal.is-closing {
    animation: esVeil 130ms ease-in reverse both;
    pointer-events: none;
}

@keyframes esVeil {
    from { opacity: 0; }
    to   { opacity: 1; }
}

@keyframes esRise {
    from { opacity: 0; transform: translateY(14px) scale(.97); }
    to   { opacity: 1; transform: none; }
}

@keyframes esSwap {
    from { opacity: 0; transform: translateY(6px); }
    to   { opacity: 1; transform: none; }
}

.esModal__box {
    position: relative;
    width: min(720px, 100%);
    max-height: calc(100vh - 40px);
    overflow-y: auto;
    border-radius: var(--es-radius);
    /* Explicit white behind the token, so the box is opaque even if a host page's stylesheet
       reaches the tokens. */
    background: #fff;
    background: var(--es-paper, #fff);
    color: var(--es-ink);
    box-shadow: 0 24px 60px -12px rgba(10, 14, 24, .4);
    animation: esRise 220ms cubic-bezier(.22, 1, .36, 1) 40ms both;
}

.esModal.is-closing .esModal__box {
    animation: esRise 130ms ease-in reverse both;
}

.esModal__box--narrow { width: min(560px, 100%); }
.esModal__box:focus { outline: none; }

/* Swapping a session for a speaker inside an open overlay: the box stays, the content changes. */
.esModal__body {
    padding: 24px;
    animation: esSwap 200ms ease-out both;
}

.esModal__close {
    position: absolute;
    top: 12px;
    right: 12px;
    z-index: 1;
    width: 40px;
    height: 40px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--es-line);
    border-radius: 50%;
    background: var(--es-paper);
    color: var(--es-ink);
    font-size: 1.1rem;
    cursor: pointer;
    transition: border-color .15s ease, color .15s ease, transform .15s ease;
}

.esModal__close:hover {
    border-color: var(--es-accent);
    color: var(--es-accent);
    transform: rotate(90deg);
}

.esModal__img {
    display: block;
    width: 100%;
    height: auto;
    max-height: 42vh;
    object-fit: contain;
    background: var(--es-canvas);
    border-radius: var(--es-radius) var(--es-radius) 0 0;
}

.esModal__type {
    display: inline-block;
    margin-bottom: 8px;
    padding: 2px 10px;
    border-radius: 999px;
    background: var(--es-accent-soft);
    color: var(--es-accent);
    font-size: .72rem;
    font-weight: 700;
    text-transform: capitalize;
}

.esModal__title {
    /* Clear of the close button, or a long title runs underneath it. */
    padding-right: 46px;
    font-size: clamp(1.2rem, 1rem + .9vw, 1.6rem);
}

.esModal__sub {
    margin-top: 6px !important;
    color: var(--es-soft-ink);
}

.esModal__facts {
    display: flex;
    flex-wrap: wrap;
    gap: 8px 18px;
    margin-top: 14px;
    padding-top: 14px;
    border-top: 1px solid var(--es-line);
    font-size: .87rem;
    color: var(--es-soft-ink);
}

.esModal__facts span { display: inline-flex; align-items: center; gap: 6px; }

.esModal__dir {
    margin-top: 10px !important;
    font-size: .87rem;
    color: var(--es-soft-ink);
}

.esModal__label {
    margin: 20px 0 10px !important;
    font-size: .8rem;
    font-weight: 700;
    color: var(--es-soft-ink);
}

.esModal__people {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
}

.esModal__tags {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: 14px;
}

.esTag {
    padding: 2px 10px;
    border: 1px solid var(--es-line);
    border-radius: 999px;
    font-size: .74rem;
    color: var(--es-soft-ink);
}

.esModal__acts {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    margin-top: 18px;
}

.esModal__acts .esBtn { min-height: 44px; padding: 10px 18px; font-size: .88rem; }

/* a speaker inside the session dialog */
.esPerson {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    padding: 5px 14px 5px 5px;
    border: 1px solid var(--es-line);
    border-radius: 999px;
    background: none;
    font: inherit;
    color: inherit;
    text-align: left;
    cursor: pointer;
    transition: border-color .15s ease, background-color .15s ease;
}

.esPerson:hover {
    border-color: var(--es-accent);
    background: var(--es-accent-soft);
}

.esPerson:focus-visible {
    outline: 2px solid var(--es-accent);
    outline-offset: 2px;
}

.esPerson__pic {
    width: 34px;
    height: 34px;
    flex: none;
    border-radius: 50%;
    object-fit: contain;
    background: var(--es-accent-soft);
    color: var(--es-accent);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: .74rem;
    font-weight: 700;
}

.esPerson__text { min-width: 0; }
.esPerson__name { display: block; font-size: .88rem; font-weight: 600; }
.esPerson__byline { display: block; font-size: .76rem; color: var(--es-soft-ink); }

/* the speaker dialog */
.esBio__head {
    display: flex;
    align-items: center;
    gap: 16px;
    padding-right: 46px;
}

.esBio__pic {
    width: 88px;
    height: 88px;
    flex: none;
    border-radius: 50%;
    object-fit: contain;
    background: var(--es-accent-soft);
    color: var(--es-accent);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-family: var(--es-display);
    font-size: 1.5rem;
    font-weight: 700;
}

.esBio__list {
    margin: 0;
    padding: 0;
    list-style: none;
}

.esBio__list li {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 4px 12px;
    padding: 9px 0;
    border-top: 1px solid var(--es-line);
}

.esBio__list li:first-child { border-top: 0; }
.esBio__list .esOpen { font-weight: 600; }
.esBio__when { font-size: .8rem; color: var(--es-soft-ink); }

@media (prefers-reduced-motion: reduce) {
    /* Nothing moves, but the dialog still has to appear and disappear. */
    .esModal,
    .esModal__box,
    .esModal__body,
    .esModal.is-closing,
    .esModal.is-closing .esModal__box {
        animation-duration: 1ms;
    }

    .esModal__close:hover { transform: none; }
    .esFace:hover .esFace__pic { transform: none; }
}

@media (max-width: 560px) {
    .esBio__head { gap: 12px; }
    .esBio__pic { width: 64px; height: 64px; font-size: 1.1rem; }
    .esModal__body { padding: 18px; }
}

/* ============================================================================================
   EVENT WEBSITE — the six later themes, and the motion that belongs to them.

   The first four skins are quiet by design (a medical congress does not want its page moving).
   These six are the opposite brief: colour that drifts, edges that light up, and hovers that
   answer. Everything here is CSS - no scroll handlers, no rAF loop - and every animated surface
   is a compositor-only transform or opacity, so a phone is not asked to repaint the page 60 times
   a second. All of it collapses under prefers-reduced-motion at the end of the block.
   ============================================================================================ */

/* Midnight: deep navy, an electric blue that glows rather than shouts. */
.es[data-site-theme="midnight"] {
    --es-ink: #eaf0ff;
    --es-soft-ink: #9aa8c7;
    --es-paper: #0b1020;
    --es-canvas: #121a31;
    --es-line: #22304f;
    --es-accent: #4d8bff;
    --es-accent-ink: #05091a;
    --es-accent-soft: #16213c;
    --es-tracking: -.025em;
    --es-glow: 0 0 34px -6px rgba(77, 139, 255, .55);
}

/* Aurora: the same dark ground with violet and teal lights drifting behind it. */
.es[data-site-theme="aurora"] {
    --es-ink: #f2ecff;
    --es-soft-ink: #a9a2c9;
    --es-paper: #0a0a16;
    --es-canvas: #14122a;
    --es-line: #2a2545;
    --es-accent: #a78bfa;
    --es-accent-ink: #100c22;
    --es-accent-soft: #1d1838;
    --es-display-weight: 600;
    --es-radius: 22px;
    --es-glow: 0 0 40px -8px rgba(167, 139, 250, .6);
}

/* Sunrise: coral into amber on warm paper. Daylight, outdoors, community. */
.es[data-site-theme="sunrise"] {
    --es-ink: #2a1810;
    --es-soft-ink: #7c6152;
    --es-paper: #fffaf5;
    --es-canvas: #fdeee2;
    --es-line: #f2ddcc;
    --es-accent: #ea6a3a;
    --es-accent-soft: #fde3d5;
    --es-radius: 20px;
    --es-glow: 0 14px 34px -16px rgba(234, 106, 58, .7);
}

/* Forest: deep green on sand. Unhurried, outdoors, research. */
.es[data-site-theme="forest"] {
    --es-ink: #16261c;
    --es-soft-ink: #5e7264;
    --es-paper: #fbfaf5;
    --es-canvas: #eef2e6;
    --es-line: #dde3d3;
    --es-accent: #2f6b45;
    --es-accent-soft: #e0ecdf;
    --es-display: "Iowan Old Style", Georgia, serif;
    --es-display-weight: 600;
    --es-radius: 12px;
}

/* Mono: black, white, one red mark. Nothing else, on purpose. */
.es[data-site-theme="mono"] {
    --es-ink: #0a0a0a;
    --es-soft-ink: #6b6b6b;
    --es-paper: #ffffff;
    --es-canvas: #f2f2f2;
    --es-line: #d8d8d8;
    --es-accent: #e11d1d;
    --es-accent-soft: #fdeaea;
    --es-display-weight: 800;
    --es-tracking: -.04em;
    --es-radius: 0px;
}

/* Neon: near-black with magenta and lime lit up along the edges. */
.es[data-site-theme="neon"] {
    --es-ink: #f4f7ff;
    --es-soft-ink: #9ba3b8;
    --es-paper: #08080c;
    --es-canvas: #101018;
    --es-line: #23232f;
    --es-accent: #ff2fb9;
    --es-accent-ink: #0a0a10;
    --es-accent-soft: #23122a;
    --es-accent-2: #b6ff3a;
    --es-display-weight: 800;
    --es-tracking: -.03em;
    --es-glow: 0 0 30px -4px rgba(255, 47, 185, .65);
}

/* ---------- ambient background ----------
   One fixed layer per site, painting two slow blobs and a fine grid. It is behind everything,
   pointer-transparent, and only switched on for the themes that were designed with it - the
   quiet four keep their plain paper. */

.es[data-site-theme="midnight"],
.es[data-site-theme="aurora"],
.es[data-site-theme="neon"],
.es[data-site-theme="sunrise"] {
    position: relative;
    isolation: isolate;
}

.es[data-site-theme="midnight"]::before,
.es[data-site-theme="aurora"]::before,
.es[data-site-theme="neon"]::before,
.es[data-site-theme="sunrise"]::before {
    content: "";
    position: fixed;
    inset: -20vmax;
    z-index: -1;
    pointer-events: none;
    background:
        radial-gradient(38vmax 38vmax at 18% 22%, color-mix(in srgb, var(--es-accent) 42%, transparent), transparent 70%),
        radial-gradient(32vmax 32vmax at 82% 68%, color-mix(in srgb, var(--es-accent-2, var(--es-accent)) 34%, transparent), transparent 70%);
    filter: blur(30px);
    opacity: .5;
    /* Two drifts at different, deliberately unrelated durations, so the pair never repeats the
       same arrangement twice within a visit - the movement reads as ambient, not as a loop. */
    animation: esDriftA 34s ease-in-out infinite alternate,
               esDriftB 47s ease-in-out infinite alternate;
}

/* The dark themes carry a faint grid over the drift; it gives the colour something to sit on. */
.es[data-site-theme="midnight"]::after,
.es[data-site-theme="aurora"]::after,
.es[data-site-theme="neon"]::after {
    content: "";
    position: fixed;
    inset: 0;
    z-index: -1;
    pointer-events: none;
    background-image:
        linear-gradient(color-mix(in srgb, var(--es-line) 55%, transparent) 1px, transparent 1px),
        linear-gradient(90deg, color-mix(in srgb, var(--es-line) 55%, transparent) 1px, transparent 1px);
    background-size: 64px 64px;
    /* Fades out towards the bottom so it never competes with the content down there. */
    mask-image: radial-gradient(120vmax 90vmax at 50% 0%, #000 20%, transparent 78%);
    opacity: .5;
}

@keyframes esDriftA {
    from { transform: translate3d(-4%, -3%, 0) scale(1); }
    to   { transform: translate3d(5%, 4%, 0) scale(1.12); }
}

@keyframes esDriftB {
    from { transform: rotate(0deg); }
    to   { transform: rotate(8deg); }
}

/* Sections sit on the paper; on an animated theme they let the drift through instead. */
.es[data-site-theme="midnight"] .esSec--stats,
.es[data-site-theme="aurora"] .esSec--stats,
.es[data-site-theme="neon"] .esSec--stats,
.es[data-site-theme="midnight"] .esSec--speakers,
.es[data-site-theme="aurora"] .esSec--speakers,
.es[data-site-theme="neon"] .esSec--speakers,
.es[data-site-theme="midnight"] .esSec--faq,
.es[data-site-theme="aurora"] .esSec--faq,
.es[data-site-theme="neon"] .esSec--faq,
.es[data-site-theme="midnight"] .esSec--sponsors,
.es[data-site-theme="aurora"] .esSec--sponsors,
.es[data-site-theme="neon"] .esSec--sponsors {
    background: color-mix(in srgb, var(--es-canvas) 72%, transparent);
    backdrop-filter: blur(6px);
}

.es[data-site-theme="midnight"] .esBar,
.es[data-site-theme="aurora"] .esBar,
.es[data-site-theme="neon"] .esBar {
    background: color-mix(in srgb, var(--es-paper) 72%, transparent);
}

/* A lit edge on the primary button, for the themes that have a glow token. */
.es[data-site-theme="midnight"] .esBtn--solid,
.es[data-site-theme="aurora"] .esBtn--solid,
.es[data-site-theme="neon"] .esBtn--solid,
.es[data-site-theme="sunrise"] .esBtn--solid {
    box-shadow: var(--es-glow);
}

/* Neon's second colour earns its keep on the one thing that must be found: the register button. */
.es[data-site-theme="neon"] .esBtn--solid {
    background: linear-gradient(100deg, var(--es-accent) 0%, var(--es-accent-2) 140%);
    border-color: transparent;
}

/* ============================================================================================
   GLASS and HOLOGRAPHIC — two looks built around light rather than colour, and two page shapes
   built around motion rather than photography. The rule for all four: the hero keeps moving, the
   sections below animate once as they arrive, and nothing loops behind text being read.
   ============================================================================================ */

/* Glass: frosted panels over a light that keeps moving. Cool, quiet, expensive-looking. */
.es[data-site-theme="glass"] {
    --es-ink: #0d1424;
    --es-soft-ink: #5b6780;
    --es-paper: #eef2f9;
    --es-canvas: #e6ecf6;
    --es-line: rgba(255, 255, 255, .55);
    --es-accent: #2563eb;
    --es-accent-ink: #ffffff;
    --es-accent-soft: #dbe6ff;
    --es-accent-2: #22d3ee;
    --es-display-weight: 700;
    --es-tracking: -.025em;
    --es-radius: 22px;
    --es-glow: 0 18px 40px -18px rgba(37, 99, 235, .75);
}

/* Holographic: iridescence on near-black. Loud, and meant to be. */
.es[data-site-theme="holo"] {
    --es-ink: #f2f3ff;
    --es-soft-ink: #a2a6c8;
    --es-paper: #07070e;
    --es-canvas: #0e0e1a;
    --es-line: #23233a;
    --es-accent: #8b5cf6;
    --es-accent-ink: #0a0a12;
    --es-accent-soft: #1b1430;
    --es-accent-2: #22d3ee;
    --es-display-weight: 800;
    --es-tracking: -.03em;
    --es-glow: 0 0 34px -6px rgba(139, 92, 246, .8);
}

/* Both join the ambient background the dark themes already carry. */
.es[data-site-theme="glass"],
.es[data-site-theme="holo"] {
    position: relative;
    isolation: isolate;
}

.es[data-site-theme="glass"]::before,
.es[data-site-theme="holo"]::before {
    content: "";
    position: fixed;
    inset: -20vmax;
    z-index: -1;
    pointer-events: none;
    background:
        radial-gradient(40vmax 40vmax at 16% 18%, color-mix(in srgb, var(--es-accent) 46%, transparent), transparent 70%),
        radial-gradient(34vmax 34vmax at 84% 72%, color-mix(in srgb, var(--es-accent-2) 40%, transparent), transparent 70%);
    filter: blur(34px);
    opacity: .55;
    animation: esDriftA 34s ease-in-out infinite alternate,
               esDriftB 47s ease-in-out infinite alternate;
}

/* Holo adds a third light that cycles hue, which is what makes it read as iridescent rather
   than simply purple. Glass keeps its two: frosted glass over a disco is not glass. */
.es[data-site-theme="holo"]::after {
    content: "";
    position: fixed;
    inset: -10vmax;
    z-index: -1;
    pointer-events: none;
    background: conic-gradient(from 0deg at 50% 50%,
        rgba(139, 92, 246, .35), rgba(34, 211, 238, .32), rgba(236, 72, 153, .30),
        rgba(250, 204, 21, .22), rgba(139, 92, 246, .35));
    filter: blur(60px);
    opacity: .42;
    animation: esHoloTurn 44s linear infinite;
}

@keyframes esHoloTurn {
    from { transform: rotate(0deg) scale(1.05); }
    to   { transform: rotate(360deg) scale(1.05); }
}

/* Glass's panels: the surface is the theme. Sections let the drift through and sit on frost. */
.es[data-site-theme="glass"] :is(.esSec--stats, .esSec--speakers, .esSec--faq, .esSec--sponsors),
.es[data-site-theme="holo"] :is(.esSec--stats, .esSec--speakers, .esSec--faq, .esSec--sponsors) {
    background: color-mix(in srgb, var(--es-canvas) 60%, transparent);
    backdrop-filter: blur(14px);
}

.es[data-site-theme="glass"] .esBar,
.es[data-site-theme="holo"] .esBar {
    background: color-mix(in srgb, var(--es-paper) 62%, transparent);
    backdrop-filter: blur(16px);
}

.es[data-site-theme="glass"] :is(.esFace, .esStat, .esFaq__item, .esCard),
.es[data-site-theme="holo"] :is(.esFace, .esStat, .esFaq__item, .esCard) {
    background: color-mix(in srgb, var(--es-paper) 55%, transparent);
    border: 1px solid var(--es-line);
    backdrop-filter: blur(12px);
    box-shadow: 0 16px 40px -28px rgba(8, 12, 30, .8);
}

.es[data-site-theme="glass"] .esBtn--solid,
.es[data-site-theme="holo"] .esBtn--solid {
    box-shadow: var(--es-glow);
}

/* The register button catches the light as it passes — the one place the iridescence is allowed
   to touch something a reader has to act on. */
.es[data-site-theme="holo"] .esBtn--solid {
    background: linear-gradient(100deg, var(--es-accent) 0%, var(--es-accent-2) 60%, #ec4899 130%);
    background-size: 220% 100%;
    border-color: transparent;
    animation: esSheen 7s ease-in-out infinite;
}

@keyframes esSheen {
    0%, 100% { background-position: 0% 50%; }
    50%      { background-position: 100% 50%; }
}

/* Holo headings pick the light up through the letters themselves.
   The kinetic headline is excluded: its words are separate spans, and a gradient clipped to the
   h1 paints nothing behind a child — the headline came out invisible. There, the type's own
   movement is the effect. */
.es[data-site-theme="holo"] .esHero__title:not(.esKin__title),
.es[data-site-theme="holo"] .esSec__title {
    background: linear-gradient(100deg, #f2f3ff 0%, var(--es-accent-2) 30%, var(--es-accent) 60%, #ec4899 100%);
    background-size: 260% 100%;
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    animation: esSheen 9s ease-in-out infinite;
}

/* ---------- page shape: kinetic type ---------- */

.esHero--kinetic {
    position: relative;
    display: flex;
    align-items: center;
    min-height: clamp(520px, 78vh, 760px);
    /* Side gutter, same as every other shape: without it the headline sits against the edge of
       the window with nothing to breathe into.
       The bottom is restated as !important because a global `section { padding-bottom: 0
       !important }` (custom.css:4408) would otherwise strip it, and on a short screen the button
       row ends up flush against the edge of the hero. */
    padding: clamp(40px, 8vw, 96px) 20px;
    padding-block-end: clamp(40px, 8vw, 96px) !important;
    overflow: hidden;
    background: var(--es-paper);
}

/* A grid that creeps: enough to say the surface is alive, not enough to read as a pattern. */
.esKin__grid {
    position: absolute;
    inset: -40px;
    z-index: 0;
    pointer-events: none;
    background-image:
        linear-gradient(color-mix(in srgb, var(--es-line) 70%, transparent) 1px, transparent 1px),
        linear-gradient(90deg, color-mix(in srgb, var(--es-line) 70%, transparent) 1px, transparent 1px);
    background-size: 58px 58px;
    mask-image: radial-gradient(80vmax 60vmax at 30% 20%, #000 10%, transparent 75%);
    animation: esKinGrid 18s linear infinite;
}

@keyframes esKinGrid {
    from { transform: translate3d(0, 0, 0); }
    to   { transform: translate3d(58px, 58px, 0); }
}

.esKin__blob {
    position: absolute;
    z-index: 0;
    width: 46vmax;
    height: 46vmax;
    border-radius: 50%;
    pointer-events: none;
    filter: blur(48px);
    opacity: .5;
}

.esKin__blob--a {
    top: -18vmax;
    right: -10vmax;
    background: radial-gradient(circle, color-mix(in srgb, var(--es-accent) 58%, transparent), transparent 68%);
    animation: esKinFloatA 26s ease-in-out infinite alternate;
}

.esKin__blob--b {
    bottom: -22vmax;
    left: -12vmax;
    background: radial-gradient(circle, color-mix(in srgb, var(--es-accent-2, var(--es-accent)) 50%, transparent), transparent 68%);
    animation: esKinFloatB 33s ease-in-out infinite alternate;
}

@keyframes esKinFloatA {
    from { transform: translate3d(0, 0, 0) scale(1); }
    to   { transform: translate3d(-6%, 8%, 0) scale(1.14); }
}

@keyframes esKinFloatB {
    from { transform: translate3d(0, 0, 0) scale(1.08); }
    to   { transform: translate3d(7%, -6%, 0) scale(1); }
}

.esHero--kinetic .esHero__in {
    position: relative;
    z-index: 1;
    width: 100%;
    max-width: 1180px;
    margin: 0 auto;
}

.esKin__title { display: block; }

/* Each word arrives on its own beat, rising and un-blurring into place. The delay is set per
   word in the markup so a long headline still finishes inside a second and a half. */
.esKin__word {
    display: inline-block;
    animation: esKinWord .7s cubic-bezier(.22, 1, .36, 1) both;
    animation-delay: var(--esKinDelay, 0ms);
}

@keyframes esKinWord {
    from { opacity: 0; transform: translate3d(0, .5em, 0) rotate(2deg); filter: blur(8px); }
    to   { opacity: 1; transform: none; filter: blur(0); }
}

/* The cycling line: one window, the items stacked inside it, the stack stepped down on a timer.
   --esKinCount is written per site, so the loop is as long as there are facts to show. */
.esKin__cycle {
    display: flex;
    align-items: baseline;
    gap: 8px;
    margin: 6px 0 18px;
    font-size: clamp(1rem, .9rem + .6vw, 1.35rem);
    color: var(--es-soft-ink);
}

.esKin__cycleLead { flex: none; }

.esKin__cycleWrap {
    position: relative;
    display: block;
    height: 1.6em;
    overflow: hidden;
}

.esKin__cycleTrack { display: block; }

.esKin__cycleItem {
    display: block;
    height: 1.6em;
    line-height: 1.6em;
    white-space: nowrap;
    color: var(--es-accent);
    font-weight: 650;
}

/* The track steps up one line at a time and rests between steps — the rest is the point, since
   the line has to be readable. A duplicate of the first line sits at the end of the track, so the
   loop finishes by arriving at it rather than snapping back through the whole stack.

   One set of keyframes per possible length, because a keyframe percentage cannot be computed from
   a custom property. Two, three and four are the only lengths this line is ever built from. */
.esKin__cycleWrap--n2 .esKin__cycleTrack { animation: esKinCycle2 6s cubic-bezier(.76, 0, .24, 1) infinite; }
.esKin__cycleWrap--n3 .esKin__cycleTrack { animation: esKinCycle3 9s cubic-bezier(.76, 0, .24, 1) infinite; }
.esKin__cycleWrap--n4 .esKin__cycleTrack { animation: esKinCycle4 12s cubic-bezier(.76, 0, .24, 1) infinite; }

@keyframes esKinCycle2 {
    0%,  42%  { transform: translateY(0); }
    50%, 92%  { transform: translateY(-1.6em); }
    100%      { transform: translateY(-3.2em); }
}

@keyframes esKinCycle3 {
    0%,  28%  { transform: translateY(0); }
    33%, 61%  { transform: translateY(-1.6em); }
    66%, 95%  { transform: translateY(-3.2em); }
    100%      { transform: translateY(-4.8em); }
}

@keyframes esKinCycle4 {
    0%,  21%  { transform: translateY(0); }
    25%, 46%  { transform: translateY(-1.6em); }
    50%, 71%  { transform: translateY(-3.2em); }
    75%, 96%  { transform: translateY(-4.8em); }
    100%      { transform: translateY(-6.4em); }
}

/* ---------- page shape: aurora canvas ---------- */

.esHero--aurora {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: clamp(560px, 86vh, 860px);
    padding: clamp(40px, 8vw, 96px) 20px;
    /* See the note on the kinetic hero: the global section rule is !important. */
    padding-block-end: clamp(40px, 8vw, 96px) !important;
    overflow: hidden;
    background: var(--es-canvas);
    text-align: center;
}

.esAur__light {
    position: absolute;
    z-index: 0;
    border-radius: 50%;
    pointer-events: none;
    filter: blur(60px);
    mix-blend-mode: screen;
}

.esAur__light--a {
    top: -20vmax;
    left: -10vmax;
    width: 60vmax;
    height: 60vmax;
    background: radial-gradient(circle, color-mix(in srgb, var(--es-accent) 70%, transparent), transparent 66%);
    animation: esAurA 28s ease-in-out infinite alternate;
}

.esAur__light--b {
    right: -16vmax;
    bottom: -24vmax;
    width: 56vmax;
    height: 56vmax;
    background: radial-gradient(circle, color-mix(in srgb, var(--es-accent-2, var(--es-accent)) 62%, transparent), transparent 66%);
    animation: esAurB 37s ease-in-out infinite alternate;
}

.esAur__light--c {
    top: 22%;
    left: 40%;
    width: 42vmax;
    height: 42vmax;
    background: radial-gradient(circle, color-mix(in srgb, var(--es-accent) 40%, #ffffff), transparent 62%);
    opacity: .5;
    animation: esAurC 46s linear infinite;
}

@keyframes esAurA {
    from { transform: translate3d(0, 0, 0) scale(1); }
    to   { transform: translate3d(12%, 10%, 0) scale(1.2); }
}

@keyframes esAurB {
    from { transform: translate3d(0, 0, 0) scale(1.1); }
    to   { transform: translate3d(-10%, -8%, 0) scale(1); }
}

@keyframes esAurC {
    from { transform: rotate(0deg) translateX(4%) rotate(0deg); }
    to   { transform: rotate(360deg) translateX(4%) rotate(-360deg); }
}

/* Grain over the lot: without it a big soft gradient reads as a screensaver. With it, it reads
   as a printed surface that happens to be moving. */
.esAur__grain {
    position: absolute;
    inset: 0;
    z-index: 1;
    pointer-events: none;
    opacity: .16;
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='120' height='120'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2'/></filter><rect width='120' height='120' filter='url(%23n)' opacity='.5'/></svg>");
    animation: esAurGrain 1.2s steps(3) infinite;
}

@keyframes esAurGrain {
    0%   { transform: translate3d(0, 0, 0); }
    33%  { transform: translate3d(-2%, 1%, 0); }
    66%  { transform: translate3d(1%, -2%, 0); }
    100% { transform: translate3d(0, 0, 0); }
}

.esHero--aurora .esHero__in {
    position: relative;
    z-index: 2;
    width: 100%;
    max-width: 900px;
    margin: 0 auto;
}

.esHero--aurora .esHero__title,
.esHero--aurora .esHero__sub { margin-inline: auto; }

.esHero--aurora .esHero__meta,
.esHero--aurora .esHero__acts { justify-content: center; }

/* The same light moves through the letters, so the headline belongs to the background rather
   than sitting on top of it. */
/* Single text node by construction — see the note on the holo heading rule above. */
.esAur__title {
    background: linear-gradient(100deg,
        var(--es-ink) 0%,
        color-mix(in srgb, var(--es-accent) 70%, var(--es-ink)) 35%,
        color-mix(in srgb, var(--es-accent-2, var(--es-accent)) 75%, var(--es-ink)) 65%,
        var(--es-ink) 100%);
    background-size: 260% 100%;
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    animation: esSheen 11s ease-in-out infinite;
}

/* ---------- how much of it runs ----------
   full   : as written above.
   subtle : sections still arrive with movement; nothing loops behind text being read.
   none   : a still page. The script reveals everything at once and this stops the rest. */

.es[data-site-motion="subtle"] :is(
    .esKin__grid, .esKin__blob, .esKin__cycleTrack, .esAur__light, .esAur__grain,
    .esMarquee__track, .esPulse span, .esCursorGlow, .esAur__title, .esHero__more) {
    animation: none !important;
}

.es[data-site-motion="subtle"]::before,
.es[data-site-motion="subtle"]::after {
    animation: none !important;
}

.es[data-site-motion="subtle"] :is(.esHero__title, .esSec__title, .esBtn--solid) {
    animation: none !important;
}

/* A word that never arrives is a word nobody reads: with the entrance animation off, the kinetic
   headline has to be visible as it stands. */
.es[data-site-motion="subtle"] .esKin__word,
.es[data-site-motion="none"] .esKin__word {
    animation: none !important;
    opacity: 1;
    transform: none;
    filter: none;
}

.es[data-site-motion="none"] *,
.es[data-site-motion="none"]::before,
.es[data-site-motion="none"]::after {
    animation: none !important;
    transition: none !important;
}

/* The still page shows everything, whatever the script did or did not get to.
   Without this, an element still holding the pre-reveal state stays at opacity 0 for ever: the
   reveal that would have cleared it is exactly what "none" switched off. Same principle as the
   .es-animate guard above — content must never depend on an animation having run. */
.es[data-site-motion="none"] .es-reveal {
    opacity: 1 !important;
    transform: none !important;
}

@media (prefers-reduced-motion: reduce) {
    .es *,
    .es::before,
    .es::after {
        animation: none !important;
        transition: none !important;
    }

    .esKin__word { opacity: 1; transform: none; filter: none; }
}

.es[data-site-theme="neon"] .esStat__num,
.es[data-site-theme="neon"] .esCd__box span {
    color: var(--es-accent-2);
}

/* Mono has no colour to animate, so its motion is structural: rules that draw themselves. */
.es[data-site-theme="mono"] .esSec__title {
    position: relative;
    padding-bottom: 12px;
}

.es[data-site-theme="mono"] .esSec__title::after {
    content: "";
    position: absolute;
    left: 0;
    bottom: 0;
    height: 3px;
    width: 0;
    background: var(--es-accent);
    transition: width .8s cubic-bezier(.22, 1, .36, 1);
}

.es[data-site-theme="mono"] .es-reveal.is-in .esSec__title::after,
.es[data-site-theme="mono"] .esSec__head:hover .esSec__title::after {
    width: 64px;
}

/* ---------- hover, everywhere it means something ---------- */

.esSession {
    position: relative;
    transition: background-color .25s ease, transform .25s cubic-bezier(.22, 1, .36, 1);
}

/* A line that grows out of the left edge: the row is being pointed at, not lifted off the page. */
.esSession::before {
    content: "";
    position: absolute;
    left: -14px;
    top: 14px;
    bottom: 14px;
    width: 2px;
    background: var(--es-accent);
    transform: scaleY(0);
    transform-origin: top;
    transition: transform .3s cubic-bezier(.22, 1, .36, 1);
}

.esSession:hover::before { transform: scaleY(1); }
.esSession:hover { transform: translateX(4px); }
.esSession:hover .esSession__title .esOpen { color: var(--es-accent); }

.esFaq__item {
    transition: border-color .25s ease, transform .25s cubic-bezier(.22, 1, .36, 1);
}

.esFaq__item:hover {
    border-color: var(--es-accent);
    transform: translateY(-2px);
}

.esStat {
    transition: transform .3s cubic-bezier(.22, 1, .36, 1);
}

.esStat:hover { transform: translateY(-4px); }

.esDay__mark {
    transition: transform .3s cubic-bezier(.22, 1, .36, 1), box-shadow .3s ease;
}

.esDay:hover .esDay__mark {
    transform: translateY(-3px);
    box-shadow: var(--es-glow, 0 12px 24px -14px rgba(10, 14, 24, .5));
}

.esShot {
    overflow: hidden;
    border-radius: var(--es-radius);
}

.esLogo,
.es .spFooter__logo {
    transition: transform .25s cubic-bezier(.22, 1, .36, 1);
}

.es .spFooter__logo:hover { transform: translateY(-3px); }

/* The bar's links get an underline that grows from the middle rather than a background swap. */
.esBar__link {
    position: relative;
}

.esBar__link::after {
    content: "";
    position: absolute;
    left: 50%;
    right: 50%;
    bottom: 4px;
    height: 2px;
    background: var(--es-accent);
    transition: left .25s ease, right .25s ease;
}

.esBar__link:hover::after { left: 12px; right: 12px; }

@media (prefers-reduced-motion: reduce) {
    .es[data-site-theme="midnight"]::before,
    .es[data-site-theme="aurora"]::before,
    .es[data-site-theme="neon"]::before,
    .es[data-site-theme="sunrise"]::before {
        animation: none;
    }

    .esSession,
    .esSession::before,
    .esFaq__item,
    .esStat,
    .esDay__mark,
    .esBar__link::after,
    .es .spFooter__logo {
        transition: none;
    }

    .esSession:hover,
    .esStat:hover,
    .esFaq__item:hover,
    .esDay:hover .esDay__mark,
    .es .spFooter__logo:hover {
        transform: none;
    }
}

/* ---------- the five later page shapes ----------
   Each one is a different answer to "what does a reader see first", not a restyled version of the
   same band: colour, type, a physical object, a moving band, or nothing but the facts. */

/* canvas — the colour is the artwork, and it moves */
.esHero--canvas {
    position: relative;
    display: flex;
    align-items: center;
    min-height: min(86vh, 720px);
    padding: clamp(40px, 7vw, 100px) 20px;
    overflow: hidden;
    background: var(--es-canvas);
}

.esHero--canvas .esHero__in {
    position: relative;
    z-index: 1;
    width: 100%;
    max-width: 1180px;
    margin: 0 auto;
}

.esHero__wash {
    position: absolute;
    inset: -25%;
    filter: blur(46px);
}

.esHero__wash span {
    position: absolute;
    width: 46vmax;
    height: 46vmax;
    border-radius: 50%;
    opacity: .55;
    will-change: transform;
}

/* Three blobs, three different periods: the arrangement they make never quite repeats. */
.esHero__wash span:nth-child(1) {
    left: 2%;
    top: 4%;
    background: var(--es-accent);
    animation: esWashA 26s ease-in-out infinite alternate;
}

.esHero__wash span:nth-child(2) {
    right: 4%;
    top: 22%;
    background: var(--es-accent-2, var(--es-accent));
    opacity: .4;
    animation: esWashB 37s ease-in-out infinite alternate;
}

.esHero__wash span:nth-child(3) {
    left: 34%;
    bottom: -12%;
    background: color-mix(in srgb, var(--es-accent) 55%, var(--es-ink));
    opacity: .3;
    animation: esWashC 45s ease-in-out infinite alternate;
}

@keyframes esWashA {
    from { transform: translate3d(0, 0, 0) scale(1); }
    to   { transform: translate3d(14%, 8%, 0) scale(1.18); }
}

@keyframes esWashB {
    from { transform: translate3d(0, 0, 0) scale(1.1); }
    to   { transform: translate3d(-12%, 12%, 0) scale(.92); }
}

@keyframes esWashC {
    from { transform: translate3d(0, 0, 0); }
    to   { transform: translate3d(9%, -14%, 0); }
}

/* editorial — the headline is the design */
.esHero--editorial {
    padding: clamp(40px, 6vw, 96px) 20px;
    border-bottom: 2px solid var(--es-ink);
}

.esHero--editorial .esHero__in { max-width: 1180px; margin: 0 auto; }

.esHero--editorial .esHero__title {
    font-size: clamp(2.4rem, 1rem + 6.4vw, 6rem);
    max-width: 16ch;
}

.esHero--editorial .esHero__rule {
    height: 1px;
    margin: clamp(20px, 3vw, 40px) 0;
    background: var(--es-line);
}

.esHero--editorial .esHero__columns {
    display: grid;
    grid-template-columns: 1.3fr .7fr;
    gap: clamp(20px, 4vw, 56px);
    align-items: start;
}

/* The opening line is set like a standfirst, because that is what it is. */
.esHero--editorial .esHero__sub {
    margin-top: 0 !important;
    font-size: clamp(1.05rem, 1rem + .5vw, 1.4rem);
    color: var(--es-ink);
    max-width: 46ch;
}

.esHero--editorial .esHero__cut {
    width: 100%;
    height: auto;
    border-radius: var(--es-radius);
    margin-bottom: 18px;
}

/* ticket — the pass itself, stub and all */
.esHero--ticket {
    padding: clamp(34px, 6vw, 80px) 20px;
    background-image: var(--es-hero, none);
    background-size: cover;
    background-position: center;
    background-color: var(--es-canvas);
}

.esHero__pass {
    display: grid;
    grid-template-columns: 1fr auto;
    max-width: 1080px;
    margin: 0 auto;
    border-radius: var(--es-radius);
    background: var(--es-paper);
    box-shadow: 0 30px 60px -30px rgba(10, 14, 24, .45);
    overflow: hidden;
    transition: transform .4s cubic-bezier(.22, 1, .36, 1), box-shadow .4s ease;
}

.esHero__pass:hover {
    transform: translateY(-4px);
    box-shadow: 0 40px 70px -30px rgba(10, 14, 24, .5);
}

.esHero__passMain { padding: clamp(24px, 4vw, 56px); }

/* The stub is torn off, so the join is a real perforation rather than a border. */
.esHero__stub {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 4px;
    min-width: 160px;
    padding: clamp(24px, 4vw, 56px) 26px;
    background: var(--es-accent-soft);
    color: var(--es-accent);
    border-left: 2px dashed color-mix(in srgb, var(--es-accent) 45%, transparent);
}

.esHero__stub::before,
.esHero__stub::after {
    content: "";
    position: absolute;
    left: -11px;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    background: var(--es-canvas);
}

.esHero__stub::before { top: -10px; }
.esHero__stub::after { bottom: -10px; }

.esHero__stubDay {
    font-family: var(--es-display);
    font-size: clamp(2.4rem, 1.4rem + 3vw, 4rem);
    font-weight: var(--es-display-weight);
    line-height: 1;
}

.esHero__stubMon { font-size: .95rem; font-weight: 600; }

.esHero__stubWord {
    margin-top: 10px;
    font-size: .7rem;
    letter-spacing: .18em;
    opacity: .75;
}

/* marquee — the name runs, the facts stand still */
.esHero--marquee { padding-bottom: clamp(34px, 5vw, 72px); }

.esMarquee {
    padding: 14px 0;
    background: var(--es-accent);
    color: var(--es-accent-ink);
    overflow: hidden;
    /* A scrolling band is decoration: keeping it out of the reading order means a screen reader
       gets the h1 once, not the headline four times. */
    user-select: none;
}

.esMarquee__track {
    display: inline-flex;
    align-items: center;
    gap: 26px;
    white-space: nowrap;
    font-family: var(--es-display);
    font-weight: var(--es-display-weight);
    font-size: clamp(1.4rem, 1rem + 2vw, 2.6rem);
    letter-spacing: var(--es-tracking);
    animation: esMarquee 26s linear infinite;
}

.esMarquee:hover .esMarquee__track { animation-play-state: paused; }

.esMarquee__dot { opacity: .55; }

@keyframes esMarquee {
    from { transform: translateX(0); }
    /* Half, because the headline is repeated twice over: the reset lands mid-sequence and reads
       as continuous. */
    to   { transform: translateX(-50%); }
}

.esHero--marquee .esHero__in {
    max-width: 1180px;
    margin: 0 auto;
    padding: clamp(28px, 4vw, 60px) 20px 0;
}

/* The h1 stays for search engines and screen readers; the band above is what people see. */
.esHero__title--sr {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* focus — one screen, centred, and a hint that there is more */
.esHero--focus {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 100vh;
    padding: clamp(40px, 8vw, 110px) 20px;
    text-align: center;
    background-image: var(--es-hero, none);
    background-size: cover;
    background-position: center;
    background-color: var(--es-canvas);
}

.esHero--focus .esHero__scrim {
    position: absolute;
    inset: 0;
    background: color-mix(in srgb, var(--es-paper) 78%, transparent);
}

.esHero--focus .esHero__in {
    position: relative;
    z-index: 1;
    max-width: 760px;
}

.esHero--focus .esHero__mark { max-height: 62px; margin-bottom: 24px; }
.esHero--focus .esHero__title { margin-inline: auto; }
.esHero--focus .esHero__sub { margin-inline: auto; }
.esHero--focus .esHero__meta,
.esHero--focus .esHero__acts { justify-content: center; }

.esHero__more {
    position: absolute;
    left: 50%;
    bottom: 26px;
    transform: translateX(-50%);
    color: var(--es-soft-ink);
    font-size: 1.4rem;
    animation: esNudge 2.4s ease-in-out infinite;
}

@keyframes esNudge {
    0%, 100% { transform: translate(-50%, 0); opacity: .5; }
    50%      { transform: translate(-50%, 8px); opacity: 1; }
}

@media (max-width: 860px) {
    .esHero--editorial .esHero__columns { grid-template-columns: 1fr; }
    .esHero__pass { grid-template-columns: 1fr; }

    .esHero__stub {
        flex-direction: row;
        gap: 12px;
        border-left: 0;
        border-top: 2px dashed color-mix(in srgb, var(--es-accent) 45%, transparent);
    }

    .esHero__stub::before { top: -10px; left: -10px; }
    .esHero__stub::after { bottom: auto; top: -10px; left: auto; right: -10px; }
    .esHero__stubWord { margin-top: 0; }
}

@media (prefers-reduced-motion: reduce) {
    .esHero__wash span,
    .esMarquee__track,
    .esHero__more {
        animation: none;
    }

    .esHero__pass { transition: none; }
    .esHero__pass:hover { transform: none; }
}

/* Sample copy standing in for something the client has not written. Legible, obviously
   provisional, and impossible to mistake for their own words - it only ever appears in the
   preview pane. */
.es-sample {
    opacity: .62;
    font-style: italic;
}

/* ---------- countdown: the sentence around the numbers ---------- */

.esCd {
    flex-direction: column;
    align-items: center;
    gap: 10px;
    text-align: center;
}

.esCd__lead {
    margin: 0;
    font-size: .95rem;
    font-weight: 600;
    color: var(--es-soft-ink);
}

.esCd__foot {
    margin: 2px 0 0;
    font-size: .88rem;
    color: var(--es-soft-ink);
}

.esCd__foot a {
    color: var(--es-accent);
    font-weight: 600;
    text-decoration: none;
    border-bottom: 1px solid color-mix(in srgb, var(--es-accent) 40%, transparent);
    transition: border-color .2s ease;
}

.esCd__foot a:hover { border-color: var(--es-accent); }

/* ---------- signature motion for Canvas, Editorial and Focus ----------

   Three different ideas rather than the same drift three times, and each one built so it never
   settles into an obvious loop: periods that do not divide into each other, and delays that put
   the elements permanently out of step. Everything is transform/opacity on a compositor layer,
   nothing measures layout, and the whole block is switched off under prefers-reduced-motion. */

/* Grain: a still texture over flat colour, so a page with no photograph does not read as a slab
   of paint. Cheap - one repeating SVG, no animation. */
.esGrain {
    position: absolute;
    inset: 0;
    pointer-events: none;
    opacity: .32;
    mix-blend-mode: overlay;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='.5'/%3E%3C/svg%3E");
}

/* A light that follows the pointer. It sits under the words and never takes a click. */
.esCursorGlow {
    position: absolute;
    inset: 0;
    pointer-events: none;
    background: radial-gradient(26vmax 26vmax at var(--es-mx, 50%) var(--es-my, 40%),
                color-mix(in srgb, var(--es-accent) 30%, transparent), transparent 70%);
    opacity: .8;
    /* Trails the pointer rather than sticking to it: instant tracking reads as a bug. */
    transition: background-position .2s ease;
}

/* canvas — a ring turning behind the headline, off-centre so it never looks like a loading spinner */
.esRing {
    position: absolute;
    top: 50%;
    left: 50%;
    width: min(78vmin, 720px);
    height: min(78vmin, 720px);
    margin: calc(min(78vmin, 720px) / -2) 0 0 calc(min(78vmin, 720px) / -2);
    pointer-events: none;
    border-radius: 50%;
    border: 1px solid color-mix(in srgb, var(--es-ink) 16%, transparent);
    /* The conic sweep is what actually reads as movement; the ring is just its edge. */
    background: conic-gradient(from 0deg,
                transparent 0 62%,
                color-mix(in srgb, var(--es-accent) 26%, transparent) 78%,
                transparent 92%);
    mask: radial-gradient(circle, transparent 61%, #000 62%);
    animation: esSpin 42s linear infinite;
}

.esHero--canvas .esHero__in { mix-blend-mode: normal; }

@keyframes esSpin {
    to { transform: rotate(360deg); }
}

/* editorial — the rule draws itself in, and the picture creeps forward over a minute */
.esHero--editorial { position: relative; overflow: hidden; }

.esHero--editorial .esHero__rule {
    transform-origin: left;
    animation: esRule 1.4s cubic-bezier(.22, 1, .36, 1) .2s both;
}

@keyframes esRule {
    from { transform: scaleX(0); }
    to   { transform: scaleX(1); }
}

.esHero--editorial .esHero__cut {
    /* Ken Burns, slowed right down: at 64 seconds a reader never catches it moving, they only
       notice the page is not static. */
    animation: esCreep 64s ease-in-out infinite alternate;
}

@keyframes esCreep {
    from { transform: scale(1) translate3d(0, 0, 0); }
    to   { transform: scale(1.07) translate3d(-1.5%, -1%, 0); }
}

/* The kicker gets a marching underline, the one loud thing on an otherwise quiet layout. */
.esHero--editorial .esHero__kicker {
    position: relative;
    display: inline-block;
    padding-bottom: 6px;
}

.esHero--editorial .esHero__kicker::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 2px;
    background: repeating-linear-gradient(90deg,
                var(--es-accent) 0 10px, transparent 10px 20px);
    background-size: 20px 2px;
    animation: esMarch 3.5s linear infinite;
}

@keyframes esMarch {
    to { background-position: 20px 0; }
}

/* focus — rings going out from the middle, three periods that never line up */
.esPulse {
    position: absolute;
    top: 50%;
    left: 50%;
    pointer-events: none;
}

.esPulse span {
    position: absolute;
    top: 0;
    left: 0;
    width: 42vmin;
    height: 42vmin;
    margin: -21vmin 0 0 -21vmin;
    border-radius: 50%;
    border: 1px solid color-mix(in srgb, var(--es-accent) 34%, transparent);
    opacity: 0;
    animation: esPulseOut 9s ease-out infinite;
}

/* Not thirds: a third of the period would put all three in step every cycle. */
.esPulse span:nth-child(2) { animation-duration: 11s; animation-delay: 2.6s; }
.esPulse span:nth-child(3) { animation-duration: 14s; animation-delay: 5.1s; }

@keyframes esPulseOut {
    0%   { transform: scale(.35); opacity: 0; }
    12%  { opacity: .55; }
    100% { transform: scale(2.4); opacity: 0; }
}

.esHero--focus .esHero__in,
.esHero--canvas .esHero__in,
.esHero--editorial .esHero__in {
    position: relative;
    z-index: 2;
}

@media (prefers-reduced-motion: reduce) {
    .esRing,
    .esPulse span,
    .esHero--editorial .esHero__cut,
    .esHero--editorial .esHero__rule,
    .esHero--editorial .esHero__kicker::after {
        animation: none;
    }

    .esHero--editorial .esHero__rule { transform: none; }
    .esCursorGlow { display: none; }
}

/* ---------- agenda rows on the site: faces and the booking button ----------
   The programme is where a reader decides to come, so the row carries what that decision needs:
   who is on, and the way in. Matching the public agenda page rather than inventing a second
   arrangement of the same information. */

.esSession__head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 8px 10px;
}

.esSession__dur {
    display: block;
    font-weight: 400;
    font-size: .78rem;
    color: var(--es-soft-ink);
    font-variant-numeric: tabular-nums;
}

.esSession__people {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: 10px;
}

.esSession__acts {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: 12px;
}

/* A row button, not a hero one: present enough to press, quiet enough that a day of them does not
   turn the programme into a wall of colour. */
.esBtn--sm {
    min-height: 40px;
    padding: 9px 16px;
    font-size: .85rem;
}

.esBtn--sm i { font-size: 1rem; }

@media (max-width: 560px) {
    .esSession__acts .esBtn--sm { flex: 1 1 auto; justify-content: center; }
}

/* ============================================================================================
   FIVE MORE PAGE SHAPES — deck, rail, cinema, itinerary, dossier — and one more look,
   liquid glass. Same two axes as the rest: the shape decides the structure, the look decides
   the colour, and neither refers to the other.

   Every hero here sets its own padding-block-end with !important, because the global
   `section { padding-bottom: 0 !important }` higher up this file strips it otherwise.
   ============================================================================================ */

/* Every hero that carries a logo has to say how big it is, or the file's own size wins - a
   2000px mark makes a hero three screens tall. */
.esHero--deck .esHero__mark,
.esHero--cinema .esHero__mark,
.esHero--dossier .esHero__mark {
    max-height: 56px;
    width: auto;
    margin-bottom: 22px;
}

/* ---------- Deck: the page deals itself out ---------- */

.esHero--deck {
    position: sticky;
    top: 0;
    display: flex;
    align-items: flex-end;
    min-height: 100vh;
    padding: clamp(40px, 8vw, 96px) 20px;
    padding-block-end: clamp(40px, 8vw, 96px) !important;
    overflow: hidden;
    background-color: var(--es-ink);
}

/* Same bed as the spotlight hero: a blurred copy to fill the frame, the real picture held
   inside it. A poster or a logo is never cropped to fit. */
.esHero--deck::before,
.esHero--deck::after,
.esHero--rail::before,
.esHero--rail::after {
    content: "";
    position: absolute;
    inset: 0;
    background-image: var(--es-hero, none);
    background-position: center;
    background-repeat: no-repeat;
}

.esHero--deck::before,
.esHero--rail::before {
    background-size: cover;
    transform: scale(1.15);
    filter: blur(26px) saturate(1.15) brightness(.72);
}

.esHero--deck::after,
.esHero--rail::after { background-size: contain; }

.esHero--deck .esHero__scrim,
.esHero--rail .esHero__scrim {
    position: absolute;
    inset: 0;
    z-index: 1;
    background: linear-gradient(180deg, rgba(6, 10, 20, .22) 0%, rgba(6, 10, 20, .84) 100%);
}

.esHero--deck .esHero__title,
.esHero--rail .esHero__title { color: #fff; }

.esHero--deck .esHero__kicker,
.esHero--rail .esHero__kicker { color: #fff; opacity: .9; }

.esHero--deck :is(.esHero__sub, .esHero__meta),
.esHero--rail :is(.esHero__sub, .esHero__meta) { color: rgba(255, 255, 255, .86); }

/* The second button inherits its colour, and on a dark bed that leaves it unreadable. */
.esHero--deck .esBtn--ghost,
.esHero--rail .esBtn--ghost,
.esHero--cinema .esBtn--ghost {
    color: #fff;
    border-color: rgba(255, 255, 255, .55);
}

.esHero--deck .esHero__in {
    position: relative;
    z-index: 1;
    width: 100%;
    max-width: 1180px;
    margin: 0 auto;
}

/* Each section parks under the one before it, a little lower, so the edges of everything
   already read stay visible down the top of the window - the deck is the navigation. */
.es--deck main > .esSec {
    position: sticky;
    /* Under the bar, not behind it: the bar is sticky too, and a card parked at 0 slides its
       heading straight under the navigation. */
    top: calc(var(--esBarH, 70px) + var(--esIdx, 0) * 10px);
    background: var(--es-paper);
    border-top: 1px solid var(--es-line);
    border-radius: clamp(18px, 2vw, 28px) clamp(18px, 2vw, 28px) 0 0;
    box-shadow: 0 -24px 60px -40px rgba(8, 12, 30, .55);
}

/* A card sliding over another needs an edge, or the two papers read as one long page. */
.es--deck main > .esSec::before {
    content: "";
    position: absolute;
    top: 0;
    left: 50%;
    transform: translateX(-50%);
    width: 54px;
    height: 4px;
    margin-top: 10px;
    border-radius: 99px;
    background: var(--es-line);
}

.es--deck main > .esSec:last-child {
    position: static;
    box-shadow: none;
}

/* ---------- Rail: rows you grab and throw ---------- */

.esHero--rail {
    position: relative;
    display: flex;
    align-items: center;
    min-height: 100vh;
    padding: clamp(40px, 8vw, 96px) 20px;
    padding-block-end: clamp(40px, 8vw, 96px) !important;
    overflow: hidden;
    background-color: var(--es-ink);
}

.esHero--rail .esHero__in {
    position: relative;
    z-index: 1;
    width: 100%;
    max-width: 1180px;
    margin: 0 auto;
    padding-left: clamp(16px, 3vw, 34px);
    border-left: 2px solid var(--es-accent);
}

.esRail__hint {
    position: absolute;
    left: 50%;
    bottom: clamp(18px, 4vw, 40px);
    z-index: 1;
    display: flex;
    align-items: center;
    gap: 12px;
    transform: translateX(-50%);
    color: rgba(255, 255, 255, .82);
    font-size: .82rem;
}

.esRail__hintLine {
    width: clamp(40px, 12vw, 120px);
    height: 1px;
    background: currentColor;
    opacity: .5;
}

.es-animate .esRail__hintLine {
    transform-origin: left center;
    animation: esRailHint 3.6s ease-in-out infinite;
}

@keyframes esRailHint {
    0%, 100% { transform: scaleX(.35); opacity: .35; }
    50%      { transform: scaleX(1); opacity: .8; }
}

/* The row itself. The window clips, the track moves, and the arrows sit over both. */
/* min-width: 0 on both, or the window takes the width of the content it is meant to clip -
   a grid or flex child's automatic minimum is its content, and nothing then scrolls. */
.esRail {
    position: relative;
    min-width: 0;
    max-width: 100%;
}

.esRail__win {
    overflow: hidden;
    min-width: 0;
    max-width: 100%;
}

.es--rail .esDay > * { min-width: 0; }

.esRail__track {
    display: flex !important;
    grid-template-columns: none !important;
    width: max-content;
    max-width: none;
    align-items: stretch;
    gap: clamp(14px, 2vw, 26px);
    cursor: grab;
    touch-action: pan-y;
    will-change: transform;
}

.esRail.is-grabbed .esRail__track { cursor: grabbing; }

.esRail__track:focus-visible {
    outline: 2px solid var(--es-accent);
    outline-offset: 4px;
    border-radius: 12px;
}

/* Cards in a row need a width of their own: a grid child sized by its column has none here. */
.esRail__track > * { flex: 0 0 auto; }
.esFaces.esRail__track > * { width: clamp(180px, 42vw, 220px); }
.esGallery.esRail__track > * { width: clamp(220px, 60vw, 320px); }
.esDay__list.esRail__track > * { width: min(78vw, 420px); }

/* The arrows sit above the row, not over it: a control on top of a card covers the thing it is
   meant to help you read, and on a phone it lands exactly where a thumb already is. */
.esRail { padding-top: 52px; }

.esRail__arrow {
    position: absolute;
    top: 0;
    z-index: 2;
    display: grid;
    place-items: center;
    width: 44px;
    height: 44px;
    border: 1px solid var(--es-line);
    border-radius: 50%;
    background: color-mix(in srgb, var(--es-paper) 88%, transparent);
    backdrop-filter: blur(10px);
    color: var(--es-ink);
    font-size: 1.2rem;
    cursor: pointer;
    transition: transform 140ms ease-out, opacity 140ms ease-out;
}

.esRail__arrow:active { transform: scale(.94); }
.esRail__arrow--prev { right: 52px; }
.esRail__arrow--next { right: 0; }

.esRail.is-start .esRail__arrow--prev,
.esRail.is-end .esRail__arrow--next {
    opacity: .28;
    pointer-events: none;
}

/* On the rail shape the sessions lose their two-column layout: a card in a row is read as a
   card, not as a line in a table. */
.es--rail .esDay {
    grid-template-columns: 1fr;
    gap: 0;
}

/* The date reads as the row's heading rather than a block of its own. */
.es--rail .esDay__mark {
    display: flex;
    align-items: baseline;
    gap: 8px;
    padding: 0;
    background: none;
    text-align: left;
}

.es--rail .esDay__num { font-size: 1.5rem; }
.es--rail .esDay__list.esRail__track > * {
    padding: 18px;
    border: 1px solid var(--es-line);
    border-radius: var(--es-radius);
    background: var(--es-paper);
}

/* ---------- Cinema: letterboxed, and the picture keeps moving ---------- */

.esHero--cinema {
    position: relative;
    display: flex;
    align-items: flex-end;
    min-height: min(92vh, 780px);
    padding: clamp(64px, 12vw, 132px) 20px clamp(96px, 14vw, 150px);
    padding-block-end: clamp(96px, 14vw, 150px) !important;
    overflow: hidden;
    background: #05060a;
    color: #f6f7fb;
}

.esCine__shot {
    position: absolute;
    inset: -6%;
    background-image: var(--es-hero, none);
    background-size: cover;
    background-position: center;
    background-color: #0b0d14;
}

/* No photograph supplied: a lit ground rather than a black rectangle. */
.esHero--cinema:not([style*="--es-hero"]) .esCine__shot {
    background-image:
        radial-gradient(60% 70% at 25% 30%, color-mix(in srgb, var(--es-accent) 65%, transparent), transparent 70%),
        radial-gradient(50% 60% at 80% 70%, color-mix(in srgb, var(--es-accent) 35%, transparent), transparent 70%);
}

.es-animate .esCine__shot { animation: esKenBurns 46s ease-in-out infinite alternate; }

@keyframes esKenBurns {
    from { transform: scale(1) translate3d(0, 0, 0); }
    to   { transform: scale(1.12) translate3d(-2%, -1.5%, 0); }
}

.esHero--cinema .esHero__scrim {
    position: absolute;
    inset: 0;
    background: linear-gradient(180deg, rgba(5, 6, 10, .55) 0%, rgba(5, 6, 10, .2) 38%, rgba(5, 6, 10, .88) 100%);
}

.esCine__bar {
    position: absolute;
    left: 0;
    right: 0;
    z-index: 3;
    height: clamp(26px, 6vh, 58px);
    background: #05060a;
}

.esCine__bar--top { top: 0; }
.esCine__bar--bottom { bottom: 0; }

.esHero--cinema .esHero__in {
    position: relative;
    z-index: 2;
    width: 100%;
    max-width: 1180px;
    margin: 0 auto;
    text-align: left;
}

.esHero--cinema .esHero__title {
    color: #fff;
    text-shadow: 0 2px 30px rgba(0, 0, 0, .5);
}

.esHero--cinema .esHero__sub { color: rgba(246, 247, 251, .82); }

/* The slate line. Tabular figures because it is read as a number, not as words. */
.esCine__strip {
    position: absolute;
    left: 0;
    right: 0;
    bottom: clamp(26px, 6vh, 58px);
    z-index: 3;
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 6px 22px;
    padding: 12px clamp(20px, 5vw, 60px);
    border-top: 1px solid rgba(255, 255, 255, .16);
    background: rgba(5, 6, 10, .55);
    backdrop-filter: blur(8px);
    color: rgba(246, 247, 251, .9);
    font-size: .84rem;
}

.esCine__code {
    font-variant-numeric: tabular-nums;
    letter-spacing: .06em;
    color: var(--es-accent);
}

.esCine__where { color: #fff; font-weight: 600; }

/* ---------- Itinerary: the event as a journey ---------- */

.esHero--itin {
    padding: clamp(48px, 9vw, 110px) 20px;
    padding-block-end: clamp(48px, 9vw, 110px) !important;
    background:
        radial-gradient(80% 60% at 50% 0%, color-mix(in srgb, var(--es-accent) 12%, transparent), transparent 70%),
        var(--es-canvas);
}

.esHero--itin .esHero__in {
    width: 100%;
    max-width: 980px;
    margin: 0 auto;
    text-align: center;
}

.esItin__pass {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    gap: clamp(12px, 3vw, 30px);
    margin: clamp(22px, 4vw, 40px) 0;
    padding: clamp(20px, 3vw, 32px);
    border: 1px solid var(--es-line);
    border-radius: var(--es-radius);
    background: var(--es-paper);
    box-shadow: 0 30px 60px -48px rgba(8, 12, 30, .8);
    text-align: left;
}

.esItin__leg { display: grid; gap: 2px; }
.esItin__leg--to { text-align: right; }

.esItin__legLabel {
    font-size: .74rem;
    letter-spacing: .08em;
    color: var(--es-soft-ink);
}

.esItin__legBig {
    font-family: var(--es-display);
    font-weight: var(--es-display-weight);
    font-size: clamp(1.3rem, 1rem + 1.4vw, 2rem);
    line-height: 1.1;
    letter-spacing: var(--es-tracking);
}

.esItin__legSmall { font-size: .86rem; color: var(--es-soft-ink); }

.esItin__route {
    display: flex;
    align-items: center;
    gap: 8px;
    color: var(--es-accent);
    font-size: 1.15rem;
}

.esItin__dots {
    width: clamp(24px, 8vw, 90px);
    height: 2px;
    background: repeating-linear-gradient(90deg, currentColor 0 6px, transparent 6px 12px);
    opacity: .6;
}

.es-animate .esItin__route i { animation: esItinFly 7s ease-in-out infinite; }

@keyframes esItinFly {
    0%, 100% { transform: translateX(-4px); }
    50%      { transform: translateX(4px); }
}

/* The gate spans the whole pass under the two legs: it is where the journey ends up. */
.esItin__gate {
    grid-column: 1 / -1;
    display: grid;
    gap: 2px;
    padding-top: 16px;
    border-top: 1px dashed var(--es-line);
}

.esItin__gateName {
    font-weight: 600;
    font-size: 1.05rem;
}

/* The route carries on down the programme: a dotted line with a stop at every day. */
.es--itinerary .esSec--agenda .esDay {
    position: relative;
    padding-left: 34px;
    border-top: 0;
}

.es--itinerary .esSec--agenda .esDay::before {
    content: "";
    position: absolute;
    left: 10px;
    top: 0;
    bottom: 0;
    width: 2px;
    background: repeating-linear-gradient(180deg, var(--es-line) 0 6px, transparent 6px 12px);
}

.es--itinerary .esSec--agenda .esDay::after {
    content: "";
    position: absolute;
    left: 4px;
    top: 34px;
    width: 14px;
    height: 14px;
    border: 3px solid var(--es-accent);
    border-radius: 50%;
    background: var(--es-paper);
}

.es--itinerary .esSec--agenda .esDay:last-child::before { bottom: 50%; }

/* ---------- Dossier: the page opens as its own contents ---------- */

.esHero--dossier {
    padding: clamp(56px, 10vw, 128px) 20px;
    padding-block-end: clamp(48px, 8vw, 96px) !important;
    background: var(--es-paper);
}

.esHero--dossier .esHero__in {
    width: 100%;
    max-width: 880px;
    margin: 0 auto;
}

.esDos__head { padding-bottom: clamp(24px, 4vw, 44px); }

.esDos__index {
    margin: 0;
    padding: 0;
    list-style: none;
    border-top: 1px solid var(--es-ink);
}

.esDos__line { border-bottom: 1px solid var(--es-line); }

.esDos__go {
    display: grid;
    grid-template-columns: auto auto 1fr auto;
    align-items: baseline;
    gap: 14px;
    width: 100%;
    padding: 16px 2px;
    border: 0;
    background: none;
    color: inherit;
    font: inherit;
    text-align: left;
    cursor: pointer;
}

.esDos__num {
    color: var(--es-accent);
    font-variant-numeric: tabular-nums;
    font-size: .8rem;
}

.esDos__name {
    font-family: var(--es-display);
    font-weight: var(--es-display-weight);
    font-size: clamp(1.1rem, .95rem + .9vw, 1.7rem);
    letter-spacing: var(--es-tracking);
}

/* The rule between the title and the note is the hover: it fills toward the note, which is
   where the eye is going anyway. */
.esDos__rule {
    height: 1px;
    background: var(--es-line);
    transform: scaleX(1);
    transform-origin: left center;
}

.esDos__note {
    color: var(--es-soft-ink);
    font-size: .88rem;
    white-space: nowrap;
}

.esDos__go:hover .esDos__rule,
.esDos__go:focus-visible .esDos__rule { background: var(--es-accent); }

.esDos__go:hover .esDos__name,
.esDos__go:focus-visible .esDos__name { color: var(--es-accent); }

.esDos__go:focus-visible { outline: 2px solid var(--es-accent); outline-offset: 3px; }

.esDos__acts { margin-top: clamp(26px, 4vw, 44px); }

/* Nothing but type on this shape, so the sections under it keep the same narrow measure. */
.es--dossier .esSec__in { max-width: 880px; }

@media (max-width: 640px) {
    .esDos__go { grid-template-columns: auto 1fr; }
    .esDos__rule { display: none; }
    .esDos__note { grid-column: 2; white-space: normal; }

    .esItin__pass { grid-template-columns: 1fr; text-align: left; }
    .esItin__leg--to { text-align: left; }
    .esItin__route { transform: rotate(90deg); justify-self: start; }
}

/* ---------- Liquid glass: lenses, not panels ---------- */

.es[data-site-theme="liquidglass"] {
    --es-ink: #0b1020;
    --es-soft-ink: #56607a;
    --es-paper: #f2f5fb;
    --es-canvas: #e8eef8;
    --es-line: rgba(255, 255, 255, .7);
    --es-accent: #0b63f6;
    --es-accent-ink: #ffffff;
    --es-accent-soft: #dce8ff;
    --es-accent-2: #34d0d8;
    --es-display-weight: 700;
    --es-tracking: -.03em;
    --es-radius: 26px;
    --es-lens: rgba(255, 255, 255, .44);
    --es-lens-edge: rgba(255, 255, 255, .92);
    --es-glow: 0 20px 44px -22px rgba(11, 99, 246, .65);

    position: relative;
    isolation: isolate;
}

/* One bright ground, so there is something for the lenses to bend. */
.es[data-site-theme="liquidglass"]::before {
    content: "";
    position: fixed;
    inset: -20vmax;
    z-index: -1;
    pointer-events: none;
    background:
        radial-gradient(38vmax 38vmax at 18% 14%, rgba(11, 99, 246, .42), transparent 70%),
        radial-gradient(34vmax 34vmax at 82% 78%, rgba(52, 208, 216, .38), transparent 70%),
        radial-gradient(28vmax 28vmax at 60% 30%, rgba(167, 139, 250, .26), transparent 70%);
    filter: blur(30px);
    opacity: .7;
    animation: esDriftA 38s ease-in-out infinite alternate,
               esDriftB 53s ease-in-out infinite alternate;
}

/* A lens is three things at once: what it blurs, the bright edge where light catches it, and
   the inner shadow that reads as thickness. Two of the three and it looks like a grey box. */
/* No position here: the bar is sticky, and setting position on it drops it out of the top of
   the page - the lens is a background, not a layout change. */
.es[data-site-theme="liquidglass"] :is(.esBar, .esFace, .esStat, .esFaq__item, .esCard, .esItin__pass, .esRail__arrow) {
    background: var(--es-lens);
    border: 1px solid var(--es-lens-edge);
    backdrop-filter: blur(22px) saturate(180%);
    -webkit-backdrop-filter: blur(22px) saturate(180%);
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, .9),
        inset 0 -14px 24px -22px rgba(11, 16, 32, .55),
        0 22px 50px -34px rgba(11, 16, 32, .7);
}

/* Bigger surfaces read as thicker glass: more blur, a deeper shadow. */
.es[data-site-theme="liquidglass"] :is(.esSec--stats, .esSec--speakers, .esSec--faq, .esSec--sponsors) {
    background: color-mix(in srgb, var(--es-canvas) 55%, transparent);
    backdrop-filter: blur(28px) saturate(160%);
    -webkit-backdrop-filter: blur(28px) saturate(160%);
}

/* Never glass on glass: inside a frosted section the cards go to a plain tint. */
.es[data-site-theme="liquidglass"] :is(.esSec--stats, .esSec--speakers, .esSec--faq) :is(.esFace, .esStat, .esFaq__item) {
    background: rgba(255, 255, 255, .62);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
}

.es[data-site-theme="liquidglass"] .esBtn--solid {
    border: 1px solid rgba(255, 255, 255, .5);
    box-shadow: var(--es-glow), inset 0 1px 0 rgba(255, 255, 255, .55);
}

/* The specular sweep: the answer to a press, not an idle loop. */
.es[data-site-theme="liquidglass"] .esBtn--solid::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: inherit;
    background: linear-gradient(100deg, transparent 20%, rgba(255, 255, 255, .55) 50%, transparent 80%);
    opacity: 0;
    transform: translateX(-30%);
    transition: opacity 180ms ease-out, transform 420ms cubic-bezier(.22, 1, .36, 1);
    pointer-events: none;
}

.es[data-site-theme="liquidglass"] .esBtn--solid {
    position: relative;
    overflow: hidden;
}

.es[data-site-theme="liquidglass"] .esBtn--solid:hover::after,
.es[data-site-theme="liquidglass"] .esBtn--solid:focus-visible::after {
    opacity: 1;
    transform: translateX(30%);
}

.es[data-site-theme="liquidglass"] .esBtn--solid:active { transform: scale(.985); }

/* The bar is a lens over whatever is behind it, so its links carry the page's own ink rather
   than the soft grey that reads fine on flat paper and disappears over a photograph. */
.es[data-site-theme="liquidglass"] .esBar__link {
    color: var(--es-ink);
    font-weight: 600;
}

/* Vibrancy: text over a lens needs weight and contrast, never a mid grey. */
.es[data-site-theme="liquidglass"] :is(.esFace, .esStat, .esFaq__item, .esItin__pass) .esStat__label,
.es[data-site-theme="liquidglass"] :is(.esFace, .esStat, .esFaq__item, .esItin__pass) small {
    color: #3c465f;
    letter-spacing: .01em;
}

/* A visitor who asked for less transparency gets a solid surface, not a smear. */
@media (prefers-reduced-transparency: reduce) {
    .es[data-site-theme="liquidglass"] :is(.esBar, .esFace, .esStat, .esFaq__item, .esCard, .esItin__pass, .esRail__arrow),
    .es[data-site-theme="liquidglass"] :is(.esSec--stats, .esSec--speakers, .esSec--faq, .esSec--sponsors) {
        background: #ffffff;
        backdrop-filter: none;
        -webkit-backdrop-filter: none;
        border-color: var(--es-line);
    }

    .es[data-site-theme="liquidglass"]::before { display: none; }
}

@media (prefers-contrast: more) {
    .es[data-site-theme="liquidglass"] :is(.esBar, .esFace, .esStat, .esFaq__item, .esCard, .esItin__pass) {
        background: #ffffff;
        border-color: var(--es-ink);
    }
}

/* ---------- what stops when the motion is turned down ---------- */

.es:not(.es-animate) :is(.esCine__shot, .esRail__hintLine, .esItin__route i) {
    animation: none !important;
}

.es[data-site-motion="subtle"] :is(.esCine__shot, .esItin__route i) {
    animation: none !important;
}

@media (prefers-reduced-motion: reduce) {
    .esRail__track { transition: none !important; }
    .es--deck main > .esSec { position: static; }
}
