/**
 * @file
 * Event detail page.
 *
 * The old layout was col-md-9 (image, title, body) + col-md-3 (When / Where /
 * More Information / Cost) with the "Book now" button appearing twice — once
 * under the body and again inside the Cost panel — and a Google Maps JS SDK
 * loaded with an inline API key on every event page.
 *
 * New layout keeps all four information panels and every field, but:
 *  - the key facts (date, venue, price) sit in a summary strip directly under
 *    the title, where they are seen before any scrolling;
 *  - booking is a single, persistent action rather than two competing buttons;
 *  - the map is a static image that only upgrades to an interactive embed on
 *    click, so no third-party SDK loads unless the visitor asks for it.
 */

.event-detail {
  /* Was --space-2xl. That 64px sat on top of the related-events band's own
     margin, its padding and the section padding around the block — five
     separate spaces stacked into one gap of about 180px between the end of the
     description and "Similar events". The band owns the separation now; the
     article just stops. */
  padding-block-end: var(--space-l);
}

/**
 * Hero band — edge to edge.
 *
 * On the live site the node renders inside the page grid's `content` column, so
 * the hero was boxed in at 1320px with the dark page showing either side. This
 * breaks it out from whatever depth it is nested at: `100vw` wide, pulled back
 * by half the difference between the viewport and its own container. Works at
 * any nesting level, needs no change to the page template, and `body` already
 * carries `overflow-x: clip` so the scrollbar cannot cause a horizontal scroll.
 */
.event-hero,
.hub-hero {
  position: relative;
  inline-size: 100vw;
  max-inline-size: 100vw;
  margin-inline: calc(50% - 50vw);
  border-block-end: 1px solid var(--border-subtle);
  background: var(--surface-1);
}

/**
 * THE BAND IS PAINTED FROM THE PHOTOGRAPH.
 *
 * `--hero-c1` and `--hero-c2` are set inline per event by the Twig, from
 * _mosman_events_2026_node_palette(). The recipe is the slideshow's: a diagonal
 * wash with a soft bloom of each colour in opposite corners, which reads with
 * depth where a two-stop linear ramp reads as a flat swatch.
 *
 * Both colours are produced inside a lightness band chosen for white text, so
 * the title, the flags and the ReadSpeaker control sitting on this band keep
 * their contrast whatever the picture is. Where there is no readable image the
 * custom properties are absent and the fallbacks are the theme's own surfaces
 * — a plain dark band, exactly as before.
 */
.event-hero {
  padding-block-start: var(--space-m);
  background:
    radial-gradient(
      120% 140% at 8% 0%,
      color-mix(in oklab, var(--hero-c1, var(--surface-2)) 78%, transparent) 0%,
      transparent 62%
    ),
    radial-gradient(
      120% 140% at 92% 100%,
      color-mix(in oklab, var(--hero-c2, var(--surface-1)) 78%, transparent) 0%,
      transparent 64%
    ),
    linear-gradient(
      118deg,
      var(--hero-c1, var(--surface-2)) 0%,
      color-mix(in oklab, var(--hero-c1, var(--surface-2)) 50%, var(--hero-c2, var(--surface-1))) 50%,
      var(--hero-c2, var(--surface-1)) 100%
    );
}

/*
 * How wide the event header is allowed to get.
 *
 * One number, read by the picture and by the title block beneath it. Lower it
 * to 825px for the artwork's exact native size and a 465px band; raise it to
 * var(--wrapper-max) to go back to the full-width header.
 *
 * Below this width the header is simply the available space, so phones and
 * tablets are untouched by it.
 */
.event-hero {
  /*
   * THE SPLIT HERO: PICTURE ON THE LEFT, THE WORDS BESIDE IT.
   *
   * Nothing on this page may be cropped, so the only way to make the band
   * shorter is to make the picture narrower — and a narrower picture left a
   * column of empty canvas down the right of a 1320px page, which looked
   * worse than the height it saved. The words go in that column. The picture
   * gives up width, the height follows it down, and the space is used.
   *
   * Measured at 1440: the header reached the description at 904px before and
   * reaches it at 571px now, so the first paragraph is above the fold on a
   * 1080p laptop instead of a screen below it.
   *
   * IT ALSO GETS SHARPER. The artwork is authored 825 x 465. Painted at the
   * full 1320 container it was a 1.60x upscale — soft, with no more detail to
   * show. In a 58% column it is about 760px wide, under its own size, so the
   * browser is sampling down rather than inventing pixels.
   */
  --event-hero-max: var(--wrapper-max);
  --event-hero-split: 58%;
}

/*
 * One grid, above 64rem only.
 *
 * Below that the two children stay in source order and full width — picture,
 * then words — which is the right thing on a phone and is exactly what they
 * did before this rule existed. The breakpoint is the theme's existing layout
 * one, the width the nav collapses at, not a number invented here.
 */
