:root {
  --color-navy:      #1D3043;
  --color-bg-hero:   #EAF1F7;
  /* Ghost backdrop tint — neutral cool grey at low alpha so the fixed
     "Our Facebook Channels" plate reads slightly DARKER over the light hero
     and slightly LIGHTER over the dark sections (ghosts on any background). */
  --color-text-mute: rgba(124, 140, 161, 0.14);
  --font-title:      'DM Serif Display', serif;
  --font-body-jp:    'Noto Serif JP', serif;
  --font-body-cn:    'Noto Serif SC', serif;

  /* Single responsive gutter — anchored to the 1920 design: 240/1920 = 12.5vw.
     Scales proportionally below 1920, caps at 240px at/above it.
     1200 → 150px, 1440 → 180px, 1920 → 240px (design baseline). */
  --gutter: clamp(40px, 12.5vw, 240px);
}

/* ─── Body ──────────────────────────────────────────────── */
body {
  font-family: var(--font-body-jp);
  color: var(--color-navy);
  /* Navy base so any sliver behind the fading hero during the scroll
     transition reads dark (continuous with the dark sections), never light. */
  background: var(--color-navy);
}

[data-lang="cn"] body,
[data-lang="cn"] .section-nav,
[data-lang="cn"] .lang-switcher {
  font-family: var(--font-body-cn);
}

/* ─── Fixed right-side nav ───────────────────────────────── */
.site-nav {
  position: fixed;
  top: 120px;
  right: var(--gutter);
  z-index: 100;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0;
}

.lang-switcher {
  display: flex;
  align-items: center;
  gap: 20px;
  margin-bottom: 2rem;
}

