/* Speaker lineup isn't announced yet -- hidden until it's ready. Content and
   markup are left in place so this is a one-line revert once it's ready. */
#speakers {
  display: none !important;
}

/* Sticky header + scroll-spy underline, layered on top of the Framer export */
.framer-1kfysrm-container,
.framer-1kfysrm-container nav {
  position: fixed !important;
  top: 0 !important;
  left: 0 !important;
  right: 0 !important;
  z-index: 999 !important;
  transform: none !important;
  opacity: 1 !important;
  visibility: visible !important;
}

/* Hides the header while the Join Us modal is open. Needs the extra class
   for specificity to win over the "visibility: visible !important" rule
   directly above. */
.framer-1kfysrm-container.demcon-header-hidden,
.framer-1kfysrm-container.demcon-header-hidden nav {
  visibility: hidden !important;
  pointer-events: none !important;
}

/* The nav links have their own native onMouseEnter/data-highlight hover
   behavior (from Framer's built-in current-hash highlighting), which
   otherwise fights with our scroll-driven underline over the same visual
   real estate on hover/mouse-leave. Force ours to win unconditionally. */
.framer-1kfysrm-container a.framer-YmthU {
  border-bottom: 2px solid transparent !important;
  padding-bottom: 4px;
  text-decoration: none !important;
  transition: border-color 0.2s ease;
  overflow: visible !important;
}

.framer-1kfysrm-container a.framer-YmthU.demcon-nav-active {
  border-bottom-color: #ff0074 !important;
}

/* Nav item labels pop up slightly on hover (transform, not font-size, so
   growing one item never reflows/shifts its neighbors) and switch to the
   same pink as the active-item underline, except the current section's
   already-underlined item. */
.framer-1kfysrm-container a.framer-YmthU p {
  transition: transform 0.15s ease, color 0.15s ease;
  transform: translateY(0) scale(1);
}

.framer-1kfysrm-container a.framer-YmthU:not(.demcon-nav-active):hover p.framer-text {
  transform: translateY(-3px) scale(1.15) !important;
  color: #ff0074 !important;
}

.framer-1kfysrm-container,
.framer-1kfysrm-container .framer-8gkjzd,
.framer-1kfysrm-container nav,
.framer-1kfysrm-container .framer-text,
.framer-1kfysrm-container img,
.framer-1kfysrm-container svg,
.framer-1kfysrm-container [data-border='true']::after {
  transition: background-color 0.25s ease, color 0.25s ease, filter 0.25s ease, border-color 0.25s ease;
}

/* Solid brand-color fill that snaps to whatever section is behind the header. */
.framer-1kfysrm-container.demcon-nav-yellow,
.framer-1kfysrm-container.demcon-nav-yellow .framer-8gkjzd,
.framer-1kfysrm-container.demcon-nav-yellow nav {
  background-color: #fff500 !important;
  backdrop-filter: none !important;
}

.framer-1kfysrm-container.demcon-nav-white,
.framer-1kfysrm-container.demcon-nav-white .framer-8gkjzd,
.framer-1kfysrm-container.demcon-nav-white nav {
  background-color: #faf9f5 !important;
  backdrop-filter: none !important;
}

.framer-1kfysrm-container.demcon-nav-black,
.framer-1kfysrm-container.demcon-nav-black .framer-8gkjzd,
.framer-1kfysrm-container.demcon-nav-black nav {
  background-color: #030509 !important;
  backdrop-filter: none !important;
}

.framer-1kfysrm-container.demcon-nav-yellow,
.framer-1kfysrm-container.demcon-nav-white {
  --border-color: #000;
}

.framer-1kfysrm-container.demcon-nav-yellow .framer-text,
.framer-1kfysrm-container.demcon-nav-white .framer-text {
  color: #000 !important;
}

.framer-1kfysrm-container.demcon-nav-black {
  --border-color: #fff;
}

.framer-1kfysrm-container.demcon-nav-black .framer-text {
  color: #fff !important;
}

.framer-1kfysrm-container.demcon-nav-black img,
.framer-1kfysrm-container.demcon-nav-black svg {
  filter: brightness(0) invert(1) !important;
}

/* `*` here (not just the tagged element) is what actually stops the native
   pointer from flashing back: Framer bakes explicit `cursor: pointer` onto
   arbitrary descendants (icons, number labels, nav links inside the header),
   and an element's own explicit cursor always wins over one inherited from
   an ancestor, so an ancestor-only rule leaves every such descendant
   uncovered. !important is needed to beat those baked declarations
   regardless of their own specificity.

   ::before/::after are ALSO needed, separately from `*` -- the universal
   selector only ever matches real elements, never pseudo-elements, so it
   can't reach them no matter how broadly it's written. Framer draws most
   button/link borders (anything with data-border="true") via exactly
   such a pseudo-element, which carries its own baked `cursor: pointer`
   rather than just inheriting it from its host -- without these, that
   pseudo-element kept showing the native pointer arrow the whole time
   the (correctly cursor:none) real element underneath it was hovered,
   reading as a second, smaller cursor showing through the custom one
   (confirmed live: reported on both header nav links and buttons
   elsewhere, everywhere this same data-border pattern is used). */
.demcon-cursor-target:hover,
.demcon-cursor-target:hover *,
.demcon-cursor-target:hover::before,
.demcon-cursor-target:hover::after,
.demcon-cursor-target:hover *::before,
.demcon-cursor-target:hover *::after {
  cursor: none !important;
}

.demcon-hover-grow {
  transition: transform 0.2s ease;
}

.demcon-hover-grow:hover {
  transform: scale(1.06);
}

/* Header "JOIN US" button: turns solid red on hover (excluded from the
   generic .demcon-hover-grow box-scale above -- see enhanceButtons in
   sticky-nav.js), with only its label growing slightly in place. */
.framer-NoclU {
  transition: background-color 0.2s ease-out;
}

.framer-NoclU:hover {
  background-color: rgb(255, 0, 0) !important;
}

.framer-NoclU p {
  transition: font-size 0.2s ease-out, color 0.2s ease-out;
}

.framer-NoclU:hover p.framer-text {
  font-size: 18px !important;
  color: #fff !important;
}

/* Hero email-capture "SUBSCRIBE" button: turns solid red on hover (excluded
   from the generic .demcon-hover-grow box-scale -- see enhanceButtons in
   sticky-nav.js), with only its label growing slightly in place. */
.framer-qtbtE:hover {
  background-color: rgb(255, 0, 0) !important;
}

.framer-qtbtE p {
  transition: font-size 0.2s ease-out;
}

.framer-qtbtE:hover p.framer-text {
  font-size: 18px !important;
}

.demcon-custom-cursor {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 2147483647;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.12s ease;
  will-change: transform;
}

.demcon-custom-cursor.demcon-custom-cursor-visible {
  opacity: 1;
}

/* Belt-and-suspenders alongside the JS-side check in setupCustomCursor:
   force it invisible on touch devices regardless of any stray JS state. */
@media (hover: none) {
  .demcon-custom-cursor {
    display: none !important;
  }
}

.demcon-read-more {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  width: fit-content;
  margin: 36px 0 0;
  padding: 12px 22px;
  border: 2px solid #030509;
  border-radius: 999px;
  background: #030509;
  font-family: 'League Spartan', 'League Spartan Placeholder', sans-serif;
  font-size: 15px;
  font-weight: 600;
  line-height: 120%;
  letter-spacing: -0.02em;
  text-transform: uppercase;
  color: #faf9f5;
  text-decoration: none;
  cursor: pointer;
  transition: background 0.2s ease, border-color 0.2s ease, color 0.2s ease;
}

.demcon-read-more::after {
  content: '\2192';
  font-size: 16px;
  transition: transform 0.2s ease;
}

.demcon-read-more:hover {
  background: #ff3131;
  border-color: #ff3131;
  color: #faf9f5;
}

.demcon-read-more:hover::after {
  transform: translateX(4px);
}

.demcon-newsletter-note {
  margin: 0 0 32px;
  max-width: 260px;
  font-family: 'Satoshi', 'Satoshi Placeholder', sans-serif;
  font-size: 14px;
  line-height: 1.5em;
  letter-spacing: -0.01em;
  text-align: left;
  color: rgba(255, 255, 255, 0.7);
}

/* Comic-book speech bubble shown on hover over the hero's Subscribe button. */
.demcon-subscribe-bubble {
  position: fixed;
  z-index: 2147483000;
  left: 0;
  top: 0;
  transform: translate(14px, -50%);
  background: #fff;
  border: 2.5px solid #030509;
  border-radius: 16px;
  padding: 10px 16px;
  font-family: 'Satoshi', 'Satoshi Placeholder', sans-serif;
  font-size: 14px;
  font-weight: 600;
  line-height: 1.4em;
  text-align: center;
  color: #030509;
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  box-shadow: 3px 3px 0 #030509;
  transition: opacity 0.15s ease, transform 0.15s ease;
}

.demcon-subscribe-bubble::after,
.demcon-subscribe-bubble::before {
  content: '';
  position: absolute;
  top: 50%;
  border-style: solid;
}

.demcon-subscribe-bubble::after {
  left: -10px;
  transform: translateY(-50%);
  border-width: 8px 10px 8px 0;
  border-color: transparent #030509 transparent transparent;
}

.demcon-subscribe-bubble::before {
  left: -6.5px;
  transform: translateY(-50%);
  border-width: 6px 8px 6px 0;
  border-color: transparent #fff transparent transparent;
  z-index: 1;
}

.demcon-subscribe-bubble.demcon-subscribe-bubble-visible {
  opacity: 1;
  transform: translate(20px, -50%);
}

/* Hero "Pensive Woman in White Hood" fade-in-on-load. Framer's original
   appear-animation spec (delay 1.5s, duration 1.3s) read as sluggish once
   this became the very first thing a visitor sees -- retimed to fire almost
   immediately instead. The baked SSR placeholder on this element said
   translateY(40px), but that never matched its real per-element config
   (x:0, y:0) -- intentionally not carried over here. sticky-nav.js flips the
   inline opacity directly once loaded (its baked inline opacity:0 takes
   precedence over any class-based rule, so only the transition lives here). */
.framer-1mmgwc5 {
  transition: opacity 0.6s ease-out 0.1s;
}

/* "SAME SHIT, DIFFERENT CENTURY" bubble -- originally an on-scroll rotate-in
   (rotate 50deg -> 0deg, spring bounce), moved to fire on load alongside the
   hero image above instead, on the same fast timing. A spring has no exact
   CSS equivalent; this back-out cubic-bezier approximates the same slight
   overshoot. */
.framer-1sl0blg {
  transition: opacity 0.6s cubic-bezier(0.34, 1.56, 0.64, 1) 0.1s, transform 0.6s cubic-bezier(0.34, 1.56, 0.64, 1) 0.1s;
}

/* Extra scroll runway for the whole hero section (map background included)
   before the next section (Salons) begins -- this section's own height is
   `min-content` (Framer), driven by its flex children/padding, so adding
   bottom padding is what actually makes it vertically longer. Without this
   the map's parallax barely gets to play before Salons arrives underneath
   it. Matches the specificity of Framer's own baked padding shorthand on
   this selector so it reliably wins regardless of stylesheet order. */
.framer-L1F0F .framer-10ecca0 {
  padding-bottom: 60vh;
}

/* Scroll-driven hero intro (setupHeroIntro in sticky-nav.js): the
   illustration + speech bubble are pinned full-screen on load, with a
   "scroll down" prompt, then fade out (and the "Welcome to DEMCON" content
   pops in) over a short scroll distance -- spacer height is set inline by
   sticky-nav.js to match that same distance exactly. Transparent background
   so the map/yellow section behind it stays visible and parallaxes
   underneath the illustration the whole time, not just after it fades. */
.demcon-hero-intro-overlay {
  position: fixed;
  inset: 0;
  z-index: 500;
  display: flex;
  /* flex-end (not center) so her skirt/feet sit flush with the bottom of
     the viewport regardless of viewport height, instead of leaving a
     gap below her that grows/shrinks with the window. */
  align-items: flex-end;
  justify-content: center;
  background: transparent;
}

/* Mobile-only backdrop (setupHeroIntro, sticky-nav.js) that masks a
   gray-showing-through bug on real phones -- see that file for the full
   explanation. Created unconditionally in JS (no window.innerWidth check
   there at all), gated to mobile purely by this media query instead --
   it's the single most reliable way to scope it, since a media query is
   evaluated live against the real, current viewport with no dependency on
   exactly when it happens to run, unlike a one-time JS width check made
   during this deferred script's very first synchronous execution. */
.demcon-hero-yellow-backdrop {
  display: none;
}

@media (max-width: 767.98px) {
  .demcon-hero-yellow-backdrop {
    display: block;
  }
}

/* Repositioned next to her head (this element is now a child of the
   illustration itself, not the viewport-wide overlay -- see setupHeroIntro
   in sticky-nav.js -- so these percentages are relative to *her*, not the
   screen, and stay put next to her head regardless of viewport width). The
   illustration's overflow:visible lets it hang past her own box edge.
   Width also needs a fixed override, not just position: Framer's own width
   is a percentage (meant to resolve against the much wider ancestor it used
   to sit in before this element moved here), so against the illustration's
   own ~510px box it rendered tiny -- aspect-ratio (still Framer's own rule,
   untouched) derives the matching height from this fixed width. */
.demcon-hero-intro-overlay .framer-1sl0blg {
  top: 6% !important;
  right: -38% !important;
  bottom: auto !important;
  left: auto !important;
  width: 420px !important;
  height: auto !important;
}

/* Mobile only: the desktop bubble above is a fixed 420px wide -- against
   the illustration's much narrower box at phone widths (~280px), the
   -38% right offset no longer clears her at all, so the (still 420px,
   wider than the whole screen) bubble ended up dead center over her
   face instead of beside her head. Confirmed on a real phone, not just
   a resized viewport. Shrunk and repositioned above/right of her head
   here -- her face is fully clear underneath it -- and hides the map
   background image (.framer-1yprofd) behind her, which was also
   requested to go on mobile only. Desktop's rule above is untouched.
   Only hidden for the *opening* intro frame, though -- it needs to come
   back once the intro releases into the "Welcome to DEMCON" content
   (setupHeroIntro in sticky-nav.js), which pins that content (and, on
   mobile, the map itself -- see pin() there) in place. Without the map
   there, that whole section reads as a huge blank gap. body's own
   .demcon-map-revealed class (added by setupHeroIntro right as the
   sequence begins) flips it back on -- two classes beats this rule's
   one, !important or not.
   visibility (not display) is what actually hides it -- confirmed on a
   real phone that display:none was the cause of two separate problems:
   it takes the element out of layout *and* defers its background image's
   own network fetch until the instant this class flips it back to
   display:block, so the image visibly loaded in late, after the "Welcome
   to DEMCON" text next to it already had. visibility:hidden keeps it
   fully laid out (image loading and all) the entire time it's invisible,
   so revealing it here is just an instant, already-loaded opacity fade
   with no fresh layout/fetch surprise -- which was also why it sometimes
   rendered in a visibly different spot than where it settles at rest. */