@media (min-width: 64rem) {
  .event-hero__inner {
    display: grid;
    grid-template-columns: var(--event-hero-split) 1fr;
    gap: var(--space-xl);
    align-items: center;
    inline-size: min(var(--event-hero-max), 100% - var(--gutter) * 2);
    margin-inline: auto;
  }

  /*
   * The children stop sizing themselves once the grid is doing it.
   *
   * Both carry `inline-size: min(--event-hero-max, 100% - gutter*2)` and
   * `margin-inline: auto` for the stacked case. Left in place inside a grid
   * column they would each try to be the width of the whole page and then
   * centre themselves inside a track half that size.
   */
  .event-hero__inner > .event-hero__media,
  .event-hero__inner > .event-hero__body {
    inline-size: auto;
    margin-inline: 0;
    max-inline-size: none;
  }

  .event-hero__inner > .event-hero__body {
    padding-block: 0;
  }

  /*
   * CLEARANCE FOR THE ARC, WHICH IS DEEPEST EXACTLY WHERE THE PICTURE IS.
   *
   * The band's foot is cut with `border-end-start-radius: 50% var(--band-arc)`
   * and `overflow: hidden` (see css/theme/utilities.css). A 50% horizontal
   * radius means the bottom edge is at its lowest in the MIDDLE of the band
   * and rises by the full arc depth at the left and right EDGES — and the
   * picture's bottom-left corner is about as close to the left edge as
   * anything on this page gets. With the grid taking the body's old bottom
   * padding away, the corner of the photograph was being clipped off by the
   * curve.
   *
   * The arithmetic, at 1440: the ellipse is rx = 720 (half the band), ry =
   * 48px. At the picture's left edge, one gutter in, the curve has already
   * risen 31.5px. On a narrower screen the gutter shrinks and the picture sits
   * closer to the edge, so the rise is worse. Reserving the WHOLE arc depth
   * is the only figure that holds at every width, and --space-s on top of it
   * is the visible gap between the photograph and the curve.
   */
  .event-hero__inner {
    padding-block: var(--space-l) calc(var(--band-arc) + var(--space-s));
  }
}

/**
 * THE PICTURE'S BOX: THE PAGE CONTAINER, CENTRED.
 *
 * It was `inline-size: 100%` on a 100vw band — the photograph stretched from
 * screen edge to screen edge and cropped to 16:9 to do it. On a 1536px display
 * that is a 1200px press photo scaled past its own resolution with the top and
 * bottom of the subject cut away.
 *
 * At the container's width it lines up with the header, the title and the
 * rails, and it is shown at ITS OWN ratio: `aspect-ratio` is gone and the image
 * sets the height, capped so a portrait photograph cannot produce an absurd
 * band. What used to be photograph either side is now the colour band above.
 */
.event-hero__media {
  position: relative;
  /*
   * --event-hero-max, NOT --wrapper-max. THIS IS THE HEIGHT CONTROL.
   *
   * Nothing on this page may be cropped — the note below explains why the old
   * 28rem ceiling was removed — so the only way to make the picture shorter
   * is to make it narrower, and the height follows the ratio down.
   *
   * At the full 1320 container the band was 744px: 83% of the viewport on a
   * 1080p laptop, before the title. At 900 it is 507px, 56%, and the title
   * and the fact strip come up with it.
   *
   * IT ALSO GETS SHARPER, WHICH IS THE PART WORTH KNOWING. The artwork is
   * authored 825 x 465. Painting it 1320 wide was a 1.60x upscale — soft, and
   * there was no more detail to show. At 900 the upscale is 1.09x, which is
   * invisible. Going below 900 buys height at no cost in quality down to 825,
   * where it is pixel for pixel.
   *
   * The token is on `.event-hero` so the picture and `.event-hero__body`
   * cannot be given different widths by accident; they are one column.
   */
  inline-size: min(var(--event-hero-max), 100% - var(--gutter) * 2);
  margin-inline: auto;

  /**
   * 825 × 465 — the client's ratio, and the whole of it.
   *
   * It was 16/9 under a 28rem ceiling, and the ceiling was doing the real work:
   * at the 1320px container it capped the box at 448px and `cover` cropped a
   * third of the picture away to fit. Stating the ratio and removing the cap
   * means the frame the editor uploaded is the frame that shows — 1320 × 744 at
   * the full container, scaling down with it.
   */
  aspect-ratio: 825 / 465;
  max-block-size: none;
  overflow: hidden;
  border-radius: var(--card-radius);

  /* A hairline of the picture's own edge against the wash, so a photograph
     whose border is close to the palette does not bleed into it. */
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--me-black) 22%, transparent);
  background: var(--surface-2);
}

/* Flatten whatever the field formatter wrapped the image in, so every element
   in the chain down to the <img> fills the box. Naming the wrappers is a list
   that is wrong the next time a formatter changes. */
.event-hero__media > *,
.event-hero__media > * > * {
  display: block;
  inline-size: 100%;
  block-size: 100%;
  margin: 0;
}

.event-hero__media img {
  display: block;
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  object-position: center;
}

/* The ratio holds at every width — at 390px it is a 220px band, which is the
   right size for a phone without changing how the picture is composed. */

