/* ==========================================================================
   Tokens
   ========================================================================== */
:root {
  --ground:   #0A0D10;
  --text:     #E4EAEE;
  --muted:    #9AA6B0;   /* text. NOT #7C8892 — see Task 5 */
  --hairline: #7C8892;   /* rules and borders only, never text */
  --signal:   #CDD9E1;

  --glass-a:    .67;
  --glass-blur: 32px;
  --photo-dim:  .43;

  --edge:     rgba(228,234,238,.18);
  --edge-top: rgba(228,234,238,.45);
  --gutter:   clamp(24px, 8.33vw, 120px);
  --r-panel:  28px;
  --r-card:   20px;
  --r-ctrl:   12px;

  /* Hero panel's inner inset — shared with .hero-logos so the funder marks
     line up with the Register button: left edge when the grid is below the
     panel, bottom edge when it's beside the panel (Task 5, organizer
     requests 2026-07-28). */
  --hero-pad-x: clamp(28px, 4vw, 56px);
  --hero-pad-y: clamp(30px, 3.2vw, 40px);
}

/* ==========================================================================
   Base / reset (carried over from the design export's <style> block)
   ========================================================================== */
html { scroll-behavior: smooth; background: var(--ground); }
body {
  margin: 0;
  background: var(--ground);
  color: var(--text);
  font-family: 'Pretendard Variable', Pretendard, -apple-system, system-ui, sans-serif;
  -webkit-font-smoothing: antialiased;
}
p { margin: 0; }
h1, h2, h3 { margin: 0; font-weight: 300; }
a { color: var(--text); text-decoration: none; }
a:hover { color: var(--text); opacity: .68; }
::selection { background: rgba(228,234,238,.22); }

/* Focus-visible outline is a system affordance on every interactive element,
   not a fourth accent role for --signal. */
a:focus-visible, .btn:focus-visible {
  outline: 2px solid var(--signal);
  outline-offset: 3px;
  border-radius: 4px;
}

/* ==========================================================================
   Primitives
   ========================================================================== */
.glass {
  background: rgba(10,13,16,var(--glass-a));
  backdrop-filter: blur(var(--glass-blur));
  -webkit-backdrop-filter: blur(var(--glass-blur));
  border: 1px solid var(--edge);
  border-top-color: var(--edge-top);
}

.btn {
  display: inline-block;
  background: var(--signal);
  color: var(--ground);
  font-weight: 600;
  border-radius: var(--r-ctrl);
}
/* Buttons brighten their fill on hover instead of dimming like plain links. */
.btn:hover { opacity: 1; filter: brightness(1.08); }

/* Standard skip-link pattern: hidden off-screen until focused.
   Secondary utility affordance — no --signal, no primary-CTA treatment. */
.skip { position: absolute; left: -9999px; }
.skip:focus {
  left: var(--gutter);
  top: 12px;
  z-index: 40;
  padding: 12px 18px;
  background: #0E1114;
  color: var(--text);
  border: 1px solid var(--edge);
  border-radius: var(--r-ctrl);
  font-size: 15px;
}

/* ==========================================================================
   Page chrome — fixed backdrop photo, positioning context
   ========================================================================== */
.page { position: relative; width: 100%; }

.backdrop { position: fixed; inset: 0; z-index: 0; overflow: hidden; }
.backdrop-pic { display: block; width: 100%; height: 100%; }
.backdrop-img { width: 100%; height: 100%; object-fit: cover; display: block; }
.backdrop-dim { position: absolute; inset: 0; background: var(--ground); opacity: var(--photo-dim); pointer-events: none; }

.content { position: relative; z-index: 1; }

/* ==========================================================================
   Nav
   ========================================================================== */
/* Sized to the hero so the reveal observer's -96px rootMargin fires just
   before the hero clears the viewport. A 0-height sentinel pinned at
   document y=0 could never intersect that shrunk root at any scroll
   position, leaving the nav permanently shown from the first callback. */
#nav-sentinel { position: absolute; top: 0; left: 0; width: 1px; height: 100vh; pointer-events: none; }

.nav {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 20;
  height: 64px;
  transform: translateY(-102%);
  opacity: 0;
  pointer-events: none;
  transition: transform .34s cubic-bezier(.2,.7,.2,1), opacity .26s ease;
  display: flex;
  align-items: center;
  padding: 0 var(--gutter);
}
.nav--shown { transform: translateY(0); opacity: 1; pointer-events: auto; }
/* A keyboard user tabbing into the nav while it's scroll-hidden must still
   see it — reveal on focus the same way it reveals on scroll. */
.nav:focus-within { transform: translateY(0); opacity: 1; pointer-events: auto; }

