/* ==================================================================
   MDriven Academy — the Tiki / jungle layer
   ------------------------------------------------------------------
   Loaded after theme.css. Everything here is additive: it introduces a
   canopy palette and a set of decorative components, and leaves the
   MDriven brand tokens alone.

   The palette is SAMPLED FROM THE ARTWORK rather than invented, which
   is why it sits so comfortably next to the existing brand colours —
   Tiki's beak is #f57f1e against MDriven's #f6821f, and his claws are
   #f1a241 against the brand amber #f59c1a. Green and brown are the
   only genuinely new hues.
   ================================================================== */

:root {
  /* Sampled from "Tiki - PNG.png" */
  --canopy:       #5f926a;   /* leaf, lit face   */
  --canopy-mid:   #587e60;   /* leaf, shade face */
  --canopy-deep:  #37543f;   /* depth            */
  --canopy-lite:  #8ab690;
  --bark:         #a47052;
  --bark-deep:    #7f4c38;
  --cream:        #efe8d5;   /* Tiki's eye patch */
  --plume:        #231f1e;   /* Tiki's feathers  */

  /* Grounds derived from the above */
  --jungle-night: #0c2029;   /* brand ink navy pulled toward the canopy */
  --jungle-deep:  #0d3330;
  --jungle-moss:  #12402f;
  --paper-warm:   #f6f4ec;   /* cream, thinned — a field-guide page   */

  --leaf-radius: 62% 38% 55% 45% / 55% 48% 52% 45%;
}

@media (prefers-color-scheme: dark) {
  :root {
    --paper-warm: #14211f;
    --cream: #e7e0cd;
  }
}

/* ------------------------------------------------------------------
   Motion. Everything is slow and small on purpose: this is decoration
   behind 40+ lessons of dense reading, and anything livelier would
   compete with the text.
   ------------------------------------------------------------------ */

@keyframes tiki-sway   { 0%,100% { transform: rotate(-1.6deg); } 50% { transform: rotate(1.6deg); } }
@keyframes tiki-sway-b { 0%,100% { transform: rotate(1.2deg); } 50% { transform: rotate(-2deg); } }
@keyframes tiki-bob    { 0%,100% { transform: translateY(0); }   50% { transform: translateY(-9px); } }
@keyframes tiki-drift {
  0%   { transform: translate3d(0,-8vh,0) rotate(0deg);      opacity: 0; }
  8%   { opacity: .75; }
  92%  { opacity: .55; }
  100% { transform: translate3d(22vw,104vh,0) rotate(420deg); opacity: 0; }
}
@keyframes tiki-glow   { 0%,100% { opacity: .55; } 50% { opacity: .9; } }
@keyframes tiki-rise   { from { opacity: 0; transform: translateY(16px); } to { opacity: 1; transform: none; } }
@keyframes tiki-trail  { from { stroke-dashoffset: var(--trail-len, 1000); } }

/* One class, used by anything that should fade up on load. Staggered by
   an inline --d so a section arrives as a sequence, not a jolt. */
.j-rise { animation: tiki-rise .55s cubic-bezier(.2,.7,.3,1) both; animation-delay: var(--d, 0s); }

@media (prefers-reduced-motion: reduce) {
  .j-rise,
  .j-hero__fronds > * svg,
  .j-leaves > i,
  .j-hero__sun,
  .j-tiki img { animation: none !important; }
  .j-rise { opacity: 1; transform: none; }
}

/* ==================================================================
   The hero scene
   ================================================================== */

.j-hero {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  margin-bottom: 40px;
  padding: 0;
  border-radius: 0 0 28px 28px;
  background:
    radial-gradient(58rem 30rem at 82% -14%, rgba(246,130,31,.30), transparent 62%),
    radial-gradient(46rem 26rem at 8% 108%, rgba(95,146,106,.34), transparent 66%),
    linear-gradient(157deg, var(--jungle-night) 0%, var(--jungle-deep) 52%, var(--jungle-moss) 100%);
  color: #fff;
}

/* Full-bleed out of the centred .wrap it sits inside. */
.j-hero.is-bleed {
  width: 100vw;
  margin-left: calc(50% - 50vw);
  margin-right: calc(50% - 50vw);
  border-radius: 0;
}

.j-hero__inner {
  position: relative;
  z-index: 3;
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 62px 22px 68px;
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, .85fr);
  gap: 30px;
  align-items: center;
}

/* --- warm light from the top right, the same hue as Tiki's beak --- */
.j-hero__sun {
  position: absolute;
  inset: auto auto 0 0;
  top: -22%;
  right: -6%;
  left: auto;
  width: 32rem;
  height: 32rem;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(246,130,31,.55), rgba(245,156,26,.16) 45%, transparent 70%);
  filter: blur(6px);
  animation: tiki-glow 9s ease-in-out infinite;
  z-index: 1;
  pointer-events: none;
}

/* --- canopy silhouettes, layered for depth --- */
.j-hero__canopy {
  position: absolute;
  inset: 0 0 auto 0;
  width: 100%;
  height: 190px;
  z-index: 2;
  pointer-events: none;
  opacity: .85;
}
/* The second canopy sits at the foot of the hero, flipped, so the section is
   framed top and bottom rather than only crowned. */
.j-hero__canopy + .j-leaves + .j-hero__fronds + .j-hero__inner + .j-hero__canopy,
.j-hero > .j-hero__canopy:last-of-type {
  inset: auto 0 -2px 0;
  top: auto;
  height: 110px;
  transform: scaleY(-1);
  opacity: .42;
}

/* --- big fronds in the corners, swaying from their stems --- */
.j-hero__fronds { position: absolute; inset: 0; z-index: 2; pointer-events: none; }

/* The wrapper holds POSITION and the base rotation that sweeps the frond into
   frame; the inner <svg> carries the sway. Keeping them apart matters: a
   keyframe that animates `transform` replaces the whole property, so a base
   rotate or flip declared on the same element is silently dropped. */
.j-hero__fronds > * { position: absolute; display: block; }
.j-hero__fronds > * svg {
  display: block;
  width: 100%;
  height: auto;
  transform-origin: 8% 8%;
  will-change: transform;
}

.j-hero__fronds .f1 {
  top: -40px; left: -52px; width: 340px;
  transform: rotate(18deg);
  opacity: .5;
}
.j-hero__fronds .f1 svg { animation: tiki-sway 13s ease-in-out infinite; }

.j-hero__fronds .f2 {
  top: -56px; right: -46px; width: 310px;
  transform: scaleX(-1) rotate(14deg);
  opacity: .38;
}
.j-hero__fronds .f2 svg { animation: tiki-sway-b 16s ease-in-out infinite; }

@media (max-width: 980px) { .j-hero__fronds .f2 { display: none; } }
@media (max-width: 620px) { .j-hero__fronds .f1 { width: 230px; opacity: .32; } }

/* --- copy --- */
.j-hero__eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  margin-bottom: 14px;
  padding: 6px 14px 6px 8px;
  border-radius: 999px;
  background: rgba(255,255,255,.09);
  border: 1px solid rgba(255,255,255,.16);
  font-size: .78rem;
  font-weight: 600;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--cream);
}
.j-hero__eyebrow img { width: 26px; height: auto; display: block; }

.j-hero h1 {
  margin: 0 0 14px;
  font-size: clamp(2.1rem, 4.6vw, 3.35rem);
  line-height: 1.06;
  letter-spacing: -.03em;
  font-weight: 700;
  color: #fff;
  max-width: 17ch;
}
.j-hero h1 em { font-style: normal; color: var(--orange); }
.j-hero__lead {
  margin: 0 0 26px;
  max-width: 52ch;
  font-size: 1.08rem;
  line-height: 1.62;
  color: rgba(255,255,255,.82);
}

/* Any ghost button sitting on a dark jungle ground. Scoped to both surfaces
   because the default .btn-ghost is dark-on-light and vanishes here. */
