/* =========================================================================
   PPG (Pekan Produsente Groep) - ppgza.org
   Shared stylesheet for all pages.

   BRAND SWAP: this file uses a placeholder agri-appropriate palette and
   system/Google Fonts. When PPG's real logo/colors/fonts are ready, only
   the :root variables below and the @import font line need to change -
   nothing else in this file or in the page markup should need to move.
   ========================================================================= */

@import url('https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,600;0,700;1,700&family=Inter:wght@400;500;600&family=Oswald:wght@700&family=Playfair+Display:ital,wght@1,500&display=swap');

:root {
  /* --- FARM PALETTE: brown / green / white (About, Tickets, Signup) --- */
  --color-primary: #2f5233;      /* deep field green */
  --color-primary-dark: #1f3a22;
  --color-primary-light: #6b8e4e;/* leaf green */
  --color-accent: #7a4a24;       /* soil brown */
  --color-accent-dark: #5c3618;
  --color-earth: #6b4423;        /* soil brown (text accents) */
  --color-bg: #f7f6f2;           /* near-white */
  --color-surface: #ffffff;
  --color-text: #262620;
  --color-text-muted: #5b5b52;
  --color-border: #e5e0d3;

  /* Pecan husk decoration defaults (overridden per-page below) */
  --husk-color: #4a7c3f;
  --nut-color: #6b4423;

  /* --- PLACEHOLDER TYPOGRAPHY (swap fonts here) --- */
  --font-heading: 'Poppins', 'Segoe UI', Arial, sans-serif;
  --font-body: 'Inter', 'Segoe UI', Arial, sans-serif;

  --radius: 10px;
  --shadow: 0 4px 18px rgba(34, 61, 38, 0.08);
  --max-width: 1080px;
}

/* ---------------------------------------------------------------------
   Pecan husk page accents - green husks on About, brown husks on
   Tickets/Signup. Stylised original SVG art (not photography), sized
   and placed by .pecan-deco below.
   --------------------------------------------------------------------- */
body.husk-green {
  --husk-color: #4a7c3f;
  --nut-color: #6b4423;
  /* Real photo, uploaded as pecan-green.jpg alongside the other site files.
     ?v=2 is a cache-busting version tag - bump it whenever the image changes
     so returning visitors' browsers fetch the new file instead of an old
     cached copy. */
  --hero-photo: url('pecan-green.jpg?v=2');
}
body.husk-brown {
  --husk-color: #6b4a2f;
  --nut-color: #4a3220;
  /* Real photo, uploaded as pecan-brown.jpg alongside the other site files. */
  --hero-photo: url('pecan-brown.jpg?v=2');
}
/* Symposium (main) page: same "nuts behind the header" treatment as
   About, but its own close-up photo -- now young green pecans still on
   the tree, among the leaves. Uploaded as pecan-hero.jpg. */
body.husk-hero {
  --husk-color: #4a7c3f;
  --nut-color: #6b4423;
  --hero-photo: url('pecan-hero.jpg?v=2');
}
/* Brown-toned wash for husk-brown pages, replacing the default green
   wash so the brown photo reads as brown rather than green-tinted. */
body.husk-brown .hero {
  background:
    linear-gradient(160deg, rgba(92,54,24,0.88) 0%, rgba(58,36,16,0.85) 100%),
    var(--hero-photo, none) center/cover no-repeat,
    repeating-linear-gradient(135deg, rgba(255,255,255,0.04) 0 2px, transparent 2px 24px);
}
/* Symposium pages: same green wash as the default .hero rule below, but
   5 percentage points more transparent (0.92->0.87, 0.88->0.83) so the
   new tree photo reads through a bit more clearly behind the header. */
body.husk-hero .hero {
  background:
    linear-gradient(160deg, rgba(47,82,51,0.87) 0%, rgba(34,61,38,0.83) 100%),
    var(--hero-photo, none) center/cover no-repeat,
    repeating-linear-gradient(135deg, rgba(255,255,255,0.04) 0 2px, transparent 2px 24px);
}
/* .hero picks up --hero-photo as a background layer beneath its color
   wash (see .hero rule below) - missing file just falls through to the
   plain color wash, so this is safe before the photos are uploaded. */

.pecan-deco {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  overflow: hidden;
}
.pecan-deco svg { position: absolute; opacity: 0.15; }
.pecan-deco svg:nth-of-type(1) { top: -34px; right: -24px; }
.pecan-deco svg:nth-of-type(2) { bottom: -18px; left: 4%; }
.pecan-deco svg:nth-of-type(3) { top: 38%; right: 14%; display: none; }
@media (min-width: 761px) {
  .pecan-deco svg:nth-of-type(3) { display: block; }
}
.pecan-husk { fill: var(--husk-color); }
.pecan-nut { fill: var(--nut-color); }
.pecan-vein { stroke: rgba(0,0,0,0.25); stroke-width: 2; fill: none; }

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
  margin: 0;
  font-family: var(--font-body);
  color: var(--color-text);
  background: var(--color-bg);
  line-height: 1.6;
}

img { max-width: 100%; display: block; }

a { color: var(--color-primary); }