.nav-brand {
  font-family: 'Stack Sans Notch', sans-serif;
  font-size: 17px;
  font-weight: 400;
  letter-spacing: .01em;
  color: var(--text);
}

.nav-links {
  flex: 1;
  min-width: 0;
  display: flex;
  justify-content: center;
  gap: clamp(18px, 2.4vw, 34px);
}

.nav-link { font-size: 15px; color: var(--muted); }
.nav-link--active { color: var(--signal); }

.nav .btn { font-size: 15px; padding: 9px 20px; }

/* Floating mobile register CTA. Hidden on desktop by default — Task 3 reveals
   it under a mobile breakpoint, Task 4 wires up its shown/active behaviour. */
.mobile-cta {
  display: none;
  position: fixed;
  right: 20px;
  bottom: 20px;
  z-index: 20;
  font-size: 15px;
  padding: 12px 22px;
}

/* ==========================================================================
   Section shell — shared by about / program / speakers / venue / register / organizers
   ========================================================================== */
.section { padding: 0 var(--gutter); scroll-margin-top: 88px; margin-top: 36px; }

.panel { border-radius: var(--r-panel); padding: clamp(44px,5.5vw,76px) clamp(28px,6vw,88px); }

.panel h2, .venue h2 {
  font-family: 'Stack Sans Notch', sans-serif;
  font-size: 44px;
  font-weight: 300;
  letter-spacing: -.015em;
}

/* ==========================================================================
   Hero
   ========================================================================== */
.hero {
  min-height: 100vh;
  box-sizing: border-box;
  display: flex;
  align-items: flex-end;
  gap: clamp(28px, 4vw, 64px);
  padding: 28px var(--gutter) clamp(48px,6vh,80px);
}

.hero-panel { width: clamp(320px,58.34%,720px); border-radius: var(--r-panel); }

/* Vertical padding is symmetric now that the logo row lives outside the
   panel (Task 5) — the body's own bottom padding is the panel's bottom edge. */
.hero-body { padding: var(--hero-pad-y) var(--hero-pad-x); }

.hero-kicker { font-size: 13px; letter-spacing: .06em; color: var(--muted); }

.hero h1 {
  font-family: 'Stack Sans Notch', sans-serif;
  font-size: 76px;
  font-weight: 300;
  line-height: 1.02;
  letter-spacing: -.022em;
  margin: 18px 0 0;
}

.hero-sub {
  font-size: 28px;
  font-weight: 400;
  line-height: 1.34;
  color: var(--muted);
  margin: 16px 0 0;
  max-width: 520px;
  text-wrap: pretty;
}

.hero-meta {
  display: flex;
  margin: 30px 0 0;
  border-top: 1px solid var(--edge);
  border-bottom: 1px solid var(--edge);
}
.hero-meta > div { flex: 1; padding: 14px 20px; font-size: 15px; color: var(--text); }
.hero-meta > div:first-child { padding-left: 0; }
.hero-meta > div:last-child { padding-right: 0; }
.hero-meta > div + div { border-left: 1px solid var(--edge); }
/* Date and venue carry more weight than "Free and open to all" (organizer
   request 2026-07-31) — a step up the existing type scale plus weight,
   not a new size. ".hero-meta > div" is (0,1,1); needs matching specificity
   to actually win, not just a bare class. */
.hero-meta > div.hero-meta-prime { font-size: 20px; font-weight: 500; }

.hero .btn { margin: 28px 0 0; font-size: 17px; padding: 15px 30px; }

/* Funder marks (Task 5, organizer request 2026-07-28): moved out of the glass
   panel into the open backdrop to its right, sized up since credit to the
   funders is the point. 2x2 grid. Beside the panel (desktop) its bottom
   edge lines up with the Register button's bottom edge — anchored via the
   same --hero-pad-y the panel's own bottom padding uses, not a hardcoded
   offset, so it holds as the panel's height changes. */
.hero-logos {
  display: grid;
  grid-template-columns: repeat(2, auto);
  align-items: center;
  justify-items: start;
  gap: clamp(20px, 3vw, 36px) clamp(28px, 4vw, 52px);
  margin-bottom: var(--hero-pad-y);
}
.hero-logos > div { width: auto; height: auto; }

/* ==========================================================================
   About
   ========================================================================== */
#about .panel h2 { margin: 0 0 40px; }
#about p { font-size: 17px; line-height: 1.62; color: var(--text); text-wrap: pretty; max-width: 900px; }
#about p + p { margin-top: 16px; }