.lang-btn {
  color: #1D3043;
  opacity: 0.45;
  padding: 0;
  font-family: inherit;
  font-size: 18px;
  transition: opacity 0.2s;
  position: relative;
  height: 2.2em;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.lang-btn:not(:last-child)::after {
  content: '|';
  position: absolute;
  left: 100%;
  top: 50%;
  transform: translateY(-50%);
  width: 20px;
  text-align: center;
  color: #1D304399;
  font-size: 14px;
  pointer-events: none;
}

.lang-btn.active {
  opacity: 1;
  color: #EAF1F7;
  background: linear-gradient(133deg, #FFBB7C 0%, #F93A00 47%, #005EBB 87%);
  border-radius: 9999px;
  border: none;
  outline: none;
  width: 2.2em;
}

.lang-btn:hover:not(.active) {
  opacity: 1;
}

.section-nav {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 20px;
}

.section-nav a {
  position: relative;
  font-size: 20px;
  color: #1D304399;
  transition: color 0.2s;
}

.section-nav a.active,
.section-nav a:hover {
  color: #1D3043;
}

/* Active-section indicator — the orange gradient blob (Vector.svg) sits to the
   LEFT of the active nav link, pointing at the current section. Decorative
   baked-gradient asset → background-image (NOT mask) to keep its colors.
   Desktop column layout only; the ≤tablet row layout is owned separately. */
.section-nav a.active::before {
  content: "";
  position: absolute;
  right: calc(100% + 10px);
  top: 50%;
  transform: translateY(-50%);
  width: 34px;
  height: 20px;
  background: url("data:image/svg+xml,%3Csvg%20width='47'%20height='27'%20viewBox='0%200%2047%2027'%20fill='none'%20xmlns='http://www.w3.org/2000/svg'%3E%20%3Cg%20filter='url(%23filter0_f_96_21)'%3E%20%3Cpath%20d='M6.27079%2010.763C7.7817%205.12418%2014.8889%203.98778%2024.4708%2010.0996C33.7718%2015.9509%2038.0611%2018.857%2040.013%2020.065C37.6399%2020.141%2027.674%2020.7016%2021.8146%2020.8844C9.50377%2021.2685%204.75989%2016.4017%206.27079%2010.763Z'%20fill='url(%23paint0_linear_96_21)'/%3E%20%3C/g%3E%20%3Cdefs%3E%20%3Cfilter%20id='filter0_f_96_21'%20x='0'%20y='0'%20width='46.0137'%20height='26.9062'%20filterUnits='userSpaceOnUse'%20color-interpolation-filters='sRGB'%3E%20%3CfeFlood%20flood-opacity='0'%20result='BackgroundImageFix'/%3E%20%3CfeBlend%20mode='normal'%20in='SourceGraphic'%20in2='BackgroundImageFix'%20result='shape'/%3E%20%3CfeGaussianBlur%20stdDeviation='3'%20result='effect1_foregroundBlur_96_21'/%3E%20%3C/filter%3E%20%3ClinearGradient%20id='paint0_linear_96_21'%20x1='44.5408'%20y1='21.0478'%20x2='2.31392'%20y2='9.7331'%20gradientUnits='userSpaceOnUse'%3E%20%3Cstop%20stop-color='%23FFBB7C'/%3E%20%3Cstop%20offset='0.576966'%20stop-color='%23F93A00'/%3E%20%3Cstop%20offset='1'%20stop-color='%23F93A00'%20stop-opacity='0'/%3E%20%3C/linearGradient%3E%20%3C/defs%3E%20%3C/svg%3E") center / contain no-repeat;
  pointer-events: none;
}

/* ─── Section 1 — Hero ───────────────────────────────────── */
#hero {
  position: relative;
  width: 100%;
  height: 100vh;
  min-height: 600px;
  background-image:
    linear-gradient(rgba(234, 241, 247, 0.72), rgba(234, 241, 247, 0.72)),
    url('../bg-hero.png');
  background-size: cover;
  background-position: center;
  overflow: hidden;
  display: flex;
  align-items: center;
}

/* Ghost backdrop — viewport-fixed, constant through the whole scroll.
   z-index 2 sits ABOVE the section backgrounds (hero photo, accounts ink z0 +
   navy z1) but BELOW the orbs (z3), grain (z4) and all foreground content (z5+),
   so the orbs and text render over the ghost while still picking up the grain. */
.ghost-plate {
  position: fixed;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  user-select: none;
  /* overflow intentionally not hidden — Channels must show fully at bottom */
}

/* "Our" + "Facebook" block — top-left, natural flow */
.ghost-upper {
  position: absolute;
  top: 0;
  left: -0.02em;
  line-height: 1;
}

.ghost-upper .ghost-line {
  display: block;
  font-family: var(--font-title);
  font-size: clamp(90px, 13vw, 190px);
  font-weight: 400;
  color: var(--color-text-mute);
  white-space: nowrap;
}

/* "Channels" — bottom-right, flush to bottom edge */
.ghost-line--bottom {
  position: absolute;
  bottom: -0.15em;
  right: 0;
  font-family: 'DM Serif Display', serif;
  font-size: 288px;
  font-weight: 400;
  line-height: 1.2;
  letter-spacing: 2px;
  color: var(--color-text-mute);
  white-space: nowrap;
}

/* Ball cluster — the cluster hugs the red ball.
   SIZE is driven by viewport HEIGHT (50vh), not just width, because the band
   between the fixed-height nav (~310px) and "Channels" (~302px) is the scarce
   dimension. So the ball is 520px only when there's ~1040–1080px of height and
   shrinks proportionally on a real (shorter) browser window — no more blow-out.
     --ball-size : tune 50vh for overall presence; 520px is the design cap.
   POSITION:
   - top: pin just below the nav (fixed-px, so a fixed top is most stable).
     The ball hangs down and is allowed to overlap "Channels" slightly.
   - right: red CENTRE aligned under the "簡" lang button. The lang row is
     fixed-px, so "簡"'s centre sits ~gutter+50px from the right. Since
     centre = right + size/2, right = gutter + 50px - size/2 (tune the 50px). */
.ball-cluster {
  --ball-size: clamp(300px, min(27.1vw, 50vh), 520px);
  position: absolute;
  top: 372px;
  right: calc(var(--gutter) + 50px - var(--ball-size) / 2);
  width: var(--ball-size);
  height: var(--ball-size);
  z-index: 3;               /* above ghost (z2), BELOW grain (z4) so the ball picks up the grain */
}

/* Gradient ball — fills the cluster (z-index 1) */
.hero-ball {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: linear-gradient(135deg, #FFBB7C 0%, #F93A00 23.5%, #005EBB 70.3%);
  border: 1px solid rgba(255, 255, 255, 0.27);
  box-shadow: 0px -4px 6px rgba(255, 255, 255, 0.60) inset;
  filter: blur(1px);
  z-index: 1;
  overflow: hidden;
}

.hero-ball::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='400'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.72' numOctaves='4' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='400' height='400' filter='url(%23n)' opacity='1'/%3E%3C/svg%3E");
  mix-blend-mode: overlay;
  opacity: 0.6;
  pointer-events: none;
}

/* Grey translucent circle — on top (z-index 2). Positioned RELATIVE to the
   red ball (cluster == red), so the overlap ratio stays fixed at every width.
   Overlap target: half the grey WIDTH and 4/5 of the grey HEIGHT sit over red.
   left = -width/2 (half-width overlap), top = -height/5 (4/5-height overlap). */
.ball-grey {
  position: absolute;
  top: -17.7%;
  left: -44.25%;
  width: 88.5%;
  height: 88.5%;
  border-radius: 50%;
  background:
    radial-gradient(circle at 5% 0%, rgba(255, 255, 255, 0.14) 0%, rgba(255, 255, 255, 0) 100%),
    rgba(15, 75, 134, 0.08);
  box-shadow: 0px 4px 6px rgba(255, 255, 255, 0.60) inset;
  border: 1px solid rgba(234, 241, 247, 0.05);
  z-index: 2;
}

/* Main title */
.hero-content {
  position: relative;
  z-index: 6;               /* above ghost (z2) and grain (z4) — title stays crisp */
  padding-left: var(--gutter);
}

.hero-brand {
  font-family: var(--font-body-jp);
  font-size: clamp(11px, 0.85vw, 14px);
  letter-spacing: 0.1em;
  opacity: 0.55;
  margin-bottom: 0.6rem;
}

.hero-title {
  font-family: var(--font-title);
  font-weight: 400;
  line-height: 1;
}

.hero-title .line-our {
  display: block;
  font-size: clamp(52px, 6.67vw, 128px);
  line-height: clamp(52px, 6.67vw, 128px);
  color: #1D3043;
}

.hero-title .line-facebook {
  display: block;
  font-size: clamp(64px, 8.33vw, 160px);
  line-height: clamp(64px, 8.33vw, 160px);
  color: rgba(29, 48, 67, 0.70);
  font-style: italic;
}

.hero-title .line-channels {
  display: block;
  font-size: clamp(52px, 6.67vw, 128px);
  line-height: clamp(52px, 6.67vw, 128px);
  color: #1D3043;
}

/* ─── Section 2 — Accounts ───────────────────────────────── */
/* Background stack (matches Frame-7), bottom → top:
   1. .accounts-bg  — ink photo, parallaxed on scroll
   2. ::before      — navy tint #1D3043 @80% (CC), flattens the ink into deep blue
   3. ::after       — grain speckle (pure-black noise PNG; opacity = strength knob,
                      raw mean alpha ~40% is too heavy applied at full strength)
   Foreground content sits above all three via z-index. */
.section-accounts {
  position: relative;
  width: 100%;
  min-height: 100vh;
  overflow: hidden;
  background: var(--color-navy);
  color: var(--color-bg-hero);
}

.accounts-bg {
  position: absolute;
  inset: -12% 0;            /* taller than the section so parallax never bares an edge */
  background-image: url('../bg-accounts.png');
  background-size: cover;
  background-position: center;
  z-index: 0;
  will-change: transform;
}

.section-accounts::before {          /* navy tint */
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: rgba(29, 48, 67, 0.80);
  z-index: 1;
}
/* grain (::after) is provided by the shared .has-grain rule below */

/* ─── Shared grain film — site-wide texture over the dark sections ─── */
/* opacity is the global strength knob. z-index 4 sits ABOVE each section's
   background + decorative circles (so the account orbs pick up the grain) but
   BELOW the text content (z5+), which stays crisp. Applied to #accounts,
   #pages and #groups via the .has-grain class. */
.has-grain {
  position: relative;
  overflow: hidden;
}
.has-grain::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 4;
  pointer-events: none;
  background-image: url('../grain.png');
  background-repeat: repeat;
  background-size: 256px 256px;     /* fine grain — small tile = finer speckle */
  opacity: 0.4;
}
/* Hero is the only LIGHT section — plain-opacity black grain reads too heavy
   there. Overlay (the blend Figma uses) is luminosity-aware, so the grain stays
   subtle over the light hero while the dark sections keep the plain grain that
   already works. */