h1, h2, h3, h4 {
  font-family: var(--font-heading);
  color: var(--color-primary-dark);
  line-height: 1.2;
  margin: 0 0 0.5em;
}

p { margin: 0 0 1em; }

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

/* Skip link for accessibility */
.skip-link {
  position: absolute;
  left: -999px;
  top: auto;
  background: var(--color-primary-dark);
  color: #fff;
  padding: 10px 16px;
  z-index: 1000;
  border-radius: 0 0 var(--radius) 0;
}
.skip-link:focus { left: 0; top: 0; }

/* ---------------------------------------------------------------------
   Header / Nav
   --------------------------------------------------------------------- */
.site-header {
  position: sticky;
  top: 0;
  z-index: 100;
  background: var(--color-surface);
  border-bottom: 1px solid var(--color-border);
}

.nav-wrap {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 24px;
  max-width: var(--max-width);
  margin: 0 auto;
}

.brand {
  display: flex;
  align-items: center;
  gap: 10px;
  text-decoration: none;
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 1.25rem;
  color: var(--color-primary-dark);
}

/* Placeholder logo mark - swap for the real PPG logo image later */
.brand-mark {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-accent) 100%);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  font-weight: 700;
  font-size: 0.95rem;
  flex-shrink: 0;
}

.nav-toggle {
  display: none;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  flex-shrink: 0;
  background: rgba(47,82,51,0.06);
  border: 1px solid rgba(47,82,51,0.3);
  border-radius: 8px;
  cursor: pointer;
}
.nav-toggle:hover,
.nav-toggle[aria-expanded="true"] {
  background: rgba(47,82,51,0.14);
  border-color: var(--color-primary);
}
.nav-toggle-icon {
  display: block;
  width: 22px;
  height: 3px;
  border-radius: 3px;
  background: var(--color-primary-dark);
  position: relative;
  transition: background 0.15s ease;
}
.nav-toggle-icon::before,
.nav-toggle-icon::after {
  content: "";
  position: absolute;
  left: 0;
  width: 22px;
  height: 3px;
  border-radius: 3px;
  background: var(--color-primary-dark);
  transition: transform 0.2s ease, top 0.2s ease, opacity 0.2s ease;
}
.nav-toggle-icon::before { top: -7px; }
.nav-toggle-icon::after { top: 7px; }

/* Morph into an "X" once the menu is open, so the button's state is obvious. */
.nav-toggle[aria-expanded="true"] .nav-toggle-icon { background: transparent; }
.nav-toggle[aria-expanded="true"] .nav-toggle-icon::before { top: 0; transform: rotate(45deg); }
.nav-toggle[aria-expanded="true"] .nav-toggle-icon::after { top: 0; transform: rotate(-45deg); }

.site-nav ul {
  list-style: none;
  display: flex;
  gap: 28px;
  margin: 0;
  padding: 0;
}

.site-nav a {
  text-decoration: none;
  color: var(--color-text);
  font-weight: 500;
  padding: 6px 2px;
  border-bottom: 2px solid transparent;
}

.site-nav a:hover {
  color: var(--color-primary-dark);
  border-bottom-color: var(--color-accent);
}

.nav-cta {
  background: var(--color-primary);
  color: #fff !important;
  padding: 9px 18px !important;
  border-radius: 999px;
  border-bottom: none !important;
}
.nav-cta:hover { background: var(--color-primary-dark); }

.lang-toggle {
  background: none;
  border: 1px solid var(--color-border);
  border-radius: 999px;
  padding: 6px 14px;
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 0.85rem;
  color: var(--color-text);
  cursor: pointer;
}
.lang-toggle:hover { border-color: var(--color-primary); color: var(--color-primary-dark); }

@media (max-width: 760px) {
  .nav-toggle { display: inline-flex; align-items: center; justify-content: center; }
  .site-nav {
    display: none;
    width: 100%;
    order: 3;
  }
  .site-nav.is-open { display: block; }
  .nav-wrap { flex-wrap: wrap; }
  .site-nav ul {
    flex-direction: column;
    gap: 4px;
    padding: 14px 0 6px;
    border-top: 1px solid var(--color-border);
    margin-top: 12px;
  }
  .nav-cta { display: inline-block; }
}

/* ---------------------------------------------------------------------
   Hero
   --------------------------------------------------------------------- */
.hero {
  position: relative;
  overflow: hidden;
  background:
    linear-gradient(160deg, rgba(47,82,51,0.92) 0%, rgba(34,61,38,0.88) 100%),
    var(--hero-photo, none) center/cover no-repeat,
    repeating-linear-gradient(135deg, rgba(255,255,255,0.04) 0 2px, transparent 2px 24px);
  color: #fff;
  padding: 80px 0 70px;
}

.hero .container { max-width: var(--max-width); position: relative; z-index: 1; }

.eyebrow {
  display: inline-block;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--color-accent);
  background: rgba(255,255,255,0.08);
  border: 1px solid rgba(255,255,255,0.25);
  padding: 6px 14px;
  border-radius: 999px;
  margin-bottom: 18px;
}
/* Dark hero backgrounds need light text - brown reads as illegible there. */
.hero .eyebrow {
  color: #fff;
  background: rgba(255,255,255,0.16);
  border-color: rgba(255,255,255,0.38);
}