/* NRF / ASPIRE project blocks (organizer request 2026-08-13): each funder
   gets its own framed block under the intro — same frame tokens as the
   cards — with logo, project title, description, and its own link row:
   NRF to the previous editions, ASPIRE to the JST program page. */
.about-projects { display: grid; grid-template-columns: repeat(2,minmax(0,1fr)); gap: 20px; margin-top: 28px; }
.about-project {
  border-radius: var(--r-card);
  background: rgba(228,234,238,.045);
  border: 1px solid rgba(228,234,238,.18);
  border-top-color: rgba(228,234,238,.4);
  padding: 28px 30px 30px;
  display: flex;
  flex-direction: column;
}
/* align-self keeps the SVG marks at the frame's left edge — the flex
   column's default stretch hands them the full width and the SVG viewBox
   re-centers the artwork inside it. */
.about-project img.mark--nrf    { height: 40px; width: auto; align-self: flex-start; }
.about-project img.mark--aspire { height: 27px; width: auto; margin-top: 7px; align-self: flex-start; }
.about-project h3 { font-size: 20px; font-weight: 500; margin-top: 22px; }
#about .about-project p { font-size: 15px; line-height: 1.6; color: var(--muted); margin-top: 10px; }
/* Link row pinned to the frame bottom so both blocks' links align even
   when the descriptions run different lengths. Links are outlined chips
   (organizer request 2026-08-13: previous-edition links were near-invisible
   as 13px muted text) — visible tap targets, but hairline-bordered, never
   the primary-CTA --signal fill. */
.about-project-links {
  margin-top: auto;
  padding-top: 22px;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
}
.about-project-links-label { font-size: 13px; color: var(--muted); letter-spacing: .05em; text-transform: uppercase; margin-right: 4px; }
.about-project-links a {
  display: inline-block;
  font-size: 15px;
  color: var(--text);
  padding: 9px 18px;
  border: 1px solid var(--edge-top);
  border-radius: var(--r-ctrl);
}
.about-project-links a:hover { opacity: 1; background: rgba(228,234,238,.08); }

/* ==========================================================================
   Program
   ========================================================================== */
#program .panel { padding-bottom: clamp(36px,4.2vw,60px); }

.program-head { display: flex; align-items: baseline; justify-content: space-between; margin: 0 0 34px; }
.program-head h2 { margin: 0; }
.program-meta { font-size: 13px; color: var(--muted); letter-spacing: .05em; }

.program-groups { display: grid; grid-template-columns: repeat(2, minmax(0,1fr)); column-gap: 56px; }
.program-group h3 { margin: 0 0 14px; font-size: 20px; color: var(--text); }

.program-list { border-top: 1px solid var(--edge); }

.row {
  display: grid;
  grid-template-columns: 138px minmax(0,1fr);
  gap: 24px;
  align-items: baseline;
  padding: 17px 0 17px 26px;
  border-left: 1px solid transparent;
  border-bottom: 1px solid rgba(228,234,238,.09);
}
.row:last-child { border-bottom: 0; }

.row-time { font-variant-numeric: tabular-nums; font-size: 17px; color: var(--muted); white-space: nowrap; }
.row-name { font-size: 17px; color: var(--text); }

.row-time--text { color: var(--text); }

.row-kind { font-size: 13px; letter-spacing: .09em; text-transform: uppercase; color: var(--muted); }

.row--keynote, .row--talk { align-items: start; }
.row--keynote { padding-block: 30px; border-left-color: var(--signal); }
.row--talk    { padding-block: 22px; }

.row--keynote .row-time,
.row--talk .row-time { color: var(--text); }
.row--keynote .row-time { padding-top: 20px; }
.row--talk .row-time { padding-top: 18px; }
.row--keynote .row-name, .row--talk .row-name { font-size: 20px; font-weight: 500; margin: 10px 0 0; }

.title-rule {
  font-size: 15px;
  color: var(--muted);
  margin: 14px 0 12px;
  height: auto;
  width: auto;
  background: none;
}
.title-rule:empty {
  height: 1px;
  width: min(300px, 100%);
  background: color-mix(in srgb, var(--hairline) 28%, transparent);
}
.row--keynote .title-rule:empty {
  width: min(340px, 100%);
  background: color-mix(in srgb, var(--hairline) 32%, transparent);
}

.row--muted { color: var(--muted); }
.row--muted .row-time { opacity: .75; }
.row--muted .row-name { color: var(--muted); }

.row-qa { font-size: 13px; color: var(--muted); margin-top: 6px; }

/* ==========================================================================
   Speakers
   ========================================================================== */
#speakers .panel h2 { margin: 0 0 8px; }