#hero.has-grain::after {
  mix-blend-mode: overlay;
  opacity: 0.35;
}

/* Foreground copy — top-left block. #pages reuses these 1:1 (Frame-8 mirrors
   Frame-7's left text block), so the selectors are grouped to stay one source. */
.accounts-content,
.pages-content,
.groups-content {
  position: absolute;
  top: 47%;                 /* vertically centred-ish, matches Frame-7 */
  left: var(--gutter);
  transform: translateY(-50%);
  z-index: 5;
  max-width: 44%;
}
.accounts-label,
.pages-label,
.groups-label {
  font-family: var(--font-body-jp);
  font-size: clamp(11px, 0.85vw, 14px);
  letter-spacing: 0.1em;
  opacity: 0.6;
  margin-bottom: 0.9rem;
}
.accounts-title,
.pages-title,
.groups-title {
  font-family: var(--font-title);
  font-weight: 400;
  font-size: clamp(64px, 8.33vw, 160px);
  line-height: 1;
  color: var(--color-bg-hero);
  margin-bottom: 1.6rem;
}
.accounts-desc,
.pages-desc,
.groups-desc {
  font-family: var(--font-body-jp);
  font-size: clamp(13px, 1.05vw, 18px);
  line-height: 1.9;
  opacity: 0.72;
}
.accounts-desc span,
.pages-desc span,
.groups-desc span { display: block; }

/* Groups left block adds a hairline divider between title and copy (Frame-9). */
.groups-title { margin-bottom: 0; }
.groups-rule {
  display: block;
  width: 64px;
  height: 1px;
  background: var(--color-bg-hero);
  opacity: 0.5;
  margin: 1.6rem 0 1.5rem;
}
/* Accounts/Pages carry the same title→desc rule on MOBILE only (phone2/3); the
   desktop Frame-7/8 have no rule there, so hide it on desktop. */
.accounts-rule,
.pages-rule { display: none; }

/* "GROUP" category badge after each group link name (Frame-9 / phone4). Brand
   orange, tracked small caps; shown on desktop + mobile. font-size is em-relative
   so it scales with whichever link size is in effect (desktop clamp / mobile --u).
   A flex item inside the inline-flex/flex <a>, so it sits inline after the name. */
.groups-links__tag {
  font-family: var(--font-body-jp);
  font-size: 0.5em;
  font-weight: 500;
  letter-spacing: 0.12em;
  color: #F93A00;
}

/* Account links — bottom-right */
.accounts-links {
  position: absolute;
  right: var(--gutter);
  bottom: 13%;
  z-index: 5;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 1.1rem;
}
.accounts-links a {
  font-family: var(--font-title);
  font-size: clamp(16px, 1.45vw, 24px);
  color: var(--color-bg-hero);
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  opacity: 0.92;
  transition: opacity 0.2s;
}
.accounts-links a:hover { opacity: 1; }
/* Shared link arrow — one icon for every FB link (account / group / page).
   CSS mask so the arrow inherits the link color (currentColor) instead of the
   asset's baked #EAF1F7 fill. Source aspect 20:13 → keep height proportional. */