@media (max-width: 767.98px) {
  /* Mobile only: same scroll runway as the unscoped 60vh rule above, but
     in dvh (dynamic viewport height) -- iOS Safari's address bar hides as
     you scroll past this point, growing the real visible viewport, and a
     plain vh value doesn't reliably grow to match on every iOS version,
     leaving this section's own painted yellow background short of the
     actual screen. The gap that opened up below it exposed body's own
     background (light gray) instead -- reading as a stray white/gray
     card bleeding in from behind. dvh tracks the toolbar's real, current
     state, so this keeps pace with it instead. Plain-vh browsers ignore
     this line outright (unsupported property value), so they simply keep
     the unscoped rule's value -- this is additive, not a replacement. */
  .framer-L1F0F .framer-10ecca0 {
    padding-bottom: 60dvh;
  }

  .framer-1yprofd {
    visibility: hidden;
    /* !important on opacity here (and in the reveal rule below) because
       Framer's own baked rule for this element (.framer-L1F0F
       .framer-1yprofd, two classes) outranks this bare one-class
       selector on specificity alone -- without it, its baked opacity:.8
       would win outright and never actually go to 0 while hidden. */
    opacity: 0 !important;
  }

  body.demcon-map-revealed .framer-1yprofd {
    visibility: visible;
    opacity: 0.8 !important;
    transition: opacity 0.4s ease;
  }

  /* Mobile only: the signup form (.framer-1b6jkmt, a sibling of the
     title/date group inside "Container" -- see setupHeroIntro,
     sticky-nav.js) carries a Framer-baked solid white background that
     was never meant to show. Confirmed present at every width, but only
     actually visible on mobile -- harmless on desktop, so removed here
     rather than left as an implicit no-op there. */
  .framer-1b6jkmt {
    background: transparent !important;
  }

  /* Mobile only: the form's own background above is removed, but the
     text input itself still needs a solid background to read as a field
     against the yellow behind it -- its own baked background is
     transparent (confirmed), which is fine once the form had a white
     card behind it, but leaves it invisible now that the form itself is
     transparent too. */
  .framer-1b6jkmt .framer-form-text-input {
    background: #fff !important;
  }

  /* Mobile only: THE actual source of the "white/gray card bleeding
     behind the hero text" bug, AND of the signup form being pushed
     completely off-screen underneath it -- confirmed live via Safari's
     own Web Inspector (Responsive Design Mode, real WebKit -- this
     repo's own Chromium-based test tooling never reproduces it) on the
     reporter's own device. setupHeroIntro (sticky-nav.js) inserts a
     ".demcon-hero-intro-spacer" sized to match her illustration's own
     natural height, meant to hold that space open in whatever this
     element's *original* parent was before the illustration gets moved
     into the fade-out overlay -- turns out that original parent is
     nested *inside* "Container" itself (a Framer structural detail, not
     something this rewrite created), not a plain sibling of it, so this
     spacer was landing inside Container's own flex column, stacked right
     between the title/date group and the signup form. At this element's
     mobile size (her illustration is forced to a much taller 546px here
     -- see .framer-1mmgwc5 below), that's enough invisible height to
     push the form ~550-700px further down than the title, off the
     bottom of the viewport entirely, while simultaneously stretching
     "Container"'s own box tall enough to expose a huge span of the page
     background (fixed above, but this is what was actually behind it)
     underneath/around the text. Zeroed here via CSS rather than a
     one-time width check in JS -- this file's own history already
     includes a case where a single load-time window.innerWidth read
     went stale before this exact script's synchronous setup code ran a
     media query has no such timing dependency at all. Desktop still
     gets the spacer's full height, completely untouched -- this is
     mobile-only both because the illustration's own inflated 546px only
     applies there, and because desktop's own version of this same
     layout was already confirmed correct as-is. */
  .demcon-hero-intro-spacer {
    height: 0 !important;
  }

  /* Mobile only: the "Welcome to DEMCON" text block (title, tagline,
     date/location, signup form) was tall enough that the form only came
     into view after scrolling a little, rather than sitting fully
     visible within the initial viewport alongside everything else.
     Trimmed font sizes and the two gaps between title-group rows (not
     touching layout/position, just visual density) to bring the whole
     block comfortably inside a typical phone viewport. Desktop keeps its
     original sizing -- these are all scoped inside this same mobile
     media query. */
  .framer-ai4nw9 {
    row-gap: 24px !important;
  }

  .framer-1wm52dr {
    row-gap: 14px !important;
  }

  .framer-vc8yb8 h1 {
    font-size: 50px !important;
  }

  .framer-1mzcc26 h1 {
    font-size: 32px !important;
  }

  .framer-ai4nw9 p.text-type {
    font-size: 17px !important;
  }

  .framer-ai4nw9 [data-framer-name="Description"] p {
    font-size: 15px !important;
  }

  .framer-6c79b6 {
    row-gap: 20px !important;
  }

  /* Ellipse badge ("A Global Left Summit") is a separate absolutely-
     positioned decorative shape (width baked as a fixed 262px at this
     breakpoint) behind the text, not sized to its text content -- wider
     than the gap between the two flanking star icons, so it visually
     overlapped/extended past them. Width is the only thing that needs
     touching: it's centered via a translate(-50%, -50%) transform (not
     shown here, already baked), which recalculates against whatever the
     element's current width is on its own, with no need to also touch
     left/position. Confirmed live: 235px sits it comfortably between the
     stars' inner edges. */
  .framer-BJaSS.framer-h4e17i {
    width: 235px !important;
  }

  /* Bigger than Framer's own baked mobile width (75%) -- she reads as too
     small/distant on a phone screen otherwise. Width is capped at 100%
     (full-bleed, no side cropping); pushing it further to close the gap
     above her would start cropping her shoulders off the sides instead
     (confirmed -- tried 119%, looked broken). Height is scaled to match
     100%'s ratio (75%/410px -> 100%/546px) so she isn't stretched. Flush
     against the bottom of the viewport via the overlay's own
     align-items:flex-end -- an earlier version added a translateY to
     pull her up further/closer to the header, but that left a gap of
     bare yellow below her feet instead, which read worse than the gap
     above her had. */
  .framer-1mmgwc5 {
    width: 100% !important;
    height: 546px !important;
  }

  /* Bubble stays the same fixed 210px (per request) -- since it's
     already positioned as a child of the illustration via top/right
     percentages (not an independent fixed position), it rides along
     with her automatically as she grows/moves above; only the
     percentages themselves needed retuning for her new box size, same
     as when they were first calibrated for the old, smaller box.
     Bumped up again to 270px (per request, larger + nudged left) --
     -8%'s right edge had also drifted past the viewport's own right
     edge once she grew to a full 100% width hero box (same percentage,
     bigger box to measure it against); -2% both clears that and reads
     as the requested leftward nudge. */
  .demcon-hero-intro-overlay .framer-1sl0blg {
    top: -10% !important;
    right: -2% !important;
    width: 270px !important;
  }
}

/* Hard scroll lock for setupHeroScrollScrub (sticky-nav.js) -- see that
   function's own comment for why preventDefault() on wheel/touchmove
   alone isn't enough on a real phone (native momentum/inertia scrolling
   after the finger lifts isn't stoppable that way). overflow:hidden on
   both html and body since different engines treat either as "the"
   scrolling element. */
html.demcon-hero-scroll-lock,
html.demcon-hero-scroll-lock body {
  overflow: hidden !important;
}

/* Bottom-right rather than bottom-center -- centered under her, this sat
   right on top of her own artwork (skirt/legs) at most viewport heights
   and was easy to lose against it. */
.demcon-hero-scroll-arrow {
  position: absolute;
  right: 40px;
  bottom: 40px;
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
}

/* Mobile only: bottom-right (like desktop) put it right at her shoulder
   now that she's bigger and full-width -- centered instead reads
   cleaner and doesn't compete with her own artwork for that corner. Now
   that she's flush to the bottom edge too, this sits squarely over her
   own artwork geometrically -- z-index:2 above (her illustration is
   z-index:1) keeps it actually visible on top instead of painting
   underneath her, which is what a bare z-index:auto sibling does next to
   a z-index:1 one regardless of DOM/paint order. */
@media (max-width: 767.98px) {
  .demcon-hero-scroll-arrow {
    right: auto;
    left: 50%;
    transform: translateX(-50%);
  }

  /* Soft white halo behind the label+circle so they read clearly against
     whatever busy part of her artwork they land on, now that she's big
     enough to fill the space behind them completely (solid yellow
     background never shows through there anymore). A radial gradient
     (not a hard-edged circle) so it fades into her artwork instead of
     looking like a sticker. z-index:-1 keeps it *behind* the label/circle
     (both painted in normal flow) while still inside this element's own
     stacking context, which -- because the element itself is already
     z-index:2 -- stays above her (z-index:1) rather than dropping below
     it entirely. */
  .demcon-hero-scroll-arrow::before {
    content: '';
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 180px;
    height: 180px;
    background: radial-gradient(circle, rgba(255, 255, 255, 0.9) 0%, rgba(255, 255, 255, 0.55) 45%, rgba(255, 255, 255, 0) 75%);
    z-index: -1;
    pointer-events: none;
  }

  /* -on-dark only (the About page's text-reveal hero, setupHeroTextRevealLock
     -- see the comment on that class further down) -- NOT the plain
     .demcon-hero-scroll-arrow shared with the homepage's own Hero, which
     wasn't part of what was asked and reads fine as-is. That page's hero
     text is a single tall paragraph rather than the homepage's fixed
     illustration, so the same bottom:40px this arrow inherits from the
     base (unscoped) rule above sat close enough to the bottom of the
     text that its own 180px halo visibly overlapped the last line
     (confirmed live: the halo's white glow bled directly over "us.").
     Shrinking the halo and nudging the whole arrow further down (smaller
     bottom offset) both widen that gap -- smaller halo needs less
     clearance to begin with, and moving down adds clearance on top of
     that. */
  .demcon-hero-scroll-arrow-on-dark {
    bottom: 44px;
  }

  .demcon-hero-scroll-arrow-on-dark::before {
    width: 80px;
    height: 80px;
  }
}

.demcon-hero-scroll-label {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: #030509;
}

.demcon-hero-scroll-arrow {
  transition: opacity 0.3s ease-out;
}

/* Used on the About page's text-reveal hero (setupHeroTextRevealLock),
   which sits on a black background -- the default dark label would be
   invisible there. The circle+arrow itself already has its own light
   background regardless, so it needs no override. */
.demcon-hero-scroll-arrow-on-dark .demcon-hero-scroll-label {
  color: rgba(255, 255, 255, 0.85);
}

.demcon-hero-scroll-circle {
  width: 52px;
  height: 52px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 2px solid #030509;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.7);
  animation: demcon-arrow-bounce 1.6s ease-in-out infinite;
}

@keyframes demcon-arrow-bounce {
  0%,
  100% {
    transform: translateY(0);
  }
  50% {
    transform: translateY(8px);
  }
}

/* Generic scroll-reveal fade-up for every other baked-hidden element on the
   page (not just data-framer-appear-id ones -- see setupScrollReveal in
   sticky-nav.js) is applied entirely via inline styles set right before
   each element is revealed, so no shared selector-based rule is needed
   here. */

/* Text Ticker / about-page sub-nav marquee -- setupMarquee() (sticky-
   nav.js) rebuilds this section's contents from scratch: a full-bleed
   viewport div (100vw, so it always spans edge to edge regardless of any
   ancestor's padding/width) containing a track filled with enough
   repeated copies of the 3 links to exceed twice the viewport width,
   then doubled once more so animating exactly halfway across loops
   seamlessly no matter the screen size. */
.demcon-marquee-viewport {
  position: relative;
  left: 50%;
  width: 100vw;
  margin-left: -50vw;
  overflow: hidden;
  background-color: rgb(255, 153, 255);
  color: rgb(3, 5, 9);
  padding: 19px 0;
}

.demcon-marquee-track {
  display: flex;
  width: max-content;
  white-space: nowrap;
  font-family: 'IBM Plex Mono', monospace;
  font-weight: 400;
  font-style: normal;
  line-height: normal;
  letter-spacing: normal;
  font-size: 24px;
  text-transform: uppercase;
  animation: demcon-marquee 90s linear infinite;
}

.demcon-marquee-group {
  display: flex;
  flex: none;
  white-space: nowrap;
}

@media (max-width: 767.98px) {
  /* This marquee only exists on the About page (built entirely by
     setupMarquee(), sticky-nav.js -- .demcon-marquee-viewport/-track
     aren't used anywhere else), so no page-scoping needed here beyond
     the breakpoint itself. 19px vertical padding + a 24px font was
     tuned for desktop, where it's a comfortably slim sub-nav under a
     much bigger logo -- on a phone the logo is smaller but this bar
     wasn't, so it read as visually louder than the header above it
     instead of a quiet secondary nav. Both cut down together (thinner
     bar, smaller type) rather than just the font alone, so the bar's
     own height shrinks to match instead of leaving now-oversized
     padding around smaller text. */
  .demcon-marquee-viewport {
    padding: 8px 0;
  }

  .demcon-marquee-track {
    font-size: 14px;
  }
}

@keyframes demcon-marquee {
  from {
    transform: translateX(0);
  }
  to {
    transform: translateX(-50%);
  }
}

/* Agenda list row hover highlight, cycling through 4 colors by row index
   (see setupAgendaHover in sticky-nav.js, which tags each row with one of
   these classes). Each row's title already carries "transition: all 0.15s
   ease" baked into its inline style (Framer's own authored transition,
   meant to animate exactly this), so plain :hover rules are enough -- no
   JS needed for the animation itself. !important is required because the
   same properties (background-color, padding) are also set inline for the
   resting state, which otherwise always wins over a stylesheet rule. */
.demcon-agenda-hue-0:hover,
.demcon-agenda-hue-1:hover,
.demcon-agenda-hue-2:hover,
.demcon-agenda-hue-3:hover {
  padding-left: 16px !important;
  padding-right: 16px !important;
  font-size: 32px !important;
}

.demcon-agenda-hue-0:hover {
  background-color: #aee9f5 !important;
}

.demcon-agenda-hue-1:hover {
  background-color: #b3f5a3 !important;
}

.demcon-agenda-hue-2:hover {
  background-color: #fff500 !important;
}

.demcon-agenda-hue-3:hover {
  background-color: #ff99e5 !important;
}

/* Agenda row accordion -- each of the 8 verticals expands to show a
   one-line description on click, same grid-template-rows 0fr/1fr height
   trick and single-open-per-list behavior as the Principles accordion
   (about/index.html), just applied to plain divs instead of a <ul>/<li>
   since that's what Agenda's own markup already uses. Placeholder copy:
   this content never existed anywhere in the static export (confirmed
   against the original pristine zip) -- it lived in Framer's own hosted
   component code, which isn't part of any export. Swap in real copy for
   each vertical whenever it's ready. */
.demcon-agenda-row {
  border-radius: 4px;
  transition: transform 0.15s ease;
}

.demcon-agenda-item-detail {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 0.25s ease;
}

.demcon-agenda-item.is-open .demcon-agenda-item-detail {
  grid-template-rows: 1fr;
}

.demcon-agenda-item-detail p {
  overflow: hidden;
  margin: 0;
  max-width: 640px;
  font-family: 'Satoshi', 'Satoshi Placeholder', sans-serif;
  font-weight: 300;
  font-size: 17px;
  line-height: 1.55em;
  color: #3a3a3a;
  padding: 0;
}

.demcon-agenda-item.is-open .demcon-agenda-item-detail p {
  padding-bottom: 24px;
}

.demcon-agenda-item.is-open .ux-title + span {
  transform: rotate(90deg) !important;
}

.ux-title + span {
  transition: transform 0.2s ease;
  display: inline-block;
}

/* Participate section CTA buttons ("Apply to Speak" / "Register to Attend"
   / "Request Info") -- the small icon-wrapper square (already black by
   default via Framer's own CSS) turns red while hovering anywhere on the
   button. Framer originally drove this via a JS-toggled ".hover" class
   rather than plain CSS :hover; a plain :hover selector reaches the same
   element without needing to replicate that class-toggling. Exact color
   (rgb(255, 0, 0), not a rounder-looking approximation) and the 45deg
   rotation below are both pulled directly from the original compiled
   bundle's "Default" button variant (still recoverable via git history --
   see commit 6ea3780), which is what these plain CTA buttons use. */
button:hover [data-framer-name="Icon Wrapper"] {
  background-color: rgb(255, 0, 0) !important;
}

[data-framer-name="Icon Wrapper"] {
  transition: background-color 0.2s ease-out;
}

/* The arrow points diagonally (up-right) at rest and rotates to point
   straight right on hover. */
button:hover [data-framer-name="Icon Wrapper"] svg {
  transform: rotate(45deg) !important;
}

[data-framer-name="Icon Wrapper"] svg {
  transition: transform 0.2s ease-out;
}

/* Text-swap-on-hover: the visible label slides right while a second copy
   (fixed to match by setupCtaButtonHover in sticky-nav.js) slides in from
   its off-screen resting position to replace it. Framer's own original CSS
   drove this by toggling the "position" property itself (relative <->
   absolute) between the two states -- but "position" can never be
   animated by a CSS transition, in either direction, so that approach can
   only ever snap instantly no matter what transition/duration is set on
   it. Using transform: translateX() instead (leaving "position" alone at
   all times) produces the same visual end state while actually being
   transitionable. The exact slide distance (--cta-slide) is measured per
   button in JS rather than a single guessed constant: .framer-1g31bsy
   (position:absolute, fixed left:-200px) and .framer-12vo1jw
   (position:relative, wherever this particular button's own padding and
   text length puts it) sit relative to different things, so the real gap
   between their resting spots varies by button -- a guessed constant
   overshoots on some buttons and undershoots on others. */
.framer-TkHaO .framer-1g31bsy,
.framer-TkHaO .framer-12vo1jw {
  transition: transform 0.2s ease-out, opacity 0.2s ease-out;
}

/* .framer-dk5ask (the shared clipping container both slide through) has
   overflow:hidden, so without this a sliding text is fully opaque right up
   until the instant it crosses the clip boundary, where it gets abruptly
   chopped off mid-character rather than fading -- reads as the box itself
   glitching/disappearing rather than a clean slide transition. Fading each
   copy out as it leaves and in as it arrives means the clip boundary is
   never crossed by fully-opaque text. !important is required: this
   element's baked SSR inline style says opacity:1 (Framer's static export
   default for a RichTextContainer, unaware that in this particular
   component it's meant to start hidden), which otherwise beats a plain
   stylesheet rule regardless of specificity. */
.framer-TkHaO .framer-1g31bsy {
  opacity: 0 !important;
}

.framer-TkHaO:hover .framer-1g31bsy,
.framer-TkHaO:hover .framer-12vo1jw {
  transform: translateX(var(--cta-slide, 200px)) !important;
}

.framer-TkHaO:hover .framer-1g31bsy {
  opacity: 1 !important;
}

.framer-TkHaO:hover .framer-12vo1jw {
  opacity: 0 !important;
}

/* The Text Wrapper's ::after pseudo-element (Framer's own generic
   data-border="true" outline mechanism) is already present at rest -- this
   isn't a border this conversion added or removed. What read as it
   "expanding" on hover was the sliding text uncovering more of that
   already-there edge as it moves out of the way, making a border that was
   always the same size suddenly more noticeable. Pinning it to one fixed
   value, unconditionally (never toggled by :hover), keeps it visually
   identical in both states -- present, but never appearing to change. */
.framer-TkHaO .framer-6fk6yc[data-border]::after {
  border: 1px solid rgb(35, 31, 32) !important;
}

/* Join Us modal -- hand-built to replace Framer's React-only overlay, which
   this session confirmed rendered underneath the sticky header. */
.demcon-modal {
  position: fixed;
  inset: 0;
  z-index: 10000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
}

.demcon-modal[hidden] {
  display: none;
}

.demcon-modal-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(3, 5, 9, 0.7);
}

.demcon-modal-dialog {
  position: relative;
  z-index: 1;
  display: flex;
  width: 100%;
  max-width: 940px;
  max-height: calc(100vh - 48px);
  overflow-y: auto;
  border: 2.5px solid #030509;
  border-radius: 16px;
  box-shadow: 8px 8px 0 #030509;
  background: #fff;
}

.demcon-modal-close {
  position: absolute;
  top: 16px;
  right: 16px;
  z-index: 2;
  width: 36px;
  height: 36px;
  border: 2px solid #030509;
  border-radius: 50%;
  background: #fff;
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
}

.demcon-modal-panel-dark,
.demcon-modal-panel-light {
  flex: 1 1 50%;
  min-width: 0;
  padding: 48px 36px;
}

.demcon-modal-panel-dark {
  background: #030509;
  color: #fff;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
}

/* Same [hidden] specificity fix as .demcon-modal-form/.demcon-propose-
   modal-success elsewhere in this file -- the Register modal hides this
   panel (along with its own heading) on submit so only the Thank You
   message remains, and the explicit display:flex above would otherwise
   silently defeat that. */
.demcon-modal-panel-dark[hidden] {
  display: none;
}

.demcon-modal-mark {
  margin: 0;
  font-family: 'League Spartan', 'League Spartan Placeholder', sans-serif;
  font-size: 32px;
  font-weight: 700;
  letter-spacing: -0.02em;
  text-transform: uppercase;
}

.demcon-modal-mark span {
  display: block;
  font-size: 40px;
  color: #ff66ff;
}

.demcon-modal-note {
  margin: 0;
  font-size: 14px;
  line-height: 1.6em;
}

.demcon-modal-when h6 {
  margin: 0 0 8px;
  font-family: 'Inter', sans-serif;
  font-size: 14px;
  letter-spacing: -0.01em;
  text-transform: uppercase;
}