.group-head { display: flex; align-items: baseline; gap: 14px; border-bottom: 1px solid var(--edge); }
.group-head h3 { font-size: 20px; color: var(--text); }
.group-head-note { font-size: 13px; color: var(--muted); letter-spacing: .05em; }
.group-head--keynotes { padding: 34px 0 20px; }
.group-head--talks { padding: 52px 0 18px; }

/* Keynotes (Task 14): stacked full-width rows, not a grid — each keynote's
   abstract length is independent now, so there's no equal-height constraint
   to satisfy with a grid. */
.grid-keynotes { display: flex; flex-direction: column; gap: 28px; margin-top: 28px; }
.grid-talks { display: grid; grid-template-columns: repeat(6,minmax(0,1fr)); gap: 20px; margin-top: 24px; }

.card {
  border-radius: var(--r-card);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  background: rgba(228,234,238,.045);
  border: 1px solid rgba(228,234,238,.18);
  border-top-color: rgba(228,234,238,.4);
  transition: border-color .2s ease;
}
.card-photo { aspect-ratio: 4/5; min-height: 0; overflow: hidden; }
/* Source portraits are framed very differently (tight headshot vs. half-body,
   varying headroom) — a single crop rule put faces at different scales.
   Per-person custom properties let one shared rule do per-portrait tuning. */
.card-photo img {
  width: 100%; height: 100%; display: block;
  object-fit: cover;
  object-position: var(--crop-x, 50%) var(--crop-y, 25%);
  transform: scale(var(--crop-zoom, 1));
  /* Zoom must center on the same point object-position already framed, or
     a face that isn't already at the box's visual center drifts toward an
     edge as --crop-zoom increases. */
  transform-origin: var(--crop-x, 50%) var(--crop-y, 25%);
}

/* Per-portrait face-scale tuning (Task 5, organizer request 2026-07-28) — see
   task-5-report.md for the target fraction and per-pass notes. Cards not
   listed here already read close to the target at the default crop. */
.card-photo--yiyun img  { --crop-y: 8%; --crop-zoom: 1.55; }
.card-photo--kening img { --crop-y: 12%; --crop-zoom: 2.2; }
.card-photo--jiayi img  { --crop-x: 72%; --crop-y: 24%; --crop-zoom: 1.4; }
.card-photo--hyeonbeom img { --crop-y: 16%; --crop-zoom: 1.55; }
.card-photo--seungwoo img { --crop-y: 22%; --crop-zoom: 1.5; }
.card-body { padding: 24px 26px 28px; flex: 1; display: flex; flex-direction: column; }
.card-name {
  font-family: 'Stack Sans Notch', sans-serif;
  font-size: 28px;
  font-weight: 350;
  letter-spacing: -.012em;
  line-height: 1.14;
  min-height: 36px;
  display: block;
}
/* Card-foot now stacks institution + department (Task 9) instead of a single
   centered line — column, not the old row/align-items:center. */
.card-foot { padding-top: 12px; display: flex; flex-direction: column; gap: 3px; }
.card-rule {
  font-size: 13px;
  color: var(--text);
  height: auto;
  width: auto;
  background: none;
}
.card-rule:empty { height: 1px; width: min(200px,100%); background: color-mix(in srgb, var(--hairline) 28%, transparent); }

/* Department: secondary to the institution, its own line beneath, 13px muted
   (Task 9, organizer-supplied 2026-07-31). Not every card has one — ETRI's
   card omits it entirely, no :empty scaffold needed. */
.card-dept { font-size: 13px; color: var(--muted); line-height: 1.4; }

/* Talk title in the display face, sized between the 28px name above and
   the abstract below (organizer request 2026-08-13) — name > title >
   abstract is the read order. Weight stays at 400 so it never reads
   heavier than the 20px/300 section headings (organizer request, same day). */
.card-title {
  font-family: 'Stack Sans Notch', sans-serif;
  font-size: 19px;
  font-weight: 400;
  letter-spacing: -.01em;
  line-height: 1.3;
  color: var(--text);
  margin-top: 16px;
}
/* Abstract runs the full body width (organizer request 2026-08-13) — the
   long Kening abstract needs the horizontal room to keep the row height
   down. 15px, up from 13px, for reading comfort (same request). */
.card-abstract { font-size: 15px; color: var(--muted); line-height: 1.6; margin-top: 10px; }

/* Keynote cards (organizer request 2026-08-13): photo and text are two
   separate framed boxes, not one card — the row container drops the card
   chrome and each half carries its own border/background. Photo keeps its
   fixed 220px width and 4/5 crop; the body frame grows with its text. */