.accounts-links__icon {
  display: inline-block;
  width: 0.86em;
  height: 0.56em;
  background: currentColor;
  opacity: 0.7;
  -webkit-mask: url("data:image/svg+xml,%3Csvg%20width='20'%20height='13'%20viewBox='0%200%2020%2013'%20fill='none'%20xmlns='http://www.w3.org/2000/svg'%3E%20%3Cpath%20d='M8.25666%203.1748C8.25666%201.98117%208.7548%200.940055%209.6513%200.387456C10.5516%20-0.167152%2011.6697%20-0.117199%2012.673%200.478557V0.480309C13.3873%200.904848%2014.5165%201.54412%2015.6408%202.18145C16.7571%202.81418%2017.8716%203.44596%2018.5362%203.84054C19.4308%204.37193%2019.9923%205.25578%2019.9999%206.25647C20.0074%207.25712%2019.4598%208.16049%2018.5615%208.73547C18.0395%209.0695%2016.854%209.80184%2015.6425%2010.5505C14.4227%2011.3043%2013.1729%2012.0765%2012.508%2012.5021C11.5582%2013.1101%2010.4851%2013.161%209.6193%2012.6563C8.75833%2012.1541%208.24329%2011.1838%208.24319%2010.0634V8.96322C8.24319%208.42819%208.66025%207.99439%209.17463%207.99439C9.68878%207.99468%2010.1061%208.42837%2010.1061%208.96322V10.0634C10.1062%2010.5574%2010.3172%2010.8412%2010.5305%2010.9657C10.739%2011.087%2011.0875%2011.1356%2011.531%2010.8518C12.2114%2010.4163%2013.4771%209.63448%2014.6909%208.88438C15.9125%208.12947%2017.0805%207.4088%2017.5863%207.08514C18.0327%206.79926%2018.1401%206.47657%2018.1387%206.27223C18.1372%206.06828%2018.0255%205.76817%2017.6115%205.5224C16.967%205.13981%2015.8749%204.52031%2014.7498%203.88258C13.6328%203.24942%2012.4845%202.59874%2011.75%202.16217C11.226%201.85088%2010.8309%201.91387%2010.6013%202.05531C10.3682%202.1991%2010.1196%202.53917%2010.1196%203.1748V6.6051C10.1193%207.13975%209.70211%207.57363%209.18811%207.57393H0.931443C0.417198%207.57393%200.000225705%207.13993%200%206.6051C0%206.07008%200.417059%205.63628%200.931443%205.63628H8.25666V3.1748Z'%20fill='%23EAF1F7'/%3E%20%3C/svg%3E") center / contain no-repeat;
          mask: url("data:image/svg+xml,%3Csvg%20width='20'%20height='13'%20viewBox='0%200%2020%2013'%20fill='none'%20xmlns='http://www.w3.org/2000/svg'%3E%20%3Cpath%20d='M8.25666%203.1748C8.25666%201.98117%208.7548%200.940055%209.6513%200.387456C10.5516%20-0.167152%2011.6697%20-0.117199%2012.673%200.478557V0.480309C13.3873%200.904848%2014.5165%201.54412%2015.6408%202.18145C16.7571%202.81418%2017.8716%203.44596%2018.5362%203.84054C19.4308%204.37193%2019.9923%205.25578%2019.9999%206.25647C20.0074%207.25712%2019.4598%208.16049%2018.5615%208.73547C18.0395%209.0695%2016.854%209.80184%2015.6425%2010.5505C14.4227%2011.3043%2013.1729%2012.0765%2012.508%2012.5021C11.5582%2013.1101%2010.4851%2013.161%209.6193%2012.6563C8.75833%2012.1541%208.24329%2011.1838%208.24319%2010.0634V8.96322C8.24319%208.42819%208.66025%207.99439%209.17463%207.99439C9.68878%207.99468%2010.1061%208.42837%2010.1061%208.96322V10.0634C10.1062%2010.5574%2010.3172%2010.8412%2010.5305%2010.9657C10.739%2011.087%2011.0875%2011.1356%2011.531%2010.8518C12.2114%2010.4163%2013.4771%209.63448%2014.6909%208.88438C15.9125%208.12947%2017.0805%207.4088%2017.5863%207.08514C18.0327%206.79926%2018.1401%206.47657%2018.1387%206.27223C18.1372%206.06828%2018.0255%205.76817%2017.6115%205.5224C16.967%205.13981%2015.8749%204.52031%2014.7498%203.88258C13.6328%203.24942%2012.4845%202.59874%2011.75%202.16217C11.226%201.85088%2010.8309%201.91387%2010.6013%202.05531C10.3682%202.1991%2010.1196%202.53917%2010.1196%203.1748V6.6051C10.1193%207.13975%209.70211%207.57363%209.18811%207.57393H0.931443C0.417198%207.57393%200.000225705%207.13993%200%206.6051C0%206.07008%200.417059%205.63628%200.931443%205.63628H8.25666V3.1748Z'%20fill='%23EAF1F7'/%3E%20%3C/svg%3E") center / contain no-repeat;
}
/* Divider — 1px hairline spanning the full width of the links column */
.accounts-links__sep {
  width: 100%;
  height: 1px;
  background: var(--color-bg-hero);
}

/* ─── Floating orbs ──────────────────────────────────────── */
/* Two separate spheres (Frame-5 left + Rectangle-22 right) — they do NOT
   overlap. Four nested transform owners so the GSAP layers never fight:
   .orb-field (scroll parallax) > .orb-pos (static centring, translate -50%)
     > .orb-wrap (entrance scale) > .orb img (continuous float).
   Positions/sizes are measured from Frame-7 (centre %, diameter as vw capped
   at each SVG's native size: right 336px, left 248px). */