/**
 * NO SCRIM ON THE PICTURE.
 *
 * There was a black gradient across the bottom of the photograph, and it was
 * there for one reason: the title was pulled up 48px and sat ON the image, so
 * the image's lower edge had to be dark enough to carry white text.
 *
 * The title is not on the image any more — it is on the colour band below it,
 * which is produced inside a lightness range chosen for white text. So the
 * scrim was darkening the bottom third of every event's artwork to solve a
 * contrast problem that no longer exists.
 */

/**
 * A TINTED BAND IS ALWAYS DARK — INCLUDING IN LIGHT MODE.
 *
 * The palette is produced inside a lightness range chosen for white text (see
 * _mosman_events_2026_dark_ui_hex()), so the band under the title is dark
 * whichever scheme the visitor is in. In light mode `--text-primary` is near
 * black, and without this the title would be dark type on a dark wash — the
 * one contrast failure this treatment can introduce.
 *
 * The class is only on the hero when there IS a palette; with no readable image
 * the band falls back to the theme's surfaces and the scheme's own text colours
 * are correct.
 */
.event-hero--tinted .event-hero__body {
  color: var(--me-white);
}

.event-hero--tinted .event-hero__title {
  color: var(--me-white);
}

.event-hero--tinted .listen-button {
  color: var(--me-white);
  border-color: color-mix(in srgb, var(--me-white) 45%, transparent);
}

.event-hero--tinted .listen-button:hover {
  color: var(--me-white);
  border-color: var(--me-white);
}

.event-hero__body {
  position: relative;
  /* The same column as the picture above it — see the note on
     `.event-hero__media`. A title wider than the photograph it belongs to
     reads as two unrelated blocks. */
  inline-size: min(var(--event-hero-max), 100% - var(--gutter) * 2);
  margin-inline: auto;
  /* Was `margin-block-start: -3rem`, which lifted the title onto the
     photograph. The picture now has its own box with its own corners, and text
     dropped over the corner of a rounded box reads as a mistake. The title sits
     under it, on the band. */
  padding-block: var(--space-m) var(--space-l);
}

.event-hero__status {
  display: inline-block;
  margin-block-end: var(--space-2xs);
  padding: 0.35rem var(--space-2xs);
  border-radius: var(--radius-xs);
  background: var(--accent-solid);
  color: var(--me-white);
  font-size: var(--step--2);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
}

/**
 * Full width, not 22ch.
 *
 * The measure cap is right for a paragraph and wrong for a heading: "Mosman
 * Market - Saturday 5 September" broke onto a second line with two thirds of
 * the hero empty beside it. A title is read in one glance, so it gets the whole
 * width and wraps only when it genuinely must. `text-wrap: balance` keeps the
 * lines even on the titles that still need two.
 */
.event-hero__title {
  font-size: var(--step-5);
  inline-size: 100%;
  max-inline-size: 100%;
  text-wrap: balance;
  margin-block-end: var(--space-s);
}

/**
 * The hero starts flush against the header.
 *
 * The rule itself lives in layout.css, with the other `.section` spacing. It is
 * a `:has(.event-detail)` DESCENDANT test — the version here was
 * `:has(> .event-detail)`, and the node is never a direct child of the section:
 * Drupal wraps it in a region and a block first, so the child combinator
 * matched nothing and the gap stayed.
 */

/**
 * Genre, under the body.
 *
 * It was one column of the hero fact strip, above the description. Here it
 * reads as "more like this", which is what a genre link is for.
 */
.event-genres {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2xs) var(--space-s);
  margin-block-start: var(--space-l);
  padding-block-start: var(--space-m);
  border-block-start: 1px solid var(--border-subtle);
}

.event-genres__label {
  margin: 0;
  color: var(--text-muted);
  font-size: var(--step--2);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
}

.event-genres .field,
.event-genres .field__items {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2xs);
  margin: 0;
  padding: 0;
  list-style: none;
}

.event-genres .field__item {
  display: inline-block;
}

/* Fact strip — retained for the listing card and any embed that still uses it;
   the event hero no longer prints one. */
.event-facts {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-s) var(--space-l);
  padding-block: var(--space-s);
  border-block-start: 1px solid var(--border-subtle);
}

.event-facts__item {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2xs);
  min-inline-size: 0;
}

.event-facts__icon {
  display: grid;
  place-items: center;
  inline-size: 2rem;
  block-size: 2rem;
  flex: none;
  border-radius: var(--radius-s);
  background: color-mix(in srgb, var(--me-magenta) 18%, transparent);
  color: var(--me-magenta-300);
}

.event-facts__icon svg {
  inline-size: 1rem;
  block-size: 1rem;
}

.event-facts__value {
  font-weight: var(--weight-bold);
  line-height: var(--leading-snug);
}

.event-facts__value--free {
  color: var(--me-success);
}

/**
 * The body and its two rails.
 *
 * Narrow: one column, body first, panels under it.
 * Medium (62rem+): body with a single right rail — both panel groups stack there.
 * Wide (78rem+): logistics left, body centre, actions right.
 *
 * The three-column arrangement is what stops a short event from having a tall
 * column of panels hanging beside empty space. Source order is left, body,
 * right, so the tab order matches what is on screen.
 */