.card--keynote {
  flex-direction: row;
  align-items: flex-start;
  gap: 24px;
  background: none;
  border: 0;
  border-radius: 0;
  overflow: visible;
}
.card--keynote .card-photo,
.card--keynote .card-body {
  border-radius: var(--r-card);
  background: rgba(228,234,238,.045);
  border: 1px solid rgba(228,234,238,.18);
  border-top-color: rgba(228,234,238,.4);
}
.card--keynote .card-photo { flex: 0 0 220px; width: 220px; }
.card--keynote .card-body { flex: 1; min-width: 0; padding: 28px 32px 32px; }

.card--talk {
  border-radius: 14px;
  background: rgba(228,234,238,.04);
  border-color: rgba(228,234,238,.16);
  border-top-color: rgba(228,234,238,.34);
}
.card--talk .card-photo { aspect-ratio: 3/4; }
.card--talk .card-photo--placeholder {
  background: rgba(228,234,238,.055);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 18px;
  box-sizing: border-box;
}
.card-photo-note { font-size: 13px; color: var(--muted); text-align: center; line-height: 1.5; }
.card--talk .card-body { padding: 16px 18px 20px; }
.card--talk .card-name { font-size: 20px; font-weight: 400; letter-spacing: normal; line-height: 1.2; min-height: 48px; }
/* No longer a fixed height — talk cards with a department line now run two
   rows here, ETRI's (no department) stays one; the grid row still equalizes
   every card's overall height regardless. */
.card--talk .card-foot { padding-top: 12px; }
.card--talk .card-rule:empty { width: min(120px,100%); background: color-mix(in srgb, var(--hairline) 26%, transparent); }

/* Cards lift their border on hover — a visible, non-accent affordance.
   Speaker cards are <a> now (Task 5, external speaker links) — override the
   global a:hover opacity dip so the whole card doesn't dim, and keep the
   name from reading as a plain text link. */
.card:hover, .card--talk:hover { border-color: var(--edge-top); }
.card:hover, .card--talk:hover, .card--org:hover, .committee-item:hover { opacity: 1; }

/* ==========================================================================
   Venue
   ========================================================================== */
.venue {
  border-radius: var(--r-panel);
  overflow: hidden;
  display: grid;
  grid-template-columns: minmax(0,1fr) minmax(0,1fr);
}
#venue h2 { margin: 0 0 34px; }

.venue-info { padding: clamp(44px,5vw,72px) clamp(28px,5vw,72px) clamp(44px,5vw,72px) clamp(28px,6vw,88px); }
.venue-info .place { font-size: 20px; color: var(--text); line-height: 1.4; }
.venue-info .room { font-size: 17px; color: var(--muted); margin-top: 8px; }
.venue-divider { height: 1px; background: var(--edge); margin: 28px 0; }
.venue-info .addr { font-size: 17px; color: var(--text); line-height: 1.6; }
.venue-info .addr-kr { font-size: 17px; color: var(--muted); line-height: 1.6; margin-top: 4px; }
.venue-info .label { font-size: 15px; color: var(--muted); letter-spacing: .05em; text-transform: uppercase; }
.venue-info p { font-size: 17px; color: var(--text); line-height: 1.6; margin-top: 12px; max-width: 420px; text-wrap: pretty; }
.venue-fallback { margin-top: 8px; }
.venue-fallback a { text-decoration: underline; text-underline-offset: 3px; }

.venue-map { position: relative; min-height: 520px; border-left: 1px solid var(--edge); }
.venue-map iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }

/* ==========================================================================
   Register
   ========================================================================== */
#register .panel { padding: clamp(64px,8vw,112px) clamp(28px,6vw,88px); }
#register h2 { margin: 0; }
.register-body { max-width: 640px; }
.register-body p { font-size: 17px; color: var(--muted); line-height: 1.6; margin: 26px 0 0; }
.register-body .btn { margin: 48px 0 0; font-size: 20px; padding: 20px 44px; }

/* ==========================================================================
   Organizers
   ========================================================================== */
#organizers .panel h2 { margin: 14px 0 0; }
.org-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 48px; margin: 0 0 40px; padding-bottom: 28px; }
.org-kicker { font-size: 13px; color: var(--muted); letter-spacing: .07em; }
.org-logos { display: flex; align-items: center; gap: 28px; flex: none; }
.org-logos > div, .org-logos > a { width: auto; height: auto; display: flex; align-items: center; }
/* make Lab mark links out (organizer request 2026-07-30) — keep it at full
   opacity on hover like the card links, not the plain a:hover dip. */
.org-logos > a:hover { opacity: 1; }

/* Chairs (photographed) and Committee (text-only) are two distinct groups,
   not one grid — organizer decision 2026-07-31 that student committee
   members are represented as text, not photo cards. */
