/* SpeakLab — Course guide
   The animated guide character and the 8-week journey on course-details.html.
   Kept in its own file because course-details.html already carries a long
   inline <style>, and this is a self-contained feature that should be easy to
   lift out again if it ever stops earning its place.

   Colour tokens match the rest of the site: navy #1e3a8a, blue #3b82f6,
   teal #0f766e, gold #d97706. */

.sl-journey-section {
  max-width: 1200px;
  width: 92%;
  margin: 0 auto 30px;
}

.sl-journey-card {
  position: relative;
  border-radius: 35px;
  padding: 46px 44px;
  /* NO `overflow: hidden` here. Any ancestor with overflow hidden/auto/scroll
     becomes the scroll container for `position: sticky` inside it, which stops
     the guide following the scroll — that was the actual bug, not the column
     height. Nothing here needs clipping: border-radius already clips the
     background on its own. */
  border: 1px solid rgba(255, 255, 255, 0.4);
  box-shadow: 0 15px 30px rgba(0, 0, 0, 0.05);
  /* Same recipe as .theme-1 on this page, rotated to blue so the journey reads
     as its own chapter without leaving the palette. */
  background:
    radial-gradient(circle at 12% 100%, rgba(59, 130, 246, 0.55) 0%, transparent 58%),
    linear-gradient(135deg, #e2e8f0 0%, #eef2f7 100%);
}

.sl-journey-head {
  text-align: center;
  max-width: 620px;
  margin: 0 auto 38px;
}

.sl-journey-head h2 {
  font-size: clamp(1.7rem, 4.5vw, 2.2rem);
  font-weight: 800;
  letter-spacing: -0.02em;
  line-height: 1.15;
  margin-bottom: 10px;
  color: #111;
}

.sl-journey-head p {
  color: #475569;
  font-size: 1rem;
  line-height: 1.65;
}

/* ── Layout: guide on the left, weeks scrolling past on the right ────────── */
.sl-journey {
  display: grid;
  grid-template-columns: 300px 1fr;
  gap: 40px;
  /* Deliberately NOT `align-items: start`. That shrink-wraps the guide column
     to its own content, and a sticky element can only travel inside its
     parent's box — so the guide would pin at the top and never follow the
     scroll. The column has to stretch to the full row height. */
}

.sl-guide-col { height: 100%; }

/* The guide follows you down the section instead of scrolling away, which is
   what makes it feel like a guide rather than a picture. */
.sl-guide {
  position: sticky;
  top: 96px;
  text-align: center;
}

.sl-coach {
  width: 190px;
  height: 210px;
  display: block;
  margin: 0 auto;
}

/* ── Speech bubble ───────────────────────────────────────────────────────── */
.sl-bubble {
  position: relative;
  background: #fff;
  border: 1px solid #e2e8f0;
  border-radius: 18px;
  padding: 16px 18px;
  margin-top: 14px;
  box-shadow: 0 10px 26px rgba(15, 23, 42, 0.08);
  min-height: 92px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.sl-bubble::before {
  content: "";
  position: absolute;
  top: -9px;
  left: 50%;
  transform: translateX(-50%) rotate(45deg);
  width: 16px;
  height: 16px;
  background: #fff;
  border-left: 1px solid #e2e8f0;
  border-top: 1px solid #e2e8f0;
  border-radius: 3px;
}

.sl-bubble p {
  font-size: 0.95rem;
  line-height: 1.6;
  color: #1e293b;
  font-weight: 500;
  margin: 0;
  transition: opacity 0.25s ease;
}

.sl-bubble p.is-swapping { opacity: 0; }

/* ── Voice toggle ────────────────────────────────────────────────────────── */
/* Off by default and never auto-started: browsers block unprompted audio, and
   a page that starts talking at you on load is worse than a silent one.
   Hidden entirely when the browser has no speech synthesis. */
.sl-sound {
  margin: 12px auto 0;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-family: inherit;
  font-size: 0.8rem;
  font-weight: 700;
  color: #1e3a8a;
  background: #fff;
  border: 1px solid #dbe2ec;
  border-radius: 999px;
  padding: 8px 16px;
  cursor: pointer;
  transition: background 0.2s ease, border-color 0.2s ease, color 0.2s ease;
}

.sl-sound:hover { background: #eff6ff; border-color: #bfdbfe; }

.sl-sound.is-on {
  background: linear-gradient(135deg, #1e3a8a, #3b82f6);
  border-color: transparent;
  color: #fff;
}

.sl-sound[hidden] { display: none; }

/* ── Character animation ─────────────────────────────────────────────────── */
.sl-shadow { fill: rgba(15, 23, 42, 0.10); }
.sl-skin   { fill: #f2c6a0; }
.sl-hair   { fill: #23282f; }
.sl-body   { fill: #1e3a8a; }
.sl-collar { fill: #ffffff; }
.sl-badge  { fill: #d97706; }
.sl-eye    { fill: #23282f; }
.sl-brow   { stroke: #23282f; stroke-width: 3; stroke-linecap: round; fill: none; }
.sl-mouth  { fill: #8c4a3f; }

/* Gentle idle bob — the single cheapest thing that stops a drawing looking
   like a sticker. */
.sl-figure { animation: sl-bob 3.6s ease-in-out infinite; transform-origin: 100px 210px; }
@keyframes sl-bob {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-4px); }
}

/* Blink: a fast scaleY on both eyes, with a long pause between. */
.sl-eyes { animation: sl-blink 5.2s infinite; transform-origin: 100px 96px; }
@keyframes sl-blink {
  0%, 92%, 100% { transform: scaleY(1); }
  95%           { transform: scaleY(0.08); }
}

/* Waving arm, at a different period from the bob so the two never lock into
   an obviously repeating loop. */
.sl-arm { animation: sl-wave 4.3s ease-in-out infinite; transform-origin: 146px 176px; }
@keyframes sl-wave {
  0%, 62%, 100% { transform: rotate(0deg); }
  70%           { transform: rotate(-16deg); }
  78%           { transform: rotate(4deg); }
  86%           { transform: rotate(-12deg); }
}

/* The mouth only moves while a new line is being "spoken" — JS adds the class
   for about a second when the bubble text changes. */
.sl-mouth { transform-origin: 100px 116px; }
.sl-coach.is-talking .sl-mouth { animation: sl-talk 0.26s ease-in-out infinite; }
@keyframes sl-talk {
  0%, 100% { transform: scaleY(0.45); }
  50%      { transform: scaleY(1.15); }
}

/* ── Week cards ──────────────────────────────────────────────────────────── */
/* The wrapper owns the rail so the <ol> can contain only <li> — a <div> child
   of <ol> is invalid HTML, and it also made the JS offsetTop maths depend on
   which element happened to be positioned. */
.sl-weeks-wrap { position: relative; }

.sl-weeks {
  list-style: none;
  margin: 0;
  padding: 0 0 0 40px;
}

/* The rail behind the dots, and the coloured line that fills as you scroll. */
.sl-weeks-wrap::before {
  content: "";
  position: absolute;
  left: 13px;
  top: 12px;
  bottom: 12px;
  width: 3px;
  border-radius: 999px;
  background: #dbe2ec;
}

.sl-rail-fill {
  position: absolute;
  left: 13px;
  top: 12px;
  width: 3px;
  height: 0;
  border-radius: 999px;
  background: linear-gradient(180deg, #1e3a8a, #3b82f6, #0f766e);
  transition: height 0.5s cubic-bezier(0.4, 0, 0.2, 1);
}

.sl-week {
  position: relative;
  background: rgba(255, 255, 255, 0.9);
  border: 1px solid rgba(255, 255, 255, 0.9);
  border-radius: 22px;
  padding: 24px 26px;
  margin-bottom: 18px;
  box-shadow: 0 8px 24px rgba(15, 23, 42, 0.05);
  /* Reveal state — JS flips .is-in when the card scrolls into view. */
  opacity: 0;
  transform: translateY(22px);
  transition: opacity 0.5s ease, transform 0.5s ease, box-shadow 0.3s ease, border-color 0.3s ease;
}

.sl-week.is-in { opacity: 1; transform: translateY(0); }

.sl-week.is-active {
  border-color: #3b82f6;
  box-shadow: 0 14px 34px rgba(30, 58, 138, 0.16);
}

/* The dot on the rail. */
.sl-week::before {
  content: "";
  position: absolute;
  left: -34px;
  top: 30px;
  width: 15px;
  height: 15px;
  border-radius: 50%;
  background: #fff;
  border: 3px solid #cbd5e1;
  transition: border-color 0.3s ease, transform 0.3s ease;
}

.sl-week.is-active::before {
  border-color: #3b82f6;
  transform: scale(1.25);
}

.sl-week-tag {
  display: inline-block;
  font-size: 0.68rem;
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: #1e3a8a;
  background: #eff6ff;
  border-radius: 999px;
  padding: 5px 13px;
  margin-bottom: 10px;
}

.sl-week h3 {
  font-size: 1.25rem;
  font-weight: 800;
  letter-spacing: -0.01em;
  margin-bottom: 8px;
  color: #0f172a;
}

.sl-week p {
  font-size: 0.94rem;
  line-height: 1.65;
  color: #475569;
  margin: 0;
}

.sl-week-win {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px dashed #e2e8f0;
  font-size: 0.86rem;
  line-height: 1.5;
  color: #0f766e;
  font-weight: 600;
}

/* ── Counters ────────────────────────────────────────────────────────────── */
.sl-counters {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 16px;
  margin-top: 34px;
}

.sl-counter {
  background: rgba(255, 255, 255, 0.9);
  border-radius: 20px;
  padding: 22px 16px;
  text-align: center;
  box-shadow: 0 8px 24px rgba(15, 23, 42, 0.05);
}

.sl-counter-num {
  display: block;
  font-size: clamp(1.9rem, 5vw, 2.6rem);
  font-weight: 800;
  line-height: 1;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
  background: linear-gradient(135deg, #1e3a8a, #3b82f6);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
}

.sl-counter-label {
  display: block;
  margin-top: 6px;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #64748b;
}

/* ── The detail cards below the journey ──────────────────────────────────────
   Program Overview / What You'll Learn / pricing used to be flat blocks of
   static gradient. Same treatment as the events cards: the gradient itself
   drifts, a soft glow moves behind the content, and each card gets a flowing
   accent bar. The theme-1/2/3 backgrounds are untouched — only animated. */
/* Animating background-position on the original two-layer gradients barely
   moved anything — the colour has to actually travel through several stops to
   read as changing. Same recipe as .tilt-color-bg on the homepage: a long
   multi-stop gradient at 800% size, slid end to end.

   Every stop is a light (200/300-level) tone on purpose: these cards carry
   near-black text, so the background can cycle but must never darken enough to
   hurt the contrast. The selector is .detail-card.theme-N so it outranks the
   single-class rules in the page's own inline <style>. */
.detail-card.theme-1 {
  background: linear-gradient(270deg, #e9d5ff, #fed7aa, #fbcfe8, #c7d2fe, #e9d5ff);
  background-size: 800% 800%;
  animation: sl-card-flow 15s ease infinite;
}

.detail-card.theme-2 {
  background: linear-gradient(270deg, #a7f3d0, #6ee7b7, #a5f3fc, #d9f99d, #a7f3d0);
  background-size: 800% 800%;
  animation: sl-card-flow 13s ease infinite;
}

.detail-card.theme-3 {
  background: linear-gradient(270deg, #fecdd3, #fda4af, #fde68a, #f9a8d4, #fecdd3);
  background-size: 800% 800%;
  animation: sl-card-flow 17s ease infinite;
}

/* Deliberately different durations so the three never pulse in lockstep, which
   is what makes a page look like a screensaver. */
@keyframes sl-card-flow {
  0%   { background-position: 0% 50%; }
  50%  { background-position: 100% 50%; }
  100% { background-position: 0% 50%; }
}

/* ::before on .detail-card is already the noise texture, so the glow goes on
   ::after. z-index 0 keeps it under .detail-content, which is z-index 1. */
.detail-card.theme-1::after,
.detail-card.theme-2::after,
.detail-card.theme-3::after {
  content: "";
  position: absolute;
  top: -120px;
  right: -120px;
  width: 320px;
  height: 320px;
  border-radius: 50%;
  pointer-events: none;
  z-index: 0;
  opacity: 0.3;
  animation: sl-card-glow 16s ease-in-out infinite alternate;
}

.detail-card.theme-1::after { background: radial-gradient(circle, #f59e0b 0%, transparent 68%); }
.detail-card.theme-2::after { background: radial-gradient(circle, #14b8a6 0%, transparent 68%); }
.detail-card.theme-3::after { background: radial-gradient(circle, #f43f5e 0%, transparent 68%); }

@keyframes sl-card-glow {
  0%   { transform: translate(0, 0) scale(1); }
  50%  { transform: translate(-50px, 60px) scale(1.2); }
  100% { transform: translate(24px, 110px) scale(0.92); }
}

/* Flowing accent bar at the top of each card. */
.sl-card-accent {
  display: block;
  width: 58px;
  height: 5px;
  border-radius: 999px;
  margin-bottom: 22px;
  background-size: 200% 100%;
  animation: sl-accent-flow 5s linear infinite;
}

.theme-1 .sl-card-accent { background-image: linear-gradient(90deg, #b45309, #f59e0b, #f43f5e, #f59e0b, #b45309); }
.theme-2 .sl-card-accent { background-image: linear-gradient(90deg, #0f766e, #14b8a6, #84cc16, #14b8a6, #0f766e); }
.theme-3 .sl-card-accent { background-image: linear-gradient(90deg, #be123c, #f43f5e, #f59e0b, #f43f5e, #be123c); }

@keyframes sl-accent-flow {
  0%   { background-position: 0% 50%; }
  100% { background-position: 200% 50%; }
}

@media (max-width: 640px) {
  .sl-card-accent { width: 44px; height: 4px; margin-bottom: 16px; }
  .detail-card.theme-1::after,
  .detail-card.theme-2::after,
  .detail-card.theme-3::after { width: 220px; height: 220px; top: -90px; right: -90px; }
}

/* ── Generic scroll reveal, used on the existing cards further down ───────── */
.sl-reveal {
  opacity: 0;
  transform: translateY(24px);
  transition: opacity 0.6s ease, transform 0.6s ease;
}
.sl-reveal.is-in { opacity: 1; transform: translateY(0); }

/* ── Mobile ──────────────────────────────────────────────────────────────── */
@media (max-width: 900px) {
  /* Two tuning knobs for the whole locked layout. The site header is fixed, so
     the guide pins below it; the cards then pin below the guide. Change these
     two and everything else follows. */
  .sl-journey {
    /* How far below the top of the screen everything locks. The site header is
       sticky and about 58px tall on a phone, so this leaves a deliberate gap
       under it — at 74px the guide tucked right against the header and looked
       jammed. Raise this number to lock lower, lower it to lock higher; the
       cards follow automatically. */
    --sl-lock-top: 96px;
    --sl-guide-h: 158px;
    /* Block, not grid. A grid item's sticky travel is limited to its own grid
       area — one row tall here — so the guide could never follow the scroll.
       As a plain block container the whole section becomes its travel range. */
    display: block;
  }

  /* Dissolve the column box entirely so .sl-guide becomes a direct child of
     .sl-journey. Sticky is bounded by the parent's box, and the column was
     exactly as tall as the guide itself — zero room to move. */
  .sl-guide-col { display: contents; }

  /* On a phone the guide locks under the header and stays there while the
     weeks slide up underneath it. */
  .sl-guide {
    top: var(--sl-lock-top);
    margin-bottom: 18px;
    display: grid;
    grid-template-columns: 84px 1fr;
    align-items: center;
    gap: 12px;
    text-align: left;
    background: rgba(255, 255, 255, 0.92);
    border: 1px solid #e2e8f0;
    border-radius: 20px;
    padding: 12px 14px;
    box-shadow: 0 10px 26px rgba(15, 23, 42, 0.08);
    z-index: 5;
  }

  /* Smaller than before: the guide is pinned now, so every pixel it occupies
     is a pixel permanently taken from the card the visitor is reading. */
  .sl-coach { width: 78px; height: 86px; margin: 0; }

  .sl-bubble {
    margin-top: 0;
    border: 0;
    box-shadow: none;
    padding: 0;
    min-height: 0;
    background: transparent;
    text-align: left;
  }
  .sl-bubble::before { display: none; }
  .sl-bubble p { font-size: 0.89rem; line-height: 1.55; }

  /* The button is a third child of a two-column grid, so it was being dropped
     into the 96px icon column and wrapping onto three lines. Span the row. */
  .sl-sound {
    grid-column: 1 / -1;
    justify-self: center;
    margin: 2px 0 0;
    white-space: nowrap;
  }

  /* ── Stacked week cards ─────────────────────────────────────────────────
     Each card sticks just under the guide, so the next one slides up and
     covers the one before it instead of the whole list scrolling past. The
     tops step down by 8px each, which leaves a visible sliver of every card
     underneath — that sliver is what makes the stack read as a stack. */
  .sl-weeks { padding-left: 0; }
  .sl-weeks-wrap::before, .sl-rail-fill { display: none; }
  .sl-week::before { display: none; }

  .sl-week {
    position: sticky;
    /* Solid, not translucent: stacked cards must hide what is behind them. */
    background: #fff;
    margin-bottom: 20px;
    box-shadow: 0 -6px 26px rgba(15, 23, 42, 0.10);
  }

  /* Each card locks just below the pinned guide, stepping 8px further down
     than the one before so a sliver of every card underneath stays visible —
     that sliver is what makes it read as a stack rather than a swap. */
  .sl-week:nth-of-type(1) { top: calc(var(--sl-lock-top) + var(--sl-guide-h) + 10px); }
  .sl-week:nth-of-type(2) { top: calc(var(--sl-lock-top) + var(--sl-guide-h) + 18px); }
  .sl-week:nth-of-type(3) { top: calc(var(--sl-lock-top) + var(--sl-guide-h) + 26px); }
  .sl-week:nth-of-type(4) { top: calc(var(--sl-lock-top) + var(--sl-guide-h) + 34px); }

  /* The last card has nothing stacking over it, so give it room to breathe
     rather than leaving a gap the stack never fills. */
  .sl-week:last-of-type { margin-bottom: 0; }
}

/* ── Mobile: the detail cards below the journey ──────────────────────────────
   Program Overview, What You'll Learn and the pricing panel kept their 40px
   desktop padding on a 360px screen, which is most of a phone's width spent on
   empty margin and made the page far longer than it needed to be. */
@media (max-width: 640px) {
  .detail-wrapper { padding-bottom: 40px; gap: 18px; }

  .detail-card {
    padding: 26px 20px;
    border-radius: 26px;
    margin-bottom: 18px;
  }

  .detail-card h2 { font-size: 1.4rem; margin-bottom: 18px; }

  .overview-grid { gap: 10px; }
  .overview-item { padding: 13px 15px; border-radius: 15px; }
  .overview-item h5 { font-size: 0.66rem; margin-bottom: 3px; }
  .overview-item p { font-size: 0.95rem; line-height: 1.35; }

  .curr-item {
    padding: 15px 16px;
    gap: 12px;
    border-radius: 16px;
    margin-bottom: 10px;
  }
  .curr-icon { width: 24px; height: 24px; margin-top: 2px; }
  .curr-content h4 { font-size: 1rem; margin-bottom: 3px; }
  .curr-content p { font-size: 0.87rem; line-height: 1.45; }

  .price-box { margin-bottom: 18px; }
  .price-amount { font-size: 2.5rem; margin: 6px 0 4px; }
  .price-title { font-size: 0.8rem; }
  .price-regular { font-size: 0.95rem; }
  .price-details { padding: 18px; border-radius: 18px; }
  .price-details ul { font-size: 0.88rem !important; }

  .enroll-btn {
    padding: 16px 18px;
    font-size: 0.92rem;
    margin-top: 20px;
    border-radius: 32px;
  }
}

@media (max-width: 640px) {
  .sl-journey-section { width: 94%; }
  .sl-journey-card { padding: 28px 18px; border-radius: 26px; }
  .sl-journey-head { margin-bottom: 24px; }
  .sl-journey-head p { font-size: 0.93rem; }

  /* No rail rules here: below 900px the cards stack instead, and the rail and
     its dots are hidden. This block only sizes the cards themselves — an
     earlier version re-applied the rail padding and left the text indented
     against a rail that was no longer drawn. */
  .sl-week { padding: 20px 18px; border-radius: 18px; }
  .sl-week h3 { font-size: 1.1rem; }
  .sl-week p { font-size: 0.89rem; }
  .sl-week-win { font-size: 0.82rem; }

  .sl-counters { gap: 10px; margin-top: 24px; }
  .sl-counter { padding: 16px 8px; border-radius: 16px; }
  .sl-counter-label { font-size: 0.62rem; letter-spacing: 0.05em; }
}

/* ── Reduced motion ──────────────────────────────────────────────────────── */
/* The character idles, blinks and waves forever. Hold all of it still for
   anyone who has asked the OS for less movement, and show every revealed
   element outright rather than leaving it invisible. */
@media (prefers-reduced-motion: reduce) {
  .sl-figure, .sl-eyes, .sl-arm, .sl-coach.is-talking .sl-mouth,
  .detail-card.theme-1, .detail-card.theme-2, .detail-card.theme-3,
  .detail-card.theme-1::after, .detail-card.theme-2::after, .detail-card.theme-3::after,
  .sl-card-accent { animation: none; }
  .sl-week, .sl-reveal { opacity: 1; transform: none; transition: none; }
  /* Cards sliding over each other is motion too — let them scroll normally. */
  .sl-week { position: static; }
  .sl-rail-fill { transition: none; }
  .sl-bubble p { transition: none; }
}