/**
 * WIDTH: THE PAGE CONTAINER, NOT A CONTAINER INSIDE IT.
 *
 * This was `min(var(--wrapper-max), 100% - var(--gutter) * 2)` — the site's
 * standard wrapper. That expression is right for a box sitting on the full
 * viewport, and wrong here: the article is already inside `.grid-page`'s
 * `content` column, which IS that expression. Subtracting the gutter a second
 * time took another 85px off at desktop width, so the body and the two rails
 * lined up 42px inside the header's logo and menu on either side — the
 * misalignment the client could see.
 *
 * `min(var(--wrapper-max), 100%)` keeps the cap for any context where this
 * article is not in the page grid, and takes nothing off when it is.
 */
.event-detail__grid {
  inline-size: min(var(--wrapper-max), 100%);
  margin-inline: auto;
  padding-block-start: var(--space-xl);
  display: grid;
  gap: var(--space-l) var(--space-xl);
  grid-template-columns: minmax(0, 1fr);
  align-items: start;
}

.event-detail__main {
  min-inline-size: 0;
  /* Body first on a phone; the panels follow. */
  order: -1;
}

@media (min-width: 62rem) {
  .event-detail__grid {
    /* 22rem, was 20rem. At this width the two panel groups share one column,
       so the venue name, the date and the contact lines all wrap in it. */
    grid-template-columns: minmax(0, 1fr) 22rem;
  }

  .event-detail__main {
    order: 0;
    grid-column: 1;
    grid-row: 1 / span 2;
  }

  .event-detail__rail--start { grid-column: 2; grid-row: 1; }
  .event-detail__rail--end { grid-column: 2; grid-row: 2; }
}

@media (min-width: 78rem) {
  .event-detail__grid {
    /**
     * 21rem a side, was 19rem.
     *
     * The rails carry a map, an address, a phone number and an email address —
     * all things that wrap badly in 304px. At 336px the body still keeps 552px
     * at the 1320px container, which is inside the comfortable measure for
     * running text, so the width comes out of empty gutter rather than out of
     * the description.
     */
    grid-template-columns: 21rem minmax(0, 1fr) 21rem;
  }

  .event-detail__rail--start { grid-column: 1; grid-row: 1; }
  .event-detail__main { grid-column: 2; grid-row: 1; }
  .event-detail__rail--end { grid-column: 3; grid-row: 1; }
}

.event-detail__rail {
  min-inline-size: 0;
}

/* Panels within a rail are spaced by the rail, not by margins that would
   collapse differently once the rails split. */
.event-detail__rail > .panel + .panel {
  margin-block-start: var(--space-m);
}

/**
 * Sidebar panels — the old .date-container / .location-container /
 * .more-info / .tickets, unified.
 *
 * TYPE SIZE
 * ---------
 * Everything in these panels was set at `--step--1` or smaller: a 13px venue
 * name, a 13px phone number, a 13px email address. That is caption size, and
 * these are the facts a visitor came to the page for — the date, where it is,
 * what it costs, who to call. They read at body size now, with the one line
 * that identifies each panel (the venue, the date) a step above that.
 *
 * The panels also carry more padding and a rule under the title, so that at the
 * larger size they still read as panels rather than as loose text.
 */
.panel {
  /*
   * The padding is a variable because the title breaks out of it — see
   * .panel__title below. Two places need the same number, and a hard-coded
   * 1.5rem in one of them is a band that does not reach the panel's edge at
   * exactly one breakpoint, which is the kind of thing nobody notices until
   * it ships.
   */
  --panel-pad: var(--space-m);

  padding: var(--panel-pad);
  border: 1px solid var(--border-subtle);
  border-radius: var(--card-radius);
  background: var(--surface-1);

  /* So the heading band's top corners follow the panel's radius. */
  overflow: hidden;
}

@media (min-width: 48rem) {
  .panel {
    --panel-pad: var(--space-l);
  }
}

.panel + .panel {
  margin-block-start: var(--space-m);
}

/**
 * The heading is a BAND across the top of the panel.
 *
 * It was magenta type with a magenta rule under it, sitting on the panel's own
 * surface. At caption size, in a colour used everywhere else on the page for
 * links and accents, it read as another line of content rather than as the
 * label of the box it belongs to — and four panels down the rail gave four
 * magenta rules competing with the real accents.
 *
 * A band separates the label from the content structurally, not just
 * typographically. The negative margins pull it out to the panel's edges; the
 * panel's `overflow: hidden` clips its top corners to the radius.
 *
 * `--me-ink` and `--me-white`, not `--surface-*`: those two flip in light mode,
 * and this band should be dark with white type in both. White on #0b0f16 is
 * 18.7:1, so the uppercase caption size is comfortably past WCAG AA.
 */