/* No divider inside Organizers (organizer request 2026-07-31) — .group-head's
   base rule draws a border-bottom line for Speakers' keynote/talk headings;
   this overrides it back off without touching that shared class. Same top
   padding for both now that they sit side by side, not stacked. */
.group-head--chairs { border-bottom: 0; padding: 0 0 20px; }
/* Committee's heading padding shrinks so the shared-institution note (below)
   sits close under "Committee", with its own margin restoring the usual
   20px gap before the list — same total rhythm as Chairs, split in two. */
.group-head--committee { border-bottom: 0; padding: 0 0 6px; }
.group-head-note--committee { margin: 0 0 20px; }

/* Chairs / Committee side by side, same pattern and column-gap as the
   Program section's Morning / Afternoon split (task 10). */
.org-groups { display: grid; grid-template-columns: repeat(2,minmax(0,1fr)); column-gap: 56px; align-items: start; }

.grid-chairs { display: grid; grid-template-columns: repeat(3,minmax(0,150px)); gap: 22px; }
.org-photo { position: relative; aspect-ratio: 1/1; border-radius: 10px; overflow: hidden; border: 1px solid rgba(228,234,238,.16); transition: border-color .2s ease; }
/* Same per-portrait crop tuning as .card-photo img — Inami's source is a
   half-body landscape shot, so unzoomed his face reads far smaller than
   Andrea's tight headshot beside it. */
.org-photo img {
  width: 100%; height: 100%; object-fit: cover; display: block;
  object-position: var(--crop-x, 50%) var(--crop-y, 25%);
  transform: scale(var(--crop-zoom, 1));
  transform-origin: var(--crop-x, 50%) var(--crop-y, 25%);
}
.org-photo--inami img { --crop-x: 60%; --crop-y: 12%; --crop-zoom: 2; }
.org-photo--seungwoo img { --crop-y: 22%; --crop-zoom: 1.3; }
.org-photo .card-photo-note { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; padding: 0 10px; box-sizing: border-box; }
.card--org:hover .org-photo { border-color: var(--edge-top); }

/* Half-width column no longer fits a 3-up text grid (task 10) — two columns. */
.committee-list { display: grid; grid-template-columns: repeat(2,minmax(0,1fr)); column-gap: 32px; row-gap: 28px; }
.committee-item { display: block; }
.committee-item .org-rule:not(.org-rule--role) { margin-top: 0; }
.org-rule {
  font-size: 15px;
  line-height: 1.3;
  color: var(--text);
  height: auto;
  width: auto;
  background: none;
  margin-top: 20px;
}
.org-rule:empty { height: 1px; width: min(120px,100%); background: color-mix(in srgb, var(--hairline) 30%, transparent); }
/* Role sits tight under the name (organizer request 2026-08-13) — the old
   two-line min-height reservation on .org-rule is gone for the same reason. */
.org-rule--role { font-size: 13px; color: var(--muted); margin-top: 4px; }
.org-rule--role:empty { width: min(70px,100%); background: color-mix(in srgb, var(--hairline) 20%, transparent); }

/* ==========================================================================
   Footer
   ========================================================================== */
.footer { padding: 36px var(--gutter) 0; }
.footer-panel {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 40px;
  padding: 26px clamp(28px,4vw,56px);
  border-radius: var(--r-card) var(--r-card) 0 0;
  border-bottom: 0;
}
.footer-text { display: flex; flex-direction: column; gap: 8px; max-width: 760px; }
.footer-text div { font-size: 13px; color: var(--muted); line-height: 1.5; }
.footer-logos { display: flex; align-items: center; gap: 24px; flex: none; }
.footer-logos > div { width: 124px; height: 52px; overflow: hidden; }

/* Footer logos keep the fixed-box "contain" treatment. */
.footer-logos img { width: 100%; height: 100%; object-fit: contain; display: block; }

/* Organizers header sizes make Lab / KAIST to a common optical height —
   object-fit: contain fits each mark to its longest axis, and box-fitting
   made KAIST read larger than make Lab. Tuned by eye. */
.hero-logos img, .org-logos img { display: block; width: auto; }
.org-logos img  { height: 34px; }
.org-logos img.mark--kaist    { height: 25px; }
/* make Lab's mark is "make::lab", entirely lowercase — no capitals. The
   24px box above was sized as if it were a cap-height mark, but it was
   actually rendering ASCENDER height (k/l/b) — ratio .991 of its own box,
   vs. KAIST's .518 — so it read ~1.8x too large next to KAIST/SUSTech's
   capitals (task 10). Box reduced so the ascender lands near their cap
   height, then nudged up by eye: lowercase carries less visual weight
   than capitals at an identical rendered height. */