.j-hero .btn-ghost,
.j-cta .btn-ghost {
  color: #fff;
  border-color: rgba(255,255,255,.34);
  background: rgba(255,255,255,.07);
}
.j-hero .btn-ghost:hover,
.j-cta .btn-ghost:hover { background: rgba(255,255,255,.16); color: #fff; }

.j-hero__pills { display: flex; flex-wrap: wrap; gap: 8px; margin: 24px 0 0; }
.j-hero__pills .pill {
  background: rgba(255,255,255,.08);
  border-color: rgba(255,255,255,.18);
  color: rgba(255,255,255,.88);
}
.j-hero__pills .pill b { color: #fff; }

/* --- Tiki himself --- */
.j-tiki { position: relative; z-index: 3; justify-self: center; }
.j-tiki img {
  display: block;
  width: 100%;
  max-width: 330px;
  height: auto;
  filter: drop-shadow(0 22px 34px rgba(0,0,0,.42));
  animation: tiki-bob 6.5s ease-in-out infinite;
}
@media (max-width: 820px) {
  .j-hero__inner { grid-template-columns: 1fr; padding: 46px 20px 54px; }
  .j-tiki { order: -1; }
  .j-tiki img { max-width: 210px; margin: 0 auto; animation-duration: 8s; }
}

/* --- drifting leaves --- */
.j-leaves { position: absolute; inset: 0; z-index: 2; overflow: hidden; pointer-events: none; }
.j-leaves > i {
  position: absolute;
  top: 0;
  width: 13px;
  height: 13px;
  background: var(--canopy);
  border-radius: var(--leaf-radius);
  opacity: 0;
  animation: tiki-drift var(--dur, 17s) linear infinite;
  animation-delay: var(--delay, 0s);
}
.j-leaves > i:nth-child(even) { background: var(--canopy-lite); width: 10px; height: 10px; }
.j-leaves > i:nth-child(3n)   { background: var(--amber); width: 8px; height: 8px; }

/* ==================================================================
   Section furniture
   ================================================================== */

/* A hand-drawn-feeling rule with a leaf at its centre. */
.j-rule {
  display: flex;
  align-items: center;
  gap: 14px;
  margin: 46px 0 22px;
}
.j-rule::before, .j-rule::after {
  content: "";
  height: 1px;
  flex: 1;
  background: linear-gradient(90deg, transparent, var(--border), transparent);
}
.j-rule span {
  font-size: .76rem;
  font-weight: 700;
  letter-spacing: .13em;
  text-transform: uppercase;
  color: var(--canopy-mid);
  white-space: nowrap;
}

.j-section-head { max-width: 62ch; margin-bottom: 20px; }
.j-section-head h2 { margin: 0 0 6px; letter-spacing: -.02em; }
.j-section-head p { margin: 0; color: var(--muted); }

/* --- benefit cards, each with a leaf-shaped icon plate --- */
.j-benefits {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 14px;
}
.j-benefit {
  position: relative;
  overflow: hidden;
  padding: 22px 20px 20px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: var(--shadow-sm);
  transition: transform .18s ease, box-shadow .18s ease, border-color .18s ease;
}
.j-benefit:hover { transform: translateY(-3px); box-shadow: var(--shadow); border-color: var(--canopy-lite); }
/* A faint canopy wash in the corner, so the card is not flat white. */
.j-benefit::after {
  content: "";
  position: absolute;
  top: -34px; right: -34px;
  width: 110px; height: 110px;
  border-radius: var(--leaf-radius);
  background: var(--canopy);
  opacity: .07;
}
.j-benefit__icon {
  display: grid;
  place-items: center;
  width: 48px; height: 48px;
  margin-bottom: 14px;
  border-radius: var(--leaf-radius);
  background: linear-gradient(140deg, var(--canopy), var(--canopy-mid));
  box-shadow: inset 0 1px 0 rgba(255,255,255,.35), 0 4px 10px rgba(17,43,60,.14);
  color: #fff;
  font-size: 1.35rem;
  line-height: 1;
}
.j-benefit--warm .j-benefit__icon { background: linear-gradient(140deg, var(--orange), var(--amber)); }
.j-benefit--bark .j-benefit__icon { background: linear-gradient(140deg, var(--bark), var(--bark-deep)); }
.j-benefit h3 { margin: 0 0 5px; font-size: 1.03rem; }
.j-benefit p  { margin: 0; color: var(--muted); font-size: .93rem; line-height: 1.6; }

/* --- the trail: how the programme works, as a path through camp --- */
.j-trail { position: relative; margin: 0; padding: 0; list-style: none; }
.j-trail::before {
  content: "";
  position: absolute;
  left: 21px; top: 16px; bottom: 30px;
  width: 2px;
  background: repeating-linear-gradient(180deg, var(--bark) 0 7px, transparent 7px 14px);
  opacity: .55;
}
.j-trail li {
  position: relative;
  padding: 0 0 22px 58px;
}
.j-trail li:last-child { padding-bottom: 0; }
.j-trail__marker {
  position: absolute;
  left: 0; top: 0;
  display: grid;
  place-items: center;
  width: 44px; height: 44px;
  border-radius: 50%;
  background: var(--surface);
  border: 2px solid var(--canopy);
  color: var(--canopy-mid);
  font-weight: 700;
  box-shadow: 0 0 0 5px var(--bg);
}
.j-trail li:last-child .j-trail__marker { border-color: var(--orange); color: var(--orange-dark); }
.j-trail h3 { margin: 6px 0 4px; font-size: 1rem; }
.j-trail p  { margin: 0; color: var(--muted); font-size: .93rem; line-height: 1.6; }

/* --- the closing call to action --- */
.j-cta {
  position: relative;
  overflow: hidden;
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: 26px;
  margin: 40px 0 26px;
  padding: 30px 32px;
  border-radius: 22px;
  background:
    radial-gradient(30rem 16rem at 88% 120%, rgba(246,130,31,.28), transparent 64%),
    linear-gradient(150deg, var(--jungle-deep), var(--jungle-night));
  color: #fff;
}
.j-cta h2 { margin: 0 0 6px; color: #fff; letter-spacing: -.02em; }
.j-cta p  { margin: 0 0 18px; color: rgba(255,255,255,.78); max-width: 54ch; }
.j-cta img { width: 150px; height: auto; filter: drop-shadow(0 14px 22px rgba(0,0,0,.4)); }
@media (max-width: 700px) {
  .j-cta { grid-template-columns: 1fr; padding: 24px; }
  .j-cta img { display: none; }
}

/* ==================================================================
   Lessons
   ------------------------------------------------------------------
   The reading surface stays bright and calm — 1,500 words of technical
   prose is the job here. Colour arrives at the edges: a canopy band
   over the header, leaf markers on structure, bark tones on rules, and
   Tiki tracking progress in the sidebar.
   ================================================================== */

/* --- a canopy band across the top of the lesson header --- */
.lesson-main > .page-head {
  position: relative;
  padding-top: 30px;
  margin-bottom: 22px;
}
.lesson-main > .page-head::before {
  content: "";
  position: absolute;
  top: 0; left: -14px; right: -14px;
  height: 5px;
  border-radius: 999px;
  background: linear-gradient(90deg,
    var(--canopy) 0%, var(--canopy-lite) 22%,
    var(--amber) 52%, var(--orange) 74%, var(--bark) 100%);
  opacity: .9;
}

.lesson-main .eyebrow { color: var(--canopy-mid); }

/* --- objectives read as a field note --- */
.objectives {
  position: relative;
  overflow: hidden;
  border-left: 3px solid var(--canopy);
  background: linear-gradient(180deg, rgba(95,146,106,.07), transparent 70%), var(--surface);
}
.objectives h3 { color: var(--canopy-mid); }
.objectives ul { list-style: none; padding-left: 0; }
.objectives li { position: relative; padding-left: 24px; margin-bottom: 6px; }
/* A leaf, drawn in CSS rather than shipped as an icon font. */
.objectives li::before {
  content: "";
  position: absolute;
  left: 2px; top: .52em;
  width: 11px; height: 11px;
  background: var(--canopy);
  border-radius: var(--leaf-radius);
  transform: rotate(-18deg);
}

/* --- prose structure gets quiet jungle punctuation --- */
.prose h2 {
  position: relative;
  padding-left: 18px;
}
.prose h2::before {
  content: "";
  position: absolute;
  left: 0; top: .34em; bottom: .28em;
  width: 4px;
  border-radius: 999px;
  background: linear-gradient(180deg, var(--canopy), var(--amber));
}
.prose h3 { color: var(--teal); }
.prose blockquote {
  border-left: 3px solid var(--bark);
  background: linear-gradient(90deg, rgba(164,112,82,.08), transparent);
}
.prose table thead th { background: rgba(95,146,106,.10); }
.prose pre { border-left: 3px solid var(--canopy-mid); }
.prose hr {
  border: 0;
  height: 10px;
  background: no-repeat center/22px 10px
    radial-gradient(circle at 50% 120%, var(--canopy) 40%, transparent 42%),
    linear-gradient(90deg, transparent, var(--border) 15%, var(--border) 85%, transparent);
}

/* --- callouts, recoloured to the canopy palette --- */
.callout { position: relative; padding-left: 42px; border-radius: var(--radius); }
.callout::before {
  position: absolute;
  left: 14px; top: 15px;
  font-size: 1rem;
  line-height: 1;
}
.callout-note    { border-left-color: var(--teal); background: rgba(11,84,97,.05); }
.callout-note::before    { content: "🧭"; }
.callout-tip     { border-left-color: var(--canopy); background: rgba(95,146,106,.09); }
.callout-tip .callout-label { color: var(--canopy-mid); }
.callout-tip::before     { content: "🌿"; }
.callout-warning::before { content: "🔥"; }
.callout-example { border-left-color: var(--bark); background: rgba(164,112,82,.09); }
.callout-example .callout-label { color: var(--bark-deep); }
.callout-example::before { content: "🥾"; }

/* ------------------------------------------------------------------
   Tiki as the progress marker in the lesson sidebar.
   He climbs the outline as lessons are passed, which turns a progress
   bar into something worth glancing at.
   ------------------------------------------------------------------ */

.j-guide {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 14px;
  padding: 13px 15px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: linear-gradient(140deg, rgba(95,146,106,.14), rgba(246,130,31,.09));
}
/* The head crop, not the whole bird: at this size a full-body Tiki is a
   smudge, whereas the beak alone is unmistakably him. */
.j-guide img {
  width: 74px;
  height: auto;
  flex: none;
  animation: tiki-bob 7s ease-in-out infinite;
}
.j-guide__text { min-width: 0; }
.j-guide__text b { display: block; font-size: .92rem; }
.j-guide__text span { font-size: .82rem; color: var(--muted); }

/* The outline's progress bar, restyled as a jungle trail. */
.j-progress {
  position: relative;
  height: 8px;
  margin: 4px 0 2px;
  border-radius: 999px;
  background: rgba(164,112,82,.22);
  overflow: hidden;
}
.j-progress > span {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: linear-gradient(90deg, var(--canopy), var(--amber), var(--orange));
  transition: width .6s cubic-bezier(.2,.7,.3,1);
}

/* ==================================================================
   Empty states and small flourishes
   ================================================================== */

.j-empty {
  display: grid;
  place-items: center;
  gap: 10px;
  padding: 34px 20px;
  text-align: center;
  color: var(--muted);
}
.j-empty img { width: 130px; height: auto; opacity: .9; animation: tiki-bob 8s ease-in-out infinite; }

/* Tiki peeking over the top-right corner of a card.
   The card itself must not clip him, and the margin reserves the space he
   occupies above it so he never collides with whatever sits higher up. */
.j-peek { position: relative; overflow: visible; margin-top: 52px; }
.j-peek__bird {
  position: absolute;
  top: -40px; right: 16px;
  z-index: 2;
  width: 104px;
  height: auto;
  pointer-events: none;
  filter: drop-shadow(0 10px 14px rgba(17,43,60,.24));
  animation: tiki-bob 7.5s ease-in-out infinite;
}
@media (max-width: 620px) { .j-peek__bird { display: none; } .j-peek { margin-top: 0; } }

/* The masthead mark gets a canopy underline so the brand reads as
   part of the same world without changing the logo itself. */
.site-header { border-bottom-color: transparent; box-shadow: 0 2px 0 rgba(95,146,106,.22); }
.site-header::after {
  content: "";
  display: block;
  height: 3px;
  background: linear-gradient(90deg, var(--canopy) 0%, var(--amber) 46%, var(--orange) 72%, var(--bark) 100%);
}

/* Course cards on the catalogue pick up a canopy edge on hover. */
.course-card { transition: transform .18s ease, box-shadow .18s ease, border-color .18s ease; }
.course-card:hover { transform: translateY(-3px); border-color: var(--canopy-lite); box-shadow: var(--shadow); }

/* ==================================================================
   Dark mode
   ------------------------------------------------------------------
   The canopy palette was sampled from artwork drawn for white, so a
   few of its members are too dark to sit on a dark ground. These are
   the ones that actually fail a contrast check, not a blanket
   re-theme — most of the layer works unchanged because it is built
   from the surface tokens theme.css already flips.
   ================================================================== */

@media (prefers-color-scheme: dark) {
  :root {
    /* Lifted so leaf green still reads as green rather than as sludge. */
    --canopy:      #74a97e;
    --canopy-mid:  #8ab690;
    --canopy-lite: #a6cbab;
    --bark:        #b5825f;
  }

  /* #0b5461 on a dark ground is close to invisible. */
  .prose h3 { color: var(--canopy-lite); }

  .j-rule span { color: var(--canopy-lite); }

  .j-benefit::after { opacity: .12; }

  .callout-note    { background: rgba(11,84,97,.20); }
  .callout-tip     { background: rgba(95,146,106,.16); }
  .callout-example { background: rgba(164,112,82,.16); }

  .j-progress { background: rgba(255,255,255,.10); }

  /* The hero and CTA are already dark by design; only their edge needs
     softening so they do not read as a hole in the page. */
  .j-hero, .j-cta { box-shadow: 0 0 0 1px rgba(255,255,255,.06); }

  .j-trail__marker { background: var(--surface); }
}

/* ==================================================================
   Print
   ------------------------------------------------------------------
   The dedicated printable lesson view does not load this stylesheet at
   all. This covers someone printing an ordinary page straight from the
   reader: scenery costs ink and says nothing on paper.
   ================================================================== */

@media print {
  .j-hero, .j-cta, .j-leaves, .j-hero__fronds,
  .j-hero__canopy, .j-hero__sun, .j-guide, .j-peek__bird { display: none !important; }
  .site-header::after { display: none; }
  .lesson-main > .page-head::before { display: none; }
  .j-benefit, .j-trail__marker { box-shadow: none; }
}

/* ==================================================================
   Tiki, speaking
   ------------------------------------------------------------------
   A speech bubble with a tail pointing back at his beak. Used across
   the site to carry page-specific guidance — never a bare greeting,
   because a mascot that only says hello is furniture you learn to
   ignore, and once ignored it stops working where it matters.
   ================================================================== */

@keyframes tiki-pop {
  0%   { opacity: 0; transform: translateY(6px) scale(.96); }
  60%  { opacity: 1; transform: translateY(0)   scale(1.012); }
  100% { opacity: 1; transform: none; }
}
/* A small lean-in on the beak, as though he is leaning over to talk. */
@keyframes tiki-talk {
  0%, 88%, 100% { transform: rotate(0deg)   translateY(0); }
  92%           { transform: rotate(-4deg)  translateY(-2px); }
  96%           { transform: rotate(2.5deg) translateY(0); }
}

.tiki {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  margin: 0 0 20px;
}
.tiki[hidden] { display: none; }

.tiki__bird { flex: none; }
.tiki__bird img {
  display: block;
  width: 100%;
  height: auto;
  transform-origin: 30% 70%;
  animation: tiki-talk 9s ease-in-out infinite;
}

.tiki__bubble {
  position: relative;
  flex: 1;
  min-width: 0;
  padding: 13px 16px;
  border: 1px solid var(--border);
  border-radius: 14px;
  background: var(--surface);
  box-shadow: var(--shadow-sm);
  animation: tiki-pop .45s cubic-bezier(.2,.8,.3,1) both;
}
/* The tail: two triangles, the back one forming the border. */
.tiki__bubble::before,
.tiki__bubble::after {
  content: "";
  position: absolute;
  top: 18px;
  border-style: solid;
  border-width: 9px 11px 9px 0;
}
.tiki__bubble::before { left: -11px; border-color: transparent var(--border) transparent transparent; }
.tiki__bubble::after  { left: -10px; border-color: transparent var(--surface) transparent transparent; }

.tiki__line { margin: 0; font-size: .94rem; line-height: 1.6; color: var(--text); }
.tiki__line b { color: var(--text); }
.tiki__actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 11px; }

.tiki__close {
  position: absolute;
  top: 6px; right: 8px;
  width: 24px; height: 24px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--muted);
  font-size: 1.1rem;
  line-height: 1;
  cursor: pointer;
}
.tiki__close:hover { background: var(--surface-2); color: var(--text); }

/* --- sizes --- */
.tiki--sm .tiki__bird { width: 56px; }
.tiki--sm .tiki__line { font-size: .88rem; }
.tiki--md .tiki__bird { width: 78px; }
.tiki--lg .tiki__bird { width: 104px; }
.tiki--lg .tiki__line { font-size: 1rem; }
.tiki--lg .tiki__bubble { padding: 16px 19px; }

/* The bubble's tail should meet the beak, which sits lower in the larger crops. */
.tiki--sm .tiki__bubble::before, .tiki--sm .tiki__bubble::after { top: 12px; }
.tiki--lg .tiki__bubble::before, .tiki--lg .tiki__bubble::after { top: 24px; }

/* --- tones --- */
.tiki--win .tiki__bubble {
  border-color: var(--canopy);
  background: linear-gradient(120deg, rgba(95,146,106,.14), var(--surface) 62%);
}
.tiki--win .tiki__bubble::before { border-right-color: var(--canopy); }
.tiki--win .tiki__bubble::after  { border-right-color: #eef4ef; }

.tiki--warn .tiki__bubble {
  border-color: var(--amber);
  background: linear-gradient(120deg, rgba(245,156,26,.14), var(--surface) 62%);
}
.tiki--warn .tiki__bubble::before { border-right-color: var(--amber); }
.tiki--warn .tiki__bubble::after  { border-right-color: #fef6ea; }

.tiki--quiet .tiki__bubble { background: var(--surface-2); box-shadow: none; }
.tiki--quiet .tiki__bubble::after { border-right-color: var(--surface-2); }

/* On a dark jungle ground the bubble has to invert or it punches a hole. */
.j-hero .tiki__bubble, .j-cta .tiki__bubble {
  background: rgba(255,255,255,.10);
  border-color: rgba(255,255,255,.20);
  color: #fff;
}
.j-hero .tiki__line, .j-cta .tiki__line { color: rgba(255,255,255,.9); }
.j-hero .tiki__bubble::after, .j-cta .tiki__bubble::after { border-right-color: rgba(255,255,255,.10); }
.j-hero .tiki__bubble::before, .j-cta .tiki__bubble::before { border-right-color: rgba(255,255,255,.20); }

@media (max-width: 560px) {
  .tiki--lg .tiki__bird { width: 76px; }
  .tiki--md .tiki__bird { width: 62px; }
}

@media (prefers-reduced-motion: reduce) {
  .tiki__bird img, .tiki__bubble { animation: none !important; }
}

@media (prefers-color-scheme: dark) {
  .tiki--win .tiki__bubble::after  { border-right-color: #1b2b25; }
  .tiki--warn .tiki__bubble::after { border-right-color: #2b2419; }
}

@media print { .tiki { display: none !important; } }

/* ==================================================================
   The theme, applied everywhere
   ------------------------------------------------------------------
   Everything above this point decorates two pages. This section takes
   the same palette across every remaining surface — dashboard,
   catalogue, badges, notes, leaderboard, account, verify, assessment,
   result, certificate and the admin — so the site reads as one place
   rather than a themed front door onto a plain application.

   The restraint that keeps it usable: colour lands on FURNITURE
   (headers, rules, chips, plates, table headings) and never on the
   reading surface. Body copy stays on its existing ground.
   ================================================================== */

/* --- the page itself: a warm canopy wash instead of flat grey ------ */
/* Deliberately NOT background-attachment: fixed. A fixed background forces a
   full repaint on every scroll frame, which is expensive on mobile and
   composites unreliably in some engines. The .jf scenery layer is already
   position:fixed and gives the same "the page moves past the jungle" effect
   for the cost of one composited layer. */
body {
  background-color: var(--bg);
  background-image:
    radial-gradient(46rem 26rem at 108% -6%,  rgba(95,146,106,.10), transparent 62%),
    radial-gradient(38rem 22rem at -8% 4%,    rgba(246,130,31,.06), transparent 58%);
  background-repeat: no-repeat;
}

/* --- every page header gets the canopy band ------------------------ */
.wrap > .page-head,
.lesson-main > .page-head,
.page-head.j-banded {
  position: relative;
  padding-top: 26px;
}
.wrap > .page-head::before,
.lesson-main > .page-head::before,
.page-head.j-banded::before {
  content: "";
  position: absolute;
  top: 0; left: 0; width: 132px;
  height: 5px;
  border-radius: 999px;
  background: linear-gradient(90deg, var(--canopy), var(--amber) 58%, var(--orange));
}
/* The lesson header runs the band full width, because it sits under a
   breadcrumb and needs to separate two blocks rather than label one. */
.lesson-main > .page-head::before { left: -14px; right: -14px; width: auto; }

.eyebrow { color: var(--canopy-mid); }
.lesson-main .eyebrow, .j-hero__eyebrow { color: var(--canopy-mid); }

/* --- stats: the accent bar picks up the canopy --------------------- */
.stat { border-radius: var(--radius-lg); }
.stat::before { background: linear-gradient(180deg, var(--canopy), var(--amber)); width: 4px; }
.stat .label { color: var(--canopy-mid); }

/* --- pills and chips read as leaf tags ----------------------------- */
.pill {
  border-color: color-mix(in srgb, var(--canopy) 30%, var(--border));
  background: linear-gradient(180deg, rgba(95,146,106,.08), var(--surface));
}
.concept-chip, .discourse-tag {
  border-color: color-mix(in srgb, var(--canopy) 32%, var(--border));
  background: rgba(95,146,106,.09);
}

/* --- badges keep their meaning, borrow the palette ----------------- */
.badge-pass  { background: rgba(95,146,106,.16); color: var(--canopy-deep); border-color: var(--canopy); }
.badge-level { color: var(--canopy-mid); border-color: color-mix(in srgb, var(--canopy) 30%, var(--border)); }
@media (prefers-color-scheme: dark) {
  .badge-pass { color: var(--canopy-lite); }
}

/* --- cards lift toward the canopy on hover ------------------------- */
.card { border-radius: var(--radius-lg); }
.stat, .card, .outline-card, .lb-table, .mini-card, .bk-item, .j-benefit {
  transition: border-color .18s ease, box-shadow .18s ease, transform .18s ease;
}
a.card:hover, .mini-card:hover, .bk-item:hover {
  border-color: color-mix(in srgb, var(--canopy) 45%, var(--border));
}

/* --- score hero: pass reads as canopy, fail stays warm not alarming - */
.score-hero {
  position: relative;
  overflow: hidden;
  border-radius: 22px;
}
.score-hero.pass {
  background: linear-gradient(150deg, rgba(95,146,106,.22), rgba(245,156,26,.10));
  border-color: var(--canopy);
}
.score-hero.fail {
  background: linear-gradient(150deg, rgba(245,156,26,.16), rgba(164,112,82,.10));
  border-color: var(--amber);
}
.score-hero .score-num { letter-spacing: -.03em; }

/* --- assessment call to action, as a trail signpost ---------------- */
.assessment-cta {
  position: relative;
  overflow: hidden;
  border-left: 4px solid var(--orange);
  background: linear-gradient(120deg, rgba(246,130,31,.07), var(--surface) 58%);
}
.assessment-cta::after {
  content: "";
  position: absolute;
  right: -32px; bottom: -32px;
  width: 118px; height: 118px;
  border-radius: var(--leaf-radius);
  background: var(--canopy);
  opacity: .08;
}
.assessment-cta h3 { color: var(--orange-dark); }
@media (prefers-color-scheme: dark) { .assessment-cta h3 { color: var(--amber); } }

/* --- outline / sidebar --------------------------------------------- */
.outline-card { border-radius: var(--radius-lg); overflow: hidden; }
.outline-head { background: linear-gradient(150deg, rgba(95,146,106,.10), transparent); }
.outline-course { color: var(--canopy-mid); }
.outline-module { color: var(--canopy-mid); }

/* --- tables: canopy-tinted headings, everywhere -------------------- */
.admin-table thead th, .lb-table thead th, .prose table thead th {
  background: linear-gradient(180deg, rgba(95,146,106,.13), rgba(95,146,106,.06));
  color: var(--canopy-deep);
}
@media (prefers-color-scheme: dark) {
  .admin-table thead th, .lb-table thead th, .prose table thead th { color: var(--canopy-lite); }
}

/* --- forms take a canopy focus ring -------------------------------- */
input:focus-visible, textarea:focus-visible, select:focus-visible,
.option:focus-within, .btn:focus-visible, a:focus-visible {
  outline-color: var(--canopy);
}
input[type=text]:focus, input[type=email]:focus,
input[type=password]:focus, textarea:focus {
  border-color: var(--canopy);
}

/* --- footer: the canopy line closes the page ----------------------- */
.site-footer { position: relative; }
.site-footer::before {
  content: "";
  position: absolute;
  top: -1px; left: 0; right: 0;
  height: 3px;
  background: linear-gradient(90deg, var(--canopy), var(--amber) 48%, var(--orange) 72%, var(--bark));
  opacity: .75;
}

/* --- section rules across the app ---------------------------------- */
.section-title { position: relative; padding-left: 15px; }
.section-title::before {
  content: "";
  position: absolute;
  left: 0; top: .3em; bottom: .26em;
  width: 3px;
  border-radius: 999px;
  background: linear-gradient(180deg, var(--canopy), var(--amber));
}

/* --- admin keeps the theme, at lower volume ------------------------ */
.admin-nav a.active { border-bottom-color: var(--orange); color: var(--orange-dark); }
@media (prefers-color-scheme: dark) { .admin-nav a.active { color: var(--amber); } }

/* ==================================================================
   Tiki, getting your attention
   ------------------------------------------------------------------
   Motion does the work that sound cannot: browsers block audio until
   the user has interacted with the page, and plenty of people browse
   muted. The squawk is the primary attention cue and the chime is a
   bonus on top of it, not the other way round.
   ================================================================== */

@keyframes tiki-squawk {
  0%   { transform: rotate(0deg)    scale(1); }
  18%  { transform: rotate(-9deg)   scale(1.09); }
  38%  { transform: rotate(7deg)    scale(1.05); }
  58%  { transform: rotate(-5deg)   scale(1.03); }
  78%  { transform: rotate(3deg)    scale(1.01); }
  100% { transform: rotate(0deg)    scale(1); }
}
@keyframes tiki-ring {
  0%   { box-shadow: 0 0 0 0 rgba(246,130,31,.45); }
  100% { box-shadow: 0 0 0 18px rgba(246,130,31,0); }
}

.is-squawk { animation: tiki-squawk .62s cubic-bezier(.3,1.4,.4,1) 1 !important; }

/* An outcome worth noticing: the bubble pulses twice, once. */
.tiki--shout .tiki__bubble { animation: tiki-pop .45s cubic-bezier(.2,.8,.3,1) both, tiki-ring 1.1s ease-out .45s 2; }
.tiki--shout .tiki__bird img { animation: tiki-squawk .62s cubic-bezier(.3,1.4,.4,1) .2s 1, tiki-talk 9s ease-in-out 1s infinite; }

.tiki__bird img { cursor: pointer; }

/* --- the sound toggle in the masthead ----------------------------- */
.snd-toggle {
  display: inline-grid;
  place-items: center;
  width: 34px; height: 34px;
  padding: 0;
  border: 1px solid rgba(255,255,255,.22);
  border-radius: 50%;
  background: rgba(255,255,255,.07);
  color: #fff;
  font-size: .95rem;
  line-height: 1;
  cursor: pointer;
}
.snd-toggle:hover { background: rgba(255,255,255,.16); }
.snd-toggle[aria-pressed="false"] { opacity: .55; }

@media (prefers-reduced-motion: reduce) {
  .is-squawk,
  .tiki--shout .tiki__bubble,
  .tiki--shout .tiki__bird img { animation: none !important; }
}

@media print { .snd-toggle { display: none !important; } }

/* ==================================================================
   The reading vine
   ------------------------------------------------------------------
   Scroll progress for a long lesson, as a vine that grows down the
   gutter with Tiki riding the tip. It only appears where there is
   genuinely spare gutter — a progress ornament must never cost the
   reading column any width.
   ================================================================== */

.vine {
  position: fixed;
  top: 50%;
  left: 22px;
  z-index: 5;
  width: 60px;
  height: 620px;
  margin-top: -310px;
  pointer-events: none;
  opacity: 0;
  transition: opacity .5s ease;
}
.vine.is-ready { opacity: 1; }
.vine svg { display: block; width: 60px; height: 620px; overflow: visible; }

/* The unread length has to look like a VINE, not a stray hairline — at 0%
   progress the track is all there is, and a faint line reads as a rendering
   artefact rather than an ornament. */
.vine__track {
  stroke: var(--bark);
  stroke-width: 4;
  stroke-linecap: round;
  opacity: .5;
}
.vine__grow {
  stroke: url(#vineGrad);
  stroke: var(--canopy);
  stroke-width: 3.4;
  stroke-linecap: round;
  stroke-dasharray: 100;
  stroke-dashoffset: 100;
  transition: stroke-dashoffset .18s linear;
  filter: drop-shadow(0 1px 2px rgba(17,43,60,.18));
}
.vine.is-done .vine__grow { stroke: var(--orange); }

/* Unopened leaves are visible as buds rather than absent: the shape of the
   whole route should be legible before any of it is travelled. */
.vine__leaf {
  fill: var(--bark);
  opacity: .34;
  transform-box: fill-box;
  transform-origin: 0% 50%;
  transition: opacity .4s ease, scale .5s cubic-bezier(.3,1.5,.4,1), fill .4s ease;
  scale: .42;
}
.vine__leaf.is-open { fill: var(--canopy); opacity: .95; scale: 1; }
.vine__leaf:nth-child(even) { fill: var(--canopy-mid); }

.vine__tiki {
  position: absolute;
  top: 0; left: 0;
  width: 62px;
  height: auto;
  transition: transform .18s linear;
  filter: drop-shadow(0 4px 7px rgba(17,43,60,.28));
}

.vine__pct {
  position: absolute;
  left: 4px;
  bottom: -26px;
  width: 60px;
  text-align: center;
  font-size: .72rem;
  font-weight: 700;
  letter-spacing: .04em;
  color: var(--canopy-mid);
}
.vine.is-done .vine__pct { color: var(--orange); }

/* Motion off: the vine still reports progress, it just stops easing. */
.vine.is-still .vine__grow,
.vine.is-still .vine__tiki,
.vine.is-still .vine__leaf { transition: none; }

/* The gutter has to be genuinely spare. Below this the vine is hidden
   outright rather than overlapping the column. */
@media (max-width: 1279px) { .vine { display: none; } }
@media (max-height: 680px) { .vine { display: none; } }
@media print { .vine { display: none !important; } }

/* ==================================================================
   Jungle framing on ordinary pages
   ------------------------------------------------------------------
   READABILITY IS THE CONSTRAINT. Scenery is confined to two gutter
   columns beside the content measure, each clipping its own overflow,
   so nothing can end up behind text. The width is derived from the
   content width rather than chosen, which means no later change to a
   frond's size or position can push it into the reading column.

   Below the point where a real gutter exists, the scenery is gone.
   ================================================================== */

.jf {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
}
.site-header, .wrap, .site-footer { position: relative; z-index: 1; }

.jf__gutter {
  position: absolute;
  top: 0;
  bottom: 0;
  /* Exactly the space beside the content measure, and never a pixel more.
     A gutter narrower than this floor is not worth decorating. */
  width: max(0px, calc((100vw - var(--maxw)) / 2 - 12px));
  overflow: hidden;          /* the guarantee: nothing escapes into the column */
}
.jf__gutter--l { left: 0; }
.jf__gutter--r { right: 0; }

.jf__vine {
  position: absolute;
  top: 0;
  width: 120px;
  height: 300px;
  opacity: .34;
}
.jf__gutter--l .jf__vine { right: 6px; }
.jf__gutter--r .jf__vine { left: 6px; }

.jf__corner {
  position: absolute;
  bottom: 0;
  width: 300px;
  height: 260px;
  opacity: .34;
}
.jf__gutter--l .jf__corner { left: -40px; }
.jf__gutter--r .jf__corner { right: -40px; }

.jf__vine path { animation: tiki-sway 17s ease-in-out infinite; transform-origin: top center; }

/* No gutter, no scenery. Cheaper than clipping it to nothing, and it
   guarantees phones and laptops carry none of the cost. */
@media (max-width: 1320px) { .jf { display: none; } }

@media (prefers-color-scheme: dark) {
  .jf__vine, .jf__corner { opacity: .26; }
}

@media (prefers-reduced-motion: reduce) { .jf__vine path { animation: none !important; } }
@media print { .jf { display: none !important; } }

/* ==================================================================
   Deeper jungle on the interior
   ================================================================== */

/* --- the masthead gets a canopy hanging beneath it ------------------ */
.site-header::after {
  height: 4px;
  background: linear-gradient(90deg, var(--canopy) 0%, var(--canopy-lite) 18%, var(--amber) 48%, var(--orange) 72%, var(--bark) 100%);
}

/* --- cards read as field-guide paper -------------------------------- */
/* Kept off .card, because a card is the usual home for body copy and a tint
   under paragraphs is exactly the thing that makes long text tiring. The wash
   goes only on cards that hold headings, figures and numbers. */
.stat, .outline-card, .j-benefit, .mini-card, .lb-table {
  background-image:
    radial-gradient(28rem 14rem at 108% -18%, rgba(95,146,106,.05), transparent 60%);
}

/* --- course cards become expedition cards --------------------------- */
.course-card {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-lg);
}
/* A canopy strip across the head of the card. */
.course-card::before {
  content: "";
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 4px;
  background: linear-gradient(90deg, var(--canopy), var(--amber) 55%, var(--orange));
}
/* A leaf watermark in the corner, so the card is not flat. */
.course-card::after {
  content: "";
  position: absolute;
  right: -40px; top: -40px;
  width: 132px; height: 132px;
  border-radius: var(--leaf-radius);
  background: var(--canopy);
  opacity: .06;
  transition: opacity .2s ease, transform .25s ease;
}
.course-card:hover::after { opacity: .12; transform: scale(1.08) rotate(-8deg); }

/* --- lesson rows in the outline get a trail spine ------------------- */
.outline-list { position: relative; }
.outline-list::before {
  content: "";
  position: absolute;
  left: 15px; top: 6px; bottom: 6px;
  width: 2px;
  background: repeating-linear-gradient(180deg, var(--bark) 0 5px, transparent 5px 11px);
  opacity: .32;
}

/* --- the score hero gets a canopy crown ----------------------------- */
.score-hero::before {
  content: "";
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 46px;
  background:
    radial-gradient(16px 12px at 8%  0, var(--canopy) 60%, transparent 62%),
    radial-gradient(20px 15px at 24% 0, var(--canopy-mid) 60%, transparent 62%),
    radial-gradient(17px 12px at 41% 0, var(--canopy) 60%, transparent 62%),
    radial-gradient(21px 15px at 58% 0, var(--canopy-mid) 60%, transparent 62%),
    radial-gradient(16px 12px at 75% 0, var(--canopy) 60%, transparent 62%),
    radial-gradient(20px 14px at 92% 0, var(--canopy-mid) 60%, transparent 62%);
  opacity: .5;
}

/* --- the outline's current lesson gets a Tiki footprint ------------- */
.outline-list a.here { position: relative; }

/* --- empty states sit in a clearing --------------------------------- */
.j-empty, .empty, .empty-state {
  position: relative;
  border-radius: 20px;
  background: radial-gradient(20rem 10rem at 50% 120%, rgba(95,146,106,.14), transparent 68%);
}

/* ==================================================================
   The lesson page, redesigned
   ------------------------------------------------------------------
   Three problems with the old header: the lesson number was buried in
   a line of grey metadata, the module was not shown at all, and status
   competed with the title. This puts the number first as a trail
   marker, names the module above the title, and demotes status to a
   row of quiet chips underneath.
   ================================================================== */

.lhead {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 20px;
  align-items: start;
  position: relative;
  margin: 0 0 26px;
  padding: 26px 0 22px;
  border-bottom: 1px solid var(--border);
}
/* The canopy band belongs to this header now, not the generic .page-head. */
.lhead::before {
  content: "";
  position: absolute;
  top: 0; left: -14px; right: -14px;
  height: 5px;
  border-radius: 999px;
  background: linear-gradient(90deg, var(--canopy), var(--canopy-lite) 20%, var(--amber) 54%, var(--orange) 76%, var(--bark));
}

/* A trail marker carrying the lesson number. */
.lhead__marker {
  display: grid;
  place-items: center;
  align-content: center;
  width: 76px; height: 76px;
  border-radius: 22px;
  background: linear-gradient(150deg, var(--canopy), var(--canopy-mid));
  color: #fff;
  box-shadow: inset 0 1px 0 rgba(255,255,255,.28), 0 8px 18px rgba(17,43,60,.16);
}
.lhead__n  { font-size: 1.9rem; font-weight: 700; line-height: 1; letter-spacing: -.03em; }
.lhead__of { font-size: .66rem; letter-spacing: .09em; text-transform: uppercase; opacity: .82; margin-top: 3px; }

.lhead__module {
  font-size: .74rem;
  font-weight: 700;
  letter-spacing: .11em;
  text-transform: uppercase;
  color: var(--canopy-mid);
  margin-bottom: 6px;
}
.lhead h1 {
  margin: 0 0 6px;
  font-size: clamp(1.7rem, 3.1vw, 2.35rem);
  line-height: 1.12;
  letter-spacing: -.028em;
}
.lhead__sub { margin: 0 0 14px; color: var(--muted); font-size: 1.04rem; line-height: 1.5; }

.lhead__meta { display: flex; flex-wrap: wrap; gap: 7px; }
.lmeta {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 11px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--surface);
  font-size: .8rem;
  color: var(--muted);
  text-decoration: none;
}
a.lmeta:hover { border-color: var(--canopy); color: var(--text); text-decoration: none; }
.lmeta--pass { background: rgba(95,146,106,.15); border-color: var(--canopy); color: var(--canopy-deep); font-weight: 600; }
.lmeta--try  { background: rgba(245,156,26,.15); border-color: var(--amber);  color: var(--orange-dark); font-weight: 600; }
.lmeta--lock { background: var(--surface-2); }
.lmeta--mark { background: rgba(245,156,26,.15); border-color: var(--amber); color: var(--orange-dark); }
@media (prefers-color-scheme: dark) {
  .lmeta--pass { color: var(--canopy-lite); }
  .lmeta--try, .lmeta--mark { color: var(--amber); }
}

@media (max-width: 620px) {
  .lhead { grid-template-columns: 1fr; gap: 14px; }
  .lhead__marker { width: 60px; height: 60px; border-radius: 18px; }
  .lhead__n { font-size: 1.5rem; }
}

/* --- reading measure and rhythm ------------------------------------ */
/* 1,500 words of technical prose: the body needs a little more size and
   a lot more leading than the rest of the interface. */
.lesson-main .prose { font-size: 1.045rem; line-height: 1.75; }
.lesson-main .prose > p { margin: 0 0 1.15em; }
.lesson-main .prose h2 { margin-top: 2.2em; scroll-margin-top: 24px; }
.lesson-main .prose h3 { margin-top: 1.7em; }
.lesson-main .prose > ul, .lesson-main .prose > ol { margin-bottom: 1.2em; }

/* --- table of contents --------------------------------------------- */
.toc {
  margin-bottom: 14px;
  padding: 14px 16px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface);
}
.toc__head {
  font-size: .7rem;
  font-weight: 700;
  letter-spacing: .11em;
  text-transform: uppercase;
  color: var(--canopy-mid);
  margin-bottom: 9px;
}
.toc__list { list-style: none; margin: 0; padding: 0; counter-reset: toc; }
.toc__list li { position: relative; margin-bottom: 2px; }
.toc__list a {
  display: block;
  padding: 5px 8px 5px 16px;
  border-radius: 7px;
  font-size: .86rem;
  line-height: 1.4;
  color: var(--muted);
  text-decoration: none;
  border-left: 2px solid transparent;
}
.toc__list a:hover { background: var(--surface-2); color: var(--text); text-decoration: none; }
/* The section currently under the reader's eye. */
.toc__list a.is-here {
  border-left-color: var(--orange);
  background: rgba(246,130,31,.09);
  color: var(--text);
  font-weight: 600;
}

/* --- trail navigation ---------------------------------------------- */
.trailnav {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
  margin-top: 30px;
}
.trailnav__card {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 15px 17px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface);
  text-decoration: none;
  color: inherit;
  transition: transform .16s ease, border-color .16s ease, box-shadow .16s ease;
}
.trailnav__card:hover {
  transform: translateY(-2px);
  border-color: var(--canopy);
  box-shadow: var(--shadow-sm);
  text-decoration: none;
}
.trailnav__card--next { text-align: right; border-left-width: 1px; }
.trailnav__card--next:hover { border-color: var(--orange); }
.trailnav__dir {
  font-size: .72rem;
  font-weight: 700;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--canopy-mid);
}
.trailnav__card--next .trailnav__dir { color: var(--orange-dark); }
@media (prefers-color-scheme: dark) { .trailnav__card--next .trailnav__dir { color: var(--amber); } }
.trailnav__title { font-weight: 600; line-height: 1.35; }

@media (max-width: 620px) { .trailnav { grid-template-columns: 1fr; } }

/* ==================================================================
   Legibility corrections
   ------------------------------------------------------------------
   Measured against the rendered page rather than eyeballed.

   --muted carries lead paragraphs, captions and lesson subtitles —
   body copy, not chrome — and at #5d7080 it scraped AA at 4.73:1.
   #4a5c6b takes it to 6.38 on the page and 6.92 on a card, which is
   at the AAA boundary for the sizes it is actually used at. Dark mode
   was already comfortable and is left alone.
   ================================================================== */
:root { --muted: #4a5c6b; }

/* The canopy greens are decorative fills, but two of them carry text:
   section labels and the module eyebrow. Those take the deeper shade so
   they are read as text rather than as a tint. */
.j-rule span,
.lhead__module,
.toc__head,
.outline-course,
.outline-module,
.stat .label { color: #3f6b4c; }

@media (prefers-color-scheme: dark) {
  .j-rule span,
  .lhead__module,
  .toc__head,
  .outline-course,
  .outline-module,
  .stat .label { color: var(--canopy-lite); }
}

/* Nothing decorative may sit between the reader and the words. Any
   surface that can hold a paragraph stays opaque. */
.prose, .card, .callout, .objectives, .tiki__bubble { backdrop-filter: none; }

/* ==================================================================
   Tiki's tutor
   ------------------------------------------------------------------
   Collapsed by default. A chat panel sitting open between the lesson
   body and the assessment would compete with the reading; closed, it
   is an offer the learner takes when they want it.
   ================================================================== */

/* --- Tiki's dock -----------------------------------------------------------

   The tutor floats in the corner instead of sitting at the foot of the lesson.
   You reach for a tutor at the paragraph that lost you, not after scrolling
   past everything that did not — docked, Tiki is one click away from wherever
   the reader actually got stuck, and his answer lands beside the text that
   prompted the question.

   Only the shell is new. The log, bubbles, starters and composer below are
   unchanged, which is also why the panel keeps the `tchat__*` names inside. */

.tdock {
  position: fixed;
  right: clamp(12px, 2.2vw, 28px);
  bottom: clamp(12px, 2.2vw, 28px);
  z-index: 40;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 12px;
  /* The dock is a positioning frame, not a surface: without this the invisible
     box would swallow clicks on the lesson beneath it. */
  pointer-events: none;
}
.tdock > * { pointer-events: auto; }

/* ----- the launcher Tiki perches on */

.tdock__fab {
  order: 2;                          /* below the panel, whatever the source order */
  position: relative;
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 9px 20px 9px 14px;
  border: 1px solid var(--amber);
  border-radius: 999px;
  /* Ink and teal, NOT the brand orange — Tiki is orange, and an orange bird on
     an orange pill is a silhouette with nothing to read. Against the ink he
     separates at 5.67:1 and the amber ring keeps the button unmistakably
     MDriven. The label is white at 14.64:1; on the orange it measured 2.58:1
     against a 4.5:1 requirement, which is what sent this back to the drawing
     board in the first place. */
  background: linear-gradient(135deg, var(--ink), var(--teal));
  color: #fff;
  font: inherit;
  font-weight: 700;
  font-size: .95rem;
  cursor: pointer;
  box-shadow: 0 10px 26px rgba(17, 43, 60, .28);
  transition: transform .2s cubic-bezier(.34, 1.4, .5, 1), box-shadow .2s ease;
}
.tdock__fab:hover  { transform: translateY(-2px) scale(1.02); box-shadow: 0 14px 32px rgba(17, 43, 60, .34); }
.tdock__fab:active { transform: translateY(0) scale(.99); }
.tdock__fab:focus-visible { outline: 3px solid var(--teal); outline-offset: 3px; }

/* Tiki leans out of the pill rather than sitting inside it — he is perched on
   the button, which is the whole joke. */
.tdock__bird {
  width: 46px;
  height: auto;
  margin: -16px -2px -14px 0;
  filter: drop-shadow(0 3px 5px rgba(17, 43, 60, .3));
  transform-origin: 60% 90%;
  animation: tdock-bob 4.5s ease-in-out infinite;
}
@keyframes tdock-bob {
  0%, 88%, 100% { transform: translateY(0) rotate(0deg); }
  92%           { transform: translateY(-3px) rotate(-5deg); }
  96%           { transform: translateY(-1px) rotate(3deg); }
}

/* A leaf tucked behind the pill, so the launcher reads as part of the jungle
   rather than a generic support widget. */
.tdock__leaf {
  position: absolute;
  right: 8px;
  top: -13px;
  width: 26px;
  height: 15px;
  border-radius: 0 100% 0 100%;
  background: var(--canopy);
  transform: rotate(-24deg);
  transform-origin: bottom right;
  animation: tdock-leaf 6s ease-in-out infinite;
  pointer-events: none;
}
@keyframes tdock-leaf {
  0%, 100% { transform: rotate(-24deg); }
  50%      { transform: rotate(-16deg); }
}

.tdock__label { white-space: nowrap; }
.tdock__x {
  display: none;
  font-size: 1.5rem;
  line-height: 1;
  margin-top: -2px;
}
/* Open: the pill collapses to a round close button and gets out of the way. */
.tdock.is-open .tdock__fab   { padding: 11px 15px; }
.tdock.is-open .tdock__label,
.tdock.is-open .tdock__bird,
.tdock.is-open .tdock__leaf  { display: none; }
.tdock.is-open .tdock__x     { display: block; }

/* Spent quota: still openable (the transcript is worth reading back), but it
   stops advertising itself. */
.tdock.is-spent .tdock__fab { background: var(--surface-2); color: var(--muted); border-color: var(--border); }
.tdock.is-spent .tdock__label { font-weight: 600; }
.tdock.is-spent .tdock__bird { filter: grayscale(.7) drop-shadow(0 3px 5px rgba(17, 43, 60, .2)); }

/* ----- the panel */

.tdock__panel {
  order: 1;
  display: flex;
  flex-direction: column;
  /* Sized for reading a conversation, not for peeking at one. With the
     suggestion chips gone the log takes everything the header and composer do
     not, so the extra height all goes to the chat. */
  width: min(460px, calc(100vw - 24px));
  height: min(78vh, 720px);
  max-height: calc(100vh - 96px);
  border: 1px solid var(--border);
  border-radius: 18px;
  background: var(--surface);
  box-shadow: 0 24px 60px rgba(17, 43, 60, .3);
  overflow: hidden;

  /* Closed state. `visibility` rather than `display` so the transition runs,
     and so the panel is removed from the tab order while hidden. */
  visibility: hidden;
  opacity: 0;
  transform: translateY(14px) scale(.97);
  transform-origin: bottom right;
  transition: opacity .2s ease, transform .24s cubic-bezier(.34, 1.3, .55, 1), visibility .24s;
}
.tdock.is-open .tdock__panel {
  visibility: visible;
  opacity: 1;
  transform: translateY(0) scale(1);
}

.tdock__head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px 10px 14px;
  background: linear-gradient(120deg, rgba(95, 146, 106, .18), rgba(246, 130, 31, .13));
  border-bottom: 1px solid var(--border);
  flex: none;
}
.tdock__headbird { width: 38px; height: auto; flex: none; }
.tdock__headtext { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.tdock__headtext b { font-size: .95rem; }
.tdock__headtext span {
  font-size: .78rem;
  color: var(--muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.tdock__close {
  margin-left: auto;
  flex: none;
  width: 30px;
  height: 30px;
  border: 1px solid transparent;
  border-radius: 9px;
  background: none;
  color: var(--muted);
  font-size: 1.35rem;
  line-height: 1;
  cursor: pointer;
  transition: background .15s ease, color .15s ease;
}
.tdock__close:hover { background: var(--surface-2); color: var(--ink); border-color: var(--border); }

/* Phones: a bottom sheet, because a 420px card beside a 380px screen is not a
   card, it IS the screen. */
@media (max-width: 560px) {
  .tdock { left: 0; right: 0; bottom: 0; align-items: stretch; gap: 0; }
  .tdock__fab {
    align-self: flex-end;
    margin: 0 12px 12px;
  }
  .tdock__panel {
    width: 100%;
    max-height: 82vh;
    border-radius: 18px 18px 0 0;
    border-bottom: none;
    transform-origin: bottom center;
  }
  /* Open on a phone the sheet fills the screen, so a launcher floating below it
     is just page content peeping through a gap. The header's close button is
     the only control that needs to be there. */
  .tdock.is-open .tdock__fab { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .tdock__bird, .tdock__leaf { animation: none; }
  .tdock__panel { transition: opacity .12s ease, visibility .12s; transform: none; }
  .tdock.is-open .tdock__panel { transform: none; }
  .tdock__fab:hover { transform: none; }
}

.tchat__log {
  /* The panel is a flex column with a fixed height, so the log takes everything
     the header and composer leave. min-height:0 is what actually lets a flex
     child scroll rather than grow its parent. */
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;      /* scrolling the chat must not scroll the lesson */
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  scrollbar-width: thin;
}

.tchat__row { display: flex; gap: 10px; align-items: flex-start; }
.tchat__row--me { justify-content: flex-end; }
/* Smaller than it was inline: in a 460px panel the avatar column and the
   bubble are competing for the same line length, and the words win. */
.tchat__avatar { width: 34px; height: auto; flex: none; margin-top: 1px; }

.tchat__bubble {
  /* Tiki's answers are the content here, so they get the width. The learner's
     own messages stay narrower (below) — they are short, and keeping them
     visibly inset is what makes the two sides readable at a glance. */
  max-width: 88%;
  padding: 11px 14px;
  border-radius: 14px;
  border: 1px solid var(--border);
  background: var(--surface-2);
  font-size: .94rem;
  line-height: 1.62;
}
.tchat__row--me .tchat__bubble {
  max-width: 82%;
  background: linear-gradient(140deg, rgba(246,130,31,.16), rgba(245,156,26,.10));
  border-color: var(--amber);
}
.tchat__bubble > :first-child { margin-top: 0; }
.tchat__bubble > :last-child { margin-bottom: 0; }
.tchat__bubble p { margin: 0 0 .7em; }
.tchat__bubble ul, .tchat__bubble ol { margin: 0 0 .7em; padding-left: 20px; }
.tchat__bubble li { margin-bottom: 3px; }
.tchat__bubble code {
  padding: 1px 5px;
  border-radius: 5px;
  background: rgba(95,146,106,.14);
  font-size: .92em;
}
.tchat__bubble pre {
  margin: 0 0 .7em;
  padding: 10px 12px;
  border-radius: 9px;
  border: 1px solid var(--border);
  border-left: 3px solid var(--canopy);
  background: var(--bg);
  overflow-x: auto;
}
.tchat__bubble pre code { background: none; padding: 0; }
.tchat__bubble.is-warn { border-color: var(--amber); background: rgba(245,156,26,.12); }

/* Three dots while Tiki is thinking. */
.tchat__bubble.is-thinking { display: flex; gap: 5px; align-items: center; padding: 15px 16px; }
.tchat__bubble.is-thinking i {
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--canopy);
  animation: tchat-dot 1.25s ease-in-out infinite;
}
.tchat__bubble.is-thinking i:nth-child(2) { animation-delay: .16s; }
.tchat__bubble.is-thinking i:nth-child(3) { animation-delay: .32s; }
@keyframes tchat-dot {
  0%, 60%, 100% { opacity: .28; transform: translateY(0); }
  30%           { opacity: 1;   transform: translateY(-4px); }
}

/* --- the MDriven library --------------------------------------------------

   Tiki teaches the lesson and refuses everything else. When he refuses, this
   is the way out: the same question answered from the MDriven wiki, site and
   video corpus, with citations.

   It is styled as a deliberately different object — teal instead of canopy
   green, a field-notebook rule down the left, its own header — because the
   learner must never have to wonder which side of the lesson boundary an
   answer came from. Looking different IS the feature. */

.tchat__scope {
  margin: .55em 0 0;
  padding-top: .5em;
  border-top: 1px dashed var(--border);
  font-size: .82rem;
  color: var(--muted);
}

.tchat__offer {
  flex: none;                        /* same flex-shrink trap as .tchat__lib */
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 10px;
  margin: 2px 0 2px 44px;            /* clears Tiki's avatar column */
  padding: 9px 12px;
  border: 1px dashed var(--teal);
  border-radius: 12px;
  background: rgba(11, 84, 97, .06);
  font-size: .85rem;
  color: var(--muted);
}
.tchat__offerbtn {
  margin-left: auto;
  padding: 6px 12px;
  border: 1px solid var(--teal);
  border-radius: 999px;
  background: var(--surface-2);
  color: var(--teal);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
  transition: background .18s ease, transform .18s ease;
}
.tchat__offerbtn:hover { background: rgba(11, 84, 97, .12); transform: translateY(-1px); }
.tchat__offerbtn:disabled { opacity: .5; cursor: default; transform: none; }

.tchat__lib {
  /* `.tchat__log` is a flex column with a max-height, so every child is a
     shrinkable flex item. A flex item's automatic minimum size normally stops
     it collapsing below its content — but `overflow: hidden` below (needed for
     the rounded corners) resolves that minimum to zero, and the card gets
     crushed to a sliver with its answer clipped away. The plain chat rows
     escape this only because they never set `overflow`. */
  flex: none;
  margin: 4px 0 4px 44px;
  border: 1px solid var(--border);
  border-left: 4px solid var(--teal);
  border-radius: 12px;
  background: var(--surface-2);
  overflow: hidden;
}
.tchat__libhead {
  padding: 7px 13px;
  background: rgba(11, 84, 97, .09);
  border-bottom: 1px solid var(--border);
  font-size: .76rem;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--teal);
}
/* Numbers are rendered as content, not as list markers: passages from one page
   are merged into a single row that carries several citation numbers ("1, 2,
   3."), which a counter cannot express. */
.tchat__libsrc {
  margin: 0;
  padding: 10px 13px 0;
  list-style: none;
  font-size: .8rem;
  color: var(--muted);
}
.tchat__libnum {
  display: inline-block;
  min-width: 1.6em;
  margin-right: 4px;
  color: var(--teal);
  font-weight: 700;
}
.tchat__libsrc:empty { padding: 0; }
.tchat__libsrc li { margin-bottom: 3px; }
.tchat__libsrc a { color: var(--teal); text-decoration: underline; text-underline-offset: 2px; }
.tchat__libbody {
  padding: 11px 13px;
  font-size: .92rem;
  line-height: 1.62;
}
.tchat__libbody > :first-child { margin-top: 0; }
.tchat__libbody > :last-child  { margin-bottom: 0; }
.tchat__libbody h3, .tchat__libbody h4, .tchat__libbody h5, .tchat__libbody h6,
.tchat__bubble h3, .tchat__bubble h4, .tchat__bubble h5, .tchat__bubble h6 {
  margin: 1em 0 .35em;
  font-size: .95em;
  font-weight: 700;
  line-height: 1.3;
}
.tchat__libbody h3, .tchat__libbody h4 { color: var(--teal); }
.tchat__bubble h3, .tchat__bubble h4   { color: var(--canopy-deep); }
.tchat__libbody p  { margin: 0 0 .7em; }
.tchat__libbody ul, .tchat__libbody ol { margin: 0 0 .7em; padding-left: 20px; }
/* Tinted from --teal rather than from ink: an ink wash at 7% disappears
   against the dark palette, the way the canopy tint on .tchat__bubble does
   not. Same reason `pre` sits on var(--bg) instead of a fixed colour. */
.tchat__libbody code {
  background: rgba(11, 84, 97, .13);
  padding: 1px 5px;
  border-radius: 5px;
  font-size: .92em;
}
.tchat__libbody pre {
  margin: 0 0 .7em;
  padding: 10px 12px;
  border-radius: 9px;
  border: 1px solid var(--border);
  border-left: 3px solid var(--teal);
  background: var(--bg);
  overflow-x: auto;
}
.tchat__libbody pre code { background: none; padding: 0; }
.tchat__libbody.is-warn { color: var(--ink); background: rgba(245, 156, 26, .12); }
.tchat__libbody.is-thinking { display: flex; gap: 5px; align-items: center; padding: 15px 16px; }
.tchat__libbody.is-thinking i {
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--teal);
  animation: tchat-dot 1.25s ease-in-out infinite;
}
.tchat__libbody.is-thinking i:nth-child(2) { animation-delay: .16s; }
.tchat__libbody.is-thinking i:nth-child(3) { animation-delay: .32s; }

@media (max-width: 640px) {
  .tchat__offer, .tchat__lib { margin-left: 0; }
  .tchat__offerbtn { margin-left: 0; }
}

/* The composer is the panel's floor. With nothing between it and the log it
   needs its own edge, or the last message appears to sit on top of the input. */
.tchat__form {
  flex: none;
  padding: 12px 16px 14px;
  border-top: 1px solid var(--border);
  background: var(--surface);
}
.tchat__inputwrap { position: relative; }
.tchat__inputwrap textarea {
  width: 100%;
  padding: 11px 46px 11px 13px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--surface-2);
  color: var(--text);
  font: inherit;
  font-size: .94rem;
  line-height: 1.5;
  resize: vertical;
}
.tchat__inputwrap textarea:focus { outline: 2px solid var(--canopy); outline-offset: 1px; }

.tchat__actions { display: flex; align-items: center; gap: 12px; margin-top: 9px; }
.tchat__quota { font-size: .78rem; color: var(--muted); }
.tchat__actions .btn { margin-left: auto; }
.tchat.is-spent .tchat__form textarea,
.tchat.is-spent .tchat__starter { opacity: .55; pointer-events: none; }

/* ==================================================================
   Dictation
   ================================================================== */

.notes-field { position: relative; }
.notes-field textarea { padding-right: 48px; }

.dictate-btn {
  position: absolute;
  right: 9px;
  bottom: 9px;
  display: grid;
  place-items: center;
  width: 32px; height: 32px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 50%;
  background: var(--surface);
  cursor: pointer;
  transition: border-color .16s ease, background .16s ease;
}
.dictate-btn:hover { border-color: var(--canopy); }
.dictate-btn[hidden] { display: none; }

/* A microphone drawn in CSS — one element, no icon font, no extra request. */
.dictate-btn__icon {
  position: relative;
  width: 9px; height: 14px;
  border-radius: 5px;
  background: var(--muted);
  transition: background .16s ease;
}
.dictate-btn__icon::before {
  content: "";
  position: absolute;
  left: -5px; bottom: -6px;
  width: 19px; height: 10px;
  border: 2px solid var(--muted);
  border-top: 0;
  border-radius: 0 0 11px 11px;
  transition: border-color .16s ease;
}
.dictate-btn__icon::after {
  content: "";
  position: absolute;
  left: 3px; bottom: -9px;
  width: 3px; height: 4px;
  background: var(--muted);
  transition: background .16s ease;
}

.dictate-btn[data-state="recording"] {
  border-color: var(--red);
  background: var(--red-soft);
  animation: tiki-ring 1.4s ease-out infinite;
}
.dictate-btn[data-state="recording"] .dictate-btn__icon,
.dictate-btn[data-state="recording"] .dictate-btn__icon::after { background: var(--red); }
.dictate-btn[data-state="recording"] .dictate-btn__icon::before { border-color: var(--red); }

.dictate-btn[data-state="working"] { border-color: var(--amber); }
.dictate-btn[data-state="working"] .dictate-btn__icon,
.dictate-btn[data-state="working"] .dictate-btn__icon::after { background: var(--amber); }
.dictate-btn[data-state="working"] .dictate-btn__icon::before { border-color: var(--amber); }

.dictate-note { font-size: .78rem; color: var(--muted); }
.dictate-note.is-warn { color: var(--orange-dark); }
.dictate-note.is-ok { color: var(--canopy-deep); }
@media (prefers-color-scheme: dark) {
  .dictate-note.is-warn { color: var(--amber); }
  .dictate-note.is-ok { color: var(--canopy-lite); }
}

.visually-hidden {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

@media (prefers-reduced-motion: reduce) {
  .tchat__bubble.is-thinking i,
  .dictate-btn[data-state="recording"] { animation: none !important; }
}

@media print { .tdock, .dictate-btn, .dictate-note { display: none !important; } }