.panel__title {
  margin:
    calc(var(--panel-pad) * -1)
    calc(var(--panel-pad) * -1)
    var(--space-m);
  padding: var(--space-xs) var(--panel-pad);

  /*
   * ONE FLAT BRAND COLOUR. NO GRADIENT, AND NO TINT FROM THE PHOTOGRAPH.
   *
   * This band used to be a 118deg gradient whose two ends were mixed from
   * `--hero-c1` / `--hero-c2`, the colours GD reads out of the event's own
   * article image — so a mahjong page got green headings and a market page
   * orange ones. The mixes were kept small on purpose, because artwork can be
   * any colour at all and white type had to survive the worst of it.
   *
   * It is now flat `--me-magenta`. That drops the per-event tint entirely,
   * which is the point: every panel on every event reads the same.
   * `--hero-c1` / `--hero-c2` are still produced by the preprocess and still
   * used by the hero band above; they simply no longer reach here.
   *
   * READ THIS BEFORE LEAVING IT ALONE — IT DOES NOT PASS WCAG AA.
   *
   * White on #ec008c measures 4.25:1. The label is `--step--1` (about 14.7px)
   * and bold, which is NOT "large text" under WCAG — that needs 18.66px bold —
   * so the requirement is 4.5:1 and this is 0.25 short. The old gradient
   * cleared it comfortably at both ends.
   *
   * `--panel-title-bg` exists so the fix is one line. `--me-magenta-600`
   * (#db0083) measures 4.84:1 against white and is the token the theme already
   * reserves for "accent as a SOLID FILL under white text"; to the eye it is
   * the same pink. Swap it below if this band has to be compliant.
   */
  background: var(--panel-title-bg);
  color: var(--me-white);
  border-block-end: 1px solid color-mix(in srgb, var(--me-white) 10%, transparent);

  font-size: var(--step--1);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
}

.panel__row + .panel__row {
  margin-block-start: var(--space-2xs);
  padding-block-start: var(--space-2xs);
  border-block-start: 1px solid var(--border-subtle);
}

.panel dt {
  color: var(--text-muted);
  font-size: var(--step--2);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  font-weight: var(--weight-bold);
}

.panel dd {
  margin: 0 0 var(--space-2xs);
}

/* ---------------------------------------------------------------------------
 * Panel typography
 * ------------------------------------------------------------------------ */
/* The magenta rule that used to sit under the title went with the band. */

.panel .meta {
  font-size: var(--step-0);
  line-height: var(--leading-normal);
  color: var(--text-secondary);
}

/* The line that identifies the panel: the venue, the date, the contact. Bold
   at the same size rather than a step larger — a 19rem rail wraps a venue name
   onto three lines at --step-1, and the pink panel title already carries the
   hierarchy. */
.panel .meta--strong {
  font-size: var(--step-0);
  font-weight: var(--weight-bold);
  line-height: var(--leading-snug);
  color: var(--text-primary);
}

.panel p + p {
  margin-block-start: var(--space-2xs);
}

/**
 * Plain links inside a panel — a phone number, an email address.
 *
 * They had no colour rule at all, only `word-break`, so an email address in the
 * Booking panel rendered in the browser's default link blue and its visited
 * state in purple, on a dark magenta-accented page.
 *
 * `:not(.button):not(.action-link)` matters: without it this rule underlined
 * the "Book a stall" button and painted its label magenta on a magenta fill.
 * Anything that already has its own treatment keeps it.
 */
.panel a:not(.button):not(.action-link),
.panel a:not(.button):not(.action-link):visited {
  color: var(--text-accent);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.2em;
  overflow-wrap: anywhere;
}

.panel a:not(.button):not(.action-link):hover {
  color: var(--me-magenta-300);
  text-decoration-thickness: 2px;
}