.org-logos img.mark--makelab  { height: 15px; }
/* Task 8 sized this box by eye and overshot: canvas ink-scan of the rendered
   marks (Task 9 review) measured SUSTech's actual wordmark-capitals height —
   the "SUSTech" letters only, excluding the flame icon and the tagline block
   to their right, which both extend well past the letters' own vertical
   extent — not the flame/tagline-inflated box. Measured cap ÷ box ratios:
   KAIST .527 (letters only, excluding the base swoosh, which is a separate
   ink band below the letters), SUSTech .485. Box height below = KAIST's
   current rendered cap height (25px × .527) ÷ SUSTech's own ratio, so the
   two now match. It also ships as dark navy/orange ink, not a white variant
   like the other two marks' files — invisible on this dark panel, so it's
   forced to a white silhouette to match. */
.org-logos img.mark--sustech  { height: 27px; filter: brightness(0) invert(1); }
.org-logos img.mark--utokyo   { height: 22px; }

/* Hero row sizes marks to a common CAP HEIGHT, not a common box height or a
   common bounding-box height. Each source packages its wordmark differently
   — ASPIRE's box is a third symbol/tagline, make Lab's box is ~100% letters
   — so equal box height put wildly different letter sizes side by side.
   Measured cap-height ÷ box-height per mark (canvas ink-scan of the "NRF"
   letters alone, the "ASPIRE" line alone, the "KAIST" line alone, and the
   full make Lab wordmark, which is all letterform so its own full height is
   its cap-height): NRF .350, ASPIRE .537, KAIST .533, make Lab .998.
   Box height below = target cap height ÷ that ratio — NRF and ASPIRE end up
   with visibly bigger boxes than make Lab/KAIST because more of their box
   is symbol/tagline, not letters. That is correct, not a bug.
   Each source file also bakes in a different amount of transparent margin
   before its artwork starts, so equal box widths still leave a ragged left
   edge — --x-nudge (a percentage of the mark's own rendered width, so it
   holds across breakpoints) corrects that per mark. */
.hero-logos img { transform: translateX(var(--x-nudge, 0)); }
.hero-logos img.mark--nrf     { height: 69px; }
.hero-logos img.mark--aspire  { height: 45px; }
.hero-logos img.mark--kaist   { height: 45px; --x-nudge: -8.1%; }
.hero-logos img.mark--makelab { height: 24px; }

/* ==========================================================================
   Responsive — tablet (<=1024px)
   ========================================================================== */
@media (max-width: 1024px) {
  :root { --gutter: 32px; }
  .grid-talks       { grid-template-columns: repeat(3, minmax(0,1fr)); }
  .hero-panel       { width: min(83.33%, 900px); }   /* 10 of 12 cols, still left-aligned */
  .venue            { grid-template-columns: 1fr; }
  .venue-map        { min-height: 380px; border-left: 0; border-top: 1px solid var(--edge); }

  /* The panel now fills most of the row's width, so there's no open backdrop
     to its right for the 2x2 funder-mark grid — drop it below instead. */
  .hero { flex-direction: column; align-items: flex-start; justify-content: flex-end; gap: 28px; }
  /* Below the panel, line the marks up with the Register button's left edge
     — same inset the panel's own content uses, not the section gutter. The
     bottom-alignment margin only makes sense beside the panel; drop it here. */
  .hero-logos { padding-left: var(--hero-pad-x); margin-bottom: 0; }
  /* Same cap-height ratios as the base rule, smaller target cap height. */
  .hero-logos img.mark--nrf     { height: 54px; }
  .hero-logos img.mark--aspire  { height: 35px; }
  .hero-logos img.mark--kaist   { height: 36px; }
  .hero-logos img.mark--makelab { height: 19px; }
}

/* ==========================================================================
   Responsive — mobile (<=767px)
   ========================================================================== */
