/* Inflaboo · homepage layer
   Shared structure for all three directions. What separates them is the
   palette/type file (generated) plus the .dir-a / .dir-b / .dir-c and
   .hero-* hooks at the bottom of this file.
   ------------------------------------------------------------------------ */

/* Language switcher ------------------------------------------------------
   Three languages, not a toggle. Bill 96 requires French to be at least as
   prominent and as easy to reach as the others, so every language is a
   sibling in one list at the same size. Nothing is hidden behind a dropdown
   and French is never the thing you have to go find. */

.langs {
  list-style: none;
  display: flex;
  align-items: center;
  gap: 0.15rem;
  margin: 0;
  padding: 0;
  font-size: 0.8125rem;
  font-weight: 700;
}
.langs li + li::before {
  content: "";
  display: inline-block;
  width: 1px;
  height: 0.8em;
  margin-right: 0.15rem;
  background: color-mix(in srgb, currentColor 35%, transparent);
  vertical-align: -0.05em;
}
.langs a,
.langs__current {
  display: inline-block;
  padding: 0.3em 0.5em;
  text-decoration: none;
  border-radius: 4px;
}
/* Full language names cost roughly 190px, which at 390px leaves nothing for
   the CTA and wraps the sticky header onto two rows. Codes below the nav
   breakpoint, full names above it. The link keeps a title attribute so the
   short form is still identifiable. */
.langs__full { display: none; }
.langs__short { display: inline; }
@media (min-width: 56em) {
  .langs__full { display: inline; }
  .langs__short { display: none; }
}

.langs a { opacity: 0.7; }
.langs a:hover, .langs a:focus-visible { opacity: 1; background: color-mix(in srgb, var(--brand-2) 35%, transparent); }
.langs__current { color: var(--brand-1-deep); }

/* Photographs vs placeholders --------------------------------------------
   A slot either holds a real image or holds the description of the shot it
   is still waiting for. Both keep the same --ratio so swapping one for the
   other never changes the layout. */

/* A real photo must not keep the placeholder's dashed border and pastel
   gradient. Those two things are the visual language for "this slot is still
   empty", and leaving them on a finished image makes the page read as less
   done than it is. Solid border, dark backing behind the image. */
.shot--photo {
  position: relative;
  overflow: hidden;
  border-style: solid;
  border-color: var(--ink);
  background: var(--ink);
}
.shot__img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* The illustration caption is a disclosure, not decoration: these images show
   an event that has not happened, on a site that will take registration
   money. It sits ON the image so it cannot be separated from what it
   describes, and it is opaque enough to read against any of them. */