.orb-field {
  position: absolute;
  inset: 0;
  z-index: 3;                /* above navy tint + ghost, BELOW grain (z4) so orbs get the grain */
  pointer-events: none;
}
.orb-pos {
  position: absolute;
  transform: translate(-50%, -50%);   /* anchor by centre — never touched by GSAP */
  aspect-ratio: 1;
}
.orb-pos--right { left: 69.7%; top: 53.8%; width: clamp(210px, 17.5vw, 336px); }
.orb-pos--left  { left: 51.8%; top: 68.3%; width: clamp(155px, 12.9vw, 248px); }

.orb-wrap {
  width: 100%;
  height: 100%;
  will-change: transform;
}
.orb {
  display: block;
  width: 100%;
  height: 100%;
  will-change: transform;
}

/* ─── Morph stage — Accounts + Pages share one pinned frame ── */
/* Just a positioning context. On desktop, scroll-fx.js turns #pages into an
   absolute overlay inside this stage and pins it for the cross-fade morph.
   Without GSAP (or below 1024 / reduced-motion) #accounts and #pages remain
   normal stacked sections in flow, so the page degrades to fully-visible. */
.morph-stage { position: relative; }

/* ─── Section 3 — Pages ──────────────────────────────────── */
/* Same background recipe as #accounts but the bottom layer is the dark fluid
   gradient (bg-pages.png) instead of the ink photo. Layers bottom → top:
   1. .pages-bg  — gradient image, parallaxed; dimmed so it reads as Frame-8's
      soft dark waves rather than a stark b/w plate.
   2. ::before   — navy tint (lighter alpha than accounts so more wave shows).
   3. ::after    — shared grain via .has-grain. */
.section-pages {
  position: relative;
  width: 100%;
  min-height: 100vh;
  overflow: hidden;
  background: var(--color-navy);
  color: var(--color-bg-hero);
}
.pages-bg {
  position: absolute;
  inset: -12% 0;            /* taller than the section so parallax never bares an edge */
  background-image: url('../bg-pages.png');
  background-size: cover;
  background-position: center;
  opacity: 0.55;            /* dim the high-contrast gradient into a subtle wave */
  z-index: 0;
  will-change: transform, filter;
}
.section-pages::before {     /* navy tint — cooler/lighter than accounts */
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: rgba(29, 48, 67, 0.62);
  z-index: 1;
}

/* Orb positions for #pages — DIFFER from accounts (Frame-8): orange top-right,
   large blue sphere lower-centre. Scoped to #pages so they don't clash with the
   shared .orb-pos--right/--left used by #accounts. Width capped at native SVG
   size (orange 256, blue 368). Positions are hand-tuned final values. */
#pages .orb-pos--right { left: 64%; top: 50%; width: clamp(160px, 13.3vw, 256px); }
#pages .orb-pos--left  { left: 52%; top: 87%; width: clamp(230px, 19.2vw, 368px); }

/* Page links — bottom-right, matching accounts (5 items, per Frame-8). Reuses
   .accounts-links layout/typography (the element carries both classes), so it
   keeps accounts' bottom:13% anchor rather than sitting up near the section-nav. */
.pages-links {
  top: auto;
  bottom: 13%;
  transform: none;
}

/* ─── Section 4 — Groups ─────────────────────────────────── */
/* Same background recipe as #accounts but the bottom layer is the dark fluid
   photo (bg-groups.png). Layers bottom → top:
   1. .groups-bg  — photo, parallaxed; dimmed so it reads as Frame-9's soft dark
      waves rather than a stark photo.
   2. ::before    — navy tint #1D3043CC (rgba 0.80), flattens the photo into deep
      blue (user-specified colour).
   3. ::after     — shared grain via .has-grain. */
.section-groups {
  position: relative;
  width: 100%;
  min-height: 100vh;
  overflow: hidden;
  background: var(--color-navy);
  color: var(--color-bg-hero);
}
.groups-bg {
  position: absolute;
  inset: -12% 0;            /* taller than the section so parallax never bares an edge */
  background-image: url('../bg-groups.png');
  background-size: cover;
  background-position: center;
  opacity: 0.55;            /* dim the photo into soft dark waves */
  z-index: 0;
  will-change: transform, filter;
}
.section-groups::before {    /* navy tint #1D3043CC == rgba(29,48,67,0.80) */
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: rgba(29, 48, 67, 0.80);
  z-index: 1;
}

/* Orb positions for #groups (Frame-9): a dominant red/white sphere centre-right
   (Frame-16, native 488) and a smaller orange→blue sphere lower-LEFT (Rectangle-23,
   native 256) that slightly overlaps the copy. Scoped to #groups so they don't
   clash with the shared .orb-pos--right/--left used by #accounts. The copy (z5)
   sits above the orbs (z3), so the small sphere reads as peeking behind the text. */
#groups .orb-pos--right { left: 68%; top: 73%; width: clamp(300px, 25vw, 488px); }
#groups .orb-pos--left  { left: 27%; top: 83%; width: clamp(170px, 14vw, 256px); }

/* Group links — bottom-right (3 items, per Frame-9). Reuses .accounts-links
   layout/typography (the element carries both classes) for the bottom:13% anchor. */
.groups-links {
  top: auto;
  bottom: 13%;
  transform: none;
}

/* ─── Nav — light text over dark sections ────────────────── */
/* Toggled by the IntersectionObserver in main.js when a dark section sits
   behind the fixed nav. User requirement: text becomes #EAF1F7. */
