/* ==========================================================================
   BALTIC WAVE FEST 2026 — landing
   Identity source: the 22.08 Gdańsk poster (Vsi Svoi Agency).
   Palette: electric blue canvas · magenta panel · acid yellow · black.
   Motif: the logo's wave — it runs through hero, lineup and the final block.
   ========================================================================== */

/* WEIGHT FALLS AS SIZE RISES — the page's one typographic law.
   Gilroy ships 400-900, but 800 and 900 are deliberately NOT declared here: an
   undeclared face cannot be reached for by accident, and a synthesised fake bold
   is louder than either. Heavy weight at display size is what makes a layout
   read cheap; the same word set large and thin reads expensive. So:

     >= 44px  display .... 400   (.final__big, .sec-title, .ticket__day)
     28-46px  large ...... 500-700 (the price 500, a name on a card 700 — over a
                                  photograph the weight does structural work)
     16-34px  mid ........ 600   (tags, event name, credits, leads)
     <= 21px  UI ......... 700   (buttons, the ribbon, the day counter)
     body ................ 500

   Bold is not banned — it is confined to the sizes where it is a texture rather
   than a shout. */
@font-face{font-family:Gilroy;src:url(../fonts/gilroy-regular.woff2) format('woff2');font-weight:400;font-style:normal;font-display:swap}
@font-face{font-family:Gilroy;src:url(../fonts/gilroy-medium.woff2) format('woff2');font-weight:500;font-style:normal;font-display:swap}
@font-face{font-family:Gilroy;src:url(../fonts/gilroy-semibold.woff2) format('woff2');font-weight:600;font-style:normal;font-display:swap}
@font-face{font-family:Gilroy;src:url(../fonts/gilroy-bold.woff2) format('woff2');font-weight:700;font-style:normal;font-display:swap}

:root{
  --blue-900:#04165a;
  --blue-800:#072a92;
  --blue-700:#0b3fd4;
  --blue-500:#1a68f5;
  --magenta:#ce02ff;
  --sun:#ffe800;
  --ink:#08080d;
  --ink-2:#16161c;
  --paper:#ffffff;

  --wrap:1180px;
  --gut:clamp(20px,5vw,56px);
  --r-pill:999px;
  --r-card:26px;

  /* A short ladder with real gaps: --mark (the wordmark) · --sub (the date,
     the second fact on the page) · --act (the lineup and the action) · --meta
     (page furniture). v1 read as mush because it had six sizes and no ratio
     between them — the fix is a ladder, not one flattened size. */
  --sub:clamp(27px,2.7vw,39px);
  --act:clamp(15px,1.25vw,18px);
  --meta:clamp(14px,1.1vw,17px);

  --ease:cubic-bezier(.22,.61,.36,1);
}

*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%;text-size-adjust:100%}
body{
  margin:0;
  background:var(--ink);
  color:var(--paper);
  font-family:Gilroy,"Segoe UI",system-ui,sans-serif;
  font-weight:500;
  font-size:clamp(16px,1.05vw,18px);
  line-height:1.5;
  overflow-x:hidden;
}
img{max-width:100%;height:auto;display:block}
a{color:inherit}
h1,h2,h3,p{margin:0}

.wrap{width:100%;max-width:var(--wrap);margin-inline:auto;padding-inline:var(--gut)}