.hero h1 {
  color: #fff;
  font-size: clamp(2rem, 4vw, 3rem);
  /* Wide enough that short headlines (like the symposium slogan) sit on
     one line on any normal desktop viewport -- this is a readability
     ceiling for a hypothetical very long headline on an ultrawide
     monitor, not a target width, so it should rarely actually bind. */
  max-width: 40ch;
}
/* Slogan treatment -- italicized so it reads as PPG's tagline rather than
   a plain headline. Two variants in play while we compare them: the live
   site keeps the bold heading font (Poppins, now with a true italic
   weight loaded above) just slanted; /fnb trials a distinct serif italic
   using Playfair Display, imported but otherwise unused on the site, for
   a more editorial "signature line" look. */
.hero h1.tagline {
  font-style: italic;
}
.hero h1.tagline-serif {
  font-family: 'Playfair Display', Georgia, serif;
  font-style: italic;
  font-weight: 500;
}

.hero p.lead {
  color: rgba(255,255,255,0.88);
  font-size: 1.1rem;
  max-width: 60ch;
}

.hero-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 14px 28px;
  margin: 26px 0 30px;
  padding: 0;
  list-style: none;
}
.hero-meta li {
  display: flex;
  align-items: center;
  gap: 8px;
  font-weight: 500;
  color: #fff;
}
.hero-meta .dot {
  width: 8px; height: 8px; border-radius: 50%;
  background: #fff;
  opacity: 0.85;
  flex-shrink: 0;
}
.hero-meta a {
  /* overrides the global link color, which is a dark green that would
     be nearly invisible against the hero photo behind it - stays the
     same white as the rest of this list, with an underline so it still
     reads as a link (a maps link to the Border Hotel address). */
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 2px;
}
.hero-meta a:hover,
.hero-meta a:focus-visible { text-decoration-thickness: 2px; }

.btn {
  display: inline-block;
  font-family: var(--font-body);
  font-weight: 600;
  padding: 13px 26px;
  border-radius: 999px;
  text-decoration: none;
  border: 2px solid transparent;
  cursor: pointer;
  font-size: 1rem;
}
.btn-primary {
  background: var(--color-accent);
  color: #fff;
}
.btn-primary:hover { background: var(--color-accent-dark); }
.btn-outline {
  border-color: rgba(255,255,255,0.6);
  color: #fff;
  background: transparent;
}
.btn-outline:hover { background: rgba(255,255,255,0.12); }
.btn-row { display: flex; gap: 14px; flex-wrap: wrap; }

/* Schedule note under the hero's ticket button - the day's two start
   times, kept quiet (small, translucent) so the button stays the loud
   thing in the hero. */
.hero-schedule {
  margin-top: 12px;
  font-size: 0.85rem;
  font-weight: 500;
  letter-spacing: 0.02em;
  color: rgba(255,255,255,0.75);
}

/* ---------------------------------------------------------------------
   Sections
   --------------------------------------------------------------------- */
section.block { padding: 64px 0; }
section.block.alt { background: var(--color-surface); border-top: 1px solid var(--color-border); border-bottom: 1px solid var(--color-border); }

.section-head { max-width: 60ch; margin-bottom: 36px; }
.section-head .eyebrow { background: rgba(47,82,51,0.08); border-color: rgba(47,82,51,0.18); color: var(--color-primary); }

.grid {
  display: grid;
  gap: 24px;
}
.grid-3 { grid-template-columns: repeat(3, 1fr); }
.grid-2 { grid-template-columns: repeat(2, 1fr); }
@media (max-width: 760px) {
  .grid-3, .grid-2 { grid-template-columns: 1fr; }
}

.card {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  padding: 26px;
  box-shadow: var(--shadow);
}

/* ---------------------------------------------------------------------
   Host teaser block (Symposium page)
   Designed for a simple content swap: once the host is confirmed and
   paid, replace the placeholder avatar with a real <img>, swap the
   name/title text, and swap the bio paragraph. No layout changes needed.
   --------------------------------------------------------------------- */
