/* =========================================================================
   UniPulse marketing site — styles
   Palette mirrors the app (constants/theme.ts): deep-navy dark theme,
   #2E90FA blue accent, and the logo's #3381C3→#3EC9EA→#2E56B5 pulse gradient.
   ========================================================================= */

:root {
  --bg: #0a0e1a;
  --surface: #131a2a;
  --surface-alt: #1a2234;
  --border: #232c42;
  --primary: #2e90fa;
  --primary-2: #3aa0ff;
  --cyan: #3ec9ea;
  --indigo: #2e56b5;
  --text: #f5f7fa;
  --text-muted: #8a94a6;
  --text-faint: #5b647a;
  --radius: 16px;
  --radius-sm: 10px;
  --max: 1140px;
  --font-head: 'Sora', system-ui, sans-serif;
  --font-body: 'Inter', system-ui, -apple-system, sans-serif;
}

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

html {
  scroll-behavior: smooth;
  scroll-padding-top: 90px;
}

body {
  font-family: var(--font-body);
  background: var(--bg);
  color: var(--text);
  line-height: 1.6;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
}

.container {
  width: 100%;
  max-width: var(--max);
  margin: 0 auto;
  padding: 0 24px;
}

h1,
h2,
h3,
h4 {
  font-family: var(--font-head);
  font-weight: 700;
  line-height: 1.12;
  letter-spacing: -0.02em;
}

a {
  color: inherit;
  text-decoration: none;
}

.grad-text {
  background: linear-gradient(100deg, var(--cyan), var(--primary) 55%, var(--indigo));
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
}

.link {
  color: var(--primary);
  font-weight: 600;
}
.link:hover {
  text-decoration: underline;
}

.eyebrow {
  display: inline-block;
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--cyan);
  margin-bottom: 14px;
}

/* ── ambient background orbs ─────────────────────────────────────────── */
.bg-orbs {
  position: fixed;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  pointer-events: none;
}
.orb {
  position: absolute;
  border-radius: 50%;
  filter: blur(90px);
  opacity: 0.5;
}
.orb-1 {
  width: 520px;
  height: 520px;
  background: radial-gradient(circle, rgba(46, 144, 250, 0.5), transparent 70%);
  top: -160px;
  right: -120px;
  animation: float1 18s ease-in-out infinite;
}
.orb-2 {
  width: 460px;
  height: 460px;
  background: radial-gradient(circle, rgba(62, 201, 234, 0.35), transparent 70%);
  top: 40%;
  left: -180px;
  animation: float2 22s ease-in-out infinite;
}
.orb-3 {
  width: 400px;
  height: 400px;
  background: radial-gradient(circle, rgba(46, 86, 181, 0.4), transparent 70%);
  bottom: -160px;
  right: 10%;
  animation: float1 26s ease-in-out infinite;
}
@keyframes float1 {
  0%,
  100% {
    transform: translate(0, 0);
  }
  50% {
    transform: translate(-40px, 50px);
  }
}
@keyframes float2 {
  0%,
  100% {
    transform: translate(0, 0);
  }
  50% {
    transform: translate(50px, -40px);
  }
}

/* ── buttons ─────────────────────────────────────────────────────────── */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 0.95rem;
  padding: 14px 26px;
  border-radius: var(--radius-sm);
  border: none;
  cursor: pointer;
  transition: transform 0.18s ease, box-shadow 0.18s ease, background 0.18s ease;
}
.btn-primary {
  background: linear-gradient(135deg, var(--primary-2), var(--primary));
  color: #fff;
  box-shadow: 0 8px 24px rgba(46, 144, 250, 0.35);
}
.btn-primary:hover {
  transform: translateY(-2px);
  box-shadow: 0 12px 30px rgba(46, 144, 250, 0.45);
}
.btn-sm {
  padding: 10px 18px;
  font-size: 0.88rem;
}

/* ── nav ─────────────────────────────────────────────────────────────── */
.nav {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 100;
  transition: background 0.3s ease, border-color 0.3s ease, backdrop-filter 0.3s ease;
  border-bottom: 1px solid transparent;
}
.nav.scrolled {
  background: rgba(10, 14, 26, 0.8);
  backdrop-filter: blur(14px);
  border-bottom-color: var(--border);
}
.nav-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 72px;
  gap: 20px;
}
.brand {
  display: inline-flex;
  align-items: center;
  gap: 10px;
}
.logo-mark {
  width: 34px;
  height: 34px;
  flex-shrink: 0;
}
.brand-name {
  font-family: var(--font-head);
  font-weight: 800;
  font-size: 1.25rem;
  letter-spacing: -0.02em;
}
.nav-menu {
  display: flex;
  align-items: center;
  gap: 30px;
  margin-left: auto;
}
.nav-links {
  display: flex;
  gap: 30px;
}
.nav-links a {
  font-size: 0.92rem;
  font-weight: 500;
  color: var(--text-muted);
  transition: color 0.18s ease;
  position: relative;
}
.nav-links a::after {
  content: '';
  position: absolute;
  left: 0;
  bottom: -6px;
  width: 0;
  height: 2px;
  background: var(--primary);
  transition: width 0.22s ease;
}
.nav-links a:hover {
  color: var(--text);
}
.nav-links a:hover::after {
  width: 100%;
}
.nav-toggle {
  display: none;
  flex-direction: column;
  gap: 5px;
  background: none;
  border: none;
  cursor: pointer;
  padding: 6px;
}
.nav-toggle span {
  width: 24px;
  height: 2px;
  background: var(--text);
  border-radius: 2px;
  transition: transform 0.25s ease, opacity 0.25s ease;
}
.nav-toggle.open span:nth-child(1) {
  transform: translateY(7px) rotate(45deg);
}
.nav-toggle.open span:nth-child(2) {
  opacity: 0;
}
.nav-toggle.open span:nth-child(3) {
  transform: translateY(-7px) rotate(-45deg);
}

/* ── hero ────────────────────────────────────────────────────────────── */
.hero {
  /* Bottom padding is deliberately generous: the hero and the phone preview
     should be the entire first screen, with Features starting below the fold
     rather than peeking into it. */
  padding: 140px 0 200px;
}
.hero-inner {
  display: grid;
  grid-template-columns: 1.05fr 0.95fr;
  gap: 48px;
  align-items: center;
}
.hero h1 {
  font-size: clamp(2.6rem, 6vw, 4.2rem);
  font-weight: 800;
  margin-bottom: 22px;
}
.hero-sub {
  font-size: 1.15rem;
  color: var(--text-muted);
  max-width: 520px;
  margin-bottom: 34px;
}
.download-row {
  display: flex;
  gap: 14px;
  flex-wrap: wrap;
  margin-bottom: 22px;
}
.store-btn {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  padding: 11px 20px 11px 18px;
  border-radius: 12px;
  background: var(--surface);
  border: 1px solid var(--border);
  transition: transform 0.18s ease, border-color 0.18s ease, background 0.18s ease;
}
.store-btn:hover {
  transform: translateY(-2px);
  border-color: var(--primary);
  background: var(--surface-alt);
}
.store-icon {
  width: 26px;
  height: 26px;
  color: #fff;
}
.store-btn-text {
  display: flex;
  flex-direction: column;
  line-height: 1.15;
}
.store-btn-text small {
  font-size: 0.68rem;
  color: var(--text-muted);
}
.store-btn-text strong {
  font-size: 1.05rem;
  font-family: var(--font-head);
  font-weight: 700;
}
.avail {
  font-size: 0.9rem;
  color: var(--text-faint);
}
.avail strong {
  color: var(--text-muted);
  font-weight: 600;
}