/* the topbar is fixed, so anchored sections must clear it */
:where(#lineup,#tickets,#location){scroll-margin-top:clamp(64px,8vh,88px)}

.skip{position:absolute;left:-9999px;top:0;background:var(--sun);color:#000;padding:12px 18px;z-index:100;border-radius:0 0 12px 0}
.skip:focus{left:0}

:where(a,button,iframe):focus-visible{outline:3px solid var(--sun);outline-offset:3px;border-radius:6px}

/* ------------------------------------------------------------- wave motif */
.wave-set use{
  fill:none;
  stroke:currentColor;
  stroke-width:2.4;
  stroke-linecap:round;
  vector-effect:non-scaling-stroke;
}

/* ------------------------------------------------------------------ buttons */
.btn{
  --bg:var(--sun); --fg:#000;
  display:inline-flex;align-items:center;justify-content:center;gap:.5em;
  padding:14px 26px;
  background:var(--bg);color:var(--fg);
  font-weight:700;font-size:1rem;line-height:1;
  border-radius:var(--r-pill);
  text-decoration:none;
  white-space:nowrap;
  transition:transform .28s var(--ease),box-shadow .28s var(--ease),background-color .28s var(--ease);
  will-change:transform;
}
.btn:hover{transform:translateY(-3px);box-shadow:0 12px 0 -4px rgba(0,0,0,.28)}
.btn:active{transform:translateY(-1px)}
/* The primary action was the only object in the hero tied to no scale at all —
   160x44 at 16px on every width, so it shrank against everything else as the
   screen grew. It now rides the same --act step as the lineup beside it. */
.btn--hero{
  padding:clamp(16px,2.1svh,22px) clamp(32px,4.6vw,70px);
  font-size:calc(var(--act) * 1.12);
  letter-spacing:0;
}
/* The ghost pill is gone with the last thing that used it. The only non-buying
   action on the page (the route link) is blue-on-paper now, which is the rule
   the ticket set — a hollow outline was a third button style for one button. */

/* ------------------------------------------------------------------ topbar */
/* Transparent over the hero so the hero's yellow pill is the only call to
   action in the first screen. The bar earns its background on scroll. */
.topbar{
  position:fixed;top:0;left:0;right:0;z-index:50;
  display:flex;align-items:center;gap:clamp(14px,3vw,34px);
  padding:14px var(--gut);
  background:transparent;
  border-bottom:1px solid transparent;
  transition:background-color .3s var(--ease),border-color .3s var(--ease);
}
.topbar.is-stuck{
  background:rgba(4,22,90,.78);
  -webkit-backdrop-filter:blur(14px);
  backdrop-filter:blur(14px);
  border-bottom-color:rgba(255,255,255,.14);
}
/* Over the hero the full-size wordmark is already on screen — a second copy in
   the bar is just the same object twice. It arrives when the hero leaves. */
.topbar__brand{
  display:block;flex:0 0 auto;
  opacity:0;visibility:hidden;
  transition:opacity .3s var(--ease),visibility .3s;
}
.topbar.is-stuck .topbar__brand{opacity:1;visibility:visible}
.topbar__brand img{width:clamp(84px,9vw,116px);height:auto;display:block}
.topbar__nav{
  display:flex;gap:24px;margin-inline-start:auto;
  font-weight:600;font-size:var(--meta);
}
.topbar__nav a{text-decoration:none;opacity:.82;transition:opacity .2s}
.topbar__nav a:hover{opacity:1}
.topbar__cta{
  padding:10px 20px;font-size:var(--meta);
  opacity:0;visibility:hidden;transform:translateY(-6px);
  transition:opacity .3s var(--ease),transform .3s var(--ease),visibility .3s;
}
.topbar.is-stuck .topbar__cta{opacity:1;visibility:visible;transform:none}

/* ================================================================== HERO */
/* One poster, not a poster with a UI strip bolted under it. The old navy
   `.hero__floor` was a second material with its own background and a hard rule
   across the artists' shins; the type now sits straight on the canvas and the
   photograph fades out to meet it. */
.hero{
  position:relative;
  isolation:isolate;
  overflow:hidden;
  background:
    radial-gradient(120% 80% at 78% 8%, #2a7bff 0%, rgba(42,123,255,0) 58%),
    linear-gradient(168deg,#0b3fd4 0%,#0a34b2 46%,#04165a 100%);
  min-height:100svh;
  display:flex;
  flex-direction:column;

  /* The artists' band is the one measured object; the sun and both tags are
     positioned as a share of it, so they travel with the figures. */
  --crowd-h:clamp(300px,60svh,620px);
  /* Share of the source photo left visible. Landmarks in artists-hero.webp:
     standing heads .05 · seated head .243 · his eyes .314 · his chin .403 ·
     hands/knees .59. At .63 the cut lands below the knees, and the bottom
     fifth is dissolved by the mask anyway — so no crop line falls on a face
     or on a joint. */
  --crowd-show:.63;
  /* 1500x1407 source → 1.0661. Stating the box width explicitly (instead of
     letting shrink-to-fit find it) is what makes the tags' percentages mean
     something fixed. */
  --crowd-w:calc(var(--crowd-h) / var(--crowd-show) * 1.0661);
  --mark-h:clamp(150px,38svh,470px);
  --mark-pad:var(--gut);
  --mark-top:clamp(56px,7svh,96px);
  /* What the wordmark ACTUALLY renders at: --mark-h is only a ceiling, because
     below ~430px the 1.9243 lockup runs out of width first and height follows
     the width instead. Stating both cases here is what lets other objects be
     placed against the mark's real bottom edge rather than against a guess. */
  --mark-real:min(var(--mark-h), calc((100vw - var(--mark-pad) * 2) / 1.9243));
  /* The line of standing heads, in page coordinates: the crowd is floor-anchored,
     and the heads sit at .05 of the full photo (crop landmarks above). */
  --heads-y:calc(100svh - var(--crowd-h) + var(--crowd-h) / var(--crowd-show) * .05);
}
.hero__waves{position:absolute;inset:-6% -12%;z-index:0;color:rgba(255,255,255,.24);pointer-events:none}
.hero__waves svg{width:100%;height:100%}

/* --- the wordmark: the one large object on the page ---------------------
   Sized off viewport HEIGHT, not width — a 1.92:1 lockup grows tall as it
   grows wide, and height is the scarce axis on a landscape screen. It sits
   near the top and is allowed to overlap the top of the crowd's box, which is
   empty air above the tallest head. */
.hero__mark{
  position:relative;z-index:2;
  margin:var(--mark-top) 0 0;
  padding-inline:var(--mark-pad);
  text-align:center;
  pointer-events:none;
}
.hero__mark img{
  display:inline-block;
  width:min(calc(var(--mark-h) * 1.9243), 100%);
  height:auto;
  filter:drop-shadow(0 22px 52px rgba(0,0,0,.32));
}

/* --- the crowd, and everything anchored to it --------------------------- */
.hero__crowd{
  position:absolute;z-index:3;
  left:50%;bottom:0;translate:-50% 0;
  width:var(--crowd-w);
  height:var(--crowd-h);
  pointer-events:none;
}
/* The poster's yellow sun. It stands behind the group and is measured off the
   group, so it can never land tangent to the line of heads the way a
   viewport-sized circle did. Solid fill, not a soft radial: a blurred patch
   reads as a glow effect, a circle reads as a drawn object. */
.hero__sun{
  position:absolute;z-index:0;
  left:50%;top:10%;translate:-50% 0;
  width:32%;aspect-ratio:1;
  background:var(--sun);
  border-radius:50%;
}
.hero__crowd-clip{
  position:absolute;inset:0;z-index:1;
  overflow:hidden;
  -webkit-mask-image:linear-gradient(to bottom,#000 0 var(--fade-from,72%),rgba(0,0,0,0) 97%);
  mask-image:linear-gradient(to bottom,#000 0 var(--fade-from,72%),rgba(0,0,0,0) 97%);
}
.hero__crowd-clip picture{display:block}
.hero__crowd-clip img{
  display:block;
  height:calc(var(--crowd-h) / var(--crowd-show));
  width:100%;max-width:none;
}

/* --- the two paper tags -------------------------------------------------
   One family, two instances: white stock, dark ink, a slight tilt. White is
   what makes them read as objects laid on the poster rather than more page —
   and it keeps yellow meaning exactly two things on this page, the sun and
   the action. `rotate` and `translate` are the standalone properties on
   purpose, so GSAP can own `transform` without a second owner fighting it. */
.tag{
  position:absolute;z-index:4;
  background:var(--paper);color:var(--blue-900);
  border-radius:18px;
  box-shadow:0 14px 34px rgba(2,10,48,.30);
  text-align:center;
  white-space:nowrap;
  line-height:1;
}
.tag[hidden]{display:none}
/* Vertical: a share of the crowd's height, counted up from the hero's floor —
   the crowd's bottom IS the hero's bottom, so this needs no containing block.
   Horizontal: the crowd's own edge, but floored/capped against the viewport so
   a tag can never be pushed off-screen when the group overflows the window. */
.tag--days{
  /* .455, not .44: at 1920 the crowd is at its 620px ceiling while the wordmark
     is still growing, and .44 let the tag's box touch the mark's first letter. */
  left:max(calc(50vw - var(--crowd-w) * .455), calc(var(--gut) + 52px));
  bottom:calc(var(--crowd-h) * var(--days-y,1.015));
  translate:-50% 50%;
  rotate:-4deg;
  padding:.62em .9em .68em;
  display:grid;gap:.18em;
}
.tag__n{
  display:block;
  font-weight:600;font-size:clamp(20px,2vw,30px);
  letter-spacing:-.02em;text-transform:uppercase;
  font-variant-numeric:tabular-nums;
}
.tag__t{
  display:block;
  font-weight:700;font-size:clamp(10px,.85vw,13px);
  letter-spacing:0;text-transform:uppercase;
  color:var(--blue-700);
}
.tag--city{
  left:min(calc(50vw + var(--crowd-w) * .38), calc(100vw - var(--gut) - 60px));
  bottom:calc(var(--crowd-h) * var(--city-y,.42));
  translate:-50% 50%;
  rotate:-5deg;
  padding:.34em .66em .40em;
  border-radius:var(--r-pill);
  font-weight:600;font-size:clamp(19px,2.2vw,34px);
  letter-spacing:-.01em;text-transform:uppercase;
}

/* --- the foot: date, lineup, action ------------------------------------
   Grouped left and kept together, so the eye reads date → who → buy in one
   sweep instead of crossing 550px of empty band. The open right half is the
   counterweight to the city tag hanging above it. */
.hero__foot{
  position:relative;z-index:5;
  margin-top:auto;
  padding:clamp(64px,11svh,130px) var(--gut) clamp(22px,4svh,46px);
}
/* Measured: white caps over the photograph came out at 2.2:1 on a phone and
   2.95:1 on a desktop — under the 3:1 floor for large text. This is the canvas
   colour itself, faded in from nothing, so it deepens the ground rather than
   laying a second surface over it: no edge anywhere, unlike the navy strip it
   replaced. */
.hero__foot::before{
  content:'';
  position:absolute;inset:0;z-index:-1;
  background:linear-gradient(to top,
    rgba(4,22,90,.95) 0%,
    rgba(4,22,90,.82) 45%,
    rgba(4,22,90,0) 100%);
  pointer-events:none;
}
.hero__foot-in{
  width:100%;max-width:var(--wrap);margin-inline:auto;
  display:flex;align-items:center;justify-content:center;flex-wrap:wrap;
  gap:clamp(16px,2.4vw,38px);
}
/* An outlined plate, not a filled one: it shares the row with the yellow
   button and must not compete with it. */
.hero__day{
  flex:0 0 auto;
  font-weight:500;font-size:var(--sub);line-height:1;
  letter-spacing:-.025em;
  padding:.32em .42em .36em;
  border:2px solid rgba(255,255,255,.82);
  border-radius:18px;
  font-variant-numeric:tabular-nums;
}
/* Caps at display weight with zero tracking — the poster sets the artists this
   way. Three lines, so the lineup reads as names instead of one run-on rule. */
.hero__acts{
  flex:0 1 auto;
  font-weight:600;font-size:var(--act);line-height:1.24;
  text-transform:uppercase;letter-spacing:0;
}
.hero__foot .btn{pointer-events:auto}

/* ================================================================= RIBBON */
/* The band and the type share one path: a 56-unit stroke on it draws the
   ribbon, a <textPath> on it carries the words. Magenta keeps the role it has
   held since v1 — it owns this band and the Moon Shot tile, and nothing else.
   Yellow stays what the hero made it: the sun and the action. */
.ribbon{
  /* the block is the seam between the hero's navy and the lineup's ink, so it
     carries the transition itself instead of stacking two hard edges */
  background:linear-gradient(to bottom,var(--blue-900),var(--ink));
  overflow:hidden;
  display:flex;justify-content:center;
  padding-block:clamp(6px,1.4vw,18px);
}
/* Wider than the viewport on purpose: the wave's flat ends are cut off screen,
   and the floor keeps the band and the type legible on a phone — scaled purely
   to the container it would come out at 12px type on a 390 screen. */
.ribbon__svg{
  flex:0 0 auto;
  width:max(calc(100% + 120px),780px);
  height:auto;
  display:block;
}
.ribbon__band{fill:none;stroke:var(--magenta);stroke-width:56}
.ribbon__text{
  fill:var(--ink);
  font-family:Gilroy,"Segoe UI",system-ui,sans-serif;
  font-weight:700;font-size:25px;letter-spacing:-.01em;
}

/* ================================================================= LINEUP */
.lineup{background:var(--ink);padding-block:clamp(58px,8vw,110px)}
.sec-title{
  font-weight:400;font-size:clamp(34px,6vw,74px);line-height:.94;letter-spacing:-.025em;
  margin-bottom:clamp(24px,3.4vw,44px);
}
.sec-title--dark{color:#000}
/* --- the lineup: the original bento, restored ----------------------------
   [wide][thin] / [thin][wide] on twelve columns; one full-width card per row
   on the phone. The rail that stood here in between is gone with the four
   ready-made photo tiles it was built for.

   Position is stated per card rather than left to source order, because the
   two do not agree any more: Kavabanga has to sit in the WIDE cell of the
   second row while staying the second card a phone shows. Source order is the
   billing order; the grid coordinates are the layout.

   Each card is a plate: its own ground colour, its own thick wave, the artist
   cut out on top. The tiles used to arrive with backgrounds baked in — these
   cutouts have none, so the card has to bring the ground itself. */
.acts{display:grid;gap:clamp(14px,1.8vw,22px);grid-template-columns:repeat(12,1fr)}
.act{
  position:relative;overflow:hidden;isolation:isolate;
  border-radius:var(--r-card);
  /* -15% off every stop of the old clamp(340,40vw,470). The cutouts are anchored
     to the bottom edge and sized by WIDTH, so a shorter card takes the height out
     of the empty ground above the figures, not out of the figures: measured
     headroom above the tallest head goes 109-125px -> 38-78px, and nothing is
     clipped at any width.
     The top stop is 394 and not the arithmetic 399.5, which is a further 1.5%:
     two rows plus the gap have to clear 811px — what a 900px screen leaves under
     the topbar — and at 400 the grid came to 822 and missed by 11. The point of
     the cut was fitting that screen, so the number follows the measurement. */
  min-height:clamp(289px,34vw,394px);
  display:flex;align-items:flex-end;
  padding:clamp(18px,2.4vw,30px);
  transition:transform .5s var(--ease);
  will-change:transform;
}
.act:hover{transform:translateY(-6px)}
.act__name{
  position:relative;z-index:3;
  /* Bold is right here and wrong on the ticket's date: at 30-56px over a
     photograph the weight is what separates the name from the picture, while
     the same weight at 92px is the thing that reads cheap. Size decides. */
  font-weight:700;line-height:.88;letter-spacing:-.03em;
  font-size:clamp(30px,4.4vw,56px);
  text-transform:uppercase;
  max-width:62%;
  text-shadow:0 2px 18px rgba(0,0,0,.22);
}
.act__art{position:absolute;z-index:2;bottom:0;right:0;pointer-events:none}
.act__art img{width:100%;height:auto;display:block}

/* The ground. Same wave as the rest of the page, drawn THICK — the hero's line
   is 2.4 units and reads as a drawn line; this one is a band, which is what a
   printed plate behind a cutout wants. The width is a CSS length under
   `non-scaling-stroke`, so the band stays the same weight on a 300px card and
   on a 700px one instead of thinning out with the viewBox. */
.act__ground{position:absolute;inset:0;z-index:1;pointer-events:none}
.act__ground svg{width:100%;height:100%;display:block}
.act__waves use{
  fill:none;
  stroke:currentColor;
  stroke-width:clamp(16px,2.4vw,34px);
  stroke-linecap:round;
  vector-effect:non-scaling-stroke;
}

/* One ground colour per card, each keeping the role it holds on the rest of the
   page: yellow is the sun and the action, magenta owns the ribbon and Moon
   Shot, blue is the festival's own field, and Kavabanga keeps the charcoal and
   orange of their own post. The wave is a second tone of the same plate, never
   a fifth colour. */
.act--qp {grid-column:1 / span 7;  grid-row:1; background:var(--sun); color:#000}
.act--db {grid-column:8 / span 5;  grid-row:1; background:linear-gradient(150deg,var(--blue-700),var(--blue-900)); color:var(--paper)}
.act--ms {grid-column:1 / span 5;  grid-row:2; background:var(--magenta); color:var(--paper)}
.act--kdk{grid-column:6 / span 7;  grid-row:2; background:#232326; color:var(--paper)}

.act--qp  .act__ground{color:rgba(11,63,212,.30)}
.act--db  .act__ground{color:rgba(255,255,255,.18)}
.act--ms  .act__ground{color:rgba(4,22,90,.32)}
/* Kavabanga's wave is the ground's own tone, not a second hue.
   Orange was tried first, at .44, and came out muddy brown — not because orange
   is wrong but because a TRANSLUCENT far-away hue always lands somewhere between
   the two: 44% of #ff6a13 over #232326 IS brown, arithmetically. The choices were
   a solid orange (clean, but it turns the loudest card into a hazard sign) or a
   tone of the plate itself, which is what the group's own post does.
   Strength is matched, not guessed: the wave-to-ground luminance gap is 47 on
   Quest Pistols, 35 on Den Baurin, 13 on Moon Shot. White at .17 over this
   charcoal gives 37 — the same family, so no card's wave shouts over another's. */
.act--kdk .act__ground{color:rgba(255,255,255,.17)}

/* Cutout size is per artist, not per cell: these are four different crops with
   four different amounts of headroom, and the wide cells can carry a bigger
   figure than the narrow ones without the name colliding with it. */
/* Quest Pistols is the one group of three spread side by side, so it needs the
   most room on the right and the least width: at 70% the third figure's leg
   landed under the name. */
.act--qp  .act__art{right:-4%;width:min(62%,420px)}
.act--kdk .act__art{right:-1%;width:min(66%,440px)}
.act--ms  .act__art{right:-2%;width:min(86%,380px)}
.act--db  .act__art{right:0;width:min(84%,360px)}

/* ================================================================== EXTRAS */
/* Sits between two loud blocks — the lineup's four saturated plates above, the
   yellow till below — so it deliberately does not compete. No filled colour
   cards: the plates are the canvas lifted 5.5%, and the only object with weight
   is the afterparty, which is the one item here carrying facts. */
.extra{background:var(--blue-900);padding-block:clamp(48px,6.4vw,88px)}
.extra__grid{
  list-style:none;margin:0;padding:0;
  display:grid;gap:clamp(12px,1.6vw,20px);
  grid-template-columns:repeat(3,minmax(0,1fr));
}
.extra__item{
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:clamp(14px,1.6vw,22px);
  min-height:clamp(150px,15vw,208px);
  padding:clamp(22px,2.4vw,32px) clamp(14px,1.6vw,22px);
  background:rgba(255,255,255,.055);
  border-radius:var(--r-card);
  text-align:center;
}
.extra__ico{width:clamp(38px,4vw,54px);height:auto;display:block;opacity:.92}
.extra__name{font-weight:600;font-size:clamp(17px,1.7vw,24px);letter-spacing:-.01em}

/* Built like a lineup card — its own plate, its own thick wave — because that
   is what it is: another act on the bill, after the last one. */
/* ONE left spine: name -> terms, both flush left. The when is out of flow in the
   top-right corner (see .extra__after-when), so it does not break the spine — a
   corner tag is a different kind of object from a stacked tier, the same way the
   hero's two stickers are.
   The first cut put 22:00 (76px), the name (32px) and the note (17px) in a flex
   row with `margin-inline-start:auto` on the note. That produced the scatter:
   three objects, three sizes, two opposing rags (left-aligned act, right-aligned
   note) and a shared baseline that nobody can perceive across half a plate
   between a 76px and a 17px object. The fix is not better spacing — it is one
   left edge for everything that stays in the column. The wave owns the middle of
   the right side, the way the artist cutout owns it on a lineup card. */
.extra__after{
  position:relative;overflow:hidden;isolation:isolate;
  display:grid;justify-items:start;
  /* Only bites in the 768-900 band, where min-height exceeds the content and
     `end` left the block sitting low with a bare strip above it. Everywhere else
     the content is taller than min-height and this is a no-op. */
  align-content:center;
  row-gap:clamp(7px,.9vw,13px);
  /* Same min-height as a card in the row above: this is a fourth object of the
     same family, not a strip under them. */
  min-height:clamp(150px,15vw,208px);
  margin-top:clamp(12px,1.6vw,20px);
  padding:clamp(24px,3vw,40px) clamp(24px,3.2vw,46px);
  background:var(--ink-2);
  border-radius:var(--r-card);
}
/* A BAND, not a line — the same distinction the lineup cards make. #w1 at 2.4
   units reads as a drawn line and belongs in a wave field (the hero, the final
   block); a plate behind content wants the thick version, and the width is a
   CSS length under non-scaling-stroke so it does not thin out as the plate
   widens. First attempt used the field treatment here and the wave arrived as a
   grey smudge across the plate. */
.extra__after-wave{
  position:absolute;inset:0;z-index:-1;width:100%;height:100%;
  color:rgba(255,255,255,.13);
}
.extra__waves use{
  fill:none;stroke:currentColor;
  stroke-width:clamp(13px,1.8vw,26px);
  stroke-linecap:round;
  vector-effect:non-scaling-stroke;
}
/* The when — a corner tag, the way the hero lays a sticker on the poster rather
   than stacking one more line into the column. It leaves the left spine to the
   name and the terms, and the wave keeps the middle of the right side.
   It stays SMALL: this plate announces what happens, and only then when — the
   76px numeral of the first cut inverted that and fought the odometer one
   section below, which is where big numbers belong on this page.
   Ring is the ribbon's magenta exactly. The glyphs are the same hue lifted,
   because pure #ce02ff on this plate measures 4.36:1 — under the 4.5 a 16px
   numeral needs. The ring itself is a graphic and passes at 4.36. */
.extra__after-when{
  position:absolute;z-index:1;
  top:clamp(15px,1.9vw,26px);
  right:clamp(15px,2vw,30px);
  /* The side padding tightens on narrow screens instead of at a breakpoint: at
     320 the name is 165px in a 232px box, and every unit the tag gives back is
     clearance between it and the last letter (measured 7.7px before, 13.4 after). */
  padding:.4em clamp(.56em,1.55vw,.74em) .46em;
  border:2px solid var(--magenta);
  border-radius:var(--r-pill);
  color:color-mix(in srgb,var(--magenta) 70%,#fff);
  font-weight:600;font-size:clamp(16px,1.4vw,21px);line-height:1;
  letter-spacing:-.01em;font-variant-numeric:tabular-nums;
  white-space:nowrap;
}
/* Deliberately the LINEUP ACT's own name style — same size, weight and caps as
   .act__name. The plate claims to be another act on the bill; this is what makes
   that claim visible instead of merely stated in a comment. */
.extra__after-name{
  font-weight:700;font-size:clamp(30px,4.4vw,56px);line-height:.88;
  letter-spacing:-.03em;text-transform:uppercase;
}
/* The terms run ACROSS, not down: the loud clause and the quiet one share a
   baseline in one row, which keeps the plate a band and keeps the left edge
   single. Stacking them turned the price into a lone object in the corner. */
.extra__after-note{
  display:flex;flex-wrap:wrap;align-items:baseline;
  gap:.35em clamp(14px,1.6vw,26px);
  font-weight:500;font-size:var(--meta);line-height:1.4;
}
/* No opacity on the <p>: opacity composites the whole subtree, so the previous
   `p{opacity:.74} b{opacity:1}` could never lift the loud clause back to full —
   it was .74 too. The quiet half carries its own alpha instead. */
.extra__after-note b{font-weight:600;font-size:clamp(17px,1.5vw,21px);letter-spacing:-.015em}
.extra__after-note span{opacity:.58}

@media (max-width:720px){
  /* Three across a phone would be 100px cells. Two up, the third full width —
     which is also the reading order, so nothing is orphaned in a lonely row. */
  .extra__grid{grid-template-columns:repeat(2,minmax(0,1fr))}
  .extra__item:last-child{grid-column:1/-1;min-height:clamp(120px,30vw,160px)}
  .extra__after{row-gap:clamp(8px,2.2vw,14px)}
  /* The two clauses stop sharing a line at this width — let them stack, but
     keep the same single left edge. */
  .extra__after-note{gap:.2em;margin-top:clamp(2px,1vw,8px)}
  .extra__after-note b{flex:1 0 100%}
}

/* ================================================================ TICKETS */
/* The one bright band on a page that runs blue -> dark -> dark -> YELLOW ->
   blue -> blue -> dark, and it lands exactly on the moment of payment. That is
   why the yellow stays: it is the page's only self-advertising surface.

   The ticket on it is PAPER, not a black slab. White is not a new material
   here — the hero's two tags are already `--paper` stickers, so the ticket is
   the same stock, one size up. A black rectangle covering half the section also
   inverted the hierarchy: the object was huge and the two things a visitor came
   to click were small pills in a corner.

   So the tear runs HORIZONTALLY and the counterfoil holds the actions. The part
   you tear off is the part you act with — and it gives both buttons the full
   width of the ticket instead of a 300px column. */
.tickets{position:relative;overflow:hidden;background:var(--sun);color:#000;padding-block:clamp(58px,8vw,110px)}
.tickets__chev{position:absolute;right:clamp(-20px,-1vw,0px);top:clamp(40px,6vw,80px);width:clamp(90px,14vw,210px);color:#000;opacity:.9}
.tickets__chev svg{width:100%;height:auto;display:block}

.ticket{
  position:relative;
  background:var(--paper);color:var(--ink);
  border-radius:22px;
  overflow:hidden;              /* clips the two notches into half-circles */
}

/* --- the face: what admission this is, and what it costs ----------------- */
.ticket__top{
  display:flex;align-items:flex-start;justify-content:space-between;gap:clamp(20px,3vw,40px);
  padding:clamp(24px,3.2vw,44px);
}
/* Date, event and venue are ONE fact — which admission this is — so they are one
   block, not three lines on equal gaps. */
.ticket__when{display:flex;align-items:baseline;gap:.42em}
.ticket__day{font-weight:400;font-size:clamp(46px,6.4vw,92px);line-height:.84;letter-spacing:-.035em}
.ticket__year{font-weight:600;font-size:clamp(16px,1.5vw,22px);opacity:.45}
.ticket__event{font-weight:600;font-size:clamp(20px,2.2vw,32px);line-height:1;letter-spacing:-.02em;margin-top:.42em}
.ticket__where{font-weight:600;font-size:var(--meta);opacity:.6;margin-top:.28em}

.ticket__meta{display:grid;justify-items:end;gap:clamp(14px,2vw,22px);text-align:end}
/* The price is the only fact this section owns. On paper it cannot be yellow —
   it takes the blue of the buttons instead, so the eye travels price -> action
   instead of price -> nothing. */
.ticket__price{font-weight:600;font-size:clamp(15px,1.3vw,18px);opacity:.75;white-space:nowrap}
.ticket__price b{
  font-weight:500;font-size:clamp(30px,3.4vw,46px);letter-spacing:-.03em;
  color:var(--blue-700);opacity:1;margin-inline:.14em;
}
/* Drawn, not an image: no request, and it scales cleanly. */
.ticket__bars{
  display:block;width:clamp(110px,12vw,170px);height:clamp(30px,3.4vw,42px);opacity:.72;
  background:repeating-linear-gradient(90deg,
    currentColor 0 2px, transparent 2px 5px,
    currentColor 5px 9px, transparent 9px 11px,
    currentColor 11px 13px, transparent 13px 17px);
}

/* --- the counterfoil: the whole reason the section exists ---------------- */
.ticket__act{
  position:relative;
  display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
  gap:clamp(12px,1.4vw,18px);
  padding:clamp(22px,2.8vw,34px) clamp(24px,3.2vw,44px) clamp(22px,2.8vw,34px);
  border-block-start:3px dashed rgba(8,8,13,.2);
}
.ticket__act::before,
.ticket__act::after{
  content:'';position:absolute;top:-15px;
  width:28px;height:28px;border-radius:50%;
  background:var(--sun);        /* the section's own ground, through the punch */
}
.ticket__act::before{left:-15px}
.ticket__act::after{right:-15px}

/* Full-width, full-height targets. Blue on paper is the strongest contrast this
   palette can make inside a yellow section — a yellow button would have been the
   same colour as the ground it sits on. */
.ticket__op{
  display:flex;align-items:center;justify-content:center;gap:.6em;
  padding:clamp(16px,1.9vw,23px) clamp(16px,2vw,28px);
  border-radius:var(--r-pill);
  background:var(--blue-700);color:var(--paper);
  font-weight:700;font-size:clamp(17px,1.6vw,21px);line-height:1;
  text-decoration:none;
  transition:transform .28s var(--ease),background-color .28s var(--ease);
}
.ticket__op svg{width:1.05em;height:1.05em;transition:transform .28s var(--ease)}
.ticket__op:hover{transform:translateY(-2px);background:var(--blue-500)}
.ticket__op:hover svg{transform:translateX(4px)}
/* No verb on these. The button IS the act of buying, and printing "Купити на
   Turnup" on a button already labelled Turnup was the same word twice. */
.ticket__note{grid-column:1/-1;font-size:13px;font-weight:600;opacity:.55;text-align:center}

@media (max-width:640px){
  /* One column, because two 150px buttons are worse than two full-width ones. */
  .ticket__top{flex-direction:column;gap:clamp(22px,5vw,32px)}
  /* Stacked, the face left the entire right half of the ticket empty. The
     bottom line spans it instead: bars on the left, price in the bottom-right
     corner — which is where a printed ticket puts its price anyway.
     `row-reverse` keeps the price first in the source (it is the meaningful
     one) while placing it last on screen. */
  .ticket__meta{
    width:100%;
    display:flex;flex-direction:row-reverse;
    align-items:flex-end;justify-content:space-between;
    text-align:start;
  }
  .ticket__act{grid-template-columns:minmax(0,1fr)}
}


/* =============================================================== LOCATION */
/* The map's land is painted in this exact colour, so the panel has no frame and
   needs none: land meets canvas invisibly and only the water is cut by the edge.
   The old 3px inset ring was the opposite move — a border drawn around a picture
   that already had no relationship to the page. */
.place{background:var(--blue-900);padding-block:clamp(48px,6.4vw,88px)}
.place__panel{position:relative}
/* HEIGHT, not aspect-ratio. A ratio ties the map's height to the page's width,
   and a map is the one block on the page with no natural height — at 900px the
   2.4 ratio made the panel 623px tall and the section 1019px, taller than the
   hero's own lineup and nearly twice the closing block underneath it. Measured
   across 12 widths: the tail deflated on every tablet size. A clamped height
   holds the section near-constant and lets the crop do the adapting. */
.place__map{
  position:relative;
  height:clamp(255px,34vw,430px);
  border-radius:var(--r-card);
  overflow:hidden;
}
.place__map img{width:100%;height:100%;object-fit:cover;object-position:center}
.place__pin{position:absolute;inset:0;width:100%;height:100%;pointer-events:none}
.place__dot{fill:var(--sun)}
/* The rings scale about (0,0), which the parent <g> has already translated onto
   the venue — no transform-box needed, and no engine disagreement about where an
   SVG element's transform origin is. */
.place__ring{fill:none;stroke:var(--sun);stroke-width:3}
.place__ring:nth-child(1){scale:1;opacity:.5}
.place__ring:nth-child(2){scale:1.9;opacity:.26}
.place__ring:nth-child(3){scale:2.8;opacity:.11}
/* Declared only where motion is welcome. The global reduce-motion override kills
   animations by running them once at .01ms, which for a ripple lands on
   `opacity:0` — an invisible marker. Stated this way, reduced motion simply
   never gets the animation and keeps the static rings above. */
@media (prefers-reduced-motion:no-preference){
  .place__ring{animation:ripple 5s var(--ease) infinite}
  .place__ring:nth-child(2){animation-delay:1.66s}
  .place__ring:nth-child(3){animation-delay:3.33s}
}
@keyframes ripple{
  from{scale:.55;opacity:.6}
  to{scale:3.3;opacity:0}
}
/* Required, not decorative: the map data is OpenStreetMap's and ODbL 4.3 makes
   the notice a condition of using it. So it stays — as quiet as a required line
   can be, and it earns its contrast back on hover. */
.place__src{
  margin-top:10px;text-align:end;
  font-size:11.5px;font-weight:500;opacity:.42;
  transition:opacity .28s var(--ease);
}
.place__src a{text-decoration:underline;text-underline-offset:2px}
.place__src:hover{opacity:.85}

/* An object placed ON the map, not a column beside it: the card carries every
   fact this section owns, so no fact has two homes. The atmospheric note that
   used to sit here said "Stocznia Cesarska" a second time — the address already
   says it, so the note was a duplicate with a contrast problem, not a sentence. */
.place__card{
  position:absolute;left:clamp(16px,2.4vw,40px);bottom:clamp(16px,2.4vw,40px);
  width:min(400px,52%);
  background:var(--paper);color:var(--ink);
  border-radius:22px;
  padding:clamp(20px,2.2vw,30px);
}
/* Between the phone and the desktop the map is short and wide, so a 52% card
   reaches the pin: measured at 768 the card's right edge landed 13px from the
   marker and the outer ripple ran under it. The card gives width back exactly
   where the map has none to spare. */
@media (min-width:641px) and (max-width:1023px){
  .place__card{width:min(340px,46%)}
}
.place__name{font-weight:500;font-size:clamp(26px,2.9vw,40px);line-height:1;letter-spacing:-.02em}
.place__addr{font-weight:600;font-size:var(--meta);opacity:.62;margin-top:.7em;line-height:1.45}
/* Blue on paper — the rule the ticket already set. Yellow stays reserved for
   "buy", so the one non-buying action on the page cannot borrow it. */
.place__go{
  display:flex;align-items:center;justify-content:center;gap:.6em;
  margin-top:clamp(16px,1.8vw,24px);
  padding:15px 22px;
  border-radius:var(--r-pill);
  background:var(--blue-700);color:var(--paper);
  font-weight:700;font-size:clamp(15px,1.3vw,17px);line-height:1;
  text-decoration:none;
  transition:transform .28s var(--ease),background-color .28s var(--ease);
}
.place__go svg{width:1.05em;height:1.05em;transition:transform .28s var(--ease)}
.place__go:hover{transform:translateY(-2px);background:var(--blue-500)}
.place__go:hover svg{transform:translateX(4px)}

/* ================================================================== FINAL */
/* The page's peak, and it has to measure like one. The tail used to deflate
   (lineup 1144 -> tickets 751 -> place 625 -> final 613 -> foot 340), so the
   last call read as the page running out rather than arriving. The block now
   outgrows everything below the lineup, and the ground brightens downward into
   it — you scroll INTO the light and then hit the poster's black band. */
.final{
  position:relative;overflow:hidden;isolation:isolate;
  background:linear-gradient(180deg,#04165a 0%,#0b3fd4 64%,#0e46e0 100%);
  padding-block:clamp(150px,16.5vw,240px);
  text-align:center;
}
/* `preserveAspectRatio:none` stretched the wave to the box: on a tall block the
   crests flattened into ripples and on a narrow one they turned into spikes —
   the one motif that runs through every section, distorted only here. `slice`
   is the same rule the hero already uses: cover the box, keep the shape. */
.final__waves{position:absolute;inset:0;z-index:0;color:rgba(255,255,255,.16)}
.final__waves svg{width:100%;height:100%}
.final__inner{
  position:relative;z-index:2;
  display:flex;flex-direction:column;align-items:center;
  gap:clamp(30px,4.4vw,60px);
  text-align:center;
}
/* The floor matters as much as the ceiling: at the old 44px minimum this line
   was the same size as the ticket's 22.08 on a phone, so the closing statement
   and a date on a stub carried equal weight. 62px makes it the largest type on
   the page after the wordmark, on every screen. */
.final__big{font-weight:400;font-size:clamp(62px,10.2vw,140px);line-height:.86;letter-spacing:-.04em}
/* `width:100%` is load-bearing: .final__inner is a column flex box with
   align-items:center, so this row resolved to shrink-to-fit, and the buttons'
   `min-width:min(320px,100%)` then measured 100% against a width that depended
   on them. The pair collapsed into a stack on a 1440 screen with 300px to spare
   on either side. */
/* --- the odometer clock ---------------------------------------------------
   Mechanism from svoifest.com (which took it from daisyUI's .countdown):
   ::before holds 00–99 as one pre-wrapped block, the cell is a one-row-tall
   window onto it, and setting --value slides the strip on `transform`.

   Two things carried over because they are load-bearing, not decoration:

   `tabular-nums` — Gilroy's default figures are proportional (its "1" is much
   narrower than its "0"), so without tnum every row of the strip would sit at a
   different width and the column would twitch sideways once a second.

   `round(1em, 1px)` — --row is both the height of one strip row and the
   distance the strip travels per unit. clamp() hands out fractional font sizes,
   the engine lays each line box out on a whole pixel, and the two then disagree
   by a fraction that accumulates with the value: invisible at 07, a quarter of
   a digit at 58. Rounding both to the same whole pixel keeps them in step. */
.cd{
  display:flex;justify-content:center;align-items:flex-start;
  gap:clamp(14px,2.6vw,44px);
}
.cd__unit{display:flex;flex-direction:column;align-items:center;gap:.45em}
.cd__roll{
  display:block;
  height:1em;                       /* fallback where round() is unsupported */
  overflow:hidden;
  font-weight:400;
  font-size:clamp(38px,4.6vw,66px);
  line-height:1;
  letter-spacing:-.03em;
  font-variant-numeric:tabular-nums;
}
.cd__roll::before{
  content:"00\A 01\A 02\A 03\A 04\A 05\A 06\A 07\A 08\A 09\A 10\A 11\A 12\A 13\A 14\A 15\A 16\A 17\A 18\A 19\A 20\A 21\A 22\A 23\A 24\A 25\A 26\A 27\A 28\A 29\A 30\A 31\A 32\A 33\A 34\A 35\A 36\A 37\A 38\A 39\A 40\A 41\A 42\A 43\A 44\A 45\A 46\A 47\A 48\A 49\A 50\A 51\A 52\A 53\A 54\A 55\A 56\A 57\A 58\A 59\A 60\A 61\A 62\A 63\A 64\A 65\A 66\A 67\A 68\A 69\A 70\A 71\A 72\A 73\A 74\A 75\A 76\A 77\A 78\A 79\A 80\A 81\A 82\A 83\A 84\A 85\A 86\A 87\A 88\A 89\A 90\A 91\A 92\A 93\A 94\A 95\A 96\A 97\A 98\A 99";
  display:block;white-space:pre;text-align:center;
  transform:translateY(calc(var(--value,0) * -1em));
  transition:transform 700ms cubic-bezier(1,0,0,1);
}
@supports (height: round(1em, 1px)){
  .cd__roll{--row:round(1em,1px);height:var(--row);line-height:var(--row)}
  .cd__roll::before{transform:translateY(calc(var(--value,0) * -1 * var(--row)))}
}
/* Past 99 the strip runs out, so the script drops that cell to plain text
   rather than sliding the window into empty space. */
.cd__roll.is-plain::before{content:none}
.cd__name{font-weight:600;font-size:clamp(12px,1vw,15px);opacity:.7}
/* 16:00 is a fact the page did not carry anywhere before, and it belongs to the
   clock: the countdown says how long, this says until what. */
.final__when{font-weight:500;font-size:var(--meta);opacity:.72;margin-top:-.6em}

.final__actions{display:flex;width:100%;gap:clamp(12px,1.4vw,18px);flex-wrap:wrap;justify-content:center}
/* Two doors to one action, sized like doors. Both leave the site, so both carry
   the same arrow the ticket counterfoil uses. */
.btn--final{
  min-width:min(320px,100%);
  padding:clamp(19px,2vw,25px) clamp(26px,3vw,42px);
  font-size:clamp(16px,1.45vw,20px);
}
.btn--final svg{width:1.05em;height:1.05em;transition:transform .28s var(--ease)}
.btn--final:hover svg{transform:translateX(4px)}

/* ================================================================== FOOTER */
/* One honest row. What stood here was a third copy of the wordmark (210px), the
   organiser line, and "Офіційні квитки: Turnup · Karabas" as plain text — the
   operators named a third time and not clickable, which is the worst of both.
   Everything left here is either a credit or a live link. */
.foot{background:var(--ink);padding-block:clamp(26px,3.4vw,44px)}
.foot__row{
  display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;
  gap:12px clamp(20px,3vw,40px);
  font-size:.94rem;
}
.foot__c{opacity:.55}
/* nowrap so the agency's name breaks as a unit: on a 390 screen the line wrapped
   mid-name and the underline carried the break with it. */
.foot__c a{white-space:nowrap;text-decoration:underline;text-underline-offset:3px;text-decoration-thickness:1px;transition:color .28s var(--ease)}
.foot__c a:hover{color:var(--sun)}
.foot__ig{
  display:inline-flex;align-items:center;gap:.55em;
  font-weight:600;text-decoration:none;
  transition:color .28s var(--ease);
}
.foot__ig svg{width:1.2em;height:1.2em;flex:none}
.foot__ig:hover{color:var(--sun)}
.foot__by{opacity:.55}
.foot__by a{
  text-decoration:underline;text-underline-offset:3px;text-decoration-thickness:1px;
  white-space:nowrap;transition:color .28s var(--ease);
}
.foot__by a:hover{color:var(--sun)}
@media (max-width:760px){
  /* Each clause on its own line, and the middot goes with the layout that
     needed it — a separator between things that are no longer side by side is
     just a dot left hanging at the end of a line. */
  .foot__row{flex-direction:column;align-items:flex-start;gap:11px}
  .foot__c-part{display:block}
  .foot__sep{display:none}
}

/* ================================================================ RESPONSIVE */
/* One boundary for the whole hero, not two. Below it the binding constraint
   flips: WIDTH caps how large the group can be drawn, so a taller band has to
   come from showing more of the photo rather than from scaling it up — hence a
   much larger --crowd-show, with the fade starting lower to match, or half the
   figures would dissolve. The action goes full width and takes its own row. */
/* The card only has to leave the map when the map gets too narrow to hold it —
   that is ~640px, not 900. Tablets keep the card ON the map, which is both the
   better composition and 200px shorter than the stacked version. */
@media (max-width:640px){
  .place__map{height:clamp(240px,60vw,300px)}
  /* `relative`, not `static`: .place__map is a positioned element, so it paints
     in the positioned layer and a static card underneath it loses whatever it
     overlaps. Measured before the fix — the card's top 27px were behind the map
     and the venue name was sliced along its cap line. */
  .place__card{
    position:relative;z-index:2;inset:auto;width:auto;
    margin-top:-26px;margin-inline:clamp(10px,3vw,26px);
    padding:clamp(22px,5vw,28px);
  }
}

@media (max-width:900px){
  /* The bento collapses to one card per row — four cells across a 390px screen
     would be 97px each. Row placement is released back to source order here,
     which is the billing order, so the phone reads QP · Kavabanga · Moon Shot ·
     Den Baurin top to bottom. */
  .acts{grid-template-columns:repeat(6,1fr)}
  .act--qp,.act--kdk,.act--ms,.act--db{grid-column:span 6;grid-row:auto}
  .act{min-height:clamp(238px,52.7vw,357px)}
  .hero{
    --crowd-h:clamp(320px,56svh,640px);--crowd-show:.96;--fade-from:58%;
    --mark-h:clamp(140px,30svh,330px);
    --mark-pad:14px;
    --mark-top:clamp(56px,9svh,90px);
  }
  .hero__foot-in{row-gap:14px;column-gap:16px;justify-content:flex-start}
  /* The sticker is CENTRED IN THE FREE BAND — between the wordmark's real
     bottom edge and the line of heads — instead of sitting at a fixed share of
     the crowd. No single share can be right here: the band the sticker has to
     fit into is 43px tall at 320x568 and 145px at 430x932, and the share that
     centres it is 1.007 at one end and 1.086 at the other. A constant put it on
     the "F" of FEST at 320 and on a head at 768. */
  .tag--days{
    top:calc((var(--mark-top) + var(--mark-real) + var(--heads-y)) / 2);
    bottom:auto;
    translate:-50% -50%;
  }
  .hero__foot .btn--hero{flex:1 0 100%;width:100%}
  /* Compact: the phone has no spare air. The tag keeps its shape and drops to
     roughly half its desktop footprint. */
  .tag--days{padding:.5em .7em .56em;border-radius:14px;gap:.12em}
  .tag__n{font-size:17px}
  .tag__t{font-size:9.5px}
  .tag--city{font-size:22px;padding:.3em .6em .36em}
}
/* 568px-tall phones are the one place the foot could still reach the seated
   figure's chin: mark + crowd + a two-row foot simply do not fit. A taller band
   lifts every landmark (the crowd is bottom-anchored), and the mark gives back
   the room. Measured clearance chin→foot goes from -19px to +77px. */
@media (max-width:900px) and (max-height:700px){
  /* The mark gives back a little more here than the room the foot needs: on a
     568px screen the free band above the heads is otherwise thinner than the
     sticker itself, so there is no position that clears both. */
  .hero{--crowd-h:64svh;--fade-from:50%;--mark-h:clamp(96px,20svh,240px);
        --mark-top:clamp(44px,7svh,70px)}
  .hero__foot{padding-bottom:clamp(14px,2.6svh,26px)}
  .hero__foot-in{row-gap:10px}
}
@media (max-width:760px){
  .topbar__nav{display:none}
  .topbar__cta{margin-inline-start:auto}
  /* A full-width card is narrower than a 7-column cell but much shorter, so the
     cutouts come down a step to keep the name clear of the figure. */
  .act--qp  .act__art{width:62%;right:-2%}
  .act--kdk .act__art{width:60%}
  .act--ms  .act__art{width:58%}
  .act--db  .act__art{width:56%}
}

/* ============================================================== MOTION OFF */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}
  .btn:hover,.act:hover,.ticket__op:hover{transform:none}
}
