/* ====== CSS VARIABLES ====== */
/* ══════════════════════════════════════════════════════════════
   [hidden] MEANS HIDDEN — the one rule, instead of a patch per element
   ══════════════════════════════════════════════════════════════
   The `hidden` attribute is only `display: none` in the browser's OWN
   stylesheet, which every authored `display` beats. So an element set to
   `display: flex` here stays on screen while `el.hidden` reports `true` --
   and that is unfalsifiable from the console, because the property IS
   true. Only measuring the rectangle shows it.

   This has now cost three separate bugs in this panel:
     - .mail-alert   — a 30×105 empty amber box with a stray button on a
                       perfectly healthy list (v92, §9.70)
     - .filter-badge — a filter count that would not go away
     - #qrcIssue     — «أصدِر الرموز الناقصة» offering itself on the
                       committee tabs, where it does nothing (v95)

   Each was patched one element at a time. This is the general fix, and it
   is also what the HTML spec recommends authors write. `!important`
   is the point of it: the whole failure mode is an authored rule winning
   against the attribute.

   If an element ever needs to be laid out while carrying `hidden`, that
   element is using the wrong tool -- use a class.
   ══════════════════════════════════════════════════════════════ */
[hidden] { display: none !important; }


:root {
  --primary: #002366;
  --primary-light: #003399;
  --primary-dark: #001a4d;
  --gold: #D4AF37;
  --gold-light: #e8c84a;
  --gold-dark: #b8942f;
  --white: #ffffff;
  --off-white: #F4F7F9;
  --gray-50: #f8fafb;
  --gray-100: #f0f3f5;
  --gray-200: #e2e6ea;
  --gray-300: #c5cdd4;
  --gray-400: #9aa4af;
  --gray-500: #6b7a88;
  --gray-600: #4a5568;
  --gray-700: #2d3748;
  --gray-800: #1a202c;
  --gray-900: #0d1117;
  --shadow-sm: 0 1px 3px rgba(0,0,0,0.08);
  --shadow-md: 0 4px 12px rgba(0,0,0,0.1);
  --shadow-lg: 0 8px 30px rgba(0,0,0,0.12);
  --shadow-xl: 0 20px 60px rgba(0,0,0,0.15);
  --radius-sm: 8px;
  --radius-md: 12px;
  --radius-lg: 20px;
  --radius-xl: 28px;
  --transition: 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  /* Tajawal: clean geometric Arabic. Best modern body face for screen.
     Cairo: same designer, same proportions — works as a display/heading
     face without feeling decorative. IBM Plex Mono: all numerals share
     one instrument-readout voice. --font-en kept as an alias. */
  --font-ar: 'Tajawal', system-ui, sans-serif;
  --font-display: 'Cairo', 'Tajawal', system-ui, sans-serif;
  --font-num: 'IBM Plex Mono', ui-monospace, monospace;
  --font-en: 'IBM Plex Mono', ui-monospace, monospace;
  --navbar-height: 82px;
}

/* ====== RESET & BASE ====== */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { scroll-behavior: smooth; }
body {
  font-family: var(--font-ar);
  background: var(--white);
  color: var(--gray-800);
  line-height: 1.7;
  font-size: 16px;
  overflow-x: hidden;
  transition: background var(--transition), color var(--transition);
  padding-top: 0; /* set by syncBodyPadding() in JS after navbar renders */
  /* Sticky footer: sections are swapped by JS and some (e.g. a 'wip'
     section, or FAQ with few entries) are far shorter than the viewport.
     Without this the footer floated up mid-screen with dead white space
     beneath it. Column flex + margin-top:auto on .footer pins it to the
     bottom on short pages while still flowing normally on long ones. */
  min-height: 100vh;
  min-height: 100svh;   /* avoids the mobile URL-bar jump */
  display: flex;
  flex-direction: column;
}
body > main { flex: 1 0 auto; }
body > .footer { flex-shrink: 0; }

a { text-decoration: none; color: inherit; }
ul { list-style: none; }
img { max-width: 100%; display: block; }
button { cursor: pointer; border: none; background: none; font-family: inherit; }
input, select, textarea { font-family: inherit; font-size: 1rem; }

.container { max-width: 1200px; margin: 0 auto; padding: 0 24px; }
/* Block padding ONLY -- never the `padding: 56px 0` shorthand.
 *
 * This is the bug behind "text is glued to the screen edge on mobile", and
 * it hid for a long time. Almost every section wraps its content in
 * `class="container section-pad"`. The shorthand set padding-left and
 * padding-right to 0, and because .section-pad has the same specificity as
 * .container but is declared AFTER it, it silently won -- so .container's
 * 24px side padding never applied to any section on any screen size.
 *
 * On desktop nothing looked wrong: max-width:1200px leaves plenty of empty
 * space beside the text anyway, so the missing padding was invisible. On a
 * 375px phone the container is full-bleed, so the text ran edge to edge.
 * An earlier attempt to fix this raised .hero-content's padding, which is a
 * different element entirely -- the sections were never touched, which is
 * why the problem "was fixed" and yet persisted. */
.section-pad { padding-top: 56px; padding-bottom: 56px; }
.section { display: none; }
.section.active { display: block; }

/* ====== SCROLLBAR ====== */
::-webkit-scrollbar { width: 8px; }
::-webkit-scrollbar-track { background: var(--gray-100); }
::-webkit-scrollbar-thumb { background: var(--gray-300); border-radius: 4px; }
::-webkit-scrollbar-thumb:hover { background: var(--gray-400); }

/* ====== NAVBAR ====== */
/* ====== NAVBAR — reference site approach ======
 * Links wrap naturally onto a new line (flex-wrap:wrap) instead of
 * using a hamburger menu. This is simpler, needs zero JS, and works
 * perfectly on every screen size. The logo sits on the left, links
 * on the right; on narrow screens the links wrap below the logo.
 * --navbar-height is set to 'auto' — body padding-top uses a fixed
 * safe value that covers the tallest case (logo row + link row). */