@media (max-width: 767px) {
  :root {
    --gutter: 16px;
    --glass-blur: 16px;    /* 32px blur on a fixed backdrop janks on phones */
    --r-panel: 20px;
    --r-card: 14px;
  }
  .hero h1 { font-size: 40px; }
  .panel h2, .venue h2 { font-size: 30px; }
  .hero-sub { font-size: 20px; }
  .hero-panel { width: 100%; }
  .hero-meta  { flex-direction: column; }
  .hero-meta > div { border-left: 0; border-top: 1px solid var(--edge); padding: 12px 0; }
  .hero-meta > div + div { border-left: 0; }
  .hero-meta > div:first-child { border-top: 0; }

  .row { grid-template-columns: 1fr; gap: 6px; padding: 16px 0 16px 16px; }

  .program-groups { grid-template-columns: 1fr; row-gap: 34px; }

  .grid-talks      { grid-template-columns: repeat(2, minmax(0,1fr)); }

  /* Keynote row has no room for a 220px photo column beside text at this
     width — stack photo above content instead. */
  .card--keynote { flex-direction: column; gap: 16px; }
  .card--keynote .card-photo { width: 100%; flex: 0 0 auto; }
  .card--keynote .card-body { padding: 20px 20px 24px; }
  .about-projects  { grid-template-columns: 1fr; }
  .org-groups      { grid-template-columns: 1fr; row-gap: 34px; }
  .committee-list  { grid-template-columns: 1fr; row-gap: 20px; }
  .venue-map { min-height: 240px; }

  .section + .section { margin-top: 20px; }

  /* Nav's own Register hides in favour of .mobile-cta — exactly one CTA visible */
  .nav .btn { display: none; }

  /* No mobile menu (user decision) — links hide, wordmark alone stays as a link to #top */
  .nav-links { display: none; }
  .nav { justify-content: flex-start; }

  /* Footer: logos first, text below (funder conditions require marks prominent) */
  .footer-panel { flex-direction: column-reverse; align-items: flex-start; gap: 24px; }
  .footer-text  { max-width: none; }
  .footer-logos { flex-wrap: wrap; gap: 16px; }

  /* Organizers header: same space-between pattern as the footer, same fix */
  .org-head { flex-direction: column; align-items: flex-start; gap: 20px; }

  /* Footer's fixed-width logo boxes can't shrink in a space-between row —
     let them wrap and shrink below their desktop size once the row is
     narrower than it. */
  .footer-logos { flex-wrap: wrap; }
  .footer-logos > div { width: min(124px, 40vw); }

  /* Hero row (2x2 grid, now below the panel — see the 1024px breakpoint) —
     same cap-height ratios, smallest target cap height. */
  .hero-logos { gap: 18px 28px; }
  .hero-logos img.mark--nrf     { height: 43px; }
  .hero-logos img.mark--aspire  { height: 28px; }
  .hero-logos img.mark--kaist   { height: 28px; }
  .hero-logos img.mark--makelab { height: 15px; }

  /* Organizers marks must stay horizontal at every width (overrides the
     wrap the footer gets above) — shrink the marks instead of stacking them. */
  .org-logos { flex-wrap: nowrap; gap: 12px; }
  .org-logos img { height: 26px; }
  .org-logos img.mark--kaist   { height: 19px; }
  /* Same ascender-vs-cap-height correction as the base rule (task 10), scaled down. */
  .org-logos img.mark--makelab { height: 11px; }
  /* Same ratio-matched fix as the base rule, scaled to mobile's smaller KAIST box. */
  .org-logos img.mark--sustech { height: 21px; }
  .org-logos img.mark--utokyo  { height: 17px; }

  /* Sticky .mobile-cta needs clearance so the footer's last line isn't hidden under it */
  .content { padding-bottom: 96px; }

  /* 2-up talk cards have ~140px inner width — desktop padding/size crowd "Prof. Pengcheng An" */
  .card--talk .card-body { padding: 14px 10px 16px; }
  .card--talk .card-name { font-size: 17px; min-height: auto; }

  /* Program header: meta text collides with "Program" in a baseline row at this width */
  .program-head { flex-direction: column; align-items: flex-start; gap: 8px; }
}

/* ==========================================================================
   Responsive — sticky mobile Register bar (<=767px)
   ========================================================================== */
@media (max-width: 767px) {
  .mobile-cta {
    display: block; position: fixed; left: 16px; right: 16px; bottom: 16px; z-index: 30;
    padding: 16px; border-radius: var(--r-ctrl);
    background: var(--signal); color: var(--ground);
    font-size: 17px; font-weight: 600; text-align: center;
    opacity: 0; transform: translateY(120%);
    transition: opacity .26s ease, transform .3s cubic-bezier(.2,.7,.2,1);
    pointer-events: none;
  }
  .mobile-cta.is-shown { opacity: 1; transform: none; pointer-events: auto; }
}

/* ==========================================================================
   Reduced-preference fallbacks
   ========================================================================== */
@media (prefers-reduced-transparency: reduce) {
  .glass { background: #0E1114; backdrop-filter: none; -webkit-backdrop-filter: none; }
  .backdrop { opacity: .35; }
}
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .backdrop { position: absolute; }   /* stop the fixed-backdrop parallax */
  .nav, .mobile-cta { transition: none; }
}