.host-teaser {
  /* Fixed kyknet colors (client hex), independent of page theme:
     F7C700 gold / 0081C3 bright blue / 004A92 deep blue */
  background: linear-gradient(135deg, #004A92 0%, #0081C3 100%);
  color: #fff;
  border-radius: var(--radius);
  padding: 36px;
  display: flex;
  gap: 28px;
  align-items: center;
  flex-wrap: wrap;
}
.host-avatar {
  width: 150px;
  height: 150px;
  border-radius: 50%;
  background: rgba(255,255,255,0.12);
  border: 2px dashed rgba(255,255,255,0.5);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.host-avatar svg { width: 44px; height: 44px; opacity: 0.85; }
.host-avatar svg.mask-icon { width: 95px; height: 48px; opacity: 1; }
.host-avatar img.mask-icon { width: 128px; height: auto; opacity: 1; }
/* Real, confirmed host photo -- fills the circle instead of floating a
   small icon inside it. Kept as a modifier (.has-photo) so the dashed
   placeholder look above stays available for any future unnamed host. */
.host-avatar.has-photo {
  /* Frame bumped +15% (225px -> 258.75px) so the whole profile area
     reads bigger, on top of the tighter face crop below. */
  width: 258.75px;
  height: 258.75px;
  background: none;
  border: 3px solid rgba(255,255,255,0.35);
  overflow: hidden;
}
.host-avatar.has-photo img.host-photo {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 50%;
  /* Zoomed in further on the host's face (now 20% total) and the
     transform-origin moved up in stages (28% -> 15% -> 10% -> 5%) so the
     crop's field of view keeps shifting higher, leaving a clearer
     margin of background above her head instead of cropping tight to
     the hairline. overflow:hidden on the parent frame keeps the badge
     itself exactly the same size (aside from the +15% bump above). */
  transform: scale(1.2);
  transform-origin: 50% 5%;
}
/* FNB test page only: host photo shown as a rounded square instead of
   a circle, per feedback. Everything else (frame size, zoom, crop
   position) stays identical to the circular version used elsewhere --
   only the corner treatment changes, via this modifier class. */
.host-avatar.has-photo.square-photo,
.host-avatar.has-photo.square-photo img.host-photo {
  border-radius: var(--radius);
}
.host-info { flex: 1; min-width: 220px; }
.host-info .eyebrow { background: rgba(247,199,0,0.16); border-color: rgba(247,199,0,0.45); color: #F7C700; }
.host-info h3 { color: #fff; margin-bottom: 6px; font-size: 1.4rem; }
.host-info .host-name-placeholder { font-style: italic; color: rgba(255,255,255,0.75); font-weight: 500; }
.host-info .host-name-placeholder.confirmed { font-style: normal; color: #fff; font-weight: 700; }
.host-info p { color: rgba(255,255,255,0.85); margin-bottom: 0; }
/* kykNET and Carte Blanche are linked out to their DStv show pages --
   plain inline links, styled like normal text (no brand colours, custom
   fonts or outline effects) so they read as part of the paragraph, with
   an underline as the only link affordance. */
.host-bio a.brand-kyknet-link,
.host-bio a.brand-carteblanche-link {
  color: inherit;
  text-decoration: underline;
  text-decoration-color: rgba(255,255,255,0.45);
  text-underline-offset: 2px;
}
.host-bio a.brand-kyknet-link:hover,
.host-bio a.brand-carteblanche-link:hover,
.host-bio a.brand-kyknet-link:focus-visible,
.host-bio a.brand-carteblanche-link:focus-visible {
  text-decoration-color: currentColor;
}

/* ---------------------------------------------------------------------
   Sponsor teaser -- "Main Sponsor" horizontal card (currently only used
   on the /fnb test page, sitting right under .host-teaser above).
   Badge sized to match .host-avatar.has-photo (225px), in the sponsor's
   own brand colours instead of the site's kyknet blue. Markup order
   controls left/right placement -- FNB's card puts .sponsor-info first
   (left, over the gradient's darker portion so white text stays
   legible) and .sponsor-logo-badge second (right, per feedback); the
   badge itself is an opaque white circle so it reads fine over either
   end of the gradient regardless.
   FNB's variant (.sponsor-teaser-fnb) fades their brand teal from dark
   on the left to a much lighter, near-white teal on the right, the way
   fnb.co.za's own promo banners do.
   .sponsor-speaker-slot is a placeholder for a future speaker + bio --
   an opaque panel with its own dark-on-light contrast (not white-on-
   gradient) so it stays legible no matter how light the backdrop gets;
   swap the dashed avatar and TBC copy once that person is confirmed.
   --------------------------------------------------------------------- */
.sponsor-teaser {
  color: #fff;
  border-radius: var(--radius);
  padding: 36px;
  display: flex;
  gap: 28px;
  align-items: center;
  flex-wrap: wrap;
  margin-top: 24px;
}
.sponsor-teaser-fnb {
  /* FNB brand teal (sampled from fnb.co.za's own promo banners), fading
     to a pale near-white teal on the right. */
  background: linear-gradient(90deg, #076267 0%, #0FA0A5 32%, #4FC9C8 64%, #BDEDEA 100%);
}
.sponsor-logo-badge {
  /* Badge circle bumped +10% (225px -> 247.5px) so the logo reads bigger
     overall, on top of the thinner ring below. */
  width: 247.5px;
  height: 247.5px;
  border-radius: 50%;
  background: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  /* Ring padding halved again (10px -> 5px) so the white margin reads as
     a hairline ring; the logo <img> below fills the rest of the circle
     (100% of the padded box), so it grows to compensate. */
  padding: 5px;
  box-shadow: 0 6px 18px rgba(0,0,0,0.18);
}
.sponsor-logo-badge img { width: 100%; height: auto; object-fit: contain; }
.sponsor-info { flex: 1; min-width: 240px; }
.sponsor-info .eyebrow { background: rgba(255,255,255,0.22); border-color: rgba(255,255,255,0.55); color: #fff; }
.sponsor-info h3 { color: #fff; margin: 6px 0; font-size: 1.5rem; }
.sponsor-info p { color: rgba(255,255,255,0.95); margin-bottom: 0; }
.sponsor-speaker-slot {
  margin-top: 18px;
  padding: 12px 18px;
  background: rgba(255,255,255,0.85);
  border: 1px dashed rgba(4,73,77,0.4);
  border-radius: var(--radius);
  display: flex;
  align-items: center;
  gap: 14px;
}
.sponsor-speaker-avatar {
  width: 52px;
  height: 52px;
  border-radius: 50%;
  border: 2px dashed rgba(4,73,77,0.45);
  flex-shrink: 0;
}
.sponsor-speaker-text p {
  margin: 0;
  font-size: 0.85rem;
  color: #075056;
}
.sponsor-speaker-label {
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-size: 0.72rem;
  color: #0C7A80;
  margin-bottom: 2px;
}

/* ---------------------------------------------------------------------
   Speaker line-up (Symposium page)
   .speaker-badge (colour + icon, no photo) is kept for any future unnamed
   slot; every current card is a real confirmed speaker/partner instead
   (see .speaker-card.confirmed below).
   --------------------------------------------------------------------- */
.speaker-grid { margin-top: 8px; }
.speaker-card {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  padding: 26px 20px;
  text-align: center;
  box-shadow: var(--shadow);
}
.speaker-badge {
  width: 64px;
  height: 64px;
  border-radius: 50%;
  margin: 0 auto 14px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.speaker-card h4 {
  margin: 0 0 4px;
  color: var(--color-primary-dark);
  font-size: 1.05rem;
}
.speaker-bio {
  margin: 0 0 10px;
  font-size: 0.88rem;
  color: var(--color-text-muted);
  font-style: italic;
}
.speaker-tbc {
  margin: 0;
  font-size: 0.8rem;
  color: var(--color-text-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

/* ---------------------------------------------------------------------
   Confirmed speakers (real name/photo/bio)
   Add class "confirmed" to a .speaker-card once a real speaker signs on,
   replacing its .speaker-badge with a <picture> containing two
   pre-cropped images: a <source media="(max-width: 760px)"> pointing at
   a "-mobile" crop, and a fallback .speaker-photo <img> pointing at a
   "-2" crop for tablet/desktop. Wrap the text in .speaker-card-body.
   Both images display at their own natural aspect ratio -- crop each
   file yourself before uploading, no CSS cropping is applied.
   --------------------------------------------------------------------- */
.speaker-card.confirmed {
  padding: 0;
  text-align: left;
  overflow: hidden;
}
.speaker-photo {
  display: block;
  width: 100%;
  height: auto;
  background: var(--color-border);
}
.speaker-card-body {
  padding: 18px 20px 22px;
}
.speaker-card.confirmed h4 {
  text-align: left;
}
.speaker-card.confirmed .speaker-bio {
  font-style: normal;
  text-align: left;
}
.speaker-confirmed-tag {
  margin: 0;
  font-size: 0.8rem;
  color: var(--color-primary);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-weight: 600;
}
.speaker-logo-link {
  display: block;
  /* Negative left/right margins cancel out .speaker-card-body's 20px side
     padding, so this link spans the card's full inner width -- letting
     .speaker-logo's 80% below mean 80% of the *card*, not 80% of the
     already-padded content box (which would look noticeably smaller). */
  margin: 12px -20px 0;
  text-align: center;
}
.speaker-logo {
  display: inline-block;
  /* 48% = 80% reduced by 40%, per sponsor-logo-size feedback. */
  width: 48%;
  height: auto;
  object-fit: contain;
}
/* Optional "speaking on" topics block for a confirmed speaker's session
   abstracts -- a small label plus one or more title+description pairs,
   sitting under the bio/org tag inside .speaker-card-body. */
.speaker-topics {
  margin-top: 14px;
  padding-top: 14px;
  border-top: 1px solid var(--color-border);
}
.speaker-topics-label {
  margin: 0 0 8px;
  font-size: 0.78rem;
  color: var(--color-text-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-weight: 600;
}
.speaker-topic + .speaker-topic { margin-top: 12px; }
.speaker-topic-title {
  margin: 0 0 3px;
  font-size: 0.92rem;
  font-weight: 700;
  color: var(--color-primary-dark);
}
.speaker-topic-desc {
  margin: 0;
  font-size: 0.85rem;
  color: var(--color-text-muted);
}

/* Sponsor-confirmed / speaker-still-TBC card: swaps the .speaker-photo
   for a clickable logo banner (linking to the sponsor's site) in place
   of a photo, above the usual .speaker-card-body text. */
.speaker-logo-banner {
  display: block;
  background: var(--color-surface);
  border-bottom: 1px solid var(--color-border);
  padding: 32px 24px;
  text-align: center;
}
.speaker-logo-banner img {
  display: inline-block;
  width: 70%;
  max-width: 220px;
  height: auto;
}

/* "More speakers coming" teaser -- closes out the speaker grid. Reuses
   the base (non-confirmed) .speaker-card layout but with a dashed
   border and muted background so it reads as an open, upcoming slot
   rather than another filled profile -- a professional "watch this
   space" note, not a placeholder joke. Single card width, same as
   every other card in the grid, on both the main page and /fnb. */
.speaker-card-more {
  background: var(--color-bg);
  border-style: dashed;
  border-width: 2px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}
.speaker-more-badge {
  width: 60px;
  height: 60px;
  border-radius: 50%;
  margin: 0 auto 14px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--color-primary);
  flex-shrink: 0;
}
.speaker-card-more h4 { color: var(--color-primary-dark); }
.speaker-card-more .speaker-bio { font-style: normal; }

/* ---------------------------------------------------------------------
   Timeline / details list
   --------------------------------------------------------------------- */
.detail-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 14px; }
.detail-list li {
  display: flex;
  gap: 14px;
  align-items: flex-start;
  padding: 16px 18px;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
}
.detail-list strong { color: var(--color-primary-dark); display: block; font-family: var(--font-heading); }

/* ---------------------------------------------------------------------
   Coming soon / stub
   --------------------------------------------------------------------- */
.stub {
  position: relative;
  overflow: hidden;
  text-align: center;
  padding: 90px 24px;
  max-width: 640px;
  margin: 0 auto;
  background:
    linear-gradient(rgba(247,246,242,0.9), rgba(247,246,242,0.9)),
    var(--hero-photo, none) center/cover no-repeat;
  border-radius: var(--radius);
}
.stub > *:not(.pecan-deco) { position: relative; z-index: 1; }
.stub .icon-badge {
  width: 72px; height: 72px;
  margin: 0 auto 22px;
  border-radius: 50%;
  background: rgba(47,82,51,0.08);
  display: flex; align-items: center; justify-content: center;
}
.stub .icon-badge svg { width: 32px; height: 32px; color: var(--color-primary); }

/* ---------------------------------------------------------------------
   Ticket embed
   --------------------------------------------------------------------- */
.form-embed {
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--color-surface);
  box-shadow: var(--shadow);
}
.form-embed iframe {
  width: 100%;
  min-height: 900px;
  border: 0;
  display: block;
}
.form-fallback {
  padding: 22px 26px;
  border-top: 1px solid var(--color-border);
  font-size: 0.95rem;
  color: var(--color-text-muted);
}

.notice {
  background: rgba(201,151,46,0.12);
  border: 1px solid rgba(201,151,46,0.4);
  color: var(--color-earth);
  padding: 14px 18px;
  border-radius: var(--radius);
  font-size: 0.92rem;
  margin-bottom: 26px;
}

/* ---------------------------------------------------------------------
   Sponsor credits scroll - a "fake media player" styled exactly like
   the real embedded player below it, but instead of a stream it loops
   a scrolling sponsor-credits reel (name, logo, contact, address),
   like the end of a broadcast. Ported from the credits/ draft preview
   (Option C there) once that design was approved. Its label reads
   "Sponsors"/"Borge" rather than "Live" so it never implies an active
   stream - that honesty lives in the real watch-live section directly
   below. The controlbar's volume/settings/fullscreen icons are still
   just decoration, but tapping or clicking anywhere on the frame (see
   script.js) genuinely toggles the reel's scroll and swaps the
   play/pause icon to match - real behaviour, not just a hover trick.
   --------------------------------------------------------------------- */
.player-wrap { display: flex; flex-direction: column; align-items: center; margin-top: 8px; width: 100%; }
.player { position: relative; width: 100%; max-width: 640px; }
.player-frame {
  position: relative;
  aspect-ratio: 16 / 9;
  border-radius: 8px;
  overflow: hidden;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  /* dark charcoal, not pure black/near-black-green - keeps the same
     subtle top-lit depth (radial highlight fading into the panel)
     but reads as a neutral dark gray screen bezel. */
  background:
    radial-gradient(120% 100% at 50% 0%, rgba(85,85,85,0.55) 0%, rgba(30,30,30,0.95) 55%),
    linear-gradient(180deg, #3c3c3c 0%, #1c1c1c 100%);
  box-shadow: 0 16px 40px rgba(0,0,0,0.28);
}
.player-topbar {
  position: absolute; top: 0; left: 0; right: 0;
  display: flex; align-items: center; justify-content: flex-end;
  padding: 12px 14px;
  background: linear-gradient(180deg, rgba(0,0,0,0.45) 0%, rgba(0,0,0,0) 100%);
  z-index: 3;
}
.player-watermark {
  color: rgba(255,255,255,0.55);
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 0.66rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.player-credits {
  position: absolute; inset: 0;
  overflow: hidden;
  z-index: 1;
}
.player-credits-list {
  /* top offset (not padding) so it sits outside the box that -50% is
     calculated against - padding here would make the two halves'
     combined height uneven with one set's height, causing a visible
     jump/shake at the loop seam. */
  position: absolute; top: 10px; left: 0; right: 0;
  list-style: none; margin: 0; padding: 0;
  animation: player-credits-scroll 29s linear infinite;
}
.player-frame.is-paused .player-credits-list { animation-play-state: paused; }
@keyframes player-credits-scroll {
  from { transform: translateY(0); }
  to   { transform: translateY(-50%); }
}
.player-credit {
  text-align: center;
  padding: 35px 6%;
}
.player-credit-logo {
  display: inline-flex; align-items: center; justify-content: center;
  margin-bottom: 8px;
  min-height: 34px;
}
.player-credit-logo-stack {
  /* stacks two sponsor logos vertically in one credit card (used when
     a sponsor is deciding between two brand names/logos) instead of
     the default side-by-side inline-flex flow. */
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
}
.player-credit-logo img {
  display: block;
  max-height: 40px; max-width: 220px;
  width: auto; height: auto;
}
.player-credit-logo.is-lg img {
  max-height: 80px; max-width: 440px;
}
.player-credit-logo.is-md img {
  max-height: 60px; max-width: 330px;
}

/* King Price: +25% over the default 40px/220px sponsor logo size */
.is-kingprice img { max-height: 50px; max-width: 275px; }

/* Nedbank: 30% smaller than the +150% (x2.5) size, per request */
.is-nedbank img { max-height: 105px; max-width: 577.5px; }

/* Nedbank: very fine hairline border, same technique as .nutmech-logo-outline -
   traces the alpha edge of the logo artwork rather than using a boxy CSS border. */
.nedbank-logo-outline {
  /* a thin white outline traced right at the alpha edge - stacked
     0-blur drop-shadows in every direction (half-pixel offsets, so
     it's narrower than a full 1px ring), so it reads as a clean thin
     outline rather than a soft glow. Needed because the navy linework
     is otherwise low-contrast against the dark player background. */
  filter:
    drop-shadow(0.5px 0 0 #fff)
    drop-shadow(-0.5px 0 0 #fff)
    drop-shadow(0 0.5px 0 #fff)
    drop-shadow(0 -0.5px 0 #fff)
    drop-shadow(0.5px 0.5px 0 #fff)
    drop-shadow(0.5px -0.5px 0 #fff)
    drop-shadow(-0.5px 0.5px 0 #fff)
    drop-shadow(-0.5px -0.5px 0 #fff);
}
.player-credit-logo.is-xl img {
  /* ~3.2x the default max-height (40px -> 128px): started at 4x (160px)
     then sized down 20% for SAPCOR's combined company+broker credit. */
  max-height: 90px; max-width: 258px;
}
.nutmech-logo-outline {
  /* a thin white outline traced right at the alpha edge - stacked
     0-blur drop-shadows in every direction (half-pixel offsets, so
     it's narrower than a full 1px ring), so it reads as a clean thin
     outline rather than a soft glow. Needed because the navy linework
     is otherwise low-contrast against the dark player background. */
  filter:
    drop-shadow(0.5px 0 0 #fff)
    drop-shadow(-0.5px 0 0 #fff)
    drop-shadow(0 0.5px 0 #fff)
    drop-shadow(0 -0.5px 0 #fff)
    drop-shadow(0.5px 0.5px 0 #fff)
    drop-shadow(0.5px -0.5px 0 #fff)
    drop-shadow(-0.5px 0.5px 0 #fff)
    drop-shadow(-0.5px -0.5px 0 #fff);
}
.savage-logo-outline {
  /* a thin white outline traced right at the alpha edge - same stacked
     0-blur drop-shadow technique as the other credit logos above.
     Needed because the wordmark itself is solid black, which is
     otherwise invisible against the dark player background. */
  filter:
    drop-shadow(0.5px 0 0 #fff)
    drop-shadow(-0.5px 0 0 #fff)
    drop-shadow(0 0.5px 0 #fff)
    drop-shadow(0 -0.5px 0 #fff)
    drop-shadow(0.5px 0.5px 0 #fff)
    drop-shadow(0.5px -0.5px 0 #fff)
    drop-shadow(-0.5px 0.5px 0 #fff)
    drop-shadow(-0.5px -0.5px 0 #fff);
}
.player-credit-title { padding: 20px 6% 24px; }
.player-credit-title p {
  color: #fff;
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 1.05rem;
  letter-spacing: 0.02em;
  margin: 0;
}
.player-credit-name {
  color: #fff;
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 0.96rem;
  margin: 0 0 4px;
}
.player-credit-contact {
  margin: 0; font-size: 0.78rem; color: rgba(255,255,255,0.6);
  font-family: var(--font-body);
}
.player-credit-contact a { color: rgba(255,255,255,0.6); text-decoration: none; }
.player-credit-contact a:hover,
.player-credit-contact a:focus-visible { color: #fff; text-decoration: underline; }
.player-credit-contact .sep { color: rgba(255,255,255,0.3); padding: 0 6px; }
.player-credit-address {
  margin: 3px 0 0; font-size: 0.72rem; color: rgba(255,255,255,0.42);
  font-family: var(--font-body);
}
.player-credit-address a { color: inherit; text-decoration: none; }
.player-credit-address a:hover,
.player-credit-address a:focus-visible { color: #fff; text-decoration: underline; }

.player-controlbar {
  position: absolute; left: 0; right: 0; bottom: 0;
  height: 36px;
  display: flex; align-items: center; gap: 12px;
  padding: 0 12px;
  background: linear-gradient(0deg, rgba(0,0,0,0.65) 0%, rgba(0,0,0,0) 100%);
  z-index: 4;
}
.player-icon { width: 16px; height: 16px; color: rgba(255,255,255,0.9); flex: 0 0 auto; }
.player-progress {
  flex: 1;
  height: 3px;
  /* bright white, not just a dim track tint - this sliver is what
     shows through past the fill's 94% end, i.e. the few pixels of
     "unwatched" bar the scrubber dot is sitting just before. */
  background: #fff;
  border-radius: 2px;
  position: relative;
}
.player-progress-fill {
  /* accent brown, not alert red - this loop is decorative, never a
     stand-in for an active broadcast. Stops just short of the end
     (94%) rather than the full width, so a sliver of the white track
     and the scrubber dot both sit a little before the very end. */
  position: absolute; left: 0; top: 0; bottom: 0;
  width: 94%;
  background: var(--color-accent);
  border-radius: 2px;
}
.player-progress-fill::after {
  /* a small, quiet scrubber-handle dot at the played-out end of the
     bar - reads as "played to the end" rather than "still live",
     kept subtle so it doesn't compete with the sponsor reel above.
     7.7px = 10% bigger than the original 7px. */
  content: "";
  position: absolute;
  right: -2.2px; top: 50%;
  width: 7.7px; height: 7.7px;
  background: #fff;
  opacity: 0.8;
  border-radius: 50%;
  transform: translateY(-50%);
}
.player-time {
  color: #fff;
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 0.66rem;
  letter-spacing: 0.04em;
  flex: 0 0 auto;
}

@media (max-width: 480px) {
  .player-credit { padding: 14px 7%; }
  .player-credit-title { padding: 18px 7% 22px; }
}

/* ---------------------------------------------------------------------
   Watch live - static teaser for the embedded YouTube player. No real
   stream exists until the symposium itself (16 Oct 2026), so this is
   deliberately NOT a working player: no controls, no progress bar,
   just the frame, a muted play icon and a placeholder message, so
   nothing here looks clickable/functional before it actually is.
   --------------------------------------------------------------------- */
.watch-live-wrap { display: flex; flex-direction: column; align-items: center; margin-top: 8px; }
.watch-live-player {
  position: relative;
  width: 100%;
  max-width: 640px;
  aspect-ratio: 16 / 9;
  border-radius: 10px;
  overflow: hidden;
  background:
    radial-gradient(120% 100% at 50% 0%, rgba(47,82,51,0.55) 0%, rgba(12,20,15,0.95) 55%),
    linear-gradient(180deg, #16241a 0%, #0b120d 100%);
  box-shadow: 0 16px 40px rgba(0,0,0,0.28);
}
.watch-live-topbar {
  position: absolute; top: 0; left: 0; right: 0;
  display: flex; justify-content: flex-end;
  padding: 12px 14px;
  background: linear-gradient(180deg, rgba(0,0,0,0.45) 0%, rgba(0,0,0,0) 100%);
}
.watch-live-watermark {
  color: rgba(255,255,255,0.55);
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 0.66rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.watch-live-body {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 14px;
  text-align: center;
  padding: 0 12%;
}
.watch-live-icon {
  width: 56px; height: 56px;
  border-radius: 50%;
  border: 2px solid rgba(255,255,255,0.3);
  display: flex; align-items: center; justify-content: center;
  flex: 0 0 auto;
}
.watch-live-icon svg { width: 20px; height: 20px; color: rgba(255,255,255,0.55); margin-left: 3px; }
.watch-live-msg {
  color: rgba(255,255,255,0.88);
  font-family: var(--font-heading);
  font-weight: 600;
  font-size: 1.02rem;
  margin: 0;
}
.watch-live-submsg {
  color: rgba(255,255,255,0.5);
  font-family: var(--font-body);
  font-size: 0.84rem;
  margin: 0;
}
.watch-live-caption {
  margin: 16px 0 0;
  text-align: center;
  font-family: var(--font-body);
  font-size: 0.85rem;
  color: var(--color-text-muted);
}

/* ---------------------------------------------------------------------
   Footer
   --------------------------------------------------------------------- */
.site-footer {
  background: var(--color-primary-dark);
  color: rgba(255,255,255,0.82);
  padding: 46px 0 30px;
  margin-top: 40px;
}
.footer-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 28px;
  margin-bottom: 28px;
}
@media (max-width: 760px) {
  .footer-grid { grid-template-columns: 1fr; }
}
.site-footer h4 { color: #fff; font-size: 0.95rem; text-transform: uppercase; letter-spacing: 0.05em; }
.site-footer a { color: rgba(255,255,255,0.78); text-decoration: none; }
.site-footer a:hover { color: #fff; text-decoration: underline; }
/* Org name in the footer's first column links back home -- keep it reading
   as a heading (solid white, no underline) rather than a dimmer body link,
   with an underline only on hover/focus as the link affordance. */
.site-footer h4 a { color: inherit; text-decoration: none; }
.site-footer h4 a:hover,
.site-footer h4 a:focus-visible { text-decoration: underline; }
.site-footer ul { list-style: none; padding: 0; margin: 0; display: grid; gap: 8px; }
.footer-bottom {
  border-top: 1px solid rgba(255,255,255,0.15);
  padding-top: 20px;
  font-size: 0.85rem;
  color: rgba(255,255,255,0.6);
  display: flex;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 10px;
}

/* Motto - placed quietly beneath the footer on every page. */
.footer-motto {
  text-align: center;
  font-size: 0.72rem;
  font-style: italic;
  letter-spacing: 0.02em;
  color: rgba(255,255,255,0.35);
  margin: 10px 0 0;
}