/* Recurrence line, e.g. "Every week on Tuesday". */
.event-recurrence {
  /**
   * TWO TUNED NUMBERS THAT MUST KEEP THEIR SUM. DO NOT ROUND EITHER TO A
   * TOKEN WITHOUT RE-MEASURING BOTH.
   *
   * The badge column has to meet the rails twice over, and the two targets
   * are different lines:
   *
   *   --recurrence-drop  puts the badge's OWN WORDING on the baseline of the
   *                      "When" and "Booking" title bands.
   *   --recurrence-gap   puts the first line of the BODY on the first line
   *                      inside those panels — "Every week on Wednesday" and
   *                      "Free".
   *
   * THE SUM IS WHAT HOLDS THE BODY IN PLACE: 0.4 + 1.75 = 2.15rem, which is
   * what the single gap used to be before the badge moved. Change one of
   * these and change the other by the same amount in the other direction, or
   * the body slips off the panels.
   *
   * WHERE 0.4rem COMES FROM. The badge is a 26.8px chip and the title band is
   * a 41.2px bar, so their text centres do not coincide even though their box
   * tops nearly do. Measured, the badge's baseline sat 6.4px above the
   * titles' at three columns (6.2px at two, where the --step clamps have not
   * topped out). 0.4rem is that 6.4px.
   *
   * WHERE 1.75rem COMES FROM. The panels start their content 66.2px below the
   * grid's content edge: a 1px panel border, the 41.2px title band
   * (--space-xs above and below a --step--1 line at --leading-tight) and the
   * band's --space-m bottom margin. The body's first line sits about 5.0px
   * inside its own box because .prose runs at --leading-loose. So from the
   * badge's new bottom edge the gap is 28px.
   *
   * THE COST, SO IT CAN BE REVERSED KNOWINGLY: the pill's top edge now sits
   * about 5px below the panels' top edges instead of 1px above them. Aligning
   * the wording and aligning the boxes are mutually exclusive here, because
   * the chip and the band are different heights. Set --recurrence-drop to 0
   * and --recurrence-gap to 2.15rem to go back to flush boxes.
   *
   * RE-MEASURE IF: .panel__title's padding, font size or line-height changes,
   * .prose's line-height changes, or this badge's font size or padding
   * changes.
   */
  --recurrence-drop: 0.4rem;
  --recurrence-gap: 1.75rem;

  /**
   * A BLOCK, NOT AN INLINE BOX.
   *
   * This was `inline-flex`, which put the pill in an anonymous line box in
   * the body column. Its spacing then came from that line box — roughly 6px
   * of descender slop underneath, varying with the column's own font size —
   * rather than from anything written here, and there was no margin at all,
   * which is why the description started hard against it.
   *
   * `flex` with `fit-content` keeps the pill hugging its text exactly as
   * before and puts the gap under this file's control.
   */
  display: flex;
  inline-size: fit-content;
  align-items: center;
  gap: var(--space-3xs);
  margin-block: var(--recurrence-drop) var(--recurrence-gap);
  padding: 0.25rem var(--space-2xs);
  border-radius: var(--radius-pill);

  /**
   * PINK, AND NOT THE PINK THE BRIEF NAMED — HERE IS WHY.
   *
   * It was `--me-info` blue, the one cool accent on an otherwise magenta
   * page. The tint now comes from `--me-magenta`, the same token behind the
   * panel title bands, so the badge belongs to the rail it sits beside.
   *
   * The TEXT is `--text-accent`, not `--me-magenta`. #ec008c on its own 16%
   * tint measures 4.09:1 on the dark canvas and 3.23:1 on the light one; the
   * badge is --step--2 bold (about 11.8px), which is nowhere near WCAG's
   * "large text" of 18.66px bold, so it needs 4.5:1 and misses in both
   * themes. `--text-accent` is the theme's existing answer to exactly this —
   * #ff63ba on dark, #ec008c-adjacent #b8006e on light — and measures 6.40:1
   * and 4.90:1 on this tint. To the eye it is the same pink.
   */
  background: color-mix(in srgb, var(--me-magenta) 16%, transparent);
  color: var(--text-accent);

  font-size: var(--step--2);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
}

/**
 * A ONE-OFF EVENT HAS NO BADGE, SO THE BODY TAKES THE BADGE'S PLACE.
 *
 * `.event-recurrence` only renders for a recurring event. Without it the body
 * is the first thing in the column, and the column should still start on the
 * same line as the "When" and "Booking" title bands — the reader should not
 * be able to tell, from where the text starts, whether the event repeats.
 *
 * The target is the badge's own wording, which already sits on those titles'
 * baseline. Left alone the body's first line lands 2.7px above it: the badge
 * centres --step--2 text in a 26.8px chip, the body centres --step-0 text in
 * a --leading-loose line box, and the two half-leadings do not coincide.
 *
 * MEASURED, AND IT IS NOT A FONT ARTEFACT. The same 2.7px comes back within
 * 0.1px under Inter, DejaVu Sans, Carlito and FreeSans — both numbers scale
 * with the same font, so the difference between them does not move.
 *
 * `:has()` rather than a template class: the template already decides whether
 * to print the badge, and a second source of truth for the same fact is one
 * that can disagree with the first.
 */
.event-detail__main:not(:has(> .event-recurrence)) > .prose {
  margin-block-start: 0.17rem;
}

/**
 * Booking stays in view in the three-column layout — but the STICKY ELEMENT IS
 * THE RAIL, not the panel.
 *
 * With `position: sticky` on `.booking-panel` alone, the panel froze while its
 * own sibling — "More information" — kept scrolling, and the two overlapped:
 * the second panel's heading disappeared under the first. A sticky box does not
 * take its siblings with it. Sticking the rail keeps the group together, and
 * the browser stops it at the foot of the grid row rather than letting it ride
 * over anything.
 */
.booking-panel {
  position: static;
}

/**
 * The booking button is a control, not a slab.
 *
 * It was reading as much bigger than it is: full rail width, a 46px minimum
 * height and the accent glow around it, sitting under a single short word. The
 * page already has a second "Book now" under the body, so the two together were
 * most of what the eye landed on.
 *
 * It keeps the full width — a primary action in a rail panel wants a target
 * edge to edge — and loses the height and the halo, which is where the bulk
 * actually was.
 */
/**
 * The cost, at the rail's own scale.
 *
 * It was set inline at `--step-2` — 28px — which made "$33" the loudest thing
 * on the page after the title, over a venue name and a date set at 18px two
 * inches away. It is still the panel's headline, so it keeps the weight and the
 * accent colour for a free event; it just stops shouting.
 */