.lang-btn { transition: opacity 0.2s, color 0.3s; }
body.nav-on-dark .lang-btn { color: var(--color-bg-hero); }
body.nav-on-dark .lang-btn:not(:last-child)::after { color: rgba(234, 241, 247, 0.6); }
body.nav-on-dark .section-nav a { color: rgba(234, 241, 247, 0.6); }
body.nav-on-dark .section-nav a.active,
body.nav-on-dark .section-nav a:hover { color: var(--color-bg-hero); }

/* ─── Mobile menu toggle + overlay — base (desktop hides them) ────────────── */
/* The hamburger/X button and the phone-menu overlay belong to the <1024 mobile
   template only. At ≥1024 the always-visible fixed .site-nav owns navigation, so
   both stay hidden here; the mobile template below switches them on. */
.menu-toggle,
.mobile-menu { display: none; }

/* ═══ Mobile template — the 390 master design (phone.svg), rebuilt in fluid
   units and scaled to a CENTERED COLUMN capped at 720 (--col). Covers everything
   below 1024; at ≥1024 the desktop morph site (above) takes over at the breakpoint.
   Scaling is "proportional, not stretched": --u is ONE 390-design pixel in scaled
   space, so any design measurement maps as calc(<designpx> * var(--u)) and the whole
   composition enlarges as a single unit (390 → 720) while text/vector stay crisp.
   PHASE 1 ships the hero (phone1) + the expandable menu (phone-menu); the dark
   sections keep their stacked desktop styling until later phases. ═══════════════ */
