/* Socratica hero — implemented from Figma frames `hero` / `hero-events` (1440x800) */

@font-face {
  font-family: 'Rectal';
  src: url('assets/rectal.otf') format('opentype');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

:root {
  --ink: #1d1915;
  --ink-60: rgba(29, 25, 21, 0.6);
  --ink-25: rgba(29, 25, 21, 0.25);
  --ink-20: rgba(29, 25, 21, 0.2);
  --cream: #f5f5ec;
  /* must match --cream exactly: the bar's bottom edge butts against
     .copy-section, and even a 2-level hue difference reads as a hard seam
     there (Safari color-manages into wide gamut and exaggerates it). */
  --bar: var(--cream);
  --bar-h: 120px;
  --drawer-ease: cubic-bezier(0.32, 0.72, 0, 1);
}

* { margin: 0; padding: 0; box-sizing: border-box; }

/* iOS flashes a translucent black box over anything tappable, sized to the
   element's *bounding box* — on .events-tab that is a 258x29 rectangle around a
   curved SVG shape, so it reads as a stray slab. Buttons here have their own
   visual feedback; keyboard focus rings are untouched. */
button, a {
  -webkit-tap-highlight-color: transparent;
}

button {
  -webkit-touch-callout: none; /* no long-press callout on a control */
  user-select: none;
}

html, body { height: 100%; }

/* ---------- Lenis smooth scroll ----------
   Only applied once script.js instantiates Lenis, which it does on fine
   pointers only — touch keeps native scrolling. See PERFORMANCE.md. */
html.lenis, html.lenis body { height: auto; }

.lenis.lenis-smooth { scroll-behavior: auto !important; }

/* opt-out hook: put data-lenis-prevent on any inner scroller added later */
.lenis.lenis-smooth [data-lenis-prevent] { overscroll-behavior: contain; }

.lenis.lenis-stopped { overflow: hidden; }

body {
  background: var(--cream);
  font-family: 'Geist', sans-serif;
  /* clip, not hidden: hidden would make body its own scroll container
     and break window scrolling / position: sticky */
  overflow-x: clip;
}

.hero {
  position: relative;
  width: 100%;
  height: 100svh;
  min-height: 640px;
  overflow: clip;
  background: var(--cream);
}

/* ---------- Background + hover mask ---------- */

/* the parallax transforms this every frame; unpromoted, Safari re-rasterizes
   the whole subtree at each offset */
.bg {
  position: absolute;
  inset: 0;
  will-change: transform;
}

.bg-layer {
  position: absolute;
  inset: 0;
  background-size: cover;
  background-position: center bottom;
  /* not promoted: .bg already is, and a second full-viewport layer per photo
     blows iOS's layer budget and leaves bands unpainted */
}

.bg-normal { background-image: url('assets/bg-normal.jpg'); }

/* a phone crops this to a sliver; the wide source is pure decode cost */
@media (max-width: 900px) {
  .bg-normal { background-image: url('assets/bg-normal-sm.jpg'); }
}

.bg-hover {
  background-image: url('assets/bg-hover.jpg');
  /* soft circular mask that follows the cursor (script.js drives the vars) */
  --mx: 50%;
  --my: 55%;
  --mr: 0px;
  -webkit-mask-image: radial-gradient(circle var(--mr) at var(--mx) var(--my),
    #000 15%, rgba(0,0,0,0.85) 40%, rgba(0,0,0,0.45) 65%, rgba(0,0,0,0.15) 85%, transparent 100%);
  mask-image: radial-gradient(circle var(--mr) at var(--mx) var(--my),
    #000 15%, rgba(0,0,0,0.85) 40%, rgba(0,0,0,0.45) 65%, rgba(0,0,0,0.15) 85%, transparent 100%);
}

/* no cursor, so the mask can never open; script.js skips its loop too */
@media (hover: none), (pointer: coarse) {
  .bg-hover { display: none; }
}

/* cream fade over the top of the hero art, per the updated design */
.bg-fade {
  position: absolute;
  inset: 0;
  background: linear-gradient(to bottom, rgba(245, 245, 236, 0.8), rgba(245, 245, 236, 0) 46.65%);
  pointer-events: none;
}

.bg-paper {
  position: absolute;
  inset: 0;
  background-image: url('assets/figma/paper-texture.png');
  background-size: 432px 288px;
  background-position: top left;
  opacity: 0.2;
  pointer-events: none;
}

/* ---------- Headline (morphing text) ---------- */

.headline {
  position: absolute;
  /* 140px @ an 800px hero, then held — the sizes below cap at the 1440 design
     values, so the offset must too. % (not vh) tracks .hero's min-height. */
  top: min(12.5svh, 120px);
  left: 50%;
  transform: translateX(-50%);
  /* was a fixed box holding a single word; now a line of running text, so it
     sizes to its content and wraps at narrow widths instead of clipping */
  width: max-content;
  max-width: 92vw;
  margin: 0;
  font-family: 'Rectal', serif;
  font-weight: 400;
  font-size: clamp(46px, 5.6vw, 80px);
  line-height: 1.1;
  letter-spacing: 0.02em;
  color: var(--ink);
  text-align: center;
  pointer-events: none;
  user-select: none;
  z-index: 3;
}

/* the goo sits on the role box, not the whole line — filtering the static
   words too would make them pulse every time a morph starts */
.headline-role {
  position: relative;
  /* its own line, always: the words differ enough in width that inline the
     headline flipped between one and two lines as it cycled, jumping the
     whole hero on every morph */
  display: block;
  margin: 0 auto;
  font-style: normal; /* it's an <em>; the emphasis is the motion, not italics */
  height: 1.1em; /* = .headline line-height, so the line box can't grow */
  width: 0; /* replaced per word by script.js */
  filter: url(#threshold) blur(0.6px);
  /* duration and delay are set per swap by script.js — they differ by
     direction, see createInlineMorph */
  transition-property: width;
  transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
  transition-duration: 0.2s;
}

/* while a word rests, drop the goo filter so the type is pixel-crisp;
   script.js re-enables it the moment a morph starts */
.headline-role.is-resting { filter: none; }

.headline-role .morph-word {
  font-family: inherit;
  font-size: inherit;
  line-height: 1.1;
  letter-spacing: inherit;
  padding-left: 0;
  /* the global .morph-word cap-trims to sit in a fixed box; inline it has to
     share a baseline with the words before it instead */
  text-box: normal;
  color: inherit;
}

@media (prefers-reduced-motion: reduce) {
  /* script.js sets duration/delay inline, so drop the property itself */
  .headline-role { transition-property: none; }
}

.morph-word {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--ink);
  font-family: 'Rectal', serif;
  /* cap height ~69px per the updated design (People at 94px) */
  font-size: clamp(54px, 6.5vw, 94px);
  line-height: 1;
  text-box: trim-both cap alphabetic;
  letter-spacing: 0.02em;
  /* letter-spacing trails the last glyph; this recenters the visible run */
  padding-left: 0.09em;
  white-space: nowrap;
  user-select: none;
}

.morph-word svg { display: block; width: min(92vw, 428px); height: auto; }

/* ---------- Navbar ---------- */

.navbar {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: 20px;
  z-index: 5;
}

.nav-pill-wrap { position: relative; }

/* background shapes live in a goo-filtered layer so the tail melts out of
   the pill; the crisp content (wordmark, dots, link) sits unfiltered above */
.nav-goo {
  position: absolute;
  inset: -24px;
  filter: url(#goo);
  pointer-events: none;
}

.nav-goo-pill {
  position: absolute;
  top: 24px;
  left: 24px;
  right: 24px;
  height: 40px;
  background: var(--ink);
  border-radius: 12px;
}

.nav-goo-tail {
  position: absolute;
  top: 62px; /* 24 + pill 40 - 2px overlap from Figma */
  left: 35px; /* 24 + 11.02 from Figma */
  width: 156.12px;
  height: 32px;
  background: url('assets/figma/nav-tail.svg') no-repeat center / 100% 100%;
  transform: translateY(-32px) scaleY(0.6);
  transform-origin: top center;
  transition: transform 0.45s var(--drawer-ease);
}

.nav-open .nav-goo-tail { transform: none; }

.nav-pill {
  position: relative;
  display: flex;
  align-items: center;
  gap: 48px;
  padding: 10px 16.25px 10px 20px;
  background: transparent;
  border: none;
  border-radius: 12px;
  cursor: pointer;
}

.nav-wordmark { display: block; width: 80.344px; height: 12px; }

/* 3x3 dot grid; collapses to an X (corners + center) when open */
.nav-dots {
  box-sizing: border-box;
  display: grid;
  grid-template-columns: repeat(3, 1.875px);
  grid-template-rows: repeat(3, 1.875px);
  gap: 2.5px;
  padding: 4.69px;
  width: 20px;
  height: 20px;
}

.nav-dots i {
  width: 1.875px;
  height: 1.875px;
  border-radius: 0.625px;
  background: rgba(245, 245, 236, 0.6);
  transition: transform 0.35s var(--drawer-ease), opacity 0.35s ease;
}

/* the edge-mid dots slide into the center and vanish: 3x3 grid morphs to an X */
.nav-open .nav-dots i:nth-child(2) { transform: translateY(4.375px) scale(0); opacity: 0; }
.nav-open .nav-dots i:nth-child(4) { transform: translateX(4.375px) scale(0); opacity: 0; }
.nav-open .nav-dots i:nth-child(6) { transform: translateX(-4.375px) scale(0); opacity: 0; }
.nav-open .nav-dots i:nth-child(8) { transform: translateY(-4.375px) scale(0); opacity: 0; }

.nav-dropdown {
  position: absolute;
  top: calc(100% - 2px);
  left: 11.02px;
  width: 156.12px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  transform: translateY(-8px);
  pointer-events: none;
  transition: opacity 0.25s ease 0.12s, transform 0.45s var(--drawer-ease);
}

.nav-open .nav-dropdown {
  opacity: 1;
  transform: none;
  pointer-events: auto;
}

.nav-dropdown-link {
  font-family: 'Geist', sans-serif;
  font-weight: 500;
  font-size: 12px;
  line-height: 1;
  text-box: trim-both cap alphabetic;
  text-transform: uppercase;
  color: #f5f5f2;
  text-decoration: none;
}

/* ---------- Upcoming events tab ---------- */

/* tab + bar move together as one element, so no gap can open between them */
.drawer {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 6;
  pointer-events: none;
  transform: translateY(var(--bar-h));
  transition: transform 0.65s var(--drawer-ease);
}

.hero.events-open .drawer { transform: translateY(0); }

.events-tab {
  position: relative;
  display: block;
  margin: 0 auto;
  width: 258px;
  height: 29px;
  border: none;
  background: transparent;
  padding: 0;
  cursor: pointer;
  pointer-events: auto;
}

.tab-shape { position: absolute; inset: 0; width: 100%; height: 100%; }

/* the path carries Figma's fill as an attribute; drive it from the token so
   the tab and the bar it sits on can never drift apart */
.tab-shape path { fill: var(--bar); }

.tab-content {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
}

.tab-label {
  font-family: 'Geist', sans-serif;
  font-weight: 500;
  font-size: 12px;
  line-height: 1;
  text-transform: uppercase;
  color: var(--ink);
}

.tab-chevron {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 8px;
  height: 8px;
}

/* chevron points up at rest, flips down when the drawer is open */
.tab-chevron svg {
  display: block;
  transform: rotate(-90deg);
  transition: transform 0.4s var(--drawer-ease);
}

.hero.events-open .tab-chevron svg { transform: rotate(90deg); }

/* ---------- Events bar (infinite carousel) ---------- */

.events-bar {
  height: var(--bar-h);
  background: var(--bar);
  padding: 10px 0;
  overflow: hidden;
  pointer-events: auto;
  cursor: grab;
}

.events-bar.dragging { cursor: grabbing; }

.events-track {
  display: flex;
  gap: 10px;
  height: 100px;
  width: max-content;
  will-change: transform;
}

.event-card {
  display: flex;
  align-items: flex-start;
  height: 100px;
  padding: 10px;
  border: 0.5px solid var(--ink-20);
  border-radius: 32px;
  flex: none;
  user-select: none;
  text-decoration: none;
  color: inherit;
  -webkit-user-drag: none;
  /* cards rise into place when the drawer opens; script.js staggers the
     delay by each card's horizontal position so it cascades left to right */
  opacity: 0;
  transform: translateY(32px) scale(0.96);
  transition: opacity 0.45s ease, transform 0.6s var(--drawer-ease);
}

.event-card.card-in {
  opacity: 1;
  transform: none;
}

.event-date {
  position: relative;
  width: 80px;
  height: 80px;
  border-radius: 24px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  flex: none;
}

.event-date::before {
  content: '';
  position: absolute;
  inset: 0;
  background-image: var(--tile);
  background-size: cover;
  background-position: center;
}

.event-date::after {
  content: '';
  position: absolute;
  inset: 0;
  background-color: rgba(29, 25, 21, 0.1);
}

.event-date-paper {
  position: absolute;
  inset: 0;
  background-image: url('assets/figma/paper-texture.png');
  background-size: 432px 288px;
  background-position: top left;
  opacity: 0.2;
  z-index: 1;
}

.event-date span {
  position: relative;
  z-index: 1;
  font-family: 'Geist', sans-serif;
  font-weight: 500;
  font-size: 14px;
  line-height: 1;
  color: #fff;
  text-align: center;
  width: 80px;
}

.event-info {
  display: flex;
  flex-direction: column;
  justify-content: center;
  /* two-line title + two meta rows just fits the 100px card at 10px */
  gap: 10px;
  height: 100%;
  padding-left: 20px;
  padding-right: 10px;
}

.event-title {
  font-family: 'Rectal', serif;
  font-weight: 400;
  font-size: 18px;
  line-height: 1.15;
  text-box: trim-both cap alphabetic;
  text-transform: uppercase;
  color: var(--ink);
  white-space: nowrap;
}

.event-meta {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.event-meta-row {
  display: flex;
  align-items: center;
  gap: 6px;
  font-family: 'Geist', sans-serif;
  font-weight: 500;
  font-size: 14px;
  line-height: 1;
  color: var(--ink-60);
  white-space: nowrap;
}

/* Figma cap-trims every text box in the card, so the 14/10px gaps
   measure from the glyphs themselves rather than the line boxes */
.event-meta-row span,
.event-date span,
.tab-label,
.menu-btn span {
  text-box: trim-both cap alphabetic;
}

.event-dot {
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: var(--ink-60);
  flex: none;
}

/* ---------- Grid overlay ---------- */

/* ---------- Copy section (scroll-driven goo morph) ---------- */

/* non-pinned variant: lines morph as it scrolls through */
.copy-section {
  position: relative;
  height: 840px;
  background: var(--cream);
}

.copy-deco {
  position: absolute;
  object-fit: contain;
  pointer-events: none;
  opacity: 0;
  /* no will-change: mix-blend-mode below already forces a backdrop read-back */
}

.copy-deco.deco-multiply { mix-blend-mode: multiply; }
.copy-deco.deco-darken { mix-blend-mode: darken; }

/* positions from the Figma 1440x840 frame, measured to each image's own box */
.deco-bulb { width: 96px; height: 96px; left: calc(50% - 508px); top: 558px; }
.deco-cloud { width: 125px; height: 125px; left: calc(50% + 11px); top: 116px; }
.deco-notebook { width: 139px; height: 139px; left: calc(50% + 431px); top: 537px; }

.copy-line .hl {
  font-style: normal;
  color: #6a97be;
}

/* The role cycles on the same goo morph as the headline and Makers line.
   Its two words are absolutely stacked, so the box contributes no width of
   its own — script.js measures each word and animates between them. The
   slide lands inside the morph, where the blur hides the reflow. */
.copy-role {
  position: relative;
  display: inline-block;
  vertical-align: top;
  font-style: normal; /* it's an <em>; the emphasis is the colour, not italics */
  height: 1.2em; /* = .copy-line span line-height, so the line box can't grow */
  width: 0; /* replaced per word by script.js */
  filter: url(#threshold) blur(0.6px);
  /* duration and delay are set per swap by script.js — they differ by
     direction, see the spill note there */
  transition-property: width;
  transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
  transition-duration: 0.2s;
}

.copy-role.is-resting { filter: none; }

.copy-role .morph-word {
  font-family: inherit;
  font-size: inherit;
  line-height: 1.2;
  letter-spacing: 0;
  padding-left: 0;
  /* the global .morph-word cap-trims to sit in a fixed box; inline it has to
     share a baseline with the words either side of it instead */
  text-box: normal;
  color: #6a97be;
}

@media (prefers-reduced-motion: reduce) {
  /* script.js sets duration/delay inline, so drop the property itself */
  .copy-role { transition-property: none; }
}

.copy-sticky {
  position: relative;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}

/* the line carries the #inkbleed filter (alpha threshold + print-bleed
   displacement), the inner span gets blurred — the threshold crushes the
   blur into ink blobs without touching the background or ink color */
.copy-line {
  opacity: 0;
}

.copy-line span {
  display: block;
  font-family: 'Rectal', serif;
  font-size: clamp(40px, 5.55vw, 80px);
  line-height: 1.2;
  color: var(--ink);
  text-align: center;
  will-change: transform;
}

/* ---------- Makers section ---------- */

/* pinned like the reference: the wrapper provides scroll room while the
   inner viewport-sized panel sticks; tiles are anchored at the panel
   center and scrub outward to their quadrants */
/* svh: on iOS 100vh is the toolbar-hidden height, so a sticky panel sized
   that way overflows the visible area and gets cut off */
.makers-section {
  position: relative;
  height: calc(100svh + 1760px); /* pin room matches the reference scrub length */
  background: var(--cream);
}

.makers-pin {
  position: sticky;
  top: 0;
  height: 100svh;
  min-height: 800px;
  overflow: hidden;
}

/* fixed 1440x800 design stage, scaled to fit the viewport so the
   Figma composition holds at any screen size */
.makers-stage {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 1440px;
  height: 800px;
  margin: -400px 0 0 -720px;
  /* no will-change: changes on resize only. Promoting it retains a 1440x800
     layer (~41 MB at 3x DPR, scale 1 on mobile) for nothing. */
}

.tile {
  position: absolute;
  left: 50%;
  top: 50%;
  border-radius: 48px;
  overflow: hidden;
  will-change: transform;
}

.tile-sm { border-radius: 32px; }

/* ---- compositor-driven collage + hero parallax ----
   iOS scrolls ahead of the main thread, so a JS handler always paints where
   the scroll *was*. script.js generates the keyframes. */
@supports (animation-timeline: view()) {
  /* `contain` = where the section fully covers the viewport = the pinned scrub */
  .makers-section { view-timeline: --makers block; }

  .tile,
  .tile img {
    animation-timing-function: linear;
    animation-fill-mode: both;
    animation-timeline: --makers;
    animation-range: contain 0% contain 100%;
  }

  .bg {
    animation: hero-parallax linear both;
    animation-timeline: scroll(root block);
    animation-range: 0 120svh;
  }

  /* matches the old sy * 0.28 over the same 120vh window */
  @keyframes hero-parallax {
    to { transform: translate3d(0, 33.6svh, 0); }
  }
}

.tile img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.makers-morph {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: min(92vw, 700px);
  height: clamp(38px, 5.4vw, 78px);
  filter: url(#threshold) blur(0.6px);
  pointer-events: none;
  z-index: 2;
}

.makers-morph.is-resting { filter: none; }

.makers-morph .morph-word {
  font-size: clamp(40px, 4.45vw, 64px);
  letter-spacing: 0.02em;
  padding-left: 0.02em;
  color: #140803;
}

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

.footer {
  position: relative;
  height: 100svh; /* see .makers-pin — vh is the toolbar-hidden height on iOS */
  min-height: 800px;
  overflow: clip;
  padding: 0 20px;
  display: flex;
  flex-direction: column;
  align-items: center;
}

.footer-bg {
  position: absolute;
  inset: 0;
  background-image: linear-gradient(to bottom, #f5f5f2 16.5%, rgba(245, 245, 242, 0)), url('assets/footer-bg.jpg');
  background-size: auto, cover;
  background-position: center, center bottom;
}

@media (max-width: 900px) {
  .footer-bg {
    background-image: linear-gradient(to bottom, #f5f5f2 16.5%, rgba(245, 245, 242, 0)), url('assets/footer-bg-sm.jpg');
  }
}

.footer-card {
  position: relative;
  width: 100%;
  flex: 1 0 0;
  border-radius: 48px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  padding-top: 220px;
  gap: 32px;
}

.footer-heading {
  display: flex;
  flex-direction: column;
  align-items: center;
  margin: 0;
}

.footer-line span {
  display: block;
  font-family: 'Rectal', serif;
  font-weight: 400;
  font-size: clamp(40px, 4.45vw, 64px);
  line-height: 1.2;
  color: #140803;
  text-align: center;
  will-change: transform;
}

.ink-line { opacity: 0; }

.footer-form {
  display: flex;
  align-items: center;
  gap: 24px;
  height: 48px;
  background: #140803;
  border-radius: 16px;
  padding: 6px 6px 6px 20px;
  opacity: 0;
  transform: translateY(24px);
}

.footer-input {
  background: transparent;
  border: none;
  outline: none;
  width: 150px;
  font-family: 'Geist', sans-serif;
  font-weight: 500;
  font-size: 12px;
  text-transform: uppercase;
  color: #f5f5f2;
}

.footer-input::placeholder {
  color: #f5f5f2;
  text-transform: uppercase;
}

.footer-submit {
  height: 100%;
  background: #f5f5f2;
  border: none;
  border-radius: 12px;
  padding: 10px 20px;
  font-family: 'Geist', sans-serif;
  font-weight: 500;
  font-size: 12px;
  text-transform: uppercase;
  color: #140803;
  cursor: pointer;
}

/* oversized: bleeds off both sides and crops at the bottom edge;
   assembles once out of ink when the footer comes into view */
/* exact Figma placement (1440x800 frame): x -58.65, width 1517.3,
   baseline 33.37px below the frame bottom — the bleed is asymmetric */
.footer-wordmark {
  position: absolute;
  left: -4.0729vw;
  width: 105.3683vw;
  bottom: -2.3172vw;
  opacity: 0;
  pointer-events: none;
}

.footer-wordmark img {
  display: block;
  width: 100%;
  height: auto;
  /* transform only: will-change:filter would retain a filter layer for the
     life of the page, and applyInk drops to none once resolved */
  will-change: transform;
}

.defs { position: absolute; }

/* ---------- Tablet / narrow desktop ----------
   Type is fluid at every width via clamp() in the base rules; these are the
   layout pieces that stop fitting once the viewport drops below the design. */

@media (max-width: 1024px) {
  /* the pinned panel must never be taller than the viewport or it can't stick */
  .makers-pin { height: 100svh; min-height: 0; }
  .footer { height: auto; min-height: 100svh; }

  /* decorations are placed in vw so they stay beside the copy, not off-screen */
  .deco-bulb { width: 78px; height: 78px; left: calc(50% - 38vw); top: 62%; }
  .deco-cloud { width: 104px; height: 104px; left: calc(50% + 1vw); top: 12%; }
  .deco-notebook { width: 112px; height: 112px; left: calc(50% + 30vw); top: 60%; }
}

/* ---------- Mobile ---------- */

@media (max-width: 768px) {
  :root { --bar-h: 104px; }

  /* --- hero --- */
  .hero { min-height: 520px; }

  /* same unit as the base rule, a touch lower per the mobile design */
  .headline { top: 19%; }

  .morph-word { letter-spacing: 0.06em; padding-left: 0.06em; }

  .navbar { padding: 14px; }

  /* --- events drawer --- */
  .events-bar {
    padding: 8px 0;
    /* let vertical page scrolling through the bar still work on touch */
    touch-action: pan-y;
  }

  .events-track { gap: 8px; height: 88px; }

  .event-card { height: 88px; padding: 8px; border-radius: 26px; }
  .event-date { width: 64px; height: 64px; border-radius: 18px; }
  .event-date span { font-size: 12px; width: 64px; }
  .event-info { padding-left: 14px; padding-right: 6px; gap: 10px; }
  .event-title { font-size: 15px; }
  .event-meta { gap: 7px; }
  .event-meta-row { font-size: 12px; gap: 5px; }

  /* --- copy section --- */
  .copy-section { height: 100svh; min-height: 560px; }
  /* at phone sizes each line wraps to two; balance keeps the pair even */
  .copy-line span { line-height: 1.2; text-wrap: balance; }

  .deco-bulb {
    width: 62px; height: 62px;
    left: calc(50% - 41vw); top: 16%;
  }
  .deco-cloud {
    width: 84px; height: 84px;
    left: calc(50% + 16vw); top: 9%;
  }
  .deco-notebook {
    width: 82px; height: 82px;
    left: calc(50% + 22vw); top: 76%;
  }

  /* --- makers section --- */
  /* shorter scrub, and the stage runs unscaled: script.js compresses the
     orbits into an ellipse that fits a narrow screen instead */
  .makers-section { height: calc(100svh + 1100px); }

  /* --- footer --- */
  .footer-card { padding-top: 20svh; gap: 24px; }
  .footer-form { gap: 12px; padding-left: 16px; }
  .footer-input { width: 120px; }
  .footer-submit { padding: 10px 14px; }
}