.booking-panel__cost {
  margin-block: 0 var(--space-2xs);
  /* --step-0, was --step-1. The panel holds one number and one button; with
     the number set a step above the venue and the date beside it, the cheapest
     fact on the page was also the loudest. */
  font-size: var(--step-0);
  font-weight: var(--weight-bold);
  line-height: var(--leading-snug);
  color: var(--text-primary);
}

/**
 * The whole panel, one step down.
 *
 * "When", "Where" and "More information" are reference: a visitor reads them.
 * "Booking" is a control with a price on it — the cost, the contact name and
 * the button — and it reads at the rail's smaller scale so that the button is
 * the thing the eye lands on rather than the type around it.
 */
/*
 * The booking panel's heading is the same size as the others.
 *
 * It was a step smaller, on the reasoning that the rail's own scale should let
 * the button be the thing the eye lands on. As a band across the top of the
 * panel it is no longer competing with the button for attention — the band
 * separates them — so a heading a step smaller than the three beside it just
 * looked like a mistake.
 */
.booking-panel .panel__title {
  font-size: var(--step--1);
}

.booking-panel .meta,
.booking-panel .meta--strong,
.booking-panel .field,
.booking-panel .field__item {
  font-size: var(--step--0);
}

.booking-panel__cost--free {
  color: var(--me-success);
}

.booking-panel .button--primary {
  /* Sized by its label, never by the panel. The `inline-size` is a backstop for
     a cached template still emitting `button--block`; `max-inline-size` keeps a
     long label ("Join waitlist", a translation) inside the panel rather than
     letting the inline-flex box push past its edge. */
  inline-size: auto;
  max-inline-size: 100%;

  /**
   * 34px tall, sized by its label.
   *
   * It was 40px with 1rem type and generous side padding, which in a rail
   * panel next to two short lines still read as a slab. 0.4rem/0.9rem of
   * padding around 14px type gives a 34px × ~104px control — comfortably past
   * the 24px minimum target WCAG 2.2 asks for, and small enough that the panel
   * reads as one thing rather than as a button with some text above it.
   */
  padding: 0.35rem 0.85rem;
  min-block-size: 2.1rem;
  font-size: var(--step--1);

  /* A tight glow. The default accent shadow spreads 28px at a -10px inset,
     which on a full-width button painted well outside the panel and made the
     button look bigger than it is. */
  box-shadow: 0 3px 10px -7px color-mix(in srgb, var(--me-magenta) 45%, transparent);
}

/**
 * The one under the body is the same size as any other button on the site.
 *
 * It was `.button--large` — 18px type in a 61px pill — on the assumption that
 * the page's single action deserved to shout. Next to the rail's copy of the
 * same action it just looked oversized.
 */
.event-detail__main > .u-mt-l > .button--primary {
  padding-block: 0.55rem;
  min-block-size: 2.5rem;
  box-shadow: 0 3px 10px -7px color-mix(in srgb, var(--me-magenta) 45%, transparent);
}

@media (min-width: 78rem) {
  .event-detail__rail--end {
    position: sticky;
    top: calc(var(--header-offset) + var(--space-m));
  }
}

/*
 * THE MOBILE BOOKING BAR LIVED HERE, INSIDE A `max-width: 61.999rem` BLOCK.
 *
 * A fixed element across the foot of the viewport carrying the cost and a
 * Book now button. Removed at the client's request, along with the
 * `.page:has(.booking-bar)` rule that reserved room for it at the foot of the
 * page and the `min-width: 62rem` rule that hid it on desktop. The media
 * query went too, because nothing was left inside it.
 *
 * `--booking-bar-h` in base/tokens.css went with it.
 *
 * Nothing is uncovered by its going: the inline booking button in the body
 * prints at every width, and the Booking panel carries the same link beside
 * the prices.
 *
 * If it ever comes back, the two things that made it work were `position:
 * fixed` rather than sticky — sticky only sticks inside its own containing
 * block, and the bar was a child of the article, so it came unstuck at
 * "Similar events" and scrolled away for the bottom third of the page — and
 * the `env(safe-area-inset-bottom)` padding that kept the button clear of the
 * iPhone home indicator.
 */

/* Map: static by default, upgraded on request. */
.event-map {
  position: relative;
  margin-block-start: var(--space-2xs);
  border-radius: var(--radius-s);
  overflow: hidden;
  background: var(--surface-2);
  aspect-ratio: 4 / 3;
}

.event-map img,
.event-map iframe {
  display: block;
  inline-size: 100%;
  block-size: 100%;
  border: 0;
  object-fit: cover;
  /* The embeds render their own light UI; without this they sit as a bright
     rectangle against the dark panel. */
  color-scheme: light;
}

.event-map__consent {
  position: absolute;
  inset: 0;
  display: grid;
  place-content: center;
  gap: var(--space-2xs);
  padding: var(--space-s);
  text-align: center;
  background: var(--surface-2);
  color: var(--text-secondary);
  font-size: var(--step--1);
}

/* Accessibility helper mirroring the old ReadSpeaker button placement. */
.listen-button {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3xs);
  padding: 0.4rem var(--space-2xs);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-pill);
  color: var(--text-secondary);
  font-size: var(--step--2);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  text-decoration: none;
}