.shot__illus {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  margin: 0;
  padding: 0.5em 0.85em;
  font-size: 0.75rem;
  line-height: 1.3;
  font-weight: 600;
  color: #fff;
  background: color-mix(in srgb, #000 68%, transparent);
  backdrop-filter: blur(2px);
}

/* Header ----------------------------------------------------------------- */

.site-head {
  position: sticky;
  top: 0;
  z-index: 60;
  flex-wrap: wrap;
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 0.75rem clamp(1rem, 4vw, 2.5rem);
  background: color-mix(in srgb, var(--paper) 88%, transparent);
  backdrop-filter: blur(10px);
  border-bottom: 2px solid color-mix(in srgb, var(--ink) 10%, transparent);
}
.wordmark { text-decoration: none; display: flex; align-items: center; }
/* The real vector wordmark. Height-constrained with width auto so the SVG's
   own 521:149 aspect drives the box and the mark can never be squashed.
   Sizes went up on 2026-08-08 along with the crop in tools/extract_brand.py:
   before that crop the SVG carried the source PDF's margins, so half of any
   height set here was empty space and the mark read small in a header it was
   supposed to anchor. Both changes were needed -- either one alone leaves it
   either still small or scaled past the row height. */
.wordmark__img {
  height: 2.25rem;
  width: auto;
  display: block;
}
@media (min-width: 56em) {
  .wordmark__img { height: 2.9rem; }
}

/* Mascot ------------------------------------------------------------------
   Booo is a supporting device. Her brief answered "real photos of people"
   for what sells the event, so the mascot never takes a hero slot and never
   competes with a photograph. Decorative everywhere it appears. */

.mascot { display: block; height: auto; pointer-events: none; }

.mascot--experience {
  width: clamp(90px, 14vw, 150px);
  margin: -0.5rem 0 -1rem auto;
}

.mascot--cta {
  width: clamp(110px, 18vw, 190px);
  margin: 0 auto 0.5rem;
}
.site-nav { display: none; gap: 1.25rem; }
.site-nav a { text-decoration: none; font-weight: 600; font-size: 0.95rem; }
.site-nav a:hover { color: var(--brand-1); }

/* Exactly one `margin-left: auto` in the header at any breakpoint. With the
   nav hidden on mobile, the switcher carries it; once the nav appears, the
   nav takes it and everything after simply follows. Two competing autos
   split the free space between them, which reads as a layout bug. */
.site-head .langs { margin-left: auto; }

/* No header CTA on mobile. Even with the languages shortened to codes, the
   wordmark, the switcher and the button do not fit on one 390px row, so the
   sticky header became two rows deep at every scroll position. The hero CTA
   sits immediately below this header and the signup section repeats it, so
   what is dropped here is a duplicate, not the conversion path. */
.site-head .btn { display: none; }

@media (min-width: 56em) {
  .site-nav { display: flex; margin-left: auto; }
  .site-head .langs { margin-left: 1.5rem; }
  .site-head .btn { display: inline-block; margin-left: 1rem; }
}

/* Hero ------------------------------------------------------------------- */

.hero { position: relative; }
.hero__media .shot { border-radius: 0; border-left: 0; border-right: 0; }
.hero__body { padding: clamp(1.75rem, 5vw, 3.5rem) clamp(1.25rem, 4vw, 2.5rem); }
.hero__kicker { margin-bottom: 1rem; }
.hero__title { color: var(--brand-1); margin-bottom: 0.15em; }
/* Sized by WIDTH, not height. The text it replaced was eight capitals at up
   to 7rem, which is ~34rem wide -- matching that presence off a height value
   would mean guessing the font's cap height. Width also means the mark grows
   with the column instead of leaving a short logo stranded in a wide hero.
   The direction colour rules above it are inert now and kept only so a
   text-based h1 still renders correctly if the image ever fails to load. */
.hero__wordmark {
  width: min(100%, 34rem);
  height: auto;
  display: block;
  margin-bottom: 0.35em;
}
.hero__slogan {
  font-family: var(--font-display);
  font-weight: var(--display-weight);
  font-size: clamp(1.15rem, 3.2vw, 1.9rem);
  line-height: 1.2;
  margin-bottom: 0.6em;
}
.hero__sub { font-size: clamp(1rem, 2vw, 1.2rem); max-width: 46ch; opacity: 0.85; }
.hero__note { font-size: 0.8125rem; opacity: 0.65; max-width: 42ch; margin-top: 0.9rem; }
/* The organizer credit inherits the hero body colour on purpose: that pair is
   already measured by check_contrast.py, and a new colour here would be an
   unmeasured one. No opacity either - this line exists to be read. */
.hero__org { font-size: 0.875rem; margin-top: 1rem; }
.hero__org a { color: inherit; font-weight: 700; text-decoration: underline; text-underline-offset: 0.15em; }
.site-foot__org { text-decoration: none; color: inherit; }
.contact__link { color: inherit; text-underline-offset: 0.15em; }
.hero__scroll {
  display: block;
  width: fit-content;
  margin: 0 clamp(1.25rem, 4vw, 2.5rem) 2rem;
  font-size: 0.85rem;
  font-weight: 700;
  text-decoration: none;
  border-bottom: 2px solid var(--brand-2);
  padding-bottom: 0.15rem;
}

/* Sections --------------------------------------------------------------- */

.sect { padding: clamp(3.5rem, 9vw, 7rem) 0; }
.sect--alt { background: var(--surface); }

.band {
  background: var(--brand-4);
  color: var(--paper);
  padding: clamp(3.5rem, 9vw, 7rem) 0;
  text-align: center;
}
.band .eyebrow { color: var(--brand-3); }
.band .lede { opacity: 0.9; }
.pull {
  margin: 2.5rem auto 0;
  max-width: 22ch;
  font-family: var(--font-display);
  font-weight: var(--display-weight);
  font-size: clamp(1.35rem, 4vw, 2.4rem);
  line-height: 1.15;
  color: var(--brand-3);
}

.split { display: grid; gap: clamp(1.75rem, 4vw, 3rem); align-items: center; }
@media (min-width: 52em) {
  .split { grid-template-columns: 1fr 1fr; }
  .split--reverse .split__media { order: 2; }
}

.tags { list-style: none; display: flex; flex-wrap: wrap; gap: 0.5rem; padding: 0; margin: 1.5rem 0 0; }
.tags li {
  font-size: 0.8125rem;
  font-weight: 700;
  padding: 0.4em 0.85em;
  border-radius: 999px;
  background: color-mix(in srgb, var(--brand-2) 30%, var(--paper));
  border: 1.5px solid color-mix(in srgb, var(--ink) 12%, transparent);
}

/* Cards ------------------------------------------------------------------ */

.cards { display: grid; gap: 1.25rem; margin-top: 2.5rem; }
@media (min-width: 44em) { .cards { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 72em) { .cards { grid-template-columns: repeat(4, 1fr); } }

.card {
  background: var(--paper);
  border: 2px solid var(--ink);
  border-radius: var(--radius);
  padding: clamp(1.25rem, 3vw, 1.75rem);
  box-shadow: 6px 6px 0 0 var(--brand-2);
}
.card__num {
  display: block;
  font-family: var(--font-display);
  font-weight: var(--display-weight);
  font-size: 2rem;
  line-height: 1;
  color: var(--brand-1);
  margin-bottom: 0.5rem;
}
.card p { margin: 0; font-size: 0.95rem; }
.card:nth-child(2) { box-shadow: 6px 6px 0 0 var(--brand-3); }
.card:nth-child(3) { box-shadow: 6px 6px 0 0 var(--brand-1); }
.card:nth-child(4) { box-shadow: 6px 6px 0 0 var(--brand-4); }

/* Costume options -------------------------------------------------------- */

.options { list-style: none; counter-reset: opt; padding: 0; margin: 0; display: grid; gap: 1rem; }
.option {
  counter-increment: opt;
  position: relative;
  padding: 1.15rem 1.25rem 1.15rem 3.5rem;
  border-radius: var(--radius);
  background: var(--paper);
  border: 2px solid color-mix(in srgb, var(--ink) 15%, transparent);
}
.option::before {
  content: counter(opt);
  position: absolute;
  left: 1rem; top: 1.15rem;
  width: 1.9rem; height: 1.9rem;
  display: grid; place-items: center;
  border-radius: 999px;
  background: var(--brand-1-deep);
  color: #fff;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 0.95rem;
}
.option h3 { margin-bottom: 0.25rem; }
.option p { margin: 0 0 0.5rem; font-size: 0.95rem; }

/* Timeline --------------------------------------------------------------- */

.timeline { list-style: none; padding: 0; margin: 2.5rem 0 0; }
.timeline__row {
  display: grid;
  grid-template-columns: 4.25rem 1fr;
  gap: 1rem;
  padding: 1.15rem 0;
  border-top: 2px solid color-mix(in srgb, var(--ink) 12%, transparent);
}
.timeline__row:last-child { border-bottom: 2px solid color-mix(in srgb, var(--ink) 12%, transparent); }
.timeline__time {
  font-family: var(--font-display);
  font-weight: var(--display-weight);
  /* 1.15rem is 18.4px: just under the 18.66px bold threshold for "large
     text", so this is held to 4.5:1 and needs the deep magenta. */
  font-size: 1.15rem;
  color: var(--brand-1-deep);
  padding-top: 0.1rem;
}
.timeline__body h3 { font-size: 1.1rem; margin-bottom: 0.2rem; }
.timeline__body p { margin: 0; font-size: 0.95rem; opacity: 0.85; }

/* Gallery ---------------------------------------------------------------- */

.grid4 { display: grid; gap: 1rem; margin-top: 2.5rem; grid-template-columns: repeat(2, 1fr); }
@media (min-width: 60em) { .grid4 { grid-template-columns: repeat(4, 1fr); } }

/* Signup ----------------------------------------------------------------- */

.signup {
  /* Whole-section white-on-magenta. The plain brand magenta is 3.53:1 here,
     which fails for every line of body copy in the block. */
  background: var(--brand-1-deep);
  color: #fff;
  padding: clamp(3.5rem, 9vw, 7rem) 0;
  text-align: center;
}
.signup .eyebrow { color: var(--brand-3); }
.signup .btn { background: var(--ink); box-shadow: 0 6px 0 0 #000; }
.signup__form {
  display: grid;
  gap: 0.75rem;
  margin: 2rem auto 0;
  max-width: 34rem;
}
@media (min-width: 34em) { .signup__form { grid-template-columns: 1fr auto; } }

/* `display: grid` above is an AUTHOR rule and the `hidden` attribute is only
   a UA rule, so author wins and setting form.hidden = true leaves the form
   fully rendered. Without this line the success panel appears BELOW a form
   that still looks ready to be filled in again.
   Found by screenshotting the success state, not by any assertion: the probe
   checked `form.hidden === true`, which was true the whole time. Anything
   that sets `display` needs its own [hidden] rule or the attribute is inert. */
.signup__form[hidden] { display: none; }
/* Scoped to the email field on purpose. This was a bare `input` selector
   until the CASL consent checkbox arrived, and 0.95em of padding on a
   checkbox renders it as a large empty square. */
.signup__form input[type="email"] {
  font: inherit;
  padding: 0.95em 1.1em;
  border: 2px solid var(--ink);
  border-radius: var(--radius);
  background: #fff;
  color: var(--ink);
  min-width: 0;
}

/* CASL express consent. Spans the full width so it sits under the field and
   the button rather than being dealt into the two-column grid, and reads
   left-aligned because it is a statement being agreed to, not a caption. */
.signup__consent {
  grid-column: 1 / -1;
  display: flex;
  align-items: flex-start;
  gap: 0.6rem;
  text-align: left;
  font-size: 0.9rem;
  line-height: 1.4;
  cursor: pointer;
}
.signup__consent input {
  flex: none;
  width: 1.15em;
  height: 1.15em;
  margin-top: 0.15em;
  accent-color: var(--ink);
  cursor: pointer;
}

.signup .fineprint { opacity: 0.8; }

/* Signup success / error.
   Both are opaque panels that break OUT of the section's white-on-magenta so
   a state change is unmistakable. An earlier version kept the magenta and
   used only a translucent white border; it was legible but read as more of
   the same block rather than as "this worked".

   Every value here comes from site.json, never from this file (rule 3), and
   every pair below is in check_contrast.py's PAIRS list, so a future palette
   edit that breaks one fails the build rather than shipping. */
.signup__done,
.signup__error {
  max-width: 34rem;
  margin: 2rem auto 0;
  padding: 1.5rem 1.5rem 1.5rem 3.75rem;
  border-radius: var(--radius);
  text-align: left;
  position: relative;
}

/* Success. The tint against the section's brand-1-deep is 4.72:1, which is
   what makes the panel separate; a mid-green outline was measured at 1.51:1
   against the same magenta and dropped as invisible. */
.signup__done {
  background: var(--success-tint);
  color: var(--success-deep);
  /* Mixed with BLACK, not with transparent, matching .btn. A translucent
     shadow over the section's magenta muddies to a grey-green bar instead of
     reading as a shadow - the same lighten-through-transparency trap the
     panel background avoids. */
  box-shadow: 0 8px 0 0 color-mix(in srgb, var(--success) 60%, black);
}
.signup__done-title {
  font-weight: 800;
  font-size: 1.35rem;
  margin: 0 0 0.4rem;
  color: var(--success-deep);
}
.signup__done-body { margin: 0; line-height: 1.5; }

/* Decorative tick. Lives in CSS rather than in the language files precisely
   because it is not copy - it says the same thing in all three languages and
   has no translation. aria-hidden is implicit: generated content is not
   exposed as text content, and the panel already carries role="status". */
.signup__done::before {
  content: "\2713";
  position: absolute;
  left: 1.25rem;
  top: 1.35rem;
  font-size: 1.6rem;
  line-height: 1;
  font-weight: 900;
  color: var(--success);
}

/* Error. Deliberately NOT red: the only red-adjacent hue in this palette is
   the brand magenta the section is already painted with, so a red panel on
   magenta would read as decoration rather than as a problem. Neutral paper
   with ink text is the pair check_contrast.py already verifies for body copy,
   and it separates from the magenta as hard as the green does. */
.signup__error {
  background: var(--paper);
  color: var(--ink);
  margin-top: 1rem;
  padding-left: 1.5rem;
}

/* The no-JS path is now a real page at /merci/, not a :target reveal here.
   The anchor version worked but could not be tested: :target does not match
   in headless Chrome at all (verified 2026-08-17 with a minimal repro - even
   a plain element with no `hidden` attribute returned matches(':target')
   === false). It also needed :has() to hide the form, since the form PRECEDES
   the panel in the DOM and no sibling combinator reaches backwards. Two
   untestable selectors carrying the path nobody watches was the wrong trade.

   The panel below is now revealed only by site.js, which sets form.hidden and
   clears the panel's own `hidden`. `id="merci"` stays because that is what
   getElementById finds; it no longer has any CSS meaning. */

.counter { margin-top: 2.5rem; }
.counter__num {
  display: block;
  font-family: var(--font-display);
  font-weight: var(--display-weight);
  font-size: clamp(3rem, 12vw, 6rem);
  line-height: 1;
  color: var(--brand-3);
  font-variant-numeric: tabular-nums;
}
.counter__label { font-weight: 700; letter-spacing: 0.02em; }

/* FAQ -------------------------------------------------------------------- */

.faq { margin-top: 2.5rem; }
.faq__item { border-top: 2px solid color-mix(in srgb, var(--ink) 12%, transparent); }
.faq__item:last-child { border-bottom: 2px solid color-mix(in srgb, var(--ink) 12%, transparent); }
.faq__item summary {
  cursor: pointer;
  list-style: none;
  padding: 1.1rem 2.5rem 1.1rem 0;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(1rem, 2.2vw, 1.2rem);
  position: relative;
}
.faq__item summary::-webkit-details-marker { display: none; }
.faq__item summary::after {
  content: "+";
  position: absolute;
  right: 0.25rem; top: 50%;
  transform: translateY(-50%);
  font-size: 1.6rem;
  line-height: 1;
  color: var(--brand-1);
}
.faq__item[open] summary::after { content: "\2013"; }
.faq__item p { margin: 0 0 1.2rem; opacity: 0.85; }

/* Contact ---------------------------------------------------------------- */

.contact { margin: 2.5rem 0 0; }
.contact__row {
  display: grid;
  gap: 0.35rem;
  padding: 0.9rem 0;
  border-top: 2px solid color-mix(in srgb, var(--ink) 10%, transparent);
}
@media (min-width: 44em) { .contact__row { grid-template-columns: 18rem 1fr; align-items: center; } }
.contact dt { font-weight: 700; }
/* Milena has not sent contact details yet. The empty rule renders as a
   visible blank so the gap is obvious rather than silently missing. */
.contact__empty {
  margin: 0;
  height: 1.6rem;
  border-radius: 6px;
  background: repeating-linear-gradient(
    45deg,
    color-mix(in srgb, var(--ink) 7%, transparent) 0 6px,
    transparent 6px 12px
  );
  border: 1.5px dashed color-mix(in srgb, var(--ink) 22%, transparent);
}
/* Quebec pre-contract disclosure: legal name, address and phone must be
   shown before any payment. Flagged so they cannot be dropped by accident. */
.contact__flag {
  display: block;
  font-size: 0.7rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--brand-1-deep);
  opacity: 0.9;
}

/* Footer ----------------------------------------------------------------- */

.site-foot {
  background: var(--ink);
  color: var(--paper);
  padding: clamp(3rem, 7vw, 5rem) 0 2.5rem;
}
.site-foot__label {
  font-family: var(--font-display);
  font-size: 0.75rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  opacity: 0.55;
  margin: 2rem 0 0.5rem;
}
.site-foot__label:first-child { margin-top: 0; }
.site-foot__org { display: flex; align-items: center; gap: 1rem; margin: 0; }
.site-foot__logo { width: 4.25rem; height: auto; flex: none; }
.site-foot__orgname {
  font-family: var(--font-display);
  font-weight: var(--display-weight);
  font-size: 1.5rem;
}
.sponsors { display: grid; gap: 0.75rem; grid-template-columns: repeat(2, 1fr); }
@media (min-width: 44em) { .sponsors { grid-template-columns: repeat(4, 1fr); } }
.sponsors__slot {
  aspect-ratio: 3/2;
  display: grid;
  place-items: center;
  text-align: center;
  padding: 0.5rem;
  font-size: 0.7rem;
  opacity: 0.5;
  border: 1.5px dashed currentColor;
  border-radius: 8px;
}
.site-foot__legal { list-style: none; padding: 0; margin: 2.5rem 0 0; display: flex; flex-wrap: wrap; gap: 1.25rem; font-size: 0.9rem; }
.site-foot .fineprint { opacity: 0.5; }

/* ------------------------------------------------------------------------
   Direction hooks. Same markup, three different personalities.
   ------------------------------------------------------------------------ */

/* B · Festival poster: hard edges, massive type, flat blocks.
   Directions A and C were deleted 2026-08-06 along with the picker: the
   client chose B on 08-04. */
.dir-b .hero__title { line-height: 0.86; }
.dir-b .card { box-shadow: none; border-width: 3px; }
.dir-b .card:nth-child(n) { box-shadow: none; }
/* Both card colours are light, so both take ink type. The card number
   inherits the brand magenta by default, which lands at 1.61:1 on the cyan
   card: the single worst pair in the palette and completely invisible.
   Neither card can carry a magenta numeral, deep or otherwise. */
.dir-b .card:nth-child(odd) { background: var(--brand-2); }
.dir-b .card:nth-child(odd) .card__num { color: var(--ink); }
.dir-b .card:nth-child(even) { background: var(--brand-3); color: var(--ink); }
.dir-b .card:nth-child(even) .card__num { color: var(--ink); }
.dir-b .tags li { border-radius: 0; background: var(--brand-2); }
.dir-b .btn { text-transform: uppercase; letter-spacing: 0.03em; }
.dir-b .eyebrow { background: var(--ink); color: var(--brand-2); display: inline-block; padding: 0.3em 0.7em; }
.dir-b .pull { color: var(--brand-2); max-width: 26ch; }

/* Hero variant ----------------------------------------------------------- */

/* poster: copy block first on flat colour, photo as a hard block beside it.
   The fullbleed and split variants went with directions A and C. */
.hero-poster .hero__body { background: var(--brand-2); }
.hero-poster .hero__title { color: var(--brand-4); }
@media (min-width: 52em) {
  /* The min-height is what stops the photo resizing between languages, and it
     is measured rather than chosen. With align-items:stretch and the shot at
     height:100%/aspect-ratio:auto, the image has no height of its own: it takes
     whichever column is taller, which is always the copy column. So the hero
     photo grew or shrank purely on how a translated string happened to wrap,
     and since it is object-fit:cover, a different height meant a different crop
     of the photograph. Measured 2026-08-20 across fr/en/es at eight viewport
     widths: the image ranged 714-784px at the same width, e.g. at 1100px the
     Spanish hero was 61px shorter than the French one. 800 dominates the
     tallest copy column found anywhere (784px, English at 900px), so the row
     height is now a design decision and all three languages match. If the copy
     ever grows past 800 the divergence comes back - re-measure, do not nudge.

     It has to be grid-template-rows and NOT min-height on .hero. The scroll
     link is a second grid row spanning both columns, so a min-height on the
     section is shared between the two rows and the image only gets what is
     left: min-height:800px was tried first on 2026-08-20 and moved the French
     image from 714 to 728 while English stayed at 784. Measured, not assumed. */
  .hero-poster .hero { display: grid; grid-template-columns: 1.1fr 0.9fr; align-items: stretch; grid-template-rows: minmax(800px, auto) auto; }
  .hero-poster .hero__body { display: flex; flex-direction: column; justify-content: center; }
  .hero-poster .hero__media .shot { height: 100%; aspect-ratio: auto; border: 0; }
  .hero-poster .hero__scroll { grid-column: 1 / -1; }
}

/* fullbleed: the photo fills the top of the page and the copy sits on it.
   Added 2026-08-06 as the alternate for the client to compare against poster.
   It is what her own answer #4 asked for ("una fotografía gigante que haga
   soñar al visitante, con un botón de inscripción muy visible") and what
   three of her four reference sites do.

   Below 52em it is NOT an overlay: the photo stacks above a solid purple
   copy panel. Overlaying text on a busy photograph at phone width is how
   hero copy becomes unreadable, and there is no scrim strong enough to fix
   it that does not also hide the photo. */

.hero-fullbleed .hero__body {
  background: var(--brand-4);
  color: var(--paper);
}
.hero-fullbleed .hero__title { color: var(--brand-3); }
.hero-fullbleed .hero__media .shot { border-left: 0; border-right: 0; }

/* The TBC chips are drawn in ink for light backgrounds and vanish on purple. */
.hero-fullbleed .hero__body .tbc {
  color: var(--paper);
  border-color: color-mix(in srgb, var(--paper) 55%, transparent);
  background: repeating-linear-gradient(
    45deg,
    color-mix(in srgb, var(--paper) 16%, transparent) 0 6px,
    transparent 6px 12px
  );
}

@media (min-width: 52em) {
  .hero-fullbleed .hero { display: grid; }
  .hero-fullbleed .hero__media,
  .hero-fullbleed .hero__body { grid-area: 1 / 1; }
  .hero-fullbleed .hero__media .shot {
    aspect-ratio: auto;
    height: min(84vh, 800px);
    border: 0;
  }
  /* Panel right, not left. The hero photograph's subjects (the volleyball
     rally) sit centre-left, and a left-hand panel buried them, leaving a
     flamingo's back as the visible hero. Where the copy sits has to follow
     the photo, not a default. Revisit this if the hero image changes. */
  .hero-fullbleed .hero__body {
    align-self: center;
    justify-self: end;
    max-width: 44rem;
    /* Bottom margin clears the illustration caption pinned to the photo. */
    margin: 0 clamp(1.5rem, 5vw, 4rem) 3rem;
    padding: clamp(1.5rem, 3vw, 2.5rem);
    border-radius: var(--radius);
    background: color-mix(in srgb, var(--brand-4) 92%, transparent);
    backdrop-filter: blur(3px);
  }
  .hero-fullbleed .hero__scroll { position: relative; z-index: 2; }
}

/* Confirmation page (/merci/) --------------------------------------------
   Reuses the signup section's own treatment so the page a visitor lands on
   after a JS-off submit looks like the block they submitted from, rather than
   like a different site. Only the switcher and the CTA need placing. */
.merci__main { min-height: 100vh; display: flex; align-items: center; }
.merci__lang {
  justify-content: center;
  margin-bottom: 1.5rem;
  color: #fff;
}
.merci__lang .langs__current { color: var(--brand-3); }
.merci__lang a { color: #fff; }
.merci__cta { margin-top: 2rem; }