/* ── phone mockup ────────────────────────────────────────────────────── */
.hero-visual {
  position: relative;
  display: flex;
  justify-content: center;
}
.phone {
  position: relative;
  /* Overridden further down — see 'the phone's size'. Kept as the fallback
     for anyone reading this rule first. */
  width: 324px;
  height: 672px;
  background: #05070e;
  border-radius: 42px;
  border: 10px solid #05070e;
  box-shadow: 0 40px 80px rgba(0, 0, 0, 0.55), inset 0 0 0 2px #1c2438;
  overflow: hidden;
  z-index: 2;
  animation: phoneFloat 6s ease-in-out infinite;
}
@keyframes phoneFloat {
  0%,
  100% {
    transform: translateY(0);
  }
  50% {
    transform: translateY(-14px);
  }
}
.phone-notch {
  position: absolute;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  width: 120px;
  height: 24px;
  background: #05070e;
  border-radius: 0 0 16px 16px;
  z-index: 3;
}
.phone-screen {
  position: relative;
  width: 100%;
  height: 100%;
  background: linear-gradient(180deg, #0a0e1a, #0e1424);
  padding: 40px 14px 14px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
/* ── phone mockup ────────────────────────────────────────────────────
   Mirrors the real feed screen: status bar, header with bell and avatar, the
   three top widgets, a photo-led event card with the type tag and university
   badge, the filter FAB, and a five-tab bar with labels. */
.app-status {
  display: flex;
  justify-content: space-between;
  font-size: 0.5rem;
  color: var(--text-muted);
  padding: 0 2px 6px;
  letter-spacing: 0.02em;
}
.app-status-r { letter-spacing: 0.08em; }

.app-header { display: flex; align-items: center; gap: 7px; padding: 0 2px 10px; }
.app-header .logo-mark { flex: none; }
.app-header-title {
  font-family: var(--font-head);
  font-weight: 700;
  font-size: 0.9rem;
  flex: 1;
  text-align: center;
  margin-left: -8px;
}
.app-bell {
  position: relative;
  width: 20px;
  height: 20px;
  color: var(--text);
  flex: none;
}
.app-bell svg { width: 100%; height: 100%; }
.app-badge {
  position: absolute;
  top: -4px;
  right: -4px;
  min-width: 11px;
  height: 11px;
  border-radius: 999px;
  background: #f04438;
  color: #fff;
  font-size: 0.4rem;
  font-style: normal;
  font-weight: 800;
  display: grid;
  place-items: center;
  padding: 0 2px;
}
.app-avatar {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  flex: none;
  background: linear-gradient(140deg, #8ab4d8, #4a6f92);
}

.app-widgets { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; }
.app-widget {
  position: relative;
  border-radius: 12px;
  padding: 8px 8px 10px;
  min-height: 54px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  color: #fff;
}
.app-widget b { font-size: 0.5rem; font-weight: 800; line-height: 1.2; }
.app-widget small { font-size: 0.44rem; opacity: 0.75; }
/* The oversized graphic bleeding off the corner is what makes these read as
   the app's widgets rather than as three flat tiles. */
.app-w-art {
  position: absolute;
  right: -8px;
  top: -6px;
  width: 40px;
  height: 40px;
  opacity: 0.5;
}
.app-w-liked { background: linear-gradient(150deg, #4a1f3d, #2a1830); }
.app-w-liked .app-w-art { color: #e8477f; opacity: 0.85; }
.app-w-tickets { background: linear-gradient(150deg, #2a2f3a, #1b1f28); }
.app-w-tickets .app-w-art { color: #cfd6e0; }
.app-w-collector { background: linear-gradient(150deg, #17497a, #123a63); }
.app-w-collector .app-w-art { color: #6bb6ff; }

.app-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 14px;
  overflow: hidden;
  animation: cardFloat 6s ease-in-out infinite;
}
.app-card-1 { animation-delay: -0.4s; }
.app-card-2 { animation-delay: -2.6s; }
@keyframes cardFloat {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-4px); }
}

.app-photo { position: relative; height: 74px; }
/* Stand-ins for the photography, not photographs: a mockup should not imply we
   have licensed images we don't. */
.app-photo-row { background: linear-gradient(160deg, #4a7a9e 0%, #2f5c7d 45%, #23405a 100%); }
.app-photo-solar { background: linear-gradient(160deg, #6d86a3 0%, #3d5f80 55%, #24384f 100%); }
.app-foryou {
  position: absolute;
  top: 7px;
  right: 7px;
  background: linear-gradient(120deg, #ff5fa2, #d63384);
  color: #fff;
  font-size: 0.44rem;
  font-weight: 800;
  padding: 3px 7px;
  border-radius: 999px;
}

.app-card-body { padding: 9px 10px 10px; }
.app-tagrow { display: flex; align-items: center; gap: 5px; margin-bottom: 6px; }
.app-tag {
  color: #fff;
  font-size: 0.44rem;
  font-weight: 800;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  padding: 3px 7px;
  border-radius: 999px;
}
.app-uni {
  width: 13px;
  height: 13px;
  border-radius: 50%;
  background: #00a6d6;
  flex: none;
}
.app-heart { margin-left: auto; width: 13px; height: 13px; color: var(--text-faint); }
.app-heart svg { width: 100%; height: 100%; }

.app-card-title { font-weight: 700; font-size: 0.76rem; margin-bottom: 3px; line-height: 1.25; }
.app-card-meta { color: var(--text-muted); font-size: 0.55rem; }
.app-card-host {
  display: flex;
  align-items: center;
  gap: 4px;
  color: var(--text-faint);
  font-size: 0.55rem;
  font-weight: 600;
  margin-top: 4px;
}
.app-card-host svg { width: 9px; height: 9px; flex: none; }
.app-card-desc {
  color: var(--text-muted);
  font-size: 0.55rem;
  line-height: 1.45;
  margin: 6px 0 0;
}
.app-card-link {
  color: var(--primary-2);
  font-size: 0.55rem;
  font-weight: 700;
  text-align: right;
  margin-top: 6px;
}

.app-fab {
  position: absolute;
  right: 12px;
  bottom: 52px;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  border: 0;
  background: var(--primary);
  color: #fff;
  display: grid;
  place-items: center;
  box-shadow: 0 6px 16px -4px rgb(46 144 250 / 0.7);
}
.app-fab svg { width: 15px; height: 15px; }

.app-tabbar {
  margin-top: auto;
  display: flex;
  justify-content: space-between;
  padding: 8px 4px 2px;
  border-top: 1px solid var(--border);
}
.app-tab {
  position: relative;
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  color: var(--text-faint);
}
.app-tab svg { width: 13px; height: 13px; }
.app-tab small { font-size: 0.4rem; font-weight: 600; }
.app-tab.is-active { color: var(--primary); }
.app-dot {
  position: absolute;
  top: -1px;
  right: 26%;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: #f04438;
}

/* ── how it works: two tracks ────────────────────────────────────────── */
.tracks { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
.track {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 24px;
  transition: transform 0.35s cubic-bezier(0.22, 1, 0.36, 1), border-color 0.35s ease;
}
.track:hover { transform: translateY(-4px); border-color: rgb(46 144 250 / 0.4); }
.track-head { margin-bottom: 18px; }
.track-badge {
  display: inline-block;
  font-size: 0.66rem;
  font-weight: 800;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 4px 10px;
  border-radius: 999px;
  margin-bottom: 10px;
}
.track-badge-student { background: rgb(46 144 250 / 0.16); color: var(--primary-2); }
.track-badge-host { background: rgb(31 174 110 / 0.16); color: #58d6a0; }
.track-head h3 { font-family: var(--font-head); font-size: 1.15rem; margin: 0; }
.track-steps { list-style: none; margin: 0; padding: 0; display: grid; gap: 14px; }
.track-steps li { display: flex; gap: 12px; align-items: flex-start; }
.track-steps .step-num {
  flex: none;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--surface-alt);
  border: 1px solid var(--border);
  color: var(--text);
  font-size: 0.78rem;
  font-weight: 700;
  display: grid;
  place-items: center;
}
.track-steps b { display: block; font-size: 0.94rem; margin-bottom: 2px; }
.track-steps span { color: var(--text-muted); font-size: 0.88rem; line-height: 1.55; }
.track-link {
  display: inline-block;
  margin-top: 18px;
  color: var(--primary-2);
  font-weight: 600;
  font-size: 0.9rem;
  text-decoration: none;
}
.track-link:hover { text-decoration: underline; }

/* ── feature icons ───────────────────────────────────────────────────── */
/* Line icons rather than emoji: emoji render differently on every platform and
   read as placeholder art on a page that is otherwise designed. */
.feature-icon {
  width: 42px;
  height: 42px;
  border-radius: 12px;
  background: linear-gradient(150deg, rgb(46 144 250 / 0.18), rgb(62 201 234 / 0.1));
  border: 1px solid rgb(46 144 250 / 0.25);
  color: var(--primary-2);
  display: grid;
  place-items: center;
  margin-bottom: 14px;
}
.feature-icon svg { width: 21px; height: 21px; }

/* ── wordmark ────────────────────────────────────────────────────────── */
.brand-name span { color: var(--primary); }

@media (max-width: 820px) {
  .tracks { grid-template-columns: 1fr; }
}
.phone-glow {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 340px;
  height: 340px;
  background: radial-gradient(circle, rgba(46, 144, 250, 0.4), transparent 65%);
  filter: blur(40px);
  z-index: 1;
}

/* ── sections ────────────────────────────────────────────────────────── */
.section {
  padding: 100px 0;
  position: relative;
}
.section-alt {
  background: linear-gradient(180deg, transparent, rgba(19, 26, 42, 0.5), transparent);
}
.section-head {
  text-align: center;
  max-width: 640px;
  margin: 0 auto 60px;
}
.section-head h2 {
  font-size: clamp(2rem, 4vw, 2.9rem);
  margin-bottom: 16px;
}
.section-head p {
  color: var(--text-muted);
  font-size: 1.08rem;
}

/* ── feature grid ────────────────────────────────────────────────────── */
.feature-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 22px;
}
.feature-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 30px 26px;
  transition: transform 0.22s ease, border-color 0.22s ease, box-shadow 0.22s ease;
}
.feature-card:hover {
  transform: translateY(-6px);
  border-color: rgba(46, 144, 250, 0.5);
  box-shadow: 0 16px 40px rgba(0, 0, 0, 0.35);
}
.feature-icon {
  width: 54px;
  height: 54px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.5rem;
  border-radius: 14px;
  background: linear-gradient(135deg, rgba(46, 144, 250, 0.18), rgba(62, 201, 234, 0.12));
  border: 1px solid rgba(46, 144, 250, 0.25);
  margin-bottom: 20px;
}
.feature-card h3 {
  font-size: 1.2rem;
  margin-bottom: 10px;
}
.feature-card p {
  color: var(--text-muted);
  font-size: 0.96rem;
}

/* ── steps ───────────────────────────────────────────────────────────── */
.steps {
  display: flex;
  align-items: flex-start;
  justify-content: center;
  gap: 8px;
  flex-wrap: wrap;
}
.step {
  flex: 1;
  min-width: 220px;
  max-width: 300px;
  text-align: center;
  padding: 0 12px;
}
.step-num {
  width: 56px;
  height: 56px;
  margin: 0 auto 20px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-head);
  font-weight: 800;
  font-size: 1.4rem;
  border-radius: 50%;
  background: linear-gradient(135deg, var(--primary-2), var(--indigo));
  color: #fff;
  box-shadow: 0 8px 22px rgba(46, 144, 250, 0.4);
}
.step h3 {
  font-size: 1.15rem;
  margin-bottom: 10px;
}
.step p {
  color: var(--text-muted);
  font-size: 0.95rem;
}
.step-line {
  flex: 0 0 60px;
  height: 2px;
  margin-top: 28px;
  background: linear-gradient(90deg, var(--border), var(--primary), var(--border));
  opacity: 0.6;
}

/* ── about ───────────────────────────────────────────────────────────── */
.about-inner {
  display: grid;
  grid-template-columns: 1.3fr 1fr;
  gap: 56px;
  align-items: center;
}
.about-copy h2 {
  font-size: clamp(2rem, 4vw, 2.8rem);
  margin-bottom: 20px;
}
.about-copy p {
  color: var(--text-muted);
  font-size: 1.05rem;
  margin-bottom: 16px;
}
.about-stats {
  display: flex;
  flex-direction: column;
  gap: 18px;
}
.stat {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 24px 28px;
  display: flex;
  align-items: baseline;
  gap: 18px;
}
.stat-num {
  font-family: var(--font-head);
  font-weight: 800;
  font-size: 2.6rem;
  line-height: 1;
  background: linear-gradient(135deg, var(--cyan), var(--primary));
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  min-width: 52px;
}
.stat-label {
  color: var(--text-muted);
  font-weight: 500;
}

/* ── contact ─────────────────────────────────────────────────────────── */
.contact-form {
  max-width: 620px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: 18px;
}
.form-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 18px;
}
.contact-form label {
  display: flex;
  flex-direction: column;
  gap: 8px;
  font-size: 0.88rem;
  font-weight: 600;
  color: var(--text-muted);
}
.contact-form input,
.contact-form textarea {
  font-family: var(--font-body);
  font-size: 1rem;
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 13px 15px;
  transition: border-color 0.18s ease, box-shadow 0.18s ease;
  resize: vertical;
}
.contact-form input:focus,
.contact-form textarea:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 3px rgba(46, 144, 250, 0.18);
}
.contact-form input::placeholder,
.contact-form textarea::placeholder {
  color: var(--text-faint);
}
.contact-form .btn {
  align-self: flex-start;
}
.form-note {
  font-size: 0.92rem;
  font-weight: 600;
}
.form-note.ok {
  color: #4ade80;
}
.form-note.err {
  color: #f26d6d;
}

/* ── footer ──────────────────────────────────────────────────────────── */
.footer {
  border-top: 1px solid var(--border);
  padding: 60px 0 30px;
  background: rgba(10, 14, 26, 0.6);
}
.footer-inner {
  display: grid;
  grid-template-columns: 1.2fr 2fr;
  gap: 40px;
  margin-bottom: 40px;
}
.footer-tag {
  color: var(--text-muted);
  margin-top: 14px;
  font-size: 0.95rem;
}
.footer-links {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 24px;
}
.footer-col h4 {
  font-size: 0.82rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--text-faint);
  margin-bottom: 16px;
}
.footer-col a {
  display: block;
  color: var(--text-muted);
  font-size: 0.94rem;
  margin-bottom: 10px;
  transition: color 0.18s ease;
}
.footer-col a:hover {
  color: var(--primary);
}
.footer-bottom {
  display: flex;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 10px;
  padding-top: 26px;
  border-top: 1px solid var(--border);
  color: var(--text-faint);
  font-size: 0.85rem;
}

/* ── legal pages ─────────────────────────────────────────────────────── */
.legal {
  padding: 130px 0 90px;
}
.legal-content {
  max-width: 780px;
  margin: 0 auto;
}
.legal-content h1 {
  font-size: clamp(2rem, 5vw, 3rem);
  margin-bottom: 8px;
}
.legal-updated {
  color: var(--text-faint);
  font-size: 0.9rem;
  margin-bottom: 40px;
}
.legal-content h2 {
  font-size: 1.35rem;
  margin: 36px 0 12px;
}
.legal-content p,
.legal-content li {
  color: var(--text-muted);
  font-size: 1rem;
  margin-bottom: 12px;
}
.legal-content ul {
  padding-left: 22px;
  margin-bottom: 12px;
}
.legal-back {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--primary);
  font-weight: 600;
  margin-bottom: 30px;
}
.legal-note {
  background: var(--surface);
  border: 1px solid var(--border);
  border-left: 3px solid var(--primary);
  border-radius: var(--radius-sm);
  padding: 16px 18px;
  color: var(--text-muted);
  font-size: 0.9rem;
  margin-bottom: 30px;
}

/* ── scroll-reveal ───────────────────────────────────────────────────── */
.reveal {
  opacity: 0;
  transform: translateY(28px);
  transition: opacity 0.7s cubic-bezier(0.22, 1, 0.36, 1), transform 0.7s cubic-bezier(0.22, 1, 0.36, 1);
}
.reveal.in {
  opacity: 1;
  transform: none;
}

/* ── responsive ──────────────────────────────────────────────────────── */
@media (max-width: 900px) {
  .hero-inner {
    grid-template-columns: 1fr;
    text-align: center;
  }
  .hero-copy {
    order: 2;
  }
  .hero-visual {
    order: 1;
  }
  .hero-sub {
    margin-left: auto;
    margin-right: auto;
  }
  .download-row,
  .about-stats {
    justify-content: center;
  }
  .about-inner {
    grid-template-columns: 1fr;
    gap: 36px;
  }
  .feature-grid {
    grid-template-columns: repeat(2, 1fr);
  }
  .step-line {
    display: none;
  }
  .footer-inner {
    grid-template-columns: 1fr;
    gap: 32px;
  }
}

@media (max-width: 620px) {
  .nav-menu {
    display: none;
  }
  .nav-toggle {
    display: flex;
  }
  /* mobile dropdown — the whole menu (links + CTA) drops as one panel */
  .nav.menu-open .nav-menu {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 20px;
    position: absolute;
    top: 72px;
    left: 0;
    right: 0;
    margin: 0;
    background: rgba(10, 14, 26, 0.97);
    backdrop-filter: blur(14px);
    border-bottom: 1px solid var(--border);
    padding: 22px 24px 28px;
  }
  .nav.menu-open .nav-links {
    flex-direction: column;
    gap: 18px;
  }
  .feature-grid {
    grid-template-columns: 1fr;
  }
  .form-row {
    grid-template-columns: 1fr;
  }
  .hero {
    padding: 120px 0 70px;
  }
  .section {
    padding: 70px 0;
  }
}

/* respect reduced-motion */
@media (prefers-reduced-motion: reduce) {
  * {
    animation: none !important;
    transition: none !important;
  }
  .reveal {
    opacity: 1;
    transform: none;
  }
}


/* ── extra motion ─────────────────────────────────────────────────────
   Everything below is decoration layered on top of a page that already works
   without it. All of it is switched off under prefers-reduced-motion at the
   bottom of this file. */

/* Reveals can stagger: a child with --d waits its turn, so a grid of cards
   arrives as a wave rather than all at once. */
.reveal { transition-delay: var(--d, 0s); }

/* Cards lift toward the cursor. Transform and shadow only — animating those
   two is what keeps it on the compositor instead of forcing layout. */
.feature-card,
.step {
  transition: transform 0.35s cubic-bezier(0.22, 1, 0.36, 1), border-color 0.35s ease, box-shadow 0.35s ease;
}
.feature-card:hover,
.step:hover {
  transform: translateY(-6px);
  border-color: rgb(46 144 250 / 0.45);
  box-shadow: 0 18px 40px -22px rgb(46 144 250 / 0.55);
}
.feature-card:hover .feature-icon { transform: scale(1.12) rotate(-6deg); }
.feature-icon { transition: transform 0.35s cubic-bezier(0.34, 1.56, 0.64, 1); }

/* The store buttons are the page's primary action; they deserve to respond. */
.store-btn { transition: transform 0.25s cubic-bezier(0.22, 1, 0.36, 1), border-color 0.25s ease; }
.store-btn:hover { transform: translateY(-3px); border-color: rgb(46 144 250 / 0.6); }

/* A sheen that crosses the button once on hover. */
.btn-primary { position: relative; overflow: hidden; }
.btn-primary::after {
  content: '';
  position: absolute;
  inset: 0 auto 0 -60%;
  width: 40%;
  background: linear-gradient(90deg, transparent, rgb(255 255 255 / 0.28), transparent);
  transform: skewX(-20deg);
  transition: left 0.55s ease;
}
.btn-primary:hover::after { left: 120%; }

/* The gradient in the headline drifts, so the hero is never entirely static. */
.grad-text {
  background-size: 220% auto;
  animation: gradientShift 7s ease-in-out infinite;
}
@keyframes gradientShift {
  0%, 100% { background-position: 0% center; }
  50% { background-position: 100% center; }
}

/* The whole phone drifts, slower than the cards inside it, so the two
   movements read as one object rather than as separate effects. */
.phone { animation: phoneFloat 9s ease-in-out infinite; }
@keyframes phoneFloat {
  0%, 100% { transform: translateY(0) rotate(0deg); }
  50% { transform: translateY(-12px) rotate(-0.6deg); }
}

/* Section headings get a short brand rule that draws itself in when the
   section arrives. */
.section-head h2 { position: relative; }
.section-head.in h2::after {
  content: '';
  position: absolute;
  left: 0;
  bottom: -10px;
  height: 3px;
  border-radius: 2px;
  background: linear-gradient(90deg, var(--primary), var(--cyan));
  animation: ruleIn 0.7s 0.15s cubic-bezier(0.22, 1, 0.36, 1) both;
}
@keyframes ruleIn { from { width: 0; opacity: 0; } to { width: 54px; opacity: 1; } }

/* The stat numbers count up via a CSS property so the browser interpolates an
   integer rather than JavaScript ticking a timer. */
@property --n { syntax: '<integer>'; initial-value: 0; inherits: false; }
.stat-num.count { animation: countUp 1.4s 0.2s ease-out both; counter-reset: n var(--n); }
.stat-num.count::after { content: counter(n); }
@keyframes countUp { to { --n: var(--to, 0); } }

/* Nav links grow an underline from the middle out. */
.nav-links a { position: relative; }
.nav-links a::after {
  content: '';
  position: absolute;
  left: 50%;
  right: 50%;
  bottom: -5px;
  height: 2px;
  background: var(--primary);
  border-radius: 2px;
  transition: left 0.28s ease, right 0.28s ease;
}
.nav-links a:hover::after { left: 0; right: 0; }

@media (prefers-reduced-motion: reduce) {
  .app-card,
  .phone,
  .grad-text,
  .stat-num.count,
  .section-head.in h2::after { animation: none !important; }
  .feature-card:hover,
  .step:hover,
  .store-btn:hover { transform: none; }
  .btn-primary::after { display: none; }
}


/* ══ hero showcase ══════════════════════════════════════════════════════
   Three switchable app screens with hover annotations. The phone is bigger
   than before on purpose: it is the only thing on the page that shows what
   the product actually is, so it earns the space. */

.showcase { position: relative; display: flex; flex-direction: column; align-items: center; }

.sc-tabs {
  position: relative;
  display: inline-flex;
  gap: 2px;
  padding: 4px;
  margin-bottom: 18px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 999px;
}
.sc-tab {
  position: relative;
  z-index: 1;
  background: none;
  border: 0;
  color: var(--text-muted);
  font: inherit;
  font-size: 0.85rem;
  font-weight: 600;
  padding: 8px 18px;
  border-radius: 999px;
  cursor: pointer;
  transition: color 0.25s ease;
}
.sc-tab.is-on { color: #fff; }
/* One moving pill rather than a background per tab, so the selection slides
   between them instead of blinking from one to the next. */
.sc-ink {
  position: absolute;
  top: 4px;
  bottom: 4px;
  border-radius: 999px;
  background: linear-gradient(120deg, var(--primary), var(--indigo));
  transition: left 0.42s cubic-bezier(0.22, 1, 0.36, 1), width 0.42s cubic-bezier(0.22, 1, 0.36, 1);
}

/* Also overridden — see 'the phone's size'. */
.phone { width: 350px; height: 696px; }
.phone-screen { position: relative; overflow: hidden; padding: 0; }

/* The three screens sit side by side and the track slides. Transform only —
   no reflow, and the panels keep their state while off-screen. */
.sc-track {
  display: flex;
  width: 400%;
  height: 100%;
  transition: transform 0.55s cubic-bezier(0.22, 1, 0.36, 1);
}
.app-screen {
  width: 25%;
  height: 100%;
  padding: 34px 12px 10px;
  display: flex;
  flex-direction: column;
  gap: 9px;
  background: linear-gradient(180deg, #0a0e1a, #0e1424);
}
.sc-hint { color: var(--text-faint); font-size: 0.78rem; margin: 16px 0 0; text-align: center; }

/* ── hotspots ──────────────────────────────────────────────────────── */
.hot { position: relative; outline: none; }
.hot-ring {
  position: absolute;
  inset: -5px;
  border-radius: 14px;
  border: 1.5px solid transparent;
  transition: border-color 0.25s ease, background 0.25s ease;
  pointer-events: none;
}
.hot:hover .hot-ring,
.hot:focus-visible .hot-ring {
  border-color: rgb(46 144 250 / 0.75);
  background: rgb(46 144 250 / 0.07);
}
/* A single slow pulse on the first hotspot of each screen, so it is discoverable
   without a permanent glow competing with the content. */
.hot::after {
  content: '';
  position: absolute;
  top: -5px;
  right: -5px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--primary);
  box-shadow: 0 0 0 0 rgb(46 144 250 / 0.6);
  animation: hotPing 2.6s ease-out infinite;
}
.hot:hover::after, .hot:focus-visible::after { animation: none; opacity: 0; }
@keyframes hotPing {
  0% { box-shadow: 0 0 0 0 rgb(46 144 250 / 0.55); }
  70% { box-shadow: 0 0 0 10px rgb(46 144 250 / 0); }
  100% { box-shadow: 0 0 0 0 rgb(46 144 250 / 0); }
}

/* The note sits OUTSIDE the phone: inside it would cover the very thing it is
   describing, and the phone is too narrow to read a sentence in. */
.tip {
  position: absolute;
  right: calc(100% + 26px);
  top: 50%;
  transform: translateY(-50%) translateX(8px);
  width: 210px;
  background: var(--surface-alt);
  border: 1px solid rgb(46 144 250 / 0.35);
  border-radius: 12px;
  padding: 11px 13px;
  font-size: 0.8rem;
  line-height: 1.5;
  color: var(--text);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.28s ease, transform 0.28s cubic-bezier(0.22, 1, 0.36, 1);
  z-index: 5;
  box-shadow: 0 18px 40px -20px rgb(0 0 0 / 0.8);
}
.tip::after {
  content: '';
  position: absolute;
  right: -6px;
  top: 50%;
  width: 10px;
  height: 10px;
  margin-top: -5px;
  background: var(--surface-alt);
  border-right: 1px solid rgb(46 144 250 / 0.35);
  border-top: 1px solid rgb(46 144 250 / 0.35);
  transform: rotate(45deg);
}
.hot:hover .tip, .hot:focus-visible .tip { opacity: 1; transform: translateY(-50%) translateX(0); }

/* ── map screen ────────────────────────────────────────────────────── */
.map-chips { display: flex; gap: 5px; overflow: hidden; }
.map-chips-types { margin: -3px 0; }
.mchip {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  white-space: nowrap;
  font-size: 0.48rem;
  font-weight: 600;
  color: var(--text-muted);
  border: 1px solid var(--border);
  background: var(--surface);
  border-radius: 999px;
  padding: 4px 8px;
}
.mchip svg { width: 8px; height: 8px; }
.mchip.is-on { background: var(--primary); border-color: var(--primary); color: #fff; }
.t-academic { border-color: #2e90fa; color: #6fb6ff; }
.t-social { border-color: #a24be0; color: #c78ff0; }
.t-sports { border-color: #1fae6e; color: #5fd6a4; }
.t-music { border-color: #e8a33d; color: #f0c07d; }
.t-culture { border-color: #f87171; color: #ffa4a4; }

.map-canvas { position: relative; flex: 1; border-radius: 10px; overflow: hidden; min-height: 150px; }
.map-svg { width: 100%; height: 100%; display: block; }
.map-pin { position: absolute; width: 24px; height: 33px; }
.map-pin svg { width: 100%; height: 100%; filter: drop-shadow(0 3px 5px rgb(0 0 0 / 0.5)); }
.map-pin i {
  position: absolute;
  top: 5px;
  left: 50%;
  transform: translateX(-50%);
  width: 11px;
  height: 11px;
  color: #fff;
}
.map-pin i svg { width: 100%; height: 100%; }
/* Each pin drifts a little, so the map reads as live rather than as a picture. */
.pin-music { left: 26%; top: 22%; animation: pinBob 4.2s ease-in-out infinite; }
.pin-sports { left: 40%; top: 58%; animation: pinBob 4.2s ease-in-out -1.4s infinite; }
.pin-academic { left: 60%; top: 52%; animation: pinBob 4.2s ease-in-out -2.8s infinite; }
@keyframes pinBob { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-4px); } }

.map-count {
  position: absolute;
  left: 8px;
  bottom: 8px;
  background: rgb(10 14 26 / 0.9);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 3px 9px;
  font-size: 0.5rem;
  font-weight: 700;
}

/* ── communities screen ────────────────────────────────────────────── */
.app-plus {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--surface-alt);
  color: var(--text);
  display: grid;
  place-items: center;
  flex: none;
}
.app-plus svg { width: 10px; height: 10px; }

.com-search { display: flex; gap: 5px; }
.com-searchbox {
  flex: 1;
  display: flex;
  align-items: center;
  gap: 5px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 6px 8px;
  font-size: 0.5rem;
  color: var(--text-faint);
}
.com-searchbox svg { width: 9px; height: 9px; }
.com-filter {
  width: 26px;
  border: 1px solid var(--border);
  background: var(--surface);
  border-radius: 10px;
  display: grid;
  place-items: center;
  color: var(--text-muted);
}
.com-filter svg { width: 10px; height: 10px; }

.com-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 8px;
}
.com-card + .com-card { margin-top: 7px; }
.com-card-cut { opacity: 0.75; }
.com-top { display: flex; align-items: center; gap: 6px; }
.com-logo { width: 22px; height: 22px; border-radius: 7px; flex: none; }
.l-aero { background: linear-gradient(140deg, #1b3a5c, #0e2138); }
.l-film { background: linear-gradient(140deg, #f7d9e8, #d98fbb); }
.l-volley { background: linear-gradient(140deg, #dfe8f5, #8fa8c8); }
.com-name { font-size: 0.6rem; font-weight: 700; flex: 1; line-height: 1.2; }
.com-follow {
  background: var(--primary);
  color: #fff;
  font-size: 0.46rem;
  font-weight: 700;
  padding: 4px 8px;
  border-radius: 999px;
  flex: none;
}
.com-tags { display: flex; gap: 4px; margin: 6px 0; }
.ctag {
  font-size: 0.4rem;
  font-weight: 800;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 3px 6px;
  border-radius: 5px;
  background: var(--surface-alt);
  color: var(--text-muted);
}
.ctag-academic { background: rgb(46 144 250 / 0.18); color: #6fb6ff; }
.ctag-culture { background: rgb(248 113 113 / 0.18); color: #ffa4a4; }
.ctag-sports { background: rgb(31 174 110 / 0.18); color: #5fd6a4; }

.com-body { position: relative; display: flex; gap: 7px; }
.com-photo { width: 62px; height: 40px; border-radius: 7px; flex: none; }
.p-aero { background: linear-gradient(140deg, #7d9ab8, #3d5a78); }
.p-film { background: linear-gradient(140deg, #b8c4a8, #5c7a4a); }
.com-text { flex: 1; min-width: 0; }
.com-text p { font-size: 0.5rem; color: var(--text-muted); margin: 0 0 5px; line-height: 1.35; }
.com-counts { display: flex; align-items: center; gap: 3px; font-size: 0.46rem; color: var(--text-faint); }
.com-counts svg { width: 7px; height: 7px; }
.com-uni {
  position: absolute;
  right: 0;
  bottom: 0;
  width: 13px;
  height: 13px;
  border-radius: 50%;
  background: #00a6d6;
}

/* On a narrow screen the notes have nowhere to sit beside the phone, so they
   drop under the hotspot and stay readable. */
@media (max-width: 980px) {
  .tip {
    right: auto;
    left: 50%;
    top: calc(100% + 12px);
    transform: translateX(-50%) translateY(6px);
    width: min(240px, 78vw);
  }
  .tip::after { display: none; }
  .hot:hover .tip, .hot:focus-visible .tip { transform: translateX(-50%) translateY(0); }
}
@media (max-width: 560px) {
  .phone { width: 280px; }
}

@media (prefers-reduced-motion: reduce) {
  .sc-track, .sc-ink, .tip { transition: none; }
  .map-pin, .hot::after { animation: none; }
}

/* ── showcase fixes ───────────────────────────────────────────────────── */

/* The filter button is absolutely positioned, and without a positioned
   ancestor of its own it resolved against .phone-screen — so the Feed's button
   stayed on screen while the Map and Communities panels slid past underneath
   it. Each panel now establishes its own containing block. */
.app-screen { position: relative; }

/* The map has to take the space left between the chips and the tab bar.
   Without this it sized to its own min-height, overflowed the screen, and
   pushed the tab bar out of the phone entirely. min-height:0 is the part that
   actually allows a flex child to shrink below its content. */
.hot-map { flex: 1; min-height: 0; display: flex; }
.hot-map .map-canvas { flex: 1; min-height: 0; }

/* Names sit next to their logo, not centred in the leftover space. */
.com-name { text-align: left; }


/* ── showcase fixes, round two ────────────────────────────────────────── */

/* THIS is why the tabs did nothing. The glow is a 340px decorative blob with
   z-index 1, centred on .hero-visual — which now includes the tabs above the
   phone. It came later in the DOM at the same stacking level, so it sat over
   the buttons and ate every click. It is decoration; it should never have been
   in the hit-testing path at all. */
.phone-glow { pointer-events: none; }
.showcase { position: relative; z-index: 2; }

/* ── the phone's size, and the only rule that decides it ──────────────
   Three earlier `.phone` rules also set width/height at the same specificity,
   so the last one in the file wins and the others are dead weight — which is
   why raising the height further up changed nothing on screen.

   350x600 is the geometry every screen inside was laid out against. To make the
   preview bigger we SCALE it rather than stretch the box: growing the box left
   the contents at their old size, so the padding, the card heights and the tab
   bar all stopped agreeing with each other. A transform enlarges it the way
   zooming a picture does, and nothing inside has to know.

   Change --phone-zoom, not these numbers. */
.phone { width: 350px; height: 600px; }
.app-screen { padding: 16px 12px 14px; }

/* Photo, with the gradient still underneath as the fallback. */
.app-photo { overflow: hidden; }
.app-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* Step 3 sat further right than 1 and 2: the list items are flex rows, and
   without this the number column sizes to its own content — so a wider third
   line pushed it across. A fixed column keeps every step on the same axis. */
.track-steps li { align-items: flex-start; }
.track-steps .step-num { flex: 0 0 26px; }
.track-steps li > div { flex: 1; min-width: 0; }

/* With the in-phone tab bar gone, the last (deliberately cut off) card takes
   the leftover height so the screen reads as a feed you could keep scrolling,
   rather than a short list floating above empty space. */
.app-card-cut { flex: 1; min-height: 60px; }
.app-card-cut .app-photo { height: 100%; }
.com-card-cut { flex: 1; min-height: 60px; }
.app-fab { bottom: 14px; }


/* ── showcase, round three ────────────────────────────────────────────── */

/* The blue pulsing dots read as unread badges — the app uses exactly that
   shape for notifications — so they suggested something needing attention
   rather than something worth hovering. The ring on hover is enough. */
.hot::after { display: none; }

/* The second card shows only the TOP of the next banner: a peek at what
   continues below the fold, not a second full-height card. */
.app-card-cut { flex: none; min-height: 0; }
.app-card-cut .app-photo { height: 74px; }

/* One note for the whole showcase, outside the phone, so nothing clips it. */
.sc-note {
  min-height: 3.2em;
  max-width: 330px;
  margin: 16px auto 0;
  text-align: center;
  color: var(--text-muted);
  font-size: 0.86rem;
  line-height: 1.55;
  transition: opacity 0.22s ease;
}
.sc-note.is-swapping { opacity: 0; }


/* ── showcase, round four ─────────────────────────────────────────────── */

/* Taller banners: at 74px the photo was a stripe, and the card read as text
   with a coloured edge rather than as the image-led card the app shows. */
.app-photo { height: 104px; }
.app-card-cut .app-photo { height: 104px; }

/* The pin's glyph is centred on the CIRCLE, not on the teardrop. The circle
   sits at cy=16 of a 44-tall viewBox — 36% down — so anchoring by percentage
   keeps it right whatever size the pin is drawn at. A fixed top offset was
   half a glyph low and clipped the bottom of the ring. */
.map-pin i {
  top: 36%;
  left: 50%;
  transform: translate(-50%, -50%);
}

/* The phone sits left in its column so the callout has somewhere to live. */
@media (min-width: 981px) {
  .hero-visual { justify-content: flex-start; }
  .showcase { align-items: flex-start; }
  .sc-tabs { align-self: center; margin-left: 0; }
}

/* The callout: to the RIGHT of the phone, with a leader line drawn from the
   hotspot to it. Bigger and higher-contrast than the old caption — it is the
   thing doing the explaining, so it should not read as a footnote. */
.sc-callout {
  position: absolute;
  left: calc(100% + 22px);
  top: 50%;
  width: 196px;
  transform: translateY(-50%);
  background: var(--surface-alt);
  border: 1px solid rgb(46 144 250 / 0.4);
  border-left: 3px solid var(--primary);
  border-radius: 12px;
  padding: 14px 15px;
  font-size: 0.86rem;
  line-height: 1.55;
  color: var(--text);
  box-shadow: 0 20px 45px -22px rgb(0 0 0 / 0.85);
  transition: top 0.35s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.2s ease;
  z-index: 4;
}
.sc-callout.is-swapping { opacity: 0.25; }
.sc-callout.is-idle {
  color: var(--text-faint);
  border-color: var(--border);
  border-left-color: var(--border);
  background: transparent;
  box-shadow: none;
}

/* The leader is drawn over the whole showcase, so it can start inside the
   phone and finish outside it. */
.sc-leader {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
  pointer-events: none;
  z-index: 3;
}
.sc-leader path {
  fill: none;
  stroke: var(--primary);
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
  opacity: 0;
  transition: opacity 0.25s ease;
}
.sc-leader.is-on path { opacity: 0.9; }

/* The phone is the positioning anchor for the callout. */
.showcase .phone { position: relative; }

@media (max-width: 980px) {
  .sc-callout {
    position: static;
    transform: none;
    width: min(320px, 86vw);
    margin: 18px auto 0;
    text-align: center;
  }
  .sc-leader { display: none; }
}


/* ── showcase, round five ─────────────────────────────────────────────── */

/* The three filter rows overlapped: the middle one carried a negative margin
   to tighten things up, which pulled it into its neighbours. Spacing belongs to
   the container, not to one row. */
.map-chips-types { margin: 0; }
.app-screen > .hot .map-chips + .map-chips { margin-top: 5px; }

/* The callout goes back under the phone. */
.sc-callout {
  position: absolute;
  left: 50%;
  top: calc(100% + 26px);
  transform: translateX(-50%);
  width: 300px;
  text-align: left;
}
.sc-callout-title {
  display: block;
  font-family: var(--font-head);
  font-size: 0.98rem;
  line-height: 1.3;
  color: var(--text);
  margin-bottom: 3px;
}
.sc-callout-text {
  display: block;
  font-size: 0.82rem;
  line-height: 1.5;
  color: var(--text-muted);
}
.sc-callout.is-idle .sc-callout-title { color: var(--text-muted); }
.sc-callout.is-idle .sc-callout-text { color: var(--text-faint); }

/* The showcase needs room underneath for the callout, which is absolutely
   positioned and so contributes no height of its own. */
.showcase { padding-bottom: 116px; }

@media (min-width: 981px) {
  .hero-visual { justify-content: center; }
  .showcase { align-items: center; }
}
@media (max-width: 980px) {
  .sc-callout { position: absolute; width: min(320px, 86vw); text-align: center; }
  .sc-leader { display: none; }
}

/* ── showcase: standing hint vs per-part description ────────────────────
   Two elements doing two jobs. The hint is always there, under the phone, and
   is deliberately NOT a box — a permanent panel competes with the screen it is
   describing. The description is a box to the right, and only exists while
   something is hovered, which is what makes the leader line worth drawing. */
.sc-hint {
  margin: 18px auto 0;
  max-width: 30ch;
  text-align: center;
  font-size: 0.86rem;
  color: var(--text-muted);
  transition: opacity 0.25s ease;
}
.showcase.is-tipping .sc-hint { opacity: 0; }

.sc-tip {
  position: absolute;
  z-index: 6;
  width: 250px;
  padding: 14px 16px;
  border-radius: 14px;
  background: rgba(16, 22, 34, 0.96);
  border: 1px solid rgba(46, 144, 250, 0.34);
  box-shadow: 0 18px 44px rgba(0, 0, 0, 0.5);
  backdrop-filter: blur(10px);
  animation: tipIn 0.22s cubic-bezier(0.22, 1, 0.36, 1);
}
@keyframes tipIn {
  from { opacity: 0; transform: translateX(-8px); }
  to   { opacity: 1; transform: none; }
}
.sc-tip-title {
  display: block;
  font-size: 0.95rem;
  font-weight: 700;
  line-height: 1.25;
  color: var(--text);
  margin-bottom: 5px;
}
.sc-tip-text {
  display: block;
  font-size: 0.8rem;
  line-height: 1.5;
  color: var(--text-muted);
}
@media (max-width: 900px) {
  /* No room beside the phone — the description takes the hint's place. */
  .sc-tip { position: static; width: auto; margin: 14px auto 0; animation: none; }
  .sc-leader { display: none; }
}

/* ── segmented control ─────────────────────────────────────────────────
   Replaces a <select>. A native dropdown is the one control whose styling the
   browser keeps for itself, so it looked out of place no matter what we set —
   and with two options a dropdown hides half the choice behind a click anyway. */
.segmented { border: 0; padding: 0; margin: 0 0 4px; }
.segmented legend {
  padding: 0 0 8px;
  font-size: 0.84rem;
  font-weight: 600;
  color: var(--text-muted);
}
.segmented > label { display: inline-flex; }
.segmented input {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
}
.segmented span {
  display: inline-block;
  padding: 10px 16px;
  border: 1px solid var(--border);
  background: rgba(255, 255, 255, 0.02);
  color: var(--text-muted);
  font-size: 0.88rem;
  font-weight: 600;
  cursor: pointer;
  transition: background 0.18s ease, color 0.18s ease, border-color 0.18s ease;
}
.segmented > label:first-of-type span { border-radius: 10px 0 0 10px; }
.segmented > label:last-of-type span { border-radius: 0 10px 10px 0; border-left: 0; }
.segmented span:hover { color: var(--text); background: rgba(255, 255, 255, 0.05); }
.segmented input:checked + span {
  background: var(--primary);
  border-color: var(--primary);
  color: #fff;
}
/* Keyboard users need the focus ring the hidden radio would have shown. */
.segmented input:focus-visible + span { outline: 2px solid var(--primary); outline-offset: 2px; }

/* ── university badge ──────────────────────────────────────────────────
   The blue disc now carries the institution's mark, as in the app, instead of
   being a bare dot that meant nothing. */
.app-uni, .com-uni { overflow: hidden; display: inline-flex; align-items: center; justify-content: center; }
.app-uni img, .com-uni img { width: 78%; height: 78%; object-fit: contain; }

/* Real association artwork, replacing the placeholder gradients. */
.com-logo, .com-photo { overflow: hidden; }
.com-logo img { width: 100%; height: 100%; object-fit: cover; border-radius: inherit; }
.com-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* The description falls back under the phone when there is no room beside it —
   same shape as the narrow-screen rule, but decided at runtime from the actual
   space to the right rather than from a breakpoint. */
.sc-tip.is-below {
  position: static;
  width: auto;
  max-width: 320px;
  margin: 14px auto 0;
  animation: none;
}

/* The clipped card at the foot of the communities list needs enough body to
   read as "there is more below", otherwise it looks like an empty card sitting
   in dead space. */
.com-card-cut { padding-bottom: 0; }
.com-card-cut .com-body { margin-top: 8px; }

/* ── map pins ──────────────────────────────────────────────────────────
   The glyph now lives inside the pin's own SVG, translated to the head centre,
   so there is no overlay left to position. These rules only need to stop the
   old ones applying. */
.map-pin i { display: none; }

/* ── feed hotspot ──────────────────────────────────────────────────────
   The whole feed is one highlight now; the widgets row and the top card keep
   their layout but lose their own rings. */
.app-widgets-plain { display: contents; }
.app-card-plain { display: contents; }
.hot-feed { display: block; }

/* ── who's asking ──────────────────────────────────────────────────────
   Two radios drawn by hand, so they match the rest of the form instead of
   whatever the browser felt like. The label is the same grey as the other
   field labels — it is a question, not a heading. */
.who { border: 0; padding: 0; margin: 0 0 2px; }
.who legend {
  padding: 0 0 10px;
  font-size: 0.85rem;
  font-weight: 500;
  color: var(--text-muted);
}
.app-tabitem {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  color: #5d6b80;
}
.app-tabitem svg { width: 15px; height: 15px; }
.app-tabitem i { font-style: normal; font-size: 0.4rem; font-weight: 600; letter-spacing: 0.1px; }
.app-tabitem.is-on { color: var(--primary); }

/* The screens now share the height with the tab bar. */
.phone-screen { display: flex; flex-direction: column; }
.sc-track { flex: 1; min-height: 0; }

/* The widget graphics were bleeding off two edges at once, so the heart and the
   ticket were clipped at the top as well as the right. Pulling them in and down
   keeps the "oversized graphic" look without losing the shape. */
.app-w-art { right: -2px; top: 4px; width: 34px; height: 34px; }

/* The first event card sat under the widgets row. */
.app-widgets { margin-bottom: 10px; }

/* The phone had almost no edge against the dark page. A visible rim reads as
   hardware; the inner hairline is the screen bezel. */
.phone {
  border-color: #161d2c;
  box-shadow:
    0 40px 80px rgba(0, 0, 0, 0.55),
    inset 0 0 0 2px #2b3650,
    0 0 0 1px rgba(120, 150, 200, 0.22);
}

/* The track kept `height: 100%` from its original rule, which resolves against
   the FULL phone screen — so it claimed every pixel and pushed the tab bar out
   of the phone. As a flex child it should take what's left instead. */
.sc-track { height: auto; }

/* The screens carried `height: 100%` as well, which resolves against the phone
   rather than against the track's share of it. In a flex row they stretch to
   the track anyway, so auto is both correct and self-maintaining. */
.app-screen { height: auto; }

/* ── tab bar, deterministically ────────────────────────────────────────
   Negotiating this through the flex cascade meant fighting three older rules
   that each set `height: 100%` on the track and the screens. Pinning the bar to
   the bottom of the screen and reserving the same space inside the scroll area
   is independent of all of them — and the bar is decoration, so taking it out
   of flow costs nothing. */
.phone-screen { position: relative; }
.app-tabbar {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 4;
  background: #0d1420;
  border-top: 1px solid rgba(255, 255, 255, 0.07);
}
/* Content stops above the bar rather than running under it. */
.app-screen { padding-bottom: 52px; }
/* The floating filter button clears it too. */
.app-fab { bottom: 58px; }

/* ── round five ────────────────────────────────────────────────────────── */

/* The widgets row sat too far from the first card. */
.app-widgets { margin-bottom: 6px; }

/* The cut-off community card is clipped by the tab bar, exactly like the feed's
   last card — so it must not round off at the bottom, and it is not faded: it
   is a normal card that simply runs out of screen. */
.com-card-cut {
  border-bottom-left-radius: 0;
  border-bottom-right-radius: 0;
  opacity: 1;
  border-bottom: 0;
}

/* Follower and member counts get the app's icons rather than bare numbers. */
.com-counts { display: flex; gap: 10px; align-items: center; }
.com-counts span { display: inline-flex; align-items: center; gap: 3px; }
.com-counts svg { width: 8px; height: 8px; }

/* A person, not an empty disc. */
.app-avatar { display: inline-flex; align-items: center; justify-content: center; color: #9fb0c8; }
.app-avatar svg { width: 11px; height: 11px; }

/* "Who's asking" is a field label, so it reads like the others — and the two
   options sit under it, left aligned, rather than centred in the form. */
.who legend { color: var(--text-muted); font-weight: 500; }
.who { display: block; text-align: left; }

/* ── round six ─────────────────────────────────────────────────────────── */

/* The filter chips were clipped along their top edge: the row above them has no
   bottom margin and the chips' own row had none either, so the ring and the
   rounded corners ran under it. */

/* Features sat a long way below the hero, which reserves height for the phone.
   The hero keeps its own bottom padding for the callout, so this closes the gap
   without the two colliding. */
#features { padding-top: 8px; }
.hero { padding-bottom: 30px; }

/* The contact section head is centred, and the fieldset inherited it — so the
   two options sat under the section title rather than under their own label. */
.contact-form .who,
.contact-form .who legend,
.contact-form .contact-form .who { align-self: stretch; }

/* The chips row clips horizontally on purpose — chips run off the right edge
   the way a scrollable row does — but `overflow: hidden` was shaving their top
   and bottom borders too, because the flex line box is shorter than the chips'
   border box. A little vertical padding gives the borders room inside the clip. */
.map-chips { padding: 3px 0; }

/* The showcase reserved 116px below the phone for a callout that used to be
   absolutely positioned there. The hint is in normal flow now, so that padding
   is just a hole between the hero and the features. */
.showcase { padding-bottom: 8px; }

/* ── who's asking ──────────────────────────────────────────────────────
   A two-option segmented control, left aligned under its own label. The first
   attempt at this looked wrong for a reason that had nothing to do with the
   design: it used var(--line) and var(--brand), which this stylesheet never
   defines, so its border and selected state fell back to inherited values. */
.who { border: 0; padding: 0; margin: 0 0 2px; display: block; text-align: left; }
.who legend {
  padding: 0 0 10px;
  font-size: 0.88rem;
  font-weight: 600;
  color: var(--text-muted);
}
.who-seg { display: inline-flex; }
.who-seg input { position: absolute; opacity: 0; width: 0; height: 0; }
.who-seg span {
  display: block;
  padding: 10px 16px;
  border: 1px solid var(--border);
  background: rgba(255, 255, 255, 0.02);
  color: var(--text-muted);
  font-size: 0.9rem;
  font-weight: 600;
  cursor: pointer;
  transition: background 0.16s ease, color 0.16s ease, border-color 0.16s ease;
}
.who-seg label:first-child span { border-radius: 10px 0 0 10px; }
.who-seg label:last-child span { border-radius: 0 10px 10px 0; border-left: 0; }
.who-seg span:hover { color: var(--text); background: rgba(255, 255, 255, 0.05); }
.who-seg input:checked + span { background: var(--primary); border-color: var(--primary); color: #fff; }
.who-seg input:focus-visible + span { outline: 2px solid var(--primary); outline-offset: 2px; }

/* The chips row no longer clips itself. The phone already clips at its own
   edge, which is the effect we wanted — a row that runs off the side — and
   doing it here as well was shaving the chips' borders. */
.map-chips { overflow: visible; padding: 0; }

/* ── Host tab ──────────────────────────────────────────────────────────
   The statistics screen, showing the two charts the app's host dashboard
   leads with. Everything is inline SVG so it animates without a library. */
.hs-event {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 7px 8px;
  border-radius: 9px;
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid rgba(255, 255, 255, 0.06);
}
.hs-dot { width: 6px; height: 6px; border-radius: 50%; background: #1FAE6E; flex: none; }
.hs-event b { display: block; font-size: 0.52rem; color: #eaf1ff; line-height: 1.3; }
.hs-event small { font-size: 0.44rem; color: #7f8ca3; }

.hs-card {
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid rgba(255, 255, 255, 0.06);
  border-radius: 10px;
  padding: 8px 9px;
}
.hs-title { font-size: 0.5rem; font-weight: 700; color: #eaf1ff; margin-bottom: 5px; }
.hs-line { width: 100%; height: auto; display: block; }
.hs-axis { display: flex; justify-content: space-between; font-size: 0.4rem; color: #6c7889; margin-top: 2px; }

.hs-area { fill: rgba(46, 144, 250, 0.16); stroke: none; }
.hs-stroke { fill: none; stroke: #2E90FA; stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; }
.hs-dots circle { fill: #2E90FA; stroke: #0d1420; stroke-width: 1.4; }

.hs-pierow { display: flex; align-items: center; gap: 9px; }
.hs-pie { width: 62px; height: 62px; flex: none; transform: rotate(-90deg); }
.hs-seg { fill: none; stroke-width: 5.4; }
.hs-centre, .hs-centrelbl { transform: rotate(90deg); transform-origin: 21px 21px; text-anchor: middle; }
.hs-centre { fill: #eaf1ff; font-size: 6px; font-weight: 800; }
.hs-centrelbl { fill: #7f8ca3; font-size: 2.6px; }

.hs-legend { list-style: none; margin: 0; padding: 0; display: grid; gap: 3px; min-width: 0; flex: 1; }
.hs-legend li { display: flex; align-items: center; gap: 4px; font-size: 0.42rem; color: #9fb0c8; }
.hs-legend i { width: 5px; height: 5px; border-radius: 1.5px; flex: none; }
.hs-legend b { margin-left: auto; color: #eaf1ff; font-size: 0.44rem; }

.hs-stats { display: flex; gap: 6px; }
.hs-stats div {
  flex: 1;
  text-align: center;
  padding: 6px 2px;
  border-radius: 9px;
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid rgba(255, 255, 255, 0.06);
}
.hs-stats b { display: block; font-size: 0.62rem; color: #eaf1ff; }
.hs-stats small { font-size: 0.38rem; color: #7f8ca3; }

/* ── animation ─────────────────────────────────────────────────────────
   The DEFAULT state is the finished chart, and `.is-live` replays it from
   empty. The other way round — hidden by default, revealed by the class — meant
   that if the class never landed (arriving by keyboard, or a handler that did
   not run) the charts stayed permanently blank. Failing to a drawn chart is the
   safe direction.

   The animation runs when the Host panel becomes active rather than on load:
   all four screens sit side by side on the track, so a load-time animation
   would play off-screen and you would arrive at a finished chart anyway. */
.app-screen[data-screen='host'].is-live .hs-stroke {
  animation: hsDraw 1.1s cubic-bezier(0.22, 1, 0.36, 1);
}
.app-screen[data-screen='host'].is-live .hs-area { animation: hsFade 0.9s ease; }
.app-screen[data-screen='host'].is-live .hs-dots circle { animation: hsPop 0.9s ease backwards; }
.app-screen[data-screen='host'].is-live .hs-dots circle:nth-child(1) { animation-delay: 0.15s; }
.app-screen[data-screen='host'].is-live .hs-dots circle:nth-child(2) { animation-delay: 0.32s; }
.app-screen[data-screen='host'].is-live .hs-dots circle:nth-child(3) { animation-delay: 0.49s; }
.app-screen[data-screen='host'].is-live .hs-dots circle:nth-child(4) { animation-delay: 0.66s; }
.app-screen[data-screen='host'].is-live .hs-dots circle:nth-child(5) { animation-delay: 0.83s; }
.app-screen[data-screen='host'].is-live .hs-dots circle:nth-child(6) { animation-delay: 1s; }

/* Each segment sweeps in from nothing, so the ring assembles piece by piece
   rather than rotating as one arc. No !important anywhere: an important
   declaration in a normal rule outranks an animation, which is why the first
   version of this could never move. */
.app-screen[data-screen='host'].is-live .hs-seg { animation: hsSeg 0.75s cubic-bezier(0.22, 1, 0.36, 1) backwards; }
.app-screen[data-screen='host'].is-live .hs-seg:nth-of-type(1) { animation-delay: 0.05s; }
.app-screen[data-screen='host'].is-live .hs-seg:nth-of-type(2) { animation-delay: 0.17s; }
.app-screen[data-screen='host'].is-live .hs-seg:nth-of-type(3) { animation-delay: 0.29s; }
.app-screen[data-screen='host'].is-live .hs-seg:nth-of-type(4) { animation-delay: 0.41s; }
.app-screen[data-screen='host'].is-live .hs-seg:nth-of-type(5) { animation-delay: 0.53s; }
.app-screen[data-screen='host'].is-live .hs-seg:nth-of-type(6) { animation-delay: 0.65s; }

@keyframes hsDraw { from { stroke-dasharray: 320; stroke-dashoffset: 320; } to { stroke-dasharray: 320; stroke-dashoffset: 0; } }
@keyframes hsFade { from { opacity: 0; } to { opacity: 1; } }
@keyframes hsPop { from { opacity: 0; transform: scale(0.4); } to { opacity: 1; transform: scale(1); } }
@keyframes hsSeg { from { stroke-dasharray: 0 100; } }

.hs-dots circle { transform-box: fill-box; transform-origin: center; }

@media (prefers-reduced-motion: reduce) {
  .app-screen[data-screen='host'].is-live .hs-stroke,
  .app-screen[data-screen='host'].is-live .hs-area,
  .app-screen[data-screen='host'].is-live .hs-dots circle,
  .app-screen[data-screen='host'].is-live .hs-seg { animation: none; }
}

/* The tip animates in, but a transform during the animation would move the box
   AFTER the leader line was drawn to it — so the entry animation fades only. */
@keyframes tipIn { from { opacity: 0; } to { opacity: 1; } }

/* A larger donut on the Host preview; the legend keeps its own column. */
.hs-pie { width: 82px; height: 82px; }

/* ── map pins drop in ──────────────────────────────────────────────────
   Same reasoning as the Host charts: the default state is the pin in place, and
   `.is-live` replays the drop. Hiding by default would mean an empty map for
   anyone the class never reached.

   They fall from above and settle, staggered, so the map reads as filling with
   what is on rather than arriving fully populated. */
.app-screen[data-screen='map'].is-live .map-pin {
  animation: pinDrop 0.62s cubic-bezier(0.34, 1.56, 0.64, 1) backwards;
}
.app-screen[data-screen='map'].is-live .map-pin:nth-of-type(1) { animation-delay: 0.12s; }
.app-screen[data-screen='map'].is-live .map-pin:nth-of-type(2) { animation-delay: 0.26s; }
.app-screen[data-screen='map'].is-live .map-pin:nth-of-type(3) { animation-delay: 0.4s; }

/* The shadow spreads as the pin lands, which is what sells the drop. */
.app-screen[data-screen='map'].is-live .map-pin::after {
  content: '';
  position: absolute;
  left: 50%;
  bottom: -2px;
  width: 14px;
  height: 4px;
  margin-left: -7px;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.45);
  filter: blur(2px);
  animation: pinShadow 0.62s cubic-bezier(0.34, 1.56, 0.64, 1) backwards;
  animation-delay: inherit;
}

@keyframes pinDrop {
  from { transform: translateY(-26px) scale(0.7); opacity: 0; }
  60%  { opacity: 1; }
  to   { transform: none; opacity: 1; }
}
@keyframes pinShadow {
  from { transform: scale(0.2); opacity: 0; }
  to   { transform: none; opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .app-screen[data-screen='map'].is-live .map-pin,
  .app-screen[data-screen='map'].is-live .map-pin::after { animation: none; }
}

/* The host card fills the rest of the screen, so the preview reaches the phone's
   bottom edge instead of stopping short of it. */
.app-screen[data-screen='host'] { display: flex; flex-direction: column; }
.app-screen[data-screen='host'] .hot-hostpie { flex: 1; display: flex; }
.app-screen[data-screen='host'] .hot-hostpie .hs-card { flex: 1; display: flex; flex-direction: column; }
.app-screen[data-screen='host'] .hs-pierow { flex: 1; align-items: center; }
.hs-stats { margin-top: auto; }

/* ── preview zoom ──────────────────────────────────────────────────────
   One number controls how big the phone renders.

   The scale has to live in the keyframes as well as on the element: .phone runs
   phoneFloat infinitely, and an animation's transform replaces the element's
   own — so setting `transform: scale()` alone would be discarded the moment the
   float started, and the phone would snap back to its old size.

   A scaled element still occupies its UNSCALED box in the layout, so the
   vertical margins give back the height it visually gains ((zoom - 1) x 600 / 2
   at each end). Without them the hint underneath would sit behind the phone. */
:root { --phone-zoom: 1.16; }

.phone {
  transform: scale(var(--phone-zoom));
  margin: calc((var(--phone-zoom) - 1) * 300px) 0;
}

@keyframes phoneFloat {
  0%, 100% { transform: translateY(0) rotate(0deg) scale(var(--phone-zoom)); }
  50% { transform: translateY(-12px) rotate(-0.6deg) scale(var(--phone-zoom)); }
}

@media (max-width: 900px) {
  /* No room to zoom on a narrow screen, and the phone is the whole column there. */
  :root { --phone-zoom: 1; }
}

/* The honeypot. Off-screen rather than display:none — some bots skip fields
   that are hidden outright, and the whole point is that they fill this one in.
   aria-hidden and tabindex="-1" in the markup keep it away from screen readers
   and the keyboard, so no real visitor can reach it. */
.hp {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}