.listen-button:hover {
  color: var(--text-primary);
  border-color: var(--me-magenta);
}

.listen-button svg {
  inline-size: 0.9rem;
  block-size: 0.9rem;
}

/* Share row */
.share {
  display: flex;
  align-items: center;
  gap: var(--space-2xs);
  margin-block-start: var(--space-m);
  padding-block-start: var(--space-m);
  border-block-start: 1px solid var(--border-subtle);
}

.share__label {
  color: var(--text-muted);
  font-size: var(--step--2);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
}

/* `background: transparent` is explicit: a <button> styled as a share control
   otherwise picks up the browser's default grey, which is why the copy-link and
   add-to-calendar circles looked filled next to the anchor ones. */
.share__link {
  display: grid;
  place-items: center;
  background: transparent;
  padding: 0;
  inline-size: 2.25rem;
  block-size: 2.25rem;
  border-radius: 50%;
  border: 1px solid var(--border-subtle);
  color: var(--text-secondary);
  transition:
    color var(--duration-fast) var(--ease-out),
    border-color var(--duration-fast) var(--ease-out);
}

.share__link:hover {
  color: var(--me-magenta-300);
  border-color: var(--me-magenta);
}

.share__link svg {
  inline-size: 1rem;
  block-size: 1rem;
}


/**
 * Related events sit BELOW the two-column grid, full width.
 *
 * Inside the left column they were squeezed against a taller right rail, which
 * left a large empty block under the body copy and gave the rail nothing to sit
 * beside. As their own band they read as a section, and the void is gone.
 */
/**
 * ONE space above the rule, not four.
 *
 * `.event-related` is a `.section--tight` (32px top and bottom) carrying a
 * 64px top margin and 48px of its own top padding, inside a region wrapper
 * that is itself a padded `.section`, under an article that ended with 64px of
 * padding. Every one of those was defensible on its own and together they put
 * a screenful of empty canvas between the description and the next thing to
 * look at.
 *
 * The band now states its whole spacing here: the section padding is zeroed,
 * the margin is gone, and the rule sits 32px under the content with the
 * heading 32px below that.
 *
 * The width matches `.event-detail__grid` for the same reason it does there —
 * the block renders inside the page grid's content column, so subtracting the
 * gutter again would inset "Similar events" from the body above it.
 */
.event-related {
  inline-size: min(var(--wrapper-max), 100%);
  margin-inline: auto;
  margin-block-start: 0;
  padding-block: var(--space-l);
  border-block-start: 1px solid var(--border-subtle);
}

/* The section padding the class would otherwise add, on the element itself and
   on any wrapper Drupal puts between it and the page grid. */
.event-related.section {
  /* Space below as well as above: the newsletter card follows, and with no
     bottom padding the last row of "Similar events" sat directly on it. The
     card adds its own 48px margin on top of this. */
  padding-block: var(--space-l);
}

.section:has(.event-related) {
  padding-block-start: 0;
}

/* The reveal control, centred under the rail. */
/**
 * On a hub page the same block is the page's content, not an aside.
 *
 * No heading and no rule: the hub's own heading block sits directly above it
 * and already carries a border, so a second divider two rows down read as a
 * separator between two things that are one thing.
 */
.event-related--hub {
  border-block-start: 0;
  padding-block-start: var(--space-xl);
}

.event-related__actions {
  margin-block: var(--space-m) 0;
  text-align: center;
}

.event-related__count {
  display: inline-block;
  min-inline-size: 1.5rem;
  margin-inline-start: var(--space-3xs);
  padding-inline: var(--space-3xs);
  border-radius: var(--radius-pill);
  background: var(--surface-3);
  color: var(--text-secondary);
  font-size: var(--step--2);
  font-variant-numeric: tabular-nums;
}

.event-related__count:empty {
  display: none;
}


/*
 * The one-line précis above the title in the split hero.
 *
 * Two facts, separated by a dot drawn with a pseudo-element rather than typed
 * into the markup, so a missing venue does not leave a dangling separator.
 */
.event-hero__precis {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2xs);
  margin: 0 0 var(--space-xs);
  color: var(--text-accent);
  font-size: var(--step--1);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
}

.event-hero__action {
  margin: var(--space-m) 0 0;
}

/*
 * THREE BOOKING BUTTONS IS DELIBERATE, NOT AN OVERSIGHT.
 *
 * One beside the picture for somebody who has already decided, one in the
 * Booking panel beside the prices, one at the end of the description for
 * somebody who read first. They are a screen apart, so no two are ever on
 * screen together on a laptop.
 *
 * A rule to hide the body's copy while the hero's is visible was written and
 * removed: `.event-hero__action` lives inside the hero and the body button is
 * in `.event-detail__grid`, a SIBLING of the hero, so no combinator reaches
 * from one to the other. It silently matched nothing, which is the worst kind
 * of CSS to leave behind. If they ever do need to be mutually exclusive, the
 * switch has to come from a class on the page root, not from a selector that
 * walks between these two.
 */