@media (max-width: 1023px) {
  :root {
    --col: min(100vw, 720px);          /* centered column width, capped at 720 */
    --u: calc(var(--col) / 390);       /* one 390-design pixel, scaled by WIDTH */
    /* Height-aware design pixel: --u capped so the full 390×844 design also fits
       the section HEIGHT. The morph pins each section to 100svh, so a width-only
       --u overflows (and overflow:hidden clips) the lower link rows on short /
       wide-aspect viewports. Clamping to 100svh/844 guarantees the copy + every
       link row stays inside the pinned frame at any <1024 size. */
    --su: min(var(--u), calc(100svh / 844));
  }

  /* ── Morph sections (<1024) — accounts / pages / groups foreground ───────
     Each section fills the viewport (svh, so the mobile URL bar never bares an
     edge) and the immersive pinned background morph (js/scroll-fx.js) cross-fades
     the three backgrounds — mirroring the desktop "frame stays, background
     changes". On top of that, this phase lays out the foreground per phone2/3/4:
     a top-anchored copy block (label → title → [groups rule] → 2-line desc), a
     full-width link list below it, and the decorative orbs anchored near the
     BOTTOM at z3 — below the copy/links (z5), so the link rows can overlap them
     (per the design). scroll-fx reveals pages/groups copy-then-links as they
     morph in; accounts is the landing layer and stays statically visible. */
  /* Foreground flows top→down as a flex column: copy (label → title → long rule
     → desc) then the link list, with the gap controlled by ONE margin (not a
     brittle fixed top) so it stays consistent across sections regardless of the
     groups rule's extra height. Vertical spacing + type scale use --su
     (height-aware) so the whole stack always fits the pinned 100svh frame; the
     orbs stay absolute (out of flow) anchored near the bottom. */
  .section-accounts,
  .section-pages,
  .section-groups {
    min-height: 100svh;
    display: flex;
    flex-direction: column;
    justify-content: flex-start;
    padding: calc(80 * var(--su)) calc(26 * var(--u)) calc(40 * var(--su));
    box-sizing: border-box;
  }

  /* Copy block — in normal flow now (overrides the desktop absolute 47% /
     translateY(-50%) / max-width:44% placement). */
  .accounts-content,
  .pages-content,
  .groups-content {
    position: static;
    transform: none;
    top: auto;
    left: auto;
    width: 100%;
    max-width: none;
  }
  .accounts-label,
  .pages-label,
  .groups-label { font-size: calc(17.5 * var(--su)); margin-bottom: calc(12 * var(--su)); }
  .accounts-title,
  .pages-title,
  .groups-title { font-size: calc(77.5 * var(--su)); line-height: 1.06; margin-bottom: calc(18 * var(--su)); }
  .accounts-desc,
  .pages-desc,
  .groups-desc { font-size: calc(20.5 * var(--su)); line-height: 1.55; margin-top: calc(14 * var(--su)); }
  /* Title→desc rule — all three sections (phone2/3/4), a ~120px line at the 390
     design width (120 * --su), more prominent than the desktop 64px groups rule.
     accounts-rule / pages-rule are display:none on desktop, so re-show them here. */
  .accounts-rule,
  .pages-rule,
  .groups-rule {
    display: block;
    width: calc(120 * var(--su));
    height: 1px;
    background: var(--color-bg-hero);
    opacity: 0.45;
    margin: calc(18 * var(--su)) 0 calc(16 * var(--su));
  }

  /* Link list — in flow directly below the copy, tightened to one consistent gap.
     Full-width rows: name (+ groups GROUP badge) left, shared arrow icon right,
     1px hairline above each row. Overrides the desktop bottom-right column
     (.accounts-links / .pages-links / .groups-links all carry .accounts-links;
     this rule is later in source so it wins). --su-sized so Pages' 5 rows always
     stay inside the frame (no clipping) while still overlapping the bottom orb. */
  .accounts-links {
    position: static;
    transform: none;
    top: auto;
    right: auto;
    bottom: auto;
    left: auto;
    width: 100%;
    margin-top: calc(30 * var(--su));
    display: block;
    gap: 0;
  }
  .accounts-links > li:not(.accounts-links__sep) {
    border-top: 1px solid rgba(234, 241, 247, 0.16);
  }
  .accounts-links__sep { display: none; }   /* row top-borders replace the explicit dividers */
  .accounts-links a {
    display: flex;
    align-items: center;
    gap: calc(10 * var(--su));
    width: 100%;
    font-size: calc(26.25 * var(--su));
    padding: calc(18 * var(--su)) 0;
    opacity: 1;
  }
  .accounts-links a .accounts-links__icon { margin-left: auto; }

  /* Orbs — anchored near the bottom per phone2/3/4. .orb-pos centres on its
     left/top (translate(-50%,-50%)), so these are the orb CENTRES: vertical as a
     % of the section (tracks the svh height), horizontal as a % of the 390 canvas
     mapped to the full-bleed width, diameter in --u (caps with the column). */
  #accounts .orb-pos--right { left: 62.1%; top: 68.8%; width: calc(160 * var(--u)); }
  #accounts .orb-pos--left  { left: 21.8%; top: 82.2%; width: calc(100 * var(--u)); }
  #pages .orb-pos--right    { left: 71.8%; top: 27.7%; width: calc(100 * var(--u)); }
  #pages .orb-pos--left     { left: 66.9%; top: 84.6%; width: calc(180 * var(--u)); }
  #groups .orb-pos--right   { left: 62.8%; top: 84.6%; width: calc(160 * var(--u)); }
  #groups .orb-pos--left    { left: 21.8%; top: 75.1%; width: calc(100 * var(--u)); }

  /* Desktop fixed nav → replaced by the hamburger + overlay on mobile. */
  .site-nav { display: none; }

  /* ── Mobile menu OPEN control (hamburger) — fixed top-right ── */
  .menu-toggle {
    display: block;
    position: fixed;
    top: calc(24 * var(--u));
    right: calc(24 * var(--u));
    width: calc(44 * var(--u));
    height: calc(44 * var(--u));
    z-index: 200;                       /* above the overlay (z150) */
    padding: 0;
    border: 0;
    background: none;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
  }
  .menu-toggle svg { display: block; width: 100%; height: 100%; }
  /* Hidden while the overlay is open — the dialog's own .mobile-menu__close (X)
     takes over, so the modal's close control lives inside the dialog subtree. */
  body.menu-open .menu-toggle { display: none; }

  /* ── Mobile menu CLOSE control (X) — top-right, INSIDE the dialog ── */
  /* position:fixed (not absolute): .mobile-menu is the scroll container on short
     viewports, so an absolute X would scroll away. Fixed pins it to the overlay's
     top-right corner regardless of scroll. The open overlay has transform:none, so
     fixed resolves against the viewport (== the inset:0 overlay). Stays in the
     dialog DOM subtree, so the focus trap / aria-modal scoping is unaffected. */
  .mobile-menu__close {
    position: fixed;
    top: calc(24 * var(--u));
    right: calc(24 * var(--u));
    width: calc(44 * var(--u));
    height: calc(44 * var(--u));
    z-index: 7;                         /* above inner content (z6), orb (z3), grain (z4) */
    padding: 0;
    border: 0;
    background: none;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
  }
  .mobile-menu__close svg { display: block; width: 100%; height: 100%; }

  /* lock background scroll while the overlay is open */
  body.menu-open { overflow: hidden; }

  /* ── Hero (phone1) ─────────────────────────────────────────── */
  #hero {
    height: 100svh;
    min-height: 0;
    align-items: stretch;
  }

  /* Ghost watermark — full-viewport, same form as desktop: "Our Facebook" hugs
     the LEFT edge, "Channels" hugs the RIGHT edge. Sized in pure vw (NOT --u):
     the ghost spans the real viewport edges, so it must track the actual viewport
     width — not the 720-capped column (which froze the size above 720 and made the
     text read RELATIVELY LONGER as the window shrank toward the cap). vw values are
     tuned so the longest 8-char word (Facebook / Channels, ~4.2 width:font ratio)
     stays fully on-screen — no clipping — at every width. */
  .ghost-upper .ghost-line { font-size: 18.5vw; }
  .ghost-line--bottom { font-size: 22vw; }

  /* Ball cluster — overlapping grey + gradient circles, upper-middle (phone1).
     The cluster box == the red ball; the grey ball sits to its upper-LEFT (base
     %s). We want the MIDPOINT between the two balls' centres on the horizontal
     centre, not the red ball alone. Grey centre lands on the cluster's left edge
     (left -44.25% + width 88.5% ⇒ centre 0); red centre is at +S/2. Their midpoint
     is S/4 from the cluster's left edge, so from left:50% shift the box left by
     S/4 to bring that midpoint to centre. */
  .ball-cluster {
    --ball-size: calc(196 * var(--u));
    /* Horizontal centring via LEFT, not transform: the .ball float animation
       (animations.css) owns this element's `transform` (translateY), so a static
       `transform: translateX(...)` here is overridden by the animation and silently
       drops — leaving the grey centre on centre. Positioning with `left` keeps the
       float (transform) and the horizontal anchor independent.
       box_left = 50% − S/4 ⇒ grey centre at 50%−S/4, red centre at 50%+S/4 ⇒ the
       MIDPOINT of the two ball centres lands exactly on 50%. */
    left: calc(50% - var(--ball-size) * 0.25);
    right: auto;
    /* ball CENTRE at 2/5 of the hero height (#hero is 100svh, position:relative,
       so % here is of the hero box). top = 2/5 − halfBall. Vertical float lives in
       the .ball transform; top stays independent. */
    top: calc(40% - var(--ball-size) * 0.5);
    width: var(--ball-size);
    height: var(--ball-size);
  }

  /* Headline block — centred BOTH axes in the viewport (always, at any height
     below 1024). The BLOCK is centred (shrink-wrap to its widest line via
     max-content + translate(-50%,-50%)), but the text inside stays LEFT-aligned. */
  .hero-content {
    position: absolute;
    top: 60%;                 /* block CENTRE at 3/5 of the hero height */
    left: 50%;
    transform: translate(-50%, -50%);
    width: max-content;
    max-width: calc(var(--col) - 52 * var(--u));
    padding: 0;
    text-align: left;
  }
  .hero-brand {
    font-size: calc(13 * var(--u));
    opacity: 0.6;
    margin-bottom: calc(14 * var(--u));
  }
  .hero-title .line-our,
  .hero-title .line-channels {
    font-size: calc(40 * var(--u));
    line-height: 1.18;
  }
  .hero-title .line-facebook {
    font-size: calc(46 * var(--u));
    line-height: 1.18;
  }

  /* ── Menu overlay (phone-menu) — navy #1D3043 + grain ──────── */
  .mobile-menu {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 150;
    background: var(--color-navy);
    /* --u scales with WIDTH only, so on short (landscape) <1024 viewports the
       width-derived row/lang sizes can exceed the fixed overlay height. Allow
       vertical scroll (x stays hidden so the corner orb still clips) so the
       content is always reachable instead of cropped with no scroll path. */
    overflow: hidden auto;
    -webkit-overflow-scrolling: touch;
    /* hidden until toggled: fade + lift, non-interactive while closed */
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transform: translateY(calc(-12 * var(--u)));
    transition: opacity 0.28s ease, transform 0.28s ease, visibility 0s linear 0.28s;
  }
  body.menu-open .mobile-menu {
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    transform: none;
    transition: opacity 0.28s ease, transform 0.28s ease, visibility 0s;
  }
  /* shared grain sits above the navy, below the content (z6) */
  .mobile-menu.has-grain::after { opacity: 0.4; }

  /* Gradient circle clipped by the top-right corner (Rectangle-23). Same brand
     gradient as the hero ball; oversized and pushed off the corner so only the
     lower-left arc shows, with the X (menu-toggle) sitting over it. */
  .mobile-menu__orb {
    position: absolute;
    top: calc(-64 * var(--u));
    right: calc(-36 * var(--u));
    width: calc(200 * var(--u));
    height: calc(200 * var(--u));
    border-radius: 50%;
    background: linear-gradient(204deg, #FFBB7C 4%, #F93A00 46%, #005EBB 84%);
    border: 1px solid rgba(255, 255, 255, 0.27);
    box-shadow: 0 -4px 6px rgba(255, 255, 255, 0.4) inset;
    filter: blur(1px);
    z-index: 3;                         /* below grain (z4) so the orb picks it up */
  }

  /* Content column — centered, capped at --col. min-height (not height) + the
     nav's auto block-margins below let it grow taller than the overlay and scroll
     on short viewports instead of clipping. box-sizing so the vertical padding is
     included in the min-height. */
  .mobile-menu__inner {
    position: relative;
    z-index: 6;
    width: var(--col);
    min-height: 100%;
    box-sizing: border-box;
    margin: 0 auto;
    padding: calc(72 * var(--u)) calc(26 * var(--u));
    display: flex;
    flex-direction: column;
  }

  /* Section list — big serif rows with a trailing arrow + hairline dividers.
     `margin: auto 0` absorbs the free vertical space: when the viewport is tall the
     nav sits centred with the lang row pushed to the bottom; when it's short the
     auto margins collapse to 0, nav and lang stack from the top and the overlay
     scrolls — no unreachable content. */
  .mobile-menu__nav { display: flex; flex-direction: column; margin: auto 0; }
  .mobile-menu__nav a {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: calc(28 * var(--u)) 0;
    font-family: var(--font-title);
    font-size: calc(34 * var(--u));
    color: var(--color-bg-hero);
    border-bottom: 1px solid rgba(234, 241, 247, 0.16);
  }
  .mobile-menu__nav a:first-child { border-top: 1px solid rgba(234, 241, 247, 0.16); }
  .mobile-menu__nav .accounts-links__icon {
    width: calc(22 * var(--u));
    height: calc(14 * var(--u));
    opacity: 0.85;
  }

  /* Language switcher — sits at the bottom of the column (pushed there by the
     nav's auto margins on tall viewports; flows right after the nav when short).
     In normal flow (not absolute) so it can never overlap the nav when the
     overlay scrolls. flex-shrink:0 keeps the row from being squeezed. */
  .mobile-menu__lang {
    justify-content: center;
    gap: calc(20 * var(--u));
    margin: calc(40 * var(--u)) 0 0;
    flex-shrink: 0;
  }
  .mobile-menu .lang-btn { color: rgba(234, 241, 247, 0.6); font-size: calc(18 * var(--u)); }
  .mobile-menu .lang-btn:not(:last-child)::after { color: rgba(234, 241, 247, 0.6); }
  .mobile-menu .lang-btn.active { color: var(--color-bg-hero); }
}