.demcon-modal-when p {
  margin: 0;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 14px;
  line-height: 1.7em;
  letter-spacing: -0.02em;
  text-transform: uppercase;
  color: #fff500;
}

.demcon-modal-when p span {
  color: rgba(255, 255, 255, 0.6);
}

.demcon-modal-panel-light h2 {
  margin: 0 0 28px;
  font-family: 'RT Goodneighbor Script', 'RT Goodneighbor Script Placeholder', sans-serif;
  font-size: 32px;
  font-weight: 400;
  line-height: 1.1em;
  color: #030509;
}

.demcon-modal-panel-light h2 span {
  font-family: 'Hidalgo Regular', 'Hidalgo Regular Placeholder', sans-serif;
  font-size: 40px;
  color: #00b3ff;
  -webkit-text-stroke: 1px #030509;
}

.demcon-modal-form {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

/* Without this, form.hidden = true (used on submit to swap in the success
   message) has no visual effect -- the [hidden] attribute's default
   display:none carries the same specificity as .demcon-modal-form's own
   display:flex above, and this rule wins by coming later in the sheet. */
.demcon-modal-form[hidden] {
  display: none;
}

.demcon-modal-form label {
  display: flex;
  flex-direction: column;
  gap: 6px;
  font-family: 'Satoshi', 'Satoshi Placeholder', sans-serif;
  font-size: 14px;
  font-weight: 600;
  text-transform: uppercase;
  color: #030509;
}

.demcon-modal-form label em {
  font-style: normal;
  font-weight: 400;
  text-transform: none;
  opacity: 0.5;
}

.demcon-modal-form input,
.demcon-modal-form textarea {
  padding: 12px 14px;
  border: 1.5px solid rgba(3, 5, 9, 0.3);
  border-radius: 8px;
  font-family: 'Satoshi', 'Satoshi Placeholder', sans-serif;
  font-size: 15px;
  color: #030509;
  background: #fff;
}

.demcon-modal-form textarea {
  resize: vertical;
  min-height: 44px;
  font-family: inherit;
}

.demcon-modal-form input:focus,
.demcon-modal-form textarea:focus {
  outline: none;
  border-color: #030509;
}

/* Custom "Salon Topic / Vertical" dropdown -- a native <select>'s open
   popup is drawn by the browser/OS itself (on macOS Chrome, a native-
   themed gray/blue listbox) and doesn't reliably take CSS styling on its
   options across engines, so a real select couldn't be made to actually
   look black/white/pink once opened. This is a hand-built combobox
   instead: a styled trigger button plus an absolutely-positioned list,
   with a hidden input carrying the chosen value for the form. */
.demcon-select {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.demcon-select-label {
  font-family: 'Satoshi', 'Satoshi Placeholder', sans-serif;
  font-size: 14px;
  font-weight: 600;
  text-transform: uppercase;
  color: #030509;
}

/* Qualified with .demcon-modal-form (see the same note on
   .demcon-propose-links-add/-remove above) so this ties, and wins over,
   the shared .demcon-modal-form button pink-pill rule -- otherwise that
   rule's extra type selector (button) outranks a single bare class. */
.demcon-modal-form .demcon-select-trigger {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  width: 100%;
  margin-top: 0;
  padding: 12px 14px;
  border: 1.5px solid rgba(3, 5, 9, 0.3);
  border-radius: 8px;
  background: #030509;
  color: #fff;
  font-family: 'Satoshi', 'Satoshi Placeholder', sans-serif;
  font-size: 15px;
  font-weight: 400;
  text-align: left;
  text-transform: none;
  cursor: pointer;
}

.demcon-modal-form .demcon-select-trigger:focus {
  outline: none;
  border-color: #030509;
  box-shadow: 0 0 0 2px #ff0074;
}

.demcon-select.is-invalid .demcon-select-trigger {
  border-color: #ff0074;
  box-shadow: 0 0 0 2px rgba(255, 0, 116, 0.3);
}

.demcon-select-value {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.demcon-select-trigger[data-placeholder] .demcon-select-value {
  color: rgba(255, 255, 255, 0.55);
}

.demcon-select-arrow {
  flex: none;
  font-size: 12px;
  transition: transform 0.2s ease;
}

.demcon-select.is-open .demcon-select-arrow {
  transform: rotate(180deg);
}

.demcon-select-list {
  position: absolute;
  z-index: 20;
  top: calc(100% + 6px);
  left: 0;
  right: 0;
  max-height: 260px;
  overflow-y: auto;
  margin: 0;
  padding: 6px;
  list-style: none;
  border: 1.5px solid #030509;
  border-radius: 8px;
  background: #030509;
  box-shadow: 4px 4px 0 #030509;
}

.demcon-select-list[hidden] {
  display: none;
}

.demcon-select-list li {
  padding: 10px 12px;
  border-radius: 6px;
  font-family: 'Satoshi', 'Satoshi Placeholder', sans-serif;
  font-size: 15px;
  color: #fff;
  cursor: pointer;
}

.demcon-select-list li:hover,
.demcon-select-list li.is-active {
  background: #ff0074;
}

.demcon-select-list li[aria-selected="true"] {
  font-weight: 700;
}

.demcon-modal-form button {
  margin-top: 8px;
  padding: 14px 20px;
  border: 2px solid #030509;
  border-radius: 999px;
  background: #ff0074;
  color: #fff;
  font-family: 'League Spartan', 'League Spartan Placeholder', sans-serif;
  font-size: 15px;
  font-weight: 600;
  text-transform: uppercase;
  cursor: pointer;
}

@media (max-width: 767.98px) {
  .demcon-modal-dialog {
    flex-direction: column;
  }
}

/* Blinking cursor for the hero typewriter effect (sticky-nav.js's
   setupTypewriter). Framer's own runtime drove this blink at runtime; no
   equivalent keyframes existed in the static export's retained CSS. */
.text-type__cursor {
  animation: demcon-cursor-blink 1s step-end infinite;
}

@keyframes demcon-cursor-blink {
  0%,
  100% {
    opacity: 1;
  }
  50% {
    opacity: 0;
  }
}

/* Mobile support -- the static export only ever baked in the Desktop nav
   variant (Framer's React runtime used to swap in a separate Mobile variant
   client-side based on viewport, and hydration is gone), so the header and
   several big display headings need their own hand-built responsive rules
   here instead of relying on missing Framer breakpoint markup. */

/* Hamburger button: hidden on desktop, shown only below the mobile
   breakpoint (see the @media block further down). */
.demcon-mobile-menu-toggle {
  display: none;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  flex: none;
  border: 2px solid #030509;
  border-radius: 50%;
  background: none;
  padding: 0;
  cursor: pointer;
}

.demcon-mobile-menu-toggle svg {
  width: 22px;
  height: 22px;
}

@media (max-width: 979.98px) {
  /* Framer's own nav-content rule hardcodes a 1200px desktop width with no
     mobile override (only the Desktop breakpoint variant was baked into
     this static export), which pushes the logo and hamburger off-screen at
     narrow widths since the row centers itself around that phantom width.
     Extended up to 979.98px (not just the 767.98px phone cutoff) -- this
     whole hamburger treatment is also the fix for the 768-979.98px tablet
     range, where Framer's own un-redesigned desktop nav clips/hides link
     text (the logo and JOIN US button keep their fixed desktop widths,
     squeezing the middle link row below the space it needs). */
  .framer-1kfysrm-container .framer-k2S74.framer-1fjhuce {
    width: 100% !important;
  }

  /* Same problem one level down: the logo's own flex:none wrapper is stuck
     at a fixed 317px (sized to fit the desktop menu list that lived
     alongside it), which is wider than most phones. Let it size to its
     actual content (just the logo now that the menu list is hidden). */
  .framer-1kfysrm-container .framer-fiv2ey,
  .framer-1kfysrm-container .framer-qf0vfz {
    width: auto !important;
  }

  /* The row centers its two children (logo group + button/hamburger group)
     as a pair, which only looked right on desktop because the menu list
     padded that pair out to the full row width. With the menu list hidden,
     centering pulls both groups toward the middle and off-screen at each
     edge -- push them to opposite ends instead. */
  .framer-1kfysrm-container .framer-wyw1yc {
    place-content: space-between !important;
  }

  /* Extra breathing room so the logo isn't jammed against the screen edge. */
  .framer-1kfysrm-container .framer-8gkjzd {
    padding-left: 36px !important;
  }

  /* Hide the desktop link row and "JOIN US" button in their normal flow
     position; both reappear as a stacked full-width dropdown below the
     header when .demcon-mobile-menu-open is toggled on. */
  .framer-1kfysrm-container .framer-1ce9b2z,
  .framer-1kfysrm-container .framer-qf0vfz .framer-fp7wp4-container {
    display: none;
  }

  .demcon-mobile-menu-toggle {
    display: flex;
  }

  .framer-1kfysrm-container.demcon-mobile-menu-open .framer-1ce9b2z,
  .framer-1kfysrm-container.demcon-mobile-menu-open .framer-qf0vfz .framer-fp7wp4-container {
    display: flex !important;
    flex-direction: column !important;
    align-items: stretch !important;
    position: fixed;
    left: 0;
    right: 0;
    width: 100%;
    gap: 28px !important;
    padding: 36px;
    background: #030509;
    border-bottom: none;
    box-sizing: border-box;
  }

  .framer-1kfysrm-container.demcon-mobile-menu-open .framer-qf0vfz .framer-fp7wp4-container {
    padding-top: 0;
    /* This is the last of the two stacked fixed-position panels (links
       list above, this "JOIN US" panel below it) that make up the open
       mobile menu overlay -- neither had a bottom/height, so their box
       heights just tracked their own content, leaving a gap between the
       end of "JOIN US" and the true bottom of the screen where the page
       underneath (still there, just visually behind) showed through.
       bottom:0 stretches this trailing panel the rest of the way down. */
    bottom: 0;
  }

  /* Match the mobile dropdown's look to the reference site's old mobile
     menu: black background, bold white uppercase labels, no border box
     around "JOIN US" -- it reads as just another link in the stack. */
  .framer-1kfysrm-container.demcon-mobile-menu-open .framer-1ce9b2z a.framer-YmthU {
    width: 100%;
    border-bottom: none !important;
  }

  .framer-1kfysrm-container.demcon-mobile-menu-open .framer-1ce9b2z a.framer-YmthU p.framer-text,
  .framer-1kfysrm-container.demcon-mobile-menu-open .framer-qf0vfz .framer-fp7wp4-container p.framer-text {
    color: #fff !important;
    font-size: 28px !important;
    font-weight: 700 !important;
    text-transform: uppercase;
  }

  .framer-1kfysrm-container.demcon-mobile-menu-open .framer-qf0vfz .framer-fp7wp4-container a {
    width: 100%;
  }

  .framer-1kfysrm-container.demcon-mobile-menu-open .framer-qf0vfz .framer-fp7wp4-container [data-border='true']::after {
    border: none !important;
  }

  /* JOIN US reads as "just another link" in this stacked mobile menu (see
     the comment above removing its border), but was still carrying its
     desktop nav-button tap treatment underneath -- solid red fill,
     shrinking/whitening label (.framer-NoclU's own :hover rules, tuned
     for the bordered button it is everywhere else) -- instead of the
     plain translateY-and-pink-text pop every other item in this same
     list gets on tap (.framer-YmthU's :hover rule). Overriding both of
     its rules here, scoped to this open-mobile-menu context only, so it
     matches the rest of the stack instead of the button styling. */
  .framer-1kfysrm-container.demcon-mobile-menu-open .framer-qf0vfz .framer-fp7wp4-container .framer-NoclU:hover {
    background-color: transparent !important;
  }

  .framer-1kfysrm-container.demcon-mobile-menu-open .framer-qf0vfz .framer-fp7wp4-container .framer-NoclU p {
    transition: transform 0.15s ease, color 0.15s ease !important;
  }

  .framer-1kfysrm-container.demcon-mobile-menu-open .framer-qf0vfz .framer-fp7wp4-container .framer-NoclU:hover p.framer-text {
    transform: translateY(-3px) scale(1.15);
    color: #ff0074 !important;
    font-size: 28px !important;
  }

  /* The toggle turns into a yellow "X" close button while the menu is open
     (icon itself is swapped in sticky-nav.js). */
  .framer-1kfysrm-container.demcon-mobile-menu-open .demcon-mobile-menu-toggle {
    position: fixed;
    top: 18px;
    right: 24px;
    z-index: 1;
    background: #fff500;
    border-color: #030509;
  }

  /* On a dark nav (About page), .demcon-nav-black's own svg rule inverts
     every icon to white so the plain hamburger lines show up against the
     near-black header -- but once open, this same icon becomes the X
     inside a solid yellow circle, and that same white-invert filter
     leaves it nearly invisible against the yellow instead. The X's own
     inline stroke (sticky-nav.js) is already dark; just needs the
     inherited invert filter switched back off here for it to show. */
  .framer-1kfysrm-container.demcon-nav-black.demcon-mobile-menu-open .demcon-mobile-menu-toggle svg {
    filter: none !important;
  }
}

@media (max-width: 767.98px) {
  /* Oversized display headings: Framer baked one fixed desktop font-size
     per element with no mobile override, so each is scaled down here.
     Grouped by their shared current size/font rather than one rule per
     element. */
  .framer-1mzcc26 h1,
  .framer-158qr1l h1,
  .framer-1dq21v1 h1,
  .framer-1eig5wz h1 {
    --framer-font-size: 36px !important;
  }

  .framer-vc8yb8 h1 {
    --framer-font-size: 56px !important;
  }

  .framer-1un2mu8 h1 {
    --framer-font-size: 44px !important;
  }

  .framer-10hfbqq h2,
  .framer-tylev3 h2 {
    --framer-font-size: 40px !important;
  }

  .framer-1b7xxj1 h2 {
    --framer-font-size: 32px !important;
  }

  .framer-hvsa7k h4,
  .framer-1g3j7jn h2 {
    --framer-font-size: 34px !important;
  }

  .framer-1xjikn1 h2 {
    --framer-font-size: 40px !important;
  }

  .framer-l9qtcv h2 {
    --framer-font-size: 32px !important;
  }

  /* Footer: Framer actually did author a mobile layout for this one (real
     column-stacking rules exist in the retained stylesheet), but every rule
     is scoped under a ".framer-v-1769wxc" mobile-variant class that only
     the removed React runtime ever applied at hydration time -- it's not on
     the real <footer> element (which only ever carries the Desktop variant
     class), so none of it can match. Re-applying the same values directly
     on the base classes here, using 100% instead of the variant's hardcoded
     390px so it holds up narrower than one specific phone. */
  .framer-wxmzE.framer-f91918 {
    width: 100% !important;
    flex-direction: column !important;
    padding: 60px 20px 14px !important;
  }

  .framer-13tog5q,
  .framer-8w9uat {
    flex: none !important;
    width: 100% !important;
  }

  .framer-1fyds12 {
    flex-direction: column !important;
    gap: 40px !important;
  }

  .framer-1kn4y54,
  .framer-1ves7pb {
    width: 100% !important;
  }

  .framer-5jaly6 {
    flex-direction: column !important;
    place-content: flex-start !important;
    align-items: flex-start !important;
    gap: 16px !important;
  }

  .framer-md55zm {
    flex-wrap: wrap !important;
    justify-content: flex-start !important;
    gap: 12px 32px !important;
    width: 100% !important;
  }
}

@media (min-width: 980px) and (max-width: 1199.98px) {
  /* Upper half of the 768-1199.98px tablet gap: keeps the real desktop nav
     row (no hamburger) but frees the logo and JOIN US button from their
     fixed 317px/265px desktop widths -- sized to fit alongside a menu list
     that had a full 1200px+ row to share them with -- so the middle link
     row gets the room it actually needs and stops clipping. Confirmed via
     live measurement: at this width freeing these two alone leaves far
     more room than the link row needs, so nothing else here has to shrink. */
  .framer-1kfysrm-container .framer-fiv2ey,
  .framer-1kfysrm-container .framer-qf0vfz {
    width: auto !important;
    min-width: 0 !important;
  }
}

@media (min-width: 768px) and (max-width: 1199.98px) {
  /* Hero tagline ("FEBRUARY 02-04, 2027 - Bruxelles, Belgium"): Framer
     baked this row at a fixed 500px box with white-space:pre (never
     wraps), sized for the ~1200px+ desktop hero where that box has room
     to spare. Across the whole tablet range the box itself renders
     narrower than the 24px single-line text needs (measured ~542px),
     clipping both ends. The row already centers its text and sits in a
     hero column with plenty of clear space either side, so instead of
     fighting the fixed width, just let it overflow its own box -- text
     stays centered and fully readable instead of getting cropped. */
  .framer-19uexfm {
    overflow: visible !important;
  }
}

/* DemCon Principles summary (about/index.html) -- hand-built, not a Framer
   export, since this content/section doesn't exist in the original design.
   Matches the site's existing type (IBM Plex Mono labels, League Spartan
   headings/body) and near-black palette used elsewhere. Yellow margins
   (matching the homepage hero's background) rather than pink, which
   clashed sitting right next to the pink text marquee below it; accents
   (label, toggle icon, link) use the marquee's own pink instead, so the
   two read as intentionally paired rather than clashing. */
.demcon-principles-section {
  background: #fff500;
  padding: 140px 30px;
  position: relative;
  overflow: hidden;
  /* Unlike the Framer-native sections around it, which each explicitly
     set flex:none + width:100% on themselves, this one is hand-built and
     had no such override -- it was picking up some other flex default
     (flex: 0 1 auto) from the page's flex layout instead of stretching,
     leaving equal gaps on both sides instead of spanning the viewport. */
  flex: none;
  width: 100%;
}

/* The Universal Declaration of Human Rights, recreated as text (public
   domain -- not an image), zoomed in and run behind the DemCon Principles
   card. setupUDHRSequence (sticky-nav.js) locks scrolling once this
   section reaches the top of the viewport and animates .demcon-udhr-stage
   upward through the text, pausing on each "right to" in turn -- see
   .demcon-udhr-hit below. */
.demcon-udhr-bg {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
}

.demcon-udhr-stage {
  position: relative;
  padding: 0 60px;
  column-count: 2;
  column-gap: 80px;
  transition: transform 0.6s cubic-bezier(0.34, 0.76, 0.4, 1);
  will-change: transform;
}

.demcon-udhr-article {
  font-family: Georgia, 'Times New Roman', serif;
  font-size: clamp(20px, 2.4vw, 32px);
  line-height: 1.6;
  text-align: justify;
  color: rgba(3, 5, 9, 0.14);
  margin: 0 0 2.2em;
  break-inside: avoid;
  -webkit-column-break-inside: avoid;
}

.demcon-udhr-num {
  font-weight: 700;
}

.demcon-udhr-hit {
  background: none;
  color: rgba(255, 0, 116, 0.4);
  font-weight: 700;
  font-style: italic;
  transition: color 0.4s ease, background-color 0.4s ease, font-size 0.4s ease;
}

.demcon-udhr-hit.is-active {
  color: #ff0074;
  background-color: rgba(255, 255, 255, 0.92);
  font-size: 1.2em;
  border-radius: 4px;
  padding: 0 0.1em;
}

@media (max-width: 767.98px) {
  .demcon-udhr-stage {
    padding: 24px 24px 0;
    column-count: 1;
  }
}

.demcon-principles-container {
  max-width: 1100px;
  margin: 0 auto;
  /* Bumped from 0.86 to 0.97 opacity (and a stronger blur) -- the UDHR
     text running behind this card was showing through prominently
     enough to compete with the card's own text, hurting legibility. */
  background: rgba(250, 249, 245, 0.97);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  padding: 80px;
  border-radius: 8px;
  position: relative;
  z-index: 1;
}

@media (max-width: 767.98px) {
  .demcon-principles-container {
    padding: 48px 28px;
    /* Bleed out to the full viewport width -- the parent section's own
       24px horizontal padding (see .demcon-principles-section above)
       otherwise leaves this card inset with yellow gutters on both
       sides. Negative margin matching that padding pulls it back out
       to the edges; border-radius dropped since a rounded card flush
       against the viewport edge no longer reads as a "card". */
    margin-left: -24px;
    margin-right: -24px;
    border-radius: 0;
  }
}

.demcon-principles-label {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 18px;
  letter-spacing: -0.02em;
  text-transform: uppercase;
  color: #ff99ff;
  margin: 0 0 24px;
}

.demcon-principles-heading {
  font-family: 'League Spartan', sans-serif;
  font-weight: 700;
  font-size: clamp(36px, 5vw, 56px);
  letter-spacing: -0.02em;
  color: #030509;
  margin: 0 0 32px;
}

.demcon-principles-intro {
  font-family: 'League Spartan', sans-serif;
  font-weight: 300;
  font-size: 20px;
  line-height: 1.5em;
  color: #3a3a3a;
  max-width: 760px;
  margin: 0 0 64px;
}

/* Single column now -- the two category subtitles are tabs (click to
   switch which list of items shows below) rather than two permanent
   side-by-side lists. */
.demcon-principles-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 12px 32px;
  margin-bottom: 32px;
}

.demcon-principles-tab {
  all: unset;
  box-sizing: border-box;
  cursor: pointer;
  font-family: 'League Spartan', sans-serif;
  font-weight: 700;
  font-size: 22px;
  letter-spacing: -0.02em;
  color: rgba(3, 5, 9, 0.35);
  padding-bottom: 8px;
  border-bottom: 3px solid transparent;
  transition: color 0.2s ease, border-color 0.2s ease;
}

.demcon-principles-tab:hover {
  color: rgba(3, 5, 9, 0.7);
}

.demcon-principles-tab.is-active {
  color: #030509;
  border-bottom-color: #ff99ff;
}

.demcon-principles-panels {
  margin-bottom: 56px;
}

.demcon-principles-panel {
  display: none;
  list-style: none;
  margin: 0;
  padding: 0;
}

.demcon-principles-panel.is-active {
  display: block;
}

/* Each item is a toggle button (click to expand a 1-sentence explanation)
   that also "pops" on hover -- a slight lift/scale plus a highlight color,
   cycling through the same 4 hues the Agenda list's rows already use (see
   setupAgendaHover/demcon-agenda-hue-* ), so the two interactions feel like
   one shared pattern rather than two different ones. */
.demcon-principles-item {
  border-top: 1px solid rgba(3, 5, 9, 0.12);
}

.demcon-principles-toggle {
  all: unset;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  font-family: 'League Spartan', sans-serif;
  font-weight: 400;
  font-size: 18px;
  color: #3a3a3a;
  padding: 12px 12px 12px 24px;
  position: relative;
  cursor: pointer;
  border-radius: 4px;
  transition: background-color 0.15s ease, transform 0.15s ease, padding 0.15s ease;
}

.demcon-principles-toggle:before {
  content: '';
  position: absolute;
  left: 0;
  width: 12px;
  height: 2px;
  background: #ff99ff;
  transition: opacity 0.15s ease;
}

.demcon-principles-toggle:hover,
.demcon-principles-toggle:focus-visible {
  transform: scale(1.03);
  padding-left: 30px;
  padding-right: 18px;
}

.demcon-principles-toggle:hover:before,
.demcon-principles-toggle:focus-visible:before {
  opacity: 0;
}

.demcon-principles-hue-0 .demcon-principles-toggle:hover {
  background-color: #aee9f5;
}

.demcon-principles-hue-1 .demcon-principles-toggle:hover {
  background-color: #b3f5a3;
}

.demcon-principles-hue-2 .demcon-principles-toggle:hover {
  background-color: #fff500;
}

.demcon-principles-hue-3 .demcon-principles-toggle:hover {
  background-color: #ff99e5;
}

.demcon-principles-item-icon {
  font-family: 'League Spartan', sans-serif;
  font-weight: 700;
  font-size: 20px;
  line-height: 1;
  color: #030509;
  transition: transform 0.2s ease;
}

.demcon-principles-item.is-open .demcon-principles-item-icon {
  transform: rotate(45deg);
}

.demcon-principles-item-detail {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 0.25s ease;
}

.demcon-principles-item.is-open .demcon-principles-item-detail {
  grid-template-rows: 1fr;
}

.demcon-principles-item-detail p {
  overflow: hidden;
  margin: 0;
  font-family: 'League Spartan', sans-serif;
  font-weight: 300;
  font-size: 16px;
  line-height: 1.5em;
  color: #3a3a3a;
  padding: 0 12px 0 24px;
}

.demcon-principles-item.is-open .demcon-principles-item-detail p {
  padding-bottom: 16px;
}

.demcon-principles-full-link {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-family: 'League Spartan', sans-serif;
  font-weight: 700;
  font-size: 16px;
  letter-spacing: -0.01em;
  text-transform: uppercase;
  text-decoration: none;
  color: #030509;
  background-color: #ff99ff;
  border: 2px solid #030509;
  border-radius: 999px;
  padding: 14px 26px;
  transition: background-color 0.2s ease, color 0.2s ease, transform 0.2s ease, box-shadow 0.2s ease;
}

.demcon-principles-full-link-arrow {
  transition: transform 0.2s ease;
}

.demcon-principles-full-link:hover,
.demcon-principles-full-link:focus-visible {
  background-color: #030509;
  color: #ff99ff;
  border-color: #030509;
  transform: translate(-2px, -2px);
  box-shadow: 4px 4px 0 #ff99ff;
}

.demcon-principles-full-link:hover .demcon-principles-full-link-arrow,
.demcon-principles-full-link:focus-visible .demcon-principles-full-link-arrow {
  transform: translateX(4px);
}

.demcon-principles-full-link:active {
  transform: translate(0, 0);
  box-shadow: none;
}

@media (max-width: 767.98px) {
  /* Top/bottom bumped up from 90px -- this section's total height is
     what setupStackedSectionOffsets (sticky-nav.js) measures to decide
     how long it stays pinned/stuck while scrolling (top = vh - own
     height), and until now that height was almost entirely down to
     .demcon-principles-container's own (in-flow) height, since the UDHR
     animation behind it (.demcon-udhr-bg) is position:absolute and
     contributes nothing to layout height on its own. That meant the pin
     was effectively sized around the white card, with the animation just
     along for whatever ride that gave it -- easy to scroll past before
     it's actually done. Padding is real in-flow space on this section
     itself, so growing it directly grows the section's own height (and
     with it, the pin) independent of the card -- the animation now gets
     a dedicated scroll runway of its own.

     Pulled back from 400px to 200px -- this is unrelated to (and doesn't
     fix) the hero-peeking-through-early bug reported separately: the UDHR
     background (.demcon-udhr-bg) is inset:0 on this section, so it's
     already visible starting at this section's own top edge regardless
     of how much padding sits above the white card -- that peek is
     entirely about the hero section above falling short of the real
     viewport, not about this padding "reaching back" into it. 400px just
     read as more dead space than intended once the hero-height bug made
     a sliver of it visible prematurely. */
  .demcon-principles-section {
    padding: 200px 24px;
  }

  .demcon-principles-grid {
    grid-template-columns: 1fr;
    gap: 40px;
  }
}

/* The page root renders `display:contents`, so its actual flex layout (and
   this reordering) comes from the grandparent -- Framer bakes a flex
   `order` on every section, per breakpoint, reflecting each one's position
   in the *original homepage's* section sequence (e.g. order:2 and order:9
   here, since that's where "Statistics - Section" and "Bruxelles Calling"
   used to sit among many other homepage sections). Reusing them on this
   new page needs their order reset to match *this* page's sequence
   instead, at every breakpoint -- our own new Principles section has no
   baked order at all (defaults to 0), which otherwise puts it first.
   Sequence here: hero, Principles, marquee, Bruxelles Calling, Join Us.
   Scoped to .demcon-about-page so it doesn't touch these same classes'
   ordering back on the homepage, where they're shared from. */
.demcon-about-page .framer-tuaoj3 {
  order: 1 !important;
}

.demcon-about-page .demcon-principles-section {
  order: 2 !important;
}

.demcon-about-page .framer-1rbxc9t {
  order: 4 !important;
}

/* Join Us section, duplicated from the homepage and placed right after
   Bruxelles Calling in the HTML -- but source order doesn't matter here
   (see above), so it needs its own explicit order too, or it defaults to
   0 and renders first. */
.demcon-about-page .framer-13wosc3 {
  order: 5 !important;
}

/* As a hero, this section should cover the full viewport on load, not
   just whatever height its baked padding (114px) + text naturally add up
   to -- min-height:100vh overrides the baked height:min-content. Content
   stays centered via the baked place-content:center/align-items:center on
   this same class. Scoped to .demcon-about-page so the homepage's own,
   mid-scroll use of this same section is untouched.

   100vh alone left a white strip along the bottom edge on a real phone
   (confirmed live, reported after the padding/justify-content changes
   above -- unrelated to those, this reproduces independent of them):
   vh is a fixed pixel value based on the LARGEST possible viewport
   (address bar collapsed), but mobile Safari often still has the
   address bar showing right after a fresh page load, and if it later
   auto-collapses without vh being recalculated in step, this section's
   height can end up shorter than the now-larger *current* viewport,
   exposing the plain page background (light gray, confirmed live)
   below it. dvh tracks the real, current visible viewport instead --
   plain declaration order handles the fallback, since engines that
   don't recognize dvh simply skip it and keep the vh value above. Same
   fix as this section's own sticky-positioning rule further down (and
   every other stacking section on both pages), just missing here. */
.demcon-about-page .framer-tuaoj3 {
  min-height: 100vh !important;
  min-height: 100dvh !important;
}

@media (max-width: 767.98px) {
  /* Framer bakes 110px/112px top/bottom padding on this section for this
     breakpoint, and its own justify-content:center vertically centers
     the single "Because another politics is possible..." text block
     within that padded box (this section is flex-direction:column, so
     justify-content -- not align-items, which governs the cross/
     horizontal axis here instead -- is the one controlling vertical
     position; confirmed live after align-items:flex-start alone made
     no visible difference at all) -- together these left a large empty
     gap above the text (confirmed live: ~176px from the section's own
     top edge to where the text starts) before anything appeared,
     reading as too much dead space on a phone. Cutting padding-top
     alone barely moved it (confirmed live: dropping it to 24px only
     closed the gap to ~133px) -- centering means roughly HALF of
     whatever vertical room padding-top gives back gets redistributed
     below the text instead of just closing the gap above it. Switched
     to flex-start instead so padding-top directly and predictably sets
     this gap on its own, with no redistribution -- the leftover space
     that used to split evenly above/below now just becomes extra room
     below the text instead (unproblematic: nothing else sits below it
     on this section, and it's the About page's own hero, so there's no
     dwell-timing concern the way there was on the homepage's Journey
     Section).

     100px, not 24px -- with justify-content now flex-start, padding-top
     IS the gap directly (no more centering redistribution to lean on),
     so it needs to actually clear the fixed nav (84px tall) itself: 24px
     left the first line of text rendering straight underneath/behind
     the nav (confirmed live). 100px comfortably clears it with a little
     room to spare.

     A top:84px sticky-offset override (tried, then reverted) seemed like
     the more "correct" fix at first -- settle the section's own box below
     the nav, not just its text -- but position:sticky's `top` only
     affects where the element gets visually clamped once scrolled; it
     does NOT add to the element's own contribution to document flow.
     That mismatch (box rendered 84px lower than where the flow model
     thinks it sits) meant the NEXT section in the stack (Principles)
     still started its own natural flow position 84px too early --
     confirmed live as the UDHR/yellow section visibly painting over the
     hero's own bottom edge (it has the higher z-index of the two),
     covering the scroll arrow, well before it should have been reachable.
     Plain padding-top has no such side effect -- it's real, in-flow space
     that the element's own height already accounts for, so nothing about
     where the next section starts needs to change to accommodate it. */
  .demcon-about-page .framer-tuaoj3 {
    justify-content: flex-start !important;
    padding-top: 100px !important;
  }
}

/* Same scroll-stacking cards effect as the homepage: each section pins
   to the top of the viewport and holds there while the next one slides
   up to cover it (z-index following page order), driven by the same
   setupStackedSectionOffsets (sticky-nav.js) top-offset technique. Height
   is left auto (not clamped to exactly 100vh) so a section taller than
   one viewport -- Principles easily is, once its list/tabs/UDHR
   background are all accounted for -- scrolls through naturally instead
   of getting clipped by place-content:center the way Agenda originally
   did on the homepage. The marquee (framer-ok10fm) is NOT part of this
   stack -- see further down, it's now a slim sticky sub-nav instead of
   its own full-viewport slide. */
.demcon-about-page .framer-tuaoj3,
.demcon-about-page .demcon-principles-section,
.demcon-about-page .framer-1rbxc9t,
.demcon-about-page .framer-13wosc3 {
  position: sticky;
  top: 0;
  height: auto;
  /* dvh added alongside the plain vh already here -- same white-strip-
     at-the-bottom issue (see the comment on framer-tuaoj3's own
     min-height rule above) applies to all four of these, not just that
     one, since none of them had the dynamic fallback. */
  min-height: 100vh;
  min-height: 100dvh;
  overflow: visible;
  box-shadow: 0 -40px 60px -20px rgba(3, 5, 9, 0.45);
}

.demcon-about-page .framer-tuaoj3 { z-index: 1; }
.demcon-about-page .demcon-principles-section { z-index: 2; }
.demcon-about-page .framer-1rbxc9t { z-index: 3; }
.demcon-about-page .framer-13wosc3 { z-index: 4; }

/* Join Us's own "Dots" decorative background (Framer-baked, data-framer-
   name="Dots") sits at top:-63px relative to its section -- fine on the
   original homepage where the section clips its own overflow, but this
   section now has overflow:visible (required above, for the box-shadow
   that sells the stacked-card effect), which let that 63px of excess
   quietly poke out above the section's own top edge. Since this section
   has a higher z-index than Bruxelles Calling (the card behind/above it
   in the stack), that sliver of dots pattern was painting straight over
   Bruxelles Calling instead of stopping at the boundary between them.
   Resetting top to 0 removes the excess at the source rather than
   clipping it, so it doesn't just relocate the same overflow to the
   section's bottom edge instead. */
.demcon-about-page .framer-13wosc3 .framer-1tdr2s1 {
  top: 0 !important;
}

/* The old repeating "DEMCON 2027 / FOR THE GLOBAL LEFT / ..." marquee
   text is now 3 clickable section links (Principles, Bruxelles Calling,
   Join Us) instead -- same pink band, same scrolling-marquee motion
   (setupMarquee still duplicates the content for a seamless loop), but
   it acts as a mini sub-nav for the 3 stacked cards below it instead of
   being its own full-viewport slide.
   Plain position:sticky doesn't work here: sitting between Hero and
   Principles in document flow, a sticky version would already be pinned
   (and visible) the moment Hero scrolls away -- before "What we stand
   for" has even started -- and nesting it *inside* Principles instead so
   its natural position lined up with that section's own top would trap
   it in Principles' stacking context, so Bruxelles/Join Us (higher
   z-index as *siblings* of Principles) would cover it once they slid up,
   regardless of any z-index set on it internally. Instead
   setupAboutSubNav (sticky-nav.js) toggles this class via plain
   position:fixed once scroll actually reaches the top of the Principles
   section, and removes it again if scrolled back above that -- fixed
   positioning always paints above normal in-flow content regardless of
   any ancestor's stacking context, so it stays visibly on top through
   Bruxelles and Join Us both. Hidden (display:none) the rest of the
   time, so it doesn't occupy space or show up early. */
.demcon-about-page .framer-ok10fm.demcon-marquee-section {
  display: none;
  /* setupMarquee() rebuilds this section's contents itself (see
     sticky-nav.js) -- reset whatever box model Framer baked onto the
     section element so none of it can reintroduce a padding/width gap
     around the full-bleed viewport div inside. */
  padding: 0 !important;
  margin: 0 !important;
  background: transparent !important;
}

.demcon-about-page .framer-ok10fm.demcon-marquee-section.demcon-subnav-pinned {
  display: block;
  position: fixed;
  left: 0;
  right: 0;
  z-index: 500;
}

@media (max-width: 767.98px) {
  /* Removed outright on mobile rather than kept-but-shrunk (an earlier
     version of this just reduced its padding/font-size) -- the label +
     "What we stand for" heading underneath it scrolls out of view for
     good once this section settles into its pinned position (this
     section's own content is much taller than one viewport; see
     .demcon-principles-intro's history in this file for the full
     investigation into why that can't be fixed with simple spacing
     tricks short of either an ugly gap or turning this marquee into a
     second sticky header) -- given that, keeping this bar around just
     to link to a heading that's already gone wasn't earning its keep on
     a phone. setupAboutSubNav (sticky-nav.js) still toggles
     .demcon-subnav-pinned the same as before; this only overrides the
     rule above that makes the pinned state visible, same specificity
     but later in source order so it wins the tie. Desktop keeps the
     marquee/sub-nav exactly as it was. */
  .demcon-about-page .framer-ok10fm.demcon-marquee-section.demcon-subnav-pinned {
    display: none;
  }

  /* Removing the marquee above turned out to have a side effect specific
     to THIS section (Bruxelles Calling): confirmed live that its own
     native "BRUXELLES CALLING //" label and "THREE DAYS. ONE CITY."
     heading are baked by Framer itself with display:none at this exact
     breakpoint (the .hidden-pe22md class Framer generates for its own
     responsive variant system -- pe22md is this site's hash for
     max-width:767.98px), with no alternate mobile-specific variant
     provided in its place -- confirmed live, the only other sibling
     inside this same wrapper is a decorative rotated icon graphic, not
     replacement text. Unlike Principles' label/heading (hand-built, own
     custom classes, always visible, just eventually scrolls out of view
     -- a separate, already-investigated issue), this section apparently
     relied on the marquee/sub-nav to be the only thing ever announcing
     its name on a phone. Restoring these Framer-hidden variants directly
     (matching desktop's own display:contents for this same element,
     confirmed live) rather than leaving the section headerless now that
     the marquee's gone. Scoped narrowly to .framer-18jxntj specifically
     (the shared wrapper around just these two variants) rather than the
     bare .hidden-pe22md class, which Framer reuses site-wide for
     unrelated legitimately-mobile-hidden content -- overriding that
     directly would have unhidden things far outside this section. */
  .demcon-about-page .framer-1rbxc9t .framer-18jxntj .hidden-pe22md {
    display: contents !important;
  }
}

.demcon-subnav-link {
  color: inherit;
  text-decoration: none;
  cursor: pointer;
}

.demcon-subnav-link.is-active {
  font-weight: 700;
  text-decoration: underline;
}

/* Pausing on hover matters more here than a purely decorative marquee
   would -- these are real links now, and text still sliding out from
   under the cursor mid-click is a bad time. */
.demcon-marquee-section:hover .demcon-marquee-track {
  animation-play-state: paused;
}

/* Homepage stacking-cards scroll: each main section pins to the top of
   the viewport and holds there while the next one scrolls up over it, so
   sections stack rather than scroll past each other. Increasing z-index
   in document order makes each later section cover the ones before it --
   DemocracySalons now covers the Hero itself too (the Hero keeps its own
   separate scroll-locked intro via setupHeroIntro's wheel/touch lock, it
   just no longer sits permanently above the stack once that intro is
   done). Clamped to exactly one viewport with overflow:hidden (no
   scrollbar, nothing native to scroll) -- a card whose own content is
   taller than that gets revealed by setupTallCardReveal (sticky-nav.js)
   instead, which translateY's just that inner content wrapper as the
   visitor keeps wheeling/touching, so it still reads as one continuous
   scroll gesture rather than a nested scrollable region, and only lets
   the real page scroll (and the next card's cover) proceed once the
   current card's content has fully scrolled by. The box-shadow sits on
   each card's top edge, so as it slides up it visibly casts a shadow
   onto whatever it's covering underneath. */
.demcon-home-page [data-framer-name="Hero Section"] {
  position: relative;
  z-index: 0;
}

.demcon-home-page [data-framer-name="DemocracySalons"],
.demcon-home-page [data-framer-name="Journey Section"],
.demcon-home-page [data-framer-name="Agenda"],
.demcon-home-page [data-framer-name="Speakers - Section"],
.demcon-home-page [data-framer-name="Participation"],
.demcon-home-page [data-framer-name="JOIN US"] {
  position: sticky;
  top: 0;
  height: 100vh;
  /* dvh tracks the *actual visible* viewport, shrinking/growing live as a
     mobile browser's address bar collapses or expands on scroll -- vh
     alone stays fixed at whichever height happened to be current on page
     load. Plain declaration order handles the fallback: engines that
     don't recognize dvh simply skip it and keep the vh value above;
     engines that do let this one win since it comes second. */
  height: 100dvh;
  overflow: hidden;
  box-shadow: 0 -40px 60px -20px rgba(3, 5, 9, 0.45);
  /* Forces its own isolated compositing layer -- without this, the
     frosted-glass card's backdrop-filter:blur can sample straight through
     into whatever's rendered behind it in a sibling sticky section
     (Agenda's dotted background) during the moment one card is sliding
     up to cover another, instead of respecting the z-index stacking that
     should make the covering card fully opaque on top. */
  isolation: isolate;
  /* isolation:isolate fixes the stacking-context *logic* (which layer is
     allowed to show through which), but on a real phone that's separate
     from whether the browser's compositor actually keeps each of these
     sections pre-painted on its own stable GPU layer moment to moment --
     reported live as the Journey/Agenda handoff occasionally "bleeding"
     into each other, intermittently and worse on a fast flick-scroll
     (never reproduced on a slow/controlled scroll), which is the
     signature of a compositing/paint timing issue rather than a CSS
     correctness one. will-change hints the browser to promote each
     section to its own layer ahead of time instead of deciding
     mid-scroll, which is the standard mitigation for exactly this
     "correct at rest, glitches under fast scroll" symptom. */
  will-change: transform;
}

.demcon-home-page [data-framer-name="DemocracySalons"] { z-index: 1; }

/* Mobile only: DemocracySalons' content (hosts row, Propose a Salon
   button) comfortably fits one viewport on desktop, so it never needed
   the auto-height treatment the other tall cards below get -- but at
   phone widths the same content wraps across far more lines and no
   longer fits, and the shared height:100vh/dvh + overflow:hidden rule
   above was silently clipping the overflow off the bottom of the card
   with no way to scroll to it (confirmed: scrollHeight exceeded
   clientHeight at 375px wide). Scoped to mobile only, and following the
   exact same auto-height/setupStackedSectionOffsets pattern as Agenda
   just below, so desktop's already-correct layout is untouched.
   overflow stays hidden, unlike Agenda/Participation below -- height:auto
   already grows the box to fully contain Salons' own real content (its
   own "Container" child rect fits comfortably inside with room to spare,
   confirmed live), so nothing here actually needs overflow:visible to
   avoid clipping. Framer bakes several full-bleed decorative children
   sized on the assumption their overhang gets clipped by the section's
   own overflow (the same pattern fixed for Agenda's dotted background
   and the about page's "Dots" layer, elsewhere in this file) -- visible
   let whichever of those live inside Salons bleed out past the card's
   own edges instead, reading as a stray white/cream rectangle floating
   free of it. Kept hidden here closes that off at the source. */
@media (max-width: 767.98px) {
  .demcon-home-page [data-framer-name="DemocracySalons"] {
    height: auto;
    min-height: 100vh;
    min-height: 100dvh;
    overflow: hidden;
  }
}
.demcon-home-page [data-framer-name="Journey Section"] { z-index: 2; }
.demcon-home-page [data-framer-name="Agenda"] { z-index: 3; }
/* Agenda's own baked layout centers its content (place-content:center)
   within whatever height it's given -- clamped to exactly 100vh with
   overflow:hidden, that meant its actual content (eyebrow + heading +
   intro + all 7 rows), being taller than one viewport, got clipped
   symmetrically top AND bottom: the eyebrow ("THE ARCHITECTURE // 02")
   above the visible window, presumably the last row or so below it. The
   old setupTallCardReveal-based pan (sticky-nav.js) only ever revealed
   more content by scrolling *down* from that already-clipped starting
   point, so the eyebrow was never reachable. Same fix as the others: let
   it grow to its natural height and hand the scroll-through to
   setupStackedSectionOffsets instead (its entry in setupTallCardReveal's
   config has been removed). */
.demcon-home-page [data-framer-name="Agenda"] {
  height: auto;
  min-height: 100vh;
  min-height: 100dvh;
  overflow: visible;
}

/* The dotted background layer (.framer-ple7iu) is Framer's own
   "full-bleed decoration" pattern -- baked with oversized width/height
   (up to 208%) and negative top/left offsets so it overhangs Agenda's own
   box on the bottom and sides. That overhang was invisible back when
   Agenda clipped with overflow:hidden, but now that the section needs
   overflow:visible (see above, for the eyebrow fix), the overhang is
   free to bleed into whatever's adjacent instead of staying contained to
   Agenda's own card. Pin it flush to Agenda's actual box on all four
   sides instead, so it can't extend anywhere the section itself doesn't. */
.demcon-home-page [data-framer-name="Agenda"] .framer-ple7iu {
  top: 0 !important;
  left: 0 !important;
  right: 0 !important;
  bottom: 0 !important;
  width: 100% !important;
  height: 100% !important;
}
.demcon-home-page [data-framer-name="Speakers - Section"] { z-index: 4; }
/* Participation ("PARTICIPATE // 04") was never added to this stacking
   system at all -- it's ordinary, non-sticky content sitting between
   Agenda (sticky, z-index:3) and JOIN US (sticky, z-index:5), so as the
   page scrolled, Agenda just stayed permanently stuck/opaque on top of it
   the entire time Participation scrolled by underneath, and JOIN US's
   cover began before Participation was ever revealed -- effectively
   invisible the whole time despite rendering correctly in the DOM. Giving
   it its own slot (z-index:4, between the two) fixes that. Its own
   height is left natural (auto/visible, not clamped to 100vh) since its
   3-card grid is slightly taller than one viewport and would otherwise
   get clipped -- setupStackedSectionOffsets (sticky-nav.js) handles the
   scroll-through the same way it does for Journey Section. */
.demcon-home-page [data-framer-name="Participation"] {
  z-index: 4;
  height: auto;
  min-height: 100vh;
  min-height: 100dvh;
  overflow: visible;
}
.demcon-home-page [data-framer-name="JOIN US"] { z-index: 5; }
/* JOIN US is the last stacked card, and the footer is now its last child
   (moved in the HTML) instead of separate ordinary content the visitor
   only reached once this sticky section released -- so the footer needs
   to be part of the same scroll-through as the card, not clipped by the
   100vh+overflow:hidden the other clamped cards use. */
.demcon-home-page [data-framer-name="JOIN US"] {
  height: auto;
  min-height: 100vh;
  min-height: 100dvh;
  overflow: visible;
}

/* "The Movement" (Journey Section) rebuild: unlike the other stacking
   cards, this one isn't clamped to one viewport at all -- its height is
   left to grow to whatever the video's own natural aspect ratio renders
   at (full width, auto height, never cropped), and native position:sticky
   handles the rest: since the section is taller than the viewport and
   nothing clips it, it stays pinned at top:0 while the page keeps
   scrolling past its own extra height, only letting the next card's
   cover begin once the section's full height -- video included -- has
   actually scrolled by. No JS panning/reveal needed for this one now
   (setupTallCardReveal no longer has an entry for it). The video (moved
   in the HTML to be a direct child of the section, ahead of Container) is
   a normal in-flow element that drives the section's height; the
   translucent text card is pulled out of flow instead (absolute) so it
   can overlay the lower portion of the image without adding to that
   height. */
.demcon-home-page [data-framer-name="Journey Section"] {
  position: sticky;
  height: auto;
  /* The video is width:100%, height:auto at a fixed aspect ratio -- its
     rendered height tracks the viewport's WIDTH only, never its height.
     On a wide-but-short window the video comfortably exceeds one
     viewport, but on a fullscreen/tall window (more height, similar
     width) it can end up SHORTER than the viewport -- confirmed via two
     side-by-side screenshots, narrow window fine, fullscreen bleeding.
     That gap at the bottom of the section exposed Agenda's dotted
     background underneath while "stuck", not a compositing artifact.
     min-height is a floor against that, independent of the video. */
  min-height: 100vh;
  min-height: 100dvh;
  overflow: visible;
  display: block;
  /* min-height above stretches the SECTION's own box, but the video
     inside only ever sizes to its own aspect ratio (height:auto) -- it
     never stretches to match. So whenever min-height actually kicks in
     (video shorter than viewport), there's a literal transparent gap
     between the video's bottom edge and the section's bottom edge. The
     section itself has no background of its own (its only visual
     content is the full-bleed video), so that gap let whatever's
     stacked behind it on screen (Agenda, higher z-index) show straight
     through -- the real remaining cause of the dots "bleeding in" after
     the first min-height fix. A solid fallback color closes it without
     stretching/distorting the video itself. */
  background-color: #030509;
  /* Framer's own baked padding (118px top, 48px sides) on this section
     used to be harmless when the video was pulled out via
     position:absolute (which ignores the parent's padding box), but now
     that it's a normal in-flow child, that padding was leaving a visible
     cream margin around three sides of it instead of true full-bleed. */
  padding: 0 !important;
}

.demcon-home-page [data-framer-name="Journey Section"] > .framer-17um2q1 {
  position: static;
  width: 100%;
  height: auto;
  overflow: visible;
  z-index: 0;
  margin: 0;
  padding: 0;
}

.demcon-home-page [data-framer-name="Journey Section"] > .framer-17um2q1 video {
  display: block;
  width: 100%;
  height: auto !important;
  object-fit: initial !important;
}

/* Text card overlays the lower portion of the image, roughly where the
   banner photo's subject (a dog on a leash) is in frame -- absolute +
   centered instead of flex-positioned, since it's no longer a flex
   sibling of the video (which is why the Section above needs its own
   position:relative removed in favor of this being the Section's own
   sticky box acting as containing block). */
.demcon-home-page [data-framer-name="Journey Section"] > .framer-lnr63p {
  position: absolute;
  left: 50%;
  bottom: 110px;
  transform: translateX(-50%);
  z-index: 1;
  width: calc(100% - 80px);
  max-width: 760px;
  background: rgba(250, 249, 245, 0.88);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  border-radius: 12px;
  padding: 56px 56px 80px;
}

.demcon-home-page [data-framer-name="Journey Section"] .framer-1ibrk5w {
  align-items: center;
  place-content: center;
  text-align: center;
  gap: 32px !important;
}

/* .framer-wcxrxy (the heading's real flex-item wrapper -- .ssr-variant in
   between is display:contents and passes through), .framer-hvsa7k
   (heading), and .framer-fobxvj (paragraph) all use Framer's
   "flex:1 0 0; width:1px" row-fill trick -- grow from a 1px basis to
   fill available width, which only works with a horizontal main axis.
   Highlight is now flex-direction:column above, so nothing is growing
   that "width:1px" anymore and it renders literally: framer-wcxrxy
   collapses to 1px, and framer-hvsa7k's own "width:100%" (still needed,
   since it's a real 1px width otherwise) is 100% of that collapsed 1px
   parent. Override every level back to filling the column's width. */
.demcon-home-page [data-framer-name="Journey Section"] .framer-wcxrxy,
.demcon-home-page [data-framer-name="Journey Section"] .framer-hvsa7k,
.demcon-home-page [data-framer-name="Journey Section"] .framer-fobxvj {
  width: 100% !important;
  max-width: 100% !important;
  flex: none !important;
}

.demcon-home-page [data-framer-name="Journey Section"] .framer-1m75x1d {
  flex-direction: column;
  align-items: center;
  text-align: center;
}

/* Highlight's real direct children are just .framer-e571rj (arrow) and
   .framer-wcxrxy (which originally held BOTH the heading and the
   paragraph side by side as its own nested row, via space-between --
   that's what put the paragraph off the right edge of the card, since a
   648px-wide paragraph doesn't fit in whatever sliver of that row the
   heading left behind). The paragraph (.framer-fobxvj) has been moved in
   the HTML to be its own direct sibling within Highlight instead, so all
   three -- heading wrapper, arrow, paragraph -- can be ordered
   independently in Highlight's column: heading, then arrow directly
   under it, then paragraph. */
.demcon-home-page [data-framer-name="Journey Section"] .framer-1m75x1d > .framer-wcxrxy {
  order: 1;
  flex-direction: row;
  align-items: center;
  justify-content: center;
}

.demcon-home-page [data-framer-name="Journey Section"] .framer-1m75x1d > .framer-e571rj {
  order: 2;
  margin-top: -36px;
}

.demcon-home-page [data-framer-name="Journey Section"] .framer-1m75x1d > .framer-fobxvj {
  order: 3;
  margin-top: -73px;
}

/* The arrow's own baked SVG is a tall, vertical squiggle-and-arrow inside
   a wrapper sized to match (aspect-ratio:.228, ~39x277px) -- rotating
   just the <img> visually lays it on its side but leaves the WRAPPER's
   layout box tall and narrow, so the rotated image overflows sideways
   into the heading above it instead of getting its own row. Give the
   wrapper a swapped, and larger, horizontal box instead so nothing
   overlaps, then rotate + mirror the image within it and pulse it.
   Opacity/transform (both the wrapper's slide-in and Framer's own baked
   inline "opacity:0, revealed on scroll by setupScrollReveal") are owned
   by setupMovementReveal + the .demcon-movement-card rules below instead
   of forced here -- that generic reveal never actually fired for this
   element (confirmed: its rect legitimately satisfies the reveal check
   once the card is in view, but nothing ever flips it, and nothing then
   re-hides it either -- it just never gets the one-time trigger), so
   setupMovementReveal clears the baked inline styles and this stylesheet
   takes over instead of fighting that trigger. */
.demcon-home-page [data-framer-name="Journey Section"] .framer-e571rj {
  width: 460px !important;
  height: 125px !important;
  aspect-ratio: auto !important;
  overflow: visible;
  position: relative;
}

/* Framer's own image rule (.framer-jmwsl6) ships with position:absolute
   plus large negative top/right offsets tuned for a totally different
   layout -- left unchecked, the image escapes this wrapper entirely and
   renders up near the page's top edge instead of under the heading where
   the wrapper actually sits. Pinned back to absolute-but-centered instead
   of static flow, because the -90deg rotation below swaps this element's
   effective on-screen width/height: the box is sized narrow-and-tall
   (165x620) so that once rotated it PAINTS as wide-and-short (620x165),
   matching the wrapper's own footprint above -- sizing it 620x165 before
   rotation (matching the wrapper 1:1) was the earlier bug, since that
   paints as a tall 165-wide sliver post-rotation instead of a wide one. */
.demcon-home-page [data-framer-name="Journey Section"] .framer-e571rj img {
  position: absolute !important;
  top: 50% !important;
  left: 50% !important;
  right: auto !important;
  bottom: auto !important;
  width: 125px !important;
  height: 460px !important;
  margin: 0;
  transform-origin: center;
  transform: translate(-50%, -50%) rotate(-90deg) scaleX(-1);
}

.demcon-home-page [data-framer-name="Journey Section"] > .framer-lnr63p.demcon-movement-revealed .framer-e571rj img {
  animation: demcon-arrow-pulse 1.8s ease-in-out infinite;
}

@keyframes demcon-arrow-pulse {
  0%, 100% {
    transform: translate(-50%, -50%) rotate(-90deg) scaleX(-1) scale(1);
    opacity: 1;
  }
  50% {
    transform: translate(-50%, -50%) rotate(-90deg) scaleX(-1) scale(1.15);
    opacity: 0.55;
  }
}

/* Staged reveal once the card scrolls into view (setupMovementReveal):
   the card fades in first, then the heading slides in from the left
   together with the arrow directly beneath it, then the paragraph (and
   its "read more" link) slides up -- each group's own transition-delay
   creates the sequence off the single .demcon-movement-revealed class
   toggle, rather than separate JS timers. setupMovementReveal clears
   Framer's baked inline opacity/transform on these elements first, so
   these rules have sole control -- no !important needed here. */
.demcon-home-page [data-framer-name="Journey Section"] > .framer-lnr63p.demcon-movement-card {
  opacity: 0;
  transition: opacity 0.8s ease 0.2s;
}

.demcon-home-page [data-framer-name="Journey Section"] > .framer-lnr63p.demcon-movement-revealed {
  opacity: 1;
}

.demcon-home-page [data-framer-name="Journey Section"] .framer-1m75x1d > .framer-wcxrxy,
.demcon-home-page [data-framer-name="Journey Section"] .framer-1m75x1d > .framer-e571rj {
  opacity: 0;
  transform: translateX(-60px);
  transition: opacity 0.6s ease 0.55s, transform 0.6s ease 0.55s;
}

.demcon-home-page [data-framer-name="Journey Section"] > .framer-lnr63p.demcon-movement-revealed .framer-1m75x1d > .framer-wcxrxy,
.demcon-home-page [data-framer-name="Journey Section"] > .framer-lnr63p.demcon-movement-revealed .framer-1m75x1d > .framer-e571rj {
  opacity: 1;
  transform: translateX(0);
}

.demcon-home-page [data-framer-name="Journey Section"] .framer-1m75x1d > .framer-fobxvj {
  opacity: 0;
  transform: translateY(40px);
  transition: opacity 0.6s ease 0.95s, transform 0.6s ease 0.95s;
}

.demcon-home-page [data-framer-name="Journey Section"] > .framer-lnr63p.demcon-movement-revealed .framer-1m75x1d > .framer-fobxvj {
  opacity: 1;
  transform: translateY(0);
}

/* setupMovementReveal (sticky-nav.js) inserts this poster <img> as a
   fallback for the section's broken video, unconditionally on every
   screen size, but its own sizing/cropping rules all live inside the
   mobile media query below -- so on desktop it was rendering completely
   unstyled, at its native 3707x2780px intrinsic size (confirmed live:
   massively overflowing the layout). Desktop already has the (equally
   broken, but out-of-scope) video in that same spot, so the fix is just
   to hide this fallback there rather than give it its own desktop
   sizing -- the mobile media query's own display:block below overrides
   this back on for the breakpoint that actually needs it. */
.demcon-movement-poster {
  display: none;
}

@media (max-width: 767.98px) {
  /* THE actual source of "Read More" rendering far below the visible
     card, seemingly under the footer -- confirmed live (getBoundingClientRect
     on the paragraph's own <p> vs its .framer-fobxvj wrapper): Framer bakes
     a fixed height:182px on this wrapper for this breakpoint, sized for
     however many lines the paragraph took on whatever viewport that
     baking assumed. At this card's actual mobile width the same text
     wraps across far more lines (459px tall, confirmed), overflowing the
     fixed-height wrapper -- the text itself still renders fine
     (overflow:visible, not clipped), but "Read More" (this wrapper's
     *other* flex child, right after the paragraph) lays out based on the
     paragraph's true 459px, past the wrapper's own (wrong, too-short)
     182px box -- which is also the card's own only source of height, so
     the whole card ends far shorter than its actual content, and
     "Read More" reads as escaped into whatever comes after it on the
     page. auto lets it size to its real content instead. */
  .demcon-home-page [data-framer-name="Journey Section"] .framer-fobxvj {
    height: auto !important;
  }

  /* Same fixed-height bug as .framer-fobxvj above, but on the heading's
     own wrapper: .framer-wcxrxy's single child is .ssr-variant
     (display:contents, passes through invisibly), whose own single real
     child -- .framer-hvsa7k -- is the one actually carrying a baked
     height:206px (confirmed via grep: three responsive height rules ship
     for this class, tuned for wider breakpoints). On a phone the 2-line
     heading only needs ~56px, so the extra ~150px rendered as a large
     dead gap between the heading and the paragraph below it. */
  .demcon-home-page [data-framer-name="Journey Section"] .framer-hvsa7k {
    height: auto !important;
  }

  /* Framer also bakes this same text a permanent inline opacity:0 (a
     scroll-reveal animation state that nothing here ever triggers --
     setupMovementReveal clears the equivalent baked state on the
     heading/arrow/paragraph but never targeted this element), so the
     "THE MOVEMENT // 01" label rendered invisible-but-space-reserved on
     every viewport. Confirmed this predates all of today's mobile work
     (reproduces on desktop too), so left as an out-of-scope pre-existing
     bug there, same as the broken video below -- overridden with
     !important here only, scoped to mobile. */
  .demcon-home-page [data-framer-name="Journey Section"] .framer-7orsv7 {
    opacity: 1 !important;
    transform: none !important;
  }

  /* Highlight's gap (heading/label/paragraph stack) trimmed down to match
     the smaller card below -- 32px read as excessive once the card's own
     padding and font sizes were reduced to fit one phone viewport. */
  .demcon-home-page [data-framer-name="Journey Section"] .framer-1ibrk5w {
    gap: 14px !important;
  }

  .demcon-home-page [data-framer-name="Journey Section"] .framer-wcxrxy h4 {
    font-size: 26px !important;
  }

  .demcon-home-page [data-framer-name="Journey Section"] .framer-fobxvj p {
    font-size: 15px !important;
  }

  /* Same restructuring as the Contact CTA section (search
     ".demcon-contact-cta-card" for that one) applied here for the same
     reason: this card used to float absolutely over the lower portion of
     the image (translucent, blurred, inset with side margins) -- on this
     375px-wide phone that read as messy for the same underlying cause,
     a busy photo smeared by backdrop-blur behind text instead of a crisp
     image. Restructured into the same two-block pattern -- image as a
     plain, full-strength hero band up top, this card as an ordinary
     opaque, full-width, square-cornered block flowing directly below it
     -- rather than overlapping.

     Switched off position:absolute (the desktop base rule's left:50%/
     bottom:110px/transform:translateX(-50%) all go with it, hence
     resetting each individually below) back to a normal flow child --
     .framer-17um2q1 (the image) is a real sibling element here, not a
     background layer like Contact's ::before, so once the card is back
     in normal flow it already lands directly after the image with no
     extra margin-top needed to clear it, unlike Contact's card.

     Also drops the "move the card up to cover the dog" positioning from
     the previous version of this rule -- that only mattered when the
     card overlapped the image; now that it sits below instead, the full
     photo (dog included) shows in the hero band same as the rest of it. */
  .demcon-home-page [data-framer-name="Journey Section"] > .framer-lnr63p {
    position: static;
    left: auto;
    top: auto;
    bottom: auto;
    transform: none;
    width: 100%;
    max-width: none;
    background: #faf9f5;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    border-radius: 0;
    padding: 32px 20px;
    /* Putting the card in normal flow (above) means its own height now
       adds to the section's total, but Agenda (the next stacking
       section, higher z-index) starts covering things the instant its
       own natural document position reaches the visible viewport --
       which, confirmed live on a real phone, was happening while the
       card's paragraph was barely a line in, cutting off "Read More"
       and most of the copy almost immediately after the section
       settled. The section's own extra height beyond one viewport is
       what delays that (same mechanism that gives Agenda/Participation/
       etc. their own much longer dwell before whatever follows THEM
       covers anything) -- this card's own natural content was barely
       exceeding one viewport at all, leaving almost no such buffer.
       margin-bottom, not padding, so the extra space is a gap AFTER the
       card rather than inside it -- confirmed live it doesn't reintroduce
       a visible black gap either (it only ever exists below the fold at
       the moment of catching, and gets seamlessly covered by Agenda's
       own incoming opaque background as it climbs, not shown as a bare
       gap first). */
    margin-bottom: 420px;
  }

  /* The arrow graphic is baked at a fixed 460x125px (pre-rotation) --
     wider than this entire card on a phone (~295px), so it's hidden
     there already (confirmed: display:none, a Framer responsive
     default) -- but the paragraph's own -73px margin-top and the arrow
     wrapper's -36px were both tuned to compensate for that arrow's
     *presence* (pulling the heading/paragraph up to overlap the space it
     would otherwise leave). With the arrow already gone here, those
     same pulls just overlapped the heading with the paragraph instead.
     Zeroed out now that height:auto above means the wrapper genuinely
     sizes to its content and doesn't need this compensation. */
  .demcon-home-page [data-framer-name="Journey Section"] .framer-1m75x1d > .framer-e571rj {
    margin-top: 0 !important;
  }

  /* This flex column's own baked inline gap (48px, tuned for the desktop
     layout with the arrow graphic taking up a row between heading and
     paragraph) still applied here even with the arrow hidden, reading as
     a big dead gap between "Another world is on her way..." and the
     paragraph below it -- confirmed live via getBoundingClientRect on
     both (48px gap even with this margin-top zeroed), not something
     margin-top alone could close. Overridden down to 8px so the
     paragraph sits right up against the heading. */
  .demcon-home-page [data-framer-name="Journey Section"] .framer-1m75x1d {
    gap: 8px !important;
  }

  .demcon-home-page [data-framer-name="Journey Section"] .framer-1m75x1d > .framer-fobxvj {
    margin-top: 0 !important;
  }

  /* The background video's actual source file is broken (confirmed live:
     a DEMUXER_ERROR_COULD_NOT_OPEN decode failure -- the file at that
     path is literally an HTML document, not a video at all), so it never
     plays and instead shows the browser's own "video failed" paused-icon
     overlay on a black frame. The <video> tag's own poster image is a
     real, valid JPEG (confirmed), so a plain <img> using that same file
     (inserted by setupMovementReveal, sticky-nav.js) replaces it here --
     sized/cropped by this same media query's rules below and wiped in
     via clip-path (updatePosterWipe, sticky-nav.js). Explicitly hidden
     on desktop instead (base .demcon-movement-poster rule, above this
     media query) -- desktop keeps the (equally broken, but unreported/
     out of scope here) video untouched -- not part of what was asked. */
  .demcon-home-page [data-framer-name="Journey Section"] > .framer-17um2q1 video {
    display: none !important;
  }

  /* This wrapper (Framer's own "Gallery" flex container) was being
     vertically CENTERED within the section's full 812px height by
     Framer's own baked flex-centering (still wins here despite this
     stylesheet's own display:block on the section itself -- confirmed
     empirically, not fully explained by the cascade). Centering left
     ~265px of solid black dead space above AND below the image band
     (whose own height is fixed by its aspect ratio), and -- worse --
     put the banner photo's own protest-banner text entirely behind the
     translucent card below, which starts well above where the centered
     image began. Flipped to top-alignment instead (kept IN-FLOW,
     deliberately not pulled out via position:absolute -- an earlier
     version of this fix did that and, even though every check here came
     back clean, it's needless structural change for what's really just
     a vertical-alignment fix, and this stacking-cards page leans on
     several scripts reading each section's own natural offsetHeight
     elsewhere, so least-footprint wins). */
  .demcon-home-page [data-framer-name="Journey Section"] {
    display: flex !important;
    align-items: flex-start !important;
    justify-content: flex-start !important;
    /* Framer bakes a 10px flex gap here, leaving a visible sliver of the
       section's own black background between the hero image and the
       card below it (confirmed live) instead of the two sitting flush
       -- zeroed to match the Contact CTA section's own flush hero-to-
       card transition. */
    gap: 0 !important;
    /* setupStackedSectionOffsets (sticky-nav.js) computes this section's
       own sticky `top` inline as min(0, vh - height) -- 0 on a phone
       (this section's own content fits close to one viewport), meaning
       it settles flush against the very top of the viewport, directly
       under the fixed nav (84px tall). Overridden to 84px here instead
       -- external !important beats that plain (non-important) inline
       style -- so the section settles with its own top edge exactly at
       the nav's bottom edge instead. Paired with removing the wrapper's
       old margin-top:90px compensation below: previously the image was
       pushed down by a flat margin THE ENTIRE TIME the section was even
       partially on screen (including while still scrolling into place,
       reading as a big dead black gap above the image -- confirmed
       live); now the section itself simply settles 84px lower, so the
       image (flush at the wrapper's own top, no margin) lands exactly
       below the nav only once actually caught, with no gap needed
       before or after that point.

       getEntranceProgress (setupMovementReveal, sticky-nav.js) is aware
       of this same 84px on mobile -- it divides by (vh - stuckTop)
       instead of a flat vh, so "fully caught" still correctly reads as
       progress 1 instead of asymptoting just short of it. */
    top: 84px !important;
  }

  /* flex-start alone lands this flush at y:0, clear of any Framer-baked
     centering (see comment above), but that's still section-LOCAL y:0 --
     with the section's own sticky top offset also 0 (its natural, JS-
     computed value -- see setupStackedSectionOffsets), that meant the
     fixed nav (84px tall) covered the image's own top ~84px once the
     section actually caught/settled, eating into the banner text's own
     visible range (which starts at just 7.5% of the image's height).
     An earlier version of this fix compensated with a flat 90px
     margin-top on this wrapper -- correct once settled, but that same
     90px shows up as a solid, empty black gap for the ENTIRE time the
     section is still scrolling into place beforehand too (confirmed
     live), since the margin is baked into the wrapper regardless of
     scroll position, not something that only matters once stuck.

     Removed in favor of moving the compensation onto the SECTION's own
     sticky offset instead (see the mobile-only `top: 84px` override on
     the section itself, a few rules up) -- the section now settles with
     its own top edge exactly at the nav's bottom edge instead of at the
     very top of the viewport, so the image (now flush at this wrapper's
     own y:0, no margin) lands exactly below the nav once caught, with
     no dead space needed at any point along the way. */
  .demcon-home-page [data-framer-name="Journey Section"] > .framer-17um2q1 {
    height: 300px !important;
  }

  /* Enlarged from its natural ~281px (100%-width, aspect-fit) height to
     300px and cropped via object-fit:cover -- object-position pinned to
     the right so the ~9% trimmed off comes entirely from the LEFT edge
     of the frame (blank banner fabric there, confirmed via pixel
     analysis: the banner's actual text spans only 19%-83% of the
     image's width), leaving the readable "CAMPAIGN AGAINST RACISM &
     FASCISM" text fully intact with margin to spare on both sides. Text
     also confirmed (same pixel analysis, scanning for bright/white
     rows) to span roughly the TOP 8%-42% of the image's height -- at
     90px top + 300px height that lands the text between y=90 and
     y~216, comfortably within the hero band, well clear of the
     (now full-width, non-overlapping) card below it. */
  /* clip-path starts fully closed (bottom inset 100% = nothing shown) --
     updatePosterWipe (sticky-nav.js) scrubs this bottom inset down to 0%
     in step with scroll, wiping the image in top-to-bottom rather than
     fading it in as a whole. No transition here deliberately: the wipe
     is meant to track scroll position directly, not lag behind it. */
  .demcon-movement-poster {
    display: block;
    width: 100%;
    height: 300px;
    object-fit: cover;
    object-position: 100% center;
    clip-path: inset(0 0 100% 0);
  }

  /* The card/heading/paragraph delays used to be pushed later here (by
     the poster's own 0.5s fade duration) back when setupMovementReveal
     triggered the poster and the card at the exact same instant -- that
     coordination no longer applies now that the poster wipes in
     progressively as the section scrolls into place (updatePosterWipe,
     sticky-nav.js) while the card waits for that wipe to specifically
     clear the banner text (checkCardReveal's 0.42 progress threshold);
     by the time the card's own reveal fires the wipe has already passed
     that point, so nothing here needs to wait on it further -- the base
     (desktop) delays above this media query apply unmodified here too. */
}

/* Homepage's JOIN US section, replaced with a hand-built contact form
   (matching contact/index.html's own field set: Name, Email, Phone,
   Message) over the same background illustration contact/index.html's
   Hero Section uses. Hand-built rather than transplanting Framer's own
   Contact Section markup -- that markup leans on ancestor context (the
   exact "flex:1 0 0; width:1px" row-fill trick that broke the Movement
   heading above) that doesn't carry over cleanly to a new home, and it's
   a lot of animation-wrapper markup for a single form. This section
   keeps data-framer-name="JOIN US" so it inherits the stacking-cards
   position/height/overflow/shadow/z-index rules already set above. */
.demcon-contact-cta-section {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  width: 100%;
  padding: 80px 30px 0;
  /* Base fill behind the ::before image layer, so whatever the
     circle-clip hasn't reached yet during the paint-in wipe shows a
     solid backdrop instead of nothing -- matches the same cream used by
     the other stacking cards (Journey, Agenda, Participation). */
  background-color: #faf9f5;
}

/* Background lives on a ::before instead of the section's own
   background-image, so it can be masked/"painted in" independently of
   the card sitting on top of it (masking the section itself would also
   hide the card). No reference for the original effect exists anywhere
   in the export or the pristine zip, so this is a built interpretation
   of "paints in" -- a soft-edged diagonal sweep (mask-image gradient,
   not a hard clip-path line) reading more like a brush stroke than a
   mechanical wipe. A plain top-to-bottom edge also read as barely
   visible against this particular image, since its top is mostly flat
   yellow sky that matches the site's own yellow brand color used
   elsewhere, so revealing top-first didn't look like much was changing
   until the wipe was most of the way done -- sweeping diagonally
   exposes a mix of sky and ground together instead. Driven directly off
   scroll position via the --demcon-reveal-progress custom property
   (setupContactCtaReveal, sticky-nav.js), not a fixed-duration
   transition -- a timed transition either finishes long before the
   visitor scrolls all the way to the section or gets blown past
   entirely, so it's scrubbed 1:1 with actual scroll instead.
   .demcon-contact-revealed only gets added once that scroll-driven
   reveal reaches 100%, which is what the card's own (separately timed)
   fade-in keys off below. */
.demcon-contact-cta-section::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 0;
  background-image: url('/assets/framer/images/2pNkAt6qf7rO0fSwFrouSKBTE.png');
  background-size: cover;
  background-position: center center;
  background-repeat: no-repeat;
  -webkit-mask-image: linear-gradient(
    115deg,
    #000 0%,
    #000 calc(var(--demcon-reveal-progress, 0%) - 14%),
    transparent calc(var(--demcon-reveal-progress, 0%) + 14%),
    transparent 100%
  );
  mask-image: linear-gradient(
    115deg,
    #000 0%,
    #000 calc(var(--demcon-reveal-progress, 0%) - 14%),
    transparent calc(var(--demcon-reveal-progress, 0%) + 14%),
    transparent 100%
  );
}

/* Footer (moved in the HTML to be the last child of this section, right
   after the card) so it's part of the same scroll-through as the card
   instead of a separate chunk of ordinary content the visitor only
   reaches after this sticky section finally releases -- see the
   height:auto override below, same technique as Journey Section and
   Participation. */
.demcon-contact-cta-section > .framer-1yncser-container {
  width: 100%;
  margin-top: 80px;
}

.demcon-contact-cta-card {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: 620px;
  background: rgba(255, 255, 255, 0.94);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  border-radius: 16px;
  padding: 48px;
  box-shadow: 0 20px 60px rgba(3, 5, 9, 0.35);
  opacity: 0;
  /* .demcon-contact-revealed only gets added once the scroll-driven wipe
     above has actually reached 100% (see setupContactCtaReveal), so this
     half-second delay is purely the "pause after it paints in" -- no
     need to also account for a wipe duration now that it's scroll-scrubbed. */
  transition: opacity 0.7s ease 0.5s;
}

.demcon-contact-cta-section.demcon-contact-revealed .demcon-contact-cta-card {
  opacity: 1;
}

.demcon-contact-cta-label {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 16px;
  letter-spacing: -0.02em;
  text-transform: uppercase;
  color: #ff0074;
  margin: 0 0 12px;
}

.demcon-contact-cta-heading {
  font-family: 'League Spartan', sans-serif;
  font-weight: 700;
  font-size: clamp(28px, 4vw, 36px);
  letter-spacing: -0.02em;
  color: #030509;
  margin: 0 0 16px;
}

.demcon-contact-cta-intro {
  font-family: 'League Spartan', sans-serif;
  font-weight: 300;
  font-size: 16px;
  line-height: 1.5em;
  color: #3a3a3a;
  margin: 0 0 32px;
}

.demcon-contact-cta-form {
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.demcon-contact-cta-field {
  display: flex;
  flex-direction: column;
  gap: 6px;
  font-family: 'League Spartan', sans-serif;
  font-size: 14px;
  font-weight: 600;
  color: #030509;
  flex: 1;
  min-width: 0;
}

.demcon-contact-cta-field input,
.demcon-contact-cta-field textarea {
  padding: 12px 14px;
  border: 1.5px solid rgba(3, 5, 9, 0.25);
  border-radius: 8px;
  font-family: 'Satoshi', 'Satoshi Placeholder', sans-serif;
  font-size: 15px;
  font-weight: 400;
  color: #030509;
}

.demcon-contact-cta-field input:focus,
.demcon-contact-cta-field textarea:focus {
  outline: none;
  border-color: #030509;
}

.demcon-contact-cta-field textarea {
  resize: vertical;
  min-height: 100px;
  font-family: 'Satoshi', 'Satoshi Placeholder', sans-serif;
}

.demcon-contact-cta-row {
  display: flex;
  gap: 16px;
}

.demcon-contact-cta-checkbox-row {
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: 'League Spartan', sans-serif;
  font-size: 14px;
  color: #3a3a3a;
}

.demcon-contact-cta-checkbox-row input {
  width: 18px;
  height: 18px;
  flex: none;
}

.demcon-contact-cta-form button {
  align-self: flex-start;
  margin-top: 8px;
  padding: 14px 28px;
  border: 2px solid #030509;
  border-radius: 999px;
  background: #ff0074;
  color: #fff;
  font-family: 'League Spartan', sans-serif;
  font-size: 15px;
  font-weight: 700;
  text-transform: uppercase;
  cursor: pointer;
  transition: background-color 0.2s ease, transform 0.2s ease;
}

.demcon-contact-cta-form button:hover {
  background: #030509;
  transform: translateY(-2px);
}

@media (max-width: 767.98px) {
  .demcon-contact-cta-row {
    flex-direction: column;
  }

  /* Previously a floating rounded, translucent/blurred card centered over
     the (also previously full-height) background image -- on the narrow
     375x1892 mobile box that combination read as genuinely messy rather
     than the intended "frosted glass over a photo" look: the card's own
     blur smeared the small, already-tightly-cropped hero band behind it
     into a washed-out haze instead of a crisp photo. Restructured as two
     stacked, non-overlapping blocks instead -- a plain hero image band up
     top (this same ::before, now full-strength instead of something to
     blur), then this card as an ordinary opaque, full-width, square-
     cornered block sitting below it, more like a normal page section
     than a floating card. Squared off (no border-radius) and shadow
     removed to match that flush, edge-to-edge look; solid instead of
     translucent since there's no image directly behind it to blend with
     anymore. */
  .demcon-contact-cta-card {
    margin-top: 320px;
    padding: 32px 24px;
    background: #faf9f5;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    border-radius: 0;
    box-shadow: none;
  }

  /* Companion to the card change above: this section used to center its
     (image + floating card) block within its own considerable height
     (1892px tall at a 375-wide phone, confirmed live -- the card's own
     content plus the footer merged into the same scroll-through) via
     justify-content:center. With the card now meant to sit directly
     below a fixed-height hero band instead of floating mid-section,
     centering no longer makes sense -- switched to flex-start so
     everything stacks from the top instead, and the side/top padding
     (previously 80px 30px 0, shared with desktop) dropped to 0 so the
     hero image and the now-full-width card can both reach the true
     screen edges. Top padding is handled via the card's own 320px
     margin-top above instead, matching the hero band's own height (see
     the ::before rule below) so the card starts exactly where the image
     ends rather than needing a second hardcoded number to stay in sync. */
  .demcon-contact-cta-section {
    padding: 0;
    justify-content: flex-start;
  }

  /* Mobile only: the stacking-cards row (see setupStackedSectionOffsets,
     sticky-nav.js) is a flex column driven by a per-section `order` Framer
     bakes into its own mobile CSS (DemocracySalons:1, Journey:3, Agenda:5,
     Speakers:7, Participation:8) -- but this section is hand-built (see
     comment above .demcon-contact-cta-section), so it has no Framer-baked
     order at all and defaults to 0. On desktop every section defaults to
     order:0 alike, so ties just fall back to DOM order, which already
     happens to be correct there -- nothing to fix. On mobile, order:0 put
     this section in flex position right after the Hero, ahead of every
     other card, so its z-index:5 painted over DemocracySalons instead of
     appearing after Participation like it should. */
  .demcon-contact-cta-section {
    order: 9;
  }

  /* Base rule above sizes this ::before with inset:0, covering the
     section's own full height -- fine on desktop (that section is
     roughly as tall as it is wide), but on mobile this section is
     extremely tall relative to its width (its card's content plus the
     footer merged into the same scroll-through, per the comment on
     .framer-1yncser-container above -- confirmed live: 375x1892 at a
     375px-wide phone). background-size:cover against that aspect ratio
     has to scale the 1536x1024 source image up ~1.85x just to satisfy
     the height, cropping away all but a ~13%-wide center sliver of its
     width -- which happens to land on flat yellow sky, hiding both the
     desert scenery and the "Democracy Salons" road-sign entirely
     (confirmed live: that sliver is blank yellow start to finish).
     Constrained to a fixed band pinned to the section's top instead of
     stretching the full height -- sized so cover's height-driven scale
     drops to ~0.31x (vs. ~1.85x before), keeping most of the image's
     width on screen -- and repositioned to frame the sign specifically,
     since this band is exactly the part actually visible before the
     card fades in over it (the reveal sequencing itself -- diagonal
     wipe, then card -- is unchanged, this only fixes what that wipe
     reveals). The rest of the section's own great height falls back to
     the plain cream background-color already set on the section. */
  .demcon-contact-cta-section::before {
    inset: auto;
    top: 0;
    left: 0;
    right: 0;
    height: 320px;
    background-position: 65% 32%;
  }

  /* No footer-specific full-bleed override needed here (there used to be
     one) -- now that .demcon-contact-cta-section's own left/right
     padding is 0 on mobile (see that rule above), the footer's existing
     site-wide mobile fix (search ".framer-wxmzE.framer-f91918" further
     up this file -- sets width:100% !important to recover a mobile
     layout Framer's export never wired up) already resolves to the full
     375px viewport width on its own, same as it does on the About/
     Contact pages. Confirmed live: removing the extra override changed
     nothing, the footer stayed flush. */
}

/* Mobile only: DemocracySalons' description/hosts/divider/propose-a-salon
   block (framer-6yy4j6 and its children) sits noticeably left-aligned
   with a big gap under the logo graphic on mobile -- reducing the gap
   (Left Content's own 72px row-gap, between the logo and this text
   block) and centering the pieces that were still left-aligned within
   their own (already horizontally-centered-as-a-box) containers. Scoped
   to mobile only; desktop's own layout here is untouched and was already
   correct. */
@media (max-width: 767.98px) {
  .framer-sfxkli {
    row-gap: 10px !important;
  }

  .demcon-hosts {
    margin-top: 16px !important;
  }

  .framer-1atslb2 {
    align-items: center !important;
    text-align: center !important;
  }

  .framer-1atslb2 p {
    text-align: center !important;
  }

  .demcon-hosts {
    align-items: center !important;
  }

  .demcon-hosts-label {
    text-align: center !important;
  }

  .demcon-hosts-row {
    justify-content: center !important;
  }

  /* Section Header's own row-gap (24px, Framer-baked) applies uniformly
     to all four of its children's spacing (description-hosts, hosts-
     divider, divider-propose) -- pulling just "Propose a Salon" closer
     to the divider above it needs a negative margin on it specifically,
     not a smaller shared gap (which would also tighten the other two). */
  .framer-1ilqax {
    margin-top: -12px !important;
  }

  /* Disables the hover-triggered name/title reveal (see .demcon-host-
     reveal and its :hover rules above) -- on a touch device, tapping a
     host photo can leave it "stuck" in a hover-like state (no real mouse
     to move away and clear it) rather than the deliberate mouse-in/
     mouse-out interaction this was designed for, reading as a random
     photo-expands-and-won't-close glitch. Tapping still opens the full
     bio modal regardless (that's a separate click handler, untouched) --
     this only cancels the extra hover animation, not the modal itself. */
  .demcon-host:hover .demcon-host-reveal {
    max-width: 0 !important;
    padding-right: 0 !important;
    opacity: 0 !important;
  }

  .demcon-host:hover ~ .demcon-host {
    transform: none !important;
  }

  /* Mobile only: this whole content block (logo, description, hosts,
     divider, propose-a-salon) was vertically centered within the
     section (Container's own baked justify-content:center) rather than
     anchored to its top -- combined with the section's own baked 95px
     padding-top, that pushed a big empty gap above the logo and, since
     centering divides the section's leftover height evenly above and
     below, squeezed "Propose a Salon" down far enough to sit under the
     "Next Salon" notification strip that slides up over this card (see
     setupSalonPopup, sticky-nav.js) once it's caught up. Anchoring to
     the top instead and shrinking the section's own padding moves
     everything up, clearing the strip. */
  .framer-86nu57 {
    justify-content: flex-start !important;
  }

  [data-framer-name="DemocracySalons"] {
    padding-top: 30px !important;
  }
}

/* Salons section (index.html): "Our Hosts" mini-bios, sitting between the
   description and the divider/Propose-a-Salon button. Extra margin-top
   pushes this and everything after it (divider, Propose a Salon) further
   down the card -- there was a lot of unclaimed vertical space between
   the button and the bottom strip otherwise. */
.demcon-hosts {
  display: flex;
  flex-direction: column;
  gap: 16px;
  margin-top: 32px;
}

.demcon-hosts-label {
  margin: 0;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 20px;
  font-weight: 700;
  letter-spacing: -0.02em;
  text-transform: uppercase;
  color: rgb(35, 31, 32);
}

.demcon-hosts-row {
  display: flex;
  flex-wrap: wrap;
  gap: 32px;
}

/* Fixed 88x88 footprint matching just the photo -- both the photo and the
   reveal panel are positioned absolutely within it (below), so the
   reveal's hover-expansion never changes this element's own box size.
   Without that, the expanding reveal was part of normal flex flow,
   growing this button's width and pushing the next host onto a new line
   every time you hovered the first one. */
.demcon-host {
  position: relative;
  display: block;
  width: 112px;
  height: 112px;
  border: none;
  background: none;
  padding: 0;
  margin: 0;
  font: inherit;
  color: inherit;
  cursor: pointer;
  text-align: left;
}

.demcon-host-photo-wrap {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 2;
  width: 112px;
  height: 112px;
  border-radius: 50%;
  overflow: hidden;
  /* Solid fill behind the photo -- the source PNGs' circular crop doesn't
     quite reach the edges of their own transparent canvas, so without
     this the gap between the photo and the ring showed whatever was
     behind it (the page background) instead of reading as one seamless
     black circle. */
  background: #030509;
  box-shadow: 0 0 0 3px #030509;
}

/* The two source photos (1080x1440 canvas) both have the exact same
   circular subject crop within them: a 1049x1049 circle at x:[14,1063],
   y:[221,1270] -- checked directly against the actual pixel content
   (bbox of the non-transparent area), not guessed. object-fit:cover
   can't target an arbitrary crop rectangle like that (only alignment via
   object-position), so instead this sizes/positions the raw <img> by
   hand: scale by 88/1049 so the circle itself becomes exactly 88px, then
   offset so the circle's own center (not the canvas's center, which sits
   noticeably higher) lands in the middle of this 112px frame. Fills the
   circle edge to edge with no leftover margin and nothing cropped off. */
.demcon-host-photo {
  display: block;
  position: absolute;
  width: 115.33px;
  height: 153.71px;
  left: -1.49px;
  top: -23.58px;
  max-width: none;
}

/* Hidden by default, sitting absolutely behind the photo (see .demcon-host
   above for why) -- hovering (or focusing, for keyboard users) slides it
   out from behind the circular photo into a wider black pill, revealing
   name + title. Clicking opens the full bio modal regardless of hover
   state. Because it's positioned absolutely, expanding it only ever
   overlaps whatever's to its right -- it never pushes that content
   around -- and the next host's photo (z-index:2, above this panel's
   z-index:1) stays visible on top if the two overlap. */
.demcon-host-reveal {
  position: absolute;
  top: 0;
  left: 56px;
  z-index: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  height: 112px;
  max-width: 0;
  padding-left: 68px;
  padding-right: 0;
  border-radius: 0 56px 56px 0;
  background: #030509;
  color: #fff;
  overflow: hidden;
  white-space: nowrap;
  opacity: 0;
  transition: max-width 0.35s ease, opacity 0.3s ease, padding-right 0.35s ease;
}

.demcon-host:hover .demcon-host-reveal,
.demcon-host:focus-visible .demcon-host-reveal {
  max-width: 280px;
  padding-right: 24px;
  opacity: 1;
}

/* Since the reveal panel overlaps whatever's next in the row instead of
   pushing it aside (it's position:absolute -- see above), later hosts
   need to visually slide out of its way themselves for the "make room"
   effect to still read. transform is layout-agnostic -- it repaints the
   element in a new spot without changing the box it occupies, so this
   shift (unlike the old max-width-driven growth) can never force
   .demcon-hosts-row to wrap or grow, and so never ripples out to resize
   the Container flex row or shove the decorative graphic beside it. */
.demcon-host:hover ~ .demcon-host,
.demcon-host:focus-visible ~ .demcon-host {
  transform: translateX(220px);
  transition: transform 0.35s ease;
}

.demcon-host-name {
  font-family: 'League Spartan', sans-serif;
  font-size: 15px;
  font-weight: 700;
  letter-spacing: -0.01em;
}

.demcon-host-title {
  font-family: 'League Spartan', sans-serif;
  font-size: 13px;
  font-weight: 300;
  opacity: 0.8;
}

/* Yellow "next salon" notification strip -- a fixed bottom bar (like a
   cookie-consent or promo popup) that slides up from off-screen once the
   DemocracySalons card has fully caught up and covers the viewport (see
   setupSalonPopup in sticky-nav.js), rather than sitting pinned inside
   the section from the start. translateY(100%) -> translateY(0) is the
   same slide-up technique as a typical bottom-sheet notification;
   padding-top on the wrap leaves room for the tab (below) to poke up
   above the bar without being clipped by overflow:hidden. */
.demcon-salon-strip-wrap {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 500;
  padding-top: 42px;
  overflow: hidden;
  transform: translateY(100%);
  transition: transform 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}

.demcon-salon-strip-wrap.visible {
  transform: translateY(0);
}

/* Three-column grid rather than a flex row -- the middle column is
   sized to the date's own content and the two 1fr side columns absorb
   the rest equally, so the date lands exactly centered on the bar as a
   whole while the title and button naturally settle toward the left and
   right respectively (not centered as one grouped cluster). */
.demcon-salon-strip {
  position: relative;
  display: flex;
  align-items: center;
  /* space-between (rather than a grid with a fixed-width centered column)
     splits whatever room is left over into the two gaps on either side of
     the date evenly, so the date sits exactly midway between the title
     and the button regardless of how wide either of those is. */
  justify-content: space-between;
  padding: 16px 160px;
  background: #fff500;
  border-top: 2.5px solid #030509;
  /* Softer than a typical elevated-card shadow -- this bar already reads
     as "on top" from the slide-up entrance and the tab poking above it,
     so a heavy shadow underneath it was more than it needed. */
  box-shadow: 0 -4px 28px rgba(3, 5, 9, 0.15);
}

/* Small flag poking up above the bar's top edge, same idea as a promo
   ribbon on a bottom-sheet notification -- uses the exact blue sampled
   from the Democracy Salons logo graphic elsewhere in this section
   (rgb(0,179,255)), not an approximation. Bordered on top/left/right
   (not bottom) so the bar's own border-top reads as a continuation of
   the same line around the tab, one unmistakably continuous outline
   instead of the tab looking like a separate floating chip. */
.demcon-salon-strip-tab {
  position: absolute;
  top: -38px;
  left: 88px;
  padding: 10px 22px;
  border: 2.5px solid #030509;
  border-bottom: none;
  border-radius: 8px 8px 0 0;
  background: #00b3ff;
  color: #fff;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 15px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  /* Flat, hard-edged offset (no blur) instead of a soft rgba shadow --
     matches the pop-art drop shadow behind the white "SALONS" text in the
     Democracy Salons logo graphic. */
  text-shadow: 2px 2px 0 #030509;
  white-space: nowrap;
}

.demcon-salon-strip-title {
  /* Clears the tab poking up above the bar's left edge -- without this
     the title's default start position (right at the bar's own left
     padding) sits partly underneath the tab instead of after it. */
  margin: 0 0 0 90px;
  font-family: 'League Spartan', sans-serif;
  font-size: 28px;
  font-weight: 700;
  letter-spacing: -0.02em;
  text-transform: uppercase;
  color: #030509;
  white-space: nowrap;
}

.demcon-salon-strip-date {
  margin: 0;
  font-family: 'League Spartan', sans-serif;
  font-size: 20px;
  font-weight: 300;
  color: #3a3a3a;
  white-space: nowrap;
}

.demcon-salon-strip-cta {
  flex: none;
  padding: 16px 40px;
  border: 2px solid #030509;
  border-radius: 999px;
  background: #ff0074;
  color: #fff;
  font-family: 'League Spartan', sans-serif;
  font-size: 18px;
  font-weight: 700;
  letter-spacing: -0.01em;
  text-transform: uppercase;
  text-decoration: none;
  transition: background-color 0.2s ease, transform 0.2s ease;
  /* This is a real <button>, not an <a> -- unlike Framer's own baked
     links/buttons, a plain <button> here has no baked cursor:pointer of
     its own, computing to the browser default ("default", a plain
     arrow) instead. enhanceButtons (sticky-nav.js) only tags elements
     that already compute to cursor:pointer as custom-cursor targets, so
     without this, this one button was silently skipped -- it kept
     showing the native arrow on hover instead of the custom cursor every
     other clickable element on the site gets (confirmed live). */
  cursor: pointer;
}

.demcon-salon-strip-cta:hover {
  background: #030509;
  transform: translateY(-2px);
}

@media (max-width: 767.98px) {
  /* Title + button share the top row (button right-aligned next to the
     title instead of stacked below the date), date drops to its own row
     spanning the full width underneath. Grid (not flex) so the button
     can sit in the title's row without needing them wrapped in a shared
     container of their own. */
  .demcon-salon-strip {
    display: grid;
    grid-template-columns: 1fr auto;
    grid-template-areas: 'title cta' 'date date';
    align-items: center;
    row-gap: 6px;
    column-gap: 12px;
    padding: 20px 24px;
  }

  .demcon-salon-strip-title {
    grid-area: title;
    margin-left: 0;
    padding-left: 10px;
    font-size: 18px;
    white-space: normal;
  }

  .demcon-salon-strip-date {
    grid-area: date;
    padding-left: 10px;
    font-size: 13px;
    /* Baked #3a3a3a (a mid-gray meant for a white background elsewhere)
       doesn't have enough contrast against this bar's bright yellow --
       matching the near-black (#030509) used for the title/tab/border
       right on this same bar instead. */
    color: #030509;
  }

  .demcon-salon-strip-cta {
    grid-area: cta;
    padding: 10px 20px;
    font-size: 14px;
  }

  /* Tab sits close to the bar's left padding instead of Framer's own
     88px-in desktop position, which left an oddly wide bare stretch of
     border to its left on a narrow phone screen. Shrunk (padding and
     font-size both) and pulled its poke-up height in to match (top) --
     at the original desktop size, it reached up far enough to overlap
     "Propose a Salon" (part of the DemocracySalons card scrolled behind
     this fixed bar, visible through the transparent space the wrap's own
     padding-top holds open above the bar itself) instead of just poking
     above the bar's own edge like it does on desktop. */
  .demcon-salon-strip-tab {
    left: 20px;
    padding: 6px 14px;
    font-size: 11px;
    top: -26px;
  }

  /* Matches the tab's own shrink above -- was reserving enough transparent
     space for the *original* 38px-tall tab to poke up into; taller than
     the new smaller tab actually needs, which was extra transparent space
     for whatever's scrolled behind the bar (see .demcon-salon-strip-tab
     comment) to show through. */
  .demcon-salon-strip-wrap {
    padding-top: 26px;
  }
}

/* Plain spacer sitting in normal document flow between DemocracySalons
   and Journey Section -- invisible itself, but its height pushes Journey
   Section's own natural document position further down, which is what
   actually delays when its rect.top crosses into the viewport (the thing
   setupSalonPopup watches to know when to hide the strip, and the thing
   that visually paints over DemocracySalons via z-index once it does).
   DemocracySalons's own sticky pin is untouched by this -- it isn't
   released by scrolling past whatever comes after it, only ever covered
   once something else's box shares the same screen space -- so for the
   whole height of this spacer, DemocracySalons (and the strip) just sit
   still on screen exactly as before, instead of Journey's content
   already peeking up from the bottom edge immediately after. */
.demcon-salon-pause-spacer {
  height: 500px;
}

/* Mobile only: this spacer's shared flex parent (with DemocracySalons,
   Journey Section, etc. -- see setupStackedSectionOffsets, sticky-nav.js)
   uses a per-section `order` Framer bakes in at this breakpoint
   (DemocracySalons:1, Journey Section:3, ...) to sequence the stack --
   exact same mechanism, and exact same bug, already fixed once this
   session for the hand-built JOIN US section. This spacer is also
   hand-built (not a Framer-authored component), so it has no baked
   order either and defaults to 0, tying it with Hero Section and
   landing it in flex order *before* DemocracySalons instead of after --
   its own transparent 500px then opened up a real gap between Hero and
   Salons, exposing the page's plain background there (read as a stray
   white/gray "bleed" arriving before the Salons card, sliding in behind
   the still-pinned Hero text). order:2 sits it correctly between
   DemocracySalons (1) and Journey Section (3), matching its actual
   purpose and DOM position. Desktop never bakes order at all (every
   section defaults to 0 there, so plain DOM order already puts this
   spacer in the right place) -- confirmed unaffected. */
@media (max-width: 767.98px) {
  .demcon-salon-pause-spacer {
    order: 2;
  }
}

/* Same fix, same reason, for the 768-1199.98px tablet range: Framer's own
   real (non-dead) tablet media query bakes the exact same per-section
   `order` values as its mobile one (DemocracySalons:1, Journey:3, Agenda:5,
   Speakers:7, Participation:8 -- confirmed by inspecting that CSS
   directly), so both this hand-built spacer and .demcon-contact-cta-section
   below default to order:0 there too, landing them in the same wrong flex
   position (right after Hero, painting over every card between them and
   the one they actually belong after) that the mobile-only rules above and
   below already fix for phones. Confirmed live: at 900px wide, scrolling
   showed the JOIN US card's contents covering the viewport from barely
   past the hero all the way through where Participation should still be
   visible, exactly this bug. */
@media (min-width: 768px) and (max-width: 1199.98px) {
  .demcon-salon-pause-spacer {
    order: 2;
  }

  .demcon-contact-cta-section {
    order: 9;
  }
}

/* Host bio modal -- reuses the shared .demcon-modal/.demcon-modal-dialog
   shell (see the Join Us modal above) for the overlay/backdrop/close
   button, with its own single-column body layout. */
.demcon-host-modal-dialog {
  flex-direction: column;
  max-width: 480px;
  padding: 48px 36px;
}

.demcon-host-modal-photo-wrap {
  position: relative;
  width: 120px;
  height: 120px;
  margin: 0 auto 24px;
  border-radius: 50%;
  overflow: hidden;
  /* Same seamless-fill fix as the card circles (see .demcon-host-photo-
     wrap) -- solid black behind the photo in case any sliver of the
     source PNG's transparent margin is still visible. */
  background: #030509;
  box-shadow: 0 0 0 3px #030509;
}

/* Same exact-crop technique as the small card photos (see
   .demcon-host-photo), recalculated for this 120px circle: scale by
   120/1049 so the same known 1049x1049 circular subject crop fills this
   frame edge to edge, offset so its actual center (not the canvas's)
   lands in the middle. */
.demcon-host-modal-photo {
  display: block;
  position: absolute;
  width: 123.55px;
  height: 164.73px;
  left: -1.6px;
  top: -25.28px;
  max-width: none;
}

.demcon-host-modal-body {
  text-align: center;
}

.demcon-host-modal-name {
  margin: 0 0 4px;
  font-family: 'League Spartan', sans-serif;
  font-size: 24px;
  font-weight: 700;
  letter-spacing: -0.02em;
  text-transform: uppercase;
}

.demcon-host-modal-title {
  margin: 0 0 16px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 14px;
  color: #ff0074;
}

.demcon-host-modal-socials {
  display: flex;
  justify-content: center;
  align-items: center;
  flex-wrap: wrap;
  gap: 16px;
  margin: 0 0 20px;
}

.demcon-host-modal-social {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  color: #030509;
  opacity: 0.75;
  transition: opacity 0.2s ease, transform 0.2s ease;
}

.demcon-host-modal-social:hover,
.demcon-host-modal-social:focus-visible {
  opacity: 1;
  transform: translateY(-2px);
}

.demcon-host-modal-social svg {
  width: 100%;
  height: 100%;
}

.demcon-host-modal-bio {
  margin: 0 0 24px;
  font-family: 'League Spartan', sans-serif;
  font-size: 16px;
  font-weight: 300;
  line-height: 1.5em;
  color: #3a3a3a;
}

.demcon-host-modal-link {
  display: inline-block;
  font-family: 'League Spartan', sans-serif;
  font-size: 15px;
  font-weight: 700;
  color: #030509;
  text-decoration: underline;
}

/* Salon event modal -- opened from the yellow strip's "Join Here" button.
   Reuses the shared .demcon-modal shell and .demcon-modal-form's
   input/button styling (see the Join Us modal above), just single-column
   with its own heading/date/blurb on top. */
.demcon-salon-modal-dialog {
  flex-direction: column;
  max-width: 460px;
  padding: 48px 36px;
}

.demcon-salon-modal-label {
  margin: 0 0 8px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 14px;
  font-weight: 500;
  letter-spacing: -0.02em;
  text-transform: uppercase;
  color: #ff0074;
}

.demcon-salon-modal-heading {
  margin: 0 0 8px;
  font-family: 'League Spartan', sans-serif;
  font-size: 28px;
  font-weight: 700;
  letter-spacing: -0.02em;
  text-transform: uppercase;
}

.demcon-salon-modal-when {
  margin: 0 0 20px;
  font-family: 'League Spartan', sans-serif;
  font-size: 16px;
  font-weight: 300;
  color: #3a3a3a;
}

.demcon-salon-modal-bio {
  margin: 0 0 24px;
  font-family: 'League Spartan', sans-serif;
  font-size: 15px;
  font-weight: 300;
  line-height: 1.5em;
  color: #3a3a3a;
}

.demcon-salon-modal-success {
  margin: 0;
  font-family: 'League Spartan', sans-serif;
  font-size: 15px;
  font-weight: 600;
  color: #030509;
}

/* Propose a Salon modal -- opened from the "Propose a Salon" link under
   the hosts. Reuses the shared .demcon-modal shell and .demcon-modal-form
   styling (see the Join Us modal above), single-column like the salon
   event modal but wider to comfortably fit the longer pitch fields. */
.demcon-propose-modal-dialog {
  flex-direction: column;
  max-width: 560px;
  padding: 48px 36px;
}

.demcon-propose-modal-label {
  margin: 0 0 8px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 14px;
  font-weight: 500;
  letter-spacing: -0.02em;
  text-transform: uppercase;
  color: #ff0074;
}

.demcon-propose-modal-heading {
  margin: 0 0 8px;
  font-family: 'League Spartan', sans-serif;
  font-size: 28px;
  font-weight: 700;
  letter-spacing: -0.02em;
  text-transform: uppercase;
}

.demcon-propose-modal-intro {
  margin: 0 0 24px;
  font-family: 'League Spartan', sans-serif;
  font-size: 15px;
  font-weight: 300;
  line-height: 1.5em;
  color: #3a3a3a;
}

.demcon-propose-modal-success {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

/* Author styles beat the UA stylesheet's [hidden] default at equal
   specificity regardless of source order, so the explicit display:flex
   above would otherwise leave this visible even while .hidden -- same
   fix as .demcon-modal-form[hidden] elsewhere in this file. */
.demcon-propose-modal-success[hidden] {
  display: none;
}

/* Same stylized blue treatment as "JOIN US" in the Join Us modal's own
   heading (.demcon-modal-panel-light h2 span) -- reused here so both
   modals' "you're in" moments read as the same visual language. */
.demcon-propose-modal-success-heading {
  margin: 0;
  font-family: 'Hidalgo Regular', 'Hidalgo Regular Placeholder', sans-serif;
  font-size: 40px;
  color: #00b3ff;
  -webkit-text-stroke: 1px #030509;
}

.demcon-propose-modal-success-body {
  margin: 0;
  font-family: 'League Spartan', sans-serif;
  font-size: 15px;
  font-weight: 600;
  color: #030509;
}

/* Shared across every form on the site (see wireBackendForm, sticky-nav.js):
   .demcon-form-error is appended once per form and toggled visible on a
   failed submission; .demcon-form-success replaces a form's own contents
   on the ones that had no purpose-built success panel of their own
   (newsletter signup, homepage contact form, Join Us modal). */
.demcon-form-error {
  margin: 12px 0 0;
  font-family: 'League Spartan', sans-serif;
  font-size: 14px;
  font-weight: 600;
  color: #ff0074;
}

.demcon-form-success {
  margin: 0;
  font-family: 'League Spartan', sans-serif;
  font-size: 15px;
  font-weight: 600;
  color: #030509;
}

/* Honeypot field: present in the DOM and part of the tab order like any
   other input would be, but positioned off-screen rather than
   display:none/visibility:hidden -- some bots skip fields hidden that way,
   fewer skip ones simply moved out of the visible page. Real visitors
   never see or reach it (tabindex="-1" keeps it out of keyboard
   navigation too), so any value in it on submit means a bot filled out
   every field it could find. */
.demcon-form-honeypot {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* Relevant Links field -- a single-line input per link (not a free-text
   textarea) with a "+ Add another link" control that appends more rows,
   so each link is its own value rather than one blob of pasted text. */
.demcon-propose-links {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.demcon-propose-links-label {
  font-family: 'Satoshi', 'Satoshi Placeholder', sans-serif;
  font-size: 14px;
  font-weight: 600;
  text-transform: uppercase;
  color: #030509;
}

.demcon-propose-links-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.demcon-propose-links-row {
  display: flex;
  align-items: center;
  gap: 8px;
}

.demcon-propose-links-row input {
  flex: 1;
  min-width: 0;
}

/* Qualified with .demcon-modal-form (rather than left bare) so these two
   specificity-tie with, and override, the shared .demcon-modal-form
   button pink-pill rule above -- otherwise that rule's extra type
   selector (button) outranks a single bare class here and both of these
   render as pink pills instead of their own plain styles. */
.demcon-modal-form .demcon-propose-links-remove {
  flex: none;
  width: 32px;
  height: 32px;
  margin-top: 0;
  padding: 0;
  border: 1.5px solid rgba(3, 5, 9, 0.3);
  border-radius: 50%;
  background: #fff;
  color: #030509;
  font-size: 16px;
  font-weight: 400;
  text-transform: none;
  line-height: 1;
  cursor: pointer;
}

.demcon-modal-form .demcon-propose-links-remove:hover {
  border-color: #030509;
}

.demcon-modal-form .demcon-propose-links-add {
  align-self: flex-start;
  margin-top: 2px;
  padding: 0;
  border: none;
  border-radius: 0;
  background: none;
  font-family: 'League Spartan', sans-serif;
  font-size: 14px;
  font-weight: 700;
  text-transform: none;
  color: #ff0074;
  cursor: pointer;
}

.demcon-modal-form .demcon-propose-links-add:hover {
  background: none;
  text-decoration: underline;
}

/* Bruxelles Calling's "Top" block (about/index.html) -- drop the mobile
   breakpoint's hard-coded height so the section grows to fit its own text.

   Framer bakes `height:830px` onto .framer-fn74nn inside its
   max-width:767.98px block (the base rule is height:min-content; only
   the mobile variant pins it), together with place-content:center. Once
   that block's content needs more than 830px -- which it does as soon as
   "BRUXELLES CALLING //" / "THREE DAYS. ONE CITY." start wrapping on a
   narrower viewport -- centering makes the excess overflow EQUALLY above
   and below the box rather than just running off the bottom, so the
   heading renders above the container's own top edge. Measured live at
   692px: the heading sat 33px above .framer-1rbxc9t's top, i.e. outside
   the section's own #faf9f5 background.

   Nothing clips it back, because the scroll-stacking rule above sets
   overflow:visible on all four stacking sections (required for the
   box-shadow that sells the stacked-card effect). So the escaped heading
   painted straight over the Principles card still pinned underneath --
   reported live as "BRUXELLES CALLING //" colliding with the accordion
   rows, and getting worse as the viewport narrowed and the text wrapped
   further. Same failure mode as the Join Us "Dots" overflow noted above:
   a Framer-baked fixed offset that only misbehaves once these sections
   stopped clipping their own overflow.

   height:min-content restores the base rule's own behaviour, so the block
   sizes to its text, the section grows with it (measured 1012px -> 1341px
   at 692px wide) and the white card ends up expanding *underneath* the
   heading instead of the heading escaping above it. Scoped to
   .demcon-about-page so the homepage's own use of this shared section is
   untouched; .demcon-about-page .framer-fn74nn (0,2,0) outranks Framer's
   own .framer-fn74nn (0,1,0) without needing !important. */
@media (max-width: 767.98px) {
  .demcon-about-page .framer-fn74nn {
    height: min-content;
  }
}