/* ====== NAVBAR ====== */
.navbar {
  position: fixed;
  top: 0; left: 0; right: 0;
  background: var(--primary-dark, #001a33);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  z-index: 1000;
  box-shadow: 0 2px 20px rgba(0,0,0,0.2);
}

.nav-container {
  max-width: 1300px;
  margin: 0 auto;
  padding: 8px 20px;
  min-height: 64px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  /* nowrap, NOT wrap: with wrap, .nav-links jumped to its own second row
     instead of shrinking, which made the bar ~108px tall and also fed
     setupNavOverflow() a full-width measurement, so it under-filled the
     "المزيد" menu. Forcing one row lets .nav-links shrink (it has
     min-width:0) and the overflow split then reflects reality. */
  flex-wrap: nowrap;
}

/* ── Text brand (3 levels) ── */
/* The brand is the ONLY block in the bar allowed to shrink (v38).
 *
 * It was `flex-shrink: 0` with `white-space: nowrap` on all three of its
 * lines, so it claimed its full natural width no matter how narrow the
 * screen got. .nav-actions is deliberately `flex: none` (a control that
 * shrinks is a control you mis-tap) and .nav-links is either content-sized
 * or replaced by the hamburger — which left nothing able to give. On a
 * 390px phone in Arabic the row overflowed and pushed the LAST item in
 * .nav-actions past the edge: measured at left:-41px, i.e. the hamburger
 * was entirely off-screen and the whole menu unreachable, with no visual
 * hint that anything was missing.
 *
 * Now the brand yields first and its lines ellipsize. A shortened title is
 * a cosmetic loss; an unreachable menu is a broken site. */
.nav-brand {
  display: flex;
  align-items: center;
  cursor: pointer;
  flex: 0 1 auto;
  min-width: 0;
  text-decoration: none;
}
.nav-text-brand {
  display: flex;
  flex-direction: column;
  gap: 1px;
  line-height: 1.2;
  min-width: 0;
}
.nav-brand-ar, .nav-brand-en, .nav-brand-code {
  overflow: hidden;
  text-overflow: ellipsis;
}
.nav-brand-ar {
  font-size: 0.88rem;
  font-weight: 700;
  color: var(--gold, #D4AF37);
  white-space: nowrap;
  font-family: var(--font-ar, 'Tajawal', sans-serif);
}
.nav-brand-en {
  font-size: 0.65rem;
  color: rgba(255,255,255,0.7);
  white-space: nowrap;
  font-family: var(--font-en, 'IBM Plex Mono', monospace);
  letter-spacing: 0.02em;
}
.nav-brand-code {
  font-size: 0.6rem;
  color: rgba(255,255,255,0.4);
  white-space: nowrap;
  font-family: var(--font-en, 'IBM Plex Mono', monospace);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

/* ── Desktop links ── */
/* BUG FIX: this was flex-wrap:nowrap while nav-container had a fixed
   height:64px and no overflow handling — on any desktop width narrower
   than roughly 1500-1800px (i.e. most real laptops, including the most
   common 1366px width), the 15 nav links silently overflowed the fixed-
   height box and were clipped by the global `overflow-x:hidden` on
   html/body, with no scroll and no "more" menu. Now wraps onto as many
   rows as the actual viewport needs; nav-container's height:64px was
   changed to min-height so it can grow to fit the wrapped rows. */
/* SUPERSEDES the wrap fix described above. Wrapping did stop the links
   being clipped, but it made the bar grow to 2-3 rows on exactly the
   laptop widths most visitors use, which looked bad. Now the bar is a
   single non-wrapping row and setupNavOverflow() moves whatever does not
   fit into the "المزيد" menu -- one tidy row at every width, with every
   link still reachable. */
.nav-links {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 2px;
  flex-wrap: nowrap;
  flex: 1 1 auto;
  min-width: 0;
}

/* ── Grouped menus (v35) ──────────────────────────────────────────
 * Replaces the measured "المزيد" overflow menu. Six group headers plus a
 * CTA fit one row at every desktop width, so nothing is ever moved or
 * clipped and there is no JS layout pass to get wrong. */
.nav-group { position: relative; flex: none; }
.nav-group-btn { gap: 6px; cursor: pointer; background: none; border: none; font: inherit; }
.nav-caret { font-size: 0.6rem; opacity: 0.75; transition: transform 0.2s ease; }
.nav-group.open .nav-caret { transform: rotate(180deg); }
.nav-group.open > .nav-group-btn,
.nav-group-btn.active {
  color: var(--gold, #D4AF37);
  background: rgba(212,175,55,0.12);
}
.nav-submenu {
  position: absolute;
  top: calc(100% + 10px);
  inset-inline-start: 50%;
  transform: translateX(50%) translateY(-6px);
  min-width: 240px;
  background: rgba(2,10,35,0.98);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  border: 1px solid rgba(255,255,255,0.12);
  border-radius: 14px;
  box-shadow: 0 20px 48px rgba(0,0,0,0.48);
  padding: 7px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  /* Animated with opacity/visibility rather than display:none, which
   * cannot transition. */
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.18s ease, transform 0.18s ease, visibility 0.18s;
  z-index: 1200;
}
/* LTR mirror: inset-inline-start:50% flips side, so the translate must
 * flip with it or the panel hangs off the wrong edge in English. */
html[dir="ltr"] .nav-submenu { transform: translateX(-50%) translateY(-6px); }
.nav-group.open .nav-submenu {
  opacity: 1;
  visibility: visible;
  transform: translateX(50%) translateY(0);
}
html[dir="ltr"] .nav-group.open .nav-submenu { transform: translateX(-50%) translateY(0); }
/* An invisible bridge across the gap between button and panel, so the
 * pointer can travel down without the menu closing under it. */
.nav-submenu::before {
  content: '';
  position: absolute;
  inset-inline: 0;
  top: -12px;
  height: 12px;
}
.nav-sub {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  padding: 10px 12px;
  border-radius: 9px;
  color: rgba(255,255,255,0.82);
  text-decoration: none;
  font-size: 0.87rem;
  font-weight: 500;
  white-space: nowrap;
  transition: background 0.16s ease, color 0.16s ease;
}
.nav-sub i { width: 18px; text-align: center; font-size: 0.85rem; opacity: 0.85; }
.nav-sub:hover, .nav-sub:focus-visible {
  background: rgba(255,255,255,0.09);
  color: #fff;
  outline: none;
}
.nav-sub.active { background: rgba(212,175,55,0.16); color: var(--gold, #D4AF37); }

/* ── "سجّل الآن" call to action (v38) ──
 * GOLD, last in the row, and always visible.
 *
 * Three separate decisions, each one a correction of an earlier attempt:
 *
 * 1. OUTSIDE #navLinks (v36). That bar collapses into the hamburger below
 *    the nav breakpoint, so a CTA inside it was buried in a menu on an
 *    ordinary laptop. A call to action that can be hidden is not one.
 *
 * 2. LAST in the DOM (v38). v36 put .nav-actions between the brand and the
 *    links, which left the CTA stranded mid-bar and, in English, colliding
 *    with the menu. The row now ends with it, where the eye finishes.
 *
 * 3. GOLD, with NAVY text (v38). v36 used blue-on-white because gold had
 *    failed once before — but that failure was about position, not colour.
 *    Gold on the navy bar is the highest-contrast pairing in the palette,
 *    and navy text on gold beats white text on gold by a wide margin
 *    (white on #D4AF37 is roughly 2:1 — below any accessibility floor;
 *    navy on gold is ~8:1). It is also the conference's own accent, so the
 *    one button everything funnels toward is the one thing wearing it. */
.nav-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  /* flex: none — never absorb leftover space and never be compressed. The
     brand and the links negotiate the remaining width between them; these
     three controls keep their intrinsic size at every viewport. */
  flex: none;
}
.nav-cta {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  background: linear-gradient(135deg, #E3C25A 0%, #D4AF37 55%, #BE9A28 100%);
  color: #002366 !important;
  font-weight: 800;
  font-size: 0.88rem;
  letter-spacing: 0.01em;
  text-decoration: none;
  border-radius: 40px;
  padding: 9px 20px;
  white-space: nowrap;
  border: 1px solid rgba(255,255,255,0.34);
  box-shadow: 0 4px 14px rgba(212,175,55,0.38);
  transition: filter 0.16s ease, transform 0.16s ease, box-shadow 0.16s ease;
}
.nav-cta:hover, .nav-cta:focus-visible {
  filter: brightness(1.08);
  transform: translateY(-1px);
  box-shadow: 0 7px 20px rgba(212,175,55,0.55);
  outline: none;
}
.nav-cta:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
.nav-cta:active { transform: translateY(0); }
.nav-cta.active { filter: brightness(1.08); }
.nav-cta i { font-size: 0.82rem; }

/* ── Why there is no `overflow: hidden` safety net here ──
 * The obvious guard against the overlap is `.nav-links { overflow: hidden }`
 * so stray labels are clipped instead of painted over the CTA. It was tried
 * and it is WRONG: .nav-group is position:relative and .nav-submenu is
 * position:absolute inside it, so .nav-links is their containing block —
 * clipping it clips all six dropdown menus. Measured: the submenu's bottom
 * edge sits at 206px while the clipping box ends at 51px, i.e. roughly
 * three-quarters of every menu disappears.
 *
 * So the ONLY correct fix for the collision is the breakpoint below being
 * genuinely large enough. There is no seatbelt; the numbers have to be
 * right, which is why they are measured rather than estimated. */


/* ── Mobile menu grouping ── */
.nav-drop-heading {
  padding: 12px 14px 5px;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  color: var(--gold, #D4AF37);
  opacity: 0.9;
}
.nav-dropdown .nav-drop-sub { padding-inline-start: 26px; }
/* The CTA is no longer copied into the mobile menu: it sits permanently in
 * .nav-actions, visible at every width, so a duplicate inside the drawer
 * would just be the same button twice. */
.nav-btn {
  color: rgba(255,255,255,0.8);
  padding: 7px 10px;
  border-radius: 7px;
  font-size: 0.85rem;
  font-weight: 500;
  white-space: nowrap;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  transition: all 0.18s ease;
  border: none;
  background: transparent;
  cursor: pointer;
  text-decoration: none;
  font-family: var(--font-ar, 'Tajawal', sans-serif);
}
.nav-btn i { font-size: 0.78rem; }
/* Leading icons OFF on the top-level bar at every width (v38). With them,
 * the row needs 1403px in English and 1308px in Arabic, while
 * .nav-container is capped at 1300px — so it could not fit on any screen,
 * and the items slid under their neighbours instead of wrapping. Without
 * them the row needs 1274px and fits. The caret is exempt: it is the only
 * signal that an item opens a menu. Submenu items keep their icons — there
 * is room inside a dropdown. */
.nav-btn > i:not(.nav-caret) { display: none; }
.nav-btn:hover { color: #fff; background: rgba(255,255,255,0.1); }
.nav-btn.active {
  color: var(--gold, #D4AF37);
  background: rgba(212,175,55,0.12);
}

/* ── Hamburger button (hidden on desktop) ── */
.nav-toggle {
  display: none;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  width: 40px;
  height: 40px;
  padding: 8px;
  background: rgba(255,255,255,0.08);
  border: 1px solid rgba(255,255,255,0.15);
  border-radius: 8px;
  cursor: pointer;
  flex-shrink: 0;
}
.nav-toggle span {
  display: block;
  width: 100%;
  height: 2px;
  background: var(--gold, #D4AF37);
  border-radius: 2px;
  transition: transform 0.25s ease, opacity 0.25s ease;
}
/* X state when open */
.nav-toggle.open span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.nav-toggle.open span:nth-child(2) { opacity: 0; transform: scaleX(0); }
.nav-toggle.open span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

/* ── Mobile dropdown (hidden by default) ── */
.nav-dropdown {
  display: none;
  position: absolute;
  top: 100%;
  right: 0; left: 0;
  background: rgba(2,10,35,0.97);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  border-top: 2px solid rgba(212,175,55,0.3);
  box-shadow: 0 16px 48px rgba(0,0,0,0.5);
  padding: 6px 0 10px;
  z-index: 999;
}
.nav-dropdown.open { display: block; }
.nav-dropdown .nav-btn {
  width: 100%;
  padding: 13px 24px;
  border-radius: 0;
  font-size: 1rem;
  border-bottom: 1px solid rgba(255,255,255,0.06);
  /* RTL: text and icon aligned to the right */
  justify-content: flex-end;
  text-align: right;
  direction: rtl;
}
.nav-dropdown .nav-btn:last-child { border-bottom: none; }
.nav-dropdown .nav-btn:hover,
.nav-dropdown .nav-btn.active {
  background: rgba(212,175,55,0.1);
  color: var(--gold, #D4AF37);
  border-right: 3px solid var(--gold, #D4AF37);
  padding-right: 21px;
}

/* ====== HERO ====== */
.hero {
  position: relative;
  min-height: 85vh;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  background: linear-gradient(135deg, #001a33 0%, #002366 50%, #003399 100%);
}

/* ── Hero background image (diag23 — final direction, chosen by the
 * client after two failed attempts) ──
 * diag21 (blurred backdrop behind a contain photo) and diag22 (solid
 * navy behind a contain photo) both tried to show the ENTIRE photo
 * with zero cropping inside a box whose shape changes on every screen
 * size. That is not really a CSS bug to fix -- it's an unstable goal:
 * "show 100% of an arbitrary photo, in an arbitrary box shape" cannot
 * look good on every screen at once, because the box and the photo
 * almost never share an aspect ratio, and which one loses (empty
 * space vs. a photo shrunk to a strip) always looks like a mistake on
 * SOME screen size.
 *
 * Final direction: object-fit:cover, full-bleed, on every screen size.
 * This is what the overwhelming majority of hero banners on the web
 * actually do, for exactly this reason -- the box is ALWAYS 100% photo,
 * never a color or a blur standing in for one, so it never looks
 * broken. The real trade-off is cropping, not emptiness: some edges of
 * the photo won't be visible on some screens. That's controlled by
 * object-position, which defaults to center/center but is exposed as
 * "نقطة تركيز الصورة" in the admin Media page (media_bgFocus) --
 * if a future photo crops badly on some screen, the organiser can fix
 * it themselves in one click, without needing another code change. */
.hero-bg-img-wrap {
  position: absolute;
  inset: 0;
  z-index: 0;
}
.hero-bg-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center center; /* overridden inline by app.js from media_bgFocus */
  display: block;
}

/* ====== HERO: FADE THE PHOTO IN OVER ITS STAND-IN (v93) ======
 *
 * The blurred 24px stand-in is painted as the WRAP's background by the
 * inline head script, so it is already on screen when this image starts
 * downloading. These two classes let the real photo cross-fade over it
 * instead of snapping in.
 *
 * Note what is NOT here: a default `opacity: 0` on .hero-bg-img. The
 * transparency is added by app.js only when a stand-in genuinely exists
 * underneath, because an image left at opacity 0 whose load event never
 * fires is invisible permanently — a much worse outcome than the flash of
 * loading this replaces. The stylesheet's job is only to describe the
 * transition; app.js decides whether it is safe to use it.
 *
 * No CSS blur anywhere: a 24px image stretched across the hero is blurred
 * by the browser's own upscaling. A filter would also blur this element's
 * children, and cost a repaint on a phone for nothing. */
.hero-bg-img.is-fading-in { opacity: 0; }
.hero-bg-img.is-loaded { opacity: 1; transition: opacity 0.5s ease; }

@media (prefers-reduced-motion: reduce) {
  .hero-bg-img.is-loaded { transition: none; }
}
.hero-overlay {
  position: absolute;
  inset: 0;
  background: linear-gradient(135deg, rgba(0,17,51,0.85) 0%, rgba(0,35,102,0.75) 100%);
  z-index: 1;
}
/* On mobile the background fill gets stronger -- the image already
 * carries the context, and readable text matters more. The value is
 * overridden by JS (overlayOpacity from admin) for the uploaded image
 * case, but as a CSS-only base this ensures the fallback gradient
 * hero also works correctly on small screens. */

.hero-content {
  position: relative;
  z-index: 2;
  text-align: center;
  color: var(--white);
  padding: 40px 24px;
  max-width: 800px;
}

/* v23: Syrian flag "kicker" badge above the hero title -- same quiet
 * glassmorphism language as the countdown cells just below it, so it
 * reads as a deliberate part of the hero rather than a sticker pasted on. */
.hero-flag-badge {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 16px;
  padding: 6px 18px 6px 14px;
  background: rgba(255,255,255,0.1);
  border: 1px solid rgba(255,255,255,0.22);
  border-radius: 999px;
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}
.hero-flag-badge-icon {
  width: 26px;
  height: auto;
  border-radius: 3px;
  box-shadow: 0 1px 4px rgba(0,0,0,0.35);
  flex-shrink: 0;
  display: block;
}
.hero-flag-badge span {
  font-size: 0.78rem;
  font-weight: 600;
  color: rgba(255,255,255,0.92);
  letter-spacing: 0.02em;
}

.hero-title {
  overflow-wrap: anywhere;
  text-wrap: pretty;
  font-size: clamp(2.2rem, 6vw, 4rem);
  font-weight: 700;
  margin-bottom: 8px;
  line-height: 1.2;
}


.hero-subtitle {
  font-family: var(--font-en);
  font-size: clamp(1rem, 2.5vw, 1.5rem);
  font-weight: 300;
  color: rgba(255,255,255,0.8);
  margin-bottom: 16px;
  letter-spacing: 2px;
}

/* v23: bilingual conference-dates line, between the subtitle and the
 * quote. A quiet gold "chip" rather than plain text -- reads as a fact
 * (like the countdown/stats below it), not another line of prose. */
.hero-dates {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  margin: 0 auto 18px;
  padding: 8px 22px;
  background: rgba(212,175,55,0.12);
  border: 1px solid rgba(212,175,55,0.35);
  border-radius: 999px;
  color: var(--gold);
  font-family: var(--font-num);
  font-size: clamp(0.85rem, 2vw, 1rem);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.01em;
}
.hero-dates i { font-size: 0.85em; }

.hero-quote {
  font-size: clamp(0.95rem, 2vw, 1.2rem);
  font-style: italic;
  color: var(--white);
  margin-bottom: 32px;
  line-height: 1.6;
}

/* ====== COUNTDOWN ====== */
.countdown {
  display: flex;
  justify-content: center;
  gap: 16px;
  margin-bottom: 36px;
  flex-wrap: wrap;
}

.cd-item {
  background: rgba(255,255,255,0.1);
  backdrop-filter: blur(8px);
  border: 1px solid rgba(255,255,255,0.15);
  border-radius: var(--radius-md);
  padding: 16px 24px;
  min-width: 85px;
  text-align: center;
}

.cd-num {
  display: block;
  font-family: var(--font-en);
  font-size: 2.2rem;
  font-weight: 700;
  color: var(--gold);
  line-height: 1;
}

.cd-label {
  font-size: 0.8rem;
  color: rgba(255,255,255,0.7);
  margin-top: 4px;
  display: block;
}

/* ====== BUTTONS ====== */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 14px 32px;
  border-radius: 50px;
  font-weight: 600;
  font-size: 1rem;
  transition: all var(--transition);
  border: 2px solid transparent;
}

.btn-gold {
  background: var(--gold);
  color: var(--primary-dark);
}
.btn-gold:hover {
  background: var(--gold-light);
  transform: translateY(-2px);
  box-shadow: 0 8px 25px rgba(212,175,55,0.35);
}

.btn-outline {
  border-color: var(--white);
  color: var(--white);
}
.btn-outline:hover {
  background: var(--white);
  color: var(--primary);
  transform: translateY(-2px);
}

.hero-actions { display: flex; gap: 16px; justify-content: center; flex-wrap: wrap; }

/* ====== SECTION TITLE ====== */
.section-title {
  text-align: center;
  margin-bottom: 48px;
}

.section-title h2 {
  font-size: clamp(1.6rem, 3.5vw, 2.4rem);
  font-weight: 700;
  color: var(--primary);
  margin-bottom: 12px;
}

.title-line {
  width: 80px;
  height: 4px;
  background: linear-gradient(90deg, var(--gold), var(--primary));
  margin: 0 auto;
  border-radius: 2px;
}

.section-subtitle {
  color: var(--gray-500);
  font-size: 1.05rem;
  margin-top: 16px;
}

/* ====== ABOUT STATS ====== */
.about-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 24px;
  margin-bottom: 64px;
}

.about-card {
  background: var(--off-white);
  border-radius: var(--radius-md);
  padding: 32px 20px;
  text-align: center;
  transition: all var(--transition);
  border: 1px solid transparent;
}
.about-card:hover {
  transform: translateY(-4px);
  box-shadow: var(--shadow-lg);
  border-color: var(--gold);
}

.about-icon {
  font-size: 2rem;
  color: var(--gold);
  margin-bottom: 12px;
}

.about-num {
  font-family: var(--font-en);
  font-size: 2.5rem;
  font-weight: 700;
  color: var(--primary);
  line-height: 1;
  margin-bottom: 6px;
}

.about-text {
  color: var(--gray-500);
  font-size: 0.95rem;
}

.about-desc {
  max-width: 800px;
  margin: 0 auto;
  text-align: center;
  font-size: 1.05rem;
  color: var(--gray-600);
  line-height: 1.8;
}
.about-desc p { margin: 0 0 1em; line-height: 1.8; }
.about-desc p:last-child { margin-bottom: 0; }

/* Wraps the description + the (optional) three logos beside it.
 * Row on desktop (text first, logos alongside), stacked on mobile with
 * the logos below the text so nothing feels squeezed. Hidden by app.js
 * (display:none on #aboutLogos) when the admin hasn't uploaded any. */
.about-content {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 36px;
  max-width: 1000px;
  margin: 0 auto;
  flex-wrap: wrap;
}
.about-content .about-desc { margin: 0; flex: 1 1 480px; }
.about-logos {
  display: flex;
  align-items: center;
  gap: 22px;
  flex: 0 0 auto;
  flex-wrap: wrap;
  justify-content: center;
}
.about-logo-item {
  width: 210px; height: 210px;
  display: flex; align-items: center; justify-content: center;
  background: var(--white);
  border-radius: var(--radius-md);
  box-shadow: 0 4px 18px rgba(0,0,0,0.1);
  padding: 18px;
  transition: transform 0.22s ease, box-shadow 0.22s ease;
}
.about-logo-item img { max-width: 100%; max-height: 100%; object-fit: contain; }
.about-logo-item.is-clickable { cursor: zoom-in; }
.about-logo-item.is-clickable:hover,
.about-logo-item.is-clickable:focus-visible {
  transform: scale(1.07);
  box-shadow: 0 12px 32px rgba(0,0,0,0.18);
  outline: none;
}

/* ====== LIGHTBOX / MEMBER MODAL ====== */
.lb-overlay {
  position: fixed; inset: 0;
  background: rgba(0,10,30,0.82);
  z-index: 2000;
  display: flex; align-items: center; justify-content: center;
  padding: 20px;
  opacity: 0;
  transition: opacity 0.2s ease;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}
.lb-overlay.is-open { opacity: 1; }
.lb-box {
  position: relative;
  background: var(--white);
  border-radius: 16px;
  max-width: 640px;
  width: 100%;
  padding: 28px 24px;
  text-align: center;
  box-shadow: 0 20px 60px rgba(0,0,0,0.4);
  transform: translateY(12px) scale(0.98);
  transition: transform 0.22s ease;
  margin: auto;
}
.lb-overlay.is-open .lb-box { transform: translateY(0) scale(1); }
.lb-close {
  position: absolute; top: 10px;
  /* RTL page: the close button belongs on the LEFT, which is the
   * "far" corner from where Arabic text begins. */
  left: 12px;
  width: 36px; height: 36px;
  border-radius: 50%;
  border: none;
  background: var(--gray-100, #f0f2f5);
  color: var(--gray-600, #4a5568);
  font-size: 1rem;
  cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  transition: background 0.18s ease;
}
.lb-close:hover { background: var(--gray-200, #e2e8f0); }

/* Image-only lightbox (about-section logos) */
.lb-image-box { background: var(--white); padding: 34px 24px 20px; }
.lb-image-box img {
  max-width: 100%;
  max-height: 70vh;
  object-fit: contain;
  display: block;
  margin: 0 auto;
}
.lb-caption {
  margin-top: 14px;
  font-size: 0.9rem;
  color: var(--gray-500);
}

/* Multi-image gallery lightbox (v22) — showGalleryLightbox() in app.js.
 * Same box as the single-image lightbox, just wider (room for the nav
 * buttons either side of the photo) plus a "n / total" counter. */
.lb-gallery-box { max-width: 760px; }
.lb-nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 44px; height: 44px;
  border-radius: 50%;
  border: none;
  background: rgba(0,17,51,0.55);
  color: #fff;
  font-size: 1rem;
  cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  transition: background 0.18s ease, transform 0.18s ease;
  z-index: 1;
}
.lb-nav:hover { background: rgba(0,17,51,0.8); transform: translateY(-50%) scale(1.08); }
.lb-nav.lb-next { left: 10px; }
.lb-nav.lb-prev { right: 10px; }
.lb-gallery-count {
  margin-top: 10px;
  font-family: var(--font-num);
  font-size: 0.85rem;
  color: var(--gray-500);
  font-variant-numeric: tabular-nums;
}

/* Committee member detail modal */
/* PERSON-PHOTO IDENTITY (v34) — circular crop, object-fit:cover.
 *
 * This replaces the "squircle + object-fit:contain" experiment. contain
 * guaranteed the whole photo was visible, but in practice the uploads are
 * every aspect ratio at once, so almost every portrait sat inside visible
 * letterbox bands. Against the light card that read as a white border on
 * two sides and no border on the others -- the frames stopped matching
 * each other, which is precisely what looked broken.
 *
 * cover fills the frame every time, so a row of members is visually
 * uniform regardless of what was uploaded. object-position is biased
 * slightly ABOVE centre because heads sit in the upper part of a portrait;
 * a plain `center` on a tall photo crops the forehead.
 *
 * The full, uncropped photo is still one tap away: clicking any member
 * opens the modal/lightbox. Cropping the thumbnail costs nothing because
 * the uncropped view already exists.
 *
 * Same identity everywhere a person appears: .member-photo,
 * .member-modal-photo, .speaker-photo, .dyn-article-photo. */
.member-modal-photo {
  width: 280px; height: 280px;
  margin: 6px auto 20px;
  position: relative;
  border-radius: 50%;
  overflow: hidden;
  border: 5px solid var(--gold);
  box-shadow: 0 8px 32px rgba(212,175,55,0.4);
  background: linear-gradient(135deg, rgba(212,175,55,0.12), rgba(0,35,102,0.06));
}
.member-modal-photo img { width: 100%; height: 100%; object-fit: cover; object-position: center 25%; }
.member-modal-icon {
  width: 180px; height: 180px;
  margin: 6px auto 20px;
  border-radius: 22%;
  background: var(--primary);
  color: var(--gold);
  display: flex; align-items: center; justify-content: center;
  font-size: 4rem;
}
.member-modal h3 {
  font-size: 1.5rem; color: var(--primary);
  margin-bottom: 8px;
}
.member-modal-role {
  color: var(--gold); font-weight: 600;
  font-size: 1.05rem; margin-bottom: 14px;
}
.member-modal-bio {
  font-size: 0.98rem; line-height: 1.85;
  color: var(--gray-600, #4a5568);
  text-align: right;
}

/* "Tap for details" hint on the committee cards */
.committee-card.is-clickable { cursor: pointer; }
.card-more-hint {
  display: block;
  margin-top: 12px;
  font-size: 0.74rem;
  color: var(--gray-400, #a0aec0);
  opacity: 0;
  transition: opacity 0.2s ease;
}
.committee-card.is-clickable:hover .card-more-hint,
.committee-card.is-clickable:focus-visible .card-more-hint { opacity: 1; }
/* Touch devices have no hover, so keep the hint permanently visible. */
@media (hover: none) {
  .card-more-hint { opacity: 0.75; }
}

/* ====== TRACKS ====== */
.tracks-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 24px;
}

.track-card {
  background: var(--off-white);
  border-radius: var(--radius-md);
  padding: 32px 24px;
  transition: all var(--transition);
  border: 1px solid var(--gray-200);
  position: relative;
  overflow: hidden;
}

.track-card::before {
  content: '';
  position: absolute;
  top: 0; right: 0;
  width: 4px;
  height: 100%;
  background: var(--gold);
  transform: scaleY(0);
  transition: transform var(--transition);
}

.track-card:hover {
  transform: translateY(-4px);
  box-shadow: var(--shadow-lg);
  border-color: var(--gold);
}
.track-card:hover::before { transform: scaleY(1); }

.track-icon {
  width: 56px; height: 56px;
  background: var(--primary);
  border-radius: var(--radius-sm);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.5rem;
  color: var(--gold);
  margin-bottom: 16px;
}

.track-card h3 {
  font-size: 1.1rem;
  font-weight: 600;
  color: var(--primary);
  margin-bottom: 8px;
}

.track-card p {
  color: var(--gray-500);
  font-size: 0.9rem;
  line-height: 1.6;
}

/* ====== SCHEDULE ====== */
.schedule-controls {
  display: flex;
  gap: 16px;
  align-items: center;
  margin-bottom: 32px;
  flex-wrap: wrap;
  justify-content: center;
}

.day-tabs {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}

.day-tab {
  padding: 10px 20px;
  border-radius: 50px;
  background: var(--gray-100);
  color: var(--gray-600);
  font-weight: 500;
  font-size: 0.9rem;
  transition: all var(--transition);
  border: 1px solid var(--gray-200);
}
.day-tab:hover { background: var(--gray-200); }
.day-tab.active {
  background: var(--primary);
  color: var(--white);
  border-color: var(--primary);
}

.schedule-filter {
  padding: 10px 20px;
  border-radius: 50px;
  border: 1px solid var(--gray-200);
  background: var(--white);
  color: var(--gray-700);
  font-size: 0.9rem;
  cursor: pointer;
  min-width: 160px;
}

.schedule-grid {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

/* ====== PROGRAMME AS A RAIL (redesigned v79) ======
 *
 * Was: a time column beside a bordered box, repeated down the page. That
 * reads as a spreadsheet and gives no sense of a day unfolding. Each entry
 * now hangs off a vertical rail with its own marker, so the eye follows
 * the day in order and the hour is the thing that stands out.
 */
.schedule-item {
  position: relative;
  display: grid;
  /* rail · time · details */
  grid-template-columns: 26px 92px 1fr;
  gap: 0 16px;
  align-items: start;
  padding: 18px 20px 18px 0;
  background: transparent;
  border: 0;
  border-radius: 0;
  transition: transform .2s var(--ease-out);
}
.schedule-item:hover { transform: translateX(-3px); }
/* RTL: the rail is on the right, so the hover nudge goes left (inward). */

.schedule-rail { position: relative; align-self: stretch; }
/* The line itself, drawn between markers rather than as one long element,
 * so it never overshoots the first or last entry. */
.schedule-rail::before {
  content: '';
  position: absolute;
  inset-block: -18px 0;
  inset-inline-start: 50%;
  width: 2px;
  transform: translateX(50%);
  background: linear-gradient(var(--gray-200), var(--gray-200));
}
.schedule-item:first-child .schedule-rail::before { inset-block-start: 22px; }
.schedule-item:last-child  .schedule-rail::before { inset-block-end: calc(100% - 22px); }

.schedule-dot {
  position: absolute;
  top: 16px;
  inset-inline-start: 50%;
  transform: translateX(50%);
  width: 13px; height: 13px;
  border-radius: 50%;
  background: #fff;
  border: 3px solid var(--gold);
  box-shadow: 0 0 0 4px rgba(212, 175, 55, .16);
  z-index: 1;
  transition: box-shadow .2s ease, border-color .2s ease;
}
.schedule-item:hover .schedule-dot {
  border-color: var(--primary);
  box-shadow: 0 0 0 6px rgba(0, 35, 102, .14);
}

.schedule-time {
  font-family: var(--font-en);
  font-weight: 700;
  color: var(--primary);
  font-size: .9rem;
  white-space: nowrap;
  padding-top: 12px;
  font-variant-numeric: tabular-nums;
  /* A time is LTR even on an RTL page. */
  direction: ltr;
  text-align: start;
}

.schedule-details {
  background: #fff;
  border: 1px solid var(--gray-200);
  border-radius: 14px;
  padding: 14px 16px;
  box-shadow: 0 1px 3px rgba(0, 35, 102, .05);
  transition: box-shadow .22s var(--ease-out), border-color .2s ease;
}
.schedule-item:hover .schedule-details {
  border-color: var(--gold);
  box-shadow: 0 10px 26px rgba(0, 35, 102, .10);
}
.schedule-details h4 {
  font-size: 1.02rem;
  font-weight: 700;
  color: var(--primary);
  margin: 0 0 6px;
  line-height: 1.6;
}
.schedule-details .schedule-speaker {
  font-size: .86rem;
  color: var(--gray-600, #4a5568);
  display: flex; align-items: center; gap: 6px;
  margin-bottom: 8px;
}
.schedule-details .schedule-speaker i { color: var(--gold); font-size: .8rem; }
.schedule-details .schedule-track {
  display: inline-flex; align-items: center;
  padding: 3px 11px;
  border-radius: 999px;
  background: rgba(0, 35, 102, .07);
  color: var(--primary);
  font-size: .75rem;
  font-weight: 600;
}

/* ====== COMMITTEES ====== */
.committees-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 28px;
}

/* ====== POSTERS ====== */
/* ====== SCIENTIFIC POSTERS (redesigned v78) ======
 *
 * The previous card cropped the poster with object-fit:cover into a 200px
 * landscape strip. A scientific poster is a TALL document — cropping it to
 * a wide band shows an arbitrary slice of the middle, usually a chunk of a
 * chart with no title and no author. It told the visitor nothing.
 *
 * Now the WHOLE poster is shown, uncropped, on a blurred colour-matched
 * copy of itself (buildImgFrame — the same treatment used for gallery art).
 * The frame is portrait, because posters are, so the natural shape fits
 * with little wasted space. Nothing is cut off; the full-size view is one
 * tap away as before.
 */
.posters-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(268px, 1fr));
  gap: 26px;
}

.poster-card {
  position: relative;
  display: flex;
  flex-direction: column;
  background: #fff;
  border-radius: 18px;
  overflow: hidden;
  border: 1px solid var(--gray-200);
  box-shadow: 0 2px 10px rgba(0, 35, 102, .05);
  transition: transform .28s var(--ease-spring), box-shadow .28s var(--ease-out),
              border-color .2s ease;
}
.poster-card:hover {
  transform: translateY(-6px);
  box-shadow: 0 18px 44px rgba(0, 35, 102, .16);
  border-color: var(--gold);
}

.poster-image {
  position: relative;
  width: 100%;
  /* Portrait: the shape a poster actually is. */
  aspect-ratio: 3 / 4;
  background: #0d1b3a;
  overflow: hidden;
}
.poster-noimg {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  color: rgba(255,255,255,.28); font-size: 2.6rem;
}
/* A quiet ordinal, so a wall of posters can be referred to by number. */
.poster-index {
  position: absolute;
  top: 10px; inset-inline-start: 10px;
  min-width: 26px; height: 26px;
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0 8px;
  border-radius: 999px;
  background: rgba(3, 10, 28, .66);
  color: #fff;
  font-size: .74rem; font-weight: 700;
  font-variant-numeric: tabular-nums;
  z-index: 3;
}

.poster-body {
  display: flex;
  flex-direction: column;
  gap: 9px;
  padding: 15px 17px 17px;
  flex: 1;
}
.poster-body h3 {
  font-size: 1.02rem;
  line-height: 1.55;
  color: var(--primary);
  margin: 0;
}
.poster-meta { display: flex; flex-wrap: wrap; gap: 6px; }
.poster-chip {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 3px 10px;
  border-radius: 999px;
  background: rgba(0, 35, 102, .06);
  color: var(--gray-600, #4a5568);
  font-size: .76rem;
  line-height: 1.7;
}
.poster-chip i { font-size: .72rem; opacity: .75; }
.poster-chip--who {
  background: rgba(212, 175, 55, .16);
  color: #7a5c00;
  font-weight: 600;
}
.poster-body .poster-desc {
  font-size: .85rem;
  line-height: 1.8;
  color: var(--gray-600, #4a5568);
  margin: 0;
  /* Three lines, then the modal. Cards in a row stay the same height. */
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.poster-placeholder {
  grid-column: 1 / -1;
  text-align: center;
  padding: 80px 20px;
  color: var(--gray-400);
}
.poster-placeholder i { font-size: 4rem; margin-bottom: 16px; color: var(--gray-300); }
.poster-placeholder p { font-size: 1.1rem; }

/* ====== REGISTRATION TABS ====== */
.reg-tabs {
  display: flex;
  gap: 8px;
  justify-content: center;
  margin-bottom: 32px;
  flex-wrap: wrap;
}

.reg-tab {
  padding: 12px 28px;
  border-radius: 50px;
  background: var(--gray-100);
  color: var(--gray-600);
  font-weight: 500;
  font-size: 0.95rem;
  transition: all var(--transition);
  border: 1px solid var(--gray-200);
  display: flex;
  align-items: center;
  gap: 8px;
}
.reg-tab:hover { background: var(--gray-200); }
.reg-tab.active {
  background: var(--primary);
  color: var(--white);
  border-color: var(--primary);
}
.reg-tab.active i { color: var(--gold); }

.reg-panel { display: none; }
.reg-panel.active { display: block; }

/* ====== REGISTRATION ======
 * .reg-grid was written for TWO cards (form + workshops), 1.2fr/0.8fr.
 * The workshops card was removed from the public forms entirely
 * (changelog #30), leaving only .reg-form-card as a single child --
 * but the two-column grid definition stayed, so the form rendered
 * squeezed into ~60% of the width with dead space beside it. Fixed by
 * centering the single card with a readable max-width instead. */
.reg-grid {
  display: flex;
  justify-content: center;
}
.reg-grid > .reg-form-card {
  width: 100%;
  max-width: 760px;
}

.reg-form-card, .reg-workshops-card {
  background: var(--off-white);
  border-radius: var(--radius-md);
  padding: 32px;
  border: 1px solid var(--gray-200);
}

.reg-form-card h3, .reg-workshops-card h3 {
  font-size: 1.2rem;
  font-weight: 600;
  color: var(--primary);
  margin-bottom: 24px;
  display: flex;
  align-items: center;
  gap: 10px;
}
.reg-form-card h3 i, .reg-workshops-card h3 i { color: var(--gold); }

.reg-form { display: grid; grid-template-columns: minmax(0,1fr) minmax(0,1fr); gap: 16px; }
.reg-form .full { grid-column: 1 / -1; }

.form-group { display: flex; flex-direction: column; gap: 6px; }
.form-group label { font-size: 0.9rem; font-weight: 500; color: var(--gray-700); }
/* The red required-marker. Scoped to a DIRECT child span of a DIRECT child
 * label, because the loose ".form-group label span" it replaced matched far
 * more than the asterisk: the workshop picker builds each choice as a
 * <label class="ws-card"> whose title, presenter, time and description are
 * all <span>s, so an entire workshop card rendered in error-red. Its own
 * .ws-card-name colour could not win -- ".form-group label span" scores
 * 0-1-2 against 0-1-0. All 17 asterisks in index.html are direct children,
 * verified, so nothing else changes. */
.form-group > label > span { color: #e53e3e; }
.form-group input, .form-group select, .form-group textarea {
  padding: 12px 16px;
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-sm);
  font-size: 0.95rem;
  font-family: inherit;
  background: var(--white);
  color: var(--gray-800);
  transition: border var(--transition);
  resize: vertical;
}
/* Multi-line fields (bio, abstract) need more breathing room than a
 * single-line input -- otherwise the text visually hugs the top-right
 * corner (the natural starting corner for RTL text), which read as
 * "stuck to the edge" rather than properly formatted. More generous
 * padding and looser line-height fix that without changing where RTL
 * text naturally starts. */
.form-group textarea {
  padding: 16px 18px;
  line-height: 1.75;
}
.form-group input:focus, .form-group select:focus, .form-group textarea:focus {
  outline: none;
  border-color: var(--gold);
  box-shadow: 0 0 0 3px rgba(212,175,55,0.15);
}

/* Custom validation error state -- used instead of the browser's
 * native validation tooltips (forms carry novalidate) so every
 * invalid/empty required field gets a visible red border plus a
 * specific inline message, in place. */
.form-group input.input-error,
.form-group select.input-error,
.form-group textarea.input-error {
  border-color: #e53e3e;
  box-shadow: 0 0 0 3px rgba(229,62,62,0.12);
}
.field-error-msg {
  color: #e53e3e;
  font-size: 0.82rem;
  margin-top: 2px;
}
.form-check.input-error span { color: #e53e3e; }
.form-check.input-error { outline: 1px solid #e53e3e; border-radius: var(--radius-sm); padding: 6px; }

.ws-checkboxes { display: flex; flex-direction: column; gap: 8px; }
.ws-checkboxes label {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 0.9rem;
  color: var(--gray-600);
  cursor: pointer;
}
.ws-checkboxes input[type="checkbox"] {
  width: 18px; height: 18px;
  accent-color: var(--gold);
  cursor: pointer;
}

.btn-submit {
  grid-column: 1 / -1;
  justify-content: center;
  margin-top: 8px;
}

.reg-success {
  text-align: center;
  padding: 40px 20px;
}
.reg-success i {
  font-size: 3rem;
  color: #38a169;
  margin-bottom: 16px;
}
.reg-success h3 { font-size: 1.3rem; color: var(--primary); margin-bottom: 8px; }
.reg-success p { color: var(--gray-500); }

/* ====== WORKSHOPS ====== */
.workshop-item {
  background: var(--white);
  border-radius: var(--radius-sm);
  padding: 16px;
  margin-bottom: 12px;
  border: 1px solid var(--gray-200);
  transition: all var(--transition);
}
.workshop-item:hover { border-color: var(--gold); }
.workshop-item h4 { font-size: 0.95rem; font-weight: 600; color: var(--gray-800); margin-bottom: 4px; }
.workshop-item p { font-size: 0.85rem; color: var(--gray-500); }
.workshop-item .ws-capacity {
  display: inline-block;
  margin-top: 6px;
  font-size: 0.8rem;
  padding: 2px 10px;
  border-radius: 50px;
  background: var(--gray-100);
  color: var(--gray-600);
}

/* ====== SPONSORS ====== */
.sponsors-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 24px;
}

.sponsor-card {
  background: var(--off-white);
  border-radius: var(--radius-md);
  padding: 32px 20px;
  text-align: center;
  transition: all var(--transition);
  border: 1px solid var(--gray-200);
}
.sponsor-card:hover {
  transform: translateY(-4px);
  box-shadow: var(--shadow-lg);
  border-color: var(--gold);
}
.sponsor-card .sponsor-logo {
  width: 100%;
  height: 100px;
  margin: 0 auto 12px;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  border-radius: var(--radius-sm);
  background: var(--white);
  padding: 8px;
}
.sponsor-card .sponsor-logo img {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  transition: transform var(--transition);
}
.sponsor-card:hover .sponsor-logo img {
  transform: scale(1.05);
}

.sponsor-card .sponsor-placeholder {
  width: 80px; height: 80px;
  margin: 0 auto 12px;
  background: var(--gray-200);
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 2rem;
  color: var(--gray-400);
}
.sponsor-card h4 { font-size: 1rem; font-weight: 600; color: var(--gray-800); margin-bottom: 4px; }
.sponsor-card p { font-size: 0.85rem; color: var(--gray-500); }
.sponsor-card .sponsor-tier {
  display: inline-block;
  margin-top: 8px;
  font-size: 0.75rem;
  padding: 3px 14px;
  border-radius: 50px;
  background: var(--gold);
  color: var(--primary-dark);
  font-weight: 600;
}

/* ====== FOOTER ====== */
.footer {
  background: var(--primary-dark);
  color: rgba(255,255,255,0.85);
  padding: 28px 0 0;
  margin-top: 48px;
}
.footer-grid {
  display: grid;
  grid-template-columns: minmax(0,1.4fr) minmax(0,1fr) minmax(0,1.2fr);
  gap: 24px;
  padding-bottom: 20px;
}

.footer-desc {
  font-size: 0.78rem; line-height: 1.6;
  color: rgba(255,255,255,0.6); margin-bottom: 6px;
}
.footer-dates {
  font-size: 0.75rem; color: var(--gold);
  display: flex; align-items: center; gap: 6px;
}

.footer-links h4, .footer-contact h4 {
  font-size: 0.78rem; font-weight: 700;
  margin-bottom: 10px; color: var(--gold);
  text-transform: uppercase; letter-spacing: 0.06em;
}
.footer-links ul li { margin-bottom: 6px; }
.footer-links ul li a {
  font-size: 0.78rem; color: rgba(255,255,255,0.6);
  transition: color 0.2s ease;
}
.footer-links ul li a:hover { color: var(--gold); }

.footer-contact p {
  font-size: 0.78rem; margin-bottom: 6px;
  display: flex; align-items: center; gap: 8px;
  color: rgba(255,255,255,0.65);
}
.footer-contact p i { color: var(--gold); width: 16px; flex: 0 0 16px; }

/* ── Footer closing bar ── */
.footer-bottom {
  border-top: 1px solid rgba(255,255,255,0.10);
  padding: 12px 0 14px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  flex-wrap: wrap;
}
.footer-copy {
  font-size: 0.72rem;
  color: rgba(255,255,255,0.45);
}
.footer-code {
  font-size: 0.68rem;
  font-family: var(--font-en, 'IBM Plex Mono', monospace);
  letter-spacing: 0.1em;
  color: rgba(255,255,255,0.3);
}


/* ====== SCROLL TO TOP ====== */
.scroll-top {
  position: fixed;
  bottom: 30px; left: 30px;
  width: 48px; height: 48px;
  border-radius: 50%;
  background: var(--gold);
  color: var(--primary-dark);
  font-size: 1.1rem;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: var(--shadow-lg);
  opacity: 0;
  visibility: hidden;
  transform: translateY(20px);
  transition: all var(--transition);
  z-index: 999;
}
.scroll-top.visible {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}
.scroll-top:hover {
  background: var(--gold-light);
  transform: translateY(-3px);
  box-shadow: 0 8px 25px rgba(212,175,55,0.4);
}

/* ====== RESPONSIVE ====== */


/* ====== Drag & Drop Upload Zones (research submission) ====== */
.dropzone {
  position: relative;
  border: 2px dashed rgba(0,35,102,0.25);
  border-radius: 12px;
  padding: 16px;
  text-align: center;
  background: rgba(0,35,102,0.02);
  transition: border-color 0.2s, background 0.2s;
  cursor: pointer;
  margin-bottom: 4px;
}
.dropzone:hover { border-color: var(--primary, #002366); background: rgba(0,35,102,0.05); }
.dropzone.dz-over { border-color: var(--gold, #D4AF37); background: rgba(212,175,55,0.08); }
.dropzone .dz-hint { color: #6b7280; font-size: 0.85rem; pointer-events: none; }
.dropzone .dz-hint i { font-size: 1.3rem; display: block; margin-bottom: 4px; color: var(--primary, #002366); }
.dropzone .dz-filename { font-size: 0.8rem; color: #111827; margin-top: 4px; font-weight: 600; pointer-events: none; }
.dropzone input.dz-input { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; cursor: pointer; }

/* ====== Submission progress bar ====== */
.submit-progress-wrap { margin-bottom: 12px; }
.submit-progress {
  height: 8px; background: #e5e7eb; border-radius: 999px; overflow: hidden;
}
.submit-progress-fill {
  height: 100%; width: 0%;
  background: linear-gradient(90deg, var(--primary, #002366), var(--gold, #D4AF37));
  transition: width 0.15s ease-out;
}
.submit-progress-label {
  text-align: center; font-size: 0.75rem; color: #6b7280; margin-top: 4px;
}

/* ====== Small visual polish (additive, non-breaking) ====== */
.btn { transition: transform 0.15s ease, box-shadow 0.15s ease; }
.btn:hover { transform: translateY(-2px); box-shadow: 0 8px 20px rgba(0,0,0,0.15); }
.card, .poster-card, .track-card, .sponsor-card { transition: transform 0.2s ease, box-shadow 0.2s ease; }
.card:hover, .poster-card:hover, .track-card:hover, .sponsor-card:hover {
  transform: translateY(-4px);
  box-shadow: 0 12px 28px rgba(0,0,0,0.12);
}
.form-file, input[type="text"], input[type="email"], input[type="tel"], select, textarea {
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.form-file:focus, input:focus, select:focus, textarea:focus {
  outline: none;
  box-shadow: 0 0 0 3px rgba(0,35,102,0.1);
}

/* ====== Dynamic homepage sections — alternating full-width bands (diag21) ======
 * Replaces the old "cards in a grid" layout. #dynamicSectionsContainer now
 * sits OUTSIDE .container (see index.html) so each .dyn-band can be
 * full-bleed; the readable content inside gets its own .container.
 *
 * Color rule: hero=navy, About=white (both above this container), then
 * bands alternate navy/white starting with navy. is_active:false sections
 * are filtered out in app.js BEFORE alternating, so hiding one never
 * shifts the colors of every band after it. The footer is completely
 * outside this loop and stays navy regardless — see .footer below. */
#dynamicSectionsContainer {
  display: flex;
  flex-direction: column;
}
.dyn-band { padding: 64px 0; }
.dyn-band-blue {
  background: linear-gradient(135deg, #001a33 0%, #002366 55%, #012f7a 100%);
}
.dyn-band-white { background: #fff; }

/* Image + text side by side; alternates which side the image is on. */
.dyn-row {
  display: flex;
  align-items: center;
  gap: 48px;
}
.dyn-row-flip { flex-direction: row-reverse; }
.dyn-row-textonly { display: block; max-width: 760px; margin: 0 auto; text-align: center; }
.dyn-row-media { flex: 1 1 44%; min-width: 0; }
.dyn-row-body { flex: 1 1 56%; min-width: 0; }

/* CONTENT-IMAGE IDENTITY (v34) — fill the frame with object-fit:cover.
 *
 * Reverts the object-fit:contain experiment. The intent behind contain was
 * "never crop anything", but the result was worse than the problem: real
 * uploads are portrait, landscape and square in the same section, so nearly
 * every image floated inside letterbox bands of a different size. On a white
 * band those bands read as ragged white edges, and no two sections lined up
 * with each other -- the "غير متناسق" look.
 *
 * cover makes every frame the same shape and edge-to-edge, which is what
 * makes a page of mixed photos look deliberate. Nothing is lost: the frame
 * is cursor:zoom-in and opens the lightbox, which still shows the complete
 * uncropped image.
 *
 * If a specific photo ever crops badly, the fix is object-position on that
 * image (the hero already exposes exactly that control in the admin as
 * "نقطة تركيز الصورة"), NOT switching the whole site back to contain. */
.dyn-img-frame {
  position: relative;
  width: 100%;
  aspect-ratio: 4 / 3;
  border-radius: 14px;
  overflow: hidden;
}
.dyn-img-frame img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  display: block;
  border-radius: 14px;
  cursor: zoom-in;
}
/* ====== MULTI-IMAGE GALLERY (v22, updated) ======
 * Replaces .dyn-img-frame when a section carries more than one photo
 * (sections.images). Tiles are cropped with object-fit:cover so the grid is
 * uniform -- see the note on .dyn-img-frame above for why the intervening
 * "never crop" (contain) version was reverted in v34: in a 2x2 grid of
 * mixed aspect ratios, contain gave every tile a different-sized white
 * margin and the collage stopped reading as a grid at all. Each tile opens
 * the lightbox, which shows the whole image uncropped. Mobile sizing lives
 * in the one consolidated 600px block at the end of this file (§9.21) --
 * never add a third @media block here. */
.dyn-gallery {
  display: grid;
  gap: 8px;
  aspect-ratio: 4 / 3;
  border-radius: 16px;
  overflow: hidden;
  box-shadow: 0 10px 30px rgba(0,0,0,0.12);
}
.dyn-gallery-item {
  position: relative;
  cursor: zoom-in;
  overflow: hidden;
  background: transparent;
}
.dyn-gallery-item img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  display: block;
  transition: transform 0.45s var(--ease-out, ease);
}
.dyn-gallery-item:hover img,
.dyn-gallery-item:focus-visible img { transform: scale(1.07); }

.dyn-gallery--1 { grid-template-columns: 1fr; }
.dyn-gallery--2 { grid-template-columns: 1fr 1fr; }
/* 3 photos: one large tile + two stacked beside it — a warmer, more
 * editorial "collage" instead of three identical boxes in a row. */
.dyn-gallery--3 { grid-template-columns: 1.4fr 1fr; grid-template-rows: 1fr 1fr; }
.dyn-gallery--3 .dyn-gallery-item:first-child { grid-row: 1 / 3; }
.dyn-gallery--many { grid-template-columns: 1fr 1fr; grid-template-rows: 1fr 1fr; }
/* Overlay on the 4th tile when more photos exist than fit the grid. */
.dyn-gallery-more {
  position: absolute;
  inset: 0;
  background: rgba(0,17,51,0.64);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.5rem;
  font-weight: 700;
  font-family: var(--font-num);
  pointer-events: none;
}

/* Small colored kicker bar — keeps the admin's per-section custom color
 * meaningful as an accent even though the band background itself now
 * follows the fixed navy/white alternation. */
.dyn-eyebrow { display: block; width: 42px; height: 4px; border-radius: 3px; margin-bottom: 14px; }
.dyn-row-textonly .dyn-eyebrow { margin-inline: auto; }

.dyn-section-title { font-size: 1.5rem; font-weight: 700; margin-bottom: 8px; color: var(--primary); }
.dyn-section-subtitle { color: var(--gray-500, #6b7280); font-size: 0.95rem; margin-bottom: 12px; }
/* Real paragraphs (built by formatParagraphs in app.js) — NOT pre-wrap.
 * pre-wrap preserved every typed blank line, producing huge dead gaps.
 * Uniform margin between paragraphs gives predictable spacing instead. */
.dyn-section-desc { color: #374151; font-size: 1rem; }
.dyn-section-desc p { line-height: 1.9; margin: 0 0 1.1em; overflow-wrap: anywhere; }
.dyn-section-desc p:last-child { margin-bottom: 0; }

/* Navy bands need light text — the desc/title colors above are the
 * white-band (light) defaults; these override them inside a blue band. */
.dyn-band-blue .dyn-section-title { color: #fff; }
.dyn-band-blue .dyn-section-subtitle { color: rgba(255,255,255,0.72); }
.dyn-band-blue .dyn-section-desc { color: rgba(255,255,255,0.92); }
.dyn-band-blue .dyn-section-desc strong { color: var(--gold, #D4AF37); }
.dyn-band-blue .dyn-section-desc a { color: var(--gold, #D4AF37); }
.dyn-band-blue .dyn-section-desc a:hover { color: #fff; }
.dyn-band-blue .dyn-section-desc blockquote { border-color: var(--gold, #D4AF37); color: rgba(255,255,255,0.85); }
.dyn-band-blue .dyn-section-desc table { border-color: rgba(255,255,255,0.25); }
.dyn-band-blue .dyn-section-desc th, .dyn-band-blue .dyn-section-desc td { border-color: rgba(255,255,255,0.18); }

/* ====== LONG-FORM ARTICLE SECTIONS (diag22) ======
 * Auto-selected by isLongFormSection() in app.js for a dynamic section
 * whose text is long or contains an inline image -- e.g. a director's
 * welcome letter or a hospital-history piece. A warm off-white "paper"
 * tone (not stark #fff, so it visually reads as "an article", not just
 * another alternating band) and a narrow, centered reading column
 * instead of the full 1200px width, which is what long Arabic prose
 * actually needs to stay comfortable to read. */
.dyn-band-article {
  background: #fdfcf9;
  border-top: 1px solid rgba(0,35,102,0.06);
  border-bottom: 1px solid rgba(0,35,102,0.06);
}
.dyn-article-container { max-width: 720px; margin: 0 auto; position: relative; }
/* A large, soft quotation mark — a warmer, more human, editorial touch
 * for what these sections usually are (a personal letter, a welcome
 * message) rather than a plain corporate text block. Purely decorative
 * (not real Arabic punctuation), so it sits at the RTL reading start
 * (top-right) regardless of language. */
.dyn-article-container::before {
  content: '\201C';
  position: absolute;
  top: -22px;
  right: -6px;
  font-family: Georgia, 'Times New Roman', serif;
  font-size: 7rem;
  line-height: 1;
  color: var(--gold, #D4AF37);
  opacity: 0.16;
  pointer-events: none;
  user-select: none;
}
.dyn-article-byline {
  display: flex;
  align-items: center;
  gap: 20px;
  margin-bottom: 28px;
  flex-wrap: wrap;
}
/* Circular portrait, same identity as .member-photo/.speaker-photo. */
.dyn-article-photo {
  width: 96px;
  height: 96px;
  border-radius: 50%;
  object-fit: cover;
  object-position: center 25%;
  border: 3px solid var(--gold, #D4AF37);
  background: linear-gradient(135deg, rgba(212,175,55,0.12), rgba(0,35,102,0.06));
  flex-shrink: 0;
  cursor: zoom-in;
}
.dyn-article-byline-text { min-width: 0; }
.dyn-article-title { font-size: 1.4rem; font-weight: 700; color: var(--primary); margin: 0 0 4px; }
.dyn-article-subtitle { color: var(--gray-500, #6b7280); font-size: 0.95rem; margin: 0; }
.dyn-article-desc { font-size: 1.05rem; line-height: 2; color: #374151; }
.dyn-article-desc p { margin: 0 0 1.3em; }
.dyn-article-desc p:last-child { margin-bottom: 0; }

/* "Show more" truncation (long articles only -- see buildCollapsibleDesc
 * in app.js). The fade sits INSIDE the clamped box's own bounds so it
 * never gets clipped by the box's own overflow:hidden, and matches
 * .dyn-band-article's fixed #fdfcf9 background exactly. */
.dyn-article-desc.dyn-clamped {
  position: relative;
  max-height: 280px;
  overflow: hidden;
}
.dyn-article-desc.dyn-clamped::after {
  content: '';
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 90px;
  background: linear-gradient(to bottom, rgba(253,252,249,0), #fdfcf9);
  pointer-events: none;
}
.dyn-read-more-btn {
  display: flex;
  align-items: center;
  gap: 7px;
  margin: 16px auto 0;
  padding: 10px 26px;
  border: 1.5px solid rgba(212,175,55,0.4);
  border-radius: 999px;
  background: rgba(212,175,55,0.08);
  color: var(--gold-dark, #a8842c);
  font-weight: 700;
  font-size: 0.92rem;
  font-family: inherit;
  cursor: pointer;
  transition: background 0.2s ease, transform 0.2s ease, border-color 0.2s ease;
}
.dyn-read-more-btn:hover {
  background: rgba(212,175,55,0.16);
  border-color: rgba(212,175,55,0.7);
  transform: translateY(-1px);
}
.dyn-read-more-btn i { font-size: 0.75rem; transition: transform 0.25s ease; }
.dyn-read-more-btn.is-expanded i { transform: rotate(180deg); }
/* An inline image pasted mid-article (e.g. an old photo of the
 * hospital building) sizes itself naturally -- no forced height, no
 * object-fit, so it is never cropped and never fights the text column. */
.dyn-article-desc img {
  max-width: 100%;
  height: auto;
  border-radius: 10px;
  margin: 24px 0;
  display: block;
}

/* Filmstrip of extra photos under a long-form article (v22) — small,
 * quiet, secondary to the article text; opens the same gallery lightbox
 * as the byline photo, starting at whichever tile was tapped. */
.dyn-article-gallery {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 28px;
}
.dyn-article-gallery-item {
  width: 88px;
  height: 88px;
  flex-shrink: 0;
  border-radius: 10px;
  overflow: hidden;
  cursor: zoom-in;
  background: rgba(0,0,0,0.04);
}
.dyn-article-gallery-item img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  display: block;
  transition: transform 0.35s ease;
}
.dyn-article-gallery-item:hover img,
.dyn-article-gallery-item:focus-visible img { transform: scale(1.08); }


/* ====== Aesthetic refinement pass (public site) ====== */
.about-card, .track-card, .committee-card, .sponsor-card, .poster-card, .workshop-item {
  border-radius: 16px;
}
.section-title h2 {
  position: relative;
  display: inline-block;
}
.title-line {
  background: linear-gradient(90deg, var(--gold, #D4AF37), transparent);
  height: 3px;
  border-radius: 3px;
}
.hero {
  position: relative;
}
.hero::after {
  content: '';
  position: absolute;
  bottom: 0; left: 0; right: 0;
  height: 60px;
  background: linear-gradient(to top, rgba(0,0,0,0.15), transparent);
  pointer-events: none;
}
.btn-gold {
  background: linear-gradient(135deg, var(--gold, #D4AF37), var(--gold-dark, #b8952e));
}
.navbar {
  box-shadow: 0 2px 12px rgba(0,0,0,0.15);
}
.about-card, .poster-card, .track-card, .sponsor-card {
  box-shadow: 0 4px 14px rgba(0,0,0,0.06);
}
.countdown .cd-item {
  transition: transform 0.2s ease;
}
.countdown .cd-item:hover { transform: translateY(-2px); }

.field-help {
  display: block;
  margin-top: 4px;
  font-size: 0.78rem;
  color: var(--gray-500, #6b7280);
}

/* ============================================================
   MOBILE POLISH — public site
   Consolidated additions. Deliberately additive so the existing
   breakpoints above keep working.
   ============================================================ */


/* Nothing should ever cause horizontal page scrolling on a phone. */
html, body { max-width: 100%; overflow-x: hidden; }
img, video, iframe { max-width: 100%; height: auto; }

/* ============================================================
   DESIGN LAYER
   Three ideas, applied with restraint:
   1. Kufic display face for headings — monumental, institutional.
   2. One monospace voice for every numeral on the page, so the
      countdown, the stats and the schedule times all read as an
      instrument readout. Medicine is measurement; the numbers
      should look measured.
   3. A single structural device — a gold rule ending in a square
      node — used as the section eyebrow. No 01/02/03 numbering:
      these sections are not a sequence, so numbering them would
      encode something untrue.
   ============================================================ */

/* ---- 1. Display typography ---- */
.hero-title,
.section-title h2,
.about-num,
.dyn-section-title,
.admin-page > h2 {
  font-family: var(--font-display);
  font-weight: 600;
  letter-spacing: 0;      /* Kufi is already wide; extra tracking muddies it */
}

.hero-title {
  font-weight: 700;
  line-height: 1.15;
  text-wrap: balance;
}

.section-title h2 { font-weight: 600; }

/* Arabic body text stays in the humanist face — Kufi is for display only. */
.hero-quote, .about-desc, .poster-desc, .dyn-section-desc { font-family: var(--font-ar); }

/* ---- 2. The numeral voice ---- */
.cd-num,
.about-num,
.schedule-time,
.stat-box div:first-child,
.nav-logo-fallback {
  font-family: var(--font-num);
  font-variant-numeric: tabular-nums;   /* digits stop jittering as they tick */
  font-feature-settings: "tnum" 1;
}

.cd-num {
  font-weight: 600;
  letter-spacing: -0.02em;
}

.about-num {
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* Countdown reads as a panel of instrument cells rather than four
   floating boxes: shared hairline, subtle inner glow, gold seconds. */
.cd-item {
  position: relative;
  border: 1px solid rgba(212,175,55,0.22);
  background: rgba(255,255,255,0.06);
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.08);
}
.cd-item::after {
  /* small gold tick in the corner — the "live" indicator */
  content: '';
  position: absolute;
  top: 6px; left: 6px;
  width: 4px; height: 4px;
  background: var(--gold);
  border-radius: 1px;
  opacity: 0.55;
}
.cd-label {
  font-family: var(--font-ar);
  letter-spacing: 0.02em;
  opacity: 0.7;
}

/* ---- 3. The gold rule + node section eyebrow ---- */
.section-title { position: relative; }
.section-title h2::before {
  content: '';
  display: block;
  width: 44px;
  height: 2px;
  margin: 0 0 14px auto;         /* RTL: rule starts at the right edge */
  background: linear-gradient(to left, var(--gold), rgba(212,175,55,0));
  position: relative;
}
.section-title h2::after {
  content: '';
  position: absolute;
  top: 0; right: 0;
  width: 6px; height: 6px;
  background: var(--gold);
  transform: translateY(-2px) rotate(45deg);   /* the node */
}

/* ---- Hero depth: layered light instead of a flat gradient ---- */
.hero-content { position: relative; z-index: 2; }
.hero::before {
  content: '';
  position: absolute;
  inset: 0;
  /* two soft off-centre lights give the navy some volume */
  background:
    radial-gradient(60% 50% at 78% 18%, rgba(212,175,55,0.13), transparent 70%),
    radial-gradient(70% 60% at 12% 88%, rgba(255,255,255,0.07), transparent 70%);
  pointer-events: none;
  z-index: 1;
}

/* ---- Cards: quieter, more precise ---- */
.about-card, .track-card, .committee-card, .poster-card, .sponsor-card {
  border: 1px solid rgba(0,35,102,0.07);
  background: #fff;
}
.about-card {
  position: relative;
  overflow: hidden;
}
.about-card::before {
  /* hairline gold edge, only on the stat cards — they carry the proof */
  content: '';
  position: absolute;
  top: 0; right: 0; left: 0;
  height: 2px;
  background: linear-gradient(to left, var(--gold), rgba(212,175,55,0.15));
}

/* ---- Buttons: crisper press feedback than a generic lift ---- */
.btn { position: relative; }
.btn-gold {
  box-shadow: 0 1px 0 rgba(255,255,255,0.25) inset, 0 6px 18px rgba(212,175,55,0.28);
}
.btn:active { transform: translateY(1px); }

/* ============================================================
   MOTION — one orchestrated idea: sections settle into place as
   you scroll, and the stat numbers tick up once when first seen.
   Everything is opt-out via prefers-reduced-motion.
   ============================================================ */
.reveal {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 0.6s cubic-bezier(0.22, 1, 0.36, 1),
              transform 0.6s cubic-bezier(0.22, 1, 0.36, 1);
  will-change: opacity, transform;
}
.reveal.is-visible { opacity: 1; transform: none; }
/* Stagger children so a grid arrives as a wave, not a single block. */
.reveal-stagger > * {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity 0.5s ease, transform 0.5s ease;
}
.reveal-stagger.is-visible > * { opacity: 1; transform: none; }
.reveal-stagger.is-visible > *:nth-child(1) { transition-delay: 0.00s; }
.reveal-stagger.is-visible > *:nth-child(2) { transition-delay: 0.06s; }
.reveal-stagger.is-visible > *:nth-child(3) { transition-delay: 0.12s; }
.reveal-stagger.is-visible > *:nth-child(4) { transition-delay: 0.18s; }
.reveal-stagger.is-visible > *:nth-child(5) { transition-delay: 0.24s; }
.reveal-stagger.is-visible > *:nth-child(6) { transition-delay: 0.30s; }
.reveal-stagger.is-visible > *:nth-child(7) { transition-delay: 0.36s; }
.reveal-stagger.is-visible > *:nth-child(8) { transition-delay: 0.42s; }

@media (prefers-reduced-motion: reduce) {
  .reveal, .reveal-stagger > * {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }
  .btn:active { transform: none; }
  * { animation-duration: 0.001s !important; }
}

/* ============================================================
   LOADING SKELETONS — content arrives from the API a moment after
   the page paints. A shimmer placeholder is honest about that and
   stops the layout from jumping.
   ============================================================ */
.skeleton {
  background: linear-gradient(100deg, #eef1f5 30%, #f7f9fb 50%, #eef1f5 70%);
  background-size: 200% 100%;
  animation: skeleton-sweep 1.2s ease-in-out infinite;
  border-radius: 8px;
  color: transparent !important;
}
@keyframes skeleton-sweep {
  from { background-position: 200% 0; }
  to   { background-position: -200% 0; }
}
.skeleton-card { height: 150px; }
.skeleton-line { height: 12px; margin-bottom: 8px; }
.skeleton-line.short { width: 55%; }

/* ---- Visible keyboard focus (was relying on the browser default) ---- */
a:focus-visible, button:focus-visible, input:focus-visible,
select:focus-visible, textarea:focus-visible, [tabindex]:focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: 2px;
  border-radius: 4px;
}

/* ============================================================
   DETAILED SUBMISSION FORM
   Section headings, intro/notes, and the declaration checkboxes
   for the abstract-submission form.
   ============================================================ */
.form-intro {
  grid-column: 1 / -1;
  color: var(--gray-600, #4a5568);
  line-height: 1.8;
  font-size: 0.92rem;
  margin-bottom: 4px;
}
.form-section-title {
  grid-column: 1 / -1;
  font-family: var(--font-display);
  font-size: 1.05rem;
  font-weight: 600;
  color: var(--primary);
  margin: 20px 0 4px;
  padding-bottom: 8px;
  border-bottom: 2px solid rgba(212,175,55,0.25);
  position: relative;
}
.form-section-title::after {
  content: '';
  position: absolute;
  bottom: -2px; right: 0;
  width: 44px; height: 2px;
  background: var(--gold);
}
.form-note {
  grid-column: 1 / -1;
  font-size: 0.85rem;
  color: var(--gray-500, #6b7280);
  background: rgba(0,35,102,0.03);
  border-right: 3px solid var(--gold);
  padding: 8px 12px;
  border-radius: 6px;
  margin-bottom: 4px;
}
.form-check {
  grid-column: 1 / -1;
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 12px 14px;
  background: rgba(0,35,102,0.02);
  border: 1px solid rgba(0,35,102,0.08);
  border-radius: 10px;
  margin-bottom: 8px;
  cursor: pointer;
  line-height: 1.7;
  font-size: 0.9rem;
  transition: border-color 0.15s ease, background 0.15s ease;
}
.form-check:hover { border-color: rgba(212,175,55,0.4); background: rgba(212,175,55,0.04); }
.form-check input[type="checkbox"] {
  flex: 0 0 20px;
  width: 20px;
  height: 20px;
  margin-top: 2px;
  accent-color: var(--primary);
  cursor: pointer;
}

/* ============================================================
   COMMITTEE MEMBER CARDS — with photos and group headings
   ============================================================ */
.committee-group { margin-bottom: 40px; }
.committee-group-title {
  font-family: var(--font-display);
  font-size: 1.15rem;
  font-weight: 600;
  color: var(--primary);
  margin-bottom: 20px;
  padding-bottom: 10px;
  border-bottom: 2px solid rgba(212,175,55,0.2);
  display: flex;
  align-items: center;
  gap: 10px;
}
.committee-group-title i { color: var(--gold); }

.committee-card {
  background: #fff;
  border-radius: 14px;
  padding: 30px 22px;
  text-align: center;
  border: 1px solid rgba(0,35,102,0.07);
  box-shadow: 0 2px 8px rgba(0,0,0,0.05);
  transition: transform 0.25s ease, box-shadow 0.25s ease;
}
/* Both mouse hover AND a tap/press on touch screens now visibly enlarge
 * the card (scale, not just a shadow lift), per request. :active
 * covers touch devices that have no real ":hover". transform-origin
 * keeps the card growing from its own center instead of drifting. */
.committee-card {
  transform-origin: center center;
}
.committee-card:hover,
.committee-card:active {
  transform: translateY(-6px) scale(1.06);
  box-shadow: 0 14px 32px rgba(0,0,0,0.14);
  z-index: 2;
  position: relative;
}

/* Member photo — circular, cover. See the note on .member-modal-photo. */
/* Member portrait — a filled circular crop, NOT a contained image.
 * See buildPortrait() in app.js for why people are cropped and artwork is
 * not. object-position keeps the head in frame; a plain centre cuts
 * foreheads on tall photos. aspect-ratio guards the circle if the width
 * is ever overridden without the height. */
.member-photo {
  width: 124px; height: 124px;
  aspect-ratio: 1 / 1;
  border-radius: 50%;
  overflow: hidden;
  margin: 0 auto 14px;
  border: 3px solid var(--gold);
  box-shadow: 0 2px 10px rgba(212,175,55,0.32), inset 0 0 0 1px rgba(255,255,255,0.35);
  background: linear-gradient(135deg, rgba(212,175,55,0.12), rgba(0,35,102,0.06));
}
.member-photo img {
  width: 100%; height: 100%;
  object-fit: cover;
  object-position: center 25%;
  display: block;
}

.committee-card h3 { font-size: 1.05rem; font-weight: 700; color: var(--primary); margin-bottom: 5px; }
.member-role { font-size: 0.85rem; color: var(--gold); font-weight: 600; margin-bottom: 7px; }
.member-bio { font-size: 0.87rem; color: var(--gray-600, #4a5568); line-height: 1.65; }

/* Admin: member thumbnail in the item list */

/* ============================================================
   SECTION NOTES — optional admin-filled text above content
   ============================================================ */
.section-note {
  max-width: 700px;
  margin: 0 auto 24px;
  padding: 12px 18px;
  background: rgba(212,175,55,0.08);
  border-right: 3px solid var(--gold);
  border-radius: 8px;
  font-size: 0.95rem;
  color: var(--gray-700, #374151);
  line-height: 1.7;
}

/* ============================================================
   POSTERS SECTION — Call-to-Action state
   ============================================================ */
.posters-cta {
  text-align: center;
  padding: 40px 20px;
  max-width: 680px;
  margin: 0 auto;
}
.cta-badge {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  background: linear-gradient(135deg, var(--primary), #1a3a6b);
  color: #fff;
  padding: 12px 24px;
  border-radius: 999px;
  font-weight: 700;
  font-size: 1rem;
  margin-bottom: 20px;
  box-shadow: 0 4px 14px rgba(0,35,102,0.25);
}
.cta-badge i { color: var(--gold); font-size: 1.2rem; }
.cta-desc {
  font-size: 0.97rem;
  color: var(--gray-600, #4a5568);
  line-height: 1.8;
  margin-bottom: 10px;
}
.cta-invite {
  font-size: 0.97rem;
  color: var(--primary);
  font-weight: 600;
  margin-bottom: 24px;
}
.cta-actions {
  display: flex;
  gap: 12px;
  justify-content: center;
  flex-wrap: wrap;
}
.cta-actions .btn { min-width: 200px; }

/* ============================================================
   WORKSHOPS SECTION — workshop cards
   ============================================================ */
.workshops-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 20px;
}
.workshop-card {
  background: #fff;
  border-radius: 14px;
  padding: 24px 20px;
  text-align: center;
  border: 1px solid rgba(0,35,102,0.07);
  box-shadow: 0 2px 8px rgba(0,0,0,0.05);
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.workshop-card:hover { transform: translateY(-3px); box-shadow: 0 8px 20px rgba(0,0,0,0.1); }
.workshop-icon { font-size: 1.8rem; color: var(--primary); margin-bottom: 12px; }
.workshop-card h3 { font-size: 1rem; font-weight: 700; color: var(--primary); margin-bottom: 8px; }
.workshop-capacity { font-size: 0.85rem; color: var(--gray-500); display: flex; align-items: center; justify-content: center; gap: 6px; }

/* Sponsors contact note styling */
.section-note--contact {
  background: rgba(0,35,102,0.04);
  border-right-color: var(--primary);
  font-weight: 600;
  margin-top: 8px;
}


/* ============================================================
   TIMELINE SECTION
   ============================================================ */
.timeline-list {
  position: relative;
  max-width: 700px;
  margin: 0 auto;
  padding: 8px 0;
}
.timeline-list::before {
  content: '';
  position: absolute;
  top: 0; bottom: 0;
  right: 24px;
  width: 2px;
  background: linear-gradient(to bottom, var(--gold), rgba(212,175,55,0.15));
}
.timeline-item {
  display: flex;
  align-items: flex-start;
  gap: 20px;
  margin-bottom: 24px;
  position: relative;
}
.timeline-dot {
  flex: 0 0 50px;
  width: 50px; height: 50px;
  border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-size: 1rem;
  position: relative;
  z-index: 1;
  box-shadow: 0 2px 8px rgba(0,0,0,0.1);
  border: 2px solid #fff;
}
.timeline-dot.done { background: #38a169; color: #fff; }
.timeline-dot.upcoming { background: var(--primary); color: var(--gold); }
.timeline-dot.current { background: var(--gold); color: var(--primary); animation: pulse-dot 2s ease-in-out infinite; }
@keyframes pulse-dot {
  0%, 100% { box-shadow: 0 0 0 0 rgba(212,175,55,0.4); }
  50% { box-shadow: 0 0 0 8px rgba(212,175,55,0); }
}
.timeline-body {
  flex: 1;
  background: #fff;
  border-radius: 12px;
  padding: 14px 18px;
  border: 1px solid rgba(0,35,102,0.07);
  box-shadow: 0 2px 6px rgba(0,0,0,0.04);
}
.timeline-body .tl-date {
  font-family: var(--font-num);
  font-size: 0.82rem;
  color: var(--gold);
  font-weight: 600;
  margin-bottom: 4px;
}
.timeline-body .tl-label {
  font-size: 0.95rem;
  color: var(--primary);
  font-weight: 600;
}
.timeline-body .tl-done-badge {
  display: inline-block;
  font-size: 0.72rem;
  background: #c6f6d5; color: #22543d;
  border-radius: 999px; padding: 2px 8px;
  margin-top: 4px; font-weight: 600;
}

/* ============================================================
   CONTACT SECTION
   ============================================================ */
.contact-grid {
  display: grid;
  grid-template-columns: 1fr 1.4fr;
  gap: 24px;
  align-items: start;
}
.contact-info-card {
  background: #fff;
  border-radius: 16px;
  padding: 28px 24px;
  box-shadow: 0 2px 12px rgba(0,0,0,0.06);
  border: 1px solid rgba(0,35,102,0.06);
}
.contact-item {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 14px 0;
  border-bottom: 1px dashed rgba(0,35,102,0.08);
  text-decoration: none;
  color: inherit;
  transition: color 0.15s ease;
}
.contact-item:last-child { border-bottom: none; }
.contact-item:hover .contact-item-text { color: var(--primary); }
.contact-icon {
  flex: 0 0 44px; width: 44px; height: 44px;
  border-radius: 12px;
  display: flex; align-items: center; justify-content: center;
  font-size: 1.1rem;
}
.contact-icon.phone { background: #e0f2fe; color: #0369a1; }
.contact-icon.whatsapp { background: #dcfce7; color: #16a34a; }
.contact-icon.telegram { background: #dbeafe; color: #1d4ed8; }
.contact-icon.email { background: #fef9c3; color: #a16207; }
.contact-icon.address { background: #ffe4e6; color: #be123c; }
.contact-item-label { font-size: 0.75rem; color: var(--gray-500); }
.contact-item-text { font-size: 0.95rem; font-weight: 600; color: var(--gray-700); direction: ltr; }
.contact-map-card {
  border-radius: 16px;
  overflow: hidden;
  box-shadow: 0 2px 12px rgba(0,0,0,0.06);
  height: 320px;
  /* A quiet shimmer sits UNDER the iframe so the area reads as "map
     loading" rather than an empty grey rectangle. The iframe paints
     straight over it, so nothing needs to remove it. */
  background:
    linear-gradient(100deg, var(--gray-100) 30%, #eef2f7 50%, var(--gray-100) 70%)
    var(--gray-100);
  background-size: 200% 100%;
  animation: mapSkeleton 1.4s ease-in-out infinite;
}
@keyframes mapSkeleton {
  from { background-position: 200% 0; }
  to   { background-position: -200% 0; }
}
@media (prefers-reduced-motion: reduce) {
  .contact-map-card { animation: none; }
}
.map-wrapper, .map-wrapper iframe { width: 100%; height: 100%; border: none; display: block; }
.map-fallback-link {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  width: 100%; height: 100%;
  background: linear-gradient(135deg, rgba(0,35,102,0.05), rgba(212,175,55,0.08));
  color: var(--primary);
  font-weight: 600;
  text-decoration: none;
  transition: background 0.2s ease;
}
.map-fallback-link:hover { background: rgba(212,175,55,0.14); }
.map-fallback-link i { font-size: 1.4rem; color: var(--gold); }


/* ============================================================
   SECTION STATUS — WIP banner
   ============================================================ */
.wip-banner {
  text-align: center;
  padding: 40px 20px;
  max-width: 600px;
  margin: 0 auto;
}
.wip-icon {
  font-size: 2.5rem;
  color: var(--gold);
  margin-bottom: 14px;
}
.wip-badge {
  display: inline-flex; align-items: center; gap: 8px;
  background: rgba(212,175,55,0.12);
  border: 1px solid rgba(212,175,55,0.3);
  color: #92400e;
  padding: 6px 16px;
  border-radius: 999px;
  font-size: 0.82rem;
  font-weight: 700;
  margin-bottom: 14px;
}
.wip-badge::before {
  content: '';
  width: 8px; height: 8px;
  background: var(--gold);
  border-radius: 50%;
  animation: pulse-dot 1.5s ease-in-out infinite;
}
.wip-title { font-family: var(--font-display); font-size: 1.2rem; color: var(--primary); margin-bottom: 10px; }
.wip-text { font-size: 0.93rem; color: var(--gray-600); line-height: 1.8; }

/* ================================================================
   DESIGN REFINEMENT LAYER — modern polish pass
   Appended before the responsive layer so breakpoint rules still win.
   ================================================================ */

/* ── Richer depth tokens ── */
:root {
  --shadow-xs: 0 1px 2px rgba(0,35,102,0.05);
  --shadow-soft: 0 2px 12px rgba(0,35,102,0.07);
  --shadow-card: 0 4px 20px rgba(0,35,102,0.08);
  --shadow-hover: 0 14px 40px rgba(0,35,102,0.16);
  --shadow-gold: 0 8px 28px rgba(212,175,55,0.28);
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-spring: cubic-bezier(0.34, 1.4, 0.64, 1);
}

/* ── BUTTONS: layered depth + a light sweep on hover ── */
.btn {
  position: relative;
  overflow: hidden;
  letter-spacing: 0.01em;
  transition: transform 0.28s var(--ease-out), box-shadow 0.28s var(--ease-out), background 0.2s ease;
}
.btn::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(120deg, transparent 30%, rgba(255,255,255,0.28) 50%, transparent 70%);
  transform: translateX(-120%);
  transition: transform 0.65s var(--ease-out);
  pointer-events: none;
}
.btn:hover::after { transform: translateX(120%); }
.btn-gold {
  background: linear-gradient(135deg, var(--gold-light, #e3c35a) 0%, var(--gold) 55%, var(--gold-dark, #b8952e) 100%);
  box-shadow: var(--shadow-gold);
}
.btn-gold:hover {
  transform: translateY(-3px);
  box-shadow: 0 14px 38px rgba(212,175,55,0.42);
}
.btn-gold:active { transform: translateY(-1px); }
.btn-outline {
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  background: rgba(255,255,255,0.06);
  border-width: 2px;
}
.btn-outline:hover { transform: translateY(-3px); box-shadow: 0 12px 32px rgba(255,255,255,0.16); }

/* ── STAT CARDS: gradient top edge, spring lift ── */
.about-card {
  background: #fff;
  box-shadow: var(--shadow-card);
  border: 1px solid rgba(0,35,102,0.06);
  position: relative;
  overflow: hidden;
  transition: transform 0.32s var(--ease-spring), box-shadow 0.32s var(--ease-out);
}
.about-card::before {
  content: '';
  position: absolute;
  top: 0; right: 0; left: 0;
  height: 3px;
  background: linear-gradient(90deg, var(--gold) 0%, var(--gold-light, #e3c35a) 50%, var(--gold) 100%);
  transform: scaleX(0);
  transform-origin: right;
  transition: transform 0.4s var(--ease-out);
}
.about-card:hover {
  transform: translateY(-7px);
  box-shadow: var(--shadow-hover);
  border-color: rgba(212,175,55,0.35);
}
.about-card:hover::before { transform: scaleX(1); }
.about-icon {
  transition: transform 0.4s var(--ease-spring);
  display: inline-flex;
}
.about-card:hover .about-icon { transform: scale(1.16) rotate(-6deg); }
.about-num {
  background: linear-gradient(135deg, var(--primary) 0%, var(--primary-light, #0040a0) 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  font-weight: 700;
}

/* ── TRACK CARDS: icon plate glow + smoother edge ── */
.track-card {
  background: #fff;
  box-shadow: var(--shadow-card);
  border: 1px solid rgba(0,35,102,0.06);
  transition: transform 0.32s var(--ease-spring), box-shadow 0.32s var(--ease-out), border-color 0.25s ease;
}
.track-card::before { width: 3px; background: linear-gradient(180deg, var(--gold), var(--gold-dark, #b8952e)); }
.track-card:hover { transform: translateY(-7px); box-shadow: var(--shadow-hover); }
.track-icon {
  background: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark, #001a33) 100%);
  box-shadow: 0 4px 14px rgba(0,35,102,0.22);
  transition: transform 0.38s var(--ease-spring), box-shadow 0.3s ease;
}
.track-card:hover .track-icon {
  transform: scale(1.09) rotate(-5deg);
  box-shadow: 0 8px 22px rgba(212,175,55,0.4);
}

/* ── COMMITTEE CARDS ── */
.committee-card {
  box-shadow: var(--shadow-card);
  transition: transform 0.32s var(--ease-spring), box-shadow 0.32s var(--ease-out);
}
.member-photo {
  transition: transform 0.38s var(--ease-spring), box-shadow 0.3s ease;
}
.committee-card:hover .member-photo {
  transform: scale(1.07);
  box-shadow: 0 8px 26px rgba(212,175,55,0.45);
}

/* ── SECTION TITLES: refined eyebrow ── */
.section-title h2 {
  background: linear-gradient(135deg, var(--primary) 0%, var(--primary-light, #0040a0) 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
}
.title-line {
  background: linear-gradient(90deg, transparent, var(--gold), transparent);
  height: 2px;
}

/* ── COUNTDOWN: glassmorphism ── */
.cd-item {
  background: rgba(255,255,255,0.09);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border: 1px solid rgba(255,255,255,0.16);
  box-shadow: 0 6px 22px rgba(0,0,0,0.16);
  transition: transform 0.3s var(--ease-spring), background 0.25s ease;
}
.cd-item:hover {
  transform: translateY(-4px);
  background: rgba(255,255,255,0.14);
}
.cd-num {
  background: linear-gradient(135deg, #f0d878 0%, var(--gold) 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
}

/* ── NAV: animated underline on the active link ── */
.nav-btn {
  position: relative;
  transition: color 0.22s ease, background 0.22s ease;
}
.nav-btn::after {
  content: '';
  position: absolute;
  bottom: 2px; right: 50%; left: 50%;
  height: 2px;
  background: var(--gold);
  border-radius: 2px;
  transition: right 0.28s var(--ease-out), left 0.28s var(--ease-out);
}
.nav-btn:hover::after, .nav-btn.active::after { right: 20%; left: 20%; }
/* No underline inside the dropdown — it uses a side bar instead */
.nav-dropdown .nav-btn::after { display: none; }

/* ── BRAND text: subtle gradient on the Arabic line ── */
.nav-brand-ar {
  background: linear-gradient(135deg, #f0d878 0%, var(--gold) 60%, #c9a635 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
}
.nav-brand { transition: opacity 0.2s ease; }
.nav-brand:hover { opacity: 0.86; }

/* ── HAMBURGER: gold glow on hover ── */
.nav-toggle {
  transition: background 0.22s ease, border-color 0.22s ease, box-shadow 0.22s ease;
}
.nav-toggle:hover {
  background: rgba(212,175,55,0.14);
  border-color: rgba(212,175,55,0.45);
  box-shadow: 0 0 0 4px rgba(212,175,55,0.1);
}

/* ── POSTER / SPONSOR cards ── */
.poster-card, .sponsor-card {
  box-shadow: var(--shadow-card);
  transition: transform 0.32s var(--ease-spring), box-shadow 0.32s var(--ease-out);
}
.poster-card:hover, .sponsor-card:hover {
  transform: translateY(-6px);
  box-shadow: var(--shadow-hover);
}

/* ── FORMS: softer focus ring ── */
.form-group input,
.form-group select,
.form-group textarea {
  transition: border-color 0.2s ease, box-shadow 0.2s ease, background 0.2s ease;
}
.form-group input:focus,
.form-group select:focus,
.form-group textarea:focus {
  box-shadow: 0 0 0 4px rgba(212,175,55,0.14);
}

/* ── SCROLLBAR (public site) ── */
html { scrollbar-width: thin; scrollbar-color: var(--gold) rgba(0,35,102,0.06); }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: rgba(0,35,102,0.05); }
::-webkit-scrollbar-thumb {
  background: linear-gradient(180deg, var(--gold), var(--gold-dark, #b8952e));
  border-radius: 8px;
  border: 2px solid transparent;
  background-clip: padding-box;
}
::-webkit-scrollbar-thumb:hover { background: var(--primary); background-clip: padding-box; }

/* ── SCROLL-TO-TOP button ── */
.scroll-top, #scrollTop, .back-to-top {
  box-shadow: var(--shadow-gold);
  transition: transform 0.3s var(--ease-spring), box-shadow 0.3s ease;
}
.scroll-top:hover, #scrollTop:hover, .back-to-top:hover {
  transform: translateY(-4px) scale(1.06);
  box-shadow: 0 14px 34px rgba(212,175,55,0.45);
}

/* ── FOOTER refinement ── */
.footer {
  background: linear-gradient(165deg, var(--primary-dark, #001a33) 0%, #000d1f 100%);
  position: relative;
}
.footer::before {
  content: '';
  position: absolute;
  top: 0; right: 0; left: 0;
  height: 2px;
  background: linear-gradient(90deg, transparent, var(--gold), transparent);
}
.footer-links ul li a {
  position: relative;
  transition: color 0.2s ease, padding-right 0.22s var(--ease-out);
}
.footer-links ul li a:hover { padding-right: 6px; }

/* ── LIGHTBOX polish ── */
.lb-overlay { backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); }
.lb-box { box-shadow: 0 28px 80px rgba(0,0,0,0.5); }

/* ── Focus-visible: accessible and on-brand everywhere ── */
:focus-visible {
  outline: 3px solid var(--gold);
  outline-offset: 3px;
  border-radius: 4px;
}

/* ── Selection colour ── */
::selection { background: rgba(212,175,55,0.28); color: var(--primary-dark, #001a33); }

/* ── Motion: honour reduced-motion for every effect added above ── */
@media (prefers-reduced-motion: reduce) {
  .btn::after { display: none; }
  .about-card:hover, .track-card:hover, .committee-card:hover,
  .poster-card:hover, .sponsor-card:hover, .cd-item:hover,
  .scroll-top:hover, #scrollTop:hover, .back-to-top:hover { transform: none; }
  .about-card:hover .about-icon,
  .track-card:hover .track-icon,
  .committee-card:hover .member-photo { transform: none; }
}

/* BUG FIX (diag21): the block that used to sit here (.about-content
 * column layout, .footer padding, old .dyn-section sizing) had NO
 * @media wrapper at all — it was leaking mobile-only overrides onto
 * every screen size, including desktop. It was also a verbatim
 * duplicate of rules already correctly scoped inside the real
 * @media (max-width: 600px) block further down. Removed here; see
 * that block for the properly-scoped versions. Read §9.8 before
 * assuming a rule you see here is actually taking effect where you
 * expect — always check it sits inside the media query you think it does. */

/* ====== RENDERED RICH CONTENT (public site) ======
 * Styles for HTML pasted by the admin into the paste-aware fields.
 * The admin side sanitizes to a safe tag whitelist and strips all of
 * Word's inline styling, so content arrives structurally intact and
 * adopts the site's own typography here. */
.dyn-section-desc, .about-desc, .track-desc,
.poster-desc, .member-bio, .section-note { overflow-wrap: anywhere; }

.dyn-section-desc h2, .dyn-section-desc h3, .dyn-section-desc h4,
.about-desc h2, .about-desc h3, .about-desc h4 {
  font-family: var(--font-display, 'Cairo', sans-serif);
  color: var(--primary);
  margin: 1.3em 0 0.5em;
  line-height: 1.35;
  font-weight: 700;
}
.dyn-section-desc h2, .about-desc h2 { font-size: 1.35rem; }
.dyn-section-desc h3, .about-desc h3 { font-size: 1.18rem; }
.dyn-section-desc h4, .about-desc h4 { font-size: 1.05rem; }
.dyn-section-desc h2:first-child, .dyn-section-desc h3:first-child,
.about-desc h2:first-child, .about-desc h3:first-child { margin-top: 0; }

.dyn-section-desc ul, .dyn-section-desc ol,
.about-desc ul, .about-desc ol {
  margin: 0 0 1.1em;
  padding-right: 1.6em;
  text-align: right;
}
.dyn-section-desc li, .about-desc li { margin-bottom: 0.45em; line-height: 1.85; }
.dyn-section-desc ul li::marker, .about-desc ul li::marker { color: var(--gold); }

.dyn-section-desc strong, .about-desc strong { font-weight: 700; color: var(--primary); }
.dyn-section-desc em, .about-desc em { font-style: italic; }

.dyn-section-desc a, .about-desc a {
  color: var(--primary);
  text-decoration: underline;
  text-decoration-color: var(--gold);
  text-underline-offset: 3px;
  transition: color 0.18s ease;
}
.dyn-section-desc a:hover, .about-desc a:hover { color: var(--gold-dark, #b8952e); }

.dyn-section-desc blockquote, .about-desc blockquote {
  margin: 1.2em 0;
  padding: 14px 20px;
  border-right: 4px solid var(--gold);
  background: rgba(212,175,55,0.06);
  border-radius: 0 10px 10px 0;
  color: var(--gray-600, #4a5568);
  font-style: italic;
}

/* Tables scroll horizontally rather than breaking the layout */
.dyn-section-desc table, .about-desc table {
  width: 100%;
  border-collapse: collapse;
  margin: 1.2em 0;
  font-size: 0.92rem;
  display: block;
  overflow-x: auto;
  white-space: nowrap;
}
.dyn-section-desc th, .dyn-section-desc td,
.about-desc th, .about-desc td {
  border: 1px solid var(--gray-200, #e2e8f0);
  padding: 10px 14px;
  text-align: right;
}
.dyn-section-desc th, .about-desc th { background: var(--primary); color: #fff; font-weight: 700; }
.dyn-section-desc tr:nth-child(even) td,
.about-desc tr:nth-child(even) td { background: rgba(0,35,102,0.025); }

/* Four about logos: flex row on desktop (grid 2x2 on phone, see below) */
.about-logos {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 18px;
}


/* ====== DETAIL MODALS (tracks / sponsors / posters) ======
 * Shares mountOverlay() and .lb-overlay/.lb-box with the committee and
 * image lightboxes, so close behaviour (backdrop, X, Escape, scroll
 * lock) is identical everywhere. */
.detail-modal { text-align: center; }
.detail-modal--wide { max-width: 760px; }
.detail-modal h3 {
  font-family: var(--font-display, 'Cairo', sans-serif);
  font-size: 1.45rem;
  color: var(--primary);
  margin: 0 0 8px;
  line-height: 1.35;
}
.detail-modal-tag {
  display: inline-block;
  background: rgba(212,175,55,0.14);
  color: var(--gold-dark, #b8952e);
  border: 1px solid rgba(212,175,55,0.35);
  border-radius: 999px;
  padding: 4px 16px;
  font-size: 0.85rem;
  font-weight: 700;
  margin-bottom: 14px;
}
.detail-modal-meta {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px 18px;
  margin-bottom: 16px;
  font-size: 0.9rem;
  color: var(--gray-600, #4a5568);
}
.detail-modal-meta i { color: var(--gold); margin-inline-end: 5px; }
.detail-modal-body {
  text-align: right;
  line-height: 1.9;
  color: var(--gray-700, #374151);
  font-size: 0.98rem;
}
.detail-modal-body p { margin: 0 0 1em; }
.detail-modal-body p:last-child { margin-bottom: 0; }

/* Modal visuals */
.modal-visual { margin: 4px auto 18px; }
.modal-visual--icon {
  width: 110px; height: 110px;
  border-radius: 50%;
  background: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark, #001a33) 100%);
  color: var(--gold);
  display: flex; align-items: center; justify-content: center;
  font-size: 2.6rem;
  box-shadow: 0 8px 26px rgba(0,35,102,0.28);
}
.modal-visual--img {
  width: 150px; height: 150px;
  border-radius: 50%;
  overflow: hidden;
  border: 4px solid var(--gold);
  box-shadow: 0 8px 28px rgba(212,175,55,0.3);
}
.modal-visual--img img { width: 100%; height: 100%; object-fit: cover; }
.modal-visual--logo {
  width: 100%;
  max-width: 260px;
  height: 130px;
  display: flex; align-items: center; justify-content: center;
  background: #fff;
  border: 1px solid var(--gray-200, #e2e8f0);
  border-radius: 12px;
  padding: 14px;
}
.modal-visual--logo img { max-width: 100%; max-height: 100%; object-fit: contain; }
/* Posters: show the whole poster, never crop it */
.modal-visual--poster {
  width: 100%;
  max-height: 58vh;
  display: flex; align-items: center; justify-content: center;
  background: var(--gray-100, #f7fafc);
  border-radius: 12px;
  overflow: hidden;
}
.modal-visual--poster img {
  max-width: 100%;
  max-height: 58vh;
  object-fit: contain;
  cursor: zoom-in;
}

/* ====== TRACK VISUAL: uploaded image instead of an icon ====== */
.track-visual--img {
  width: 64px; height: 64px;
  border-radius: 50%;
  overflow: hidden;
  margin-bottom: 16px;
  border: 2px solid var(--gold);
  box-shadow: 0 4px 14px rgba(212,175,55,0.28);
  transition: transform 0.38s var(--ease-spring, ease);
}
.track-visual--img img { width: 100%; height: 100%; object-fit: cover; display: block; }
.track-card:hover .track-visual--img { transform: scale(1.09) rotate(-5deg); }

/* Clickable cards: cursor + reveal the hint */
.track-card.is-clickable,
.sponsor-card.is-clickable,
.poster-card.is-clickable { cursor: pointer; }
.track-card.is-clickable:hover .card-more-hint,
.track-card.is-clickable:focus-visible .card-more-hint,
.sponsor-card.is-clickable:hover .card-more-hint,
.sponsor-card.is-clickable:focus-visible .card-more-hint,
.poster-card.is-clickable:hover .card-more-hint,
.poster-card.is-clickable:focus-visible .card-more-hint { opacity: 1; }


/* ====== ACCESSIBILITY ====== */
/* Visually hidden but still read by screen readers. Uses the clip
 * technique rather than display:none — display:none removes the element
 * from the accessibility tree entirely, which would defeat the purpose. */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Skip link: off-screen until focused, then a normal visible button.
 * z-index must beat the fixed navbar (1000) or it appears behind it. */
.skip-link {
  position: fixed;
  top: 8px;
  right: 8px;
  z-index: 2000;
  transform: translateY(-160%);
  background: var(--gold);
  color: var(--primary-dark, #001a33);
  padding: 12px 22px;
  border-radius: 8px;
  font-weight: 700;
  font-size: 0.92rem;
  box-shadow: 0 8px 28px rgba(0,0,0,0.35);
  transition: transform 0.22s var(--ease-out, ease);
}
.skip-link:focus {
  transform: translateY(0);
  outline: 3px solid var(--primary);
  outline-offset: 2px;
}
/* Never show a focus ring on <main> itself when the skip link sends
 * focus there — the ring would frame the whole page. */
main:focus { outline: none; }

/* ================================================================
   RESPONSIVE — §9.21: exactly two top-level max-width blocks
   (900px tablet, 600px phone) with 380px nested inside the phone block.
   Must remain the LAST thing in this file so these rules win.
   ================================================================ */


/* ====== SEATS-REMAINING NOTE (diag21) ======
 * Reuses .section-note's existing look; this just adds an urgency style
 * for the "fully booked" state so it reads differently from the normal
 * informational tone. */
.reg-capacity-note.reg-capacity-full {
  color: var(--gray-500, #6b7280);
  font-weight: 600;
}

/* ====== PRINT — full 3-day schedule (diag21) ======
 * Only the schedule section is meant to be printed (triggered by
 * #printScheduleBtn -> printFullSchedule() in app.js, which temporarily
 * renders all 3 days into #scheduleGrid first). This is a `print`
 * media type, a different concern from the `max-width` breakpoints
 * governed by §9.21 -- it is not a third responsive block. */
@media print {
  .navbar, .footer, .scroll-top, .nav-dropdown,
  .hero, .about-grid, .about-content, #dynamicSectionsContainer,
  .schedule-print-btn, .day-tabs, #trackFilter, .field-help {
    display: none !important;
  }
  .section { display: none !important; }
  .section.active { display: block !important; }
  body { padding-top: 0 !important; background: #fff !important; }
  .schedule-print-day { margin-bottom: 18px; page-break-inside: avoid; }
  .schedule-print-day-title {
    font-size: 1.15rem; font-weight: 700; color: #002366;
    margin-bottom: 8px; border-bottom: 2px solid #D4AF37; padding-bottom: 4px;
  }
  .schedule-item { break-inside: avoid; }
}

/* ====== FAQ ACCORDION (v14) ======
 * One answer open at a time (handled in renderFaq's delegated click
 * listener). The panel uses the `hidden` attribute rather than a
 * max-height transition: content height here is unpredictable (an
 * admin can paste a one-line answer or ten paragraphs), and a
 * max-height guess that's too small silently clips text -- a real
 * failure -- whereas `hidden` always shows the full answer. The
 * chevron rotation carries the open/closed affordance instead. */
.faq-list { max-width: 820px; margin: 0 auto; display: flex; flex-direction: column; gap: 12px; }
.faq-item {
  background: #fff;
  border: 1px solid var(--gray-200, #e5e7eb);
  border-radius: 12px;
  overflow: hidden;
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
.faq-item:hover { border-color: var(--gold, #D4AF37); }
.faq-item.open {
  border-color: var(--gold, #D4AF37);
  box-shadow: 0 4px 18px rgba(0,35,102,0.08);
}
.faq-q {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  padding: 16px 20px;
  background: none;
  border: 0;
  cursor: pointer;
  text-align: right;
  font-family: inherit;
  font-size: 1rem;
  font-weight: 600;
  color: var(--primary, #002366);
  line-height: 1.6;
}
.faq-q-text { flex: 1; min-width: 0; }
.faq-chevron {
  flex-shrink: 0;
  color: var(--gold, #D4AF37);
  font-size: 0.85rem;
  transition: transform 0.25s ease;
}
.faq-item.open .faq-chevron { transform: rotate(180deg); }
.faq-a { border-top: 1px solid var(--gray-200, #e5e7eb); }
.faq-a-inner { padding: 16px 20px; color: #374151; font-size: 0.97rem; }
.faq-a-inner p { line-height: 1.9; margin: 0 0 1em; overflow-wrap: anywhere; }
.faq-a-inner p:last-child { margin-bottom: 0; }
.faq-empty { text-align: center; color: var(--gray-500, #6b7280); padding: 2rem 0; }

/* ====== MEDIA GALLERY (v15) ======
 * Uniform card heights via a fixed aspect-ratio thumb box. Thumbnails
 * DO use object-fit:cover here -- unlike the hero (§9.29/§9.30) or the
 * dynamic sections (§9.24), a gallery grid needs equal-sized tiles to
 * look like a grid at all, and the full uncropped image is always one
 * click away in the lightbox. That makes cropping the correct choice
 * here rather than a compromise. */
.gallery-filters {
  display: flex;
  gap: 8px;
  justify-content: center;
  flex-wrap: wrap;
  margin-bottom: 22px;
}
.gallery-filter-btn {
  padding: 8px 18px;
  border: 1px solid var(--gray-200, #e5e7eb);
  border-radius: 999px;
  background: #fff;
  color: var(--primary, #002366);
  font-family: inherit;
  font-size: 0.9rem;
  font-weight: 600;
  cursor: pointer;
  transition: all 0.2s ease;
}
.gallery-filter-btn:hover { border-color: var(--gold, #D4AF37); }
.gallery-filter-btn.active {
  background: var(--primary, #002366);
  border-color: var(--primary, #002366);
  color: #fff;
}
.gallery-empty { text-align: center; color: var(--gray-500, #6b7280); padding: 2rem 0; }

/* Video lightbox: 16:9 responsive iframe wrapper. */
.lb-video-box { width: min(92vw, 960px); }
.lb-video-frame {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  background: #000;
  border-radius: 10px;
  overflow: hidden;
}
.lb-video-frame iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}

/* ====== LANGUAGE SWITCHER + LTR MODE (v16) ====== */
.lang-toggle {
  flex-shrink: 0;
  padding: 6px 14px;
  border: 1px solid rgba(212,175,55,0.55);
  border-radius: 999px;
  background: transparent;
  color: var(--gold, #D4AF37);
  font-family: var(--font-en, 'IBM Plex Mono', monospace);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  cursor: pointer;
  transition: background 0.2s ease, color 0.2s ease;
  margin-inline-start: auto;   /* logical: sits at the far end in BOTH directions */
}
.lang-toggle:hover { background: var(--gold, #D4AF37); color: var(--primary-dark, #001a33); }

/* --- English (LTR) mode ---
 * The site is authored RTL. Most layout already uses logical properties
 * (margin-inline, padding-inline, inset-inline) which flip automatically
 * with dir="ltr", so this block only fixes the places that are still
 * physically anchored, plus the typography change: Arabic display faces
 * (Tajawal/Cairo) have no Latin coverage worth using at heading sizes,
 * so English mode switches body/headings to the Latin stack. */
/* ====== ONE LANGUAGE AT A TIME (v49) ======
 * The brand block carries both an Arabic line and an English line and is
 * marked data-i18n-skip, so the translation walker deliberately leaves it
 * alone. That kept the two lines from being translated into two near
 * duplicates -- but it also meant the ARABIC line stayed on screen in
 * English mode, which is the one thing that must never happen.
 * Showing exactly one of the pair, chosen by the active language, keeps
 * the walker out of it AND keeps English mode purely English. */
html.lang-en .nav-brand-ar { display: none; }
html:not(.lang-en) .nav-brand-en { display: none; }

html.lang-en body,
html.lang-en .hero-title,
html.lang-en .section-title h2,
html.lang-en .dyn-section-title,
html.lang-en .faq-q {
  font-family: 'Segoe UI', system-ui, -apple-system, 'Helvetica Neue', Arial, sans-serif;
}
/* Numerals keep the instrument-readout mono face in both languages --
 * that's a deliberate design constant (see §11 Design notes). */
html.lang-en .cd-num,
html.lang-en .about-num,
html.lang-en .schedule-time { font-family: var(--font-en, 'IBM Plex Mono', monospace); }

/* Text alignment: RTL-authored blocks that were explicitly right-aligned
 * need to become left-aligned rather than inheriting from dir alone. */
html.lang-en .dyn-row-textonly,
html.lang-en .faq-q,
html.lang-en .admin-hint { text-align: left; }
html.lang-en .hero-content,
html.lang-en .section-title,
html.lang-en .gallery-filters { text-align: center; }  /* centred in both */

/* The scroll-top button is physically positioned (left:30px) for RTL;
 * in LTR it belongs on the right so it doesn't collide with content. */
html.lang-en .scroll-top { left: auto; right: 30px; }

/* ====== SPEAKERS (v20) ====== */
.speakers-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 20px;
}
.speaker-card {
  background: #fff;
  border: 1px solid var(--gray-200, #e5e7eb);
  border-radius: 14px;
  overflow: hidden;
  transition: transform 0.22s ease, box-shadow 0.22s ease, border-color 0.22s ease;
}
.speaker-card:hover {
  transform: translateY(-4px);
  border-color: var(--gold, #D4AF37);
  box-shadow: 0 12px 30px rgba(0,35,102,0.12);
}
/* Circular portrait, same identity as .member-photo. Uses object-fit:COVER
 * -- the comment here used to claim contain, which stopped being true when
 * the blur-frame treatment was removed from people in v54. */
.speaker-photo {
  width: 112px;
  height: 112px;
  aspect-ratio: 1 / 1;
  margin: 20px auto 0;
  border-radius: 50%;
  overflow: hidden;
  border: 3px solid var(--gold, #D4AF37);
  background: linear-gradient(135deg, rgba(212,175,55,0.12), rgba(0,35,102,0.06));
}
.speaker-photo img { width: 100%; height: 100%; object-fit: cover; object-position: center 25%; display: block; }
.speaker-photo--placeholder {
  display: flex; align-items: center; justify-content: center;
  color: var(--gray-400, #9ca3af); font-size: 2.1rem;
}
.speaker-body { padding: 14px 18px 20px; text-align: center; }
.speaker-name { font-size: 1.05rem; font-weight: 700; color: var(--primary); margin: 0 0 4px; }
.speaker-title { font-size: 0.88rem; color: var(--gold-dark, #a8842c); font-weight: 600; margin: 0 0 8px; }
.speaker-aff { font-size: 0.82rem; color: var(--gray-500, #6b7280); margin: 0 0 8px; }
.speaker-talk {
  font-size: 0.88rem;
  color: var(--primary);
  background: rgba(212,175,55,0.09);
  border-radius: 8px;
  padding: 8px 10px;
  margin: 0 0 10px;
  line-height: 1.6;
}
.speaker-bio { font-size: 0.85rem; color: #4b5563; text-align: start; }
.speaker-bio p { line-height: 1.8; margin: 0 0 0.8em; }
.speaker-bio p:last-child { margin-bottom: 0; }
.speakers-empty { text-align: center; color: var(--gray-500, #6b7280); padding: 1.5rem 0; line-height: 1.9; }

/* ====== CME (v20) ====== */
.cme-wrap {
  display: flex;
  align-items: flex-start;
  gap: 28px;
  background: #fff;
  border: 1px solid var(--gray-200, #e5e7eb);
  border-radius: 16px;
  padding: 26px;
  max-width: 900px;
  margin: 0 auto;
}
.cme-badge {
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  width: 120px;
  height: 120px;
  border-radius: 50%;
  background: linear-gradient(135deg, #001a33 0%, #002366 100%);
  color: #fff;
  border: 3px solid var(--gold, #D4AF37);
}
.cme-points {
  font-family: var(--font-en, 'IBM Plex Mono', monospace);
  font-size: 2.3rem;
  font-weight: 700;
  color: var(--gold, #D4AF37);
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
.cme-points-label { font-size: 0.75rem; margin-top: 4px; opacity: 0.85; }
.cme-body { flex: 1; min-width: 0; }
.cme-intro { color: #374151; font-size: 1rem; }
.cme-intro p { line-height: 1.9; margin: 0 0 1em; }
.cme-intro p:last-child { margin-bottom: 0; }
.cme-app-btn { margin-top: 14px; display: inline-flex; align-items: center; gap: 8px; }
.cme-note { margin-top: 14px; }

/* ====== ACCOMMODATION + WEATHER (v20) ====== */
.stay-intro { color: #374151; font-size: 1rem; max-width: 860px; }
.stay-intro p { line-height: 1.9; margin: 0 0 1em; }
.stay-intro p:last-child { margin-bottom: 0; }
.weather-block {
  margin-top: 26px;
  background: #fff;
  border: 1px solid var(--gray-200, #e5e7eb);
  border-radius: 14px;
  padding: 20px;
  max-width: 860px;
}
.weather-title { font-size: 1.05rem; font-weight: 700; color: var(--primary); margin: 0 0 6px; }
.weather-kind { font-size: 0.8rem; color: var(--gray-500, #6b7280); margin: 0 0 16px; line-height: 1.7; }
/* An estimate must not look like a forecast — amber tint marks it. */
.weather-kind--estimate {
  background: #fffbeb;
  border-inline-start: 3px solid #d69e2e;
  padding: 8px 10px;
  border-radius: 6px;
  color: #7b6100;
}
.weather-days { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; }
.weather-day {
  background: var(--gray-50, #f9fafb);
  border: 1px solid var(--gray-200, #e5e7eb);
  border-radius: 10px;
  padding: 14px;
  text-align: center;
}
.weather-day-name { font-size: 0.85rem; font-weight: 600; color: var(--primary); margin-bottom: 8px; }
.weather-temps {
  display: flex; align-items: baseline; justify-content: center; gap: 10px;
  font-family: var(--font-en, 'IBM Plex Mono', monospace);
  font-variant-numeric: tabular-nums;
}
.weather-max { font-size: 1.5rem; font-weight: 700; color: #c05621; }
.weather-min { font-size: 1.05rem; color: #2b6cb0; }


/* ====== CHECK-IN SECTION + REGISTRATION QR (v21) ====== */
.checkin-intro { color: #374151; font-size: 1rem; max-width: 860px; }
.checkin-intro p { line-height: 1.9; margin: 0 0 1em; }
.checkin-intro p:last-child { margin-bottom: 0; }

.reg-qr {
  margin-top: 22px;
  padding: 20px;
  background: #fff;
  border: 2px dashed var(--gold, #D4AF37);
  border-radius: 14px;
  text-align: center;
}
.reg-qr-badge { margin-bottom: 12px; line-height: 1.6; }
.reg-qr-badge strong { display: block; font-size: 1.05rem; color: var(--primary); }
.reg-qr-no {
  display: inline-block;
  margin-top: 4px;
  font-family: var(--font-en, 'IBM Plex Mono', monospace);
  font-size: 0.9rem;
  letter-spacing: 0.06em;
  color: var(--gold-dark, #a8842c);
}
.reg-qr-canvas { display: inline-block; line-height: 0; }
.reg-qr-canvas img, .reg-qr-canvas canvas { display: block; }
/* Shown only if the QR library failed to load — the raw code is still
 * usable at the reception desk, which beats an empty box. */
.reg-qr-fallback {
  font-family: var(--font-en, 'IBM Plex Mono', monospace);
  font-size: 1.1rem;
  letter-spacing: 0.08em;
  padding: 18px;
  background: var(--gray-100, #f3f4f6);
  border-radius: 8px;
  word-break: break-all;
}
.reg-qr-note { font-size: 0.85rem; color: var(--gray-500, #6b7280); line-height: 1.8; margin: 12px auto 0; max-width: 420px; }
.reg-qr-actions { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; margin-top: 14px; }
.reg-qr-warn {
  margin: 14px auto 0;
  max-width: 420px;
  font-size: 0.82rem;
  color: #7b6100;
  background: #fffbeb;
  border-radius: 8px;
  padding: 8px 10px;
  line-height: 1.7;
}

/* Print: the badge alone, so a participant can print a usable card. */
@media print {
  .reg-qr-actions, .reg-qr-warn { display: none !important; }
  .reg-qr { border-style: solid; page-break-inside: avoid; }
}

/* ── Inline form error banner ──
   Replaces a blocking alert() for server-side rejections (duplicate
   registration, rate limit, validation). Stays on the page instead of
   vanishing on dismissal, so the visitor can still read why while they
   fix the field. */
.form-error-banner {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  margin: 0 0 14px;
  padding: 12px 14px;
  border-radius: 10px;
  background: #fdecec;
  border: 1px solid #e2a0a0;
  color: #8a2020;
  font-size: 0.88rem;
  line-height: 1.55;
  text-align: right;
}
.form-error-banner i { margin-top: 3px; flex: none; }
html.lang-en .form-error-banner { text-align: left; }
@media (prefers-color-scheme: dark) {
  .form-error-banner { background: #3a1f1f; border-color: #7a3b3b; color: #ffc9c9; }
}

/* ── Registration workshop picker ──
   Card-style checkboxes rather than a bare list: on a phone a plain
   checkbox is a small tap target, and the whole row here is tappable. */
.workshop-choices { display: flex; flex-direction: column; gap: 8px; }
.workshop-choice {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 11px 13px;
  border: 1px solid var(--gray-300, #d5dae2);
  border-radius: 10px;
  cursor: pointer;
  transition: border-color .18s ease, background .18s ease;
  background: #fff;
}
.workshop-choice:hover { border-color: var(--gold, #D4AF37); background: #fffdf5; }
.workshop-choice input { width: 18px; height: 18px; accent-color: var(--primary, #002366); flex: none; cursor: pointer; }
.workshop-choice .ws-name { flex: 1; font-size: 0.92rem; }
.workshop-choice .ws-cap {
  font-size: 0.74rem; color: var(--gray-500, #6b7280);
  background: var(--gray-100, #f1f3f7); padding: 2px 8px; border-radius: 999px; flex: none;
}
.workshop-choice:has(input:checked) {
  border-color: var(--gold, #D4AF37);
  background: #fffaf0;
  box-shadow: inset 0 0 0 1px var(--gold, #D4AF37);
}
@media (prefers-color-scheme: dark) {
  .workshop-choice { background: #16233d; border-color: #2c3d5e; }
  .workshop-choice:hover, .workshop-choice:has(input:checked) { background: #1d2b47; }
  .workshop-choice .ws-cap { background: #22314f; color: #9fb0cc; }
}

/* ====== FORM CLOSED NOTICE (v26) ======
 * Shown in place of a registration form when the organiser closes that door
 * from the admin panel. Two switches exist (attendance / research), so this
 * can appear in one panel while the other stays open.
 * Deliberately calm rather than alarming: a closed door is a normal state,
 * not an error -- an angry red box would read as "the site is broken". */
.form-closed-notice {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 18px 20px;
  margin: 8px 0 4px;
  border-radius: 12px;
  background: var(--gray-100, #f3f4f6);
  border: 1px solid var(--gray-200, #e5e7eb);
  border-inline-start: 4px solid var(--gold, #D4AF37);
  color: var(--gray-600, #4a5568);
  font-size: 0.95rem;
  line-height: 1.75;
}
.form-closed-notice i { color: var(--gold, #D4AF37); margin-top: 4px; flex-shrink: 0; }

/* ================================================================
   IMAGE FRAMES — the "neither cropped nor letterboxed" solution (v35)
   ================================================================
   The problem, stated plainly: uploads arrive at every aspect ratio, and
   a fixed frame can only do one of two bad things with them.
     object-fit: cover   -> fills the frame, but visibly CUTS the photo.
     object-fit: contain -> shows all of it, but leaves blank bars, and
                            because each photo is a different shape the
                            bars differ per card, so nothing lines up.
   Both were tried here and both were rejected, correctly.

   This is the third option: show the ENTIRE photo (contain) on top of a
   blurred, zoomed copy of ITSELF that fills the frame. Nothing is cut,
   there are no blank edges, every card is the same size, and the fill is
   always colour-matched to its own photo — the technique Apple Photos,
   YouTube and Spotify use for exactly this reason.

   Usage: buildImgFrame() in app.js emits the markup. Do not hand-write it.
   ================================================================ */
.imgf {
  position: relative;
  overflow: hidden;
  /* Only ever seen through the blur if the image fails to load. */
  background: #0d1b3a;
}
.imgf > .imgf-bg,
.imgf .imgf-bg {
  position: absolute;
  /* Bleeds past the edges so the blur has real pixels to sample and does
   * not fade into a pale halo at the border. */
  inset: -12%;
  background-size: cover;
  background-position: center;
  filter: blur(24px) saturate(1.35) brightness(0.85);
  transform: scale(1.06);
  /* Decorative: it is the same image again, so it must not be announced. */
  pointer-events: none;
}
.imgf > .imgf-img,
.imgf .imgf-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;         /* the whole photo, always */
  object-position: center;
  display: block;
  z-index: 1;
}
/* A subtle inner edge keeps a pale photo from bleeding into a pale card. */
.imgf::after {
  content: '';
  position: absolute;
  inset: 0;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,0.10);
  pointer-events: none;
  z-index: 2;
}
@media (prefers-reduced-motion: reduce) {
  .imgf-bg { transition: none; }
}

/* A frame used INSIDE an existing sized box (a gallery tile, a card
 * thumbnail) rather than being the sized box itself. */
.imgf--fill { position: absolute; inset: 0; width: 100%; height: 100%; }

/* The person-photo and track-visual frames keep their existing size and
 * shape rules; they only need to become positioning contexts and lose the
 * old single-<img> sizing, which no longer applies now that the photo is
 * an absolutely-positioned layer inside .imgf. */
.track-visual--img.imgf { display: block; }
/* v54: .member-photo/.speaker-photo no longer carry .imgf at all -- people
 * are cropped with object-fit:cover instead (buildPortrait() in app.js).
 * The blur frame is now used only for ARTWORK: track visuals, gallery
 * tiles, posters and sponsor logos, where cropping would destroy content
 * and the uploaded aspect ratio is unpredictable. */

/* ====== CLAMPED CARD TEXT + "عرض المزيد" (v35) ======
 * Line-clamp, not a pixel height: every card in a row ends the same height
 * whatever the font size or language, which a fixed max-height cannot
 * guarantee across Arabic and English. */
.clamp-text.is-clamped {
  display: -webkit-box;
  -webkit-line-clamp: var(--clamp-lines, 3);
  line-clamp: var(--clamp-lines, 3);
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.clamp-toggle {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin: 8px auto 0;
  padding: 5px 14px;
  border: 1px solid rgba(0, 35, 102, 0.22);
  border-radius: 30px;
  background: transparent;
  color: var(--primary, #002366);
  font-family: inherit;
  font-size: 0.78rem;
  font-weight: 700;
  cursor: pointer;
  transition: background 0.18s ease, border-color 0.18s ease;
}
.clamp-toggle:hover, .clamp-toggle:focus-visible {
  background: rgba(0, 35, 102, 0.07);
  border-color: rgba(0, 35, 102, 0.4);
  outline: none;
}
.clamp-toggle i { font-size: 0.66rem; transition: transform 0.22s ease; }
.clamp-toggle.is-expanded i { transform: rotate(180deg); }
/* Inside a card the toggle is centred under the text it controls. */
.committee-card .clamp-toggle { margin-inline: auto; }

/* ====== WORKSHOP DESCRIPTION on the workshops page (v35) ====== */
.workshop-desc {
  font-size: 0.88rem;
  line-height: 1.75;
  color: var(--gray-600, #4a5568);
  margin: 8px 0 10px;
}
.workshop-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 14px;
  margin: 10px 0;
  font-size: 0.82rem;
  color: var(--primary, #002366);
}
.workshop-meta span { display: inline-flex; align-items: center; gap: 6px; }
.workshop-meta i { opacity: 0.7; font-size: 0.78rem; }
.workshop-req {
  font-size: 0.82rem;
  line-height: 1.7;
  color: var(--gray-600, #4a5568);
  background: rgba(0,35,102,0.04);
  border-radius: 8px;
  padding: 8px 11px;
  margin-top: 10px;
}

/* ====== WORKSHOP PICKER in the registration form (v36) ======
 * Was a cramped row of checkboxes with a bare seat count. Each option is
 * now a full-width card carrying what someone actually needs in order to
 * choose: presenter, time, what it covers, any prerequisite.
 *
 * The native checkbox is visually hidden but kept in the DOM and focusable
 * -- collectRegWorkshops() reads `.reg-workshop-cb:checked`, and keyboard
 * users still tab to a real control. The gold box is only its picture. */
.form-group--full { grid-column: 1 / -1; }

.ws-picker-label { display: block; margin-bottom: 10px; }
.ws-picker-label small {
  display: block;
  font-weight: 400;
  opacity: 0.75;
  font-size: 0.82rem;
  margin-top: 2px;
}
.workshop-choices { display: flex; flex-direction: column; gap: 10px; }

.ws-card {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 14px 15px;
  border: 1.5px solid var(--gray-200, #e5e7eb);
  border-radius: 12px;
  background: #fff;
  cursor: pointer;
  transition: border-color 0.16s ease, box-shadow 0.16s ease, background 0.16s ease;
}
.ws-card:hover { border-color: var(--gold, #D4AF37); background: #fffdf7; }
/* The native input stays in the DOM (and focusable) but is not drawn. */
.ws-card input[type="checkbox"] {
  position: absolute;
  opacity: 0;
  width: 1px; height: 1px;
  margin: 0;
}
.ws-card-box {
  flex-shrink: 0;
  width: 22px; height: 22px;
  margin-top: 2px;
  border: 2px solid var(--gray-300, #cbd5e0);
  border-radius: 6px;
  display: flex; align-items: center; justify-content: center;
  transition: background 0.16s ease, border-color 0.16s ease;
}
.ws-card-box i { font-size: 0.7rem; color: #fff; opacity: 0; transition: opacity 0.16s ease; }
.ws-card input:checked ~ .ws-card-box {
  background: var(--gold, #D4AF37);
  border-color: var(--gold, #D4AF37);
}
.ws-card input:checked ~ .ws-card-box i { opacity: 1; }
.ws-card:has(input:checked) {
  border-color: var(--gold, #D4AF37);
  background: #fffdf5;
  box-shadow: 0 2px 10px rgba(212,175,55,0.18);
}
/* Keyboard focus must be visible even though the input itself is not. */
.ws-card:focus-within { outline: 2px solid var(--primary, #002366); outline-offset: 2px; }

.ws-card-body { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
.ws-card-name { font-weight: 700; font-size: 0.98rem; color: var(--primary, #002366); }
.ws-card-meta {
  display: flex; flex-wrap: wrap; gap: 4px 14px;
  font-size: 0.8rem; color: var(--gray-600, #4a5568);
}
.ws-meta-item { display: inline-flex; align-items: center; gap: 5px; }
.ws-meta-item i { opacity: 0.65; font-size: 0.75rem; }
.ws-card-desc { font-size: 0.85rem; line-height: 1.7; color: var(--gray-600, #4a5568); }
.ws-card-req {
  font-size: 0.8rem; line-height: 1.65;
  color: var(--gray-600, #4a5568);
  background: rgba(0,35,102,0.04);
  border-radius: 7px; padding: 6px 9px;
}
.ws-card-req strong { color: var(--primary, #002366); }

/* No workshops yet (v49). A finished notice card, not a form control that
 * looks unanswered: the picker heading is hidden by renderRegWorkshops()
 * so nothing above it asks the visitor to choose. Solid and settled
 * rather than dashed — a dashed outline reads as a placeholder waiting to
 * be filled, which is the exact impression to avoid here. */
.ws-empty {
  display: flex; align-items: flex-start; gap: 13px;
  margin: 0;
  padding: 18px 20px;
  border: 1px solid var(--gray-200, #e2e8f0);
  border-inline-start: 3px solid var(--gold, #D4AF37);
  border-radius: 14px;
  background: linear-gradient(180deg, #fdfdfb 0%, var(--gray-50, #f9fafb) 100%);
}
.ws-empty-icon {
  flex: 0 0 auto;
  width: 38px; height: 38px;
  display: grid; place-items: center;
  border-radius: 11px;
  background: rgba(212,175,55,0.13);
  color: #8a6d1f;
  font-size: 0.95rem;
}
.ws-empty-text { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.ws-empty-text strong {
  font-size: 0.95rem; font-weight: 700;
  color: var(--primary, #002366);
}
.ws-empty-text span {
  font-size: 0.85rem; line-height: 1.75;
  color: var(--gray-600, #4a5568);
}


/* ====== REGISTRATION CONFIRMATION (v36) ======
 * Replaces the old block that showed the participant their QR code the
 * instant they registered. See the markup note in index.html for why that
 * was wrong: applying is not being accepted. */
.reg-success {
  text-align: center;
  padding: 38px 26px;
  background: linear-gradient(160deg, #f6fbf7 0%, #eef7f1 100%);
  border: 1px solid rgba(56,161,105,0.28);
  border-radius: 18px;
}
.reg-success-icon {
  width: 68px; height: 68px;
  margin: 0 auto 16px;
  border-radius: 50%;
  background: rgba(56,161,105,0.12);
  display: flex; align-items: center; justify-content: center;
}
.reg-success-icon i { font-size: 2.1rem; color: #2f855a; }
.reg-success h3 {
  font-size: 1.3rem; font-weight: 700; color: #22543d; margin: 0 0 10px;
}
.reg-success-lead {
  font-size: 1rem; color: var(--gray-700, #2d3748); margin: 0 0 14px; line-height: 1.8;
}
.reg-success-note {
  font-size: 0.92rem; color: var(--gray-600, #4a5568);
  line-height: 1.95; margin: 0 auto 16px; max-width: 46ch;
}
.reg-success-thanks {
  font-size: 0.9rem; font-weight: 600; color: #2f855a; margin: 0;
}

/* ============================================================
   MEDIA GALLERY — EDITORIAL MOSAIC (v44)
   ============================================================
   Replaces the old uniform grid of bordered white cards whose
   captions sat in a text block underneath. Twenty of those read
   as a spreadsheet: every tile identical, every caption forcing
   a different card height, and more chrome on screen than
   photograph.

   The principle here is the one every serious photo gallery
   converges on — Unsplash, Behance, press and museum galleries:
   THE PHOTOGRAPH IS THE TILE. No frame, no border, no caption
   block. The image fills its cell edge to edge, and text rides
   on a gradient scrim over the image itself. Every pixel that
   is not the picture has to justify itself.

   Rhythm without measurement: true masonry needs each image's
   intrinsic aspect ratio, which we do not have server-side and
   will not block rendering to discover. Instead a deterministic
   pattern promotes some tiles to 2x2 / 2x1 / 1x2 (see spanFor()
   in app.js). It repeats every 7, so the wall never looks
   mechanical, never reflows, and behaves identically in RTL.
   ============================================================ */

.gmosaic {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  /* Fixed row height + object-fit:cover is what makes the mosaic
     gapless. Without it, mixed aspect ratios leave ragged holes. */
  grid-auto-rows: 168px;
  grid-auto-flow: dense;        /* backfill gaps left by big tiles */
  gap: 12px;
}

.gcard {
  position: relative;
  margin: 0;
  overflow: hidden;
  border-radius: 14px;
  cursor: pointer;
  background: #0a1730;          /* shows while the image decodes */
  isolation: isolate;
  transition: transform .38s cubic-bezier(.2,.7,.3,1),
              box-shadow .38s cubic-bezier(.2,.7,.3,1);
}
/* Only two promoted shapes, and both are hole-proof — see spanFor() in
   app.js for why 2-row-tall tiles were removed entirely.
   hero spans every column, so it consumes whole rows.
   wide is one row tall, so anything it leaves is backfilled. */
.gcard--hero { grid-column: 1 / -1; grid-row: span 2; }
.gcard--wide { grid-column: span 2; }

.gcard-img {
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
  transition: transform .6s cubic-bezier(.2,.7,.3,1), filter .38s ease;
}
.gcard-fallback {
  width: 100%; height: 100%;
  display: flex; align-items: center; justify-content: center;
  background: linear-gradient(135deg, #0d2350, #17376f);
  color: rgba(255,255,255,.4); font-size: 1.9rem;
}

/* The scrim. Always present but nearly invisible until hover, so a
   wall of images stays a wall of images rather than a wall of
   dark boxes. On touch it is stronger, because there is no hover
   and the caption has to be legible immediately. */
.gcard-veil {
  position: absolute; inset: 0;
  background: linear-gradient(to top,
              rgba(3,10,28,.86) 0%, rgba(3,10,28,.42) 34%, rgba(3,10,28,0) 62%);
  opacity: .55;
  transition: opacity .38s ease;
  pointer-events: none;
}

.gcard-cap {
  position: absolute; inset-inline: 0; bottom: 0;
  padding: 14px 15px 13px;
  color: #fff;
  transform: translateY(6px);
  opacity: .92;
  transition: transform .38s cubic-bezier(.2,.7,.3,1), opacity .38s ease;
  pointer-events: none;
}
.gcard-title {
  display: block;
  font-weight: 800;
  font-size: .95rem;
  line-height: 1.45;
  text-shadow: 0 1px 10px rgba(0,0,0,.5);
  /* Two lines maximum: a long title must not swallow the picture. */
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
.gcard-text {
  display: block;
  font-size: .8rem;
  line-height: 1.55;
  color: rgba(255,255,255,.82);
  margin-top: 3px;
  max-height: 0; opacity: 0; overflow: hidden;
  transition: max-height .38s ease, opacity .3s ease, margin-top .38s ease;
}

/* Video: a real play affordance, centred, not a corner sticker. */
.gcard-play {
  position: absolute; inset: 0; margin: auto;
  width: 56px; height: 56px;
  display: flex; align-items: center; justify-content: center;
  border-radius: 50%;
  background: rgba(255,255,255,.92);
  color: var(--primary, #002366);
  font-size: .95rem;
  box-shadow: 0 6px 22px rgba(0,0,0,.32);
  transition: transform .38s cubic-bezier(.2,.7,.3,1), background .3s ease;
  pointer-events: none;
}
.gcard-play i { margin-inline-start: 3px; }   /* optical centring of ▶ */

/* Corner affordance that says "this opens". Hidden until hover so it
   never competes with the photograph. */
.gcard-zoom {
  position: absolute; top: 11px; inset-inline-end: 11px;
  width: 34px; height: 34px;
  display: flex; align-items: center; justify-content: center;
  border-radius: 50%;
  background: rgba(255,255,255,.16);
  backdrop-filter: blur(7px); -webkit-backdrop-filter: blur(7px);
  border: 1px solid rgba(255,255,255,.25);
  color: #fff; font-size: .78rem;
  opacity: 0; transform: scale(.82);
  transition: opacity .3s ease, transform .3s cubic-bezier(.2,.7,.3,1);
  pointer-events: none;
}

/* ---- hover / focus ---- */
@media (hover: hover) {
  .gcard:hover { transform: translateY(-3px); box-shadow: 0 18px 40px rgba(3,12,35,.30); }
  .gcard:hover .gcard-img { transform: scale(1.07); }
  .gcard:hover .gcard-veil { opacity: 1; }
  .gcard:hover .gcard-cap { transform: translateY(0); opacity: 1; }
  .gcard:hover .gcard-text { max-height: 4.6em; opacity: 1; margin-top: 4px; }
  .gcard:hover .gcard-zoom { opacity: 1; transform: scale(1); }
  .gcard:hover .gcard-play { transform: scale(1.09); background: #fff; }
}
.gcard:focus-visible {
  outline: 3px solid var(--gold, #D4AF37);
  outline-offset: 3px;
}
.gcard:focus-visible .gcard-veil { opacity: 1; }
.gcard:focus-visible .gcard-cap { transform: translateY(0); opacity: 1; }
.gcard:focus-visible .gcard-zoom { opacity: 1; transform: scale(1); }

/* ---- album bands ---- */
.galbum { margin-bottom: 54px; }
.galbum:last-child { margin-bottom: 0; }
.galbum-head { margin-bottom: 18px; }
.galbum-heading {
  display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap;
  padding-bottom: 11px;
  border-bottom: 1px solid var(--gray-200, #e5e7eb);
}
.galbum-title {
  position: relative;
  margin: 0;
  font-size: 1.22rem;
  font-weight: 800;
  color: var(--primary, #002366);
  padding-inline-start: 15px;
}
/* A gold rule rather than a bullet: reads as a section marker, and
   mirrors correctly in both writing directions. */
.galbum-title::before {
  content: '';
  position: absolute; inset-inline-start: 0; top: .18em; bottom: .18em;
  width: 4px; border-radius: 3px;
  background: linear-gradient(180deg, #E3C25A, var(--gold, #D4AF37));
}
.galbum-count {
  font-size: .76rem; font-weight: 700;
  color: var(--gray-500, #6b7280);
  background: var(--gray-100, #f3f4f6);
  padding: 3px 11px; border-radius: 20px;
}
.galbum-desc {
  margin: 9px 0 0;
  font-size: .9rem; line-height: 1.85;
  color: var(--gray-500, #6b7280);
  max-width: 68ch;
}


@media (prefers-reduced-motion: reduce) {
  .gcard, .gcard-img, .gcard-cap, .gcard-veil, .gcard-zoom, .gcard-play, .gcard-text {
    transition: none !important;
  }
  .gcard:hover { transform: none; }
  .gcard:hover .gcard-img { transform: none; }
}


/* ══════════════════════════════════════════════════════════════════
 * RESPONSIVE LAYER — MUST REMAIN THE LAST THING IN THIS FILE
 * ══════════════════════════════════════════════════════════════════
 *
 * Rules here override every base rule above simply by coming later,
 * so a mobile rule placed ABOVE a base rule silently does nothing.
 *
 * That is not hypothetical. Before this section was consolidated
 * (v53) the file had NINE top-level max-width blocks scattered
 * through it, and six mobile rules were dead because the base rule
 * they meant to override was defined further down the file:
 *
 *     .lang-toggle          never shrank on phones
 *     html.lang-en .scroll-top   kept its desktop offset
 *     .cme-wrap             kept flex-start + 26px padding
 *     .cme-badge            stayed 120px instead of 100px
 *     .cme-points           stayed 2.3rem instead of 1.9rem
 *     .speaker-bio          never centred
 *
 * THE RULE (checked by 5-أدوات/فحص-الكود.command):
 *   1. Every top-level max-width block lives in this section.
 *   2. Each breakpoint appears EXACTLY ONCE.
 *   3. Breakpoints run in DESCENDING order (widest first), so the
 *      narrower one always wins on a narrow screen.
 *   4. Nothing but @media blocks comes after this comment.
 *
 * To add a mobile rule: find the block for its breakpoint below and
 * put it there. Do NOT open a new block, and do NOT add a rule to
 * the base layer above expecting it to win — it will not.
 * See §9.21 and §9.57 in PROJECT_DOCUMENTATION.md.
 * ══════════════════════════════════════════════════════════════ */

@media (max-width: 1320px) {
  .nav-links { gap: 0; }
  .nav-btn { padding: 7px 7px; font-size: 0.8rem; }
}

@media (max-width: 1230px) {
  .nav-toggle { display: flex; }
  .nav-links { display: none; }
  /* The dropdown is absolutely positioned against the navbar, whose real
   * height is 64px until the 900px block shrinks it to 56px. Pinning it
   * to the element's own bottom edge keeps it correct at both. */
  .nav-dropdown {
    position: fixed;
    top: 64px;                          /* .nav-container min-height here */
    max-height: calc(100vh - 64px);
    overflow-y: auto;
    /* Setting overflow-y alone makes overflow-x compute to `auto` too, so
     * a few stray pixels of row margin would raise a horizontal
     * scrollbar across the whole menu. */
    overflow-x: hidden;
    -webkit-overflow-scrolling: touch;
  }
}

@media (max-width: 900px) {

  /* ── merged from source block 1 of 2 (was a separate top-level @media 900px block) ── */
  .nav-toggle { display: flex; }
  .nav-links { display: none; }
  /* BUG FIX (round 2 -- the round-1 fix at max-width:380px only covered
   * phones narrower than 380px. Real device width varies a lot more than
   * that: iPhone 12/13/14 report 390px, many Androids 412-430px. Any
   * phone in that 381-900px gap still hit the base white-space:nowrap +
   * ellipsis rule (line 164) and truncated exactly as before -- proven
   * live: the owner's own phone still showed it cut after the first fix
   * was confirmed deployed and cache was not the cause. Moving the wrap
   * behaviour here, to the SAME breakpoint the hamburger nav itself
   * turns on at, covers every phone this menu ever appears on, not one
   * guessed width. */
  .nav-brand-ar {
    font-size: 0.82rem;
    white-space: normal;
    overflow: visible;
    text-overflow: clip;
    line-height: 1.2;
  }
  .nav-brand-en { font-size: 0.6rem; }
  .nav-container { height: 56px; padding: 0 14px; }

  /* BUG FIX: .nav-dropdown is position:absolute inside a position:fixed
   * .navbar, so it renders pinned to the screen with no default height
   * limit -- on 15 nav links (~700px of rows) it silently ran past the
   * bottom of the viewport on shorter phones, with no way to scroll down
   * to the missing ones (the dropdown itself never scrolled, and being
   * fixed-on-screen it doesn't move with the page either). Switching to
   * position:fixed with an explicit max-height (viewport minus the real
   * navbar height, matching .nav-container's height just above) plus
   * overflow-y:auto makes the dropdown internally scrollable so every
   * link is always reachable, however many there are. */
  .nav-dropdown {
    position: fixed;
    top: 56px;
    max-height: calc(100vh - 56px);
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
  }

  .hero-title { font-size: clamp(1.7rem, 6vw, 2.5rem); }
  .countdown { gap: 8px; }
  .cd-num { font-size: 1.5rem; }
  .cd-label { font-size: 0.68rem; }

  .about-content { flex-direction: column; gap: 20px; }
  .about-logo-item { width: 160px; height: 160px; }
  .about-logos { gap: 14px; }

  .footer-grid {
    grid-template-columns: 1fr 1fr;
    grid-template-areas: "brand brand" "links contact";
    gap: 14px 12px;
  }
  .footer-brand { grid-area: brand; }
  .footer-links { grid-area: links; }
  .footer-contact { grid-area: contact; }

  .day-tabs { justify-content: center; }
  .schedule-item { flex-direction: column; gap: 8px; }
  .schedule-time { min-width: unset; }
  .reg-form { grid-template-columns: 1fr; gap: 12px; }
  .about-grid { grid-template-columns: repeat(2, 1fr); }
  .contact-grid { grid-template-columns: 1fr; }
  .contact-map-card { height: 240px; }
  .section-title h2 { font-size: 1.4rem; }

  /* 16px minimum on inputs — §9.7, prevents iOS zoom.
   *
   * ⚠️ THE .form-group PREFIX IS LOAD-BEARING (v91). Without it this rule
   * was (0,0,1) -- a bare element selector -- while the base rule
   * `.form-group select { font-size: .95rem }` is (0,1,1). A @media block
   * adds NO specificity, so the base rule won and every <select>,
   * <textarea> and .form-file inside a .form-group stayed at 15.2px on
   * mobile. iOS Safari zooms the whole page on focus under 16px, so the
   * zoom fix this comment has claimed since §9.7 never actually applied to
   * a single dropdown -- measured live at 375px: regSpecialty = 15.2px.
   *
   * Plain <input> happened to work only because the base rule it was
   * fighting (`input, select, textarea { font-size: 1rem }`) is also
   * (0,0,1), so source order decided it. Selects had a class-level rule
   * and lost. Matching the (0,1,1) here and relying on source order --
   * this block is at the END of the file, per the consolidated-breakpoints
   * rule -- is what makes it finally win. */
  input[type="text"], input[type="email"], input[type="tel"],
  input[type="date"], input[type="number"], select, textarea, .form-file,
  .form-group input, .form-group select, .form-group textarea,
  .form-group .form-file {
    font-size: 16px;
    min-height: 46px;
  }
  textarea { min-height: 110px; }
  .btn, .btn-gold, button[type="submit"] { min-height: 48px; }
  .dropzone { padding: 14px 10px; }

  .dyn-band { padding: 44px 0; }
  .dyn-row, .dyn-row-flip { flex-direction: column; gap: 24px; }
  .dyn-row-media { flex: none; width: 100%; }
  .dyn-img-frame, .dyn-gallery { aspect-ratio: 16 / 10; }

  /* ── merged from source block 2 of 2 (was a separate top-level @media 900px block) ── */
  .gmosaic { grid-template-columns: repeat(auto-fill, minmax(170px, 1fr)); grid-auto-rows: 146px; gap: 10px; }
}

@media (max-width: 600px) {

  /* ── merged from source block 1 of 3 (was a separate top-level @media 600px block) ── */
  .footer-bottom { justify-content: center; text-align: center; gap: 4px; }

  /* ── merged from source block 2 of 3 (was a separate top-level @media 600px block) ── */
  .nav-brand-ar { font-size: 0.76rem; }
  .nav-brand-en { font-size: 0.56rem; }
  .nav-brand-code { display: none; }
  .nav-container { height: 52px; padding: 0 12px; }
  .nav-dropdown { top: 52px; max-height: calc(100vh - 52px); }

  /* Explicit height, not min-height: a min-height here competes with the
   * base .hero min-height:85vh (larger value wins, so a smaller mobile
   * value silently did nothing). Explicit height is unambiguous. */
  .hero { height: 78vh; min-height: 420px; }
  /* 14px left only ~17px of clear space either side of the quote,
     countdown and buttons on a 375px phone -- everything ran almost
     edge-to-edge, which is what made the page feel "zoomed in". 22px
     gives real breathing room without narrowing the text noticeably. */
  .hero-content { padding: 20px 22px 24px; }
  .hero-flag-badge { padding: 5px 14px 5px 10px; margin-bottom: 10px; gap: 7px; }
  .hero-flag-badge-icon { width: 20px; }
  .hero-flag-badge span { font-size: 0.68rem; }
  .hero-dates { padding: 6px 16px; margin-bottom: 12px; }
  .hero-title { font-size: clamp(1.5rem, 6.5vw, 2rem); line-height: 1.2; margin-bottom: 4px; }
  .hero-subtitle { font-size: 0.8rem; margin-bottom: 6px; }
  .hero-quote { font-size: 0.8rem; margin-bottom: 14px; }

  /* Countdown: one horizontal row always, never 2x2 */
  .countdown { flex-wrap: nowrap; gap: 4px; margin-bottom: 16px; }
  .cd-item { flex: 1 1 0; min-width: 0; padding: 7px 3px; }
  .cd-num { font-size: 1.3rem; }
  .cd-label { font-size: 0.58rem; margin-top: 2px; }

  .hero-actions { flex-direction: column; gap: 8px; }
  .hero-actions .btn { width: 100%; padding: 10px 16px; font-size: 0.85rem; justify-content: center; border-radius: 40px; }

  /* 20px, not 14px. With .section-pad no longer zeroing these (see the note
   * at the top of the file), this is now the single value that controls how
   * far every section's text sits from the screen edge. 14px reads as
   * cramped on a 375px phone -- Arabic body text especially, because the
   * justified right edge lands hard against the bezel. */
  .container { padding-left: 20px; padding-right: 20px; }
  .section-title h2 { font-size: 1.2rem; }
  .section-title { margin-bottom: 20px; }
  /* Block padding only — see the note at .section-pad near the top of this
   * file. The shorthand here would re-zero the side padding on exactly the
   * screen size where it matters most. */
  .section-pad { padding-top: 36px; padding-bottom: 36px; }

  .committees-grid { grid-template-columns: 1fr 1fr; gap: 10px; }
  .committee-card { padding: 14px 10px; }

  /* ── REGISTRATION FORMS ON A PHONE (v99) ──────────────────────────
   * The owner's report: "the edges look cut off or squeezed."
   * Both halves of that were real, and measured at 375px:
   *
   * 1. CUT OFF — the submission panel sat OUTSIDE .container because the
   *    container's closing tag was one panel too early in index.html, so
   *    that form ran edge to edge with its border and rounded corners
   *    clipped by the viewport while the attendance form had a 20px
   *    gutter. Fixed in the markup, not here.
   *
   * 2. SQUEEZED — padding:32px is a desktop value. With the container's
   *    20px gutter it left the FIELDS just 269px of a 375px screen: 28%
   *    of the phone spent on empty margin, which is why headings wrapped
   *    onto three lines. 18px here returns ~34px to the fields and still
   *    reads as a card rather than a bare list. */
  .reg-form-card, .reg-workshops-card { padding: 20px 18px; }
  .reg-form-card h3, .reg-workshops-card h3 { font-size: 1.05rem; margin-bottom: 16px; }
  .member-photo { width: 92px; height: 92px; }
  .committee-card h3 { font-size: 0.82rem; }
  .member-role { font-size: 0.72rem; }

  /* Four logos: 2x2 grid — a 4-across row would be unreadably small here.
   * Column layout with a tight gap: the flex-basis:480px on .about-desc
   * was forcing a full-width row and leaving a big gap before the logos. */
  .about-content { flex-direction: column; gap: 16px; }
  .about-content .about-desc { flex: none; width: 100%; margin-bottom: 0; }
  .about-logos { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; width: 100%; margin-top: 0; }
  .about-logo-item { width: 100%; height: 100px; padding: 10px; }
  .about-grid { grid-template-columns: 1fr 1fr; }

  .footer { padding-top: 14px; margin-top: 28px; }
  .footer-grid { gap: 10px; padding-bottom: 10px; }
  .sponsors-grid, .posters-grid, .tracks-grid, .workshops-grid { grid-template-columns: 1fr; }

  .dyn-band { padding: 32px 0; }
  .dyn-row { gap: 18px; }
  .dyn-img-frame, .dyn-gallery { aspect-ratio: 4 / 3; }
  .dyn-gallery--2, .dyn-gallery--3, .dyn-gallery--many { aspect-ratio: 1 / 1; }
  .dyn-section-title { font-size: 1.2rem; }
  .dyn-row-textonly { text-align: right; }
  .dyn-row-textonly .dyn-eyebrow { margin-inline: 0; }
  .dyn-article-photo { width: 72px; height: 72px; }
  .dyn-article-title { font-size: 1.2rem; }
  .dyn-article-desc { font-size: 1rem; line-height: 1.9; }

  /* Detail modals (tracks / sponsors / posters) */
  .detail-modal h3 { font-size: 1.2rem; }
  .modal-visual--img { width: 120px; height: 120px; }
  .modal-visual--icon { width: 90px; height: 90px; font-size: 2rem; }
  .modal-visual--poster, .modal-visual--poster img { max-height: 44vh; }

  /* Gallery lightbox nav buttons (v22) — smaller, closer to the edge */
  .lb-nav { width: 38px; height: 38px; }
  .lb-nav.lb-next { left: 4px; }
  .lb-nav.lb-prev { right: 4px; }

  @media (max-width: 380px) {
    /* BUG FIX (by request): at this width the full title ("المؤتمر
     * العلمي الأول لمشفى حلب الجامعي") measured 208px against only
     * 170px available next to the menu/compose/English controls --
     * the base rule's white-space:nowrap + ellipsis (line 164) cut it
     * to "المؤتمر العلمي الأول لمشفى حل…", which read as broken, not
     * abbreviated. Shrinking the font further would make two whole
     * words illegible just to save 38px. There is height to spare in
     * this row (52px, vs. ~23px two lines of this text actually need),
     * so wrapping to two lines is the fix, not a smaller size. */
    .nav-brand-ar {
      font-size: 0.7rem;
      white-space: normal;
      overflow: visible;
      text-overflow: clip;
      line-height: 1.2;
    }
    .hero-title { font-size: 1.38rem; }
    .cd-num { font-size: 1.1rem; }
    .about-logo-item { height: 88px; }
    .committees-grid { grid-template-columns: 1fr; }
  }
  /* consolidated from a separate @media block (§9.21) */
  .lang-toggle { padding: 5px 10px; font-size: 0.66rem; }
  /* consolidated from a separate @media block (§9.21) */
  html.lang-en .scroll-top { right: 20px; }
  /* consolidated from a separate @media block (§9.21) */
  .cme-wrap { flex-direction: column; align-items: center; text-align: center; padding: 20px 16px; }
  .cme-badge { width: 100px; height: 100px; }
  .cme-points { font-size: 1.9rem; }
  .speaker-bio { text-align: center; }

  /* ── merged from source block 3 of 3 (was a separate top-level @media 600px block) ── */
  /* Two firm columns on a phone. auto-fill would drop to one column and
     turn the mosaic into a tall single-file stack. */
  .gmosaic { grid-template-columns: repeat(2, 1fr); grid-auto-rows: 132px; gap: 8px; }
  .gcard { border-radius: 11px; }
  /* No hover on touch: the caption must be readable without interaction. */
  .gcard-veil { opacity: .88; }
  .gcard-cap { transform: none; opacity: 1; padding: 10px 11px 9px; }
  .gcard-title { font-size: .82rem; -webkit-line-clamp: 2; }
  .gcard-text { display: none; }          /* the lightbox carries the detail */
  .gcard-play { width: 44px; height: 44px; font-size: .8rem; }
  .gcard-zoom { display: none; }
  .galbum { margin-bottom: 38px; }
  .galbum-title { font-size: 1.05rem; }
}

@media (max-width: 430px) {
  /* Below 430px the 20px gutter is itself worth trimming: it is the
     difference between a two-line and a three-line field label. The card
     keeps a visible inset so its border never touches the screen. */
  .container { padding-left: 14px; padding-right: 14px; }
  .reg-form-card, .reg-workshops-card { padding: 16px 14px; }
  .nav-cta { padding: 9px 12px; }
  .nav-cta span { display: none; }
}

@media (max-width: 420px) {
  .ws-empty { padding: 15px 15px; gap: 11px; }
  .ws-empty-icon { width: 32px; height: 32px; font-size: 0.85rem; }
}
